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