Add MongDB support to storage for performance records.
authorJérôme Benoit <jerome.benoit@sap.com>
Sat, 28 Aug 2021 07:14:11 +0000 (09:14 +0200)
committerJérôme Benoit <jerome.benoit@sap.com>
Sat, 28 Aug 2021 07:14:11 +0000 (09:14 +0200)
Cleanup the storage API

Signed-off-by: Jérôme Benoit <jerome.benoit@sap.com>
15 files changed:
package-lock.json
package.json
rollup.config.js
src/charging-station/Bootstrap.ts
src/charging-station/ChargingStation.ts
src/charging-station/ChargingStationWorker.ts
src/types/Statistics.ts
src/types/Storage.ts
src/types/Worker.ts
src/utils/Constants.ts
src/utils/PerformanceStatistics.ts
src/utils/performance-storage/JSONFileStorage.ts
src/utils/performance-storage/MongoDBStorage.ts
src/utils/performance-storage/Storage.ts
src/worker/WorkerSet.ts

index f8a19cf5c8cbbdb2224b489a8ff2c54879e99189..72215921112972bc915b01733a85e0c678135d33 100644 (file)
       "dev": true
     },
     "@jest/types": {
-      "version": "27.0.6",
-      "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.0.6.tgz",
-      "integrity": "sha512-aSquT1qa9Pik26JK5/3rvnYb4bGtm1VFNesHKmNTwmPIgOrixvhL2ghIvFRNEpzy3gU+rUgjIF/KodbkFAl++g==",
+      "version": "27.1.0",
+      "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.1.0.tgz",
+      "integrity": "sha512-pRP5cLIzN7I7Vp6mHKRSaZD7YpBTK7hawx5si8trMKqk4+WOdK8NEKOTO2G8PKWD1HbKMVckVB6/XHh/olhf2g==",
       "dev": true,
       "requires": {
         "@types/istanbul-lib-coverage": "^2.0.0",
         "@types/node": "*",
         "@types/yargs": "^16.0.0",
         "chalk": "^4.0.0"
-      },
-      "dependencies": {
-        "ansi-styles": {
-          "version": "4.3.0",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
-          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
-          "requires": {
-            "color-convert": "^2.0.1"
-          }
-        },
-        "color-convert": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
-          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
-          "requires": {
-            "color-name": "~1.1.4"
-          }
-        },
-        "color-name": {
-          "version": "1.1.4",
-          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
-          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
-        },
-        "has-flag": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
-          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
-        },
-        "supports-color": {
-          "version": "7.2.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
-          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
-          "requires": {
-            "has-flag": "^4.0.0"
-          }
-        }
       }
     },
     "@nearform/bubbleprof": {
       "dev": true
     },
     "@types/stack-utils": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.0.tgz",
