feat: add helper to build connectors/evses from configuration
[e-mobility-charging-stations-simulator.git] / src / charging-station / ChargingStation.ts
index c51064f6667c74de7624a062e09546ed1191b7d8..2456d0d6965175a2a1ce366e4cbb7b25a6c90e28 100644 (file)
@@ -46,7 +46,6 @@ import {
   type ChargingStationInfo,
   type ChargingStationOcppConfiguration,
   type ChargingStationTemplate,
-  ConnectorPhaseRotation,
   type ConnectorStatus,
   ConnectorStatusEnum,
   CurrentType,
@@ -109,7 +108,7 @@ export class ChargingStation {
   public automaticTransactionGenerator!: AutomaticTransactionGenerator | undefined;
   public ocppConfiguration!: ChargingStationOcppConfiguration | undefined;
   public wsConnection!: WebSocket | null;
-  public readonly connectors: Map<number, ConnectorStatus>;
+  public connectors: Map<number, ConnectorStatus>;
   public readonly evses: Map<number, EvseStatus>;
   public readonly requests: Map<string, CachedRequest>;
   public performanceStatistics!: PerformanceStatistics | undefined;
@@ -129,6 +128,7 @@ export class ChargingStation {
   private wsConnectionRestarted: boolean;
   private autoReconnectRetryCount: number;
   private templateFileWatcher!: fs.FSWatcher | undefined;
+  private templateFileHash!: string;
   private readonly sharedLRUCache: SharedLRUCache;
   private webSocketPingSetInterval!: NodeJS.Timeout;
   private readonly chargingStationWorkerBroadcastChannel: ChargingStationWorkerBroadcastChannel;
@@ -152,6 +152,10 @@ export class ChargingStation {
     this.initialize();
   }
 
+  public get hasEvses(): boolean {
+    return this.connectors.size === 0 && this.evses.size > 0;
+  }
+
   private get wsConnectionUrl(): URL {
     return new URL(
       `${
@@ -166,10 +170,6 @@ export class ChargingStation {
     );
   }
 
-  private get hasEvses(): boolean {
-    return this.connectors.size === 0 && this.evses.size > 0;
-  }
-
   public logPrefix = (): string => {
     return Utils.logPrefix(
       ` ${
@@ -245,6 +245,18 @@ export class ChargingStation {
     return this.getConnectorStatus(0)?.availability === AvailabilityType.Operative;
   }
 
+  public hasConnector(connectorId: number): boolean {
+    if (this.hasEvses) {
+      for (const evseStatus of this.evses.values()) {
+        if (evseStatus.connectors.has(connectorId)) {
+          return true;
+        }
+      }
+      return false;
+    }
+    return this.connectors.has(connectorId);
+  }
+
   public isConnectorAvailable(connectorId: number): boolean {
     return (
       connectorId > 0 &&
@@ -254,19 +266,27 @@ export class ChargingStation {
 
   public getNumberOfConnectors(): number {
     if (this.hasEvses) {
+      if (this.evses.size === 0) {
+        throw new BaseError('Evses not initialized, cannot get number of connectors');
+      }
       let numberOfConnectors = 0;
       for (const [evseId, evseStatus] of this.evses) {
-        if (evseId === 0) {
-          continue;
+        if (evseId > 0) {
+          numberOfConnectors += evseStatus.connectors.size;
         }
-        numberOfConnectors += evseStatus.connectors.size;
       }
       return numberOfConnectors;
     }
+    if (this.connectors.size === 0) {
+      throw new BaseError('Connectors not initialized, cannot get number of connectors');
+    }
     return this.connectors.has(0) ? this.connectors.size - 1 : this.connectors.size;
   }
 
   public getNumberOfEvses(): number {
+    if (this.evses.size === 0) {
+      throw new BaseError('Evses not initialized, cannot get number of evses');
+    }
     return this.evses.has(0) ? this.evses.size - 1 : this.evses.size;
   }
 
@@ -277,6 +297,7 @@ export class ChargingStation {
           return evseStatus.connectors.get(connectorId);
         }
       }
+      return undefined;
     }
     return this.connectors.get(connectorId);
   }
@@ -292,8 +313,8 @@ export class ChargingStation {
   public getVoltageOut(stationInfo?: ChargingStationInfo): number | undefined {
     const defaultVoltageOut = ChargingStationUtils.getDefaultVoltageOut(
       this.getCurrentOutType(stationInfo),
-      this.templateFile,
-      this.logPrefix()
+      this.logPrefix(),
+      this.templateFile
     );
     const localStationInfo: ChargingStationInfo = stationInfo ?? this.stationInfo;
     return !Utils.isUndefined(localStationInfo.voltageOut)
@@ -317,7 +338,8 @@ export class ChargingStation {
           ? ACElectricUtils.powerTotal(
               this.getNumberOfPhases(),
               this.getVoltageOut(),
-              this.getAmperageLimitation() * this.getNumberOfConnectors()
+              this.getAmperageLimitation() *
+                (this.hasEvses ? this.getNumberOfEvses() : this.getNumberOfConnectors())
             )
           : DCElectricUtils.power(this.getVoltageOut(), this.getAmperageLimitation())) /
         this.powerDivider;
@@ -355,6 +377,26 @@ export class ChargingStation {
     }
   }
 
+  public getNumberOfRunningTransactions(): number {
+    let trxCount = 0;
+    if (this.hasEvses) {
+      for (const evseStatus of this.evses.values()) {
+        for (const connectorStatus of evseStatus.connectors.values()) {
+          if (connectorStatus.transactionStarted === true) {
+            ++trxCount;
+          }
+        }
+      }
+    } else {
+      for (const connectorId of this.connectors.keys()) {
+        if (connectorId > 0 && this.getConnectorStatus(connectorId)?.transactionStarted === true) {
+          ++trxCount;
+        }
+      }
+    }
+    return trxCount;
+  }
+
   public getOutOfOrderEndMeterValues(): boolean {
     return this.stationInfo?.outOfOrderEndMeterValues ?? false;
   }
@@ -611,7 +653,7 @@ export class ChargingStation {
                     this.templateFile
                   } file have changed, reload`
                 );
-                this.sharedLRUCache.deleteChargingStationTemplate(this.stationInfo?.templateHash);
+                this.sharedLRUCache.deleteChargingStationTemplate(this.templateFileHash);
                 // Initialize
                 this.initialize();
                 // Restart the ATG
@@ -658,7 +700,7 @@ export class ChargingStation {
         }
         this.sharedLRUCache.deleteChargingStationConfiguration(this.configurationFileHash);
         this.templateFileWatcher?.close();
-        this.sharedLRUCache.deleteChargingStationTemplate(this.stationInfo?.templateHash);
+        this.sharedLRUCache.deleteChargingStationTemplate(this.templateFileHash);
         delete this.bootNotificationResponse;
         this.started = false;
         parentPort?.postMessage(MessageChannelUtils.buildStoppedMessage(this));
@@ -680,7 +722,7 @@ export class ChargingStation {
 
   public saveOcppConfiguration(): void {
     if (this.getOcppPersistentConfiguration()) {
-      this.saveConfiguration({ stationInfo: false, connectors: false, evses: false });
+      this.saveConfiguration();
     }
   }
 
@@ -703,8 +745,7 @@ export class ChargingStation {
     }
   ): void {
     options.handshakeTimeout = options?.handshakeTimeout ?? this.getConnectionTimeout() * 1000;
-    params.closeOpened = params?.closeOpened ?? false;
-    params.terminateOpened = params?.terminateOpened ?? false;
+    params = { ...{ closeOpened: false, terminateOpened: false }, ...params };
     if (this.started === false && this.starting === false) {
       logger.warn(
         `${this.logPrefix()} Cannot open OCPP connection to URL ${this.wsConnectionUrl.toString()} on stopped charging station`
@@ -872,8 +913,8 @@ export class ChargingStation {
   private getTemplateFromFile(): ChargingStationTemplate | undefined {
     let template: ChargingStationTemplate;
     try {
-      if (this.sharedLRUCache.hasChargingStationTemplate(this.stationInfo?.templateHash)) {
-        template = this.sharedLRUCache.getChargingStationTemplate(this.stationInfo.templateHash);
+      if (this.sharedLRUCache.hasChargingStationTemplate(this.templateFileHash)) {
+        template = this.sharedLRUCache.getChargingStationTemplate(this.templateFileHash);
       } else {
         const measureId = `${FileType.ChargingStationTemplate} read`;
         const beginId = PerformanceStatistics.beginMeasure(measureId);
@@ -886,6 +927,7 @@ export class ChargingStation {
           .update(JSON.stringify(template))
           .digest('hex');
         this.sharedLRUCache.setChargingStationTemplate(template);
+        this.templateFileHash = template.templateHash;
       }
     } catch (error) {
       FileUtils.handleFileException(
@@ -900,21 +942,19 @@ export class ChargingStation {
 
   private getStationInfoFromTemplate(): ChargingStationInfo {
     const stationTemplate: ChargingStationTemplate | undefined = this.getTemplateFromFile();
-    if (Utils.isNullOrUndefined(stationTemplate)) {
-      const errorMsg = `Failed to read charging station template file ${this.templateFile}`;
-      logger.error(`${this.logPrefix()} ${errorMsg}`);
-      throw new BaseError(errorMsg);
-    }
-    if (Utils.isEmptyObject(stationTemplate)) {
-      const errorMsg = `Empty charging station information from template file ${this.templateFile}`;
-      logger.error(`${this.logPrefix()} ${errorMsg}`);
-      throw new BaseError(errorMsg);
-    }
+    ChargingStationUtils.checkTemplateFile(stationTemplate, this.logPrefix(), this.templateFile);
     ChargingStationUtils.warnTemplateKeysDeprecation(
-      this.templateFile,
       stationTemplate,
-      this.logPrefix()
+      this.logPrefix(),
+      this.templateFile
     );
+    if (stationTemplate?.Connectors) {
+      ChargingStationUtils.checkConnectorsConfiguration(
+        stationTemplate,
+        this.logPrefix(),
+        this.templateFile
+      );
+    }
     const stationInfo: ChargingStationInfo =
       ChargingStationUtils.stationTemplateToStationInfo(stationTemplate);
     stationInfo.hashId = ChargingStationUtils.getHashId(this.index, stationTemplate);
@@ -962,18 +1002,18 @@ export class ChargingStation {
     stationInfo.resetTime = !Utils.isNullOrUndefined(stationTemplate?.resetTime)
       ? stationTemplate.resetTime * 1000
       : Constants.CHARGING_STATION_DEFAULT_RESET_TIME;
-    // Initialize evses or connectors if needed (FIXME: should be factored out)
-    this.initializeConnectorsOrEvses(stationInfo);
     stationInfo.maximumAmperage = this.getMaximumAmperage(stationInfo);
-    ChargingStationUtils.createStationInfoHash(stationInfo);
     return stationInfo;
   }
 
   private getStationInfoFromFile(): ChargingStationInfo | undefined {
     let stationInfo: ChargingStationInfo | undefined;
-    this.getStationInfoPersistentConfiguration() &&
-      (stationInfo = this.getConfigurationFromFile()?.stationInfo);
-    stationInfo && ChargingStationUtils.createStationInfoHash(stationInfo);
+    if (this.getStationInfoPersistentConfiguration()) {
+      stationInfo = this.getConfigurationFromFile()?.stationInfo;
+      if (stationInfo) {
+        delete stationInfo?.infoHash;
+      }
+    }
     return stationInfo;
   }
 
@@ -983,11 +1023,7 @@ export class ChargingStation {
     // Priority:
     // 1. charging station info from template
     // 2. charging station info from configuration file
-    // 3. charging station info attribute
     if (stationInfoFromFile?.templateHash === stationInfoFromTemplate.templateHash) {
-      if (this.stationInfo?.infoHash === stationInfoFromFile?.infoHash) {
-        return this.stationInfo;
-      }
       return stationInfoFromFile;
     }
     stationInfoFromFile &&
@@ -1001,7 +1037,7 @@ export class ChargingStation {
 
   private saveStationInfo(): void {
     if (this.getStationInfoPersistentConfiguration()) {
-      this.saveConfiguration({ ocppConfiguration: false, connectors: false, evses: false });
+      this.saveConfiguration();
     }
   }
 
@@ -1014,16 +1050,28 @@ export class ChargingStation {
   }
 
   private handleUnsupportedVersion(version: OCPPVersion) {
-    const errMsg = `Unsupported protocol version '${version}' configured in template file ${this.templateFile}`;
-    logger.error(`${this.logPrefix()} ${errMsg}`);
-    throw new BaseError(errMsg);
+    const errorMsg = `Unsupported protocol version '${version}' configured in template file ${this.templateFile}`;
+    logger.error(`${this.logPrefix()} ${errorMsg}`);
+    throw new BaseError(errorMsg);
   }
 
   private initialize(): void {
+    const stationTemplate = this.getTemplateFromFile();
+    ChargingStationUtils.checkTemplateFile(stationTemplate, this.logPrefix(), this.templateFile);
     this.configurationFile = path.join(
       path.dirname(this.templateFile.replace('station-templates', 'configurations')),
-      `${ChargingStationUtils.getHashId(this.index, this.getTemplateFromFile())}.json`
+      `${ChargingStationUtils.getHashId(this.index, stationTemplate)}.json`
     );
+    const chargingStationConfiguration = this.getConfigurationFromFile();
+    const featureFlag = false;
+    if (
+      featureFlag &&
+      (chargingStationConfiguration?.connectorsStatus || chargingStationConfiguration?.evsesStatus)
+    ) {
+      this.initializeConnectorsOrEvsesFromFile(chargingStationConfiguration);
+    } else {
+      this.initializeConnectorsOrEvsesFromTemplate(stationTemplate);
+    }
     this.stationInfo = this.getStationInfo();
     if (
       this.stationInfo.firmwareStatus === FirmwareStatus.Installing &&
@@ -1044,9 +1092,6 @@ export class ChargingStation {
       this.stationInfo.firmwareVersion = match?.join('.');
     }
     this.saveStationInfo();
-    // Avoid duplication of connectors or evses related information in RAM
-    delete this.stationInfo?.Connectors;
-    delete this.stationInfo?.Evses;
     this.configuredSupervisionUrl = this.getConfiguredSupervisionUrl();
     if (this.getEnableStatistics() === true) {
       this.performanceStatistics = PerformanceStatistics.getInstance(
@@ -1197,42 +1242,26 @@ export class ChargingStation {
         StandardParametersKey.ConnectorPhaseRotation
       )
     ) {
-      const connectorPhaseRotation = [];
+      const connectorsPhaseRotation: string[] = [];
       if (this.hasEvses) {
         for (const evseStatus of this.evses.values()) {
           for (const connectorId of evseStatus.connectors.keys()) {
-            // AC/DC
-            if (connectorId === 0 && this.getNumberOfPhases() === 0) {
-              connectorPhaseRotation.push(`${connectorId}.${ConnectorPhaseRotation.RST}`);
-            } else if (connectorId > 0 && this.getNumberOfPhases() === 0) {
-              connectorPhaseRotation.push(`${connectorId}.${ConnectorPhaseRotation.NotApplicable}`);
-              // AC
-            } else if (connectorId > 0 && this.getNumberOfPhases() === 1) {
-              connectorPhaseRotation.push(`${connectorId}.${ConnectorPhaseRotation.NotApplicable}`);
-            } else if (connectorId > 0 && this.getNumberOfPhases() === 3) {
-              connectorPhaseRotation.push(`${connectorId}.${ConnectorPhaseRotation.RST}`);
-            }
+            connectorsPhaseRotation.push(
+              ChargingStationUtils.getPhaseRotationValue(connectorId, this.getNumberOfPhases())
+            );
           }
         }
       } else {
         for (const connectorId of this.connectors.keys()) {
-          // AC/DC
-          if (connectorId === 0 && this.getNumberOfPhases() === 0) {
-            connectorPhaseRotation.push(`${connectorId}.${ConnectorPhaseRotation.RST}`);
-          } else if (connectorId > 0 && this.getNumberOfPhases() === 0) {
-            connectorPhaseRotation.push(`${connectorId}.${ConnectorPhaseRotation.NotApplicable}`);
-            // AC
-          } else if (connectorId > 0 && this.getNumberOfPhases() === 1) {
-            connectorPhaseRotation.push(`${connectorId}.${ConnectorPhaseRotation.NotApplicable}`);
-          } else if (connectorId > 0 && this.getNumberOfPhases() === 3) {
-            connectorPhaseRotation.push(`${connectorId}.${ConnectorPhaseRotation.RST}`);
-          }
+          connectorsPhaseRotation.push(
+            ChargingStationUtils.getPhaseRotationValue(connectorId, this.getNumberOfPhases())
+          );
         }
       }
       ChargingStationConfigurationUtils.addConfigurationKey(
         this,
         StandardParametersKey.ConnectorPhaseRotation,
-        connectorPhaseRotation.toString()
+        connectorsPhaseRotation.toString()
       );
     }
     if (
@@ -1278,12 +1307,45 @@ export class ChargingStation {
     this.saveOcppConfiguration();
   }
 
-  private initializeConnectorsOrEvses(stationInfo: ChargingStationInfo) {
-    if (stationInfo?.Connectors && !stationInfo?.Evses) {
-      this.initializeConnectors(stationInfo);
-    } else if (stationInfo?.Evses && !stationInfo?.Connectors) {
-      this.initializeEvses(stationInfo);
-    } else if (stationInfo?.Evses && stationInfo?.Connectors) {
+  private initializeConnectorsOrEvsesFromFile(configuration: ChargingStationConfiguration): void {
+    if (configuration?.connectorsStatus && !configuration?.evsesStatus) {
+      this.connectors = new Map<number, ConnectorStatus>(
+        configuration?.connectorsStatus.map((connectorStatus, connectorId) => [
+          connectorId,
+          connectorStatus,
+        ])
+      );
+    } else if (configuration?.evsesStatus && !configuration?.connectorsStatus) {
+      for (const [evseId, evseStatusConfiguration] of configuration.evsesStatus.entries()) {
+        const evseStatus = Utils.cloneObject(evseStatusConfiguration);
+        delete evseStatus.connectorsStatus;
+        this.evses.set(evseId, {
+          ...evseStatus,
+          connectors: new Map<number, ConnectorStatus>(
+            evseStatusConfiguration.connectorsStatus.map((connectorStatus, connectorId) => [
+              connectorId,
+              connectorStatus,
+            ])
+          ),
+        });
+      }
+    } else if (configuration?.evsesStatus && configuration?.connectorsStatus) {
+      const errorMsg = `Connectors and evses defined at the same time in configuration file ${this.configurationFile}`;
+      logger.error(`${this.logPrefix()} ${errorMsg}`);
+      throw new BaseError(errorMsg);
+    } else {
+      const errorMsg = `No connectors or evses defined in configuration file ${this.configurationFile}`;
+      logger.error(`${this.logPrefix()} ${errorMsg}`);
+      throw new BaseError(errorMsg);
+    }
+  }
+
+  private initializeConnectorsOrEvsesFromTemplate(stationTemplate: ChargingStationTemplate) {
+    if (stationTemplate?.Connectors && !stationTemplate?.Evses) {
+      this.initializeConnectorsFromTemplate(stationTemplate);
+    } else if (stationTemplate?.Evses && !stationTemplate?.Connectors) {
+      this.initializeEvsesFromTemplate(stationTemplate);
+    } else if (stationTemplate?.Evses && stationTemplate?.Connectors) {
       const errorMsg = `Connectors and evses defined at the same time in template file ${this.templateFile}`;
       logger.error(`${this.logPrefix()} ${errorMsg}`);
       throw new BaseError(errorMsg);
@@ -1294,72 +1356,51 @@ export class ChargingStation {
     }
   }
 
-  private initializeConnectors(stationInfo: ChargingStationInfo): void {
-    if (!stationInfo?.Connectors && this.connectors.size === 0) {
-      const logMsg = `No already defined connectors and charging station information from template ${this.templateFile} with no connectors configuration defined`;
-      logger.error(`${this.logPrefix()} ${logMsg}`);
-      throw new BaseError(logMsg);
+  private initializeConnectorsFromTemplate(stationTemplate: ChargingStationTemplate): void {
+    if (!stationTemplate?.Connectors && this.connectors.size === 0) {
+      const errorMsg = `No already defined connectors and charging station information from template ${this.templateFile} with no connectors configuration defined`;
+      logger.error(`${this.logPrefix()} ${errorMsg}`);
+      throw new BaseError(errorMsg);
     }
-    if (!stationInfo?.Connectors[0]) {
+    if (!stationTemplate?.Connectors[0]) {
       logger.warn(
         `${this.logPrefix()} Charging station information from template ${
           this.templateFile
         } with no connector id 0 configuration`
       );
     }
-    if (stationInfo?.Connectors) {
-      const configuredMaxConnectors =
-        ChargingStationUtils.getConfiguredNumberOfConnectors(stationInfo);
-      ChargingStationUtils.checkConfiguredMaxConnectors(
-        configuredMaxConnectors,
-        this.templateFile,
-        this.logPrefix()
-      );
+    if (stationTemplate?.Connectors) {
+      const { configuredMaxConnectors, templateMaxConnectors, templateMaxAvailableConnectors } =
+        ChargingStationUtils.checkConnectorsConfiguration(
+          stationTemplate,
+          this.logPrefix(),
+          this.templateFile
+        );
       const connectorsConfigHash = crypto
         .createHash(Constants.DEFAULT_HASH_ALGORITHM)
-        .update(`${JSON.stringify(stationInfo?.Connectors)}${configuredMaxConnectors.toString()}`)
+        .update(
+          `${JSON.stringify(stationTemplate?.Connectors)}${configuredMaxConnectors.toString()}`
+        )
         .digest('hex');
       const connectorsConfigChanged =
         this.connectors?.size !== 0 && this.connectorsConfigurationHash !== connectorsConfigHash;
       if (this.connectors?.size === 0 || connectorsConfigChanged) {
         connectorsConfigChanged && this.connectors.clear();
         this.connectorsConfigurationHash = connectorsConfigHash;
-        const templateMaxConnectors = ChargingStationUtils.getMaxNumberOfConnectors(
-          stationInfo.Connectors
-        );
-        ChargingStationUtils.checkTemplateMaxConnectors(
-          templateMaxConnectors,
-          this.templateFile,
-          this.logPrefix()
-        );
-        const templateMaxAvailableConnectors = stationInfo?.Connectors[0]
-          ? templateMaxConnectors - 1
-          : templateMaxConnectors;
-        if (
-          configuredMaxConnectors > templateMaxAvailableConnectors &&
-          !stationInfo?.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;
-        }
         if (templateMaxConnectors > 0) {
           for (let connectorId = 0; connectorId <= configuredMaxConnectors; connectorId++) {
             if (
               connectorId === 0 &&
-              (!stationInfo?.Connectors[connectorId] ||
-                this.getUseConnectorId0(stationInfo) === false)
+              (!stationTemplate?.Connectors[connectorId] ||
+                this.getUseConnectorId0(stationTemplate) === false)
             ) {
               continue;
             }
             const templateConnectorId =
-              connectorId > 0 && stationInfo?.randomConnectors
+              connectorId > 0 && stationTemplate?.randomConnectors
                 ? Utils.getRandomInteger(templateMaxAvailableConnectors, 1)
                 : connectorId;
-            const connectorStatus = stationInfo?.Connectors[templateConnectorId];
+            const connectorStatus = stationTemplate?.Connectors[templateConnectorId];
             ChargingStationUtils.checkStationInfoConnectorStatus(
               templateConnectorId,
               connectorStatus,
@@ -1387,43 +1428,43 @@ export class ChargingStation {
     }
   }
 
-  private initializeEvses(stationInfo: ChargingStationInfo): void {
-    if (!stationInfo?.Evses && this.evses.size === 0) {
-      const logMsg = `No already defined evses and charging station information from template ${this.templateFile} with no evses configuration defined`;
-      logger.error(`${this.logPrefix()} ${logMsg}`);
-      throw new BaseError(logMsg);
+  private initializeEvsesFromTemplate(stationTemplate: ChargingStationTemplate): void {
+    if (!stationTemplate?.Evses && this.evses.size === 0) {
+      const errorMsg = `No already defined evses and charging station information from template ${this.templateFile} with no evses configuration defined`;
+      logger.error(`${this.logPrefix()} ${errorMsg}`);
+      throw new BaseError(errorMsg);
     }
-    if (!stationInfo?.Evses[0]) {
+    if (!stationTemplate?.Evses[0]) {
       logger.warn(
         `${this.logPrefix()} Charging station information from template ${
           this.templateFile
         } with no evse id 0 configuration`
       );
     }
-    if (!stationInfo?.Evses[0]?.Connectors[0]) {
+    if (!stationTemplate?.Evses[0]?.Connectors[0]) {
       logger.warn(
         `${this.logPrefix()} Charging station information from template ${
           this.templateFile
         } with evse id 0 with no connector id 0 configuration`
       );
     }
-    if (stationInfo?.Evses) {
+    if (stationTemplate?.Evses) {
       const evsesConfigHash = crypto
         .createHash(Constants.DEFAULT_HASH_ALGORITHM)
-        .update(`${JSON.stringify(stationInfo?.Evses)}`)
+        .update(`${JSON.stringify(stationTemplate?.Evses)}`)
         .digest('hex');
       const evsesConfigChanged =
         this.evses?.size !== 0 && this.evsesConfigurationHash !== evsesConfigHash;
       if (this.evses?.size === 0 || evsesConfigChanged) {
         evsesConfigChanged && this.evses.clear();
         this.evsesConfigurationHash = evsesConfigHash;
-        const templateMaxEvses = ChargingStationUtils.getMaxNumberOfEvses(stationInfo?.Evses);
+        const templateMaxEvses = ChargingStationUtils.getMaxNumberOfEvses(stationTemplate?.Evses);
         if (templateMaxEvses > 0) {
-          for (const evse in stationInfo.Evses) {
+          for (const evse in stationTemplate.Evses) {
             const evseId = Utils.convertToInt(evse);
             this.evses.set(evseId, {
               connectors: ChargingStationUtils.buildConnectorsMap(
-                stationInfo?.Evses[evse]?.Connectors,
+                stationTemplate?.Evses[evse]?.Connectors,
                 this.logPrefix(),
                 this.templateFile
               ),
@@ -1467,8 +1508,8 @@ export class ChargingStation {
             fs.readFileSync(this.configurationFile, 'utf8')
           ) as ChargingStationConfiguration;
           PerformanceStatistics.endMeasure(measureId, beginId);
-          this.configurationFileHash = configuration.configurationHash;
           this.sharedLRUCache.setChargingStationConfiguration(configuration);
+          this.configurationFileHash = configuration.configurationHash;
         }
       } catch (error) {
         FileUtils.handleFileException(
@@ -1483,49 +1524,34 @@ export class ChargingStation {
   }
 
   private saveConnectorsStatus() {
-    if (this.getOcppPersistentConfiguration()) {
-      this.saveConfiguration({ stationInfo: false, ocppConfiguration: false, evses: false });
-    }
+    this.saveConfiguration();
   }
 
   private saveEvsesStatus() {
-    if (this.getOcppPersistentConfiguration()) {
-      this.saveConfiguration({ stationInfo: false, ocppConfiguration: false, connectors: false });
-    }
+    this.saveConfiguration();
   }
 
-  private saveConfiguration(
-    params: {
-      stationInfo?: boolean;
-      ocppConfiguration?: boolean;
-      connectors?: boolean;
-      evses?: boolean;
-    } = { stationInfo: true, ocppConfiguration: true, connectors: true, evses: true }
-  ): void {
+  private saveConfiguration(): void {
     if (this.configurationFile) {
-      params = {
-        ...params,
-        ...{ stationInfo: true, ocppConfiguration: true, connectors: true, evses: true },
-      };
       try {
         if (!fs.existsSync(path.dirname(this.configurationFile))) {
           fs.mkdirSync(path.dirname(this.configurationFile), { recursive: true });
         }
         const configurationData: ChargingStationConfiguration =
           Utils.cloneObject(this.getConfigurationFromFile()) ?? {};
-        if (params.stationInfo && this.stationInfo) {
+        if (this.getStationInfoPersistentConfiguration() && this.stationInfo) {
           configurationData.stationInfo = this.stationInfo;
         }
-        if (params.ocppConfiguration && this.ocppConfiguration?.configurationKey) {
+        if (this.getOcppPersistentConfiguration() && this.ocppConfiguration?.configurationKey) {
           configurationData.configurationKey = this.ocppConfiguration.configurationKey;
         }
-        if (params.connectors && this.connectors.size > 0) {
+        if (this.connectors.size > 0) {
           configurationData.connectorsStatus = [...this.connectors.values()].map(
             // eslint-disable-next-line @typescript-eslint/no-unused-vars
             ({ transactionSetInterval, ...connectorStatusRest }) => connectorStatusRest
           );
         }
-        if (params.evses && this.evses.size > 0) {
+        if (this.evses.size > 0) {
           configurationData.evsesStatus = [...this.evses.values()].map((evseStatus) => {
             const status = {
               ...evseStatus,
@@ -1552,8 +1578,8 @@ export class ChargingStation {
           fs.closeSync(fileDescriptor);
           PerformanceStatistics.endMeasure(measureId, beginId);
           this.sharedLRUCache.deleteChargingStationConfiguration(this.configurationFileHash);
-          this.configurationFileHash = configurationHash;
           this.sharedLRUCache.setChargingStationConfiguration(configurationData);
+          this.configurationFileHash = configurationHash;
         } else {
           logger.debug(
             `${this.logPrefix()} Not saving unchanged charging station configuration file ${
@@ -1588,6 +1614,8 @@ export class ChargingStation {
     }
     if (!Utils.isNullOrUndefined(configuration)) {
       delete configuration.stationInfo;
+      delete configuration.connectorsStatus;
+      delete configuration.evsesStatus;
       delete configuration.configurationHash;
     }
     return configuration;
@@ -1756,7 +1784,7 @@ export class ChargingStation {
   private async onMessage(data: RawData): Promise<void> {
     let request: IncomingRequest | Response | ErrorResponse;
     let messageType: number;
-    let errMsg: string;
+    let errorMsg: string;
     try {
       request = JSON.parse(data.toString()) as IncomingRequest | Response | ErrorResponse;
       if (Array.isArray(request) === true) {
@@ -1778,9 +1806,9 @@ export class ChargingStation {
           // Unknown Message
           default:
             // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
-            errMsg = `Wrong message type ${messageType}`;
-            logger.error(`${this.logPrefix()} ${errMsg}`);
-            throw new OCPPError(ErrorType.PROTOCOL_ERROR, errMsg);
+            errorMsg = `Wrong message type ${messageType}`;
+            logger.error(`${this.logPrefix()} ${errorMsg}`);
+            throw new OCPPError(ErrorType.PROTOCOL_ERROR, errorMsg);
         }
         parentPort?.postMessage(MessageChannelUtils.buildUpdatedMessage(this));
       } else {
@@ -1860,29 +1888,8 @@ export class ChargingStation {
     );
   }
 
-  private getUseConnectorId0(stationInfo?: ChargingStationInfo): boolean {
-    const localStationInfo = stationInfo ?? this.stationInfo;
-    return localStationInfo?.useConnectorId0 ?? true;
-  }
-
-  private getNumberOfRunningTransactions(): number {
-    let trxCount = 0;
-    if (this.hasEvses) {
-      for (const evseStatus of this.evses.values()) {
-        for (const connectorStatus of evseStatus.connectors.values()) {
-          if (connectorStatus.transactionStarted === true) {
-            trxCount++;
-          }
-        }
-      }
-    } else {
-      for (const connectorId of this.connectors.keys()) {
-        if (connectorId > 0 && this.getConnectorStatus(connectorId)?.transactionStarted === true) {
-          trxCount++;
-        }
-      }
-    }
-    return trxCount;
+  private getUseConnectorId0(stationTemplate?: ChargingStationTemplate): boolean {
+    return stationTemplate?.useConnectorId0 ?? true;
   }
 
   private async stopRunningTransactions(reason = StopTransactionReason.NONE): Promise<void> {
@@ -1925,25 +1932,18 @@ export class ChargingStation {
 
   // -1 for unlimited, 0 for disabling
   private getAutoReconnectMaxRetries(): number | undefined {
-    if (!Utils.isUndefined(this.stationInfo.autoReconnectMaxRetries)) {
-      return this.stationInfo.autoReconnectMaxRetries;
-    }
-    if (!Utils.isUndefined(Configuration.getAutoReconnectMaxRetries())) {
-      return Configuration.getAutoReconnectMaxRetries();
-    }
-    return -1;
+    return (
+      this.stationInfo.autoReconnectMaxRetries ?? Configuration.getAutoReconnectMaxRetries() ?? -1
+    );
   }
 
   // 0 for disabling
   private getRegistrationMaxRetries(): number | undefined {
-    if (!Utils.isUndefined(this.stationInfo.registrationMaxRetries)) {
-      return this.stationInfo.registrationMaxRetries;
-    }
-    return -1;
+    return this.stationInfo.registrationMaxRetries ?? -1;
   }
 
   private getPowerDivider(): number {
-    let powerDivider = this.getNumberOfConnectors();
+    let powerDivider = this.hasEvses ? this.getNumberOfEvses() : this.getNumberOfConnectors();
     if (this.stationInfo?.powerSharedByConnectors) {
       powerDivider = this.getNumberOfRunningTransactions();
     }
@@ -1956,7 +1956,7 @@ export class ChargingStation {
       case CurrentType.AC:
         return ACElectricUtils.amperagePerPhaseFromPower(
           this.getNumberOfPhases(stationInfo),
-          maximumPower / this.getNumberOfConnectors(),
+          maximumPower / (this.hasEvses ? this.getNumberOfEvses() : this.getNumberOfConnectors()),
           this.getVoltageOut(stationInfo)
         );
       case CurrentType.DC:
@@ -1999,30 +1999,32 @@ export class ChargingStation {
     // Initialize connectors status
     if (this.hasEvses) {
       for (const [evseId, evseStatus] of this.evses) {
-        if (evseId === 0) {
-          continue;
+        if (evseId > 0) {
+          for (const [connectorId, connectorStatus] of evseStatus.connectors) {
+            const connectorBootStatus = ChargingStationUtils.getBootConnectorStatus(
+              this,
+              connectorId,
+              connectorStatus
+            );
+            await OCPPServiceUtils.sendAndSetConnectorStatus(
+              this,
+              connectorId,
+              connectorBootStatus
+            );
+          }
         }
-        for (const [connectorId, connectorStatus] of evseStatus.connectors) {
+      }
+    } else {
+      for (const connectorId of this.connectors.keys()) {
+        if (connectorId > 0) {
           const connectorBootStatus = ChargingStationUtils.getBootConnectorStatus(
             this,
             connectorId,
-            connectorStatus
+            this.getConnectorStatus(connectorId)
           );
           await OCPPServiceUtils.sendAndSetConnectorStatus(this, connectorId, connectorBootStatus);
         }
       }
-    } else {
-      for (const connectorId of this.connectors.keys()) {
-        if (connectorId === 0) {
-          continue;
-        }
-        const connectorBootStatus = ChargingStationUtils.getBootConnectorStatus(
-          this,
-          connectorId,
-          this.getConnectorStatus(connectorId)
-        );
-        await OCPPServiceUtils.sendAndSetConnectorStatus(this, connectorId, connectorBootStatus);
-      }
     }
     if (this.stationInfo?.firmwareStatus === FirmwareStatus.Installing) {
       await this.ocppRequestService.requestHandler<
@@ -2054,21 +2056,43 @@ export class ChargingStation {
     } else {
       await this.stopRunningTransactions(reason);
     }
-    for (const connectorId of this.connectors.keys()) {
-      if (connectorId > 0) {
-        await this.ocppRequestService.requestHandler<
-          StatusNotificationRequest,
-          StatusNotificationResponse
-        >(
-          this,
-          RequestCommand.STATUS_NOTIFICATION,
-          OCPPServiceUtils.buildStatusNotificationRequest(
+    if (this.hasEvses) {
+      for (const [evseId, evseStatus] of this.evses) {
+        if (evseId > 0) {
+          for (const [connectorId, connectorStatus] of evseStatus.connectors) {
+            await this.ocppRequestService.requestHandler<
+              StatusNotificationRequest,
+              StatusNotificationResponse
+            >(
+              this,
+              RequestCommand.STATUS_NOTIFICATION,
+              OCPPServiceUtils.buildStatusNotificationRequest(
+                this,
+                connectorId,
+                ConnectorStatusEnum.Unavailable
+              )
+            );
+            delete connectorStatus?.status;
+          }
+        }
+      }
+    } else {
+      for (const connectorId of this.connectors.keys()) {
+        if (connectorId > 0) {
+          await this.ocppRequestService.requestHandler<
+            StatusNotificationRequest,
+            StatusNotificationResponse
+          >(
             this,
-            connectorId,
-            ConnectorStatusEnum.Unavailable
-          )
-        );
-        delete this.getConnectorStatus(connectorId)?.status;
+            RequestCommand.STATUS_NOTIFICATION,
+            OCPPServiceUtils.buildStatusNotificationRequest(
+              this,
+              connectorId,
+              ConnectorStatusEnum.Unavailable
+            )
+          );
+          delete this.getConnectorStatus(connectorId)?.status;
+        }
       }
     }
   }