fix: properly handle template relative file path within a directory
[e-mobility-charging-stations-simulator.git] / src / charging-station / ChargingStation.ts
index 6b83b1364e6e6add5472eaf533e0e97a643c2c96..82fba5adad98c6629a6579b8e7c8516df38bb7bb 100644 (file)
@@ -8,7 +8,7 @@ import { URL } from 'node:url'
 import { parentPort } from 'node:worker_threads'
 
 import { millisecondsToSeconds, secondsToMilliseconds } from 'date-fns'
-import merge from 'just-merge'
+import { mergeDeepRight } from 'rambda'
 import { type RawData, WebSocket } from 'ws'
 
 import { AutomaticTransactionGenerator } from './AutomaticTransactionGenerator.js'
@@ -88,7 +88,6 @@ import {
   FirmwareStatus,
   type FirmwareStatusNotificationRequest,
   type FirmwareStatusNotificationResponse,
-  type FirmwareUpgrade,
   type HeartbeatRequest,
   type HeartbeatResponse,
   type IncomingRequest,
@@ -162,13 +161,13 @@ export class ChargingStation extends EventEmitter {
   public started: boolean
   public starting: boolean
   public idTagsCache: IdTagsCache
-  public automaticTransactionGenerator!: AutomaticTransactionGenerator | undefined
-  public ocppConfiguration!: ChargingStationOcppConfiguration | undefined
+  public automaticTransactionGenerator?: AutomaticTransactionGenerator
+  public ocppConfiguration?: ChargingStationOcppConfiguration
   public wsConnection: WebSocket | null
   public readonly connectors: Map<number, ConnectorStatus>
   public readonly evses: Map<number, EvseStatus>
   public readonly requests: Map<string, CachedRequest>
-  public performanceStatistics: PerformanceStatistics | undefined
+  public performanceStatistics?: PerformanceStatistics
   public heartbeatSetInterval?: NodeJS.Timeout
   public ocppRequestService!: OCPPRequestService
   public bootNotificationRequest?: BootNotificationRequest
@@ -185,7 +184,7 @@ export class ChargingStation extends EventEmitter {
   private configuredSupervisionUrl!: URL
   private wsConnectionRetried: boolean
   private wsConnectionRetryCount: number
-  private templateFileWatcher: FSWatcher | undefined
+  private templateFileWatcher?: FSWatcher
   private templateFileHash!: string
   private readonly sharedLRUCache: SharedLRUCache
   private wsPingSetInterval?: NodeJS.Timeout
@@ -253,11 +252,6 @@ export class ChargingStation extends EventEmitter {
 
     this.add()
 
-    if (options?.autoStart != null) {
-      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-      this.stationInfo!.autoStart = options.autoStart
-    }
-
     if (this.stationInfo?.autoStart === true) {
       this.start()
     }
@@ -549,8 +543,8 @@ export class ChargingStation extends EventEmitter {
     } else {
       // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
       this.stationInfo!.supervisionUrls = url
-      this.saveStationInfo()
       this.configuredSupervisionUrl = this.getConfiguredSupervisionUrl()
+      this.saveStationInfo()
     }
   }
 
@@ -1168,6 +1162,7 @@ export class ChargingStation extends EventEmitter {
     }
     const stationInfo = stationTemplateToStationInfo(stationTemplate)
     stationInfo.hashId = getHashId(this.index, stationTemplate)
+    stationInfo.templateIndex = this.index
     stationInfo.templateName = parse(this.templateFile).name
     stationInfo.chargingStationId = getChargingStationId(this.index, stationTemplate)
     createSerialNumber(stationTemplate, stationInfo)
@@ -1197,7 +1192,7 @@ export class ChargingStation extends EventEmitter {
         } does not match firmware version pattern '${stationInfo.firmwareVersionPattern}'`
       )
     }
-    stationInfo.firmwareUpgrade = merge<FirmwareUpgrade>(
+    stationInfo.firmwareUpgrade = mergeDeepRight(
       {
         versionUpgrade: {
           step: 1
@@ -1221,6 +1216,11 @@ export class ChargingStation extends EventEmitter {
       stationInfo = this.getConfigurationFromFile()?.stationInfo
       if (stationInfo != null) {
         delete stationInfo.infoHash
+        delete (stationInfo as ChargingStationTemplate).numberOfConnectors
+        // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
+        if (stationInfo.templateIndex == null) {
+          stationInfo.templateIndex = this.index
+        }
         // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
         if (stationInfo.templateName == null) {
           stationInfo.templateName = parse(this.templateFile).name
@@ -1722,7 +1722,7 @@ export class ChargingStation extends EventEmitter {
         } else {
           delete configurationData.configurationKey
         }
-        configurationData = merge<ChargingStationConfiguration>(
+        configurationData = mergeDeepRight(
           configurationData,
           buildChargingStationAutomaticTransactionGeneratorConfiguration(this)
         )
@@ -1824,6 +1824,7 @@ export class ChargingStation extends EventEmitter {
 
   private async onOpen (): Promise<void> {
     if (this.isWebSocketConnectionOpened()) {
+      this.emit(ChargingStationEvents.updated)
       logger.info(
         `${this.logPrefix()} Connection to OCPP server through ${this.wsConnectionUrl.href} succeeded`
       )
@@ -1886,6 +1887,7 @@ export class ChargingStation extends EventEmitter {
 
   private onClose (code: WebSocketCloseEventStatusCode, reason: Buffer): void {
     this.emit(ChargingStationEvents.disconnected)
+    this.emit(ChargingStationEvents.updated)
     switch (code) {
       // Normal close
       case WebSocketCloseEventStatusCode.CLOSE_NORMAL:
@@ -1905,12 +1907,13 @@ export class ChargingStation extends EventEmitter {
           )}' and reason '${reason.toString()}'`
         )
         this.started &&
-          this.reconnect().catch(error =>
-            logger.error(`${this.logPrefix()} Error while reconnecting:`, error)
-          )
+          this.reconnect()
+            .then(() => {
+              this.emit(ChargingStationEvents.updated)
+            })
+            .catch(error => logger.error(`${this.logPrefix()} Error while reconnecting:`, error))
         break
     }
-    this.emit(ChargingStationEvents.updated)
   }
 
   private getCachedRequest (