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(
133 (stationTemplateUrl
: StationTemplateUrl
) => {
134 if (!Utils
.isUndefined(stationTemplateUrl
['numberOfStation'])) {
136 chalk
`{green ${Configuration.logPrefix()}} {red Deprecated configuration key 'numberOfStation' usage for template file '${
137 stationTemplateUrl.file
138 }' in 'stationTemplateUrls'. Use 'numberOfStations' instead}`
144 return Configuration
.getConfig()?.stationTemplateUrls
;
147 static getWorker(): WorkerConfiguration
{
148 Configuration
.warnDeprecatedConfigurationKey(
151 "Use 'worker' section to define the type of worker process model instead"
153 Configuration
.warnDeprecatedConfigurationKey(
156 "Use 'worker' section to define the type of worker process model instead"
158 Configuration
.warnDeprecatedConfigurationKey(
161 "Use 'worker' section to define the worker start delay instead"
163 Configuration
.warnDeprecatedConfigurationKey(
164 'chargingStationsPerWorker',
166 "Use 'worker' section to define the number of element(s) per worker instead"
168 Configuration
.warnDeprecatedConfigurationKey(
171 "Use 'worker' section to define the worker's element start delay instead"
173 Configuration
.warnDeprecatedConfigurationKey(
176 "Use 'worker' section to define the worker pool minimum size instead"
178 Configuration
.warnDeprecatedConfigurationKey(
181 "Use 'worker' section to define the worker pool maximum size instead"
183 Configuration
.warnDeprecatedConfigurationKey(
184 'workerPoolMaxSize;',
186 "Use 'worker' section to define the worker pool maximum size instead"
188 Configuration
.warnDeprecatedConfigurationKey(
189 'workerPoolStrategy;',
191 "Use 'worker' section to define the worker pool strategy instead"
193 let workerConfiguration
: WorkerConfiguration
= {
194 processType
: Utils
.hasOwnProp(Configuration
.getConfig(), 'workerProcess')
195 ? Configuration
.getConfig()?.workerProcess
196 : WorkerProcessType
.workerSet
,
197 startDelay
: Utils
.hasOwnProp(Configuration
.getConfig(), 'workerStartDelay')
198 ? Configuration
.getConfig()?.workerStartDelay
199 : WorkerConstants
.DEFAULT_WORKER_START_DELAY
,
200 elementsPerWorker
: Utils
.hasOwnProp(Configuration
.getConfig(), 'chargingStationsPerWorker')
201 ? Configuration
.getConfig()?.chargingStationsPerWorker
202 : WorkerConstants
.DEFAULT_ELEMENTS_PER_WORKER
,
203 elementStartDelay
: Utils
.hasOwnProp(Configuration
.getConfig(), 'elementStartDelay')
204 ? Configuration
.getConfig()?.elementStartDelay
205 : WorkerConstants
.DEFAULT_ELEMENT_START_DELAY
,
206 poolMinSize
: Utils
.hasOwnProp(Configuration
.getConfig(), 'workerPoolMinSize')
207 ? Configuration
.getConfig()?.workerPoolMinSize
208 : WorkerConstants
.DEFAULT_POOL_MIN_SIZE
,
209 poolMaxSize
: Utils
.hasOwnProp(Configuration
.getConfig(), 'workerPoolMaxSize')
210 ? Configuration
.getConfig()?.workerPoolMaxSize
211 : WorkerConstants
.DEFAULT_POOL_MAX_SIZE
,
213 Configuration
.getConfig()?.workerPoolStrategy
?? WorkerChoiceStrategies
.ROUND_ROBIN
,
215 if (Utils
.hasOwnProp(Configuration
.getConfig(), 'worker')) {
216 workerConfiguration
= { ...workerConfiguration
, ...Configuration
.getConfig()?.worker
};
218 return workerConfiguration
;
221 static getLogConsole(): boolean | undefined {
222 Configuration
.warnDeprecatedConfigurationKey(
225 "Use 'logConsole' instead"
227 return Utils
.hasOwnProp(Configuration
.getConfig(), 'logConsole')
228 ? Configuration
.getConfig()?.logConsole
232 static getLogFormat(): string | undefined {
233 return Utils
.hasOwnProp(Configuration
.getConfig(), 'logFormat')
234 ? Configuration
.getConfig()?.logFormat
238 static getLogRotate(): boolean | undefined {
239 return Utils
.hasOwnProp(Configuration
.getConfig(), 'logRotate')
240 ? Configuration
.getConfig()?.logRotate
244 static getLogMaxFiles(): number | string | false | undefined {
246 Utils
.hasOwnProp(Configuration
.getConfig(), 'logMaxFiles') &&
247 Configuration
.getConfig()?.logMaxFiles
251 static getLogMaxSize(): number | string | false | undefined {
253 Utils
.hasOwnProp(Configuration
.getConfig(), 'logMaxFiles') &&
254 Configuration
.getConfig()?.logMaxSize
258 static getLogLevel(): string | undefined {
259 return Utils
.hasOwnProp(Configuration
.getConfig(), 'logLevel')
260 ? Configuration
.getConfig()?.logLevel
?.toLowerCase()
264 static getLogFile(): string | undefined {
265 return Utils
.hasOwnProp(Configuration
.getConfig(), 'logFile')
266 ? Configuration
.getConfig()?.logFile
270 static getLogErrorFile(): string | undefined {
271 Configuration
.warnDeprecatedConfigurationKey(
274 "Use 'logErrorFile' instead"
276 return Utils
.hasOwnProp(Configuration
.getConfig(), 'logErrorFile')
277 ? Configuration
.getConfig()?.logErrorFile
281 static getSupervisionUrls(): string | string[] | undefined {
282 Configuration
.warnDeprecatedConfigurationKey(
285 "Use 'supervisionUrls' instead"
287 !Utils
.isUndefined(Configuration
.getConfig()['supervisionURLs']) &&
288 (Configuration
.getConfig().supervisionUrls
= Configuration
.getConfig()['supervisionURLs'] as
292 return Configuration
.getConfig()?.supervisionUrls
;
295 static getSupervisionUrlDistribution(): SupervisionUrlDistribution
| undefined {
296 Configuration
.warnDeprecatedConfigurationKey(
297 'distributeStationToTenantEqually',
299 "Use 'supervisionUrlDistribution' instead"
301 Configuration
.warnDeprecatedConfigurationKey(
302 'distributeStationsToTenantsEqually',
304 "Use 'supervisionUrlDistribution' instead"
306 return Utils
.hasOwnProp(Configuration
.getConfig(), 'supervisionUrlDistribution')
307 ? Configuration
.getConfig()?.supervisionUrlDistribution
308 : SupervisionUrlDistribution
.ROUND_ROBIN
;
311 private static logPrefix
= (): string => {
312 return `${new Date().toLocaleString()} Simulator configuration |`;
315 private static warnDeprecatedConfigurationKey(
317 sectionName
?: string,
322 !Utils
.isUndefined(Configuration
.getConfig()[sectionName
]) &&
323 !Utils
.isUndefined((Configuration
.getConfig()[sectionName
] as Record
<string, unknown
>)[key
])
326 chalk
`{green ${Configuration.logPrefix()}} {red Deprecated configuration key '${key}' usage in section '${sectionName}'${
327 logMsgToAppend.trim().length > 0 ? `. ${logMsgToAppend}
` : ''
330 } else if (!Utils
.isUndefined(Configuration
.getConfig()[key
])) {
332 chalk
`{green ${Configuration.logPrefix()}} {red Deprecated configuration key '${key}' usage${
333 logMsgToAppend.trim().length > 0 ? `. ${logMsgToAppend}
` : ''
339 // Read the config file
340 private static getConfig(): ConfigurationData
| null {
341 if (!Configuration
.configuration
) {
343 Configuration
.configuration
= JSON
.parse(
344 fs
.readFileSync(Configuration
.configurationFile
, 'utf8')
345 ) as ConfigurationData
;
347 FileUtils
.handleFileException(
348 Configuration
.configurationFile
,
349 FileType
.Configuration
,
350 error
as NodeJS
.ErrnoException
,
351 Configuration
.logPrefix(),
355 if (!Configuration
.configurationFileWatcher
) {
356 Configuration
.configurationFileWatcher
= Configuration
.getConfigurationFileWatcher();
359 return Configuration
.configuration
;
362 private static getConfigurationFileWatcher(): fs
.FSWatcher
| undefined {
364 return fs
.watch(Configuration
.configurationFile
, (event
, filename
): void => {
365 if (filename
?.trim().length
> 0 && event
=== 'change') {
366 // Nullify to force configuration file reading
367 Configuration
.configuration
= null;
368 if (!Utils
.isUndefined(Configuration
.configurationChangeCallback
)) {
369 Configuration
.configurationChangeCallback().catch((error
) => {
370 throw typeof error
=== 'string' ? new Error(error
) : error
;
376 FileUtils
.handleFileException(
377 Configuration
.configurationFile
,
378 FileType
.Configuration
,
379 error
as NodeJS
.ErrnoException
,
380 Configuration
.logPrefix(),
386 private static getDefaultPerformanceStorageUri(storageType
: StorageType
) {
387 switch (storageType
) {
388 case StorageType
.JSON_FILE
:
389 return `file://${path.join(
390 path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../'),
391 Constants.DEFAULT_PERFORMANCE_RECORDS_FILENAME
393 case StorageType
.SQLITE
:
394 return `file://${path.join(
395 path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../'),
396 `${Constants.DEFAULT_PERFORMANCE_RECORDS_DB_NAME}
.db
`
399 throw new Error(`Performance storage URI is mandatory with storage type '${storageType}'`);