397 lines
11 KiB
Markdown
397 lines
11 KiB
Markdown
# Quick Reference: Multi-Phase Implementation Changes
|
|
|
|
## Modified Function Nodes in flows.json
|
|
|
|
### 1. Machine Cycles (ID: 0d023d87a13bf56f) - Phase 2
|
|
**Added:**
|
|
- 5-second throttling for DB persistence
|
|
- Global variable `lastWorkOrderDbWrite` to track last write time
|
|
- Enhanced 4th output to include `progress_percent`
|
|
|
|
**Key Code Addition:**
|
|
```javascript
|
|
const lastDbWrite = global.get("lastWorkOrderDbWrite") || 0;
|
|
const timeSinceLastWrite = now - lastDbWrite;
|
|
let persistCycleCount = null;
|
|
|
|
if (timeSinceLastWrite >= 5000) {
|
|
persistCycleCount = {
|
|
topic: "UPDATE work_orders SET cycle_count = ?, good_parts = ?, progress_percent = ?, updated_at = NOW() WHERE work_order_id = ?",
|
|
payload: [cycles, produced, progress, activeOrder.id]
|
|
};
|
|
global.set("lastWorkOrderDbWrite", now);
|
|
}
|
|
```
|
|
|
|
### 2. Work Order buttons (ID: 9bbd4fade968036d) - Phases 3 & 4
|
|
|
|
**Modified Actions:**
|
|
- `start-work-order`: Now queries DB for existing progress
|
|
- `complete-work-order`: Persists final counts before marking DONE
|
|
|
|
**New Actions:**
|
|
- `resume-work-order`: Loads work order with existing database values
|
|
- `restart-work-order`: Resets work order to zero in database and global state
|
|
|
|
**Key Changes:**
|
|
|
|
**start-work-order:**
|
|
```javascript
|
|
case "start-work-order": {
|
|
msg._mode = "check-progress";
|
|
const order = msg.payload || {};
|
|
flow.set("pendingWorkOrder", order);
|
|
|
|
msg.topic = "SELECT cycle_count, good_parts, progress_percent FROM work_orders WHERE work_order_id = ?";
|
|
msg.payload = [order.id];
|
|
|
|
return [null, null, msg, null];
|
|
}
|
|
```
|
|
|
|
**resume-work-order:**
|
|
```javascript
|
|
case "resume-work-order": {
|
|
msg._mode = "resume";
|
|
const order = msg.payload || {};
|
|
const existingCycles = Number(order.cycle_count) || 0;
|
|
|
|
global.set("activeWorkOrder", order);
|
|
global.set("cycleCount", existingCycles); // Load from DB
|
|
|
|
msg.topic = "UPDATE work_orders SET status = ... 'RUNNING' ...";
|
|
return [null, null, msg, null];
|
|
}
|
|
```
|
|
|
|
**complete-work-order (Phase 4):**
|
|
```javascript
|
|
case "complete-work-order": {
|
|
const finalCycles = Number(global.get("cycleCount")) || 0;
|
|
const finalGoodParts = Math.max(0, totalProduced - scrapTotal);
|
|
|
|
msg.topic = "UPDATE work_orders SET status = 'DONE', cycle_count = ?, good_parts = ?, progress_percent = 100, updated_at = NOW() WHERE work_order_id = ?";
|
|
msg.payload = [finalCycles, finalGoodParts, order.id];
|
|
|
|
return [null, null, null, msg];
|
|
}
|
|
```
|
|
|
|
### 3. Refresh Trigger (ID: 578c92e75bf0f266) - Phase 3
|
|
|
|
**Added Modes:**
|
|
- `check-progress`: Routes to Back to UI for progress check processing
|
|
- `resume`: Routes to Back to UI and triggers work order list refresh
|
|
- `restart`: Routes to Back to UI and triggers work order list refresh
|
|
|
|
**Key Addition:**
|
|
```javascript
|
|
if (msg._mode === "resume" || msg._mode === "restart") {
|
|
const originalMsg = {...msg};
|
|
msg._mode = "select";
|
|
msg.topic = "SELECT * FROM work_orders ORDER BY updated_at DESC;";
|
|
return [msg, originalMsg];
|
|
}
|
|
if (msg._mode === "check-progress") {
|
|
return [null, msg];
|
|
}
|
|
```
|
|
|
|
### 4. Back to UI (ID: f2bab26e27e2023d) - Phases 3 & 5
|
|
|
|
**New Modes:**
|
|
- `check-progress`: Sends resumePrompt to UI if progress exists
|
|
- `resume`: Sends activeWorkOrder with database values
|
|
- `restart`: Sends activeWorkOrder with zeroed values
|
|
|
|
**Modified Modes:**
|
|
- `restore-query`: Enhanced documentation (Phase 5)
|
|
|
|
**Key Additions:**
|
|
|
|
**check-progress:**
|
|
```javascript
|
|
if (mode === "check-progress") {
|
|
const rows = Array.isArray(msg.payload) ? msg.payload : [];
|
|
|
|
if (rows.length > 0) {
|
|
const row = rows[0];
|
|
const cycleCount = Number(row.cycle_count) || 0;
|
|
const goodParts = Number(row.good_parts) || 0;
|
|
|
|
if (cycleCount > 0 || goodParts > 0) {
|
|
msg.topic = "resumePrompt";
|
|
msg.payload = {
|
|
id: pendingOrder.id,
|
|
sku: pendingOrder.sku,
|
|
cycleCount: cycleCount,
|
|
goodParts: goodParts,
|
|
progressPercent: Number(row.progress_percent) || 0
|
|
};
|
|
return [null, msg, null, null];
|
|
}
|
|
}
|
|
|
|
// Auto-start if no progress
|
|
global.set("cycleCount", 0);
|
|
return [null, null, null, null];
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Message Flow Diagram
|
|
|
|
### Load Work Order (Phase 3)
|
|
|
|
```
|
|
User clicks Load
|
|
↓
|
|
Work Order buttons (start-work-order)
|
|
↓ (output 3, _mode: "check-progress")
|
|
mariaDB (SELECT cycle_count, good_parts...)
|
|
↓
|
|
Refresh Trigger (routes check-progress)
|
|
↓ (output 2)
|
|
Back to UI (check-progress)
|
|
↓
|
|
IF progress exists:
|
|
→ Send resumePrompt to UI
|
|
→ UI shows Resume/Restart dialog
|
|
ELSE:
|
|
→ Auto-start with zero values
|
|
```
|
|
|
|
### Resume Work Order (Phase 3)
|
|
|
|
```
|
|
User clicks Resume
|
|
↓
|
|
Work Order buttons (resume-work-order)
|
|
↓ (output 3, _mode: "resume")
|
|
mariaDB (UPDATE status = 'RUNNING'...)
|
|
↓
|
|
Refresh Trigger (routes resume + refreshes WO list)
|
|
↓ (output 2)
|
|
Back to UI (resume)
|
|
↓
|
|
Send activeWorkOrder to Home template
|
|
(with existing cycle_count and good_parts)
|
|
```
|
|
|
|
### Machine Cycle (Phase 2)
|
|
|
|
```
|
|
Every machine cycle
|
|
↓
|
|
Machine Cycles function
|
|
↓
|
|
Check throttle (5 seconds elapsed?)
|
|
↓
|
|
IF yes:
|
|
↓ (output 4)
|
|
DB Guard (Cycles)
|
|
↓
|
|
mariaDB (UPDATE cycle_count, good_parts...)
|
|
ELSE:
|
|
→ Skip DB write
|
|
```
|
|
|
|
### Complete Work Order (Phase 4)
|
|
|
|
```
|
|
User clicks Done
|
|
↓
|
|
Work Order buttons (complete-work-order)
|
|
↓ Calculate final counts
|
|
↓ (output 4, _mode: "complete")
|
|
mariaDB (UPDATE status='DONE', cycle_count=?, good_parts=?...)
|
|
↓
|
|
Refresh Trigger (routes complete + refreshes WO list)
|
|
↓ (output 2)
|
|
Back to UI (complete)
|
|
↓
|
|
Clear activeWorkOrder from Home template
|
|
```
|
|
|
|
---
|
|
|
|
## Database Schema Requirements
|
|
|
|
```sql
|
|
-- Required columns in work_orders table
|
|
CREATE TABLE work_orders (
|
|
work_order_id VARCHAR(255) PRIMARY KEY,
|
|
sku VARCHAR(255),
|
|
target_qty INT,
|
|
cycle_count INT DEFAULT 0, -- Added Phase 1
|
|
good_parts INT DEFAULT 0, -- Added Phase 1
|
|
scrap_parts INT DEFAULT 0,
|
|
progress_percent INT DEFAULT 0,
|
|
status VARCHAR(50) DEFAULT 'PENDING',
|
|
cycle_time DECIMAL(10,2),
|
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
INDEX idx_work_order_id (work_order_id),
|
|
INDEX idx_status (status)
|
|
);
|
|
```
|
|
|
|
---
|
|
|
|
## Global Variables Reference
|
|
|
|
| Variable | Type | Purpose | Set By | Used By |
|
|
|----------|------|---------|--------|---------|
|
|
| activeWorkOrder | Object | Current work order | Work Order buttons | Machine Cycles, Back to UI |
|
|
| cycleCount | Number | Current cycle count | Machine Cycles, Work Order buttons | Machine Cycles, Complete |
|
|
| lastWorkOrderDbWrite | Number | Timestamp of last DB write | Machine Cycles | Machine Cycles (throttling) |
|
|
| trackingEnabled | Boolean | Production tracking active | Work Order buttons (start/stop) | Machine Cycles |
|
|
| productionStarted | Boolean | Production has started | Machine Cycles, Work Order buttons | Machine Cycles |
|
|
| moldActive | Number | Cavities in current mold | Mold settings | Machine Cycles, Complete |
|
|
| scrapPromptIssuedFor | String | Work order ID for scrap prompt | Machine Cycles | Machine Cycles |
|
|
|
|
## Flow Context Variables
|
|
|
|
| Variable | Type | Purpose | Set By | Used By |
|
|
|----------|------|---------|--------|---------|
|
|
| pendingWorkOrder | Object | Work order awaiting resume/restart decision | Work Order buttons (start) | Back to UI (check-progress) |
|
|
| lastMachineState | Number | Previous machine state (0 or 1) | Machine Cycles | Machine Cycles (edge detection) |
|
|
|
|
---
|
|
|
|
## Testing SQL Queries
|
|
|
|
### Check Current Progress
|
|
```sql
|
|
SELECT work_order_id, cycle_count, good_parts, progress_percent, status, updated_at
|
|
FROM work_orders
|
|
WHERE status = 'RUNNING';
|
|
```
|
|
|
|
### Verify Throttling (should update every ~5 seconds)
|
|
```sql
|
|
SELECT work_order_id, cycle_count, good_parts, updated_at
|
|
FROM work_orders
|
|
WHERE work_order_id = 'YOUR_WO_ID'
|
|
ORDER BY updated_at DESC
|
|
LIMIT 10;
|
|
```
|
|
|
|
### Check Completed Work Orders
|
|
```sql
|
|
SELECT work_order_id, cycle_count, good_parts, status, updated_at
|
|
FROM work_orders
|
|
WHERE status = 'DONE'
|
|
ORDER BY updated_at DESC;
|
|
```
|
|
|
|
### Manually Reset Work Order for Testing
|
|
```sql
|
|
UPDATE work_orders
|
|
SET cycle_count = 0,
|
|
good_parts = 0,
|
|
progress_percent = 0,
|
|
status = 'PENDING'
|
|
WHERE work_order_id = 'YOUR_WO_ID';
|
|
```
|
|
|
|
---
|
|
|
|
## Troubleshooting
|
|
|
|
### Work Order Not Persisting
|
|
1. Check Node-RED debug output for `[DB PERSIST]` messages
|
|
2. Verify 5 seconds have elapsed between cycles
|
|
3. Check DB Guard (Cycles) node is receiving messages
|
|
4. Verify mariaDB connection is active
|
|
|
|
### Resume Prompt Not Showing
|
|
1. UI template not yet implemented (expected - see IMPLEMENTATION_SUMMARY.md)
|
|
2. Check Back to UI debug for `[RESUME PROMPT]` message
|
|
3. Verify msg.topic === "resumePrompt" in debug output
|
|
|
|
### Cycle Count Not Restoring After Restart
|
|
1. Check `restore-query` mode in Back to UI
|
|
2. Verify work order status is 'RUNNING' in database
|
|
3. Check global.cycleCount is being set from database value
|
|
4. Look for `[RESTORE]` message in debug output
|
|
|
|
### Complete Button Not Saving Final Counts
|
|
1. Verify complete-work-order SQL includes cycle_count and good_parts
|
|
2. Check database after clicking Done
|
|
3. Look for `[COMPLETE] Persisting final counts` in debug output
|
|
|
|
---
|
|
|
|
## Rollback Commands
|
|
|
|
```bash
|
|
cd /home/mdares/projects/Plastico
|
|
|
|
# List available backups
|
|
ls -lh flows.json.backup*
|
|
|
|
# Rollback to original
|
|
cp flows.json.backup flows.json
|
|
|
|
# Rollback to specific phase
|
|
cp flows.json.backup_phase2_YYYYMMDD_HHMMSS flows.json
|
|
|
|
# Verify rollback
|
|
head -n 1 flows.json
|
|
|
|
# Restart Node-RED to apply changes
|
|
# (method depends on your setup - systemctl, pm2, docker, etc.)
|
|
```
|
|
|
|
---
|
|
|
|
## Next Implementation: UI Dialog
|
|
|
|
To complete Phase 3, add this to Home or Work Orders template:
|
|
|
|
```html
|
|
<div v-if="showResumePrompt" class="resume-dialog">
|
|
<h3>Resume Work Order?</h3>
|
|
<p>{{ resumeData.sku }} ({{ resumeData.id }})</p>
|
|
<p>Current Progress: {{ resumeData.goodParts }} / {{ resumeData.target }} parts</p>
|
|
<p>{{ resumeData.progressPercent }}% complete ({{ resumeData.cycleCount }} cycles)</p>
|
|
|
|
<button @click="resumeWorkOrder" class="btn-resume">
|
|
Resume from {{ resumeData.goodParts }} parts
|
|
</button>
|
|
<button @click="confirmRestart" class="btn-restart">
|
|
Restart from Zero
|
|
</button>
|
|
</div>
|
|
```
|
|
|
|
```javascript
|
|
// In template <script>
|
|
scope.$watch('msg', function(msg) {
|
|
if (msg.topic === "resumePrompt") {
|
|
scope.showResumePrompt = true;
|
|
scope.resumeData = msg.payload;
|
|
}
|
|
});
|
|
|
|
scope.resumeWorkOrder = function() {
|
|
scope.send({
|
|
action: "resume-work-order",
|
|
payload: scope.resumeData
|
|
});
|
|
scope.showResumePrompt = false;
|
|
};
|
|
|
|
scope.confirmRestart = function() {
|
|
if (confirm("Are you sure? This will reset all progress to zero.")) {
|
|
scope.send({
|
|
action: "restart-work-order",
|
|
payload: scope.resumeData
|
|
});
|
|
scope.showResumePrompt = false;
|
|
}
|
|
};
|
|
```
|