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 chargingStation
: ChargingStation
;
40 constructor(chargingStation
: ChargingStation
) {
42 this.chargingStation
= chargingStation
;
43 this.onmessage
= this.requestHandler
.bind(this) as (message
: MessageEvent
) => void;
44 this.onmessageerror
= this.messageErrorHandler
.bind(this) as (message
: MessageEvent
) => void;
47 private async requestHandler(messageEvent
: MessageEvent
): Promise
<void> {
48 if (this.isResponse(messageEvent
.data
) === true) {
51 const [uuid
, command
, requestPayload
] = this.validateMessageEvent(messageEvent
)
52 .data
as BroadcastChannelRequest
;
55 requestPayload
?.hashIds
!== undefined &&
56 requestPayload
?.hashIds
?.includes(this.chargingStation
.stationInfo
.hashId
) === false
60 if (requestPayload
?.hashId
!== undefined) {
62 `${this.chargingStation.logPrefix()} ${moduleName}.requestHandler: 'hashId' field usage in PDU is deprecated, use 'hashIds' instead`
67 let responsePayload
: BroadcastChannelResponsePayload
;
68 let commandResponse
: CommandResponse
;
70 commandResponse
= await this.commandHandler(command
, requestPayload
);
71 if (commandResponse
=== undefined) {
73 hashId
: this.chargingStation
.stationInfo
.hashId
,
74 status: ResponseStatus
.SUCCESS
,
78 hashId
: this.chargingStation
.stationInfo
.hashId
,
79 status: this.commandResponseToResponseStatus(command
, commandResponse
),
84 `${this.chargingStation.logPrefix()} ${moduleName}.requestHandler: Handle request error:`,
88 hashId
: this.chargingStation
.stationInfo
.hashId
,
89 status: ResponseStatus
.FAILURE
,
93 errorMessage
: (error
as Error).message
,
94 errorStack
: (error
as Error).stack
,
95 errorDetails
: (error
as OCPPError
).details
,
98 this.sendResponse([uuid
, responsePayload
]);
101 private messageErrorHandler(messageEvent
: MessageEvent
): void {
103 `${this.chargingStation.logPrefix()} ${moduleName}.messageErrorHandler: Error at handling message:`,
108 private async commandHandler(
109 command
: BroadcastChannelProcedureName
,
110 requestPayload
: BroadcastChannelRequestPayload
111 ): Promise
<CommandResponse
| undefined> {
113 case BroadcastChannelProcedureName
.START_CHARGING_STATION
:
114 this.chargingStation
.start();
116 case BroadcastChannelProcedureName
.STOP_CHARGING_STATION
:
117 await this.chargingStation
.stop();
119 case BroadcastChannelProcedureName
.OPEN_CONNECTION
:
120 this.chargingStation
.openWSConnection();
122 case BroadcastChannelProcedureName
.CLOSE_CONNECTION
:
123 this.chargingStation
.closeWSConnection();
125 case BroadcastChannelProcedureName
.START_TRANSACTION
:
126 return this.chargingStation
.ocppRequestService
.requestHandler
<
127 StartTransactionRequest
,
128 StartTransactionResponse
129 >(this.chargingStation
, RequestCommand
.START_TRANSACTION
, {
130 connectorId
: requestPayload
.connectorId
,
131 idTag
: requestPayload
.idTag
,
133 case BroadcastChannelProcedureName
.STOP_TRANSACTION
:
134 return this.chargingStation
.ocppRequestService
.requestHandler
<
135 StopTransactionRequest
,
136 StopTransactionResponse
137 >(this.chargingStation
, RequestCommand
.STOP_TRANSACTION
, {
138 transactionId
: requestPayload
.transactionId
,
139 meterStop
: this.chargingStation
.getEnergyActiveImportRegisterByTransactionId(
140 requestPayload
.transactionId
,
143 idTag
: requestPayload
.idTag
,
144 reason
: requestPayload
.reason
,
146 case BroadcastChannelProcedureName
.START_AUTOMATIC_TRANSACTION_GENERATOR
:
147 this.chargingStation
.startAutomaticTransactionGenerator(requestPayload
.connectorIds
);
149 case BroadcastChannelProcedureName
.STOP_AUTOMATIC_TRANSACTION_GENERATOR
:
150 this.chargingStation
.stopAutomaticTransactionGenerator(requestPayload
.connectorIds
);
152 case BroadcastChannelProcedureName
.STATUS_NOTIFICATION
:
153 return this.chargingStation
.ocppRequestService
.requestHandler
<
154 StatusNotificationRequest
,
155 StatusNotificationResponse
156 >(this.chargingStation
, RequestCommand
.STATUS_NOTIFICATION
, {
157 connectorId
: requestPayload
.connectorId
,
158 errorCode
: requestPayload
.errorCode
,
159 status: requestPayload
.status,
160 ...(requestPayload
.info
&& { info
: requestPayload
.info
}),
161 ...(requestPayload
.timestamp
&& { timestamp
: requestPayload
.timestamp
}),
162 ...(requestPayload
.vendorId
&& { vendorId
: requestPayload
.vendorId
}),
163 ...(requestPayload
.vendorErrorCode
&& {
164 vendorErrorCode
: requestPayload
.vendorErrorCode
,
167 case BroadcastChannelProcedureName
.HEARTBEAT
:
168 delete requestPayload
.hashId
;
169 delete requestPayload
.hashIds
;
170 delete requestPayload
.connectorIds
;
171 return this.chargingStation
.ocppRequestService
.requestHandler
<
174 >(this.chargingStation
, RequestCommand
.HEARTBEAT
, requestPayload
);
176 // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
177 throw new BaseError(`Unknown worker broadcast channel command: ${command}`);
181 private commandResponseToResponseStatus(
182 command
: BroadcastChannelProcedureName
,
183 commandResponse
: CommandResponse
186 case BroadcastChannelProcedureName
.START_TRANSACTION
:
187 case BroadcastChannelProcedureName
.STOP_TRANSACTION
:
189 (commandResponse
as StartTransactionResponse
| StopTransactionResponse
)?.idTagInfo
190 ?.status === AuthorizationStatus
.ACCEPTED
192 return ResponseStatus
.SUCCESS
;
194 return ResponseStatus
.FAILURE
;
195 case BroadcastChannelProcedureName
.STATUS_NOTIFICATION
:
196 if (Utils
.isEmptyObject(commandResponse
) === true) {
197 return ResponseStatus
.SUCCESS
;
199 return ResponseStatus
.FAILURE
;
200 case BroadcastChannelProcedureName
.HEARTBEAT
:
201 if ('currentTime' in commandResponse
) {
202 return ResponseStatus
.SUCCESS
;
204 return ResponseStatus
.FAILURE
;
206 return ResponseStatus
.FAILURE
;