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