More typing.
authorJérôme Benoit <jerome.benoit@sap.com>
Wed, 11 Nov 2020 00:53:59 +0000 (01:53 +0100)
committerJérôme Benoit <jerome.benoit@sap.com>
Wed, 11 Nov 2020 00:53:59 +0000 (01:53 +0100)
Signed-off-by: Jérôme Benoit <jerome.benoit@sap.com>
docker/config.json
src/assets/config-template.json
src/charging-station/ChargingStation.ts
src/start.ts
src/types/ChargingStationConfiguration.ts [new file with mode: 0644]
src/types/CommandStatisticsData.ts [new file with mode: 0644]
src/types/ConfigurationData.ts [new file with mode: 0644]
src/utils/Configuration.ts
src/utils/Statistics.ts

index 6535a0745af22c45648be9b8f53034f5c3e918f1..02ec20602171034dcfcd1138f410d61b4c66577e 100644 (file)
   "stationTemplateURLs": [
     {
       "file": "./src/assets/station-templates/siemens.station-template.json",
-      "numberOfStation": 0
+      "numberOfStations": 0
     },
     {
       "file": "./src/assets/station-templates/keba.station-template.json",
-      "numberOfStation": 0
+      "numberOfStations": 0
     },
     {
       "file": "./src/assets/station-templates/abb.station-template.json",
-      "numberOfStation": 0
+      "numberOfStations": 0
     },
     {
       "file": "./src/assets/station-templates/evlink.station-template.json",
-      "numberOfStation": 0
+      "numberOfStations": 0
     },
     {
       "file": "./src/assets/station-templates/schneider.station-template.json",
-      "numberOfStation": 0
+      "numberOfStations": 0
     },
     {
       "file": "./src/assets/station-templates/virtual-simple-atg.station-template.json",
-      "numberOfStation": 10
+      "numberOfStations": 10
     }
   ],
   "consoleLog": false,
index 69659bbc109d21c780b2f8491049902b21affa0b..d55855febfe22ff2dca51fde9f314e4aca6a3c93 100644 (file)
@@ -2,32 +2,32 @@
   "supervisionURLs": [
     "ws://localhost:8010/OCPP16/5be7fb271014d90008992f06"
   ],
-  "statisticsDisplayInterval": 60,
   "autoReconnectTimeout": 10,
   "autoReconnectMaxRetries": 10,
+  "statisticsDisplayInterval": 60,
   "distributeStationToTenantEqually": true,
   "useWorkerPool": false,
   "workerPoolSize": 16,
   "stationTemplateURLs": [
     {
       "file": "./src/assets/station-templates/siemens.station-template.json",
-      "numberOfStation": 1
+      "numberOfStations": 1
     },
     {
       "file": "./src/assets/station-templates/keba.station-template.json",
-      "numberOfStation": 2
+      "numberOfStations": 2
     },
     {
       "file": "./src/assets/station-templates/abb.station-template.json",
-      "numberOfStation": 2
+      "numberOfStations": 2
     },
     {
       "file": "./src/assets/station-templates/evlink.station-template.json",
-      "numberOfStation": 4
+      "numberOfStations": 4
     },
     {
       "file": "./src/assets/station-templates/schneider.station-template.json",
-      "numberOfStation": 1
+      "numberOfStations": 1
     }
   ],
   "logFile": "combined.log",
index 0a0043c9daf5d2ac8e070905afeedfaa36134339..9dcea851f02b5baea14b767436b95056ac565260 100644 (file)
@@ -1,3 +1,4 @@
+import ChargingStationConfiguration, { ConfigurationKey } from '../types/ChargingStationConfiguration';
 import Connectors, { Connector } from '../types/Connectors';
 import MeterValue, { MeterValueLocation, MeterValueMeasurand, MeterValuePhase, MeterValueUnit } from '../types/MeterValue';
 import { PerformanceObserver, performance } from 'perf_hooks';
