Encapsulate performance init code in the same class.
[e-mobility-charging-stations-simulator.git] / src / charging-station / AutomaticTransactionGenerator.ts
CommitLineData
c0560973 1import { AuthorizationStatus, AuthorizeResponse, StartTransactionResponse, StopTransactionReason, StopTransactionResponse } from '../types/ocpp/Transaction';
6af9012e
JB
2
3import ChargingStation from './ChargingStation';
4import Constants from '../utils/Constants';
57939a9d 5import PerformanceStatistics from '../utils/PerformanceStatistics';
6af9012e
JB
6import Utils from '../utils/Utils';
7import logger from '../utils/Logger';
8
9export default class AutomaticTransactionGenerator {
ad2f27c3
JB
10 public timeToStop: boolean;
11 private chargingStation: ChargingStation;
6af9012e
JB
12
13 constructor(chargingStation: ChargingStation) {
ad2f27c3
JB
14 this.chargingStation = chargingStation;
15 this.timeToStop = true;
6af9012e
JB
16 }
17
e268356b 18 public async start(): Promise<void> {
ad2f27c3
JB
19 this.timeToStop = false;
20 if (this.chargingStation.stationInfo.AutomaticTransactionGenerator.stopAfterHours &&
21 this.chargingStation.stationInfo.AutomaticTransactionGenerator.stopAfterHours > 0) {
71623267 22 // eslint-disable-next-line @typescript-eslint/no-misused-promises
e268356b
JB
23 setTimeout(async (): Promise<void> => {
24 await this.stop();
ad2f27c3 25 }, this.chargingStation.stationInfo.AutomaticTransactionGenerator.stopAfterHours * 3600 * 1000);
6af9012e 26 }
ad2f27c3 27 for (const connector in this.chargingStation.connectors) {
6af9012e 28 if (Utils.convertToInt(connector) > 0) {
e268356b 29 await this.startConnector(Utils.convertToInt(connector));
6af9012e
JB
30 }
31 }
c0560973 32 logger.info(this.logPrefix() + ' ATG started and will stop in ' + Utils.secondsToHHMMSS(this.chargingStation.stationInfo.AutomaticTransactionGenerator.stopAfterHours * 3600));
6af9012e
JB
33 }
34
c0560973
JB
35 public async stop(reason: StopTransactionReason = StopTransactionReason.NONE): Promise<void> {
36 logger.info(this.logPrefix() + ' ATG OVER => STOPPING ALL TRANSACTIONS');
ad2f27c3 37 for (const connector in this.chargingStation.connectors) {
c0560973 38 const transactionId = this.chargingStation.getConnector(Utils.convertToInt(connector)).transactionId;
ad2f27c3 39 if (this.chargingStation.getConnector(Utils.convertToInt(connector)).transactionStarted) {
c0560973 40 logger.info(this.logPrefix(Utils.convertToInt(connector)) + ' ATG OVER. Stop transaction ' + transactionId.toString());
6ed92bc1 41 await this.chargingStation.ocppRequestService.sendStopTransaction(transactionId, this.chargingStation.getEnergyActiveImportRegisterByTransactionId(transactionId),
035742f7 42 this.chargingStation.getTransactionIdTag(transactionId), reason);
6af9012e
JB
43 }
44 }
ad2f27c3 45 this.timeToStop = true;
6af9012e
JB
46 }
47
a1256107 48 private async startConnector(connectorId: number): Promise<void> {
6af9012e 49 do {
ad2f27c3 50 if (this.timeToStop) {
c0560973 51 logger.error(this.logPrefix(connectorId) + ' Entered in transaction loop while a request to stop it was made');
17991e8c
JB
52 break;
53 }
c0560973
JB
54 if (!this.chargingStation.isRegistered()) {
55 logger.error(this.logPrefix(connectorId) + ' Entered in transaction loop while the charging station is not registered');
17991e8c
JB
56 break;
57 }
c0560973
JB
58 if (!this.chargingStation.isChargingStationAvailable()) {
59 logger.info(this.logPrefix(connectorId) + ' Entered in transaction loop while the charging station is unavailable');
ab5f4b03
JB
60 await this.stop();
61 break;
62 }
c0560973
JB
63 if (!this.chargingStation.isConnectorAvailable(connectorId)) {
64 logger.info(`${this.logPrefix(connectorId)} Entered in transaction loop while the connector ${connectorId} is unavailable, stop it`);
17991e8c
JB
65 break;
66 }
c0560973
JB
67 if (!this.chargingStation?.ocppRequestService) {
68 logger.info(`${this.logPrefix(connectorId)} Transaction loop waiting for charging station service to be initialized`);
69 do {
a4cc42ea 70 await Utils.sleep(Constants.CHARGING_STATION_ATG_INITIALIZATION_TIME);
c0560973
JB
71 } while (!this.chargingStation?.ocppRequestService);
72 }
ad2f27c3
JB
73 const wait = Utils.getRandomInt(this.chargingStation.stationInfo.AutomaticTransactionGenerator.maxDelayBetweenTwoTransactions,
74 this.chargingStation.stationInfo.AutomaticTransactionGenerator.minDelayBetweenTwoTransactions) * 1000;
c0560973 75 logger.info(this.logPrefix(connectorId) + ' wait for ' + Utils.milliSecondsToHHMMSS(wait));
6af9012e 76 await Utils.sleep(wait);
6af9012e
JB
77 const start = Math.random();
78 let skip = 0;
ad2f27c3 79 if (start < this.chargingStation.stationInfo.AutomaticTransactionGenerator.probabilityOfStart) {
6af9012e 80 // Start transaction
e9017bfc 81 let startATGTransaction: (connectorId: number, self: AutomaticTransactionGenerator) => Promise<StartTransactionResponse | AuthorizeResponse>;
ad2f27c3 82 if (this.chargingStation.getEnableStatistics()) {
e9017bfc 83 startATGTransaction = PerformanceStatistics.timedFunction(this.startATGTransaction.bind(this));
6af9012e 84 } else {
e9017bfc 85 startATGTransaction = this.startATGTransaction.bind(this);
6af9012e 86 }
e9017bfc 87 const startResponse = await startATGTransaction(connectorId, this);
ef6076c1 88 if (startResponse?.idTagInfo?.status !== AuthorizationStatus.ACCEPTED) {
54b1efe0 89 logger.warn(this.logPrefix(connectorId) + ' transaction rejected');
6af9012e
JB
90 await Utils.sleep(Constants.CHARGING_STATION_ATG_WAIT_TIME);
91 } else {
92 // Wait until end of transaction
ad2f27c3
JB
93 const waitTrxEnd = Utils.getRandomInt(this.chargingStation.stationInfo.AutomaticTransactionGenerator.maxDuration,
94 this.chargingStation.stationInfo.AutomaticTransactionGenerator.minDuration) * 1000;
c0560973 95 logger.info(this.logPrefix(connectorId) + ' transaction ' + this.chargingStation.getConnector(connectorId).transactionId.toString() + ' will stop in ' + Utils.milliSecondsToHHMMSS(waitTrxEnd));
6af9012e
JB
96 await Utils.sleep(waitTrxEnd);
97 // Stop transaction
ad2f27c3 98 if (this.chargingStation.getConnector(connectorId)?.transactionStarted) {
c0560973 99 logger.info(this.logPrefix(connectorId) + ' stop transaction ' + this.chargingStation.getConnector(connectorId).transactionId.toString());
e9017bfc 100 let stopATGTransaction: (connectorId: number, self: AutomaticTransactionGenerator) => Promise<StopTransactionResponse>;
ad2f27c3 101 if (this.chargingStation.getEnableStatistics()) {
e9017bfc 102 stopATGTransaction = PerformanceStatistics.timedFunction(this.stopATGTransaction.bind(this));
6af9012e 103 } else {
e9017bfc 104 stopATGTransaction = this.stopATGTransaction.bind(this);
6af9012e 105 }
e9017bfc 106 await stopATGTransaction(connectorId, this);
6af9012e
JB
107 }
108 }
109 } else {
110 skip++;
c0560973 111 logger.info(this.logPrefix(connectorId) + ' transaction skipped ' + skip.toString());
6af9012e 112 }
ad2f27c3 113 } while (!this.timeToStop);
c0560973 114 logger.info(this.logPrefix(connectorId) + ' ATG STOPPED on the connector');
6af9012e
JB
115 }
116
65c5527e 117 // eslint-disable-next-line consistent-this
e9017bfc 118 private async startATGTransaction(connectorId: number, self: AutomaticTransactionGenerator): Promise<StartTransactionResponse | AuthorizeResponse> {
ad2f27c3
JB
119 if (self.chargingStation.hasAuthorizedTags()) {
120 const tagId = self.chargingStation.getRandomTagId();
e644918b 121 if (self.chargingStation.getAutomaticTransactionGeneratorRequireAuthorize()) {
5fdab605 122 // Authorize tagId
163547b1 123 const authorizeResponse = await self.chargingStation.ocppRequestService.sendAuthorize(connectorId, tagId);
5fdab605 124 if (authorizeResponse?.idTagInfo?.status === AuthorizationStatus.ACCEPTED) {
c0560973 125 logger.info(self.logPrefix(connectorId) + ' start transaction for tagID ' + tagId);
5fdab605 126 // Start transaction
57939a9d 127 return self.chargingStation.ocppRequestService.sendStartTransaction(connectorId, tagId);
5fdab605 128 }
4faad557 129 return authorizeResponse;
ef6076c1 130 }
c0560973 131 logger.info(self.logPrefix(connectorId) + ' start transaction for tagID ' + tagId);
5fdab605 132 // Start transaction
57939a9d 133 return self.chargingStation.ocppRequestService.sendStartTransaction(connectorId, tagId);
6af9012e 134 }
c0560973 135 logger.info(self.logPrefix(connectorId) + ' start transaction without a tagID');
57939a9d 136 return self.chargingStation.ocppRequestService.sendStartTransaction(connectorId);
6af9012e
JB
137 }
138
65c5527e 139 // eslint-disable-next-line consistent-this
e9017bfc 140 private async stopATGTransaction(connectorId: number, self: AutomaticTransactionGenerator): Promise<StopTransactionResponse> {
c0560973 141 const transactionId = self.chargingStation.getConnector(connectorId).transactionId;
57939a9d 142 return self.chargingStation.ocppRequestService.sendStopTransaction(transactionId, self.chargingStation.getEnergyActiveImportRegisterByTransactionId(transactionId),
6e0964c8 143 self.chargingStation.getTransactionIdTag(transactionId));
c0560973
JB
144 }
145
6e0964c8 146 private logPrefix(connectorId?: number): string {
c0560973 147 if (connectorId) {
54b1efe0 148 return Utils.logPrefix(' ' + this.chargingStation.stationInfo.chargingStationId + ' | ATG on connector #' + connectorId.toString() + ':');
c0560973 149 }
54b1efe0 150 return Utils.logPrefix(' ' + this.chargingStation.stationInfo.chargingStationId + ' | ATG:');
6af9012e
JB
151 }
152}