1 import { AuthorizationStatus
, AuthorizeResponse
, StartTransactionResponse
, StopTransactionReason
, StopTransactionResponse
} from
'../types/ocpp/Transaction';
2 import { PerformanceObserver
, performance
} from
'perf_hooks';
4 import ChargingStation from
'./ChargingStation';
5 import Constants from
'../utils/Constants';
6 import Utils from
'../utils/Utils';
7 import logger from
'../utils/Logger';
9 export default class AutomaticTransactionGenerator
{
10 public timeToStop
: boolean;
11 private chargingStation
: ChargingStation
;
12 private performanceObserver
: PerformanceObserver
;
14 constructor(chargingStation
: ChargingStation
) {
15 this.chargingStation
= chargingStation
;
16 this.timeToStop
= true;
17 if (this.chargingStation
.getEnableStatistics()) {
18 this.performanceObserver
= new PerformanceObserver((list
) => {
19 const entry
= list
.getEntries()[0];
20 this.chargingStation
.statistics
.logPerformance(entry
, Constants
.ENTITY_AUTOMATIC_TRANSACTION_GENERATOR
);
21 this.performanceObserver
.disconnect();
26 public async start(): Promise
<void> {
27 this.timeToStop
= false;
28 if (this.chargingStation
.stationInfo
.AutomaticTransactionGenerator
.stopAfterHours
&&
29 this.chargingStation
.stationInfo
.AutomaticTransactionGenerator
.stopAfterHours
> 0) {
30 // eslint-disable-next-line @typescript-eslint/no-misused-promises
31 setTimeout(async (): Promise
<void> => {
33 }, this.chargingStation
.stationInfo
.AutomaticTransactionGenerator
.stopAfterHours
* 3600 * 1000);
35 for (const connector
in this.chargingStation
.connectors
) {
36 if (Utils
.convertToInt(connector
) > 0) {
37 await this.startConnector(Utils
.convertToInt(connector
));
40 logger
.info(this.logPrefix() + ' ATG started and will stop in ' + Utils
.secondsToHHMMSS(this.chargingStation
.stationInfo
.AutomaticTransactionGenerator
.stopAfterHours
* 3600));
43 public async stop(reason
: StopTransactionReason
= StopTransactionReason
.NONE
): Promise
<void> {
44 logger
.info(this.logPrefix() + ' ATG OVER => STOPPING ALL TRANSACTIONS');
45 for (const connector
in this.chargingStation
.connectors
) {
46 const transactionId
= this.chargingStation
.getConnector(Utils
.convertToInt(connector
)).transactionId
;
47 if (this.chargingStation
.getConnector(Utils
.convertToInt(connector
)).transactionStarted
) {
48 logger
.info(this.logPrefix(Utils
.convertToInt(connector
)) + ' ATG OVER. Stop transaction ' + transactionId
.toString());
49 await this.chargingStation
.ocppRequestService
.sendStopTransaction(transactionId
, this.chargingStation
.getTransactionMeterStop(transactionId
), this.chargingStation
.getTransactionIdTag(transactionId
), reason
);
52 this.timeToStop
= true;
55 public async startConnector(connectorId
: number): Promise
<void> {
57 if (this.timeToStop
) {
58 logger
.error(this.logPrefix(connectorId
) + ' Entered in transaction loop while a request to stop it was made');
61 if (!this.chargingStation
.isRegistered()) {
62 logger
.error(this.logPrefix(connectorId
) + ' Entered in transaction loop while the charging station is not registered');
65 if (!this.chargingStation
.isChargingStationAvailable()) {
66 logger
.info(this.logPrefix(connectorId
) + ' Entered in transaction loop while the charging station is unavailable');
70 if (!this.chargingStation
.isConnectorAvailable(connectorId
)) {
71 logger
.info(`${this.logPrefix(connectorId)} Entered in transaction loop while the connector ${connectorId} is unavailable, stop it`);
74 if (!this.chargingStation
?.ocppRequestService
) {
75 logger
.info(`${this.logPrefix(connectorId)} Transaction loop waiting for charging station service to be initialized`);
77 await Utils
.sleep(Constants
.CHARGING_STATION_ATG_INITIALIZATION_TIME
);
78 } while (!this.chargingStation
?.ocppRequestService
);
80 const wait
= Utils
.getRandomInt(this.chargingStation
.stationInfo
.AutomaticTransactionGenerator
.maxDelayBetweenTwoTransactions
,
81 this.chargingStation
.stationInfo
.AutomaticTransactionGenerator
.minDelayBetweenTwoTransactions
) * 1000;
82 logger
.info(this.logPrefix(connectorId
) + ' wait for ' + Utils
.milliSecondsToHHMMSS(wait
));
83 await Utils
.sleep(wait
);
84 const start
= Math.random();
86 if (start
< this.chargingStation
.stationInfo
.AutomaticTransactionGenerator
.probabilityOfStart
) {
89 let startResponse
: StartTransactionResponse
| AuthorizeResponse
;
90 if (this.chargingStation
.getEnableStatistics()) {
91 const startTransaction
= performance
.timerify(this.startTransaction
);
92 this.performanceObserver
.observe({ entryTypes
: ['function'] });
93 startResponse
= await startTransaction(connectorId
, this);
95 startResponse
= await this.startTransaction(connectorId
, this);
97 if (startResponse
?.idTagInfo
?.status !== AuthorizationStatus
.ACCEPTED
) {
98 logger
.info(this.logPrefix(connectorId
) + ' transaction rejected');
99 await Utils
.sleep(Constants
.CHARGING_STATION_ATG_WAIT_TIME
);
101 // Wait until end of transaction
102 const waitTrxEnd
= Utils
.getRandomInt(this.chargingStation
.stationInfo
.AutomaticTransactionGenerator
.maxDuration
,
103 this.chargingStation
.stationInfo
.AutomaticTransactionGenerator
.minDuration
) * 1000;
104 logger
.info(this.logPrefix(connectorId
) + ' transaction ' + this.chargingStation
.getConnector(connectorId
).transactionId
.toString() + ' will stop in ' + Utils
.milliSecondsToHHMMSS(waitTrxEnd
));
105 await Utils
.sleep(waitTrxEnd
);
107 if (this.chargingStation
.getConnector(connectorId
)?.transactionStarted
) {
108 logger
.info(this.logPrefix(connectorId
) + ' stop transaction ' + this.chargingStation
.getConnector(connectorId
).transactionId
.toString());
109 if (this.chargingStation
.getEnableStatistics()) {
110 const stopTransaction
= performance
.timerify(this.stopTransaction
);
111 this.performanceObserver
.observe({ entryTypes
: ['function'] });
112 await stopTransaction(connectorId
, this);
114 await this.stopTransaction(connectorId
, this);
120 logger
.info(this.logPrefix(connectorId
) + ' transaction skipped ' + skip
.toString());
122 } while (!this.timeToStop
);
123 logger
.info(this.logPrefix(connectorId
) + ' ATG STOPPED on the connector');
126 // eslint-disable-next-line consistent-this
127 private async startTransaction(connectorId
: number, self: AutomaticTransactionGenerator
): Promise
<StartTransactionResponse
| AuthorizeResponse
> {
128 if (self.chargingStation
.hasAuthorizedTags()) {
129 const tagId
= self.chargingStation
.getRandomTagId();
130 if (self.chargingStation
.stationInfo
.AutomaticTransactionGenerator
.requireAuthorize
) {
132 const authorizeResponse
= await self.chargingStation
.ocppRequestService
.sendAuthorize(tagId
);
133 if (authorizeResponse
?.idTagInfo
?.status === AuthorizationStatus
.ACCEPTED
) {
134 logger
.info(self.logPrefix(connectorId
) + ' start transaction for tagID ' + tagId
);
136 return await self.chargingStation
.ocppRequestService
.sendStartTransaction(connectorId
, tagId
);
138 return authorizeResponse
;
140 logger
.info(self.logPrefix(connectorId
) + ' start transaction for tagID ' + tagId
);
142 return await self.chargingStation
.ocppRequestService
.sendStartTransaction(connectorId
, tagId
);
144 logger
.info(self.logPrefix(connectorId
) + ' start transaction without a tagID');
145 return await self.chargingStation
.ocppRequestService
.sendStartTransaction(connectorId
);
148 // eslint-disable-next-line consistent-this
149 private async stopTransaction(connectorId
: number, self: AutomaticTransactionGenerator
): Promise
<StopTransactionResponse
> {
150 const transactionId
= self.chargingStation
.getConnector(connectorId
).transactionId
;
151 return await self.chargingStation
.ocppRequestService
.sendStopTransaction(transactionId
, self.chargingStation
.getTransactionMeterStop(transactionId
), self.chargingStation
.getTransactionIdTag(transactionId
));
154 private logPrefix(connectorId
: number = null): string {
156 return Utils
.logPrefix(' ' + this.chargingStation
.stationInfo
.chargingStationId
+ ' ATG on connector #' + connectorId
.toString() + ':');
158 return Utils
.logPrefix(' ' + this.chargingStation
.stationInfo
.chargingStationId
+ ' ATG:');