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, |
5cc4b63b | 170 | commandParams?: JsonType, |
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)) { |
f22266fd | 175 | return (await this.sendMessage( |
08f130a0 | 176 | chargingStation, |
9bf0ef23 | 177 | generateUUID(), |
18bf8274 | 178 | this.buildRequestPayload<RequestType>(chargingStation, commandName, commandParams), |
94a464f9 | 179 | commandName, |
66a7748d JB |
180 | params |
181 | )) as ResponseType | |
94a464f9 | 182 | } |
e909d2a7 | 183 | // OCPPError usage here is debatable: it's an error in the OCPP stack but not targeted to sendError(). |
94a464f9 JB |
184 | throw new OCPPError( |
185 | ErrorType.NOT_SUPPORTED, | |
6c8f5d90 | 186 | `Unsupported OCPP command '${commandName}'`, |
94a464f9 | 187 | commandName, |
66a7748d JB |
188 | commandParams |
189 | ) | |
c0560973 JB |
190 | } |
191 | ||
5cc4b63b | 192 | private buildRequestPayload<Request extends JsonType>( |
08f130a0 | 193 | chargingStation: ChargingStation, |
78085c42 | 194 | commandName: OCPP16RequestCommand, |
66a7748d | 195 | commandParams?: JsonType |
f22266fd | 196 | ): Request { |
66a7748d JB |
197 | let connectorId: number | undefined |
198 | let energyActiveImportRegister: number | |
199 | commandParams = commandParams as JsonObject | |
78085c42 | 200 | switch (commandName) { |
78085c42 | 201 | case OCPP16RequestCommand.BOOT_NOTIFICATION: |
78085c42 | 202 | case OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION: |
22e0d48e | 203 | case OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION: |
78085c42 | 204 | case OCPP16RequestCommand.METER_VALUES: |
78085c42 | 205 | case OCPP16RequestCommand.STATUS_NOTIFICATION: |
36c462a4 | 206 | case OCPP16RequestCommand.DATA_TRANSFER: |
66a7748d | 207 | return commandParams as unknown as Request |
36c462a4 | 208 | case OCPP16RequestCommand.AUTHORIZE: |
78085c42 | 209 | return { |
36c462a4 | 210 | idTag: Constants.DEFAULT_IDTAG, |
66a7748d JB |
211 | ...commandParams |
212 | } as unknown as Request | |
36c462a4 | 213 | case OCPP16RequestCommand.HEARTBEAT: |
66a7748d | 214 | return OCPP16Constants.OCPP_REQUEST_EMPTY as unknown as Request |
78085c42 JB |
215 | case OCPP16RequestCommand.START_TRANSACTION: |
216 | return { | |
36c462a4 JB |
217 | idTag: Constants.DEFAULT_IDTAG, |
218 | meterStart: chargingStation.getEnergyActiveImportRegisterByConnectorId( | |
5199f9fd | 219 | commandParams.connectorId as number, |
66a7748d | 220 | true |
36c462a4 JB |
221 | ), |
222 | timestamp: new Date(), | |
90aceaf6 JB |
223 | ...(OCPP16ServiceUtils.hasReservation( |
224 | chargingStation, | |
5199f9fd JB |
225 | commandParams.connectorId as number, |
226 | commandParams.idTag as string | |
90aceaf6 | 227 | ) && { |
66a7748d | 228 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion |
90aceaf6 JB |
229 | reservationId: chargingStation.getReservationBy( |
230 | 'connectorId', | |
231 | chargingStation.getConnectorStatus(0)?.status === OCPP16ChargePointStatus.Reserved | |
232 | ? 0 | |
5199f9fd | 233 | : (commandParams.connectorId as number) |
66a7748d | 234 | )!.reservationId |
90aceaf6 | 235 | }), |
66a7748d JB |
236 | ...commandParams |
237 | } as unknown as Request | |
78085c42 | 238 | case OCPP16RequestCommand.STOP_TRANSACTION: |
66a7748d | 239 | chargingStation.stationInfo?.transactionDataMeterValues === true && |
f1e731bd | 240 | (connectorId = chargingStation.getConnectorIdByTransactionId( |
5199f9fd | 241 | commandParams.transactionId as number |
f938317f | 242 | )) |
36c462a4 | 243 | energyActiveImportRegister = chargingStation.getEnergyActiveImportRegisterByTransactionId( |
5199f9fd | 244 | commandParams.transactionId as number, |
66a7748d JB |
245 | true |
246 | ) | |
78085c42 | 247 | return { |
5199f9fd | 248 | idTag: chargingStation.getTransactionIdTag(commandParams.transactionId as number), |
36c462a4 JB |
249 | meterStop: energyActiveImportRegister, |
250 | timestamp: new Date(), | |
66a7748d | 251 | ...(chargingStation.stationInfo?.transactionDataMeterValues === true && { |
36c462a4 | 252 | transactionData: OCPP16ServiceUtils.buildTransactionDataMeterValues( |
66a7748d | 253 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion |
e1d9a0f4 | 254 | chargingStation.getConnectorStatus(connectorId!)!.transactionBeginMeterValue!, |
36c462a4 JB |
255 | OCPP16ServiceUtils.buildTransactionEndMeterValue( |
256 | chargingStation, | |
66a7748d | 257 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion |
e1d9a0f4 | 258 | connectorId!, |
66a7748d JB |
259 | energyActiveImportRegister |
260 | ) | |
261 | ) | |
78085c42 | 262 | }), |
66a7748d JB |
263 | ...commandParams |
264 | } as unknown as Request | |
78085c42 | 265 | default: |
e909d2a7 | 266 | // OCPPError usage here is debatable: it's an error in the OCPP stack but not targeted to sendError(). |
78085c42 JB |
267 | throw new OCPPError( |
268 | ErrorType.NOT_SUPPORTED, | |
269 | // eslint-disable-next-line @typescript-eslint/restrict-template-expressions | |
6c8f5d90 | 270 | `Unsupported OCPP command '${commandName}'`, |
78085c42 | 271 | commandName, |
66a7748d JB |
272 | commandParams |
273 | ) | |
78085c42 JB |
274 | } |
275 | } | |
c0560973 | 276 | } |