Merge pull request #6 from LucasBrazi06/memory-optimization
authorJérôme Benoit <jerome.benoit@piment-noir.org>
Thu, 21 Jan 2021 08:49:01 +0000 (09:49 +0100)
committerGitHub <noreply@github.com>
Thu, 21 Jan 2021 08:49:01 +0000 (09:49 +0100)
17 files changed:
.gitignore
package-lock.json
package.json
src/assets/config-template.json
src/charging-station/AutomaticTransactionGenerator.ts
src/charging-station/ChargingStation.ts
src/charging-station/StationWorker.ts
src/charging-station/Worker.ts
src/scripts/deleteChargingStations.ts [new file with mode: 0644]
src/scripts/scriptConfig-template.json [new file with mode: 0644]
src/scripts/setCSPublicFlag.ts [new file with mode: 0644]
src/start.ts
src/types/ConfigurationData.ts
src/types/ocpp/1.6/Requests.ts
src/types/ocpp/1.6/Transaction.ts
src/utils/Configuration.ts
src/utils/Constants.ts

index 2b4ebbd73117cb2c27b7b62ab3bfac0a7ca9449f..3a622e5d9846860203ea9f46b6870000015570fa 100644 (file)
@@ -86,3 +86,4 @@ Thumbs.db
 # MTA
 *.mta
 mta_archives/
+src/scripts/scriptConfig.json
index 209b0e9a33753995caf7d55e30254edda297dc4d..c5dd1f8650e2ee68f42874e82b6eca0385743a85 100644 (file)
       "integrity": "sha1-DGwfq+KyPRcXPZpht7cJPrnhdp4=",
       "dev": true
     },
+    "bl": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz",
+      "integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==",
+      "requires": {
+        "readable-stream": "^2.3.5",
+        "safe-buffer": "^5.1.1"
+      }
+    },
     "bluebird": {
       "version": "3.7.2",
       "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
         "pako": "~1.0.5"
       }
     },
+    "bson": {
+      "version": "1.1.5",
+      "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.5.tgz",
+      "integrity": "sha512-kDuEzldR21lHciPQAIulLs1LZlCXdLziXI6Mb/TDkwXhb//UORJNPXgcRs2CuO4H0DcMkpfT3/ySsP3unoZjBg=="
+    },
     "buffer": {
       "version": "5.2.1",
       "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.2.1.tgz",
       "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
       "dev": true
     },
+    "denque": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.0.tgz",
+      "integrity": "sha512-CYiCSgIF1p6EUByQPlGkKnP1M9g0ZV3qMIrqMqZqdwazygIA/YP2vrbcyl1h/WppKJTdl1F85cXIle+394iDAQ=="
+    },
     "depcheck": {
       "version": "0.8.3",
       "resolved": "https://registry.npmjs.org/depcheck/-/depcheck-0.8.3.tgz",
         "safe-buffer": "^5.1.2"
       }
     },
+    "memory-pager": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz",
+      "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==",
+      "optional": true
+    },
     "meow": {
       "version": "3.7.0",
       "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz",
       "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
       "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ=="
     },
+    "mongodb": {
+      "version": "3.6.3",
+      "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.3.tgz",
+      "integrity": "sha512-rOZuR0QkodZiM+UbQE5kDsJykBqWi0CL4Ec2i1nrGrUI3KO11r6Fbxskqmq3JK2NH7aW4dcccBuUujAP0ERl5w==",
+      "requires": {
+        "bl": "^2.2.1",
+        "bson": "^1.1.4",
+        "denque": "^1.4.1",
+        "require_optional": "^1.0.1",
+        "safe-buffer": "^5.1.2",
+        "saslprep": "^1.0.0"
+      }
+    },
     "morphdom": {
       "version": "2.6.1",
       "resolved": "https://registry.npmjs.org/morphdom/-/morphdom-2.6.1.tgz",
       "version": "2.3.6",
       "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
       "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
-      "dev": true,
       "requires": {
         "core-util-is": "~1.0.0",
         "inherits": "~2.0.3",
       "integrity": "sha1-wR6XJ2tluOKSP3Xav1+y7ww4Qbk=",
       "dev": true
     },
+    "require_optional": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz",
+      "integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==",
+      "requires": {
+        "resolve-from": "^2.0.0",
+        "semver": "^5.1.0"
+      },
+      "dependencies": {
+        "resolve-from": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz",
+          "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c="
+        }
+      }
+    },
     "resolve": {
       "version": "1.10.0",
       "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz",
       "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
       "dev": true
     },
