feat: add support for evses in all identified code paths
[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 if (chargingStation.hasEvses) {
407 for (const [evseId, evseStatus] of chargingStation.evses) {
408 if (evseId > 0) {
409 for (const [connectorId, connectorStatus] of evseStatus.connectors) {
410 if (connectorStatus?.authorizeIdTag === requestPayload.idTag) {
411 authorizeConnectorId = connectorId;
412 break;
413 }
414 }
415 }
416 }
417 } else {
418 for (const connectorId of chargingStation.connectors.keys()) {
419 if (
420 connectorId > 0 &&
421 chargingStation.getConnectorStatus(connectorId)?.authorizeIdTag === requestPayload.idTag
422 ) {
423 authorizeConnectorId = connectorId;
424 break;
425 }
426 }
427 }
428 const authorizeConnectorIdDefined = !Utils.isNullOrUndefined(authorizeConnectorId);
429 if (payload.idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED) {
430 authorizeConnectorIdDefined &&
431 (chargingStation.getConnectorStatus(authorizeConnectorId).idTagAuthorized = true);
432 logger.debug(
433 `${chargingStation.logPrefix()} idTag '${requestPayload.idTag}' accepted${
434 authorizeConnectorIdDefined ? ` on connector id ${authorizeConnectorId}` : ''
435 }`
436 );
437 } else {
438 if (authorizeConnectorIdDefined) {
439 chargingStation.getConnectorStatus(authorizeConnectorId).idTagAuthorized = false;
440 delete chargingStation.getConnectorStatus(authorizeConnectorId)?.authorizeIdTag;
441 }
442 logger.debug(
443 `${chargingStation.logPrefix()} idTag '${requestPayload.idTag}' rejected with status '${
444 payload.idTagInfo.status
445 }'${authorizeConnectorIdDefined ? ` on connector id ${authorizeConnectorId}` : ''}`
446 );
447 }
448 }
449
450 private async handleResponseStartTransaction(
451 chargingStation: ChargingStation,
452 payload: OCPP16StartTransactionResponse,
453 requestPayload: OCPP16StartTransactionRequest
454 ): Promise<void> {
455 const connectorId = requestPayload.connectorId;
456 if (connectorId === 0 || chargingStation.hasConnector(connectorId) === false) {
457 logger.error(
458 `${chargingStation.logPrefix()} Trying to start a transaction on a non existing connector id ${connectorId.toString()}`
459 );
460 return;
461 }
462 if (
463 chargingStation.getConnectorStatus(connectorId)?.transactionRemoteStarted === true &&
464 chargingStation.getAuthorizeRemoteTxRequests() === true &&
465 chargingStation.getLocalAuthListEnabled() === true &&
466 chargingStation.hasIdTags() &&
467 chargingStation.getConnectorStatus(connectorId)?.idTagLocalAuthorized === false
468 ) {
469 logger.error(
470 `${chargingStation.logPrefix()} Trying to start a transaction with a not local authorized idTag ${
471 chargingStation.getConnectorStatus(connectorId)?.localAuthorizeIdTag
472 } on connector id ${connectorId.toString()}`
473 );
474 await this.resetConnectorOnStartTransactionError(chargingStation, connectorId);
475 return;
476 }
477 if (
478 chargingStation.getConnectorStatus(connectorId)?.transactionRemoteStarted === true &&
479 chargingStation.getAuthorizeRemoteTxRequests() === true &&
480 chargingStation.getMustAuthorizeAtRemoteStart() === true &&
481 chargingStation.getConnectorStatus(connectorId)?.idTagLocalAuthorized === false &&
482 chargingStation.getConnectorStatus(connectorId)?.idTagAuthorized === false
483 ) {
484 logger.error(
485 `${chargingStation.logPrefix()} Trying to start a transaction with a not authorized idTag ${
486 chargingStation.getConnectorStatus(connectorId)?.authorizeIdTag
487 } on connector id ${connectorId.toString()}`
488 );
489 await this.resetConnectorOnStartTransactionError(chargingStation, connectorId);
490 return;
491 }
492 if (
493 chargingStation.getConnectorStatus(connectorId)?.idTagAuthorized &&
494 chargingStation.getConnectorStatus(connectorId)?.authorizeIdTag !== requestPayload.idTag
495 ) {
496 logger.error(
497 `${chargingStation.logPrefix()} Trying to start a transaction with an idTag ${
498 requestPayload.idTag
499 } different from the authorize request one ${
500 chargingStation.getConnectorStatus(connectorId)?.authorizeIdTag
501 } on connector id ${connectorId.toString()}`
502 );
503 await this.resetConnectorOnStartTransactionError(chargingStation, connectorId);
504 return;
505 }
506 if (
507 chargingStation.getConnectorStatus(connectorId)?.idTagLocalAuthorized &&
508 chargingStation.getConnectorStatus(connectorId)?.localAuthorizeIdTag !== requestPayload.idTag
509 ) {
510 logger.error(
511 `${chargingStation.logPrefix()} Trying to start a transaction with an idTag ${
512 requestPayload.idTag
513 } different from the local authorized one ${
514 chargingStation.getConnectorStatus(connectorId)?.localAuthorizeIdTag
515 } on connector id ${connectorId.toString()}`
516 );
517 await this.resetConnectorOnStartTransactionError(chargingStation, connectorId);
518 return;
519 }
520 if (chargingStation.getConnectorStatus(connectorId)?.transactionStarted === true) {
521 logger.debug(
522 `${chargingStation.logPrefix()} Trying to start a transaction on an already used connector id ${connectorId.toString()}: %j`,
523 chargingStation.getConnectorStatus(connectorId)
524 );
525 return;
526 }
527 if (
528 chargingStation.getConnectorStatus(connectorId)?.status !==
529 OCPP16ChargePointStatus.Available &&
530 chargingStation.getConnectorStatus(connectorId)?.status !== OCPP16ChargePointStatus.Preparing
531 ) {
532 logger.error(
533 `${chargingStation.logPrefix()} Trying to start a transaction on connector id ${connectorId.toString()} with status ${
534 chargingStation.getConnectorStatus(connectorId)?.status
535 }`
536 );
537 return;
538 }
539 if (!Number.isInteger(payload.transactionId)) {
540 logger.warn(
541 `${chargingStation.logPrefix()} Trying to start a transaction on connector id ${connectorId.toString()} with a non integer transaction id ${
542 payload.transactionId
543 }, converting to integer`
544 );
545 payload.transactionId = Utils.convertToInt(payload.transactionId);
546 }
547
548 if (payload.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) {
549 chargingStation.getConnectorStatus(connectorId).transactionStarted = true;
550 chargingStation.getConnectorStatus(connectorId).transactionId = payload.transactionId;
551 chargingStation.getConnectorStatus(connectorId).transactionIdTag = requestPayload.idTag;
552 chargingStation.getConnectorStatus(
553 connectorId
554 ).transactionEnergyActiveImportRegisterValue = 0;
555 chargingStation.getConnectorStatus(connectorId).transactionBeginMeterValue =
556 OCPP16ServiceUtils.buildTransactionBeginMeterValue(
557 chargingStation,
558 connectorId,
559 requestPayload.meterStart
560 );
561 chargingStation.getBeginEndMeterValues() &&
562 (await chargingStation.ocppRequestService.requestHandler<
563 OCPP16MeterValuesRequest,
564 OCPP16MeterValuesResponse
565 >(chargingStation, OCPP16RequestCommand.METER_VALUES, {
566 connectorId,
567 transactionId: payload.transactionId,
568 meterValue: [chargingStation.getConnectorStatus(connectorId).transactionBeginMeterValue],
569 }));
570 await OCPP16ServiceUtils.sendAndSetConnectorStatus(
571 chargingStation,
572 connectorId,
573 OCPP16ChargePointStatus.Charging
574 );
575 logger.info(
576 `${chargingStation.logPrefix()} Transaction with id ${payload.transactionId.toString()} STARTED on ${
577 chargingStation.stationInfo.chargingStationId
578 }#${connectorId.toString()} for idTag '${requestPayload.idTag}'`
579 );
580 if (chargingStation.stationInfo.powerSharedByConnectors) {
581 chargingStation.powerDivider++;
582 }
583 const configuredMeterValueSampleInterval =
584 ChargingStationConfigurationUtils.getConfigurationKey(
585 chargingStation,
586 OCPP16StandardParametersKey.MeterValueSampleInterval
587 );
588 chargingStation.startMeterValues(
589 connectorId,
590 configuredMeterValueSampleInterval
591 ? Utils.convertToInt(configuredMeterValueSampleInterval.value) * 1000
592 : Constants.DEFAULT_METER_VALUES_INTERVAL
593 );
594 } else {
595 logger.warn(
596 `${chargingStation.logPrefix()} Starting transaction with id ${payload.transactionId.toString()} REJECTED with status '${
597 payload.idTagInfo?.status
598 }', idTag '${requestPayload.idTag}'`
599 );
600 await this.resetConnectorOnStartTransactionError(chargingStation, connectorId);
601 }
602 }
603
604 private async resetConnectorOnStartTransactionError(
605 chargingStation: ChargingStation,
606 connectorId: number
607 ): Promise<void> {
608 ChargingStationUtils.resetConnectorStatus(chargingStation.getConnectorStatus(connectorId));
609 chargingStation.stopMeterValues(connectorId);
610 parentPort?.postMessage(MessageChannelUtils.buildUpdatedMessage(chargingStation));
611 if (
612 chargingStation.getConnectorStatus(connectorId)?.status !== OCPP16ChargePointStatus.Available
613 ) {
614 await OCPP16ServiceUtils.sendAndSetConnectorStatus(
615 chargingStation,
616 connectorId,
617 OCPP16ChargePointStatus.Available
618 );
619 }
620 }
621
622 private async handleResponseStopTransaction(
623 chargingStation: ChargingStation,
624 payload: OCPP16StopTransactionResponse,
625 requestPayload: OCPP16StopTransactionRequest
626 ): Promise<void> {
627 const transactionConnectorId = chargingStation.getConnectorIdByTransactionId(
628 requestPayload.transactionId
629 );
630 if (Utils.isNullOrUndefined(transactionConnectorId)) {
631 logger.error(
632 `${chargingStation.logPrefix()} Trying to stop a non existing transaction with id ${requestPayload.transactionId.toString()}`
633 );
634 return;
635 }
636 chargingStation.getBeginEndMeterValues() === true &&
637 chargingStation.getOcppStrictCompliance() === false &&
638 chargingStation.getOutOfOrderEndMeterValues() === true &&
639 (await chargingStation.ocppRequestService.requestHandler<
640 OCPP16MeterValuesRequest,
641 OCPP16MeterValuesResponse
642 >(chargingStation, OCPP16RequestCommand.METER_VALUES, {
643 connectorId: transactionConnectorId,
644 transactionId: requestPayload.transactionId,
645 meterValue: [
646 OCPP16ServiceUtils.buildTransactionEndMeterValue(
647 chargingStation,
648 transactionConnectorId,
649 requestPayload.meterStop
650 ),
651 ],
652 }));
653 if (
654 chargingStation.isChargingStationAvailable() === false ||
655 chargingStation.isConnectorAvailable(transactionConnectorId) === false
656 ) {
657 await OCPP16ServiceUtils.sendAndSetConnectorStatus(
658 chargingStation,
659 transactionConnectorId,
660 OCPP16ChargePointStatus.Unavailable
661 );
662 } else {
663 await OCPP16ServiceUtils.sendAndSetConnectorStatus(
664 chargingStation,
665 transactionConnectorId,
666 OCPP16ChargePointStatus.Available
667 );
668 }
669 if (chargingStation.stationInfo.powerSharedByConnectors) {
670 chargingStation.powerDivider--;
671 }
672 ChargingStationUtils.resetConnectorStatus(
673 chargingStation.getConnectorStatus(transactionConnectorId)
674 );
675 chargingStation.stopMeterValues(transactionConnectorId);
676 parentPort?.postMessage(MessageChannelUtils.buildUpdatedMessage(chargingStation));
677 const logMsg = `${chargingStation.logPrefix()} Transaction with id ${requestPayload.transactionId.toString()} STOPPED on ${
678 chargingStation.stationInfo.chargingStationId
679 }#${transactionConnectorId?.toString()} with status '${
680 payload.idTagInfo?.status ?? 'undefined'
681 }'`;
682 if (
683 Utils.isNullOrUndefined(payload.idTagInfo) ||
684 payload.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED
685 ) {
686 logger.info(logMsg);
687 } else {
688 logger.warn(logMsg);
689 }
690 }
691 }