Untangle charging station info from charging station template data
authorJérôme Benoit <jerome.benoit@sap.com>
Mon, 23 May 2022 19:02:28 +0000 (21:02 +0200)
committerJérôme Benoit <jerome.benoit@sap.com>
Mon, 23 May 2022 19:02:28 +0000 (21:02 +0200)
structure

Should fix https://github.com/jerome-benoit/emobility-charging-stations-simulator/issues/222

Signed-off-by: Jérôme Benoit <jerome.benoit@sap.com>
src/charging-station/AutomaticTransactionGenerator.ts
src/charging-station/ChargingStation.ts
src/charging-station/ChargingStationUtils.ts
src/charging-station/ocpp/1.6/OCPP16ResponseService.ts
src/charging-station/ocpp/1.6/OCPP16ServiceUtils.ts
src/types/AutomaticTransactionGenerator.ts
src/types/ChargingStationInfo.ts
src/types/ChargingStationTemplate.ts

index b806d5cf0044dfbd8ea473c7c52267412d39ce49..fedda8ef3d032f67bbcde78796ab0af6d44379d3 100644 (file)
@@ -10,6 +10,10 @@ import {
   StopTransactionRequest,
   StopTransactionResponse,
 } from '../types/ocpp/Transaction';
+import {
+  AutomaticTransactionGeneratorConfiguration,
+  Status,
+} from '../types/AutomaticTransactionGenerator';
 import { MeterValuesRequest, RequestCommand } from '../types/ocpp/Requests';
 
 import type ChargingStation from './ChargingStation';
@@ -17,7 +21,6 @@ import Constants from '../utils/Constants';
 import { MeterValuesResponse } from '../types/ocpp/Responses';
 import { OCPP16ServiceUtils } from './ocpp/1.6/OCPP16ServiceUtils';
 import PerformanceStatistics from '../performance/PerformanceStatistics';
-import { Status } from '../types/AutomaticTransactionGenerator';
 import Utils from '../utils/Utils';
 import logger from '../utils/Logger';
 
