Performance statistics: add JSON file storage support.
authorJérôme Benoit <jerome.benoit@sap.com>
Wed, 25 Aug 2021 20:42:45 +0000 (22:42 +0200)
committerJérôme Benoit <jerome.benoit@sap.com>
Wed, 25 Aug 2021 20:42:45 +0000 (22:42 +0200)
Signed-off-by: Jérôme Benoit <jerome.benoit@piment-noir.org>
16 files changed:
.gitignore
README.md
mta.yaml [deleted file]
package-lock.json
package.json
src/assets/config-template.json
src/types/ConfigurationData.ts
src/types/Statistics.ts
src/types/Storage.ts [new file with mode: 0644]
src/utils/Configuration.ts
src/utils/FileUtils.ts
src/utils/PerformanceStatistics.ts
src/utils/performance-storage/JSONFileStorage.ts [new file with mode: 0644]
src/utils/performance-storage/MongoDBStorage.ts [new file with mode: 0644]
src/utils/performance-storage/Storage.ts [new file with mode: 0644]
src/utils/performance-storage/StorageFactory.ts [new file with mode: 0644]

index 5dfe331fa4d91000fc72e23bd62721875b4f9757..16320497751f89e045dda5ac75793b4b1e4048c8 100644 (file)
@@ -90,3 +90,4 @@ Thumbs.db
 mta_archives/
 
 *.tar.gz
+performanceMeasurements.json
index 407c160eef46fb2fa1e71e50cad8429a8df38a21..2aed81736da3ba4cab922738e83ee6c5f8be1f8f 100644 (file)
--- a/README.md
+++ b/README.md
@@ -22,13 +22,13 @@ Key | Value(s) | Default Value | Value type | Description
 --- | -------| --------------| ---------- | ------------
 supervisionURLs | | [] | string[] |  array of connection URIs to OCPP-J servers
 distributeStationsToTenantsEqually | true/false | true | boolean | distribute charging stations uniformly to the OCPP-J servers
-statisticsDisplayInterval | | 60 | integer | seconds between charging stations statistics output in the logs 
 workerProcess | workerSet/staticPool/dynamicPool | workerSet | string | worker threads process type
 workerStartDelay | | 500 | integer | milliseconds to wait at charging station worker threads startup
 workerPoolMinSize | | 4 | integer | worker threads pool minimum number of threads
 workerPoolMaxSize | | 16 | integer | worker threads pool maximum number of threads
 workerPoolStrategy | ROUND_ROBIN/LESS_RECENTLY_USED/... | [poolifier](https://github.com/poolifier/poolifier) default: ROUND_ROBBIN | string | worker threads pool [poolifier](https://github.com/poolifier/poolifier) worker choice strategy
 chargingStationsPerWorker | | 1 | integer | number of charging stations per worker threads for the `workerSet` process type
+logStatisticsInterval | | 60 | integer | seconds between charging stations statistics output in the logs 
 logConsole | true/false | false | boolean | output logs on the console 
 logFormat | | simple | string | winston log format
 logRotate | true/false | true | boolean | enable daily log files rotation
@@ -36,7 +36,8 @@ logMaxFiles | | 7 | integer | maximum number of log files to keep
 logLevel | emerg/alert/crit/error/warning/notice/info/debug | info | string | winston logging level
 logFile | | combined.log | string | log file relative path
 logErrorFile | | error.log | string | error log file relative path 
-stationTemplateURLs | | {}[] | { file: string; numberOfStations: number; }[] | array of charging template file URIs
+performanceStorage | | { "enabled": false, "type": "jsonfile", "file:///performanceMeasurements.json" } | { enabled: string; type: string; URI: string; } | performance storage configuration section
+stationTemplateURLs | | {}[] | { file: string; numberOfStations: number; }[] | array of charging station templates URIs configuration section (template file name and number of stations)
  
 ### Charging station template
 
@@ -77,9 +78,9 @@ meteringPerTransaction | true/false | true | boolean | enable metering history o
 transactionDataMeterValues | true/false | false | boolean | enable transaction data MeterValues at stop transaction
 mainVoltageMeterValues | true/false | true | boolean | include charging station main voltage MeterValues on three phased charging stations
 phaseLineToLineVoltageMeterValues | true/false | true | boolean | include charging station line to line voltage MeterValues on three phased charging stations
-Configuration | | | ChargingStationConfiguration | charging stations OCPP configuration parameters
-AutomaticTransactionGenerator | | | AutomaticTransactionGenerator | charging stations ATG configuration
-Connectors | | | Connectors | charging stations connectors configuration
+Configuration | | | ChargingStationConfiguration | charging stations OCPP parameters configuration section
+AutomaticTransactionGenerator | | | AutomaticTransactionGenerator | charging stations ATG configuration section
+Connectors | | | Connectors | charging stations connectors configuration section
 
 #### Configuration section
 
diff --git a/mta.yaml b/mta.yaml
deleted file mode 100644 (file)
index 6fa9350..0000000
--- a/mta.yaml
+++ /dev/null
@@ -1,20 +0,0 @@
-ID: ev-simulator
-_schema-version: '3.3'
-version: 0.0.1
-
-build-parameters:
-  before-all:
-    - builder: custom
-      commands:
-        - npm install
-        - npm run build
-
-resources:
-  - name: dynatrace-service
-    type: user-provided-service
-    optional: true
-
-modules:
-  - name: ev-simulator
-    type: nodejs
-    path: dist
index 36735fc72374188d78d4774186aeee3c97ec821e..19343c08939cd85606054db86055772bdca3f22d 100644 (file)
       }
     },
     "@es-joy/jsdoccomment": {
-      "version": "0.10.7",
-      "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.10.7.tgz",
-      "integrity": "sha512-aNKZEoMESDzOBjKxCWrFuG50mcpMeKVBnBNko4+IZZ5t9zXYs8GT1KB0ZaOq1YUsKumDRc6YII/TQm309MJ0KQ==",
+      "version": "0.10.8",
+      "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.10.8.tgz",
+      "integrity": "sha512-3P1JiGL4xaR9PoTKUHa2N/LKwa2/eUdRqGwijMWWgBqbFEqJUVpmaOi2TcjcemrsRMgFLBzQCK4ToPhrSVDiFQ==",
       "dev": true,
       "requires": {
-        "comment-parser": "1.2.3",
+        "comment-parser": "1.2.4",
         "esquery": "^1.4.0",
         "jsdoc-type-pratt-parser": "1.1.1"
       }
       "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==",
       "dev": true
     },
