X-Git-Url: https://git.piment-noir.org/?a=blobdiff_plain;f=src%2Fcharging-station%2FBootstrap.ts;h=64953a9687a8f730a233ce7517ffa1f2842bbd3c;hb=56f945900ccdbffb165bc42026fa20158c7032fe;hp=05fffa5b517692a2ff510b73d5376a66485dd3cd;hpb=8c7d214a2afc023ffdf34bbae8bfd4e16961bcc4;p=e-mobility-charging-stations-simulator.git diff --git a/src/charging-station/Bootstrap.ts b/src/charging-station/Bootstrap.ts index 05fffa5b..64953a96 100644 --- a/src/charging-station/Bootstrap.ts +++ b/src/charging-station/Bootstrap.ts @@ -1,18 +1,15 @@ // Partial Copyright Jerome Benoit. 2021-2024. All Rights Reserved. import { EventEmitter } from 'node:events' -import { dirname, extname, join, parse } from 'node:path' +import { dirname, extname, join } from 'node:path' import process, { exit } from 'node:process' import { fileURLToPath } from 'node:url' import { isMainThread } from 'node:worker_threads' -import type { Worker } from 'worker_threads' import chalk from 'chalk' -import { type MessageHandler, availableParallelism } from 'poolifier' +import { availableParallelism, type MessageHandler } from 'poolifier' +import type { Worker } from 'worker_threads' -import { waitChargingStationEvents } from './Helpers.js' -import type { AbstractUIServer } from './ui-server/AbstractUIServer.js' -import { UIServerFactory } from './ui-server/UIServerFactory.js' import { version } from '../../package.json' import { BaseError } from '../exception/index.js' import { type Storage, StorageFactory } from '../performance/index.js' @@ -25,28 +22,30 @@ import { type ChargingStationWorkerMessageData, ChargingStationWorkerMessageEvents, ConfigurationSection, - type InternalTemplateStatistics, ProcedureName, type SimulatorState, type Statistics, type StorageConfiguration, + type TemplateStatistics, type UIServerConfiguration, type WorkerConfiguration } from '../types/index.js' import { Configuration, Constants, - buildTemplateStatisticsPayload, formatDurationMilliSeconds, generateUUID, handleUncaughtException, handleUnhandledRejection, isAsyncFunction, isNotEmptyArray, - logPrefix, - logger + logger, + logPrefix } from '../utils/index.js' import { type WorkerAbstract, WorkerFactory } from '../worker/index.js' +import { buildTemplateName, waitChargingStationEvents } from './Helpers.js' +import type { AbstractUIServer } from './ui-server/AbstractUIServer.js' +import { UIServerFactory } from './ui-server/UIServerFactory.js' const moduleName = 'Bootstrap' @@ -63,7 +62,7 @@ export class Bootstrap extends EventEmitter { private workerImplementation?: WorkerAbstract private readonly uiServer: AbstractUIServer private storage?: Storage - private readonly templateStatistics: Map + private readonly templateStatistics: Map private readonly version: string = version private initializedCounters: boolean private started: boolean @@ -82,12 +81,15 @@ export class Bootstrap extends EventEmitter { this.started = false this.starting = false this.stopping = false + this.initializedCounters = false this.uiServerStarted = false + this.templateStatistics = new Map() + this.initializeWorkerImplementation( + Configuration.getConfigurationSection(ConfigurationSection.worker) + ) this.uiServer = UIServerFactory.getUIServerImplementation( Configuration.getConfigurationSection(ConfigurationSection.uiServer) ) - this.templateStatistics = new Map() - this.initializedCounters = false this.initializeCounters() Configuration.configurationChangeCallback = async () => { if (isMainThread) { @@ -118,7 +120,7 @@ export class Bootstrap extends EventEmitter { return { version: this.version, started: this.started, - templateStatistics: buildTemplateStatisticsPayload(this.templateStatistics) + templateStatistics: this.templateStatistics } } @@ -176,11 +178,12 @@ export class Bootstrap extends EventEmitter { } ) this.initializeCounters() - const workerConfiguration = Configuration.getConfigurationSection( - ConfigurationSection.worker - ) - this.initializeWorkerImplementation(workerConfiguration) - await this.workerImplementation?.start() + // eslint-disable-next-line @typescript-eslint/unbound-method + if (isAsyncFunction(this.workerImplementation?.start)) { + await this.workerImplementation.start() + } else { + (this.workerImplementation?.start as () => void)() + } const performanceStorageConfiguration = Configuration.getConfigurationSection( ConfigurationSection.performanceStorage @@ -208,9 +211,7 @@ export class Bootstrap extends EventEmitter { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion for (const stationTemplateUrl of Configuration.getStationTemplateUrls()!) { try { - const nbStations = - this.templateStatistics.get(parse(stationTemplateUrl.file).name)?.configured ?? - stationTemplateUrl.numberOfStations + const nbStations = stationTemplateUrl.numberOfStations for (let index = 1; index <= nbStations; index++) { await this.addChargingStation(index, stationTemplateUrl.file) } @@ -223,6 +224,9 @@ export class Bootstrap extends EventEmitter { ) } } + const workerConfiguration = Configuration.getConfigurationSection( + ConfigurationSection.worker + ) console.info( chalk.green( `Charging stations simulator ${ @@ -272,7 +276,6 @@ export class Bootstrap extends EventEmitter { console.error(chalk.red('Error while waiting for charging stations to stop: '), error) } await this.workerImplementation?.stop() - delete this.workerImplementation this.removeAllListeners() this.uiServer.clearCaches() this.initializedCounters = false @@ -290,6 +293,10 @@ export class Bootstrap extends EventEmitter { private async restart (): Promise { await this.stop() + // FIXME: initialize worker implementation only if the worker section has changed + this.initializeWorkerImplementation( + Configuration.getConfigurationSection(ConfigurationSection.worker) + ) if ( this.uiServerStarted && Configuration.getConfigurationSection(ConfigurationSection.uiServer) @@ -302,7 +309,7 @@ export class Bootstrap extends EventEmitter { } private async waitChargingStationsStopped (): Promise { - return await new Promise((resolve, reject) => { + return await new Promise((resolve, reject: (reason?: unknown) => void) => { const waitTimeout = setTimeout(() => { const timeoutMessage = `Timeout ${formatDurationMilliSeconds( Constants.STOP_CHARGING_STATIONS_TIMEOUT @@ -351,7 +358,7 @@ export class Bootstrap extends EventEmitter { workerConfiguration.processType!, { workerStartDelay: workerConfiguration.startDelay, - elementStartDelay: workerConfiguration.elementStartDelay, + elementAddDelay: workerConfiguration.elementAddDelay, // eslint-disable-next-line @typescript-eslint/no-non-null-assertion poolMaxSize: workerConfiguration.poolMaxSize!, // eslint-disable-next-line @typescript-eslint/no-non-null-assertion @@ -359,7 +366,9 @@ export class Bootstrap extends EventEmitter { elementsPerWorker, poolOptions: { messageHandler: this.messageHandler.bind(this) as MessageHandler, - workerOptions: { resourceLimits: workerConfiguration.resourceLimits } + ...(workerConfiguration.resourceLimits != null && { + workerOptions: { resourceLimits: workerConfiguration.resourceLimits } + }) } } ) @@ -495,10 +504,7 @@ export class Bootstrap extends EventEmitter { const stationTemplateUrls = Configuration.getStationTemplateUrls()! if (isNotEmptyArray(stationTemplateUrls)) { for (const stationTemplateUrl of stationTemplateUrls) { - const templateName = join( - parse(stationTemplateUrl.file).dir, - parse(stationTemplateUrl.file).name - ) + const templateName = buildTemplateName(stationTemplateUrl.file) this.templateStatistics.set(templateName, { configured: stationTemplateUrl.numberOfStations, added: 0, @@ -539,21 +545,26 @@ export class Bootstrap extends EventEmitter { public async addChargingStation ( index: number, - stationTemplateFile: string, + templateFile: string, options?: ChargingStationOptions ): Promise { + if (!this.started && !this.starting) { + throw new BaseError( + 'Cannot add charging station while the charging stations simulator is not started' + ) + } await this.workerImplementation?.addElement({ index, templateFile: join( dirname(fileURLToPath(import.meta.url)), 'assets', 'station-templates', - stationTemplateFile + templateFile ), options }) // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const templateStatistics = this.templateStatistics.get(parse(stationTemplateFile).name)! + const templateStatistics = this.templateStatistics.get(buildTemplateName(templateFile))! ++templateStatistics.added templateStatistics.indexes.add(index) } @@ -572,7 +583,7 @@ export class Bootstrap extends EventEmitter { exit(exitCodes.gracefulShutdownError) }) }) - .catch(error => { + .catch((error: unknown) => { console.error(chalk.red('Error while shutdowning charging stations simulator: '), error) exit(exitCodes.gracefulShutdownError) })