Commit | Line | Data |
---|---|---|
edd13439 | 1 | // Partial Copyright Jerome Benoit. 2021-2023. All Rights Reserved. |
c8eeb62b | 2 | |
b52c969d JB |
3 | import fs from 'fs'; |
4 | import path from 'path'; | |
5 | import { fileURLToPath } from 'url'; | |
6 | ||
6c1761d4 | 7 | import type { JSONSchemaType } from 'ajv'; |
b52c969d | 8 | |
8114d10e | 9 | import OCPPError from '../../../exception/OCPPError'; |
6c1761d4 JB |
10 | import type { JsonObject, JsonType } from '../../../types/JsonType'; |
11 | import type { OCPP16MeterValuesRequest } from '../../../types/ocpp/1.6/MeterValues'; | |
b52c969d | 12 | import { |
27782dbc JB |
13 | type OCPP16BootNotificationRequest, |
14 | type OCPP16DataTransferRequest, | |
c9a4f9ea | 15 | type OCPP16DiagnosticsStatusNotificationRequest, |
27782dbc | 16 | type OCPP16HeartbeatRequest, |
b52c969d | 17 | OCPP16RequestCommand, |
27782dbc | 18 | type OCPP16StatusNotificationRequest, |
c9a4f9ea | 19 | type OCPP16UpdateFirmwareRequest, |
b52c969d | 20 | } from '../../../types/ocpp/1.6/Requests'; |
6c1761d4 | 21 | import type { |
b52c969d JB |
22 | OCPP16AuthorizeRequest, |
23 | OCPP16StartTransactionRequest, | |
24 | OCPP16StopTransactionRequest, | |
25 | } from '../../../types/ocpp/1.6/Transaction'; | |
8114d10e | 26 | import { ErrorType } from '../../../types/ocpp/ErrorType'; |
d270cc87 | 27 | import { OCPPVersion } from '../../../types/ocpp/OCPPVersion'; |
6c1761d4 | 28 | import type { RequestParams } from '../../../types/ocpp/Requests'; |
8114d10e | 29 | import Constants from '../../../utils/Constants'; |
c0560973 | 30 | import Utils from '../../../utils/Utils'; |
8114d10e JB |
31 | import type ChargingStation from '../../ChargingStation'; |
32 | import OCPPRequestService from '../OCPPRequestService'; | |
33 | import type OCPPResponseService from '../OCPPResponseService'; | |
34 | import { OCPP16ServiceUtils } from './OCPP16ServiceUtils'; | |
c0560973 | 35 | |
909dcf2d JB |
36 | const moduleName = 'OCPP16RequestService'; |
37 | ||
c0560973 | 38 | export default class OCPP16RequestService extends OCPPRequestService { |
b3fc3ff5 | 39 | protected jsonSchemas: Map<OCPP16RequestCommand, JSONSchemaType<JsonObject>>; |
b52c969d | 40 | |
08f130a0 | 41 | public constructor(ocppResponseService: OCPPResponseService) { |
909dcf2d | 42 | if (new.target?.name === moduleName) { |
06127450 | 43 | throw new TypeError(`Cannot construct ${new.target?.name} instances directly`); |
9f2e3130 | 44 | } |
d270cc87 | 45 | super(OCPPVersion.VERSION_16, ocppResponseService); |
b52c969d JB |
46 | this.jsonSchemas = new Map<OCPP16RequestCommand, JSONSchemaType<JsonObject>>([ |
47 | [ | |
48 | OCPP16RequestCommand.AUTHORIZE, | |
49 | JSON.parse( | |
50 | fs.readFileSync( | |
51 | path.resolve( | |
52 | path.dirname(fileURLToPath(import.meta.url)), | |
53 | '../../../assets/json-schemas/ocpp/1.6/Authorize.json' | |
54 | ), | |
55 | 'utf8' | |
56 | ) | |
57 | ) as JSONSchemaType<OCPP16AuthorizeRequest>, | |
58 | ], | |
59 | [ | |
60 | OCPP16RequestCommand.BOOT_NOTIFICATION, | |
61 | JSON.parse( | |
62 | fs.readFileSync( | |
63 | path.resolve( | |
64 | path.dirname(fileURLToPath(import.meta.url)), | |
65 | '../../../assets/json-schemas/ocpp/1.6/BootNotification.json' | |
66 | ), | |
67 | 'utf8' | |
68 | ) | |
69 | ) as JSONSchemaType<OCPP16BootNotificationRequest>, | |
70 | ], | |
71 | [ | |
72 | OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, | |
73 | JSON.parse( | |
74 | fs.readFileSync( | |
75 | path.resolve( | |
76 | path.dirname(fileURLToPath(import.meta.url)), | |
77 | '../../../assets/json-schemas/ocpp/1.6/DiagnosticsStatusNotification.json' | |
78 | ), | |
79 | 'utf8' | |
80 | ) | |
c9a4f9ea | 81 | ) as JSONSchemaType<OCPP16DiagnosticsStatusNotificationRequest>, |
b52c969d JB |
82 | ], |
83 | [ | |
84 | OCPP16RequestCommand.HEARTBEAT, | |
85 | JSON.parse( | |
86 | fs.readFileSync( | |
87 | path.resolve( | |
88 | path.dirname(fileURLToPath(import.meta.url)), | |
89 | '../../../assets/json-schemas/ocpp/1.6/Heartbeat.json' | |
90 | ), | |
91 | 'utf8' | |
92 | ) | |
93 | ) as JSONSchemaType<OCPP16HeartbeatRequest>, | |
94 | ], | |
95 | [ | |
96 | OCPP16RequestCommand.METER_VALUES, | |
97 | JSON.parse( | |
98 | fs.readFileSync( | |
99 | path.resolve( | |
100 | path.dirname(fileURLToPath(import.meta.url)), | |
101 | '../../../assets/json-schemas/ocpp/1.6/MeterValues.json' | |
102 | ), | |
103 | 'utf8' | |
104 | ) | |
105 | ) as JSONSchemaType<OCPP16MeterValuesRequest>, | |
106 | ], | |
107 | [ | |
108 | OCPP16RequestCommand.STATUS_NOTIFICATION, | |
109 | JSON.parse( | |
110 | fs.readFileSync( | |
111 | path.resolve( | |
112 | path.dirname(fileURLToPath(import.meta.url)), | |
113 | '../../../assets/json-schemas/ocpp/1.6/StatusNotification.json' | |
114 | ), | |
115 | 'utf8' | |
116 | ) | |
117 | ) as JSONSchemaType<OCPP16StatusNotificationRequest>, | |
118 | ], | |
119 | [ | |
120 | OCPP16RequestCommand.START_TRANSACTION, | |
121 | JSON.parse( | |
122 | fs.readFileSync( | |
123 | path.resolve( | |
124 | path.dirname(fileURLToPath(import.meta.url)), | |
125 | '../../../assets/json-schemas/ocpp/1.6/StartTransaction.json' | |
126 | ), | |
127 | 'utf8' | |
128 | ) | |
129 | ) as JSONSchemaType<OCPP16StartTransactionRequest>, | |
130 | ], | |
131 | [ | |
132 | OCPP16RequestCommand.STOP_TRANSACTION, | |
133 | JSON.parse( | |
134 | fs.readFileSync( | |
135 | path.resolve( | |
136 | path.dirname(fileURLToPath(import.meta.url)), | |
137 | '../../../assets/json-schemas/ocpp/1.6/StopTransaction.json' | |
138 | ), | |
139 | 'utf8' | |
140 | ) | |
141 | ) as JSONSchemaType<OCPP16StopTransactionRequest>, | |
142 | ], | |
91a7d3ea JB |
143 | [ |
144 | OCPP16RequestCommand.DATA_TRANSFER, | |
145 | JSON.parse( | |
146 | fs.readFileSync( | |
147 | path.resolve( | |
148 | path.dirname(fileURLToPath(import.meta.url)), | |
149 | '../../../assets/json-schemas/ocpp/1.6/DataTransfer.json' | |
150 | ), | |
151 | 'utf8' | |
152 | ) | |
153 | ) as JSONSchemaType<OCPP16DataTransferRequest>, | |
154 | ], | |
c9a4f9ea JB |
155 | [ |
156 | OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, | |
157 | JSON.parse( | |
158 | fs.readFileSync( | |
159 | path.resolve( | |
160 | path.dirname(fileURLToPath(import.meta.url)), | |
161 | '../../../assets/json-schemas/ocpp/1.6/FirmwareStatusNotification.json' | |
162 | ), | |
163 | 'utf8' | |
164 | ) | |
165 | ) as JSONSchemaType<OCPP16UpdateFirmwareRequest>, | |
166 | ], | |
b52c969d | 167 | ]); |
9952c548 | 168 | this.buildRequestPayload.bind(this); |
9f2e3130 JB |
169 | } |
170 | ||
6c1761d4 | 171 | public async requestHandler<RequestType extends JsonType, ResponseType extends JsonType>( |
08f130a0 | 172 | chargingStation: ChargingStation, |
94a464f9 | 173 | commandName: OCPP16RequestCommand, |
5cc4b63b | 174 | commandParams?: JsonType, |
be9b0d50 | 175 | params?: RequestParams |
6c1761d4 | 176 | ): Promise<ResponseType> { |
ed6cfcff | 177 | if (OCPP16ServiceUtils.isRequestCommandSupported(chargingStation, commandName) === true) { |
6c1761d4 | 178 | const requestPayload = this.buildRequestPayload<RequestType>( |
b52c969d JB |
179 | chargingStation, |
180 | commandName, | |
181 | commandParams | |
182 | ); | |
f22266fd | 183 | return (await this.sendMessage( |
08f130a0 | 184 | chargingStation, |
94a464f9 | 185 | Utils.generateUUID(), |
b52c969d | 186 | requestPayload, |
94a464f9 JB |
187 | commandName, |
188 | params | |
6c1761d4 | 189 | )) as unknown as ResponseType; |
94a464f9 | 190 | } |
e909d2a7 | 191 | // OCPPError usage here is debatable: it's an error in the OCPP stack but not targeted to sendError(). |
94a464f9 JB |
192 | throw new OCPPError( |
193 | ErrorType.NOT_SUPPORTED, | |
6c8f5d90 | 194 | `Unsupported OCPP command '${commandName}'`, |
94a464f9 | 195 | commandName, |
7369e417 | 196 | commandParams |
94a464f9 | 197 | ); |
c0560973 JB |
198 | } |
199 | ||
5cc4b63b | 200 | private buildRequestPayload<Request extends JsonType>( |
08f130a0 | 201 | chargingStation: ChargingStation, |
78085c42 | 202 | commandName: OCPP16RequestCommand, |
5cc4b63b | 203 | commandParams?: JsonType |
f22266fd | 204 | ): Request { |
68c993d5 | 205 | let connectorId: number; |
cf058664 | 206 | let energyActiveImportRegister: number; |
5cc4b63b | 207 | commandParams = commandParams as JsonObject; |
78085c42 JB |
208 | switch (commandName) { |
209 | case OCPP16RequestCommand.AUTHORIZE: | |
210 | return { | |
211 | ...(!Utils.isUndefined(commandParams?.idTag) | |
212 | ? { idTag: commandParams.idTag } | |
213 | : { idTag: Constants.DEFAULT_IDTAG }), | |
f22266fd | 214 | } as unknown as Request; |
78085c42 JB |
215 | case OCPP16RequestCommand.BOOT_NOTIFICATION: |
216 | return { | |
217 | chargePointModel: commandParams?.chargePointModel, | |
218 | chargePointVendor: commandParams?.chargePointVendor, | |
219 | ...(!Utils.isUndefined(commandParams?.chargeBoxSerialNumber) && { | |
220 | chargeBoxSerialNumber: commandParams.chargeBoxSerialNumber, | |
221 | }), | |
222 | ...(!Utils.isUndefined(commandParams?.chargePointSerialNumber) && { | |
223 | chargePointSerialNumber: commandParams.chargePointSerialNumber, | |
224 | }), | |
225 | ...(!Utils.isUndefined(commandParams?.firmwareVersion) && { | |
226 | firmwareVersion: commandParams.firmwareVersion, | |
227 | }), | |
228 | ...(!Utils.isUndefined(commandParams?.iccid) && { iccid: commandParams.iccid }), | |
229 | ...(!Utils.isUndefined(commandParams?.imsi) && { imsi: commandParams.imsi }), | |
230 | ...(!Utils.isUndefined(commandParams?.meterSerialNumber) && { | |
231 | meterSerialNumber: commandParams.meterSerialNumber, | |
232 | }), | |
233 | ...(!Utils.isUndefined(commandParams?.meterType) && { | |
234 | meterType: commandParams.meterType, | |
235 | }), | |
f22266fd | 236 | } as unknown as Request; |
78085c42 JB |
237 | case OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION: |
238 | return { | |
c9a4f9ea | 239 | status: commandParams?.status, |
f22266fd | 240 | } as unknown as Request; |
78085c42 | 241 | case OCPP16RequestCommand.HEARTBEAT: |
f22266fd | 242 | return {} as unknown as Request; |
78085c42 JB |
243 | case OCPP16RequestCommand.METER_VALUES: |
244 | return { | |
245 | connectorId: commandParams?.connectorId, | |
246 | transactionId: commandParams?.transactionId, | |
7369e417 | 247 | meterValue: commandParams?.meterValue, |
f22266fd | 248 | } as unknown as Request; |
78085c42 JB |
249 | case OCPP16RequestCommand.STATUS_NOTIFICATION: |
250 | return { | |
251 | connectorId: commandParams?.connectorId, | |
78085c42 | 252 | status: commandParams?.status, |
93b4a429 | 253 | errorCode: commandParams?.errorCode, |
f22266fd | 254 | } as unknown as Request; |
78085c42 JB |
255 | case OCPP16RequestCommand.START_TRANSACTION: |
256 | return { | |
257 | connectorId: commandParams?.connectorId, | |
258 | ...(!Utils.isUndefined(commandParams?.idTag) | |
259 | ? { idTag: commandParams?.idTag } | |
260 | : { idTag: Constants.DEFAULT_IDTAG }), | |
08f130a0 | 261 | meterStart: chargingStation.getEnergyActiveImportRegisterByConnectorId( |
78085c42 JB |
262 | commandParams?.connectorId as number |
263 | ), | |
aef5c03d | 264 | timestamp: new Date(), |
f22266fd | 265 | } as unknown as Request; |
78085c42 | 266 | case OCPP16RequestCommand.STOP_TRANSACTION: |
08f130a0 | 267 | connectorId = chargingStation.getConnectorIdByTransactionId( |
f479a792 JB |
268 | commandParams?.transactionId as number |
269 | ); | |
7acb3f7b JB |
270 | commandParams?.meterStop && |
271 | (energyActiveImportRegister = | |
272 | chargingStation.getEnergyActiveImportRegisterByTransactionId( | |
273 | commandParams?.transactionId as number, | |
274 | true | |
275 | )); | |
78085c42 JB |
276 | return { |
277 | transactionId: commandParams?.transactionId, | |
cf058664 JB |
278 | idTag: |
279 | commandParams?.idTag ?? | |
280 | chargingStation.getTransactionIdTag(commandParams?.transactionId as number), | |
281 | meterStop: commandParams?.meterStop ?? energyActiveImportRegister, | |
aef5c03d | 282 | timestamp: new Date(), |
cf058664 | 283 | reason: commandParams?.reason, |
08f130a0 | 284 | ...(chargingStation.getTransactionDataMeterValues() && { |
78085c42 | 285 | transactionData: OCPP16ServiceUtils.buildTransactionDataMeterValues( |
08f130a0 | 286 | chargingStation.getConnectorStatus(connectorId).transactionBeginMeterValue, |
78085c42 | 287 | OCPP16ServiceUtils.buildTransactionEndMeterValue( |
08f130a0 | 288 | chargingStation, |
68c993d5 | 289 | connectorId, |
cf058664 | 290 | (commandParams?.meterStop as number) ?? energyActiveImportRegister |
78085c42 JB |
291 | ) |
292 | ), | |
293 | }), | |
f22266fd | 294 | } as unknown as Request; |
91a7d3ea JB |
295 | case OCPP16RequestCommand.DATA_TRANSFER: |
296 | return commandParams as unknown as Request; | |
c9a4f9ea JB |
297 | case OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION: |
298 | return { | |
299 | status: commandParams?.status, | |
300 | } as unknown as Request; | |
78085c42 | 301 | default: |
e909d2a7 | 302 | // OCPPError usage here is debatable: it's an error in the OCPP stack but not targeted to sendError(). |
78085c42 JB |
303 | throw new OCPPError( |
304 | ErrorType.NOT_SUPPORTED, | |
305 | // eslint-disable-next-line @typescript-eslint/restrict-template-expressions | |
6c8f5d90 | 306 | `Unsupported OCPP command '${commandName}'`, |
78085c42 | 307 | commandName, |
7369e417 | 308 | commandParams |
78085c42 JB |
309 | ); |
310 | } | |
311 | } | |
c0560973 | 312 | } |