Add initial support for ESM build
[e-mobility-charging-stations-simulator.git] / src / charging-station / Bootstrap.ts
index 18e934db0351ace719cf67f4ca44dd606f0a09e5..82aef5cd28c65a1c4be1246c6a6a2ac9fc2b6443 100644 (file)
@@ -6,17 +6,21 @@ import {
   ChargingStationWorkerMessageEvents,
 } from '../types/ChargingStationWorker';
 
+import { AbstractUIServer } from './ui-server/AbstractUIServer';
+import { ApplicationProtocol } from '../types/UIProtocol';
+import { ChargingStationUtils } from './ChargingStationUtils';
 import Configuration from '../utils/Configuration';
 import { StationTemplateUrl } from '../types/ConfigurationData';
 import Statistics from '../types/Statistics';
 import { Storage } from '../performance/storage/Storage';
 import { StorageFactory } from '../performance/storage/StorageFactory';
-import { UIServiceUtils } from './ui-websocket-services/UIServiceUtils';
-import UIWebSocketServer from './UIWebSocketServer';
+import UIServerFactory from './ui-server/UIServerFactory';
+import { UIServiceUtils } from './ui-server/ui-services/UIServiceUtils';
 import Utils from '../utils/Utils';
 import WorkerAbstract from '../worker/WorkerAbstract';
 import WorkerFactory from '../worker/WorkerFactory';
 import chalk from 'chalk';
+import { fileURLToPath } from 'url';
 import { isMainThread } from 'worker_threads';
 import path from 'path';
 import { version } from '../../package.json';
