type ChargingStationWorkerMessage,
type ChargingStationWorkerMessageData,
ChargingStationWorkerMessageEvents,
+ ConfigurationSection,
ProcedureName,
type StationTemplateUrl,
type Statistics,
+ type StorageConfiguration,
+ type UIServerConfiguration,
+ type WorkerConfiguration,
} from '../types';
import {
Configuration,
dirname(fileURLToPath(import.meta.url)),
`ChargingStationWorker${extname(fileURLToPath(import.meta.url))}`,
);
- Configuration.getUIServer().enabled === true &&
- (this.uiServer = UIServerFactory.getUIServerImplementation(Configuration.getUIServer()));
- Configuration.getPerformanceStorage().enabled === true &&
+ Configuration.getConfigurationSection<UIServerConfiguration>(ConfigurationSection.uiServer)
+ .enabled === true &&
+ (this.uiServer = UIServerFactory.getUIServerImplementation(
+ Configuration.getConfigurationSection<UIServerConfiguration>(ConfigurationSection.uiServer),
+ ));
+ Configuration.getConfigurationSection<StorageConfiguration>(
+ ConfigurationSection.performanceStorage,
+ ).enabled === true &&
(this.storage = StorageFactory.getStorage(
- Configuration.getPerformanceStorage().type!,
- Configuration.getPerformanceStorage().uri!,
+ Configuration.getConfigurationSection<StorageConfiguration>(
+ ConfigurationSection.performanceStorage,
+ ).type!,
+ Configuration.getConfigurationSection<StorageConfiguration>(
+ ConfigurationSection.performanceStorage,
+ ).uri!,
this.logPrefix(),
));
Configuration.setConfigurationChangeCallback(async () => Bootstrap.getInstance().restart());
this.version
} started with ${this.numberOfChargingStations.toString()} charging station(s) from ${this.numberOfChargingStationTemplates.toString()} configured charging station template(s) and ${
Configuration.workerDynamicPoolInUse()
- ? `${Configuration.getWorker().poolMinSize?.toString()}/`
+ ? `${Configuration.getConfigurationSection<WorkerConfiguration>(
+ ConfigurationSection.worker,
+ ).poolMinSize?.toString()}/`
: ''
}${this.workerImplementation?.size}${
Configuration.workerPoolInUse()
- ? `/${Configuration.getWorker().poolMaxSize?.toString()}`
+ ? `/${Configuration.getConfigurationSection<WorkerConfiguration>(
+ ConfigurationSection.worker,
+ ).poolMaxSize?.toString()}`
: ''
- } worker(s) concurrently running in '${Configuration.getWorker().processType}' mode${
+ } worker(s) concurrently running in '${
+ Configuration.getConfigurationSection<WorkerConfiguration>(
+ ConfigurationSection.worker,
+ ).processType
+ }' mode${
!isNullOrUndefined(this.workerImplementation?.maxElementsPerWorker)
? ` (${this.workerImplementation?.maxElementsPerWorker} charging station(s) per worker)`
: ''
private initializeWorkerImplementation(): void {
let elementsPerWorker: number | undefined;
- if (Configuration.getWorker()?.elementsPerWorker === 'auto') {
+ if (
+ Configuration.getConfigurationSection<WorkerConfiguration>(ConfigurationSection.worker)
+ ?.elementsPerWorker === 'auto'
+ ) {
elementsPerWorker =
this.numberOfChargingStations > availableParallelism()
? Math.round(this.numberOfChargingStations / availableParallelism())
this.workerImplementation === null &&
(this.workerImplementation = WorkerFactory.getWorkerImplementation<ChargingStationWorkerData>(
this.workerScript,
- Configuration.getWorker().processType!,
+ Configuration.getConfigurationSection<WorkerConfiguration>(ConfigurationSection.worker)
+ .processType!,
{
- workerStartDelay: Configuration.getWorker().startDelay,
- elementStartDelay: Configuration.getWorker().elementStartDelay,
- poolMaxSize: Configuration.getWorker().poolMaxSize!,
- poolMinSize: Configuration.getWorker().poolMinSize!,
+ workerStartDelay: Configuration.getConfigurationSection<WorkerConfiguration>(
+ ConfigurationSection.worker,
+ ).startDelay,
+ elementStartDelay: Configuration.getConfigurationSection<WorkerConfiguration>(
+ ConfigurationSection.worker,
+ ).elementStartDelay,
+ poolMaxSize: Configuration.getConfigurationSection<WorkerConfiguration>(
+ ConfigurationSection.worker,
+ ).poolMaxSize!,
+ poolMinSize: Configuration.getConfigurationSection<WorkerConfiguration>(
+ ConfigurationSection.worker,
+ ).poolMinSize!,
elementsPerWorker:
- elementsPerWorker ?? (Configuration.getWorker().elementsPerWorker as number),
+ elementsPerWorker ??
+ (Configuration.getConfigurationSection<WorkerConfiguration>(ConfigurationSection.worker)
+ .elementsPerWorker as number),
poolOptions: {
messageHandler: this.messageHandler.bind(this) as (message: unknown) => void,
},
import { UIHttpServer } from './UIHttpServer';
import { UIServerUtils } from './UIServerUtils';
import { UIWebSocketServer } from './UIWebSocketServer';
-import { ApplicationProtocol, type UIServerConfiguration } from '../../types';
+import { ApplicationProtocol, ConfigurationSection, type UIServerConfiguration } from '../../types';
import { Configuration } from '../../utils';
export class UIServerFactory {
),
);
}
- switch (uiServerConfiguration?.type ?? Configuration.getUIServer().type) {
+ switch (
+ uiServerConfiguration?.type ??
+ Configuration.getConfigurationSection<UIServerConfiguration>(ConfigurationSection.uiServer)
+ .type
+ ) {
case ApplicationProtocol.WS:
- return new UIWebSocketServer(uiServerConfiguration ?? Configuration.getUIServer());
+ return new UIWebSocketServer(
+ uiServerConfiguration ??
+ Configuration.getConfigurationSection<UIServerConfiguration>(
+ ConfigurationSection.uiServer,
+ ),
+ );
case ApplicationProtocol.HTTP:
- return new UIHttpServer(uiServerConfiguration ?? Configuration.getUIServer());
+ return new UIHttpServer(
+ uiServerConfiguration ??
+ Configuration.getConfigurationSection<UIServerConfiguration>(
+ ConfigurationSection.uiServer,
+ ),
+ );
default:
return null;
}
import { parentPort } from 'node:worker_threads';
import {
+ ConfigurationSection,
type IncomingRequestCommand,
+ type LogConfiguration,
MessageType,
type RequestCommand,
type Statistics,
+ type StorageConfiguration,
type TimestampedData,
} from '../types';
import {
public start(): void {
this.startLogStatisticsInterval();
- if (Configuration.getPerformanceStorage().enabled) {
+ if (
+ Configuration.getConfigurationSection<StorageConfiguration>(
+ ConfigurationSection.performanceStorage,
+ ).enabled
+ ) {
logger.info(
`${this.logPrefix()} storage enabled: type ${
- Configuration.getPerformanceStorage().type
- }, uri: ${Configuration.getPerformanceStorage().uri}`,
+ Configuration.getConfigurationSection<StorageConfiguration>(
+ ConfigurationSection.performanceStorage,
+ ).type
+ }, uri: ${
+ Configuration.getConfigurationSection<StorageConfiguration>(
+ ConfigurationSection.performanceStorage,
+ ).uri
+ }`,
);
}
}
}
private startLogStatisticsInterval(): void {
- const logStatisticsInterval = Configuration.getLog().enabled
- ? Configuration.getLog().statisticsInterval!
+ const logStatisticsInterval = Configuration.getConfigurationSection<LogConfiguration>(
+ ConfigurationSection.log,
+ ).enabled
+ ? Configuration.getConfigurationSection<LogConfiguration>(ConfigurationSection.log)
+ .statisticsInterval!
: 0;
if (logStatisticsInterval > 0 && !this.displayInterval) {
this.displayInterval = setInterval(() => {
logger.info(
`${this.logPrefix()} already logged every ${formatDurationSeconds(logStatisticsInterval)}`,
);
- } else if (Configuration.getLog().enabled) {
+ } else if (
+ Configuration.getConfigurationSection<LogConfiguration>(ConfigurationSection.log).enabled
+ ) {
logger.info(
`${this.logPrefix()} log interval is set to ${logStatisticsInterval?.toString()}. Not logging statistics`,
);
this.statistics.statisticsData.get(entryName)!.measurementTimeSeries as TimestampedData[],
),
);
- if (Configuration.getPerformanceStorage().enabled) {
+ if (
+ Configuration.getConfigurationSection<StorageConfiguration>(
+ ConfigurationSection.performanceStorage,
+ ).enabled
+ ) {
parentPort?.postMessage(buildPerformanceStatisticsMessage(this.statistics));
}
}
type ServerOptions = ListenOptions;
+export enum ConfigurationSection {
+ log = 'log',
+ performanceStorage = 'performanceStorage',
+ worker = 'worker',
+ uiServer = 'uiServer',
+}
+
export enum SupervisionUrlDistribution {
ROUND_ROBIN = 'round-robin',
RANDOM = 'random',
} from './ChargingStationTemplate';
export {
type ConfigurationData,
+ ConfigurationSection,
type LogConfiguration,
type StationTemplateUrl,
type StorageConfiguration,
import {
ApplicationProtocol,
type ConfigurationData,
+ ConfigurationSection,
FileType,
type LogConfiguration,
type StationTemplateUrl,
} from '../types';
import { WorkerConstants, WorkerProcessType } from '../worker';
-enum ConfigurationSection {
- log = 'log',
- performanceStorage = 'performanceStorage',
- worker = 'worker',
- uiServer = 'uiServer',
-}
-
export class Configuration {
private static configurationFile = join(
dirname(fileURLToPath(import.meta.url)),
private static configurationSectionCache = new Map<
ConfigurationSection,
LogConfiguration | StorageConfiguration | WorkerConfiguration | UIServerConfiguration
- >();
+ >([
+ [ConfigurationSection.log, Configuration.buildLogSection()],
+ [ConfigurationSection.performanceStorage, Configuration.buildPerformanceStorageSection()],
+ [ConfigurationSection.worker, Configuration.buildWorkerSection()],
+ [ConfigurationSection.uiServer, Configuration.buildUIServerSection()],
+ ]);
private static configurationChangeCallback: () => Promise<void>;
Configuration.configurationChangeCallback = cb;
}
- public static getUIServer(): UIServerConfiguration {
+ public static getConfigurationSection<T>(sectionName: ConfigurationSection): T {
+ 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']) &&
+ (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;
+ }
+
+ public static workerPoolInUse(): boolean {
+ return [WorkerProcessType.dynamicPool, WorkerProcessType.staticPool].includes(
+ Configuration.buildWorkerSection().processType!,
+ );
+ }
+
+ public static workerDynamicPoolInUse(): boolean {
+ return Configuration.buildWorkerSection().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;
+ }
+
+ 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,
}),
};
}
- return Configuration.getConfigurationSection<StorageConfiguration>(
- ConfigurationSection.performanceStorage,
- storageConfiguration,
- );
+ return 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;
- }
-
- 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,
`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 => {
}
}
- 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 {
if (!Configuration.configurationData) {
try {
import { Configuration } from './Configuration';
import { insertAt } from './Utils';
+import { ConfigurationSection, type LogConfiguration } from '../types';
let transports: transport[];
-if (Configuration.getLog().rotate === true) {
- const logMaxFiles = Configuration.getLog().maxFiles;
- const logMaxSize = Configuration.getLog().maxSize;
+if (
+ Configuration.getConfigurationSection<LogConfiguration>(ConfigurationSection.log).rotate === true
+) {
+ const logMaxFiles = Configuration.getConfigurationSection<LogConfiguration>(
+ ConfigurationSection.log,
+ ).maxFiles;
+ const logMaxSize = Configuration.getConfigurationSection<LogConfiguration>(
+ ConfigurationSection.log,
+ ).maxSize;
transports = [
new DailyRotateFile({
filename: insertAt(
- Configuration.getLog().errorFile!,
+ Configuration.getConfigurationSection<LogConfiguration>(ConfigurationSection.log)
+ .errorFile!,
'-%DATE%',
- Configuration.getLog().errorFile!.indexOf('.log'),
+ Configuration.getConfigurationSection<LogConfiguration>(
+ ConfigurationSection.log,
+ ).errorFile!.indexOf('.log'),
),
level: 'error',
...(logMaxFiles && { maxFiles: logMaxFiles }),
}),
new DailyRotateFile({
filename: insertAt(
- Configuration.getLog().file!,
+ Configuration.getConfigurationSection<LogConfiguration>(ConfigurationSection.log).file!,
'-%DATE%',
- Configuration.getLog().file!.indexOf('.log'),
+ Configuration.getConfigurationSection<LogConfiguration>(
+ ConfigurationSection.log,
+ ).file!.indexOf('.log'),
),
...(logMaxFiles && { maxFiles: logMaxFiles }),
...(logMaxSize && { maxSize: logMaxSize }),
];
} else {
transports = [
- new TransportType.File({ filename: Configuration.getLog().errorFile, level: 'error' }),
- new TransportType.File({ filename: Configuration.getLog().file }),
+ new TransportType.File({
+ filename: Configuration.getConfigurationSection<LogConfiguration>(ConfigurationSection.log)
+ .errorFile,
+ level: 'error',
+ }),
+ new TransportType.File({
+ filename: Configuration.getConfigurationSection<LogConfiguration>(ConfigurationSection.log)
+ .file,
+ }),
];
}
export const logger = createLogger({
- silent: !Configuration.getLog().enabled,
- level: Configuration.getLog().level,
- format: format.combine(format.splat(), (format[Configuration.getLog().format!] as FormatWrap)()),
+ silent: !Configuration.getConfigurationSection<LogConfiguration>(ConfigurationSection.log)
+ .enabled,
+ level: Configuration.getConfigurationSection<LogConfiguration>(ConfigurationSection.log).level,
+ format: format.combine(
+ format.splat(),
+ (
+ format[
+ Configuration.getConfigurationSection<LogConfiguration>(ConfigurationSection.log).format!
+ ] as FormatWrap
+ )(),
+ ),
transports,
});
// If enabled, log to the `console` with the format:
// `${info.level}: ${info.message} JSON.stringify({ ...rest }) `
//
-if (Configuration.getLog().console) {
+if (Configuration.getConfigurationSection<LogConfiguration>(ConfigurationSection.log).console) {
logger.add(
new TransportType.Console({
format: format.combine(
format.splat(),
- (format[Configuration.getLog().format!] as FormatWrap)(),
+ (
+ format[
+ Configuration.getConfigurationSection<LogConfiguration>(ConfigurationSection.log)
+ .format!
+ ] as FormatWrap
+ )(),
),
}),
);