413 lines
12 KiB
Markdown
413 lines
12 KiB
Markdown
# 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:**
|
||
1. Added 5-second throttling to 4th output of Machine Cycles function
|
||
2. Uses global variable `lastWorkOrderDbWrite` to track last DB write timestamp
|
||
3. Only sends DB update when 5+ seconds have elapsed since last write
|
||
4. SQL UPDATE includes: `cycle_count`, `good_parts`, `progress_percent`, `updated_at`
|
||
|
||
**4th Output SQL:**
|
||
```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 function
|
||
- `flows.json` - Refresh Trigger function
|
||
- `flows.json` - Back to UI function
|
||
|
||
**New Actions in Work Order Buttons:**
|
||
|
||
1. **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 = ?`
|
||
|
||
2. **resume-work-order** (new):
|
||
- Loads work order with existing cycle_count and good_parts from database
|
||
- Initializes `global.cycleCount` with existing value
|
||
- Sets status to RUNNING
|
||
- User must still click START to begin tracking
|
||
|
||
3. **restart-work-order** (new):
|
||
- Resets database values to 0
|
||
- Resets `global.cycleCount` to 0
|
||
- Sets status to RUNNING
|
||
- User must click START to begin tracking
|
||
|
||
**Back to UI - New Modes:**
|
||
|
||
1. **check-progress**:
|
||
- Receives DB query results
|
||
- If progress exists (cycles > 0 OR good_parts > 0), sends `resumePrompt` to UI
|
||
- If no progress, auto-starts with zero values
|
||
- Message format:
|
||
```javascript
|
||
{
|
||
topic: "resumePrompt",
|
||
payload: {
|
||
id, sku, target,
|
||
cycleCount, goodParts, progressPercent
|
||
}
|
||
}
|
||
```
|
||
|
||
2. **resume**:
|
||
- Sends activeWorkOrder to UI with database values
|
||
- Maintains current progress
|
||
|
||
3. **restart**:
|
||
- Sends activeWorkOrder to UI with zeroed values
|
||
|
||
**Refresh Trigger - New Routes:**
|
||
- Handles `check-progress`, `resume`, and `restart` modes
|
||
- 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
|
||
|
||
**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:**
|
||
1. Before marking status = 'DONE', retrieve current global state values
|
||
2. Calculate final good_parts using same formula as Machine Cycles:
|
||
- `finalCycles = global.cycleCount`
|
||
- `totalProduced = finalCycles × cavities`
|
||
- `finalGoodParts = max(0, totalProduced - scrapTotal)`
|
||
|
||
**Updated SQL:**
|
||
```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.activeWorkOrder` and `global.cycleCount` from database
|
||
- Sets `trackingEnabled = false` and `productionStarted = 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:**
|
||
|
||
1. **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
|
||
|
||
2. **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
|
||
|
||
3. **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:**
|
||
1. User loads work order → Query DB for progress
|
||
2. If progress exists → Prompt Resume/Restart
|
||
3. User chooses → Global state initialized from DB
|
||
4. Production runs → Every 5s write to DB
|
||
5. Node-RED restarts → Restore from DB
|
||
6. 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
|
||
```sql
|
||
-- 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
|
||
|
||
1. **UI Dialogs Not Implemented:**
|
||
- Resume/Restart prompt dialog needs to be added to Home or Work Orders template
|
||
- Backend sends `resumePrompt` message but UI doesn't handle it yet
|
||
- Workaround: System auto-starts with zero values if no UI dialog present
|
||
|
||
2. **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
|
||
|
||
3. **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:
|
||
|
||
```bash
|
||
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
|
||
|
||
1. **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
|
||
|
||
2. **Testing** (Priority: HIGH):
|
||
- Run through all test cases in Phase 8 checklist
|
||
- Verify database persistence works correctly
|
||
- Test crash recovery scenarios
|
||
|
||
3. **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 object
|
||
- `cycleCount` - Current cycle count
|
||
- `lastWorkOrderDbWrite` - Timestamp of last DB persistence (Phase 2)
|
||
- `trackingEnabled` - Whether production tracking is active
|
||
- `productionStarted` - Whether production has started
|
||
- `moldActive` - 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_count` INT DEFAULT 0
|
||
- `good_parts` INT DEFAULT 0
|
||
- `progress_percent` INT DEFAULT 0
|
||
- `status` VARCHAR (PENDING, RUNNING, DONE)
|
||
- `updated_at` TIMESTAMP
|
||
- `scrap_parts` INT
|
||
- `target_qty` INT
|
||
- Additional fields: sku, cycle_time, etc.
|
||
|
||
---
|
||
|
||
## Support
|
||
|
||
For issues or questions:
|
||
1. Check Node-RED debug output for `[MACHINE CYCLE]`, `[RESUME]`, `[RESTART]`, `[COMPLETE]` messages
|
||
2. Verify database schema has all required columns
|
||
3. Check backups if rollback is needed
|
||
4. Review this document for expected behavior
|
||
|
||
**Implementation completed by**: Claude Code
|
||
**Based on**: Recommendation.txt multi-phase plan
|