@@ -24,9 +28,10 @@ import { version } from '../../package.json';
 export default class Bootstrap {
   private static instance: Bootstrap | null = null;
   private workerImplementation: WorkerAbstract<ChargingStationWorkerData> | null = null;
-  private readonly uiWebSocketServer!: UIWebSocketServer;
+  private readonly uiServer!: AbstractUIServer;
   private readonly storage!: Storage;
-  private numberOfChargingStations: number;
+  private numberOfChargingStationTemplates!: number;
+  private numberOfChargingStations!: number;
   private readonly version: string = version;
   private started: boolean;
   private readonly workerScript: string;
@@ -34,14 +39,15 @@ export default class Bootstrap {
   private constructor() {
     this.started = false;
     this.workerScript = path.join(
-      path.resolve(__dirname, '../'), // wouldn't path.resolve(./ChargingStationWorker.js) achieve the same result ?
+      path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../'),
       'charging-station',
-      'ChargingStationWorker.js'
+      'ChargingStationWorker' + path.extname(fileURLToPath(import.meta.url))
     );
+    this.initialize();
     this.initWorkerImplementation();
-    Configuration.getUIWebSocketServer().enabled &&
-      (this.uiWebSocketServer = new UIWebSocketServer({
-        ...Configuration.getUIWebSocketServer().options,
+    Configuration.getUIServer().enabled &&
+      (this.uiServer = UIServerFactory.getUIServerImplementation(ApplicationProtocol.WS, {
+        ...Configuration.getUIServer().options,
         handleProtocols: UIServiceUtils.handleProtocols,
       }));
     Configuration.getPerformanceStorage().enabled &&
@@ -63,13 +69,14 @@ export default class Bootstrap {
   public async start(): Promise<void> {
     if (isMainThread && !this.started) {
       try {
-        this.numberOfChargingStations = 0;
+        this.initialize();
         await this.storage?.open();
         await this.workerImplementation.start();
-        this.uiWebSocketServer?.start();
+        this.uiServer?.start();
         const stationTemplateUrls = Configuration.getStationTemplateUrls();
+        this.numberOfChargingStationTemplates = stationTemplateUrls.length;
         // Start ChargingStation object in worker thread
-        if (stationTemplateUrls) {
+        if (!Utils.isEmptyArray(stationTemplateUrls)) {
           for (const stationTemplateUrl of stationTemplateUrls) {
             try {
               const nbStations = stationTemplateUrl.numberOfStations ?? 0;
@@ -79,14 +86,18 @@ export default class Bootstrap {
             } catch (error) {
               console.error(
                 chalk.red(
-                  'Charging station start with template file ' + stationTemplateUrl.file + ' error '
+                  'Error at starting charging station with template file ' +
+                    stationTemplateUrl.file +
+                    ': '
                 ),
                 error
               );
             }
           }
         } else {
-          console.warn(chalk.yellow('No stationTemplateUrls defined in configuration, exiting'));
+          console.warn(
+            chalk.yellow("'stationTemplateUrls' not defined or empty in configuration, exiting")
+          );
         }
         if (this.numberOfChargingStations === 0) {
           console.warn(
@@ -97,12 +108,14 @@ export default class Bootstrap {
             chalk.green(
               `Charging stations simulator ${
                 this.version
-              } started with ${this.numberOfChargingStations.toString()} charging station(s) and ${
-                Utils.workerDynamicPoolInUse()
+              } started with ${this.numberOfChargingStations.toString()} charging station(s) from ${this.numberOfChargingStationTemplates.toString()} configured charging station template(s) and ${
+                ChargingStationUtils.workerDynamicPoolInUse()
                   ? `${Configuration.getWorkerPoolMinSize().toString()}/`
                   : ''
               }${this.workerImplementation.size}${
-                Utils.workerPoolInUse() ? `/${Configuration.getWorkerPoolMaxSize().toString()}` : ''
+                ChargingStationUtils.workerPoolInUse()
+                  ? `/${Configuration.getWorkerPoolMaxSize().toString()}`
+                  : ''
               } worker(s) concurrently running in '${Configuration.getWorkerProcess()}' mode${
                 this.workerImplementation.maxElementsPerWorker
                   ? ` (${this.workerImplementation.maxElementsPerWorker} charging station(s) per worker)`
@@ -123,7 +136,7 @@ export default class Bootstrap {
   public async stop(): Promise<void> {
     if (isMainThread && this.started) {
       await this.workerImplementation.stop();
-      this.uiWebSocketServer?.stop();
+      this.uiServer?.stop();
       await this.storage?.close();
     } else {
       console.error(chalk.red('Trying to stop the charging stations simulator while not started'));
@@ -133,6 +146,7 @@ export default class Bootstrap {
 
   public async restart(): Promise<void> {
     await this.stop();
+    this.initialize();
     this.initWorkerImplementation();
     await this.start();
   }
@@ -152,9 +166,9 @@ export default class Bootstrap {
         },
         messageHandler: async (msg: ChargingStationWorkerMessage) => {
           if (msg.id === ChargingStationWorkerMessageEvents.STARTED) {
-            this.uiWebSocketServer.chargingStations.add(msg.data.id as string);
+            this.uiServer.chargingStations.add(msg.data.id as string);
           } else if (msg.id === ChargingStationWorkerMessageEvents.STOPPED) {
-            this.uiWebSocketServer.chargingStations.delete(msg.data.id as string);
+            this.uiServer.chargingStations.delete(msg.data.id as string);
           } else if (msg.id === ChargingStationWorkerMessageEvents.PERFORMANCE_STATISTICS) {
             await this.storage.storePerformanceStatistics(msg.data as unknown as Statistics);
           }
@@ -163,6 +177,11 @@ export default class Bootstrap {
     );
   }
 
+  private initialize() {
+    this.numberOfChargingStations = 0;
+    this.numberOfChargingStationTemplates = 0;
+  }
+
   private async startChargingStation(
     index: number,
     stationTemplateUrl: StationTemplateUrl
@@ -170,7 +189,7 @@ export default class Bootstrap {
     const workerData: ChargingStationWorkerData = {
       index,
       templateFile: path.join(
-        path.resolve(__dirname, '../'),
+        path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../'),
         'assets',
         'station-templates',
         path.basename(stationTemplateUrl.file)