Commit | Line | Data |
---|---|---|
a19b897d | 1 | // Partial Copyright Jerome Benoit. 2021-2024. All Rights Reserved. |
c8eeb62b | 2 | |
24d15716 | 3 | import type { ValidateFunction } from 'ajv' |
b52c969d | 4 | |
66a7748d JB |
5 | import type { ChargingStation } from '../../../charging-station/index.js' |
6 | import { OCPPError } from '../../../exception/index.js' | |
b52c969d | 7 | import { |
268a74bb JB |
8 | ErrorType, |
9 | type JsonObject, | |
10 | type JsonType, | |
11 | type OCPP16AuthorizeRequest, | |
27782dbc | 12 | type OCPP16BootNotificationRequest, |
90aceaf6 | 13 | OCPP16ChargePointStatus, |
27782dbc | 14 | type OCPP16DataTransferRequest, |
c9a4f9ea | 15 | type OCPP16DiagnosticsStatusNotificationRequest, |
e9a4164c | 16 | type OCPP16FirmwareStatusNotificationRequest, |
27782dbc | 17 | type OCPP16HeartbeatRequest, |
268a74bb | 18 | type OCPP16MeterValuesRequest, |
b52c969d | 19 | OCPP16RequestCommand, |
268a74bb | 20 | type OCPP16StartTransactionRequest, |
27782dbc | 21 | type OCPP16StatusNotificationRequest, |
268a74bb JB |
22 | type OCPP16StopTransactionRequest, |
23 | OCPPVersion, | |
66a7748d JB |
24 | type RequestParams |
25 | } from '../../../types/index.js' | |
26 | import { Constants, generateUUID } from '../../../utils/index.js' | |
27 | import { OCPPRequestService } from '../OCPPRequestService.js' | |
28 | import type { OCPPResponseService } from '../OCPPResponseService.js' | |
4c3f6c20 JB |
29 | import { OCPP16Constants } from './OCPP16Constants.js' |
30 | import { OCPP16ServiceUtils } from './OCPP16ServiceUtils.js' | |
c0560973 | 31 | |
66a7748d | 32 | const moduleName = 'OCPP16RequestService' |
909dcf2d | 33 | |
268a74bb | 34 | export class OCPP16RequestService extends OCPPRequestService { |
d5490a13 | 35 | protected payloadValidateFunctions: Map<OCPP16RequestCommand, ValidateFunction<JsonType>> |
b52c969d | 36 | |
66a7748d | 37 | public constructor (ocppResponseService: OCPPResponseService) { |
5199f9fd JB |
38 | // if (new.target.name === moduleName) { |
39 | // throw new TypeError(`Cannot construct ${new.target.name} instances directly`) | |
b768993d | 40 | // } |
66a7748d | 41 | super(OCPPVersion.VERSION_16, ocppResponseService) |
d5490a13 | 42 | this.payloadValidateFunctions = new Map<OCPP16RequestCommand, ValidateFunction<JsonType>>([ |
b52c969d JB |
43 | [ |
44 | OCPP16RequestCommand.AUTHORIZE, | |
24d15716 JB |
45 | this.ajv |
46 | .compile( | |
47 | OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16AuthorizeRequest>( | |
48 | 'assets/json-schemas/ocpp/1.6/Authorize.json', | |
49 | moduleName, | |
50 | 'constructor' | |
51 | ) | |
52 | ) | |
53 | .bind(this) | |
b52c969d JB |
54 | ], |
55 | [ | |
56 | OCPP16RequestCommand.BOOT_NOTIFICATION, | |
24d15716 JB |
57 | this.ajv |
58 | .compile( | |
59 | OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16BootNotificationRequest>( | |
60 | 'assets/json-schemas/ocpp/1.6/BootNotification.json', | |
61 | moduleName, | |
62 | 'constructor' | |
63 | ) | |
64 | ) | |
65 | .bind(this) | |
b52c969d JB |
66 | ], |
67 | [ | |
68 | OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, | |
24d15716 JB |
69 | this.ajv |
70 | .compile( | |
71 | OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16DiagnosticsStatusNotificationRequest>( | |
72 | 'assets/json-schemas/ocpp/1.6/DiagnosticsStatusNotification.json', | |
73 | moduleName, | |
74 | 'constructor' | |
75 | ) | |
76 | ) | |
77 | .bind(this) | |
b52c969d JB |
78 | ], |
79 | [ | |
80 | OCPP16RequestCommand.HEARTBEAT, | |
24d15716 JB |
81 | this.ajv |
82 | .compile( | |
83 | OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16HeartbeatRequest>( | |
84 | 'assets/json-schemas/ocpp/1.6/Heartbeat.json', | |
85 | moduleName, | |
86 | 'constructor' | |
87 | ) | |
88 | ) | |
89 | .bind(this) | |
b52c969d JB |
90 | ], |
91 | [ | |
92 | OCPP16RequestCommand.METER_VALUES, | |
24d15716 JB |
93 | this.ajv |
94 | .compile( | |
95 | OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16MeterValuesRequest>( | |
96 | 'assets/json-schemas/ocpp/1.6/MeterValues.json', | |
97 | moduleName, | |
98 | 'constructor' | |
99 | ) | |
100 | ) | |
101 | .bind(this) | |
b52c969d JB |
102 | ], |
103 | [ | |
104 | OCPP16RequestCommand.STATUS_NOTIFICATION, | |
24d15716 JB |
105 | this.ajv |
106 | .compile( | |
107 | OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16StatusNotificationRequest>( | |
108 | 'assets/json-schemas/ocpp/1.6/StatusNotification.json', | |
109 | moduleName, | |
110 | 'constructor' | |
111 | ) | |
112 | ) | |
113 | .bind(this) | |
b52c969d JB |
114 | ], |
115 | [ | |
116 | OCPP16RequestCommand.START_TRANSACTION, | |
24d15716 JB |
117 | this.ajv |
118 | .compile( | |
119 | OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16StartTransactionRequest>( | |
120 | 'assets/json-schemas/ocpp/1.6/StartTransaction.json', | |
121 | moduleName, | |
122 | 'constructor' | |
123 | ) | |
124 | ) | |
125 | .bind(this) | |
b52c969d JB |
126 | ], |
127 | [ | |
128 | OCPP16RequestCommand.STOP_TRANSACTION, | |
24d15716 JB |
129 | this.ajv |
130 | .compile( | |
131 | OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16StopTransactionRequest>( | |
132 | 'assets/json-schemas/ocpp/1.6/StopTransaction.json', | |
133 | moduleName, | |
134 | 'constructor' | |
135 | ) | |
136 | ) | |
137 | .bind(this) | |
b52c969d | 138 | ], |
91a7d3ea JB |
139 | [ |
140 | OCPP16RequestCommand.DATA_TRANSFER, | |
24d15716 JB |
141 | this.ajv |
142 | .compile( | |
143 | OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16DataTransferRequest>( | |
144 | 'assets/json-schemas/ocpp/1.6/DataTransfer.json', | |
145 | moduleName, | |
146 | 'constructor' | |
147 | ) | |
148 | ) | |
149 | .bind(this) | |
91a7d3ea | 150 | ], |
c9a4f9ea JB |
151 | [ |
152 | OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, | |
24d15716 JB |
153 | this.ajv |
154 | .compile( | |
155 | OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16FirmwareStatusNotificationRequest>( | |
156 | 'assets/json-schemas/ocpp/1.6/FirmwareStatusNotification.json', | |
157 | moduleName, | |
158 | 'constructor' | |
159 | ) | |
160 | ) | |
161 | .bind(this) | |
66a7748d JB |
162 | ] |
163 | ]) | |
ba9a56a6 | 164 | this.buildRequestPayload = this.buildRequestPayload.bind(this) |
9f2e3130 JB |
165 | } |
166 | ||
6c1761d4 | 167 | public async requestHandler<RequestType extends JsonType, ResponseType extends JsonType>( |
08f130a0 | 168 | chargingStation: ChargingStation, |
94a464f9 | 169 | commandName: OCPP16RequestCommand, |
a9671b9e | 170 | commandParams?: RequestType, |
66a7748d | 171 | params?: RequestParams |
6c1761d4 | 172 | ): Promise<ResponseType> { |
62340a29 | 173 | // FIXME?: add sanity checks on charging station availability, connector availability, connector status, etc. |
66a7748d | 174 | if (OCPP16ServiceUtils.isRequestCommandSupported(chargingStation, commandName)) { |
1d85f415 | 175 | // Pre request actions hook |
a9671b9e JB |
176 | switch (commandName) { |
177 | case OCPP16RequestCommand.START_TRANSACTION: | |
178 | await OCPP16ServiceUtils.sendAndSetConnectorStatus( | |
179 | chargingStation, | |
180 | (commandParams as OCPP16StartTransactionRequest).connectorId, | |
181 | OCPP16ChargePointStatus.Preparing | |
182 | ) | |
183 | break | |
184 | } | |
f22266fd | 185 | return (await this.sendMessage( |
08f130a0 | 186 | chargingStation, |
9bf0ef23 | 187 | generateUUID(), |
18bf8274 | 188 | this.buildRequestPayload<RequestType>(chargingStation, commandName, commandParams), |
94a464f9 | 189 | commandName, |
66a7748d JB |
190 | params |
191 | )) as ResponseType | |
94a464f9 | 192 | } |
e909d2a7 | 193 | // OCPPError usage here is debatable: it's an error in the OCPP stack but not targeted to sendError(). |
94a464f9 JB |
194 | throw new OCPPError( |
195 | ErrorType.NOT_SUPPORTED, | |
6c8f5d90 | 196 | `Unsupported OCPP command '${commandName}'`, |
94a464f9 | 197 | commandName, |
66a7748d JB |
198 | commandParams |
199 | ) | |
c0560973 JB |
200 | } |
201 | ||
5cc4b63b | 202 | private buildRequestPayload<Request extends JsonType>( |
08f130a0 | 203 | chargingStation: ChargingStation, |
78085c42 | 204 | commandName: OCPP16RequestCommand, |
66a7748d | 205 | commandParams?: JsonType |
f22266fd | 206 | ): Request { |
66a7748d JB |
207 | let connectorId: number | undefined |
208 | let energyActiveImportRegister: number | |
209 | commandParams = commandParams as JsonObject | |
78085c42 | 210 | switch (commandName) { |
78085c42 | 211 | case OCPP16RequestCommand.BOOT_NOTIFICATION: |
78085c42 | 212 | case OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION: |
22e0d48e | 213 | case OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION: |
78085c42 | 214 | case OCPP16RequestCommand.METER_VALUES: |
78085c42 | 215 | case OCPP16RequestCommand.STATUS_NOTIFICATION: |
36c462a4 | 216 | case OCPP16RequestCommand.DATA_TRANSFER: |
66a7748d | 217 | return commandParams as unknown as Request |
36c462a4 | 218 | case OCPP16RequestCommand.AUTHORIZE: |
78085c42 | 219 | return { |
36c462a4 | 220 | idTag: Constants.DEFAULT_IDTAG, |
66a7748d JB |
221 | ...commandParams |
222 | } as unknown as Request | |
36c462a4 | 223 | case OCPP16RequestCommand.HEARTBEAT: |
66a7748d | 224 | return OCPP16Constants.OCPP_REQUEST_EMPTY as unknown as Request |
78085c42 JB |
225 | case OCPP16RequestCommand.START_TRANSACTION: |
226 | return { | |
36c462a4 JB |
227 | idTag: Constants.DEFAULT_IDTAG, |
228 | meterStart: chargingStation.getEnergyActiveImportRegisterByConnectorId( | |
5199f9fd | 229 | commandParams.connectorId as number, |
66a7748d | 230 | true |
36c462a4 JB |
231 | ), |
232 | timestamp: new Date(), | |
90aceaf6 JB |
233 | ...(OCPP16ServiceUtils.hasReservation( |
234 | chargingStation, | |
5199f9fd JB |
235 | commandParams.connectorId as number, |
236 | commandParams.idTag as string | |
90aceaf6 | 237 | ) && { |
66a7748d | 238 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion |
90aceaf6 JB |
239 | reservationId: chargingStation.getReservationBy( |
240 | 'connectorId', | |
241 | chargingStation.getConnectorStatus(0)?.status === OCPP16ChargePointStatus.Reserved | |
242 | ? 0 | |
5199f9fd | 243 | : (commandParams.connectorId as number) |
66a7748d | 244 | )!.reservationId |
90aceaf6 | 245 | }), |
66a7748d JB |
246 | ...commandParams |
247 | } as unknown as Request | |
78085c42 | 248 | case OCPP16RequestCommand.STOP_TRANSACTION: |
66a7748d | 249 | chargingStation.stationInfo?.transactionDataMeterValues === true && |
f1e731bd | 250 | (connectorId = chargingStation.getConnectorIdByTransactionId( |
5199f9fd | 251 | commandParams.transactionId as number |
f938317f | 252 | )) |
36c462a4 | 253 | energyActiveImportRegister = chargingStation.getEnergyActiveImportRegisterByTransactionId( |
5199f9fd | 254 | commandParams.transactionId as number, |
66a7748d JB |
255 | true |
256 | ) | |
78085c42 | 257 | return { |
5199f9fd | 258 | idTag: chargingStation.getTransactionIdTag(commandParams.transactionId as number), |
36c462a4 JB |
259 | meterStop: energyActiveImportRegister, |
260 | timestamp: new Date(), | |
66a7748d | 261 | ...(chargingStation.stationInfo?.transactionDataMeterValues === true && { |
36c462a4 | 262 | transactionData: OCPP16ServiceUtils.buildTransactionDataMeterValues( |
66a7748d | 263 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion |
e1d9a0f4 | 264 | chargingStation.getConnectorStatus(connectorId!)!.transactionBeginMeterValue!, |
36c462a4 JB |
265 | OCPP16ServiceUtils.buildTransactionEndMeterValue( |
266 | chargingStation, | |
66a7748d | 267 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion |
e1d9a0f4 | 268 | connectorId!, |
66a7748d JB |
269 | energyActiveImportRegister |
270 | ) | |
271 | ) | |
78085c42 | 272 | }), |
66a7748d JB |
273 | ...commandParams |
274 | } as unknown as Request | |
78085c42 | 275 | default: |
e909d2a7 | 276 | // OCPPError usage here is debatable: it's an error in the OCPP stack but not targeted to sendError(). |
78085c42 JB |
277 | throw new OCPPError( |
278 | ErrorType.NOT_SUPPORTED, | |
279 | // eslint-disable-next-line @typescript-eslint/restrict-template-expressions | |
6c8f5d90 | 280 | `Unsupported OCPP command '${commandName}'`, |
78085c42 | 281 | commandName, |
66a7748d JB |
282 | commandParams |
283 | ) | |
78085c42 JB |
284 | } |
285 | } | |
c0560973 | 286 | } |