1 import { AuthorizationStatus
, AuthorizeResponse
, StartTransactionResponse
, StopTransactionReason
, StopTransactionResponse
} from
'../types/ocpp/Transaction';
3 import ChargingStation from
'./ChargingStation';
4 import Constants from
'../utils/Constants';
5 import { PerformanceObserver
} from
'perf_hooks';
6 import PerformanceStatistics from
'../utils/PerformanceStatistics';
7 import Utils from
'../utils/Utils';
8 import logger from
'../utils/Logger';
10 export default class AutomaticTransactionGenerator
{
11 public timeToStop
: boolean;
12 private chargingStation
: ChargingStation
;
13 private performanceObserver
!: PerformanceObserver
;
15 constructor(chargingStation
: ChargingStation
) {
16 this.chargingStation
= chargingStation
;
17 this.timeToStop
= true;
18 if (this.chargingStation
.getEnableStatistics()) {
19 this.performanceObserver
= PerformanceStatistics
.initPerformanceObserver(Constants
.ENTITY_AUTOMATIC_TRANSACTION_GENERATOR
, this.chargingStation
.performanceStatistics
,
20 this.performanceObserver
);
24 public async start(): Promise
<void> {
25 this.timeToStop
= false;
26 if (this.chargingStation
.stationInfo
.AutomaticTransactionGenerator
.stopAfterHours
&&
27 this.chargingStation
.stationInfo
.AutomaticTransactionGenerator
.stopAfterHours
> 0) {
28 // eslint-disable-next-line @typescript-eslint/no-misused-promises
29 setTimeout(async (): Promise
<void> => {
31 }, this.chargingStation
.stationInfo
.AutomaticTransactionGenerator
.stopAfterHours
* 3600 * 1000);
33 for (const connector
in this.chargingStation
.connectors
) {
34 if (Utils
.convertToInt(connector
) > 0) {
35 await this.startConnector(Utils
.convertToInt(connector
));
38 logger
.info(this.logPrefix() + ' ATG started and will stop in ' + Utils
.secondsToHHMMSS(this.chargingStation
.stationInfo
.AutomaticTransactionGenerator
.stopAfterHours
* 3600));
41 public async stop(reason
: StopTransactionReason
= StopTransactionReason
.NONE
): Promise
<void> {
42 logger
.info(this.logPrefix() + ' ATG OVER => STOPPING ALL TRANSACTIONS');
43 for (const connector
in this.chargingStation
.connectors
) {
44 const transactionId
= this.chargingStation
.getConnector(Utils
.convertToInt(connector
)).transactionId
;
45 if (this.chargingStation
.getConnector(Utils
.convertToInt(connector
)).transactionStarted
) {
46 logger
.info(this.logPrefix(Utils
.convertToInt(connector
)) + ' ATG OVER. Stop transaction ' + transactionId
.toString());
47 await this.chargingStation
.ocppRequestService
.sendStopTransaction(transactionId
, this.chargingStation
.getEnergyActiveImportRegisterByTransactionId(transactionId
),
48 this.chargingStation
.getTransactionIdTag(transactionId
), reason
);
51 this.timeToStop
= true;
54 private async startConnector(connectorId
: number): Promise
<void> {
56 if (this.timeToStop
) {
57 logger
.error(this.logPrefix(connectorId
) + ' Entered in transaction loop while a request to stop it was made');
60 if (!this.chargingStation
.isRegistered()) {
61 logger
.error(this.logPrefix(connectorId
) + ' Entered in transaction loop while the charging station is not registered');
64 if (!this.chargingStation
.isChargingStationAvailable()) {
65 logger
.info(this.logPrefix(connectorId
) + ' Entered in transaction loop while the charging station is unavailable');
69 if (!this.chargingStation
.isConnectorAvailable(connectorId
)) {
70 logger
.info(`${this.logPrefix(connectorId)} Entered in transaction loop while the connector ${connectorId} is unavailable, stop it`);
73 if (!this.chargingStation
?.ocppRequestService
) {
74 logger
.info(`${this.logPrefix(connectorId)} Transaction loop waiting for charging station service to be initialized`);
76 await Utils
.sleep(Constants
.CHARGING_STATION_ATG_INITIALIZATION_TIME
);
77 } while (!this.chargingStation
?.ocppRequestService
);
79 const wait
= Utils
.getRandomInt(this.chargingStation
.stationInfo
.AutomaticTransactionGenerator
.maxDelayBetweenTwoTransactions
,
80 this.chargingStation
.stationInfo
.AutomaticTransactionGenerator
.minDelayBetweenTwoTransactions
) * 1000;
81 logger
.info(this.logPrefix(connectorId
) + ' wait for ' + Utils
.milliSecondsToHHMMSS(wait
));
82 await Utils
.sleep(wait
);
83 const start
= Math.random();
85 if (start
< this.chargingStation
.stationInfo
.AutomaticTransactionGenerator
.probabilityOfStart
) {
87 let startTransaction
: (connectorId
: number, self: AutomaticTransactionGenerator
) => Promise
<StartTransactionResponse
| AuthorizeResponse
>;
88 if (this.chargingStation
.getEnableStatistics()) {
89 startTransaction
= PerformanceStatistics
.timedFunction(this.startTransaction
.bind(this), this.performanceObserver
);
91 startTransaction
= this.startTransaction
.bind(this);
93 const startResponse
= await startTransaction(connectorId
, this);
94 if (startResponse
?.idTagInfo
?.status !== AuthorizationStatus
.ACCEPTED
) {
95 logger
.warn(this.logPrefix(connectorId
) + ' transaction rejected');
96 await Utils
.sleep(Constants
.CHARGING_STATION_ATG_WAIT_TIME
);
98 // Wait until end of transaction
99 const waitTrxEnd
= Utils
.getRandomInt(this.chargingStation
.stationInfo
.AutomaticTransactionGenerator
.maxDuration
,
100 this.chargingStation
.stationInfo
.AutomaticTransactionGenerator
.minDuration
) * 1000;
101 logger
.info(this.logPrefix(connectorId
) + ' transaction ' + this.chargingStation
.getConnector(connectorId
).transactionId
.toString() + ' will stop in ' + Utils
.milliSecondsToHHMMSS(waitTrxEnd
));
102 await Utils
.sleep(waitTrxEnd
);
104 if (this.chargingStation
.getConnector(connectorId
)?.transactionStarted
) {
105 logger
.info(this.logPrefix(connectorId
) + ' stop transaction ' + this.chargingStation
.getConnector(connectorId
).transactionId
.toString());
106 let stopTransaction
: (connectorId
: number, self: AutomaticTransactionGenerator
) => Promise
<StopTransactionResponse
>;
107 if (this.chargingStation
.getEnableStatistics()) {
108 stopTransaction
= PerformanceStatistics
.timedFunction(this.stopTransaction
.bind(this), this.performanceObserver
);
110 stopTransaction
= this.stopTransaction
.bind(this);
112 await stopTransaction(connectorId
, this);
117 logger
.info(this.logPrefix(connectorId
) + ' transaction skipped ' + skip
.toString());
119 } while (!this.timeToStop
);
120 logger
.info(this.logPrefix(connectorId
) + ' ATG STOPPED on the connector');
123 // eslint-disable-next-line consistent-this
124 private async startTransaction(connectorId
: number, self: AutomaticTransactionGenerator
): Promise
<StartTransactionResponse
| AuthorizeResponse
> {
125 if (self.chargingStation
.hasAuthorizedTags()) {
126 const tagId
= self.chargingStation
.getRandomTagId();
127 if (self.chargingStation
.getAutomaticTransactionGeneratorRequireAuthorize()) {
129 const authorizeResponse
= await self.chargingStation
.ocppRequestService
.sendAuthorize(connectorId
, tagId
);
130 if (authorizeResponse
?.idTagInfo
?.status === AuthorizationStatus
.ACCEPTED
) {
131 logger
.info(self.logPrefix(connectorId
) + ' start transaction for tagID ' + tagId
);
133 return self.chargingStation
.ocppRequestService
.sendStartTransaction(connectorId
, tagId
);
135 return authorizeResponse
;
137 logger
.info(self.logPrefix(connectorId
) + ' start transaction for tagID ' + tagId
);
139 return self.chargingStation
.ocppRequestService
.sendStartTransaction(connectorId
, tagId
);
141 logger
.info(self.logPrefix(connectorId
) + ' start transaction without a tagID');
142 return self.chargingStation
.ocppRequestService
.sendStartTransaction(connectorId
);
145 // eslint-disable-next-line consistent-this
146 private async stopTransaction(connectorId
: number, self: AutomaticTransactionGenerator
): Promise
<StopTransactionResponse
> {
147 const transactionId
= self.chargingStation
.getConnector(connectorId
).transactionId
;
148 return self.chargingStation
.ocppRequestService
.sendStopTransaction(transactionId
, self.chargingStation
.getEnergyActiveImportRegisterByTransactionId(transactionId
),
149 self.chargingStation
.getTransactionIdTag(transactionId
));
152 private logPrefix(connectorId
?: number): string {
154 return Utils
.logPrefix(' ' + this.chargingStation
.stationInfo
.chargingStationId
+ ' | ATG on connector #' + connectorId
.toString() + ':');
156 return Utils
.logPrefix(' ' + this.chargingStation
.stationInfo
.chargingStationId
+ ' | ATG:');