Skip to content

Architecture

rbnboe edited this page Oct 15, 2026 · 1 revision

Architecture

System overview

Browser ──HTTP──> Next.js frontend ──> FastAPI backend ──> PostgreSQL
                                            ├── OPC UA ──> Siemens S7 PLC
                                            └── AMQP ────> RabbitMQ / ERP

The frontend calls the FastAPI backend through server-side Next.js proxy routes. The backend persists application data and runs the PLC order worker. Development Compose starts PostgreSQL and the backend; production Compose uses the separately published backend and frontend images behind Traefik.

Order worker

OrderWorkerHost owns the shared production and delivery dispatch loop. It acquires a database leadership lock before PLC dispatch so multiple backend instances do not run competing order workers. It observes both order slots, reconciles persisted state after restart, and delegates queue admission and PLC handshakes to the order services. Orders from both types share a durable FIFO and exclusive dispatch state.

Product catalog synchronization runs in a separate background task and uses a dedicated PLC read connection when configured. The live inventory endpoint uses another separate PlcStorageGateway; inventory reads therefore do not share the dispatch connection.

RabbitMQ flows

There are two independent stock flows:

  • Inbound ERP to MES: the storage consumer reads snapshots and deltas from the configured RABBITMQ_STORAGE_INBOUND_QUEUE and updates the database-backed legacy /storage data. It remains disabled without a configured queue.
  • Outbound MES to ERP: the order worker publishes stock deltas for confirmed good units. Events are first stored in a PostgreSQL outbox and are marked published only after broker confirmation. Failed publishes remain pending for retry; consumers should deduplicate on the stable AMQP message_id.

The frontend inventory page uses GET /plc-storage/ and reads current PLC values directly. It does not use the RabbitMQ-backed /storage endpoint.

Order state and recovery

Orders are durably admitted to PostgreSQL before the API returns 202 Accepted. A single active run is dispatched at a time. The state machine records queued, preparing, starting, running, completion, reset, interruption, and review-required states. On restart, the worker reconciles a persisted active run against current PLC observations before dispatching another order. When PLC evidence cannot establish an outcome, the run can require manual review.

See Data Model and API for routes and records, and OPC UA Integration for the handshake details.

Clone this wiki locally