1 import ConfigurationData
, {
4 SupervisionUrlDistribution
,
7 } from
'../types/ConfigurationData';
9 import Constants from
'./Constants';
10 import { EmptyObject
} from
'../types/EmptyObject';
11 import { FileType
} from
'../types/FileType';
12 import { HandleErrorParams
} from
'../types/Error';
13 import { StorageType
} from
'../types/Storage';
14 import WorkerConstants from
'../worker/WorkerConstants';
15 import { WorkerProcessType
} from
'../types/Worker';
16 import chalk from
'chalk';
17 import { fileURLToPath
} from
'url';
19 import path from
'path';
21 export default class Configuration
{
22 private static configurationFile
= path
.join(
23 path
.resolve(path
.dirname(fileURLToPath(import.meta
.url
)), '../'),
28 private static configurationFileWatcher
: fs
.FSWatcher
;
29 private static configuration
: ConfigurationData
| null = null;
30 private static configurationChangeCallback
: () => Promise
<void>;
32 static setConfigurationChangeCallback(cb
: () => Promise
<void>): void {
33 Configuration
.configurationChangeCallback
= cb
;
36 static getLogStatisticsInterval(): number {
37 Configuration
.warnDeprecatedConfigurationKey(
38 'statisticsDisplayInterval',
40 "Use 'logStatisticsInterval' instead"
43 return Configuration
.objectHasOwnProperty(Configuration
.getConfig(), 'logStatisticsInterval')
44 ? Configuration
.getConfig().logStatisticsInterval
45 : Constants
.DEFAULT_LOG_STATISTICS_INTERVAL
;
48 static getUIServer(): UIServerConfiguration
{
49 if (Configuration
.objectHasOwnProperty(Configuration
.getConfig(), 'uiWebSocketServer')) {
51 chalk
`{green ${Configuration.logPrefix()}} {red Deprecated configuration section 'uiWebSocketServer' usage. Use 'uiServer' instead}`
54 let uiServerConfiguration
: UIServerConfiguration
= {
57 host
: Constants
.DEFAULT_UI_WEBSOCKET_SERVER_HOST
,
58 port
: Constants
.DEFAULT_UI_WEBSOCKET_SERVER_PORT
,
61 if (Configuration
.objectHasOwnProperty(Configuration
.getConfig(), 'uiServer')) {
62 uiServerConfiguration
= {
63 ...uiServerConfiguration
,
64 ...Configuration
.getConfig().uiServer
,
67 return uiServerConfiguration
;
70 static getPerformanceStorage(): StorageConfiguration
{
71 Configuration
.warnDeprecatedConfigurationKey('URI', 'performanceStorage', "Use 'uri' instead");
72 let storageConfiguration
: StorageConfiguration
= {
74 type: StorageType
.JSON_FILE
,
75 uri
: this.getDefaultPerformanceStorageUri(StorageType
.JSON_FILE
),
77 if (Configuration
.objectHasOwnProperty(Configuration
.getConfig(), 'performanceStorage')) {
78 storageConfiguration
= {
79 ...storageConfiguration
,
80 ...Configuration
.getConfig().performanceStorage
,
83 return storageConfiguration
;
86 static getAutoReconnectMaxRetries(): number {
87 Configuration
.warnDeprecatedConfigurationKey(
88 'autoReconnectTimeout',
90 "Use 'ConnectionTimeOut' OCPP parameter in charging station template instead"
92 Configuration
.warnDeprecatedConfigurationKey(
95 "Use 'ConnectionTimeOut' OCPP parameter in charging station template instead"
97 Configuration
.warnDeprecatedConfigurationKey(
98 'autoReconnectMaxRetries',
100 'Use it in charging station template instead'
103 if (Configuration
.objectHasOwnProperty(Configuration
.getConfig(), 'autoReconnectMaxRetries')) {
104 return Configuration
.getConfig().autoReconnectMaxRetries
;
108 static getStationTemplateUrls(): StationTemplateUrl
[] {
109 Configuration
.warnDeprecatedConfigurationKey(
110 'stationTemplateURLs',
112 "Use 'stationTemplateUrls' instead"
114 !Configuration
.isUndefined(Configuration
.getConfig()['stationTemplateURLs']) &&
115 (Configuration
.getConfig().stationTemplateUrls
= Configuration
.getConfig()[
116 'stationTemplateURLs'
117 ] as StationTemplateUrl
[]);
118 Configuration
.getConfig().stationTemplateUrls
.forEach((stationUrl
: StationTemplateUrl
) => {
119 if (!Configuration
.isUndefined(stationUrl
['numberOfStation'])) {
121 chalk
`{green ${Configuration.logPrefix()}} {red Deprecated configuration key 'numberOfStation' usage for template file '${
123 }' in 'stationTemplateUrls'. Use 'numberOfStations' instead}`
128 return Configuration
.getConfig().stationTemplateUrls
;
131 static getWorker(): WorkerConfiguration
{
132 Configuration
.warnDeprecatedConfigurationKey(
135 "Use 'worker' section to define the type of worker process model instead"
137 Configuration
.warnDeprecatedConfigurationKey(
140 "Use 'worker' section to define the type of worker process model instead"
142 Configuration
.warnDeprecatedConfigurationKey(
145 "Use 'worker' section to define the worker start delay instead"
147 Configuration
.warnDeprecatedConfigurationKey(
148 'chargingStationsPerWorker',
150 "Use 'worker' section to define the number of element(s) per worker instead"
152 Configuration
.warnDeprecatedConfigurationKey(
155 "Use 'worker' section to define the worker's element start delay instead"
157 Configuration
.warnDeprecatedConfigurationKey(
160 "Use 'worker' section to define the worker pool minimum size instead"
162 Configuration
.warnDeprecatedConfigurationKey(
165 "Use 'worker' section to define the worker pool maximum size instead"
167 Configuration
.warnDeprecatedConfigurationKey(
168 'workerPoolMaxSize;',
170 "Use 'worker' section to define the worker pool maximum size instead"
172 Configuration
.warnDeprecatedConfigurationKey(
173 'workerPoolStrategy;',
175 "Use 'worker' section to define the worker pool strategy instead"
177 let workerConfiguration
: WorkerConfiguration
= {
178 processType
: Configuration
.objectHasOwnProperty(Configuration
.getConfig(), 'workerProcess')
179 ? Configuration
.getConfig().workerProcess
180 : WorkerProcessType
.WORKER_SET
,
181 startDelay
: Configuration
.objectHasOwnProperty(Configuration
.getConfig(), 'workerStartDelay')
182 ? Configuration
.getConfig().workerStartDelay
183 : WorkerConstants
.DEFAULT_WORKER_START_DELAY
,
184 elementsPerWorker
: Configuration
.objectHasOwnProperty(
185 Configuration
.getConfig(),
186 'chargingStationsPerWorker'
188 ? Configuration
.getConfig().chargingStationsPerWorker
189 : WorkerConstants
.DEFAULT_ELEMENTS_PER_WORKER
,
190 elementStartDelay
: Configuration
.objectHasOwnProperty(
191 Configuration
.getConfig(),
194 ? Configuration
.getConfig().elementStartDelay
195 : WorkerConstants
.DEFAULT_ELEMENT_START_DELAY
,
196 poolMinSize
: Configuration
.objectHasOwnProperty(
197 Configuration
.getConfig(),
200 ? Configuration
.getConfig().workerPoolMinSize
201 : WorkerConstants
.DEFAULT_POOL_MIN_SIZE
,
202 poolMaxSize
: Configuration
.objectHasOwnProperty(
203 Configuration
.getConfig(),
206 ? Configuration
.getConfig().workerPoolMaxSize
207 : WorkerConstants
.DEFAULT_POOL_MAX_SIZE
,
208 poolStrategy
: Configuration
.getConfig().workerPoolStrategy
,
210 if (Configuration
.objectHasOwnProperty(Configuration
.getConfig(), 'worker')) {
211 workerConfiguration
= { ...workerConfiguration
, ...Configuration
.getConfig().worker
};
213 return workerConfiguration
;
216 static getLogConsole(): boolean {
217 Configuration
.warnDeprecatedConfigurationKey('consoleLog', null, "Use 'logConsole' instead");
218 return Configuration
.objectHasOwnProperty(Configuration
.getConfig(), 'logConsole')
219 ? Configuration
.getConfig().logConsole
223 static getLogFormat(): string {
224 return Configuration
.objectHasOwnProperty(Configuration
.getConfig(), 'logFormat')
225 ? Configuration
.getConfig().logFormat
229 static getLogRotate(): boolean {
230 return Configuration
.objectHasOwnProperty(Configuration
.getConfig(), 'logRotate')
231 ? Configuration
.getConfig().logRotate
235 static getLogMaxFiles(): number {
236 return Configuration
.objectHasOwnProperty(Configuration
.getConfig(), 'logMaxFiles')
237 ? Configuration
.getConfig().logMaxFiles
241 static getLogLevel(): string {
242 return Configuration
.objectHasOwnProperty(Configuration
.getConfig(), 'logLevel')
243 ? Configuration
.getConfig().logLevel
.toLowerCase()
247 static getLogFile(): string {
248 return Configuration
.objectHasOwnProperty(Configuration
.getConfig(), 'logFile')
249 ? Configuration
.getConfig().logFile
253 static getLogErrorFile(): string {
254 Configuration
.warnDeprecatedConfigurationKey('errorFile', null, "Use 'logErrorFile' instead");
255 return Configuration
.objectHasOwnProperty(Configuration
.getConfig(), 'logErrorFile')
256 ? Configuration
.getConfig().logErrorFile
260 static getSupervisionUrls(): string | string[] {
261 Configuration
.warnDeprecatedConfigurationKey(
264 "Use 'supervisionUrls' instead"
266 !Configuration
.isUndefined(Configuration
.getConfig()['supervisionURLs']) &&
267 (Configuration
.getConfig().supervisionUrls
= Configuration
.getConfig()[
271 return Configuration
.getConfig().supervisionUrls
;
274 static getSupervisionUrlDistribution(): SupervisionUrlDistribution
{
275 Configuration
.warnDeprecatedConfigurationKey(
276 'distributeStationToTenantEqually',
278 "Use 'supervisionUrlDistribution' instead"
280 Configuration
.warnDeprecatedConfigurationKey(
281 'distributeStationsToTenantsEqually',
283 "Use 'supervisionUrlDistribution' instead"
285 return Configuration
.objectHasOwnProperty(
286 Configuration
.getConfig(),
287 'supervisionUrlDistribution'
289 ? Configuration
.getConfig().supervisionUrlDistribution
290 : SupervisionUrlDistribution
.ROUND_ROBIN
;
293 private static logPrefix(): string {
294 return new Date().toLocaleString() + ' Simulator configuration |';
297 private static warnDeprecatedConfigurationKey(
299 sectionName
?: string,
304 !Configuration
.isUndefined(Configuration
.getConfig()[sectionName
]) &&
305 !Configuration
.isUndefined(
306 (Configuration
.getConfig()[sectionName
] as Record
<string, unknown
>)[key
]
310 chalk
`{green ${Configuration.logPrefix()}} {red Deprecated configuration key '${key}' usage in section '${sectionName}'${
311 logMsgToAppend && '. ' + logMsgToAppend
314 } else if (!Configuration
.isUndefined(Configuration
.getConfig()[key
])) {
316 chalk
`{green ${Configuration.logPrefix()}} {red Deprecated configuration key '${key}' usage${
317 logMsgToAppend && '. ' + logMsgToAppend
323 // Read the config file
324 private static getConfig(): ConfigurationData
{
325 if (!Configuration
.configuration
) {
327 Configuration
.configuration
= JSON
.parse(
328 fs
.readFileSync(Configuration
.configurationFile
, 'utf8')
329 ) as ConfigurationData
;
331 Configuration
.handleFileException(
332 Configuration
.logPrefix(),
333 FileType
.Configuration
,
334 Configuration
.configurationFile
,
335 error
as NodeJS
.ErrnoException
338 if (!Configuration
.configurationFileWatcher
) {
339 Configuration
.configurationFileWatcher
= Configuration
.getConfigurationFileWatcher();
342 return Configuration
.configuration
;
345 private static getConfigurationFileWatcher(): fs
.FSWatcher
{
347 return fs
.watch(Configuration
.configurationFile
, (event
, filename
): void => {
348 if (filename
&& event
=== 'change') {
349 // Nullify to force configuration file reading
350 Configuration
.configuration
= null;
351 if (!Configuration
.isUndefined(Configuration
.configurationChangeCallback
)) {
352 Configuration
.configurationChangeCallback().catch((error
) => {
353 throw typeof error
=== 'string' ? new Error(error
) : error
;
359 Configuration
.handleFileException(
360 Configuration
.logPrefix(),
361 FileType
.Configuration
,
362 Configuration
.configurationFile
,
363 error
as NodeJS
.ErrnoException
368 private static getDefaultPerformanceStorageUri(storageType
: StorageType
) {
369 const SQLiteFileName
= `${Constants.DEFAULT_PERFORMANCE_RECORDS_DB_NAME}.db`;
370 switch (storageType
) {
371 case StorageType
.JSON_FILE
:
372 return `file://${path.join(
373 path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../'),
374 Constants.DEFAULT_PERFORMANCE_RECORDS_FILENAME
376 case StorageType
.SQLITE
:
377 return `file://${path.join(
378 path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../'),
382 throw new Error(`Performance storage URI is mandatory with storage type '${storageType}'`);
386 private static objectHasOwnProperty(object
: unknown
, property
: string): boolean {
387 return Object.prototype
.hasOwnProperty
.call(object
, property
) as boolean;
390 private static isUndefined(obj
: unknown
): boolean {
391 return typeof obj
=== 'undefined';
394 private static handleFileException(
398 error
: NodeJS
.ErrnoException
,
399 params
: HandleErrorParams
<EmptyObject
> = { throwError
: true }
401 const prefix
= logPrefix
.length
!== 0 ? logPrefix
+ ' ' : '';
402 if (error
.code
=== 'ENOENT') {
404 chalk
.green(prefix
) + chalk
.red(fileType
+ ' file ' + filePath
+ ' not found: '),
407 } else if (error
.code
=== 'EEXIST') {
409 chalk
.green(prefix
) + chalk
.red(fileType
+ ' file ' + filePath
+ ' already exists: '),
412 } else if (error
.code
=== 'EACCES') {
414 chalk
.green(prefix
) + chalk
.red(fileType
+ ' file ' + filePath
+ ' access denied: '),
419 chalk
.green(prefix
) + chalk
.red(fileType
+ ' file ' + filePath
+ ' error: '),
423 if (params
?.throwError
) {