Force WS connection opened closing at connection retries
[e-mobility-charging-stations-simulator.git] / src / charging-station / AutomaticTransactionGenerator.ts
CommitLineData
c8eeb62b
JB
1// Partial Copyright Jerome Benoit. 2021. All Rights Reserved.
2
c0560973 3import { AuthorizationStatus, AuthorizeResponse, StartTransactionResponse, StopTransactionReason, StopTransactionResponse } from '../types/ocpp/Transaction';
6af9012e
JB
4
5import ChargingStation from './ChargingStation';
6import Constants from '../utils/Constants';
a6b3c6c3 7import PerformanceStatistics from '../performance/PerformanceStatistics';
6af9012e
JB
8import Utils from '../utils/Utils';
9import logger from '../utils/Logger';
10
11export default class AutomaticTransactionGenerator {
ad2f27c3 12 public timeToStop: boolean;
7d75bee1
JB
13 private startDate!: Date;
14 private stopDate!: Date;
ad2f27c3 15 private chargingStation: ChargingStation;
6af9012e
JB
16
17 constructor(chargingStation: ChargingStation) {
ad2f27c3
JB
18 this.chargingStation = chargingStation;
19 this.timeToStop = true;
6af9012e
JB
20 }
21
7d75bee1
JB
22 public start(): void {
23 this.startDate = new Date();
24 this.stopDate = new Date(this.startDate.getTime() + (this.chargingStation.stationInfo?.AutomaticTransactionGenerator?.stopAfterHours ?? Constants.CHARGING_STATION_ATG_DEFAULT_STOP_AFTER_HOURS) * 3600 * 1000);
ad2f27c3 25 this.timeToStop = false;
ad2f27c3 26 for (const connector in this.chargingStation.connectors) {
6af9012e 27 if (Utils.convertToInt(connector) > 0) {
7d75bee1
JB
28 // Avoid hogging the event loop with a busy loop
29 setImmediate(() => {
30 this.startOnConnector(Utils.convertToInt(connector)).catch(() => { /* This is intentional */ });
31 });
6af9012e
JB
32 }
33 }
d7d1db72 34 logger.info(this.logPrefix() + ' started and will run for ' + Utils.formatDurationMilliSeconds(this.stopDate.getTime() - this.startDate.getTime()));
6af9012e
JB
35 }
36
c0560973 37 public async stop(reason: StopTransactionReason = StopTransactionReason.NONE): Promise<void> {
7d75bee1 38 logger.info(this.logPrefix() + ' over. Stopping all transactions');
ad2f27c3 39 for (const connector in this.chargingStation.connectors) {
c0560973 40 const transactionId = this.chargingStation.getConnector(Utils.convertToInt(connector)).transactionId;
ad2f27c3 41 if (this.chargingStation.getConnector(Utils.convertToInt(connector)).transactionStarted) {
7d75bee1 42 logger.info(this.logPrefix(Utils.convertToInt(connector)) + ' over. Stop transaction ' + transactionId.toString());
6ed92bc1 43 await this.chargingStation.ocppRequestService.sendStopTransaction(transactionId, this.chargingStation.getEnergyActiveImportRegisterByTransactionId(transactionId),
035742f7 44 this.chargingStation.getTransactionIdTag(transactionId), reason);
6af9012e
JB
45 }
46 }
ad2f27c3 47 this.timeToStop = true;
6af9012e
JB
48 }
49
7d75bee1
JB
50 private async startOnConnector(connectorId: number): Promise<void> {
51 logger.info(this.logPrefix(connectorId) + ' started on connector');
52 let transactionSkip = 0;
53 let totalTransactionSkip = 0;
54 while (!this.timeToStop) {
55 if ((new Date()) > this.stopDate) {
56 await this.stop();
17991e8c
JB
57 break;
58 }
c0560973
JB
59 if (!this.chargingStation.isRegistered()) {
60 logger.error(this.logPrefix(connectorId) + ' Entered in transaction loop while the charging station is not registered');
17991e8c
JB
61 break;
62 }
c0560973
JB
63 if (!this.chargingStation.isChargingStationAvailable()) {
64 logger.info(this.logPrefix(connectorId) + ' Entered in transaction loop while the charging station is unavailable');
ab5f4b03
JB
65 await this.stop();
66 break;
67 }
c0560973
JB
68 if (!this.chargingStation.isConnectorAvailable(connectorId)) {
69 logger.info(`${this.logPrefix(connectorId)} Entered in transaction loop while the connector ${connectorId} is unavailable, stop it`);
17991e8c
JB
70 break;
71 }
c0560973
JB
72 if (!this.chargingStation?.ocppRequestService) {
73 logger.info(`${this.logPrefix(connectorId)} Transaction loop waiting for charging station service to be initialized`);
74 do {
a4cc42ea 75 await Utils.sleep(Constants.CHARGING_STATION_ATG_INITIALIZATION_TIME);
c0560973
JB
76 } while (!this.chargingStation?.ocppRequestService);
77 }
ad2f27c3
JB
78 const wait = Utils.getRandomInt(this.chargingStation.stationInfo.AutomaticTransactionGenerator.maxDelayBetweenTwoTransactions,
79 this.chargingStation.stationInfo.AutomaticTransactionGenerator.minDelayBetweenTwoTransactions) * 1000;
d7d1db72 80 logger.info(this.logPrefix(connectorId) + ' waiting for ' + Utils.formatDurationMilliSeconds(wait));
6af9012e 81 await Utils.sleep(wait);
c37528f1 82 const start = Utils.secureRandom();
ad2f27c3 83 if (start < this.chargingStation.stationInfo.AutomaticTransactionGenerator.probabilityOfStart) {
7d75bee1 84 transactionSkip = 0;
6af9012e 85 // Start transaction
aef1b33a 86 const startResponse = await this.startTransaction(connectorId);
ef6076c1 87 if (startResponse?.idTagInfo?.status !== AuthorizationStatus.ACCEPTED) {
54b1efe0 88 logger.warn(this.logPrefix(connectorId) + ' transaction rejected');
6af9012e
JB
89 await Utils.sleep(Constants.CHARGING_STATION_ATG_WAIT_TIME);
90 } else {
91 // Wait until end of transaction
ad2f27c3
JB
92 const waitTrxEnd = Utils.getRandomInt(this.chargingStation.stationInfo.AutomaticTransactionGenerator.maxDuration,
93 this.chargingStation.stationInfo.AutomaticTransactionGenerator.minDuration) * 1000;
d7d1db72 94 logger.info(this.logPrefix(connectorId) + ' transaction ' + this.chargingStation.getConnector(connectorId).transactionId.toString() + ' will stop in ' + Utils.formatDurationMilliSeconds(waitTrxEnd));
6af9012e
JB
95 await Utils.sleep(waitTrxEnd);
96 // Stop transaction
ad2f27c3 97 if (this.chargingStation.getConnector(connectorId)?.transactionStarted) {
c0560973 98 logger.info(this.logPrefix(connectorId) + ' stop transaction ' + this.chargingStation.getConnector(connectorId).transactionId.toString());
aef1b33a 99 await this.stopTransaction(connectorId);
6af9012e
JB
100 }
101 }
102 } else {
7d75bee1
JB
103 transactionSkip++;
104 totalTransactionSkip++;
105 logger.info(this.logPrefix(connectorId) + ' skipped transaction ' + transactionSkip.toString() + '/' + totalTransactionSkip.toString());
6af9012e 106 }
7d75bee1
JB
107 }
108 logger.info(this.logPrefix(connectorId) + ' stopped on connector');
6af9012e
JB
109 }
110
aef1b33a
JB
111 private async startTransaction(connectorId: number): Promise<StartTransactionResponse | AuthorizeResponse> {
112 const measureId = 'StartTransaction with ATG';
113 const beginId = PerformanceStatistics.beginMeasure(measureId);
114 let startResponse: StartTransactionResponse;
115 if (this.chargingStation.hasAuthorizedTags()) {
116 const tagId = this.chargingStation.getRandomTagId();
117 if (this.chargingStation.getAutomaticTransactionGeneratorRequireAuthorize()) {
5fdab605 118 // Authorize tagId
aef1b33a 119 const authorizeResponse = await this.chargingStation.ocppRequestService.sendAuthorize(connectorId, tagId);
5fdab605 120 if (authorizeResponse?.idTagInfo?.status === AuthorizationStatus.ACCEPTED) {
aef1b33a 121 logger.info(this.logPrefix(connectorId) + ' start transaction for tagID ' + tagId);
5fdab605 122 // Start transaction
aef1b33a
JB
123 startResponse = await this.chargingStation.ocppRequestService.sendStartTransaction(connectorId, tagId);
124 PerformanceStatistics.endMeasure(measureId, beginId);
125 return startResponse;
5fdab605 126 }
aef1b33a 127 PerformanceStatistics.endMeasure(measureId, beginId);
4faad557 128 return authorizeResponse;
ef6076c1 129 }
aef1b33a 130 logger.info(this.logPrefix(connectorId) + ' start transaction for tagID ' + tagId);
5fdab605 131 // Start transaction
aef1b33a
JB
132 startResponse = await this.chargingStation.ocppRequestService.sendStartTransaction(connectorId, tagId);
133 PerformanceStatistics.endMeasure(measureId, beginId);
134 return startResponse;
6af9012e 135 }
aef1b33a
JB
136 logger.info(this.logPrefix(connectorId) + ' start transaction without a tagID');
137 startResponse = await this.chargingStation.ocppRequestService.sendStartTransaction(connectorId);
138 PerformanceStatistics.endMeasure(measureId, beginId);
139 return startResponse;
6af9012e
JB
140 }
141
aef1b33a
JB
142 private async stopTransaction(connectorId: number): Promise<StopTransactionResponse> {
143 const measureId = 'StopTransaction with ATG';
144 const beginId = PerformanceStatistics.beginMeasure(measureId);
145 const transactionId = this.chargingStation.getConnector(connectorId).transactionId;
146 const stopResponse = this.chargingStation.ocppRequestService.sendStopTransaction(transactionId,
147 this.chargingStation.getEnergyActiveImportRegisterByTransactionId(transactionId), this.chargingStation.getTransactionIdTag(transactionId));
148 PerformanceStatistics.endMeasure(measureId, beginId);
149 return stopResponse;
c0560973
JB
150 }
151
6e0964c8 152 private logPrefix(connectorId?: number): string {
c0560973 153 if (connectorId) {
54b1efe0 154 return Utils.logPrefix(' ' + this.chargingStation.stationInfo.chargingStationId + ' | ATG on connector #' + connectorId.toString() + ':');
c0560973 155 }
54b1efe0 156 return Utils.logPrefix(' ' + this.chargingStation.stationInfo.chargingStationId + ' | ATG:');
6af9012e
JB
157 }
158}