m2-inno-bedpressure/stores/bedPressureStore.ts
2025-06-21 13:29:01 +07:00

320 lines
No EOL
10 KiB
TypeScript

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' | 'mock';
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<string, SensorData>;
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<string, SensorData>) => void;
updateSensorData: (updater: (prev: Record<string, SensorData>) => Record<string, SensorData>) => 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<void>;
fetchSensorData: () => Promise<void>;
fetchSensorHistory: (sensorId: string, timespan?: number) => Promise<void>;
// Alarm actions
acknowledgeAlarm: (alarmId: string) => void;
silenceAlarm: (alarmId: string, durationMs?: number) => void;
silenceAllAlarms: (durationMs?: number) => void;
}
export const useBedPressureStore = create<BedPressureStore>((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[];
if (sensors.length === 0) return 0;
return sensors.reduce((sum: number, sensor: SensorData) => sum + sensor.currentValue, 0) / sensors.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[] = [];
data.sensors.forEach((sensor: {
id: string;
label: string;
zone: string;
value: number; // Changed from pressure to value
source: 'hardware' | 'mock';
pin?: number;
digitalState?: number;
warningThreshold?: number;
alarmThreshold?: number;
warningDelayMs?: number;
status?: string;
}) => {
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(1),
{
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,
activeAlarms: alarmManager.getActiveAlarms()
});
if (newAlerts.length > 0) {
get().addAlerts(newAlerts);
}
}
} catch (error) {
console.error('Failed to fetch sensor data:', error);
}
},
fetchSensorHistory: async (sensorId: string, timespan?: number) => {
try {
const { selectedTimespan } = get();
const timespanToUse = timespan || selectedTimespan;
const response = await fetch(`/api/sensors/history?sensorId=${sensorId}&timespan=${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() });
}
}));