1 import fs from
'node:fs';
2 import path from
'node:path';
3 import { fileURLToPath
} from
'node:url';
5 import chalk from
'chalk';
6 import merge from
'just-merge';
7 import { WorkerChoiceStrategies
} from
'poolifier';
9 // import { Constants, FileUtils, Utils } from './internal';
10 import { Constants
} from
'./Constants';
11 import { FileUtils
} from
'./FileUtils';
12 import { Utils
} from
'./Utils';
15 type ConfigurationData
,
17 type StationTemplateUrl
,
18 type StorageConfiguration
,
20 SupervisionUrlDistribution
,
21 type UIServerConfiguration
,
22 type WorkerConfiguration
,
24 import { WorkerConstants
, WorkerProcessType
} from
'../worker';
26 export class Configuration
{
27 private static configurationFile
= path
.join(
28 path
.resolve(path
.dirname(fileURLToPath(import.meta
.url
)), '../'),
33 private static configurationFileWatcher
: fs
.FSWatcher
| undefined;
34 private static configuration
: ConfigurationData
| null = null;
35 private static configurationChangeCallback
: () => Promise
<void>;
37 private constructor() {
38 // This is intentional
41 static setConfigurationChangeCallback(cb
: () => Promise
<void>): void {
42 Configuration
.configurationChangeCallback
= cb
;
45 static getLogStatisticsInterval(): number | undefined {
46 Configuration
.warnDeprecatedConfigurationKey(
47 'statisticsDisplayInterval',
49 "Use 'logStatisticsInterval' instead"
52 return Utils
.hasOwnProp(Configuration
.getConfig(), 'logStatisticsInterval')
53 ? Configuration
.getConfig()?.logStatisticsInterval
54 : Constants
.DEFAULT_LOG_STATISTICS_INTERVAL
;
57 static getUIServer(): UIServerConfiguration
{
58 if (Utils
.hasOwnProp(Configuration
.getConfig(), 'uiWebSocketServer')) {
60 chalk
`{green ${Configuration.logPrefix()}} {red Deprecated configuration section 'uiWebSocketServer' usage. Use 'uiServer' instead}`
63 let uiServerConfiguration
: UIServerConfiguration
= {
65 type: ApplicationProtocol
.WS
,
67 host
: Constants
.DEFAULT_UI_SERVER_HOST
,
68 port
: Constants
.DEFAULT_UI_SERVER_PORT
,
71 if (Utils
.hasOwnProp(Configuration
.getConfig(), 'uiServer')) {
72 uiServerConfiguration
= merge
<UIServerConfiguration
>(
73 uiServerConfiguration
,
74 Configuration
.getConfig()?.uiServer
77 if (Utils
.isCFEnvironment() === true) {
78 delete uiServerConfiguration
.options
?.host
;
79 uiServerConfiguration
.options
.port
= parseInt(process
.env
.PORT
);
81 return uiServerConfiguration
;
84 static getPerformanceStorage(): StorageConfiguration
{
85 Configuration
.warnDeprecatedConfigurationKey('URI', 'performanceStorage', "Use 'uri' instead");
86 let storageConfiguration
: StorageConfiguration
= {
88 type: StorageType
.JSON_FILE
,
89 uri
: this.getDefaultPerformanceStorageUri(StorageType
.JSON_FILE
),
91 if (Utils
.hasOwnProp(Configuration
.getConfig(), 'performanceStorage')) {
92 storageConfiguration
= {
93 ...storageConfiguration
,
94 ...Configuration
.getConfig()?.performanceStorage
,
97 return storageConfiguration
;
100 static getAutoReconnectMaxRetries(): number | undefined {
101 Configuration
.warnDeprecatedConfigurationKey(
102 'autoReconnectTimeout',
104 "Use 'ConnectionTimeOut' OCPP parameter in charging station template instead"
106 Configuration
.warnDeprecatedConfigurationKey(
109 "Use 'ConnectionTimeOut' OCPP parameter in charging station template instead"
111 Configuration
.warnDeprecatedConfigurationKey(
112 'autoReconnectMaxRetries',
114 'Use it in charging station template instead'
117 if (Utils
.hasOwnProp(Configuration
.getConfig(), 'autoReconnectMaxRetries')) {
118 return Configuration
.getConfig()?.autoReconnectMaxRetries
;
122 static getStationTemplateUrls(): StationTemplateUrl
[] | undefined {
123 Configuration
.warnDeprecatedConfigurationKey(
124 'stationTemplateURLs',
126 "Use 'stationTemplateUrls' instead"
128 !Utils
.isUndefined(Configuration
.getConfig()['stationTemplateURLs']) &&
129 (Configuration
.getConfig().stationTemplateUrls
= Configuration
.getConfig()[
130 'stationTemplateURLs'
131 ] as StationTemplateUrl
[]);
132 Configuration
.getConfig().stationTemplateUrls
.forEach((stationUrl
: StationTemplateUrl
) => {
133 if (!Utils
.isUndefined(stationUrl
['numberOfStation'])) {
135 chalk
`{green ${Configuration.logPrefix()}} {red Deprecated configuration key 'numberOfStation' usage for template file '${
137 }' in 'stationTemplateUrls'. Use 'numberOfStations' instead}`
142 return Configuration
.getConfig()?.stationTemplateUrls
;
145 static getWorker(): WorkerConfiguration
{
146 Configuration
.warnDeprecatedConfigurationKey(
149 "Use 'worker' section to define the type of worker process model instead"
151 Configuration
.warnDeprecatedConfigurationKey(
154 "Use 'worker' section to define the type of worker process model instead"
156 Configuration
.warnDeprecatedConfigurationKey(
159 "Use 'worker' section to define the worker start delay instead"
161 Configuration
.warnDeprecatedConfigurationKey(
162 'chargingStationsPerWorker',
164 "Use 'worker' section to define the number of element(s) per worker instead"
166 Configuration
.warnDeprecatedConfigurationKey(
169 "Use 'worker' section to define the worker's element start delay instead"
171 Configuration
.warnDeprecatedConfigurationKey(
174 "Use 'worker' section to define the worker pool minimum size instead"
176 Configuration
.warnDeprecatedConfigurationKey(
179 "Use 'worker' section to define the worker pool maximum size instead"
181 Configuration
.warnDeprecatedConfigurationKey(
182 'workerPoolMaxSize;',
184 "Use 'worker' section to define the worker pool maximum size instead"
186 Configuration
.warnDeprecatedConfigurationKey(
187 'workerPoolStrategy;',
189 "Use 'worker' section to define the worker pool strategy instead"
191 let workerConfiguration
: WorkerConfiguration
= {
192 processType
: Utils
.hasOwnProp(Configuration
.getConfig(), 'workerProcess')
193 ? Configuration
.getConfig()?.workerProcess
194 : WorkerProcessType
.workerSet
,
195 startDelay
: Utils
.hasOwnProp(Configuration
.getConfig(), 'workerStartDelay')
196 ? Configuration
.getConfig()?.workerStartDelay
197 : WorkerConstants
.DEFAULT_WORKER_START_DELAY
,
198 elementsPerWorker
: Utils
.hasOwnProp(Configuration
.getConfig(), 'chargingStationsPerWorker')
199 ? Configuration
.getConfig()?.chargingStationsPerWorker
200 : WorkerConstants
.DEFAULT_ELEMENTS_PER_WORKER
,
201 elementStartDelay
: Utils
.hasOwnProp(Configuration
.getConfig(), 'elementStartDelay')
202 ? Configuration
.getConfig()?.elementStartDelay
203 : WorkerConstants
.DEFAULT_ELEMENT_START_DELAY
,
204 poolMinSize
: Utils
.hasOwnProp(Configuration
.getConfig(), 'workerPoolMinSize')
205 ? Configuration
.getConfig()?.workerPoolMinSize
206 : WorkerConstants
.DEFAULT_POOL_MIN_SIZE
,
207 poolMaxSize
: Utils
.hasOwnProp(Configuration
.getConfig(), 'workerPoolMaxSize')
208 ? Configuration
.getConfig()?.workerPoolMaxSize
209 : WorkerConstants
.DEFAULT_POOL_MAX_SIZE
,
211 Configuration
.getConfig()?.workerPoolStrategy
?? WorkerChoiceStrategies
.ROUND_ROBIN
,
213 if (Utils
.hasOwnProp(Configuration
.getConfig(), 'worker')) {
214 workerConfiguration
= { ...workerConfiguration
, ...Configuration
.getConfig()?.worker
};
216 return workerConfiguration
;
219 static getLogConsole(): boolean | undefined {
220 Configuration
.warnDeprecatedConfigurationKey(
223 "Use 'logConsole' instead"
225 return Utils
.hasOwnProp(Configuration
.getConfig(), 'logConsole')
226 ? Configuration
.getConfig()?.logConsole
230 static getLogFormat(): string | undefined {
231 return Utils
.hasOwnProp(Configuration
.getConfig(), 'logFormat')
232 ? Configuration
.getConfig()?.logFormat
236 static getLogRotate(): boolean | undefined {
237 return Utils
.hasOwnProp(Configuration
.getConfig(), 'logRotate')
238 ? Configuration
.getConfig()?.logRotate
242 static getLogMaxFiles(): number | string | false | undefined {
244 Utils
.hasOwnProp(Configuration
.getConfig(), 'logMaxFiles') &&
245 Configuration
.getConfig()?.logMaxFiles
249 static getLogMaxSize(): number | string | false | undefined {
251 Utils
.hasOwnProp(Configuration
.getConfig(), 'logMaxFiles') &&
252 Configuration
.getConfig()?.logMaxSize
256 static getLogLevel(): string | undefined {
257 return Utils
.hasOwnProp(Configuration
.getConfig(), 'logLevel')
258 ? Configuration
.getConfig()?.logLevel
?.toLowerCase()
262 static getLogFile(): string | undefined {
263 return Utils
.hasOwnProp(Configuration
.getConfig(), 'logFile')
264 ? Configuration
.getConfig()?.logFile
268 static getLogErrorFile(): string | undefined {
269 Configuration
.warnDeprecatedConfigurationKey(
272 "Use 'logErrorFile' instead"
274 return Utils
.hasOwnProp(Configuration
.getConfig(), 'logErrorFile')
275 ? Configuration
.getConfig()?.logErrorFile
279 static getSupervisionUrls(): string | string[] | undefined {
280 Configuration
.warnDeprecatedConfigurationKey(
283 "Use 'supervisionUrls' instead"
285 !Utils
.isUndefined(Configuration
.getConfig()['supervisionURLs']) &&
286 (Configuration
.getConfig().supervisionUrls
= Configuration
.getConfig()['supervisionURLs'] as
290 return Configuration
.getConfig()?.supervisionUrls
;
293 static getSupervisionUrlDistribution(): SupervisionUrlDistribution
| undefined {
294 Configuration
.warnDeprecatedConfigurationKey(
295 'distributeStationToTenantEqually',
297 "Use 'supervisionUrlDistribution' instead"
299 Configuration
.warnDeprecatedConfigurationKey(
300 'distributeStationsToTenantsEqually',
302 "Use 'supervisionUrlDistribution' instead"
304 return Utils
.hasOwnProp(Configuration
.getConfig(), 'supervisionUrlDistribution')
305 ? Configuration
.getConfig()?.supervisionUrlDistribution
306 : SupervisionUrlDistribution
.ROUND_ROBIN
;
309 private static logPrefix
= (): string => {
310 return `${new Date().toLocaleString()} Simulator configuration |`;
313 private static warnDeprecatedConfigurationKey(
315 sectionName
?: string,
320 !Utils
.isUndefined(Configuration
.getConfig()[sectionName
]) &&
321 !Utils
.isUndefined((Configuration
.getConfig()[sectionName
] as Record
<string, unknown
>)[key
])
324 chalk
`{green ${Configuration.logPrefix()}} {red Deprecated configuration key '${key}' usage in section '${sectionName}'${
325 logMsgToAppend.trim().length > 0 && `. ${logMsgToAppend}
`
328 } else if (!Utils
.isUndefined(Configuration
.getConfig()[key
])) {
330 chalk
`{green ${Configuration.logPrefix()}} {red Deprecated configuration key '${key}' usage${
331 logMsgToAppend.trim().length > 0 && `. ${logMsgToAppend}
`
337 // Read the config file
338 private static getConfig(): ConfigurationData
| null {
339 if (!Configuration
.configuration
) {
341 Configuration
.configuration
= JSON
.parse(
342 fs
.readFileSync(Configuration
.configurationFile
, 'utf8')
343 ) as ConfigurationData
;
345 FileUtils
.handleFileException(
346 Configuration
.configurationFile
,
347 FileType
.Configuration
,
348 error
as NodeJS
.ErrnoException
,
349 Configuration
.logPrefix(),
353 if (!Configuration
.configurationFileWatcher
) {
354 Configuration
.configurationFileWatcher
= Configuration
.getConfigurationFileWatcher();
357 return Configuration
.configuration
;
360 private static getConfigurationFileWatcher(): fs
.FSWatcher
| undefined {
362 return fs
.watch(Configuration
.configurationFile
, (event
, filename
): void => {
363 if (filename
?.trim().length
> 0 && event
=== 'change') {
364 // Nullify to force configuration file reading
365 Configuration
.configuration
= null;
366 if (!Utils
.isUndefined(Configuration
.configurationChangeCallback
)) {
367 Configuration
.configurationChangeCallback().catch((error
) => {
368 throw typeof error
=== 'string' ? new Error(error
) : error
;
374 FileUtils
.handleFileException(
375 Configuration
.configurationFile
,
376 FileType
.Configuration
,
377 error
as NodeJS
.ErrnoException
,
378 Configuration
.logPrefix(),
384 private static getDefaultPerformanceStorageUri(storageType
: StorageType
) {
385 switch (storageType
) {
386 case StorageType
.JSON_FILE
:
387 return `file://${path.join(
388 path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../'),
389 Constants.DEFAULT_PERFORMANCE_RECORDS_FILENAME
391 case StorageType
.SQLITE
:
392 return `file://${path.join(
393 path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../'),
394 `${Constants.DEFAULT_PERFORMANCE_RECORDS_DB_NAME}
.db
`
397 throw new Error(`Performance storage URI is mandatory with storage type '${storageType}'`);