Gracefully handle simulator missing files or files not found.
authorJérôme Benoit <jerome.benoit@sap.com>
Mon, 31 May 2021 13:21:11 +0000 (15:21 +0200)
committerJérôme Benoit <jerome.benoit@sap.com>
Mon, 31 May 2021 13:21:11 +0000 (15:21 +0200)
Signed-off-by: Jérôme Benoit <jerome.benoit@sap.com>
package-lock.json
package.json
src/charging-station/ChargingStation.ts
src/utils/Configuration.ts
src/utils/FileUtils.ts [new file with mode: 0644]
src/utils/Utils.ts

index ddefd7e5e548c0d45bc174069767bcebc067b643..e83a77ec26fe48b1d5581d3d83f0db7c987cd575 100644 (file)
       }
     },
     "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",
       "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": {
index 594003fc0948130311ef508a95420e1059ca0233..5982e6b36e8e78aea50455988248a481aa8daca7 100644 (file)
@@ -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",
index 8b8091fdbf4e65eb3435e6273bbbbad104e0a747..5e4bf7b34a595144c843c6bad1cf426c29ee06ea 100644 (file)
@@ -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<void> => {
-      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<void> => {
+        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 {
index a347969683fe546333c3db870934c64b55a4f369..98483898a708b43a05dc6fec7f61a7a97c825c3a 100644 (file)
@@ -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<void> => {
-      // 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<void> => {
+        // 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 (file)
index 0000000..8edbb4c
--- /dev/null
@@ -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;
+    }
+  }
+}
index 6d37271ff6382f06c18711aed2e0852ddae61f0c..7485b14f3162e89fbc75456f8228995c54170d04 100644 (file)
@@ -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<T>(object: T): T {