Avoid circular modules import
[e-mobility-charging-stations-simulator.git] / src / charging-station / Bootstrap.ts
1 import Configuration from '../utils/Configuration';
2 import { StationWorkerData } from '../types/Worker';
3 import Utils from '../utils/Utils';
4 import WorkerAbstract from '../worker/WorkerAbstract';
5 import WorkerFactory from '../worker/WorkerFactory';
6 import { isMainThread } from 'worker_threads';
7 import path from 'path';
8
9 export default class Bootstrap {
10 private static instance: Bootstrap;
11 private started: boolean;
12 private workerScript: string;
13 private workerImplementationInstance: WorkerAbstract;
14
15 private constructor() {
16 this.started = false;
17 this.workerScript = path.join(path.resolve(__dirname, '../'), 'charging-station', 'StationWorker.js');
18 Configuration.setConfigurationChangeCallback(async () => this.restart());
19 }
20
21 public static getInstance(): Bootstrap {
22 if (!Bootstrap.instance) {
23 Bootstrap.instance = new Bootstrap();
24 }
25 return Bootstrap.instance;
26 }
27
28 public async start(): Promise<void> {
29 if (isMainThread && !this.started) {
30 try {
31 let numStationsTotal = 0;
32 await this.getWorkerImplementationInstance().start();
33 // Start ChargingStation object in worker thread
34 if (Configuration.getStationTemplateURLs()) {
35 for (const stationURL of Configuration.getStationTemplateURLs()) {
36 try {
37 const nbStations = stationURL.numberOfStations ? stationURL.numberOfStations : 0;
38 for (let index = 1; index <= nbStations; index++) {
39 const workerData: StationWorkerData = {
40 index,
41 templateFile: path.join(path.resolve(__dirname, '../'), 'assets', 'station-templates', path.basename(stationURL.file))
42 };
43 await this.getWorkerImplementationInstance().addElement(workerData);
44 numStationsTotal++;
45 }
46 } catch (error) {
47 // eslint-disable-next-line no-console
48 console.error('Charging station start with template file ' + stationURL.file + ' error ', error);
49 }
50 }
51 } else {
52 console.log('No stationTemplateURLs defined in configuration, exiting');
53 }
54 if (numStationsTotal === 0) {
55 console.log('No charging station template enabled in configuration, exiting');
56 } else {
57 console.log(`Charging station simulator started with ${numStationsTotal.toString()} charging station(s) and ${Utils.workerDynamicPoolInUse() ? `${Configuration.getWorkerPoolMinSize().toString()}/` : ''}${this.getWorkerImplementationInstance().size}${Utils.workerPoolInUse() ? `/${Configuration.getWorkerPoolMaxSize().toString()}` : ''} worker(s) concurrently running in '${Configuration.getWorkerProcess()}' mode${this.getWorkerImplementationInstance().maxElementsPerWorker ? ` (${this.getWorkerImplementationInstance().maxElementsPerWorker} charging station(s) per worker)` : ''}`);
58 }
59 this.started = true;
60 } catch (error) {
61 // eslint-disable-next-line no-console
62 console.error('Bootstrap start error ', error);
63 }
64 }
65 }
66
67 public async stop(): Promise<void> {
68 if (isMainThread && this.started) {
69 if (this.getWorkerImplementationInstance()) {
70 await this.getWorkerImplementationInstance().stop();
71 // Nullify to force worker implementation instance creation
72 this.workerImplementationInstance = null;
73 }
74 }
75 this.started = false;
76 }
77
78 public async restart(): Promise<void> {
79 await this.stop();
80 await this.start();
81 }
82
83 private getWorkerImplementationInstance(): WorkerAbstract {
84 if (!this.workerImplementationInstance) {
85 this.workerImplementationInstance = WorkerFactory.getWorkerImplementation<StationWorkerData>(this.workerScript, Configuration.getWorkerProcess(), {
86 poolMaxSize: Configuration.getWorkerPoolMaxSize(),
87 poolMinSize: Configuration.getWorkerPoolMinSize(),
88 elementsPerWorker: Configuration.getChargingStationsPerWorker()
89 });
90 }
91 return this.workerImplementationInstance;
92 }
93 }