this.uiServer = UIServerFactory.getUIServerImplementation(
Configuration.getConfigurationSection<UIServerConfiguration>(ConfigurationSection.uiServer),
);
- this.on(ChargingStationWorkerMessageEvents.started, this.workerEventStarted);
- this.on(ChargingStationWorkerMessageEvents.stopped, this.workerEventStopped);
- this.on(ChargingStationWorkerMessageEvents.updated, this.workerEventUpdated);
- this.on(
- ChargingStationWorkerMessageEvents.performanceStatistics,
- this.workerEventPerformanceStatistics,
- );
Configuration.configurationChangeCallback = async () => Bootstrap.getInstance().restart(false);
}
if (this.started === false) {
if (this.starting === false) {
this.starting = true;
+ this.on(ChargingStationWorkerMessageEvents.started, this.workerEventStarted);
+ this.on(ChargingStationWorkerMessageEvents.stopped, this.workerEventStopped);
+ this.on(ChargingStationWorkerMessageEvents.updated, this.workerEventUpdated);
+ this.on(
+ ChargingStationWorkerMessageEvents.performanceStatistics,
+ this.workerEventPerformanceStatistics,
+ );
this.initializeCounters();
const workerConfiguration = Configuration.getConfigurationSection<WorkerConfiguration>(
ConfigurationSection.worker,
this.numberOfChargingStations,
)
.then(() => {
+ this.removeAllListeners();
resolve('Charging stations stopped');
})
.catch(reject)
import { type FSWatcher, readFileSync, watch } from 'node:fs';
-import { dirname, join, resolve } from 'node:path';
+import { dirname, join } from 'node:path';
import { env } from 'node:process';
import { fileURLToPath } from 'node:url';
import chalk from 'chalk';
import merge from 'just-merge';
-import { Constants } from './Constants';
import {
- hasOwnProp,
- isCFEnvironment,
- isNotEmptyString,
- isUndefined,
+ buildPerformanceUriFilePath,
+ checkWorkerElementsPerWorker,
+ checkWorkerProcessType,
+ getDefaultPerformanceStorageUri,
+ handleFileException,
logPrefix,
- once,
-} from './Utils';
+} from './ConfigurationUtils';
+import { Constants } from './Constants';
+import { hasOwnProp, isCFEnvironment, isUndefined, once } from './Utils';
import {
ApplicationProtocol,
type ConfigurationData,
| WorkerConfiguration
| UIServerConfiguration;
-// Avoid ESM race condition at class initialization
-const configurationLogPrefix = (): string => {
- return logPrefix(' Simulator configuration |');
-};
-
export class Configuration {
public static configurationChangeCallback: () => Promise<void>;
let storageConfiguration: StorageConfiguration = {
enabled: false,
type: StorageType.JSON_FILE,
- uri: Configuration.getDefaultPerformanceStorageUri(StorageType.JSON_FILE),
+ uri: getDefaultPerformanceStorageUri(StorageType.JSON_FILE),
};
if (hasOwnProp(Configuration.getConfigurationData(), ConfigurationSection.performanceStorage)) {
storageConfiguration = {
...(Configuration.getConfigurationData()?.performanceStorage?.type ===
StorageType.JSON_FILE &&
Configuration.getConfigurationData()?.performanceStorage?.uri && {
- uri: Configuration.buildPerformanceUriFilePath(
+ uri: buildPerformanceUriFilePath(
new URL(Configuration.getConfigurationData()!.performanceStorage!.uri!).pathname,
),
}),
...(hasOwnProp(Configuration.getConfigurationData(), ConfigurationSection.worker) &&
Configuration.getConfigurationData()?.worker),
};
- if (!Object.values(WorkerProcessType).includes(workerConfiguration.processType!)) {
- throw new SyntaxError(
- `Invalid worker process type '${workerConfiguration.processType}' defined in configuration`,
- );
- }
+ checkWorkerProcessType(workerConfiguration.processType!);
+ checkWorkerElementsPerWorker(workerConfiguration.elementsPerWorker);
return workerConfiguration;
}
(stationTemplateUrl: StationTemplateUrl) => {
if (!isUndefined(stationTemplateUrl?.['numberOfStation' as keyof StationTemplateUrl])) {
console.error(
- `${chalk.green(configurationLogPrefix())} ${chalk.red(
+ `${chalk.green(logPrefix())} ${chalk.red(
`Deprecated configuration key 'numberOfStation' usage for template file '${stationTemplateUrl.file}' in 'stationTemplateUrls'. Use 'numberOfStations' instead`,
)}`,
);
('staticPool' as WorkerProcessType)
) {
console.error(
- `${chalk.green(configurationLogPrefix())} ${chalk.red(
+ `${chalk.green(logPrefix())} ${chalk.red(
`Deprecated configuration 'staticPool' value usage in worker section 'processType' field. Use '${WorkerProcessType.fixedPool}' value instead`,
)}`,
);
// uiServer section
if (hasOwnProp(Configuration.getConfigurationData(), 'uiWebSocketServer')) {
console.error(
- `${chalk.green(configurationLogPrefix())} ${chalk.red(
+ `${chalk.green(logPrefix())} ${chalk.red(
`Deprecated configuration section 'uiWebSocketServer' usage. Use '${ConfigurationSection.uiServer}' instead`,
)}`,
);
)
) {
console.error(
- `${chalk.green(configurationLogPrefix())} ${chalk.red(
+ `${chalk.green(logPrefix())} ${chalk.red(
`Deprecated configuration key '${key}' usage in section '${sectionName}'${
logMsgToAppend.trim().length > 0 ? `. ${logMsgToAppend}` : ''
}`,
!isUndefined(Configuration.getConfigurationData()?.[key as keyof ConfigurationData])
) {
console.error(
- `${chalk.green(configurationLogPrefix())} ${chalk.red(
+ `${chalk.green(logPrefix())} ${chalk.red(
`Deprecated configuration key '${key}' usage${
logMsgToAppend.trim().length > 0 ? `. ${logMsgToAppend}` : ''
}`,
Configuration.configurationFileWatcher = Configuration.getConfigurationFileWatcher();
}
} catch (error) {
- Configuration.handleFileException(
+ handleFileException(
Configuration.configurationFile,
FileType.Configuration,
error as NodeJS.ErrnoException,
- configurationLogPrefix(),
+ logPrefix(),
);
}
}
Configuration.configurationFileReloading = true;
const consoleWarnOnce = once(console.warn, this);
consoleWarnOnce(
- `${chalk.green(configurationLogPrefix())} ${chalk.yellow(
+ `${chalk.green(logPrefix())} ${chalk.yellow(
`${FileType.Configuration} ${this.configurationFile} file have changed, reload`,
)}`,
);
}
});
} catch (error) {
- Configuration.handleFileException(
+ handleFileException(
Configuration.configurationFile,
FileType.Configuration,
error as NodeJS.ErrnoException,
- configurationLogPrefix(),
+ logPrefix(),
);
}
}
-
- private static handleFileException(
- file: string,
- fileType: FileType,
- error: NodeJS.ErrnoException,
- logPfx: string,
- ): void {
- const prefix = isNotEmptyString(logPfx) ? `${logPfx} ` : '';
- let logMsg: string;
- switch (error.code) {
- case 'ENOENT':
- logMsg = `${fileType} file ${file} not found: `;
- break;
- case 'EEXIST':
- logMsg = `${fileType} file ${file} already exists: `;
- break;
- case 'EACCES':
- logMsg = `${fileType} file ${file} access denied: `;
- break;
- case 'EPERM':
- logMsg = `${fileType} file ${file} permission denied: `;
- break;
- default:
- logMsg = `${fileType} file ${file} error: `;
- }
- console.error(`${chalk.green(prefix)}${chalk.red(logMsg)}`, error);
- throw error;
- }
-
- private static getDefaultPerformanceStorageUri(storageType: StorageType) {
- switch (storageType) {
- case StorageType.JSON_FILE:
- return Configuration.buildPerformanceUriFilePath(
- `${Constants.DEFAULT_PERFORMANCE_DIRECTORY}/${Constants.DEFAULT_PERFORMANCE_RECORDS_FILENAME}`,
- );
- case StorageType.SQLITE:
- return Configuration.buildPerformanceUriFilePath(
- `${Constants.DEFAULT_PERFORMANCE_DIRECTORY}/${Constants.DEFAULT_PERFORMANCE_RECORDS_DB_NAME}.db`,
- );
- default:
- throw new Error(`Unsupported storage type '${storageType}'`);
- }
- }
-
- private static buildPerformanceUriFilePath(file: string) {
- return `file://${join(resolve(dirname(fileURLToPath(import.meta.url)), '../'), file)}`;
- }
}
--- /dev/null
+import { dirname, join, resolve } from 'node:path';
+import { fileURLToPath } from 'node:url';
+
+import chalk from 'chalk';
+
+import { Constants } from './Constants';
+import { isNotEmptyString, logPrefix as utilsLogPrefix } from './Utils';
+import { FileType, StorageType } from '../types';
+import { WorkerProcessType } from '../worker';
+
+export const logPrefix = (): string => {
+ return utilsLogPrefix(' Simulator configuration |');
+};
+
+export const buildPerformanceUriFilePath = (file: string) => {
+ return `file://${join(resolve(dirname(fileURLToPath(import.meta.url)), '../'), file)}`;
+};
+
+export const getDefaultPerformanceStorageUri = (storageType: StorageType) => {
+ switch (storageType) {
+ case StorageType.JSON_FILE:
+ return buildPerformanceUriFilePath(
+ `${Constants.DEFAULT_PERFORMANCE_DIRECTORY}/${Constants.DEFAULT_PERFORMANCE_RECORDS_FILENAME}`,
+ );
+ case StorageType.SQLITE:
+ return buildPerformanceUriFilePath(
+ `${Constants.DEFAULT_PERFORMANCE_DIRECTORY}/${Constants.DEFAULT_PERFORMANCE_RECORDS_DB_NAME}.db`,
+ );
+ default:
+ throw new Error(`Unsupported storage type '${storageType}'`);
+ }
+};
+
+export const handleFileException = (
+ file: string,
+ fileType: FileType,
+ error: NodeJS.ErrnoException,
+ logPfx: string,
+): void => {
+ const prefix = isNotEmptyString(logPfx) ? `${logPfx} ` : '';
+ let logMsg: string;
+ switch (error.code) {
+ case 'ENOENT':
+ logMsg = `${fileType} file ${file} not found: `;
+ break;
+ case 'EEXIST':
+ logMsg = `${fileType} file ${file} already exists: `;
+ break;
+ case 'EACCES':
+ logMsg = `${fileType} file ${file} access denied: `;
+ break;
+ case 'EPERM':
+ logMsg = `${fileType} file ${file} permission denied: `;
+ break;
+ default:
+ logMsg = `${fileType} file ${file} error: `;
+ }
+ console.error(`${chalk.green(prefix)}${chalk.red(logMsg)}`, error);
+ throw error;
+};
+
+export const checkWorkerProcessType = (workerProcessType: WorkerProcessType): void => {
+ if (!Object.values(WorkerProcessType).includes(workerProcessType)) {
+ throw new SyntaxError(
+ `Invalid worker process type '${workerProcessType}' defined in configuration`,
+ );
+ }
+};
+
+export const checkWorkerElementsPerWorker = (
+ elementsPerWorker: number | 'auto' | 'single' | undefined,
+): void => {
+ if (
+ elementsPerWorker !== undefined &&
+ elementsPerWorker !== 'auto' &&
+ elementsPerWorker !== 'single' &&
+ !Number.isSafeInteger(elementsPerWorker)
+ ) {
+ throw new SyntaxError(
+ `Invalid number of elements per worker '${elementsPerWorker}' defined in configuration`,
+ );
+ }
+ if (Number.isSafeInteger(elementsPerWorker) && (elementsPerWorker as number) <= 0) {
+ throw RangeError(
+ `Invalid negative or zero number of elements per worker '${elementsPerWorker}' defined in configuration`,
+ );
+ }
+};