import {
ApplicationProtocol,
type ConfigurationData,
+ ConfigurationSection,
FileType,
type LogConfiguration,
type StationTemplateUrl,
type UIServerConfiguration,
type WorkerConfiguration,
} from '../types';
-import { WorkerConstants, WorkerProcessType } from '../worker';
+import {
+ DEFAULT_ELEMENT_START_DELAY,
+ DEFAULT_POOL_MAX_SIZE,
+ DEFAULT_POOL_MIN_SIZE,
+ DEFAULT_WORKER_START_DELAY,
+ WorkerProcessType,
+} from '../worker';
-enum ConfigurationSection {
- log = 'log',
- performanceStorage = 'performanceStorage',
- worker = 'worker',
- uiServer = 'uiServer',
-}
+type ConfigurationSectionType =
+ | LogConfiguration
+ | StorageConfiguration
+ | WorkerConfiguration
+ | UIServerConfiguration;
export class Configuration {
private static configurationFile = join(
'config.json',
);
- private static configurationFileWatcher: FSWatcher | undefined;
- private static configurationData: ConfigurationData | null = null;
+ private static configurationFileWatcher?: FSWatcher;
+ private static configurationData?: ConfigurationData;
private static configurationSectionCache = new Map<
ConfigurationSection,
- LogConfiguration | StorageConfiguration | WorkerConfiguration | UIServerConfiguration
- >();
+ ConfigurationSectionType
+ >([
+ [ConfigurationSection.log, Configuration.buildLogSection()],
+ [ConfigurationSection.performanceStorage, Configuration.buildPerformanceStorageSection()],
+ [ConfigurationSection.worker, Configuration.buildWorkerSection()],
+ [ConfigurationSection.uiServer, Configuration.buildUIServerSection()],
+ ]);
- private static configurationChangeCallback: () => Promise<void>;
+ private static configurationChangeCallback?: () => Promise<void>;
private constructor() {
// This is intentional
Configuration.configurationChangeCallback = cb;
}
- public static getUIServer(): UIServerConfiguration {
+ public static getConfigurationSection<T extends ConfigurationSectionType>(
+ sectionName: ConfigurationSection,
+ ): T {
+ if (!Configuration.isConfigurationSectionCached(sectionName)) {
+ Configuration.cacheConfigurationSection(sectionName);
+ }
+ return Configuration.configurationSectionCache.get(sectionName) as T;
+ }
+
+ public static getAutoReconnectMaxRetries(): number | undefined {
+ Configuration.warnDeprecatedConfigurationKey(
+ 'autoReconnectTimeout',
+ undefined,
+ "Use 'ConnectionTimeOut' OCPP parameter in charging station template instead",
+ );
+ Configuration.warnDeprecatedConfigurationKey(
+ 'connectionTimeout',
+ undefined,
+ "Use 'ConnectionTimeOut' OCPP parameter in charging station template instead",
+ );
+ Configuration.warnDeprecatedConfigurationKey(
+ 'autoReconnectMaxRetries',
+ undefined,
+ 'Use it in charging station template instead',
+ );
+ if (hasOwnProp(Configuration.getConfigurationData(), 'autoReconnectMaxRetries')) {
+ return Configuration.getConfigurationData()?.autoReconnectMaxRetries;
+ }
+ }
+
+ public static getStationTemplateUrls(): StationTemplateUrl[] | undefined {
+ Configuration.warnDeprecatedConfigurationKey(
+ 'stationTemplateURLs',
+ undefined,
+ "Use 'stationTemplateUrls' instead",
+ );
+ // eslint-disable-next-line @typescript-eslint/dot-notation
+ !isUndefined(
+ Configuration.getConfigurationData()!['stationTemplateURLs' as keyof ConfigurationData],
+ ) &&
+ (Configuration.getConfigurationData()!.stationTemplateUrls =
+ Configuration.getConfigurationData()![
+ // eslint-disable-next-line @typescript-eslint/dot-notation
+ 'stationTemplateURLs' as keyof ConfigurationData
+ ] as StationTemplateUrl[]);
+ Configuration.getConfigurationData()!.stationTemplateUrls.forEach(
+ (stationTemplateUrl: StationTemplateUrl) => {
+ // eslint-disable-next-line @typescript-eslint/dot-notation
+ if (!isUndefined(stationTemplateUrl['numberOfStation' as keyof StationTemplateUrl])) {
+ console.error(
+ `${chalk.green(Configuration.logPrefix())} ${chalk.red(
+ `Deprecated configuration key 'numberOfStation' usage for template file '${stationTemplateUrl.file}' in 'stationTemplateUrls'. Use 'numberOfStations' instead`,
+ )}`,
+ );
+ }
+ },
+ );
+ return Configuration.getConfigurationData()?.stationTemplateUrls;
+ }
+
+ public static getSupervisionUrls(): string | string[] | undefined {
+ Configuration.warnDeprecatedConfigurationKey(
+ 'supervisionURLs',
+ undefined,
+ "Use 'supervisionUrls' instead",
+ );
+ // eslint-disable-next-line @typescript-eslint/dot-notation
+ if (
+ !isUndefined(
+ Configuration.getConfigurationData()!['supervisionURLs' as keyof ConfigurationData],
+ )
+ ) {
+ Configuration.getConfigurationData()!.supervisionUrls = Configuration.getConfigurationData()![
+ // eslint-disable-next-line @typescript-eslint/dot-notation
+ 'supervisionURLs' as keyof ConfigurationData
+ ] as string | string[];
+ }
+ return Configuration.getConfigurationData()?.supervisionUrls;
+ }
+
+ public static getSupervisionUrlDistribution(): SupervisionUrlDistribution | undefined {
+ Configuration.warnDeprecatedConfigurationKey(
+ 'distributeStationToTenantEqually',
+ undefined,
+ "Use 'supervisionUrlDistribution' instead",
+ );
+ Configuration.warnDeprecatedConfigurationKey(
+ 'distributeStationsToTenantsEqually',
+ undefined,
+ "Use 'supervisionUrlDistribution' instead",
+ );
+ return hasOwnProp(Configuration.getConfigurationData(), 'supervisionUrlDistribution')
+ ? Configuration.getConfigurationData()?.supervisionUrlDistribution
+ : SupervisionUrlDistribution.ROUND_ROBIN;
+ }
+
+ public static workerPoolInUse(): boolean {
+ return [WorkerProcessType.dynamicPool, WorkerProcessType.staticPool].includes(
+ Configuration.buildWorkerSection().processType!,
+ );
+ }
+
+ public static workerDynamicPoolInUse(): boolean {
+ return Configuration.buildWorkerSection().processType === WorkerProcessType.dynamicPool;
+ }
+
+ private static isConfigurationSectionCached(sectionName: ConfigurationSection): boolean {
+ return Configuration.configurationSectionCache.has(sectionName);
+ }
+
+ private static cacheConfigurationSection(sectionName: ConfigurationSection): void {
+ switch (sectionName) {
+ case ConfigurationSection.log:
+ Configuration.configurationSectionCache.set(sectionName, Configuration.buildLogSection());
+ break;
+ case ConfigurationSection.performanceStorage:
+ Configuration.configurationSectionCache.set(
+ sectionName,
+ Configuration.buildPerformanceStorageSection(),
+ );
+ break;
+ case ConfigurationSection.worker:
+ Configuration.configurationSectionCache.set(
+ sectionName,
+ Configuration.buildWorkerSection(),
+ );
+ break;
+ case ConfigurationSection.uiServer:
+ Configuration.configurationSectionCache.set(
+ sectionName,
+ Configuration.buildUIServerSection(),
+ );
+ break;
+ default:
+ // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
+ throw new Error(`Unknown configuration section '${sectionName}'`);
+ }
+ }
+
+ private static buildUIServerSection(): UIServerConfiguration {
if (hasOwnProp(Configuration.getConfigurationData(), 'uiWebSocketServer')) {
console.error(
`${chalk.green(Configuration.logPrefix())} ${chalk.red(
delete uiServerConfiguration.options?.host;
uiServerConfiguration.options!.port = parseInt(process.env.PORT!);
}
- return Configuration.getConfigurationSection<UIServerConfiguration>(
- ConfigurationSection.uiServer,
- uiServerConfiguration,
- );
+ return uiServerConfiguration;
}
- public static getPerformanceStorage(): StorageConfiguration {
+ private static buildPerformanceStorageSection(): StorageConfiguration {
Configuration.warnDeprecatedConfigurationKey(
'URI',
ConfigurationSection.performanceStorage,
let storageConfiguration: StorageConfiguration = {
enabled: false,
type: StorageType.JSON_FILE,
- uri: this.getDefaultPerformanceStorageUri(StorageType.JSON_FILE),
+ uri: Configuration.getDefaultPerformanceStorageUri(StorageType.JSON_FILE),
};
if (hasOwnProp(Configuration.getConfigurationData(), ConfigurationSection.performanceStorage)) {
storageConfiguration = {
}),
};
}
- return Configuration.getConfigurationSection<StorageConfiguration>(
- ConfigurationSection.performanceStorage,
- storageConfiguration,
- );
- }
-
- public static getAutoReconnectMaxRetries(): number | undefined {
- Configuration.warnDeprecatedConfigurationKey(
- 'autoReconnectTimeout',
- undefined,
- "Use 'ConnectionTimeOut' OCPP parameter in charging station template instead",
- );
- Configuration.warnDeprecatedConfigurationKey(
- 'connectionTimeout',
- undefined,
- "Use 'ConnectionTimeOut' OCPP parameter in charging station template instead",
- );
- Configuration.warnDeprecatedConfigurationKey(
- 'autoReconnectMaxRetries',
- undefined,
- 'Use it in charging station template instead',
- );
- if (hasOwnProp(Configuration.getConfigurationData(), 'autoReconnectMaxRetries')) {
- return Configuration.getConfigurationData()?.autoReconnectMaxRetries;
- }
- }
-
- public static getStationTemplateUrls(): StationTemplateUrl[] | undefined {
- Configuration.warnDeprecatedConfigurationKey(
- 'stationTemplateURLs',
- undefined,
- "Use 'stationTemplateUrls' instead",
- );
- // eslint-disable-next-line @typescript-eslint/dot-notation
- !isUndefined(Configuration.getConfigurationData()!['stationTemplateURLs']) &&
- (Configuration.getConfigurationData()!.stationTemplateUrls =
- Configuration.getConfigurationData()![
- // eslint-disable-next-line @typescript-eslint/dot-notation
- 'stationTemplateURLs'
- ] as StationTemplateUrl[]);
- Configuration.getConfigurationData()!.stationTemplateUrls.forEach(
- (stationTemplateUrl: StationTemplateUrl) => {
- // eslint-disable-next-line @typescript-eslint/dot-notation
- if (!isUndefined(stationTemplateUrl['numberOfStation'])) {
- console.error(
- `${chalk.green(Configuration.logPrefix())} ${chalk.red(
- `Deprecated configuration key 'numberOfStation' usage for template file '${stationTemplateUrl.file}' in 'stationTemplateUrls'. Use 'numberOfStations' instead`,
- )}`,
- );
- }
- },
- );
- return Configuration.getConfigurationData()?.stationTemplateUrls;
+ return storageConfiguration;
}
- public static getLog(): LogConfiguration {
+ private static buildLogSection(): LogConfiguration {
Configuration.warnDeprecatedConfigurationKey(
'logEnabled',
undefined,
...(hasOwnProp(Configuration.getConfigurationData(), ConfigurationSection.log) &&
Configuration.getConfigurationData()?.log),
};
- return Configuration.getConfigurationSection<LogConfiguration>(
- ConfigurationSection.log,
- logConfiguration,
- );
+ return logConfiguration;
}
- public static getWorker(): WorkerConfiguration {
+ private static buildWorkerSection(): WorkerConfiguration {
Configuration.warnDeprecatedConfigurationKey(
'useWorkerPool',
undefined,
);
const defaultWorkerConfiguration: WorkerConfiguration = {
processType: WorkerProcessType.workerSet,
- startDelay: WorkerConstants.DEFAULT_WORKER_START_DELAY,
+ startDelay: DEFAULT_WORKER_START_DELAY,
elementsPerWorker: 'auto',
- elementStartDelay: WorkerConstants.DEFAULT_ELEMENT_START_DELAY,
- poolMinSize: WorkerConstants.DEFAULT_POOL_MIN_SIZE,
- poolMaxSize: WorkerConstants.DEFAULT_POOL_MAX_SIZE,
+ elementStartDelay: DEFAULT_ELEMENT_START_DELAY,
+ poolMinSize: DEFAULT_POOL_MIN_SIZE,
+ poolMaxSize: DEFAULT_POOL_MAX_SIZE,
};
hasOwnProp(Configuration.getConfigurationData(), 'workerPoolStrategy') &&
delete Configuration.getConfigurationData()?.workerPoolStrategy;
`Invalid worker process type '${workerConfiguration.processType}' defined in configuration`,
);
}
- return Configuration.getConfigurationSection<WorkerConfiguration>(
- ConfigurationSection.worker,
- workerConfiguration,
- );
- }
-
- public static workerPoolInUse(): boolean {
- return [WorkerProcessType.dynamicPool, WorkerProcessType.staticPool].includes(
- Configuration.getWorker().processType!,
- );
- }
-
- public static workerDynamicPoolInUse(): boolean {
- return Configuration.getWorker().processType === WorkerProcessType.dynamicPool;
- }
-
- public static getSupervisionUrls(): string | string[] | undefined {
- Configuration.warnDeprecatedConfigurationKey(
- 'supervisionURLs',
- undefined,
- "Use 'supervisionUrls' instead",
- );
- // eslint-disable-next-line @typescript-eslint/dot-notation
- if (!isUndefined(Configuration.getConfigurationData()!['supervisionURLs'])) {
- Configuration.getConfigurationData()!.supervisionUrls = Configuration.getConfigurationData()![
- // eslint-disable-next-line @typescript-eslint/dot-notation
- 'supervisionURLs'
- ] as string | string[];
- }
- return Configuration.getConfigurationData()?.supervisionUrls;
- }
-
- public static getSupervisionUrlDistribution(): SupervisionUrlDistribution | undefined {
- Configuration.warnDeprecatedConfigurationKey(
- 'distributeStationToTenantEqually',
- undefined,
- "Use 'supervisionUrlDistribution' instead",
- );
- Configuration.warnDeprecatedConfigurationKey(
- 'distributeStationsToTenantsEqually',
- undefined,
- "Use 'supervisionUrlDistribution' instead",
- );
- return hasOwnProp(Configuration.getConfigurationData(), 'supervisionUrlDistribution')
- ? Configuration.getConfigurationData()?.supervisionUrlDistribution
- : SupervisionUrlDistribution.ROUND_ROBIN;
+ return workerConfiguration;
}
private static logPrefix = (): string => {
) {
if (
sectionName &&
- !isUndefined(Configuration.getConfigurationData()![sectionName]) &&
+ !isUndefined(Configuration.getConfigurationData()![sectionName as keyof ConfigurationData]) &&
!isUndefined(
- (Configuration.getConfigurationData()![sectionName] as Record<string, unknown>)[key],
+ (
+ Configuration.getConfigurationData()![sectionName as keyof ConfigurationData] as Record<
+ string,
+ unknown
+ >
+ )[key],
)
) {
console.error(
}`,
)}`,
);
- } else if (!isUndefined(Configuration.getConfigurationData()![key])) {
+ } else if (
+ !isUndefined(Configuration.getConfigurationData()![key as keyof ConfigurationData])
+ ) {
console.error(
`${chalk.green(Configuration.logPrefix())} ${chalk.red(
`Deprecated configuration key '${key}' usage${
}
}
- private static getConfigurationSection<T>(
- sectionName: ConfigurationSection,
- sectionConfiguration?: T,
- ): T {
- if (!Configuration.configurationSectionCache.has(sectionName) && sectionConfiguration) {
- Configuration.configurationSectionCache.set(sectionName, sectionConfiguration);
- }
- return Configuration.configurationSectionCache.get(sectionName) as T;
- }
-
- private static getConfigurationData(): ConfigurationData | null {
+ private static getConfigurationData(): ConfigurationData | undefined {
if (!Configuration.configurationData) {
try {
Configuration.configurationData = JSON.parse(
readFileSync(Configuration.configurationFile, 'utf8'),
) as ConfigurationData;
+ if (!Configuration.configurationFileWatcher) {
+ Configuration.configurationFileWatcher = Configuration.getConfigurationFileWatcher();
+ }
} catch (error) {
Configuration.handleFileException(
Configuration.configurationFile,
Configuration.logPrefix(),
);
}
- if (!Configuration.configurationFileWatcher) {
- Configuration.configurationFileWatcher = Configuration.getConfigurationFileWatcher();
- }
}
return Configuration.configurationData;
}
try {
return watch(Configuration.configurationFile, (event, filename): void => {
if (filename!.trim()!.length > 0 && event === 'change') {
- // Nullify to force configuration file reading
- Configuration.configurationData = null;
+ delete Configuration.configurationData;
Configuration.configurationSectionCache.clear();
if (!isUndefined(Configuration.configurationChangeCallback)) {
- Configuration.configurationChangeCallback().catch((error) => {
+ Configuration.configurationChangeCallback!().catch((error) => {
throw typeof error === 'string' ? new Error(error) : error;
});
}