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