Fix charging station initialization order
[e-mobility-charging-stations-simulator.git] / src / charging-station / ChargingStation.ts
index bd23b37d9cdd7a57cc1a616e5a978c2284443a35..3129ae556daa5b639cfc654a02a2070e92b5e70d 100644 (file)
@@ -84,7 +84,6 @@ import type OCPPRequestService from './ocpp/OCPPRequestService';
 import SharedLRUCache from './SharedLRUCache';
 
 export default class ChargingStation {
-  public hashId!: string;
   public readonly templateFile: string;
   public authorizedTagsCache: AuthorizedTagsCache;
   public stationInfo!: ChargingStationInfo;
@@ -96,12 +95,12 @@ export default class ChargingStation {
   public performanceStatistics!: PerformanceStatistics;
   public heartbeatSetInterval!: NodeJS.Timeout;
   public ocppRequestService!: OCPPRequestService;
+  public bootNotificationRequest!: BootNotificationRequest;
   public bootNotificationResponse!: BootNotificationResponse | null;
   public powerDivider!: number;
   private readonly index: number;
   private configurationFile!: string;
   private configurationFileHash!: string;
-  private bootNotificationRequest!: BootNotificationRequest;
   private connectorsConfigurationHash!: string;
   private ocppIncomingRequestService!: OCPPIncomingRequestService;
   private readonly messageBuffer: Set<string>;
@@ -117,15 +116,15 @@ export default class ChargingStation {
   constructor(index: number, templateFile: string) {
     this.index = index;
     this.templateFile = templateFile;
-    this.stopped = false;
-    this.wsConnectionRestarted = false;
-    this.autoReconnectRetryCount = 0;
-    this.sharedLRUCache = SharedLRUCache.getInstance();
-    this.authorizedTagsCache = AuthorizedTagsCache.getInstance();
     this.connectors = new Map<number, ConnectorStatus>();
     this.requests = new Map<string, CachedRequest>();
     this.messageBuffer = new Set<string>();
+    this.sharedLRUCache = SharedLRUCache.getInstance();
+    this.authorizedTagsCache = AuthorizedTagsCache.getInstance();
     this.chargingStationWorkerBroadcastChannel = new ChargingStationWorkerBroadcastChannel(this);
+    this.stopped = false;
+    this.wsConnectionRestarted = false;
+    this.autoReconnectRetryCount = 0;
 
     this.initialize();
   }
@@ -152,10 +151,6 @@ export default class ChargingStation {
     );
   }
 
-  public getBootNotificationRequest(): BootNotificationRequest {
-    return this.bootNotificationRequest;
-  }
-
   public getRandomIdTag(): string {
     const authorizationFile = ChargingStationUtils.getAuthorizationFile(this.stationInfo);
     const index = Math.floor(
@@ -178,8 +173,8 @@ export default class ChargingStation {
       : true;
   }
 
-  public getMayAuthorizeAtRemoteStart(): boolean | undefined {
-    return this.stationInfo.mayAuthorizeAtRemoteStart ?? true;
+  public getMustAuthorizeAtRemoteStart(): boolean | undefined {
+    return this.stationInfo.mustAuthorizeAtRemoteStart ?? true;
   }
 
   public getPayloadSchemaValidation(): boolean | undefined {
@@ -507,7 +502,9 @@ export default class ChargingStation {
             this.initialize();
             // Restart the ATG
             this.stopAutomaticTransactionGenerator();
-            this.startAutomaticTransactionGenerator();
+            if (this.getAutomaticTransactionGeneratorConfigurationFromTemplate()?.enable) {
+              this.startAutomaticTransactionGenerator();
+            }
             if (this.getEnableStatistics()) {
               this.performanceStatistics.restart();
             } else {
@@ -707,8 +704,15 @@ export default class ChargingStation {
         break;
     }
 
+    if (this.isWebSocketConnectionOpened()) {
+      logger.warn(
+        `${this.logPrefix()} OCPP connection to URL ${this.wsConnectionUrl.toString()} is already opened`
+      );
+      return;
+    }
+
     logger.info(
-      this.logPrefix() + ' Open OCPP connection to URL ' + this.wsConnectionUrl.toString()
+      `${this.logPrefix()} Open OCPP connection to URL ${this.wsConnectionUrl.toString()}`
     );
 
     this.wsConnection = new WebSocket(this.wsConnectionUrl, protocol, options);
@@ -743,7 +747,26 @@ export default class ChargingStation {
     }
   }
 
-  private flushMessageBuffer() {
+  public startAutomaticTransactionGenerator(): void {
+    if (!this.automaticTransactionGenerator) {
+      this.automaticTransactionGenerator = AutomaticTransactionGenerator.getInstance(
+        this.getAutomaticTransactionGeneratorConfigurationFromTemplate(),
+        this
+      );
+    }
+    if (!this.automaticTransactionGenerator.started) {
+      this.automaticTransactionGenerator.start();
+    }
+  }
+
+  public stopAutomaticTransactionGenerator(): void {
+    if (this.automaticTransactionGenerator?.started) {
+      this.automaticTransactionGenerator.stop();
+      this.automaticTransactionGenerator = null;
+    }
+  }
+
+  private flushMessageBuffer(): void {
     if (this.messageBuffer.size > 0) {
       this.messageBuffer.forEach((message) => {
         // TODO: evaluate the need to track performance
@@ -817,6 +840,7 @@ export default class ChargingStation {
     );
     const stationInfo: ChargingStationInfo =
       ChargingStationUtils.stationTemplateToStationInfo(stationTemplate);
+    stationInfo.hashId = ChargingStationUtils.getHashId(this.index, stationTemplate);
     stationInfo.chargingStationId = ChargingStationUtils.getChargingStationId(
       this.index,
       stationTemplate
@@ -926,20 +950,19 @@ export default class ChargingStation {
   }
 
   private initialize(): void {
-    this.hashId = ChargingStationUtils.getHashId(this.index, this.getTemplateFromFile());
-    logger.info(`${this.logPrefix()} Charging station hashId '${this.hashId}'`);
     this.configurationFile = path.join(
       path.dirname(this.templateFile.replace('station-templates', 'configurations')),
-      this.hashId + '.json'
+      ChargingStationUtils.getHashId(this.index, this.getTemplateFromFile()) + '.json'
     );
     this.stationInfo = this.getStationInfo();
     this.saveStationInfo();
+    logger.info(`${this.logPrefix()} Charging station hashId '${this.stationInfo.hashId}'`);
     // Avoid duplication of connectors related information in RAM
     this.stationInfo?.Connectors && delete this.stationInfo.Connectors;
     this.configuredSupervisionUrl = this.getConfiguredSupervisionUrl();
     if (this.getEnableStatistics()) {
       this.performanceStatistics = PerformanceStatistics.getInstance(
-        this.hashId,
+        this.stationInfo.hashId,
         this.stationInfo.chargingStationId,
         this.configuredSupervisionUrl
       );
@@ -1515,7 +1538,7 @@ export default class ChargingStation {
     } catch (error) {
       // Log
       logger.error(
-        `${this.logPrefix()} Incoming OCPP '${
+        `${this.logPrefix()} Incoming OCPP command '${
           commandName ?? requestCommandName ?? null
         }' message '${data.toString()}' matching cached request '${JSON.stringify(
           this.requests.get(messageId)
@@ -1524,7 +1547,7 @@ export default class ChargingStation {
       );
       if (!(error instanceof OCPPError)) {
         logger.warn(
-          `${this.logPrefix()} Error thrown at incoming OCPP '${
+          `${this.logPrefix()} Error thrown at incoming OCPP command '${
             commandName ?? requestCommandName ?? null
           }' message '${data.toString()}' handling is not an OCPPError:`,
           error
@@ -1742,27 +1765,8 @@ export default class ChargingStation {
       }
     }
     // Start the ATG
-    this.startAutomaticTransactionGenerator();
-  }
-
-  private startAutomaticTransactionGenerator() {
     if (this.getAutomaticTransactionGeneratorConfigurationFromTemplate()?.enable) {
-      if (!this.automaticTransactionGenerator) {
-        this.automaticTransactionGenerator = AutomaticTransactionGenerator.getInstance(
-          this.getAutomaticTransactionGeneratorConfigurationFromTemplate(),
-          this
-        );
-      }
-      if (!this.automaticTransactionGenerator.started) {
-        this.automaticTransactionGenerator.start();
-      }
-    }
-  }
-
-  private stopAutomaticTransactionGenerator(): void {
-    if (this.automaticTransactionGenerator?.started) {
-      this.automaticTransactionGenerator.stop();
-      this.automaticTransactionGenerator = null;
+      this.startAutomaticTransactionGenerator();
     }
   }
 
@@ -1774,42 +1778,44 @@ export default class ChargingStation {
     // Stop heartbeat
     this.stopHeartbeat();
     // 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) {
-          const transactionId = this.getConnectorStatus(connectorId).transactionId;
-          if (
-            this.getBeginEndMeterValues() &&
-            this.getOcppStrictCompliance() &&
-            !this.getOutOfOrderEndMeterValues()
-          ) {
-            // FIXME: Implement OCPP version agnostic helpers
-            const transactionEndMeterValue = OCPP16ServiceUtils.buildTransactionEndMeterValue(
-              this,
-              connectorId,
-              this.getEnergyActiveImportRegisterByTransactionId(transactionId)
-            );
-            await this.ocppRequestService.requestHandler<MeterValuesRequest, MeterValuesResponse>(
-              this,
-              RequestCommand.METER_VALUES,
-              {
+    if (this.getNumberOfRunningTransactions() > 0) {
+      if (this.automaticTransactionGenerator?.started) {
+        this.stopAutomaticTransactionGenerator();
+      } else {
+        for (const connectorId of this.connectors.keys()) {
+          if (connectorId > 0 && this.getConnectorStatus(connectorId)?.transactionStarted) {
+            const transactionId = this.getConnectorStatus(connectorId).transactionId;
+            if (
+              this.getBeginEndMeterValues() &&
+              this.getOcppStrictCompliance() &&
+              !this.getOutOfOrderEndMeterValues()
+            ) {
+              // FIXME: Implement OCPP version agnostic helpers
+              const transactionEndMeterValue = OCPP16ServiceUtils.buildTransactionEndMeterValue(
+                this,
                 connectorId,
-                transactionId,
-                meterValue: [transactionEndMeterValue],
-              }
-            );
+                this.getEnergyActiveImportRegisterByTransactionId(transactionId)
+              );
+              await this.ocppRequestService.requestHandler<MeterValuesRequest, MeterValuesResponse>(
+                this,
+                RequestCommand.METER_VALUES,
+                {
+                  connectorId,
+                  transactionId,
+                  meterValue: [transactionEndMeterValue],
+                }
+              );
+            }
+            await this.ocppRequestService.requestHandler<
+              StopTransactionRequest,
+              StopTransactionResponse
+            >(this, RequestCommand.STOP_TRANSACTION, {
+              transactionId,
+              meterStop: this.getEnergyActiveImportRegisterByTransactionId(transactionId),
+              idTag: this.getTransactionIdTag(transactionId),
+              reason,
+            });
           }
-          await this.ocppRequestService.requestHandler<
-            StopTransactionRequest,
-            StopTransactionResponse
-          >(this, RequestCommand.STOP_TRANSACTION, {
-            transactionId,
-            meterStop: this.getEnergyActiveImportRegisterByTransactionId(transactionId),
-            idTag: this.getTransactionIdTag(transactionId),
-            reason,
-          });
         }
       }
     }