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