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