UI server: comment and code cleanups
[e-mobility-charging-stations-simulator.git] / src / charging-station / ui-server / ui-services / AbstractUIService.ts
1 import { RawData } from 'ws';
2
3 import BaseError from '../../../exception/BaseError';
4 import { Bootstrap } from '../../../internal';
5 import { JsonType } from '../../../types/JsonType';
6 import {
7 ProcedureName,
8 ProtocolRequest,
9 ProtocolRequestHandler,
10 ProtocolResponse,
11 ProtocolVersion,
12 RequestPayload,
13 ResponsePayload,
14 ResponseStatus,
15 } from '../../../types/UIProtocol';
16 import logger from '../../../utils/Logger';
17 import UIServiceWorkerBroadcastChannel from '../../UIServiceWorkerBroadcastChannel';
18 import type { AbstractUIServer } from '../AbstractUIServer';
19
20 const moduleName = 'AbstractUIService';
21
22 export default abstract class AbstractUIService {
23 protected readonly version: ProtocolVersion;
24 protected readonly uiServer: AbstractUIServer;
25 protected readonly requestHandlers: Map<ProcedureName, ProtocolRequestHandler>;
26 protected uiServiceWorkerBroadcastChannel: UIServiceWorkerBroadcastChannel;
27
28 constructor(uiServer: AbstractUIServer, version: ProtocolVersion) {
29 this.version = version;
30 this.uiServer = uiServer;
31 this.requestHandlers = new Map<ProcedureName, ProtocolRequestHandler>([
32 [ProcedureName.LIST_CHARGING_STATIONS, this.handleListChargingStations.bind(this)],
33 [ProcedureName.START_SIMULATOR, this.handleStartSimulator.bind(this)],
34 [ProcedureName.STOP_SIMULATOR, this.handleStopSimulator.bind(this)],
35 ]);
36 this.uiServiceWorkerBroadcastChannel = new UIServiceWorkerBroadcastChannel(this);
37 }
38
39 public async requestHandler(request: RawData | JsonType): Promise<void> {
40 let messageId: string;
41 let command: ProcedureName;
42 let requestPayload: RequestPayload | undefined;
43 let responsePayload: ResponsePayload;
44 try {
45 [messageId, command, requestPayload] = this.requestValidation(request);
46
47 if (this.requestHandlers.has(command) === false) {
48 throw new BaseError(
49 `${command} is not implemented to handle message payload ${JSON.stringify(
50 requestPayload,
51 null,
52 2
53 )}`
54 );
55 }
56
57 // Call the request handler to build the response payload
58 responsePayload = await this.requestHandlers.get(command)(messageId, requestPayload);
59 } catch (error) {
60 // Log
61 logger.error(`${this.logPrefix(moduleName, 'messageHandler')} Handle request error:`, error);
62 responsePayload = {
63 status: ResponseStatus.FAILURE,
64 command,
65 requestPayload,
66 responsePayload,
67 errorMessage: (error as Error).message,
68 errorStack: (error as Error).stack,
69 };
70 }
71
72 if (responsePayload !== undefined) {
73 // Send the response
74 this.sendResponse(messageId ?? 'error', responsePayload);
75 }
76 }
77
78 public sendRequest(
79 messageId: string,
80 procedureName: ProcedureName,
81 requestPayload: RequestPayload
82 ): void {
83 this.uiServer.sendRequest(this.buildProtocolRequest(messageId, procedureName, requestPayload));
84 }
85
86 public sendResponse(messageId: string, responsePayload: ResponsePayload): void {
87 this.uiServer.sendResponse(this.buildProtocolResponse(messageId, responsePayload));
88 }
89
90 public logPrefix(modName: string, methodName: string): string {
91 return this.uiServer.logPrefix(modName, methodName);
92 }
93
94 private buildProtocolRequest(
95 messageId: string,
96 procedureName: ProcedureName,
97 requestPayload: RequestPayload
98 ): string {
99 return JSON.stringify([messageId, procedureName, requestPayload] as ProtocolRequest);
100 }
101
102 private buildProtocolResponse(messageId: string, responsePayload: ResponsePayload): string {
103 return JSON.stringify([messageId, responsePayload] as ProtocolResponse);
104 }
105
106 // Validate the raw data received from the UI server
107 private requestValidation(rawData: RawData | JsonType): ProtocolRequest {
108 // logger.debug(
109 // `${this.logPrefix(
110 // moduleName,
111 // 'requestValidation'
112 // )} Data received in string format: ${rawData.toString()}`
113 // );
114
115 const data = JSON.parse(rawData.toString()) as JsonType[];
116
117 if (Array.isArray(data) === false) {
118 throw new BaseError('UI protocol request is not an array');
119 }
120
121 if (data.length !== 3) {
122 throw new BaseError('UI protocol request is malformed');
123 }
124
125 return data as ProtocolRequest;
126 }
127
128 private handleListChargingStations(): ResponsePayload {
129 // TODO: remove cast to unknown
130 return {
131 status: ResponseStatus.SUCCESS,
132 ...Array.from(this.uiServer.chargingStations.values()),
133 } as unknown as ResponsePayload;
134 }
135
136 private async handleStartSimulator(): Promise<ResponsePayload> {
137 await Bootstrap.getInstance().start();
138 return { status: ResponseStatus.SUCCESS };
139 }
140
141 private async handleStopSimulator(): Promise<ResponsePayload> {
142 await Bootstrap.getInstance().stop();
143 return { status: ResponseStatus.SUCCESS };
144 }
145 }