Add 95th percentile to performance statistics
[e-mobility-charging-stations-simulator.git] / src / charging-station / AutomaticTransactionGenerator.ts
1 import { AuthorizationStatus, AuthorizeResponse, StartTransactionResponse, StopTransactionReason, StopTransactionResponse } from '../types/ocpp/Transaction';
2
3 import ChargingStation from './ChargingStation';
4 import Constants from '../utils/Constants';
5 import PerformanceStatistics from '../utils/PerformanceStatistics';
6 import Utils from '../utils/Utils';
7 import logger from '../utils/Logger';
8
9 export default class AutomaticTransactionGenerator {
10 public timeToStop: boolean;
11 private chargingStation: ChargingStation;
12
13 constructor(chargingStation: ChargingStation) {
14 this.chargingStation = chargingStation;
15 this.timeToStop = true;
16 }
17
18 public async start(): Promise<void> {
19 this.timeToStop = false;
20 if (this.chargingStation.stationInfo.AutomaticTransactionGenerator.stopAfterHours &&
21 this.chargingStation.stationInfo.AutomaticTransactionGenerator.stopAfterHours > 0) {
22 // eslint-disable-next-line @typescript-eslint/no-misused-promises
23 setTimeout(async (): Promise<void> => {
24 await this.stop();
25 }, this.chargingStation.stationInfo.AutomaticTransactionGenerator.stopAfterHours * 3600 * 1000);
26 }
27 for (const connector in this.chargingStation.connectors) {
28 if (Utils.convertToInt(connector) > 0) {
29 await this.startConnector(Utils.convertToInt(connector));
30 }
31 }
32 logger.info(this.logPrefix() + ' ATG started and will stop in ' + Utils.secondsToHHMMSS(this.chargingStation.stationInfo.AutomaticTransactionGenerator.stopAfterHours * 3600));
33 }
34
35 public async stop(reason: StopTransactionReason = StopTransactionReason.NONE): Promise<void> {
36 logger.info(this.logPrefix() + ' ATG OVER => STOPPING ALL TRANSACTIONS');
37 for (const connector in this.chargingStation.connectors) {
38 const transactionId = this.chargingStation.getConnector(Utils.convertToInt(connector)).transactionId;
39 if (this.chargingStation.getConnector(Utils.convertToInt(connector)).transactionStarted) {
40 logger.info(this.logPrefix(Utils.convertToInt(connector)) + ' ATG OVER. Stop transaction ' + transactionId.toString());
41 await this.chargingStation.ocppRequestService.sendStopTransaction(transactionId, this.chargingStation.getEnergyActiveImportRegisterByTransactionId(transactionId),
42 this.chargingStation.getTransactionIdTag(transactionId), reason);
43 }
44 }
45 this.timeToStop = true;
46 }
47
48 private async startConnector(connectorId: number): Promise<void> {
49 do {
50 if (this.timeToStop) {
51 logger.error(this.logPrefix(connectorId) + ' Entered in transaction loop while a request to stop it was made');
52 break;
53 }
54 if (!this.chargingStation.isRegistered()) {
55 logger.error(this.logPrefix(connectorId) + ' Entered in transaction loop while the charging station is not registered');
56 break;
57 }
58 if (!this.chargingStation.isChargingStationAvailable()) {
59 logger.info(this.logPrefix(connectorId) + ' Entered in transaction loop while the charging station is unavailable');
60 await this.stop();
61 break;
62 }
63 if (!this.chargingStation.isConnectorAvailable(connectorId)) {
64 logger.info(`${this.logPrefix(connectorId)} Entered in transaction loop while the connector ${connectorId} is unavailable, stop it`);
65 break;
66 }
67 if (!this.chargingStation?.ocppRequestService) {
68 logger.info(`${this.logPrefix(connectorId)} Transaction loop waiting for charging station service to be initialized`);
69 do {
70 await Utils.sleep(Constants.CHARGING_STATION_ATG_INITIALIZATION_TIME);
71 } while (!this.chargingStation?.ocppRequestService);
72 }
73 const wait = Utils.getRandomInt(this.chargingStation.stationInfo.AutomaticTransactionGenerator.maxDelayBetweenTwoTransactions,
74 this.chargingStation.stationInfo.AutomaticTransactionGenerator.minDelayBetweenTwoTransactions) * 1000;
75 logger.info(this.logPrefix(connectorId) + ' wait for ' + Utils.milliSecondsToHHMMSS(wait));
76 await Utils.sleep(wait);
77 const start = Math.random();
78 let skip = 0;
79 if (start < this.chargingStation.stationInfo.AutomaticTransactionGenerator.probabilityOfStart) {
80 // Start transaction
81 const startResponse = await this.startTransaction(connectorId);
82 if (startResponse?.idTagInfo?.status !== AuthorizationStatus.ACCEPTED) {
83 logger.warn(this.logPrefix(connectorId) + ' transaction rejected');
84 await Utils.sleep(Constants.CHARGING_STATION_ATG_WAIT_TIME);
85 } else {
86 // Wait until end of transaction
87 const waitTrxEnd = Utils.getRandomInt(this.chargingStation.stationInfo.AutomaticTransactionGenerator.maxDuration,
88 this.chargingStation.stationInfo.AutomaticTransactionGenerator.minDuration) * 1000;
89 logger.info(this.logPrefix(connectorId) + ' transaction ' + this.chargingStation.getConnector(connectorId).transactionId.toString() + ' will stop in ' + Utils.milliSecondsToHHMMSS(waitTrxEnd));
90 await Utils.sleep(waitTrxEnd);
91 // Stop transaction
92 if (this.chargingStation.getConnector(connectorId)?.transactionStarted) {
93 logger.info(this.logPrefix(connectorId) + ' stop transaction ' + this.chargingStation.getConnector(connectorId).transactionId.toString());
94 await this.stopTransaction(connectorId);
95 }
96 }
97 } else {
98 skip++;
99 logger.info(this.logPrefix(connectorId) + ' transaction skipped ' + skip.toString());
100 }
101 } while (!this.timeToStop);
102 logger.info(this.logPrefix(connectorId) + ' ATG STOPPED on the connector');
103 }
104
105 // eslint-disable-next-line consistent-this
106 private async startTransaction(connectorId: number): Promise<StartTransactionResponse | AuthorizeResponse> {
107 const measureId = 'StartTransaction with ATG';
108 const beginId = PerformanceStatistics.beginMeasure(measureId);
109 let startResponse: StartTransactionResponse;
110 if (this.chargingStation.hasAuthorizedTags()) {
111 const tagId = this.chargingStation.getRandomTagId();
112 if (this.chargingStation.getAutomaticTransactionGeneratorRequireAuthorize()) {
113 // Authorize tagId
114 const authorizeResponse = await this.chargingStation.ocppRequestService.sendAuthorize(connectorId, tagId);
115 if (authorizeResponse?.idTagInfo?.status === AuthorizationStatus.ACCEPTED) {
116 logger.info(this.logPrefix(connectorId) + ' start transaction for tagID ' + tagId);
117 // Start transaction
118 startResponse = await this.chargingStation.ocppRequestService.sendStartTransaction(connectorId, tagId);
119 PerformanceStatistics.endMeasure(measureId, beginId);
120 return startResponse;
121 }
122 PerformanceStatistics.endMeasure(measureId, beginId);
123 return authorizeResponse;
124 }
125 logger.info(this.logPrefix(connectorId) + ' start transaction for tagID ' + tagId);
126 // Start transaction
127 startResponse = await this.chargingStation.ocppRequestService.sendStartTransaction(connectorId, tagId);
128 PerformanceStatistics.endMeasure(measureId, beginId);
129 return startResponse;
130 }
131 logger.info(this.logPrefix(connectorId) + ' start transaction without a tagID');
132 startResponse = await this.chargingStation.ocppRequestService.sendStartTransaction(connectorId);
133 PerformanceStatistics.endMeasure(measureId, beginId);
134 return startResponse;
135 }
136
137 // eslint-disable-next-line consistent-this
138 private async stopTransaction(connectorId: number): Promise<StopTransactionResponse> {
139 const measureId = 'StopTransaction with ATG';
140 const beginId = PerformanceStatistics.beginMeasure(measureId);
141 const transactionId = this.chargingStation.getConnector(connectorId).transactionId;
142 const stopResponse = this.chargingStation.ocppRequestService.sendStopTransaction(transactionId,
143 this.chargingStation.getEnergyActiveImportRegisterByTransactionId(transactionId), this.chargingStation.getTransactionIdTag(transactionId));
144 PerformanceStatistics.endMeasure(measureId, beginId);
145 return stopResponse;
146 }
147
148 private logPrefix(connectorId?: number): string {
149 if (connectorId) {
150 return Utils.logPrefix(' ' + this.chargingStation.stationInfo.chargingStationId + ' | ATG on connector #' + connectorId.toString() + ':');
151 }
152 return Utils.logPrefix(' ' + this.chargingStation.stationInfo.chargingStationId + ' | ATG:');
153 }
154 }