1 // Partial Copyright Jerome Benoit. 2021. All Rights Reserved.
3 import { AuthorizationStatus
, AuthorizeResponse
, StartTransactionResponse
, StopTransactionReason
, StopTransactionResponse
} from
'../types/ocpp/Transaction';
5 import ChargingStation from
'./ChargingStation';
6 import Constants from
'../utils/Constants';
7 import PerformanceStatistics from
'../performance/PerformanceStatistics';
8 import Utils from
'../utils/Utils';
9 import logger from
'../utils/Logger';
11 export default class AutomaticTransactionGenerator
{
12 public timeToStop
: boolean;
13 private chargingStation
: ChargingStation
;
15 constructor(chargingStation
: ChargingStation
) {
16 this.chargingStation
= chargingStation
;
17 this.timeToStop
= true;
20 public async start(): Promise
<void> {
21 this.timeToStop
= false;
22 if (this.chargingStation
.stationInfo
.AutomaticTransactionGenerator
.stopAfterHours
&&
23 this.chargingStation
.stationInfo
.AutomaticTransactionGenerator
.stopAfterHours
> 0) {
24 // eslint-disable-next-line @typescript-eslint/no-misused-promises
25 setTimeout(async (): Promise
<void> => {
27 }, this.chargingStation
.stationInfo
.AutomaticTransactionGenerator
.stopAfterHours
* 3600 * 1000);
29 for (const connector
in this.chargingStation
.connectors
) {
30 if (Utils
.convertToInt(connector
) > 0) {
31 await this.startConnector(Utils
.convertToInt(connector
));
34 logger
.info(this.logPrefix() + ' ATG started and will stop in ' + Utils
.secondsToHHMMSS(this.chargingStation
.stationInfo
.AutomaticTransactionGenerator
.stopAfterHours
* 3600));
37 public async stop(reason
: StopTransactionReason
= StopTransactionReason
.NONE
): Promise
<void> {
38 logger
.info(this.logPrefix() + ' ATG OVER => STOPPING ALL TRANSACTIONS');
39 for (const connector
in this.chargingStation
.connectors
) {
40 const transactionId
= this.chargingStation
.getConnector(Utils
.convertToInt(connector
)).transactionId
;
41 if (this.chargingStation
.getConnector(Utils
.convertToInt(connector
)).transactionStarted
) {
42 logger
.info(this.logPrefix(Utils
.convertToInt(connector
)) + ' ATG OVER. Stop transaction ' + transactionId
.toString());
43 await this.chargingStation
.ocppRequestService
.sendStopTransaction(transactionId
, this.chargingStation
.getEnergyActiveImportRegisterByTransactionId(transactionId
),
44 this.chargingStation
.getTransactionIdTag(transactionId
), reason
);
47 this.timeToStop
= true;
50 private async startConnector(connectorId
: number): Promise
<void> {
52 if (this.timeToStop
) {
53 logger
.error(this.logPrefix(connectorId
) + ' Entered in transaction loop while a request to stop it was made');
56 if (!this.chargingStation
.isRegistered()) {
57 logger
.error(this.logPrefix(connectorId
) + ' Entered in transaction loop while the charging station is not registered');
60 if (!this.chargingStation
.isChargingStationAvailable()) {
61 logger
.info(this.logPrefix(connectorId
) + ' Entered in transaction loop while the charging station is unavailable');
65 if (!this.chargingStation
.isConnectorAvailable(connectorId
)) {
66 logger
.info(`${this.logPrefix(connectorId)} Entered in transaction loop while the connector ${connectorId} is unavailable, stop it`);
69 if (!this.chargingStation
?.ocppRequestService
) {
70 logger
.info(`${this.logPrefix(connectorId)} Transaction loop waiting for charging station service to be initialized`);
72 await Utils
.sleep(Constants
.CHARGING_STATION_ATG_INITIALIZATION_TIME
);
73 } while (!this.chargingStation
?.ocppRequestService
);
75 const wait
= Utils
.getRandomInt(this.chargingStation
.stationInfo
.AutomaticTransactionGenerator
.maxDelayBetweenTwoTransactions
,
76 this.chargingStation
.stationInfo
.AutomaticTransactionGenerator
.minDelayBetweenTwoTransactions
) * 1000;
77 logger
.info(this.logPrefix(connectorId
) + ' wait for ' + Utils
.milliSecondsToHHMMSS(wait
));
78 await Utils
.sleep(wait
);
79 const start
= Math.random();
81 if (start
< this.chargingStation
.stationInfo
.AutomaticTransactionGenerator
.probabilityOfStart
) {
83 const startResponse
= await this.startTransaction(connectorId
);
84 if (startResponse
?.idTagInfo
?.status !== AuthorizationStatus
.ACCEPTED
) {
85 logger
.warn(this.logPrefix(connectorId
) + ' transaction rejected');
86 await Utils
.sleep(Constants
.CHARGING_STATION_ATG_WAIT_TIME
);
88 // Wait until end of transaction
89 const waitTrxEnd
= Utils
.getRandomInt(this.chargingStation
.stationInfo
.AutomaticTransactionGenerator
.maxDuration
,
90 this.chargingStation
.stationInfo
.AutomaticTransactionGenerator
.minDuration
) * 1000;
91 logger
.info(this.logPrefix(connectorId
) + ' transaction ' + this.chargingStation
.getConnector(connectorId
).transactionId
.toString() + ' will stop in ' + Utils
.milliSecondsToHHMMSS(waitTrxEnd
));
92 await Utils
.sleep(waitTrxEnd
);
94 if (this.chargingStation
.getConnector(connectorId
)?.transactionStarted
) {
95 logger
.info(this.logPrefix(connectorId
) + ' stop transaction ' + this.chargingStation
.getConnector(connectorId
).transactionId
.toString());
96 await this.stopTransaction(connectorId
);
101 logger
.info(this.logPrefix(connectorId
) + ' transaction skipped ' + skip
.toString());
103 } while (!this.timeToStop
);
104 logger
.info(this.logPrefix(connectorId
) + ' ATG STOPPED on the connector');
107 // eslint-disable-next-line consistent-this
108 private async startTransaction(connectorId
: number): Promise
<StartTransactionResponse
| AuthorizeResponse
> {
109 const measureId
= 'StartTransaction with ATG';
110 const beginId
= PerformanceStatistics
.beginMeasure(measureId
);
111 let startResponse
: StartTransactionResponse
;
112 if (this.chargingStation
.hasAuthorizedTags()) {
113 const tagId
= this.chargingStation
.getRandomTagId();
114 if (this.chargingStation
.getAutomaticTransactionGeneratorRequireAuthorize()) {
116 const authorizeResponse
= await this.chargingStation
.ocppRequestService
.sendAuthorize(connectorId
, tagId
);
117 if (authorizeResponse
?.idTagInfo
?.status === AuthorizationStatus
.ACCEPTED
) {
118 logger
.info(this.logPrefix(connectorId
) + ' start transaction for tagID ' + tagId
);
120 startResponse
= await this.chargingStation
.ocppRequestService
.sendStartTransaction(connectorId
, tagId
);
121 PerformanceStatistics
.endMeasure(measureId
, beginId
);
122 return startResponse
;
124 PerformanceStatistics
.endMeasure(measureId
, beginId
);
125 return authorizeResponse
;
127 logger
.info(this.logPrefix(connectorId
) + ' start transaction for tagID ' + tagId
);
129 startResponse
= await this.chargingStation
.ocppRequestService
.sendStartTransaction(connectorId
, tagId
);
130 PerformanceStatistics
.endMeasure(measureId
, beginId
);
131 return startResponse
;
133 logger
.info(this.logPrefix(connectorId
) + ' start transaction without a tagID');
134 startResponse
= await this.chargingStation
.ocppRequestService
.sendStartTransaction(connectorId
);
135 PerformanceStatistics
.endMeasure(measureId
, beginId
);
136 return startResponse
;
139 // eslint-disable-next-line consistent-this
140 private async stopTransaction(connectorId
: number): Promise
<StopTransactionResponse
> {
141 const measureId
= 'StopTransaction with ATG';
142 const beginId
= PerformanceStatistics
.beginMeasure(measureId
);
143 const transactionId
= this.chargingStation
.getConnector(connectorId
).transactionId
;
144 const stopResponse
= this.chargingStation
.ocppRequestService
.sendStopTransaction(transactionId
,
145 this.chargingStation
.getEnergyActiveImportRegisterByTransactionId(transactionId
), this.chargingStation
.getTransactionIdTag(transactionId
));
146 PerformanceStatistics
.endMeasure(measureId
, beginId
);
150 private logPrefix(connectorId
?: number): string {
152 return Utils
.logPrefix(' ' + this.chargingStation
.stationInfo
.chargingStationId
+ ' | ATG on connector #' + connectorId
.toString() + ':');
154 return Utils
.logPrefix(' ' + this.chargingStation
.stationInfo
.chargingStationId
+ ' | ATG:');