Commit | Line | Data |
---|---|---|
6c8f5d90 | 1 | import BaseError from '../exception/BaseError'; |
a9ed42b2 | 2 | import type OCPPError from '../exception/OCPPError'; |
10db00b2 JB |
3 | import { |
4 | HeartbeatRequest, | |
5 | RequestCommand, | |
6 | type StatusNotificationRequest, | |
7 | } from '../types/ocpp/Requests'; | |
8 | import type { HeartbeatResponse, StatusNotificationResponse } from '../types/ocpp/Responses'; | |
89b7a234 | 9 | import { |
6c8f5d90 | 10 | AuthorizationStatus, |
89b7a234 JB |
11 | StartTransactionRequest, |
12 | StartTransactionResponse, | |
89b7a234 JB |
13 | StopTransactionRequest, |
14 | StopTransactionResponse, | |
15 | } from '../types/ocpp/Transaction'; | |
16 | import { | |
17 | BroadcastChannelProcedureName, | |
18 | BroadcastChannelRequest, | |
6c8f5d90 JB |
19 | BroadcastChannelRequestPayload, |
20 | BroadcastChannelResponsePayload, | |
21 | MessageEvent, | |
89b7a234 | 22 | } from '../types/WorkerBroadcastChannel'; |
f27eb751 | 23 | import { ResponseStatus } from '../ui/web/src/types/UIProtocol'; |
6c8f5d90 | 24 | import logger from '../utils/Logger'; |
a9ed42b2 | 25 | import Utils from '../utils/Utils'; |
db2336d9 | 26 | import type ChargingStation from './ChargingStation'; |
1598b27c | 27 | import WorkerBroadcastChannel from './WorkerBroadcastChannel'; |
89b7a234 | 28 | |
4e3ff94d JB |
29 | const moduleName = 'ChargingStationWorkerBroadcastChannel'; |
30 | ||
a9ed42b2 JB |
31 | type CommandResponse = |
32 | | StartTransactionResponse | |
33 | | StopTransactionResponse | |
10db00b2 JB |
34 | | StatusNotificationResponse |
35 | | HeartbeatResponse; | |
89b7a234 | 36 | |
1598b27c | 37 | export default class ChargingStationWorkerBroadcastChannel extends WorkerBroadcastChannel { |
89b7a234 JB |
38 | private readonly chargingStation: ChargingStation; |
39 | ||
40 | constructor(chargingStation: ChargingStation) { | |
1598b27c | 41 | super(); |
89b7a234 | 42 | this.chargingStation = chargingStation; |
02a6943a | 43 | this.onmessage = this.requestHandler.bind(this) as (message: MessageEvent) => void; |
6c8f5d90 | 44 | this.onmessageerror = this.messageErrorHandler.bind(this) as (message: MessageEvent) => void; |
89b7a234 JB |
45 | } |
46 | ||
02a6943a | 47 | private async requestHandler(messageEvent: MessageEvent): Promise<void> { |
5dea4c94 JB |
48 | const validatedMessageEvent = this.validateMessageEvent(messageEvent); |
49 | if (validatedMessageEvent === false) { | |
6c8f5d90 JB |
50 | return; |
51 | } | |
5dea4c94 JB |
52 | if (this.isResponse(validatedMessageEvent.data) === true) { |
53 | return; | |
54 | } | |
55 | const [uuid, command, requestPayload] = validatedMessageEvent.data as BroadcastChannelRequest; | |
6c8f5d90 | 56 | |
2afb4d15 JB |
57 | if ( |
58 | requestPayload?.hashIds !== undefined && | |
59 | requestPayload?.hashIds?.includes(this.chargingStation.stationInfo.hashId) === false | |
60 | ) { | |
61 | return; | |
62 | } | |
63 | if (requestPayload?.hashId !== undefined) { | |
64 | logger.error( | |
65 | `${this.chargingStation.logPrefix()} ${moduleName}.requestHandler: 'hashId' field usage in PDU is deprecated, use 'hashIds' instead` | |
66 | ); | |
67 | return; | |
4eca248c | 68 | } |
89b7a234 | 69 | |
6c8f5d90 JB |
70 | let responsePayload: BroadcastChannelResponsePayload; |
71 | let commandResponse: CommandResponse; | |
72 | try { | |
73 | commandResponse = await this.commandHandler(command, requestPayload); | |
74 | if (commandResponse === undefined) { | |
10d244c0 | 75 | responsePayload = { |
51c83d6f | 76 | hashId: this.chargingStation.stationInfo.hashId, |
10d244c0 JB |
77 | status: ResponseStatus.SUCCESS, |
78 | }; | |
6c8f5d90 | 79 | } else { |
10d244c0 | 80 | responsePayload = { |
51c83d6f | 81 | hashId: this.chargingStation.stationInfo.hashId, |
10db00b2 | 82 | status: this.commandResponseToResponseStatus(command, commandResponse), |
10d244c0 | 83 | }; |
6c8f5d90 JB |
84 | } |
85 | } catch (error) { | |
86 | logger.error( | |
87 | `${this.chargingStation.logPrefix()} ${moduleName}.requestHandler: Handle request error:`, | |
88 | error | |
89 | ); | |
90 | responsePayload = { | |
51c83d6f | 91 | hashId: this.chargingStation.stationInfo.hashId, |
6c8f5d90 JB |
92 | status: ResponseStatus.FAILURE, |
93 | command, | |
94 | requestPayload, | |
95 | commandResponse, | |
96 | errorMessage: (error as Error).message, | |
97 | errorStack: (error as Error).stack, | |
a9ed42b2 | 98 | errorDetails: (error as OCPPError).details, |
6c8f5d90 JB |
99 | }; |
100 | } | |
101 | this.sendResponse([uuid, responsePayload]); | |
102 | } | |
103 | ||
104 | private messageErrorHandler(messageEvent: MessageEvent): void { | |
105 | logger.error( | |
106 | `${this.chargingStation.logPrefix()} ${moduleName}.messageErrorHandler: Error at handling message:`, | |
5e3cb728 | 107 | { messageEvent } |
6c8f5d90 JB |
108 | ); |
109 | } | |
110 | ||
111 | private async commandHandler( | |
112 | command: BroadcastChannelProcedureName, | |
113 | requestPayload: BroadcastChannelRequestPayload | |
114 | ): Promise<CommandResponse | undefined> { | |
89b7a234 | 115 | switch (command) { |
4f69be04 JB |
116 | case BroadcastChannelProcedureName.START_CHARGING_STATION: |
117 | this.chargingStation.start(); | |
118 | break; | |
119 | case BroadcastChannelProcedureName.STOP_CHARGING_STATION: | |
120 | await this.chargingStation.stop(); | |
121 | break; | |
122 | case BroadcastChannelProcedureName.OPEN_CONNECTION: | |
123 | this.chargingStation.openWSConnection(); | |
124 | break; | |
125 | case BroadcastChannelProcedureName.CLOSE_CONNECTION: | |
126 | this.chargingStation.closeWSConnection(); | |
127 | break; | |
89b7a234 | 128 | case BroadcastChannelProcedureName.START_TRANSACTION: |
6c8f5d90 | 129 | return this.chargingStation.ocppRequestService.requestHandler< |
89b7a234 JB |
130 | StartTransactionRequest, |
131 | StartTransactionResponse | |
132 | >(this.chargingStation, RequestCommand.START_TRANSACTION, { | |
6c8f5d90 JB |
133 | connectorId: requestPayload.connectorId, |
134 | idTag: requestPayload.idTag, | |
89b7a234 | 135 | }); |
89b7a234 | 136 | case BroadcastChannelProcedureName.STOP_TRANSACTION: |
6c8f5d90 | 137 | return this.chargingStation.ocppRequestService.requestHandler< |
89b7a234 JB |
138 | StopTransactionRequest, |
139 | StopTransactionResponse | |
140 | >(this.chargingStation, RequestCommand.STOP_TRANSACTION, { | |
6c8f5d90 | 141 | transactionId: requestPayload.transactionId, |
89b7a234 | 142 | meterStop: this.chargingStation.getEnergyActiveImportRegisterByTransactionId( |
07989fad JB |
143 | requestPayload.transactionId, |
144 | true | |
89b7a234 | 145 | ), |
cf058664 JB |
146 | idTag: requestPayload.idTag, |
147 | reason: requestPayload.reason, | |
89b7a234 | 148 | }); |
4f69be04 | 149 | case BroadcastChannelProcedureName.START_AUTOMATIC_TRANSACTION_GENERATOR: |
a5e9befc | 150 | this.chargingStation.startAutomaticTransactionGenerator(requestPayload.connectorIds); |
89b7a234 | 151 | break; |
4f69be04 | 152 | case BroadcastChannelProcedureName.STOP_AUTOMATIC_TRANSACTION_GENERATOR: |
a5e9befc | 153 | this.chargingStation.stopAutomaticTransactionGenerator(requestPayload.connectorIds); |
db2336d9 | 154 | break; |
a9ed42b2 JB |
155 | case BroadcastChannelProcedureName.STATUS_NOTIFICATION: |
156 | return this.chargingStation.ocppRequestService.requestHandler< | |
157 | StatusNotificationRequest, | |
158 | StatusNotificationResponse | |
159 | >(this.chargingStation, RequestCommand.STATUS_NOTIFICATION, { | |
160 | connectorId: requestPayload.connectorId, | |
161 | errorCode: requestPayload.errorCode, | |
162 | status: requestPayload.status, | |
163 | ...(requestPayload.info && { info: requestPayload.info }), | |
164 | ...(requestPayload.timestamp && { timestamp: requestPayload.timestamp }), | |
165 | ...(requestPayload.vendorId && { vendorId: requestPayload.vendorId }), | |
166 | ...(requestPayload.vendorErrorCode && { | |
167 | vendorErrorCode: requestPayload.vendorErrorCode, | |
168 | }), | |
169 | }); | |
10db00b2 JB |
170 | case BroadcastChannelProcedureName.HEARTBEAT: |
171 | delete requestPayload.hashId; | |
172 | delete requestPayload.hashIds; | |
173 | delete requestPayload.connectorIds; | |
174 | return this.chargingStation.ocppRequestService.requestHandler< | |
175 | HeartbeatRequest, | |
176 | HeartbeatResponse | |
177 | >(this.chargingStation, RequestCommand.HEARTBEAT, requestPayload); | |
6c8f5d90 JB |
178 | default: |
179 | // eslint-disable-next-line @typescript-eslint/restrict-template-expressions | |
ce7a4fc3 | 180 | throw new BaseError(`Unknown worker broadcast channel command: ${command}`); |
6c8f5d90 JB |
181 | } |
182 | } | |
183 | ||
10db00b2 JB |
184 | private commandResponseToResponseStatus( |
185 | command: BroadcastChannelProcedureName, | |
186 | commandResponse: CommandResponse | |
187 | ): ResponseStatus { | |
188 | switch (command) { | |
189 | case BroadcastChannelProcedureName.START_TRANSACTION: | |
190 | case BroadcastChannelProcedureName.STOP_TRANSACTION: | |
191 | if ( | |
192 | (commandResponse as StartTransactionResponse | StopTransactionResponse)?.idTagInfo | |
193 | ?.status === AuthorizationStatus.ACCEPTED | |
194 | ) { | |
195 | return ResponseStatus.SUCCESS; | |
196 | } | |
197 | return ResponseStatus.FAILURE; | |
198 | case BroadcastChannelProcedureName.STATUS_NOTIFICATION: | |
199 | if (Utils.isEmptyObject(commandResponse) === true) { | |
200 | return ResponseStatus.SUCCESS; | |
201 | } | |
202 | return ResponseStatus.FAILURE; | |
203 | case BroadcastChannelProcedureName.HEARTBEAT: | |
204 | if ('currentTime' in commandResponse) { | |
205 | return ResponseStatus.SUCCESS; | |
206 | } | |
207 | return ResponseStatus.FAILURE; | |
208 | default: | |
209 | return ResponseStatus.FAILURE; | |
89b7a234 JB |
210 | } |
211 | } | |
212 | } |