# 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 ```text 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 data - `mqtt`: Mosquitto broker - `backend`: FastAPI API, MQTT consumer, downtime logic, OEE computation, WebSocket notifications - `edge-simulator`: publishes simulated machine events on MQTT - `frontend`: React + TypeScript operator/dashboard interface ## Repository Layout ```text /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: ```bash 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 II - `INJ-02` - Engel e-victory - `INJ-03` - Arburg Allrounder Seeded production orders: - `OF-1001` running on `INJ-01` - `OF-1002` planned on `INJ-02` The edge simulator continuously publishes cycles and stop events for the demo machines. ## MQTT Topics And Payloads Topic pattern: ```text plasttrack/machines/{machine_code}/events ``` Raw signal event example: ```json { "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: ```json { "machine_id": "INJ-01", "event_type": "machine_stopped", "timestamp": "2026-06-14T10:35:20Z", "auto_detected": true } ``` ## REST API Machines: - `GET /api/dashboard` - `GET /api/machines` - `GET /api/machines/{machine_id}` - `GET /api/machines/{machine_id}/config` - `PATCH /api/machines/{machine_id}/config` - `GET /api/machines/{machine_id}/status` - `GET /api/machines/{machine_id}/events` - `GET /api/machines/{machine_id}/cycles` - `GET /api/machines/{machine_id}/downtimes` Production orders: - `GET /api/production-orders` - `POST /api/production-orders` - `POST /api/production-orders/{id}/start` - `POST /api/production-orders/{id}/pause` - `POST /api/production-orders/{id}/close` Downtimes: - `GET /api/downtimes/open` - `POST /api/downtimes/{id}/qualify` Scrap: - `GET /api/scraps` - `POST /api/scraps` OEE: - `GET /api/oee/daily?date=2026-06-14` - `GET /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: ```bash curl http://localhost:8000/api/machines ``` Create a production order: ```bash 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: ```bash 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: ```bash 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: ```text 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 at `100%` - quality: `good quantity / total produced quantity` ## How The Simulator Works The simulator publishes: - `digital_input_changed` for `machine_power_on` - `digital_input_changed` for `cycle_signal` - `digital_input_changed` for `general_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: 1. Read passive, electrically isolated machine signals only. 2. Publish normalized JSON events to the same MQTT topic pattern. 3. Keep event timestamps in UTC ISO-8601 format. 4. Map per-machine debounce, min/max cycle time, and stop detection delay in the database or future admin UI. Expected minimum signals: - `machine_power_on` - `cycle_signal` Optional signals: - `auto_mode` - `mold_open` - `ejector_forward` - `general_alarm` - `pump_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: ```bash cd backend pytest ``` Frontend build check: ```bash 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 mode - `Machine`: selected machine detail, cycle trend, recent events, downtime history - `TRS`: daily OEE/TRS summary and per-machine OEE - `OF`: production order creation and status management - `Arrets`: open downtime qualification - `Rebuts`: scrap declaration and scrap log - `Config`: 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