Improve OCPP error handling, fix performance storage default file path
[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
JB
12 public timeToStop: boolean;
13 private chargingStation: ChargingStation;
6af9012e
JB
14
15 constructor(chargingStation: ChargingStation) {
ad2f27c3
JB
16 this.chargingStation = chargingStation;
17 this.timeToStop = true;
6af9012e
JB
18 }
19
e268356b 20 public async start(): Promise<void> {
ad2f27c3
JB
21 this.timeToStop = false;
22 if (this.chargingStation.stationInfo.AutomaticTransactionGenerator.stopAfterHours &&
23 this.chargingStation.stationInfo.AutomaticTransactionGenerator.stopAfterHours > 0) {
71623267 24 // eslint-disable-next-line @typescript-eslint/no-misused-promises
e268356b
JB
25 setTimeout(async (): Promise<void> => {
26 await this.stop();
ad2f27c3 27 }, this.chargingStation.stationInfo.AutomaticTransactionGenerator.stopAfterHours * 3600 * 1000);
6af9012e 28 }
ad2f27c3 29 for (const connector in this.chargingStation.connectors) {
6af9012e 30 if (Utils.convertToInt(connector) > 0) {
e268356b 31 await this.startConnector(Utils.convertToInt(connector));
6af9012e
JB
32 }
33 }
c0560973 34 logger.info(this.logPrefix() + ' ATG started and will stop in ' + Utils.secondsToHHMMSS(this.chargingStation.stationInfo.AutomaticTransactionGenerator.stopAfterHours * 3600));
6af9012e
JB
35 }
36
c0560973
JB
37 public async stop(reason: StopTransactionReason = StopTransactionReason.NONE): Promise<void> {
38 logger.info(this.logPrefix() + ' ATG 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) {
c0560973 42 logger.info(this.logPrefix(Utils.convertToInt(connector)) + ' ATG 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
a1256107 50 private async startConnector(connectorId: number): Promise<void> {
6af9012e 51 do {
ad2f27c3 52 if (this.timeToStop) {
c0560973 53 logger.error(this.logPrefix(connectorId) + ' Entered in transaction loop while a request to stop it was made');
17991e8c
JB
54 break;
55 }
c0560973
JB
56 if (!this.chargingStation.isRegistered()) {
57 logger.error(this.logPrefix(connectorId) + ' Entered in transaction loop while the charging station is not registered');
17991e8c
JB
58 break;
59 }
c0560973
JB
60 if (!this.chargingStation.isChargingStationAvailable()) {
61 logger.info(this.logPrefix(connectorId) + ' Entered in transaction loop while the charging station is unavailable');
ab5f4b03
JB
62 await this.stop();
63 break;
64 }
c0560973
JB
65 if (!this.chargingStation.isConnectorAvailable(connectorId)) {
66 logger.info(`${this.logPrefix(connectorId)} Entered in transaction loop while the connector ${connectorId} is unavailable, stop it`);
17991e8c
JB
67 break;
68 }
c0560973
JB
69 if (!this.chargingStation?.ocppRequestService) {
70 logger.info(`${this.logPrefix(connectorId)} Transaction loop waiting for charging station service to be initialized`);
71 do {
a4cc42ea 72 await Utils.sleep(Constants.CHARGING_STATION_ATG_INITIALIZATION_TIME);
c0560973
JB
73 } while (!this.chargingStation?.ocppRequestService);
74 }
ad2f27c3
JB
75 const wait = Utils.getRandomInt(this.chargingStation.stationInfo.AutomaticTransactionGenerator.maxDelayBetweenTwoTransactions,
76 this.chargingStation.stationInfo.AutomaticTransactionGenerator.minDelayBetweenTwoTransactions) * 1000;
c0560973 77 logger.info(this.logPrefix(connectorId) + ' wait for ' + Utils.milliSecondsToHHMMSS(wait));
6af9012e 78 await Utils.sleep(wait);
6af9012e
JB
79 const start = Math.random();
80 let skip = 0;
ad2f27c3 81 if (start < this.chargingStation.stationInfo.AutomaticTransactionGenerator.probabilityOfStart) {
6af9012e 82 // Start transaction
aef1b33a 83 const startResponse = await this.startTransaction(connectorId);
ef6076c1 84 if (startResponse?.idTagInfo?.status !== AuthorizationStatus.ACCEPTED) {
54b1efe0 85 logger.warn(this.logPrefix(connectorId) + ' transaction rejected');
6af9012e
JB
86 await Utils.sleep(Constants.CHARGING_STATION_ATG_WAIT_TIME);
87 } else {
88 // Wait until end of transaction
ad2f27c3
JB
89 const waitTrxEnd = Utils.getRandomInt(this.chargingStation.stationInfo.AutomaticTransactionGenerator.maxDuration,
90 this.chargingStation.stationInfo.AutomaticTransactionGenerator.minDuration) * 1000;
c0560973 91 logger.info(this.logPrefix(connectorId) + ' transaction ' + this.chargingStation.getConnector(connectorId).transactionId.toString() + ' will stop in ' + Utils.milliSecondsToHHMMSS(waitTrxEnd));
6af9012e
JB
92 await Utils.sleep(waitTrxEnd);
93 // Stop transaction
ad2f27c3 94 if (this.chargingStation.getConnector(connectorId)?.transactionStarted) {
c0560973 95 logger.info(this.logPrefix(connectorId) + ' stop transaction ' + this.chargingStation.getConnector(connectorId).transactionId.toString());
aef1b33a 96 await this.stopTransaction(connectorId);
6af9012e
JB
97 }
98 }
99 } else {
100 skip++;
c0560973 101 logger.info(this.logPrefix(connectorId) + ' transaction skipped ' + skip.toString());
6af9012e 102 }
ad2f27c3 103 } while (!this.timeToStop);
c0560973 104 logger.info(this.logPrefix(connectorId) + ' ATG STOPPED on the connector');
6af9012e
JB
105 }
106
65c5527e 107 // eslint-disable-next-line consistent-this
aef1b33a
JB
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()) {
5fdab605 115 // Authorize tagId
aef1b33a 116 const authorizeResponse = await this.chargingStation.ocppRequestService.sendAuthorize(connectorId, tagId);
5fdab605 117 if (authorizeResponse?.idTagInfo?.status === AuthorizationStatus.ACCEPTED) {
aef1b33a 118 logger.info(this.logPrefix(connectorId) + ' start transaction for tagID ' + tagId);
5fdab605 119 // Start transaction
aef1b33a
JB
120 startResponse = await this.chargingStation.ocppRequestService.sendStartTransaction(connectorId, tagId);
121 PerformanceStatistics.endMeasure(measureId, beginId);
122 return startResponse;
5fdab605 123 }
aef1b33a 124 PerformanceStatistics.endMeasure(measureId, beginId);
4faad557 125 return authorizeResponse;
ef6076c1 126 }
aef1b33a 127 logger.info(this.logPrefix(connectorId) + ' start transaction for tagID ' + tagId);
5fdab605 128 // Start transaction
aef1b33a
JB
129 startResponse = await this.chargingStation.ocppRequestService.sendStartTransaction(connectorId, tagId);
130 PerformanceStatistics.endMeasure(measureId, beginId);
131 return startResponse;
6af9012e 132 }
aef1b33a
JB
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;
6af9012e
JB
137 }
138
65c5527e 139 // eslint-disable-next-line consistent-this
aef1b33a
JB
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);
147 return stopResponse;
c0560973
JB
148 }
149
6e0964c8 150 private logPrefix(connectorId?: number): string {
c0560973 151 if (connectorId) {
54b1efe0 152 return Utils.logPrefix(' ' + this.chargingStation.stationInfo.chargingStationId + ' | ATG on connector #' + connectorId.toString() + ':');
c0560973 153 }
54b1efe0 154 return Utils.logPrefix(' ' + this.chargingStation.stationInfo.chargingStationId + ' | ATG:');
6af9012e
JB
155 }
156}