refactor(simulator): use helper for undefined or null checks
[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.bind(this);
284 }
285
286 public async responseHandler(
287 chargingStation: ChargingStation,
288 commandName: OCPP16RequestCommand,
289 payload: JsonType,
290 requestPayload: JsonType
291 ): Promise<void> {
292 if (
293 chargingStation.isRegistered() === true ||
294 commandName === OCPP16RequestCommand.BOOT_NOTIFICATION
295 ) {
296 if (
297 this.responseHandlers.has(commandName) === true &&
298 OCPP16ServiceUtils.isRequestCommandSupported(chargingStation, commandName) === true
299 ) {
300 try {
301 this.validatePayload(chargingStation, commandName, payload);
302 await this.responseHandlers.get(commandName)(chargingStation, payload, requestPayload);
303 } catch (error) {
304 logger.error(
305 `${chargingStation.logPrefix()} ${moduleName}.responseHandler: Handle response error:`,
306 error
307 );
308 throw error;
309 }
310 } else {
311 // Throw exception
312 throw new OCPPError(
313 ErrorType.NOT_IMPLEMENTED,
314 `${commandName} is not implemented to handle response PDU ${JSON.stringify(
315 payload,
316 null,
317 2
318 )}`,
319 commandName,
320 payload
321 );
322 }
323 } else {
324 throw new OCPPError(
325 ErrorType.SECURITY_ERROR,
326 `${commandName} cannot be issued to handle response PDU ${JSON.stringify(
327 payload,
328 null,
329 2
330 )} while the charging station is not registered on the central server.`,
331 commandName,
332 payload
333 );
334 }
335 }
336
337 private validatePayload(
338 chargingStation: ChargingStation,
339 commandName: OCPP16RequestCommand,
340 payload: JsonType
341 ): boolean {
342 if (this.jsonSchemas.has(commandName) === true) {
343 return this.validateResponsePayload(
344 chargingStation,
345 commandName,
346 this.jsonSchemas.get(commandName),
347 payload
348 );
349 }
350 logger.warn(
351 `${chargingStation.logPrefix()} ${moduleName}.validatePayload: No JSON schema found for command '${commandName}' PDU validation`
352 );
353 return false;
354 }
355
356 private handleResponseBootNotification(
357 chargingStation: ChargingStation,
358 payload: OCPP16BootNotificationResponse
359 ): void {
360 if (payload.status === RegistrationStatusEnumType.ACCEPTED) {
361 ChargingStationConfigurationUtils.addConfigurationKey(
362 chargingStation,
363 OCPP16StandardParametersKey.HeartbeatInterval,
364 payload.interval.toString(),
365 {},
366 { overwrite: true, save: true }
367 );
368 ChargingStationConfigurationUtils.addConfigurationKey(
369 chargingStation,
370 OCPP16StandardParametersKey.HeartBeatInterval,
371 payload.interval.toString(),
372 { visible: false },
373 { overwrite: true, save: true }
374 );
375 OCPP16ServiceUtils.startHeartbeatInterval(chargingStation, payload.interval);
376 }
377 if (Object.values(RegistrationStatusEnumType).includes(payload.status)) {
378 const logMsg = `${chargingStation.logPrefix()} Charging station in '${
379 payload.status
380 }' state on the central server`;
381 payload.status === RegistrationStatusEnumType.REJECTED
382 ? logger.warn(logMsg)
383 : logger.info(logMsg);
384 } else {
385 logger.error(
386 `${chargingStation.logPrefix()} Charging station boot notification response received: %j with undefined registration status`,
387 payload
388 );
389 }
390 }
391
392 private handleResponseAuthorize(
393 chargingStation: ChargingStation,
394 payload: OCPP16AuthorizeResponse,
395 requestPayload: OCPP16AuthorizeRequest
396 ): void {
397 let authorizeConnectorId: number;
398 for (const connectorId of chargingStation.connectors.keys()) {
399 if (
400 connectorId > 0 &&
401 chargingStation.getConnectorStatus(connectorId)?.authorizeIdTag === requestPayload.idTag
402 ) {
403 authorizeConnectorId = connectorId;
404 break;
405 }
406 }
407 const authorizeConnectorIdDefined = !Utils.isNullOrUndefined(authorizeConnectorId);
408 if (payload.idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED) {
409 authorizeConnectorIdDefined &&
410 (chargingStation.getConnectorStatus(authorizeConnectorId).idTagAuthorized = true);
411 logger.debug(
412 `${chargingStation.logPrefix()} IdTag '${requestPayload.idTag}' accepted${
413 authorizeConnectorIdDefined ? ` on connector ${authorizeConnectorId}` : ''
414 }`
415 );
416 } else {
417 if (authorizeConnectorIdDefined) {
418 chargingStation.getConnectorStatus(authorizeConnectorId).idTagAuthorized = false;
419 delete chargingStation.getConnectorStatus(authorizeConnectorId)?.authorizeIdTag;
420 }
421 logger.debug(
422 `${chargingStation.logPrefix()} IdTag '${requestPayload.idTag}' rejected with status '${
423 payload.idTagInfo.status
424 }'${authorizeConnectorIdDefined ? ` on connector ${authorizeConnectorId}` : ''}`
425 );
426 }
427 }
428
429 private async handleResponseStartTransaction(
430 chargingStation: ChargingStation,
431 payload: OCPP16StartTransactionResponse,
432 requestPayload: OCPP16StartTransactionRequest
433 ): Promise<void> {
434 const connectorId = requestPayload.connectorId;
435
436 let transactionConnectorId: number;
437 for (const id of chargingStation.connectors.keys()) {
438 if (id > 0 && id === connectorId) {
439 transactionConnectorId = id;
440 break;
441 }
442 }
443 if (Utils.isNullOrUndefined(transactionConnectorId)) {
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 ${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 ${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 ${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 chargingStation.ocppRequestService.requestHandler<
558 OCPP16StatusNotificationRequest,
559 OCPP16StatusNotificationResponse
560 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
561 connectorId,
562 status: OCPP16ChargePointStatus.Charging,
563 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
564 });
565 chargingStation.getConnectorStatus(connectorId).status = OCPP16ChargePointStatus.Charging;
566 logger.info(
567 `${chargingStation.logPrefix()} Transaction ${payload.transactionId.toString()} STARTED on ${
568 chargingStation.stationInfo.chargingStationId
569 }#${connectorId.toString()} for idTag '${requestPayload.idTag}'`
570 );
571 if (chargingStation.stationInfo.powerSharedByConnectors) {
572 chargingStation.powerDivider++;
573 }
574 const configuredMeterValueSampleInterval =
575 ChargingStationConfigurationUtils.getConfigurationKey(
576 chargingStation,
577 OCPP16StandardParametersKey.MeterValueSampleInterval
578 );
579 chargingStation.startMeterValues(
580 connectorId,
581 configuredMeterValueSampleInterval
582 ? Utils.convertToInt(configuredMeterValueSampleInterval.value) * 1000
583 : Constants.DEFAULT_METER_VALUES_INTERVAL
584 );
585 } else {
586 logger.warn(
587 `${chargingStation.logPrefix()} Starting transaction id ${payload.transactionId.toString()} REJECTED with status '${
588 payload.idTagInfo?.status
589 }', idTag '${requestPayload.idTag}'`
590 );
591 await this.resetConnectorOnStartTransactionError(chargingStation, connectorId);
592 }
593 }
594
595 private async resetConnectorOnStartTransactionError(
596 chargingStation: ChargingStation,
597 connectorId: number
598 ): Promise<void> {
599 chargingStation.resetConnectorStatus(connectorId);
600 if (
601 chargingStation.getConnectorStatus(connectorId)?.status !== OCPP16ChargePointStatus.Available
602 ) {
603 await chargingStation.ocppRequestService.requestHandler<
604 OCPP16StatusNotificationRequest,
605 OCPP16StatusNotificationResponse
606 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
607 connectorId,
608 status: OCPP16ChargePointStatus.Available,
609 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
610 });
611 chargingStation.getConnectorStatus(connectorId).status = OCPP16ChargePointStatus.Available;
612 }
613 }
614
615 private async handleResponseStopTransaction(
616 chargingStation: ChargingStation,
617 payload: OCPP16StopTransactionResponse,
618 requestPayload: OCPP16StopTransactionRequest
619 ): Promise<void> {
620 const transactionConnectorId = chargingStation.getConnectorIdByTransactionId(
621 requestPayload.transactionId
622 );
623 if (Utils.isNullOrUndefined(transactionConnectorId)) {
624 logger.error(
625 `${chargingStation.logPrefix()} Trying to stop a non existing transaction ${requestPayload.transactionId.toString()}`
626 );
627 return;
628 }
629 chargingStation.getBeginEndMeterValues() === true &&
630 chargingStation.getOcppStrictCompliance() === false &&
631 chargingStation.getOutOfOrderEndMeterValues() === true &&
632 (await chargingStation.ocppRequestService.requestHandler<
633 OCPP16MeterValuesRequest,
634 OCPP16MeterValuesResponse
635 >(chargingStation, OCPP16RequestCommand.METER_VALUES, {
636 connectorId: transactionConnectorId,
637 transactionId: requestPayload.transactionId,
638 meterValue: [
639 OCPP16ServiceUtils.buildTransactionEndMeterValue(
640 chargingStation,
641 transactionConnectorId,
642 requestPayload.meterStop
643 ),
644 ],
645 }));
646 if (
647 chargingStation.isChargingStationAvailable() === false ||
648 chargingStation.isConnectorAvailable(transactionConnectorId) === false
649 ) {
650 await chargingStation.ocppRequestService.requestHandler<
651 OCPP16StatusNotificationRequest,
652 OCPP16StatusNotificationResponse
653 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
654 connectorId: transactionConnectorId,
655 status: OCPP16ChargePointStatus.Unavailable,
656 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
657 });
658 chargingStation.getConnectorStatus(transactionConnectorId).status =
659 OCPP16ChargePointStatus.Unavailable;
660 } else {
661 await chargingStation.ocppRequestService.requestHandler<
662 OCPP16BootNotificationRequest,
663 OCPP16BootNotificationResponse
664 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
665 connectorId: transactionConnectorId,
666 status: OCPP16ChargePointStatus.Available,
667 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
668 });
669 chargingStation.getConnectorStatus(transactionConnectorId).status =
670 OCPP16ChargePointStatus.Available;
671 }
672 if (chargingStation.stationInfo.powerSharedByConnectors) {
673 chargingStation.powerDivider--;
674 }
675 chargingStation.resetConnectorStatus(transactionConnectorId);
676 const logMsg = `${chargingStation.logPrefix()} Transaction ${requestPayload.transactionId.toString()} STOPPED on ${
677 chargingStation.stationInfo.chargingStationId
678 }#${transactionConnectorId?.toString()} with status '${
679 payload.idTagInfo?.status ?? 'undefined'
680 }'`;
681 if (
682 Utils.isNullOrUndefined(payload.idTagInfo) ||
683 payload.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED
684 ) {
685 logger.info(logMsg);
686 } else {
687 logger.warn(logMsg);
688 }
689 }
690 }