+    "@types/json5": {
+      "version": "0.0.29",
+      "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
+      "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=",
+      "dev": true
+    },
     "@types/keyv": {
       "version": "3.1.2",
       "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.2.tgz",
       "integrity": "sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ==",
       "dev": true
     },
-    "binary": {
-      "version": "0.3.0",
-      "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz",
-      "integrity": "sha1-n2BVO8XOjDOG87VTz/R0Yq3sqnk=",
-      "dev": true,
-      "requires": {
-        "buffers": "~0.1.1",
-        "chainsaw": "~0.1.0"
-      }
-    },
     "binary-extensions": {
       "version": "2.2.0",
       "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
       "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
       "dev": true
     },
-    "binwrap": {
-      "version": "0.2.2",
-      "resolved": "https://registry.npmjs.org/binwrap/-/binwrap-0.2.2.tgz",
-      "integrity": "sha512-Y+Wvypk3JhH5GPZAvlwJAWOVH/OsOhQMSj37vySuWHwQivoALplPxfBA8b973rFJI7OS+O+1YmmYXIiEXVMAcw==",
-      "dev": true,
-      "requires": {
-        "mustache": "^3.0.1",
-        "request": "^2.88.0",
-        "request-promise": "^4.2.4",
-        "tar": "^4.4.10",
-        "unzip-stream": "^0.3.0"
-      },
-      "dependencies": {
-        "mkdirp": {
-          "version": "0.5.5",
-          "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
-          "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
-          "dev": true,
-          "requires": {
-            "minimist": "^1.2.5"
-          }
-        },
-        "safe-buffer": {
-          "version": "5.2.1",
-          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
-          "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
-          "dev": true
-        },
-        "tar": {
-          "version": "4.4.17",
-          "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.17.tgz",
-          "integrity": "sha512-q7OwXq6NTdcYIa+k58nEMV3j1euhDhGCs/VRw9ymx/PbH0jtIM2+VTgDE/BW3rbLkrBUXs5fzEKgic5oUciu7g==",
-          "dev": true,
-          "requires": {
-            "chownr": "^1.1.4",
-            "fs-minipass": "^1.2.7",
-            "minipass": "^2.9.0",
-            "minizlib": "^1.3.3",
-            "mkdirp": "^0.5.5",
-            "safe-buffer": "^5.2.1",
-            "yallist": "^3.1.1"
-          }
-        },
-        "yallist": {
-          "version": "3.1.1",
-          "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
-          "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
-          "dev": true
-        }
-      }
-    },
     "bit-twiddle": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/bit-twiddle/-/bit-twiddle-1.0.2.tgz",
         }
       }
     },
-    "bluebird": {
-      "version": "3.7.2",
-      "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
-      "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
-      "dev": true
-    },
     "bn.js": {
       "version": "5.2.0",
       "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz",
       }
     },
     "bson": {
-      "version": "4.4.1",
-      "resolved": "https://registry.npmjs.org/bson/-/bson-4.4.1.tgz",
-      "integrity": "sha512-Uu4OCZa0jouQJCKOk1EmmyqtdWAP5HVLru4lQxTwzJzxT+sJ13lVpEZU/MATDxtHiekWMAL84oQY3Xn1LpJVSg==",
+      "version": "4.5.1",
+      "resolved": "https://registry.npmjs.org/bson/-/bson-4.5.1.tgz",
+      "integrity": "sha512-XqFP74pbTVLyLy5KFxVfTUyRrC1mgOlmu/iXHfXqfCKT59jyP9lwbotGfbN59cHBRbJSamZNkrSopjv+N0SqAA==",
       "requires": {
         "buffer": "^5.6.0"
       },
       "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=",
       "dev": true
     },
