Initial commit, 90% there
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
const current = Number(msg.payload) || 0;
|
||||
|
||||
let zeroStreak = flow.get("zeroStreak") || 0;
|
||||
zeroStreak = current === 0 ? zeroStreak + 1 : 0;
|
||||
flow.set("zeroStreak", zeroStreak);
|
||||
|
||||
const prev = flow.get("lastMachineState") ?? 0;
|
||||
flow.set("lastMachineState", current);
|
||||
|
||||
global.set("machineOnline", true);
|
||||
|
||||
let productionRunning = !!global.get("productionStarted");
|
||||
let stateChanged = false;
|
||||
|
||||
if (current === 1 && !productionRunning) {
|
||||
productionRunning = true;
|
||||
stateChanged = true;
|
||||
} else if (current === 0 && zeroStreak >= 2 && productionRunning) {
|
||||
productionRunning = false;
|
||||
stateChanged = true;
|
||||
}
|
||||
|
||||
global.set("productionStarted", productionRunning);
|
||||
|
||||
const stateMsg = stateChanged
|
||||
? {
|
||||
_mode: "production-state",
|
||||
machineOnline: true,
|
||||
productionStarted: productionRunning
|
||||
}
|
||||
: null;
|
||||
|
||||
const activeOrder = global.get("activeWorkOrder");
|
||||
// FIX: moldActive is object, extract cavities property
|
||||
const cavities = Number(global.get("moldActive")) || 1;
|
||||
if (!activeOrder || !activeOrder.id || cavities <= 0) {
|
||||
return [null, stateMsg, { _triggerKPI: true }, null];
|
||||
}
|
||||
|
||||
var trackingEnabled = !!global.get("trackingEnabled");
|
||||
if (!trackingEnabled) {
|
||||
return [null, stateMsg, { _triggerKPI: true }, null];
|
||||
}
|
||||
|
||||
if (prev === 1 || current !== 1) {
|
||||
return [null, stateMsg, { _triggerKPI: true }, null];
|
||||
}
|
||||
|
||||
let cycles = Number(global.get("cycleCount") || 0) + 1;
|
||||
global.set("cycleCount", cycles);
|
||||
|
||||
// Clear startup mode after first real cycle
|
||||
if (global.get("kpiStartupMode")) {
|
||||
global.set("kpiStartupMode", false);
|
||||
node.warn('[MACHINE CYCLE] First cycle - cleared kpiStartupMode');
|
||||
}
|
||||
|
||||
const now = Date.now();
|
||||
const lastCycleTime = global.get("lastCycleTime") || now;
|
||||
const timeSinceLastCycle = now - lastCycleTime;
|
||||
|
||||
let operatingTime = global.get("operatingTime") || 0;
|
||||
operatingTime += (timeSinceLastCycle / 1000);
|
||||
|
||||
global.set("operatingTime", operatingTime);
|
||||
node.warn(`[MACHINE CYCLE] operatingTime updated to ${operatingTime}`);
|
||||
global.set("lastCycleTime", now);
|
||||
|
||||
const scrapTotal = Number(activeOrder.scrap) || 0;
|
||||
const totalProduced = cycles * cavities;
|
||||
// FIX: Guard negative produced
|
||||
const produced = Math.max(0, totalProduced - scrapTotal);
|
||||
const target = Number(activeOrder.target) || 0;
|
||||
const progress = target > 0 ? Math.min(100, Math.round((produced / target) * 100)) : 0;
|
||||
|
||||
activeOrder.good = produced;
|
||||
activeOrder.progressPercent = progress;
|
||||
activeOrder.lastUpdateIso = new Date().toISOString();
|
||||
global.set("activeWorkOrder", activeOrder);
|
||||
|
||||
const promptIssued = global.get("scrapPromptIssuedFor") || null;
|
||||
if (!promptIssued && target > 0 && produced >= target) {
|
||||
global.set("scrapPromptIssuedFor", activeOrder.id);
|
||||
msg._mode = "scrap-prompt";
|
||||
msg.scrapPrompt = {
|
||||
id: activeOrder.id,
|
||||
sku: activeOrder.sku || "",
|
||||
target,
|
||||
produced
|
||||
};
|
||||
return [null, msg, null, null];
|
||||
}
|
||||
|
||||
const dbMsg = {
|
||||
_mode: "cycle",
|
||||
cycle: {
|
||||
id: activeOrder.id,
|
||||
sku: activeOrder.sku || "",
|
||||
target,
|
||||
good: produced,
|
||||
scrap: Number(activeOrder.scrap) || 0,
|
||||
cycleTime: Number(activeOrder.cycleTime || activeOrder.theoreticalCycleTime || 0),
|
||||
progressPercent: progress,
|
||||
lastUpdateIso: activeOrder.lastUpdateIso,
|
||||
machineOnline: true,
|
||||
productionStarted: productionRunning
|
||||
},
|
||||
// SQL with bound parameters for safety
|
||||
topic: "UPDATE work_orders SET good_parts = ?, progress_percent = ?, updated_at = NOW() WHERE work_order_id = ?",
|
||||
payload: [produced, progress, activeOrder.id]
|
||||
};
|
||||
|
||||
const kpiTrigger = { _triggerKPI: true };
|
||||
|
||||
if (trackingEnabled && dbMsg) {
|
||||
global.set("lastMachineCycleTime", Date.now());
|
||||
}
|
||||
|
||||
// 4th output: persist cycle_count and good_parts (throttled to 5 seconds)
|
||||
const lastDbWrite = global.get("lastWorkOrderDbWrite") || 0;
|
||||
const timeSinceLastWrite = now - lastDbWrite;
|
||||
let persistCycleCount = null;
|
||||
|
||||
// Only send DB update every 5 seconds (5000ms) to reduce DB load
|
||||
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);
|
||||
node.warn(`[DB PERSIST] Writing to work_orders: cycles=${cycles}, good_parts=${produced}, progress=${progress}%`);
|
||||
}
|
||||
|
||||
return [dbMsg, stateMsg, kpiTrigger, persistCycleCount];
|
||||
Reference in New Issue
Block a user