@@ -27,22 +30,33 @@ export default class AutomaticTransactionGenerator {
     AutomaticTransactionGenerator
   >();
 
+  public readonly configuration: AutomaticTransactionGeneratorConfiguration;
   public started: boolean;
   private readonly chargingStation: ChargingStation;
   private readonly connectorsStatus: Map<number, Status>;
 
-  private constructor(chargingStation: ChargingStation) {
+  private constructor(
+    automaticTransactionGeneratorConfiguration: AutomaticTransactionGeneratorConfiguration,
+    chargingStation: ChargingStation
+  ) {
+    this.configuration = automaticTransactionGeneratorConfiguration;
     this.chargingStation = chargingStation;
     this.connectorsStatus = new Map<number, Status>();
     this.stopConnectors();
     this.started = false;
   }
 
-  public static getInstance(chargingStation: ChargingStation): AutomaticTransactionGenerator {
+  public static getInstance(
+    automaticTransactionGeneratorConfiguration: AutomaticTransactionGeneratorConfiguration,
+    chargingStation: ChargingStation
+  ): AutomaticTransactionGenerator {
     if (!AutomaticTransactionGenerator.instances.has(chargingStation.hashId)) {
       AutomaticTransactionGenerator.instances.set(
         chargingStation.hashId,
-        new AutomaticTransactionGenerator(chargingStation)
+        new AutomaticTransactionGenerator(
+          automaticTransactionGeneratorConfiguration,
+          chargingStation
+        )
       );
     }
     return AutomaticTransactionGenerator.instances.get(chargingStation.hashId);
@@ -140,19 +154,15 @@ export default class AutomaticTransactionGenerator {
       }
       const wait =
         Utils.getRandomInteger(
-          this.chargingStation.stationInfo.AutomaticTransactionGenerator
-            .maxDelayBetweenTwoTransactions,
-          this.chargingStation.stationInfo.AutomaticTransactionGenerator
-            .minDelayBetweenTwoTransactions
+          this.configuration.maxDelayBetweenTwoTransactions,
+          this.configuration.minDelayBetweenTwoTransactions
         ) * 1000;
       logger.info(
         this.logPrefix(connectorId) + ' waiting for ' + Utils.formatDurationMilliSeconds(wait)
       );
       await Utils.sleep(wait);
       const start = Utils.secureRandom();
-      if (
-        start < this.chargingStation.stationInfo.AutomaticTransactionGenerator.probabilityOfStart
-      ) {
+      if (start < this.configuration.probabilityOfStart) {
         this.connectorsStatus.get(connectorId).skippedConsecutiveTransactions = 0;
         // Start transaction
         const startResponse = await this.startTransaction(connectorId);
@@ -163,10 +173,8 @@ export default class AutomaticTransactionGenerator {
         } else {
           // Wait until end of transaction
           const waitTrxEnd =
-            Utils.getRandomInteger(
-              this.chargingStation.stationInfo.AutomaticTransactionGenerator.maxDuration,
-              this.chargingStation.stationInfo.AutomaticTransactionGenerator.minDuration
-            ) * 1000;
+            Utils.getRandomInteger(this.configuration.maxDuration, this.configuration.minDuration) *
+            1000;
           logger.info(
             this.logPrefix(connectorId) +
               ' transaction ' +
@@ -257,7 +265,7 @@ export default class AutomaticTransactionGenerator {
     this.connectorsStatus.get(connectorId).startDate = new Date();
     this.connectorsStatus.get(connectorId).stopDate = new Date(
       this.connectorsStatus.get(connectorId).startDate.getTime() +
-        (this.chargingStation.stationInfo?.AutomaticTransactionGenerator?.stopAfterHours ??
+        (this.configuration.stopAfterHours ??
           Constants.CHARGING_STATION_ATG_DEFAULT_STOP_AFTER_HOURS) *
           3600 *
           1000 -
@@ -376,9 +384,7 @@ export default class AutomaticTransactionGenerator {
   }
 
   private getRequireAuthorize(): boolean {
-    return (
-      this.chargingStation.stationInfo?.AutomaticTransactionGenerator?.requireAuthorize ?? true
-    );
+    return this.configuration?.requireAuthorize ?? true;
   }
 
   private logPrefix(connectorId?: number): string {
index f283c50da4e4549d3364074cc6eaea88fa1e6f6d..96d889ee48b7f2a9168ccc5737d554885e546bc9 100644 (file)
@@ -44,6 +44,7 @@ import { WSError, WebSocketCloseEventStatusCode } from '../types/WebSocket';
 import WebSocket, { Data, OPEN, RawData } from 'ws';
 
 import AutomaticTransactionGenerator from './AutomaticTransactionGenerator';
+import { AutomaticTransactionGeneratorConfiguration } from '../types/AutomaticTransactionGenerator';
 import BaseError from '../exception/BaseError';
 import { ChargePointErrorCode } from '../types/ocpp/ChargePointErrorCode';
 import { ChargePointStatus } from '../types/ocpp/ChargePointStatus';
@@ -91,16 +92,17 @@ export default class ChargingStation {
   public heartbeatSetInterval!: NodeJS.Timeout;
   public ocppRequestService!: OCPPRequestService;
   public bootNotificationResponse!: BootNotificationResponse | null;
+  public powerDivider!: number;
   private readonly index: number;
   private configurationFile!: string;
   private bootNotificationRequest!: BootNotificationRequest;
   private connectorsConfigurationHash!: string;
   private ocppIncomingRequestService!: OCPPIncomingRequestService;
   private readonly messageBuffer: Set<string>;
-  private wsConfiguredConnectionUrl!: URL;
+  private configuredSupervisionUrl!: URL;
   private wsConnectionRestarted: boolean;
-  private stopped: boolean;
   private autoReconnectRetryCount: number;
+  private stopped: boolean;
   private automaticTransactionGenerator!: AutomaticTransactionGenerator;
   private webSocketPingSetInterval!: NodeJS.Timeout;
 
@@ -114,20 +116,19 @@ export default class ChargingStation {
     this.requests = new Map<string, CachedRequest>();
     this.messageBuffer = new Set<string>();
     this.initialize();
-    this.authorizedTags = this.getAuthorizedTags();
   }
 
   private get wsConnectionUrl(): URL {
-    return this.getSupervisionUrlOcppConfiguration()
-      ? new URL(
-          ChargingStationConfigurationUtils.getConfigurationKey(
+    return new URL(
+      (this.getSupervisionUrlOcppConfiguration()
+        ? ChargingStationConfigurationUtils.getConfigurationKey(
             this,
             this.getSupervisionUrlOcppKey()
-          ).value +
-            '/' +
-            this.stationInfo.chargingStationId
-        )
-      : this.wsConfiguredConnectionUrl;
+          ).value
+        : this.configuredSupervisionUrl.href) +
+        '/' +
+        this.stationInfo.chargingStationId
+    );
   }
 
   public logPrefix(): string {
@@ -162,11 +163,12 @@ export default class ChargingStation {
     return this.stationInfo.mayAuthorizeAtRemoteStart ?? true;
   }
 
-  public getNumberOfPhases(): number | undefined {
-    switch (this.getCurrentOutType()) {
+  public getNumberOfPhases(stationInfo?: ChargingStationInfo): number | undefined {
+    const localStationInfo: ChargingStationInfo = stationInfo ?? this.stationInfo;
+    switch (this.getCurrentOutType(stationInfo)) {
       case CurrentType.AC:
-        return !Utils.isUndefined(this.stationInfo.numberOfPhases)
-          ? this.stationInfo.numberOfPhases
+        return !Utils.isUndefined(localStationInfo.numberOfPhases)
+          ? localStationInfo.numberOfPhases
           : 3;
       case CurrentType.DC:
         return 0;
@@ -217,22 +219,23 @@ export default class ChargingStation {
     return this.connectors.get(id);
   }
 
-  public getCurrentOutType(): CurrentType | undefined {
-    return this.stationInfo.currentOutType ?? CurrentType.AC;
+  public getCurrentOutType(stationInfo?: ChargingStationInfo): CurrentType {
+    return (stationInfo ?? this.stationInfo).currentOutType ?? CurrentType.AC;
   }
 
   public getOcppStrictCompliance(): boolean {
     return this.stationInfo?.ocppStrictCompliance ?? false;
   }
 
-  public getVoltageOut(): number | undefined {
+  public getVoltageOut(stationInfo?: ChargingStationInfo): number | undefined {
     const defaultVoltageOut = ChargingStationUtils.getDefaultVoltageOut(
-      this.getCurrentOutType(),
+      this.getCurrentOutType(stationInfo),
       this.templateFile,
       this.logPrefix()
     );
-    return !Utils.isUndefined(this.stationInfo.voltageOut)
-      ? this.stationInfo.voltageOut
+    const localStationInfo: ChargingStationInfo = stationInfo ?? this.stationInfo;
+    return !Utils.isUndefined(localStationInfo.voltageOut)
+      ? localStationInfo.voltageOut
       : defaultVoltageOut;
   }
 
@@ -250,9 +253,9 @@ export default class ChargingStation {
               this.getAmperageLimitation() * this.getNumberOfConnectors()
             )
           : DCElectricUtils.power(this.getVoltageOut(), this.getAmperageLimitation())) /
-        this.stationInfo.powerDivider;
+        this.powerDivider;
     }
-    const connectorMaximumPower = this.getMaximumPower() / this.stationInfo.powerDivider;
+    const connectorMaximumPower = this.getMaximumPower() / this.powerDivider;
     const connectorChargingProfilePowerLimit = this.getChargingProfilePowerLimit(connectorId);
     return Math.min(
       isNaN(connectorMaximumPower) ? Infinity : connectorMaximumPower,
@@ -487,7 +490,7 @@ export default class ChargingStation {
     FileUtils.watchJsonFile<string[]>(
       this.logPrefix(),
       FileType.Authorization,
-      this.getAuthorizationFile(),
+      ChargingStationUtils.getAuthorizationFile(this.stationInfo),
       this.authorizedTags
     );
     // Monitor charging station template file
@@ -507,12 +510,7 @@ export default class ChargingStation {
             // Initialize
             this.initialize();
             // Restart the ATG
-            if (
-              !this.stationInfo.AutomaticTransactionGenerator.enable &&
-              this.automaticTransactionGenerator
-            ) {
-              this.automaticTransactionGenerator.stop();
-            }
+            this.stopAutomaticTransactionGenerator();
             this.startAutomaticTransactionGenerator();
             if (this.getEnableStatistics()) {
               this.performanceStatistics.restart();
@@ -568,8 +566,7 @@ export default class ChargingStation {
   public async reset(reason?: StopTransactionReason): Promise<void> {
     await this.stop(reason);
     await Utils.sleep(this.stationInfo.resetTime);
-    this.stationInfo = this.getStationInfo();
-    this.stationInfo?.Connectors && delete this.stationInfo.Connectors;
+    this.initialize();
     this.start();
   }
 
@@ -616,7 +613,7 @@ export default class ChargingStation {
                 : DCElectricUtils.power(this.getVoltageOut(), limit);
         }
 
-        const connectorMaximumPower = this.getMaximumPower() / this.stationInfo.powerDivider;
+        const connectorMaximumPower = this.getMaximumPower() / this.powerDivider;
         if (limit > connectorMaximumPower) {
           logger.error(
             `${this.logPrefix()} Charging profile id ${
@@ -728,74 +725,99 @@ export default class ChargingStation {
   }
 
   private getStationInfoFromTemplate(): ChargingStationInfo {
-    const stationInfo: ChargingStationInfo = this.getTemplateFromFile();
-    if (Utils.isNullOrUndefined(stationInfo)) {
+    const stationTemplate: ChargingStationTemplate = this.getTemplateFromFile();
+    if (Utils.isNullOrUndefined(stationTemplate)) {
       const errorMsg = 'Failed to read charging station template file';
       logger.error(`${this.logPrefix()} ${errorMsg}`);
       throw new BaseError(errorMsg);
     }
-    if (Utils.isEmptyObject(stationInfo)) {
+    if (Utils.isEmptyObject(stationTemplate)) {
       const errorMsg = `Empty charging station information from template file ${this.templateFile}`;
       logger.error(`${this.logPrefix()} ${errorMsg}`);
       throw new BaseError(errorMsg);
     }
-    const chargingStationId = ChargingStationUtils.getChargingStationId(this.index, stationInfo);
     // Deprecation template keys section
     ChargingStationUtils.warnDeprecatedTemplateKey(
-      stationInfo,
+      stationTemplate,
       'supervisionUrl',
       this.templateFile,
       this.logPrefix(),
       "Use 'supervisionUrls' instead"
     );
     ChargingStationUtils.convertDeprecatedTemplateKey(
-      stationInfo,
+      stationTemplate,
       'supervisionUrl',
       'supervisionUrls'
     );
-    if (!Utils.isEmptyArray(stationInfo.power)) {
-      stationInfo.power = stationInfo.power as number[];
-      const powerArrayRandomIndex = Math.floor(Utils.secureRandom() * stationInfo.power.length);
+    const stationInfo: ChargingStationInfo =
+      ChargingStationUtils.stationTemplateToStationInfo(stationTemplate);
+    stationInfo.chargingStationId = ChargingStationUtils.getChargingStationId(
+      this.index,
+      stationTemplate
+    );
+    ChargingStationUtils.createSerialNumber(stationTemplate, stationInfo);
+    if (!Utils.isEmptyArray(stationTemplate.power)) {
+      stationTemplate.power = stationTemplate.power as number[];
+      const powerArrayRandomIndex = Math.floor(Utils.secureRandom() * stationTemplate.power.length);
       stationInfo.maximumPower =
-        stationInfo.powerUnit === PowerUnits.KILO_WATT
-          ? stationInfo.power[powerArrayRandomIndex] * 1000
-          : stationInfo.power[powerArrayRandomIndex];
+        stationTemplate.powerUnit === PowerUnits.KILO_WATT
+          ? stationTemplate.power[powerArrayRandomIndex] * 1000
+          : stationTemplate.power[powerArrayRandomIndex];
     } else {
-      stationInfo.power = stationInfo.power as number;
+      stationTemplate.power = stationTemplate.power as number;
       stationInfo.maximumPower =
-        stationInfo.powerUnit === PowerUnits.KILO_WATT
-          ? stationInfo.power * 1000
-          : stationInfo.power;
-    }
-    delete stationInfo.power;
-    delete stationInfo.powerUnit;
-    stationInfo.chargingStationId = chargingStationId;
-    stationInfo.resetTime = stationInfo.resetTime
-      ? stationInfo.resetTime * 1000
+        stationTemplate.powerUnit === PowerUnits.KILO_WATT
+          ? stationTemplate.power * 1000
+          : stationTemplate.power;
+    }
+    stationInfo.resetTime = stationTemplate.resetTime
+      ? stationTemplate.resetTime * 1000
       : Constants.CHARGING_STATION_DEFAULT_RESET_TIME;
+    const configuredMaxConnectors = ChargingStationUtils.getConfiguredNumberOfConnectors(
+      this.index,
+      stationTemplate
+    );
+    ChargingStationUtils.checkConfiguredMaxConnectors(
+      configuredMaxConnectors,
+      this.templateFile,
+      Utils.logPrefix()
+    );
+    const templateMaxConnectors =
+      ChargingStationUtils.getTemplateMaxNumberOfConnectors(stationTemplate);
+    ChargingStationUtils.checkTemplateMaxConnectors(
+      templateMaxConnectors,
+      this.templateFile,
+      Utils.logPrefix()
+    );
+    if (
+      configuredMaxConnectors >
+        (stationTemplate?.Connectors[0] ? templateMaxConnectors - 1 : templateMaxConnectors) &&
+      !stationTemplate?.randomConnectors
+    ) {
+      logger.warn(
+        `${this.logPrefix()} Number of connectors exceeds the number of connector configurations in template ${
+          this.templateFile
+        }, forcing random connector configurations affectation`
+      );
+      stationInfo.randomConnectors = true;
+    }
+    // Build connectors if needed (FIXME: should be factored out)
+    this.initializeConnectors(stationInfo, configuredMaxConnectors, templateMaxConnectors);
+    stationInfo.maximumAmperage = this.getMaximumAmperage(stationInfo);
+    ChargingStationUtils.createStationInfoHash(stationInfo);
     return stationInfo;
   }
 
   private getStationInfoFromFile(): ChargingStationInfo | null {
     let stationInfo: ChargingStationInfo = null;
-    if (this.getStationInfoPersistentConfiguration()) {
-      stationInfo = this.getConfigurationFromFile()?.stationInfo ?? null;
-    }
-    if (stationInfo) {
-      stationInfo = ChargingStationUtils.createStationInfoHash(stationInfo);
-    }
+    this.getStationInfoPersistentConfiguration() &&
+      (stationInfo = this.getConfigurationFromFile()?.stationInfo ?? null);
+    stationInfo && ChargingStationUtils.createStationInfoHash(stationInfo);
     return stationInfo;
   }
 
   private getStationInfo(): ChargingStationInfo {
     const stationInfoFromTemplate: ChargingStationInfo = this.getStationInfoFromTemplate();
-    this.hashId = ChargingStationUtils.getHashId(stationInfoFromTemplate);
-    this.configurationFile = path.join(
-      path.resolve(__dirname, '../'),
-      'assets',
-      'configurations',
-      this.hashId + '.json'
-    );
     const stationInfoFromFile: ChargingStationInfo = this.getStationInfoFromFile();
     // Priority: charging station info from template > charging station info from configuration file > charging station info attribute
     if (stationInfoFromFile?.templateHash === stationInfoFromTemplate.templateHash) {
@@ -804,7 +826,7 @@ export default class ChargingStation {
       }
       return stationInfoFromFile;
     }
-    ChargingStationUtils.createSerialNumber(stationInfoFromTemplate, stationInfoFromFile);
+    ChargingStationUtils.createSerialNumber(this.getTemplateFromFile(), stationInfoFromFile);
     return stationInfoFromTemplate;
   }
 
@@ -835,50 +857,38 @@ export default class ChargingStation {
   }
 
   private initialize(): void {
-    this.stationInfo = this.getStationInfo();
+    this.hashId = ChargingStationUtils.getHashId(this.index, this.getTemplateFromFile());
     logger.info(`${this.logPrefix()} Charging station hashId '${this.hashId}'`);
-    this.bootNotificationRequest = ChargingStationUtils.createBootNotificationRequest(
-      this.stationInfo
-    );
-    this.ocppConfiguration = this.getOcppConfiguration();
-    this.stationInfo?.Configuration && delete this.stationInfo.Configuration;
-    this.wsConfiguredConnectionUrl = new URL(
-      this.getConfiguredSupervisionUrl().href + '/' + this.stationInfo.chargingStationId
+    this.configurationFile = path.join(
+      path.resolve(__dirname, '../'),
+      'assets',
+      'configurations',
+      this.hashId + '.json'
     );
-    // Build connectors if needed
-    const maxConnectors = this.getMaxNumberOfConnectors();
-    this.checkMaxConnectors(maxConnectors);
-    const templateMaxConnectors = this.getTemplateMaxNumberOfConnectors();
-    this.checkTemplateMaxConnectors(templateMaxConnectors);
-    if (
-      maxConnectors >
-        (this.stationInfo?.Connectors[0] ? templateMaxConnectors - 1 : templateMaxConnectors) &&
-      !this.stationInfo?.randomConnectors
-    ) {
-      logger.warn(
-        `${this.logPrefix()} Number of connectors exceeds the number of connector configurations in template ${
-          this.templateFile
-        }, forcing random connector configurations affectation`
-      );
-      this.stationInfo.randomConnectors = true;
-    }
-    this.initializeConnectors(this.stationInfo, maxConnectors, templateMaxConnectors);
-    this.stationInfo.maximumAmperage = this.getMaximumAmperage();
-    if (this.stationInfo) {
-      this.stationInfo = ChargingStationUtils.createStationInfoHash(this.stationInfo);
-    }
+    this.stationInfo = this.getStationInfo();
     this.saveStationInfo();
     // Avoid duplication of connectors related information in RAM
     this.stationInfo?.Connectors && delete this.stationInfo.Connectors;
-    // OCPP configuration
-    this.initializeOcppConfiguration();
+    this.configuredSupervisionUrl = this.getConfiguredSupervisionUrl();
     if (this.getEnableStatistics()) {
       this.performanceStatistics = PerformanceStatistics.getInstance(
         this.hashId,
         this.stationInfo.chargingStationId,
-        this.wsConnectionUrl
+        this.configuredSupervisionUrl
       );
     }
+    this.bootNotificationRequest = ChargingStationUtils.createBootNotificationRequest(
+      this.stationInfo
+    );
+    this.authorizedTags = ChargingStationUtils.getAuthorizedTags(
+      this.stationInfo,
+      this.templateFile,
+      this.logPrefix()
+    );
+    this.powerDivider = this.getPowerDivider();
+    // OCPP configuration
+    this.ocppConfiguration = this.getOcppConfiguration();
+    this.initializeOcppConfiguration();
     switch (this.getOcppVersion()) {
       case OCPPVersion.VERSION_16:
         this.ocppIncomingRequestService =
@@ -898,7 +908,6 @@ export default class ChargingStation {
         status: RegistrationStatus.ACCEPTED,
       };
     }
-    this.stationInfo.powerDivider = this.getPowerDivider();
   }
 
   private initializeOcppConfiguration(): void {
@@ -934,7 +943,7 @@ export default class ChargingStation {
       ChargingStationConfigurationUtils.addConfigurationKey(
         this,
         this.getSupervisionUrlOcppKey(),
-        this.getConfiguredSupervisionUrl().href,
+        this.configuredSupervisionUrl.href,
         { reboot: true }
       );
     } else if (
@@ -1065,7 +1074,7 @@ export default class ChargingStation {
 
   private initializeConnectors(
     stationInfo: ChargingStationInfo,
-    maxConnectors: number,
+    configuredMaxConnectors: number,
     templateMaxConnectors: number
   ): void {
     if (!stationInfo?.Connectors && this.connectors.size === 0) {
@@ -1085,7 +1094,7 @@ export default class ChargingStation {
     if (stationInfo?.Connectors) {
       const connectorsConfigHash = crypto
         .createHash(Constants.DEFAULT_HASH_ALGORITHM)
-        .update(JSON.stringify(stationInfo?.Connectors) + maxConnectors.toString())
+        .update(JSON.stringify(stationInfo?.Connectors) + configuredMaxConnectors.toString())
         .digest('hex');
       const connectorsConfigChanged =
         this.connectors?.size !== 0 && this.connectorsConfigurationHash !== connectorsConfigHash;
@@ -1098,7 +1107,7 @@ export default class ChargingStation {
           const lastConnectorId = Utils.convertToInt(lastConnector);
           if (
             lastConnectorId === 0 &&
-            this.getUseConnectorId0() &&
+            this.getUseConnectorId0(stationInfo) &&
             stationInfo?.Connectors[lastConnector]
           ) {
             this.connectors.set(
@@ -1113,7 +1122,7 @@ export default class ChargingStation {
         }
         // Generate all connectors
         if ((stationInfo?.Connectors[0] ? templateMaxConnectors - 1 : templateMaxConnectors) > 0) {
-          for (let index = 1; index <= maxConnectors; index++) {
+          for (let index = 1; index <= configuredMaxConnectors; index++) {
             const randConnectorId = stationInfo?.randomConnectors
               ? Utils.getRandomInteger(Utils.convertToInt(lastConnector), 1)
               : index;
@@ -1143,32 +1152,6 @@ export default class ChargingStation {
     }
   }
 
-  private checkMaxConnectors(maxConnectors: number): void {
-    if (maxConnectors <= 0) {
-      logger.warn(
-        `${this.logPrefix()} Charging station information from template ${
-          this.templateFile
-        } with ${maxConnectors} connectors`
-      );
-    }
-  }
-
-  private checkTemplateMaxConnectors(templateMaxConnectors: number): void {
-    if (templateMaxConnectors === 0) {
-      logger.warn(
-        `${this.logPrefix()} Charging station information from template ${
-          this.templateFile
-        } with empty connectors configuration`
-      );
-    } else if (templateMaxConnectors < 0) {
-      logger.error(
-        `${this.logPrefix()} Charging station information from template ${
-          this.templateFile
-        } with no connectors configuration defined`
-      );
-    }
-  }
-
   private getConfigurationFromFile(): ChargingStationConfiguration | null {
     let configuration: ChargingStationConfiguration = null;
     if (this.configurationFile && fs.existsSync(this.configurationFile)) {
@@ -1494,43 +1477,10 @@ export default class ChargingStation {
     logger.error(this.logPrefix() + ' WebSocket error: %j', error);
   }
 
-  private getAuthorizationFile(): string | undefined {
-    return (
-      this.stationInfo.authorizationFile &&
-      path.join(
-        path.resolve(__dirname, '../'),
-        'assets',
-        path.basename(this.stationInfo.authorizationFile)
-      )
-    );
-  }
-
-  private getAuthorizedTags(): string[] {
-    let authorizedTags: string[] = [];
-    const authorizationFile = this.getAuthorizationFile();
-    if (authorizationFile) {
-      try {
-        // Load authorization file
-        authorizedTags = JSON.parse(fs.readFileSync(authorizationFile, 'utf8')) as string[];
-      } catch (error) {
-        FileUtils.handleFileException(
-          this.logPrefix(),
-          FileType.Authorization,
-          authorizationFile,
-          error as NodeJS.ErrnoException
-        );
-      }
-    } else {
-      logger.info(
-        this.logPrefix() + ' No authorization file given in template file ' + this.templateFile
-      );
-    }
-    return authorizedTags;
-  }
-
-  private getUseConnectorId0(): boolean | undefined {
-    return !Utils.isUndefined(this.stationInfo.useConnectorId0)
-      ? this.stationInfo.useConnectorId0
+  private getUseConnectorId0(stationInfo?: ChargingStationInfo): boolean | undefined {
+    const localStationInfo = stationInfo ?? this.stationInfo;
+    return !Utils.isUndefined(localStationInfo.useConnectorId0)
+      ? localStationInfo.useConnectorId0
       : true;
   }
 
@@ -1585,50 +1535,28 @@ export default class ChargingStation {
 
   private getPowerDivider(): number {
     let powerDivider = this.getNumberOfConnectors();
-    if (this.stationInfo.powerSharedByConnectors) {
+    if (this.stationInfo?.powerSharedByConnectors) {
       powerDivider = this.getNumberOfRunningTransactions();
     }
     return powerDivider;
   }
 
-  private getTemplateMaxNumberOfConnectors(): number {
-    if (!this.stationInfo?.Connectors) {
-      return -1;
-    }
-    return Object.keys(this.stationInfo?.Connectors).length;
-  }
-
-  private getMaxNumberOfConnectors(): number {
-    let maxConnectors: number;
-    if (!Utils.isEmptyArray(this.stationInfo.numberOfConnectors)) {
-      const numberOfConnectors = this.stationInfo.numberOfConnectors as number[];
-      // Distribute evenly the number of connectors
-      maxConnectors = numberOfConnectors[(this.index - 1) % numberOfConnectors.length];
-    } else if (!Utils.isUndefined(this.stationInfo.numberOfConnectors)) {
-      maxConnectors = this.stationInfo.numberOfConnectors as number;
-    } else {
-      maxConnectors = this.stationInfo?.Connectors[0]
-        ? this.getTemplateMaxNumberOfConnectors() - 1
-        : this.getTemplateMaxNumberOfConnectors();
-    }
-    return maxConnectors;
-  }
-
-  private getMaximumPower(): number {
-    return (this.stationInfo['maxPower'] as number) ?? this.stationInfo.maximumPower;
+  private getMaximumPower(stationInfo?: ChargingStationInfo): number {
+    const localStationInfo = stationInfo ?? this.stationInfo;
+    return (localStationInfo['maxPower'] as number) ?? localStationInfo.maximumPower;
   }
 
-  private getMaximumAmperage(): number | undefined {
-    const maximumPower = this.getMaximumPower();
-    switch (this.getCurrentOutType()) {
+  private getMaximumAmperage(stationInfo: ChargingStationInfo): number | undefined {
+    const maximumPower = this.getMaximumPower(stationInfo);
+    switch (this.getCurrentOutType(stationInfo)) {
       case CurrentType.AC:
         return ACElectricUtils.amperagePerPhaseFromPower(
-          this.getNumberOfPhases(),
+          this.getNumberOfPhases(stationInfo),
           maximumPower / this.getNumberOfConnectors(),
-          this.getVoltageOut()
+          this.getVoltageOut(stationInfo)
         );
       case CurrentType.DC:
-        return DCElectricUtils.amperage(maximumPower, this.getVoltageOut());
+        return DCElectricUtils.amperage(maximumPower, this.getVoltageOut(stationInfo));
     }
   }
 
@@ -1741,9 +1669,12 @@ export default class ChargingStation {
   }
 
   private startAutomaticTransactionGenerator() {
-    if (this.stationInfo.AutomaticTransactionGenerator.enable) {
+    if (this.getAutomaticTransactionGeneratorConfigurationFromTemplate()?.enable) {
       if (!this.automaticTransactionGenerator) {
-        this.automaticTransactionGenerator = AutomaticTransactionGenerator.getInstance(this);
+        this.automaticTransactionGenerator = AutomaticTransactionGenerator.getInstance(
+          this.getAutomaticTransactionGeneratorConfigurationFromTemplate(),
+          this
+        );
       }
       if (!this.automaticTransactionGenerator.started) {
         this.automaticTransactionGenerator.start();
@@ -1751,6 +1682,13 @@ export default class ChargingStation {
     }
   }
 
+  private stopAutomaticTransactionGenerator(): void {
+    if (this.automaticTransactionGenerator?.started) {
+      this.automaticTransactionGenerator.stop();
+      this.automaticTransactionGenerator = null;
+    }
+  }
+
   private async stopMessageSequence(
     reason: StopTransactionReason = StopTransactionReason.NONE
   ): Promise<void> {
@@ -1758,12 +1696,9 @@ export default class ChargingStation {
     this.stopWebSocketPing();
     // Stop heartbeat
     this.stopHeartbeat();
-    // Stop the ATG
-    if (
-      this.stationInfo.AutomaticTransactionGenerator.enable &&
-      this.automaticTransactionGenerator?.started
-    ) {
-      this.automaticTransactionGenerator.stop();
+    // Stop ongoing transactions
+    if (this.automaticTransactionGenerator?.configuration?.enable) {
+      this.stopAutomaticTransactionGenerator();
     } else {
       for (const connectorId of this.connectors.keys()) {
         if (connectorId > 0 && this.getConnectorStatus(connectorId)?.transactionStarted) {
@@ -1966,12 +1901,8 @@ export default class ChargingStation {
     // Stop heartbeat
     this.stopHeartbeat();
     // Stop the ATG if needed
-    if (
-      this.stationInfo.AutomaticTransactionGenerator.enable &&
-      this.stationInfo.AutomaticTransactionGenerator.stopOnConnectionFailure &&
-      this.automaticTransactionGenerator?.started
-    ) {
-      this.automaticTransactionGenerator.stop();
+    if (this.automaticTransactionGenerator?.configuration?.stopOnConnectionFailure) {
+      this.stopAutomaticTransactionGenerator();
     }
     if (
       this.autoReconnectRetryCount < this.getAutoReconnectMaxRetries() ||
@@ -2008,6 +1939,10 @@ export default class ChargingStation {
     }
   }
 
+  private getAutomaticTransactionGeneratorConfigurationFromTemplate(): AutomaticTransactionGeneratorConfiguration | null {
+    return this.getTemplateFromFile()?.AutomaticTransactionGenerator ?? null;
+  }
+
   private initializeConnectorStatus(connectorId: number): void {
     this.getConnectorStatus(connectorId).idTagLocalAuthorized = false;
     this.getConnectorStatus(connectorId).idTagAuthorized = false;
index afe9a52fc3ef72f26dc387b4fcd95b9aebf60281..b57e98e6741222f309d04d8ac84ed3ccbf4bec37 100644 (file)
@@ -13,14 +13,18 @@ import { ChargingStationConfigurationUtils } from './ChargingStationConfiguratio
 import ChargingStationInfo from '../types/ChargingStationInfo';
 import Configuration from '../utils/Configuration';
 import Constants from '../utils/Constants';
+import { FileType } from '../types/FileType';
+import FileUtils from '../utils/FileUtils';
 import { SampledValueTemplate } from '../types/MeasurandPerPhaseSampledValueTemplates';
 import { StandardParametersKey } from '../types/ocpp/Configuration';
 import Utils from '../utils/Utils';
 import { WebSocketCloseEventStatusString } from '../types/WebSocket';
 import { WorkerProcessType } from '../types/Worker';
 import crypto from 'crypto';
+import fs from 'fs';
 import logger from '../utils/Logger';
 import moment from 'moment';
+import path from 'path';
 
 export class ChargingStationUtils {
   public static getChargingStationId(
@@ -40,34 +44,92 @@ export class ChargingStationUtils {
           idSuffix;
   }
 
-  public static getHashId(stationInfo: ChargingStationInfo): string {
+  public static getHashId(index: number, stationTemplate: ChargingStationTemplate): string {
     const hashBootNotificationRequest = {
-      chargePointModel: stationInfo.chargePointModel,
-      chargePointVendor: stationInfo.chargePointVendor,
-      ...(!Utils.isUndefined(stationInfo.chargeBoxSerialNumberPrefix) && {
-        chargeBoxSerialNumber: stationInfo.chargeBoxSerialNumberPrefix,
+      chargePointModel: stationTemplate.chargePointModel,
+      chargePointVendor: stationTemplate.chargePointVendor,
+      ...(!Utils.isUndefined(stationTemplate.chargeBoxSerialNumberPrefix) && {
+        chargeBoxSerialNumber: stationTemplate.chargeBoxSerialNumberPrefix,
       }),
-      ...(!Utils.isUndefined(stationInfo.chargePointSerialNumberPrefix) && {
-        chargePointSerialNumber: stationInfo.chargePointSerialNumberPrefix,
+      ...(!Utils.isUndefined(stationTemplate.chargePointSerialNumberPrefix) && {
+        chargePointSerialNumber: stationTemplate.chargePointSerialNumberPrefix,
       }),
-      ...(!Utils.isUndefined(stationInfo.firmwareVersion) && {
-        firmwareVersion: stationInfo.firmwareVersion,
+      ...(!Utils.isUndefined(stationTemplate.firmwareVersion) && {
+        firmwareVersion: stationTemplate.firmwareVersion,
       }),
-      ...(!Utils.isUndefined(stationInfo.iccid) && { iccid: stationInfo.iccid }),
-      ...(!Utils.isUndefined(stationInfo.imsi) && { imsi: stationInfo.imsi }),
-      ...(!Utils.isUndefined(stationInfo.meterSerialNumberPrefix) && {
-        meterSerialNumber: stationInfo.meterSerialNumberPrefix,
+      ...(!Utils.isUndefined(stationTemplate.iccid) && { iccid: stationTemplate.iccid }),
+      ...(!Utils.isUndefined(stationTemplate.imsi) && { imsi: stationTemplate.imsi }),
+      ...(!Utils.isUndefined(stationTemplate.meterSerialNumberPrefix) && {
+        meterSerialNumber: stationTemplate.meterSerialNumberPrefix,
       }),
-      ...(!Utils.isUndefined(stationInfo.meterType) && {
-        meterType: stationInfo.meterType,
+      ...(!Utils.isUndefined(stationTemplate.meterType) && {
+        meterType: stationTemplate.meterType,
       }),
     };
     return crypto
       .createHash(Constants.DEFAULT_HASH_ALGORITHM)
-      .update(JSON.stringify(hashBootNotificationRequest) + stationInfo.chargingStationId)
+      .update(
+        JSON.stringify(hashBootNotificationRequest) +
+          ChargingStationUtils.getChargingStationId(index, stationTemplate)
+      )
       .digest('hex');
   }
 
+  public static getTemplateMaxNumberOfConnectors(stationTemplate: ChargingStationTemplate): number {
+    const templateConnectors = stationTemplate?.Connectors;
+    if (!templateConnectors) {
+      return -1;
+    }
+    return Object.keys(templateConnectors).length;
+  }
+
+  public static checkTemplateMaxConnectors(
+    templateMaxConnectors: number,
+    templateFile: string,
+    logPrefix: string
+  ): void {
+    if (templateMaxConnectors === 0) {
+      logger.warn(
+        `${logPrefix} Charging station information from template ${templateFile} with empty connectors configuration`
+      );
+    } else if (templateMaxConnectors < 0) {
+      logger.error(
+        `${logPrefix} Charging station information from template ${templateFile} with no connectors configuration defined`
+      );
+    }
+  }
+
+  public static getConfiguredNumberOfConnectors(
+    index: number,
+    stationTemplate: ChargingStationTemplate
+  ): number {
+    let configuredMaxConnectors: number;
+    if (!Utils.isEmptyArray(stationTemplate.numberOfConnectors)) {
+      const numberOfConnectors = stationTemplate.numberOfConnectors as number[];
+      // Distribute evenly the number of connectors
+      configuredMaxConnectors = numberOfConnectors[(index - 1) % numberOfConnectors.length];
+    } else if (!Utils.isUndefined(stationTemplate.numberOfConnectors)) {
+      configuredMaxConnectors = stationTemplate.numberOfConnectors as number;
+    } else {
+      configuredMaxConnectors = stationTemplate?.Connectors[0]
+        ? ChargingStationUtils.getTemplateMaxNumberOfConnectors(stationTemplate) - 1
+        : ChargingStationUtils.getTemplateMaxNumberOfConnectors(stationTemplate);
+    }
+    return configuredMaxConnectors;
+  }
+
+  public static checkConfiguredMaxConnectors(
+    configuredMaxConnectors: number,
+    templateFile: string,
+    logPrefix: string
+  ): void {
+    if (configuredMaxConnectors <= 0) {
+      logger.warn(
+        `${logPrefix} Charging station information from template ${templateFile} with ${configuredMaxConnectors} connectors`
+      );
+    }
+  }
+
   public static createBootNotificationRequest(
     stationInfo: ChargingStationInfo
   ): BootNotificationRequest {
@@ -157,7 +219,20 @@ export class ChargingStationUtils {
     }
   }
 
-  public static createStationInfoHash(stationInfo: ChargingStationInfo): ChargingStationInfo {
+  public static stationTemplateToStationInfo(
+    stationTemplate: ChargingStationTemplate
+  ): ChargingStationInfo {
+    stationTemplate = Utils.cloneObject(stationTemplate);
+    delete stationTemplate.power;
+    delete stationTemplate.powerUnit;
+    delete stationTemplate.Configuration;
+    delete stationTemplate.AutomaticTransactionGenerator;
+    delete stationTemplate.chargeBoxSerialNumberPrefix;
+    delete stationTemplate.chargePointSerialNumberPrefix;
+    return stationTemplate;
+  }
+
+  public static createStationInfoHash(stationInfo: ChargingStationInfo): void {
     const previousInfoHash = stationInfo?.infoHash ?? '';
     delete stationInfo.infoHash;
     const currentInfoHash = crypto
@@ -172,13 +247,15 @@ export class ChargingStationUtils {
     } else {
       stationInfo.infoHash = previousInfoHash;
     }
-    return stationInfo;
   }
 
   public static createSerialNumber(
+    stationTemplate: ChargingStationTemplate,
     stationInfo: ChargingStationInfo,
-    existingStationInfo?: ChargingStationInfo | null,
-    params: { randomSerialNumberUpperCase?: boolean; randomSerialNumber?: boolean } = {
+    params: {
+      randomSerialNumberUpperCase?: boolean;
+      randomSerialNumber?: boolean;
+    } = {
       randomSerialNumberUpperCase: true,
       randomSerialNumber: true,
     }
@@ -186,29 +263,29 @@ export class ChargingStationUtils {
     params = params ?? {};
     params.randomSerialNumberUpperCase = params?.randomSerialNumberUpperCase ?? true;
     params.randomSerialNumber = params?.randomSerialNumber ?? true;
-    if (existingStationInfo) {
-      existingStationInfo?.chargePointSerialNumber &&
-        (stationInfo.chargePointSerialNumber = existingStationInfo.chargePointSerialNumber);
-      existingStationInfo?.chargeBoxSerialNumber &&
-        (stationInfo.chargeBoxSerialNumber = existingStationInfo.chargeBoxSerialNumber);
-      existingStationInfo?.meterSerialNumber &&
-        (stationInfo.meterSerialNumber = existingStationInfo.meterSerialNumber);
-    } else {
-      const serialNumberSuffix = params?.randomSerialNumber
-        ? ChargingStationUtils.getRandomSerialNumberSuffix({
-            upperCase: params.randomSerialNumberUpperCase,
-          })
-        : '';
-      stationInfo.chargePointSerialNumber =
-        stationInfo?.chargePointSerialNumberPrefix &&
-        stationInfo.chargePointSerialNumberPrefix + serialNumberSuffix;
-      stationInfo.chargeBoxSerialNumber =
-        stationInfo?.chargeBoxSerialNumberPrefix &&
-        stationInfo.chargeBoxSerialNumberPrefix + serialNumberSuffix;
-      stationInfo.meterSerialNumber =
-        stationInfo?.meterSerialNumberPrefix &&
-        stationInfo.meterSerialNumberPrefix + serialNumberSuffix;
-    }
+    const serialNumberSuffix = params?.randomSerialNumber
+      ? ChargingStationUtils.getRandomSerialNumberSuffix({
+          upperCase: params.randomSerialNumberUpperCase,
+        })
+      : '';
+    stationTemplate?.chargePointSerialNumberPrefix &&
+    stationInfo &&
+    Utils.isNullOrUndefined(stationInfo?.chargePointSerialNumber)
+      ? (stationInfo.chargePointSerialNumber =
+          stationTemplate.chargePointSerialNumberPrefix + serialNumberSuffix)
+      : stationInfo && delete stationInfo.chargePointSerialNumber;
+    stationTemplate?.chargeBoxSerialNumberPrefix &&
+    stationInfo &&
+    Utils.isNullOrUndefined(stationInfo?.chargeBoxSerialNumber)
+      ? (stationInfo.chargeBoxSerialNumber =
+          stationTemplate.chargeBoxSerialNumberPrefix + serialNumberSuffix)
+      : stationInfo && delete stationInfo.chargeBoxSerialNumber;
+    stationTemplate?.meterSerialNumberPrefix &&
+    stationInfo &&
+    Utils.isNullOrUndefined(stationInfo?.meterSerialNumber)
+      ? (stationInfo.meterSerialNumber =
+          stationTemplate.meterSerialNumberPrefix + serialNumberSuffix)
+      : stationInfo && delete stationInfo.meterSerialNumber;
   }
 
   public static getAmperageLimitationUnitDivider(stationInfo: ChargingStationInfo): number {
@@ -434,6 +511,42 @@ export class ChargingStationUtils {
     );
   }
 
+  public static getAuthorizedTags(
+    stationInfo: ChargingStationInfo,
+    templateFile: string,
+    logPrefix: string
+  ): string[] {
+    let authorizedTags: string[] = [];
+    const authorizationFile = ChargingStationUtils.getAuthorizationFile(stationInfo);
+    if (authorizationFile) {
+      try {
+        // Load authorization file
+        authorizedTags = JSON.parse(fs.readFileSync(authorizationFile, 'utf8')) as string[];
+      } catch (error) {
+        FileUtils.handleFileException(
+          logPrefix,
+          FileType.Authorization,
+          authorizationFile,
+          error as NodeJS.ErrnoException
+        );
+      }
+    } else {
+      logger.info(logPrefix + ' No authorization file given in template file ' + templateFile);
+    }
+    return authorizedTags;
+  }
+
+  public static getAuthorizationFile(stationInfo: ChargingStationInfo): string | undefined {
+    return (
+      stationInfo.authorizationFile &&
+      path.join(
+        path.resolve(__dirname, '../'),
+        'assets',
+        path.basename(stationInfo.authorizationFile)
+      )
+    );
+  }
+
   private static getRandomSerialNumberSuffix(params?: {
     randomBytesLength?: number;
     upperCase?: boolean;
index 1bcce7055b6814d4de4c7344f6b35d2757b9d2ea..90c04035aedc3a587011fe715d3f19c44675f595 100644 (file)
@@ -335,7 +335,7 @@ export default class OCPP16ResponseService extends OCPPResponseService {
           requestPayload.idTag
       );
       if (chargingStation.stationInfo.powerSharedByConnectors) {
-        chargingStation.stationInfo.powerDivider++;
+        chargingStation.powerDivider++;
       }
       const configuredMeterValueSampleInterval =
         ChargingStationConfigurationUtils.getConfigurationKey(
@@ -441,7 +441,7 @@ export default class OCPP16ResponseService extends OCPPResponseService {
           OCPP16ChargePointStatus.AVAILABLE;
       }
       if (chargingStation.stationInfo.powerSharedByConnectors) {
-        chargingStation.stationInfo.powerDivider--;
+        chargingStation.powerDivider--;
       }
       logger.info(
         chargingStation.logPrefix() +
index 1c8e1369b25fe997e9aac7ac509b3a91eb9c4920..0599cfa0e07915d7bd77429bf4e412c1f178d269 100644 (file)
@@ -770,16 +770,16 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils {
     chargingStation: ChargingStation,
     measurandType: OCPP16MeterValueMeasurand
   ): void {
-    if (Utils.isUndefined(chargingStation.stationInfo.powerDivider)) {
+    if (Utils.isUndefined(chargingStation.powerDivider)) {
       const errMsg = `${chargingStation.logPrefix()} MeterValues measurand ${
         measurandType ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
       }: powerDivider is undefined`;
       logger.error(errMsg);
       throw new OCPPError(ErrorType.INTERNAL_ERROR, errMsg, OCPP16RequestCommand.METER_VALUES);
-    } else if (chargingStation.stationInfo?.powerDivider <= 0) {
+    } else if (chargingStation?.powerDivider <= 0) {
       const errMsg = `${chargingStation.logPrefix()} MeterValues measurand ${
         measurandType ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
-      }: powerDivider have zero or below value ${chargingStation.stationInfo.powerDivider}`;
+      }: powerDivider have zero or below value ${chargingStation.powerDivider}`;
       logger.error(errMsg);
       throw new OCPPError(ErrorType.INTERNAL_ERROR, errMsg, OCPP16RequestCommand.METER_VALUES);
     }
index 940a5d75b7115bf212928223e514f01109e8b112..83e88c68cfc3ad9588efc85951c3bb651aaf149a 100644 (file)
@@ -1,3 +1,15 @@
+export interface AutomaticTransactionGeneratorConfiguration {
+  enable: boolean;
+  minDuration: number;
+  maxDuration: number;
+  minDelayBetweenTwoTransactions: number;
+  maxDelayBetweenTwoTransactions: number;
+  probabilityOfStart: number;
+  stopAfterHours: number;
+  stopOnConnectionFailure: boolean;
+  requireAuthorize?: boolean;
+}
+
 export interface Status {
   start?: boolean;
   startDate?: Date;
index 1cf27163a77588a8085d1ba38cd56b134ac9dfd2..1c6415ae801ed52b0fb3c2975bf08bae64114f41 100644 (file)
@@ -1,13 +1,21 @@
 import ChargingStationTemplate from './ChargingStationTemplate';
 
-export default interface ChargingStationInfo extends ChargingStationTemplate {
+export default interface ChargingStationInfo
+  extends Omit<
+    ChargingStationTemplate,
+    | 'AutomaticTransactionGenerator'
+    | 'Configuration'
+    | 'power'
+    | 'powerUnit'
+    | 'chargeBoxSerialNumberPrefix'
+    | 'chargePointSerialNumberPrefix'
+  > {
   infoHash?: string;
   chargingStationId?: string;
   chargeBoxSerialNumber?: string;
   chargePointSerialNumber?: string;
   meterSerialNumber?: string;
   maximumPower?: number; // Always in Watt
-  powerDivider?: number;
   maximumAmperage?: number; // Always in Ampere
 }
 
index 212cb64e706e33672ce2260fa5c7eb3f5b61b441..db8ff3826a2f8cc5d599ed5c78fc3207414dd3c4 100644 (file)
@@ -1,3 +1,4 @@
+import { AutomaticTransactionGeneratorConfiguration } from './AutomaticTransactionGenerator';
 import ChargingStationOcppConfiguration from './ChargingStationOcppConfiguration';
 import { ClientOptions } from 'ws';
 import { ClientRequestArgs } from 'http';
@@ -29,18 +30,6 @@ export enum Voltage {
   VOLTAGE_800 = 800,
 }
 
-export interface AutomaticTransactionGenerator {
-  enable: boolean;
-  minDuration: number;
-  maxDuration: number;
-  minDelayBetweenTwoTransactions: number;
-  maxDelayBetweenTwoTransactions: number;
-  probabilityOfStart: number;
-  stopAfterHours: number;
-  stopOnConnectionFailure: boolean;
-  requireAuthorize?: boolean;
-}
-
 export type WsOptions = ClientOptions & ClientRequestArgs;
 
 export default interface ChargingStationTemplate {
@@ -95,6 +84,6 @@ export default interface ChargingStationTemplate {
   phaseLineToLineVoltageMeterValues?: boolean;
   customValueLimitationMeterValues?: boolean;
   Configuration?: ChargingStationOcppConfiguration;
-  AutomaticTransactionGenerator: AutomaticTransactionGenerator;
+  AutomaticTransactionGenerator?: AutomaticTransactionGeneratorConfiguration;
   Connectors: Record<string, ConnectorStatus>;
 }