12 KiB
Multi-Phase Implementation Summary
Plastico Work Order Persistence System
Date: 2025-11-29 Phases Completed: 2, 3, 4, 5, 6 (Backend complete, UI dialogs pending)
Overview
Implemented a comprehensive work order persistence system that ensures production data survives Node-RED restarts, allows resuming work orders, and makes the database the source of truth for production counts.
Phase 2: Cycle Persistence (COMPLETE)
Goal
Write good_parts and cycle_count to database every 5 seconds (throttled)
Changes Made
Modified Files:
flows.json- Machine Cycles function node
Implementation:
- Added 5-second throttling to 4th output of Machine Cycles function
- Uses global variable
lastWorkOrderDbWriteto track last DB write timestamp - Only sends DB update when 5+ seconds have elapsed since last write
- SQL UPDATE includes:
cycle_count,good_parts,progress_percent,updated_at
4th Output SQL:
UPDATE work_orders
SET cycle_count = ?,
good_parts = ?,
progress_percent = ?,
updated_at = NOW()
WHERE work_order_id = ?
Flow: Machine Cycles → DB Guard (Cycles) → mariaDB
Maximum Data Loss: 5 seconds of cycles (typically 1-2 cycles)
Risk Level: LOW
Phase 3: Resume/Restart Prompt (COMPLETE - Backend)
Goal
Prevent accidental progress loss when clicking Load by prompting user to Resume or Restart
Changes Made
Modified Files:
flows.json- Work Order buttons functionflows.json- Refresh Trigger functionflows.json- Back to UI function
New Actions in Work Order Buttons:
-
start-work-order (modified):
- Now queries database for existing progress first
- Sets
_mode = "check-progress" - Stores pending order in flow context
- SQL:
SELECT cycle_count, good_parts, progress_percent FROM work_orders WHERE work_order_id = ?
-
resume-work-order (new):
- Loads work order with existing cycle_count and good_parts from database
- Initializes
global.cycleCountwith existing value - Sets status to RUNNING
- User must still click START to begin tracking
-
restart-work-order (new):
- Resets database values to 0
- Resets
global.cycleCountto 0 - Sets status to RUNNING
- User must click START to begin tracking
Back to UI - New Modes:
-
check-progress:
- Receives DB query results
- If progress exists (cycles > 0 OR good_parts > 0), sends
resumePromptto UI - If no progress, auto-starts with zero values
- Message format:
{ topic: "resumePrompt", payload: { id, sku, target, cycleCount, goodParts, progressPercent } } -
resume:
- Sends activeWorkOrder to UI with database values
- Maintains current progress
-
restart:
- Sends activeWorkOrder to UI with zeroed values
Refresh Trigger - New Routes:
- Handles
check-progress,resume, andrestartmodes - Routes messages to Back to UI for processing
UI Changes Needed (NOT YET IMPLEMENTED):
- Home or Work Orders template needs resume/restart dialog
- Listen for
msg.topic === "resumePrompt" - Display: "WO-{id} has {goodParts}/{target} parts. Resume or Restart?"
- Two buttons:
- Resume (green): sends
{action: "resume-work-order", payload: order} - Restart (orange): sends
{action: "restart-work-order", payload: order}with confirmation
- Resume (green): sends
Risk Level: MEDIUM (changes core Load button behavior)
Phase 4: Complete Button Persistence (COMPLETE)
Goal
Ensure final production numbers are written to work_orders before marking DONE
Changes Made
Modified Files:
flows.json- Work Order buttons function (complete-work-order case)
Implementation:
- Before marking status = 'DONE', retrieve current global state values
- Calculate final good_parts using same formula as Machine Cycles:
finalCycles = global.cycleCounttotalProduced = finalCycles × cavitiesfinalGoodParts = max(0, totalProduced - scrapTotal)
Updated SQL:
UPDATE work_orders
SET status = 'DONE',
cycle_count = ?,
good_parts = ?,
progress_percent = 100,
updated_at = NOW()
WHERE work_order_id = ?
Parameters: [finalCycles, finalGoodParts, order.id]
Risk Level: LOW (just ensures final sync before completion)
Phase 5: Session Restore Status (COMPLETE)
Goal
When restoring session, work order stays RUNNING but user must click START
Changes Made
Modified Files:
flows.json- Back to UI function (restore-query mode)
Implementation: Already correctly implemented:
- Query finds work orders WHERE status = 'RUNNING'
- Restores
global.activeWorkOrderandglobal.cycleCountfrom database - Sets
trackingEnabled = falseandproductionStarted = false - User MUST click START button to begin tracking
- Work order remains RUNNING so user doesn't have to Load again
Added:
- Enhanced logging to clarify behavior
- Comment documentation of Phase 5 requirements
Risk Level: LOW (no logic changes, clarification only)
Phase 6: Database as Source of Truth (COMPLETE)
Goal
Make work_orders table the source of truth for UI display
Changes Made
Already Implemented in Phases 2-5:
-
Load/Resume Logic:
- start-work-order queries database for current values
- resume-work-order initializes global state from database
- restart-work-order resets database AND global state to 0
-
Persistence:
- Machine Cycles writes to database every 5 seconds
- Complete button writes final counts to database
- All cycle_count and good_parts stored in work_orders table
-
UI Updates:
- Back to UI modes (resume, restart, restore-query) use database values
- activeWorkOrder payload includes good_parts from database
- Work order list refresh queries database for latest values
Data Flow:
- User loads work order → Query DB for progress
- If progress exists → Prompt Resume/Restart
- User chooses → Global state initialized from DB
- Production runs → Every 5s write to DB
- Node-RED restarts → Restore from DB
- User clicks START → Continue from last persisted count
Risk Level: MEDIUM (changes data flow, requires testing)
Phase 7: Tab Switch Refresh (OPTIONAL - NOT IMPLEMENTED)
This phase is optional UI enhancement and was not implemented. Current behavior:
- Global state maintains active work order when switching tabs
- If needed later, can add tab activation listener in Home template
Files Modified Summary
| File | Modified Nodes | Phases | Backups Created |
|---|---|---|---|
| flows.json | Machine Cycles | 2 | flows.json.backup_phase2_* |
| flows.json | Work Order buttons | 2, 3, 4 | flows.json.backup_phase3_, backup_phase4_ |
| flows.json | Refresh Trigger | 3 | flows.json.backup_phase3_* |
| flows.json | Back to UI | 3, 5 | flows.json.backup_phase3_* |
Testing Checklist (Phase 8)
1. New Work Order Start
- Load WO with 0 progress → should start normally (no prompt)
- Verify cycle_count and good_parts are 0 in database
2. Resume Existing Work Order
- Stop production at 50/200 parts
- Restart Node-RED
- Load same WO → prompt should show "50/200 parts"
- Click Resume → continues from 50
- Machine cycles → good_parts increments correctly
- Check database shows incremental updates every 5 seconds
3. Restart Existing Work Order
- Load WO with 100/500 parts → prompt shows
- Click Restart → (UI needs confirmation dialog)
- After restart → cycle_count and good_parts are 0 in DB
- Verify global.cycleCount is 0
- Start production → counts from 0
4. Tab Switch
- Start production at WO with 25 parts
- Switch to Graphs tab
- Switch back to Home
- Progress still shows 25+ parts correctly
5. Node-RED Restart with Restore
- Production running at 75/300 parts
- Kill Node-RED (simulate crash)
- Restart Node-RED
- Verify session restore finds RUNNING work order
- Work order shows 75± parts (within 5-second window)
- Click START → production continues from ~75
6. Complete Work Order
- Finish work order at 250/250 parts
- Click Done
- Query database:
SELECT cycle_count, good_parts, status FROM work_orders WHERE work_order_id = ? - Verify cycle_count and good_parts are persisted
- Verify status = 'DONE'
7. Power Failure Simulation
- Production at 150 parts
- Kill Node-RED process immediately (simulate power loss)
- Restart
- Check DB for last persisted value
- Maximum 5 seconds / 1-2 cycles of data loss
8. Database Persistence Verification
-- Check throttling works (updates every 5 seconds)
SELECT work_order_id, cycle_count, good_parts, updated_at
FROM work_orders
WHERE status = 'RUNNING';
-- Verify completed work orders have final counts
SELECT work_order_id, cycle_count, good_parts, status
FROM work_orders
WHERE status = 'DONE';
Known Limitations
-
UI Dialogs Not Implemented:
- Resume/Restart prompt dialog needs to be added to Home or Work Orders template
- Backend sends
resumePromptmessage but UI doesn't handle it yet - Workaround: System auto-starts with zero values if no UI dialog present
-
5-Second Data Loss Window:
- Acceptable trade-off for reduced database load
- Typical cycle times are 3-15 seconds
- Loss of 1-2 cycles on crash is acceptable for manufacturing use case
-
Scrap Counting:
- Scrap is stored separately and added to activeWorkOrder object
- Not persisted in throttled updates (only on complete)
- Could be added to Phase 2 persistence if needed
Rollback Instructions
If issues arise, restore from backups:
cd /home/mdares/projects/Plastico
# Rollback to before Phase 2
cp flows.json.backup flows.json
# Or rollback to specific phase
cp flows.json.backup_phase2_YYYYMMDD_HHMMSS flows.json
Backups created:
- flows.json.backup (original)
- flows.json.backup_phase2_*
- flows.json.backup_phase3_*
- flows.json.backup_phase4_*
Success Criteria
✅ Work orders persist progress across Node-RED restarts (Phase 2) ✅ Resume/Restart backend logic implemented (Phase 3) ⏳ Resume/Restart UI dialog (Phase 3 - needs UI template work) ✅ work_orders table reflects current production state (Phase 2, 4) ✅ Complete button persists final counts (Phase 4) ✅ Session restore maintains RUNNING status (Phase 5) ✅ Database is source of truth (Phase 6) ✅ Maximum 5 seconds of data loss on crash (Phase 2) ⏳ Tab switches don't lose data (Phase 7 - not implemented, optional)
Next Steps
-
Implement UI Dialog (Priority: HIGH):
- Add resume/restart dialog to Home or Work Orders template
- Listen for
msg.topic === "resumePrompt" - Create two action buttons that send resume/restart actions
-
Testing (Priority: HIGH):
- Run through all test cases in Phase 8 checklist
- Verify database persistence works correctly
- Test crash recovery scenarios
-
Optional Enhancements:
- Phase 7: Tab switch state refresh
- Add scrap to throttled persistence
- Visual indicator showing "last saved" timestamp in UI
Technical Notes
Global Variables Used
activeWorkOrder- Current work order objectcycleCount- Current cycle countlastWorkOrderDbWrite- Timestamp of last DB persistence (Phase 2)trackingEnabled- Whether production tracking is activeproductionStarted- Whether production has startedmoldActive- Number of cavities in current mold
Flow Context Variables
pendingWorkOrder- Temporarily stores work order during progress check (Phase 3)lastMachineState- Previous machine state for edge detection
Database Schema Requirements
Table: work_orders
work_order_id(PRIMARY KEY)cycle_countINT DEFAULT 0good_partsINT DEFAULT 0progress_percentINT DEFAULT 0statusVARCHAR (PENDING, RUNNING, DONE)updated_atTIMESTAMPscrap_partsINTtarget_qtyINT- Additional fields: sku, cycle_time, etc.
Support
For issues or questions:
- Check Node-RED debug output for
[MACHINE CYCLE],[RESUME],[RESTART],[COMPLETE]messages - Verify database schema has all required columns
- Check backups if rollback is needed
- Review this document for expected behavior
Implementation completed by: Claude Code Based on: Recommendation.txt multi-phase plan