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
} from
'../utils/Constants';
43 import { logger
} from
'../utils/Logger';
44 import { Utils
} from
'../utils/Utils';
46 const moduleName
= 'ChargingStationWorkerBroadcastChannel';
48 type CommandResponse
=
49 | StartTransactionResponse
50 | StopTransactionResponse
52 | BootNotificationResponse
53 | StatusNotificationResponse
56 | DataTransferResponse
57 | DiagnosticsStatusNotificationResponse
58 | FirmwareStatusNotificationResponse
;
60 type CommandHandler
= (
61 requestPayload
?: BroadcastChannelRequestPayload
62 ) => Promise
<CommandResponse
| void> | void;
64 export class ChargingStationWorkerBroadcastChannel
extends WorkerBroadcastChannel
{
65 private readonly commandHandlers
: Map
<BroadcastChannelProcedureName
, CommandHandler
>;
66 private readonly chargingStation
: ChargingStation
;
68 constructor(chargingStation
: ChargingStation
) {
70 const requestParams
: RequestParams
= {
73 this.commandHandlers
= new Map
<BroadcastChannelProcedureName
, CommandHandler
>([
74 [BroadcastChannelProcedureName
.START_CHARGING_STATION
, () => this.chargingStation
.start()],
76 BroadcastChannelProcedureName
.STOP_CHARGING_STATION
,
77 async () => this.chargingStation
.stop(),
80 BroadcastChannelProcedureName
.OPEN_CONNECTION
,
81 () => this.chargingStation
.openWSConnection(),
84 BroadcastChannelProcedureName
.CLOSE_CONNECTION
,
85 () => this.chargingStation
.closeWSConnection(),
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
.START_TRANSACTION
,
99 async (requestPayload
?: BroadcastChannelRequestPayload
) =>
100 this.chargingStation
.ocppRequestService
.requestHandler
<
101 StartTransactionRequest
,
102 StartTransactionResponse
103 >(this.chargingStation
, RequestCommand
.START_TRANSACTION
, requestPayload
, requestParams
),
106 BroadcastChannelProcedureName
.STOP_TRANSACTION
,
107 async (requestPayload
?: BroadcastChannelRequestPayload
) =>
108 this.chargingStation
.ocppRequestService
.requestHandler
<
109 StopTransactionRequest
,
110 StartTransactionResponse
112 this.chargingStation
,
113 RequestCommand
.STOP_TRANSACTION
,
115 meterStop
: this.chargingStation
.getEnergyActiveImportRegisterByTransactionId(
116 requestPayload
.transactionId
,
125 BroadcastChannelProcedureName
.AUTHORIZE
,
126 async (requestPayload
?: BroadcastChannelRequestPayload
) =>
127 this.chargingStation
.ocppRequestService
.requestHandler
<
130 >(this.chargingStation
, RequestCommand
.AUTHORIZE
, requestPayload
, requestParams
),
133 BroadcastChannelProcedureName
.BOOT_NOTIFICATION
,
134 async (requestPayload
?: BroadcastChannelRequestPayload
) => {
135 this.chargingStation
.bootNotificationResponse
=
136 await this.chargingStation
.ocppRequestService
.requestHandler
<
137 BootNotificationRequest
,
138 BootNotificationResponse
140 this.chargingStation
,
141 RequestCommand
.BOOT_NOTIFICATION
,
143 ...this.chargingStation
.bootNotificationRequest
,
147 skipBufferingOnError
: true,
151 return this.chargingStation
.bootNotificationResponse
;
155 BroadcastChannelProcedureName
.STATUS_NOTIFICATION
,
156 async (requestPayload
?: BroadcastChannelRequestPayload
) =>
157 this.chargingStation
.ocppRequestService
.requestHandler
<
158 StatusNotificationRequest
,
159 StatusNotificationResponse
161 this.chargingStation
,
162 RequestCommand
.STATUS_NOTIFICATION
,
168 BroadcastChannelProcedureName
.HEARTBEAT
,
169 async (requestPayload
?: BroadcastChannelRequestPayload
) =>
170 this.chargingStation
.ocppRequestService
.requestHandler
<
173 >(this.chargingStation
, RequestCommand
.HEARTBEAT
, requestPayload
, requestParams
),
176 BroadcastChannelProcedureName
.METER_VALUES
,
177 async (requestPayload
?: BroadcastChannelRequestPayload
) => {
178 const configuredMeterValueSampleInterval
=
179 ChargingStationConfigurationUtils
.getConfigurationKey(
181 StandardParametersKey
.MeterValueSampleInterval
183 return this.chargingStation
.ocppRequestService
.requestHandler
<
187 this.chargingStation
,
188 RequestCommand
.METER_VALUES
,
191 // FIXME: Implement OCPP version agnostic helpers
192 OCPP16ServiceUtils
.buildMeterValue(
193 this.chargingStation
,
194 requestPayload
.connectorId
,
195 this.chargingStation
.getConnectorStatus(requestPayload
.connectorId
)
197 configuredMeterValueSampleInterval
198 ? Utils
.convertToInt(configuredMeterValueSampleInterval
.value
) * 1000
199 : Constants
.DEFAULT_METER_VALUES_INTERVAL
209 BroadcastChannelProcedureName
.DATA_TRANSFER
,
210 async (requestPayload
?: BroadcastChannelRequestPayload
) =>
211 this.chargingStation
.ocppRequestService
.requestHandler
<
214 >(this.chargingStation
, RequestCommand
.DATA_TRANSFER
, requestPayload
, requestParams
),
217 BroadcastChannelProcedureName
.DIAGNOSTICS_STATUS_NOTIFICATION
,
218 async (requestPayload
?: BroadcastChannelRequestPayload
) =>
219 this.chargingStation
.ocppRequestService
.requestHandler
<
220 DiagnosticsStatusNotificationRequest
,
221 DiagnosticsStatusNotificationResponse
223 this.chargingStation
,
224 RequestCommand
.DIAGNOSTICS_STATUS_NOTIFICATION
,
230 BroadcastChannelProcedureName
.FIRMWARE_STATUS_NOTIFICATION
,
231 async (requestPayload
?: BroadcastChannelRequestPayload
) =>
232 this.chargingStation
.ocppRequestService
.requestHandler
<
233 FirmwareStatusNotificationRequest
,
234 FirmwareStatusNotificationResponse
236 this.chargingStation
,
237 RequestCommand
.FIRMWARE_STATUS_NOTIFICATION
,
243 this.chargingStation
= chargingStation
;
244 this.onmessage
= this.requestHandler
.bind(this) as (message
: MessageEvent
) => void;
245 this.onmessageerror
= this.messageErrorHandler
.bind(this) as (message
: MessageEvent
) => void;
248 private async requestHandler(messageEvent
: MessageEvent
): Promise
<void> {
249 const validatedMessageEvent
= this.validateMessageEvent(messageEvent
);
250 if (validatedMessageEvent
=== false) {
253 if (this.isResponse(validatedMessageEvent
.data
) === true) {
256 const [uuid
, command
, requestPayload
] = validatedMessageEvent
.data
as BroadcastChannelRequest
;
258 requestPayload
?.hashIds
!== undefined &&
259 requestPayload
?.hashIds
?.includes(this.chargingStation
.stationInfo
.hashId
) === false
263 if (requestPayload
?.hashId
!== undefined) {
265 `${this.chargingStation.logPrefix()} ${moduleName}.requestHandler: 'hashId' field usage in PDU is deprecated, use 'hashIds' array instead`
269 let responsePayload
: BroadcastChannelResponsePayload
;
270 let commandResponse
: CommandResponse
| void;
272 commandResponse
= await this.commandHandler(command
, requestPayload
);
274 commandResponse
=== undefined ||
275 commandResponse
=== null ||
276 Utils
.isEmptyObject(commandResponse
as CommandResponse
)
279 hashId
: this.chargingStation
.stationInfo
.hashId
,
280 status: ResponseStatus
.SUCCESS
,
283 responsePayload
= this.commandResponseToResponsePayload(
286 commandResponse
as CommandResponse
291 `${this.chargingStation.logPrefix()} ${moduleName}.requestHandler: Handle request error:`,
295 hashId
: this.chargingStation
.stationInfo
.hashId
,
296 status: ResponseStatus
.FAILURE
,
299 commandResponse
: commandResponse
as CommandResponse
,
300 errorMessage
: (error
as Error).message
,
301 errorStack
: (error
as Error).stack
,
302 errorDetails
: (error
as OCPPError
).details
,
305 this.sendResponse([uuid
, responsePayload
]);
309 private messageErrorHandler(messageEvent
: MessageEvent
): void {
311 `${this.chargingStation.logPrefix()} ${moduleName}.messageErrorHandler: Error at handling message:`,
316 private async commandHandler(
317 command
: BroadcastChannelProcedureName
,
318 requestPayload
: BroadcastChannelRequestPayload
319 ): Promise
<CommandResponse
| void> {
320 if (this.commandHandlers
.has(command
) === true) {
321 this.cleanRequestPayload(command
, requestPayload
);
322 return this.commandHandlers
.get(command
)(requestPayload
);
324 throw new BaseError(`Unknown worker broadcast channel command: ${command}`);
327 private cleanRequestPayload(
328 command
: BroadcastChannelProcedureName
,
329 requestPayload
: BroadcastChannelRequestPayload
331 delete requestPayload
.hashId
;
332 delete requestPayload
.hashIds
;
334 BroadcastChannelProcedureName
.START_AUTOMATIC_TRANSACTION_GENERATOR
,
335 BroadcastChannelProcedureName
.STOP_AUTOMATIC_TRANSACTION_GENERATOR
,
336 ].includes(command
) === false && delete requestPayload
.connectorIds
;
339 private commandResponseToResponsePayload(
340 command
: BroadcastChannelProcedureName
,
341 requestPayload
: BroadcastChannelRequestPayload
,
342 commandResponse
: CommandResponse
343 ): BroadcastChannelResponsePayload
{
344 const responseStatus
= this.commandResponseToResponseStatus(command
, commandResponse
);
345 if (responseStatus
=== ResponseStatus
.SUCCESS
) {
347 hashId
: this.chargingStation
.stationInfo
.hashId
,
348 status: responseStatus
,
352 hashId
: this.chargingStation
.stationInfo
.hashId
,
353 status: responseStatus
,
360 private commandResponseToResponseStatus(
361 command
: BroadcastChannelProcedureName
,
362 commandResponse
: CommandResponse
365 case BroadcastChannelProcedureName
.START_TRANSACTION
:
366 case BroadcastChannelProcedureName
.STOP_TRANSACTION
:
367 case BroadcastChannelProcedureName
.AUTHORIZE
:
371 | StartTransactionResponse
372 | StopTransactionResponse
374 )?.idTagInfo
?.status === AuthorizationStatus
.ACCEPTED
376 return ResponseStatus
.SUCCESS
;
378 return ResponseStatus
.FAILURE
;
379 case BroadcastChannelProcedureName
.BOOT_NOTIFICATION
:
380 if (commandResponse
?.status === RegistrationStatusEnumType
.ACCEPTED
) {
381 return ResponseStatus
.SUCCESS
;
383 return ResponseStatus
.FAILURE
;
384 case BroadcastChannelProcedureName
.DATA_TRANSFER
:
385 if (commandResponse
?.status === DataTransferStatus
.ACCEPTED
) {
386 return ResponseStatus
.SUCCESS
;
388 return ResponseStatus
.FAILURE
;
389 case BroadcastChannelProcedureName
.STATUS_NOTIFICATION
:
390 case BroadcastChannelProcedureName
.METER_VALUES
:
391 if (Utils
.isEmptyObject(commandResponse
) === true) {
392 return ResponseStatus
.SUCCESS
;
394 return ResponseStatus
.FAILURE
;
395 case BroadcastChannelProcedureName
.HEARTBEAT
:
396 if ('currentTime' in commandResponse
) {
397 return ResponseStatus
.SUCCESS
;
399 return ResponseStatus
.FAILURE
;
401 return ResponseStatus
.FAILURE
;