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