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