-    "buffers": {
-      "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz",
-      "integrity": "sha1-skV5w77U1tOWru5tmorn9Ugqt7s=",
-      "dev": true
-    },
     "bufferutil": {
       "version": "4.0.3",
       "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.3.tgz",
       "integrity": "sha512-twuUuJRrIrsELHz6foJtZlqrz6FC36zoHZJvvThsrM1UWPKxyoilw1Rka6Hk0AmPFKHKUoGwGfAtvNZNtNZu0g==",
       "dev": true
     },
-    "chainsaw": {
-      "version": "0.1.0",
-      "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz",
-      "integrity": "sha1-XqtQsor+WAdNDVgpE4iCi15fvJg=",
-      "dev": true,
-      "requires": {
-        "traverse": ">=0.3.0 <0.4"
-      }
-    },
     "chalk": {
       "version": "2.4.2",
       "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
       }
     },
     "chownr": {
-      "version": "1.1.4",
-      "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
-      "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
-      "dev": true
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
+      "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ=="
     },
     "ci-info": {
       "version": "1.6.0",
       "dev": true
     },
     "comment-parser": {
-      "version": "1.2.3",
-      "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.2.3.tgz",
-      "integrity": "sha512-vnqDwBSXSsdAkGS5NjwMIPelE47q+UkEgWKHvCDNhVIIaQSUFY6sNnEYGzdoPGMdpV+7KR3ZkRd7oyWIjtuvJg==",
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.2.4.tgz",
+      "integrity": "sha512-pm0b+qv+CkWNriSTMsfnjChF9kH0kxz55y44Wo5le9qLxMj5xDQAaEd9ZN1ovSuk9CsrncWaFwgpOMg7ClJwkw==",
       "dev": true
     },
     "commist": {
       "dev": true
     },
     "denque": {
-      "version": "1.5.0",
-      "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.0.tgz",
-      "integrity": "sha512-CYiCSgIF1p6EUByQPlGkKnP1M9g0ZV3qMIrqMqZqdwazygIA/YP2vrbcyl1h/WppKJTdl1F85cXIle+394iDAQ=="
+      "version": "1.5.1",
+      "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz",
+      "integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw=="
     },
     "depcheck": {
       "version": "0.8.3",
       }
     },
     "eslint-plugin-import": {
-      "version": "2.24.1",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.24.1.tgz",
-      "integrity": "sha512-KSFWhNxPH8OGJwpRJJs+Z7I0a13E2iFQZJIvSnCu6KUs4qmgAm3xN9GYBCSoiGWmwA7gERZPXqYQjcoCROnYhQ==",
+      "version": "2.24.2",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.24.2.tgz",
+      "integrity": "sha512-hNVtyhiEtZmpsabL4neEj+6M5DCLgpYyG9nzJY8lZQeQXEn5UPW1DpUdsMHMXsq98dbNm7nt1w9ZMSVpfJdi8Q==",
       "dev": true,
       "requires": {
         "array-includes": "^3.1.3",
         "pkg-up": "^2.0.0",
         "read-pkg-up": "^3.0.0",
         "resolve": "^1.20.0",
-        "tsconfig-paths": "^3.10.1"
+        "tsconfig-paths": "^3.11.0"
       },
       "dependencies": {
         "debug": {
       }
     },
     "eslint-plugin-jsdoc": {
-      "version": "36.0.7",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-36.0.7.tgz",
-      "integrity": "sha512-x73l/WCRQ1qCjLq46Ca7csuGd5o3y3vbJIa3cktg11tdf3UZleBdIXKN9Cf0xjs3tXYPEy2SoNXowT8ydnjNDQ==",
+      "version": "36.0.8",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-36.0.8.tgz",
+      "integrity": "sha512-brNjHvRuBy5CaV01mSp6WljrO/T8fHNj0DXG38odOGDnhI7HdcbLKX7DpSvg2Rfcifwh8GlnNFzx13sI05t3bg==",
       "dev": true,
       "requires": {
-        "@es-joy/jsdoccomment": "0.10.7",
-        "comment-parser": "1.2.3",
+        "@es-joy/jsdoccomment": "0.10.8",
+        "comment-parser": "1.2.4",
         "debug": "^4.3.2",
         "esquery": "^1.4.0",
         "jsdoc-type-pratt-parser": "^1.1.1",
       }
     },
     "fs-minipass": {
-      "version": "1.2.7",
-      "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz",
-      "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==",
-      "dev": true,
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
+      "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
       "requires": {
-        "minipass": "^2.6.0"
+        "minipass": "^3.0.0"
       }
     },
     "fs.realpath": {
     "lodash": {
       "version": "4.17.21",
       "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
-      "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
+      "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
+      "dev": true
     },
     "lodash.clonedeep": {
       "version": "4.5.0",
       "integrity": "sha1-XZXBqGDRSFNSl4MqFX5L5Z6YgNQ=",
       "dev": true
     },
-    "mbt": {
-      "version": "1.2.3",
-      "resolved": "https://registry.npmjs.org/mbt/-/mbt-1.2.3.tgz",
-      "integrity": "sha512-gQuq1yYKUNg4IVVBsJ35YVqIHJOaKY2FMoselaZpHA6rxU0A6kijyBrHsT82uAXBoIUUQyxZcLfa6az0XJPcbA==",
-      "dev": true,
-      "requires": {
-        "binwrap": "0.2.2"
-      }
-    },
     "md5.js": {
       "version": "1.3.5",
       "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
       "dev": true
     },
     "minipass": {
-      "version": "2.9.0",
-      "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz",
-      "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==",
-      "dev": true,
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz",
+      "integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==",
       "requires": {
-        "safe-buffer": "^5.1.2",
-        "yallist": "^3.0.0"
+        "yallist": "^4.0.0"
       },
       "dependencies": {
         "yallist": {
-          "version": "3.1.1",
-          "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
-          "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
-          "dev": true
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+          "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
         }
       }
     },
     "minizlib": {
-      "version": "1.3.3",
-      "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz",
-      "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==",
-      "dev": true,
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
+      "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
       "requires": {
-        "minipass": "^2.9.0"
+        "minipass": "^3.0.0",
+        "yallist": "^4.0.0"
+      },
+      "dependencies": {
+        "yallist": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+          "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
+        }
       }
     },
     "mkdirp": {
       "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ=="
     },
     "mongodb": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.1.0.tgz",
-      "integrity": "sha512-Gx9U9MsFWgJ3E0v4oHAdWvYTGBznNYPCkhmD/3i/kPTY/URnPfHD5/6VoKUFrdgQTK3icFiM9976hVbqCRBO9Q==",
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.1.1.tgz",
+      "integrity": "sha512-fbACrWEyvr6yl0sSiCGV0sqEiBwTtDJ8iSojmkDjAfw9JnOZSAkUyv9seFSPYhPPKwxp1PDtyjvBNfMDz0WBLQ==",
       "requires": {
-        "bson": "^4.4.0",
+        "bson": "^4.5.1",
         "denque": "^1.5.0",
-        "mongodb-connection-string-url": "^1.0.1",
+        "mongodb-connection-string-url": "^2.0.0",
         "saslprep": "^1.0.0"
       }
     },
     "mongodb-connection-string-url": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-1.1.2.tgz",
