205 lines
7.9 KiB
Plaintext
205 lines
7.9 KiB
Plaintext
// ============================================================================
|
|
// STARTUP RECOVERY Function - Session State Restoration (Issue 1)
|
|
// Purpose: Restore session state from database on Node-RED startup/crash recovery
|
|
// Trigger: Inject node on startup
|
|
// Outputs: 2 (UI notification, database query)
|
|
// ============================================================================
|
|
|
|
// This function should be called on Node-RED startup (via inject node with "inject once after X seconds")
|
|
// It queries the session_state table and restores global variables
|
|
|
|
const mode = msg.mode || "check";
|
|
|
|
switch (mode) {
|
|
// ========================================================================
|
|
// STEP 1: Query database for session state
|
|
// ========================================================================
|
|
case "check": {
|
|
msg._mode = "query-session-state";
|
|
msg.topic = "SELECT * FROM session_state WHERE session_key = 'current_session';";
|
|
|
|
node.warn("[RECOVERY] Checking for existing session state...");
|
|
|
|
return [null, msg];
|
|
}
|
|
|
|
// ========================================================================
|
|
// STEP 2: Process query results and prompt user
|
|
// ========================================================================
|
|
case "process-results": {
|
|
const results = msg.payload;
|
|
|
|
if (!results || results.length === 0) {
|
|
node.warn("[RECOVERY] No session state found in database");
|
|
return [null, null];
|
|
}
|
|
|
|
const session = results[0];
|
|
|
|
// Check if there's a valid session to restore
|
|
if (!session.work_order_id || session.cycle_count === 0) {
|
|
node.warn("[RECOVERY] Session state exists but is empty - nothing to restore");
|
|
return [null, null];
|
|
}
|
|
|
|
// Check if tracking was enabled
|
|
if (!session.tracking_enabled) {
|
|
node.warn("[RECOVERY] Previous session was not actively tracking - restoring state without prompt");
|
|
|
|
// Restore the state silently
|
|
global.set("cycleCount", session.cycle_count || 0);
|
|
global.set("productionStartTime", session.production_start_time);
|
|
global.set("operatingTime", Number(session.operating_time) || 0);
|
|
global.set("downtime", Number(session.downtime) || 0);
|
|
global.set("lastUpdateTime", session.last_update_time || Date.now());
|
|
global.set("trackingEnabled", false);
|
|
global.set("scrapPromptIssuedFor", session.scrap_prompt_issued_for || null);
|
|
global.set("currentSessionId", session.current_session_id || null);
|
|
|
|
return [null, null];
|
|
}
|
|
|
|
// Session was actively tracking - prompt user
|
|
node.warn(`[RECOVERY] Found active session - Work Order: ${session.work_order_id}, Cycles: ${session.cycle_count}`);
|
|
|
|
const promptMsg = {
|
|
_mode: "recovery-prompt",
|
|
recoveryPrompt: {
|
|
workOrderId: session.work_order_id,
|
|
cycleCount: session.cycle_count,
|
|
operatingTime: Number(session.operating_time) || 0,
|
|
downtime: Number(session.downtime) || 0,
|
|
timestamp: session.last_update_time,
|
|
sessionData: session
|
|
}
|
|
};
|
|
|
|
return [promptMsg, null];
|
|
}
|
|
|
|
// ========================================================================
|
|
// STEP 3: User chose to restore session
|
|
// ========================================================================
|
|
case "restore": {
|
|
const sessionData = msg.payload;
|
|
|
|
if (!sessionData) {
|
|
node.error("[RECOVERY] No session data provided for restore");
|
|
return [null, null];
|
|
}
|
|
|
|
node.warn(`[RECOVERY] Restoring session - Work Order: ${sessionData.work_order_id}, Cycles: ${sessionData.cycle_count}`);
|
|
|
|
// Restore all global variables
|
|
global.set("cycleCount", sessionData.cycle_count || 0);
|
|
global.set("productionStartTime", sessionData.production_start_time);
|
|
global.set("operatingTime", Number(sessionData.operating_time) || 0);
|
|
global.set("downtime", Number(sessionData.downtime) || 0);
|
|
global.set("lastUpdateTime", sessionData.last_update_time || Date.now());
|
|
global.set("trackingEnabled", !!sessionData.tracking_enabled);
|
|
global.set("scrapPromptIssuedFor", sessionData.scrap_prompt_issued_for || null);
|
|
global.set("currentSessionId", sessionData.current_session_id || null);
|
|
|
|
// Also need to restore activeWorkOrder from work_orders table
|
|
const queryMsg = {
|
|
_mode: "query-work-order",
|
|
topic: `SELECT * FROM work_orders WHERE work_order_id = '${sessionData.work_order_id}';`
|
|
};
|
|
|
|
const notificationMsg = {
|
|
_mode: "recovery-success",
|
|
notification: {
|
|
message: `Session restored: ${sessionData.work_order_id} - ${sessionData.cycle_count} cycles`,
|
|
type: "success"
|
|
}
|
|
};
|
|
|
|
return [notificationMsg, queryMsg];
|
|
}
|
|
|
|
// ========================================================================
|
|
// STEP 4: Set activeWorkOrder from query result
|
|
// ========================================================================
|
|
case "set-work-order": {
|
|
const results = msg.payload;
|
|
|
|
if (!results || results.length === 0) {
|
|
node.error("[RECOVERY] Work order not found in database");
|
|
return [null, null];
|
|
}
|
|
|
|
const workOrder = results[0];
|
|
|
|
// Reconstruct activeWorkOrder object
|
|
const activeOrder = {
|
|
id: workOrder.work_order_id,
|
|
sku: workOrder.sku,
|
|
target: workOrder.target,
|
|
good: workOrder.good_parts,
|
|
scrap: workOrder.scrap_count || 0,
|
|
cycleTime: workOrder.cycle_time,
|
|
theoreticalCycleTime: workOrder.cycle_time,
|
|
progressPercent: workOrder.progress_percent,
|
|
status: workOrder.status,
|
|
lastUpdateIso: new Date().toISOString()
|
|
};
|
|
|
|
global.set("activeWorkOrder", activeOrder);
|
|
|
|
node.warn(`[RECOVERY] Active work order restored: ${activeOrder.id}`);
|
|
|
|
return [null, null];
|
|
}
|
|
|
|
// ========================================================================
|
|
// STEP 5: User chose to start fresh
|
|
// ========================================================================
|
|
case "start-fresh": {
|
|
node.warn("[RECOVERY] User chose to start fresh - clearing session state");
|
|
|
|
// Clear all global variables
|
|
global.set("cycleCount", 0);
|
|
global.set("productionStartTime", null);
|
|
global.set("operatingTime", 0);
|
|
global.set("downtime", 0);
|
|
global.set("lastUpdateTime", Date.now());
|
|
global.set("trackingEnabled", false);
|
|
global.set("activeWorkOrder", null);
|
|
global.set("scrapPromptIssuedFor", null);
|
|
global.set("currentSessionId", null);
|
|
|
|
// Clear session_state in database
|
|
const clearMsg = {
|
|
_mode: "clear-session-state",
|
|
topic: `
|
|
UPDATE session_state
|
|
SET
|
|
work_order_id = NULL,
|
|
cycle_count = 0,
|
|
production_start_time = NULL,
|
|
operating_time = 0,
|
|
downtime = 0,
|
|
last_update_time = NULL,
|
|
tracking_enabled = 0,
|
|
machine_state = 0,
|
|
scrap_prompt_issued_for = NULL,
|
|
current_session_id = NULL
|
|
WHERE session_key = 'current_session';
|
|
`
|
|
};
|
|
|
|
const notificationMsg = {
|
|
_mode: "recovery-cleared",
|
|
notification: {
|
|
message: "Started with fresh session",
|
|
type: "info"
|
|
}
|
|
};
|
|
|
|
return [notificationMsg, clearMsg];
|
|
}
|
|
}
|
|
|
|
// Default
|
|
return [null, null];
|