1 import type ChargingStation from
'./ChargingStation';
2 import { ChargingStationConfigurationUtils
} from
'./ChargingStationConfigurationUtils';
3 import { OCPP16ServiceUtils
} from
'./ocpp/1.6/OCPP16ServiceUtils';
4 import WorkerBroadcastChannel from
'./WorkerBroadcastChannel';
5 import BaseError from
'../exception/BaseError';
6 import type OCPPError from
'../exception/OCPPError';
7 import { StandardParametersKey
} from
'../types/ocpp/Configuration';
9 type BootNotificationRequest
,
10 type DataTransferRequest
,
11 type DiagnosticsStatusNotificationRequest
,
12 type FirmwareStatusNotificationRequest
,
13 type HeartbeatRequest
,
14 type MeterValuesRequest
,
17 type StatusNotificationRequest
,
18 } from
'../types/ocpp/Requests';
20 type BootNotificationResponse
,
21 type DataTransferResponse
,
23 type DiagnosticsStatusNotificationResponse
,
24 type FirmwareStatusNotificationResponse
,
25 type HeartbeatResponse
,
26 type MeterValuesResponse
,
27 RegistrationStatusEnumType
,
28 type StatusNotificationResponse
,
29 } from
'../types/ocpp/Responses';
32 type AuthorizeRequest
,
33 type AuthorizeResponse
,
34 type StartTransactionRequest
,
35 type StartTransactionResponse
,
36 type StopTransactionRequest
,
37 type StopTransactionResponse
,
38 } from
'../types/ocpp/Transaction';
39 import { ResponseStatus
} from
'../types/UIProtocol';
41 BroadcastChannelProcedureName
,
42 type BroadcastChannelRequest
,
43 type BroadcastChannelRequestPayload
,
44 type BroadcastChannelResponsePayload
,
46 } from
'../types/WorkerBroadcastChannel';
47 import Constants from
'../utils/Constants';
48 import logger from
'../utils/Logger';
49 import Utils from
'../utils/Utils';
51 const moduleName
= 'ChargingStationWorkerBroadcastChannel';
53 type CommandResponse
=
54 | StartTransactionResponse
55 | StopTransactionResponse
57 | BootNotificationResponse
58 | StatusNotificationResponse
61 | DataTransferResponse
62 | DiagnosticsStatusNotificationResponse
63 | FirmwareStatusNotificationResponse
;
65 type CommandHandler
= (
66 requestPayload
?: BroadcastChannelRequestPayload
67 ) => Promise
<CommandResponse
| void> | void;
69 export default class ChargingStationWorkerBroadcastChannel
extends WorkerBroadcastChannel
{
70 private readonly commandHandlers
: Map
<BroadcastChannelProcedureName
, CommandHandler
>;
71 private readonly chargingStation
: ChargingStation
;
73 constructor(chargingStation
: ChargingStation
) {
75 const requestParams
: RequestParams
= {
78 this.commandHandlers
= new Map
<BroadcastChannelProcedureName
, CommandHandler
>([
79 [BroadcastChannelProcedureName
.START_CHARGING_STATION
, () => this.chargingStation
.start()],
81 BroadcastChannelProcedureName
.STOP_CHARGING_STATION
,
82 async () => this.chargingStation
.stop(),
85 BroadcastChannelProcedureName
.OPEN_CONNECTION
,
86 () => this.chargingStation
.openWSConnection(),
89 BroadcastChannelProcedureName
.CLOSE_CONNECTION
,
90 () => this.chargingStation
.closeWSConnection(),
93 BroadcastChannelProcedureName
.START_AUTOMATIC_TRANSACTION_GENERATOR
,
94 (requestPayload
?: BroadcastChannelRequestPayload
) =>
95 this.chargingStation
.startAutomaticTransactionGenerator(requestPayload
.connectorIds
),
98 BroadcastChannelProcedureName
.STOP_AUTOMATIC_TRANSACTION_GENERATOR
,
99 (requestPayload
?: BroadcastChannelRequestPayload
) =>
100 this.chargingStation
.stopAutomaticTransactionGenerator(requestPayload
.connectorIds
),
103 BroadcastChannelProcedureName
.START_TRANSACTION
,
104 async (requestPayload
?: BroadcastChannelRequestPayload
) =>
105 this.chargingStation
.ocppRequestService
.requestHandler
<
106 StartTransactionRequest
,
107 StartTransactionResponse
108 >(this.chargingStation
, RequestCommand
.START_TRANSACTION
, requestPayload
, requestParams
),
111 BroadcastChannelProcedureName
.STOP_TRANSACTION
,
112 async (requestPayload
?: BroadcastChannelRequestPayload
) =>
113 this.chargingStation
.ocppRequestService
.requestHandler
<
114 StopTransactionRequest
,
115 StartTransactionResponse
117 this.chargingStation
,
118 RequestCommand
.STOP_TRANSACTION
,
120 meterStop
: this.chargingStation
.getEnergyActiveImportRegisterByTransactionId(
121 requestPayload
.transactionId
,
130 BroadcastChannelProcedureName
.AUTHORIZE
,
131 async (requestPayload
?: BroadcastChannelRequestPayload
) =>
132 this.chargingStation
.ocppRequestService
.requestHandler
<
135 >(this.chargingStation
, RequestCommand
.AUTHORIZE
, requestPayload
, requestParams
),
138 BroadcastChannelProcedureName
.BOOT_NOTIFICATION
,
139 async (requestPayload
?: BroadcastChannelRequestPayload
) => {
140 this.chargingStation
.bootNotificationResponse
=
141 await this.chargingStation
.ocppRequestService
.requestHandler
<
142 BootNotificationRequest
,
143 BootNotificationResponse
145 this.chargingStation
,
146 RequestCommand
.BOOT_NOTIFICATION
,
148 ...this.chargingStation
.bootNotificationRequest
,
152 skipBufferingOnError
: true,
156 return this.chargingStation
.bootNotificationResponse
;
160 BroadcastChannelProcedureName
.STATUS_NOTIFICATION
,
161 async (requestPayload
?: BroadcastChannelRequestPayload
) =>
162 this.chargingStation
.ocppRequestService
.requestHandler
<
163 StatusNotificationRequest
,
164 StatusNotificationResponse
166 this.chargingStation
,
167 RequestCommand
.STATUS_NOTIFICATION
,
173 BroadcastChannelProcedureName
.HEARTBEAT
,
174 async (requestPayload
?: BroadcastChannelRequestPayload
) =>
175 this.chargingStation
.ocppRequestService
.requestHandler
<
178 >(this.chargingStation
, RequestCommand
.HEARTBEAT
, requestPayload
, requestParams
),
181 BroadcastChannelProcedureName
.METER_VALUES
,
182 async (requestPayload
?: BroadcastChannelRequestPayload
) => {
183 const configuredMeterValueSampleInterval
=
184 ChargingStationConfigurationUtils
.getConfigurationKey(
186 StandardParametersKey
.MeterValueSampleInterval
188 return this.chargingStation
.ocppRequestService
.requestHandler
<
192 this.chargingStation
,
193 RequestCommand
.METER_VALUES
,
196 // FIXME: Implement OCPP version agnostic helpers
197 OCPP16ServiceUtils
.buildMeterValue(
198 this.chargingStation
,
199 requestPayload
.connectorId
,
200 this.chargingStation
.getConnectorStatus(requestPayload
.connectorId
)
202 configuredMeterValueSampleInterval
203 ? Utils
.convertToInt(configuredMeterValueSampleInterval
.value
) * 1000
204 : Constants
.DEFAULT_METER_VALUES_INTERVAL
214 BroadcastChannelProcedureName
.DATA_TRANSFER
,
215 async (requestPayload
?: BroadcastChannelRequestPayload
) =>
216 this.chargingStation
.ocppRequestService
.requestHandler
<
219 >(this.chargingStation
, RequestCommand
.DATA_TRANSFER
, requestPayload
, requestParams
),
222 BroadcastChannelProcedureName
.DIAGNOSTICS_STATUS_NOTIFICATION
,
223 async (requestPayload
?: BroadcastChannelRequestPayload
) =>
224 this.chargingStation
.ocppRequestService
.requestHandler
<
225 DiagnosticsStatusNotificationRequest
,
226 DiagnosticsStatusNotificationResponse
228 this.chargingStation
,
229 RequestCommand
.DIAGNOSTICS_STATUS_NOTIFICATION
,
235 BroadcastChannelProcedureName
.FIRMWARE_STATUS_NOTIFICATION
,
236 async (requestPayload
?: BroadcastChannelRequestPayload
) =>
237 this.chargingStation
.ocppRequestService
.requestHandler
<
238 FirmwareStatusNotificationRequest
,
239 FirmwareStatusNotificationResponse
241 this.chargingStation
,
242 RequestCommand
.FIRMWARE_STATUS_NOTIFICATION
,
248 this.chargingStation
= chargingStation
;
249 this.onmessage
= this.requestHandler
.bind(this) as (message
: MessageEvent
) => void;
250 this.onmessageerror
= this.messageErrorHandler
.bind(this) as (message
: MessageEvent
) => void;
253 private async requestHandler(messageEvent
: MessageEvent
): Promise
<void> {
254 const validatedMessageEvent
= this.validateMessageEvent(messageEvent
);
255 if (validatedMessageEvent
=== false) {
258 if (this.isResponse(validatedMessageEvent
.data
) === true) {
261 const [uuid
, command
, requestPayload
] = validatedMessageEvent
.data
as BroadcastChannelRequest
;
263 requestPayload
?.hashIds
!== undefined &&
264 requestPayload
?.hashIds
?.includes(this.chargingStation
.stationInfo
.hashId
) === false
268 if (requestPayload
?.hashId
!== undefined) {
270 `${this.chargingStation.logPrefix()} ${moduleName}.requestHandler: 'hashId' field usage in PDU is deprecated, use 'hashIds' array instead`
274 let responsePayload
: BroadcastChannelResponsePayload
;
275 let commandResponse
: CommandResponse
| void;
277 commandResponse
= await this.commandHandler(command
, requestPayload
);
279 commandResponse
=== undefined ||
280 commandResponse
=== null ||
281 Utils
.isEmptyObject(commandResponse
as CommandResponse
)
284 hashId
: this.chargingStation
.stationInfo
.hashId
,
285 status: ResponseStatus
.SUCCESS
,
288 responsePayload
= this.commandResponseToResponsePayload(
291 commandResponse
as CommandResponse
296 `${this.chargingStation.logPrefix()} ${moduleName}.requestHandler: Handle request error:`,
300 hashId
: this.chargingStation
.stationInfo
.hashId
,
301 status: ResponseStatus
.FAILURE
,
304 commandResponse
: commandResponse
as CommandResponse
,
305 errorMessage
: (error
as Error).message
,
306 errorStack
: (error
as Error).stack
,
307 errorDetails
: (error
as OCPPError
).details
,
310 this.sendResponse([uuid
, responsePayload
]);
314 private messageErrorHandler(messageEvent
: MessageEvent
): void {
316 `${this.chargingStation.logPrefix()} ${moduleName}.messageErrorHandler: Error at handling message:`,
321 private async commandHandler(
322 command
: BroadcastChannelProcedureName
,
323 requestPayload
: BroadcastChannelRequestPayload
324 ): Promise
<CommandResponse
| void> {
325 if (this.commandHandlers
.has(command
) === true) {
326 this.cleanRequestPayload(command
, requestPayload
);
327 return this.commandHandlers
.get(command
)(requestPayload
);
329 throw new BaseError(`Unknown worker broadcast channel command: ${command}`);
332 private cleanRequestPayload(
333 command
: BroadcastChannelProcedureName
,
334 requestPayload
: BroadcastChannelRequestPayload
336 delete requestPayload
.hashId
;
337 delete requestPayload
.hashIds
;
339 BroadcastChannelProcedureName
.START_AUTOMATIC_TRANSACTION_GENERATOR
,
340 BroadcastChannelProcedureName
.STOP_AUTOMATIC_TRANSACTION_GENERATOR
,
341 ].includes(command
) === false && delete requestPayload
.connectorIds
;
344 private commandResponseToResponsePayload(
345 command
: BroadcastChannelProcedureName
,
346 requestPayload
: BroadcastChannelRequestPayload
,
347 commandResponse
: CommandResponse
348 ): BroadcastChannelResponsePayload
{
349 const responseStatus
= this.commandResponseToResponseStatus(command
, commandResponse
);
350 if (responseStatus
=== ResponseStatus
.SUCCESS
) {
352 hashId
: this.chargingStation
.stationInfo
.hashId
,
353 status: responseStatus
,
357 hashId
: this.chargingStation
.stationInfo
.hashId
,
358 status: responseStatus
,
365 private commandResponseToResponseStatus(
366 command
: BroadcastChannelProcedureName
,
367 commandResponse
: CommandResponse
370 case BroadcastChannelProcedureName
.START_TRANSACTION
:
371 case BroadcastChannelProcedureName
.STOP_TRANSACTION
:
372 case BroadcastChannelProcedureName
.AUTHORIZE
:
376 | StartTransactionResponse
377 | StopTransactionResponse
379 )?.idTagInfo
?.status === AuthorizationStatus
.ACCEPTED
381 return ResponseStatus
.SUCCESS
;
383 return ResponseStatus
.FAILURE
;
384 case BroadcastChannelProcedureName
.BOOT_NOTIFICATION
:
385 if (commandResponse
?.status === RegistrationStatusEnumType
.ACCEPTED
) {
386 return ResponseStatus
.SUCCESS
;
388 return ResponseStatus
.FAILURE
;
389 case BroadcastChannelProcedureName
.DATA_TRANSFER
:
390 if (commandResponse
?.status === DataTransferStatus
.ACCEPTED
) {
391 return ResponseStatus
.SUCCESS
;
393 return ResponseStatus
.FAILURE
;
394 case BroadcastChannelProcedureName
.STATUS_NOTIFICATION
:
395 case BroadcastChannelProcedureName
.METER_VALUES
:
396 if (Utils
.isEmptyObject(commandResponse
) === true) {
397 return ResponseStatus
.SUCCESS
;
399 return ResponseStatus
.FAILURE
;
400 case BroadcastChannelProcedureName
.HEARTBEAT
:
401 if ('currentTime' in commandResponse
) {
402 return ResponseStatus
.SUCCESS
;
404 return ResponseStatus
.FAILURE
;
406 return ResponseStatus
.FAILURE
;