-      "integrity": "sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==",
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz",
+      "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==",
       "dev": true
     },
     "@types/tar": {
       }
     },
     "@types/yargs": {
-      "version": "16.0.3",
-      "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.3.tgz",
-      "integrity": "sha512-YlFfTGS+zqCgXuXNV26rOIeETOkXnGQXP/pjjL9P0gO/EP9jTmc7pUBhx+jVEIxpq41RX33GQ7N3DzOSfZoglQ==",
+      "version": "16.0.4",
+      "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz",
+      "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==",
       "dev": true,
       "requires": {
         "@types/yargs-parser": "*"
       }
     },
     "@types/yargs-parser": {
-      "version": "20.2.0",
-      "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.0.tgz",
-      "integrity": "sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA==",
+      "version": "20.2.1",
+      "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.1.tgz",
+      "integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw==",
       "dev": true
     },
     "@typescript-eslint/eslint-plugin": {
       }
     },
     "expect": {
-      "version": "27.0.6",
-      "resolved": "https://registry.npmjs.org/expect/-/expect-27.0.6.tgz",
-      "integrity": "sha512-psNLt8j2kwg42jGBDSfAlU49CEZxejN1f1PlANWDZqIhBOVU/c2Pm888FcjWJzFewhIsNWfZJeLjUjtKGiPuSw==",
+      "version": "27.1.0",
+      "resolved": "https://registry.npmjs.org/expect/-/expect-27.1.0.tgz",
+      "integrity": "sha512-9kJngV5hOJgkFil4F/uXm3hVBubUK2nERVfvqNNwxxuW8ZOUwSTTSysgfzckYtv/LBzj/LJXbiAF7okHCXgdug==",
       "dev": true,
       "requires": {
-        "@jest/types": "^27.0.6",
+        "@jest/types": "^27.1.0",
         "ansi-styles": "^5.0.0",
         "jest-get-type": "^27.0.6",
-        "jest-matcher-utils": "^27.0.6",
-        "jest-message-util": "^27.0.6",
+        "jest-matcher-utils": "^27.1.0",
+        "jest-message-util": "^27.1.0",
         "jest-regex-util": "^27.0.6"
       },
       "dependencies": {
       }
     },
     "jest-diff": {
-      "version": "27.0.6",
-      "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.0.6.tgz",
-      "integrity": "sha512-Z1mqgkTCSYaFgwTlP/NUiRzdqgxmmhzHY1Tq17zL94morOHfHu3K4bgSgl+CR4GLhpV8VxkuOYuIWnQ9LnFqmg==",
+      "version": "27.1.0",
+      "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.1.0.tgz",
+      "integrity": "sha512-rjfopEYl58g/SZTsQFmspBODvMSytL16I+cirnScWTLkQVXYVZfxm78DFfdIIXc05RCYuGjxJqrdyG4PIFzcJg==",
       "dev": true,
       "requires": {
         "chalk": "^4.0.0",
         "diff-sequences": "^27.0.6",
         "jest-get-type": "^27.0.6",
-        "pretty-format": "^27.0.6"
-      },
-      "dependencies": {
-        "ansi-styles": {
-          "version": "4.3.0",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
-          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
-          "requires": {
-            "color-convert": "^2.0.1"
-          }
-        },
-        "color-convert": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
-          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
-          "requires": {
-            "color-name": "~1.1.4"
-          }
-        },
-        "color-name": {
-          "version": "1.1.4",
-          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
-          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
-        },
-        "has-flag": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
-          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
-        },
-        "supports-color": {
-          "version": "7.2.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
-          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
-          "requires": {
-            "has-flag": "^4.0.0"
-          }
-        }
+        "pretty-format": "^27.1.0"
       }
     },
     "jest-get-type": {
       "dev": true
     },
     "jest-matcher-utils": {
-      "version": "27.0.6",
-      "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.0.6.tgz",
-      "integrity": "sha512-OFgF2VCQx9vdPSYTHWJ9MzFCehs20TsyFi6bIHbk5V1u52zJOnvF0Y/65z3GLZHKRuTgVPY4Z6LVePNahaQ+tA==",
+      "version": "27.1.0",
+      "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.1.0.tgz",
+      "integrity": "sha512-VmAudus2P6Yt/JVBRdTPFhUzlIN8DYJd+et5Rd9QDsO/Z82Z4iwGjo43U8Z+PTiz8CBvKvlb6Fh3oKy39hykkQ==",
       "dev": true,
       "requires": {
         "chalk": "^4.0.0",
-        "jest-diff": "^27.0.6",
+        "jest-diff": "^27.1.0",
         "jest-get-type": "^27.0.6",
-        "pretty-format": "^27.0.6"
-      },
-      "dependencies": {
-        "ansi-styles": {
-          "version": "4.3.0",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
-          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
-          "requires": {
-            "color-convert": "^2.0.1"
-          }
-        },
-        "color-convert": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
-          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
-          "requires": {
-            "color-name": "~1.1.4"
-          }
-        },
-        "color-name": {
-          "version": "1.1.4",
-          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
-          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
-        },
-        "has-flag": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
-          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
-        },
-        "supports-color": {
-          "version": "7.2.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
-          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
-          "requires": {
-            "has-flag": "^4.0.0"
-          }
-        }
+        "pretty-format": "^27.1.0"
       }
     },
     "jest-message-util": {
-      "version": "27.0.6",
-      "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.0.6.tgz",
-      "integrity": "sha512-rBxIs2XK7rGy+zGxgi+UJKP6WqQ+KrBbD1YMj517HYN3v2BG66t3Xan3FWqYHKZwjdB700KiAJ+iES9a0M+ixw==",
+      "version": "27.1.0",
+      "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.1.0.tgz",
+      "integrity": "sha512-Eck8NFnJ5Sg36R9XguD65cf2D5+McC+NF5GIdEninoabcuoOfWrID5qJhufq5FB0DRKoiyxB61hS7MKoMD0trQ==",
       "dev": true,
       "requires": {
         "@babel/code-frame": "^7.12.13",
-        "@jest/types": "^27.0.6",
+        "@jest/types": "^27.1.0",
         "@types/stack-utils": "^2.0.0",
         "chalk": "^4.0.0",
         "graceful-fs": "^4.2.4",
         "micromatch": "^4.0.4",
-        "pretty-format": "^27.0.6",
+        "pretty-format": "^27.1.0",
         "slash": "^3.0.0",
         "stack-utils": "^2.0.3"
       },
           }
         },
         "@babel/helper-validator-identifier": {
-          "version": "7.14.5",
-          "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz",
-          "integrity": "sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg==",
+          "version": "7.14.9",
+          "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.9.tgz",
+          "integrity": "sha512-pQYxPY0UP6IHISRitNe8bsijHex4TWZXi2HwKVsjPiltzlhse2znVcm9Ace510VT1kxIHjGJCZZQBX2gJDbo0g==",
           "dev": true
         },
         "@babel/highlight": {
             }
           }
         },
