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