1 import ConfigurationData
, {
5 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 type { WorkerChoiceStrategy
} from
'poolifier';
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 getWorkerProcess(): WorkerProcessType
{
165 Configuration
.warnDeprecatedConfigurationKey(
168 "Use 'workerProcess' to define the type of worker process model to use instead"
170 return Configuration
.objectHasOwnProperty(Configuration
.getConfig(), 'workerProcess')
171 ? Configuration
.getConfig().workerProcess
172 : WorkerProcessType
.WORKER_SET
;
175 static getWorkerStartDelay(): number {
176 return Configuration
.objectHasOwnProperty(Configuration
.getConfig(), 'workerStartDelay')
177 ? Configuration
.getConfig().workerStartDelay
178 : WorkerConstants
.DEFAULT_WORKER_START_DELAY
;
181 static getElementStartDelay(): number {
182 return Configuration
.objectHasOwnProperty(Configuration
.getConfig(), 'elementStartDelay')
183 ? Configuration
.getConfig().elementStartDelay
184 : WorkerConstants
.DEFAULT_ELEMENT_START_DELAY
;
187 static getWorkerPoolMinSize(): number {
188 return Configuration
.objectHasOwnProperty(Configuration
.getConfig(), 'workerPoolMinSize')
189 ? Configuration
.getConfig().workerPoolMinSize
190 : WorkerConstants
.DEFAULT_POOL_MIN_SIZE
;
193 static getWorkerPoolMaxSize(): number {
194 Configuration
.warnDeprecatedConfigurationKey(
197 "Use 'workerPoolMaxSize' instead"
199 return Configuration
.objectHasOwnProperty(Configuration
.getConfig(), 'workerPoolMaxSize')
200 ? Configuration
.getConfig().workerPoolMaxSize
201 : WorkerConstants
.DEFAULT_POOL_MAX_SIZE
;
204 static getWorkerPoolStrategy(): WorkerChoiceStrategy
{
205 return Configuration
.getConfig().workerPoolStrategy
;
208 static getChargingStationsPerWorker(): number {
209 return Configuration
.objectHasOwnProperty(
210 Configuration
.getConfig(),
211 'chargingStationsPerWorker'
213 ? Configuration
.getConfig().chargingStationsPerWorker
214 : WorkerConstants
.DEFAULT_ELEMENTS_PER_WORKER
;
217 static getLogConsole(): boolean {
218 Configuration
.warnDeprecatedConfigurationKey('consoleLog', null, "Use 'logConsole' instead");
219 return Configuration
.objectHasOwnProperty(Configuration
.getConfig(), 'logConsole')
220 ? Configuration
.getConfig().logConsole
224 static getLogFormat(): string {
225 return Configuration
.objectHasOwnProperty(Configuration
.getConfig(), 'logFormat')
226 ? Configuration
.getConfig().logFormat
230 static getLogRotate(): boolean {
231 return Configuration
.objectHasOwnProperty(Configuration
.getConfig(), 'logRotate')
232 ? Configuration
.getConfig().logRotate
236 static getLogMaxFiles(): number {
237 return Configuration
.objectHasOwnProperty(Configuration
.getConfig(), 'logMaxFiles')
238 ? Configuration
.getConfig().logMaxFiles
242 static getLogLevel(): string {
243 return Configuration
.objectHasOwnProperty(Configuration
.getConfig(), 'logLevel')
244 ? Configuration
.getConfig().logLevel
.toLowerCase()
248 static getLogFile(): string {
249 return Configuration
.objectHasOwnProperty(Configuration
.getConfig(), 'logFile')
250 ? Configuration
.getConfig().logFile
254 static getLogErrorFile(): string {
255 Configuration
.warnDeprecatedConfigurationKey('errorFile', null, "Use 'logErrorFile' instead");
256 return Configuration
.objectHasOwnProperty(Configuration
.getConfig(), 'logErrorFile')
257 ? Configuration
.getConfig().logErrorFile
261 static getSupervisionUrls(): string | string[] {
262 Configuration
.warnDeprecatedConfigurationKey(
265 "Use 'supervisionUrls' instead"
267 !Configuration
.isUndefined(Configuration
.getConfig()['supervisionURLs']) &&
268 (Configuration
.getConfig().supervisionUrls
= Configuration
.getConfig()[
272 return Configuration
.getConfig().supervisionUrls
;
275 static getSupervisionUrlDistribution(): SupervisionUrlDistribution
{
276 Configuration
.warnDeprecatedConfigurationKey(
277 'distributeStationToTenantEqually',
279 "Use 'supervisionUrlDistribution' instead"
281 Configuration
.warnDeprecatedConfigurationKey(
282 'distributeStationsToTenantsEqually',
284 "Use 'supervisionUrlDistribution' instead"
286 return Configuration
.objectHasOwnProperty(
287 Configuration
.getConfig(),
288 'supervisionUrlDistribution'
290 ? Configuration
.getConfig().supervisionUrlDistribution
291 : SupervisionUrlDistribution
.ROUND_ROBIN
;
294 private static logPrefix(): string {
295 return new Date().toLocaleString() + ' Simulator configuration |';
298 private static warnDeprecatedConfigurationKey(
300 sectionName
?: string,
305 !Configuration
.isUndefined(Configuration
.getConfig()[sectionName
]) &&
306 !Configuration
.isUndefined(
307 (Configuration
.getConfig()[sectionName
] as Record
<string, unknown
>)[key
]
311 chalk
`{green ${Configuration.logPrefix()}} {red Deprecated configuration key '${key}' usage in section '${sectionName}'${
312 logMsgToAppend && '. ' + logMsgToAppend
315 } else if (!Configuration
.isUndefined(Configuration
.getConfig()[key
])) {
317 chalk
`{green ${Configuration.logPrefix()}} {red Deprecated configuration key '${key}' usage${
318 logMsgToAppend && '. ' + logMsgToAppend
324 // Read the config file
325 private static getConfig(): ConfigurationData
{
326 if (!Configuration
.configuration
) {
328 Configuration
.configuration
= JSON
.parse(
329 fs
.readFileSync(Configuration
.configurationFile
, 'utf8')
330 ) as ConfigurationData
;
332 Configuration
.handleFileException(
333 Configuration
.logPrefix(),
334 FileType
.Configuration
,
335 Configuration
.configurationFile
,
336 error
as NodeJS
.ErrnoException
339 if (!Configuration
.configurationFileWatcher
) {
340 Configuration
.configurationFileWatcher
= Configuration
.getConfigurationFileWatcher();
343 return Configuration
.configuration
;
346 private static getConfigurationFileWatcher(): fs
.FSWatcher
{
348 return fs
.watch(Configuration
.configurationFile
, (event
, filename
): void => {
349 if (filename
&& event
=== 'change') {
350 // Nullify to force configuration file reading
351 Configuration
.configuration
= null;
352 if (!Configuration
.isUndefined(Configuration
.configurationChangeCallback
)) {
353 Configuration
.configurationChangeCallback().catch((error
) => {
354 throw typeof error
=== 'string' ? new Error(error
) : error
;
360 Configuration
.handleFileException(
361 Configuration
.logPrefix(),
362 FileType
.Configuration
,
363 Configuration
.configurationFile
,
364 error
as NodeJS
.ErrnoException
369 private static getDefaultPerformanceStorageUri(storageType
: StorageType
) {
370 const SQLiteFileName
= `${Constants.DEFAULT_PERFORMANCE_RECORDS_DB_NAME}.db`;
371 switch (storageType
) {
372 case StorageType
.JSON_FILE
:
373 return `file://${path.join(
374 path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../'),
375 Constants.DEFAULT_PERFORMANCE_RECORDS_FILENAME
377 case StorageType
.SQLITE
:
378 return `file://${path.join(
379 path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../'),
383 throw new Error(`Performance storage URI is mandatory with storage type '${storageType}'`);
387 private static objectHasOwnProperty(object
: unknown
, property
: string): boolean {
388 return Object.prototype
.hasOwnProperty
.call(object
, property
) as boolean;
391 private static isUndefined(obj
: unknown
): boolean {
392 return typeof obj
=== 'undefined';
395 private static handleFileException(
399 error
: NodeJS
.ErrnoException
,
400 params
: HandleErrorParams
<EmptyObject
> = { throwError
: true }
402 const prefix
= logPrefix
.length
!== 0 ? logPrefix
+ ' ' : '';
403 if (error
.code
=== 'ENOENT') {
405 chalk
.green(prefix
) + chalk
.red(fileType
+ ' file ' + filePath
+ ' not found: '),
408 } else if (error
.code
=== 'EEXIST') {
410 chalk
.green(prefix
) + chalk
.red(fileType
+ ' file ' + filePath
+ ' already exists: '),
413 } else if (error
.code
=== 'EACCES') {
415 chalk
.green(prefix
) + chalk
.red(fileType
+ ' file ' + filePath
+ ' access denied: '),
420 chalk
.green(prefix
) + chalk
.red(fileType
+ ' file ' + filePath
+ ' error: '),
424 if (params
?.throwError
) {