241 lines
8.8 KiB
JavaScript
241 lines
8.8 KiB
JavaScript
// ========================================
|
|
// SESSION STATE PERSISTENCE FUNCTIONS
|
|
// ========================================
|
|
|
|
// Function 1: Sync Session State to Database
|
|
// Usage: Call this from Machine cycles and Work Order buttons functions
|
|
// Throttled to avoid database overload (updates every 5 seconds or on state changes)
|
|
const syncSessionState = function(msg, node, forceUpdate = false) {
|
|
const now = Date.now();
|
|
const lastSync = flow.get("lastSessionSync") || 0;
|
|
|
|
// Throttle: only sync every 5 seconds unless forced
|
|
if (!forceUpdate && (now - lastSync) < 5000) {
|
|
return null;
|
|
}
|
|
|
|
flow.set("lastSessionSync", now);
|
|
|
|
const sessionId = global.get("currentSessionId") || "session_" + Date.now();
|
|
global.set("currentSessionId", sessionId);
|
|
|
|
const activeOrder = global.get("activeWorkOrder");
|
|
const activeOrderId = activeOrder ? activeOrder.id : null;
|
|
const activeOrderData = activeOrder ? JSON.stringify(activeOrder) : null;
|
|
|
|
const sessionData = {
|
|
session_id: sessionId,
|
|
production_start_time: global.get("productionStartTime") || null,
|
|
operating_time: global.get("operatingTime") || 0,
|
|
last_update_time: now,
|
|
tracking_enabled: global.get("trackingEnabled") ? 1 : 0,
|
|
cycle_count: global.get("cycleCount") || 0,
|
|
active_work_order_id: activeOrderId,
|
|
active_work_order_data: activeOrderData,
|
|
machine_online: global.get("machineOnline") ? 1 : 0,
|
|
production_started: global.get("productionStarted") ? 1 : 0,
|
|
last_cycle_time: global.get("lastCycleTime") || null,
|
|
last_machine_state: flow.get("lastMachineState") || 0,
|
|
created_at: now,
|
|
updated_at: now,
|
|
is_active: 1
|
|
};
|
|
|
|
// Create upsert query (INSERT ... ON DUPLICATE KEY UPDATE)
|
|
msg.topic = `
|
|
INSERT INTO session_state (
|
|
session_id, production_start_time, operating_time, last_update_time,
|
|
tracking_enabled, cycle_count, active_work_order_id, active_work_order_data,
|
|
machine_online, production_started, last_cycle_time, last_machine_state,
|
|
created_at, updated_at, is_active
|
|
) VALUES (
|
|
'${sessionData.session_id}',
|
|
${sessionData.production_start_time},
|
|
${sessionData.operating_time},
|
|
${sessionData.last_update_time},
|
|
${sessionData.tracking_enabled},
|
|
${sessionData.cycle_count},
|
|
${sessionData.active_work_order_id ? "'" + sessionData.active_work_order_id + "'" : "NULL"},
|
|
${sessionData.active_work_order_data ? "'" + sessionData.active_work_order_data.replace(/'/g, "''") + "'" : "NULL"},
|
|
${sessionData.machine_online},
|
|
${sessionData.production_started},
|
|
${sessionData.last_cycle_time},
|
|
${sessionData.last_machine_state},
|
|
${sessionData.created_at},
|
|
${sessionData.updated_at},
|
|
${sessionData.is_active}
|
|
)
|
|
ON DUPLICATE KEY UPDATE
|
|
production_start_time = VALUES(production_start_time),
|
|
operating_time = VALUES(operating_time),
|
|
last_update_time = VALUES(last_update_time),
|
|
tracking_enabled = VALUES(tracking_enabled),
|
|
cycle_count = VALUES(cycle_count),
|
|
active_work_order_id = VALUES(active_work_order_id),
|
|
active_work_order_data = VALUES(active_work_order_data),
|
|
machine_online = VALUES(machine_online),
|
|
production_started = VALUES(production_started),
|
|
last_cycle_time = VALUES(last_cycle_time),
|
|
last_machine_state = VALUES(last_machine_state),
|
|
updated_at = VALUES(updated_at),
|
|
is_active = VALUES(is_active);
|
|
`;
|
|
|
|
msg._mode = "session-sync";
|
|
msg.sessionData = sessionData;
|
|
return msg;
|
|
};
|
|
|
|
// ========================================
|
|
|
|
// Function 2: Startup Recovery
|
|
// This function should run on Node-RED startup to check for and restore previous session
|
|
const startupRecovery = function(msg, node) {
|
|
// Query for most recent active session
|
|
msg.topic = `
|
|
SELECT * FROM session_state
|
|
WHERE is_active = 1
|
|
ORDER BY updated_at DESC
|
|
LIMIT 1;
|
|
`;
|
|
msg._mode = "startup-recovery";
|
|
return msg;
|
|
};
|
|
|
|
// ========================================
|
|
|
|
// Function 3: Process Recovery Data
|
|
// This processes the database response from startup recovery query
|
|
const processRecoveryData = function(msg, node) {
|
|
if (!msg.payload || msg.payload.length === 0) {
|
|
// No previous session found
|
|
node.warn("No previous session found. Starting fresh.");
|
|
msg._recoveryStatus = "no-session";
|
|
return msg;
|
|
}
|
|
|
|
const sessionData = msg.payload[0];
|
|
const now = Date.now();
|
|
const timeSinceLastUpdate = now - sessionData.last_update_time;
|
|
const hoursElapsed = timeSinceLastUpdate / (1000 * 60 * 60);
|
|
|
|
// If last update was more than 24 hours ago, consider it stale
|
|
if (hoursElapsed > 24) {
|
|
node.warn(`Previous session is stale (${hoursElapsed.toFixed(1)} hours old). Starting fresh.`);
|
|
msg._recoveryStatus = "session-stale";
|
|
return msg;
|
|
}
|
|
|
|
// Prompt user for recovery
|
|
msg._recoveryStatus = "session-found";
|
|
msg.recoveryData = {
|
|
sessionId: sessionData.session_id,
|
|
cycleCount: sessionData.cycle_count,
|
|
operatingTime: sessionData.operating_time,
|
|
workOrderId: sessionData.active_work_order_id,
|
|
trackingEnabled: sessionData.tracking_enabled === 1,
|
|
hoursElapsed: hoursElapsed.toFixed(1),
|
|
lastUpdateTime: new Date(sessionData.last_update_time).toISOString()
|
|
};
|
|
|
|
// Prepare prompt message for user
|
|
msg.payload = {
|
|
type: "recovery-prompt",
|
|
message: `Previous session found from ${hoursElapsed.toFixed(1)} hours ago:
|
|
- Work Order: ${sessionData.active_work_order_id || "None"}
|
|
- Cycles completed: ${sessionData.cycle_count}
|
|
- Operating time: ${(sessionData.operating_time / 3600).toFixed(2)} hours
|
|
|
|
Would you like to restore this session?`,
|
|
data: sessionData
|
|
};
|
|
|
|
return msg;
|
|
};
|
|
|
|
// ========================================
|
|
|
|
// Function 4: Restore Session
|
|
// Call this when user confirms they want to restore the session
|
|
const restoreSession = function(msg, node) {
|
|
const sessionData = msg.payload;
|
|
|
|
if (!sessionData) {
|
|
node.error("No session data provided for restoration");
|
|
return null;
|
|
}
|
|
|
|
try {
|
|
// Restore global context variables
|
|
global.set("currentSessionId", sessionData.session_id);
|
|
global.set("productionStartTime", sessionData.production_start_time);
|
|
global.set("operatingTime", sessionData.operating_time || 0);
|
|
global.set("trackingEnabled", sessionData.tracking_enabled === 1);
|
|
global.set("cycleCount", sessionData.cycle_count || 0);
|
|
global.set("machineOnline", sessionData.machine_online === 1);
|
|
global.set("productionStarted", sessionData.production_started === 1);
|
|
global.set("lastCycleTime", sessionData.last_cycle_time);
|
|
|
|
// Restore flow context variables
|
|
flow.set("lastMachineState", sessionData.last_machine_state || 0);
|
|
|
|
// Restore active work order if exists
|
|
if (sessionData.active_work_order_data) {
|
|
try {
|
|
const workOrder = JSON.parse(sessionData.active_work_order_data);
|
|
global.set("activeWorkOrder", workOrder);
|
|
} catch (e) {
|
|
node.error("Failed to parse work order data: " + e.message);
|
|
}
|
|
}
|
|
|
|
node.warn("Session restored successfully: " + sessionData.session_id);
|
|
msg._recoveryStatus = "restored";
|
|
msg.payload = {
|
|
success: true,
|
|
message: "Session restored successfully",
|
|
sessionId: sessionData.session_id
|
|
};
|
|
|
|
} catch (error) {
|
|
node.error("Error restoring session: " + error.message);
|
|
msg._recoveryStatus = "restore-failed";
|
|
msg.payload = {
|
|
success: false,
|
|
message: "Failed to restore session: " + error.message
|
|
};
|
|
}
|
|
|
|
return msg;
|
|
};
|
|
|
|
// ========================================
|
|
|
|
// Function 5: Deactivate Old Sessions
|
|
// Call this when starting a new work order to mark old sessions as inactive
|
|
const deactivateOldSessions = function(msg, node) {
|
|
const currentSessionId = global.get("currentSessionId");
|
|
|
|
msg.topic = `
|
|
UPDATE session_state
|
|
SET is_active = 0, updated_at = ${Date.now()}
|
|
WHERE is_active = 1
|
|
${currentSessionId ? "AND session_id != '" + currentSessionId + "'" : ""};
|
|
`;
|
|
|
|
msg._mode = "deactivate-sessions";
|
|
return msg;
|
|
};
|
|
|
|
// ========================================
|
|
// EXPORT for use in function nodes
|
|
// ========================================
|
|
|
|
module.exports = {
|
|
syncSessionState,
|
|
startupRecovery,
|
|
processRecoveryData,
|
|
restoreSession,
|
|
deactivateOldSessions
|
|
};
|