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