-} from '../types/ChargingStationWorker';
-
-import { AbstractUIServer } from './ui-server/AbstractUIServer';
-import { ApplicationProtocol } from '../types/UIProtocol';
-import { ChargingStationUtils } from './ChargingStationUtils';
-import Configuration from '../utils/Configuration';
-import { StationTemplateUrl } from '../types/ConfigurationData';
-import Statistics from '../types/Statistics';
-import { Storage } from '../performance/storage/Storage';
-import { StorageFactory } from '../performance/storage/StorageFactory';
-import UIServerFactory from './ui-server/UIServerFactory';
-import { UIServiceUtils } from './ui-server/ui-services/UIServiceUtils';
-import Utils from '../utils/Utils';
-import WorkerAbstract from '../worker/WorkerAbstract';
-import WorkerFactory from '../worker/WorkerFactory';
-import chalk from 'chalk';
-import { fileURLToPath } from 'url';
-import { isMainThread } from 'worker_threads';
-import path from 'path';
-import { version } from '../../package.json';
-
-export default class Bootstrap {
- private static instance: Bootstrap | null = null;
- private workerImplementation: WorkerAbstract<ChargingStationWorkerData> | null = null;
- private readonly uiServer!: AbstractUIServer;
- private readonly storage!: Storage;
- private numberOfChargingStationTemplates!: number;
- private numberOfChargingStations!: number;
- private readonly version: string = version;
- private started: boolean;
- private readonly workerScript: string;
-
- private constructor() {
- this.started = false;
- this.workerScript = path.join(
- path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../'),
- 'charging-station',
- 'ChargingStationWorker.js'
- );
- this.initialize();
- this.initWorkerImplementation();
- Configuration.getUIServer().enabled &&
- (this.uiServer = UIServerFactory.getUIServerImplementation(ApplicationProtocol.WS, {
- ...Configuration.getUIServer().options,
- handleProtocols: UIServiceUtils.handleProtocols,
- }));
- Configuration.getPerformanceStorage().enabled &&
- (this.storage = StorageFactory.getStorage(
- Configuration.getPerformanceStorage().type,
- Configuration.getPerformanceStorage().uri,
- this.logPrefix()
- ));
- Configuration.setConfigurationChangeCallback(async () => Bootstrap.getInstance().restart());
- }
-
- public static getInstance(): Bootstrap {
- if (!Bootstrap.instance) {
- Bootstrap.instance = new Bootstrap();
+ ConfigurationSection,
+ ProcedureName,
+ type Statistics,
+ type StorageConfiguration,
+ type UIServerConfiguration,
+ type WorkerConfiguration
+} from '../types/index.js'
+import {
+ Configuration,
+ Constants,
+ formatDurationMilliSeconds,
+ generateUUID,
+ handleUncaughtException,
+ handleUnhandledRejection,
+ isAsyncFunction,
+ isNotEmptyArray,
+ logPrefix,
+ logger
+} from '../utils/index.js'
+import { type WorkerAbstract, WorkerFactory } from '../worker/index.js'
+
+const moduleName = 'Bootstrap'
+
+enum exitCodes {
+ succeeded = 0,
+ missingChargingStationsConfiguration = 1,
+ duplicateChargingStationTemplateUrls = 2,
+ noChargingStationTemplates = 3,
+ gracefulShutdownError = 4
+}
+
+interface TemplateChargingStations {
+ configured: number
+ added: number
+ started: number
+ indexes: Set<number>
+}
+
+export class Bootstrap extends EventEmitter {
+ private static instance: Bootstrap | null = null
+ private workerImplementation?: WorkerAbstract<ChargingStationWorkerData>
+ private readonly uiServer?: AbstractUIServer
+ private storage?: Storage
+ private readonly templatesChargingStations: Map<string, TemplateChargingStations>
+ private readonly version: string = version
+ private initializedCounters: boolean
+ private started: boolean
+ private starting: boolean
+ private stopping: boolean
+
+ private constructor () {
+ super()
+ for (const signal of ['SIGINT', 'SIGQUIT', 'SIGTERM']) {
+ process.on(signal, this.gracefulShutdown.bind(this))
+ }
+ // Enable unconditionally for now
+ handleUnhandledRejection()
+ handleUncaughtException()
+ this.started = false
+ this.starting = false
+ this.stopping = false
+ this.templatesChargingStations = new Map<string, TemplateChargingStations>()
+ this.uiServer = UIServerFactory.getUIServerImplementation(
+ Configuration.getConfigurationSection<UIServerConfiguration>(ConfigurationSection.uiServer)
+ )
+ this.initializedCounters = false
+ this.initializeCounters()
+ Configuration.configurationChangeCallback = async () => {
+ if (isMainThread) {
+ await Bootstrap.getInstance().restart()
+ }
+ }
+ }
+
+ public static getInstance (): Bootstrap {
+ if (Bootstrap.instance === null) {
+ Bootstrap.instance = new Bootstrap()
+ }
+ return Bootstrap.instance
+ }
+
+ public get numberOfChargingStationTemplates (): number {
+ return this.templatesChargingStations.size
+ }
+
+ public get numberOfConfiguredChargingStations (): number {
+ return [...this.templatesChargingStations.values()].reduce(
+ (accumulator, value) => accumulator + value.configured,
+ 0
+ )
+ }
+
+ public getLastIndex (templateName: string): number {
+ // 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]
+ }