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