feat: Add Swagger documentation support and restructure routes

- 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.
This commit is contained in:
Siwat Sirichai 2025-06-21 18:56:34 +07:00
parent a767dc3635
commit 4ae5196ef1
12 changed files with 1189 additions and 1 deletions

148
services/AlarmManagement.ts Normal file
View file

@ -0,0 +1,148 @@
import { EventEmitter } from 'events';
import { AlarmStateStore, VolatileAlert } from '../store/AlarmStateStore';
import { MeasurementPointState } from '../types/FrontendState';
export class AlarmManagement extends EventEmitter {
private alarmStore: AlarmStateStore;
private measurementPoints: Map<string, MeasurementPointState> = new Map();
constructor() {
super();
this.alarmStore = new AlarmStateStore();
this.setupEventListeners();
}
private setupEventListeners(): void {
// Forward alarm store events
this.alarmStore.on('alertCreated', (alert: VolatileAlert) => {
this.emit('alertCreated', alert);
});
this.alarmStore.on('alertUpdated', (alert: VolatileAlert) => {
this.emit('alertUpdated', alert);
});
this.alarmStore.on('alertRemoved', (alert: VolatileAlert) => {
this.emit('alertRemoved', alert);
});
}
// Update measurement points for reference
updateMeasurementPoints(measurementPoints: Record<string, MeasurementPointState>): void {
this.measurementPoints.clear();
Object.values(measurementPoints).forEach(mp => {
this.measurementPoints.set(mp.id, mp);
});
}
// Process sensor reading and check for alerts
processSensorReading(sensorId: string, value: number, timestamp: Date): void {
// Find measurement point by sensorId
const measurementPoint = Array.from(this.measurementPoints.values())
.find(mp => mp.sensorId === sensorId);
if (!measurementPoint) {
console.warn(`No measurement point found for sensor: ${sensorId}`);
return;
}
this.checkAlerts(measurementPoint, value);
}
private checkAlerts(measurementPoint: MeasurementPointState, value: number): void {
const { id: pointId, warningThreshold, alarmThreshold, warningDelayMs, label, zone } = measurementPoint;
// Check if value exceeds alarm threshold (immediate alarm)
if (value >= alarmThreshold) {
this.alarmStore.clearWarningTimer(pointId);
this.alarmStore.createAlert(
pointId,
measurementPoint.sensorId,
'ALARM',
value,
alarmThreshold,
label,
zone
);
return;
}
// Check if value exceeds warning threshold
if (value >= warningThreshold) {
const existingAlert = this.alarmStore.getAlertByMeasurementPointId(pointId);
if (!existingAlert) {
// Create warning alert
this.alarmStore.createAlert(
pointId,
measurementPoint.sensorId,
'WARNING',
value,
warningThreshold,
label,
zone
);
// Set timer for warning to escalate to alarm
const timer = setTimeout(() => {
this.alarmStore.createAlert(
pointId,
measurementPoint.sensorId,
'ALARM',
value,
warningThreshold,
label,
zone
);
}, warningDelayMs);
this.alarmStore.setWarningTimer(pointId, timer);
}
} else {
// Value is below warning threshold, clear any alerts for this point
this.alarmStore.clearWarningTimer(pointId);
this.alarmStore.removeAlertsByMeasurementPointId(pointId);
}
}
// Get all active alerts
getActiveAlerts(): VolatileAlert[] {
return this.alarmStore.getAllAlerts();
}
// Get alerts in frontend state format
getAlertStates(): Record<string, import('../types/FrontendState').AlertState> {
return this.alarmStore.toAlertStates();
}
// Acknowledge alert
acknowledgeAlert(alertId: string): boolean {
return this.alarmStore.acknowledgeAlert(alertId);
}
// Silence alert
silenceAlert(alertId: string): boolean {
return this.alarmStore.silenceAlert(alertId);
}
// Get alert by ID
getAlert(alertId: string): VolatileAlert | undefined {
return this.alarmStore.getAlert(alertId);
}
// Get statistics
getStats() {
return this.alarmStore.getStats();
}
// Clear all alerts (for testing/reset)
clearAllAlerts(): void {
this.alarmStore.clearAll();
}
// Cleanup
cleanup(): void {
this.alarmStore.clearAll();
this.removeAllListeners();
}
}