1 import { BaseError
, type OCPPError
} from
'../../../exception';
3 BroadcastChannelProcedureName
,
4 type BroadcastChannelRequestPayload
,
7 type ProtocolRequestHandler
,
12 } from
'../../../types';
13 import { Utils
, logger
} from
'../../../utils';
14 import { type AbstractUIServer
, Bootstrap
, UIServiceWorkerBroadcastChannel
} from
'../../internal';
16 const moduleName
= 'AbstractUIService';
18 export abstract class AbstractUIService
{
19 protected static readonly ProcedureNameToBroadCastChannelProcedureNameMap
: Omit
<
20 Record
<ProcedureName
, BroadcastChannelProcedureName
>,
21 | ProcedureName
.START_SIMULATOR
22 | ProcedureName
.STOP_SIMULATOR
23 | ProcedureName
.LIST_CHARGING_STATIONS
25 [ProcedureName
.START_CHARGING_STATION
]: BroadcastChannelProcedureName
.START_CHARGING_STATION
,
26 [ProcedureName
.STOP_CHARGING_STATION
]: BroadcastChannelProcedureName
.STOP_CHARGING_STATION
,
27 [ProcedureName
.CLOSE_CONNECTION
]: BroadcastChannelProcedureName
.CLOSE_CONNECTION
,
28 [ProcedureName
.OPEN_CONNECTION
]: BroadcastChannelProcedureName
.OPEN_CONNECTION
,
29 [ProcedureName
.START_AUTOMATIC_TRANSACTION_GENERATOR
]:
30 BroadcastChannelProcedureName
.START_AUTOMATIC_TRANSACTION_GENERATOR
,
31 [ProcedureName
.STOP_AUTOMATIC_TRANSACTION_GENERATOR
]:
32 BroadcastChannelProcedureName
.STOP_AUTOMATIC_TRANSACTION_GENERATOR
,
33 [ProcedureName
.START_TRANSACTION
]: BroadcastChannelProcedureName
.START_TRANSACTION
,
34 [ProcedureName
.STOP_TRANSACTION
]: BroadcastChannelProcedureName
.STOP_TRANSACTION
,
35 [ProcedureName
.AUTHORIZE
]: BroadcastChannelProcedureName
.AUTHORIZE
,
36 [ProcedureName
.BOOT_NOTIFICATION
]: BroadcastChannelProcedureName
.BOOT_NOTIFICATION
,
37 [ProcedureName
.STATUS_NOTIFICATION
]: BroadcastChannelProcedureName
.STATUS_NOTIFICATION
,
38 [ProcedureName
.HEARTBEAT
]: BroadcastChannelProcedureName
.HEARTBEAT
,
39 [ProcedureName
.METER_VALUES
]: BroadcastChannelProcedureName
.METER_VALUES
,
40 [ProcedureName
.DATA_TRANSFER
]: BroadcastChannelProcedureName
.DATA_TRANSFER
,
41 [ProcedureName
.DIAGNOSTICS_STATUS_NOTIFICATION
]:
42 BroadcastChannelProcedureName
.DIAGNOSTICS_STATUS_NOTIFICATION
,
43 [ProcedureName
.FIRMWARE_STATUS_NOTIFICATION
]:
44 BroadcastChannelProcedureName
.FIRMWARE_STATUS_NOTIFICATION
,
47 protected readonly requestHandlers
: Map
<ProcedureName
, ProtocolRequestHandler
>;
48 private readonly version
: ProtocolVersion
;
49 private readonly uiServer
: AbstractUIServer
;
50 private readonly uiServiceWorkerBroadcastChannel
: UIServiceWorkerBroadcastChannel
;
51 private readonly broadcastChannelRequests
: Map
<string, number>;
53 constructor(uiServer
: AbstractUIServer
, version
: ProtocolVersion
) {
54 this.uiServer
= uiServer
;
55 this.version
= version
;
56 this.requestHandlers
= new Map
<ProcedureName
, ProtocolRequestHandler
>([
57 [ProcedureName
.LIST_CHARGING_STATIONS
, this.handleListChargingStations
.bind(this)],
58 [ProcedureName
.START_SIMULATOR
, this.handleStartSimulator
.bind(this)],
59 [ProcedureName
.STOP_SIMULATOR
, this.handleStopSimulator
.bind(this)],
61 this.uiServiceWorkerBroadcastChannel
= new UIServiceWorkerBroadcastChannel(this);
62 this.broadcastChannelRequests
= new Map
<string, number>();
65 public async requestHandler(request
: ProtocolRequest
): Promise
<void> {
66 let messageId
: string;
67 let command
: ProcedureName
;
68 let requestPayload
: RequestPayload
| undefined;
69 let responsePayload
: ResponsePayload
;
71 [messageId
, command
, requestPayload
] = request
;
73 if (this.requestHandlers
.has(command
) === false) {
75 `${command} is not implemented to handle message payload ${JSON.stringify(
83 // Call the request handler to build the response payload
84 responsePayload
= await this.requestHandlers
.get(command
)(messageId
, command
, requestPayload
);
87 logger
.error(`${this.logPrefix(moduleName, 'messageHandler')} Handle request error:`, error
);
89 hashIds
: requestPayload
?.hashIds
,
90 status: ResponseStatus
.FAILURE
,
94 errorMessage
: (error
as Error).message
,
95 errorStack
: (error
as Error).stack
,
96 errorDetails
: (error
as OCPPError
).details
,
99 // Send response for payload not forwarded to broadcast channel
100 if (responsePayload
!== undefined) {
101 this.sendResponse(messageId
, responsePayload
);
108 procedureName
: ProcedureName
,
109 requestPayload
: RequestPayload
111 this.uiServer
.sendRequest(
112 this.uiServer
.buildProtocolRequest(messageId
, procedureName
, requestPayload
)
116 public sendResponse(messageId
: string, responsePayload
: ResponsePayload
): void {
117 this.uiServer
.sendResponse(this.uiServer
.buildProtocolResponse(messageId
, responsePayload
));
120 public logPrefix
= (modName
: string, methodName
: string): string => {
121 return this.uiServer
.logPrefix(modName
, methodName
, this.version
);
124 public deleteBroadcastChannelRequest(uuid
: string): void {
125 this.broadcastChannelRequests
.delete(uuid
);
128 public getBroadcastChannelExpectedResponses(uuid
: string): number {
129 return this.broadcastChannelRequests
.get(uuid
) ?? 0;
132 protected handleProtocolRequest(
134 procedureName
: ProcedureName
,
135 payload
: RequestPayload
137 this.sendBroadcastChannelRequest(
139 AbstractUIService
.ProcedureNameToBroadCastChannelProcedureNameMap
[
141 ] as BroadcastChannelProcedureName
,
146 private sendBroadcastChannelRequest(
148 procedureName
: BroadcastChannelProcedureName
,
149 payload
: BroadcastChannelRequestPayload
151 if (Utils
.isNotEmptyArray(payload
.hashIds
)) {
152 payload
.hashIds
= payload
.hashIds
153 .filter((hashId
) => hashId
!== undefined)
155 if (this.uiServer
.chargingStations
.has(hashId
) === true) {
161 'sendBroadcastChannelRequest'
162 )} Charging station with hashId '${hashId}' not found`
166 const expectedNumberOfResponses
= Utils
.isNotEmptyArray(payload
.hashIds
)
167 ? payload
.hashIds
.length
168 : this.uiServer
.chargingStations
.size
;
169 this.uiServiceWorkerBroadcastChannel
.sendRequest([uuid
, procedureName
, payload
]);
170 this.broadcastChannelRequests
.set(uuid
, expectedNumberOfResponses
);
173 private handleListChargingStations(): ResponsePayload
{
175 status: ResponseStatus
.SUCCESS
,
176 chargingStations
: [...this.uiServer
.chargingStations
.values()],
177 } as ResponsePayload
;
180 private async handleStartSimulator(): Promise
<ResponsePayload
> {
182 await Bootstrap
.getInstance().start();
183 return { status: ResponseStatus
.SUCCESS
};
185 return { status: ResponseStatus
.FAILURE
};
189 private async handleStopSimulator(): Promise
<ResponsePayload
> {
191 await Bootstrap
.getInstance().stop();
192 return { status: ResponseStatus
.SUCCESS
};
194 return { status: ResponseStatus
.FAILURE
};