+    "saslprep": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz",
+      "integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==",
+      "optional": true,
+      "requires": {
+        "sparse-bitfield": "^3.0.3"
+      }
+    },
     "sax": {
       "version": "1.2.4",
       "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
     "semver": {
       "version": "5.6.0",
       "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz",
-      "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==",
-      "dev": true
+      "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg=="
     },
     "semver-compare": {
       "version": "1.0.0",
       "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
       "dev": true
     },
+    "sparse-bitfield": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz",
+      "integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=",
+      "optional": true,
+      "requires": {
+        "memory-pager": "^1.0.2"
+      }
+    },
     "spdx-correct": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz",
index 67fb72c9083ef018bf5abcd1f133901ae6d127bd..11c18cff7875fa46860e04825777337ebabcdbac 100644 (file)
@@ -46,6 +46,7 @@
     "git:spush": "git push --recurse-submodules=on-demand"
   },
   "dependencies": {
+    "mongodb": "^3.6.3",
     "source-map-support": "^0.5.19",
     "tslib": "^2.1.0",
     "uuid": "^8.3.2",
index adfe80ef5510346c4941f4415ae13961d9f175f7..61067bcf9839bc40656b254eda4137d46227cb2a 100644 (file)
@@ -6,6 +6,8 @@
   "statisticsDisplayInterval": 60,
   "useWorkerPool": false,
   "workerPoolSize": 16,
+  "chargingStationsPerWorker": 1,
+  "chargingStationIdSuffix": "",
   "stationTemplateURLs": [
     {
       "file": "./src/assets/station-templates/siemens.station-template.json",
index 0b1e7e2786d7f31c41f6be853c80d651b79e4db0..363605be0e8a19d8d57718c2e3a33a1fe58ae3d0 100644 (file)
@@ -97,7 +97,7 @@ export default class AutomaticTransactionGenerator {
         } else {
           startResponse = await this.startTransaction(connectorId, this);
         }
-        if (startResponse.idTagInfo.status !== AuthorizationStatus.ACCEPTED) {
+        if (startResponse?.idTagInfo?.status !== AuthorizationStatus.ACCEPTED) {
           logger.info(this._logPrefix(connectorId) + ' transaction rejected');
           await Utils.sleep(Constants.CHARGING_STATION_ATG_WAIT_TIME);
         } else {
@@ -131,7 +131,14 @@ export default class AutomaticTransactionGenerator {
     if (self._chargingStation.hasAuthorizedTags()) {
       const tagId = self._chargingStation.getRandomTagId();
       logger.info(self._logPrefix(connectorId) + ' start transaction for tagID ' + tagId);
-      return await self._chargingStation.sendStartTransaction(connectorId, tagId);
+      // Authorize tagId
+      const authorizeResponse = await self._chargingStation.sendAuthorize(tagId);
+      if (authorizeResponse?.idTagInfo?.status === AuthorizationStatus.ACCEPTED) {
+        // Start transaction
+        return await self._chargingStation.sendStartTransaction(connectorId, tagId);
+      } else {
+        return authorizeResponse as StartTransactionResponse;
+      }
     }
     logger.info(self._logPrefix(connectorId) + ' start transaction without a tagID');
     return await self._chargingStation.sendStartTransaction(connectorId);
index 6870c1f79f2802fec10f002bff3a2fe560090a21..b8ac0233f8abb66ff4c17d4e0da0ad803eaab2c0 100644 (file)
@@ -1,4 +1,4 @@
-import { AuthorizationStatus, StartTransactionRequest, StartTransactionResponse, StopTransactionReason, StopTransactionRequest, StopTransactionResponse } from '../types/ocpp/1.6/Transaction';
+import { AuthorizationStatus, AuthorizeRequest, AuthorizeResponse, StartTransactionRequest, StartTransactionResponse, StopTransactionReason, StopTransactionRequest, StopTransactionResponse } from '../types/ocpp/1.6/Transaction';
 import { AvailabilityType, BootNotificationRequest, ChangeAvailabilityRequest, ChangeConfigurationRequest, GetConfigurationRequest, HeartbeatRequest, IncomingRequestCommand, RemoteStartTransactionRequest, RemoteStopTransactionRequest, RequestCommand, ResetRequest, SetChargingProfileRequest, StatusNotificationRequest, UnlockConnectorRequest } from '../types/ocpp/1.6/Requests';
 import { BootNotificationResponse, ChangeAvailabilityResponse, ChangeConfigurationResponse, DefaultResponse, GetConfigurationResponse, HeartbeatResponse, RegistrationStatus, SetChargingProfileResponse, StatusNotificationResponse, UnlockConnectorResponse } from '../types/ocpp/1.6/RequestResponses';
 import { ChargingProfile, ChargingProfilePurposeType } from '../types/ocpp/1.6/ChargingProfile';
@@ -71,7 +71,13 @@ export default class ChargingStation {
   }
 
   _getStationName(stationTemplate: ChargingStationTemplate): string {
-    return stationTemplate.fixedName ? stationTemplate.baseName : stationTemplate.baseName + '-' + ('000000000' + this._index.toString()).substr(('000000000' + this._index.toString()).length - 4);
+    // In case of multiple instances: add instance index to charging station id
+    let instanceIndex = process.env.CF_INSTANCE_INDEX ? process.env.CF_INSTANCE_INDEX : 0;
+    instanceIndex = instanceIndex > 0 ? instanceIndex : '';
+
+    const idSuffix = Configuration.getChargingStationIdSuffix();
+
+    return stationTemplate.fixedName ? stationTemplate.baseName : stationTemplate.baseName + '-' + instanceIndex + ('000000000' + this._index.toString()).substr(('000000000' + this._index.toString()).length - 4) + idSuffix;
   }
 
   _buildStationInfo(): ChargingStationInfo {
@@ -521,8 +527,7 @@ export default class ChargingStation {
   }
 
   _startAuthorizationFileMonitoring(): void {
-    // eslint-disable-next-line @typescript-eslint/no-unused-vars
-    fs.watchFile(this._getAuthorizationFile(), (current, previous) => {
+    fs.watch(this._getAuthorizationFile()).on("change", e => {
       try {
         logger.debug(this._logPrefix() + ' Authorization file ' + this._getAuthorizationFile() + ' have changed, reload');
         // Initialize _authorizedTags
@@ -534,16 +539,25 @@ export default class ChargingStation {
   }
 
   _startStationTemplateFileMonitoring(): void {
-    // eslint-disable-next-line @typescript-eslint/no-unused-vars
-    fs.watchFile(this._stationTemplateFile, (current, previous) => {
+    fs.watch(this._stationTemplateFile).on("change", e => {
       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) {
           this._automaticTransactionGeneration.stop().catch(() => { });
         }
+        // Start the ATG
+        if (this._stationInfo.AutomaticTransactionGenerator.enable) {
+          if (!this._automaticTransactionGeneration) {
+            this._automaticTransactionGeneration = new AutomaticTransactionGenerator(this);
+          }
+          if (this._automaticTransactionGeneration.timeToStop) {
+            this._automaticTransactionGeneration.start();
+          }
+        }
         // 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);
@@ -811,6 +825,17 @@ export default class ChargingStation {
     }
   }
 
+  async sendAuthorize(idTag?: string): Promise<AuthorizeResponse> {
+    try {
+      const payload: AuthorizeRequest = {
+        ...!Utils.isUndefined(idTag) ? { idTag } : { idTag: Constants.TRANSACTION_DEFAULT_IDTAG },
+      };
+      return await this.sendMessage(Utils.generateUUID(), payload, MessageType.CALL_MESSAGE, RequestCommand.AUTHORIZE) as AuthorizeResponse;
+    } catch (error) {
+      this.handleRequestError(RequestCommand.AUTHORIZE, error);
+    }
+  }
+
   async sendStartTransaction(connectorId: number, idTag?: string): Promise<StartTransactionResponse> {
     try {
       const payload: StartTransactionRequest = {
index 68c3ea24c0f96c4def2f5c187050bd5e816dc04b..c7bc2337b0e772c4d73a3712b0606b9981500096 100644 (file)
@@ -1,8 +1,26 @@
-import { isMainThread, workerData } from 'worker_threads';
+import { isMainThread, parentPort, workerData } from 'worker_threads';
+import Constants from '../utils/Constants';
 
 import ChargingStation from './ChargingStation';
 
 if (!isMainThread) {
   const station = new ChargingStation(workerData.index as number, workerData.templateFile as string);
   station.start();
+
+  // Listener: start new charging station from main thread
+  addListener();
+}
+
+function addListener() {
+  parentPort.setMaxListeners(1000);
+  parentPort.on("message", e => {
+    if (e.id === Constants.START_NEW_CHARGING_STATION) {
+        startChargingStation(e.workerData);
+    }
+  });
+}
+
+function startChargingStation(data: any) {
+  const station = new ChargingStation(data.index as number, data.templateFile as string);
+  station.start();
 }
index 57748831ed5b4d00e6b6bd94663e984916d437c8..fbfdf3d1f047fa1886febf126750265ae73c5d79 100644 (file)
@@ -3,12 +3,14 @@ import { Worker, WorkerOptions } from 'worker_threads';
 import Configuration from '../utils/Configuration';
 import Pool from 'worker-threads-pool';
 import WorkerData from '../types/WorkerData';
+import Constants from '../utils/Constants';
 
 export default class Wrk {
   private _workerScript: string;
   private _workerData: WorkerData;
   private _index: number;
   private _concurrentWorkers: number;
+  private _worker: Worker;
 
   /**
    * Create a new `Wrk`.
@@ -42,11 +44,25 @@ export default class Wrk {
    * @return {Promise}
    * @public
    */
-  async start(): Promise<unknown> {
+  async start(): Promise<Worker> {
     if (Configuration.useWorkerPool()) {
-      return this._startWorkerWithPool();
+      this._startWorkerWithPool();
+    } else {
+      this._startWorker();
     }
-    return this._startWorker();
+    return this._worker;
+  }
+
+    /**
+   *
+   * @return {Promise}
+   * @public
+   */
+  async startNewChargingStation(workerData: WorkerData, numConcurrentWorkers: number): Promise<void> {
+    this._workerData = workerData;
+    this._index = workerData.index;
+    this._concurrentWorkers = numConcurrentWorkers;
+    this._worker.postMessage({ id : Constants.START_NEW_CHARGING_STATION, workerData: workerData });
   }
 
   /**
@@ -62,6 +78,7 @@ export default class Wrk {
         }
         worker.once('message', resolve);
         worker.once('error', reject);
+        this._worker = worker;
       });
     });
   }
@@ -81,6 +98,7 @@ export default class Wrk {
           reject(new Error(`Worker id ${this._index} stopped with exit code ${code}`));
         }
       });
+      this._worker = worker;
     });
   }
 }
@@ -92,7 +110,7 @@ class WorkerPool {
   private constructor() { }
 
   public static getInstance(): Pool {
-    if (!WorkerPool._instance) {
+    if (!WorkerPool._instance || (WorkerPool._instance?.size === WorkerPool.concurrentWorkers)) {
       WorkerPool._instance = new Pool({ max: WorkerPool.concurrentWorkers });
     }
     return WorkerPool._instance;
diff --git a/src/scripts/deleteChargingStations.ts b/src/scripts/deleteChargingStations.ts
new file mode 100644 (file)
index 0000000..db07c6c
--- /dev/null
@@ -0,0 +1,29 @@
+var MongoClient = require('mongodb');
+var fs = require('fs');
+
+// This script deletes charging stations
+// Filter charging stations by id pattern
+
+// Use Case: ev-simulator creates thousands of charging stations, which are not longer needed.
+// Delete these charging stations all at once
+
+// Config
+var config = JSON.parse(fs.readFileSync('scriptConfig.json', 'utf8'));
+
+// Mongo Connection and Query
+if (config && config.mongoConnectionString) {
+  MongoClient.connect(config.mongoConnectionString, {
+    useUnifiedTopology: true,
+    useNewUrlParser: true
+    }, async function(err, client) {
+    const db = client.db('evse');
+
+    for await (const tenantID of config.tenantIDs) {
+      let response = await db.collection(tenantID + '.chargingstations').deleteMany(
+        { _id: {'$regex': config.idPattern} }
+      );
+      console.log(response.deletedCount, `Charging Stations with id = %${config.idPattern}% deleted. TenantID =`, tenantID);
+    }
+    client.close();
+  });
+}
diff --git a/src/scripts/scriptConfig-template.json b/src/scripts/scriptConfig-template.json
new file mode 100644 (file)
index 0000000..fb911ff
--- /dev/null
@@ -0,0 +1,6 @@
+{
+  "publicFlag": true,
+  "tenantIDs": [""],
+  "idPattern": "",
+  "mongoConnectionString": "mongodb://..."
+}
diff --git a/src/scripts/setCSPublicFlag.ts b/src/scripts/setCSPublicFlag.ts
new file mode 100644 (file)
index 0000000..2bc6b7e
--- /dev/null
@@ -0,0 +1,31 @@
+var MongoClient = require('mongodb');
+var fs = require('fs');
+
+// This script sets charging stations public or private
+// Filter charging stations by id pattern
+
+// Use case: simulate charging station for roaming tests
+// charging stations are private by default
+// set public = true
+
+// Config
+var config = JSON.parse(fs.readFileSync('scriptConfig.json', 'utf8'));
+
+// Mongo Connection and Query
+if (config && config.mongoConnectionString) {
+  MongoClient.connect(config.mongoConnectionString, {
+    useUnifiedTopology: true,
+    useNewUrlParser: true
+    }, async function(err, client) {
+    const db = client.db('evse');
+
+    for await (const tenantID of config.tenantIDs) {
+      let response = await db.collection(tenantID + '.chargingstations').updateMany(
+        { _id: {'$regex': config.idPattern} },
+        { $set: { public : config.publicFlag } }
+      );
+      console.log(response.modifiedCount, `Charging Stations with id = %${config.idPattern}% updated. TenantID =`, tenantID);
+    }
+    client.close();
+  });
+}
index e841b57ec63692d173ea6f8e9d69703be546e668..fb58c2d28ca2187dbcf0bb48b8e5b4905f23c512 100644 (file)
@@ -1,31 +1,49 @@
 import Configuration from './utils/Configuration';
 import { StationTemplateURL } from './types/ConfigurationData';
+import Utils from './utils/Utils';
 import Wrk from './charging-station/Worker';
+import WorkerData from './types/WorkerData';
+import fs from 'fs';
 
 class Bootstrap {
-  static start() {
+  static async start() {
     try {
       let numStationsTotal = 0;
       let numConcurrentWorkers = 0;
+      let worker: Wrk;
+      let chargingStationsPerWorker = Configuration.getChargingStationsPerWorker();
+      let counter = 0;
       // Start each ChargingStation object in a worker thread
       if (Configuration.getStationTemplateURLs()) {
-        Configuration.getStationTemplateURLs().forEach((stationURL: StationTemplateURL) => {
+        for await (const stationURL of Configuration.getStationTemplateURLs()) {
           try {
             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', {
+              const workerData = {
                 index,
-                templateFile: stationURL.file,
-              }, numStationsTotal);
-              worker.start().catch(() => {});
+                templateFile: stationURL.file
+              } as WorkerData;
+              if(counter === 0 || counter === chargingStationsPerWorker) {
+                // Start new worker with one charging station
+                worker = await new Wrk('./dist/charging-station/StationWorker.js', workerData, numStationsTotal);
+                worker.start().catch(() => {});
+                counter = 0;
+                // Start workers sequentially to optimize memory at start time
+                await Utils.sleep(500);
+              } else {
+                // Add new charging station to existing Worker
+                worker.startNewChargingStation(workerData, numStationsTotal)
+              }
+              counter++;
+              // Start charging station sequentially to optimize memory at start time
               numConcurrentWorkers = worker.concurrentWorkers;
             }
           } catch (error) {
             // eslint-disable-next-line no-console
             console.log('Charging station start with template file ' + stationURL.file + ' error ' + JSON.stringify(error, null, ' '));
           }
-        });
+        }
       } else {
         console.log('No stationTemplateURLs defined in configuration, exiting');
       }
index 371e9c5d558587d137a031655f2dd4cc3c4757f9..e056471cfe5ed160513d727e5ca124627d07fb7a 100644 (file)
@@ -12,6 +12,8 @@ export default interface ConfigurationData {
   distributeStationsToTenantsEqually?: boolean;
   useWorkerPool?: boolean;
   workerPoolSize?: number;
+  chargingStationsPerWorker: number;
+  chargingStationIdSuffix: string;
   logFormat?: string;
   logLevel?: string;
   logRotate?: boolean;
index 1b3e25695f3285da980a57bb24abf5311eb64422..e7e662233aecb7ff6d4176111fd213aeaad30d37 100644 (file)
@@ -8,6 +8,7 @@ export enum RequestCommand {
   HEARTBEAT = 'Heartbeat',
   STATUS_NOTIFICATION = 'StatusNotification',
   CHANGE_CONFIGURATION = 'ChangeConfiguration',
+  AUTHORIZE = 'Authorize',
   START_TRANSACTION = 'StartTransaction',
   STOP_TRANSACTION = 'StopTransaction',
   METERVALUES = 'MeterValues'
index 7b072e700155d5f44bc888888e5d523938d7ceff..22fb6080efadc697771178715dbd86b69cf81ae2 100644 (file)
@@ -29,6 +29,14 @@ export interface IdTagInfo {
   expiryDate?: Date;
 }
 
+export interface AuthorizeRequest {
+  idTag: string;
+
+}
+export interface AuthorizeResponse {
+  idTagInfo: IdTagInfo;
+}
+
 export interface StartTransactionRequest {
   connectorId: number;
   idTag: string;
index ca98427eb87cee39c22c6a720e5998599e2fbabb..090e2e2c066f5e6a2e5fec886762e372fd12f327 100644 (file)
@@ -45,6 +45,14 @@ export default class Configuration {
     return Configuration.getConfig().workerPoolSize;
   }
 
+  static getChargingStationsPerWorker(): number {
+    return Configuration.objectHasOwnProperty(Configuration.getConfig(), 'chargingStationsPerWorker') ? Configuration.getConfig().chargingStationsPerWorker : 1;
+  }
+
+  static getChargingStationIdSuffix(): string {
+    return Configuration.objectHasOwnProperty(Configuration.getConfig(), 'chargingStationIdSuffix') ? Configuration.getConfig().chargingStationIdSuffix : '';
+  }
+
   static getLogConsole(): boolean {
     Configuration.deprecateConfigurationKey('consoleLog', 'Use \'logConsole\' instead');
     return Configuration.objectHasOwnProperty(Configuration.getConfig(), 'logConsole') ? Configuration.getConfig().logConsole : false;
index 7afe4cac1b5ba0ea973b0c2858be8dd208b6ea7e..6e5eb6972441ecb4109906591d1010c50ed5f2c9 100644 (file)
@@ -34,4 +34,6 @@ export default class Constants {
   static readonly CHARGING_STATION_ATG_WAIT_TIME = 2000; // Ms
 
   static readonly TRANSACTION_DEFAULT_IDTAG = '00000000';
+
+  static readonly START_NEW_CHARGING_STATION = 'startNewChargingStation';
 }