import { create } from 'zustand' import { AlarmManager, AlarmEvent } from '@/services/AlarmManager' import { SensorConfig } from '@/types/sensor' export interface SensorData { id: string; x: number; y: number; zone: string; label: string; currentValue: number; // Changed from currentPressure to currentValue (0-4095) data: Array<{ time: string; timestamp: number; value: number }>; // Changed from pressure to value status: string; source?: 'hardware'; pin?: number; digitalState?: number; warningThreshold?: number; alarmThreshold?: number; warningDelayMs?: number; warningStartTime?: number; // Track when warning state started } export interface Alert { id: string; message: string; time: string; } interface BedPressureStore { // State sensorData: Record; sensorConfig: SensorConfig[]; selectedSensor: string | null; isModalOpen: boolean; isMonitoring: boolean; alerts: Alert[]; isConnected: boolean; selectedTimespan: number; // in milliseconds activeAlarms: AlarmEvent[]; alarmManager: AlarmManager; // Actions setSensorData: (data: Record) => void; updateSensorData: (updater: (prev: Record) => Record) => void; setSensorConfig: (config: SensorConfig[]) => void; setSelectedSensor: (sensorId: string | null) => void; setIsModalOpen: (isOpen: boolean) => void; setIsMonitoring: (isMonitoring: boolean) => void; addAlerts: (newAlerts: Alert[]) => void; setIsConnected: (isConnected: boolean) => void; setSelectedTimespan: (timespan: number) => void; setActiveAlarms: (alarms: AlarmEvent[]) => void; // Computed values averageValue: () => number; // Changed from averagePressure criticalSensors: () => number; // API actions fetchSensorConfig: () => Promise; fetchSensorData: () => Promise; fetchSensorHistory: (sensorId: string, timespan?: number) => Promise; // Alarm actions acknowledgeAlarm: (alarmId: string) => void; silenceAlarm: (alarmId: string, durationMs?: number) => void; silenceAllAlarms: (durationMs?: number) => void; } export const useBedPressureStore = create((set, get) => ({ // Initial state sensorData: {}, sensorConfig: [], selectedSensor: null, isModalOpen: false, isMonitoring: true, alerts: [], isConnected: false, selectedTimespan: 24 * 60 * 60 * 1000, // Default 24 hours activeAlarms: [], alarmManager: AlarmManager.getInstance(), // Actions setSensorData: (data) => set({ sensorData: data }), updateSensorData: (updater) => set((state) => ({ sensorData: updater(state.sensorData) })), setSensorConfig: (config) => set({ sensorConfig: config }), setSelectedSensor: (sensorId) => set({ selectedSensor: sensorId }), setIsModalOpen: (isOpen) => set({ isModalOpen: isOpen }), setIsMonitoring: (isMonitoring) => set({ isMonitoring }), addAlerts: (newAlerts) => set((state) => ({ alerts: [...newAlerts, ...state.alerts].slice(0, 10) })), setIsConnected: (isConnected) => set({ isConnected }), setSelectedTimespan: (timespan) => set({ selectedTimespan: timespan }), setActiveAlarms: (alarms) => set({ activeAlarms: alarms }), // Computed values averageValue: () => { const { sensorData } = get(); const sensors = Object.values(sensorData) as SensorData[]; // Only calculate average for sensors with hardware data const hardwareSensors = sensors.filter(sensor => sensor.source === 'hardware'); if (hardwareSensors.length === 0) return 0; return hardwareSensors.reduce((sum: number, sensor: SensorData) => sum + sensor.currentValue, 0) / hardwareSensors.length; }, criticalSensors: () => { const { sensorData } = get(); return (Object.values(sensorData) as SensorData[]).filter((sensor: SensorData) => sensor.status === 'alarm' || sensor.status === 'critical' ).length; }, // API actions fetchSensorConfig: async () => { try { const response = await fetch('/api/sensors/config'); const data = await response.json(); if (data.success && data.sensors) { set({ sensorConfig: data.sensors }); } } catch (error) { console.error('Failed to fetch sensor config:', error); } }, fetchSensorData: async () => { try { const response = await fetch('/api/sensors'); const data = await response.json(); if (data.success && data.sensors) { const { sensorData, sensorConfig, alarmManager } = get(); const updated = { ...sensorData }; const newAlerts: Alert[] = []; // Only process hardware sensors, no mock data data.sensors.forEach((sensor: { id: string; label: string; zone: string; value: number; source: 'hardware'; pin?: number; digitalState?: number; warningThreshold?: number; alarmThreshold?: number; warningDelayMs?: number; status?: string; }) => { // Only process hardware data if (sensor.source !== 'hardware') { return; } const currentSensor = updated[sensor.id]; const newValue = sensor.value; const config = sensorConfig.find(s => s.id === sensor.id); // Get thresholds from sensor data or config const warningThreshold = sensor.warningThreshold || config?.warningThreshold || 2500; const alarmThreshold = sensor.alarmThreshold || config?.alarmThreshold || 3500; const warningDelayMs = sensor.warningDelayMs || config?.warningDelayMs || 60000; // Determine status and handle alarm logic let status = 'normal'; let warningStartTime = currentSensor?.warningStartTime; const now = Date.now(); if (newValue >= alarmThreshold) { status = 'alarm'; warningStartTime = undefined; // Clear warning timer for immediate alarm // Add alarm alarmManager.addAlarm( sensor.id, sensor.label, 'alarm', newValue, alarmThreshold ); newAlerts.push({ id: `${sensor.id}-${Date.now()}`, message: `ALARM: High value detected at ${sensor.label} (${newValue})`, time: new Date().toLocaleTimeString(), }); } else if (newValue >= warningThreshold) { status = 'warning'; if (!warningStartTime) { warningStartTime = now; // Start warning timer } else if (now - warningStartTime >= warningDelayMs) { status = 'alarm'; // Escalate to alarm after delay // Add escalated alarm alarmManager.addAlarm( sensor.id, sensor.label, 'alarm', newValue, warningThreshold ); newAlerts.push({ id: `${sensor.id}-${Date.now()}`, message: `ALARM: Warning escalated for ${sensor.label} (${newValue})`, time: new Date().toLocaleTimeString(), }); } else { // Add warning alarm alarmManager.addAlarm( sensor.id, sensor.label, 'warning', newValue, warningThreshold ); } } else { warningStartTime = undefined; // Clear warning timer alarmManager.clearAlarm(sensor.id); // Clear any existing alarms } // Update sensor data updated[sensor.id] = { ...sensorConfig.find(s => s.id === sensor.id), id: sensor.id, x: sensorConfig.find(s => s.id === sensor.id)?.x || 50, y: sensorConfig.find(s => s.id === sensor.id)?.y || 50, zone: sensor.zone, label: sensor.label, currentValue: newValue, data: currentSensor ? [ ...currentSensor.data.slice(-100), // Keep only last 100 points { time: new Date().toLocaleTimeString("en-US", { hour12: false }), timestamp: Date.now(), value: newValue, }, ] : [{ time: new Date().toLocaleTimeString("en-US", { hour12: false }), timestamp: Date.now(), value: newValue, }], status, source: sensor.source, pin: sensor.pin, digitalState: sensor.digitalState, warningThreshold, alarmThreshold, warningDelayMs, warningStartTime }; }); set({ sensorData: updated, isConnected: data.connected || false, activeAlarms: alarmManager.getActiveAlarms() }); if (newAlerts.length > 0) { get().addAlerts(newAlerts); } } else { // No sensor data available - set as disconnected const { alarmManager } = get(); set({ isConnected: false, activeAlarms: alarmManager.getActiveAlarms() }); } } catch (error) { console.error('Failed to fetch sensor data:', error); // Set as disconnected on error const { alarmManager } = get(); set({ isConnected: false, activeAlarms: alarmManager.getActiveAlarms() }); } }, fetchSensorHistory: async (sensorId: string, timespan?: number) => { try { const { selectedTimespan } = get(); const timespanToUse = timespan || selectedTimespan; const response = await fetch(`/api/sensors/history?sensorId=${sensorId}×pan=${timespanToUse}`); const data = await response.json(); if (data.success && data.data) { const { sensorData } = get(); const updated = { ...sensorData }; if (updated[sensorId]) { updated[sensorId] = { ...updated[sensorId], data: data.data }; set({ sensorData: updated }); } } } catch (error) { console.error('Failed to fetch sensor history:', error); } }, // Alarm actions acknowledgeAlarm: (alarmId: string) => { const { alarmManager } = get(); alarmManager.acknowledgeAlarm(alarmId); set({ activeAlarms: alarmManager.getActiveAlarms() }); }, silenceAlarm: (alarmId: string, durationMs?: number) => { const { alarmManager } = get(); alarmManager.silenceAlarm(alarmId, durationMs); set({ activeAlarms: alarmManager.getActiveAlarms() }); }, silenceAllAlarms: (durationMs?: number) => { const { alarmManager } = get(); alarmManager.silenceAllAlarms(durationMs); set({ activeAlarms: alarmManager.getActiveAlarms() }); } }));