X-Git-Url: https://git.piment-noir.org/?a=blobdiff_plain;f=src%2Fcharging-station%2FBootstrap.ts;h=34ac90f3f3e4ba2e211d47ef3639907e5ccff066;hb=2bb3c92f49572f8d81f40620df42de19217a6b4c;hp=bd7fda2da58b59f5dad6973a5e2240437d54a7c8;hpb=a33026fe3cd6ce141efa55d27d35b4eb5c1a54b9;p=e-mobility-charging-stations-simulator.git diff --git a/src/charging-station/Bootstrap.ts b/src/charging-station/Bootstrap.ts index bd7fda2d..34ac90f3 100644 --- a/src/charging-station/Bootstrap.ts +++ b/src/charging-station/Bootstrap.ts @@ -5,14 +5,11 @@ 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,29 +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, - buildTemplateName, - 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' @@ -64,9 +62,8 @@ 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 private starting: boolean private stopping: boolean @@ -84,12 +81,14 @@ export class Bootstrap extends EventEmitter { this.starting = false this.stopping = false this.uiServerStarted = false + this.templateStatistics = new Map() this.uiServer = UIServerFactory.getUIServerImplementation( Configuration.getConfigurationSection(ConfigurationSection.uiServer) ) - this.templateStatistics = new Map() - this.initializedCounters = false this.initializeCounters() + this.initializeWorkerImplementation( + Configuration.getConfigurationSection(ConfigurationSection.worker) + ) Configuration.configurationChangeCallback = async () => { if (isMainThread) { await Bootstrap.getInstance().restart() @@ -119,7 +118,7 @@ export class Bootstrap extends EventEmitter { return { version: this.version, started: this.started, - templateStatistics: buildTemplateStatisticsPayload(this.templateStatistics) + templateStatistics: this.templateStatistics } } @@ -176,12 +175,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 @@ -222,6 +221,9 @@ export class Bootstrap extends EventEmitter { ) } } + const workerConfiguration = Configuration.getConfigurationSection( + ConfigurationSection.worker + ) console.info( chalk.green( `Charging stations simulator ${ @@ -271,10 +273,8 @@ 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 await this.storage?.close() delete this.storage this.started = false @@ -297,11 +297,16 @@ export class Bootstrap extends EventEmitter { this.uiServer.stop() this.uiServerStarted = false } + this.initializeCounters() + // FIXME: initialize worker implementation only if the worker section has changed + this.initializeWorkerImplementation( + Configuration.getConfigurationSection(ConfigurationSection.worker) + ) await this.start() } 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 @@ -350,7 +355,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 @@ -358,7 +363,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 } + }) } } ) @@ -489,47 +496,44 @@ export class Bootstrap extends EventEmitter { } private initializeCounters (): void { - if (!this.initializedCounters) { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const stationTemplateUrls = Configuration.getStationTemplateUrls()! - if (isNotEmptyArray(stationTemplateUrls)) { - for (const stationTemplateUrl of stationTemplateUrls) { - const templateName = buildTemplateName(stationTemplateUrl.file) - this.templateStatistics.set(templateName, { - configured: stationTemplateUrl.numberOfStations, - added: 0, - started: 0, - indexes: new Set() - }) - this.uiServer.chargingStationTemplates.add(templateName) - } - if (this.templateStatistics.size !== stationTemplateUrls.length) { - console.error( - chalk.red( - "'stationTemplateUrls' contains duplicate entries, please check your configuration" - ) - ) - exit(exitCodes.duplicateChargingStationTemplateUrls) - } - } else { - console.error( - chalk.red("'stationTemplateUrls' not defined or empty, please check your configuration") - ) - exit(exitCodes.missingChargingStationsConfiguration) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const stationTemplateUrls = Configuration.getStationTemplateUrls()! + if (isNotEmptyArray(stationTemplateUrls)) { + for (const stationTemplateUrl of stationTemplateUrls) { + const templateName = buildTemplateName(stationTemplateUrl.file) + this.templateStatistics.set(templateName, { + configured: stationTemplateUrl.numberOfStations, + added: 0, + started: 0, + indexes: new Set() + }) + this.uiServer.chargingStationTemplates.add(templateName) } - if ( - this.numberOfConfiguredChargingStations === 0 && - Configuration.getConfigurationSection(ConfigurationSection.uiServer) - .enabled !== true - ) { + if (this.templateStatistics.size !== stationTemplateUrls.length) { console.error( chalk.red( - "'stationTemplateUrls' has no charging station enabled and UI server is disabled, please check your configuration" + "'stationTemplateUrls' contains duplicate entries, please check your configuration" ) ) - exit(exitCodes.noChargingStationTemplates) + exit(exitCodes.duplicateChargingStationTemplateUrls) } - this.initializedCounters = true + } else { + console.error( + chalk.red("'stationTemplateUrls' not defined or empty, please check your configuration") + ) + exit(exitCodes.missingChargingStationsConfiguration) + } + if ( + this.numberOfConfiguredChargingStations === 0 && + Configuration.getConfigurationSection(ConfigurationSection.uiServer) + .enabled !== true + ) { + console.error( + chalk.red( + "'stationTemplateUrls' has no charging station enabled and UI server is disabled, please check your configuration" + ) + ) + exit(exitCodes.noChargingStationTemplates) } } @@ -538,6 +542,11 @@ export class Bootstrap extends EventEmitter { 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( @@ -568,7 +577,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) })