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