Files
2025-12-02 16:27:21 +00:00

262 lines
9.9 KiB
Plaintext

switch (msg.action) {
case "upload-excel":
msg._mode = "upload";
return [msg, null, null, null];
case "refresh-work-orders":
msg._mode = "select";
msg.topic = "SELECT * FROM work_orders ORDER BY created_at DESC;";
return [null, msg, null, null];
case "start-work-order": {
// PHASE 3: Check for existing progress before loading
msg._mode = "check-progress";
const order = msg.payload || {};
if (!order.id) {
node.error("No work order id supplied for start", msg);
return [null, null, null, null];
}
// Store order temporarily in flow context for resume/restart handlers
flow.set("pendingWorkOrder", order);
// Query database for existing progress
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];
}
case "resume-work-order": {
// PHASE 3: Resume existing work order from database state
msg._mode = "resume";
const order = msg.payload || {};
const existingCycles = Number(order.cycle_count) || 0;
const existingGoodParts = Number(order.good_parts) || 0;
if (!order.id) {
node.error("No work order id supplied for resume", msg);
return [null, null, null, null];
}
// Set status to RUNNING
msg.topic = "UPDATE work_orders SET status = CASE WHEN work_order_id = ? THEN 'RUNNING' ELSE 'PENDING' END, updated_at = CASE WHEN work_order_id = ? THEN NOW() ELSE updated_at END WHERE status <> 'DONE'";
msg.payload = [order.id, order.id];
// Initialize global state with existing values from database
global.set("activeWorkOrder", order);
global.set("cycleCount", existingCycles);
flow.set("lastMachineState", 0);
global.set("scrapPromptIssuedFor", null);
node.warn(`[RESUME] Loaded WO ${order.id} with ${existingCycles} cycles, ${existingGoodParts} good parts`);
return [null, null, msg, null];
}
case "restart-work-order": {
// PHASE 3: Restart work order from zero (reset database)
msg._mode = "restart";
const order = msg.payload || {};
if (!order.id) {
node.error("No work order id supplied for restart", msg);
return [null, null, null, null];
}
// Reset database values to 0 and set status to RUNNING
msg.topic = "UPDATE work_orders SET status = CASE WHEN work_order_id = ? THEN 'RUNNING' ELSE 'PENDING' END, cycle_count = 0, good_parts = 0, progress_percent = 0, updated_at = CASE WHEN work_order_id = ? THEN NOW() ELSE updated_at END WHERE status <> 'DONE'";
msg.payload = [order.id, order.id];
// Initialize global state to zero
global.set("activeWorkOrder", order);
global.set("cycleCount", 0);
flow.set("lastMachineState", 0);
global.set("scrapPromptIssuedFor", null);
node.warn(`[RESTART] Reset WO ${order.id} to zero`);
return [null, null, msg, null];
}
case "complete-work-order": {
// PHASE 4: Persist final counts before marking DONE
msg._mode = "complete";
const order = msg.payload || {};
if (!order.id) {
node.error("No work order id supplied for complete", msg);
return [null, null, null, null];
}
// Get current state values to persist
const activeOrder = global.get("activeWorkOrder") || {};
const finalCycles = Number(global.get("cycleCount")) || 0;
const cavities = Number(global.get("moldActive")) || 1;
const scrapTotal = Number(activeOrder.scrap) || 0;
const totalProduced = finalCycles * cavities;
const finalGoodParts = Math.max(0, totalProduced - scrapTotal);
msg.completeOrder = order;
// PHASE 4: SQL updated to persist final counts
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];
// Clear ALL state on completion
global.set("activeWorkOrder", null);
global.set("trackingEnabled", false);
global.set("productionStarted", false);
global.set("kpiStartupMode", false);
global.set("operatingTime", 0);
global.set("lastCycleTime", null);
global.set("cycleCount", 0);
flow.set("lastMachineState", 0);
global.set("scrapPromptIssuedFor", null);
node.warn(`[COMPLETE] Persisting final counts: cycles=${finalCycles}, good_parts=${finalGoodParts}. Cleared all state flags`);
return [null, null, null, msg];
}
case "get-current-state": {
// Return current state for UI sync on tab switch
const activeOrder = global.get("activeWorkOrder") || null;
const trackingEnabled = global.get("trackingEnabled") || false;
const productionStarted = global.get("productionStarted") || false;
const kpis = global.get("currentKPIs") || { oee: 0, availability: 0, performance: 0, quality: 0 };
msg._mode = "current-state";
msg.payload = {
activeWorkOrder: activeOrder,
trackingEnabled: trackingEnabled,
productionStarted: productionStarted,
kpis: kpis
};
return [null, msg, null, null];
}
case "restore-session": {
// Query DB for any RUNNING work order on startup
msg._mode = "restore-query";
msg.topic = "SELECT * FROM work_orders WHERE status = 'RUNNING' LIMIT 1";
msg.payload = [];
node.warn('[RESTORE] Checking for running work order on startup');
return [null, msg, null, null];
}
case "scrap-entry": {
const { id, scrap } = msg.payload || {};
const scrapNum = Number(scrap) || 0;
if (!id) {
node.error("No work order id supplied for scrap entry", msg);
return [null, null, null, null];
}
const activeOrder = global.get("activeWorkOrder");
if (activeOrder && activeOrder.id === id) {
activeOrder.scrap = (Number(activeOrder.scrap) || 0) + scrapNum;
global.set("activeWorkOrder", activeOrder);
}
global.set("scrapPromptIssuedFor", null);
msg._mode = "scrap-update";
msg.scrapEntry = { id, scrap: scrapNum };
// SQL with bound parameters for safety
msg.topic = "UPDATE work_orders SET scrap_parts = scrap_parts + ?, updated_at = NOW() WHERE work_order_id = ?";
msg.payload = [scrapNum, id];
return [null, null, msg, null];
}
case "scrap-skip": {
const { id, remindAgain } = msg.payload || {};
if (!id) {
node.error("No work order id supplied for scrap skip", msg);
return [null, null, null, null];
}
if (remindAgain) {
global.set("scrapPromptIssuedFor", null);
}
msg._mode = "scrap-skipped";
return [null, null, null, null];
}
case "start": {
// START with KPI timestamp init - FIXED
const now = Date.now();
global.set("trackingEnabled", true);
global.set("productionStarted", true);
global.set("kpiStartupMode", true);
global.set("kpiBuffer", []);
global.set("lastKPIRecordTime", now - 60000);
global.set("productionStartTime", now);
global.set("lastMachineCycleTime", now);
global.set("lastCycleTime", now);
global.set("operatingTime", 0);
node.warn('[START] Initialized: trackingEnabled=true, productionStarted=true, kpiStartupMode=true, operatingTime=0');
const activeOrder = global.get("activeWorkOrder") || {};
msg._mode = "production-state";
msg.payload = msg.payload || {};
msg.trackingEnabled = true;
msg.productionStarted = true;
msg.machineOnline = true;
msg.payload.trackingEnabled = true;
msg.payload.productionStarted = true;
msg.payload.machineOnline = true;
return [null, msg, null, null];
}
case "stop": {
global.set("trackingEnabled", false);
global.set("productionStarted", false);
node.warn('[STOP] Set trackingEnabled=false, productionStarted=false');
// Send UI update so button state reflects change
msg._mode = "production-state";
msg.payload = msg.payload || {};
msg.trackingEnabled = false;
msg.productionStarted = false;
msg.machineOnline = true;
msg.payload.trackingEnabled = false;
msg.payload.productionStarted = false;
msg.payload.machineOnline = true;
return [null, msg, null, null];
}
case "start-tracking": {
const activeOrder = global.get('activeOrder') || {};
if (!activeOrder.id) {
node.warn('[START] Cannot start tracking: No active order loaded.');
return [null, { topic: "alert", payload: "Error: No active work order loaded." }, null, null];
}
const now = Date.now();
global.set("trackingEnabled", true);
global.set("kpiBuffer", []);
global.set("lastKPIRecordTime", now - 60000);
global.set("lastMachineCycleTime", now);
global.set("lastCycleTime", now);
global.set("operatingTime", 0.001);
node.warn('[START] Cleared kpiBuffer for fresh production run');
// FIX: Use work_order_id consistently
const dbMsg = {
topic: `UPDATE work_orders SET production_start_time = ${now}, is_tracking = 1 WHERE work_order_id = '${activeOrder.id}'`,
payload: []
};
const stateMsg = {
topic: "machineStatus",
payload: msg.payload || {}
};
stateMsg.payload.trackingEnabled = true;
stateMsg.payload.productionStarted = true;
stateMsg.payload.machineOnline = true;
return [dbMsg, stateMsg, null, null];
}
}