Fixes to OCPP command payload validation:
[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 fs from 'fs';
4 import path from 'path';
5 import { fileURLToPath } from 'url';
6
7 import type { JSONSchemaType } from 'ajv';
8
9 import OCPPError from '../../../exception/OCPPError';
10 import type { JsonObject, JsonType } from '../../../types/JsonType';
11 import { OCPP16ChargePointErrorCode } from '../../../types/ocpp/1.6/ChargePointErrorCode';
12 import { OCPP16ChargePointStatus } from '../../../types/ocpp/1.6/ChargePointStatus';
13 import { OCPP16StandardParametersKey } from '../../../types/ocpp/1.6/Configuration';
14 import type {
15 OCPP16MeterValuesRequest,
16 OCPP16MeterValuesResponse,
17 } from '../../../types/ocpp/1.6/MeterValues';
18 import {
19 type OCPP16BootNotificationRequest,
20 OCPP16RequestCommand,
21 type OCPP16StatusNotificationRequest,
22 } from '../../../types/ocpp/1.6/Requests';
23 import type {
24 DiagnosticsStatusNotificationResponse,
25 OCPP16BootNotificationResponse,
26 OCPP16DataTransferResponse,
27 OCPP16HeartbeatResponse,
28 OCPP16StatusNotificationResponse,
29 } from '../../../types/ocpp/1.6/Responses';
30 import {
31 OCPP16AuthorizationStatus,
32 type OCPP16AuthorizeRequest,
33 type OCPP16AuthorizeResponse,
34 type OCPP16StartTransactionRequest,
35 type OCPP16StartTransactionResponse,
36 type OCPP16StopTransactionRequest,
37 type OCPP16StopTransactionResponse,
38 } from '../../../types/ocpp/1.6/Transaction';
39 import { ErrorType } from '../../../types/ocpp/ErrorType';
40 import { OCPPVersion } from '../../../types/ocpp/OCPPVersion';
41 import { RegistrationStatusEnumType, type ResponseHandler } from '../../../types/ocpp/Responses';
42 import Constants from '../../../utils/Constants';
43 import logger from '../../../utils/Logger';
44 import Utils from '../../../utils/Utils';
45 import type ChargingStation from '../../ChargingStation';
46 import { ChargingStationConfigurationUtils } from '../../ChargingStationConfigurationUtils';
47 import OCPPResponseService from '../OCPPResponseService';
48 import { OCPP16ServiceUtils } from './OCPP16ServiceUtils';
49
50 const moduleName = 'OCPP16ResponseService';
51
52 export default class OCPP16ResponseService extends OCPPResponseService {
53 private responseHandlers: Map<OCPP16RequestCommand, ResponseHandler>;
54 private jsonSchemas: Map<OCPP16RequestCommand, JSONSchemaType<JsonObject>>;
55
56 public constructor() {
57 if (new.target?.name === moduleName) {
58 throw new TypeError(`Cannot construct ${new.target?.name} instances directly`);
59 }
60 super(OCPPVersion.VERSION_16);
61 this.responseHandlers = new Map<OCPP16RequestCommand, ResponseHandler>([
62 [OCPP16RequestCommand.BOOT_NOTIFICATION, this.handleResponseBootNotification.bind(this)],
63 [OCPP16RequestCommand.HEARTBEAT, this.emptyResponseHandler.bind(this)],
64 [OCPP16RequestCommand.AUTHORIZE, this.handleResponseAuthorize.bind(this)],
65 [OCPP16RequestCommand.START_TRANSACTION, this.handleResponseStartTransaction.bind(this)],
66 [OCPP16RequestCommand.STOP_TRANSACTION, this.handleResponseStopTransaction.bind(this)],
67 [OCPP16RequestCommand.STATUS_NOTIFICATION, this.emptyResponseHandler.bind(this)],
68 [OCPP16RequestCommand.METER_VALUES, this.emptyResponseHandler.bind(this)],
69 [OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, this.emptyResponseHandler.bind(this)],
70 [OCPP16RequestCommand.DATA_TRANSFER, this.emptyResponseHandler.bind(this)],
71 ]);
72 this.jsonSchemas = new Map<OCPP16RequestCommand, JSONSchemaType<JsonObject>>([
73 [
74 OCPP16RequestCommand.BOOT_NOTIFICATION,
75 JSON.parse(
76 fs.readFileSync(
77 path.resolve(
78 path.dirname(fileURLToPath(import.meta.url)),
79 '../../../assets/json-schemas/ocpp/1.6/BootNotificationResponse.json'
80 ),
81 'utf8'
82 )
83 ) as JSONSchemaType<OCPP16BootNotificationResponse>,
84 ],
85 [
86 OCPP16RequestCommand.HEARTBEAT,
87 JSON.parse(
88 fs.readFileSync(
89 path.resolve(
90 path.dirname(fileURLToPath(import.meta.url)),
91 '../../../assets/json-schemas/ocpp/1.6/HeartbeatResponse.json'
92 ),
93 'utf8'
94 )
95 ) as JSONSchemaType<OCPP16HeartbeatResponse>,
96 ],
97 [
98 OCPP16RequestCommand.AUTHORIZE,
99 JSON.parse(
100 fs.readFileSync(
101 path.resolve(
102 path.dirname(fileURLToPath(import.meta.url)),
103 '../../../assets/json-schemas/ocpp/1.6/AuthorizeResponse.json'
104 ),
105 'utf8'
106 )
107 ) as JSONSchemaType<OCPP16AuthorizeResponse>,
108 ],
109 [
110 OCPP16RequestCommand.START_TRANSACTION,
111 JSON.parse(
112 fs.readFileSync(
113 path.resolve(
114 path.dirname(fileURLToPath(import.meta.url)),
115 '../../../assets/json-schemas/ocpp/1.6/StartTransactionResponse.json'
116 ),
117 'utf8'
118 )
119 ) as JSONSchemaType<OCPP16StartTransactionResponse>,
120 ],
121 [
122 OCPP16RequestCommand.STOP_TRANSACTION,
123 JSON.parse(
124 fs.readFileSync(
125 path.resolve(
126 path.dirname(fileURLToPath(import.meta.url)),
127 '../../../assets/json-schemas/ocpp/1.6/StopTransactionResponse.json'
128 ),
129 'utf8'
130 )
131 ) as JSONSchemaType<OCPP16StopTransactionResponse>,
132 ],
133 [
134 OCPP16RequestCommand.STATUS_NOTIFICATION,
135 JSON.parse(
136 fs.readFileSync(
137 path.resolve(
138 path.dirname(fileURLToPath(import.meta.url)),
139 '../../../assets/json-schemas/ocpp/1.6/StatusNotificationResponse.json'
140 ),
141 'utf8'
142 )
143 ) as JSONSchemaType<OCPP16StatusNotificationResponse>,
144 ],
145 [
146 OCPP16RequestCommand.METER_VALUES,
147 JSON.parse(
148 fs.readFileSync(
149 path.resolve(
150 path.dirname(fileURLToPath(import.meta.url)),
151 '../../../assets/json-schemas/ocpp/1.6/MeterValuesResponse.json'
152 ),
153 'utf8'
154 )
155 ) as JSONSchemaType<OCPP16MeterValuesResponse>,
156 ],
157 [
158 OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION,
159 JSON.parse(
160 fs.readFileSync(
161 path.resolve(
162 path.dirname(fileURLToPath(import.meta.url)),
163 '../../../assets/json-schemas/ocpp/1.6/DiagnosticsStatusNotificationResponse.json'
164 ),
165 'utf8'
166 )
167 ) as JSONSchemaType<DiagnosticsStatusNotificationResponse>,
168 ],
169 [
170 OCPP16RequestCommand.DATA_TRANSFER,
171 JSON.parse(
172 fs.readFileSync(
173 path.resolve(
174 path.dirname(fileURLToPath(import.meta.url)),
175 '../../../assets/json-schemas/ocpp/1.6/DataTransferResponse.json'
176 ),
177 'utf8'
178 )
179 ) as JSONSchemaType<OCPP16DataTransferResponse>,
180 ],
181 ]);
182 this.validatePayload.bind(this);
183 }
184
185 public async responseHandler(
186 chargingStation: ChargingStation,
187 commandName: OCPP16RequestCommand,
188 payload: JsonType,
189 requestPayload: JsonType
190 ): Promise<void> {
191 if (
192 chargingStation.isRegistered() === true ||
193 commandName === OCPP16RequestCommand.BOOT_NOTIFICATION
194 ) {
195 if (
196 this.responseHandlers.has(commandName) === true &&
197 OCPP16ServiceUtils.isRequestCommandSupported(chargingStation, commandName) === true
198 ) {
199 try {
200 this.validatePayload(chargingStation, commandName, payload);
201 await this.responseHandlers.get(commandName)(chargingStation, payload, requestPayload);
202 } catch (error) {
203 logger.error(
204 `${chargingStation.logPrefix()} ${moduleName}.responseHandler: Handle response error:`,
205 error
206 );
207 throw error;
208 }
209 } else {
210 // Throw exception
211 throw new OCPPError(
212 ErrorType.NOT_IMPLEMENTED,
213 `${commandName} is not implemented to handle response PDU ${JSON.stringify(
214 payload,
215 null,
216 2
217 )}`,
218 commandName,
219 payload
220 );
221 }
222 } else {
223 throw new OCPPError(
224 ErrorType.SECURITY_ERROR,
225 `${commandName} cannot be issued to handle response PDU ${JSON.stringify(
226 payload,
227 null,
228 2
229 )} while the charging station is not registered on the central server.`,
230 commandName,
231 payload
232 );
233 }
234 }
235
236 private validatePayload(
237 chargingStation: ChargingStation,
238 commandName: OCPP16RequestCommand,
239 payload: JsonType
240 ): boolean {
241 if (this.jsonSchemas.has(commandName) === true) {
242 return this.validateResponsePayload(
243 chargingStation,
244 commandName,
245 this.jsonSchemas.get(commandName),
246 payload
247 );
248 }
249 logger.warn(
250 `${chargingStation.logPrefix()} ${moduleName}.validatePayload: No JSON schema found for command ${commandName} PDU validation`
251 );
252 return false;
253 }
254
255 private handleResponseBootNotification(
256 chargingStation: ChargingStation,
257 payload: OCPP16BootNotificationResponse
258 ): void {
259 if (payload.status === RegistrationStatusEnumType.ACCEPTED) {
260 ChargingStationConfigurationUtils.addConfigurationKey(
261 chargingStation,
262 OCPP16StandardParametersKey.HeartbeatInterval,
263 payload.interval.toString(),
264 {},
265 { overwrite: true, save: true }
266 );
267 ChargingStationConfigurationUtils.addConfigurationKey(
268 chargingStation,
269 OCPP16StandardParametersKey.HeartBeatInterval,
270 payload.interval.toString(),
271 { visible: false },
272 { overwrite: true, save: true }
273 );
274 chargingStation.heartbeatSetInterval
275 ? chargingStation.restartHeartbeat()
276 : chargingStation.startHeartbeat();
277 }
278 if (Object.values(RegistrationStatusEnumType).includes(payload.status)) {
279 const logMsg = `${chargingStation.logPrefix()} Charging station in '${
280 payload.status
281 }' state on the central server`;
282 payload.status === RegistrationStatusEnumType.REJECTED
283 ? logger.warn(logMsg)
284 : logger.info(logMsg);
285 } else {
286 logger.error(
287 chargingStation.logPrefix() +
288 ' Charging station boot notification response received: %j with undefined registration status',
289 payload
290 );
291 }
292 }
293
294 private handleResponseAuthorize(
295 chargingStation: ChargingStation,
296 payload: OCPP16AuthorizeResponse,
297 requestPayload: OCPP16AuthorizeRequest
298 ): void {
299 let authorizeConnectorId: number;
300 for (const connectorId of chargingStation.connectors.keys()) {
301 if (
302 connectorId > 0 &&
303 chargingStation.getConnectorStatus(connectorId)?.authorizeIdTag === requestPayload.idTag
304 ) {
305 authorizeConnectorId = connectorId;
306 break;
307 }
308 }
309 const isAuthorizeConnectorIdDefined = authorizeConnectorId !== undefined;
310 if (payload.idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED) {
311 isAuthorizeConnectorIdDefined &&
312 (chargingStation.getConnectorStatus(authorizeConnectorId).idTagAuthorized = true);
313 logger.debug(
314 `${chargingStation.logPrefix()} IdTag '${requestPayload.idTag}' accepted${
315 isAuthorizeConnectorIdDefined ? ` on connector ${authorizeConnectorId}` : ''
316 }`
317 );
318 } else {
319 if (isAuthorizeConnectorIdDefined) {
320 chargingStation.getConnectorStatus(authorizeConnectorId).idTagAuthorized = false;
321 delete chargingStation.getConnectorStatus(authorizeConnectorId).authorizeIdTag;
322 }
323 logger.debug(
324 `${chargingStation.logPrefix()} IdTag '${requestPayload.idTag}' rejected with status '${
325 payload.idTagInfo.status
326 }'${isAuthorizeConnectorIdDefined ? ` on connector ${authorizeConnectorId}` : ''}`
327 );
328 }
329 }
330
331 private async handleResponseStartTransaction(
332 chargingStation: ChargingStation,
333 payload: OCPP16StartTransactionResponse,
334 requestPayload: OCPP16StartTransactionRequest
335 ): Promise<void> {
336 const connectorId = requestPayload.connectorId;
337
338 let transactionConnectorId: number;
339 for (const id of chargingStation.connectors.keys()) {
340 if (id > 0 && id === connectorId) {
341 transactionConnectorId = id;
342 break;
343 }
344 }
345 if (!transactionConnectorId) {
346 logger.error(
347 chargingStation.logPrefix() +
348 ' Trying to start a transaction on a non existing connector Id ' +
349 connectorId.toString()
350 );
351 return;
352 }
353 if (
354 chargingStation.getConnectorStatus(connectorId).transactionRemoteStarted === true &&
355 chargingStation.getAuthorizeRemoteTxRequests() === true &&
356 chargingStation.getLocalAuthListEnabled() === true &&
357 chargingStation.hasAuthorizedTags() &&
358 chargingStation.getConnectorStatus(connectorId).idTagLocalAuthorized === false
359 ) {
360 logger.error(
361 chargingStation.logPrefix() +
362 ' Trying to start a transaction with a not local authorized idTag ' +
363 chargingStation.getConnectorStatus(connectorId).localAuthorizeIdTag +
364 ' on connector Id ' +
365 connectorId.toString()
366 );
367 await this.resetConnectorOnStartTransactionError(chargingStation, connectorId);
368 return;
369 }
370 if (
371 chargingStation.getConnectorStatus(connectorId).transactionRemoteStarted === true &&
372 chargingStation.getAuthorizeRemoteTxRequests() === true &&
373 chargingStation.getMustAuthorizeAtRemoteStart() === true &&
374 chargingStation.getConnectorStatus(connectorId).idTagLocalAuthorized === false &&
375 chargingStation.getConnectorStatus(connectorId).idTagAuthorized === false
376 ) {
377 logger.error(
378 chargingStation.logPrefix() +
379 ' Trying to start a transaction with a not authorized idTag ' +
380 chargingStation.getConnectorStatus(connectorId).authorizeIdTag +
381 ' on connector Id ' +
382 connectorId.toString()
383 );
384 await this.resetConnectorOnStartTransactionError(chargingStation, connectorId);
385 return;
386 }
387 if (
388 chargingStation.getConnectorStatus(connectorId).idTagAuthorized &&
389 chargingStation.getConnectorStatus(connectorId).authorizeIdTag !== requestPayload.idTag
390 ) {
391 logger.error(
392 chargingStation.logPrefix() +
393 ' Trying to start a transaction with an idTag ' +
394 requestPayload.idTag +
395 ' different from the authorize request one ' +
396 chargingStation.getConnectorStatus(connectorId).authorizeIdTag +
397 ' on connector Id ' +
398 connectorId.toString()
399 );
400 await this.resetConnectorOnStartTransactionError(chargingStation, connectorId);
401 return;
402 }
403 if (
404 chargingStation.getConnectorStatus(connectorId).idTagLocalAuthorized &&
405 chargingStation.getConnectorStatus(connectorId).localAuthorizeIdTag !== requestPayload.idTag
406 ) {
407 logger.error(
408 chargingStation.logPrefix() +
409 ' Trying to start a transaction with an idTag ' +
410 requestPayload.idTag +
411 ' different from the local authorized one ' +
412 chargingStation.getConnectorStatus(connectorId).localAuthorizeIdTag +
413 ' on connector Id ' +
414 connectorId.toString()
415 );
416 await this.resetConnectorOnStartTransactionError(chargingStation, connectorId);
417 return;
418 }
419 if (chargingStation.getConnectorStatus(connectorId)?.transactionStarted === true) {
420 logger.debug(
421 chargingStation.logPrefix() +
422 ' Trying to start a transaction on an already used connector ' +
423 connectorId.toString() +
424 ': %j',
425 chargingStation.getConnectorStatus(connectorId)
426 );
427 return;
428 }
429 if (
430 chargingStation.getConnectorStatus(connectorId)?.status !==
431 OCPP16ChargePointStatus.AVAILABLE &&
432 chargingStation.getConnectorStatus(connectorId)?.status !== OCPP16ChargePointStatus.PREPARING
433 ) {
434 logger.error(
435 `${chargingStation.logPrefix()} Trying to start a transaction on connector ${connectorId.toString()} with status ${
436 chargingStation.getConnectorStatus(connectorId)?.status
437 }`
438 );
439 return;
440 }
441 // if (!Number.isInteger(payload.transactionId)) {
442 // logger.warn(
443 // `${chargingStation.logPrefix()} Trying to start a transaction on connector ${connectorId.toString()} with a non integer transaction Id ${
444 // payload.transactionId
445 // }, converting to integer`
446 // );
447 // payload.transactionId = Utils.convertToInt(payload.transactionId);
448 // }
449
450 if (payload.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) {
451 chargingStation.getConnectorStatus(connectorId).transactionStarted = true;
452 chargingStation.getConnectorStatus(connectorId).transactionId = payload.transactionId;
453 chargingStation.getConnectorStatus(connectorId).transactionIdTag = requestPayload.idTag;
454 chargingStation.getConnectorStatus(
455 connectorId
456 ).transactionEnergyActiveImportRegisterValue = 0;
457 chargingStation.getConnectorStatus(connectorId).transactionBeginMeterValue =
458 OCPP16ServiceUtils.buildTransactionBeginMeterValue(
459 chargingStation,
460 connectorId,
461 requestPayload.meterStart
462 );
463 chargingStation.getBeginEndMeterValues() &&
464 (await chargingStation.ocppRequestService.requestHandler<
465 OCPP16MeterValuesRequest,
466 OCPP16MeterValuesResponse
467 >(chargingStation, OCPP16RequestCommand.METER_VALUES, {
468 connectorId,
469 transactionId: payload.transactionId,
470 meterValue: [chargingStation.getConnectorStatus(connectorId).transactionBeginMeterValue],
471 }));
472 await chargingStation.ocppRequestService.requestHandler<
473 OCPP16StatusNotificationRequest,
474 OCPP16StatusNotificationResponse
475 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
476 connectorId,
477 status: OCPP16ChargePointStatus.CHARGING,
478 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
479 });
480 chargingStation.getConnectorStatus(connectorId).status = OCPP16ChargePointStatus.CHARGING;
481 logger.info(
482 chargingStation.logPrefix() +
483 ' Transaction ' +
484 payload.transactionId.toString() +
485 ' STARTED on ' +
486 chargingStation.stationInfo.chargingStationId +
487 '#' +
488 connectorId.toString() +
489 " for idTag '" +
490 requestPayload.idTag +
491 "'"
492 );
493 if (chargingStation.stationInfo.powerSharedByConnectors) {
494 chargingStation.powerDivider++;
495 }
496 const configuredMeterValueSampleInterval =
497 ChargingStationConfigurationUtils.getConfigurationKey(
498 chargingStation,
499 OCPP16StandardParametersKey.MeterValueSampleInterval
500 );
501 chargingStation.startMeterValues(
502 connectorId,
503 configuredMeterValueSampleInterval
504 ? Utils.convertToInt(configuredMeterValueSampleInterval.value) * 1000
505 : Constants.DEFAULT_METER_VALUES_INTERVAL
506 );
507 } else {
508 logger.warn(
509 chargingStation.logPrefix() +
510 ' Starting transaction id ' +
511 payload.transactionId.toString() +
512 " REJECTED with status '" +
513 payload?.idTagInfo?.status +
514 "', idTag '" +
515 requestPayload.idTag +
516 "'"
517 );
518 await this.resetConnectorOnStartTransactionError(chargingStation, connectorId);
519 }
520 }
521
522 private async resetConnectorOnStartTransactionError(
523 chargingStation: ChargingStation,
524 connectorId: number
525 ): Promise<void> {
526 chargingStation.resetConnectorStatus(connectorId);
527 if (
528 chargingStation.getConnectorStatus(connectorId).status !== OCPP16ChargePointStatus.AVAILABLE
529 ) {
530 await chargingStation.ocppRequestService.requestHandler<
531 OCPP16StatusNotificationRequest,
532 OCPP16StatusNotificationResponse
533 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
534 connectorId,
535 status: OCPP16ChargePointStatus.AVAILABLE,
536 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
537 });
538 chargingStation.getConnectorStatus(connectorId).status = OCPP16ChargePointStatus.AVAILABLE;
539 }
540 }
541
542 private async handleResponseStopTransaction(
543 chargingStation: ChargingStation,
544 payload: OCPP16StopTransactionResponse,
545 requestPayload: OCPP16StopTransactionRequest
546 ): Promise<void> {
547 const transactionConnectorId = chargingStation.getConnectorIdByTransactionId(
548 requestPayload.transactionId
549 );
550 if (!transactionConnectorId) {
551 logger.error(
552 chargingStation.logPrefix() +
553 ' Trying to stop a non existing transaction ' +
554 requestPayload.transactionId.toString()
555 );
556 return;
557 }
558 if (payload.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) {
559 chargingStation.getBeginEndMeterValues() === true &&
560 chargingStation.getOcppStrictCompliance() === false &&
561 chargingStation.getOutOfOrderEndMeterValues() === true &&
562 (await chargingStation.ocppRequestService.requestHandler<
563 OCPP16MeterValuesRequest,
564 OCPP16MeterValuesResponse
565 >(chargingStation, OCPP16RequestCommand.METER_VALUES, {
566 connectorId: transactionConnectorId,
567 transactionId: requestPayload.transactionId,
568 meterValue: [
569 OCPP16ServiceUtils.buildTransactionEndMeterValue(
570 chargingStation,
571 transactionConnectorId,
572 requestPayload.meterStop
573 ),
574 ],
575 }));
576 if (
577 chargingStation.isChargingStationAvailable() === false ||
578 chargingStation.isConnectorAvailable(transactionConnectorId) === false
579 ) {
580 await chargingStation.ocppRequestService.requestHandler<
581 OCPP16StatusNotificationRequest,
582 OCPP16StatusNotificationResponse
583 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
584 connectorId: transactionConnectorId,
585 status: OCPP16ChargePointStatus.UNAVAILABLE,
586 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
587 });
588 chargingStation.getConnectorStatus(transactionConnectorId).status =
589 OCPP16ChargePointStatus.UNAVAILABLE;
590 } else {
591 await chargingStation.ocppRequestService.requestHandler<
592 OCPP16BootNotificationRequest,
593 OCPP16BootNotificationResponse
594 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
595 connectorId: transactionConnectorId,
596 status: OCPP16ChargePointStatus.AVAILABLE,
597 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
598 });
599 chargingStation.getConnectorStatus(transactionConnectorId).status =
600 OCPP16ChargePointStatus.AVAILABLE;
601 }
602 if (chargingStation.stationInfo.powerSharedByConnectors) {
603 chargingStation.powerDivider--;
604 }
605 chargingStation.resetConnectorStatus(transactionConnectorId);
606 logger.info(
607 chargingStation.logPrefix() +
608 ' Transaction ' +
609 requestPayload.transactionId.toString() +
610 ' STOPPED on ' +
611 chargingStation.stationInfo.chargingStationId +
612 '#' +
613 transactionConnectorId.toString()
614 );
615 } else {
616 logger.warn(
617 chargingStation.logPrefix() +
618 ' Stopping transaction id ' +
619 requestPayload.transactionId.toString() +
620 " REJECTED with status '" +
621 payload.idTagInfo?.status +
622 "'"
623 );
624 }
625 }
626 }