-      "integrity": "sha512-mp5lv4guWuykOpkwNNqQ0tKKytuJUjL/aC/bu/DqoJVWL5NSh4j/u+gJ+EiOdweLujHyq6JZZqcTVipHhL5xRg==",
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.0.0.tgz",
+      "integrity": "sha512-M0I1vyLoq5+HQTuPSJWbt+hIXsMCfE8sS1fS5mvP9R2DOMoi2ZD32yWqgBIITyu0dFu4qtS50erxKjvUeBiyog==",
       "requires": {
-        "@types/whatwg-url": "^8.0.0",
-        "whatwg-url": "^8.4.0"
+        "@types/whatwg-url": "^8.2.1",
+        "whatwg-url": "^9.1.0"
       }
     },
     "morphdom": {
       "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
       "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
     },
-    "mustache": {
-      "version": "3.2.1",
-      "resolved": "https://registry.npmjs.org/mustache/-/mustache-3.2.1.tgz",
-      "integrity": "sha512-RERvMFdLpaFfSRIEe632yDm5nsd0SDKn8hGmcUwswnyiE5mtdZLDybtHAz6hjJhawokF0hXvGLtx9mrQfm6FkA==",
-      "dev": true
-    },
     "mute-stream": {
       "version": "0.0.7",
       "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz",
         }
       }
     },
-    "request-promise": {
-      "version": "4.2.6",
-      "resolved": "https://registry.npmjs.org/request-promise/-/request-promise-4.2.6.tgz",
-      "integrity": "sha512-HCHI3DJJUakkOr8fNoCc73E5nU5bqITjOYFMDrKHYOXWXrgD/SBaC7LjwuPymUprRyuF06UK7hd/lMHkmUXglQ==",
-      "dev": true,
-      "requires": {
-        "bluebird": "^3.5.0",
-        "request-promise-core": "1.1.4",
-        "stealthy-require": "^1.1.1",
-        "tough-cookie": "^2.3.3"
-      },
-      "dependencies": {
-        "tough-cookie": {
-          "version": "2.5.0",
-          "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
-          "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
-          "dev": true,
-          "requires": {
-            "psl": "^1.1.28",
-            "punycode": "^2.1.1"
-          }
-        }
-      }
-    },
-    "request-promise-core": {
-      "version": "1.1.4",
-      "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz",
-      "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==",
-      "dev": true,
-      "requires": {
-        "lodash": "^4.17.19"
-      }
-    },
     "require-directory": {
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
         "through2": "~2.0.3"
       }
     },
