3 ChargingStationConfigurationUtils
,
4 WorkerBroadcastChannel
,
6 import { OCPP16ServiceUtils
} from
'./ocpp';
7 import { BaseError
, type OCPPError
} from
'../exception';
10 type AuthorizeRequest
,
11 type AuthorizeResponse
,
12 type BootNotificationRequest
,
13 type BootNotificationResponse
,
14 BroadcastChannelProcedureName
,
15 type BroadcastChannelRequest
,
16 type BroadcastChannelRequestPayload
,
17 type BroadcastChannelResponsePayload
,
18 type DataTransferRequest
,
19 type DataTransferResponse
,
21 type DiagnosticsStatusNotificationRequest
,
22 type DiagnosticsStatusNotificationResponse
,
23 type FirmwareStatusNotificationRequest
,
24 type FirmwareStatusNotificationResponse
,
25 type HeartbeatRequest
,
26 type HeartbeatResponse
,
28 type MeterValuesRequest
,
29 type MeterValuesResponse
,
30 RegistrationStatusEnumType
,
34 StandardParametersKey
,
35 type StartTransactionRequest
,
36 type StartTransactionResponse
,
37 type StatusNotificationRequest
,
38 type StatusNotificationResponse
,
39 type StopTransactionRequest
,
40 type StopTransactionResponse
,
42 import { Constants
, Utils
, logger
} from
'../utils';
44 const moduleName
= 'ChargingStationWorkerBroadcastChannel';
46 type CommandResponse
=
47 | StartTransactionResponse
48 | StopTransactionResponse
50 | BootNotificationResponse
51 | StatusNotificationResponse
54 | DataTransferResponse
55 | DiagnosticsStatusNotificationResponse
56 | FirmwareStatusNotificationResponse
;
58 type CommandHandler
= (
59 requestPayload
?: BroadcastChannelRequestPayload
60 ) => Promise
<CommandResponse
| void> | void;
62 export class ChargingStationWorkerBroadcastChannel
extends WorkerBroadcastChannel
{
63 private readonly commandHandlers
: Map
<BroadcastChannelProcedureName
, CommandHandler
>;
64 private readonly chargingStation
: ChargingStation
;
66 constructor(chargingStation
: ChargingStation
) {
68 const requestParams
: RequestParams
= {
71 this.commandHandlers
= new Map
<BroadcastChannelProcedureName
, CommandHandler
>([
72 [BroadcastChannelProcedureName
.START_CHARGING_STATION
, () => this.chargingStation
.start()],
74 BroadcastChannelProcedureName
.STOP_CHARGING_STATION
,
75 async () => this.chargingStation
.stop(),
78 BroadcastChannelProcedureName
.OPEN_CONNECTION
,
79 () => this.chargingStation
.openWSConnection(),
82 BroadcastChannelProcedureName
.CLOSE_CONNECTION
,
83 () => this.chargingStation
.closeWSConnection(),
86 BroadcastChannelProcedureName
.START_AUTOMATIC_TRANSACTION_GENERATOR
,
87 (requestPayload
?: BroadcastChannelRequestPayload
) =>
88 this.chargingStation
.startAutomaticTransactionGenerator(requestPayload
?.connectorIds
),
91 BroadcastChannelProcedureName
.STOP_AUTOMATIC_TRANSACTION_GENERATOR
,
92 (requestPayload
?: BroadcastChannelRequestPayload
) =>
93 this.chargingStation
.stopAutomaticTransactionGenerator(requestPayload
?.connectorIds
),
96 BroadcastChannelProcedureName
.START_TRANSACTION
,
97 async (requestPayload
?: BroadcastChannelRequestPayload
) =>
98 this.chargingStation
.ocppRequestService
.requestHandler
<
99 StartTransactionRequest
,
100 StartTransactionResponse
101 >(this.chargingStation
, RequestCommand
.START_TRANSACTION
, requestPayload
, requestParams
),
104 BroadcastChannelProcedureName
.STOP_TRANSACTION
,
105 async (requestPayload
?: BroadcastChannelRequestPayload
) =>
106 this.chargingStation
.ocppRequestService
.requestHandler
<
107 StopTransactionRequest
,
108 StartTransactionResponse
110 this.chargingStation
,
111 RequestCommand
.STOP_TRANSACTION
,
113 meterStop
: this.chargingStation
.getEnergyActiveImportRegisterByTransactionId(
114 requestPayload
.transactionId
,
123 BroadcastChannelProcedureName
.AUTHORIZE
,
124 async (requestPayload
?: BroadcastChannelRequestPayload
) =>
125 this.chargingStation
.ocppRequestService
.requestHandler
<
128 >(this.chargingStation
, RequestCommand
.AUTHORIZE
, requestPayload
, requestParams
),
131 BroadcastChannelProcedureName
.BOOT_NOTIFICATION
,
132 async (requestPayload
?: BroadcastChannelRequestPayload
) => {
133 this.chargingStation
.bootNotificationResponse
=
134 await this.chargingStation
.ocppRequestService
.requestHandler
<
135 BootNotificationRequest
,
136 BootNotificationResponse
138 this.chargingStation
,
139 RequestCommand
.BOOT_NOTIFICATION
,
141 ...this.chargingStation
.bootNotificationRequest
,
145 skipBufferingOnError
: true,
149 return this.chargingStation
.bootNotificationResponse
;
153 BroadcastChannelProcedureName
.STATUS_NOTIFICATION
,
154 async (requestPayload
?: BroadcastChannelRequestPayload
) =>
155 this.chargingStation
.ocppRequestService
.requestHandler
<
156 StatusNotificationRequest
,
157 StatusNotificationResponse
159 this.chargingStation
,
160 RequestCommand
.STATUS_NOTIFICATION
,
166 BroadcastChannelProcedureName
.HEARTBEAT
,
167 async (requestPayload
?: BroadcastChannelRequestPayload
) =>
168 this.chargingStation
.ocppRequestService
.requestHandler
<
171 >(this.chargingStation
, RequestCommand
.HEARTBEAT
, requestPayload
, requestParams
),
174 BroadcastChannelProcedureName
.METER_VALUES
,
175 async (requestPayload
?: BroadcastChannelRequestPayload
) => {
176 const configuredMeterValueSampleInterval
=
177 ChargingStationConfigurationUtils
.getConfigurationKey(
179 StandardParametersKey
.MeterValueSampleInterval
181 return this.chargingStation
.ocppRequestService
.requestHandler
<
185 this.chargingStation
,
186 RequestCommand
.METER_VALUES
,
189 // FIXME: Implement OCPP version agnostic helpers
190 OCPP16ServiceUtils
.buildMeterValue(
191 this.chargingStation
,
192 requestPayload
.connectorId
,
193 this.chargingStation
.getConnectorStatus(requestPayload
.connectorId
)
195 configuredMeterValueSampleInterval
196 ? Utils
.convertToInt(configuredMeterValueSampleInterval
.value
) * 1000
197 : Constants
.DEFAULT_METER_VALUES_INTERVAL
207 BroadcastChannelProcedureName
.DATA_TRANSFER
,
208 async (requestPayload
?: BroadcastChannelRequestPayload
) =>
209 this.chargingStation
.ocppRequestService
.requestHandler
<
212 >(this.chargingStation
, RequestCommand
.DATA_TRANSFER
, requestPayload
, requestParams
),
215 BroadcastChannelProcedureName
.DIAGNOSTICS_STATUS_NOTIFICATION
,
216 async (requestPayload
?: BroadcastChannelRequestPayload
) =>
217 this.chargingStation
.ocppRequestService
.requestHandler
<
218 DiagnosticsStatusNotificationRequest
,
219 DiagnosticsStatusNotificationResponse
221 this.chargingStation
,
222 RequestCommand
.DIAGNOSTICS_STATUS_NOTIFICATION
,
228 BroadcastChannelProcedureName
.FIRMWARE_STATUS_NOTIFICATION
,
229 async (requestPayload
?: BroadcastChannelRequestPayload
) =>
230 this.chargingStation
.ocppRequestService
.requestHandler
<
231 FirmwareStatusNotificationRequest
,
232 FirmwareStatusNotificationResponse
234 this.chargingStation
,
235 RequestCommand
.FIRMWARE_STATUS_NOTIFICATION
,
241 this.chargingStation
= chargingStation
;
242 this.onmessage
= this.requestHandler
.bind(this) as (message
: MessageEvent
) => void;
243 this.onmessageerror
= this.messageErrorHandler
.bind(this) as (message
: MessageEvent
) => void;
246 private async requestHandler(messageEvent
: MessageEvent
): Promise
<void> {
247 const validatedMessageEvent
= this.validateMessageEvent(messageEvent
);
248 if (validatedMessageEvent
=== false) {
251 if (this.isResponse(validatedMessageEvent
.data
) === true) {
254 const [uuid
, command
, requestPayload
] = validatedMessageEvent
.data
as BroadcastChannelRequest
;
256 requestPayload
?.hashIds
!== undefined &&
257 requestPayload
?.hashIds
?.includes(this.chargingStation
.stationInfo
.hashId
) === false
261 if (requestPayload
?.hashId
!== undefined) {
263 `${this.chargingStation.logPrefix()} ${moduleName}.requestHandler: 'hashId' field usage in PDU is deprecated, use 'hashIds' array instead`
267 let responsePayload
: BroadcastChannelResponsePayload
;
268 let commandResponse
: CommandResponse
| void;
270 commandResponse
= await this.commandHandler(command
, requestPayload
);
272 commandResponse
=== undefined ||
273 commandResponse
=== null ||
274 Utils
.isEmptyObject(commandResponse
as CommandResponse
)
277 hashId
: this.chargingStation
.stationInfo
.hashId
,
278 status: ResponseStatus
.SUCCESS
,
281 responsePayload
= this.commandResponseToResponsePayload(
284 commandResponse
as CommandResponse
289 `${this.chargingStation.logPrefix()} ${moduleName}.requestHandler: Handle request error:`,
293 hashId
: this.chargingStation
.stationInfo
.hashId
,
294 status: ResponseStatus
.FAILURE
,
297 commandResponse
: commandResponse
as CommandResponse
,
298 errorMessage
: (error
as Error).message
,
299 errorStack
: (error
as Error).stack
,
300 errorDetails
: (error
as OCPPError
).details
,
303 this.sendResponse([uuid
, responsePayload
]);
307 private messageErrorHandler(messageEvent
: MessageEvent
): void {
309 `${this.chargingStation.logPrefix()} ${moduleName}.messageErrorHandler: Error at handling message:`,
314 private async commandHandler(
315 command
: BroadcastChannelProcedureName
,
316 requestPayload
: BroadcastChannelRequestPayload
317 ): Promise
<CommandResponse
| void> {
318 if (this.commandHandlers
.has(command
) === true) {
319 this.cleanRequestPayload(command
, requestPayload
);
320 return this.commandHandlers
.get(command
)(requestPayload
);
322 throw new BaseError(`Unknown worker broadcast channel command: ${command}`);
325 private cleanRequestPayload(
326 command
: BroadcastChannelProcedureName
,
327 requestPayload
: BroadcastChannelRequestPayload
329 delete requestPayload
.hashId
;
330 delete requestPayload
.hashIds
;
332 BroadcastChannelProcedureName
.START_AUTOMATIC_TRANSACTION_GENERATOR
,
333 BroadcastChannelProcedureName
.STOP_AUTOMATIC_TRANSACTION_GENERATOR
,
334 ].includes(command
) === false && delete requestPayload
.connectorIds
;
337 private commandResponseToResponsePayload(
338 command
: BroadcastChannelProcedureName
,
339 requestPayload
: BroadcastChannelRequestPayload
,
340 commandResponse
: CommandResponse
341 ): BroadcastChannelResponsePayload
{
342 const responseStatus
= this.commandResponseToResponseStatus(command
, commandResponse
);
343 if (responseStatus
=== ResponseStatus
.SUCCESS
) {
345 hashId
: this.chargingStation
.stationInfo
.hashId
,
346 status: responseStatus
,
350 hashId
: this.chargingStation
.stationInfo
.hashId
,
351 status: responseStatus
,
358 private commandResponseToResponseStatus(
359 command
: BroadcastChannelProcedureName
,
360 commandResponse
: CommandResponse
363 case BroadcastChannelProcedureName
.START_TRANSACTION
:
364 case BroadcastChannelProcedureName
.STOP_TRANSACTION
:
365 case BroadcastChannelProcedureName
.AUTHORIZE
:
369 | StartTransactionResponse
370 | StopTransactionResponse
372 )?.idTagInfo
?.status === AuthorizationStatus
.ACCEPTED
374 return ResponseStatus
.SUCCESS
;
376 return ResponseStatus
.FAILURE
;
377 case BroadcastChannelProcedureName
.BOOT_NOTIFICATION
:
378 if (commandResponse
?.status === RegistrationStatusEnumType
.ACCEPTED
) {
379 return ResponseStatus
.SUCCESS
;
381 return ResponseStatus
.FAILURE
;
382 case BroadcastChannelProcedureName
.DATA_TRANSFER
:
383 if (commandResponse
?.status === DataTransferStatus
.ACCEPTED
) {
384 return ResponseStatus
.SUCCESS
;
386 return ResponseStatus
.FAILURE
;
387 case BroadcastChannelProcedureName
.STATUS_NOTIFICATION
:
388 case BroadcastChannelProcedureName
.METER_VALUES
:
389 if (Utils
.isEmptyObject(commandResponse
) === true) {
390 return ResponseStatus
.SUCCESS
;
392 return ResponseStatus
.FAILURE
;
393 case BroadcastChannelProcedureName
.HEARTBEAT
:
394 if ('currentTime' in commandResponse
) {
395 return ResponseStatus
.SUCCESS
;
397 return ResponseStatus
.FAILURE
;
399 return ResponseStatus
.FAILURE
;