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
,
8 type MeterValuesRequest
,
10 type StatusNotificationRequest
,
11 } from
'../types/ocpp/Requests';
13 type BootNotificationResponse
,
14 type DataTransferResponse
,
16 type HeartbeatResponse
,
17 type MeterValuesResponse
,
18 RegistrationStatusEnumType
,
19 type StatusNotificationResponse
,
20 } from
'../types/ocpp/Responses';
23 type AuthorizeRequest
,
24 type AuthorizeResponse
,
25 type StartTransactionRequest
,
26 type StartTransactionResponse
,
27 type StopTransactionRequest
,
28 type StopTransactionResponse
,
29 } from
'../types/ocpp/Transaction';
30 import { ResponseStatus
} from
'../types/UIProtocol';
32 BroadcastChannelProcedureName
,
33 type BroadcastChannelRequest
,
34 type BroadcastChannelRequestPayload
,
35 type BroadcastChannelResponsePayload
,
37 } from
'../types/WorkerBroadcastChannel';
38 import Constants from
'../utils/Constants';
39 import logger from
'../utils/Logger';
40 import Utils from
'../utils/Utils';
41 import type ChargingStation from
'./ChargingStation';
42 import { ChargingStationConfigurationUtils
} from
'./ChargingStationConfigurationUtils';
43 import { OCPP16ServiceUtils
} from
'./ocpp/1.6/OCPP16ServiceUtils';
44 import WorkerBroadcastChannel from
'./WorkerBroadcastChannel';
46 const moduleName
= 'ChargingStationWorkerBroadcastChannel';
48 type CommandResponse
=
49 | StartTransactionResponse
50 | StopTransactionResponse
52 | BootNotificationResponse
53 | StatusNotificationResponse
56 | DataTransferResponse
;
58 type CommandHandler
= (
59 requestPayload
?: BroadcastChannelRequestPayload
60 ) => Promise
<CommandResponse
| void> | void;
62 export default class ChargingStationWorkerBroadcastChannel
extends WorkerBroadcastChannel
{
63 private readonly commandHandlers
: Map
<BroadcastChannelProcedureName
, CommandHandler
>;
64 private readonly chargingStation
: ChargingStation
;
66 constructor(chargingStation
: ChargingStation
) {
68 this.commandHandlers
= new Map
<BroadcastChannelProcedureName
, CommandHandler
>([
69 [BroadcastChannelProcedureName
.START_CHARGING_STATION
, () => this.chargingStation
.start()],
71 BroadcastChannelProcedureName
.STOP_CHARGING_STATION
,
72 async () => this.chargingStation
.stop(),
75 BroadcastChannelProcedureName
.OPEN_CONNECTION
,
76 () => this.chargingStation
.openWSConnection(),
79 BroadcastChannelProcedureName
.CLOSE_CONNECTION
,
80 () => this.chargingStation
.closeWSConnection(),
83 BroadcastChannelProcedureName
.START_AUTOMATIC_TRANSACTION_GENERATOR
,
84 (requestPayload
?: BroadcastChannelRequestPayload
) =>
85 this.chargingStation
.startAutomaticTransactionGenerator(requestPayload
.connectorIds
),
88 BroadcastChannelProcedureName
.STOP_AUTOMATIC_TRANSACTION_GENERATOR
,
89 (requestPayload
?: BroadcastChannelRequestPayload
) =>
90 this.chargingStation
.stopAutomaticTransactionGenerator(requestPayload
.connectorIds
),
93 BroadcastChannelProcedureName
.START_TRANSACTION
,
94 async (requestPayload
?: BroadcastChannelRequestPayload
) =>
95 this.chargingStation
.ocppRequestService
.requestHandler
<
96 StartTransactionRequest
,
97 StartTransactionResponse
98 >(this.chargingStation
, RequestCommand
.START_TRANSACTION
, requestPayload
),
101 BroadcastChannelProcedureName
.STOP_TRANSACTION
,
102 async (requestPayload
?: BroadcastChannelRequestPayload
) =>
103 this.chargingStation
.ocppRequestService
.requestHandler
<
104 StopTransactionRequest
,
105 StartTransactionResponse
106 >(this.chargingStation
, RequestCommand
.STOP_TRANSACTION
, {
107 meterStop
: this.chargingStation
.getEnergyActiveImportRegisterByTransactionId(
108 requestPayload
.transactionId
,
115 BroadcastChannelProcedureName
.AUTHORIZE
,
116 async (requestPayload
?: BroadcastChannelRequestPayload
) =>
117 this.chargingStation
.ocppRequestService
.requestHandler
<
120 >(this.chargingStation
, RequestCommand
.AUTHORIZE
, requestPayload
),
123 BroadcastChannelProcedureName
.BOOT_NOTIFICATION
,
124 async (requestPayload
?: BroadcastChannelRequestPayload
) => {
125 this.chargingStation
.bootNotificationResponse
=
126 await this.chargingStation
.ocppRequestService
.requestHandler
<
127 BootNotificationRequest
,
128 BootNotificationResponse
130 this.chargingStation
,
131 RequestCommand
.BOOT_NOTIFICATION
,
133 ...this.chargingStation
.bootNotificationRequest
,
137 skipBufferingOnError
: true,
140 return this.chargingStation
.bootNotificationResponse
;
144 BroadcastChannelProcedureName
.STATUS_NOTIFICATION
,
145 async (requestPayload
?: BroadcastChannelRequestPayload
) =>
146 this.chargingStation
.ocppRequestService
.requestHandler
<
147 StatusNotificationRequest
,
148 StatusNotificationResponse
149 >(this.chargingStation
, RequestCommand
.STATUS_NOTIFICATION
, requestPayload
),
152 BroadcastChannelProcedureName
.HEARTBEAT
,
153 async (requestPayload
?: BroadcastChannelRequestPayload
) =>
154 this.chargingStation
.ocppRequestService
.requestHandler
<
157 >(this.chargingStation
, RequestCommand
.HEARTBEAT
, requestPayload
),
160 BroadcastChannelProcedureName
.METER_VALUES
,
161 async (requestPayload
?: BroadcastChannelRequestPayload
) => {
162 const configuredMeterValueSampleInterval
=
163 ChargingStationConfigurationUtils
.getConfigurationKey(
165 StandardParametersKey
.MeterValueSampleInterval
167 return this.chargingStation
.ocppRequestService
.requestHandler
<
170 >(this.chargingStation
, RequestCommand
.METER_VALUES
, {
172 OCPP16ServiceUtils
.buildMeterValue(
173 this.chargingStation
,
174 requestPayload
.connectorId
,
175 this.chargingStation
.getConnectorStatus(requestPayload
.connectorId
)?.transactionId
,
176 configuredMeterValueSampleInterval
177 ? Utils
.convertToInt(configuredMeterValueSampleInterval
.value
) * 1000
178 : Constants
.DEFAULT_METER_VALUES_INTERVAL
186 BroadcastChannelProcedureName
.DATA_TRANSFER
,
187 async (requestPayload
?: BroadcastChannelRequestPayload
) =>
188 this.chargingStation
.ocppRequestService
.requestHandler
<
191 >(this.chargingStation
, RequestCommand
.DATA_TRANSFER
, requestPayload
),
194 this.chargingStation
= chargingStation
;
195 this.onmessage
= this.requestHandler
.bind(this) as (message
: MessageEvent
) => void;
196 this.onmessageerror
= this.messageErrorHandler
.bind(this) as (message
: MessageEvent
) => void;
199 private async requestHandler(messageEvent
: MessageEvent
): Promise
<void> {
200 const validatedMessageEvent
= this.validateMessageEvent(messageEvent
);
201 if (validatedMessageEvent
=== false) {
204 if (this.isResponse(validatedMessageEvent
.data
) === true) {
207 const [uuid
, command
, requestPayload
] = validatedMessageEvent
.data
as BroadcastChannelRequest
;
209 requestPayload
?.hashIds
!== undefined &&
210 requestPayload
?.hashIds
?.includes(this.chargingStation
.stationInfo
.hashId
) === false
214 if (requestPayload
?.hashId
!== undefined) {
216 `${this.chargingStation.logPrefix()} ${moduleName}.requestHandler: 'hashId' field usage in PDU is deprecated, use 'hashIds' array instead`
220 let responsePayload
: BroadcastChannelResponsePayload
;
221 let commandResponse
: CommandResponse
| void;
223 commandResponse
= await this.commandHandler(command
, requestPayload
);
224 if (commandResponse
=== undefined || commandResponse
=== null) {
226 hashId
: this.chargingStation
.stationInfo
.hashId
,
227 status: ResponseStatus
.SUCCESS
,
230 responsePayload
= this.commandResponseToResponsePayload(
233 commandResponse
as CommandResponse
238 `${this.chargingStation.logPrefix()} ${moduleName}.requestHandler: Handle request error:`,
242 hashId
: this.chargingStation
.stationInfo
.hashId
,
243 status: ResponseStatus
.FAILURE
,
246 commandResponse
: commandResponse
as CommandResponse
,
247 errorMessage
: (error
as Error).message
,
248 errorStack
: (error
as Error).stack
,
249 errorDetails
: (error
as OCPPError
).details
,
252 this.sendResponse([uuid
, responsePayload
]);
256 private messageErrorHandler(messageEvent
: MessageEvent
): void {
258 `${this.chargingStation.logPrefix()} ${moduleName}.messageErrorHandler: Error at handling message:`,
263 private async commandHandler(
264 command
: BroadcastChannelProcedureName
,
265 requestPayload
: BroadcastChannelRequestPayload
266 ): Promise
<CommandResponse
| void> {
267 if (this.commandHandlers
.has(command
) === true) {
268 this.cleanRequestPayload(command
, requestPayload
);
269 return this.commandHandlers
.get(command
)(requestPayload
);
271 throw new BaseError(`Unknown worker broadcast channel command: ${command}`);
274 private cleanRequestPayload(
275 command
: BroadcastChannelProcedureName
,
276 requestPayload
: BroadcastChannelRequestPayload
278 delete requestPayload
.hashId
;
279 delete requestPayload
.hashIds
;
281 BroadcastChannelProcedureName
.START_AUTOMATIC_TRANSACTION_GENERATOR
,
282 BroadcastChannelProcedureName
.STOP_AUTOMATIC_TRANSACTION_GENERATOR
,
283 ].includes(command
) === false && delete requestPayload
.connectorIds
;
286 private commandResponseToResponsePayload(
287 command
: BroadcastChannelProcedureName
,
288 requestPayload
: BroadcastChannelRequestPayload
,
289 commandResponse
: CommandResponse
290 ): BroadcastChannelResponsePayload
{
291 const responseStatus
= this.commandResponseToResponseStatus(command
, commandResponse
);
292 if (responseStatus
=== ResponseStatus
.SUCCESS
) {
294 hashId
: this.chargingStation
.stationInfo
.hashId
,
295 status: responseStatus
,
299 hashId
: this.chargingStation
.stationInfo
.hashId
,
300 status: responseStatus
,
307 private commandResponseToResponseStatus(
308 command
: BroadcastChannelProcedureName
,
309 commandResponse
: CommandResponse
312 case BroadcastChannelProcedureName
.START_TRANSACTION
:
313 case BroadcastChannelProcedureName
.STOP_TRANSACTION
:
314 case BroadcastChannelProcedureName
.AUTHORIZE
:
318 | StartTransactionResponse
319 | StopTransactionResponse
321 )?.idTagInfo
?.status === AuthorizationStatus
.ACCEPTED
323 return ResponseStatus
.SUCCESS
;
325 return ResponseStatus
.FAILURE
;
326 case BroadcastChannelProcedureName
.BOOT_NOTIFICATION
:
327 if (commandResponse
?.status === RegistrationStatusEnumType
.ACCEPTED
) {
328 return ResponseStatus
.SUCCESS
;
330 return ResponseStatus
.FAILURE
;
331 case BroadcastChannelProcedureName
.DATA_TRANSFER
:
332 if (commandResponse
?.status === DataTransferStatus
.ACCEPTED
) {
333 return ResponseStatus
.SUCCESS
;
335 return ResponseStatus
.FAILURE
;
336 case BroadcastChannelProcedureName
.STATUS_NOTIFICATION
:
337 case BroadcastChannelProcedureName
.METER_VALUES
:
338 if (Utils
.isEmptyObject(commandResponse
) === true) {
339 return ResponseStatus
.SUCCESS
;
341 return ResponseStatus
.FAILURE
;
342 case BroadcastChannelProcedureName
.HEARTBEAT
:
343 if ('currentTime' in commandResponse
) {
344 return ResponseStatus
.SUCCESS
;
346 return ResponseStatus
.FAILURE
;
348 return ResponseStatus
.FAILURE
;