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