-    "stealthy-require": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz",
-      "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=",
-      "dev": true
-    },
     "stream-browserify": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz",
         "yallist": "^4.0.0"
       },
       "dependencies": {
-        "chownr": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
-          "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ=="
-        },
-        "fs-minipass": {
-          "version": "2.1.0",
-          "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
-          "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
-          "requires": {
-            "minipass": "^3.0.0"
-          }
-        },
-        "minipass": {
-          "version": "3.1.3",
-          "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz",
-          "integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==",
-          "requires": {
-            "yallist": "^4.0.0"
-          }
-        },
-        "minizlib": {
-          "version": "2.1.2",
-          "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
-          "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
-          "requires": {
-            "minipass": "^3.0.0",
-            "yallist": "^4.0.0"
-          }
-        },
         "yallist": {
           "version": "4.0.0",
           "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
         }
       }
     },
-    "traverse": {
-      "version": "0.3.9",
-      "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz",
-      "integrity": "sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk=",
-      "dev": true
-    },
     "trim-newlines": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz",
       }
     },
     "tsconfig-paths": {
-      "version": "3.10.1",
-      "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.10.1.tgz",
-      "integrity": "sha512-rETidPDgCpltxF7MjBZlAFPUHv5aHH2MymyPvh+vEyWAED4Eb/WeMbsnD/JDr4OKPOA1TssDHgIcpTN5Kh0p6Q==",
+      "version": "3.11.0",
+      "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.11.0.tgz",
+      "integrity": "sha512-7ecdYDnIdmv639mmDwslG6KQg1Z9STTz1j7Gcz0xa+nshh/gKDAHcPxRbWOsA3SPp0tXP2leTcY9Kw+NAkfZzA==",
       "dev": true,
       "requires": {
-        "json5": "^2.2.0",
+        "@types/json5": "^0.0.29",
+        "json5": "^1.0.1",
         "minimist": "^1.2.0",
         "strip-bom": "^3.0.0"
+      },
+      "dependencies": {
+        "json5": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
+          "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
+          "dev": true,
+          "requires": {
+            "minimist": "^1.2.0"
+          }
+        }
       }
     },
     "tslib": {
       "integrity": "sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c=",
       "dev": true
     },
-    "unzip-stream": {
-      "version": "0.3.1",
-      "resolved": "https://registry.npmjs.org/unzip-stream/-/unzip-stream-0.3.1.tgz",
-      "integrity": "sha512-RzaGXLNt+CW+T41h1zl6pGz3EaeVhYlK+rdAap+7DxW5kqsqePO8kRtWPaCiVqdhZc86EctSPVYNix30YOMzmw==",
-      "dev": true,
-      "requires": {
-        "binary": "^0.3.0",
-        "mkdirp": "^0.5.1"
-      },
-      "dependencies": {
-        "mkdirp": {
-          "version": "0.5.5",
-          "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
-          "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
-          "dev": true,
-          "requires": {
-            "minimist": "^1.2.5"
-          }
-        }
-      }
-    },
     "update-notifier": {
       "version": "2.5.0",
       "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-2.5.0.tgz",
       "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w=="
     },
     "whatwg-url": {
-      "version": "8.7.0",
-      "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz",
-      "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==",
+      "version": "9.1.0",
+      "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-9.1.0.tgz",
+      "integrity": "sha512-CQ0UcrPHyomtlOCot1TL77WyMIm/bCwrJ2D6AOKGwEczU9EpyoqAokfqrf/MioU9kHcMsmJZcg1egXix2KYEsA==",
       "requires": {
-        "lodash": "^4.7.0",
         "tr46": "^2.1.0",
         "webidl-conversions": "^6.1.0"
       }
index d12b10592072268cf6aca388ad128c5c490d3922..afb93a9fd2bca5f01c986d9d0ae92b400f3cd82c 100644 (file)
@@ -64,7 +64,7 @@
   },
   "dependencies": {
     "basic-ftp": "^4.6.6",
-    "mongodb": "^4.1.0",
+    "mongodb": "^4.1.1",
     "poolifier": "^2.0.2",
     "source-map-support": "^0.5.19",
     "tar": "^6.1.10",
     "clinic": "^9.0.0",
     "cross-env": "^7.0.3",
     "eslint": "^7.32.0",
-    "eslint-plugin-import": "^2.24.1",
-    "eslint-plugin-jsdoc": "^36.0.7",
+    "eslint-plugin-import": "^2.24.2",
+    "eslint-plugin-jsdoc": "^36.0.8",
     "eslint-plugin-node": "^11.1.0",
     "expect": "^27.0.6",
-    "mbt": "^1.2.3",
     "mocha": "^9.1.0",
     "mochawesome": "^6.2.2",
     "npm-check": "^5.9.2",
index 0bc2c30924ec67a35d15f269f9e01ee041570f34..0267c1a7fc294d83dd5ff684ea7734d2523c621d 100644 (file)
@@ -3,7 +3,9 @@
     "ws://localhost:8010/OCPP16/5be7fb271014d90008992f06"
   ],
   "distributeStationsToTenantsEqually": true,
-  "statisticsDisplayInterval": 60,
+  "performanceStorage": {
+    "enabled": true
+  },
   "chargingStationsPerWorker": 1,
   "workerProcess": "workerSet",
   "workerPoolMinSize": 4,
@@ -30,6 +32,7 @@
       "numberOfStations": 1
     }
   ],
