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 type CommandHandler
= (
38 requestPayload
?: BroadcastChannelRequestPayload
39 ) => Promise
<CommandResponse
| void> | void;
41 export default class ChargingStationWorkerBroadcastChannel
extends WorkerBroadcastChannel
{
42 private readonly commandHandlers
: Map
<BroadcastChannelProcedureName
, CommandHandler
>;
44 private readonly chargingStation
: ChargingStation
;
46 constructor(chargingStation
: ChargingStation
) {
48 this.commandHandlers
= new Map
<BroadcastChannelProcedureName
, CommandHandler
>([
49 [BroadcastChannelProcedureName
.START_CHARGING_STATION
, () => this.chargingStation
.start()],
51 BroadcastChannelProcedureName
.STOP_CHARGING_STATION
,
52 async () => this.chargingStation
.stop(),
55 BroadcastChannelProcedureName
.OPEN_CONNECTION
,
56 () => this.chargingStation
.openWSConnection(),
59 BroadcastChannelProcedureName
.CLOSE_CONNECTION
,
60 () => this.chargingStation
.closeWSConnection(),
63 BroadcastChannelProcedureName
.START_TRANSACTION
,
64 async (requestPayload
?: BroadcastChannelRequestPayload
) =>
65 this.chargingStation
.ocppRequestService
.requestHandler
<
66 StartTransactionRequest
,
67 StartTransactionResponse
68 >(this.chargingStation
, RequestCommand
.START_TRANSACTION
, {
69 connectorId
: requestPayload
.connectorId
,
70 idTag
: requestPayload
.idTag
,
74 BroadcastChannelProcedureName
.STOP_TRANSACTION
,
75 async (requestPayload
?: BroadcastChannelRequestPayload
) =>
76 this.chargingStation
.ocppRequestService
.requestHandler
<
77 StopTransactionRequest
,
78 StartTransactionResponse
79 >(this.chargingStation
, RequestCommand
.STOP_TRANSACTION
, {
80 transactionId
: requestPayload
.transactionId
,
81 meterStop
: this.chargingStation
.getEnergyActiveImportRegisterByTransactionId(
82 requestPayload
.transactionId
,
85 idTag
: requestPayload
.idTag
,
86 reason
: requestPayload
.reason
,
90 BroadcastChannelProcedureName
.START_AUTOMATIC_TRANSACTION_GENERATOR
,
91 (requestPayload
?: BroadcastChannelRequestPayload
) =>
92 this.chargingStation
.startAutomaticTransactionGenerator(requestPayload
.connectorIds
),
95 BroadcastChannelProcedureName
.STOP_AUTOMATIC_TRANSACTION_GENERATOR
,
96 (requestPayload
?: BroadcastChannelRequestPayload
) =>
97 this.chargingStation
.stopAutomaticTransactionGenerator(requestPayload
.connectorIds
),
100 BroadcastChannelProcedureName
.STATUS_NOTIFICATION
,
101 async (requestPayload
?: BroadcastChannelRequestPayload
) =>
102 this.chargingStation
.ocppRequestService
.requestHandler
<
103 StatusNotificationRequest
,
104 StatusNotificationResponse
105 >(this.chargingStation
, RequestCommand
.STATUS_NOTIFICATION
, {
106 connectorId
: requestPayload
.connectorId
,
107 errorCode
: requestPayload
.errorCode
,
108 status: requestPayload
.status,
109 ...(requestPayload
.info
&& { info
: requestPayload
.info
}),
110 ...(requestPayload
.timestamp
&& { timestamp
: requestPayload
.timestamp
}),
111 ...(requestPayload
.vendorId
&& { vendorId
: requestPayload
.vendorId
}),
112 ...(requestPayload
.vendorErrorCode
&& {
113 vendorErrorCode
: requestPayload
.vendorErrorCode
,
118 BroadcastChannelProcedureName
.HEARTBEAT
,
119 async (requestPayload
?: BroadcastChannelRequestPayload
) => {
120 delete requestPayload
.hashId
;
121 delete requestPayload
.hashIds
;
122 delete requestPayload
.connectorIds
;
123 return this.chargingStation
.ocppRequestService
.requestHandler
<
126 >(this.chargingStation
, RequestCommand
.HEARTBEAT
, requestPayload
);
130 this.chargingStation
= chargingStation
;
131 this.onmessage
= this.requestHandler
.bind(this) as (message
: MessageEvent
) => void;
132 this.onmessageerror
= this.messageErrorHandler
.bind(this) as (message
: MessageEvent
) => void;
135 private async requestHandler(messageEvent
: MessageEvent
): Promise
<void> {
136 const validatedMessageEvent
= this.validateMessageEvent(messageEvent
);
137 if (validatedMessageEvent
=== false) {
140 if (this.isResponse(validatedMessageEvent
.data
) === true) {
143 const [uuid
, command
, requestPayload
] = validatedMessageEvent
.data
as BroadcastChannelRequest
;
146 requestPayload
?.hashIds
!== undefined &&
147 requestPayload
?.hashIds
?.includes(this.chargingStation
.stationInfo
.hashId
) === false
151 if (requestPayload
?.hashId
!== undefined) {
153 `${this.chargingStation.logPrefix()} ${moduleName}.requestHandler: 'hashId' field usage in PDU is deprecated, use 'hashIds' instead`
158 let responsePayload
: BroadcastChannelResponsePayload
;
159 let commandResponse
: CommandResponse
| void;
161 commandResponse
= await this.commandHandler(command
, requestPayload
);
162 if (commandResponse
=== undefined || commandResponse
=== null) {
164 hashId
: this.chargingStation
.stationInfo
.hashId
,
165 status: ResponseStatus
.SUCCESS
,
169 hashId
: this.chargingStation
.stationInfo
.hashId
,
170 status: this.commandResponseToResponseStatus(command
, commandResponse
as CommandResponse
),
175 `${this.chargingStation.logPrefix()} ${moduleName}.requestHandler: Handle request error:`,
179 hashId
: this.chargingStation
.stationInfo
.hashId
,
180 status: ResponseStatus
.FAILURE
,
183 commandResponse
: commandResponse
as CommandResponse
,
184 errorMessage
: (error
as Error).message
,
185 errorStack
: (error
as Error).stack
,
186 errorDetails
: (error
as OCPPError
).details
,
189 this.sendResponse([uuid
, responsePayload
]);
192 private messageErrorHandler(messageEvent
: MessageEvent
): void {
194 `${this.chargingStation.logPrefix()} ${moduleName}.messageErrorHandler: Error at handling message:`,
199 private async commandHandler(
200 command
: BroadcastChannelProcedureName
,
201 requestPayload
: BroadcastChannelRequestPayload
202 ): Promise
<CommandResponse
| void> {
203 if (this.commandHandlers
.has(command
) === true) {
204 return this.commandHandlers
.get(command
)(requestPayload
);
206 throw new BaseError(`Unknown worker broadcast channel command: ${command}`);
209 private commandResponseToResponseStatus(
210 command
: BroadcastChannelProcedureName
,
211 commandResponse
: CommandResponse
214 case BroadcastChannelProcedureName
.START_TRANSACTION
:
215 case BroadcastChannelProcedureName
.STOP_TRANSACTION
:
217 (commandResponse
as StartTransactionResponse
| StopTransactionResponse
)?.idTagInfo
218 ?.status === AuthorizationStatus
.ACCEPTED
220 return ResponseStatus
.SUCCESS
;
222 return ResponseStatus
.FAILURE
;
223 case BroadcastChannelProcedureName
.STATUS_NOTIFICATION
:
224 if (Utils
.isEmptyObject(commandResponse
) === true) {
225 return ResponseStatus
.SUCCESS
;
227 return ResponseStatus
.FAILURE
;
228 case BroadcastChannelProcedureName
.HEARTBEAT
:
229 if ('currentTime' in commandResponse
) {
230 return ResponseStatus
.SUCCESS
;
232 return ResponseStatus
.FAILURE
;
234 return ResponseStatus
.FAILURE
;