Working KPIs 90% + Scrap functional
This commit is contained in:
@@ -869,7 +869,7 @@
|
||||
"z": "cac3a4383120cb57",
|
||||
"g": "b7ab5e0cc02b9508",
|
||||
"name": "Work Order buttons",
|
||||
"func": "switch (msg.action) {\n case \"upload-excel\":\n msg._mode = \"upload\";\n return [msg, null, null, null];\n case \"refresh-work-orders\":\n msg._mode = \"select\";\n msg.topic = \"SELECT * FROM work_orders ORDER BY created_at DESC;\";\n return [null, msg, null, null];\n // start/complete unchanged...\n case \"start-work-order\": {\n msg._mode = \"start\";\n const order = msg.payload || {};\n if (!order.id) {\n node.error(\"No work order id supplied for start\", msg);\n return [null, null, null, null];\n }\n msg.startOrder = order;\n\n msg.topic = `\n UPDATE work_orders\n SET\n status = CASE\n WHEN work_order_id = '${order.id}' THEN 'RUNNING'\n ELSE 'PENDING'\n END,\n updated_at = CASE\n WHEN work_order_id = '${order.id}' THEN NOW()\n ELSE updated_at\n END\n WHERE status <> 'DONE';\n `;\n\n global.set(\"activeWorkOrder\", order);\n global.set(\"cycleCount\", 0);\n flow.set(\"lastMachineState\", 0);\n global.set(\"scrapPromptIssuedFor\", null);\n return [null, null, msg, null];\n }\n case \"complete-work-order\": {\n msg._mode = \"complete\";\n const order = msg.payload || {};\n if (!order.id) {\n node.error(\"No work order id supplied for complete\", msg);\n return [null, null, null, null];\n }\n msg.completeOrder = order;\n msg.topic = `\n UPDATE work_orders\n SET status = 'DONE', updated_at = NOW()\n WHERE work_order_id = '${order.id}';\n `;\n global.set(\"activeWorkOrder\", null);\n global.set(\"cycleCount\", 0);\n flow.set(\"lastMachineState\", 0);\n global.set(\"scrapPromptIssuedFor\", null);\n return [null, null, null, msg];\n }\n case \"scrap-entry\": {\n const { id, scrap } = msg.payload || {};\n const scrapNum = Number(scrap) || 0;\n\n if (!id) {\n node.error(\"No work order id supplied for scrap entry\", msg);\n return [null, null, null, null];\n }\n\n // Update activeWorkOrder with accumulated scrap\n const activeOrder = global.get(\"activeWorkOrder\");\n if (activeOrder && activeOrder.id === id) {\n activeOrder.scrap = (Number(activeOrder.scrap) || 0) + scrapNum;\n global.set(\"activeWorkOrder\", activeOrder);\n }\n\n // Clear prompt flag so it can show again when target reached next time\n global.set(\"scrapPromptIssuedFor\", null);\n\n msg._mode = \"scrap-update\";\n msg.scrapEntry = { id, scrap: scrapNum };\n msg.topic = `\n UPDATE work_orders\n SET\n scrap_parts = scrap_parts + ${scrapNum},\n updated_at = NOW()\n WHERE work_order_id = '${id}';\n `;\n\n // CRITICAL: Do NOT set status='DONE', do NOT clear activeWorkOrder\n return [null, null, msg, null];\n }\n case \"scrap-skip\": {\n // User clicked \"No, Continue\" - just clear the prompt, keep running\n const { id } = msg.payload || {};\n\n if (!id) {\n node.error(\"No work order id supplied for scrap skip\", msg);\n return [null, null, null, null];\n }\n\n // Clear prompt flag so it can show again later\n global.set(\"scrapPromptIssuedFor\", null);\n\n msg._mode = \"scrap-skipped\";\n return [null, null, null, null];\n }\n case \"start\": {\n // START button clicked from Home dashboard\n // Enable tracking of cycles for the active work order\n global.set(\"trackingEnabled\", true);\n return [null, null, null, null];\n }\n case \"stop\": {\n // Manual STOP button clicked from Home dashboard\n // Disable tracking but keep work order active\n global.set(\"trackingEnabled\", false);\n return [null, null, null, null];\n }\n}\n",
|
||||
"func": "switch (msg.action) {\n case \"upload-excel\":\n msg._mode = \"upload\";\n return [msg, null, null, null];\n case \"refresh-work-orders\":\n msg._mode = \"select\";\n msg.topic = \"SELECT * FROM work_orders ORDER BY created_at DESC;\";\n return [null, msg, null, null];\n // start/complete unchanged...\n case \"start-work-order\": {\n msg._mode = \"start\";\n const order = msg.payload || {};\n if (!order.id) {\n node.error(\"No work order id supplied for start\", msg);\n return [null, null, null, null];\n }\n msg.startOrder = order;\n\n msg.topic = `\n UPDATE work_orders\n SET\n status = CASE\n WHEN work_order_id = '${order.id}' THEN 'RUNNING'\n ELSE 'PENDING'\n END,\n updated_at = CASE\n WHEN work_order_id = '${order.id}' THEN NOW()\n ELSE updated_at\n END\n WHERE status <> 'DONE';\n `;\n\n global.set(\"activeWorkOrder\", order);\n global.set(\"cycleCount\", 0);\n flow.set(\"lastMachineState\", 0);\n global.set(\"scrapPromptIssuedFor\", null);\n return [null, null, msg, null];\n }\n case \"complete-work-order\": {\n msg._mode = \"complete\";\n const order = msg.payload || {};\n if (!order.id) {\n node.error(\"No work order id supplied for complete\", msg);\n return [null, null, null, null];\n }\n msg.completeOrder = order;\n msg.topic = `\n UPDATE work_orders\n SET status = 'DONE', updated_at = NOW()\n WHERE work_order_id = '${order.id}';\n `;\n global.set(\"activeWorkOrder\", null);\n global.set(\"cycleCount\", 0);\n flow.set(\"lastMachineState\", 0);\n global.set(\"scrapPromptIssuedFor\", null);\n return [null, null, null, msg];\n }\n case \"scrap-entry\": {\n const { id, scrap } = msg.payload || {};\n const scrapNum = Number(scrap) || 0;\n\n if (!id) {\n node.error(\"No work order id supplied for scrap entry\", msg);\n return [null, null, null, null];\n }\n\n // Update activeWorkOrder with accumulated scrap\n const activeOrder = global.get(\"activeWorkOrder\");\n if (activeOrder && activeOrder.id === id) {\n activeOrder.scrap = (Number(activeOrder.scrap) || 0) + scrapNum;\n global.set(\"activeWorkOrder\", activeOrder);\n }\n\n // Clear prompt flag so it can show again when target reached next time\n global.set(\"scrapPromptIssuedFor\", null);\n\n msg._mode = \"scrap-update\";\n msg.scrapEntry = { id, scrap: scrapNum };\n msg.topic = `\n UPDATE work_orders\n SET\n scrap_parts = scrap_parts + ${scrapNum},\n updated_at = NOW()\n WHERE work_order_id = '${id}';\n `;\n\n // CRITICAL: Do NOT set status='DONE', do NOT clear activeWorkOrder\n return [null, null, msg, null];\n }\n case \"scrap-skip\": {\n // User clicked \"No, Continue\" - just clear the prompt, keep running\n const { id } = msg.payload || {};\n\n if (!id) {\n node.error(\"No work order id supplied for scrap skip\", msg);\n return [null, null, null, null];\n }\n\n // DON'T clear scrapPromptIssuedFor - keep it set so prompt doesn't appear again\n // Flag will be cleared when work order is completed or a new one is started\n\n msg._mode = \"scrap-skipped\";\n return [null, null, null, null];\n }\n case \"start\": {\n // START button clicked from Home dashboard\n // Enable tracking of cycles for the active work order\n global.set(\"trackingEnabled\", true);\n return [null, null, null, null];\n }\n case \"stop\": {\n // Manual STOP button clicked from Home dashboard\n // Disable tracking but keep work order active\n global.set(\"trackingEnabled\", false);\n return [null, null, null, null];\n }\n}\n",
|
||||
"outputs": 4,
|
||||
"timeout": 0,
|
||||
"noerr": 0,
|
||||
@@ -1148,7 +1148,7 @@
|
||||
"z": "cac3a4383120cb57",
|
||||
"g": "16bb591480852f51",
|
||||
"name": "Machine cycles",
|
||||
"func": "const current = Number(msg.payload) || 0;\n\nlet zeroStreak = flow.get(\"zeroStreak\") || 0;\nzeroStreak = current === 0 ? zeroStreak + 1 : 0;\nflow.set(\"zeroStreak\", zeroStreak);\n\nconst prev = flow.get(\"lastMachineState\") ?? 0;\nflow.set(\"lastMachineState\", current);\n\nglobal.set(\"machineOnline\", true); // force ONLINE for now\n\nlet productionRunning = !!global.get(\"productionStarted\");\nlet stateChanged = false;\n\nif (current === 1 && !productionRunning) {\n productionRunning = true;\n stateChanged = true;\n} else if (current === 0 && zeroStreak >= 2 && productionRunning) {\n productionRunning = false;\n stateChanged = true;\n}\n\nglobal.set(\"productionStarted\", productionRunning);\n\nconst stateMsg = stateChanged\n ? {\n _mode: \"production-state\",\n machineOnline: true,\n productionStarted: productionRunning\n }\n : null;\n\nconst activeOrder = global.get(\"activeWorkOrder\");\nconst cavities = Number(global.get(\"moldActive\") || 0);\nif (!activeOrder || !activeOrder.id || cavities <= 0) {\n // We still want to pass along any state change even if there's no active WO.\n return [null, stateMsg];\n}\n\n// Check if tracking is enabled (START button clicked)\nconst trackingEnabled = !!global.get(\"trackingEnabled\");\nif (!trackingEnabled) {\n // Cycles are happening but we're not tracking them yet\n return [null, stateMsg];\n}\n\n// only count rising edges (0 -> 1) for production totals\nif (prev === 1 || current !== 1) {\n return [null, stateMsg];\n}\n\nlet cycles = Number(global.get(\"cycleCount\") || 0) + 1;\nglobal.set(\"cycleCount\", cycles);\n\n// Calculate good parts: total produced minus accumulated scrap\nconst scrapTotal = Number(activeOrder.scrap) || 0;\nconst totalProduced = cycles * cavities;\nconst produced = totalProduced - scrapTotal;\nconst target = Number(activeOrder.target) || 0;\nconst progress = target > 0 ? Math.min(100, Math.round((produced / target) * 100)) : 0;\n\nactiveOrder.good = produced;\nactiveOrder.progressPercent = progress;\nactiveOrder.lastUpdateIso = new Date().toISOString();\nglobal.set(\"activeWorkOrder\", activeOrder);\n\nconst promptIssued = global.get(\"scrapPromptIssuedFor\") || null;\nif (!promptIssued && target > 0 && produced >= target) {\n global.set(\"scrapPromptIssuedFor\", activeOrder.id);\n msg._mode = \"scrap-prompt\";\n msg.scrapPrompt = {\n id: activeOrder.id,\n sku: activeOrder.sku || \"\",\n target,\n produced\n };\n return [null, msg]; // bypass the DB update on this cycle\n}\n\nconst dbMsg = {\n _mode: \"cycle\",\n cycle: {\n id: activeOrder.id,\n sku: activeOrder.sku || \"\",\n target,\n good: produced,\n scrap: Number(activeOrder.scrap) || 0,\n cycleTime: Number(activeOrder.cycleTime || activeOrder.theoreticalCycleTime || 0),\n progressPercent: progress,\n lastUpdateIso: activeOrder.lastUpdateIso,\n machineOnline: true,\n productionStarted: productionRunning\n },\n topic: `\n UPDATE work_orders\n SET\n good_parts = ${produced},\n progress_percent = ${progress},\n updated_at = NOW()\n WHERE work_order_id = '${activeOrder.id}';\n `\n};\n\nreturn [dbMsg, stateMsg];",
|
||||
"func": "const current = Number(msg.payload) || 0;\n\nlet zeroStreak = flow.get(\"zeroStreak\") || 0;\nzeroStreak = current === 0 ? zeroStreak + 1 : 0;\nflow.set(\"zeroStreak\", zeroStreak);\n\nconst prev = flow.get(\"lastMachineState\") ?? 0;\nflow.set(\"lastMachineState\", current);\n\nglobal.set(\"machineOnline\", true); // force ONLINE for now\n\nlet productionRunning = !!global.get(\"productionStarted\");\nlet stateChanged = false;\n\nif (current === 1 && !productionRunning) {\n productionRunning = true;\n stateChanged = true;\n} else if (current === 0 && zeroStreak >= 2 && productionRunning) {\n productionRunning = false;\n stateChanged = true;\n}\n\nglobal.set(\"productionStarted\", productionRunning);\n\nconst stateMsg = stateChanged\n ? {\n _mode: \"production-state\",\n machineOnline: true,\n productionStarted: productionRunning\n }\n : null;\n\nconst activeOrder = global.get(\"activeWorkOrder\");\nconst cavities = Number(global.get(\"moldActive\") || 0);\nif (!activeOrder || !activeOrder.id || cavities <= 0) {\n // We still want to pass along any state change even if there's no active WO.\n return [null, stateMsg];\n}\n\n// Check if tracking is enabled (START button clicked)\nconst trackingEnabled = !!global.get(\"trackingEnabled\");\nif (!trackingEnabled) {\n // Cycles are happening but we're not tracking them yet\n return [null, stateMsg];\n}\n\n// only count rising edges (0 -> 1) for production totals\nif (prev === 1 || current !== 1) {\n return [null, stateMsg];\n}\n\nlet cycles = Number(global.get(\"cycleCount\") || 0) + 1;\nglobal.set(\"cycleCount\", cycles);\n\n// Calculate good parts: total produced minus accumulated scrap\nconst scrapTotal = Number(activeOrder.scrap) || 0;\nconst totalProduced = cycles * cavities;\nconst produced = totalProduced - scrapTotal;\nconst target = Number(activeOrder.target) || 0;\nconst progress = target > 0 ? Math.min(100, Math.round((produced / target) * 100)) : 0;\n\nactiveOrder.good = produced;\nactiveOrder.progressPercent = progress;\nactiveOrder.lastUpdateIso = new Date().toISOString();\nglobal.set(\"activeWorkOrder\", activeOrder);\n\nconst promptIssued = global.get(\"scrapPromptIssuedFor\") || null;\nnode.warn(`[DEBUG] promptIssued=${promptIssued}, activeOrderId=${activeOrder.id}, produced=${produced}, target=${target}`);\n\nif (!promptIssued && target > 0 && produced >= target) {\n node.warn(`[DEBUG] TRIGGERING PROMPT!`);\n global.set(\"scrapPromptIssuedFor\", activeOrder.id);\n msg._mode = \"scrap-prompt\";\n msg.scrapPrompt = {\n id: activeOrder.id,\n sku: activeOrder.sku || \"\",\n target,\n produced\n };\n return [null, msg]; // bypass the DB update on this cycle\n}\n\nconst dbMsg = {\n _mode: \"cycle\",\n cycle: {\n id: activeOrder.id,\n sku: activeOrder.sku || \"\",\n target,\n good: produced,\n scrap: Number(activeOrder.scrap) || 0,\n cycleTime: Number(activeOrder.cycleTime || activeOrder.theoreticalCycleTime || 0),\n progressPercent: progress,\n lastUpdateIso: activeOrder.lastUpdateIso,\n machineOnline: true,\n productionStarted: productionRunning\n },\n topic: `\n UPDATE work_orders\n SET\n good_parts = ${produced},\n progress_percent = ${progress},\n updated_at = NOW()\n WHERE work_order_id = '${activeOrder.id}';\n `\n};\n\nreturn [dbMsg, stateMsg];",
|
||||
"outputs": 2,
|
||||
"timeout": 0,
|
||||
"noerr": 0,
|
||||
|
||||
Reference in New Issue
Block a user