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
, {
104 requestPayload
.meterStop
??
105 this.chargingStation
.getEnergyActiveImportRegisterByTransactionId(
106 requestPayload
.transactionId
,
113 BroadcastChannelProcedureName
.AUTHORIZE
,
114 async (requestPayload
?: BroadcastChannelRequestPayload
) =>
115 this.chargingStation
.ocppRequestService
.requestHandler
<
118 >(this.chargingStation
, RequestCommand
.AUTHORIZE
, requestPayload
),
121 BroadcastChannelProcedureName
.BOOT_NOTIFICATION
,
122 async (requestPayload
?: BroadcastChannelRequestPayload
) => {
123 this.chargingStation
.bootNotificationResponse
=
124 await this.chargingStation
.ocppRequestService
.requestHandler
<
125 BootNotificationRequest
,
126 BootNotificationResponse
128 this.chargingStation
,
129 RequestCommand
.BOOT_NOTIFICATION
,
131 ...this.chargingStation
.bootNotificationRequest
,
135 skipBufferingOnError
: true,
138 return this.chargingStation
.bootNotificationResponse
;
142 BroadcastChannelProcedureName
.STATUS_NOTIFICATION
,
143 async (requestPayload
?: BroadcastChannelRequestPayload
) =>
144 this.chargingStation
.ocppRequestService
.requestHandler
<
145 StatusNotificationRequest
,
146 StatusNotificationResponse
147 >(this.chargingStation
, RequestCommand
.STATUS_NOTIFICATION
, requestPayload
),
150 BroadcastChannelProcedureName
.HEARTBEAT
,
151 async (requestPayload
?: BroadcastChannelRequestPayload
) =>
152 this.chargingStation
.ocppRequestService
.requestHandler
<
155 >(this.chargingStation
, RequestCommand
.HEARTBEAT
, requestPayload
),
158 BroadcastChannelProcedureName
.METER_VALUES
,
159 async (requestPayload
?: BroadcastChannelRequestPayload
) => {
160 const configuredMeterValueSampleInterval
=
161 ChargingStationConfigurationUtils
.getConfigurationKey(
163 StandardParametersKey
.MeterValueSampleInterval
165 return this.chargingStation
.ocppRequestService
.requestHandler
<
168 >(this.chargingStation
, RequestCommand
.METER_VALUES
, {
170 OCPP16ServiceUtils
.buildMeterValue(
171 this.chargingStation
,
172 requestPayload
.connectorId
,
173 this.chargingStation
.getConnectorStatus(requestPayload
.connectorId
)?.transactionId
,
174 configuredMeterValueSampleInterval
175 ? Utils
.convertToInt(configuredMeterValueSampleInterval
.value
) * 1000
176 : Constants
.DEFAULT_METER_VALUES_INTERVAL
184 this.chargingStation
= chargingStation
;
185 this.onmessage
= this.requestHandler
.bind(this) as (message
: MessageEvent
) => void;
186 this.onmessageerror
= this.messageErrorHandler
.bind(this) as (message
: MessageEvent
) => void;
189 private async requestHandler(messageEvent
: MessageEvent
): Promise
<void> {
190 const validatedMessageEvent
= this.validateMessageEvent(messageEvent
);
191 if (validatedMessageEvent
=== false) {
194 if (this.isResponse(validatedMessageEvent
.data
) === true) {
197 const [uuid
, command
, requestPayload
] = validatedMessageEvent
.data
as BroadcastChannelRequest
;
199 requestPayload
?.hashIds
!== undefined &&
200 requestPayload
?.hashIds
?.includes(this.chargingStation
.stationInfo
.hashId
) === false
204 if (requestPayload
?.hashId
!== undefined) {
206 `${this.chargingStation.logPrefix()} ${moduleName}.requestHandler: 'hashId' field usage in PDU is deprecated, use 'hashIds' instead`
210 let responsePayload
: BroadcastChannelResponsePayload
;
211 let commandResponse
: CommandResponse
| void;
213 commandResponse
= await this.commandHandler(command
, requestPayload
);
214 if (commandResponse
=== undefined || commandResponse
=== null) {
216 hashId
: this.chargingStation
.stationInfo
.hashId
,
217 status: ResponseStatus
.SUCCESS
,
220 responsePayload
= this.commandResponseToResponsePayload(
223 commandResponse
as CommandResponse
228 `${this.chargingStation.logPrefix()} ${moduleName}.requestHandler: Handle request error:`,
232 hashId
: this.chargingStation
.stationInfo
.hashId
,
233 status: ResponseStatus
.FAILURE
,
236 commandResponse
: commandResponse
as CommandResponse
,
237 errorMessage
: (error
as Error).message
,
238 errorStack
: (error
as Error).stack
,
239 errorDetails
: (error
as OCPPError
).details
,
242 this.sendResponse([uuid
, responsePayload
]);
246 private messageErrorHandler(messageEvent
: MessageEvent
): void {
248 `${this.chargingStation.logPrefix()} ${moduleName}.messageErrorHandler: Error at handling message:`,
253 private async commandHandler(
254 command
: BroadcastChannelProcedureName
,
255 requestPayload
: BroadcastChannelRequestPayload
256 ): Promise
<CommandResponse
| void> {
257 if (this.commandHandlers
.has(command
) === true) {
258 this.cleanRequestPayload(command
, requestPayload
);
259 return this.commandHandlers
.get(command
)(requestPayload
);
261 throw new BaseError(`Unknown worker broadcast channel command: ${command}`);
264 private cleanRequestPayload(
265 command
: BroadcastChannelProcedureName
,
266 requestPayload
: BroadcastChannelRequestPayload
268 delete requestPayload
.hashId
;
269 delete requestPayload
.hashIds
;
271 BroadcastChannelProcedureName
.START_AUTOMATIC_TRANSACTION_GENERATOR
,
272 BroadcastChannelProcedureName
.STOP_AUTOMATIC_TRANSACTION_GENERATOR
,
273 ].includes(command
) === false && delete requestPayload
.connectorIds
;
276 private commandResponseToResponsePayload(
277 command
: BroadcastChannelProcedureName
,
278 requestPayload
: BroadcastChannelRequestPayload
,
279 commandResponse
: CommandResponse
280 ): BroadcastChannelResponsePayload
{
281 const responseStatus
= this.commandResponseToResponseStatus(command
, commandResponse
);
282 if (responseStatus
=== ResponseStatus
.SUCCESS
) {
284 hashId
: this.chargingStation
.stationInfo
.hashId
,
285 status: responseStatus
,
289 hashId
: this.chargingStation
.stationInfo
.hashId
,
290 status: responseStatus
,
297 private commandResponseToResponseStatus(
298 command
: BroadcastChannelProcedureName
,
299 commandResponse
: CommandResponse
302 case BroadcastChannelProcedureName
.START_TRANSACTION
:
303 case BroadcastChannelProcedureName
.STOP_TRANSACTION
:
304 case BroadcastChannelProcedureName
.AUTHORIZE
:
308 | StartTransactionResponse
309 | StopTransactionResponse
311 )?.idTagInfo
?.status === AuthorizationStatus
.ACCEPTED
313 return ResponseStatus
.SUCCESS
;
315 return ResponseStatus
.FAILURE
;
316 case BroadcastChannelProcedureName
.BOOT_NOTIFICATION
:
317 if (commandResponse
?.status === RegistrationStatus
.ACCEPTED
) {
318 return ResponseStatus
.SUCCESS
;
320 return ResponseStatus
.FAILURE
;
321 case BroadcastChannelProcedureName
.STATUS_NOTIFICATION
:
322 case BroadcastChannelProcedureName
.METER_VALUES
:
323 if (Utils
.isEmptyObject(commandResponse
) === true) {
324 return ResponseStatus
.SUCCESS
;
326 return ResponseStatus
.FAILURE
;
327 case BroadcastChannelProcedureName
.HEARTBEAT
:
328 if ('currentTime' in commandResponse
) {
329 return ResponseStatus
.SUCCESS
;
331 return ResponseStatus
.FAILURE
;
333 return ResponseStatus
.FAILURE
;