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