refactor: cleanup reservation handling code
[e-mobility-charging-stations-simulator.git] / src / charging-station / ocpp / 1.6 / OCPP16ResponseService.ts
CommitLineData
edd13439 1// Partial Copyright Jerome Benoit. 2021-2023. All Rights Reserved.
c8eeb62b 2
04b1261c
JB
3import { parentPort } from 'node:worker_threads';
4
6c1761d4 5import type { JSONSchemaType } from 'ajv';
844e496b 6
4c3c0d59 7import { OCPP16ServiceUtils } from './OCPP16ServiceUtils';
04b1261c
JB
8import {
9 type ChargingStation,
10 ChargingStationConfigurationUtils,
11 ChargingStationUtils,
04b1261c 12} from '../../../charging-station';
268a74bb 13import { OCPPError } from '../../../exception';
ef6fa3fb 14import {
268a74bb
JB
15 type ChangeAvailabilityResponse,
16 type ChangeConfigurationResponse,
17 type ClearChargingProfileResponse,
18 ErrorType,
19 type GenericResponse,
20 type GetConfigurationResponse,
21 type GetDiagnosticsResponse,
22 type JsonObject,
23 type JsonType,
8114d10e 24 OCPP16AuthorizationStatus,
27782dbc
JB
25 type OCPP16AuthorizeRequest,
26 type OCPP16AuthorizeResponse,
268a74bb 27 type OCPP16BootNotificationResponse,
66dd3447 28 type OCPP16CancelReservationResponse,
268a74bb
JB
29 OCPP16ChargePointStatus,
30 type OCPP16DataTransferResponse,
31 type OCPP16DiagnosticsStatusNotificationResponse,
32 type OCPP16FirmwareStatusNotificationResponse,
41189456 33 type OCPP16GetCompositeScheduleResponse,
268a74bb
JB
34 type OCPP16HeartbeatResponse,
35 OCPP16IncomingRequestCommand,
36 type OCPP16MeterValuesRequest,
37 type OCPP16MeterValuesResponse,
38 OCPP16RequestCommand,
66dd3447 39 type OCPP16ReserveNowResponse,
268a74bb 40 OCPP16StandardParametersKey,
27782dbc
JB
41 type OCPP16StartTransactionRequest,
42 type OCPP16StartTransactionResponse,
268a74bb 43 type OCPP16StatusNotificationResponse,
27782dbc
JB
44 type OCPP16StopTransactionRequest,
45 type OCPP16StopTransactionResponse,
268a74bb
JB
46 type OCPP16TriggerMessageResponse,
47 type OCPP16UpdateFirmwareResponse,
48 OCPPVersion,
02887891
JB
49 RegistrationStatusEnumType,
50 type ResponseHandler,
268a74bb
JB
51 type SetChargingProfileResponse,
52 type UnlockConnectorResponse,
53} from '../../../types';
c8faabc8 54import { Constants, Utils, buildUpdatedMessage, logger } from '../../../utils';
4c3c0d59 55import { OCPPResponseService } from '../OCPPResponseService';
c0560973 56
909dcf2d
JB
57const moduleName = 'OCPP16ResponseService';
58
268a74bb 59export class OCPP16ResponseService extends OCPPResponseService {
b3fc3ff5
JB
60 public jsonIncomingRequestResponseSchemas: Map<
61 OCPP16IncomingRequestCommand,
62 JSONSchemaType<JsonObject>
63 >;
64
58144adb 65 private responseHandlers: Map<OCPP16RequestCommand, ResponseHandler>;
b52c969d 66 private jsonSchemas: Map<OCPP16RequestCommand, JSONSchemaType<JsonObject>>;
58144adb 67
08f130a0 68 public constructor() {
b768993d
JB
69 // if (new.target?.name === moduleName) {
70 // throw new TypeError(`Cannot construct ${new.target?.name} instances directly`);
71 // }
d270cc87 72 super(OCPPVersion.VERSION_16);
58144adb
JB
73 this.responseHandlers = new Map<OCPP16RequestCommand, ResponseHandler>([
74 [OCPP16RequestCommand.BOOT_NOTIFICATION, this.handleResponseBootNotification.bind(this)],
b52c969d 75 [OCPP16RequestCommand.HEARTBEAT, this.emptyResponseHandler.bind(this)],
58144adb
JB
76 [OCPP16RequestCommand.AUTHORIZE, this.handleResponseAuthorize.bind(this)],
77 [OCPP16RequestCommand.START_TRANSACTION, this.handleResponseStartTransaction.bind(this)],
78 [OCPP16RequestCommand.STOP_TRANSACTION, this.handleResponseStopTransaction.bind(this)],
b52c969d
JB
79 [OCPP16RequestCommand.STATUS_NOTIFICATION, this.emptyResponseHandler.bind(this)],
80 [OCPP16RequestCommand.METER_VALUES, this.emptyResponseHandler.bind(this)],
81 [OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, this.emptyResponseHandler.bind(this)],
91a7d3ea 82 [OCPP16RequestCommand.DATA_TRANSFER, this.emptyResponseHandler.bind(this)],
c9a4f9ea 83 [OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, this.emptyResponseHandler.bind(this)],
b52c969d
JB
84 ]);
85 this.jsonSchemas = new Map<OCPP16RequestCommand, JSONSchemaType<JsonObject>>([
86 [
87 OCPP16RequestCommand.BOOT_NOTIFICATION,
130783a7 88 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16BootNotificationResponse>(
51022aa0 89 'assets/json-schemas/ocpp/1.6/BootNotificationResponse.json',
1b271a54
JB
90 moduleName,
91 'constructor'
e9a4164c 92 ),
b52c969d
JB
93 ],
94 [
95 OCPP16RequestCommand.HEARTBEAT,
130783a7 96 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16HeartbeatResponse>(
51022aa0 97 'assets/json-schemas/ocpp/1.6/HeartbeatResponse.json',
1b271a54
JB
98 moduleName,
99 'constructor'
e9a4164c 100 ),
b52c969d
JB
101 ],
102 [
103 OCPP16RequestCommand.AUTHORIZE,
130783a7 104 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16AuthorizeResponse>(
51022aa0 105 'assets/json-schemas/ocpp/1.6/AuthorizeResponse.json',
1b271a54
JB
106 moduleName,
107 'constructor'
e9a4164c 108 ),
b52c969d
JB
109 ],
110 [
111 OCPP16RequestCommand.START_TRANSACTION,
130783a7 112 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16StartTransactionResponse>(
51022aa0 113 'assets/json-schemas/ocpp/1.6/StartTransactionResponse.json',
1b271a54
JB
114 moduleName,
115 'constructor'
e9a4164c 116 ),
b52c969d
JB
117 ],
118 [
119 OCPP16RequestCommand.STOP_TRANSACTION,
130783a7 120 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16StopTransactionResponse>(
51022aa0 121 'assets/json-schemas/ocpp/1.6/StopTransactionResponse.json',
1b271a54
JB
122 moduleName,
123 'constructor'
e9a4164c 124 ),
b52c969d
JB
125 ],
126 [
127 OCPP16RequestCommand.STATUS_NOTIFICATION,
130783a7 128 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16StatusNotificationResponse>(
51022aa0 129 'assets/json-schemas/ocpp/1.6/StatusNotificationResponse.json',
1b271a54
JB
130 moduleName,
131 'constructor'
e9a4164c 132 ),
b52c969d
JB
133 ],
134 [
135 OCPP16RequestCommand.METER_VALUES,
130783a7 136 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16MeterValuesResponse>(
51022aa0 137 'assets/json-schemas/ocpp/1.6/MeterValuesResponse.json',
1b271a54
JB
138 moduleName,
139 'constructor'
e9a4164c 140 ),
b52c969d
JB
141 ],
142 [
143 OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION,
130783a7 144 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16DiagnosticsStatusNotificationResponse>(
51022aa0 145 'assets/json-schemas/ocpp/1.6/DiagnosticsStatusNotificationResponse.json',
1b271a54
JB
146 moduleName,
147 'constructor'
e9a4164c 148 ),
b52c969d 149 ],
91a7d3ea
JB
150 [
151 OCPP16RequestCommand.DATA_TRANSFER,
130783a7 152 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16DataTransferResponse>(
51022aa0 153 'assets/json-schemas/ocpp/1.6/DataTransferResponse.json',
1b271a54
JB
154 moduleName,
155 'constructor'
e9a4164c 156 ),
91a7d3ea 157 ],
c9a4f9ea
JB
158 [
159 OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION,
130783a7 160 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16FirmwareStatusNotificationResponse>(
51022aa0 161 'assets/json-schemas/ocpp/1.6/FirmwareStatusNotificationResponse.json',
1b271a54
JB
162 moduleName,
163 'constructor'
e9a4164c 164 ),
c9a4f9ea 165 ],
66dd3447
JB
166 [
167 OCPP16RequestCommand.RESERVE_NOW,
168 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16ReserveNowResponse>(
169 'assets/json-schemas/ocpp/1.6/ReserveNowResponse.json',
170 moduleName,
171 'constructor'
172 ),
173 ],
174 [
175 OCPP16RequestCommand.CANCEL_RESERVATION,
176 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16CancelReservationResponse>(
177 'assets/json-schemas/ocpp/1.6/CancelReservationResponse.json',
178 moduleName,
179 'constructor'
180 ),
181 ],
58144adb 182 ]);
02887891
JB
183 this.jsonIncomingRequestResponseSchemas = new Map([
184 [
185 OCPP16IncomingRequestCommand.RESET,
130783a7 186 OCPP16ServiceUtils.parseJsonSchemaFile<GenericResponse>(
51022aa0 187 'assets/json-schemas/ocpp/1.6/ResetResponse.json',
1b271a54
JB
188 moduleName,
189 'constructor'
e9a4164c 190 ),
02887891
JB
191 ],
192 [
193 OCPP16IncomingRequestCommand.CLEAR_CACHE,
130783a7 194 OCPP16ServiceUtils.parseJsonSchemaFile<GenericResponse>(
51022aa0 195 'assets/json-schemas/ocpp/1.6/ClearCacheResponse.json',
1b271a54
JB
196 moduleName,
197 'constructor'
e9a4164c 198 ),
02887891
JB
199 ],
200 [
201 OCPP16IncomingRequestCommand.CHANGE_AVAILABILITY,
130783a7 202 OCPP16ServiceUtils.parseJsonSchemaFile<ChangeAvailabilityResponse>(
51022aa0 203 'assets/json-schemas/ocpp/1.6/ChangeAvailabilityResponse.json',
1b271a54
JB
204 moduleName,
205 'constructor'
e9a4164c 206 ),
02887891
JB
207 ],
208 [
209 OCPP16IncomingRequestCommand.UNLOCK_CONNECTOR,
130783a7 210 OCPP16ServiceUtils.parseJsonSchemaFile<UnlockConnectorResponse>(
51022aa0 211 'assets/json-schemas/ocpp/1.6/UnlockConnectorResponse.json',
1b271a54
JB
212 moduleName,
213 'constructor'
e9a4164c 214 ),
02887891
JB
215 ],
216 [
217 OCPP16IncomingRequestCommand.GET_CONFIGURATION,
130783a7 218 OCPP16ServiceUtils.parseJsonSchemaFile<GetConfigurationResponse>(
51022aa0 219 'assets/json-schemas/ocpp/1.6/GetConfigurationResponse.json',
1b271a54
JB
220 moduleName,
221 'constructor'
e9a4164c 222 ),
02887891
JB
223 ],
224 [
225 OCPP16IncomingRequestCommand.CHANGE_CONFIGURATION,
130783a7 226 OCPP16ServiceUtils.parseJsonSchemaFile<ChangeConfigurationResponse>(
51022aa0 227 'assets/json-schemas/ocpp/1.6/ChangeConfigurationResponse.json',
1b271a54
JB
228 moduleName,
229 'constructor'
e9a4164c 230 ),
02887891 231 ],
41189456
JB
232 [
233 OCPP16IncomingRequestCommand.GET_COMPOSITE_SCHEDULE,
234 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16GetCompositeScheduleResponse>(
51022aa0 235 'assets/json-schemas/ocpp/1.6/GetCompositeScheduleResponse.json',
41189456
JB
236 moduleName,
237 'constructor'
238 ),
239 ],
02887891
JB
240 [
241 OCPP16IncomingRequestCommand.SET_CHARGING_PROFILE,
130783a7 242 OCPP16ServiceUtils.parseJsonSchemaFile<SetChargingProfileResponse>(
51022aa0 243 'assets/json-schemas/ocpp/1.6/SetChargingProfileResponse.json',
1b271a54
JB
244 moduleName,
245 'constructor'
e9a4164c 246 ),
02887891
JB
247 ],
248 [
249 OCPP16IncomingRequestCommand.CLEAR_CHARGING_PROFILE,
130783a7 250 OCPP16ServiceUtils.parseJsonSchemaFile<ClearChargingProfileResponse>(
51022aa0 251 'assets/json-schemas/ocpp/1.6/ClearChargingProfileResponse.json',
1b271a54
JB
252 moduleName,
253 'constructor'
e9a4164c 254 ),
02887891
JB
255 ],
256 [
257 OCPP16IncomingRequestCommand.REMOTE_START_TRANSACTION,
130783a7 258 OCPP16ServiceUtils.parseJsonSchemaFile<GenericResponse>(
51022aa0 259 'assets/json-schemas/ocpp/1.6/RemoteStartTransactionResponse.json',
1b271a54
JB
260 moduleName,
261 'constructor'
e9a4164c 262 ),
02887891
JB
263 ],
264 [
265 OCPP16IncomingRequestCommand.REMOTE_STOP_TRANSACTION,
130783a7 266 OCPP16ServiceUtils.parseJsonSchemaFile<GenericResponse>(
51022aa0 267 'assets/json-schemas/ocpp/1.6/RemoteStopTransactionResponse.json',
1b271a54
JB
268 moduleName,
269 'constructor'
e9a4164c 270 ),
02887891
JB
271 ],
272 [
273 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS,
130783a7 274 OCPP16ServiceUtils.parseJsonSchemaFile<GetDiagnosticsResponse>(
51022aa0 275 'assets/json-schemas/ocpp/1.6/GetDiagnosticsResponse.json',
1b271a54
JB
276 moduleName,
277 'constructor'
e9a4164c 278 ),
02887891
JB
279 ],
280 [
281 OCPP16IncomingRequestCommand.TRIGGER_MESSAGE,
130783a7 282 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16TriggerMessageResponse>(
51022aa0 283 'assets/json-schemas/ocpp/1.6/TriggerMessageResponse.json',
1b271a54
JB
284 moduleName,
285 'constructor'
e9a4164c 286 ),
02887891
JB
287 ],
288 [
289 OCPP16IncomingRequestCommand.DATA_TRANSFER,
130783a7 290 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16DataTransferResponse>(
51022aa0 291 'assets/json-schemas/ocpp/1.6/DataTransferResponse.json',
1b271a54
JB
292 moduleName,
293 'constructor'
e9a4164c 294 ),
02887891
JB
295 ],
296 [
297 OCPP16IncomingRequestCommand.UPDATE_FIRMWARE,
130783a7 298 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16UpdateFirmwareResponse>(
51022aa0 299 'assets/json-schemas/ocpp/1.6/UpdateFirmwareResponse.json',
1b271a54
JB
300 moduleName,
301 'constructor'
e9a4164c 302 ),
02887891
JB
303 ],
304 ]);
31f59c6d
JB
305 this.validatePayload = this.validatePayload.bind(this) as (
306 chargingStation: ChargingStation,
307 commandName: OCPP16RequestCommand,
308 payload: JsonType
309 ) => boolean;
58144adb
JB
310 }
311
f7f98c68 312 public async responseHandler(
08f130a0 313 chargingStation: ChargingStation,
e7aeea18 314 commandName: OCPP16RequestCommand,
5cc4b63b
JB
315 payload: JsonType,
316 requestPayload: JsonType
e7aeea18 317 ): Promise<void> {
ed6cfcff
JB
318 if (
319 chargingStation.isRegistered() === true ||
320 commandName === OCPP16RequestCommand.BOOT_NOTIFICATION
321 ) {
65554cc3 322 if (
ed6cfcff
JB
323 this.responseHandlers.has(commandName) === true &&
324 OCPP16ServiceUtils.isRequestCommandSupported(chargingStation, commandName) === true
65554cc3 325 ) {
124f3553 326 try {
9c5c4195 327 this.validatePayload(chargingStation, commandName, payload);
08f130a0 328 await this.responseHandlers.get(commandName)(chargingStation, payload, requestPayload);
124f3553 329 } catch (error) {
6c8f5d90
JB
330 logger.error(
331 `${chargingStation.logPrefix()} ${moduleName}.responseHandler: Handle response error:`,
332 error
333 );
124f3553
JB
334 throw error;
335 }
336 } else {
337 // Throw exception
e7aeea18
JB
338 throw new OCPPError(
339 ErrorType.NOT_IMPLEMENTED,
6c8f5d90 340 `${commandName} is not implemented to handle response PDU ${JSON.stringify(
e7aeea18
JB
341 payload,
342 null,
343 2
344 )}`,
7369e417
JB
345 commandName,
346 payload
e7aeea18 347 );
887fef76 348 }
c0560973 349 } else {
e7aeea18
JB
350 throw new OCPPError(
351 ErrorType.SECURITY_ERROR,
6c8f5d90 352 `${commandName} cannot be issued to handle response PDU ${JSON.stringify(
e7aeea18
JB
353 payload,
354 null,
355 2
439fc71b 356 )} while the charging station is not registered on the central server.`,
7369e417
JB
357 commandName,
358 payload
e7aeea18 359 );
c0560973
JB
360 }
361 }
362
9c5c4195
JB
363 private validatePayload(
364 chargingStation: ChargingStation,
365 commandName: OCPP16RequestCommand,
366 payload: JsonType
367 ): boolean {
45988780 368 if (this.jsonSchemas.has(commandName) === true) {
9c5c4195
JB
369 return this.validateResponsePayload(
370 chargingStation,
371 commandName,
372 this.jsonSchemas.get(commandName),
373 payload
374 );
375 }
376 logger.warn(
b3fc3ff5 377 `${chargingStation.logPrefix()} ${moduleName}.validatePayload: No JSON schema found for command '${commandName}' PDU validation`
9c5c4195
JB
378 );
379 return false;
380 }
381
08f130a0
JB
382 private handleResponseBootNotification(
383 chargingStation: ChargingStation,
384 payload: OCPP16BootNotificationResponse
385 ): void {
d270cc87 386 if (payload.status === RegistrationStatusEnumType.ACCEPTED) {
17ac262c
JB
387 ChargingStationConfigurationUtils.addConfigurationKey(
388 chargingStation,
f0f65a62 389 OCPP16StandardParametersKey.HeartbeatInterval,
a95873d8 390 payload.interval.toString(),
abe9e9dd 391 {},
a95873d8 392 { overwrite: true, save: true }
e7aeea18 393 );
17ac262c
JB
394 ChargingStationConfigurationUtils.addConfigurationKey(
395 chargingStation,
f0f65a62 396 OCPP16StandardParametersKey.HeartBeatInterval,
e7aeea18 397 payload.interval.toString(),
00db15b8 398 { visible: false },
a95873d8 399 { overwrite: true, save: true }
e7aeea18 400 );
8f953431 401 OCPP16ServiceUtils.startHeartbeatInterval(chargingStation, payload.interval);
672fed6e 402 }
d270cc87 403 if (Object.values(RegistrationStatusEnumType).includes(payload.status)) {
08f130a0 404 const logMsg = `${chargingStation.logPrefix()} Charging station in '${
e7aeea18
JB
405 payload.status
406 }' state on the central server`;
d270cc87 407 payload.status === RegistrationStatusEnumType.REJECTED
e7aeea18
JB
408 ? logger.warn(logMsg)
409 : logger.info(logMsg);
c0560973 410 } else {
e7aeea18 411 logger.error(
44eb6026 412 `${chargingStation.logPrefix()} Charging station boot notification response received: %j with undefined registration status`,
e7aeea18
JB
413 payload
414 );
c0560973
JB
415 }
416 }
417
e7aeea18 418 private handleResponseAuthorize(
08f130a0 419 chargingStation: ChargingStation,
e7aeea18 420 payload: OCPP16AuthorizeResponse,
ef6fa3fb 421 requestPayload: OCPP16AuthorizeRequest
e7aeea18 422 ): void {
58144adb 423 let authorizeConnectorId: number;
ded57f02
JB
424 if (chargingStation.hasEvses) {
425 for (const [evseId, evseStatus] of chargingStation.evses) {
426 if (evseId > 0) {
427 for (const [connectorId, connectorStatus] of evseStatus.connectors) {
428 if (connectorStatus?.authorizeIdTag === requestPayload.idTag) {
429 authorizeConnectorId = connectorId;
430 break;
431 }
432 }
433 }
434 }
435 } else {
436 for (const connectorId of chargingStation.connectors.keys()) {
437 if (
438 connectorId > 0 &&
439 chargingStation.getConnectorStatus(connectorId)?.authorizeIdTag === requestPayload.idTag
440 ) {
441 authorizeConnectorId = connectorId;
442 break;
443 }
58144adb
JB
444 }
445 }
9bb1159e 446 const authorizeConnectorIdDefined = !Utils.isNullOrUndefined(authorizeConnectorId);
58144adb 447 if (payload.idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED) {
44eb6026 448 authorizeConnectorIdDefined &&
1984f194 449 (chargingStation.getConnectorStatus(authorizeConnectorId).idTagAuthorized = true);
e7aeea18 450 logger.debug(
2585c6e9 451 `${chargingStation.logPrefix()} idTag '${requestPayload.idTag}' accepted${
54ebb82c 452 authorizeConnectorIdDefined ? ` on connector id ${authorizeConnectorId}` : ''
1984f194 453 }`
e7aeea18 454 );
58144adb 455 } else {
44eb6026 456 if (authorizeConnectorIdDefined) {
1984f194 457 chargingStation.getConnectorStatus(authorizeConnectorId).idTagAuthorized = false;
d812bdcb 458 delete chargingStation.getConnectorStatus(authorizeConnectorId)?.authorizeIdTag;
1984f194 459 }
e7aeea18 460 logger.debug(
2585c6e9 461 `${chargingStation.logPrefix()} idTag '${requestPayload.idTag}' rejected with status '${
e7aeea18 462 payload.idTagInfo.status
54ebb82c 463 }'${authorizeConnectorIdDefined ? ` on connector id ${authorizeConnectorId}` : ''}`
e7aeea18 464 );
58144adb
JB
465 }
466 }
467
e7aeea18 468 private async handleResponseStartTransaction(
08f130a0 469 chargingStation: ChargingStation,
e7aeea18 470 payload: OCPP16StartTransactionResponse,
ef6fa3fb 471 requestPayload: OCPP16StartTransactionRequest
e7aeea18 472 ): Promise<void> {
649287f8
JB
473 const transactionConnectorId = requestPayload.connectorId;
474 if (
475 transactionConnectorId === 0 ||
476 chargingStation.hasConnector(transactionConnectorId) === false
477 ) {
e7aeea18 478 logger.error(
649287f8 479 `${chargingStation.logPrefix()} Trying to start a transaction on a non existing connector id ${transactionConnectorId.toString()}`
e7aeea18 480 );
c0560973
JB
481 return;
482 }
e7aeea18 483 if (
649287f8
JB
484 chargingStation.getConnectorStatus(transactionConnectorId)?.transactionRemoteStarted ===
485 true &&
3a13fc92
JB
486 chargingStation.getAuthorizeRemoteTxRequests() === true &&
487 chargingStation.getLocalAuthListEnabled() === true &&
f911a4af 488 chargingStation.hasIdTags() &&
649287f8 489 chargingStation.getConnectorStatus(transactionConnectorId)?.idTagLocalAuthorized === false
e7aeea18
JB
490 ) {
491 logger.error(
44eb6026 492 `${chargingStation.logPrefix()} Trying to start a transaction with a not local authorized idTag ${
649287f8
JB
493 chargingStation.getConnectorStatus(transactionConnectorId)?.localAuthorizeIdTag
494 } on connector id ${transactionConnectorId.toString()}`
e7aeea18 495 );
649287f8 496 await this.resetConnectorOnStartTransactionError(chargingStation, transactionConnectorId);
a2653482
JB
497 return;
498 }
e7aeea18 499 if (
649287f8
JB
500 chargingStation.getConnectorStatus(transactionConnectorId)?.transactionRemoteStarted ===
501 true &&
3a13fc92
JB
502 chargingStation.getAuthorizeRemoteTxRequests() === true &&
503 chargingStation.getMustAuthorizeAtRemoteStart() === true &&
649287f8
JB
504 chargingStation.getConnectorStatus(transactionConnectorId)?.idTagLocalAuthorized === false &&
505 chargingStation.getConnectorStatus(transactionConnectorId)?.idTagAuthorized === false
e7aeea18
JB
506 ) {
507 logger.error(
44eb6026 508 `${chargingStation.logPrefix()} Trying to start a transaction with a not authorized idTag ${
649287f8
JB
509 chargingStation.getConnectorStatus(transactionConnectorId)?.authorizeIdTag
510 } on connector id ${transactionConnectorId.toString()}`
e7aeea18 511 );
649287f8 512 await this.resetConnectorOnStartTransactionError(chargingStation, transactionConnectorId);
a2653482
JB
513 return;
514 }
e7aeea18 515 if (
649287f8
JB
516 chargingStation.getConnectorStatus(transactionConnectorId)?.idTagAuthorized &&
517 chargingStation.getConnectorStatus(transactionConnectorId)?.authorizeIdTag !==
518 requestPayload.idTag
e7aeea18
JB
519 ) {
520 logger.error(
44eb6026
JB
521 `${chargingStation.logPrefix()} Trying to start a transaction with an idTag ${
522 requestPayload.idTag
523 } different from the authorize request one ${
649287f8
JB
524 chargingStation.getConnectorStatus(transactionConnectorId)?.authorizeIdTag
525 } on connector id ${transactionConnectorId.toString()}`
e7aeea18 526 );
649287f8 527 await this.resetConnectorOnStartTransactionError(chargingStation, transactionConnectorId);
a2653482
JB
528 return;
529 }
e7aeea18 530 if (
649287f8
JB
531 chargingStation.getConnectorStatus(transactionConnectorId)?.idTagLocalAuthorized &&
532 chargingStation.getConnectorStatus(transactionConnectorId)?.localAuthorizeIdTag !==
533 requestPayload.idTag
e7aeea18
JB
534 ) {
535 logger.error(
44eb6026
JB
536 `${chargingStation.logPrefix()} Trying to start a transaction with an idTag ${
537 requestPayload.idTag
538 } different from the local authorized one ${
649287f8
JB
539 chargingStation.getConnectorStatus(transactionConnectorId)?.localAuthorizeIdTag
540 } on connector id ${transactionConnectorId.toString()}`
e7aeea18 541 );
649287f8 542 await this.resetConnectorOnStartTransactionError(chargingStation, transactionConnectorId);
163547b1
JB
543 return;
544 }
649287f8
JB
545 if (chargingStation.getConnectorStatus(transactionConnectorId)?.transactionStarted === true) {
546 logger.error(
547 `${chargingStation.logPrefix()} Trying to start a transaction on an already used connector id ${transactionConnectorId.toString()}:`,
548 chargingStation.getConnectorStatus(transactionConnectorId)
e7aeea18 549 );
c0560973
JB
550 return;
551 }
649287f8
JB
552 if (chargingStation.hasEvses) {
553 for (const [evseId, evseStatus] of chargingStation.evses) {
554 if (evseStatus.connectors.size > 1) {
555 for (const [connectorId, connectorStatus] of evseStatus.connectors) {
556 if (
557 transactionConnectorId !== connectorId &&
558 connectorStatus?.transactionStarted === true
559 ) {
560 logger.error(
561 `${chargingStation.logPrefix()} Trying to start a transaction on an already used evse id ${evseId.toString()}:`,
562 evseStatus
563 );
564 await this.resetConnectorOnStartTransactionError(
565 chargingStation,
566 transactionConnectorId
567 );
568 return;
569 }
570 }
571 }
572 }
573 }
e7aeea18 574 if (
649287f8 575 chargingStation.getConnectorStatus(transactionConnectorId)?.status !==
721646e9 576 OCPP16ChargePointStatus.Available &&
649287f8
JB
577 chargingStation.getConnectorStatus(transactionConnectorId)?.status !==
578 OCPP16ChargePointStatus.Preparing
e7aeea18
JB
579 ) {
580 logger.error(
649287f8
JB
581 `${chargingStation.logPrefix()} Trying to start a transaction on connector id ${transactionConnectorId.toString()} with status ${
582 chargingStation.getConnectorStatus(transactionConnectorId)?.status
e7aeea18
JB
583 }`
584 );
290d006c
JB
585 return;
586 }
54ebb82c
JB
587 if (!Number.isInteger(payload.transactionId)) {
588 logger.warn(
649287f8 589 `${chargingStation.logPrefix()} Trying to start a transaction on connector id ${transactionConnectorId.toString()} with a non integer transaction id ${
54ebb82c
JB
590 payload.transactionId
591 }, converting to integer`
592 );
593 payload.transactionId = Utils.convertToInt(payload.transactionId);
594 }
c0560973 595
f0c6ed89 596 if (payload.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) {
649287f8
JB
597 chargingStation.getConnectorStatus(transactionConnectorId).transactionStarted = true;
598 chargingStation.getConnectorStatus(transactionConnectorId).transactionId =
599 payload.transactionId;
600 chargingStation.getConnectorStatus(transactionConnectorId).transactionIdTag =
601 requestPayload.idTag;
08f130a0 602 chargingStation.getConnectorStatus(
649287f8 603 transactionConnectorId
e7aeea18 604 ).transactionEnergyActiveImportRegisterValue = 0;
649287f8 605 chargingStation.getConnectorStatus(transactionConnectorId).transactionBeginMeterValue =
e7aeea18 606 OCPP16ServiceUtils.buildTransactionBeginMeterValue(
08f130a0 607 chargingStation,
649287f8 608 transactionConnectorId,
e7aeea18
JB
609 requestPayload.meterStart
610 );
08f130a0
JB
611 chargingStation.getBeginEndMeterValues() &&
612 (await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
613 OCPP16MeterValuesRequest,
614 OCPP16MeterValuesResponse
08f130a0 615 >(chargingStation, OCPP16RequestCommand.METER_VALUES, {
649287f8 616 connectorId: transactionConnectorId,
ef6fa3fb 617 transactionId: payload.transactionId,
649287f8
JB
618 meterValue: [
619 chargingStation.getConnectorStatus(transactionConnectorId).transactionBeginMeterValue,
620 ],
ef6fa3fb 621 }));
4ecff7ce
JB
622 await OCPP16ServiceUtils.sendAndSetConnectorStatus(
623 chargingStation,
649287f8 624 transactionConnectorId,
4ecff7ce
JB
625 OCPP16ChargePointStatus.Charging
626 );
e7aeea18 627 logger.info(
54ebb82c 628 `${chargingStation.logPrefix()} Transaction with id ${payload.transactionId.toString()} STARTED on ${
44eb6026 629 chargingStation.stationInfo.chargingStationId
649287f8 630 }#${transactionConnectorId.toString()} for idTag '${requestPayload.idTag}'`
e7aeea18 631 );
08f130a0 632 if (chargingStation.stationInfo.powerSharedByConnectors) {
1fe0632a 633 ++chargingStation.powerDivider;
c0560973 634 }
17ac262c
JB
635 const configuredMeterValueSampleInterval =
636 ChargingStationConfigurationUtils.getConfigurationKey(
637 chargingStation,
638 OCPP16StandardParametersKey.MeterValueSampleInterval
639 );
08f130a0 640 chargingStation.startMeterValues(
649287f8 641 transactionConnectorId,
e7aeea18
JB
642 configuredMeterValueSampleInterval
643 ? Utils.convertToInt(configuredMeterValueSampleInterval.value) * 1000
d3195f0a 644 : Constants.DEFAULT_METER_VALUES_INTERVAL
e7aeea18 645 );
c0560973 646 } else {
e7aeea18 647 logger.warn(
54ebb82c 648 `${chargingStation.logPrefix()} Starting transaction with id ${payload.transactionId.toString()} REJECTED with status '${
44eb6026
JB
649 payload.idTagInfo?.status
650 }', idTag '${requestPayload.idTag}'`
e7aeea18 651 );
649287f8 652 await this.resetConnectorOnStartTransactionError(chargingStation, transactionConnectorId);
a2653482
JB
653 }
654 }
655
08f130a0
JB
656 private async resetConnectorOnStartTransactionError(
657 chargingStation: ChargingStation,
658 connectorId: number
659 ): Promise<void> {
04b1261c
JB
660 ChargingStationUtils.resetConnectorStatus(chargingStation.getConnectorStatus(connectorId));
661 chargingStation.stopMeterValues(connectorId);
c8faabc8 662 parentPort?.postMessage(buildUpdatedMessage(chargingStation));
e7aeea18 663 if (
721646e9 664 chargingStation.getConnectorStatus(connectorId)?.status !== OCPP16ChargePointStatus.Available
e7aeea18 665 ) {
4ecff7ce
JB
666 await OCPP16ServiceUtils.sendAndSetConnectorStatus(
667 chargingStation,
ef6fa3fb 668 connectorId,
4ecff7ce
JB
669 OCPP16ChargePointStatus.Available
670 );
c0560973
JB
671 }
672 }
673
e7aeea18 674 private async handleResponseStopTransaction(
08f130a0 675 chargingStation: ChargingStation,
e7aeea18 676 payload: OCPP16StopTransactionResponse,
ef6fa3fb 677 requestPayload: OCPP16StopTransactionRequest
e7aeea18 678 ): Promise<void> {
08f130a0 679 const transactionConnectorId = chargingStation.getConnectorIdByTransactionId(
f479a792
JB
680 requestPayload.transactionId
681 );
d812bdcb 682 if (Utils.isNullOrUndefined(transactionConnectorId)) {
e7aeea18 683 logger.error(
54ebb82c 684 `${chargingStation.logPrefix()} Trying to stop a non existing transaction with id ${requestPayload.transactionId.toString()}`
e7aeea18 685 );
c0560973
JB
686 return;
687 }
2cace1a5
JB
688 chargingStation.getBeginEndMeterValues() === true &&
689 chargingStation.getOcppStrictCompliance() === false &&
690 chargingStation.getOutOfOrderEndMeterValues() === true &&
691 (await chargingStation.ocppRequestService.requestHandler<
692 OCPP16MeterValuesRequest,
693 OCPP16MeterValuesResponse
694 >(chargingStation, OCPP16RequestCommand.METER_VALUES, {
695 connectorId: transactionConnectorId,
696 transactionId: requestPayload.transactionId,
697 meterValue: [
698 OCPP16ServiceUtils.buildTransactionEndMeterValue(
699 chargingStation,
700 transactionConnectorId,
701 requestPayload.meterStop
702 ),
703 ],
704 }));
705 if (
706 chargingStation.isChargingStationAvailable() === false ||
707 chargingStation.isConnectorAvailable(transactionConnectorId) === false
708 ) {
4ecff7ce
JB
709 await OCPP16ServiceUtils.sendAndSetConnectorStatus(
710 chargingStation,
711 transactionConnectorId,
712 OCPP16ChargePointStatus.Unavailable
713 );
c0560973 714 } else {
4ecff7ce
JB
715 await OCPP16ServiceUtils.sendAndSetConnectorStatus(
716 chargingStation,
717 transactionConnectorId,
718 OCPP16ChargePointStatus.Available
719 );
2cace1a5
JB
720 }
721 if (chargingStation.stationInfo.powerSharedByConnectors) {
722 chargingStation.powerDivider--;
723 }
04b1261c
JB
724 ChargingStationUtils.resetConnectorStatus(
725 chargingStation.getConnectorStatus(transactionConnectorId)
726 );
727 chargingStation.stopMeterValues(transactionConnectorId);
c8faabc8 728 parentPort?.postMessage(buildUpdatedMessage(chargingStation));
54ebb82c 729 const logMsg = `${chargingStation.logPrefix()} Transaction with id ${requestPayload.transactionId.toString()} STOPPED on ${
8ca6874c 730 chargingStation.stationInfo.chargingStationId
d812bdcb 731 }#${transactionConnectorId?.toString()} with status '${
8ca6874c
JB
732 payload.idTagInfo?.status ?? 'undefined'
733 }'`;
2cace1a5
JB
734 if (
735 Utils.isNullOrUndefined(payload.idTagInfo) ||
736 payload.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED
737 ) {
738 logger.info(logMsg);
739 } else {
740 logger.warn(logMsg);
c0560973
JB
741 }
742 }
c0560973 743}