1 import BaseError from
'../exception/BaseError';
2 import type OCPPError from
'../exception/OCPPError';
3 import { StandardParametersKey
} from
'../types/ocpp/Configuration';
5 type BootNotificationRequest
,
6 type DataTransferRequest
,
7 type DiagnosticsStatusNotificationRequest
,
8 type FirmwareStatusNotificationRequest
,
10 type MeterValuesRequest
,
12 type StatusNotificationRequest
,
13 } from
'../types/ocpp/Requests';
15 type BootNotificationResponse
,
16 type DataTransferResponse
,
18 type DiagnosticsStatusNotificationResponse
,
19 type FirmwareStatusNotificationResponse
,
20 type HeartbeatResponse
,
21 type MeterValuesResponse
,
22 RegistrationStatusEnumType
,
23 type StatusNotificationResponse
,
24 } from
'../types/ocpp/Responses';
27 type AuthorizeRequest
,
28 type AuthorizeResponse
,
29 type StartTransactionRequest
,
30 type StartTransactionResponse
,
31 type StopTransactionRequest
,
32 type StopTransactionResponse
,
33 } from
'../types/ocpp/Transaction';
34 import { ResponseStatus
} from
'../types/UIProtocol';
36 BroadcastChannelProcedureName
,
37 type BroadcastChannelRequest
,
38 type BroadcastChannelRequestPayload
,
39 type BroadcastChannelResponsePayload
,
41 } from
'../types/WorkerBroadcastChannel';
42 import Constants from
'../utils/Constants';
43 import logger from
'../utils/Logger';
44 import Utils from
'../utils/Utils';
45 import type ChargingStation from
'./ChargingStation';
46 import { ChargingStationConfigurationUtils
} from
'./ChargingStationConfigurationUtils';
47 import { OCPP16ServiceUtils
} from
'./ocpp/1.6/OCPP16ServiceUtils';
48 import WorkerBroadcastChannel from
'./WorkerBroadcastChannel';
50 const moduleName
= 'ChargingStationWorkerBroadcastChannel';
52 type CommandResponse
=
53 | StartTransactionResponse
54 | StopTransactionResponse
56 | BootNotificationResponse
57 | StatusNotificationResponse
60 | DataTransferResponse
61 | DiagnosticsStatusNotificationResponse
62 | FirmwareStatusNotificationResponse
;
64 type CommandHandler
= (
65 requestPayload
?: BroadcastChannelRequestPayload
66 ) => Promise
<CommandResponse
| void> | void;
68 export default class ChargingStationWorkerBroadcastChannel
extends WorkerBroadcastChannel
{
69 private readonly commandHandlers
: Map
<BroadcastChannelProcedureName
, CommandHandler
>;
70 private readonly chargingStation
: ChargingStation
;
72 constructor(chargingStation
: ChargingStation
) {
74 this.commandHandlers
= new Map
<BroadcastChannelProcedureName
, CommandHandler
>([
75 [BroadcastChannelProcedureName
.START_CHARGING_STATION
, () => this.chargingStation
.start()],
77 BroadcastChannelProcedureName
.STOP_CHARGING_STATION
,
78 async () => this.chargingStation
.stop(),
81 BroadcastChannelProcedureName
.OPEN_CONNECTION
,
82 () => this.chargingStation
.openWSConnection(),
85 BroadcastChannelProcedureName
.CLOSE_CONNECTION
,
86 () => this.chargingStation
.closeWSConnection(),
89 BroadcastChannelProcedureName
.START_AUTOMATIC_TRANSACTION_GENERATOR
,
90 (requestPayload
?: BroadcastChannelRequestPayload
) =>
91 this.chargingStation
.startAutomaticTransactionGenerator(requestPayload
.connectorIds
),
94 BroadcastChannelProcedureName
.STOP_AUTOMATIC_TRANSACTION_GENERATOR
,
95 (requestPayload
?: BroadcastChannelRequestPayload
) =>
96 this.chargingStation
.stopAutomaticTransactionGenerator(requestPayload
.connectorIds
),
99 BroadcastChannelProcedureName
.START_TRANSACTION
,
100 async (requestPayload
?: BroadcastChannelRequestPayload
) =>
101 this.chargingStation
.ocppRequestService
.requestHandler
<
102 StartTransactionRequest
,
103 StartTransactionResponse
104 >(this.chargingStation
, RequestCommand
.START_TRANSACTION
, requestPayload
),
107 BroadcastChannelProcedureName
.STOP_TRANSACTION
,
108 async (requestPayload
?: BroadcastChannelRequestPayload
) =>
109 this.chargingStation
.ocppRequestService
.requestHandler
<
110 StopTransactionRequest
,
111 StartTransactionResponse
112 >(this.chargingStation
, RequestCommand
.STOP_TRANSACTION
, {
113 meterStop
: this.chargingStation
.getEnergyActiveImportRegisterByTransactionId(
114 requestPayload
.transactionId
,
121 BroadcastChannelProcedureName
.AUTHORIZE
,
122 async (requestPayload
?: BroadcastChannelRequestPayload
) =>
123 this.chargingStation
.ocppRequestService
.requestHandler
<
126 >(this.chargingStation
, RequestCommand
.AUTHORIZE
, requestPayload
),
129 BroadcastChannelProcedureName
.BOOT_NOTIFICATION
,
130 async (requestPayload
?: BroadcastChannelRequestPayload
) => {
131 this.chargingStation
.bootNotificationResponse
=
132 await this.chargingStation
.ocppRequestService
.requestHandler
<
133 BootNotificationRequest
,
134 BootNotificationResponse
136 this.chargingStation
,
137 RequestCommand
.BOOT_NOTIFICATION
,
139 ...this.chargingStation
.bootNotificationRequest
,
143 skipBufferingOnError
: true,
146 return this.chargingStation
.bootNotificationResponse
;
150 BroadcastChannelProcedureName
.STATUS_NOTIFICATION
,
151 async (requestPayload
?: BroadcastChannelRequestPayload
) =>
152 this.chargingStation
.ocppRequestService
.requestHandler
<
153 StatusNotificationRequest
,
154 StatusNotificationResponse
155 >(this.chargingStation
, RequestCommand
.STATUS_NOTIFICATION
, requestPayload
),
158 BroadcastChannelProcedureName
.HEARTBEAT
,
159 async (requestPayload
?: BroadcastChannelRequestPayload
) =>
160 this.chargingStation
.ocppRequestService
.requestHandler
<
163 >(this.chargingStation
, RequestCommand
.HEARTBEAT
, requestPayload
),
166 BroadcastChannelProcedureName
.METER_VALUES
,
167 async (requestPayload
?: BroadcastChannelRequestPayload
) => {
168 const configuredMeterValueSampleInterval
=
169 ChargingStationConfigurationUtils
.getConfigurationKey(
171 StandardParametersKey
.MeterValueSampleInterval
173 return this.chargingStation
.ocppRequestService
.requestHandler
<
176 >(this.chargingStation
, RequestCommand
.METER_VALUES
, {
178 OCPP16ServiceUtils
.buildMeterValue(
179 this.chargingStation
,
180 requestPayload
.connectorId
,
181 this.chargingStation
.getConnectorStatus(requestPayload
.connectorId
)?.transactionId
,
182 configuredMeterValueSampleInterval
183 ? Utils
.convertToInt(configuredMeterValueSampleInterval
.value
) * 1000
184 : Constants
.DEFAULT_METER_VALUES_INTERVAL
192 BroadcastChannelProcedureName
.DATA_TRANSFER
,
193 async (requestPayload
?: BroadcastChannelRequestPayload
) =>
194 this.chargingStation
.ocppRequestService
.requestHandler
<
197 >(this.chargingStation
, RequestCommand
.DATA_TRANSFER
, requestPayload
),
200 BroadcastChannelProcedureName
.DIAGNOSTICS_STATUS_NOTIFICATION
,
201 async (requestPayload
?: BroadcastChannelRequestPayload
) =>
202 this.chargingStation
.ocppRequestService
.requestHandler
<
203 DiagnosticsStatusNotificationRequest
,
204 DiagnosticsStatusNotificationResponse
205 >(this.chargingStation
, RequestCommand
.DIAGNOSTICS_STATUS_NOTIFICATION
, requestPayload
),
208 BroadcastChannelProcedureName
.FIRMWARE_STATUS_NOTIFICATION
,
209 async (requestPayload
?: BroadcastChannelRequestPayload
) =>
210 this.chargingStation
.ocppRequestService
.requestHandler
<
211 FirmwareStatusNotificationRequest
,
212 FirmwareStatusNotificationResponse
213 >(this.chargingStation
, RequestCommand
.FIRMWARE_STATUS_NOTIFICATION
, requestPayload
),
216 this.chargingStation
= chargingStation
;
217 this.onmessage
= this.requestHandler
.bind(this) as (message
: MessageEvent
) => void;
218 this.onmessageerror
= this.messageErrorHandler
.bind(this) as (message
: MessageEvent
) => void;
221 private async requestHandler(messageEvent
: MessageEvent
): Promise
<void> {
222 const validatedMessageEvent
= this.validateMessageEvent(messageEvent
);
223 if (validatedMessageEvent
=== false) {
226 if (this.isResponse(validatedMessageEvent
.data
) === true) {
229 const [uuid
, command
, requestPayload
] = validatedMessageEvent
.data
as BroadcastChannelRequest
;
231 requestPayload
?.hashIds
!== undefined &&
232 requestPayload
?.hashIds
?.includes(this.chargingStation
.stationInfo
.hashId
) === false
236 if (requestPayload
?.hashId
!== undefined) {
238 `${this.chargingStation.logPrefix()} ${moduleName}.requestHandler: 'hashId' field usage in PDU is deprecated, use 'hashIds' array instead`
242 let responsePayload
: BroadcastChannelResponsePayload
;
243 let commandResponse
: CommandResponse
| void;
245 commandResponse
= await this.commandHandler(command
, requestPayload
);
247 commandResponse
=== undefined ||
248 commandResponse
=== null ||
249 Utils
.isEmptyObject(commandResponse
as CommandResponse
)
252 hashId
: this.chargingStation
.stationInfo
.hashId
,
253 status: ResponseStatus
.SUCCESS
,
256 responsePayload
= this.commandResponseToResponsePayload(
259 commandResponse
as CommandResponse
264 `${this.chargingStation.logPrefix()} ${moduleName}.requestHandler: Handle request error:`,
268 hashId
: this.chargingStation
.stationInfo
.hashId
,
269 status: ResponseStatus
.FAILURE
,
272 commandResponse
: commandResponse
as CommandResponse
,
273 errorMessage
: (error
as Error).message
,
274 errorStack
: (error
as Error).stack
,
275 errorDetails
: (error
as OCPPError
).details
,
278 this.sendResponse([uuid
, responsePayload
]);
282 private messageErrorHandler(messageEvent
: MessageEvent
): void {
284 `${this.chargingStation.logPrefix()} ${moduleName}.messageErrorHandler: Error at handling message:`,
289 private async commandHandler(
290 command
: BroadcastChannelProcedureName
,
291 requestPayload
: BroadcastChannelRequestPayload
292 ): Promise
<CommandResponse
| void> {
293 if (this.commandHandlers
.has(command
) === true) {
294 this.cleanRequestPayload(command
, requestPayload
);
295 return this.commandHandlers
.get(command
)(requestPayload
);
297 throw new BaseError(`Unknown worker broadcast channel command: ${command}`);
300 private cleanRequestPayload(
301 command
: BroadcastChannelProcedureName
,
302 requestPayload
: BroadcastChannelRequestPayload
304 delete requestPayload
.hashId
;
305 delete requestPayload
.hashIds
;
307 BroadcastChannelProcedureName
.START_AUTOMATIC_TRANSACTION_GENERATOR
,
308 BroadcastChannelProcedureName
.STOP_AUTOMATIC_TRANSACTION_GENERATOR
,
309 ].includes(command
) === false && delete requestPayload
.connectorIds
;
312 private commandResponseToResponsePayload(
313 command
: BroadcastChannelProcedureName
,
314 requestPayload
: BroadcastChannelRequestPayload
,
315 commandResponse
: CommandResponse
316 ): BroadcastChannelResponsePayload
{
317 const responseStatus
= this.commandResponseToResponseStatus(command
, commandResponse
);
318 if (responseStatus
=== ResponseStatus
.SUCCESS
) {
320 hashId
: this.chargingStation
.stationInfo
.hashId
,
321 status: responseStatus
,
325 hashId
: this.chargingStation
.stationInfo
.hashId
,
326 status: responseStatus
,
333 private commandResponseToResponseStatus(
334 command
: BroadcastChannelProcedureName
,
335 commandResponse
: CommandResponse
338 case BroadcastChannelProcedureName
.START_TRANSACTION
:
339 case BroadcastChannelProcedureName
.STOP_TRANSACTION
:
340 case BroadcastChannelProcedureName
.AUTHORIZE
:
344 | StartTransactionResponse
345 | StopTransactionResponse
347 )?.idTagInfo
?.status === AuthorizationStatus
.ACCEPTED
349 return ResponseStatus
.SUCCESS
;
351 return ResponseStatus
.FAILURE
;
352 case BroadcastChannelProcedureName
.BOOT_NOTIFICATION
:
353 if (commandResponse
?.status === RegistrationStatusEnumType
.ACCEPTED
) {
354 return ResponseStatus
.SUCCESS
;
356 return ResponseStatus
.FAILURE
;
357 case BroadcastChannelProcedureName
.DATA_TRANSFER
:
358 if (commandResponse
?.status === DataTransferStatus
.ACCEPTED
) {
359 return ResponseStatus
.SUCCESS
;
361 return ResponseStatus
.FAILURE
;
362 case BroadcastChannelProcedureName
.STATUS_NOTIFICATION
:
363 case BroadcastChannelProcedureName
.METER_VALUES
:
364 if (Utils
.isEmptyObject(commandResponse
) === true) {
365 return ResponseStatus
.SUCCESS
;
367 return ResponseStatus
.FAILURE
;
368 case BroadcastChannelProcedureName
.HEARTBEAT
:
369 if ('currentTime' in commandResponse
) {
370 return ResponseStatus
.SUCCESS
;
372 return ResponseStatus
.FAILURE
;
374 return ResponseStatus
.FAILURE
;