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

391 lines
14 KiB
Plaintext

const mode = msg._mode || '';
const started = msg.startOrder || null;
const completed = msg.completeOrder || null;
delete msg._mode;
delete msg.startOrder;
delete msg.completeOrder;
delete msg.action;
delete msg.filename;
// ========================================================
// MODE: CHECK PROGRESS (PHASE 3)
// ========================================================
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;
// Retrieve pending work order from flow context
const pendingOrder = flow.get("pendingWorkOrder");
if (!pendingOrder) {
node.error("No pending work order found in check-progress");
return [null, null, null, null];
}
// If there's existing progress, send resume prompt to UI
if (cycleCount > 0 || goodParts > 0) {
msg.topic = "resumePrompt";
msg.payload = {
id: pendingOrder.id,
sku: pendingOrder.sku || "",
target: Number(pendingOrder.target) || 0,
cycleCount: cycleCount,
goodParts: goodParts,
progressPercent: Number(row.progress_percent) || 0
};
node.warn(`[RESUME PROMPT] WO ${pendingOrder.id} has ${goodParts}/${pendingOrder.target} parts (${cycleCount} cycles)`);
return [null, msg, null, null];
}
}
// No progress found - proceed with normal start (automatically call resume with 0 values)
const pendingOrder = flow.get("pendingWorkOrder");
if (pendingOrder) {
// Auto-start with zero values
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 = [pendingOrder.id, pendingOrder.id];
global.set("activeWorkOrder", pendingOrder);
global.set("cycleCount", 0);
flow.set("lastMachineState", 0);
global.set("scrapPromptIssuedFor", null);
node.warn(`[AUTO-START] WO ${pendingOrder.id} has no existing progress, starting fresh`);
// Clear pending order
flow.set("pendingWorkOrder", null);
}
return [null, null, null, null];
}
// ========================================================
// MODE: RESUME WORK ORDER (PHASE 3)
// ========================================================
if (mode === "resume") {
const order = global.get("activeWorkOrder") || {};
const cycleCount = Number(global.get("cycleCount")) || 0;
const kpis = global.get("currentKPIs") || {
oee: 0, availability: 0, performance: 0, quality: 0
};
const homeMsg = {
topic: "activeWorkOrder",
payload: {
id: order.id || "",
sku: order.sku || "",
target: Number(order.target) || 0,
good: Number(order.good) || 0,
scrap: Number(order.scrap) || 0,
cycleTime: Number(order.cycleTime || order.theoreticalCycleTime || 0),
progressPercent: Number(order.progressPercent) || 0,
lastUpdateIso: order.lastUpdateIso || null,
kpis: kpis
}
};
// Clear pending order
flow.set("pendingWorkOrder", null);
node.warn(`[RESUME] Loaded WO ${order.id} with ${cycleCount} cycles in UI`);
return [null, homeMsg, null, null];
}
// ========================================================
// MODE: RESTART WORK ORDER (PHASE 3)
// ========================================================
if (mode === "restart") {
const order = global.get("activeWorkOrder") || {};
const kpis = global.get("currentKPIs") || {
oee: 0, availability: 0, performance: 0, quality: 0
};
const homeMsg = {
topic: "activeWorkOrder",
payload: {
id: order.id || "",
sku: order.sku || "",
target: Number(order.target) || 0,
good: 0,
scrap: 0,
cycleTime: Number(order.cycleTime || order.theoreticalCycleTime || 0),
progressPercent: 0,
lastUpdateIso: null,
kpis: kpis
}
};
// Clear pending order
flow.set("pendingWorkOrder", null);
node.warn(`[RESTART] Reset WO ${order.id} to zero in UI`);
return [null, homeMsg, null, null];
}
// ========================================================
// MODE: UPLOAD
// ========================================================
if (mode === "upload") {
msg.topic = "uploadStatus";
msg.payload = { message: "✅ Work orders uploaded successfully." };
return [msg, null, null, null];
}
// ========================================================
// MODE: SELECT (Load Work Orders)
// ========================================================
if (mode === "select") {
const rawRows = Array.isArray(msg.payload) ? msg.payload : [];
msg.topic = "workOrdersList";
msg.payload = rawRows.map(row => ({
id: row.work_order_id ?? row.id ?? "",
sku: row.sku ?? "",
target: Number(row.target_qty ?? row.target ?? 0),
good: Number(row.good_parts ?? row.good ?? 0),
scrap: Number(row.scrap_count ?? row.scrap ?? 0),
progressPercent: Number(row.progress_percent ?? row.progress ?? 0),
status: (row.status ?? "PENDING").toUpperCase(),
lastUpdateIso: row.updated_at ?? row.last_update ?? null,
cycleTime: Number(row.cycle_time ?? row.theoretical_cycle_time ?? 0)
}));
return [msg, null, null, null];
}
// ========================================================
// MODE: START WORK ORDER
// ========================================================
if (mode === "start") {
const order = started || {};
const kpis = msg.kpis || global.get("currentKPIs") || {
oee: 0, availability: 0, performance: 0, quality: 0
};
const homeMsg = {
topic: "activeWorkOrder",
payload: {
id: order.id || "",
sku: order.sku || "",
target: Number(order.target) || 0,
good: Number(order.good) || 0,
scrap: Number(order.scrap) || 0,
cycleTime: Number(order.cycleTime || order.theoreticalCycleTime || 0),
progressPercent: Number(order.progressPercent) || 0,
lastUpdateIso: order.lastUpdateIso || null,
kpis: kpis
}
};
return [null, homeMsg, null, null];
}
// ========================================================
// MODE: COMPLETE WORK ORDER
// ========================================================
if (mode === "complete") {
const homeMsg = { topic: "activeWorkOrder", payload: null };
return [null, homeMsg, null, null];
}
// ========================================================
// MODE: CYCLE UPDATE DURING PRODUCTION
// ========================================================
if (mode === "cycle") {
const cycle = msg.cycle || {};
const workOrderMsg = {
topic: "workOrderCycle",
payload: {
id: cycle.id || "",
sku: cycle.sku || "",
target: Number(cycle.target) || 0,
good: Number(cycle.good) || 0,
scrap: Number(cycle.scrap) || 0,
progressPercent: Number(cycle.progressPercent) || 0,
lastUpdateIso: cycle.lastUpdateIso || new Date().toISOString(),
status: cycle.progressPercent >= 100 ? "DONE" : "RUNNING"
}
};
const kpis = msg.kpis || global.get("currentKPIs") || {
oee: 0, availability: 0, performance: 0, quality: 0
};
const homeMsg = {
topic: "activeWorkOrder",
payload: {
id: cycle.id || "",
sku: cycle.sku || "",
target: Number(cycle.target) || 0,
good: Number(cycle.good) || 0,
scrap: Number(cycle.scrap) || 0,
cycleTime: Number(cycle.cycleTime) || 0,
progressPercent: Number(cycle.progressPercent) || 0,
lastUpdateIso: cycle.lastUpdateIso || new Date().toISOString(),
kpis: kpis
}
};
return [workOrderMsg, homeMsg, null, null];
}
// ========================================================
// MODE: MACHINE PRODUCTION STATE
// ========================================================
if (mode === "production-state") {
const homeMsg = {
topic: "machineStatus",
payload: {
machineOnline: msg.machineOnline ?? true,
productionStarted: !!msg.productionStarted,
trackingEnabled: msg.payload?.trackingEnabled ?? msg.trackingEnabled ?? false
}
};
return [null, homeMsg, null, null];
}
// ========================================================
// MODE: CURRENT STATE (for tab switch sync)
// ========================================================
if (mode === "current-state") {
const state = msg.payload || {};
const homeMsg = {
topic: "currentState",
payload: {
activeWorkOrder: state.activeWorkOrder,
trackingEnabled: state.trackingEnabled,
productionStarted: state.productionStarted,
kpis: state.kpis
}
};
return [null, homeMsg, null, null];
}
// ========================================================
// MODE: RESTORE QUERY (startup state recovery)
// ========================================================
if (mode === "restore-query") {
const rows = Array.isArray(msg.payload) ? msg.payload : [];
if (rows.length > 0) {
const row = rows[0];
const restoredOrder = {
id: row.work_order_id || row.id || "",
sku: row.sku || "",
target: Number(row.target_qty || row.target || 0),
good: Number(row.good_parts || row.good || 0),
scrap: Number(row.scrap_parts || row.scrap || 0),
progressPercent: Number(row.progress_percent || 0),
cycleTime: Number(row.cycle_time || 0),
lastUpdateIso: row.updated_at || null
};
// Restore global state
global.set("activeWorkOrder", restoredOrder);
global.set("cycleCount", Number(row.cycle_count) || 0);
// PHASE 5: Don't auto-start tracking - user must click START
// But work order stays RUNNING so user doesn't have to Load again
global.set("trackingEnabled", false);
global.set("productionStarted", false);
node.warn('[RESTORE] Restored work order: ' + restoredOrder.id + ' with ' + global.get("cycleCount") + ' cycles. Status remains RUNNING, awaiting START click.');
const homeMsg = {
topic: "activeWorkOrder",
payload: restoredOrder
};
return [null, homeMsg, null, null];
} else {
node.warn('[RESTORE] No running work order found');
}
return [null, null, null, null];
}
// ========================================================
// MODE: SCRAP PROMPT
// ========================================================
if (mode === "scrap-prompt") {
const prompt = msg.scrapPrompt || {};
const homeMsg = { topic: "scrapPrompt", payload: prompt };
const tabMsg = { ui_control: { tab: "Home" } };
// output1: nothing
// output2: home template
// output3: tab navigation
// output4: graphs template (unused here)
return [null, homeMsg, tabMsg, null];
}
// ========================================================
// MODE: SCRAP UPDATE
// ========================================================
if (mode === "scrap-update") {
const activeOrder = global.get("activeWorkOrder") || {};
const kpis = msg.kpis || global.get("currentKPIs") || {
oee: 0, availability: 0, performance: 0, quality: 0
};
const homeMsg = {
topic: "activeWorkOrder",
payload: {
id: activeOrder.id || "",
sku: activeOrder.sku || "",
target: Number(activeOrder.target) || 0,
good: Number(activeOrder.good) || 0,
scrap: Number(activeOrder.scrap) || 0,
cycleTime: Number(activeOrder.cycleTime) || 0,
progressPercent: Number(activeOrder.progressPercent) || 0,
lastUpdateIso: activeOrder.lastUpdateIso || new Date().toISOString(),
kpis: kpis
}
};
return [null, homeMsg, null, null];
}
// ========================================================
// MODE: SCRAP COMPLETE
// ========================================================
if (mode === "scrap-complete") {
const homeMsg = { topic: "activeWorkOrder", payload: null };
return [null, homeMsg, null, null];
}
// ========================================================
// MODE: CHARTS → SEND REAL DATA TO GRAPH TEMPLATE
// ========================================================
//if (mode === "charts") {
// const realOEE = msg.realOEE || global.get("realOEE") || [];
// const realAvailability = msg.realAvailability || global.get("realAvailability") || [];
// const realPerformance = msg.realPerformance || global.get("realPerformance") || [];
// const realQuality = msg.realQuality || global.get("realQuality") || [];
// const chartsMsg = {
// topic: "chartsData",
// payload: {
// oee: realOEE,
// availability: realAvailability,
// performance: realPerformance,
// quality: realQuality
// }
// };
// Send ONLY to output #4
// return [null, null, null, chartsMsg];
//}
// ========================================================
// DEFAULT
// ========================================================
return [null, null, null, null];