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