-        "color-convert": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
-          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
-          "requires": {
-            "color-name": "~1.1.4"
-          }
-        },
-        "color-name": {
-          "version": "1.1.4",
-          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
-          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
-        },
         "graceful-fs": {
-          "version": "4.2.6",
-          "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz",
-          "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==",
+          "version": "4.2.8",
+          "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz",
+          "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==",
           "dev": true
         },
-        "has-flag": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
-          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
-        },
         "micromatch": {
           "version": "4.0.4",
           "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz",
       "dev": true
     },
     "pretty-format": {
-      "version": "27.0.6",
-      "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.0.6.tgz",
-      "integrity": "sha512-8tGD7gBIENgzqA+UBzObyWqQ5B778VIFZA/S66cclyd5YkFLYs2Js7gxDKf0MXtTc9zcS7t1xhdfcElJ3YIvkQ==",
+      "version": "27.1.0",
+      "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.1.0.tgz",
+      "integrity": "sha512-4aGaud3w3rxAO6OXmK3fwBFQ0bctIOG3/if+jYEFGNGIs0EvuidQm3bZ9mlP2/t9epLNC/12czabfy7TZNSwVA==",
       "dev": true,
       "requires": {
-        "@jest/types": "^27.0.6",
+        "@jest/types": "^27.1.0",
         "ansi-regex": "^5.0.0",
         "ansi-styles": "^5.0.0",
         "react-is": "^17.0.1"
index 0286220da624918e87236b5b76ac5a2641a11c66..c7306ec176bdc6685ae6465420f6facc3f743e7a 100644 (file)
@@ -97,7 +97,7 @@
     "eslint-plugin-import": "^2.24.2",
     "eslint-plugin-jsdoc": "^36.0.8",
     "eslint-plugin-node": "^11.1.0",
-    "expect": "^27.0.6",
+    "expect": "^27.1.0",
     "mocha": "^9.1.0",
     "mochawesome": "^6.2.2",
     "npm-check": "^5.9.2",
index e6b710e8d9a028460adea339091f41add0056993..14acfddd6fbc2f7e0c26140d7069eb25cff403ba 100644 (file)
@@ -20,7 +20,7 @@ export default {
     preserveModulesRoot: 'src',
     ...!isDevelopmentBuild && { plugins: [terser({ numWorkers: 2 })] }
   },
-  external: ['basic-ftp', 'chalk', 'crypto', 'perf_hooks', 'fs', 'path', 'poolifier', 'tar', 'url', 'uuid', 'ws', 'winston-daily-rotate-file', 'winston/lib/winston/transports', 'winston', 'worker_threads'],
+  external: ['basic-ftp', 'chalk', 'crypto', 'mongodb', 'perf_hooks', 'fs', 'path', 'poolifier', 'tar', 'url', 'uuid', 'ws', 'winston-daily-rotate-file', 'winston/lib/winston/transports', 'winston', 'worker_threads'],
   plugins: [
     json(),
     typescript({
index 89f81e75aff572dfdb9425df1d69a5f0990d70d2..6bcc8511f487fd36db4289aeb3d82dbaa46b2e34 100644 (file)
@@ -1,4 +1,4 @@
-import { ChargingStationWorkerData, WorkerEvents, WorkerMessage } from '../types/Worker';
+import { ChargingStationWorkerData, WorkerMessage, WorkerMessageEvents } from '../types/Worker';
 
 import Configuration from '../utils/Configuration';
 import { Storage } from '../utils/performance-storage/Storage';
@@ -38,6 +38,7 @@ export default class Bootstrap {
     if (isMainThread && !this.started) {
       try {
         let numStationsTotal = 0;
+        await Bootstrap.storage.open();
         await Bootstrap.workerImplementation.start();
         // Start ChargingStation object in worker thread
         if (Configuration.getStationTemplateURLs()) {
@@ -74,6 +75,7 @@ export default class Bootstrap {
   public async stop(): Promise<void> {
     if (isMainThread && this.started) {
       await Bootstrap.workerImplementation.stop();
+      await Bootstrap.storage.close();
     }
     this.started = false;
   }
@@ -84,7 +86,7 @@ export default class Bootstrap {
     await this.start();
   }
 
-  private initWorkerImplementation() {
+  private initWorkerImplementation(): void {
     Bootstrap.workerImplementation = WorkerFactory.getWorkerImplementation<ChargingStationWorkerData>(this.workerScript, Configuration.getWorkerProcess(),
       {
         startDelay: Configuration.getWorkerStartDelay(),
@@ -94,9 +96,10 @@ export default class Bootstrap {
         poolOptions: {
           workerChoiceStrategy: Configuration.getWorkerPoolStrategy()
         }
-      }, (msg: WorkerMessage) => {
-        if (msg.id === WorkerEvents.PERFORMANCE_STATISTICS) {
-          Bootstrap.storage.storePerformanceStatistics(msg.data);
+      // eslint-disable-next-line @typescript-eslint/no-misused-promises
+      }, async (msg: WorkerMessage) => {
+        if (msg.id === WorkerMessageEvents.PERFORMANCE_STATISTICS) {
+          await Bootstrap.storage.storePerformanceStatistics(msg.data);
         }
       });
   }
index dbabf1e5f118501de1e9f55c0266a2630b7d2c63..ebb53a9feef722fbc79e7aa4be5e5dbded75639d 100644 (file)
@@ -555,7 +555,7 @@ export default class ChargingStation {
     }
     this.stationInfo.powerDivider = this.getPowerDivider();
     if (this.getEnableStatistics()) {
-      this.performanceStatistics = new PerformanceStatistics(this.stationInfo.chargingStationId);
+      this.performanceStatistics = new PerformanceStatistics(this.stationInfo.chargingStationId, this.wsConnectionUrl);
     }
   }
 
@@ -1021,7 +1021,7 @@ export default class ChargingStation {
     return !Utils.isUndefined(this.stationInfo.reconnectExponentialDelay) ? this.stationInfo.reconnectExponentialDelay : false;
   }
 
-  private async reconnect(error: any): Promise<void> {
+  private async reconnect(error: unknown): Promise<void> {
     // Stop WebSocket ping
     this.stopWebSocketPing();
     // Stop heartbeat
index 20a812e496a78f1f128b88a2f33df652ab04ee74..81e0296d8761eca012b63085553df13615b14980 100644 (file)
@@ -1,6 +1,6 @@
 // Partial Copyright Jerome Benoit. 2021. All Rights Reserved.
 
-import { ChargingStationWorkerData, WorkerEvents, WorkerMessage } from '../types/Worker';
+import { ChargingStationWorkerData, WorkerMessage, WorkerMessageEvents } from '../types/Worker';
 import { parentPort, workerData } from 'worker_threads';
 
 import ChargingStation from './ChargingStation';
@@ -25,7 +25,7 @@ if (Utils.workerPoolInUse()) {
  */
 function addMessageListener(): void {
   parentPort?.on('message', (message: WorkerMessage) => {
-    if (message.id === WorkerEvents.START_WORKER_ELEMENT) {
+    if (message.id === WorkerMessageEvents.START_WORKER_ELEMENT) {
       startChargingStation(message.data);
     }
   });
index 7999e14ae86074a0f4a8ac798e1da9c3f835cd58..86a867584fb4e46d106a2c22c6c5d134f4d2e550 100644 (file)
@@ -1,4 +1,5 @@
 import { CircularArray } from '../utils/CircularArray';
+import { URL } from 'url';
 
 export interface StatisticsData {
   countRequest: number;
@@ -18,6 +19,7 @@ export interface StatisticsData {
 
 export default interface Statistics {
   id: string;
+  URI: string;
   createdAt: Date;
   lastUpdatedAt?: Date;
   statisticsData: Record<string, StatisticsData>;
index 32a6b22fb4a364899b5a1aecd11a9485b6229ccc..c20bef20ecbc77864d10eb3f8249f65056335732 100644 (file)
@@ -3,3 +3,7 @@ export enum StorageType {
   MONGO_DB = 'mongodb'
 }
 
+export enum DBType {
+  MONGO_DB = 'MongoDB'
+}
+
index b0fe6e76fecbff82ec27eb5a30eff5a9f078cb05..c4ad7052a5dd2c37dfbb496bdd30864e28333ce2 100644 (file)
@@ -29,11 +29,11 @@ export interface WorkerSetElement {
 }
 
 export interface WorkerMessage {
-  id: WorkerEvents;
+  id: WorkerMessageEvents;
   data: any;
 }
 
-export enum WorkerEvents {
+export enum WorkerMessageEvents {
   START_WORKER_ELEMENT = 'startWorkerElement',
   STOP_WORKER_ELEMENT = 'stopWorkerElement',
   PERFORMANCE_STATISTICS = 'performanceStatistics'
index d497628ac2e0bd260feaf7e6a8dc0444d584f76e..8323da34440df693a08ecb6b5cd2cfe1c64d37e3 100644 (file)
@@ -45,4 +45,8 @@ export default class Constants {
     MeterValueMeasurand.POWER_ACTIVE_IMPORT, MeterValueMeasurand.CURRENT_IMPORT, MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER]);
 
   static readonly DEFAULT_FLUCTUATION_PERCENT = 5;
+
+  static readonly PERFORMANCE_RECORDS_FILETYPE = 'Performance records';
+  static readonly DEFAULT_PERFORMANCE_RECORDS_DB_NAME = 'charging-stations-simulator';
+  static readonly PERFORMANCE_RECORDS_TABLE = 'performanceRecords';
 }
index 4049197a0693b3e4eb74575b06f057f4add57ae2..5d55cfcf164f2837d7ca63b3fb82a3a36c9c2507 100644 (file)
@@ -7,8 +7,9 @@ import Statistics, { StatisticsData } from '../types/Statistics';
 
 import Configuration from './Configuration';
 import { MessageType } from '../types/ocpp/MessageType';
+import { URL } from 'url';
 import Utils from './Utils';
-import { WorkerEvents } from '../types/Worker';
+import { WorkerMessageEvents } from '../types/Worker';
 import logger from './Logger';
 import { parentPort } from 'worker_threads';
 
@@ -18,10 +19,10 @@ export default class PerformanceStatistics {
   private statistics: Statistics;
   private displayInterval: NodeJS.Timeout;
 
-  public constructor(objId: string) {
+  public constructor(objId: string, URI: URL) {
     this.objId = objId;
     this.initializePerformanceObserver();
-    this.statistics = { id: this.objId ?? 'Object id not specified', createdAt: new Date(), statisticsData: {} };
+    this.statistics = { id: this.objId ?? 'Object id not specified', URI: URI.toString(), createdAt: new Date(), statisticsData: {} };
   }
 
   public static beginMeasure(id: string): string {
@@ -190,7 +191,7 @@ export default class PerformanceStatistics {
     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) {
-      parentPort.postMessage({ id: WorkerEvents.PERFORMANCE_STATISTICS, data: this.statistics });
+      parentPort.postMessage({ id: WorkerMessageEvents.PERFORMANCE_STATISTICS, data: this.statistics });
     }
   }
 
index 60e544372f0650ffdedb6d4a7c7c34c61831d340..ed1b122c1ee532bb7ef907b0d18064ebd50ab7e5 100644 (file)
@@ -1,3 +1,4 @@
+import Constants from '../Constants';
 import FileUtils from '../FileUtils';
 import Statistics from '../../types/Statistics';
 import { Storage } from './Storage';
@@ -5,35 +6,52 @@ import fs from 'fs';
 import path from 'path';
 
 export class JSONFileStorage extends Storage {
+  private fd: number | null = null;
+
   constructor(storageURI: string, logPrefix: string) {
     super(storageURI, logPrefix);
+    this.dbName = path.join(path.resolve(__dirname, '../../../'), this.storageURI.pathname.replace(/(?:^\/)|(?:\/$)/g, ''));
   }
 
   public storePerformanceStatistics(performanceStatistics: Statistics): void {
-    const performanceJSONFilePath = path.join(path.resolve(__dirname, '../../../'), this.storageURI.pathname.replace(/(?:^\/)|(?:\/$)/g, ''));
-    if (!fs.existsSync(performanceJSONFilePath)) {
-      this.open(performanceJSONFilePath);
-    }
-    fs.readFile(performanceJSONFilePath, 'utf-8', (error, data) => {
+    this.checkPerformanceRecordsFile();
+    fs.readFile(this.dbName, 'utf-8', (error, data) => {
       if (error) {
-        FileUtils.handleFileException(this.logPrefix, 'Performance measurements', performanceJSONFilePath, error);
+        FileUtils.handleFileException(this.logPrefix, Constants.PERFORMANCE_RECORDS_FILETYPE, this.dbName, error);
       } else {
-        const performanceRecords: Statistics[] = data ? JSON.parse(data.toString()) as Statistics[] : [];
+        const performanceRecords: Statistics[] = data ? JSON.parse(data) as Statistics[] : [];
         performanceRecords.push(performanceStatistics);
-        fs.writeFile(performanceJSONFilePath, JSON.stringify(performanceRecords, null, 2), 'utf-8', (err) => {
+        fs.writeFile(this.dbName, JSON.stringify(performanceRecords, null, 2), 'utf-8', (err) => {
           if (err) {
-            FileUtils.handleFileException(this.logPrefix, 'Performance measurements', performanceJSONFilePath, err);
+            FileUtils.handleFileException(this.logPrefix, Constants.PERFORMANCE_RECORDS_FILETYPE, this.dbName, err);
           }
         });
       }
     });
   }
 
-  private open(filePath: string): void {
+  public open(): void {
     try {
-      fs.openSync(filePath, 'w+');
+      this.fd = fs.openSync(this.dbName, 'a+');
     } catch (error) {
-      FileUtils.handleFileException(this.logPrefix, 'Performance measurements', filePath, error);
+      FileUtils.handleFileException(this.logPrefix, Constants.PERFORMANCE_RECORDS_FILETYPE, this.dbName, error);
+    }
+  }
+
+  public close(): void {
+    try {
+      if (this.fd) {
+        fs.closeSync(this.fd);
+        this.fd = null;
+      }
+    } catch (error) {
+      FileUtils.handleFileException(this.logPrefix, Constants.PERFORMANCE_RECORDS_FILETYPE, this.dbName, error);
+    }
+  }
+
+  private checkPerformanceRecordsFile(): void {
+    if (!this.fd) {
+      throw new Error(`${this.logPrefix} Performance records '${this.dbName}' file descriptor not found`);
     }
   }
 }
index a47984c16cba52392bd6a89b3104581775602f17..56689a560b2c0d3cd61559c262cdb3f5931327ea 100644 (file)
@@ -1,16 +1,54 @@
+import Constants from '../Constants';
+import { DBType } from '../../types/Storage';
+import { MongoClient } from 'mongodb';
 import Statistics from '../../types/Statistics';
 import { Storage } from './Storage';
 
 export class MongoDBStorage extends Storage {
+  private client: MongoClient;
+  private connected: boolean;
+
   constructor(storageURI: string, logPrefix: string) {
     super(storageURI, logPrefix);
+    this.client = new MongoClient(this.storageURI.toString());
+    this.connected = false;
+    this.dbName = this.storageURI.pathname.replace(/(?:^\/)|(?:\/$)/g, '') ?? Constants.DEFAULT_PERFORMANCE_RECORDS_DB_NAME;
   }
 
-  public storePerformanceStatistics(performanceStatistics: Statistics): void {
-    throw new Error('Method not yet implemented');
+  public async storePerformanceStatistics(performanceStatistics: Statistics): Promise<void> {
+    try {
+      this.checkDBConnection();
+      await this.client.db(this.dbName).collection<Statistics>(Constants.PERFORMANCE_RECORDS_TABLE).insertOne(performanceStatistics);
+    } catch (error) {
+      this.handleDBError(DBType.MONGO_DB, error, Constants.PERFORMANCE_RECORDS_TABLE);
+    }
   }
 
-  private open(): void {}
+  public async open(): Promise<void> {
+    try {
+      if (!this.connected) {
+        await this.client.connect();
+        this.connected = true;
+      }
+    } catch (error) {
+      this.handleDBError(DBType.MONGO_DB, error);
+    }
+  }
 
-  private close(): void {}
+  public async close(): Promise<void> {
+    try {
+      if (this.connected) {
+        await this.client.close();
+        this.connected = false;
+      }
+    } catch (error) {
+      this.handleDBError(DBType.MONGO_DB, error);
+    }
+  }
+
+  private checkDBConnection() {
+    if (!this.connected) {
+      throw new Error(`${this.logPrefix} ${DBType.MONGO_DB} connection not opened while trying to issue a request`);
+    }
+  }
 }
index 890dce7a7c6687818faf9589f52c050b511d66a6..b8922375c39f3e983406aac5e0413968d31a3ec9 100644 (file)
@@ -1,14 +1,23 @@
+import { DBType } from '../../types/Storage';
 import Statistics from '../../types/Statistics';
 import { URL } from 'url';
+import logger from '../Logger';
 
 export abstract class Storage {
-  protected storageURI: URL;
-  protected logPrefix: string;
+  protected readonly storageURI: URL;
+  protected readonly logPrefix: string;
+  protected dbName: string;
 
   constructor(storageURI: string, logPrefix: string) {
     this.storageURI = new URL(storageURI);
     this.logPrefix = logPrefix;
   }
 
-  public abstract storePerformanceStatistics(performanceStatistics: Statistics): void;
+  protected handleDBError(DBEngine: DBType, error: Error, table?: string): void {
+    logger.error(`${this.logPrefix} ${DBEngine} error${table && ` in table or collection ${table}`} %j`, error);
+  }
+
+  public abstract open(): void | Promise<void>;
+  public abstract close(): void | Promise<void>;
+  public abstract storePerformanceStatistics(performanceStatistics: Statistics): void | Promise<void>;
 }
index 1b82f1d751a1854d1ed4af9782e4b4b345e71c44..9b9ad3324540ce37dcbb828f42420535f668acf6 100644 (file)
@@ -1,6 +1,6 @@
 // Partial Copyright Jerome Benoit. 2021. All Rights Reserved.
 
-import { WorkerEvents, WorkerSetElement } from '../types/Worker';
+import { WorkerMessageEvents, WorkerSetElement } from '../types/Worker';
 
 import Utils from '../utils/Utils';
 import { Worker } from 'worker_threads';
@@ -44,7 +44,7 @@ export default class WorkerSet<T> extends WorkerAbstract {
       // Start worker sequentially to optimize memory at startup
       await Utils.sleep(this.workerStartDelay);
     }
-    this.getLastWorker().postMessage({ id: WorkerEvents.START_WORKER_ELEMENT, data: elementData });
+    this.getLastWorker().postMessage({ id: WorkerMessageEvents.START_WORKER_ELEMENT, data: elementData });
     this.getLastWorkerSetElement().numberOfWorkerElements++;
   }