5433adecf704f658deeae8683f24d31e92cfb7ce
[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 { OCPP16ServiceUtils } from './OCPP16ServiceUtils';
6 import OCPPError from '../../../exception/OCPPError';
7 import type { JsonObject, JsonType } from '../../../types/JsonType';
8 import { OCPP16ChargePointErrorCode } from '../../../types/ocpp/1.6/ChargePointErrorCode';
9 import { OCPP16ChargePointStatus } from '../../../types/ocpp/1.6/ChargePointStatus';
10 import { OCPP16StandardParametersKey } from '../../../types/ocpp/1.6/Configuration';
11 import type {
12 OCPP16MeterValuesRequest,
13 OCPP16MeterValuesResponse,
14 } from '../../../types/ocpp/1.6/MeterValues';
15 import {
16 type OCPP16BootNotificationRequest,
17 OCPP16IncomingRequestCommand,
18 OCPP16RequestCommand,
19 type OCPP16StatusNotificationRequest,
20 } from '../../../types/ocpp/1.6/Requests';
21 import type {
22 ChangeAvailabilityResponse,
23 ChangeConfigurationResponse,
24 ClearChargingProfileResponse,
25 GetConfigurationResponse,
26 GetDiagnosticsResponse,
27 OCPP16BootNotificationResponse,
28 OCPP16DataTransferResponse,
29 OCPP16DiagnosticsStatusNotificationResponse,
30 OCPP16FirmwareStatusNotificationResponse,
31 OCPP16HeartbeatResponse,
32 OCPP16StatusNotificationResponse,
33 OCPP16TriggerMessageResponse,
34 OCPP16UpdateFirmwareResponse,
35 SetChargingProfileResponse,
36 UnlockConnectorResponse,
37 } from '../../../types/ocpp/1.6/Responses';
38 import {
39 OCPP16AuthorizationStatus,
40 type OCPP16AuthorizeRequest,
41 type OCPP16AuthorizeResponse,
42 type OCPP16StartTransactionRequest,
43 type OCPP16StartTransactionResponse,
44 type OCPP16StopTransactionRequest,
45 type OCPP16StopTransactionResponse,
46 } from '../../../types/ocpp/1.6/Transaction';
47 import { ErrorType } from '../../../types/ocpp/ErrorType';
48 import { OCPPVersion } from '../../../types/ocpp/OCPPVersion';
49 import {
50 type GenericResponse,
51 RegistrationStatusEnumType,
52 type ResponseHandler,
53 } from '../../../types/ocpp/Responses';
54 import Constants from '../../../utils/Constants';
55 import logger from '../../../utils/Logger';
56 import Utils from '../../../utils/Utils';
57 import type ChargingStation from '../../ChargingStation';
58 import { ChargingStationConfigurationUtils } from '../../ChargingStationConfigurationUtils';
59 import OCPPResponseService from '../OCPPResponseService';
60
61 const moduleName = 'OCPP16ResponseService';
62
63 export default class OCPP16ResponseService extends OCPPResponseService {
64 public jsonIncomingRequestResponseSchemas: Map<
65 OCPP16IncomingRequestCommand,
66 JSONSchemaType<JsonObject>
67 >;
68
69 private responseHandlers: Map<OCPP16RequestCommand, ResponseHandler>;
70 private jsonSchemas: Map<OCPP16RequestCommand, JSONSchemaType<JsonObject>>;
71
72 public constructor() {
73 if (new.target?.name === moduleName) {
74 throw new TypeError(`Cannot construct ${new.target?.name} instances directly`);
75 }
76 super(OCPPVersion.VERSION_16);
77 this.responseHandlers = new Map<OCPP16RequestCommand, ResponseHandler>([
78 [OCPP16RequestCommand.BOOT_NOTIFICATION, this.handleResponseBootNotification.bind(this)],
79 [OCPP16RequestCommand.HEARTBEAT, this.emptyResponseHandler.bind(this)],
80 [OCPP16RequestCommand.AUTHORIZE, this.handleResponseAuthorize.bind(this)],
81 [OCPP16RequestCommand.START_TRANSACTION, this.handleResponseStartTransaction.bind(this)],
82 [OCPP16RequestCommand.STOP_TRANSACTION, this.handleResponseStopTransaction.bind(this)],
83 [OCPP16RequestCommand.STATUS_NOTIFICATION, this.emptyResponseHandler.bind(this)],
84 [OCPP16RequestCommand.METER_VALUES, this.emptyResponseHandler.bind(this)],
85 [OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, this.emptyResponseHandler.bind(this)],
86 [OCPP16RequestCommand.DATA_TRANSFER, this.emptyResponseHandler.bind(this)],
87 [OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, this.emptyResponseHandler.bind(this)],
88 ]);
89 this.jsonSchemas = new Map<OCPP16RequestCommand, JSONSchemaType<JsonObject>>([
90 [
91 OCPP16RequestCommand.BOOT_NOTIFICATION,
92 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16BootNotificationResponse>(
93 '../../../assets/json-schemas/ocpp/1.6/BootNotificationResponse.json',
94 moduleName,
95 'constructor'
96 ),
97 ],
98 [
99 OCPP16RequestCommand.HEARTBEAT,
100 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16HeartbeatResponse>(
101 '../../../assets/json-schemas/ocpp/1.6/HeartbeatResponse.json',
102 moduleName,
103 'constructor'
104 ),
105 ],
106 [
107 OCPP16RequestCommand.AUTHORIZE,
108 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16AuthorizeResponse>(
109 '../../../assets/json-schemas/ocpp/1.6/AuthorizeResponse.json',
110 moduleName,
111 'constructor'
112 ),
113 ],
114 [
115 OCPP16RequestCommand.START_TRANSACTION,
116 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16StartTransactionResponse>(
117 '../../../assets/json-schemas/ocpp/1.6/StartTransactionResponse.json',
118 moduleName,
119 'constructor'
120 ),
121 ],
122 [
123 OCPP16RequestCommand.STOP_TRANSACTION,
124 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16StopTransactionResponse>(
125 '../../../assets/json-schemas/ocpp/1.6/StopTransactionResponse.json',
126 moduleName,
127 'constructor'
128 ),
129 ],
130 [
131 OCPP16RequestCommand.STATUS_NOTIFICATION,
132 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16StatusNotificationResponse>(
133 '../../../assets/json-schemas/ocpp/1.6/StatusNotificationResponse.json',
134 moduleName,
135 'constructor'
136 ),
137 ],
138 [
139 OCPP16RequestCommand.METER_VALUES,
140 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16MeterValuesResponse>(
141 '../../../assets/json-schemas/ocpp/1.6/MeterValuesResponse.json',
142 moduleName,
143 'constructor'
144 ),
145 ],
146 [
147 OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION,
148 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16DiagnosticsStatusNotificationResponse>(
149 '../../../assets/json-schemas/ocpp/1.6/DiagnosticsStatusNotificationResponse.json',
150 moduleName,
151 'constructor'
152 ),
153 ],
154 [
155 OCPP16RequestCommand.DATA_TRANSFER,
156 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16DataTransferResponse>(
157 '../../../assets/json-schemas/ocpp/1.6/DataTransferResponse.json',
158 moduleName,
159 'constructor'
160 ),
161 ],
162 [
163 OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION,
164 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16FirmwareStatusNotificationResponse>(
165 '../../../assets/json-schemas/ocpp/1.6/FirmwareStatusNotificationResponse.json',
166 moduleName,
167 'constructor'
168 ),
169 ],
170 ]);
171 this.jsonIncomingRequestResponseSchemas = new Map([
172 [
173 OCPP16IncomingRequestCommand.RESET,
174 OCPP16ServiceUtils.parseJsonSchemaFile<GenericResponse>(
175 '../../../assets/json-schemas/ocpp/1.6/ResetResponse.json',
176 moduleName,
177 'constructor'
178 ),
179 ],
180 [
181 OCPP16IncomingRequestCommand.CLEAR_CACHE,
182 OCPP16ServiceUtils.parseJsonSchemaFile<GenericResponse>(
183 '../../../assets/json-schemas/ocpp/1.6/ClearCacheResponse.json',
184 moduleName,
185 'constructor'
186 ),
187 ],
188 [
189 OCPP16IncomingRequestCommand.CHANGE_AVAILABILITY,
190 OCPP16ServiceUtils.parseJsonSchemaFile<ChangeAvailabilityResponse>(
191 '../../../assets/json-schemas/ocpp/1.6/ChangeAvailabilityResponse.json',
192 moduleName,
193 'constructor'
194 ),
195 ],
196 [
197 OCPP16IncomingRequestCommand.UNLOCK_CONNECTOR,
198 OCPP16ServiceUtils.parseJsonSchemaFile<UnlockConnectorResponse>(
199 '../../../assets/json-schemas/ocpp/1.6/UnlockConnectorResponse.json',
200 moduleName,
201 'constructor'
202 ),
203 ],
204 [
205 OCPP16IncomingRequestCommand.GET_CONFIGURATION,
206 OCPP16ServiceUtils.parseJsonSchemaFile<GetConfigurationResponse>(
207 '../../../assets/json-schemas/ocpp/1.6/GetConfigurationResponse.json',
208 moduleName,
209 'constructor'
210 ),
211 ],
212 [
213 OCPP16IncomingRequestCommand.CHANGE_CONFIGURATION,
214 OCPP16ServiceUtils.parseJsonSchemaFile<ChangeConfigurationResponse>(
215 '../../../assets/json-schemas/ocpp/1.6/ChangeConfigurationResponse.json',
216 moduleName,
217 'constructor'
218 ),
219 ],
220 [
221 OCPP16IncomingRequestCommand.SET_CHARGING_PROFILE,
222 OCPP16ServiceUtils.parseJsonSchemaFile<SetChargingProfileResponse>(
223 '../../../assets/json-schemas/ocpp/1.6/SetChargingProfileResponse.json',
224 moduleName,
225 'constructor'
226 ),
227 ],
228 [
229 OCPP16IncomingRequestCommand.CLEAR_CHARGING_PROFILE,
230 OCPP16ServiceUtils.parseJsonSchemaFile<ClearChargingProfileResponse>(
231 '../../../assets/json-schemas/ocpp/1.6/ClearChargingProfileResponse.json',
232 moduleName,
233 'constructor'
234 ),
235 ],
236 [
237 OCPP16IncomingRequestCommand.REMOTE_START_TRANSACTION,
238 OCPP16ServiceUtils.parseJsonSchemaFile<GenericResponse>(
239 '../../../assets/json-schemas/ocpp/1.6/RemoteStartTransactionResponse.json',
240 moduleName,
241 'constructor'
242 ),
243 ],
244 [
245 OCPP16IncomingRequestCommand.REMOTE_STOP_TRANSACTION,
246 OCPP16ServiceUtils.parseJsonSchemaFile<GenericResponse>(
247 '../../../assets/json-schemas/ocpp/1.6/RemoteStopTransactionResponse.json',
248 moduleName,
249 'constructor'
250 ),
251 ],
252 [
253 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS,
254 OCPP16ServiceUtils.parseJsonSchemaFile<GetDiagnosticsResponse>(
255 '../../../assets/json-schemas/ocpp/1.6/GetDiagnosticsResponse.json',
256 moduleName,
257 'constructor'
258 ),
259 ],
260 [
261 OCPP16IncomingRequestCommand.TRIGGER_MESSAGE,
262 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16TriggerMessageResponse>(
263 '../../../assets/json-schemas/ocpp/1.6/TriggerMessageResponse.json',
264 moduleName,
265 'constructor'
266 ),
267 ],
268 [
269 OCPP16IncomingRequestCommand.DATA_TRANSFER,
270 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16DataTransferResponse>(
271 '../../../assets/json-schemas/ocpp/1.6/DataTransferResponse.json',
272 moduleName,
273 'constructor'
274 ),
275 ],
276 [
277 OCPP16IncomingRequestCommand.UPDATE_FIRMWARE,
278 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16UpdateFirmwareResponse>(
279 '../../../assets/json-schemas/ocpp/1.6/UpdateFirmwareResponse.json',
280 moduleName,
281 'constructor'
282 ),
283 ],
284 ]);
285 this.validatePayload.bind(this);
286 }
287
288 public async responseHandler(
289 chargingStation: ChargingStation,
290 commandName: OCPP16RequestCommand,
291 payload: JsonType,
292 requestPayload: JsonType
293 ): Promise<void> {
294 if (
295 chargingStation.isRegistered() === true ||
296 commandName === OCPP16RequestCommand.BOOT_NOTIFICATION
297 ) {
298 if (
299 this.responseHandlers.has(commandName) === true &&
300 OCPP16ServiceUtils.isRequestCommandSupported(chargingStation, commandName) === true
301 ) {
302 try {
303 this.validatePayload(chargingStation, commandName, payload);
304 await this.responseHandlers.get(commandName)(chargingStation, payload, requestPayload);
305 } catch (error) {
306 logger.error(
307 `${chargingStation.logPrefix()} ${moduleName}.responseHandler: Handle response error:`,
308 error
309 );
310 throw error;
311 }
312 } else {
313 // Throw exception
314 throw new OCPPError(
315 ErrorType.NOT_IMPLEMENTED,
316 `${commandName} is not implemented to handle response PDU ${JSON.stringify(
317 payload,
318 null,
319 2
320 )}`,
321 commandName,
322 payload
323 );
324 }
325 } else {
326 throw new OCPPError(
327 ErrorType.SECURITY_ERROR,
328 `${commandName} cannot be issued to handle response PDU ${JSON.stringify(
329 payload,
330 null,
331 2
332 )} while the charging station is not registered on the central server.`,
333 commandName,
334 payload
335 );
336 }
337 }
338
339 private validatePayload(
340 chargingStation: ChargingStation,
341 commandName: OCPP16RequestCommand,
342 payload: JsonType
343 ): boolean {
344 if (this.jsonSchemas.has(commandName) === true) {
345 return this.validateResponsePayload(
346 chargingStation,
347 commandName,
348 this.jsonSchemas.get(commandName),
349 payload
350 );
351 }
352 logger.warn(
353 `${chargingStation.logPrefix()} ${moduleName}.validatePayload: No JSON schema found for command '${commandName}' PDU validation`
354 );
355 return false;
356 }
357
358 private handleResponseBootNotification(
359 chargingStation: ChargingStation,
360 payload: OCPP16BootNotificationResponse
361 ): void {
362 if (payload.status === RegistrationStatusEnumType.ACCEPTED) {
363 ChargingStationConfigurationUtils.addConfigurationKey(
364 chargingStation,
365 OCPP16StandardParametersKey.HeartbeatInterval,
366 payload.interval.toString(),
367 {},
368 { overwrite: true, save: true }
369 );
370 ChargingStationConfigurationUtils.addConfigurationKey(
371 chargingStation,
372 OCPP16StandardParametersKey.HeartBeatInterval,
373 payload.interval.toString(),
374 { visible: false },
375 { overwrite: true, save: true }
376 );
377 chargingStation.heartbeatSetInterval
378 ? chargingStation.restartHeartbeat()
379 : chargingStation.startHeartbeat();
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 = authorizeConnectorId !== undefined;
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.hasAuthorizedTags() &&
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 }