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