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