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
= new Map
<
24 BroadcastChannelProcedureName
26 [ProcedureName
.START_CHARGING_STATION
, BroadcastChannelProcedureName
.START_CHARGING_STATION
],
27 [ProcedureName
.STOP_CHARGING_STATION
, BroadcastChannelProcedureName
.STOP_CHARGING_STATION
],
28 [ProcedureName
.CLOSE_CONNECTION
, BroadcastChannelProcedureName
.CLOSE_CONNECTION
],
29 [ProcedureName
.OPEN_CONNECTION
, BroadcastChannelProcedureName
.OPEN_CONNECTION
],
31 ProcedureName
.START_AUTOMATIC_TRANSACTION_GENERATOR
,
32 BroadcastChannelProcedureName
.START_AUTOMATIC_TRANSACTION_GENERATOR
,
35 ProcedureName
.STOP_AUTOMATIC_TRANSACTION_GENERATOR
,
36 BroadcastChannelProcedureName
.STOP_AUTOMATIC_TRANSACTION_GENERATOR
,
38 [ProcedureName
.SET_SUPERVISION_URL
, BroadcastChannelProcedureName
.SET_SUPERVISION_URL
],
39 [ProcedureName
.START_TRANSACTION
, BroadcastChannelProcedureName
.START_TRANSACTION
],
40 [ProcedureName
.STOP_TRANSACTION
, BroadcastChannelProcedureName
.STOP_TRANSACTION
],
41 [ProcedureName
.AUTHORIZE
, BroadcastChannelProcedureName
.AUTHORIZE
],
42 [ProcedureName
.BOOT_NOTIFICATION
, BroadcastChannelProcedureName
.BOOT_NOTIFICATION
],
43 [ProcedureName
.STATUS_NOTIFICATION
, BroadcastChannelProcedureName
.STATUS_NOTIFICATION
],
44 [ProcedureName
.HEARTBEAT
, BroadcastChannelProcedureName
.HEARTBEAT
],
45 [ProcedureName
.METER_VALUES
, BroadcastChannelProcedureName
.METER_VALUES
],
46 [ProcedureName
.DATA_TRANSFER
, BroadcastChannelProcedureName
.DATA_TRANSFER
],
48 ProcedureName
.DIAGNOSTICS_STATUS_NOTIFICATION
,
49 BroadcastChannelProcedureName
.DIAGNOSTICS_STATUS_NOTIFICATION
,
52 ProcedureName
.FIRMWARE_STATUS_NOTIFICATION
,
53 BroadcastChannelProcedureName
.FIRMWARE_STATUS_NOTIFICATION
,
57 protected readonly requestHandlers
: Map
<ProcedureName
, ProtocolRequestHandler
>;
58 private readonly version
: ProtocolVersion
;
59 private readonly uiServer
: AbstractUIServer
;
60 private readonly uiServiceWorkerBroadcastChannel
: UIServiceWorkerBroadcastChannel
;
61 private readonly broadcastChannelRequests
: Map
<string, number>;
63 constructor(uiServer
: AbstractUIServer
, version
: ProtocolVersion
) {
64 this.uiServer
= uiServer
;
65 this.version
= version
;
66 this.requestHandlers
= new Map
<ProcedureName
, ProtocolRequestHandler
>([
67 [ProcedureName
.LIST_CHARGING_STATIONS
, this.handleListChargingStations
.bind(this)],
68 [ProcedureName
.START_SIMULATOR
, this.handleStartSimulator
.bind(this)],
69 [ProcedureName
.STOP_SIMULATOR
, this.handleStopSimulator
.bind(this)],
71 this.uiServiceWorkerBroadcastChannel
= new UIServiceWorkerBroadcastChannel(this);
72 this.broadcastChannelRequests
= new Map
<string, number>();
75 public async requestHandler(request
: ProtocolRequest
): Promise
<ProtocolResponse
| undefined> {
76 let messageId
: string | undefined;
77 let command
: ProcedureName
| undefined;
78 let requestPayload
: RequestPayload
| undefined;
79 let responsePayload
: ResponsePayload
| undefined;
81 [messageId
, command
, requestPayload
] = request
;
83 if (this.requestHandlers
.has(command
) === false) {
85 `${command} is not implemented to handle message payload ${JSON.stringify(
93 // Call the request handler to build the response payload
94 responsePayload
= await this.requestHandlers
.get(command
)!(
101 logger
.error(`${this.logPrefix(moduleName, 'requestHandler')} Handle request error:`, error
);
103 hashIds
: requestPayload
?.hashIds
,
104 status: ResponseStatus
.FAILURE
,
108 errorMessage
: (error
as Error).message
,
109 errorStack
: (error
as Error).stack
,
110 errorDetails
: (error
as OCPPError
).details
,
113 if (!isNullOrUndefined(responsePayload
)) {
114 return this.uiServer
.buildProtocolResponse(messageId
!, responsePayload
!);
118 // public sendRequest(
119 // messageId: string,
120 // procedureName: ProcedureName,
121 // requestPayload: RequestPayload,
123 // this.uiServer.sendRequest(
124 // this.uiServer.buildProtocolRequest(messageId, procedureName, requestPayload),
128 public sendResponse(messageId
: string, responsePayload
: ResponsePayload
): void {
129 if (this.uiServer
.hasResponseHandler(messageId
)) {
130 this.uiServer
.sendResponse(this.uiServer
.buildProtocolResponse(messageId
, responsePayload
));
134 public logPrefix
= (modName
: string, methodName
: string): string => {
135 return this.uiServer
.logPrefix(modName
, methodName
, this.version
);
138 public deleteBroadcastChannelRequest(uuid
: string): void {
139 this.broadcastChannelRequests
.delete(uuid
);
142 public getBroadcastChannelExpectedResponses(uuid
: string): number {
143 return this.broadcastChannelRequests
.get(uuid
) ?? 0;
146 protected handleProtocolRequest(
148 procedureName
: ProcedureName
,
149 payload
: RequestPayload
,
151 this.sendBroadcastChannelRequest(
153 AbstractUIService
.ProcedureNameToBroadCastChannelProcedureNameMapping
.get(procedureName
)!,
158 private sendBroadcastChannelRequest(
160 procedureName
: BroadcastChannelProcedureName
,
161 payload
: BroadcastChannelRequestPayload
,
163 if (isNotEmptyArray(payload
.hashIds
)) {
164 payload
.hashIds
= payload
.hashIds
165 ?.filter((hashId
) => !isNullOrUndefined(hashId
))
167 if (this.uiServer
.chargingStations
.has(hashId
) === true) {
173 'sendBroadcastChannelRequest',
174 )} Charging station with hashId '${hashId}' not found`,
178 const expectedNumberOfResponses
= isNotEmptyArray(payload
.hashIds
)
179 ? payload
.hashIds
!.length
180 : this.uiServer
.chargingStations
.size
;
181 this.uiServiceWorkerBroadcastChannel
.sendRequest([uuid
, procedureName
, payload
]);
182 this.broadcastChannelRequests
.set(uuid
, expectedNumberOfResponses
);
185 private handleListChargingStations(): ResponsePayload
{
187 status: ResponseStatus
.SUCCESS
,
188 chargingStations
: [...this.uiServer
.chargingStations
.values()],
189 } as ResponsePayload
;
192 private async handleStartSimulator(): Promise
<ResponsePayload
> {
194 await Bootstrap
.getInstance().start();
195 return { status: ResponseStatus
.SUCCESS
};
197 return { status: ResponseStatus
.FAILURE
};
201 private async handleStopSimulator(): Promise
<ResponsePayload
> {
203 await Bootstrap
.getInstance().stop();
204 return { status: ResponseStatus
.SUCCESS
};
206 return { status: ResponseStatus
.FAILURE
};