--- /dev/null
+import Configuration from '../utils/Configuration';
+import { StationWorkerData } from '../types/Worker';
+import Utils from '../utils/Utils';
+import WorkerFactory from '../worker/WorkerFactory';
+import Wrk from '../worker/Wrk';
+import { isMainThread } from 'worker_threads';
+
+export default class Bootstrap {
+ private static instance: Bootstrap;
+ private isStarted: boolean;
+ private workerScript: string;
+ private workerImplementation: Wrk;
+
+ private constructor() {
+ this.isStarted = false;
+ this.workerScript = './dist/charging-station/StationWorker.js';
+ }
+
+ public static getInstance(): Bootstrap {
+ if (!Bootstrap.instance) {
+ Bootstrap.instance = new Bootstrap();
+ }
+ return Bootstrap.instance;
+ }
+
+ public async start(): Promise<void> {
+ if (isMainThread && !this.isStarted) {
+ try {
+ let numStationsTotal = 0;
+ await this.getWorkerImplementation().start();
+ // Start ChargingStation object in worker thread
+ if (Configuration.getStationTemplateURLs()) {
+ for (const stationURL of Configuration.getStationTemplateURLs()) {
+ try {
+ const nbStations = stationURL.numberOfStations ? stationURL.numberOfStations : 0;
+ for (let index = 1; index <= nbStations; index++) {
+ const workerData: StationWorkerData = {
+ index,
+ templateFile: stationURL.file
+ };
+ await this.getWorkerImplementation().addElement(workerData);
+ numStationsTotal++;
+ }
+ } catch (error) {
+ // eslint-disable-next-line no-console
+ console.error('Charging station start with template file ' + stationURL.file + ' error ', error);
+ }
+ }
+ } else {
+ console.log('No stationTemplateURLs defined in configuration, exiting');
+ }
+ if (numStationsTotal === 0) {
+ console.log('No charging station template enabled in configuration, exiting');
+ } else {
+ console.log(`Charging station simulator started with ${numStationsTotal.toString()} charging station(s) and ${Utils.workerDynamicPoolInUse() ? `${Configuration.getWorkerPoolMinSize().toString()}/` : ''}${this.getWorkerImplementation().size}${Utils.workerPoolInUse() ? `/${Configuration.getWorkerPoolMaxSize().toString()}` : ''} worker(s) concurrently running in '${Configuration.getWorkerProcess()}' mode (${this.getWorkerImplementation().maxElementsPerWorker} charging station(s) per worker)`);
+ }
+ this.isStarted = true;
+ } catch (error) {
+ // eslint-disable-next-line no-console
+ console.error('Bootstrap start error ', error);
+ }
+ }
+ }
+
+ public async stop(): Promise<void> {
+ if (isMainThread && this.isStarted) {
+ await this.getWorkerImplementation().stop();
+ if (this.workerImplementation) {
+ // Nullify to force worker implementation instance creation
+ this.workerImplementation = null;
+ }
+ }
+ this.isStarted = false;
+ }
+
+ public async restart(): Promise<void> {
+ await this.stop();
+ await this.start();
+ }
+
+ private getWorkerImplementation(): Wrk {
+ if (!this.workerImplementation) {
+ this.workerImplementation = WorkerFactory.getWorkerImpl<StationWorkerData>(this.workerScript, Configuration.getWorkerProcess(), {
+ poolMaxSize: Configuration.getWorkerPoolMaxSize(),
+ poolMinSize: Configuration.getWorkerPoolMinSize(),
+ elementsPerWorker: Configuration.getChargingStationsPerWorker()
+ });
+ }
+ return this.workerImplementation;
+ }
+}
-import Configuration from './utils/Configuration';
-import { StationWorkerData } from './types/Worker';
-import Utils from './utils/Utils';
-import WorkerFactory from './worker/WorkerFactory';
-import Wrk from './worker/Wrk';
+import Bootstrap from './charging-station/Bootstrap';
-class Bootstrap {
- static async start() {
- try {
- let numStationsTotal = 0;
- const workerImplementation: Wrk = WorkerFactory.getWorkerImpl<StationWorkerData>('./dist/charging-station/StationWorker.js', Configuration.getWorkerProcess(), {
- poolMaxSize: Configuration.getWorkerPoolMaxSize(),
- poolMinSize: Configuration.getWorkerPoolMinSize(),
- elementsPerWorker: Configuration.getChargingStationsPerWorker()
- });
- await workerImplementation.start();
- // Start ChargingStation object in worker thread
- if (Configuration.getStationTemplateURLs()) {
- for (const stationURL of Configuration.getStationTemplateURLs()) {
- try {
- const nbStations = stationURL.numberOfStations ? stationURL.numberOfStations : 0;
- for (let index = 1; index <= nbStations; index++) {
- const workerData: StationWorkerData = {
- index,
- templateFile: stationURL.file
- };
- await workerImplementation.addElement(workerData);
- numStationsTotal++;
- }
- } catch (error) {
- // eslint-disable-next-line no-console
- console.error('Charging station start with template file ' + stationURL.file + ' error ', error);
- }
- }
- } else {
- console.log('No stationTemplateURLs defined in configuration, exiting');
- }
- if (numStationsTotal === 0) {
- console.log('No charging station template enabled in configuration, exiting');
- } else {
- console.log(`Charging station simulator started with ${numStationsTotal.toString()} charging station(s) and ${Utils.workerDynamicPoolInUse() ? `${Configuration.getWorkerPoolMinSize().toString()}/` : ''}${workerImplementation.size}${Utils.workerPoolInUse() ? `/${Configuration.getWorkerPoolMaxSize().toString()}` : ''} worker(s) concurrently running in '${Configuration.getWorkerProcess()}' mode (${workerImplementation.maxElementsPerWorker} charging station(s) per worker)`);
- }
- } catch (error) {
- // eslint-disable-next-line no-console
- console.error('Bootstrap start error ', error);
- }
- }
-}
-
-Bootstrap.start().catch(
+Bootstrap.getInstance().start().catch(
(error) => {
console.error(error);
}
import ConfigurationData, { StationTemplateURL } from '../types/ConfigurationData';
+import Bootstrap from '../charging-station/Bootstrap';
import { WorkerProcessType } from '../types/Worker';
import fs from 'fs';
export default class Configuration {
+ private static configurationFilePath = './src/assets/config.json';
+ private static configurationFileWatcher: fs.FSWatcher;
private static configuration: ConfigurationData;
static getStatisticsDisplayInterval(): number {
// Read the config file
private static getConfig(): ConfigurationData {
if (!Configuration.configuration) {
- Configuration.configuration = JSON.parse(fs.readFileSync('./src/assets/config.json', 'utf8')) as ConfigurationData;
+ Configuration.configuration = JSON.parse(fs.readFileSync(Configuration.configurationFilePath, 'utf8')) as ConfigurationData;
+ if (!Configuration.configurationFileWatcher) {
+ Configuration.configurationFileWatcher = Configuration.getConfigurationFileWatcher();
+ }
}
return Configuration.configuration;
}
+ private static getConfigurationFileWatcher(): fs.FSWatcher {
+ return fs.watch(Configuration.configurationFilePath).on('change', async (e) => {
+ // Nullify to force configuration file reading
+ Configuration.configuration = null;
+ await Bootstrap.getInstance().restart();
+ });
+ }
+
private static objectHasOwnProperty(object: any, property: string): boolean {
return Object.prototype.hasOwnProperty.call(object, property) as boolean;
}
// eslint-disable-next-line @typescript-eslint/no-empty-function
public async start(): Promise<void> { }
+ /**
+ *
+ * @return {Promise<void>}
+ * @public
+ */
+ public async stop(): Promise<void> {
+ return this.pool.destroy();
+ }
+
/**
*
* @return {Promise<void>}
import WorkerSet from './WorkerSet';
import WorkerStaticPool from './WorkerStaticPool';
import Wrk from './Wrk';
+import { isMainThread } from 'worker_threads';
export default class WorkerFactory {
public static getWorkerImpl<T>(workerScript: string, workerProcessType: WorkerProcessType, options?: WorkerOptions): Wrk {
+ if (!isMainThread) {
+ throw new Error('Trying to get a worker implementation outside the main thread');
+ }
if (Utils.isUndefined(options)) {
options = {} as WorkerOptions;
}
export default class WorkerSet<T> extends Wrk {
public maxElementsPerWorker: number;
- private workers: Set<WorkerSetElement>;
+ private workerSet: Set<WorkerSetElement>;
/**
* Create a new `WorkerSet`.
*/
constructor(workerScript: string, maxElementsPerWorker = 1) {
super(workerScript);
- this.workers = new Set<WorkerSetElement>();
+ this.workerSet = new Set<WorkerSetElement>();
this.maxElementsPerWorker = maxElementsPerWorker;
}
get size(): number {
- return this.workers.size;
+ return this.workerSet.size;
}
/**
* @public
*/
public async addElement(elementData: T): Promise<void> {
- if (!this.workers) {
+ if (!this.workerSet) {
throw Error('Cannot add a WorkerSet element: workers\' set does not exist');
}
if (this.getLastWorkerSetElement().numberOfWorkerElements >= this.maxElementsPerWorker) {
await Utils.sleep(Constants.START_WORKER_DELAY);
}
+ /**
+ *
+ * @return {Promise<void>}
+ * @public
+ */
+ public async stop(): Promise<void> {
+ for (const workerSetElement of this.workerSet) {
+ await workerSetElement.worker.terminate();
+ }
+ this.workerSet.clear();
+ }
+
/**
*
* @return {Promise}
}
// FIXME: remove matching worker set element
});
- this.workers.add({ worker, numberOfWorkerElements: 0 });
+ this.workerSet.add({ worker, numberOfWorkerElements: 0 });
}
private getLastWorkerSetElement(): WorkerSetElement {
let workerSetElement: WorkerSetElement;
// eslint-disable-next-line no-empty
- for (workerSetElement of this.workers) { }
+ for (workerSetElement of this.workerSet) { }
return workerSetElement;
}
private getWorkerSetElementByWorker(worker: Worker): WorkerSetElement {
let workerSetElt: WorkerSetElement;
- this.workers.forEach((workerSetElement) => {
+ this.workerSet.forEach((workerSetElement) => {
if (JSON.stringify(workerSetElement.worker) === JSON.stringify(worker)) {
workerSetElt = workerSetElement;
}
// eslint-disable-next-line @typescript-eslint/no-empty-function
public async start(): Promise<void> { }
+ /**
+ *
+ * @return {Promise<void>}
+ * @public
+ */
+ public async stop(): Promise<void> {
+ return this.pool.destroy();
+ }
+
/**
*
* @return {Promise<void>}
}
public abstract start(): Promise<void>;
+ public abstract stop(): Promise<void>;
public abstract addElement(elementData: WorkerData): Promise<void>;
}