- Added @elysiajs/swagger dependency to package.json for API documentation. - Removed the old bed router and replaced it with a new history router. - Created a new state router to manage WebSocket connections and state updates. - Implemented a comprehensive state management system with the StateManager service. - Introduced AlarmManagement and BedService services for handling alarms and sensor readings. - Established a new MQTT service for managing MQTT connections and subscriptions. - Created an AlarmStateStore to manage volatile alerts and their states. - Defined FrontendState types for structured state management and WebSocket messaging.
201 lines
No EOL
5.3 KiB
TypeScript
201 lines
No EOL
5.3 KiB
TypeScript
import { EventEmitter } from 'events';
|
|
import { AlertState } from '../types/FrontendState';
|
|
|
|
export interface VolatileAlert {
|
|
id: string;
|
|
measurementPointId: string;
|
|
sensorId: string;
|
|
type: 'WARNING' | 'ALARM';
|
|
value: number;
|
|
threshold: number;
|
|
acknowledged: boolean;
|
|
silenced: boolean;
|
|
startTime: Date;
|
|
sensorLabel: string;
|
|
zone: string;
|
|
}
|
|
|
|
export class AlarmStateStore extends EventEmitter {
|
|
private alerts: Map<string, VolatileAlert> = new Map();
|
|
private warningTimers: Map<string, NodeJS.Timeout> = new Map();
|
|
private alertIdCounter = 0;
|
|
|
|
// Generate unique alert ID
|
|
private generateAlertId(): string {
|
|
return `alert_${Date.now()}_${++this.alertIdCounter}`;
|
|
}
|
|
|
|
// Create a new alert
|
|
createAlert(
|
|
measurementPointId: string,
|
|
sensorId: string,
|
|
type: 'WARNING' | 'ALARM',
|
|
value: number,
|
|
threshold: number,
|
|
sensorLabel: string,
|
|
zone: string
|
|
): VolatileAlert {
|
|
// Check if there's already an active alert for this measurement point
|
|
const existingAlert = this.getAlertByMeasurementPointId(measurementPointId);
|
|
|
|
if (existingAlert) {
|
|
// If upgrading from WARNING to ALARM, update the existing alert
|
|
if (existingAlert.type === 'WARNING' && type === 'ALARM') {
|
|
existingAlert.type = 'ALARM';
|
|
existingAlert.value = value;
|
|
existingAlert.threshold = threshold;
|
|
|
|
this.emit('alertUpdated', existingAlert);
|
|
return existingAlert;
|
|
}
|
|
|
|
// If it's the same type or downgrading, return existing
|
|
return existingAlert;
|
|
}
|
|
|
|
const alert: VolatileAlert = {
|
|
id: this.generateAlertId(),
|
|
measurementPointId,
|
|
sensorId,
|
|
type,
|
|
value,
|
|
threshold,
|
|
acknowledged: false,
|
|
silenced: false,
|
|
startTime: new Date(),
|
|
sensorLabel,
|
|
zone
|
|
};
|
|
|
|
this.alerts.set(alert.id, alert);
|
|
this.emit('alertCreated', alert);
|
|
|
|
return alert;
|
|
}
|
|
|
|
// Get alert by measurement point ID
|
|
getAlertByMeasurementPointId(measurementPointId: string): VolatileAlert | undefined {
|
|
return Array.from(this.alerts.values())
|
|
.find(alert => alert.measurementPointId === measurementPointId);
|
|
}
|
|
|
|
// Remove alert
|
|
removeAlert(alertId: string): boolean {
|
|
const alert = this.alerts.get(alertId);
|
|
if (alert) {
|
|
this.alerts.delete(alertId);
|
|
this.emit('alertRemoved', alert);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Remove alerts by measurement point ID
|
|
removeAlertsByMeasurementPointId(measurementPointId: string): void {
|
|
const alertsToRemove = Array.from(this.alerts.values())
|
|
.filter(alert => alert.measurementPointId === measurementPointId);
|
|
|
|
alertsToRemove.forEach(alert => {
|
|
this.alerts.delete(alert.id);
|
|
this.emit('alertRemoved', alert);
|
|
});
|
|
}
|
|
|
|
// Acknowledge alert
|
|
acknowledgeAlert(alertId: string): boolean {
|
|
const alert = this.alerts.get(alertId);
|
|
if (alert) {
|
|
alert.acknowledged = true;
|
|
this.emit('alertUpdated', alert);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Silence alert
|
|
silenceAlert(alertId: string): boolean {
|
|
const alert = this.alerts.get(alertId);
|
|
if (alert) {
|
|
alert.silenced = true;
|
|
this.emit('alertUpdated', alert);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Set warning timer
|
|
setWarningTimer(measurementPointId: string, timer: NodeJS.Timeout): void {
|
|
// Clear existing timer if any
|
|
this.clearWarningTimer(measurementPointId);
|
|
this.warningTimers.set(measurementPointId, timer);
|
|
}
|
|
|
|
// Clear warning timer
|
|
clearWarningTimer(measurementPointId: string): void {
|
|
const timer = this.warningTimers.get(measurementPointId);
|
|
if (timer) {
|
|
clearTimeout(timer);
|
|
this.warningTimers.delete(measurementPointId);
|
|
}
|
|
}
|
|
|
|
// Get all active alerts
|
|
getAllAlerts(): VolatileAlert[] {
|
|
return Array.from(this.alerts.values());
|
|
}
|
|
|
|
// Get alert by ID
|
|
getAlert(alertId: string): VolatileAlert | undefined {
|
|
return this.alerts.get(alertId);
|
|
}
|
|
|
|
// Convert to AlertState format for frontend
|
|
toAlertState(alert: VolatileAlert): AlertState {
|
|
return {
|
|
id: alert.id,
|
|
measurementPointId: alert.measurementPointId,
|
|
type: alert.type,
|
|
value: alert.value,
|
|
threshold: alert.threshold,
|
|
acknowledged: alert.acknowledged,
|
|
silenced: alert.silenced,
|
|
startTime: alert.startTime,
|
|
endTime: undefined, // Volatile alerts don't have end times
|
|
sensorLabel: alert.sensorLabel,
|
|
zone: alert.zone
|
|
};
|
|
}
|
|
|
|
// Convert all alerts to AlertState format
|
|
toAlertStates(): Record<string, AlertState> {
|
|
const result: Record<string, AlertState> = {};
|
|
this.alerts.forEach((alert, id) => {
|
|
result[id] = this.toAlertState(alert);
|
|
});
|
|
return result;
|
|
}
|
|
|
|
// Clear all alerts
|
|
clearAll(): void {
|
|
// Clear all warning timers
|
|
this.warningTimers.forEach(timer => clearTimeout(timer));
|
|
this.warningTimers.clear();
|
|
|
|
// Clear all alerts
|
|
this.alerts.clear();
|
|
|
|
this.emit('allAlertsCleared');
|
|
}
|
|
|
|
// Get statistics
|
|
getStats() {
|
|
const alerts = this.getAllAlerts();
|
|
return {
|
|
total: alerts.length,
|
|
warnings: alerts.filter(a => a.type === 'WARNING').length,
|
|
alarms: alerts.filter(a => a.type === 'ALARM').length,
|
|
acknowledged: alerts.filter(a => a.acknowledged).length,
|
|
silenced: alerts.filter(a => a.silenced).length
|
|
};
|
|
}
|
|
} |