ilia.gurielidze@autStand.com bef2f78da4 Wrking verson at least on Windows
2025-04-10 18:50:47 +04:00

170 lines
7.9 KiB
JavaScript

// --- Server-Sent Events (SSE) Management ---
// Note: Relies on several functions and variables being globally accessible:
// - Global state: currentProjectData, selectedProjectName, eventSource
// - State helpers: isProcessing, setSelectedProject
// - UI functions: uiUpdateStatusBar, uiShowProcessingState, uiClearProcessingState, uiUpdateProjectSelector, uiClearProjectDisplay
// - Core update functions: handleProjectChange, updateUIScadaCore, updateUIDrawingCore, updateUIConflictsCore
// - Utility: debounce
/**
* Processes the full data update received from the SSE stream.
* Updates the internal state and triggers UI updates based on the data.
* @param {object} fullData - The complete data object from the server.
*/
function sseProcessUpdate(fullData) {
console.log("[SSE] Received Full Data:", fullData);
// Store the latest full data globally
currentProjectData = {}; // Reset first
const incomingProjects = fullData.projects || [];
incomingProjects.forEach(projName => {
currentProjectData[projName] = {
status: fullData.status ? fullData.status[projName] : 'Unknown',
last_commit: fullData.last_commit ? fullData.last_commit[projName] : 'N/A',
// Ensure progress exists and has expected structure
progress: (fullData.progress && fullData.progress[projName])
? fullData.progress[projName]
: { overall: {}, panels: {} }
};
});
// --- Update Project Selector Dropdown ---
const selectionChanged = uiUpdateProjectSelector(incomingProjects);
if (selectionChanged) {
// If the selection was forced (e.g., previous deleted, list empty)
// handleProjectChange will update the UI for the new/empty selection
console.log("[SSE] Project selection changed by dropdown update. Calling handleProjectChange.");
handleProjectChange(); // Needs global access
return; // Stop further processing here
}
// --- Process data for the currently selected project ---
const currentSelection = document.getElementById('projectSelector')?.value; // Check selector exists
// Update global state if needed (should normally match after selector update)
if (selectedProjectName !== currentSelection) {
setSelectedProject(currentSelection); // Needs global access
}
if (!selectedProjectName || !currentProjectData[selectedProjectName]) {
console.log("[SSE] No project selected or no data available for selected project.");
// Update UI to reflect lack of selection/data
const projectSelector = document.getElementById('projectSelector');
if (projectSelector && projectSelector.options.length > 0 && projectSelector.value) {
uiUpdateStatusBar(selectedProjectName, 'Waiting for data...', 'N/A');
uiShowProcessingState('Waiting for data...');
} else {
uiUpdateStatusBar('...', 'No projects available', 'N/A');
uiClearProjectDisplay();
}
return;
}
const projectData = currentProjectData[selectedProjectName];
// Update status bar immediately
uiUpdateStatusBar(selectedProjectName, projectData.status, projectData.last_commit);
// Update main UI based on processing state
const projectIsCurrentlyProcessing = isProcessing(projectData.status);
console.log(`[SSE] Status Check: Project '${selectedProjectName}', Status: '${projectData.status}', isProcessing: ${projectIsCurrentlyProcessing}`);
if (projectIsCurrentlyProcessing) {
// If *any* project is processing, the global flag should remain true
isAnalysisGloballyActive = true;
console.log(`[State] isAnalysisGloballyActive set/kept true due to processing status: "${projectData.status}"`);
uiShowProcessingState(projectData.status);
} else {
// --- Update Global Flag: Check if ANY project is still processing ---
let anyProjectStillProcessing = false;
const projectNames = Object.keys(currentProjectData);
for (const projName of projectNames) {
if (currentProjectData[projName] && isProcessing(currentProjectData[projName].status)) {
anyProjectStillProcessing = true;
console.log(`[State] Project ${projName} is still processing. Keeping isAnalysisGloballyActive true.`);
break; // Found one, no need to check others
}
}
if (!anyProjectStillProcessing) {
isAnalysisGloballyActive = false;
console.log("[State] No projects are processing. isAnalysisGloballyActive set to false.");
}
// --- End Global Flag Update ---
// Added Debugging: Log before clearing and redrawing
console.log(`[SSE] Clearing processing state and triggering redraw for ${selectedProjectName}`);
uiClearProcessingState();
// Call core redraws immediately
if(projectData && !isProcessing(projectData.status)) {
console.log(`[SSE] Confirmed non-processing state for ${selectedProjectName}. Calling core redraw functions.`);
updateUIScadaCore(projectData); // Needs global access
updateUIDrawingCore(projectData); // Needs global access
updateUIConflictsCore(projectData); // Needs global access
} else {
// This case handles if the status somehow flipped back to processing
// between the initial check and this point (very unlikely but safe).
const latestStatus = projectData?.status || "Processing...";
console.warn(`[SSE] State unexpectedly indicates processing just before redraw for ${selectedProjectName}. Status: '${latestStatus}'. Showing processing state again.`);
uiShowProcessingState(latestStatus);
}
}
}
// Create a debounced version of the process update function
const debouncedProcessUpdate = debounce(sseProcessUpdate, 250);
/**
* Initializes the Server-Sent Events connection and sets up message handlers.
*/
function sseInitialize() {
console.log("[SSE] Initializing connection...");
// Use global eventSource variable from state.js
if (eventSource) {
console.log("[SSE] Closing existing connection.");
eventSource.close();
}
try {
eventSource = new EventSource("/stream");
eventSource.onmessage = function(event) {
try {
const data = JSON.parse(event.data);
// Call the debounced processor
debouncedProcessUpdate(data);
} catch (error) {
console.error("[SSE] Error parsing data:", error, "Data:", event.data);
// Update UI to show error
uiUpdateStatusBar(selectedProjectName, 'Error processing server update.', 'N/A');
}
};
eventSource.onerror = function(err) {
console.error("[SSE] Connection error:", err);
// Update UI to show error
uiUpdateStatusBar(selectedProjectName, 'Connection lost. Retrying...', 'N/A');
// Simple retry mechanism: close and re-initialize after a delay
if (eventSource) {
eventSource.close();
eventSource = null; // Nullify to allow re-creation
}
setTimeout(sseInitialize, 5000); // Attempt to reconnect after 5 seconds
};
eventSource.onopen = function() {
console.log("[SSE] Connection opened.");
// Optionally update UI to show connected status
uiUpdateStatusBar(selectedProjectName, "Connected. Waiting for updates...", currentProjectData[selectedProjectName]?.last_commit);
};
console.log("[SSE] Event handlers set up.");
} catch (error) {
console.error("[SSE] Failed to create EventSource:", error);
uiUpdateStatusBar(selectedProjectName, 'Failed to connect to server.', 'N/A');
}
}
// --- Export (if using modules) ---
// export { sseInitialize };