From 23132a44933014c707d4fc3d0c681dc99cee7828 Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Mon, 31 May 2021 15:21:11 +0200 Subject: [PATCH] Gracefully handle simulator missing files or files not found. MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Jérôme Benoit --- package-lock.json | 14 +++--- package.json | 2 +- src/charging-station/ChargingStation.ts | 62 +++++++++++++++---------- src/utils/Configuration.ts | 44 +++++++++++++----- src/utils/FileUtils.ts | 21 +++++++++ src/utils/Utils.ts | 3 +- 6 files changed, 100 insertions(+), 46 deletions(-) create mode 100644 src/utils/FileUtils.ts diff --git a/package-lock.json b/package-lock.json index ddefd7e5..e83a77ec 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5838,16 +5838,16 @@ } }, "eslint-plugin-jsdoc": { - "version": "35.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-35.1.0.tgz", - "integrity": "sha512-XfLaI9kzXW1mihqmeTWH+fn5Zw+sbX48Jcmwp9dftwqegj6yT/3FNnuIxmM5VyLX3AdoBNDc8p4fje7/ZxVQyg==", + "version": "35.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-35.1.1.tgz", + "integrity": "sha512-pmhv0uNluRfFNAcy1rMI5Y0uw5i7EMo8dm7BV7hBUzHdHFlHdH/ZDGhmPh11QVvAEX9Gc0pCNjon4oUw5v0ovg==", "dev": true, "requires": { "@es-joy/jsdoccomment": "^0.8.0-alpha.2", "comment-parser": "1.1.5", "debug": "^4.3.1", "esquery": "^1.4.0", - "jsdoc-type-pratt-parser": "^1.0.0", + "jsdoc-type-pratt-parser": "^1.0.1", "lodash": "^4.17.21", "regextras": "^0.8.0", "semver": "^7.3.5", @@ -8276,9 +8276,9 @@ "dev": true }, "jsdoc-type-pratt-parser": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-1.0.0.tgz", - "integrity": "sha512-aFubex6uQ7gdoU173Sqdw9Abm6EOCYDhXWhP7YlS201Z+e5Ik38AiDHlKMqoqd/WiiU3/aWz/NHV5X8GQipOTQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-1.0.1.tgz", + "integrity": "sha512-+bq+6jEywRhrF06tCvjV0ew0XdFsaWTcmfpLE+42KbU6imm0TwoJrWclDVoo/8iGf0bO4SibYJLsduMWRD18kA==", "dev": true }, "jsesc": { diff --git a/package.json b/package.json index 594003fc..5982e6b3 100644 --- a/package.json +++ b/package.json @@ -79,7 +79,7 @@ "cross-env": "^7.0.3", "eslint": "^7.27.0", "eslint-plugin-import": "^2.23.4", - "eslint-plugin-jsdoc": "^35.1.0", + "eslint-plugin-jsdoc": "^35.1.1", "expect": "^27.0.2", "mbt": "^1.2.1", "mocha": "^8.4.0", diff --git a/src/charging-station/ChargingStation.ts b/src/charging-station/ChargingStation.ts index 8b8091fd..5e4bf7b3 100644 --- a/src/charging-station/ChargingStation.ts +++ b/src/charging-station/ChargingStation.ts @@ -12,6 +12,7 @@ import { ChargingProfile } from '../types/ocpp/ChargingProfile'; import ChargingStationInfo from '../types/ChargingStationInfo'; import Configuration from '../utils/Configuration'; import Constants from '../utils/Constants'; +import FileUtils from '../utils/FileUtils'; import { MessageType } from '../types/ocpp/MessageType'; import { MeterValueMeasurand } from '../types/ocpp/MeterValues'; import OCPP16IncomingRequestService from './ocpp/1.6/OCCP16IncomingRequestService'; @@ -359,8 +360,7 @@ export default class ChargingStation { stationTemplateFromFile = JSON.parse(fs.readFileSync(fileDescriptor, 'utf8')) as ChargingStationTemplate; fs.closeSync(fileDescriptor); } catch (error) { - logger.error('Template file ' + this.stationTemplateFile + ' loading error: %j', error); - throw error; + FileUtils.handleFileException(this.logPrefix(), 'Template', this.stationTemplateFile, error); } const stationInfo: ChargingStationInfo = stationTemplateFromFile || {} as ChargingStationInfo; if (!Utils.isEmptyArray(stationTemplateFromFile.power)) { @@ -611,8 +611,7 @@ export default class ChargingStation { authorizedTags = JSON.parse(fs.readFileSync(fileDescriptor, 'utf8')) as string[]; fs.closeSync(fileDescriptor); } catch (error) { - logger.error(this.logPrefix() + ' Authorization file ' + authorizationFile + ' loading error: %j', error); - throw error; + FileUtils.handleFileException(this.logPrefix(), 'Authorization', authorizationFile, error); } } else { logger.info(this.logPrefix() + ' No authorization file given in template file ' + this.stationTemplateFile); @@ -841,36 +840,49 @@ export default class ChargingStation { } private startAuthorizationFileMonitoring(): void { - fs.watch(this.getAuthorizationFile()).on('change', (e) => { + const authorizationFile = this.getAuthorizationFile(); + if (authorizationFile) { try { - logger.debug(this.logPrefix() + ' Authorization file ' + this.getAuthorizationFile() + ' have changed, reload'); - // Initialize authorizedTags - this.authorizedTags = this.getAuthorizedTags(); + fs.watch(authorizationFile).on('change', (e) => { + try { + logger.debug(this.logPrefix() + ' Authorization file ' + authorizationFile + ' have changed, reload'); + // Initialize authorizedTags + this.authorizedTags = this.getAuthorizedTags(); + } catch (error) { + logger.error(this.logPrefix() + ' Authorization file monitoring error: %j', error); + } + }); } catch (error) { - logger.error(this.logPrefix() + ' Authorization file monitoring error: %j', error); + FileUtils.handleFileException(this.logPrefix(), 'Authorization', authorizationFile, error); } - }); + } else { + logger.info(this.logPrefix() + ' No authorization file given in template file ' + this.stationTemplateFile + '. Not monitoring changes'); + } } private startStationTemplateFileMonitoring(): void { + try { // eslint-disable-next-line @typescript-eslint/no-misused-promises - fs.watch(this.stationTemplateFile).on('change', async (e): Promise => { - try { - logger.debug(this.logPrefix() + ' Template file ' + this.stationTemplateFile + ' have changed, reload'); - // Initialize - this.initialize(); - // Stop the ATG - if (!this.stationInfo.AutomaticTransactionGenerator.enable && + fs.watch(this.stationTemplateFile).on('change', async (e): Promise => { + try { + logger.debug(this.logPrefix() + ' Template file ' + this.stationTemplateFile + ' have changed, reload'); + // Initialize + this.initialize(); + // Stop the ATG + if (!this.stationInfo.AutomaticTransactionGenerator.enable && this.automaticTransactionGeneration) { - await this.automaticTransactionGeneration.stop(); + await this.automaticTransactionGeneration.stop(); + } + // Start the ATG + this.startAutomaticTransactionGenerator(); + // FIXME?: restart heartbeat and WebSocket ping when their interval values have changed + } catch (error) { + logger.error(this.logPrefix() + ' Charging station template file monitoring error: %j', error); } - // Start the ATG - this.startAutomaticTransactionGenerator(); - // FIXME?: restart heartbeat and WebSocket ping when their interval values have changed - } catch (error) { - logger.error(this.logPrefix() + ' Charging station template file monitoring error: %j', error); - } - }); + }); + } catch (error) { + FileUtils.handleFileException(this.logPrefix(), 'Template', this.stationTemplateFile, error); + } } private getReconnectExponentialDelay(): boolean | undefined { diff --git a/src/utils/Configuration.ts b/src/utils/Configuration.ts index a3479696..98483898 100644 --- a/src/utils/Configuration.ts +++ b/src/utils/Configuration.ts @@ -41,7 +41,7 @@ export default class Configuration { static getStationTemplateURLs(): StationTemplateURL[] { Configuration.getConfig().stationTemplateURLs.forEach((stationURL: StationTemplateURL) => { if (!Configuration.isUndefined(stationURL['numberOfStation'])) { - console.error(`Deprecated configuration key 'numberOfStation' usage for template file '${stationURL.file}' in 'stationTemplateURLs'. Use 'numberOfStations' instead`); + console.error(`${Configuration.logPrefix()} Deprecated configuration key 'numberOfStation' usage for template file '${stationURL.file}' in 'stationTemplateURLs'. Use 'numberOfStations' instead`); } }); // Read conf @@ -114,16 +114,24 @@ export default class Configuration { return Configuration.objectHasOwnProperty(Configuration.getConfig(), 'distributeStationsToTenantsEqually') ? Configuration.getConfig().distributeStationsToTenantsEqually : true; } + private static logPrefix(): string { + return new Date().toLocaleString() + ' Simulator configuration |'; + } + private static deprecateConfigurationKey(key: string, logMsgToAppend = '') { if (!Configuration.isUndefined(Configuration.getConfig()[key])) { - console.error(`Deprecated configuration key '${key}' usage${logMsgToAppend && '. ' + logMsgToAppend}`); + console.error(`${Configuration.logPrefix()} Deprecated configuration key '${key}' usage${logMsgToAppend && '. ' + logMsgToAppend}`); } } // Read the config file private static getConfig(): ConfigurationData { if (!Configuration.configuration) { - Configuration.configuration = JSON.parse(fs.readFileSync(Configuration.configurationFilePath, 'utf8')) as ConfigurationData; + try { + Configuration.configuration = JSON.parse(fs.readFileSync(Configuration.configurationFilePath, 'utf8')) as ConfigurationData; + } catch (error) { + Configuration.handleFileException(Configuration.logPrefix(), 'Configuration', Configuration.configurationFilePath, error); + } if (!Configuration.configurationFileWatcher) { Configuration.configurationFileWatcher = Configuration.getConfigurationFileWatcher(); } @@ -132,21 +140,35 @@ export default class Configuration { } private static getConfigurationFileWatcher(): fs.FSWatcher { + try { // eslint-disable-next-line @typescript-eslint/no-misused-promises - return fs.watch(Configuration.configurationFilePath).on('change', async (e): Promise => { - // Nullify to force configuration file reading - Configuration.configuration = null; - if (!Configuration.isUndefined(Configuration.configurationChangeCallback)) { - await Configuration.configurationChangeCallback(); - } - }); + return fs.watch(Configuration.configurationFilePath).on('change', async (e): Promise => { + // Nullify to force configuration file reading + Configuration.configuration = null; + if (!Configuration.isUndefined(Configuration.configurationChangeCallback)) { + await Configuration.configurationChangeCallback(); + } + }); + } catch (error) { + Configuration.handleFileException(Configuration.logPrefix(), 'Configuration', Configuration.configurationFilePath, error); + } } private static objectHasOwnProperty(object: any, property: string): boolean { - return Object.prototype.hasOwnProperty.call(object, property); + return Object.prototype.hasOwnProperty.call(object, property) as boolean; } private static isUndefined(obj: any): boolean { return typeof obj === 'undefined'; } + + private static handleFileException(logPrefix: string, fileType: string, filePath: string, error: NodeJS.ErrnoException): void { + const prefix = logPrefix.length !== 0 ? logPrefix + ' ' : ''; + if (error.code === 'ENOENT') { + console.error(prefix + fileType + ' file ' + filePath + ' not found: ', error); + } else { + console.error(prefix + fileType + ' file ' + filePath + ' opening error: ', error); + } + throw error; + } } diff --git a/src/utils/FileUtils.ts b/src/utils/FileUtils.ts new file mode 100644 index 00000000..8edbb4c5 --- /dev/null +++ b/src/utils/FileUtils.ts @@ -0,0 +1,21 @@ +import logger from './Logger'; + +export default class FileUtils { + static handleFileException(logPrefix: string, fileType: string, filePath: string, error: NodeJS.ErrnoException, consoleOut = false): void { + const prefix = logPrefix.length !== 0 ? logPrefix + ' ' : ''; + if (error.code === 'ENOENT') { + if (consoleOut) { + console.error(prefix + fileType + ' file ' + filePath + ' not found: ', error); + } else { + logger.error(prefix + fileType + ' file ' + filePath + ' not found: %j', error); + } + } else { + if (consoleOut) { + console.error(prefix + fileType + ' file ' + filePath + ' opening error: ', error); + } else { + logger.error(prefix + fileType + ' file ' + filePath + ' opening error: %j', error); + } + throw error; + } + } +} diff --git a/src/utils/Utils.ts b/src/utils/Utils.ts index 6d37271f..7485b14f 100644 --- a/src/utils/Utils.ts +++ b/src/utils/Utils.ts @@ -122,8 +122,7 @@ export default class Utils { } static logPrefix(prefixString = ''): string { - const date = new Date(); - return date.toLocaleString() + prefixString; + return new Date().toLocaleString() + prefixString; } static cloneObject(object: T): T { -- 2.34.1