// In case of multiple instances: add instance index to charging station id
let instanceIndex = process.env.CF_INSTANCE_INDEX ? process.env.CF_INSTANCE_INDEX : 0;
instanceIndex = instanceIndex > 0 ? instanceIndex : '';
-
const idSuffix = stationTemplate.nameSuffix ? stationTemplate.nameSuffix : '';
-
return stationTemplate.fixedName ? stationTemplate.baseName : stationTemplate.baseName + '-' + instanceIndex.toString() + ('000000000' + this.index.toString()).substr(('000000000' + this.index.toString()).length - 4) + idSuffix;
}
import ChargingStation from './ChargingStation';
import Constants from '../utils/Constants';
+import Utils from '../utils/Utils';
if (!isMainThread) {
- startChargingStation({ index: workerData.index as number, templateFile: workerData.templateFile as string });
-
- // Listener: start new charging station from main thread
+ // Add listener to start charging station from main thread
addListener();
+ if (!Utils.isUndefined(workerData)) {
+ startChargingStation({ index: workerData.index as number, templateFile: workerData.templateFile as string });
+ }
}
function addListener() {
import Configuration from './utils/Configuration';
-import Constants from './utils/Constants';
-import Utils from './utils/Utils';
import WorkerData from './types/WorkerData';
-import WorkerGroup from './worker/WorkerGroup';
-import WorkerPool from './worker/WorkerPool';
+import WorkerFactory from './worker/WorkerFactory';
+import Wrk from './worker/Worker';
class Bootstrap {
- static async start() {
+ static start() {
try {
let numStationsTotal = 0;
- let numConcurrentWorkers = 0;
- const chargingStationsPerWorker = Configuration.getChargingStationsPerWorker();
- let chargingStationsPerWorkerCounter = 0;
- let workerImplementation: WorkerGroup | WorkerPool;
- if (Configuration.useWorkerPool()) {
- workerImplementation = new WorkerPool('./dist/charging-station/StationWorker.js');
- void workerImplementation.start();
- }
- // Start each ChargingStation object in a worker thread
+ const workerImplementation: Wrk = WorkerFactory.getWorkerImpl('./dist/charging-station/StationWorker.js');
+ void workerImplementation.start();
+ // Start ChargingStation object in worker thread
if (Configuration.getStationTemplateURLs()) {
for (const stationURL of Configuration.getStationTemplateURLs()) {
try {
index,
templateFile: stationURL.file
};
- if (Configuration.useWorkerPool()) {
- void workerImplementation.addElement(workerData);
- numConcurrentWorkers = workerImplementation.size;
- // Start worker sequentially to optimize memory at start time
- await Utils.sleep(Constants.START_WORKER_DELAY);
- } else {
- // eslint-disable-next-line no-lonely-if
- if (chargingStationsPerWorkerCounter === 0 || chargingStationsPerWorkerCounter >= chargingStationsPerWorker) {
- // Start new WorkerGroup with one charging station
- workerImplementation = new WorkerGroup('./dist/charging-station/StationWorker.js', workerData, chargingStationsPerWorker);
- void workerImplementation.start();
- numConcurrentWorkers++;
- chargingStationsPerWorkerCounter = 1;
- // Start worker sequentially to optimize memory at start time
- await Utils.sleep(Constants.START_WORKER_DELAY);
- } else {
- // Add charging station to existing WorkerGroup
- void workerImplementation.addElement(workerData);
- chargingStationsPerWorkerCounter++;
- }
- }
+ void workerImplementation.addElement(workerData);
numStationsTotal++;
}
} catch (error) {
// eslint-disable-next-line no-console
- console.log('Charging station start with template file ' + stationURL.file + ' error ' + JSON.stringify(error, null, ' '));
+ console.error('Charging station start with template file ' + stationURL.file + ' error ', error);
}
}
} else {
}
if (numStationsTotal === 0) {
console.log('No charging station template enabled in configuration, exiting');
- } else if (Configuration.useWorkerPool()) {
- console.log(`Charging station simulator started with ${numStationsTotal.toString()} charging station(s) and ${numConcurrentWorkers.toString()}/${Configuration.getWorkerPoolMaxSize().toString()} worker(s) concurrently running`);
} else {
- console.log(`Charging station simulator started with ${numStationsTotal.toString()} charging station(s) and ${numConcurrentWorkers.toString()} worker(s) concurrently running (${chargingStationsPerWorker} charging station(s) per worker)`);
+ console.log(`Charging station simulator started with ${numStationsTotal.toString()} charging station(s) and ${workerImplementation.size}${Configuration.useWorkerPool() ? `/${Configuration.getWorkerPoolMaxSize().toString()}` : ''} worker(s) concurrently running (${workerImplementation.maxElementsPerWorker} charging station(s) per worker)`);
}
} catch (error) {
// eslint-disable-next-line no-console
- console.log('Bootstrap start error ' + JSON.stringify(error, null, ' '));
+ console.error('Bootstrap start error ', error);
}
}
}
-Bootstrap.start().catch(
- (error) => {
- console.error(error);
- }
-);
+Bootstrap.start();
export default abstract class Wrk {
protected workerScript: string;
public abstract size: number;
+ public abstract maxElementsPerWorker: number;
/**
* Create a new `Wrk`.
}
public abstract start(): Promise<void>;
- public abstract addElement(elementData: WorkerData): void;
+ public abstract addElement(elementData: WorkerData): Promise<void>;
}
--- /dev/null
+import Configuration from '../utils/Configuration';
+import WorkerPool from './WorkerPool';
+import WorkerSet from './WorkerSet';
+import Wrk from './Worker';
+
+export default class WorkerFactory {
+ public static getWorkerImpl(workerScript: string): Wrk {
+ if (Configuration.useWorkerPool()) {
+ return new WorkerPool(workerScript);
+ }
+ return new WorkerSet(workerScript, Configuration.getChargingStationsPerWorker());
+ }
+}
+++ /dev/null
-import Configuration from '../utils/Configuration';
-import Constants from '../utils/Constants';
-import { Worker } from 'worker_threads';
-import WorkerData from '../types/WorkerData';
-import Wrk from './Worker';
-
-export default class WorkerGroup extends Wrk {
- private worker: Worker;
- private lastElementData: WorkerData;
- private maxWorkerElements: number;
- private numWorkerElements: number;
-
- /**
- * Create a new `WorkerGroup`.
- *
- * @param {string} workerScript
- * @param {WorkerData} workerData
- * @param {number} maxWorkerElements
- */
- constructor(workerScript: string, initialElementData: WorkerData, maxWorkerElements = 1) {
- super(workerScript);
- this.lastElementData = initialElementData;
- this.maxWorkerElements = maxWorkerElements;
- this.numWorkerElements = 0;
- }
-
- get size(): number {
- return this.numWorkerElements;
- }
-
- /**
- *
- * @return {void}
- * @public
- */
- public addElement(elementData: WorkerData): void {
- if (Configuration.useWorkerPool()) {
- throw Error('Cannot add a WorkerGroup element: the worker pool is enabled in configuration');
- }
- if (!this.worker) {
- throw Error('Cannot add a WorkerGroup element: worker does not exist');
- }
- if (this.numWorkerElements >= this.maxWorkerElements) {
- throw Error('Cannot add a WorkerGroup element: max number of elements per worker reached');
- }
- this.lastElementData = elementData;
- this.worker.postMessage({ id: Constants.START_WORKER_ELEMENT, workerData: this.lastElementData });
- this.numWorkerElements++;
- }
-
- /**
- *
- * @return {Promise<Worker>}
- * @public
- */
- public async start(): Promise<void> {
- await this.startWorker();
- }
-
- /**
- *
- * @return {Promise}
- * @private
- */
- private async startWorker() {
- return new Promise((resolve, reject) => {
- const worker = new Worker(this.workerScript, { workerData: this.lastElementData });
- worker.on('message', resolve);
- worker.on('error', reject);
- worker.on('exit', (code) => {
- if (code !== 0) {
- reject(new Error(`Worker stopped with exit code ${code}`));
- }
- });
- this.numWorkerElements++;
- this.worker = worker;
- });
- }
-}
import Configuration from '../utils/Configuration';
+import Constants from '../utils/Constants';
import Pool from 'worker-threads-pool';
+import Utils from '../utils/Utils';
import WorkerData from '../types/WorkerData';
import Wrk from './Worker';
return this.pool.size;
}
+ get maxElementsPerWorker(): number {
+ return 1;
+ }
+
/**
*
* @return {Promise<void>}
* @public
*/
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
public async start(): Promise<void> { }
/**
*
- * @return {Promise}
+ * @return {Promise<void>}
* @public
*/
public async addElement(elementData: WorkerData): Promise<void> {
worker.once('message', resolve);
worker.once('error', reject);
});
+ // Start worker sequentially to optimize memory at startup
+ void Utils.sleep(Constants.START_WORKER_DELAY);
});
}
}
--- /dev/null
+import Constants from '../utils/Constants';
+import Utils from '../utils/Utils';
+import { Worker } from 'worker_threads';
+import WorkerData from '../types/WorkerData';
+import Wrk from './Worker';
+
+export default class WorkerSet extends Wrk {
+ public maxElementsPerWorker: number;
+ private workers: Set<Worker>;
+ private lastWorkerNumberOfElements: number;
+
+ /**
+ * Create a new `WorkerSet`.
+ *
+ * @param {string} workerScript
+ * @param {number} maxElementsPerWorker
+ */
+ constructor(workerScript: string, maxElementsPerWorker = 1) {
+ super(workerScript);
+ this.workers = new Set<Worker>();
+ this.maxElementsPerWorker = maxElementsPerWorker;
+ this.lastWorkerNumberOfElements = 0;
+ }
+
+ get size(): number {
+ return this.workers.size;
+ }
+
+ /**
+ *
+ * @return {Promise<void>}
+ * @public
+ */
+ public async addElement(elementData: WorkerData): Promise<void> {
+ if (!this.workers) {
+ throw Error('Cannot add a WorkerSet element: workers set does not exist');
+ }
+ if (this.lastWorkerNumberOfElements >= this.maxElementsPerWorker) {
+ void this.startWorker();
+ this.lastWorkerNumberOfElements = 0;
+ // Start worker sequentially to optimize memory at startup
+ void Utils.sleep(Constants.START_WORKER_DELAY);
+ }
+ this.getLastWorker().postMessage({ id: Constants.START_WORKER_ELEMENT, workerData: elementData });
+ this.lastWorkerNumberOfElements++;
+ }
+
+ /**
+ *
+ * @return {Promise<void>}
+ * @public
+ */
+ public async start(): Promise<void> {
+ await this.startWorker();
+ // Start worker sequentially to optimize memory at startup
+ await Utils.sleep(Constants.START_WORKER_DELAY);
+ }
+
+ /**
+ *
+ * @return {Promise}
+ * @private
+ */
+ private async startWorker() {
+ return new Promise((resolve, reject) => {
+ const worker = new Worker(this.workerScript);
+ worker.on('message', resolve);
+ worker.on('error', reject);
+ worker.on('exit', (code) => {
+ if (code !== 0) {
+ reject(new Error(`Worker stopped with exit code ${code}`));
+ }
+ });
+ this.workers.add(worker);
+ });
+ }
+
+ private getLastWorker(): Worker {
+ let worker: Worker;
+ // eslint-disable-next-line no-empty
+ for (worker of this.workers) { }
+ return worker;
+ }
+}