1 import BaseError from
'../exception/BaseError';
2 import type OCPPError from
'../exception/OCPPError';
6 type StatusNotificationRequest
,
7 } from
'../types/ocpp/Requests';
8 import type { HeartbeatResponse
, StatusNotificationResponse
} from
'../types/ocpp/Responses';
11 StartTransactionRequest
,
12 StartTransactionResponse
,
13 StopTransactionRequest
,
14 StopTransactionResponse
,
15 } from
'../types/ocpp/Transaction';
17 BroadcastChannelProcedureName
,
18 BroadcastChannelRequest
,
19 BroadcastChannelRequestPayload
,
20 BroadcastChannelResponsePayload
,
22 } from
'../types/WorkerBroadcastChannel';
23 import { ResponseStatus
} from
'../ui/web/src/types/UIProtocol';
24 import logger from
'../utils/Logger';
25 import Utils from
'../utils/Utils';
26 import type ChargingStation from
'./ChargingStation';
27 import WorkerBroadcastChannel from
'./WorkerBroadcastChannel';
29 const moduleName
= 'ChargingStationWorkerBroadcastChannel';
31 type CommandResponse
=
32 | StartTransactionResponse
33 | StopTransactionResponse
34 | StatusNotificationResponse
37 export default class ChargingStationWorkerBroadcastChannel
extends WorkerBroadcastChannel
{
38 private readonly commandHandlers
: Map
<
39 BroadcastChannelProcedureName
,
40 (requestPayload
?: BroadcastChannelRequestPayload
) => Promise
<CommandResponse
> | void
43 private readonly chargingStation
: ChargingStation
;
45 constructor(chargingStation
: ChargingStation
) {
47 this.commandHandlers
= new Map([
48 [BroadcastChannelProcedureName
.START_CHARGING_STATION
, () => this.chargingStation
.start()],
50 BroadcastChannelProcedureName
.STOP_CHARGING_STATION
,
51 async () => await this.chargingStation
.stop(),
54 BroadcastChannelProcedureName
.OPEN_CONNECTION
,
55 () => this.chargingStation
.openWSConnection(),
58 BroadcastChannelProcedureName
.CLOSE_CONNECTION
,
59 () => this.chargingStation
.closeWSConnection(),
62 BroadcastChannelProcedureName
.START_TRANSACTION
,
63 async (requestPayload
?: BroadcastChannelRequestPayload
) =>
64 this.chargingStation
.ocppRequestService
.requestHandler
<
65 StartTransactionRequest
,
66 StartTransactionResponse
67 >(this.chargingStation
, RequestCommand
.START_TRANSACTION
, {
68 connectorId
: requestPayload
.connectorId
,
69 idTag
: requestPayload
.idTag
,
73 BroadcastChannelProcedureName
.STOP_TRANSACTION
,
74 async (requestPayload
?: BroadcastChannelRequestPayload
) =>
75 this.chargingStation
.ocppRequestService
.requestHandler
<
76 StopTransactionRequest
,
77 StartTransactionResponse
78 >(this.chargingStation
, RequestCommand
.STOP_TRANSACTION
, {
79 transactionId
: requestPayload
.transactionId
,
80 meterStop
: this.chargingStation
.getEnergyActiveImportRegisterByTransactionId(
81 requestPayload
.transactionId
,
84 idTag
: requestPayload
.idTag
,
85 reason
: requestPayload
.reason
,
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
.STATUS_NOTIFICATION
,
100 async (requestPayload
?: BroadcastChannelRequestPayload
) =>
101 this.chargingStation
.ocppRequestService
.requestHandler
<
102 StatusNotificationRequest
,
103 StatusNotificationResponse
104 >(this.chargingStation
, RequestCommand
.STATUS_NOTIFICATION
, {
105 connectorId
: requestPayload
.connectorId
,
106 errorCode
: requestPayload
.errorCode
,
107 status: requestPayload
.status,
108 ...(requestPayload
.info
&& { info
: requestPayload
.info
}),
109 ...(requestPayload
.timestamp
&& { timestamp
: requestPayload
.timestamp
}),
110 ...(requestPayload
.vendorId
&& { vendorId
: requestPayload
.vendorId
}),
111 ...(requestPayload
.vendorErrorCode
&& {
112 vendorErrorCode
: requestPayload
.vendorErrorCode
,
117 BroadcastChannelProcedureName
.HEARTBEAT
,
118 async (requestPayload
?: BroadcastChannelRequestPayload
) => {
119 delete requestPayload
.hashId
;
120 delete requestPayload
.hashIds
;
121 delete requestPayload
.connectorIds
;
122 return this.chargingStation
.ocppRequestService
.requestHandler
<
125 >(this.chargingStation
, RequestCommand
.HEARTBEAT
, requestPayload
);
129 this.chargingStation
= chargingStation
;
130 this.onmessage
= this.requestHandler
.bind(this) as (message
: MessageEvent
) => void;
131 this.onmessageerror
= this.messageErrorHandler
.bind(this) as (message
: MessageEvent
) => void;
134 private async requestHandler(messageEvent
: MessageEvent
): Promise
<void> {
135 const validatedMessageEvent
= this.validateMessageEvent(messageEvent
);
136 if (validatedMessageEvent
=== false) {
139 if (this.isResponse(validatedMessageEvent
.data
) === true) {
142 const [uuid
, command
, requestPayload
] = validatedMessageEvent
.data
as BroadcastChannelRequest
;
145 requestPayload
?.hashIds
!== undefined &&
146 requestPayload
?.hashIds
?.includes(this.chargingStation
.stationInfo
.hashId
) === false
150 if (requestPayload
?.hashId
!== undefined) {
152 `${this.chargingStation.logPrefix()} ${moduleName}.requestHandler: 'hashId' field usage in PDU is deprecated, use 'hashIds' instead`
157 let responsePayload
: BroadcastChannelResponsePayload
;
158 let commandResponse
: CommandResponse
| void;
160 commandResponse
= await this.commandHandler(command
, requestPayload
);
161 if (commandResponse
=== undefined || commandResponse
=== null) {
163 hashId
: this.chargingStation
.stationInfo
.hashId
,
164 status: ResponseStatus
.SUCCESS
,
168 hashId
: this.chargingStation
.stationInfo
.hashId
,
169 status: this.commandResponseToResponseStatus(command
, commandResponse
as CommandResponse
),
174 `${this.chargingStation.logPrefix()} ${moduleName}.requestHandler: Handle request error:`,
178 hashId
: this.chargingStation
.stationInfo
.hashId
,
179 status: ResponseStatus
.FAILURE
,
182 commandResponse
: commandResponse
as CommandResponse
,
183 errorMessage
: (error
as Error).message
,
184 errorStack
: (error
as Error).stack
,
185 errorDetails
: (error
as OCPPError
).details
,
188 this.sendResponse([uuid
, responsePayload
]);
191 private messageErrorHandler(messageEvent
: MessageEvent
): void {
193 `${this.chargingStation.logPrefix()} ${moduleName}.messageErrorHandler: Error at handling message:`,
198 private async commandHandler(
199 command
: BroadcastChannelProcedureName
,
200 requestPayload
: BroadcastChannelRequestPayload
201 ): Promise
<CommandResponse
| void> {
202 if (this.commandHandlers
.has(command
) === true) {
203 return this.commandHandlers
.get(command
)(requestPayload
);
205 throw new BaseError(`Unknown worker broadcast channel command: ${command}`);
208 private commandResponseToResponseStatus(
209 command
: BroadcastChannelProcedureName
,
210 commandResponse
: CommandResponse
213 case BroadcastChannelProcedureName
.START_TRANSACTION
:
214 case BroadcastChannelProcedureName
.STOP_TRANSACTION
:
216 (commandResponse
as StartTransactionResponse
| StopTransactionResponse
)?.idTagInfo
217 ?.status === AuthorizationStatus
.ACCEPTED
219 return ResponseStatus
.SUCCESS
;
221 return ResponseStatus
.FAILURE
;
222 case BroadcastChannelProcedureName
.STATUS_NOTIFICATION
:
223 if (Utils
.isEmptyObject(commandResponse
) === true) {
224 return ResponseStatus
.SUCCESS
;
226 return ResponseStatus
.FAILURE
;
227 case BroadcastChannelProcedureName
.HEARTBEAT
:
228 if ('currentTime' in commandResponse
) {
229 return ResponseStatus
.SUCCESS
;
231 return ResponseStatus
.FAILURE
;
233 return ResponseStatus
.FAILURE
;