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