Initial commit, 90% there
This commit is contained in:
@@ -0,0 +1,390 @@
|
||||
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];
|
||||
Reference in New Issue
Block a user