1 import BaseError from
'../exception/BaseError';
2 import type OCPPError from
'../exception/OCPPError';
6 type StatusNotificationRequest
,
7 } from
'../types/ocpp/Requests';
8 import type { HeartbeatResponse
, StatusNotificationResponse
} from
'../types/ocpp/Responses';
13 StartTransactionRequest
,
14 StartTransactionResponse
,
15 StopTransactionRequest
,
16 StopTransactionResponse
,
17 } from
'../types/ocpp/Transaction';
19 BroadcastChannelProcedureName
,
20 BroadcastChannelRequest
,
21 BroadcastChannelRequestPayload
,
22 BroadcastChannelResponsePayload
,
24 } from
'../types/WorkerBroadcastChannel';
25 import { ResponseStatus
} from
'../ui/web/src/types/UIProtocol';
26 import logger from
'../utils/Logger';
27 import Utils from
'../utils/Utils';
28 import type ChargingStation from
'./ChargingStation';
29 import WorkerBroadcastChannel from
'./WorkerBroadcastChannel';
31 const moduleName
= 'ChargingStationWorkerBroadcastChannel';
33 type CommandResponse
=
34 | StartTransactionResponse
35 | StopTransactionResponse
37 | StatusNotificationResponse
40 type CommandHandler
= (
41 requestPayload
?: BroadcastChannelRequestPayload
42 ) => Promise
<CommandResponse
| void> | void;
44 export default class ChargingStationWorkerBroadcastChannel
extends WorkerBroadcastChannel
{
45 private readonly commandHandlers
: Map
<BroadcastChannelProcedureName
, CommandHandler
>;
47 private readonly chargingStation
: ChargingStation
;
49 constructor(chargingStation
: ChargingStation
) {
51 this.commandHandlers
= new Map
<BroadcastChannelProcedureName
, CommandHandler
>([
52 [BroadcastChannelProcedureName
.START_CHARGING_STATION
, () => this.chargingStation
.start()],
54 BroadcastChannelProcedureName
.STOP_CHARGING_STATION
,
55 async () => this.chargingStation
.stop(),
58 BroadcastChannelProcedureName
.OPEN_CONNECTION
,
59 () => this.chargingStation
.openWSConnection(),
62 BroadcastChannelProcedureName
.CLOSE_CONNECTION
,
63 () => this.chargingStation
.closeWSConnection(),
66 BroadcastChannelProcedureName
.START_TRANSACTION
,
67 async (requestPayload
?: BroadcastChannelRequestPayload
) =>
68 this.chargingStation
.ocppRequestService
.requestHandler
<
69 StartTransactionRequest
,
70 StartTransactionResponse
71 >(this.chargingStation
, RequestCommand
.START_TRANSACTION
, requestPayload
),
74 BroadcastChannelProcedureName
.STOP_TRANSACTION
,
75 async (requestPayload
?: BroadcastChannelRequestPayload
) =>
76 this.chargingStation
.ocppRequestService
.requestHandler
<
77 StopTransactionRequest
,
78 StartTransactionResponse
79 >(this.chargingStation
, RequestCommand
.STOP_TRANSACTION
, {
81 meterStop
: this.chargingStation
.getEnergyActiveImportRegisterByTransactionId(
82 requestPayload
.transactionId
,
88 BroadcastChannelProcedureName
.START_AUTOMATIC_TRANSACTION_GENERATOR
,
89 (requestPayload
?: BroadcastChannelRequestPayload
) =>
90 this.chargingStation
.startAutomaticTransactionGenerator(requestPayload
.connectorIds
),
93 BroadcastChannelProcedureName
.STOP_AUTOMATIC_TRANSACTION_GENERATOR
,
94 (requestPayload
?: BroadcastChannelRequestPayload
) =>
95 this.chargingStation
.stopAutomaticTransactionGenerator(requestPayload
.connectorIds
),
98 BroadcastChannelProcedureName
.AUTHORIZE
,
99 async (requestPayload
?: BroadcastChannelRequestPayload
) =>
100 this.chargingStation
.ocppRequestService
.requestHandler
<
103 >(this.chargingStation
, RequestCommand
.AUTHORIZE
, requestPayload
),
106 BroadcastChannelProcedureName
.STATUS_NOTIFICATION
,
107 async (requestPayload
?: BroadcastChannelRequestPayload
) =>
108 this.chargingStation
.ocppRequestService
.requestHandler
<
109 StatusNotificationRequest
,
110 StatusNotificationResponse
111 >(this.chargingStation
, RequestCommand
.STATUS_NOTIFICATION
, requestPayload
),
114 BroadcastChannelProcedureName
.HEARTBEAT
,
115 async (requestPayload
?: BroadcastChannelRequestPayload
) =>
116 this.chargingStation
.ocppRequestService
.requestHandler
<
119 >(this.chargingStation
, RequestCommand
.HEARTBEAT
, requestPayload
),
122 this.chargingStation
= chargingStation
;
123 this.onmessage
= this.requestHandler
.bind(this) as (message
: MessageEvent
) => void;
124 this.onmessageerror
= this.messageErrorHandler
.bind(this) as (message
: MessageEvent
) => void;
127 private async requestHandler(messageEvent
: MessageEvent
): Promise
<void> {
128 const validatedMessageEvent
= this.validateMessageEvent(messageEvent
);
129 if (validatedMessageEvent
=== false) {
132 if (this.isResponse(validatedMessageEvent
.data
) === true) {
135 const [uuid
, command
, requestPayload
] = validatedMessageEvent
.data
as BroadcastChannelRequest
;
138 requestPayload
?.hashIds
!== undefined &&
139 requestPayload
?.hashIds
?.includes(this.chargingStation
.stationInfo
.hashId
) === false
143 if (requestPayload
?.hashId
!== undefined) {
145 `${this.chargingStation.logPrefix()} ${moduleName}.requestHandler: 'hashId' field usage in PDU is deprecated, use 'hashIds' instead`
150 let responsePayload
: BroadcastChannelResponsePayload
;
151 let commandResponse
: CommandResponse
| void;
153 commandResponse
= await this.commandHandler(command
, requestPayload
);
154 if (commandResponse
=== undefined || commandResponse
=== null) {
156 hashId
: this.chargingStation
.stationInfo
.hashId
,
157 status: ResponseStatus
.SUCCESS
,
160 const responseStatus
= this.commandResponseToResponseStatus(
162 commandResponse
as CommandResponse
164 if (responseStatus
=== ResponseStatus
.SUCCESS
) {
166 hashId
: this.chargingStation
.stationInfo
.hashId
,
167 status: responseStatus
,
171 hashId
: this.chargingStation
.stationInfo
.hashId
,
172 status: responseStatus
,
175 commandResponse
: commandResponse
as CommandResponse
,
181 `${this.chargingStation.logPrefix()} ${moduleName}.requestHandler: Handle request error:`,
185 hashId
: this.chargingStation
.stationInfo
.hashId
,
186 status: ResponseStatus
.FAILURE
,
189 commandResponse
: commandResponse
as CommandResponse
,
190 errorMessage
: (error
as Error).message
,
191 errorStack
: (error
as Error).stack
,
192 errorDetails
: (error
as OCPPError
).details
,
195 this.sendResponse([uuid
, responsePayload
]);
198 private messageErrorHandler(messageEvent
: MessageEvent
): void {
200 `${this.chargingStation.logPrefix()} ${moduleName}.messageErrorHandler: Error at handling message:`,
205 private async commandHandler(
206 command
: BroadcastChannelProcedureName
,
207 requestPayload
: BroadcastChannelRequestPayload
208 ): Promise
<CommandResponse
| void> {
209 if (this.commandHandlers
.has(command
) === true) {
210 this.cleanRequestPayload(command
, requestPayload
);
211 return this.commandHandlers
.get(command
)(requestPayload
);
213 throw new BaseError(`Unknown worker broadcast channel command: ${command}`);
216 private cleanRequestPayload(
217 command
: BroadcastChannelProcedureName
,
218 requestPayload
: BroadcastChannelRequestPayload
220 delete requestPayload
.hashId
;
221 delete requestPayload
.hashIds
;
223 BroadcastChannelProcedureName
.START_AUTOMATIC_TRANSACTION_GENERATOR
,
224 BroadcastChannelProcedureName
.STOP_AUTOMATIC_TRANSACTION_GENERATOR
,
225 ].includes(command
) === false && delete requestPayload
.connectorIds
;
228 private commandResponseToResponseStatus(
229 command
: BroadcastChannelProcedureName
,
230 commandResponse
: CommandResponse
233 case BroadcastChannelProcedureName
.START_TRANSACTION
:
234 case BroadcastChannelProcedureName
.STOP_TRANSACTION
:
235 case BroadcastChannelProcedureName
.AUTHORIZE
:
239 | StartTransactionResponse
240 | StopTransactionResponse
242 )?.idTagInfo
?.status === AuthorizationStatus
.ACCEPTED
244 return ResponseStatus
.SUCCESS
;
246 return ResponseStatus
.FAILURE
;
247 case BroadcastChannelProcedureName
.STATUS_NOTIFICATION
:
248 if (Utils
.isEmptyObject(commandResponse
) === true) {
249 return ResponseStatus
.SUCCESS
;
251 return ResponseStatus
.FAILURE
;
252 case BroadcastChannelProcedureName
.HEARTBEAT
:
253 if ('currentTime' in commandResponse
) {
254 return ResponseStatus
.SUCCESS
;
256 return ResponseStatus
.FAILURE
;
258 return ResponseStatus
.FAILURE
;