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