@@ -21,9 +22,15 @@ export default class ChargingStation {
   private _index: number;
   private _stationTemplateFile: string;
   private _stationInfo;
-  private _bootNotificationMessage;
+  private _bootNotificationMessage: {
+    chargePointModel: string,
+    chargePointVendor: string,
+    chargeBoxSerialNumber?: string,
+    firmwareVersion?: string,
+  };
+
   private _connectors: Connectors;
-  private _configuration;
+  private _configuration: ChargingStationConfiguration;
   private _connectorsConfigurationHash: string;
   private _supervisionUrl: string;
   private _wsConnectionUrl: string;
@@ -32,7 +39,7 @@ export default class ChargingStation {
   private _autoReconnectRetryCount: number;
   private _autoReconnectMaxRetries: number;
   private _autoReconnectTimeout: number;
-  private _requests;
+  private _requests: { [id: string]: [(payload?, requestPayload?) => void, (error?: OCPPError) => void, object] };
   private _messageQueue: any[];
   private _automaticTransactionGeneration: AutomaticTransactionGenerator;
   private _authorizedTags: string[];
@@ -169,8 +176,8 @@ export default class ChargingStation {
     return Utils.logPrefix(` ${this._stationInfo.name}:`);
   }
 
-  _getConfiguration() {
-    return this._stationInfo.Configuration ? this._stationInfo.Configuration : {};
+  _getConfiguration(): ChargingStationConfiguration {
+    return this._stationInfo.Configuration ? this._stationInfo.Configuration : {} as ChargingStationConfiguration;
   }
 
   _getAuthorizationFile(): string {
@@ -292,9 +299,9 @@ export default class ChargingStation {
         // Get a random url
         indexUrl = Math.floor(Math.random() * supervisionUrls.length);
       }
-      return supervisionUrls[indexUrl];
+      return supervisionUrls[indexUrl] as string;
     }
-    return supervisionUrls;
+    return supervisionUrls as string;
   }
 
   _getAuthorizeRemoteTxRequests(): boolean {
@@ -478,7 +485,7 @@ export default class ChargingStation {
       logger.error(`${this._logPrefix()} Socket: connection retry with timeout ${this._autoReconnectTimeout}ms`);
       this._autoReconnectRetryCount++;
       setTimeout(() => {
-        logger.error(this._logPrefix() + ' Socket: reconnecting try #' + this._autoReconnectRetryCount);
+        logger.error(this._logPrefix() + ' Socket: reconnecting try #' + this._autoReconnectRetryCount.toString());
         this.start();
       }, this._autoReconnectTimeout);
     } else if (this._autoReconnectTimeout !== 0 || this._autoReconnectMaxRetries !== -1) {
@@ -703,7 +710,7 @@ export default class ChargingStation {
             ...!Utils.isUndefined(meterValuesTemplate[index].value) ? { value: meterValuesTemplate[index].value } : { value: voltageMeasurandValue.toString() },
           });
           for (let phase = 1; self._getNumberOfPhases() === 3 && phase <= self._getNumberOfPhases(); phase++) {
-            const voltageValue = Utils.convertToInt(sampledValues.sampledValue[sampledValues.sampledValue.length - 1].value);
+            const voltageValue = Utils.convertToFloat(sampledValues.sampledValue[sampledValues.sampledValue.length - 1].value);
             let phaseValue: string;
             if (voltageValue >= 0 && voltageValue <= 250) {
               phaseValue = `L${phase}-N`;
@@ -720,7 +727,7 @@ export default class ChargingStation {
             });
           }
         // Power.Active.Import measurand
-        } else if (meterValuesTemplate[index].measurand && meterValuesTemplate[index].measurand === MeterValueMeasurand.POWER_ACTIVE_EXPORT && self._getConfigurationKey('MeterValuesSampledData').value.includes(MeterValueMeasurand.POWER_ACTIVE_EXPORT)) {
+        } else if (meterValuesTemplate[index].measurand && meterValuesTemplate[index].measurand === MeterValueMeasurand.POWER_ACTIVE_IMPORT && self._getConfigurationKey('MeterValuesSampledData').value.includes(MeterValueMeasurand.POWER_ACTIVE_IMPORT)) {
           // FIXME: factor out powerDivider checks
           if (Utils.isUndefined(self._stationInfo.powerDivider)) {
             const errMsg = `${self._logPrefix()} MeterValues measurand ${meterValuesTemplate[index].measurand ? meterValuesTemplate[index].measurand : MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: powerDivider is undefined`;
@@ -891,14 +898,14 @@ export default class ChargingStation {
     }
   }
 
-  async sendError(messageId, err: Error | OCPPError, commandName): Promise<unknown> {
+  async sendError(messageId: string, err: Error | OCPPError, commandName: string): Promise<unknown> {
     // Check exception type: only OCPP error are accepted
     const error = err instanceof OCPPError ? err : new OCPPError(Constants.OCPP_ERROR_INTERNAL_ERROR, err.message, err.stack && err.stack);
     // Send error
     return this.sendMessage(messageId, error, Constants.OCPP_JSON_CALL_ERROR_MESSAGE, commandName);
   }
 
-  async sendMessage(messageId, commandParams, messageType = Constants.OCPP_JSON_CALL_RESULT_MESSAGE, commandName: string): Promise<unknown> {
+  async sendMessage(messageId: string, commandParams, messageType = Constants.OCPP_JSON_CALL_RESULT_MESSAGE, commandName: string): Promise<unknown> {
     // eslint-disable-next-line @typescript-eslint/no-this-alias
     const self = this;
     // Send a message through wsConnection
@@ -974,7 +981,7 @@ export default class ChargingStation {
         logger.debug(`${self._logPrefix()} Error %j occurred when calling command %s with parameters %j`, error, commandName, commandParams);
         // Build Exception
         // eslint-disable-next-line no-empty-function
-        self._requests[messageId] = [() => { }, () => { }, '']; // Properly format the request
+        self._requests[messageId] = [() => { }, () => { }, {}]; // Properly format the request
         // Send error
         reject(error);
       }
@@ -1046,7 +1053,7 @@ export default class ChargingStation {
       }
       const configuredMeterValueSampleInterval = this._getConfigurationKey('MeterValueSampleInterval');
       this._startMeterValues(requestPayload.connectorId,
-        configuredMeterValueSampleInterval ? configuredMeterValueSampleInterval.value * 1000 : 60000);
+        configuredMeterValueSampleInterval ? Utils.convertToInt(configuredMeterValueSampleInterval.value) * 1000 : 60000);
     } else {
       logger.error(this._logPrefix() + ' Starting transaction id ' + payload.transactionId + ' REJECTED with status ' + payload.idTagInfo.status + ', idTag ' + requestPayload.idTag);
       this._resetTransactionOnConnector(requestPayload.connectorId);
@@ -1090,7 +1097,7 @@ export default class ChargingStation {
     logger.debug(this._logPrefix() + ' Heartbeat response received: %j to Heartbeat request: %j', payload, requestPayload);
   }
 
-  async handleRequest(messageId, commandName, commandPayload): Promise<void> {
+  async handleRequest(messageId: string, commandName: string, commandPayload): Promise<void> {
     let response;
     // Call
     if (typeof this['handleRequest' + commandName] === 'function') {
@@ -1124,7 +1131,7 @@ export default class ChargingStation {
     return Constants.OCPP_RESPONSE_ACCEPTED;
   }
 
-  _getConfigurationKey(key: string) {
+  _getConfigurationKey(key: string): ConfigurationKey {
     return this._configuration.configurationKey.find((configElement) => configElement.key === key);
   }
 
@@ -1149,9 +1156,9 @@ export default class ChargingStation {
     }
   }
 
-  handleRequestGetConfiguration(commandPayload) {
-    const configurationKey = [];
-    const unknownKey = [];
+  handleRequestGetConfiguration(commandPayload): { configurationKey: ConfigurationKey[]; unknownKey: string[] } {
+    const configurationKey: ConfigurationKey[] = [];
+    const unknownKey: string[] = [];
     if (Utils.isEmptyArray(commandPayload.key)) {
       for (const configuration of this._configuration.configurationKey) {
         if (Utils.isUndefined(configuration.visible)) {
index baef77d120b6022ec3ecb762bf7aff8de0a22302..61a6b5f9f56c03fe4602ac9723cede9aeacf635e 100644 (file)
@@ -1,4 +1,5 @@
 import Configuration from './utils/Configuration';
+import { StationTemplateURL } from './types/ConfigurationData';
 import Utils from './utils/Utils';
 import Wrk from './charging-station/Worker';
 import logger from './utils/Logger';
@@ -10,11 +11,11 @@ class Bootstrap {
       let numStationsTotal = 0;
       // Start each ChargingStation object in a worker thread
       if (Configuration.getStationTemplateURLs()) {
-        Configuration.getStationTemplateURLs().forEach((stationURL) => {
+        Configuration.getStationTemplateURLs().forEach((stationURL: StationTemplateURL) => {
           try {
-            const nbStation = stationURL.numberOfStation ? stationURL.numberOfStation : 0;
-            numStationsTotal += nbStation;
-            for (let index = 1; index <= nbStation; index++) {
+            const nbStations = stationURL.numberOfStations ? stationURL.numberOfStations : 0;
+            numStationsTotal += nbStations;
+            for (let index = 1; index <= nbStations; index++) {
               const worker = new Wrk('./dist/charging-station/StationWorker.js', {
                 index,
                 templateFile: stationURL.file,
diff --git a/src/types/ChargingStationConfiguration.ts b/src/types/ChargingStationConfiguration.ts
new file mode 100644 (file)
index 0000000..b39a595
--- /dev/null
@@ -0,0 +1,11 @@
+export interface ConfigurationKey {
+  key: string;
+  readonly?: boolean;
+  value: string;
+  visible?: boolean;
+  reboot?: boolean;
+}
+
+export default interface ChargingStationConfiguration {
+  configurationKey: ConfigurationKey[];
+}
diff --git a/src/types/CommandStatisticsData.ts b/src/types/CommandStatisticsData.ts
new file mode 100644 (file)
index 0000000..edc92cc
--- /dev/null
@@ -0,0 +1,10 @@
+export default interface CommandStatisticsData {
+  countRequest: number;
+  countResponse: number;
+  countError: number;
+  countTime: number;
+  minTime: number;
+  maxTime: number;
+  totalTime: number;
+  avgTime: number;
+}
diff --git a/src/types/ConfigurationData.ts b/src/types/ConfigurationData.ts
new file mode 100644 (file)
index 0000000..29a443a
--- /dev/null
@@ -0,0 +1,20 @@
+export interface StationTemplateURL {
+  file: string;
+  numberOfStations: number;
+}
+
+export default interface ConfigurationData {
+  supervisionURLs?: string[];
+  stationTemplateURLs: StationTemplateURL[];
+  statisticsDisplayInterval?: number;
+  autoReconnectTimeout?: number;
+  autoReconnectMaxRetries?: number;
+  distributeStationToTenantEqually?: boolean;
+  useWorkerPool?: boolean;
+  workerPoolSize?: number;
+  logFormat?: string;
+  logLevel?: string;
+  logFile?: string;
+  errorFile?: string;
+  consoleLog?: boolean;
+}
index 995ab5d3a5b95a74a71368c198f698d0f585446d..d15f90537aea9a1975d2cb1d46c0efa658acb9be 100644 (file)
@@ -1,13 +1,15 @@
+import ConfigurationData, { StationTemplateURL } from '../types/ConfigurationData';
+
 import Utils from './Utils';
 import fs from 'fs';
 
 export default class Configuration {
-  static configuration;
+  static configuration: ConfigurationData;
 
   // Read the config file
-  static getConfig() {
+  static getConfig(): ConfigurationData {
     if (!Configuration.configuration) {
-      Configuration.configuration = JSON.parse(fs.readFileSync('./src/assets/config.json', 'utf8'));
+      Configuration.configuration = JSON.parse(fs.readFileSync('./src/assets/config.json', 'utf8')) as ConfigurationData;
     }
     return Configuration.configuration;
   }
@@ -27,7 +29,7 @@ export default class Configuration {
     return Utils.objectHasOwnProperty(Configuration.getConfig(), 'autoReconnectMaxRetries') ? Configuration.getConfig().autoReconnectMaxRetries : -1;
   }
 
-  static getStationTemplateURLs(): any[] {
+  static getStationTemplateURLs(): StationTemplateURL[] {
     // Read conf
     return Configuration.getConfig().stationTemplateURLs;
   }
@@ -60,7 +62,7 @@ export default class Configuration {
     return Utils.objectHasOwnProperty(Configuration.getConfig(), 'errorFile') ? Configuration.getConfig().errorFile : 'error.log';
   }
 
-  static getSupervisionURLs(): string {
+  static getSupervisionURLs(): string[] {
     // Read conf
     return Configuration.getConfig().supervisionURLs;
   }
index 6adfaef4ee4b07cdae9171dc0c762647a69e810b..f2bfee4ffc53208a9cf531ab7bb3be892403b01b 100644 (file)
@@ -1,15 +1,19 @@
+import CommandStatisticsData from '../types/CommandStatisticsData';
 import Configuration from './Configuration';
 import Constants from './Constants';
+import { PerformanceEntry } from 'perf_hooks';
 import Utils from './Utils';
 import logger from './Logger';
 
 export default class Statistics {
   private static instance: Statistics;
-  private _statistics;
   private _objName: string;
+  private _commandsStatistics: {
+    [command: string]: CommandStatisticsData
+  };
 
   private constructor() {
-    this._statistics = {};
+    this._commandsStatistics = {};
   }
 
   set objName(objName: string) {
@@ -26,35 +30,35 @@ export default class Statistics {
   addMessage(command: string, messageType: number): void {
     switch (messageType) {
       case Constants.OCPP_JSON_CALL_MESSAGE:
-        if (this._statistics[command] && this._statistics[command].countRequest) {
-          this._statistics[command].countRequest++;
+        if (this._commandsStatistics[command] && this._commandsStatistics[command].countRequest) {
+          this._commandsStatistics[command].countRequest++;
         } else {
-          this._statistics[command] = {};
-          this._statistics[command].countRequest = 1;
+          this._commandsStatistics[command] = {} as CommandStatisticsData;
+          this._commandsStatistics[command].countRequest = 1;
         }
         break;
       case Constants.OCPP_JSON_CALL_RESULT_MESSAGE:
-        if (this._statistics[command]) {
-          if (this._statistics[command].countResponse) {
-            this._statistics[command].countResponse++;
+        if (this._commandsStatistics[command]) {
+          if (this._commandsStatistics[command].countResponse) {
+            this._commandsStatistics[command].countResponse++;
           } else {
-            this._statistics[command].countResponse = 1;
+            this._commandsStatistics[command].countResponse = 1;
           }
         } else {
-          this._statistics[command] = {};
-          this._statistics[command].countResponse = 1;
+          this._commandsStatistics[command] = {} as CommandStatisticsData;
+          this._commandsStatistics[command].countResponse = 1;
         }
         break;
       case Constants.OCPP_JSON_CALL_ERROR_MESSAGE:
-        if (this._statistics[command]) {
-          if (this._statistics[command].countError) {
-            this._statistics[command].countError++;
+        if (this._commandsStatistics[command]) {
+          if (this._commandsStatistics[command].countError) {
+            this._commandsStatistics[command].countError++;
           } else {
-            this._statistics[command].countError = 1;
+            this._commandsStatistics[command].countError = 1;
           }
         } else {
-          this._statistics[command] = {};
-          this._statistics[command].countError = 1;
+          this._commandsStatistics[command] = {} as CommandStatisticsData;
+          this._commandsStatistics[command].countError = 1;
         }
         break;
       default:
@@ -71,27 +75,27 @@ export default class Statistics {
       stopTransaction: 'StopTransaction',
     };
     if (MAPCOMMAND[command]) {
-      command = MAPCOMMAND[command];
+      command = MAPCOMMAND[command] as string;
     }
     // Initialize command statistics
-    if (!this._statistics[command]) {
-      this._statistics[command] = {};
+    if (!this._commandsStatistics[command]) {
+      this._commandsStatistics[command] = {} as CommandStatisticsData;
     }
     // Update current statistics timers
-    this._statistics[command].countTime = this._statistics[command].countTime ? this._statistics[command].countTime + 1 : 1;
-    this._statistics[command].minTime = this._statistics[command].minTime ? (this._statistics[command].minTime > duration ? duration : this._statistics[command].minTime) : duration;
-    this._statistics[command].maxTime = this._statistics[command].maxTime ? (this._statistics[command].maxTime < duration ? duration : this._statistics[command].maxTime) : duration;
-    this._statistics[command].totalTime = this._statistics[command].totalTime ? this._statistics[command].totalTime + duration : duration;
-    this._statistics[command].avgTime = this._statistics[command].totalTime / this._statistics[command].countTime;
+    this._commandsStatistics[command].countTime = this._commandsStatistics[command].countTime ? this._commandsStatistics[command].countTime + 1 : 1;
+    this._commandsStatistics[command].minTime = this._commandsStatistics[command].minTime ? (this._commandsStatistics[command].minTime > duration ? duration : this._commandsStatistics[command].minTime) : duration;
+    this._commandsStatistics[command].maxTime = this._commandsStatistics[command].maxTime ? (this._commandsStatistics[command].maxTime < duration ? duration : this._commandsStatistics[command].maxTime) : duration;
+    this._commandsStatistics[command].totalTime = this._commandsStatistics[command].totalTime ? this._commandsStatistics[command].totalTime + duration : duration;
+    this._commandsStatistics[command].avgTime = this._commandsStatistics[command].totalTime / this._commandsStatistics[command].countTime;
   }
 
-  logPerformance(entry, className: string): void {
+  logPerformance(entry: PerformanceEntry, className: string): void {
     this.addPerformanceTimer(entry.name, entry.duration);
     logger.info(`${this._logPrefix()} class->${className}, method->${entry.name}, duration->${entry.duration}`);
   }
 
   _display(): void {
-    logger.info(this._logPrefix() + ' %j', this._statistics);
+    logger.info(this._logPrefix() + ' %j', this._commandsStatistics);
   }
 
   _displayInterval(): void {
@@ -99,7 +103,7 @@ export default class Statistics {
       setInterval(() => {
         this._display();
       }, Configuration.getStatisticsDisplayInterval() * 1000);
-      logger.info(this._logPrefix() + ' displayed every ' + Configuration.getStatisticsDisplayInterval() + 's');
+      logger.info(this._logPrefix() + ' displayed every ' + Configuration.getStatisticsDisplayInterval().toString() + 's');
     }
   }