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