Fix performance storage jsonfile consistency at write:
[e-mobility-charging-stations-simulator.git] / src / charging-station / Bootstrap.ts
... / ...
CommitLineData
1// Partial Copyright Jerome Benoit. 2021. All Rights Reserved.
2
3import { ChargingStationWorkerData, WorkerMessage, WorkerMessageEvents } from '../types/Worker';
4
5import Configuration from '../utils/Configuration';
6import { Storage } from '../performance/storage/Storage';
7import { StorageFactory } from '../performance/storage/StorageFactory';
8import Utils from '../utils/Utils';
9import WorkerAbstract from '../worker/WorkerAbstract';
10import WorkerFactory from '../worker/WorkerFactory';
11import chalk from 'chalk';
12import { isMainThread } from 'worker_threads';
13import path from 'path';
14import { version } from '../../package.json';
15
16export default class Bootstrap {
17 private static instance: Bootstrap | null = null;
18 private static workerImplementation: WorkerAbstract | null = null;
19 private static storage: Storage;
20 private static numberOfChargingStations: number;
21 private version: string = version;
22 private started: boolean;
23 private workerScript: string;
24
25 private constructor() {
26 this.started = false;
27 this.workerScript = path.join(path.resolve(__dirname, '../'), 'charging-station', 'ChargingStationWorker.js');
28 this.initWorkerImplementation();
29 Bootstrap.storage = StorageFactory.getStorage(Configuration.getPerformanceStorage().type, Configuration.getPerformanceStorage().URI, this.logPrefix());
30 Configuration.setConfigurationChangeCallback(async () => Bootstrap.getInstance().restart());
31 }
32
33 public static getInstance(): Bootstrap {
34 if (!Bootstrap.instance) {
35 Bootstrap.instance = new Bootstrap();
36 }
37 return Bootstrap.instance;
38 }
39
40 public async start(): Promise<void> {
41 if (isMainThread && !this.started) {
42 try {
43 Bootstrap.numberOfChargingStations = 0;
44 await Bootstrap.storage.open();
45 await Bootstrap.workerImplementation.start();
46 // Start ChargingStation object in worker thread
47 if (Configuration.getStationTemplateURLs()) {
48 for (const stationURL of Configuration.getStationTemplateURLs()) {
49 try {
50 const nbStations = stationURL.numberOfStations ?? 0;
51 for (let index = 1; index <= nbStations; index++) {
52 const workerData: ChargingStationWorkerData = {
53 index,
54 templateFile: path.join(path.resolve(__dirname, '../'), 'assets', 'station-templates', path.basename(stationURL.file))
55 };
56 await Bootstrap.workerImplementation.addElement(workerData);
57 Bootstrap.numberOfChargingStations++;
58 }
59 } catch (error) {
60 console.error(chalk.red('Charging station start with template file ' + stationURL.file + ' error '), error);
61 }
62 }
63 } else {
64 console.warn(chalk.yellow('No stationTemplateURLs defined in configuration, exiting'));
65 }
66 if (Bootstrap.numberOfChargingStations === 0) {
67 console.warn(chalk.yellow('No charging station template enabled in configuration, exiting'));
68 } else {
69 console.log(chalk.green(`Charging stations simulator ${this.version} started with ${Bootstrap.numberOfChargingStations.toString()} charging station(s) and ${Utils.workerDynamicPoolInUse() ? `${Configuration.getWorkerPoolMinSize().toString()}/` : ''}${Bootstrap.workerImplementation.size}${Utils.workerPoolInUse() ? `/${Configuration.getWorkerPoolMaxSize().toString()}` : ''} worker(s) concurrently running in '${Configuration.getWorkerProcess()}' mode${Bootstrap.workerImplementation.maxElementsPerWorker ? ` (${Bootstrap.workerImplementation.maxElementsPerWorker} charging station(s) per worker)` : ''}`));
70 }
71 this.started = true;
72 } catch (error) {
73 console.error(chalk.red('Bootstrap start error '), error);
74 }
75 }
76 }
77
78 public async stop(): Promise<void> {
79 if (isMainThread && this.started) {
80 await Bootstrap.workerImplementation.stop();
81 await Bootstrap.storage.close();
82 }
83 this.started = false;
84 }
85
86 public async restart(): Promise<void> {
87 await this.stop();
88 this.initWorkerImplementation();
89 await this.start();
90 }
91
92 private initWorkerImplementation(): void {
93 Bootstrap.workerImplementation = WorkerFactory.getWorkerImplementation<ChargingStationWorkerData>(this.workerScript, Configuration.getWorkerProcess(),
94 {
95 startDelay: Configuration.getWorkerStartDelay(),
96 poolMaxSize: Configuration.getWorkerPoolMaxSize(),
97 poolMinSize: Configuration.getWorkerPoolMinSize(),
98 elementsPerWorker: Configuration.getChargingStationsPerWorker(),
99 poolOptions: {
100 workerChoiceStrategy: Configuration.getWorkerPoolStrategy()
101 },
102 messageHandler: async (msg: WorkerMessage) => {
103 if (msg.id === WorkerMessageEvents.PERFORMANCE_STATISTICS) {
104 await Bootstrap.storage.storePerformanceStatistics(msg.data);
105 }
106 }
107 });
108 }
109
110 private logPrefix(): string {
111 return Utils.logPrefix(' Bootstrap |');
112 }
113}