1 import BaseError from
'../../../exception/BaseError';
2 import type OCPPError from
'../../../exception/OCPPError';
3 import { Bootstrap
} from
'../../../internal';
7 type ProtocolRequestHandler
,
12 } from
'../../../types/UIProtocol';
14 BroadcastChannelProcedureName
,
15 type BroadcastChannelRequestPayload
,
16 } from
'../../../types/WorkerBroadcastChannel';
17 import logger from
'../../../utils/Logger';
18 import Utils from
'../../../utils/Utils';
19 import UIServiceWorkerBroadcastChannel from
'../../UIServiceWorkerBroadcastChannel';
20 import type { AbstractUIServer
} from
'../AbstractUIServer';
22 const moduleName
= 'AbstractUIService';
24 export default abstract class AbstractUIService
{
25 protected static readonly ProcedureNameToBroadCastChannelProcedureNameMap
: Omit
<
26 Record
<ProcedureName
, BroadcastChannelProcedureName
>,
27 'startSimulator' | 'stopSimulator' | 'listChargingStations'
29 [ProcedureName
.START_CHARGING_STATION
]: BroadcastChannelProcedureName
.START_CHARGING_STATION
,
30 [ProcedureName
.STOP_CHARGING_STATION
]: BroadcastChannelProcedureName
.STOP_CHARGING_STATION
,
31 [ProcedureName
.CLOSE_CONNECTION
]: BroadcastChannelProcedureName
.CLOSE_CONNECTION
,
32 [ProcedureName
.OPEN_CONNECTION
]: BroadcastChannelProcedureName
.OPEN_CONNECTION
,
33 [ProcedureName
.START_AUTOMATIC_TRANSACTION_GENERATOR
]:
34 BroadcastChannelProcedureName
.START_AUTOMATIC_TRANSACTION_GENERATOR
,
35 [ProcedureName
.STOP_AUTOMATIC_TRANSACTION_GENERATOR
]:
36 BroadcastChannelProcedureName
.STOP_AUTOMATIC_TRANSACTION_GENERATOR
,
37 [ProcedureName
.START_TRANSACTION
]: BroadcastChannelProcedureName
.START_TRANSACTION
,
38 [ProcedureName
.STOP_TRANSACTION
]: BroadcastChannelProcedureName
.STOP_TRANSACTION
,
39 [ProcedureName
.AUTHORIZE
]: BroadcastChannelProcedureName
.AUTHORIZE
,
40 [ProcedureName
.STATUS_NOTIFICATION
]: BroadcastChannelProcedureName
.STATUS_NOTIFICATION
,
41 [ProcedureName
.HEARTBEAT
]: BroadcastChannelProcedureName
.HEARTBEAT
,
42 [ProcedureName
.METER_VALUES
]: BroadcastChannelProcedureName
.METER_VALUES
,
45 protected readonly requestHandlers
: Map
<ProcedureName
, ProtocolRequestHandler
>;
46 private readonly version
: ProtocolVersion
;
47 private readonly uiServer
: AbstractUIServer
;
48 private readonly uiServiceWorkerBroadcastChannel
: UIServiceWorkerBroadcastChannel
;
49 private readonly broadcastChannelRequests
: Map
<string, number>;
51 constructor(uiServer
: AbstractUIServer
, version
: ProtocolVersion
) {
52 this.uiServer
= uiServer
;
53 this.version
= version
;
54 this.requestHandlers
= new Map
<ProcedureName
, ProtocolRequestHandler
>([
55 [ProcedureName
.LIST_CHARGING_STATIONS
, this.handleListChargingStations
.bind(this)],
56 [ProcedureName
.START_SIMULATOR
, this.handleStartSimulator
.bind(this)],
57 [ProcedureName
.STOP_SIMULATOR
, this.handleStopSimulator
.bind(this)],
59 this.uiServiceWorkerBroadcastChannel
= new UIServiceWorkerBroadcastChannel(this);
60 this.broadcastChannelRequests
= new Map
<string, number>();
63 public async requestHandler(request
: ProtocolRequest
): Promise
<void> {
64 let messageId
: string;
65 let command
: ProcedureName
;
66 let requestPayload
: RequestPayload
| undefined;
67 let responsePayload
: ResponsePayload
;
69 [messageId
, command
, requestPayload
] = request
;
71 if (this.requestHandlers
.has(command
) === false) {
73 `${command} is not implemented to handle message payload ${JSON.stringify(
81 // Call the request handler to build the response payload
82 responsePayload
= await this.requestHandlers
.get(command
)(messageId
, command
, requestPayload
);
85 logger
.error(`${this.logPrefix(moduleName, 'messageHandler')} Handle request error:`, error
);
87 hashIds
: requestPayload
.hashIds
,
88 status: ResponseStatus
.FAILURE
,
92 errorMessage
: (error
as Error).message
,
93 errorStack
: (error
as Error).stack
,
94 errorDetails
: (error
as OCPPError
).details
,
97 // Send response for payload not forwarded to broadcast channel
98 if (responsePayload
!== undefined) {
99 this.sendResponse(messageId
, responsePayload
);
106 procedureName
: ProcedureName
,
107 requestPayload
: RequestPayload
109 this.uiServer
.sendRequest(
110 this.uiServer
.buildProtocolRequest(messageId
, procedureName
, requestPayload
)
114 public sendResponse(messageId
: string, responsePayload
: ResponsePayload
): void {
115 this.uiServer
.sendResponse(this.uiServer
.buildProtocolResponse(messageId
, responsePayload
));
118 public logPrefix(modName
: string, methodName
: string): string {
119 return this.uiServer
.logPrefix(modName
, methodName
, this.version
);
122 public deleteBroadcastChannelRequest(uuid
: string): void {
123 this.broadcastChannelRequests
.delete(uuid
);
126 public getBroadcastChannelExpectedResponses(uuid
: string): number {
127 return this.broadcastChannelRequests
.get(uuid
) ?? 0;
130 protected sendBroadcastChannelRequest(
132 procedureName
: BroadcastChannelProcedureName
,
133 payload
: BroadcastChannelRequestPayload
135 if (!Utils
.isEmptyArray(payload
.hashIds
)) {
136 payload
.hashIds
= payload
.hashIds
138 if (this.uiServer
.chargingStations
.has(hashId
) === true) {
144 'sendBroadcastChannelRequest'
145 )} Charging station with hashId '${hashId}' not found`
148 .filter((hashId
) => hashId
!== undefined);
150 const expectedNumberOfResponses
= !Utils
.isEmptyArray(payload
.hashIds
)
151 ? payload
.hashIds
.length
152 : this.uiServer
.chargingStations
.size
;
153 this.uiServiceWorkerBroadcastChannel
.sendRequest([uuid
, procedureName
, payload
]);
154 this.broadcastChannelRequests
.set(uuid
, expectedNumberOfResponses
);
157 protected handleProtocolRequest(
159 procedureName
: ProcedureName
,
160 payload
: RequestPayload
162 this.sendBroadcastChannelRequest(
164 AbstractUIService
.ProcedureNameToBroadCastChannelProcedureNameMap
[
166 ] as BroadcastChannelProcedureName
,
171 private handleListChargingStations(): ResponsePayload
{
173 status: ResponseStatus
.SUCCESS
,
174 chargingStations
: [...this.uiServer
.chargingStations
.values()],
175 } as ResponsePayload
;
178 private async handleStartSimulator(): Promise
<ResponsePayload
> {
179 await Bootstrap
.getInstance().start();
180 return { status: ResponseStatus
.SUCCESS
};
183 private async handleStopSimulator(): Promise
<ResponsePayload
> {
184 await Bootstrap
.getInstance().stop();
185 return { status: ResponseStatus
.SUCCESS
};