1 import { BaseError
, type OCPPError
} from
'../../../exception';
3 BroadcastChannelProcedureName
,
4 type BroadcastChannelRequestPayload
,
7 type ProtocolRequestHandler
,
13 } from
'../../../types';
14 import { isNotEmptyArray
, isNullOrUndefined
, logger
} from
'../../../utils';
15 import { Bootstrap
} from
'../../Bootstrap';
16 import { UIServiceWorkerBroadcastChannel
} from
'../../broadcast-channel/UIServiceWorkerBroadcastChannel';
17 import type { AbstractUIServer
} from
'../AbstractUIServer';
19 const moduleName
= 'AbstractUIService';
21 export abstract class AbstractUIService
{
22 protected static readonly ProcedureNameToBroadCastChannelProcedureNameMapping
: Omit
<
23 Record
<ProcedureName
, BroadcastChannelProcedureName
>,
24 | ProcedureName
.START_SIMULATOR
25 | ProcedureName
.STOP_SIMULATOR
26 | ProcedureName
.LIST_CHARGING_STATIONS
28 [ProcedureName
.START_CHARGING_STATION
]: BroadcastChannelProcedureName
.START_CHARGING_STATION
,
29 [ProcedureName
.STOP_CHARGING_STATION
]: BroadcastChannelProcedureName
.STOP_CHARGING_STATION
,
30 [ProcedureName
.CLOSE_CONNECTION
]: BroadcastChannelProcedureName
.CLOSE_CONNECTION
,
31 [ProcedureName
.OPEN_CONNECTION
]: BroadcastChannelProcedureName
.OPEN_CONNECTION
,
32 [ProcedureName
.START_AUTOMATIC_TRANSACTION_GENERATOR
]:
33 BroadcastChannelProcedureName
.START_AUTOMATIC_TRANSACTION_GENERATOR
,
34 [ProcedureName
.STOP_AUTOMATIC_TRANSACTION_GENERATOR
]:
35 BroadcastChannelProcedureName
.STOP_AUTOMATIC_TRANSACTION_GENERATOR
,
36 [ProcedureName
.SET_SUPERVISION_URL
]: BroadcastChannelProcedureName
.SET_SUPERVISION_URL
,
37 [ProcedureName
.START_TRANSACTION
]: BroadcastChannelProcedureName
.START_TRANSACTION
,
38 [ProcedureName
.STOP_TRANSACTION
]: BroadcastChannelProcedureName
.STOP_TRANSACTION
,
39 [ProcedureName
.AUTHORIZE
]: BroadcastChannelProcedureName
.AUTHORIZE
,
40 [ProcedureName
.BOOT_NOTIFICATION
]: BroadcastChannelProcedureName
.BOOT_NOTIFICATION
,
41 [ProcedureName
.STATUS_NOTIFICATION
]: BroadcastChannelProcedureName
.STATUS_NOTIFICATION
,
42 [ProcedureName
.HEARTBEAT
]: BroadcastChannelProcedureName
.HEARTBEAT
,
43 [ProcedureName
.METER_VALUES
]: BroadcastChannelProcedureName
.METER_VALUES
,
44 [ProcedureName
.DATA_TRANSFER
]: BroadcastChannelProcedureName
.DATA_TRANSFER
,
45 [ProcedureName
.DIAGNOSTICS_STATUS_NOTIFICATION
]:
46 BroadcastChannelProcedureName
.DIAGNOSTICS_STATUS_NOTIFICATION
,
47 [ProcedureName
.FIRMWARE_STATUS_NOTIFICATION
]:
48 BroadcastChannelProcedureName
.FIRMWARE_STATUS_NOTIFICATION
,
51 protected readonly requestHandlers
: Map
<ProcedureName
, ProtocolRequestHandler
>;
52 private readonly version
: ProtocolVersion
;
53 private readonly uiServer
: AbstractUIServer
;
54 private readonly uiServiceWorkerBroadcastChannel
: UIServiceWorkerBroadcastChannel
;
55 private readonly broadcastChannelRequests
: Map
<string, number>;
57 constructor(uiServer
: AbstractUIServer
, version
: ProtocolVersion
) {
58 this.uiServer
= uiServer
;
59 this.version
= version
;
60 this.requestHandlers
= new Map
<ProcedureName
, ProtocolRequestHandler
>([
61 [ProcedureName
.LIST_CHARGING_STATIONS
, this.handleListChargingStations
.bind(this)],
62 [ProcedureName
.START_SIMULATOR
, this.handleStartSimulator
.bind(this)],
63 [ProcedureName
.STOP_SIMULATOR
, this.handleStopSimulator
.bind(this)],
65 this.uiServiceWorkerBroadcastChannel
= new UIServiceWorkerBroadcastChannel(this);
66 this.broadcastChannelRequests
= new Map
<string, number>();
69 public async requestHandler(request
: ProtocolRequest
): Promise
<ProtocolResponse
| undefined> {
70 let messageId
: string;
71 let command
: ProcedureName
;
72 let requestPayload
: RequestPayload
| undefined;
73 let responsePayload
: ResponsePayload
| undefined;
75 [messageId
, command
, requestPayload
] = request
;
77 if (this.requestHandlers
.has(command
) === false) {
79 `${command} is not implemented to handle message payload ${JSON.stringify(
87 // Call the request handler to build the response payload
88 responsePayload
= await this.requestHandlers
.get(command
)(messageId
, command
, requestPayload
);
91 logger
.error(`${this.logPrefix(moduleName, 'requestHandler')} Handle request error:`, error
);
93 hashIds
: requestPayload
?.hashIds
,
94 status: ResponseStatus
.FAILURE
,
98 errorMessage
: (error
as Error).message
,
99 errorStack
: (error
as Error).stack
,
100 errorDetails
: (error
as OCPPError
).details
,
103 if (!isNullOrUndefined(responsePayload
)) {
104 return this.uiServer
.buildProtocolResponse(messageId
, responsePayload
);
108 // public sendRequest(
109 // messageId: string,
110 // procedureName: ProcedureName,
111 // requestPayload: RequestPayload
113 // this.uiServer.sendRequest(
114 // this.uiServer.buildProtocolRequest(messageId, procedureName, requestPayload)
118 public sendResponse(messageId
: string, responsePayload
: ResponsePayload
): void {
119 if (this.uiServer
.hasResponseHandler(messageId
)) {
120 this.uiServer
.sendResponse(this.uiServer
.buildProtocolResponse(messageId
, responsePayload
));
124 public logPrefix
= (modName
: string, methodName
: string): string => {
125 return this.uiServer
.logPrefix(modName
, methodName
, this.version
);
128 public deleteBroadcastChannelRequest(uuid
: string): void {
129 this.broadcastChannelRequests
.delete(uuid
);
132 public getBroadcastChannelExpectedResponses(uuid
: string): number {
133 return this.broadcastChannelRequests
.get(uuid
) ?? 0;
136 protected handleProtocolRequest(
138 procedureName
: ProcedureName
,
139 payload
: RequestPayload
,
141 this.sendBroadcastChannelRequest(
143 AbstractUIService
.ProcedureNameToBroadCastChannelProcedureNameMapping
[
145 ] as BroadcastChannelProcedureName
,
150 private sendBroadcastChannelRequest(
152 procedureName
: BroadcastChannelProcedureName
,
153 payload
: BroadcastChannelRequestPayload
,
155 if (isNotEmptyArray(payload
.hashIds
)) {
156 payload
.hashIds
= payload
.hashIds
157 .filter((hashId
) => !isNullOrUndefined(hashId
))
159 if (this.uiServer
.chargingStations
.has(hashId
) === true) {
165 'sendBroadcastChannelRequest',
166 )} Charging station with hashId '${hashId}' not found`,
170 const expectedNumberOfResponses
= isNotEmptyArray(payload
.hashIds
)
171 ? payload
.hashIds
.length
172 : this.uiServer
.chargingStations
.size
;
173 this.uiServiceWorkerBroadcastChannel
.sendRequest([uuid
, procedureName
, payload
]);
174 this.broadcastChannelRequests
.set(uuid
, expectedNumberOfResponses
);
177 private handleListChargingStations(): ResponsePayload
{
179 status: ResponseStatus
.SUCCESS
,
180 chargingStations
: [...this.uiServer
.chargingStations
.values()],
181 } as ResponsePayload
;
184 private async handleStartSimulator(): Promise
<ResponsePayload
> {
186 await Bootstrap
.getInstance().start();
187 return { status: ResponseStatus
.SUCCESS
};
189 return { status: ResponseStatus
.FAILURE
};
193 private async handleStopSimulator(): Promise
<ResponsePayload
> {
195 await Bootstrap
.getInstance().stop();
196 return { status: ResponseStatus
.SUCCESS
};
198 return { status: ResponseStatus
.FAILURE
};