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';
19 import path from
'path';
21 export default class Configuration
{
22 private static configurationFile
= path
.join(
23 path
.resolve(__dirname
, '../'),
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 options
: ServerOptions
= {
55 host
: Constants
.DEFAULT_UI_WEBSOCKET_SERVER_HOST
,
56 port
: Constants
.DEFAULT_UI_WEBSOCKET_SERVER_PORT
,
58 let uiServerConfiguration
: UIServerConfiguration
= {
62 if (Configuration
.objectHasOwnProperty(Configuration
.getConfig(), 'uiServer')) {
63 if (Configuration
.objectHasOwnProperty(Configuration
.getConfig().uiServer
, 'options')) {
66 ...(Configuration
.objectHasOwnProperty(
67 Configuration
.getConfig().uiServer
.options
,
69 ) && { host
: Configuration
.getConfig().uiServer
.options
.host
}),
70 ...(Configuration
.objectHasOwnProperty(
71 Configuration
.getConfig().uiServer
.options
,
73 ) && { port
: Configuration
.getConfig().uiServer
.options
.port
}),
76 uiServerConfiguration
= {
77 ...uiServerConfiguration
,
78 ...(Configuration
.objectHasOwnProperty(Configuration
.getConfig().uiServer
, 'enabled') && {
79 enabled
: Configuration
.getConfig().uiServer
.enabled
,
84 return uiServerConfiguration
;
87 static getPerformanceStorage(): StorageConfiguration
{
88 Configuration
.warnDeprecatedConfigurationKey('URI', 'performanceStorage', "Use 'uri' instead");
89 let storageConfiguration
: StorageConfiguration
= {
91 type: StorageType
.JSON_FILE
,
92 uri
: this.getDefaultPerformanceStorageUri(StorageType
.JSON_FILE
),
94 if (Configuration
.objectHasOwnProperty(Configuration
.getConfig(), 'performanceStorage')) {
95 storageConfiguration
= {
96 ...storageConfiguration
,
97 ...(Configuration
.objectHasOwnProperty(
98 Configuration
.getConfig().performanceStorage
,
100 ) && { enabled
: Configuration
.getConfig().performanceStorage
.enabled
}),
101 ...(Configuration
.objectHasOwnProperty(
102 Configuration
.getConfig().performanceStorage
,
104 ) && { type: Configuration
.getConfig().performanceStorage
.type }),
105 ...(Configuration
.objectHasOwnProperty(
106 Configuration
.getConfig().performanceStorage
,
109 uri
: this.getDefaultPerformanceStorageUri(
110 Configuration
.getConfig()?.performanceStorage
?.type ?? StorageType
.JSON_FILE
115 return storageConfiguration
;
118 static getAutoReconnectMaxRetries(): number {
119 Configuration
.warnDeprecatedConfigurationKey(
120 'autoReconnectTimeout',
122 "Use 'ConnectionTimeOut' OCPP parameter in charging station template instead"
124 Configuration
.warnDeprecatedConfigurationKey(
127 "Use 'ConnectionTimeOut' OCPP parameter in charging station template instead"
129 Configuration
.warnDeprecatedConfigurationKey(
130 'autoReconnectMaxRetries',
132 'Use it in charging station template instead'
135 if (Configuration
.objectHasOwnProperty(Configuration
.getConfig(), 'autoReconnectMaxRetries')) {
136 return Configuration
.getConfig().autoReconnectMaxRetries
;
140 static getStationTemplateUrls(): StationTemplateUrl
[] {
141 Configuration
.warnDeprecatedConfigurationKey(
142 'stationTemplateURLs',
144 "Use 'stationTemplateUrls' instead"
146 !Configuration
.isUndefined(Configuration
.getConfig()['stationTemplateURLs']) &&
147 (Configuration
.getConfig().stationTemplateUrls
= Configuration
.getConfig()[
148 'stationTemplateURLs'
149 ] as StationTemplateUrl
[]);
150 Configuration
.getConfig().stationTemplateUrls
.forEach((stationUrl
: StationTemplateUrl
) => {
151 if (!Configuration
.isUndefined(stationUrl
['numberOfStation'])) {
153 chalk
`{green ${Configuration.logPrefix()}} {red Deprecated configuration key 'numberOfStation' usage for template file '${
155 }' in 'stationTemplateUrls'. Use 'numberOfStations' instead}`
160 return Configuration
.getConfig().stationTemplateUrls
;
163 static getWorkerProcess(): WorkerProcessType
{
164 Configuration
.warnDeprecatedConfigurationKey(
167 "Use 'workerProcess' to define the type of worker process model to use instead"
169 return Configuration
.objectHasOwnProperty(Configuration
.getConfig(), 'workerProcess')
170 ? Configuration
.getConfig().workerProcess
171 : WorkerProcessType
.WORKER_SET
;
174 static getWorkerStartDelay(): number {
175 return Configuration
.objectHasOwnProperty(Configuration
.getConfig(), 'workerStartDelay')
176 ? Configuration
.getConfig().workerStartDelay
177 : WorkerConstants
.DEFAULT_WORKER_START_DELAY
;
180 static getElementStartDelay(): number {
181 return Configuration
.objectHasOwnProperty(Configuration
.getConfig(), 'elementStartDelay')
182 ? Configuration
.getConfig().elementStartDelay
183 : WorkerConstants
.DEFAULT_ELEMENT_START_DELAY
;
186 static getWorkerPoolMinSize(): number {
187 return Configuration
.objectHasOwnProperty(Configuration
.getConfig(), 'workerPoolMinSize')
188 ? Configuration
.getConfig().workerPoolMinSize
189 : WorkerConstants
.DEFAULT_POOL_MIN_SIZE
;
192 static getWorkerPoolMaxSize(): number {
193 Configuration
.warnDeprecatedConfigurationKey(
196 "Use 'workerPoolMaxSize' instead"
198 return Configuration
.objectHasOwnProperty(Configuration
.getConfig(), 'workerPoolMaxSize')
199 ? Configuration
.getConfig().workerPoolMaxSize
200 : WorkerConstants
.DEFAULT_POOL_MAX_SIZE
;
203 static getWorkerPoolStrategy(): WorkerChoiceStrategy
{
204 return Configuration
.getConfig().workerPoolStrategy
;
207 static getChargingStationsPerWorker(): number {
208 return Configuration
.objectHasOwnProperty(
209 Configuration
.getConfig(),
210 'chargingStationsPerWorker'
212 ? Configuration
.getConfig().chargingStationsPerWorker
213 : WorkerConstants
.DEFAULT_ELEMENTS_PER_WORKER
;
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(__dirname, '../../'),
374 Constants.DEFAULT_PERFORMANCE_RECORDS_FILENAME
376 case StorageType
.SQLITE
:
377 return `file://${path.join(path.resolve(__dirname, '../../'), SQLiteFileName)}`;
379 throw new Error(`Performance storage URI is mandatory with storage type '${storageType}'`);
383 private static objectHasOwnProperty(object
: unknown
, property
: string): boolean {
384 return Object.prototype
.hasOwnProperty
.call(object
, property
) as boolean;
387 private static isUndefined(obj
: unknown
): boolean {
388 return typeof obj
=== 'undefined';
391 private static handleFileException(
395 error
: NodeJS
.ErrnoException
,
396 params
: HandleErrorParams
<EmptyObject
> = { throwError
: true }
398 const prefix
= logPrefix
.length
!== 0 ? logPrefix
+ ' ' : '';
399 if (error
.code
=== 'ENOENT') {
401 chalk
.green(prefix
) + chalk
.red(fileType
+ ' file ' + filePath
+ ' not found: '),
404 } else if (error
.code
=== 'EEXIST') {
406 chalk
.green(prefix
) + chalk
.red(fileType
+ ' file ' + filePath
+ ' already exists: '),
409 } else if (error
.code
=== 'EACCES') {
411 chalk
.green(prefix
) + chalk
.red(fileType
+ ' file ' + filePath
+ ' access denied: '),
416 chalk
.green(prefix
) + chalk
.red(fileType
+ ' file ' + filePath
+ ' error: '),
420 if (params
?.throwError
) {