Initial commit

This commit is contained in:
SebastianOtt 2026-05-21 20:41:03 +02:00
commit b7a3b14e06
41 changed files with 27493 additions and 0 deletions

View file

@ -0,0 +1,692 @@
import { useCallback, useEffect } from 'preact/hooks';
import { $isUpdaterModalOpen, $isSuccessPopupOpen, $isErrorPopupOpen, $websocket, $wsConnectionStatus, $updateErrorMessages, $p4FirmwareProgress, $p4WebinterfaceProgress, $stm32FirmwareProgress, $c6FirmwareProgress, $updateState, $updatedTargets, $availableUpdates, type UpdateInfo, UpdateStatus, TargetUpdateStatus, TotalUpdateStatus } from './AppProviderStore';
import { stateSetters, request_available_updates } from './AppProviderStore';
import UpdaterModal from './UpdaterModal';
// Helper function to get target index for update types
const getTargetIndex = (core: string) => {
switch (core.toLowerCase()) {
case 'p4 webinterface': return 1; // P4 webinterface (first)
case 'p4 firmware': return 2; // P4 firmware (second)
case 'stm32': return 0; // STM32 firmware
case 'c6': return 3; // C6 firmware (last)
default: return -1;
}
};
// Heartbeat configuration
const HEARTBEAT_INTERVAL = 1500; // Send ping every 1 second
const HEARTBEAT_TIMEOUT = 700; // Wait 1 second for pong response
const MAX_MISSED_HEARTBEATS = 5; // Close connection after 5 missed pongs
// Heartbeat state
let heartbeatInterval: ReturnType<typeof setInterval> | null = null;
let heartbeatTimeout: ReturnType<typeof setTimeout> | null = null;
let missedHeartbeats = 0;
// Reconnect state
let isReconnecting = false;
let reconnectTimeout: ReturnType<typeof setTimeout> | null = null;
let reload_site_after_webinterface_update = false;
// Restart detection state
let disconnectDetected = false;
let heartbeatsAfterReconnect = 0;
const HEARTBEATS_NEEDED_AFTER_RESTART = 3;
function clearHeartbeat() {
if (heartbeatInterval) {
clearInterval(heartbeatInterval);
heartbeatInterval = null;
}
if (heartbeatTimeout) {
clearTimeout(heartbeatTimeout);
heartbeatTimeout = null;
}
missedHeartbeats = 0;
}
function clearReconnect() {
if (reconnectTimeout) {
clearTimeout(reconnectTimeout);
reconnectTimeout = null;
}
isReconnecting = false;
}
function scheduleReconnect() {
if (isReconnecting) {
////console.log("Reconnect already scheduled, skipping");
return;
}
isReconnecting = true;
////console.log("Scheduling reconnect in 1 second");
reconnectTimeout = setTimeout(() => {
connectWebSocket();
isReconnecting = false;
}, 1000);
}
function sendHeartbeat() {
if ($websocket.get() && ($websocket.get() as WebSocket).readyState === ($websocket.get() as WebSocket).OPEN) {
// Send ping message
const pingMessage = JSON.stringify({ message_type: "ping" });
try {
($websocket.get() as WebSocket).send(pingMessage);
} catch (error) {
console.error("Failed to send heartbeat ping:", error);
missedHeartbeats++;
return;
}
// Set timeout for pong response
if (heartbeatTimeout) {
clearTimeout(heartbeatTimeout);
heartbeatTimeout = null;
}
heartbeatTimeout = setTimeout(() => {
missedHeartbeats++;
if (missedHeartbeats >= MAX_MISSED_HEARTBEATS) {
console.error("Too many missed heartbeats, closing connection");
// Check if we're in restarting state - this indicates device restart
const currentUpdateState = $updateState.get();
if (currentUpdateState === 'restarting') {
//console.log("🔄 Disconnect detected during restart - marking for restart detection");
disconnectDetected = true;
heartbeatsAfterReconnect = 0;
}
// Wichtig: Erst Heartbeat stoppen
clearHeartbeat();
// Dann WebSocket schließen und null setzen
if ($websocket.get()) {
($websocket.get() as WebSocket).close();
stateSetters.setWebSocketConnectionStatus("Disconnected");
$websocket.set(null);
}
}
}, HEARTBEAT_TIMEOUT);
}
}
function startHeartbeat() {
clearHeartbeat(); // Clear any existing heartbeat
heartbeatInterval = setInterval(() => {
sendHeartbeat();
}, HEARTBEAT_INTERVAL);
}
function onOpen() {
stateSetters.setWebSocketConnectionStatus("Connected");
//console.log("WebSocket erfolgreich verbunden");
missedHeartbeats = 0;
// If we reconnected after detecting a restart, start counting heartbeats
if (disconnectDetected) {
//console.log("🔄 Reconnected after restart - starting heartbeat counter");
heartbeatsAfterReconnect = 0;
}
// Start heartbeat mechanism
startHeartbeat();
// Sende initiale Statusanfrage
if ($websocket.get() !== null) {
const initial_state_request = JSON.stringify({ message_type: "initial_state_request" });
try {
($websocket.get() as WebSocket).send(initial_state_request);
} catch (error) {
console.error("Failed to send initial state request:", error);
}
// Request update status immediately to catch any missed completion messages
setTimeout(() => {
if ($websocket.get() && ($websocket.get() as WebSocket).readyState === ($websocket.get() as WebSocket).OPEN) {
const update_status_request = JSON.stringify({ message_type: "request_update_status" });
try {
($websocket.get() as WebSocket).send(update_status_request);
//console.log("Requested update status on reconnection");
} catch (error) {
console.error("Failed to send update status request:", error);
}
}
}, 500); // Reduced from 1000ms to 500ms for faster response
// send request for available updates
setTimeout(() => {
if ($websocket.get() && ($websocket.get() as WebSocket).readyState === ($websocket.get() as WebSocket).OPEN) {
request_available_updates();
}
}, 1500);
}
}
function onMessage(message: MessageEvent) {
////console.log("WebSocket message received:", message.data.toString());
// Verarbeite JSON Nachrichten
const dataFromServer = JSON.parse(message.data.toString());
if (dataFromServer.message_type === "pong") {
// reset missed heartbeats and clear pending timeout
if (heartbeatTimeout) {
clearTimeout(heartbeatTimeout);
heartbeatTimeout = null;
}
missedHeartbeats = 0;
//console.log("Heartbeat pong received");
// Count heartbeats after reconnect for restart detection
if (disconnectDetected) {
heartbeatsAfterReconnect++;
//console.log(`🔄 Heartbeat ${heartbeatsAfterReconnect}/${HEARTBEATS_NEEDED_AFTER_RESTART} after restart`);
if (heartbeatsAfterReconnect >= HEARTBEATS_NEEDED_AFTER_RESTART) {
//console.log("🎉 Device restart completed - closing success popup and resetting state");
disconnectDetected = false;
disconnectDetected = false;
heartbeatsAfterReconnect = 0;
// Close success popup and reset state
stateSetters.setSuccessPopupOpen(false);
stateSetters.setUpdateState('initial');
if (reload_site_after_webinterface_update) {
// Hard reload ohne Cache
//window.location.reload();
}
}
}
}
else if (dataFromServer.message_type === "state_update" || dataFromServer.message_type === "initial_state_response") {
// If this is the initial state response, also request update status
if (dataFromServer.message_type === "initial_state_response") {
//console.log("📊 Initial state received, requesting update status...");
setTimeout(() => {
if ($websocket.get() && ($websocket.get() as WebSocket).readyState === ($websocket.get() as WebSocket).OPEN) {
const update_status_request = JSON.stringify({ message_type: "request_update_status" });
try {
($websocket.get() as WebSocket).send(update_status_request);
//console.log("Requested update status after initial state");
} catch (error) {
console.error("Failed to send update status request:", error);
}
}
}, 200); // Short delay to ensure connection is stable
}
if (dataFromServer.free_internal_heap) {
stateSetters.setFreeInternalHeap(dataFromServer.free_internal_heap);
}
// DCDC State aktualisieren
if (dataFromServer.dcdcStateMachine) {
stateSetters.setDCDCState(dataFromServer.dcdcStateMachine.dcdc_state);
stateSetters.setPOWERGOOD(dataFromServer.dcdcStateMachine.POWERGOOD);
stateSetters.setDcdcErrorStatus(dataFromServer.dcdcStateMachine.error_flag);
stateSetters.setREF_value(dataFromServer.dcdcStateMachine.ref_value);
}
// Laser State aktualisieren
if (dataFromServer.laserStateMachine) {
// Laser state update
stateSetters.setLaserState(dataFromServer.laserStateMachine.laser_state);
stateSetters.setModulationInputMethod(dataFromServer.laserStateMachine.laser_state);
stateSetters.setREF_calibrated(dataFromServer.laserStateMachine.is_REF_calibrated);
stateSetters.setHAS_DUTY_LOOKUP_TABLE(dataFromServer.laserStateMachine.has_duty_lookup_table);
stateSetters.setLaserErrorStatus(dataFromServer.laserStateMachine.error_flag);
stateSetters.setLASERGOOD(dataFromServer.laserStateMachine.LASERGOOD);
stateSetters.setLaserSetTemp(dataFromServer.laserStateMachine.set_temp);
stateSetters.setLaserMaxTemp(dataFromServer.laserStateMachine.max_temp);
stateSetters.setInterlock(dataFromServer.laserStateMachine.interlock);
stateSetters.setModulationInput(dataFromServer.laserStateMachine.modulationInput);
stateSetters.setSetCurrent(dataFromServer.laserStateMachine.set_current);
stateSetters.setMaxCurrent(dataFromServer.laserStateMachine.max_current);
stateSetters.setThresholdCurrent(dataFromServer.laserStateMachine.threshold_current);
stateSetters.setCurrentReading(dataFromServer.laserStateMachine.current_reading);
stateSetters.setLaserTemp(dataFromServer.laserStateMachine.laser_temp);
}
// Driver State aktualisieren
if (dataFromServer.driverStateMachine) {
stateSetters.setDriverTemp(dataFromServer.driverStateMachine.driver_temp);
stateSetters.setDriverMaxTemp(dataFromServer.driverStateMachine.max_temp);
stateSetters.setVccVoltage(dataFromServer.driverStateMachine.vcc_voltage);
stateSetters.setPdVoltage(dataFromServer.driverStateMachine.pd_voltage);
}
} else if (dataFromServer.message_type === "calibration_table") {
// Log raw data to see the actual format of values
//console.log("Raw calibration message:", message.data.toString().substring(0, 500) + "...");
// Parse values and format to always show with decimal places
const parsedValues = dataFromServer.values ?
dataFromServer.values.map((val: any) => {
// Convert to number and keep as float
const numVal = Number(val);
return numVal;
}) : [];
// Erstelle einen neuen Array mit korrekten Indizes
// Fülle Indizes 0-499 mit Nullen und füge dann die tatsächlichen Werte ein
const minDuty = dataFromServer.min_duty || 500;
const completedValues = [];
// Fülle Nullen für Indizes von 0 bis (minDuty-1)
for (let i = 0; i < minDuty; i++) {
completedValues.push(0);
}
for (let i = 0; i < parsedValues.length; i++) {
completedValues.push(parsedValues[i]);
}
// Store values in state - use the completed values with correct indices
stateSetters.setCalibrationTableData(
completedValues,
minDuty,
dataFromServer.max_duty || 3500, (dataFromServer.points_count || parsedValues.length) + minDuty
);
} else if (dataFromServer.message_type === "update_information") {
//console.log("Update information received");
//console.log("Raw update_information data:", dataFromServer);
// Update Information
if (dataFromServer.update_available) {
const infos: UpdateInfo[] = [];
if (dataFromServer.update_available.p4_firmware) {
infos.push({
core: 'p4 firmware',
currentVersion: dataFromServer.update_available.p4_firmware.current_version,
newVersion: dataFromServer.update_available.p4_firmware.new_version,
icon: 'p4 firmware',
hasProgressBar: true,
updateAvailable: true,
});
//console.log("P4 Firmware update available");
}
if (dataFromServer.update_available.p4_webinterface) {
infos.push({
core: "p4 webinterface",
currentVersion: dataFromServer.update_available.p4_webinterface.current_version,
newVersion: dataFromServer.update_available.p4_webinterface.new_version,
icon: "p4 webinterface",
hasProgressBar: true,
updateAvailable: true,
});
//console.log("P4 Webinterface update available");
}
if (dataFromServer.update_available.stm32_firmware) {
infos.push({
core: "STM32",
currentVersion: dataFromServer.update_available.stm32_firmware.current_version,
newVersion: dataFromServer.update_available.stm32_firmware.new_version,
icon: "stm32",
hasProgressBar: true,
updateAvailable: true,
});
//console.log("STM32 Firmware update available");
}
if (dataFromServer.update_available.c6_firmware) {
infos.push({
core: "C6",
currentVersion: dataFromServer.update_available.c6_firmware.current_version,
newVersion: dataFromServer.update_available.c6_firmware.new_version,
icon: "c6",
hasProgressBar: true,
updateAvailable: true,
});
//console.log("C6 Firmware update available");
}
stateSetters.setAvailableUpdates(infos);
stateSetters.setUpdateChangelog(dataFromServer.changelog);
}
// Alte einzelne Progress-Message-Handler wurden durch complete_update_status ersetzt
} else if (dataFromServer.message_type === "complete_update_status") {
//console.log("📊 Complete update status received:", dataFromServer);
// Check if we're currently in restarting state - if so, ignore status updates
const currentUpdateState = $updateState.get() as any;
if (currentUpdateState === 'restarting') {
//console.log("🔄 Ignoring complete update status during RESTARTING state - countdown in progress");
return;
}
// Process the complete status information for all targets
if (dataFromServer.targets) {
const targets = dataFromServer.targets;
// STM32 Status
if (targets.stm32) {
const status = targets.stm32.status;
const progress = targets.stm32.progress || 0;
const updateAvailable = targets.stm32.update_available || false;
// Convert string status to TargetUpdateStatus enum
let statusEnum = TargetUpdateStatus.NO_UPDATE;
switch (status) {
case 'waiting': statusEnum = TargetUpdateStatus.WAITING; break;
case 'started': statusEnum = TargetUpdateStatus.IN_PROGRESS; break;
case 'in_progress': statusEnum = TargetUpdateStatus.IN_PROGRESS; break;
case 'completed': statusEnum = TargetUpdateStatus.COMPLETED; break;
case 'error': statusEnum = TargetUpdateStatus.ERROR; break;
case 'no_update': statusEnum = TargetUpdateStatus.NO_UPDATE; break;
case 'restarting': statusEnum = TargetUpdateStatus.IN_PROGRESS; break;
default: statusEnum = TargetUpdateStatus.NO_UPDATE; break;
}
stateSetters.setStm32FirmwareProgress(progress, statusEnum);
//console.log(`📊 STM32: status=${status}, progress=${progress}, available=${updateAvailable}`);
}
// P4 Webinterface Status
if (targets.p4_webinterface) {
const status = targets.p4_webinterface.status;
const progress = targets.p4_webinterface.progress || 0;
const updateAvailable = targets.p4_webinterface.update_available || false;
let statusEnum = TargetUpdateStatus.NO_UPDATE;
switch (status) {
case 'waiting': statusEnum = TargetUpdateStatus.WAITING; break;
case 'started': statusEnum = TargetUpdateStatus.IN_PROGRESS; break;
case 'in_progress': statusEnum = TargetUpdateStatus.IN_PROGRESS; break;
case 'completed': statusEnum = TargetUpdateStatus.COMPLETED; break;
case 'error': statusEnum = TargetUpdateStatus.ERROR; break;
case 'no_update': statusEnum = TargetUpdateStatus.NO_UPDATE; break;
case 'restarting': statusEnum = TargetUpdateStatus.IN_PROGRESS; break;
default: statusEnum = TargetUpdateStatus.NO_UPDATE; break;
}
stateSetters.setP4WebinterfaceProgress(progress, statusEnum);
//console.log(`📊 P4 Webinterface: status=${status}, progress=${progress}, available=${updateAvailable}`);
}
// P4 Firmware Status
if (targets.p4_firmware) {
const status = targets.p4_firmware.status;
const progress = targets.p4_firmware.progress || 0;
const updateAvailable = targets.p4_firmware.update_available || false;
let statusEnum = TargetUpdateStatus.NO_UPDATE;
switch (status) {
case 'waiting': statusEnum = TargetUpdateStatus.WAITING; break;
case 'started': statusEnum = TargetUpdateStatus.IN_PROGRESS; break;
case 'in_progress': statusEnum = TargetUpdateStatus.IN_PROGRESS; break;
case 'completed': statusEnum = TargetUpdateStatus.COMPLETED; break;
case 'error': statusEnum = TargetUpdateStatus.ERROR; break;
case 'no_update': statusEnum = TargetUpdateStatus.NO_UPDATE; break;
case 'restarting': statusEnum = TargetUpdateStatus.IN_PROGRESS; break;
default: statusEnum = TargetUpdateStatus.NO_UPDATE; break;
}
stateSetters.setP4FirmwareProgress(progress, statusEnum);
//console.log(`📊 P4 Firmware: status=${status}, progress=${progress}, available=${updateAvailable}`);
}
// C6 Status
if (targets.c6) {
const status = targets.c6.status;
const progress = targets.c6.progress || 0;
const updateAvailable = targets.c6.update_available || false;
let statusEnum = TargetUpdateStatus.NO_UPDATE;
switch (status) {
case 'waiting': statusEnum = TargetUpdateStatus.WAITING; break;
case 'started': statusEnum = TargetUpdateStatus.IN_PROGRESS; break;
case 'in_progress': statusEnum = TargetUpdateStatus.IN_PROGRESS; break;
case 'completed': statusEnum = TargetUpdateStatus.COMPLETED; break;
case 'error': statusEnum = TargetUpdateStatus.ERROR; break;
case 'no_update': statusEnum = TargetUpdateStatus.NO_UPDATE; break;
case 'restarting': statusEnum = TargetUpdateStatus.IN_PROGRESS; break;
default: statusEnum = TargetUpdateStatus.NO_UPDATE; break;
}
stateSetters.setC6FirmwareProgress(progress, statusEnum);
//console.log(`📊 C6: status=${status}, progress=${progress}, available=${updateAvailable}`);
}
}
// Check if there's an ongoing update
if (dataFromServer.update_in_progress) {
//console.log("🔄 Update in progress detected, setting total_update_status to updating");
stateSetters.setUpdateInProgress(true);
stateSetters.setTotalUpdateStatus(TotalUpdateStatus.UPDATING);
// Use the total_update_status from backend if available
if (dataFromServer.total_update_status !== undefined) {
const totalStatus = dataFromServer.total_update_status;
//console.log("📊 Backend total_update_status:", totalStatus);
switch (totalStatus) {
case 0: // INITIAL
stateSetters.setTotalUpdateStatus(TotalUpdateStatus.INITIAL);
break;
case 1: // STARTED
stateSetters.setTotalUpdateStatus(TotalUpdateStatus.STARTED);
break;
case 2: // UPDATING
stateSetters.setTotalUpdateStatus(TotalUpdateStatus.UPDATING);
break;
case 3: // SUCCESS
stateSetters.setTotalUpdateStatus(TotalUpdateStatus.SUCCESS);
break;
case 4: // ERROR
stateSetters.setTotalUpdateStatus(TotalUpdateStatus.ERROR);
break;
case 5: // RESTARTING
stateSetters.setTotalUpdateStatus(TotalUpdateStatus.RESTARTING);
break;
default:
console.warn("Unknown total_update_status:", totalStatus);
break;
}
}
// Open the updater modal, if it is not already open and no success/error popups are showing
const isModalOpen = $isUpdaterModalOpen.get();
const isSuccessPopupOpen = $isSuccessPopupOpen.get();
const isErrorPopupOpen = $isErrorPopupOpen.get();
/*console.log("🎯 Modal decision matrix:", {
isModalOpen,
totalUpdateStatus: currentUpdateState,
isSuccessPopupOpen,
isErrorPopupOpen,
shouldOpen: !isModalOpen && !isSuccessPopupOpen && !isErrorPopupOpen && currentUpdateState !== 'success' && currentUpdateState !== 'restarting'
});*/
// Don't open updater modal if success/error popups are open or we're in success/restarting state
const shouldOpenModal = !isModalOpen && !isSuccessPopupOpen && !isErrorPopupOpen &&
currentUpdateState !== 'success' && currentUpdateState !== 'restarting';
if (shouldOpenModal) {
//console.log("🚀 Opening updater modal - conditions met");
stateSetters.setUpdaterModalOpen(true);
} else {
//console.log("🚫 Skipping updater modal - conditions not met");
}
} else {
//console.log("✅ No update in progress, checking if all available updates are completed");
// Check if all targets with update_available: true are completed
let allAvailableUpdatesCompleted = true;
let hasAnyAvailableUpdates = false;
if (dataFromServer.targets.stm32 && dataFromServer.targets.stm32.update_available) {
hasAnyAvailableUpdates = true;
if (dataFromServer.targets.stm32.status !== 'completed') {
allAvailableUpdatesCompleted = false;
}
}
if (dataFromServer.targets.p4_webinterface && dataFromServer.targets.p4_webinterface.update_available) {
hasAnyAvailableUpdates = true;
if (dataFromServer.targets.p4_webinterface.status !== 'completed') {
allAvailableUpdatesCompleted = false;
}
}
if (dataFromServer.targets.p4_firmware && dataFromServer.targets.p4_firmware.update_available) {
hasAnyAvailableUpdates = true;
if (dataFromServer.targets.p4_firmware.status !== 'completed') {
allAvailableUpdatesCompleted = false;
}
}
if (dataFromServer.targets.c6 && dataFromServer.targets.c6.update_available) {
hasAnyAvailableUpdates = true;
if (dataFromServer.targets.c6.status !== 'completed') {
allAvailableUpdatesCompleted = false;
}
}
if (hasAnyAvailableUpdates && allAvailableUpdatesCompleted) {
//console.log("🎉 All available updates completed successfully, setting total_update_status to success");
stateSetters.setUpdateInProgress(false);
stateSetters.setTotalUpdateStatus(TotalUpdateStatus.SUCCESS);
} else if (!hasAnyAvailableUpdates) {
//console.log("ℹ️ No updates available, resetting total_update_status to initial");
stateSetters.setUpdateInProgress(false);
if (currentUpdateState !== 'success' && currentUpdateState !== 'error' && currentUpdateState !== 'restarting') {
stateSetters.setTotalUpdateStatus(TotalUpdateStatus.INITIAL);
}
} else {
//console.log("⏳ Some available updates still in progress, keeping current total_update_status");
}
}
} else if (dataFromServer.message_type === "response_update_status") {
console.log("📊 Update status response received:", dataFromServer);
// Check if we're currently in restarting state - if so, ignore status updates
const currentUpdateState = $updateState.get() as any;
if (currentUpdateState === 'restarting') {
//console.log("🔄 Ignoring update status during RESTARTING state - countdown in progress");
return;
}
// Always process the complete status information, regardless of update_in_progress
//console.log("🔄 Processing complete update status for all targets");
// Set progress and status for each update type if provided
if (dataFromServer.p4_firmware_progress !== undefined) {
const status = dataFromServer.p4_firmware_status || 0;
stateSetters.setP4FirmwareProgress(dataFromServer.p4_firmware_progress, status);
//console.log(`📊 P4 Firmware: progress=${dataFromServer.p4_firmware_progress}, status=${status}`);
}
if (dataFromServer.p4_webinterface_progress !== undefined) {
const status = dataFromServer.p4_webinterface_status || 0;
stateSetters.setP4WebinterfaceProgress(dataFromServer.p4_webinterface_progress, status);
//console.log(`📊 P4 Webinterface: progress=${dataFromServer.p4_webinterface_progress}, status=${status}`);
}
if (dataFromServer.stm32_firmware_progress !== undefined) {
const status = dataFromServer.stm32_firmware_status || 0;
stateSetters.setStm32FirmwareProgress(dataFromServer.stm32_firmware_progress, status);
//console.log(`📊 STM32 Firmware: progress=${dataFromServer.stm32_firmware_progress}, status=${status}`);
}
if (dataFromServer.c6_firmware_progress !== undefined) {
const status = dataFromServer.c6_firmware_status || 0;
stateSetters.setC6FirmwareProgress(dataFromServer.c6_firmware_progress, status);
//console.log(`📊 C6 Firmware: progress=${dataFromServer.c6_firmware_progress}, status=${status}`);
}
// Store updated_targets array from server for UpdaterModal
if (dataFromServer.updated_targets && Array.isArray(dataFromServer.updated_targets)) {
stateSetters.setUpdatedTargets(dataFromServer.updated_targets);
//console.log("🎯 Updated targets:", dataFromServer.updated_targets);
}
// Set current target if provided
if (dataFromServer.current_target !== undefined) {
stateSetters.setCurrentUpdateTargetIndex(dataFromServer.current_target);
//console.log("🎯 Current target:", dataFromServer.current_target);
}
// Check if there's an ongoing update
if (dataFromServer.update_in_progress) {
//console.log("🔄 Update in progress detected, setting state to updating");
stateSetters.setUpdateInProgress(true);
stateSetters.setUpdateState("updating");
// Open the updater modal, if it is not already open and no success/error popups are showing
const isModalOpen = $isUpdaterModalOpen.get();
const isSuccessPopupOpen = $isSuccessPopupOpen.get();
const isErrorPopupOpen = $isErrorPopupOpen.get();
/*console.log("🎯 Modal decision matrix:", {
isModalOpen,
updateState: currentUpdateState,
isSuccessPopupOpen,
isErrorPopupOpen,
shouldOpen: !isModalOpen && !isSuccessPopupOpen && !isErrorPopupOpen && currentUpdateState !== 'success' && currentUpdateState !== 'restarting'
});*/
// Don't open updater modal if success/error popups are open or we're in success/restarting state
const shouldOpenModal = !isModalOpen && !isSuccessPopupOpen && !isErrorPopupOpen &&
currentUpdateState !== 'success' && currentUpdateState !== 'restarting';
if (shouldOpenModal) {
//console.log("🚀 Opening updater modal - conditions met");
stateSetters.setUpdaterModalOpen(true);
} else {
//console.log("🚫 Skipping updater modal - conditions not met");
}
}
} else {
// Unbekannter Nachrichtentyp – korrektes Feld protokollieren
console.warn(`[${new Date().toISOString()}] Unbekannter Nachrichtentyp: ${dataFromServer.message_type ?? 'undefined'}`);
console.debug('Payload:', dataFromServer);
}
}
function onError(error: Event) {
$wsConnectionStatus.set("Disconnected");
//console.log("WebSocket error:", error);
clearHeartbeat();
scheduleReconnect();
}
function onClose(event: CloseEvent) {
$wsConnectionStatus.set("Disconnected");
//console.log("WebSocket closed with code:", event.code);
//console.log("Close reason:", event.reason);
//console.log("Clean close:", event.wasClean);
clearHeartbeat();
if (event.code === 1000) {
//console.log("Normal closure");
}
scheduleReconnect();
}
const connectWebSocket = () => {
// Clear any existing reconnect attempts
clearReconnect();
clearHeartbeat();
const wsAddress = `ws://hydrobuddy.local/ws`;
//console.log(`Verbinde mit: ${wsAddress}`);
if ($websocket.get() !== null) {
($websocket.get() as WebSocket).close(1000);
$websocket.set(null);
}
$websocket.set(new WebSocket(wsAddress));
if ($websocket.get() !== null) {
($websocket.get() as WebSocket).onopen = onOpen;
($websocket.get() as WebSocket).onmessage = onMessage;
($websocket.get() as WebSocket).onerror = onError;
($websocket.get() as WebSocket).onclose = onClose;
}
}
export const WebSocketProvider = () => {
// Auto-connect WebSocket at first render
useEffect(() => {
//console.log("Component mounted, starting WebSocket connection...");
connectWebSocket();
// Cleanup beim Unmounting
return () => {
clearHeartbeat();
clearReconnect();
($websocket.get() as WebSocket).close();
$websocket.set(null);
$wsConnectionStatus.set("Disconnected");
};
}, []);
};