fix: skip worker message events processing
[e-mobility-charging-stations-simulator.git] / src / charging-station / Bootstrap.ts
index 6eacfff46c462655a10a84d5209f545424694701..5b2ace54b934d1ad6eee03da8d4a6b94fe100cb5 100644 (file)
@@ -15,9 +15,9 @@ import { BaseError } from '../exception/index.js'
 import { type Storage, StorageFactory } from '../performance/index.js'
 import {
   type ChargingStationData,
+  type ChargingStationInfo,
   type ChargingStationOptions,
   type ChargingStationWorkerData,
-  type ChargingStationWorkerEventError,
   type ChargingStationWorkerMessage,
   type ChargingStationWorkerMessageData,
   ChargingStationWorkerMessageEvents,
@@ -42,7 +42,7 @@ import {
   logger,
   logPrefix
 } from '../utils/index.js'
-import { type WorkerAbstract, WorkerFactory } from '../worker/index.js'
+import { DEFAULT_ELEMENTS_PER_WORKER, type WorkerAbstract, WorkerFactory } from '../worker/index.js'
 import { buildTemplateName, waitChargingStationEvents } from './Helpers.js'
 import type { AbstractUIServer } from './ui-server/AbstractUIServer.js'
 import { UIServerFactory } from './ui-server/UIServerFactory.js'
@@ -59,12 +59,11 @@ enum exitCodes {
 
 export class Bootstrap extends EventEmitter {
   private static instance: Bootstrap | null = null
-  private workerImplementation?: WorkerAbstract<ChargingStationWorkerData>
+  private workerImplementation?: WorkerAbstract<ChargingStationWorkerData, ChargingStationInfo>
   private readonly uiServer: AbstractUIServer
   private storage?: Storage
   private readonly templateStatistics: Map<string, TemplateStatistics>
   private readonly version: string = version
-  private initializedCounters: boolean
   private started: boolean
   private starting: boolean
   private stopping: boolean
@@ -81,16 +80,15 @@ export class Bootstrap extends EventEmitter {
     this.started = false
     this.starting = false
     this.stopping = false
-    this.initializedCounters = false
     this.uiServerStarted = false
     this.templateStatistics = new Map<string, TemplateStatistics>()
-    this.initializeWorkerImplementation(
-      Configuration.getConfigurationSection<WorkerConfiguration>(ConfigurationSection.worker)
-    )
     this.uiServer = UIServerFactory.getUIServerImplementation(
       Configuration.getConfigurationSection<UIServerConfiguration>(ConfigurationSection.uiServer)
     )
     this.initializeCounters()
+    this.initializeWorkerImplementation(
+      Configuration.getConfigurationSection<WorkerConfiguration>(ConfigurationSection.worker)
+    )
     Configuration.configurationChangeCallback = async () => {
       if (isMainThread) {
         await Bootstrap.getInstance().restart()
@@ -168,16 +166,6 @@ export class Bootstrap extends EventEmitter {
           ChargingStationWorkerMessageEvents.performanceStatistics,
           this.workerEventPerformanceStatistics
         )
-        this.on(
-          ChargingStationWorkerMessageEvents.workerElementError,
-          (eventError: ChargingStationWorkerEventError) => {
-            logger.error(
-              `${this.logPrefix()} ${moduleName}.start: Error occurred while handling '${eventError.event}' event on worker:`,
-              eventError
-            )
-          }
-        )
-        this.initializeCounters()
         // eslint-disable-next-line @typescript-eslint/unbound-method
         if (isAsyncFunction(this.workerImplementation?.start)) {
           await this.workerImplementation.start()
@@ -278,7 +266,6 @@ export class Bootstrap extends EventEmitter {
         await this.workerImplementation?.stop()
         this.removeAllListeners()
         this.uiServer.clearCaches()
-        this.initializedCounters = false
         await this.storage?.close()
         delete this.storage
         this.started = false
@@ -293,10 +280,6 @@ export class Bootstrap extends EventEmitter {
 
   private async restart (): Promise<void> {
     await this.stop()
-    // FIXME: initialize worker implementation only if the worker section has changed
-    this.initializeWorkerImplementation(
-      Configuration.getConfigurationSection<WorkerConfiguration>(ConfigurationSection.worker)
-    )
     if (
       this.uiServerStarted &&
       Configuration.getConfigurationSection<UIServerConfiguration>(ConfigurationSection.uiServer)
@@ -305,6 +288,11 @@ export class Bootstrap extends EventEmitter {
       this.uiServer.stop()
       this.uiServerStarted = false
     }
+    this.initializeCounters()
+    // FIXME: initialize worker implementation only if the worker section has changed
+    this.initializeWorkerImplementation(
+      Configuration.getConfigurationSection<WorkerConfiguration>(ConfigurationSection.worker)
+    )
     await this.start()
   }
 
@@ -342,14 +330,18 @@ export class Bootstrap extends EventEmitter {
         elementsPerWorker = this.numberOfConfiguredChargingStations
         break
       case 'auto':
-      default:
         elementsPerWorker =
           this.numberOfConfiguredChargingStations > availableParallelism()
             ? Math.round(this.numberOfConfiguredChargingStations / (availableParallelism() * 1.5))
             : 1
         break
+      default:
+        elementsPerWorker = workerConfiguration.elementsPerWorker ?? DEFAULT_ELEMENTS_PER_WORKER
     }
-    this.workerImplementation = WorkerFactory.getWorkerImplementation<ChargingStationWorkerData>(
+    this.workerImplementation = WorkerFactory.getWorkerImplementation<
+    ChargingStationWorkerData,
+    ChargingStationInfo
+    >(
       join(
         dirname(fileURLToPath(import.meta.url)),
         `ChargingStationWorker${extname(fileURLToPath(import.meta.url))}`
@@ -366,7 +358,9 @@ export class Bootstrap extends EventEmitter {
         elementsPerWorker,
         poolOptions: {
           messageHandler: this.messageHandler.bind(this) as MessageHandler<Worker>,
-          workerOptions: { resourceLimits: workerConfiguration.resourceLimits }
+          ...(workerConfiguration.resourceLimits != null && {
+            workerOptions: { resourceLimits: workerConfiguration.resourceLimits }
+          })
         }
       }
     )
@@ -382,44 +376,40 @@ export class Bootstrap extends EventEmitter {
     //     2
     //   )}`
     // )
+    // Skip worker message events processing
+    // eslint-disable-next-line @typescript-eslint/dot-notation
+    if (msg['uuid'] != null) {
+      return
+    }
+    const { event, data } = msg
     try {
-      switch (msg.event) {
+      switch (event) {
         case ChargingStationWorkerMessageEvents.added:
-          this.emit(ChargingStationWorkerMessageEvents.added, msg.data)
+          this.emit(ChargingStationWorkerMessageEvents.added, data)
           break
         case ChargingStationWorkerMessageEvents.deleted:
-          this.emit(ChargingStationWorkerMessageEvents.deleted, msg.data)
+          this.emit(ChargingStationWorkerMessageEvents.deleted, data)
           break
         case ChargingStationWorkerMessageEvents.started:
-          this.emit(ChargingStationWorkerMessageEvents.started, msg.data)
+          this.emit(ChargingStationWorkerMessageEvents.started, data)
           break
         case ChargingStationWorkerMessageEvents.stopped:
-          this.emit(ChargingStationWorkerMessageEvents.stopped, msg.data)
+          this.emit(ChargingStationWorkerMessageEvents.stopped, data)
           break
         case ChargingStationWorkerMessageEvents.updated:
-          this.emit(ChargingStationWorkerMessageEvents.updated, msg.data)
+          this.emit(ChargingStationWorkerMessageEvents.updated, data)
           break
         case ChargingStationWorkerMessageEvents.performanceStatistics:
-          this.emit(ChargingStationWorkerMessageEvents.performanceStatistics, msg.data)
-          break
-        case ChargingStationWorkerMessageEvents.addedWorkerElement:
-          this.emit(ChargingStationWorkerMessageEvents.addWorkerElement, msg.data)
-          break
-        case ChargingStationWorkerMessageEvents.workerElementError:
-          this.emit(ChargingStationWorkerMessageEvents.workerElementError, msg.data)
+          this.emit(ChargingStationWorkerMessageEvents.performanceStatistics, data)
           break
         default:
           throw new BaseError(
-            `Unknown charging station worker event: '${
-              msg.event
-            }' received with data: ${JSON.stringify(msg.data, undefined, 2)}`
+            `Unknown charging station worker event: '${event}' received with data: ${JSON.stringify(data, undefined, 2)}`
           )
       }
     } catch (error) {
       logger.error(
-        `${this.logPrefix()} ${moduleName}.messageHandler: Error occurred while handling '${
-          msg.event
-        }' event:`,
+        `${this.logPrefix()} ${moduleName}.messageHandler: Error occurred while handling '${event}' event:`,
         error
       )
     }
@@ -497,47 +487,44 @@ export class Bootstrap extends EventEmitter {
   }
 
   private initializeCounters (): void {
-    if (!this.initializedCounters) {
-      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-      const stationTemplateUrls = Configuration.getStationTemplateUrls()!
-      if (isNotEmptyArray(stationTemplateUrls)) {
-        for (const stationTemplateUrl of stationTemplateUrls) {
-          const templateName = buildTemplateName(stationTemplateUrl.file)
-          this.templateStatistics.set(templateName, {
-            configured: stationTemplateUrl.numberOfStations,
-            added: 0,
-            started: 0,
-            indexes: new Set<number>()
-          })
-          this.uiServer.chargingStationTemplates.add(templateName)
-        }
-        if (this.templateStatistics.size !== stationTemplateUrls.length) {
-          console.error(
-            chalk.red(
-              "'stationTemplateUrls' contains duplicate entries, please check your configuration"
-            )
-          )
-          exit(exitCodes.duplicateChargingStationTemplateUrls)
-        }
-      } else {
-        console.error(
-          chalk.red("'stationTemplateUrls' not defined or empty, please check your configuration")
-        )
-        exit(exitCodes.missingChargingStationsConfiguration)
+    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+    const stationTemplateUrls = Configuration.getStationTemplateUrls()!
+    if (isNotEmptyArray(stationTemplateUrls)) {
+      for (const stationTemplateUrl of stationTemplateUrls) {
+        const templateName = buildTemplateName(stationTemplateUrl.file)
+        this.templateStatistics.set(templateName, {
+          configured: stationTemplateUrl.numberOfStations,
+          added: 0,
+          started: 0,
+          indexes: new Set<number>()
+        })
+        this.uiServer.chargingStationTemplates.add(templateName)
       }
-      if (
-        this.numberOfConfiguredChargingStations === 0 &&
-        Configuration.getConfigurationSection<UIServerConfiguration>(ConfigurationSection.uiServer)
-          .enabled !== true
-      ) {
+      if (this.templateStatistics.size !== stationTemplateUrls.length) {
         console.error(
           chalk.red(
-            "'stationTemplateUrls' has no charging station enabled and UI server is disabled, please check your configuration"
+            "'stationTemplateUrls' contains duplicate entries, please check your configuration"
           )
         )
-        exit(exitCodes.noChargingStationTemplates)
+        exit(exitCodes.duplicateChargingStationTemplateUrls)
       }
-      this.initializedCounters = true
+    } else {
+      console.error(
+        chalk.red("'stationTemplateUrls' not defined or empty, please check your configuration")
+      )
+      exit(exitCodes.missingChargingStationsConfiguration)
+    }
+    if (
+      this.numberOfConfiguredChargingStations === 0 &&
+      Configuration.getConfigurationSection<UIServerConfiguration>(ConfigurationSection.uiServer)
+        .enabled !== true
+    ) {
+      console.error(
+        chalk.red(
+          "'stationTemplateUrls' has no charging station enabled and UI server is disabled, please check your configuration"
+        )
+      )
+      exit(exitCodes.noChargingStationTemplates)
     }
   }
 
@@ -545,13 +532,13 @@ export class Bootstrap extends EventEmitter {
     index: number,
     templateFile: string,
     options?: ChargingStationOptions
-  ): Promise<void> {
+  ): Promise<ChargingStationInfo | undefined> {
     if (!this.started && !this.starting) {
       throw new BaseError(
         'Cannot add charging station while the charging stations simulator is not started'
       )
     }
-    await this.workerImplementation?.addElement({
+    const stationInfo = await this.workerImplementation?.addElement({
       index,
       templateFile: join(
         dirname(fileURLToPath(import.meta.url)),
@@ -565,6 +552,7 @@ export class Bootstrap extends EventEmitter {
     const templateStatistics = this.templateStatistics.get(buildTemplateName(templateFile))!
     ++templateStatistics.added
     templateStatistics.indexes.add(index)
+    return stationInfo
   }
 
   private gracefulShutdown (): void {