Initial commit, 90% there
This commit is contained in:
396
projects/Plastico/CHANGES_REFERENCE.md
Normal file
396
projects/Plastico/CHANGES_REFERENCE.md
Normal file
@@ -0,0 +1,396 @@
|
||||
# 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;
|
||||
}
|
||||
};
|
||||
```
|
||||
Reference in New Issue
Block a user