8.0 KiB
Plast Track MVP
Lightweight MES/IoT MVP for supervising multi-brand injection molding machines using passive signal acquisition, MQTT event ingestion, operator input, and real-time web dashboards.
Objective
This MVP supervises production without Euromap dependency and without sending any command back to the press. It focuses on:
- machine status visualization
- automatic cycle counting
- real cycle time capture
- current production order tracking
- produced quantity and scrap quantity
- automatic downtime detection
- operator downtime qualification
- scrap declaration
- daily OEE/TRS
- workshop dashboard in real time
Architecture
Passive machine signal / external sensor
-> isolated relay / optocoupler / industrial DI module
-> IoT gateway or edge service
-> MQTT topic
-> FastAPI backend
-> PostgreSQL
-> WebSocket / REST API
-> React frontend dashboard
Services:
database: PostgreSQL with schema and seed datamqtt: Mosquitto brokerbackend: FastAPI API, MQTT consumer, downtime logic, OEE computation, WebSocket notificationsedge-simulator: publishes simulated machine events on MQTTfrontend: React + TypeScript operator/dashboard interface
Repository Layout
/backend
/frontend
/edge-simulator
/database/init.sql
/database/seed.sql
/mqtt/mosquitto.conf
/docker-compose.yml
Run Locally
Prerequisites:
- Docker Desktop or Docker Engine with Compose
Start the full MVP:
docker compose up --build
Exposed services:
- frontend:
http://localhost:5173 - backend API:
http://localhost:8000 - backend health:
http://localhost:8000/health - PostgreSQL:
localhost:5432 - MQTT:
localhost:1883
Demo Data
Seeded machines:
INJ-01- Haitian Mars IIINJ-02- Engel e-victoryINJ-03- Arburg Allrounder
Seeded production orders:
OF-1001running onINJ-01OF-1002planned onINJ-02
The edge simulator continuously publishes cycles and stop events for the demo machines.
MQTT Topics And Payloads
Topic pattern:
plasttrack/machines/{machine_code}/events
Raw signal event example:
{
"machine_id": "INJ-01",
"event_type": "digital_input_changed",
"timestamp": "2026-06-14T10:32:15Z",
"input_name": "cycle_signal",
"input_value": true,
"cycle_time_sec": 18.7,
"source": "digital_input"
}
The backend derives valid cycles from the configured signal edge and timing rules. It still stores normalized cycle_completed events if upstream integrations publish them directly.
Automatic stop event example:
{
"machine_id": "INJ-01",
"event_type": "machine_stopped",
"timestamp": "2026-06-14T10:35:20Z",
"auto_detected": true
}
REST API
Machines:
GET /api/dashboardGET /api/machinesGET /api/machines/{machine_id}GET /api/machines/{machine_id}/configPATCH /api/machines/{machine_id}/configGET /api/machines/{machine_id}/statusGET /api/machines/{machine_id}/eventsGET /api/machines/{machine_id}/cyclesGET /api/machines/{machine_id}/downtimes
Production orders:
GET /api/production-ordersPOST /api/production-ordersPOST /api/production-orders/{id}/startPOST /api/production-orders/{id}/pausePOST /api/production-orders/{id}/close
Downtimes:
GET /api/downtimes/openPOST /api/downtimes/{id}/qualify
Scrap:
GET /api/scrapsPOST /api/scraps
OEE:
GET /api/oee/daily?date=2026-06-14GET /api/oee/machines/{machine_id}?from=2026-06-14T00:00:00Z&to=2026-06-14T23:59:59Z
Reference data:
GET /api/reference-data
WebSocket:
ws://localhost:8000/ws/dashboard
Manual API Checks
List machines:
curl http://localhost:8000/api/machines
Create a production order:
curl -X POST http://localhost:8000/api/production-orders \
-H "Content-Type: application/json" \
-d '{
"order_number": "OF-2001",
"machine_id": 2,
"article_ref": "ART-2001",
"article_name": "Bac Technique",
"mold_ref": "M-2001",
"material_ref": "PP-BLACK",
"planned_qty": 1200,
"cavities": 2,
"theoretical_cycle_time_sec": 21.5
}'
Qualify a downtime:
curl -X POST http://localhost:8000/api/downtimes/1/qualify \
-H "Content-Type: application/json" \
-d '{
"reason_code": "attente_matiere",
"comment": "Material not available at the machine",
"qualified_by": "operateur_1"
}'
Declare scrap:
curl -X POST http://localhost:8000/api/scraps \
-H "Content-Type: application/json" \
-d '{
"machine_id": "INJ-01",
"production_order_id": 1,
"quantity": 12,
"reason_code": "bavure",
"comment": "Flash on parting line",
"operator_name": "operateur_1"
}'
OEE Logic
Formula:
OEE = Availability x Performance x Quality
Implemented rules:
- planned time: overlap of started production orders with the query window
- downtime: overlap of machine downtimes with the query window
- operating time:
planned time - downtime - performance:
theoretical cycle contribution / operating time, capped at100% - quality:
good quantity / total produced quantity
How The Simulator Works
The simulator publishes:
digital_input_changedformachine_power_ondigital_input_changedforcycle_signaldigital_input_changedforgeneral_alarm
Each machine gets a nominal cycle time, a pulse width, and a random stop probability. The backend consumes these MQTT events, detects the configured cycle edge, and updates production data in PostgreSQL.
Connecting A Real Passive Gateway Later
To replace the simulator with a real gateway:
- Read passive, electrically isolated machine signals only.
- Publish normalized JSON events to the same MQTT topic pattern.
- Keep event timestamps in UTC ISO-8601 format.
- Map per-machine debounce, min/max cycle time, and stop detection delay in the database or future admin UI.
Expected minimum signals:
machine_power_oncycle_signal
Optional signals:
auto_modemold_openejector_forwardgeneral_alarmpump_running
Industrial Safety
- Never wire the IoT gateway directly to PLC outputs or machine circuits.
- Use interface relays, optocouplers, or isolated industrial input modules.
- Validate every wiring decision with a qualified automation or industrial electrical technician.
- Keep acquisition passive and non-intrusive.
- Do not modify the machine PLC logic for this MVP.
- This MVP must never command or pilot the press.
Tests
Backend tests included:
- OEE calculation unit test
- MQTT event parsing test
Run locally:
cd backend
pytest
Frontend build check:
cd frontend
npm install
npm run build
Current Limits
- No authentication or role management
- No production scheduling calendar
- No historian-grade buffering on the edge side
- No historical reporting endpoints beyond daily OEE/TRS
- No real passive gateway adapter package yet; the simulator publishes normalized MQTT payloads
- WebSocket currently pushes refresh notifications, then the UI refetches data
- The simulator models passive-signal-derived events, not actual electrical IO reads
Current UI Modules
The frontend is organized into module tabs rather than one long dashboard page:
Atelier: machine cards, status filtering, sorting, quick actions, fullscreen workshop modeMachine: selected machine detail, cycle trend, recent events, downtime historyTRS: daily OEE/TRS summary and per-machine OEEOF: production order creation and status managementArrets: open downtime qualificationRebuts: scrap declaration and scrap logConfig: persistent passive signal and timing configuration per machine
Operator forms include inline validation, inline API error feedback, and success toasts.
Industrial Readiness Backlog
Next hardening steps before a real shop-floor pilot:
- define a real passive gateway adapter spec with exact input mapping and MQTT payload examples
- add edge-side buffering for MQTT/backend outage periods
- expand tests for downtime transitions, order status transitions, and signal debounce edge cases
- add historical reporting endpoints for OEE, downtime, and scrap trends