X-Git-Url: https://git.piment-noir.org/?a=blobdiff_plain;f=src%2Fcharging-station%2FBootstrap.ts;h=047b321b55c36c910211e381482f09439422a63b;hb=d64ea57bcb4fd9151a43b16ff4d4b5ed059ad1d7;hp=acd8e73cb23ea1b7cf0b41e5e6cd3436cc4ee0f9;hpb=71ac2bd72c0bd0f742e10dc135a619a5717e6a47;p=e-mobility-charging-stations-simulator.git diff --git a/src/charging-station/Bootstrap.ts b/src/charging-station/Bootstrap.ts index acd8e73c..047b321b 100644 --- a/src/charging-station/Bootstrap.ts +++ b/src/charging-station/Bootstrap.ts @@ -41,8 +41,7 @@ import { isAsyncFunction, isNotEmptyArray, logPrefix, - logger, - max + logger } from '../utils/index.js' import { type WorkerAbstract, WorkerFactory } from '../worker/index.js' @@ -60,20 +59,21 @@ interface TemplateChargingStations { configured: number added: number started: number - lastIndex: number + indexes: Set } export class Bootstrap extends EventEmitter { private static instance: Bootstrap | null = null private workerImplementation?: WorkerAbstract - private readonly uiServer?: AbstractUIServer + private readonly uiServer: AbstractUIServer private storage?: Storage - private readonly chargingStationsByTemplate: Map + private readonly templatesChargingStations: Map private readonly version: string = version private initializedCounters: boolean private started: boolean private starting: boolean private stopping: boolean + private uiServerStarted: boolean private constructor () { super() @@ -86,10 +86,11 @@ export class Bootstrap extends EventEmitter { this.started = false this.starting = false this.stopping = false - this.chargingStationsByTemplate = new Map() + this.uiServerStarted = false this.uiServer = UIServerFactory.getUIServerImplementation( Configuration.getConfigurationSection(ConfigurationSection.uiServer) ) + this.templatesChargingStations = new Map() this.initializedCounters = false this.initializeCounters() Configuration.configurationChangeCallback = async () => { @@ -107,18 +108,27 @@ export class Bootstrap extends EventEmitter { } public get numberOfChargingStationTemplates (): number { - return this.chargingStationsByTemplate.size + return this.templatesChargingStations.size } public get numberOfConfiguredChargingStations (): number { - return [...this.chargingStationsByTemplate.values()].reduce( + return [...this.templatesChargingStations.values()].reduce( (accumulator, value) => accumulator + value.configured, 0 ) } public getLastIndex (templateName: string): number { - return this.chargingStationsByTemplate.get(templateName)?.lastIndex ?? 0 + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const indexes = [...this.templatesChargingStations.get(templateName)!.indexes] + .concat(0) + .sort((a, b) => a - b) + for (let i = 0; i < indexes.length - 1; i++) { + if (indexes[i + 1] - indexes[i] !== 1) { + return indexes[i] + } + } + return indexes[indexes.length - 1] } public getPerformanceStatistics (): IterableIterator | undefined { @@ -126,14 +136,14 @@ export class Bootstrap extends EventEmitter { } private get numberOfAddedChargingStations (): number { - return [...this.chargingStationsByTemplate.values()].reduce( + return [...this.templatesChargingStations.values()].reduce( (accumulator, value) => accumulator + value.added, 0 ) } private get numberOfStartedChargingStations (): number { - return [...this.chargingStationsByTemplate.values()].reduce( + return [...this.templatesChargingStations.values()].reduce( (accumulator, value) => accumulator + value.started, 0 ) @@ -144,6 +154,7 @@ export class Bootstrap extends EventEmitter { if (!this.starting) { this.starting = true this.on(ChargingStationWorkerMessageEvents.added, this.workerEventAdded) + this.on(ChargingStationWorkerMessageEvents.deleted, this.workerEventDeleted) this.on(ChargingStationWorkerMessageEvents.started, this.workerEventStarted) this.on(ChargingStationWorkerMessageEvents.stopped, this.workerEventStopped) this.on(ChargingStationWorkerMessageEvents.updated, this.workerEventUpdated) @@ -180,15 +191,22 @@ export class Bootstrap extends EventEmitter { ) await this.storage?.open() } - Configuration.getConfigurationSection(ConfigurationSection.uiServer) - .enabled === true && this.uiServer?.start() + if ( + !this.uiServerStarted && + Configuration.getConfigurationSection( + ConfigurationSection.uiServer + ).enabled === true + ) { + this.uiServer.start() + this.uiServerStarted = true + } // Start ChargingStation object instance in worker thread // eslint-disable-next-line @typescript-eslint/no-non-null-assertion for (const stationTemplateUrl of Configuration.getStationTemplateUrls()!) { try { const nbStations = - this.chargingStationsByTemplate.get(parse(stationTemplateUrl.file).name) - ?.configured ?? stationTemplateUrl.numberOfStations + this.templatesChargingStations.get(parse(stationTemplateUrl.file).name)?.configured ?? + stationTemplateUrl.numberOfStations for (let index = 1; index <= nbStations; index++) { await this.addChargingStation(index, stationTemplateUrl.file) } @@ -237,7 +255,7 @@ export class Bootstrap extends EventEmitter { if (this.started) { if (!this.stopping) { this.stopping = true - await this.uiServer?.sendInternalRequest( + await this.uiServer.sendInternalRequest( this.uiServer.buildProtocolRequest( generateUUID(), ProcedureName.STOP_CHARGING_STATION, @@ -252,6 +270,8 @@ export class Bootstrap extends EventEmitter { 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 @@ -266,9 +286,14 @@ export class Bootstrap extends EventEmitter { private async restart (): Promise { await this.stop() - Configuration.getConfigurationSection(ConfigurationSection.uiServer) - .enabled !== true && this.uiServer?.stop() - this.initializedCounters = false + if ( + this.uiServerStarted && + Configuration.getConfigurationSection(ConfigurationSection.uiServer) + .enabled !== true + ) { + this.uiServer.stop() + this.uiServerStarted = false + } await this.start() } @@ -300,17 +325,18 @@ export class Bootstrap extends EventEmitter { if (!isMainThread) { return } - let elementsPerWorker: number | undefined + let elementsPerWorker: number switch (workerConfiguration.elementsPerWorker) { + case 'all': + elementsPerWorker = this.numberOfConfiguredChargingStations + break case 'auto': + default: elementsPerWorker = this.numberOfConfiguredChargingStations > availableParallelism() ? Math.round(this.numberOfConfiguredChargingStations / (availableParallelism() * 1.5)) : 1 break - case 'all': - elementsPerWorker = this.numberOfConfiguredChargingStations - break } this.workerImplementation = WorkerFactory.getWorkerImplementation( join( @@ -326,7 +352,7 @@ export class Bootstrap extends EventEmitter { poolMaxSize: workerConfiguration.poolMaxSize!, // eslint-disable-next-line @typescript-eslint/no-non-null-assertion poolMinSize: workerConfiguration.poolMinSize!, - elementsPerWorker: elementsPerWorker ?? (workerConfiguration.elementsPerWorker as number), + elementsPerWorker, poolOptions: { messageHandler: this.messageHandler.bind(this) as MessageHandler, workerOptions: { resourceLimits: workerConfiguration.resourceLimits } @@ -350,6 +376,9 @@ export class Bootstrap extends EventEmitter { case ChargingStationWorkerMessageEvents.added: this.emit(ChargingStationWorkerMessageEvents.added, msg.data) break + case ChargingStationWorkerMessageEvents.deleted: + this.emit(ChargingStationWorkerMessageEvents.deleted, msg.data) + break case ChargingStationWorkerMessageEvents.started: this.emit(ChargingStationWorkerMessageEvents.started, msg.data) break @@ -386,9 +415,7 @@ export class Bootstrap extends EventEmitter { } private readonly workerEventAdded = (data: ChargingStationData): void => { - this.uiServer?.chargingStations.set(data.stationInfo.hashId, data) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - ++this.chargingStationsByTemplate.get(data.stationInfo.templateName)!.added + this.uiServer.chargingStations.set(data.stationInfo.hashId, data) logger.info( `${this.logPrefix()} ${moduleName}.workerEventAdded: Charging station ${ data.stationInfo.chargingStationId @@ -398,10 +425,27 @@ export class Bootstrap extends EventEmitter { ) } + private readonly workerEventDeleted = (data: ChargingStationData): void => { + this.uiServer.chargingStations.delete(data.stationInfo.hashId) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const templateChargingStations = this.templatesChargingStations.get( + data.stationInfo.templateName + )! + --templateChargingStations.added + templateChargingStations.indexes.delete(data.stationInfo.templateIndex) + logger.info( + `${this.logPrefix()} ${moduleName}.workerEventDeleted: Charging station ${ + data.stationInfo.chargingStationId + } (hashId: ${data.stationInfo.hashId}) deleted (${ + this.numberOfAddedChargingStations + } added from ${this.numberOfConfiguredChargingStations} configured charging station(s))` + ) + } + private readonly workerEventStarted = (data: ChargingStationData): void => { - this.uiServer?.chargingStations.set(data.stationInfo.hashId, data) + this.uiServer.chargingStations.set(data.stationInfo.hashId, data) // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - ++this.chargingStationsByTemplate.get(data.stationInfo.templateName)!.started + ++this.templatesChargingStations.get(data.stationInfo.templateName)!.started logger.info( `${this.logPrefix()} ${moduleName}.workerEventStarted: Charging station ${ data.stationInfo.chargingStationId @@ -412,9 +456,9 @@ export class Bootstrap extends EventEmitter { } private readonly workerEventStopped = (data: ChargingStationData): void => { - this.uiServer?.chargingStations.set(data.stationInfo.hashId, data) + this.uiServer.chargingStations.set(data.stationInfo.hashId, data) // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - --this.chargingStationsByTemplate.get(data.stationInfo.templateName)!.started + --this.templatesChargingStations.get(data.stationInfo.templateName)!.started logger.info( `${this.logPrefix()} ${moduleName}.workerEventStopped: Charging station ${ data.stationInfo.chargingStationId @@ -425,7 +469,7 @@ export class Bootstrap extends EventEmitter { } private readonly workerEventUpdated = (data: ChargingStationData): void => { - this.uiServer?.chargingStations.set(data.stationInfo.hashId, data) + this.uiServer.chargingStations.set(data.stationInfo.hashId, data) } private readonly workerEventPerformanceStatistics = (data: Statistics): void => { @@ -450,15 +494,15 @@ export class Bootstrap extends EventEmitter { if (isNotEmptyArray(stationTemplateUrls)) { for (const stationTemplateUrl of stationTemplateUrls) { const templateName = parse(stationTemplateUrl.file).name - this.chargingStationsByTemplate.set(templateName, { + this.templatesChargingStations.set(templateName, { configured: stationTemplateUrl.numberOfStations, added: 0, started: 0, - lastIndex: 0 + indexes: new Set() }) - this.uiServer?.chargingStationTemplates.add(templateName) + this.uiServer.chargingStationTemplates.add(templateName) } - if (this.chargingStationsByTemplate.size !== stationTemplateUrls.length) { + if (this.templatesChargingStations.size !== stationTemplateUrls.length) { console.error( chalk.red( "'stationTemplateUrls' contains duplicate entries, please check your configuration" @@ -504,17 +548,19 @@ export class Bootstrap extends EventEmitter { options }) // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - this.chargingStationsByTemplate.get(parse(stationTemplateFile).name)!.lastIndex = max( - index, - this.chargingStationsByTemplate.get(parse(stationTemplateFile).name)?.lastIndex ?? -Infinity - ) + const templateChargingStations = this.templatesChargingStations.get( + parse(stationTemplateFile).name + )! + ++templateChargingStations.added + templateChargingStations.indexes.add(index) } private gracefulShutdown (): void { this.stop() .then(() => { console.info(chalk.green('Graceful shutdown')) - this.uiServer?.stop() + this.uiServer.stop() + this.uiServerStarted = false this.waitChargingStationsStopped() .then(() => { exit(exitCodes.succeeded)