1 import { AuthorizationStatus
, AuthorizeResponse
, StartTransactionResponse
, StopTransactionReason
, StopTransactionResponse
} from
'../types/ocpp/1.6/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 private _chargingStation
: ChargingStation
;
11 private _timeToStop
: boolean;
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 get
timeToStop(): boolean {
27 return this._timeToStop
;
30 _logPrefix(connectorId
: number = null): string {
32 return Utils
.logPrefix(' ' + this._chargingStation
.stationInfo
.name
+ ' ATG on connector #' + connectorId
.toString() + ':');
34 return Utils
.logPrefix(' ' + this._chargingStation
.stationInfo
.name
+ ' ATG:');
38 this._timeToStop
= false;
39 if (this._chargingStation
.stationInfo
.AutomaticTransactionGenerator
.stopAfterHours
&&
40 this._chargingStation
.stationInfo
.AutomaticTransactionGenerator
.stopAfterHours
> 0) {
43 }, this._chargingStation
.stationInfo
.AutomaticTransactionGenerator
.stopAfterHours
* 3600 * 1000);
45 for (const connector
in this._chargingStation
.connectors
) {
46 if (Utils
.convertToInt(connector
) > 0) {
47 void this.startConnector(Utils
.convertToInt(connector
));
50 logger
.info(this._logPrefix() + ' ATG started and will stop in ' + Utils
.secondsToHHMMSS(this._chargingStation
.stationInfo
.AutomaticTransactionGenerator
.stopAfterHours
* 3600));
53 async stop(reason
: StopTransactionReason
= StopTransactionReason
.NONE
): Promise
<void> {
54 logger
.info(this._logPrefix() + ' ATG OVER => STOPPING ALL TRANSACTIONS');
55 for (const connector
in this._chargingStation
.connectors
) {
56 if (this._chargingStation
.getConnector(Utils
.convertToInt(connector
)).transactionStarted
) {
57 logger
.info(this._logPrefix(Utils
.convertToInt(connector
)) + ' ATG OVER. Stop transaction ' + this._chargingStation
.getConnector(Utils
.convertToInt(connector
)).transactionId
.toString());
58 await this._chargingStation
.sendStopTransaction(this._chargingStation
.getConnector(Utils
.convertToInt(connector
)).transactionId
, reason
);
61 this._timeToStop
= true;
64 async startConnector(connectorId
: number): Promise
<void> {
66 if (this._timeToStop
) {
67 logger
.error(this._logPrefix(connectorId
) + ' Entered in transaction loop while a request to stop it was made');
70 if (!this._chargingStation
._isRegistered()) {
71 logger
.error(this._logPrefix(connectorId
) + ' Entered in transaction loop while the charging station is not registered');
74 if (!this._chargingStation
._isChargingStationAvailable()) {
75 logger
.info(this._logPrefix(connectorId
) + ' Entered in transaction loop while the charging station is unavailable');
79 if (!this._chargingStation
._isConnectorAvailable(connectorId
)) {
80 logger
.info(`${this._logPrefix(connectorId)} Entered in transaction loop while the connector ${connectorId} is unavailable, stop it`);
83 const wait
= Utils
.getRandomInt(this._chargingStation
.stationInfo
.AutomaticTransactionGenerator
.maxDelayBetweenTwoTransactions
,
84 this._chargingStation
.stationInfo
.AutomaticTransactionGenerator
.minDelayBetweenTwoTransactions
) * 1000;
85 logger
.info(this._logPrefix(connectorId
) + ' wait for ' + Utils
.milliSecondsToHHMMSS(wait
));
86 await Utils
.sleep(wait
);
87 const start
= Math.random();
89 if (start
< this._chargingStation
.stationInfo
.AutomaticTransactionGenerator
.probabilityOfStart
) {
92 let startResponse
: StartTransactionResponse
| AuthorizeResponse
;
93 if (this._chargingStation
.getEnableStatistics()) {
94 const startTransaction
= performance
.timerify(this.startTransaction
);
95 this._performanceObserver
.observe({ entryTypes
: ['function'] });
96 startResponse
= await startTransaction(connectorId
, this);
98 startResponse
= await this.startTransaction(connectorId
, this);
100 if (startResponse
?.idTagInfo
?.status !== AuthorizationStatus
.ACCEPTED
) {
101 logger
.info(this._logPrefix(connectorId
) + ' transaction rejected');
102 await Utils
.sleep(Constants
.CHARGING_STATION_ATG_WAIT_TIME
);
104 // Wait until end of transaction
105 const waitTrxEnd
= Utils
.getRandomInt(this._chargingStation
.stationInfo
.AutomaticTransactionGenerator
.maxDuration
,
106 this._chargingStation
.stationInfo
.AutomaticTransactionGenerator
.minDuration
) * 1000;
107 logger
.info(this._logPrefix(connectorId
) + ' transaction ' + this._chargingStation
.getConnector(connectorId
).transactionId
.toString() + ' will stop in ' + Utils
.milliSecondsToHHMMSS(waitTrxEnd
));
108 await Utils
.sleep(waitTrxEnd
);
110 if (this._chargingStation
.getConnector(connectorId
)?.transactionStarted
) {
111 logger
.info(this._logPrefix(connectorId
) + ' stop transaction ' + this._chargingStation
.getConnector(connectorId
).transactionId
.toString());
112 if (this._chargingStation
.getEnableStatistics()) {
113 const stopTransaction
= performance
.timerify(this.stopTransaction
);
114 this._performanceObserver
.observe({ entryTypes
: ['function'] });
115 await stopTransaction(connectorId
, this);
117 await this.stopTransaction(connectorId
, this);
123 logger
.info(this._logPrefix(connectorId
) + ' transaction skipped ' + skip
.toString());
125 } while (!this._timeToStop
);
126 logger
.info(this._logPrefix(connectorId
) + ' ATG STOPPED on the connector');
129 // eslint-disable-next-line consistent-this
130 private async startTransaction(connectorId
: number, self: AutomaticTransactionGenerator
): Promise
<StartTransactionResponse
| AuthorizeResponse
> {
131 if (self._chargingStation
.hasAuthorizedTags()) {
132 const tagId
= self._chargingStation
.getRandomTagId();
133 if (self._chargingStation
.stationInfo
.AutomaticTransactionGenerator
.requireAuthorize
) {
135 const authorizeResponse
= await self._chargingStation
.sendAuthorize(tagId
);
136 if (authorizeResponse
?.idTagInfo
?.status === AuthorizationStatus
.ACCEPTED
) {
137 logger
.info(self._logPrefix(connectorId
) + ' start transaction for tagID ' + tagId
);
139 return await self._chargingStation
.sendStartTransaction(connectorId
, tagId
);
141 return authorizeResponse
;
143 logger
.info(self._logPrefix(connectorId
) + ' start transaction for tagID ' + tagId
);
145 return await self._chargingStation
.sendStartTransaction(connectorId
, tagId
);
147 logger
.info(self._logPrefix(connectorId
) + ' start transaction without a tagID');
148 return await self._chargingStation
.sendStartTransaction(connectorId
);
151 // eslint-disable-next-line consistent-this
152 private async stopTransaction(connectorId
: number, self: AutomaticTransactionGenerator
): Promise
<StopTransactionResponse
> {
153 return await self._chargingStation
.sendStopTransaction(self._chargingStation
.getConnector(connectorId
).transactionId
);