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