1 import BaseError from
'../exception/BaseError';
2 import type OCPPError from
'../exception/OCPPError';
3 import { StandardParametersKey
} from
'../types/ocpp/Configuration';
5 type BootNotificationRequest
,
7 type MeterValuesRequest
,
9 type StatusNotificationRequest
,
10 } from
'../types/ocpp/Requests';
12 type BootNotificationResponse
,
13 type HeartbeatResponse
,
14 type MeterValuesResponse
,
16 type StatusNotificationResponse
,
17 } from
'../types/ocpp/Responses';
20 type AuthorizeRequest
,
21 type AuthorizeResponse
,
22 type StartTransactionRequest
,
23 type StartTransactionResponse
,
24 type StopTransactionRequest
,
25 type StopTransactionResponse
,
26 } from
'../types/ocpp/Transaction';
28 BroadcastChannelProcedureName
,
29 type BroadcastChannelRequest
,
30 type BroadcastChannelRequestPayload
,
31 type BroadcastChannelResponsePayload
,
33 } from
'../types/WorkerBroadcastChannel';
34 import { ResponseStatus
} from
'../ui/web/src/types/UIProtocol';
35 import Constants from
'../utils/Constants';
36 import logger from
'../utils/Logger';
37 import Utils from
'../utils/Utils';
38 import type ChargingStation from
'./ChargingStation';
39 import { ChargingStationConfigurationUtils
} from
'./ChargingStationConfigurationUtils';
40 import { OCPP16ServiceUtils
} from
'./ocpp/1.6/OCPP16ServiceUtils';
41 import WorkerBroadcastChannel from
'./WorkerBroadcastChannel';
43 const moduleName
= 'ChargingStationWorkerBroadcastChannel';
45 type CommandResponse
=
46 | StartTransactionResponse
47 | StopTransactionResponse
49 | BootNotificationResponse
50 | StatusNotificationResponse
52 | MeterValuesResponse
;
54 type CommandHandler
= (
55 requestPayload
?: BroadcastChannelRequestPayload
56 ) => Promise
<CommandResponse
| void> | void;
58 export default class ChargingStationWorkerBroadcastChannel
extends WorkerBroadcastChannel
{
59 private readonly commandHandlers
: Map
<BroadcastChannelProcedureName
, CommandHandler
>;
60 private readonly chargingStation
: ChargingStation
;
62 constructor(chargingStation
: ChargingStation
) {
64 this.commandHandlers
= new Map
<BroadcastChannelProcedureName
, CommandHandler
>([
65 [BroadcastChannelProcedureName
.START_CHARGING_STATION
, () => this.chargingStation
.start()],
67 BroadcastChannelProcedureName
.STOP_CHARGING_STATION
,
68 async () => this.chargingStation
.stop(),
71 BroadcastChannelProcedureName
.OPEN_CONNECTION
,
72 () => this.chargingStation
.openWSConnection(),
75 BroadcastChannelProcedureName
.CLOSE_CONNECTION
,
76 () => this.chargingStation
.closeWSConnection(),
79 BroadcastChannelProcedureName
.START_AUTOMATIC_TRANSACTION_GENERATOR
,
80 (requestPayload
?: BroadcastChannelRequestPayload
) =>
81 this.chargingStation
.startAutomaticTransactionGenerator(requestPayload
.connectorIds
),
84 BroadcastChannelProcedureName
.STOP_AUTOMATIC_TRANSACTION_GENERATOR
,
85 (requestPayload
?: BroadcastChannelRequestPayload
) =>
86 this.chargingStation
.stopAutomaticTransactionGenerator(requestPayload
.connectorIds
),
89 BroadcastChannelProcedureName
.START_TRANSACTION
,
90 async (requestPayload
?: BroadcastChannelRequestPayload
) =>
91 this.chargingStation
.ocppRequestService
.requestHandler
<
92 StartTransactionRequest
,
93 StartTransactionResponse
94 >(this.chargingStation
, RequestCommand
.START_TRANSACTION
, requestPayload
),
97 BroadcastChannelProcedureName
.STOP_TRANSACTION
,
98 async (requestPayload
?: BroadcastChannelRequestPayload
) =>
99 this.chargingStation
.ocppRequestService
.requestHandler
<
100 StopTransactionRequest
,
101 StartTransactionResponse
102 >(this.chargingStation
, RequestCommand
.STOP_TRANSACTION
, {
103 meterStop
: this.chargingStation
.getEnergyActiveImportRegisterByTransactionId(
104 requestPayload
.transactionId
,
111 BroadcastChannelProcedureName
.AUTHORIZE
,
112 async (requestPayload
?: BroadcastChannelRequestPayload
) =>
113 this.chargingStation
.ocppRequestService
.requestHandler
<
116 >(this.chargingStation
, RequestCommand
.AUTHORIZE
, requestPayload
),
119 BroadcastChannelProcedureName
.BOOT_NOTIFICATION
,
120 async (requestPayload
?: BroadcastChannelRequestPayload
) => {
121 this.chargingStation
.bootNotificationResponse
=
122 await this.chargingStation
.ocppRequestService
.requestHandler
<
123 BootNotificationRequest
,
124 BootNotificationResponse
126 this.chargingStation
,
127 RequestCommand
.BOOT_NOTIFICATION
,
129 ...this.chargingStation
.bootNotificationRequest
,
133 skipBufferingOnError
: true,
136 return this.chargingStation
.bootNotificationResponse
;
140 BroadcastChannelProcedureName
.STATUS_NOTIFICATION
,
141 async (requestPayload
?: BroadcastChannelRequestPayload
) =>
142 this.chargingStation
.ocppRequestService
.requestHandler
<
143 StatusNotificationRequest
,
144 StatusNotificationResponse
145 >(this.chargingStation
, RequestCommand
.STATUS_NOTIFICATION
, requestPayload
),
148 BroadcastChannelProcedureName
.HEARTBEAT
,
149 async (requestPayload
?: BroadcastChannelRequestPayload
) =>
150 this.chargingStation
.ocppRequestService
.requestHandler
<
153 >(this.chargingStation
, RequestCommand
.HEARTBEAT
, requestPayload
),
156 BroadcastChannelProcedureName
.METER_VALUES
,
157 async (requestPayload
?: BroadcastChannelRequestPayload
) => {
158 const configuredMeterValueSampleInterval
=
159 ChargingStationConfigurationUtils
.getConfigurationKey(
161 StandardParametersKey
.MeterValueSampleInterval
163 return this.chargingStation
.ocppRequestService
.requestHandler
<
166 >(this.chargingStation
, RequestCommand
.METER_VALUES
, {
168 OCPP16ServiceUtils
.buildMeterValue(
169 this.chargingStation
,
170 requestPayload
.connectorId
,
171 this.chargingStation
.getConnectorStatus(requestPayload
.connectorId
)?.transactionId
,
172 configuredMeterValueSampleInterval
173 ? Utils
.convertToInt(configuredMeterValueSampleInterval
.value
) * 1000
174 : Constants
.DEFAULT_METER_VALUES_INTERVAL
182 this.chargingStation
= chargingStation
;
183 this.onmessage
= this.requestHandler
.bind(this) as (message
: MessageEvent
) => void;
184 this.onmessageerror
= this.messageErrorHandler
.bind(this) as (message
: MessageEvent
) => void;
187 private async requestHandler(messageEvent
: MessageEvent
): Promise
<void> {
188 const validatedMessageEvent
= this.validateMessageEvent(messageEvent
);
189 if (validatedMessageEvent
=== false) {
192 if (this.isResponse(validatedMessageEvent
.data
) === true) {
195 const [uuid
, command
, requestPayload
] = validatedMessageEvent
.data
as BroadcastChannelRequest
;
197 requestPayload
?.hashIds
!== undefined &&
198 requestPayload
?.hashIds
?.includes(this.chargingStation
.stationInfo
.hashId
) === false
202 if (requestPayload
?.hashId
!== undefined) {
204 `${this.chargingStation.logPrefix()} ${moduleName}.requestHandler: 'hashId' field usage in PDU is deprecated, use 'hashIds' instead`
208 let responsePayload
: BroadcastChannelResponsePayload
;
209 let commandResponse
: CommandResponse
| void;
211 commandResponse
= await this.commandHandler(command
, requestPayload
);
212 if (commandResponse
=== undefined || commandResponse
=== null) {
214 hashId
: this.chargingStation
.stationInfo
.hashId
,
215 status: ResponseStatus
.SUCCESS
,
218 responsePayload
= this.commandResponseToResponsePayload(
221 commandResponse
as CommandResponse
226 `${this.chargingStation.logPrefix()} ${moduleName}.requestHandler: Handle request error:`,
230 hashId
: this.chargingStation
.stationInfo
.hashId
,
231 status: ResponseStatus
.FAILURE
,
234 commandResponse
: commandResponse
as CommandResponse
,
235 errorMessage
: (error
as Error).message
,
236 errorStack
: (error
as Error).stack
,
237 errorDetails
: (error
as OCPPError
).details
,
240 this.sendResponse([uuid
, responsePayload
]);
244 private messageErrorHandler(messageEvent
: MessageEvent
): void {
246 `${this.chargingStation.logPrefix()} ${moduleName}.messageErrorHandler: Error at handling message:`,
251 private async commandHandler(
252 command
: BroadcastChannelProcedureName
,
253 requestPayload
: BroadcastChannelRequestPayload
254 ): Promise
<CommandResponse
| void> {
255 if (this.commandHandlers
.has(command
) === true) {
256 this.cleanRequestPayload(command
, requestPayload
);
257 return this.commandHandlers
.get(command
)(requestPayload
);
259 throw new BaseError(`Unknown worker broadcast channel command: ${command}`);
262 private cleanRequestPayload(
263 command
: BroadcastChannelProcedureName
,
264 requestPayload
: BroadcastChannelRequestPayload
266 delete requestPayload
.hashId
;
267 delete requestPayload
.hashIds
;
269 BroadcastChannelProcedureName
.START_AUTOMATIC_TRANSACTION_GENERATOR
,
270 BroadcastChannelProcedureName
.STOP_AUTOMATIC_TRANSACTION_GENERATOR
,
271 ].includes(command
) === true && delete requestPayload
.connectorIds
;
274 private commandResponseToResponsePayload(
275 command
: BroadcastChannelProcedureName
,
276 requestPayload
: BroadcastChannelRequestPayload
,
277 commandResponse
: CommandResponse
278 ): BroadcastChannelResponsePayload
{
279 const responseStatus
= this.commandResponseToResponseStatus(command
, commandResponse
);
280 if (responseStatus
=== ResponseStatus
.SUCCESS
) {
282 hashId
: this.chargingStation
.stationInfo
.hashId
,
283 status: responseStatus
,
287 hashId
: this.chargingStation
.stationInfo
.hashId
,
288 status: responseStatus
,
295 private commandResponseToResponseStatus(
296 command
: BroadcastChannelProcedureName
,
297 commandResponse
: CommandResponse
300 case BroadcastChannelProcedureName
.START_TRANSACTION
:
301 case BroadcastChannelProcedureName
.STOP_TRANSACTION
:
302 case BroadcastChannelProcedureName
.AUTHORIZE
:
306 | StartTransactionResponse
307 | StopTransactionResponse
309 )?.idTagInfo
?.status === AuthorizationStatus
.ACCEPTED
311 return ResponseStatus
.SUCCESS
;
313 return ResponseStatus
.FAILURE
;
314 case BroadcastChannelProcedureName
.BOOT_NOTIFICATION
:
315 if (commandResponse
?.status === RegistrationStatus
.ACCEPTED
) {
316 return ResponseStatus
.SUCCESS
;
318 return ResponseStatus
.FAILURE
;
319 case BroadcastChannelProcedureName
.STATUS_NOTIFICATION
:
320 case BroadcastChannelProcedureName
.METER_VALUES
:
321 if (Utils
.isEmptyObject(commandResponse
) === true) {
322 return ResponseStatus
.SUCCESS
;
324 return ResponseStatus
.FAILURE
;
325 case BroadcastChannelProcedureName
.HEARTBEAT
:
326 if ('currentTime' in commandResponse
) {
327 return ResponseStatus
.SUCCESS
;
329 return ResponseStatus
.FAILURE
;
331 return ResponseStatus
.FAILURE
;