m2-inno-bedpressure/routes/state.ts
Siwat Sirichai 4ae5196ef1 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.
2025-06-21 18:56:34 +07:00

172 lines
No EOL
4.4 KiB
TypeScript

import { Elysia, t } from "elysia";
import { StateManager } from '../services/StateManager';
import { BedService } from '../services/BedService';
import { WebSocketMessage, StateUpdateEvent } from '../types/FrontendState';
// Define WebSocket client type
interface WSClient {
id: string;
send: (message: string) => void;
readyState: number;
}
// Singleton instances
let stateManager: StateManager | null = null;
let bedService: BedService | null = null;
const clients = new Map<string, WSClient>();
// Initialize services
async function initializeServices() {
if (!bedService) {
bedService = new BedService();
await bedService.initialize();
}
if (!stateManager) {
stateManager = new StateManager(bedService);
await stateManager.initializeState();
// Listen for state updates and broadcast to clients
stateManager.on('stateUpdate', (event: StateUpdateEvent) => {
broadcastStateUpdate(event);
});
}
}
// Broadcast state updates to all connected clients
function broadcastStateUpdate(event: StateUpdateEvent) {
const message: WebSocketMessage = {
type: 'STATE_UPDATE',
payload: event,
timestamp: new Date()
};
const messageStr = JSON.stringify(message);
const deadClients: string[] = [];
clients.forEach((ws, id) => {
try {
if (ws.readyState === 1) { // WebSocket.OPEN
ws.send(messageStr);
} else {
deadClients.push(id);
}
} catch (error) {
console.error('Failed to send message to client:', error);
deadClients.push(id);
}
});
// Clean up dead clients
deadClients.forEach(id => {
clients.delete(id);
});
// Update connection count
if (stateManager) {
stateManager.updateConnectionCount(clients.size);
}
}
// Send current state to a specific client
async function sendCurrentState(ws: WSClient) {
try {
await initializeServices();
if (stateManager) {
const state = stateManager.getState();
const message: WebSocketMessage = {
type: 'STATE_UPDATE',
payload: {
type: 'FULL_STATE',
timestamp: new Date(),
data: state
},
timestamp: new Date()
};
ws.send(JSON.stringify(message));
}
} catch (error) {
console.error('Failed to send current state:', error);
}
}
const stateRouter = new Elysia()
.ws('/ws', {
body: t.Object({
type: t.Union([
t.Literal('ACKNOWLEDGE_ALERT'),
t.Literal('SILENCE_ALERT'),
t.Literal('HEARTBEAT')
]),
payload: t.Optional(t.Object({
alertId: t.Optional(t.String())
}))
}),
open: async (ws) => {
console.log('WebSocket client connected:', ws.id);
clients.set(ws.id, ws);
// Send current state to new client
await sendCurrentState(ws);
// Update connection count
await initializeServices();
if (stateManager) {
stateManager.updateConnectionCount(clients.size);
}
},
message: async (ws, message) => {
try {
await initializeServices();
switch (message.type) {
case 'ACKNOWLEDGE_ALERT':
if (message.payload?.alertId && stateManager) {
await stateManager.acknowledgeAlert(message.payload.alertId);
}
break;
case 'SILENCE_ALERT':
if (message.payload?.alertId && stateManager) {
await stateManager.silenceAlert(message.payload.alertId);
}
break;
case 'HEARTBEAT':
// Respond to heartbeat
ws.send(JSON.stringify({
type: 'HEARTBEAT',
payload: { message: 'pong' },
timestamp: new Date()
}));
break;
default:
console.warn('Unknown WebSocket message type:', message.type);
}
} catch (error) {
console.error('Error handling client message:', error);
ws.send(JSON.stringify({
type: 'ERROR',
payload: { message: 'Invalid message format' },
timestamp: new Date()
}));
}
},
close: (ws) => {
console.log('WebSocket client disconnected:', ws.id);
clients.delete(ws.id);
// Update connection count
if (stateManager) {
stateManager.updateConnectionCount(clients.size);
}
}
});
export default stateRouter;