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