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