Commit | Line | Data |
---|---|---|
a10297ba | 1 | import { Protocol, ProtocolCommand, ProtocolRequest, ProtocolVersion } from '../types/UIProtocol'; |
6a49ad23 | 2 | import WebSocket, { OPEN, Server, ServerOptions } from 'ws'; |
4198ad5c | 3 | |
383cb2ae | 4 | import AbstractUIService from './ui-websocket-services/AbstractUIService'; |
4198ad5c | 5 | import BaseError from '../exception/BaseError'; |
6a49ad23 | 6 | import Configuration from '../utils/Configuration'; |
4198ad5c | 7 | import { IncomingMessage } from 'http'; |
383cb2ae | 8 | import UIServiceFactory from './ui-websocket-services/UIServiceFactory'; |
4198ad5c | 9 | import Utils from '../utils/Utils'; |
9f2e3130 | 10 | import logger from '../utils/Logger'; |
4198ad5c | 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 | ||
d1888640 | 26 | public broadcastToClients(message: string): void { |
4198ad5c | 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); | |
e7aeea18 JB |
37 | const version = socket.protocol.substring( |
38 | protocolIndex + Protocol.UI.length | |
39 | ) as ProtocolVersion; | |
de9136ae | 40 | if (!this.uiServices.has(version)) { |
e7aeea18 JB |
41 | throw new BaseError( |
42 | `Could not find a UI service implementation for UI protocol version ${version}` | |
43 | ); | |
4198ad5c JB |
44 | } |
45 | // FIXME: check connection validity | |
46 | socket.on('message', (messageData) => { | |
47 | let [command, payload]: ProtocolRequest = [ProtocolCommand.UNKNOWN, {}]; | |
48 | const protocolRequest = JSON.parse(messageData.toString()) as ProtocolRequest; | |
49 | if (Utils.isIterable(protocolRequest)) { | |
50 | [command, payload] = protocolRequest; | |
51 | } else { | |
410a760d | 52 | throw new BaseError('UI protocol request is not iterable'); |
4198ad5c | 53 | } |
e7aeea18 JB |
54 | this.uiServices |
55 | .get(version) | |
56 | .handleMessage(command, payload) | |
57 | .catch(() => { | |
58 | logger.error( | |
59 | `${this.logPrefix()} Error while handling command %s message: %j`, | |
60 | command, | |
61 | payload | |
62 | ); | |
63 | }); | |
4198ad5c JB |
64 | }); |
65 | socket.on('error', (error) => { | |
9f2e3130 | 66 | logger.error(`${this.logPrefix()} Error on WebSocket: %j`, error); |
4198ad5c JB |
67 | }); |
68 | }); | |
69 | } | |
70 | ||
71 | public stop(): void { | |
72 | this.close(); | |
73 | } | |
74 | ||
75 | public logPrefix(): string { | |
410a760d | 76 | return Utils.logPrefix(' UI WebSocket Server:'); |
4198ad5c JB |
77 | } |
78 | } |