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