Strong type protocols payloads
[e-mobility-charging-stations-simulator.git] / src / charging-station / UIWebSocketServer.ts
1 import { Protocol, ProtocolCommand, ProtocolRequest, ProtocolVersion } from '../types/UIProtocol';
2 import WebSocket, { OPEN, Server, ServerOptions } from 'ws';
3
4 import AbstractUIService from './ui-websocket-services/AbstractUIService';
5 import BaseError from '../exception/BaseError';
6 import Configuration from '../utils/Configuration';
7 import { IncomingMessage } from 'http';
8 import { JsonType } from '../types/JsonType';
9 import UIServiceFactory from './ui-websocket-services/UIServiceFactory';
10 import Utils from '../utils/Utils';
11 import logger from '../utils/Logger';
12
13 export default class UIWebSocketServer extends Server {
14 public readonly chargingStations: Set<string>;
15 public readonly uiServices: Map<ProtocolVersion, AbstractUIService>;
16
17 public constructor(options?: ServerOptions, callback?: () => void) {
18 // Create the WebSocket Server
19 super(options ?? Configuration.getUIWebSocketServer().options, callback);
20 this.chargingStations = new Set<string>();
21 this.uiServices = new Map<ProtocolVersion, AbstractUIService>();
22 for (const version of Object.values(ProtocolVersion)) {
23 this.uiServices.set(version, UIServiceFactory.getUIServiceImplementation(version, this));
24 }
25 }
26
27 public broadcastToClients(message: string): void {
28 for (const client of this.clients) {
29 if (client?.readyState === OPEN) {
30 client.send(message);
31 }
32 }
33 }
34
35 public start(): void {
36 this.on('connection', (socket: WebSocket, request: IncomingMessage): void => {
37 const protocolIndex = socket.protocol.indexOf(Protocol.UI);
38 const version = socket.protocol.substring(protocolIndex + Protocol.UI.length) as ProtocolVersion;
39 if (!this.uiServices.has(version)) {
40 throw new BaseError(`Could not find a UI service implementation for UI protocol version ${version}`);
41 }
42 // FIXME: check connection validity
43 socket.on('message', (messageData) => {
44 let [command, payload]: ProtocolRequest = [ProtocolCommand.UNKNOWN, {}];
45 const protocolRequest = JSON.parse(messageData.toString()) as ProtocolRequest;
46 if (Utils.isIterable(protocolRequest)) {
47 [command, payload] = protocolRequest;
48 } else {
49 throw new BaseError('UI protocol request is not iterable');
50 }
51 this.uiServices.get(version).handleMessage(command, payload).catch(() => {
52 logger.error(`${this.logPrefix()} Error while handling command %s message: %j`, command, payload);
53 });
54 });
55 socket.on('error', (error) => {
56 logger.error(`${this.logPrefix()} Error on WebSocket: %j`, error);
57 });
58 });
59 }
60
61 public stop(): void {
62 this.close();
63 }
64
65 public logPrefix(): string {
66 return Utils.logPrefix(' UI WebSocket Server:');
67 }
68 }