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