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