dynamic graph
This commit is contained in:
parent
a606796d9e
commit
5e029ff99c
17 changed files with 1707 additions and 569 deletions
275
stores/bedPressureStore.ts
Normal file
275
stores/bedPressureStore.ts
Normal file
|
@ -0,0 +1,275 @@
|
|||
import { create } from 'zustand'
|
||||
import { AlarmManager, AlarmEvent } from '@/services/AlarmManager'
|
||||
|
||||
export interface SensorConfig {
|
||||
id: string;
|
||||
x: number;
|
||||
y: number;
|
||||
zone: string;
|
||||
label: string;
|
||||
pin?: number;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
status?: string;
|
||||
}) => {
|
||||
const currentSensor = updated[sensor.id];
|
||||
const newValue = sensor.value;
|
||||
|
||||
// Check for alarms based on thresholds
|
||||
if (sensor.alarmThreshold && newValue >= sensor.alarmThreshold) {
|
||||
alarmManager.addAlarm(sensor.id, sensor.label, 'alarm', newValue, sensor.alarmThreshold);
|
||||
} else if (sensor.warningThreshold && newValue >= sensor.warningThreshold) {
|
||||
alarmManager.addAlarm(sensor.id, sensor.label, 'warning', newValue, sensor.warningThreshold);
|
||||
} else {
|
||||
alarmManager.clearAlarm(sensor.id);
|
||||
}
|
||||
|
||||
// Check for alerts (legacy alert system)
|
||||
if (newValue > 3000 && currentSensor && currentSensor.currentValue <= 3000) {
|
||||
newAlerts.push({
|
||||
id: `${sensor.id}-${Date.now()}`,
|
||||
message: `High value detected at ${sensor.label}`,
|
||||
time: new Date().toLocaleTimeString(),
|
||||
});
|
||||
}
|
||||
|
||||
// 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: sensor.status || (newValue > 3000 ? "critical" : newValue > 2500 ? "warning" : "normal"),
|
||||
source: sensor.source,
|
||||
pin: sensor.pin,
|
||||
digitalState: sensor.digitalState,
|
||||
warningThreshold: sensor.warningThreshold,
|
||||
alarmThreshold: sensor.alarmThreshold
|
||||
};
|
||||
});
|
||||
|
||||
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}×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() });
|
||||
}
|
||||
}));
|
Loading…
Add table
Add a link
Reference in a new issue