1 import type { RawData
} from
'ws';
3 import BaseError from
'../../../exception/BaseError';
4 import { Bootstrap
} from
'../../../internal';
5 import type { JsonType
} from
'../../../types/JsonType';
9 ProtocolRequestHandler
,
14 } from
'../../../types/UIProtocol';
16 BroadcastChannelProcedureName
,
17 BroadcastChannelRequestPayload
,
18 } from
'../../../types/WorkerBroadcastChannel';
19 import logger from
'../../../utils/Logger';
20 import Utils from
'../../../utils/Utils';
21 import UIServiceWorkerBroadcastChannel from
'../../UIServiceWorkerBroadcastChannel';
22 import type { AbstractUIServer
} from
'../AbstractUIServer';
24 const moduleName
= 'AbstractUIService';
26 export default abstract class AbstractUIService
{
27 protected readonly requestHandlers
: Map
<ProcedureName
, ProtocolRequestHandler
>;
28 private readonly version
: ProtocolVersion
;
29 private readonly uiServer
: AbstractUIServer
;
30 private readonly uiServiceWorkerBroadcastChannel
: UIServiceWorkerBroadcastChannel
;
31 private readonly broadcastChannelRequests
: Map
<string, number>;
33 constructor(uiServer
: AbstractUIServer
, version
: ProtocolVersion
) {
34 this.version
= version
;
35 this.uiServer
= uiServer
;
36 this.requestHandlers
= new Map
<ProcedureName
, ProtocolRequestHandler
>([
37 [ProcedureName
.LIST_CHARGING_STATIONS
, this.handleListChargingStations
.bind(this)],
38 [ProcedureName
.START_SIMULATOR
, this.handleStartSimulator
.bind(this)],
39 [ProcedureName
.STOP_SIMULATOR
, this.handleStopSimulator
.bind(this)],
41 this.uiServiceWorkerBroadcastChannel
= new UIServiceWorkerBroadcastChannel(this);
42 this.broadcastChannelRequests
= new Map
<string, number>();
45 public async requestHandler(request
: RawData
| JsonType
): Promise
<void> {
46 let messageId
: string;
47 let command
: ProcedureName
;
48 let requestPayload
: RequestPayload
| undefined;
49 let responsePayload
: ResponsePayload
;
51 [messageId
, command
, requestPayload
] = this.requestValidation(request
);
53 if (this.requestHandlers
.has(command
) === false) {
55 `${command} is not implemented to handle message payload ${JSON.stringify(
63 // Call the request handler to build the response payload
64 responsePayload
= await this.requestHandlers
.get(command
)(messageId
, requestPayload
);
67 logger
.error(`${this.logPrefix(moduleName, 'messageHandler')} Handle request error:`, error
);
69 status: ResponseStatus
.FAILURE
,
73 errorMessage
: (error
as Error).message
,
74 errorStack
: (error
as Error).stack
,
77 // Send response for payload not forwarded to broadcast channel
78 if (responsePayload
!== undefined) {
79 this.sendResponse(messageId
?? 'error', responsePayload
);
85 procedureName
: ProcedureName
,
86 requestPayload
: RequestPayload
88 this.uiServer
.sendRequest(
89 this.uiServer
.buildProtocolRequest(messageId
, procedureName
, requestPayload
)
93 public sendResponse(messageId
: string, responsePayload
: ResponsePayload
): void {
94 this.uiServer
.sendResponse(this.uiServer
.buildProtocolResponse(messageId
, responsePayload
));
97 public logPrefix(modName
: string, methodName
: string): string {
98 return this.uiServer
.logPrefix(modName
, methodName
, this.version
);
101 public deleteBroadcastChannelRequest(uuid
: string): void {
102 this.broadcastChannelRequests
.delete(uuid
);
105 public getBroadcastChannelExpectedResponses(uuid
: string): number {
106 return this.broadcastChannelRequests
.get(uuid
) ?? 0;
109 protected sendBroadcastChannelRequest(
111 procedureName
: BroadcastChannelProcedureName
,
112 payload
: BroadcastChannelRequestPayload
114 if (!Utils
.isEmptyArray(payload
.hashIds
)) {
115 payload
.hashIds
= payload
.hashIds
117 if (this.uiServer
.chargingStations
.has(hashId
) === true) {
123 'sendBroadcastChannelRequest'
124 )} Charging station with hashId '${hashId}' not found`
127 .filter((hashId
) => hashId
!== undefined);
129 const expectedNumberOfResponses
= !Utils
.isEmptyArray(payload
.hashIds
)
130 ? payload
.hashIds
.length
131 : this.uiServer
.chargingStations
.size
;
132 this.uiServiceWorkerBroadcastChannel
.sendRequest([uuid
, procedureName
, payload
]);
133 this.broadcastChannelRequests
.set(uuid
, expectedNumberOfResponses
);
136 // Validate the raw data received from the UI server
137 private requestValidation(rawData
: RawData
| JsonType
): ProtocolRequest
{
139 // `${this.logPrefix(
141 // 'requestValidation'
142 // )} Data received in string format: ${rawData.toString()}`
145 const data
= JSON
.parse(rawData
.toString()) as JsonType
[];
147 if (Array.isArray(data
) === false) {
148 throw new BaseError('UI protocol request is not an array');
151 if (data
.length
!== 3) {
152 throw new BaseError('UI protocol request is malformed');
155 return data
as ProtocolRequest
;
158 private handleListChargingStations(): ResponsePayload
{
159 // TODO: remove cast to unknown
161 status: ResponseStatus
.SUCCESS
,
162 ...[...this.uiServer
.chargingStations
.values()],
163 } as unknown
as ResponsePayload
;
166 private async handleStartSimulator(): Promise
<ResponsePayload
> {
167 await Bootstrap
.getInstance().start();
168 return { status: ResponseStatus
.SUCCESS
};
171 private async handleStopSimulator(): Promise
<ResponsePayload
> {
172 await Bootstrap
.getInstance().stop();
173 return { status: ResponseStatus
.SUCCESS
};