1 import ConfigurationData
, {
5 SupervisionUrlDistribution
,
8 } from
'../types/ConfigurationData';
10 import Constants from
'./Constants';
11 import { EmptyObject
} from
'../types/EmptyObject';
12 import { FileType
} from
'../types/FileType';
13 import { HandleErrorParams
} from
'../types/Error';
14 import { StorageType
} from
'../types/Storage';
15 import WorkerConstants from
'../worker/WorkerConstants';
16 import { WorkerProcessType
} from
'../types/Worker';
17 import chalk from
'chalk';
18 import { fileURLToPath
} from
'url';
20 import path from
'path';
22 export default class Configuration
{
23 private static configurationFile
= path
.join(
24 path
.resolve(path
.dirname(fileURLToPath(import.meta
.url
)), '../'),
29 private static configurationFileWatcher
: fs
.FSWatcher
;
30 private static configuration
: ConfigurationData
| null = null;
31 private static configurationChangeCallback
: () => Promise
<void>;
33 static setConfigurationChangeCallback(cb
: () => Promise
<void>): void {
34 Configuration
.configurationChangeCallback
= cb
;
37 static getLogStatisticsInterval(): number {
38 Configuration
.warnDeprecatedConfigurationKey(
39 'statisticsDisplayInterval',
41 "Use 'logStatisticsInterval' instead"
44 return Configuration
.objectHasOwnProperty(Configuration
.getConfig(), 'logStatisticsInterval')
45 ? Configuration
.getConfig().logStatisticsInterval
46 : Constants
.DEFAULT_LOG_STATISTICS_INTERVAL
;
49 static getUIServer(): UIServerConfiguration
{
50 if (Configuration
.objectHasOwnProperty(Configuration
.getConfig(), 'uiWebSocketServer')) {
52 chalk
`{green ${Configuration.logPrefix()}} {red Deprecated configuration section 'uiWebSocketServer' usage. Use 'uiServer' instead}`
55 let options
: ServerOptions
= {
56 host
: Constants
.DEFAULT_UI_WEBSOCKET_SERVER_HOST
,
57 port
: Constants
.DEFAULT_UI_WEBSOCKET_SERVER_PORT
,
59 let uiServerConfiguration
: UIServerConfiguration
= {
63 if (Configuration
.objectHasOwnProperty(Configuration
.getConfig(), 'uiServer')) {
64 if (Configuration
.objectHasOwnProperty(Configuration
.getConfig().uiServer
, 'options')) {
67 ...(Configuration
.objectHasOwnProperty(
68 Configuration
.getConfig().uiServer
.options
,
70 ) && { host
: Configuration
.getConfig().uiServer
.options
.host
}),
71 ...(Configuration
.objectHasOwnProperty(
72 Configuration
.getConfig().uiServer
.options
,
74 ) && { port
: Configuration
.getConfig().uiServer
.options
.port
}),
77 uiServerConfiguration
= {
78 ...uiServerConfiguration
,
79 ...(Configuration
.objectHasOwnProperty(Configuration
.getConfig().uiServer
, 'enabled') && {
80 enabled
: Configuration
.getConfig().uiServer
.enabled
,
85 return uiServerConfiguration
;
88 static getPerformanceStorage(): StorageConfiguration
{
89 Configuration
.warnDeprecatedConfigurationKey('URI', 'performanceStorage', "Use 'uri' instead");
90 let storageConfiguration
: StorageConfiguration
= {
92 type: StorageType
.JSON_FILE
,
93 uri
: this.getDefaultPerformanceStorageUri(StorageType
.JSON_FILE
),
95 if (Configuration
.objectHasOwnProperty(Configuration
.getConfig(), 'performanceStorage')) {
96 storageConfiguration
= {
97 ...storageConfiguration
,
98 ...(Configuration
.objectHasOwnProperty(
99 Configuration
.getConfig().performanceStorage
,
101 ) && { enabled
: Configuration
.getConfig().performanceStorage
.enabled
}),
102 ...(Configuration
.objectHasOwnProperty(
103 Configuration
.getConfig().performanceStorage
,
105 ) && { type: Configuration
.getConfig().performanceStorage
.type }),
106 ...(Configuration
.objectHasOwnProperty(
107 Configuration
.getConfig().performanceStorage
,
110 uri
: this.getDefaultPerformanceStorageUri(
111 Configuration
.getConfig()?.performanceStorage
?.type ?? StorageType
.JSON_FILE
116 return storageConfiguration
;
119 static getAutoReconnectMaxRetries(): number {
120 Configuration
.warnDeprecatedConfigurationKey(
121 'autoReconnectTimeout',
123 "Use 'ConnectionTimeOut' OCPP parameter in charging station template instead"
125 Configuration
.warnDeprecatedConfigurationKey(
128 "Use 'ConnectionTimeOut' OCPP parameter in charging station template instead"
130 Configuration
.warnDeprecatedConfigurationKey(
131 'autoReconnectMaxRetries',
133 'Use it in charging station template instead'
136 if (Configuration
.objectHasOwnProperty(Configuration
.getConfig(), 'autoReconnectMaxRetries')) {
137 return Configuration
.getConfig().autoReconnectMaxRetries
;
141 static getStationTemplateUrls(): StationTemplateUrl
[] {
142 Configuration
.warnDeprecatedConfigurationKey(
143 'stationTemplateURLs',
145 "Use 'stationTemplateUrls' instead"
147 !Configuration
.isUndefined(Configuration
.getConfig()['stationTemplateURLs']) &&
148 (Configuration
.getConfig().stationTemplateUrls
= Configuration
.getConfig()[
149 'stationTemplateURLs'
150 ] as StationTemplateUrl
[]);
151 Configuration
.getConfig().stationTemplateUrls
.forEach((stationUrl
: StationTemplateUrl
) => {
152 if (!Configuration
.isUndefined(stationUrl
['numberOfStation'])) {
154 chalk
`{green ${Configuration.logPrefix()}} {red Deprecated configuration key 'numberOfStation' usage for template file '${
156 }' in 'stationTemplateUrls'. Use 'numberOfStations' instead}`
161 return Configuration
.getConfig().stationTemplateUrls
;
164 static getWorker(): WorkerConfiguration
{
165 Configuration
.warnDeprecatedConfigurationKey(
168 "Use 'worker' section to define the type of worker process model instead"
170 Configuration
.warnDeprecatedConfigurationKey(
173 "Use 'worker' section to define the type of worker process model instead"
175 Configuration
.warnDeprecatedConfigurationKey(
178 "Use 'worker' section to define the worker start delay instead"
180 Configuration
.warnDeprecatedConfigurationKey(
181 'chargingStationsPerWorker',
183 "Use 'worker' section to define the number of element(s) per worker instead"
185 Configuration
.warnDeprecatedConfigurationKey(
188 "Use 'worker' section to define the worker's element start delay instead"
190 Configuration
.warnDeprecatedConfigurationKey(
193 "Use 'worker' section to define the worker pool minimum size instead"
195 Configuration
.warnDeprecatedConfigurationKey(
198 "Use 'worker' section to define the worker pool maximum size instead"
200 Configuration
.warnDeprecatedConfigurationKey(
201 'workerPoolMaxSize;',
203 "Use 'worker' section to define the worker pool maximum size instead"
205 Configuration
.warnDeprecatedConfigurationKey(
206 'workerPoolStrategy;',
208 "Use 'worker' section to define the worker pool strategy instead"
210 const workerConfiguration
: WorkerConfiguration
= {
211 processType
: Configuration
.objectHasOwnProperty(Configuration
.getConfig(), 'workerProcess')
212 ? Configuration
.getConfig().workerProcess
213 : WorkerProcessType
.WORKER_SET
,
214 startDelay
: Configuration
.objectHasOwnProperty(Configuration
.getConfig(), 'workerStartDelay')
215 ? Configuration
.getConfig().workerStartDelay
216 : WorkerConstants
.DEFAULT_WORKER_START_DELAY
,
217 elementsPerWorker
: Configuration
.objectHasOwnProperty(
218 Configuration
.getConfig(),
219 'chargingStationsPerWorker'
221 ? Configuration
.getConfig().chargingStationsPerWorker
222 : WorkerConstants
.DEFAULT_ELEMENTS_PER_WORKER
,
223 elementStartDelay
: Configuration
.objectHasOwnProperty(
224 Configuration
.getConfig(),
227 ? Configuration
.getConfig().elementStartDelay
228 : WorkerConstants
.DEFAULT_ELEMENT_START_DELAY
,
229 poolMinSize
: Configuration
.objectHasOwnProperty(
230 Configuration
.getConfig(),
233 ? Configuration
.getConfig().workerPoolMinSize
234 : WorkerConstants
.DEFAULT_POOL_MIN_SIZE
,
235 poolMaxSize
: Configuration
.objectHasOwnProperty(
236 Configuration
.getConfig(),
239 ? Configuration
.getConfig().workerPoolMaxSize
240 : WorkerConstants
.DEFAULT_POOL_MAX_SIZE
,
241 poolStrategy
: Configuration
.getConfig().workerPoolStrategy
,
243 if (Configuration
.objectHasOwnProperty(Configuration
.getConfig(), 'worker')) {
244 return { ...workerConfiguration
, ...Configuration
.getConfig().worker
};
246 return workerConfiguration
;
249 static getLogConsole(): boolean {
250 Configuration
.warnDeprecatedConfigurationKey('consoleLog', null, "Use 'logConsole' instead");
251 return Configuration
.objectHasOwnProperty(Configuration
.getConfig(), 'logConsole')
252 ? Configuration
.getConfig().logConsole
256 static getLogFormat(): string {
257 return Configuration
.objectHasOwnProperty(Configuration
.getConfig(), 'logFormat')
258 ? Configuration
.getConfig().logFormat
262 static getLogRotate(): boolean {
263 return Configuration
.objectHasOwnProperty(Configuration
.getConfig(), 'logRotate')
264 ? Configuration
.getConfig().logRotate
268 static getLogMaxFiles(): number {
269 return Configuration
.objectHasOwnProperty(Configuration
.getConfig(), 'logMaxFiles')
270 ? Configuration
.getConfig().logMaxFiles
274 static getLogLevel(): string {
275 return Configuration
.objectHasOwnProperty(Configuration
.getConfig(), 'logLevel')
276 ? Configuration
.getConfig().logLevel
.toLowerCase()
280 static getLogFile(): string {
281 return Configuration
.objectHasOwnProperty(Configuration
.getConfig(), 'logFile')
282 ? Configuration
.getConfig().logFile
286 static getLogErrorFile(): string {
287 Configuration
.warnDeprecatedConfigurationKey('errorFile', null, "Use 'logErrorFile' instead");
288 return Configuration
.objectHasOwnProperty(Configuration
.getConfig(), 'logErrorFile')
289 ? Configuration
.getConfig().logErrorFile
293 static getSupervisionUrls(): string | string[] {
294 Configuration
.warnDeprecatedConfigurationKey(
297 "Use 'supervisionUrls' instead"
299 !Configuration
.isUndefined(Configuration
.getConfig()['supervisionURLs']) &&
300 (Configuration
.getConfig().supervisionUrls
= Configuration
.getConfig()[
304 return Configuration
.getConfig().supervisionUrls
;
307 static getSupervisionUrlDistribution(): SupervisionUrlDistribution
{
308 Configuration
.warnDeprecatedConfigurationKey(
309 'distributeStationToTenantEqually',
311 "Use 'supervisionUrlDistribution' instead"
313 Configuration
.warnDeprecatedConfigurationKey(
314 'distributeStationsToTenantsEqually',
316 "Use 'supervisionUrlDistribution' instead"
318 return Configuration
.objectHasOwnProperty(
319 Configuration
.getConfig(),
320 'supervisionUrlDistribution'
322 ? Configuration
.getConfig().supervisionUrlDistribution
323 : SupervisionUrlDistribution
.ROUND_ROBIN
;
326 private static logPrefix(): string {
327 return new Date().toLocaleString() + ' Simulator configuration |';
330 private static warnDeprecatedConfigurationKey(
332 sectionName
?: string,
337 !Configuration
.isUndefined(Configuration
.getConfig()[sectionName
]) &&
338 !Configuration
.isUndefined(
339 (Configuration
.getConfig()[sectionName
] as Record
<string, unknown
>)[key
]
343 chalk
`{green ${Configuration.logPrefix()}} {red Deprecated configuration key '${key}' usage in section '${sectionName}'${
344 logMsgToAppend && '. ' + logMsgToAppend
347 } else if (!Configuration
.isUndefined(Configuration
.getConfig()[key
])) {
349 chalk
`{green ${Configuration.logPrefix()}} {red Deprecated configuration key '${key}' usage${
350 logMsgToAppend && '. ' + logMsgToAppend
356 // Read the config file
357 private static getConfig(): ConfigurationData
{
358 if (!Configuration
.configuration
) {
360 Configuration
.configuration
= JSON
.parse(
361 fs
.readFileSync(Configuration
.configurationFile
, 'utf8')
362 ) as ConfigurationData
;
364 Configuration
.handleFileException(
365 Configuration
.logPrefix(),
366 FileType
.Configuration
,
367 Configuration
.configurationFile
,
368 error
as NodeJS
.ErrnoException
371 if (!Configuration
.configurationFileWatcher
) {
372 Configuration
.configurationFileWatcher
= Configuration
.getConfigurationFileWatcher();
375 return Configuration
.configuration
;
378 private static getConfigurationFileWatcher(): fs
.FSWatcher
{
380 return fs
.watch(Configuration
.configurationFile
, (event
, filename
): void => {
381 if (filename
&& event
=== 'change') {
382 // Nullify to force configuration file reading
383 Configuration
.configuration
= null;
384 if (!Configuration
.isUndefined(Configuration
.configurationChangeCallback
)) {
385 Configuration
.configurationChangeCallback().catch((error
) => {
386 throw typeof error
=== 'string' ? new Error(error
) : error
;
392 Configuration
.handleFileException(
393 Configuration
.logPrefix(),
394 FileType
.Configuration
,
395 Configuration
.configurationFile
,
396 error
as NodeJS
.ErrnoException
401 private static getDefaultPerformanceStorageUri(storageType
: StorageType
) {
402 const SQLiteFileName
= `${Constants.DEFAULT_PERFORMANCE_RECORDS_DB_NAME}.db`;
403 switch (storageType
) {
404 case StorageType
.JSON_FILE
:
405 return `file://${path.join(
406 path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../'),
407 Constants.DEFAULT_PERFORMANCE_RECORDS_FILENAME
409 case StorageType
.SQLITE
:
410 return `file://${path.join(
411 path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../'),
415 throw new Error(`Performance storage URI is mandatory with storage type '${storageType}'`);
419 private static objectHasOwnProperty(object
: unknown
, property
: string): boolean {
420 return Object.prototype
.hasOwnProperty
.call(object
, property
) as boolean;
423 private static isUndefined(obj
: unknown
): boolean {
424 return typeof obj
=== 'undefined';
427 private static handleFileException(
431 error
: NodeJS
.ErrnoException
,
432 params
: HandleErrorParams
<EmptyObject
> = { throwError
: true }
434 const prefix
= logPrefix
.length
!== 0 ? logPrefix
+ ' ' : '';
435 if (error
.code
=== 'ENOENT') {
437 chalk
.green(prefix
) + chalk
.red(fileType
+ ' file ' + filePath
+ ' not found: '),
440 } else if (error
.code
=== 'EEXIST') {
442 chalk
.green(prefix
) + chalk
.red(fileType
+ ' file ' + filePath
+ ' already exists: '),
445 } else if (error
.code
=== 'EACCES') {
447 chalk
.green(prefix
) + chalk
.red(fileType
+ ' file ' + filePath
+ ' access denied: '),
452 chalk
.green(prefix
) + chalk
.red(fileType
+ ' file ' + filePath
+ ' error: '),
456 if (params
?.throwError
) {