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