refactor: remove getter on stationInfo properties
[e-mobility-charging-stations-simulator.git] / src / charging-station / ChargingStation.ts
index d9f548d71c47aa15ee55d4ebf32ccdbde5ffad91..ae5f0fd1a965a6ea8b58660adce1dbf5873ec440 100644 (file)
@@ -1,6 +1,7 @@
 // Partial Copyright Jerome Benoit. 2021-2023. All Rights Reserved.
 
 import { createHash } from 'node:crypto';
+import { EventEmitter } from 'node:events';
 import { type FSWatcher, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
 import { dirname, join } from 'node:path';
 import { URL } from 'node:url';
@@ -67,6 +68,7 @@ import {
   type BootNotificationResponse,
   type CachedRequest,
   type ChargingStationConfiguration,
+  ChargingStationEvents,
   type ChargingStationInfo,
   type ChargingStationOcppConfiguration,
   type ChargingStationTemplate,
@@ -152,16 +154,15 @@ import {
   watchJsonFile,
 } from '../utils';
 
-export class ChargingStation {
+export class ChargingStation extends EventEmitter {
   public readonly index: number;
   public readonly templateFile: string;
-  public stationInfo!: ChargingStationInfo;
   public started: boolean;
   public starting: boolean;
   public idTagsCache: IdTagsCache;
   public automaticTransactionGenerator!: AutomaticTransactionGenerator | undefined;
   public ocppConfiguration!: ChargingStationOcppConfiguration | undefined;
-  public wsConnection!: WebSocket | null;
+  public wsConnection: WebSocket | null;
   public readonly connectors: Map<number, ConnectorStatus>;
   public readonly evses: Map<number, EvseStatus>;
   public readonly requests: Map<string, CachedRequest>;
@@ -171,6 +172,7 @@ export class ChargingStation {
   public bootNotificationRequest!: BootNotificationRequest;
   public bootNotificationResponse!: BootNotificationResponse | undefined;
   public powerDivider!: number;
+  private internalStationInfo!: ChargingStationInfo;
   private stopping: boolean;
   private configurationFile!: string;
   private configurationFileHash!: string;
@@ -190,9 +192,11 @@ export class ChargingStation {
   private reservationExpirationSetInterval?: NodeJS.Timeout;
 
   constructor(index: number, templateFile: string) {
+    super();
     this.started = false;
     this.starting = false;
     this.stopping = false;
+    this.wsConnection = null;
     this.wsConnectionRestarted = false;
     this.autoReconnectRetryCount = 0;
     this.index = index;
@@ -205,6 +209,16 @@ export class ChargingStation {
     this.idTagsCache = IdTagsCache.getInstance();
     this.chargingStationWorkerBroadcastChannel = new ChargingStationWorkerBroadcastChannel(this);
 
+    this.on(ChargingStationEvents.started, () => {
+      parentPort?.postMessage(buildStartedMessage(this));
+    });
+    this.on(ChargingStationEvents.stopped, () => {
+      parentPort?.postMessage(buildStoppedMessage(this));
+    });
+    this.on(ChargingStationEvents.updated, () => {
+      parentPort?.postMessage(buildUpdatedMessage(this));
+    });
+
     this.initialize();
   }
 
@@ -212,13 +226,42 @@ export class ChargingStation {
     return this.connectors.size === 0 && this.evses.size > 0;
   }
 
+  public get stationInfo(): ChargingStationInfo {
+    return {
+      ...this.internalStationInfo,
+      ...{
+        enableStatistics: false,
+        remoteAuthorization: true,
+        currentOutType: CurrentType.AC,
+        ocppStrictCompliance: true,
+        outOfOrderEndMeterValues: false,
+        beginEndMeterValues: false,
+        meteringPerTransaction: true,
+        transactionDataMeterValues: false,
+        mainVoltageMeterValues: true,
+        phaseLineToLineVoltageMeterValues: false,
+        customValueLimitationMeterValues: true,
+        supervisionUrlOcppConfiguration: false,
+        supervisionUrlOcppKey: VendorParametersKey.ConnectionUrl,
+        ocppVersion: OCPPVersion.VERSION_16,
+        ocppPersistentConfiguration: true,
+        stationInfoPersistentConfiguration: true,
+        automaticTransactionGeneratorPersistentConfiguration: true,
+        autoReconnectMaxRetries: -1,
+        registrationMaxRetries: -1,
+        reconnectExponentialDelay: false,
+        stopTransactionsOnStopped: true,
+      },
+    };
+  }
+
   private get wsConnectionUrl(): URL {
     return new URL(
       `${
-        this.getSupervisionUrlOcppConfiguration() &&
-        isNotEmptyString(this.getSupervisionUrlOcppKey()) &&
-        isNotEmptyString(getConfigurationKey(this, this.getSupervisionUrlOcppKey())?.value)
-          ? getConfigurationKey(this, this.getSupervisionUrlOcppKey())!.value
+        this.stationInfo?.supervisionUrlOcppConfiguration &&
+        isNotEmptyString(this.stationInfo?.supervisionUrlOcppKey) &&
+        isNotEmptyString(getConfigurationKey(this, this.stationInfo.supervisionUrlOcppKey!)?.value)
+          ? getConfigurationKey(this, this.stationInfo.supervisionUrlOcppKey!)!.value
           : this.configuredSupervisionUrl.href
       }/${this.stationInfo.chargingStationId}`,
     );
@@ -239,14 +282,6 @@ export class ChargingStation {
     return isNotEmptyArray(this.idTagsCache.getIdTags(getIdTagsFile(this.stationInfo)!));
   }
 
-  public getEnableStatistics(): boolean {
-    return this.stationInfo.enableStatistics ?? false;
-  }
-
-  public getRemoteAuthorization(): boolean {
-    return this.stationInfo.remoteAuthorization ?? true;
-  }
-
   public getNumberOfPhases(stationInfo?: ChargingStationInfo): number {
     const localStationInfo: ChargingStationInfo = stationInfo ?? this.stationInfo;
     switch (this.getCurrentOutType(stationInfo)) {
@@ -340,27 +375,6 @@ export class ChargingStation {
     return this.connectors.get(connectorId);
   }
 
-  public getCurrentOutType(stationInfo?: ChargingStationInfo): CurrentType {
-    return (stationInfo ?? this.stationInfo)?.currentOutType ?? CurrentType.AC;
-  }
-
-  public getOcppStrictCompliance(): boolean {
-    return this.stationInfo?.ocppStrictCompliance ?? true;
-  }
-
-  public getVoltageOut(stationInfo?: ChargingStationInfo): number {
-    const defaultVoltageOut = getDefaultVoltageOut(
-      this.getCurrentOutType(stationInfo),
-      this.logPrefix(),
-      this.templateFile,
-    );
-    return (stationInfo ?? this.stationInfo).voltageOut ?? defaultVoltageOut;
-  }
-
-  public getMaximumPower(stationInfo?: ChargingStationInfo): number {
-    return (stationInfo ?? this.stationInfo).maximumPower!;
-  }
-
   public getConnectorMaximumAvailablePower(connectorId: number): number {
     let connectorAmperageLimitationPowerLimit: number | undefined;
     if (
@@ -368,17 +382,17 @@ export class ChargingStation {
       this.getAmperageLimitation()! < this.stationInfo.maximumAmperage!
     ) {
       connectorAmperageLimitationPowerLimit =
-        (this.getCurrentOutType() === CurrentType.AC
+        (this.stationInfo?.currentOutType === CurrentType.AC
           ? ACElectricUtils.powerTotal(
               this.getNumberOfPhases(),
-              this.getVoltageOut(),
+              this.stationInfo.voltageOut!,
               this.getAmperageLimitation()! *
                 (this.hasEvses ? this.getNumberOfEvses() : this.getNumberOfConnectors()),
             )
-          : DCElectricUtils.power(this.getVoltageOut(), this.getAmperageLimitation()!)) /
+          : DCElectricUtils.power(this.stationInfo.voltageOut!, this.getAmperageLimitation()!)) /
         this.powerDivider;
     }
-    const connectorMaximumPower = this.getMaximumPower() / this.powerDivider;
+    const connectorMaximumPower = this.stationInfo.maximumPower! / this.powerDivider;
     const connectorChargingProfilesPowerLimit =
       getChargingStationConnectorChargingProfilesPowerLimit(this, connectorId);
     return min(
@@ -431,34 +445,6 @@ export class ChargingStation {
     return numberOfRunningTransactions;
   }
 
-  public getOutOfOrderEndMeterValues(): boolean {
-    return this.stationInfo?.outOfOrderEndMeterValues ?? false;
-  }
-
-  public getBeginEndMeterValues(): boolean {
-    return this.stationInfo?.beginEndMeterValues ?? false;
-  }
-
-  public getMeteringPerTransaction(): boolean {
-    return this.stationInfo?.meteringPerTransaction ?? true;
-  }
-
-  public getTransactionDataMeterValues(): boolean {
-    return this.stationInfo?.transactionDataMeterValues ?? false;
-  }
-
-  public getMainVoltageMeterValues(): boolean {
-    return this.stationInfo?.mainVoltageMeterValues ?? true;
-  }
-
-  public getPhaseLineToLineVoltageMeterValues(): boolean {
-    return this.stationInfo?.phaseLineToLineVoltageMeterValues ?? false;
-  }
-
-  public getCustomValueLimitationMeterValues(): boolean {
-    return this.stationInfo?.customValueLimitationMeterValues ?? true;
-  }
-
   public getConnectorIdByTransactionId(transactionId: number): number | undefined {
     if (this.hasEvses) {
       for (const evseStatus of this.evses.values()) {
@@ -527,10 +513,10 @@ export class ChargingStation {
 
   public setSupervisionUrl(url: string): void {
     if (
-      this.getSupervisionUrlOcppConfiguration() &&
-      isNotEmptyString(this.getSupervisionUrlOcppKey())
+      this.stationInfo?.supervisionUrlOcppConfiguration &&
+      isNotEmptyString(this.stationInfo?.supervisionUrlOcppKey)
     ) {
-      setConfigurationKeyValue(this, this.getSupervisionUrlOcppKey(), url);
+      setConfigurationKeyValue(this, this.stationInfo.supervisionUrlOcppKey!, url);
     } else {
       this.stationInfo.supervisionUrls = url;
       this.saveStationInfo();
@@ -655,7 +641,7 @@ export class ChargingStation {
     if (this.started === false) {
       if (this.starting === false) {
         this.starting = true;
-        if (this.getEnableStatistics() === true) {
+        if (this.stationInfo?.enableStatistics === true) {
           this.performanceStatistics?.start();
         }
         if (hasFeatureProfile(this, SupportedFeatureProfiles.Reservation)) {
@@ -683,10 +669,10 @@ export class ChargingStation {
                 // Restart the ATG
                 this.stopAutomaticTransactionGenerator();
                 delete this.automaticTransactionGeneratorConfiguration;
-                if (this.getAutomaticTransactionGeneratorConfiguration()?.enable === true) {
+                if (this.getAutomaticTransactionGeneratorConfiguration().enable === true) {
                   this.startAutomaticTransactionGenerator();
                 }
-                if (this.getEnableStatistics() === true) {
+                if (this.stationInfo?.enableStatistics === true) {
                   this.performanceStatistics?.restart();
                 } else {
                   this.performanceStatistics?.stop();
@@ -702,7 +688,7 @@ export class ChargingStation {
           },
         );
         this.started = true;
-        parentPort?.postMessage(buildStartedMessage(this));
+        this.emit(ChargingStationEvents.started);
         this.starting = false;
       } else {
         logger.warn(`${this.logPrefix()} Charging station is already starting...`);
@@ -712,13 +698,13 @@ export class ChargingStation {
     }
   }
 
-  public async stop(reason?: StopTransactionReason): Promise<void> {
+  public async stop(reason?: StopTransactionReason, stopTransactions?: boolean): Promise<void> {
     if (this.started === true) {
       if (this.stopping === false) {
         this.stopping = true;
-        await this.stopMessageSequence(reason);
+        await this.stopMessageSequence(reason, stopTransactions);
         this.closeWSConnection();
-        if (this.getEnableStatistics() === true) {
+        if (this.stationInfo?.enableStatistics === true) {
           this.performanceStatistics?.stop();
         }
         if (hasFeatureProfile(this, SupportedFeatureProfiles.Reservation)) {
@@ -730,7 +716,7 @@ export class ChargingStation {
         delete this.bootNotificationResponse;
         this.started = false;
         this.saveConfiguration();
-        parentPort?.postMessage(buildStoppedMessage(this));
+        this.emit(ChargingStationEvents.stopped);
         this.stopping = false;
       } else {
         logger.warn(`${this.logPrefix()} Charging station is already stopping...`);
@@ -748,7 +734,7 @@ export class ChargingStation {
   }
 
   public saveOcppConfiguration(): void {
-    if (this.getOcppPersistentConfiguration()) {
+    if (this.stationInfo?.ocppPersistentConfiguration === true) {
       this.saveConfiguration();
     }
   }
@@ -796,7 +782,7 @@ export class ChargingStation {
 
     this.wsConnection = new WebSocket(
       this.wsConnectionUrl,
-      `ocpp${this.stationInfo.ocppVersion ?? OCPPVersion.VERSION_16}`,
+      `ocpp${this.stationInfo.ocppVersion}`,
       options,
     );
 
@@ -835,17 +821,17 @@ export class ChargingStation {
       let automaticTransactionGeneratorConfiguration:
         | AutomaticTransactionGeneratorConfiguration
         | undefined;
-      const automaticTransactionGeneratorConfigurationFromFile =
-        this.getConfigurationFromFile()?.automaticTransactionGenerator;
+      const stationTemplate = this.getTemplateFromFile();
+      const stationConfiguration = this.getConfigurationFromFile();
       if (
-        this.getAutomaticTransactionGeneratorPersistentConfiguration() &&
-        automaticTransactionGeneratorConfigurationFromFile
+        this.stationInfo?.automaticTransactionGeneratorPersistentConfiguration === true &&
+        stationConfiguration?.stationInfo?.templateHash === stationTemplate?.templateHash &&
+        stationConfiguration?.automaticTransactionGenerator
       ) {
         automaticTransactionGeneratorConfiguration =
-          automaticTransactionGeneratorConfigurationFromFile;
+          stationConfiguration?.automaticTransactionGenerator;
       } else {
-        automaticTransactionGeneratorConfiguration =
-          this.getTemplateFromFile()?.AutomaticTransactionGenerator;
+        automaticTransactionGeneratorConfiguration = stationTemplate?.AutomaticTransactionGenerator;
       }
       this.automaticTransactionGeneratorConfiguration = {
         ...Constants.DEFAULT_ATG_CONFIGURATION,
@@ -869,7 +855,7 @@ export class ChargingStation {
       this.automaticTransactionGenerator?.start();
     }
     this.saveAutomaticTransactionGeneratorConfiguration();
-    parentPort?.postMessage(buildUpdatedMessage(this));
+    this.emit(ChargingStationEvents.updated);
   }
 
   public stopAutomaticTransactionGenerator(connectorIds?: number[]): void {
@@ -881,18 +867,18 @@ export class ChargingStation {
       this.automaticTransactionGenerator?.stop();
     }
     this.saveAutomaticTransactionGeneratorConfiguration();
-    parentPort?.postMessage(buildUpdatedMessage(this));
+    this.emit(ChargingStationEvents.updated);
   }
 
   public async stopTransactionOnConnector(
     connectorId: number,
-    reason = StopTransactionReason.NONE,
+    reason?: StopTransactionReason,
   ): Promise<StopTransactionResponse> {
     const transactionId = this.getConnectorStatus(connectorId)?.transactionId;
     if (
-      this.getBeginEndMeterValues() === true &&
-      this.getOcppStrictCompliance() === true &&
-      this.getOutOfOrderEndMeterValues() === false
+      this.stationInfo?.beginEndMeterValues === true &&
+      this.stationInfo?.ocppStrictCompliance === true &&
+      this.stationInfo?.outOfOrderEndMeterValues === false
     ) {
       // FIXME: Implement OCPP version agnostic helpers
       const transactionEndMeterValue = OCPP16ServiceUtils.buildTransactionEndMeterValue(
@@ -916,7 +902,7 @@ export class ChargingStation {
       {
         transactionId,
         meterStop: this.getEnergyActiveImportRegisterByTransactionId(transactionId!, true),
-        reason,
+        ...(isNullOrUndefined(reason) && { reason }),
       },
     );
   }
@@ -1033,7 +1019,7 @@ export class ChargingStation {
   }
 
   private stopReservationExpirationSetInterval(): void {
-    if (this.reservationExpirationSetInterval) {
+    if (!isNullOrUndefined(this.reservationExpirationSetInterval)) {
       clearInterval(this.reservationExpirationSetInterval);
     }
   }
@@ -1089,14 +1075,6 @@ export class ChargingStation {
     }
   }
 
-  private getSupervisionUrlOcppConfiguration(): boolean {
-    return this.stationInfo.supervisionUrlOcppConfiguration ?? false;
-  }
-
-  private getSupervisionUrlOcppKey(): string {
-    return this.stationInfo.supervisionUrlOcppKey ?? VendorParametersKey.ConnectionUrl;
-  }
-
   private getTemplateFromFile(): ChargingStationTemplate | undefined {
     let template: ChargingStationTemplate | undefined;
     try {
@@ -1181,7 +1159,7 @@ export class ChargingStation {
 
   private getStationInfoFromFile(): ChargingStationInfo | undefined {
     let stationInfo: ChargingStationInfo | undefined;
-    if (this.getStationInfoPersistentConfiguration()) {
+    if (this.stationInfo?.stationInfoPersistentConfiguration === true) {
       stationInfo = this.getConfigurationFromFile()?.stationInfo;
       if (stationInfo) {
         delete stationInfo?.infoHash;
@@ -1209,23 +1187,11 @@ export class ChargingStation {
   }
 
   private saveStationInfo(): void {
-    if (this.getStationInfoPersistentConfiguration()) {
+    if (this.stationInfo?.stationInfoPersistentConfiguration === true) {
       this.saveConfiguration();
     }
   }
 
-  private getOcppPersistentConfiguration(): boolean {
-    return this.stationInfo?.ocppPersistentConfiguration ?? true;
-  }
-
-  private getStationInfoPersistentConfiguration(): boolean {
-    return this.stationInfo?.stationInfoPersistentConfiguration ?? true;
-  }
-
-  private getAutomaticTransactionGeneratorPersistentConfiguration(): boolean {
-    return this.stationInfo?.automaticTransactionGeneratorPersistentConfiguration ?? true;
-  }
-
   private handleUnsupportedVersion(version: OCPPVersion) {
     const errorMsg = `Unsupported protocol version '${version}' configured in template file ${this.templateFile}`;
     logger.error(`${this.logPrefix()} ${errorMsg}`);
@@ -1239,17 +1205,17 @@ export class ChargingStation {
       dirname(this.templateFile.replace('station-templates', 'configurations')),
       `${getHashId(this.index, stationTemplate)}.json`,
     );
-    const chargingStationConfiguration = this.getConfigurationFromFile();
+    const stationConfiguration = this.getConfigurationFromFile();
     if (
-      chargingStationConfiguration?.stationInfo?.templateHash === stationTemplate?.templateHash &&
+      stationConfiguration?.stationInfo?.templateHash === stationTemplate?.templateHash &&
       // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
-      (chargingStationConfiguration?.connectorsStatus || chargingStationConfiguration?.evsesStatus)
+      (stationConfiguration?.connectorsStatus || stationConfiguration?.evsesStatus)
     ) {
-      this.initializeConnectorsOrEvsesFromFile(chargingStationConfiguration);
+      this.initializeConnectorsOrEvsesFromFile(stationConfiguration);
     } else {
       this.initializeConnectorsOrEvsesFromTemplate(stationTemplate);
     }
-    this.stationInfo = this.getStationInfo();
+    this.internalStationInfo = this.getStationInfo();
     if (
       this.stationInfo.firmwareStatus === FirmwareStatus.Installing &&
       isNotEmptyString(this.stationInfo.firmwareVersion) &&
@@ -1270,7 +1236,7 @@ export class ChargingStation {
     }
     this.saveStationInfo();
     this.configuredSupervisionUrl = this.getConfiguredSupervisionUrl();
-    if (this.getEnableStatistics() === true) {
+    if (this.stationInfo?.enableStatistics === true) {
       this.performanceStatistics = PerformanceStatistics.getInstance(
         this.stationInfo.hashId,
         this.stationInfo.chargingStationId!,
@@ -1293,7 +1259,7 @@ export class ChargingStation {
   }
 
   private initializeOcppServices(): void {
-    const ocppVersion = this.stationInfo.ocppVersion ?? OCPPVersion.VERSION_16;
+    const ocppVersion = this.stationInfo.ocppVersion;
     switch (ocppVersion) {
       case OCPPVersion.VERSION_16:
         this.ocppIncomingRequestService =
@@ -1324,22 +1290,22 @@ export class ChargingStation {
       addConfigurationKey(this, StandardParametersKey.HeartBeatInterval, '0', { visible: false });
     }
     if (
-      this.getSupervisionUrlOcppConfiguration() &&
-      isNotEmptyString(this.getSupervisionUrlOcppKey()) &&
-      !getConfigurationKey(this, this.getSupervisionUrlOcppKey())
+      this.stationInfo?.supervisionUrlOcppConfiguration &&
+      isNotEmptyString(this.stationInfo?.supervisionUrlOcppKey) &&
+      !getConfigurationKey(this, this.stationInfo.supervisionUrlOcppKey!)
     ) {
       addConfigurationKey(
         this,
-        this.getSupervisionUrlOcppKey(),
+        this.stationInfo.supervisionUrlOcppKey!,
         this.configuredSupervisionUrl.href,
         { reboot: true },
       );
     } else if (
-      !this.getSupervisionUrlOcppConfiguration() &&
-      isNotEmptyString(this.getSupervisionUrlOcppKey()) &&
-      getConfigurationKey(this, this.getSupervisionUrlOcppKey())
+      !this.stationInfo?.supervisionUrlOcppConfiguration &&
+      isNotEmptyString(this.stationInfo?.supervisionUrlOcppKey) &&
+      getConfigurationKey(this, this.stationInfo.supervisionUrlOcppKey!)
     ) {
-      deleteConfigurationKey(this, this.getSupervisionUrlOcppKey(), { save: false });
+      deleteConfigurationKey(this, this.stationInfo.supervisionUrlOcppKey!, { save: false });
     }
     if (
       isNotEmptyString(this.stationInfo?.amperageLimitationOcppKey) &&
@@ -1630,7 +1596,7 @@ export class ChargingStation {
   }
 
   private saveAutomaticTransactionGeneratorConfiguration(): void {
-    if (this.getAutomaticTransactionGeneratorPersistentConfiguration()) {
+    if (this.stationInfo?.automaticTransactionGeneratorPersistentConfiguration === true) {
       this.saveConfiguration();
     }
   }
@@ -1652,12 +1618,15 @@ export class ChargingStation {
         let configurationData: ChargingStationConfiguration = this.getConfigurationFromFile()
           ? cloneObject<ChargingStationConfiguration>(this.getConfigurationFromFile()!)
           : {};
-        if (this.getStationInfoPersistentConfiguration() && this.stationInfo) {
+        if (this.stationInfo?.stationInfoPersistentConfiguration === true && this.stationInfo) {
           configurationData.stationInfo = this.stationInfo;
         } else {
           delete configurationData.stationInfo;
         }
-        if (this.getOcppPersistentConfiguration() && this.ocppConfiguration?.configurationKey) {
+        if (
+          this.stationInfo?.ocppPersistentConfiguration === true &&
+          this.ocppConfiguration?.configurationKey
+        ) {
           configurationData.configurationKey = this.ocppConfiguration.configurationKey;
         } else {
           delete configurationData.configurationKey;
@@ -1667,7 +1636,7 @@ export class ChargingStation {
           buildChargingStationAutomaticTransactionGeneratorConfiguration(this),
         );
         if (
-          !this.getAutomaticTransactionGeneratorPersistentConfiguration() ||
+          !this.stationInfo?.automaticTransactionGeneratorPersistentConfiguration ||
           !this.getAutomaticTransactionGeneratorConfiguration()
         ) {
           delete configurationData.automaticTransactionGenerator;
@@ -1689,6 +1658,10 @@ export class ChargingStation {
               stationInfo: configurationData.stationInfo,
               configurationKey: configurationData.configurationKey,
               automaticTransactionGenerator: configurationData.automaticTransactionGenerator,
+              ...(this.connectors.size > 0 && {
+                connectorsStatus: configurationData.connectorsStatus,
+              }),
+              ...(this.evses.size > 0 && { evsesStatus: configurationData.evsesStatus }),
             } as ChargingStationConfiguration),
           )
           .digest('hex');
@@ -1699,7 +1672,7 @@ export class ChargingStation {
             const beginId = PerformanceStatistics.beginMeasure(measureId);
             writeFileSync(
               this.configurationFile,
-              JSON.stringify(configurationData, null, 2),
+              JSON.stringify(configurationData, undefined, 2),
               'utf8',
             );
             PerformanceStatistics.endMeasure(measureId, beginId);
@@ -1742,7 +1715,7 @@ export class ChargingStation {
 
   private getOcppConfigurationFromFile(): ChargingStationOcppConfiguration | undefined {
     const configurationKey = this.getConfigurationFromFile()?.configurationKey;
-    if (this.getOcppPersistentConfiguration() === true && configurationKey) {
+    if (this.stationInfo?.ocppPersistentConfiguration === true && configurationKey) {
       return { configurationKey };
     }
     return undefined;
@@ -1773,7 +1746,7 @@ export class ChargingStation {
             skipBufferingOnError: true,
           });
           if (this.isRegistered() === false) {
-            this.getRegistrationMaxRetries() !== -1 && ++registrationRetryCount;
+            this.stationInfo?.registrationMaxRetries !== -1 && ++registrationRetryCount;
             await sleep(
               this?.bootNotificationResponse?.interval
                 ? secondsToMilliseconds(this.bootNotificationResponse.interval)
@@ -1782,22 +1755,25 @@ export class ChargingStation {
           }
         } while (
           this.isRegistered() === false &&
-          (registrationRetryCount <= this.getRegistrationMaxRetries()! ||
-            this.getRegistrationMaxRetries() === -1)
+          (registrationRetryCount <= this.stationInfo.registrationMaxRetries! ||
+            this.stationInfo?.registrationMaxRetries) === -1
         );
       }
       if (this.isRegistered() === true) {
+        this.emit(ChargingStationEvents.registered);
         if (this.inAcceptedState() === true) {
+          this.emit(ChargingStationEvents.accepted);
           await this.startMessageSequence();
         }
       } else {
         logger.error(
-          `${this.logPrefix()} Registration failure: max retries reached (${this.getRegistrationMaxRetries()}) or retry disabled (${this.getRegistrationMaxRetries()})`,
+          `${this.logPrefix()} Registration failure: max retries reached or retry disabled (${this
+            .stationInfo?.registrationMaxRetries})`,
         );
       }
       this.wsConnectionRestarted = false;
       this.autoReconnectRetryCount = 0;
-      parentPort?.postMessage(buildUpdatedMessage(this));
+      this.emit(ChargingStationEvents.updated);
     } else {
       logger.warn(
         `${this.logPrefix()} Connection to OCPP server through ${this.wsConnectionUrl.toString()} failed`,
@@ -1827,7 +1803,7 @@ export class ChargingStation {
         this.started === true && (await this.reconnect());
         break;
     }
-    parentPort?.postMessage(buildUpdatedMessage(this));
+    this.emit(ChargingStationEvents.updated);
   }
 
   private getCachedRequest(messageType: MessageType, messageId: string): CachedRequest | undefined {
@@ -1847,7 +1823,7 @@ export class ChargingStation {
 
   private async handleIncomingMessage(request: IncomingRequest): Promise<void> {
     const [messageType, messageId, commandName, commandPayload] = request;
-    if (this.getEnableStatistics() === true) {
+    if (this.stationInfo.enableStatistics === true) {
       this.performanceStatistics?.addRequestStatistic(commandName, messageType);
     }
     logger.debug(
@@ -1938,7 +1914,7 @@ export class ChargingStation {
             logger.error(`${this.logPrefix()} ${errorMsg}`);
             throw new OCPPError(ErrorType.PROTOCOL_ERROR, errorMsg);
         }
-        parentPort?.postMessage(buildUpdatedMessage(this));
+        this.emit(ChargingStationEvents.updated);
       } else {
         throw new OCPPError(
           ErrorType.PROTOCOL_ERROR,
@@ -2009,7 +1985,7 @@ export class ChargingStation {
   }
 
   private getEnergyActiveImportRegister(connectorStatus: ConnectorStatus, rounded = false): number {
-    if (this.getMeteringPerTransaction() === true) {
+    if (this.stationInfo?.meteringPerTransaction === true) {
       return (
         (rounded === true
           ? Math.round(connectorStatus.transactionEnergyActiveImportRegisterValue!)
@@ -2027,7 +2003,7 @@ export class ChargingStation {
     return stationTemplate?.useConnectorId0 ?? true;
   }
 
-  private async stopRunningTransactions(reason = StopTransactionReason.NONE): Promise<void> {
+  private async stopRunningTransactions(reason?: StopTransactionReason): Promise<void> {
     if (this.hasEvses) {
       for (const [evseId, evseStatus] of this.evses) {
         if (evseId === 0) {
@@ -2059,16 +2035,6 @@ export class ChargingStation {
     return Constants.DEFAULT_CONNECTION_TIMEOUT;
   }
 
-  // -1 for unlimited, 0 for disabling
-  private getAutoReconnectMaxRetries(): number | undefined {
-    return this.stationInfo.autoReconnectMaxRetries ?? -1;
-  }
-
-  // -1 for unlimited, 0 for disabling
-  private getRegistrationMaxRetries(): number | undefined {
-    return this.stationInfo.registrationMaxRetries ?? -1;
-  }
-
   private getPowerDivider(): number {
     let powerDivider = this.hasEvses ? this.getNumberOfEvses() : this.getNumberOfConnectors();
     if (this.stationInfo?.powerSharedByConnectors) {
@@ -2091,6 +2057,23 @@ export class ChargingStation {
     }
   }
 
+  private getMaximumPower(stationInfo?: ChargingStationInfo): number {
+    return (stationInfo ?? this.stationInfo).maximumPower!;
+  }
+
+  private getCurrentOutType(stationInfo?: ChargingStationInfo): CurrentType {
+    return (stationInfo ?? this.stationInfo).currentOutType!;
+  }
+
+  private getVoltageOut(stationInfo?: ChargingStationInfo): number {
+    const defaultVoltageOut = getDefaultVoltageOut(
+      this.getCurrentOutType(stationInfo),
+      this.logPrefix(),
+      this.templateFile,
+    );
+    return (stationInfo ?? this.stationInfo).voltageOut ?? defaultVoltageOut;
+  }
+
   private getAmperageLimitation(): number | undefined {
     if (
       isNotEmptyString(this.stationInfo?.amperageLimitationOcppKey) &&
@@ -2155,24 +2138,25 @@ export class ChargingStation {
     }
 
     // Start the ATG
-    if (this.getAutomaticTransactionGeneratorConfiguration()?.enable === true) {
+    if (this.getAutomaticTransactionGeneratorConfiguration().enable === true) {
       this.startAutomaticTransactionGenerator();
     }
     this.wsConnectionRestarted === true && this.flushMessageBuffer();
   }
 
   private async stopMessageSequence(
-    reason: StopTransactionReason = StopTransactionReason.NONE,
+    reason?: StopTransactionReason,
+    stopTransactions = this.stationInfo?.stopTransactionsOnStopped,
   ): Promise<void> {
     // Stop WebSocket ping
     this.stopWebSocketPing();
     // Stop heartbeat
     this.stopHeartbeat();
     // Stop ongoing transactions
+    stopTransactions && (await this.stopRunningTransactions(reason));
+    // Stop the ATG
     if (this.automaticTransactionGenerator?.started === true) {
       this.stopAutomaticTransactionGenerator();
-    } else {
-      await this.stopRunningTransactions(reason);
     }
     if (this.hasEvses) {
       for (const [evseId, evseStatus] of this.evses) {
@@ -2306,10 +2290,6 @@ export class ChargingStation {
     }
   }
 
-  private getReconnectExponentialDelay(): boolean {
-    return this.stationInfo?.reconnectExponentialDelay ?? false;
-  }
-
   private async reconnect(): Promise<void> {
     // Stop WebSocket ping
     this.stopWebSocketPing();
@@ -2320,13 +2300,14 @@ export class ChargingStation {
       this.stopAutomaticTransactionGenerator();
     }
     if (
-      this.autoReconnectRetryCount < this.getAutoReconnectMaxRetries()! ||
-      this.getAutoReconnectMaxRetries() === -1
+      this.autoReconnectRetryCount < this.stationInfo.autoReconnectMaxRetries! ||
+      this.stationInfo?.autoReconnectMaxRetries === -1
     ) {
       ++this.autoReconnectRetryCount;
-      const reconnectDelay = this.getReconnectExponentialDelay()
-        ? exponentialDelay(this.autoReconnectRetryCount)
-        : secondsToMilliseconds(this.getConnectionTimeout());
+      const reconnectDelay =
+        this.stationInfo?.reconnectExponentialDelay === true
+          ? exponentialDelay(this.autoReconnectRetryCount)
+          : secondsToMilliseconds(this.getConnectionTimeout());
       const reconnectDelayWithdraw = 1000;
       const reconnectTimeout =
         reconnectDelay && reconnectDelay - reconnectDelayWithdraw > 0
@@ -2349,11 +2330,11 @@ export class ChargingStation {
         { closeOpened: true },
       );
       this.wsConnectionRestarted = true;
-    } else if (this.getAutoReconnectMaxRetries() !== -1) {
+    } else if (this.stationInfo?.autoReconnectMaxRetries !== -1) {
       logger.error(
         `${this.logPrefix()} WebSocket connection retries failure: maximum retries reached (${
           this.autoReconnectRetryCount
-        }) or retries disabled (${this.getAutoReconnectMaxRetries()})`,
+        }) or retries disabled (${this.stationInfo?.autoReconnectMaxRetries})`,
       );
     }
   }