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