+  "logStatisticsInterval": 0,
   "logFile": "combined.log",
   "logErrorFile": "error.log",
   "logConsole": false
index 91437807a629363c6df60260c4397fe1d4916726..1a2dd165113188d52690eafb4ed5d4f64da553d5 100644 (file)
@@ -1,3 +1,4 @@
+import { StorageType } from './Storage';
 import type { WorkerChoiceStrategy } from 'poolifier';
 import { WorkerProcessType } from './Worker';
 
@@ -6,10 +7,16 @@ export interface StationTemplateURL {
   numberOfStations: number;
 }
 
+export interface StorageConfiguration {
+  enabled?: boolean;
+  type?: StorageType;
+  URI?: string;
+}
+
 export default interface ConfigurationData {
   supervisionURLs?: string[];
   stationTemplateURLs: StationTemplateURL[];
-  statisticsDisplayInterval?: number;
+  performanceStorage?: StorageConfiguration;
   autoReconnectMaxRetries?: number;
   distributeStationsToTenantsEqually?: boolean;
   workerProcess?: WorkerProcessType;
@@ -18,6 +25,7 @@ export default interface ConfigurationData {
   workerPoolMaxSize?: number;
   workerPoolStrategy?: WorkerChoiceStrategy;
   chargingStationsPerWorker?: number;
+  logStatisticsInterval?: number;
   logFormat?: string;
   logLevel?: string;
   logRotate?: boolean;
index f6f17ca2d0301eb9d42d8e66e65bfee2b1501e55..7999e14ae86074a0f4a8ac798e1da9c3f835cd58 100644 (file)
@@ -18,5 +18,7 @@ export interface StatisticsData {
 
 export default interface Statistics {
   id: string;
+  createdAt: Date;
+  lastUpdatedAt?: Date;
   statisticsData: Record<string, StatisticsData>;
 }
diff --git a/src/types/Storage.ts b/src/types/Storage.ts
new file mode 100644 (file)
index 0000000..32a6b22
--- /dev/null
@@ -0,0 +1,5 @@
+export enum StorageType {
+  JSON_FILE = 'jsonfile',
+  MONGO_DB = 'mongodb'
+}
+
index a65e6c995b999d3e62527fc7b5e09d98fd5b6b6b..8be9a1da92d54a242ac06d4eb882045cf71d0963 100644 (file)
@@ -1,6 +1,7 @@
-import ConfigurationData, { StationTemplateURL } from '../types/ConfigurationData';
+import ConfigurationData, { StationTemplateURL, StorageConfiguration } from '../types/ConfigurationData';
 
 import Constants from './Constants';
+import { StorageType } from '../types/Storage';
 import type { WorkerChoiceStrategy } from 'poolifier';
 import { WorkerProcessType } from '../types/Worker';
 import fs from 'fs';
@@ -16,9 +17,30 @@ export default class Configuration {
     Configuration.configurationChangeCallback = cb;
   }
 
-  static getStatisticsDisplayInterval(): number {
+  static getLogStatisticsInterval(): number {
+    Configuration.deprecateConfigurationKey('statisticsDisplayInterval', 'Use \'logStatisticsInterval\' instead');
     // Read conf
-    return Configuration.objectHasOwnProperty(Configuration.getConfig(), 'statisticsDisplayInterval') ? Configuration.getConfig().statisticsDisplayInterval : 60;
+    return Configuration.objectHasOwnProperty(Configuration.getConfig(), 'logStatisticsInterval') ? Configuration.getConfig().logStatisticsInterval : 60;
+  }
+
+  static getPerformanceStorage(): StorageConfiguration {
+    let storageConfiguration: StorageConfiguration;
+    if (Configuration.objectHasOwnProperty(Configuration.getConfig(), 'performanceStorage')) {
+      storageConfiguration =
+      {
+        ...Configuration.objectHasOwnProperty(Configuration.getConfig().performanceStorage, 'enabled') ? { enabled: Configuration.getConfig().performanceStorage.enabled } : { enabled: false },
+        ...Configuration.objectHasOwnProperty(Configuration.getConfig().performanceStorage, 'type') ? { type: Configuration.getConfig().performanceStorage.type } : { type: StorageType.JSON_FILE },
+        ...Configuration.objectHasOwnProperty(Configuration.getConfig().performanceStorage, 'URI') ? { URI: Configuration.getConfig().performanceStorage.URI } : { URI: 'file:///performanceMeasurements.json' }
+      };
+    } else {
+      storageConfiguration =
+      {
+        enabled: false,
+        type: StorageType.JSON_FILE,
+        URI: 'file:///performanceMeasurements.json'
+      };
+    }
+    return storageConfiguration;
   }
 
   static getAutoReconnectMaxRetries(): number {
@@ -159,8 +181,12 @@ export default class Configuration {
     const prefix = logPrefix.length !== 0 ? logPrefix + ' ' : '';
     if (error.code === 'ENOENT') {
       console.error(prefix + fileType + ' file ' + filePath + ' not found: ', error);
+    } else if (error.code === 'EEXIST') {
+      console.error(prefix + fileType + ' file ' + filePath + ' already exists: ', error);
+    } else if (error.code === 'EACCES') {
+      console.error(prefix + fileType + ' file ' + filePath + ' access denied: ', error);
     } else {
-      console.error(prefix + fileType + ' file ' + filePath + ' opening error: ', error);
+      console.error(prefix + fileType + ' file ' + filePath + ' error: ', error);
     }
     throw error;
   }
index c0891e646a1da0242b31da66375a0251f733cb0f..b3940b718e2a2c0240808b0ee8aaf807b5707bfe 100644 (file)
@@ -9,11 +9,23 @@ export default class FileUtils {
       } else {
         logger.warn(prefix + fileType + ' file ' + filePath + ' not found: %j', error);
       }
+    } else if (error.code === 'EEXIST') {
+      if (consoleOut) {
+        console.warn(prefix + fileType + ' file ' + filePath + ' already exists: ', error);
+      } else {
+        logger.warn(prefix + fileType + ' file ' + filePath + ' already exists: %j', error);
+      }
+    } else if (error.code === 'EACCES') {
+      if (consoleOut) {
+        console.warn(prefix + fileType + ' file ' + filePath + ' access denied: ', error);
+      } else {
+        logger.warn(prefix + fileType + ' file ' + filePath + ' access denied: %j', error);
+      }
     } else {
       if (consoleOut) {
-        console.error(prefix + fileType + ' file ' + filePath + ' opening error: ', error);
+        console.error(prefix + fileType + ' file ' + filePath + ' error: ', error);
       } else {
-        logger.error(prefix + fileType + ' file ' + filePath + ' opening error: %j', error);
+        logger.error(prefix + fileType + ' file ' + filePath + ' error: %j', error);
       }
       throw error;
     }
index 8c536f85a669bd8b39f02b801884401631191d6d..02b2854e185d7bc794cb4ba5184908366e19141e 100644 (file)
@@ -7,10 +7,13 @@ import Statistics, { StatisticsData } from '../types/Statistics';
 
 import Configuration from './Configuration';
 import { MessageType } from '../types/ocpp/MessageType';
+import { Storage } from './performance-storage/Storage';
+import { StorageFactory } from './performance-storage/StorageFactory';
 import Utils from './Utils';
 import logger from './Logger';
 
 export default class PerformanceStatistics {
+  private static storage: Storage;
   private objId: string;
   private performanceObserver: PerformanceObserver;
   private statistics: Statistics;
@@ -19,7 +22,7 @@ export default class PerformanceStatistics {
   public constructor(objId: string) {
     this.objId = objId;
     this.initializePerformanceObserver();
-    this.statistics = { id: this.objId ?? 'Object id not specified', statisticsData: {} };
+    this.statistics = { id: this.objId ?? 'Object id not specified', createdAt: new Date(), statisticsData: {} };
   }
 
   public static beginMeasure(id: string): string {
@@ -74,7 +77,10 @@ export default class PerformanceStatistics {
   }
 
   public start(): void {
-    this.startDisplayInterval();
+    this.startLogStatisticsInterval();
+    if (Configuration.getPerformanceStorage().enabled) {
+      logger.info(`${this.logPrefix()} storage enabled: type ${Configuration.getPerformanceStorage().type}, URI: ${Configuration.getPerformanceStorage().URI}`);
+    }
   }
 
   public stop(): void {
@@ -102,14 +108,14 @@ export default class PerformanceStatistics {
     logger.info(this.logPrefix() + ' %j', this.statistics);
   }
 
-  private startDisplayInterval(): void {
-    if (Configuration.getStatisticsDisplayInterval() > 0) {
+  private startLogStatisticsInterval(): void {
+    if (Configuration.getLogStatisticsInterval() > 0) {
       this.displayInterval = setInterval(() => {
         this.logStatistics();
-      }, Configuration.getStatisticsDisplayInterval() * 1000);
-      logger.info(this.logPrefix() + ' displayed every ' + Utils.secondsToHHMMSS(Configuration.getStatisticsDisplayInterval()));
+      }, Configuration.getLogStatisticsInterval() * 1000);
+      logger.info(this.logPrefix() + ' logged every ' + Utils.secondsToHHMMSS(Configuration.getLogStatisticsInterval()));
     } else {
-      logger.info(this.logPrefix() + ' display interval is set to ' + Configuration.getStatisticsDisplayInterval().toString() + '. Not displaying statistics');
+      logger.info(this.logPrefix() + ' log interval is set to ' + Configuration.getLogStatisticsInterval().toString() + '. Not logging statistics');
     }
   }
 
@@ -173,6 +179,7 @@ export default class PerformanceStatistics {
       this.statistics.statisticsData[entryName] = {} as StatisticsData;
     }
     // Update current statistics
+    this.statistics.lastUpdatedAt = new Date();
     this.statistics.statisticsData[entryName].countTimeMeasurement = this.statistics.statisticsData[entryName].countTimeMeasurement ? this.statistics.statisticsData[entryName].countTimeMeasurement + 1 : 1;
     this.statistics.statisticsData[entryName].currentTimeMeasurement = entry.duration;
     this.statistics.statisticsData[entryName].minTimeMeasurement = this.statistics.statisticsData[entryName].minTimeMeasurement ? (this.statistics.statisticsData[entryName].minTimeMeasurement > entry.duration ? entry.duration : this.statistics.statisticsData[entryName].minTimeMeasurement) : entry.duration;
@@ -183,9 +190,19 @@ export default class PerformanceStatistics {
     this.statistics.statisticsData[entryName].medTimeMeasurement = this.median(this.statistics.statisticsData[entryName].timeMeasurementSeries);
     this.statistics.statisticsData[entryName].ninetyFiveThPercentileTimeMeasurement = this.percentile(this.statistics.statisticsData[entryName].timeMeasurementSeries, 95);
     this.statistics.statisticsData[entryName].stdDevTimeMeasurement = this.stdDeviation(this.statistics.statisticsData[entryName].timeMeasurementSeries);
+    if (Configuration.getPerformanceStorage().enabled) {
+      this.getStorage().storePerformanceStatistics(this.statistics);
+    }
   }
 
   private logPrefix(): string {
     return Utils.logPrefix(` ${this.objId} | Performance statistics`);
   }
+
+  private getStorage(): Storage {
+    if (!PerformanceStatistics.storage) {
+      PerformanceStatistics.storage = StorageFactory.getStorage(Configuration.getPerformanceStorage().type ,Configuration.getPerformanceStorage().URI, this.logPrefix());
+    }
+    return PerformanceStatistics.storage;
+  }
 }
diff --git a/src/utils/performance-storage/JSONFileStorage.ts b/src/utils/performance-storage/JSONFileStorage.ts
new file mode 100644 (file)
index 0000000..af709d9
--- /dev/null
@@ -0,0 +1,20 @@
+import FileUtils from '../FileUtils';
+import Statistics from '../../types/Statistics';
+import { Storage } from './Storage';
+import fs from 'fs';
+import path from 'path';
+
+export class JSONFileStorage extends Storage {
+  constructor(storageURI: string, logPrefix: string) {
+    super(storageURI, logPrefix);
+  }
+
+  public storePerformanceStatistics(performanceStatistics: Statistics): void {
+    const performanceJSONFilePath = path.join(path.resolve(__dirname, '../../../'), this.storageURI.pathname.replace(/^\/|\/$/g, ''));
+    fs.appendFile(performanceJSONFilePath, JSON.stringify(performanceStatistics, null, 2), 'utf8', (err) => {
+      if (err) {
+        FileUtils.handleFileException(this.logPrefix, 'Performance measurements', performanceJSONFilePath, err);
+      }
+    });
+  }
+}
diff --git a/src/utils/performance-storage/MongoDBStorage.ts b/src/utils/performance-storage/MongoDBStorage.ts
new file mode 100644 (file)
index 0000000..a47984c
--- /dev/null
@@ -0,0 +1,16 @@
+import Statistics from '../../types/Statistics';
+import { Storage } from './Storage';
+
+export class MongoDBStorage extends Storage {
+  constructor(storageURI: string, logPrefix: string) {
+    super(storageURI, logPrefix);
+  }
+
+  public storePerformanceStatistics(performanceStatistics: Statistics): void {
+    throw new Error('Method not yet implemented');
+  }
+
+  private open(): void {}
+
+  private close(): void {}
+}
diff --git a/src/utils/performance-storage/Storage.ts b/src/utils/performance-storage/Storage.ts
new file mode 100644 (file)
index 0000000..890dce7
--- /dev/null
@@ -0,0 +1,14 @@
+import Statistics from '../../types/Statistics';
+import { URL } from 'url';
+
+export abstract class Storage {
+  protected storageURI: URL;
+  protected logPrefix: string;
+
+  constructor(storageURI: string, logPrefix: string) {
+    this.storageURI = new URL(storageURI);
+    this.logPrefix = logPrefix;
+  }
+
+  public abstract storePerformanceStatistics(performanceStatistics: Statistics): void;
+}
diff --git a/src/utils/performance-storage/StorageFactory.ts b/src/utils/performance-storage/StorageFactory.ts
new file mode 100644 (file)
index 0000000..4d04dee
--- /dev/null
@@ -0,0 +1,25 @@
+import { JSONFileStorage } from './JSONFileStorage';
+import { MongoDBStorage } from './MongoDBStorage';
+import { Storage } from './Storage';
+import { StorageType } from '../../types/Storage';
+import logger from '../Logger';
+
+export class StorageFactory {
+  // eslint-disable-next-line @typescript-eslint/no-empty-function
+  private constructor() {}
+
+  public static getStorage(type: StorageType, connectionURI: string, logPrefix: string): Storage {
+    let storageInstance: Storage = null;
+    switch (type) {
+      case StorageType.JSON_FILE:
+        storageInstance = new JSONFileStorage(connectionURI, logPrefix);
+        break;
+      case StorageType.MONGO_DB:
+        storageInstance = new MongoDBStorage(connectionURI, logPrefix);
+        break;
+      default:
+        logger.error(`${logPrefix} Unknown storage type: ${type}`);
+    }
+    return storageInstance;
+  }
+}