Add GetDiagnostics command support
authorJérôme Benoit <jerome.benoit@sap.com>
Sat, 10 Jul 2021 19:50:01 +0000 (21:50 +0200)
committerJérôme Benoit <jerome.benoit@sap.com>
Sat, 10 Jul 2021 19:50:01 +0000 (21:50 +0200)
Signed-off-by: Jérôme Benoit <jerome.benoit@sap.com>
20 files changed:
.gitignore
README.md
package-lock.json
package.json
rollup.config.js
src/assets/configs-aws
src/charging-station/ChargingStation.ts
src/charging-station/ocpp/1.6/OCCP16IncomingRequestService.ts
src/charging-station/ocpp/1.6/OCPP16RequestService.ts
src/charging-station/ocpp/OCPPIncomingRequestService.ts
src/charging-station/ocpp/OCPPRequestService.ts
src/types/ChargingStationTemplate.ts
src/types/ocpp/1.6/DiagnosticsStatus.ts [new file with mode: 0644]
src/types/ocpp/1.6/Requests.ts
src/types/ocpp/1.6/Responses.ts
src/types/ocpp/Requests.ts
src/utils/Constants.ts
test/robohydra/config.json [new file with mode: 0644]
test/robohydra/messages.txt [new file with mode: 0644]
test/robohydra/plugins/wsServer/index.js [new file with mode: 0644]

index 102f38e5ac653934363ed07b68e07d71db58c88e..5dfe331fa4d91000fc72e23bd62721875b4f9757 100644 (file)
@@ -88,3 +88,5 @@ Thumbs.db
 # MTA
 *.mta
 mta_archives/
+
+*.tar.gz
index 45fee8adabd6fc4731f2cb5ecc1d73b61211c3f3..bcdefa604e29ee0a38d46877dddbb152a17e48d6 100644 (file)
--- a/README.md
+++ b/README.md
@@ -63,6 +63,7 @@ numberOfConnectors | | | integer\|integer[] | charging stations number of connec
 useConnectorId0 | true/false | true | boolean | use connector id 0 definition from the template
 randomConnectors | true/false | false | boolean | randomize runtime connector id affectation from the connector id definition in template
 resetTime | | 60 | integer | seconds to wait before the charging stations come back at reset
+autoRegister | true/false | false | boolean | set the charging station as registered at boot notification for testing purpose
 autoReconnectMaxRetries | | -1 (unlimited) | integer | connection retries to the OCPP-J server
 reconnectExponentialDelay | true/false | false | boolean | connection delay retry to the OCPP-J server
 registrationMaxRetries | | -1 (unlimited) | integer | charging stations boot notification retries
index 03650d0da1231fdae6b1b61083988fa6d45b27a9..943cfc83f8e816eb8e77acc3b1e3b28982082852 100644 (file)
       }
     },
     "@es-joy/jsdoccomment": {
-      "version": "0.8.0",
-      "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.8.0.tgz",
-      "integrity": "sha512-Xd3GzYsL2sz2pcdtYt5Q0Wz1ol/o9Nt2UQL4nFPDcaEomvPmwjJsbjkKx1SKhl2h3TgwazNBLdcNr2m0UiGiFA==",
+      "version": "0.9.0-alpha.1",
+      "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.9.0-alpha.1.tgz",
+      "integrity": "sha512-Clxxc0PwpISoYYBibA+1L2qFJ7gvFVhI2Hos87S06K+Q0cXdOhZQJNKWuaQGPAeHjZEuUB/YoWOfwjuF2wirqA==",
       "dev": true,
       "requires": {
-        "comment-parser": "^1.1.5",
+        "comment-parser": "1.1.6-beta.0",
         "esquery": "^1.4.0",
         "jsdoc-type-pratt-parser": "1.0.4"
       }
         }
       }
     },
+    "@humanwhocodes/config-array": {
+      "version": "0.5.0",
+      "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz",
+      "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==",
+      "dev": true,
+      "requires": {
+        "@humanwhocodes/object-schema": "^1.2.0",
+        "debug": "^4.1.1",
+        "minimatch": "^3.0.4"
+      }
+    },
+    "@humanwhocodes/object-schema": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz",
+      "integrity": "sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==",
+      "dev": true
+    },
     "@iarna/toml": {
       "version": "2.2.5",
       "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz",
       }
     },
     "@tsconfig/node10": {
-      "version": "1.0.7",
-      "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.7.tgz",
-      "integrity": "sha512-aBvUmXLQbayM4w3A8TrjwrXs4DZ8iduJnuJLLRGdkWlyakCf1q6uHZJBzXoRA/huAEknG5tcUyQxN3A+In5euQ==",
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz",
+      "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==",
       "dev": true
     },
     "@tsconfig/node12": {
-      "version": "1.0.7",
-      "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.7.tgz",
-      "integrity": "sha512-dgasobK/Y0wVMswcipr3k0HpevxFJLijN03A8mYfEPvWvOs14v0ZlYTR4kIgMx8g4+fTyTFv8/jLCIfRqLDJ4A==",
+      "version": "1.0.9",
+      "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz",
+      "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==",
       "dev": true
     },
     "@tsconfig/node14": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.0.tgz",
-      "integrity": "sha512-RKkL8eTdPv6t5EHgFKIVQgsDapugbuOptNd9OOunN/HAkzmmTnZELx1kNCK0rSdUYGmiFMM3rRQMAWiyp023LQ==",
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz",
+      "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==",
       "dev": true
     },
     "@tsconfig/node16": {
       }
     },
     "@types/json-schema": {
-      "version": "7.0.7",
-      "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz",
-      "integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==",
+      "version": "7.0.8",
+      "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.8.tgz",
+      "integrity": "sha512-YSBPTLTVm2e2OoQIDYx8HaeWJ5tTToLH67kXR7zYNGupXMEHa2++G8k+DczX2cFVgalypqtyZIcU19AFcmOpmg==",
       "dev": true
     },
     "@types/json5": {
       "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==",
       "dev": true
     },
+    "@types/minipass": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/@types/minipass/-/minipass-2.2.0.tgz",
+      "integrity": "sha512-wuzZksN4w4kyfoOv/dlpov4NOunwutLA/q7uc00xU02ZyUY+aoM5PWIXEKBMnm0NHd4a+N71BMjq+x7+2Af1fg==",
+      "dev": true,
+      "requires": {
+        "@types/node": "*"
+      }
+    },
     "@types/mocha": {
-      "version": "8.2.2",
-      "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.2.tgz",
-      "integrity": "sha512-Lwh0lzzqT5Pqh6z61P3c3P5nm6fzQK/MMHl9UKeneAeInVflBSz1O2EkX6gM6xfJd7FBXBY5purtLx7fUiZ7Hw==",
+      "version": "8.2.3",
+      "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.3.tgz",
+      "integrity": "sha512-ekGvFhFgrc2zYQoX4JeZPmVzZxw6Dtllga7iGHzfbYIYkAMUx/sAFP2GdFpLff+vdHXu5fl7WX9AT+TtqYcsyw==",
       "dev": true
     },
     "@types/mochawesome": {
-      "version": "6.2.0",
-      "resolved": "https://registry.npmjs.org/@types/mochawesome/-/mochawesome-6.2.0.tgz",
-      "integrity": "sha512-OiwEcIy47t99YEW479OuQe9Ljvu3y6PPPQyPChV0GO0MtraZq9FgGVIBPU1DxqlsIjxw7Edij6j49HutvQOzKQ==",
+      "version": "6.2.1",
+      "resolved": "https://registry.npmjs.org/@types/mochawesome/-/mochawesome-6.2.1.tgz",
+      "integrity": "sha512-AhQdBkT/CBdx3sI9ATeljqa5uJ3dGNKEJnsgzw9IkPeg9d9Lzxsz1eKFnenxq1qQojIW0XkIsCgdheRRXP2SQA==",
       "dev": true,
       "requires": {
         "@types/mocha": "*"
       }
     },
     "@types/node": {
-      "version": "14.17.4",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.4.tgz",
-      "integrity": "sha512-8kQ3+wKGRNN0ghtEn7EGps/B8CzuBz1nXZEIGGLP2GnwbqYn4dbTs7k+VKLTq1HvZLRCIDtN3Snx1Ege8B7L5A==",
+      "version": "14.17.5",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.5.tgz",
+      "integrity": "sha512-bjqH2cX/O33jXT/UmReo2pM7DIJREPMnarixbQ57DOOzzFaI6D2+IcwaJQaJpv0M1E9TIhPCYVxrkcityLjlqA==",
       "dev": true
     },
     "@types/offscreencanvas": {
       "integrity": "sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==",
       "dev": true
     },
+    "@types/tar": {
+      "version": "4.0.5",
+      "resolved": "https://registry.npmjs.org/@types/tar/-/tar-4.0.5.tgz",
+      "integrity": "sha512-cgwPhNEabHaZcYIy5xeMtux2EmYBitfqEceBUi2t5+ETy4dW6kswt6WX4+HqLeiiKOo42EXbGiDmVJ2x+vi37Q==",
+      "dev": true,
+      "requires": {
+        "@types/minipass": "*",
+        "@types/node": "*"
+      }
+    },
     "@types/uuid": {
-      "version": "8.3.0",
-      "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.0.tgz",
-      "integrity": "sha512-eQ9qFW/fhfGJF8WKHGEHZEyVWfZxrT+6CLIJGBcZPfxUh/+BnEj+UCGYMlr9qZuX/2AltsvwrGqp0LhEW8D0zQ==",
+      "version": "8.3.1",
+      "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.1.tgz",
+      "integrity": "sha512-Y2mHTRAbqfFkpjldbkHGY8JIzRN6XqYRliG8/24FcHm2D2PwW24fl5xMRTVGdrb7iMrwCaIEbLWerGIkXuFWVg==",
       "dev": true
     },
     "@types/webgl-ext": {
       "dev": true
     },
     "@types/ws": {
-      "version": "7.4.5",
-      "resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.4.5.tgz",
-      "integrity": "sha512-8mbDgtc8xpxDDem5Gwj76stBDJX35KQ3YBoayxlqUQcL5BZUthiqP/VQ4PQnLHqM4PmlbyO74t98eJpURO+gPA==",
+      "version": "7.4.6",
+      "resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.4.6.tgz",
+      "integrity": "sha512-ijZ1vzRawI7QoWnTNL8KpHixd2b2XVb9I9HAqI3triPsh1EC0xH0Eg6w2O3TKbDCgiNNlJqfrof6j4T2I+l9vw==",
       "dev": true,
       "requires": {
         "@types/node": "*"
       "dev": true
     },
     "@typescript-eslint/eslint-plugin": {
-      "version": "4.28.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.28.1.tgz",
-      "integrity": "sha512-9yfcNpDaNGQ6/LQOX/KhUFTR1sCKH+PBr234k6hI9XJ0VP5UqGxap0AnNwBnWFk1MNyWBylJH9ZkzBXC+5akZQ==",
+      "version": "4.28.2",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.28.2.tgz",
+      "integrity": "sha512-PGqpLLzHSxq956rzNGasO3GsAPf2lY9lDUBXhS++SKonglUmJypaUtcKzRtUte8CV7nruwnDxtLUKpVxs0wQBw==",
       "dev": true,
       "requires": {
-        "@typescript-eslint/experimental-utils": "4.28.1",
-        "@typescript-eslint/scope-manager": "4.28.1",
+        "@typescript-eslint/experimental-utils": "4.28.2",
+        "@typescript-eslint/scope-manager": "4.28.2",
         "debug": "^4.3.1",
         "functional-red-black-tree": "^1.0.1",
         "regexpp": "^3.1.0",
       },
       "dependencies": {
         "debug": {
-          "version": "4.3.1",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
-          "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
+          "version": "4.3.2",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz",
+          "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==",
           "dev": true,
           "requires": {
             "ms": "2.1.2"
       }
     },
     "@typescript-eslint/experimental-utils": {
-      "version": "4.28.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.28.1.tgz",
-      "integrity": "sha512-n8/ggadrZ+uyrfrSEchx3jgODdmcx7MzVM2sI3cTpI/YlfSm0+9HEUaWw3aQn2urL2KYlWYMDgn45iLfjDYB+Q==",
+      "version": "4.28.2",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.28.2.tgz",
+      "integrity": "sha512-MwHPsL6qo98RC55IoWWP8/opTykjTp4JzfPu1VfO2Z0MshNP0UZ1GEV5rYSSnZSUI8VD7iHvtIPVGW5Nfh7klQ==",
       "dev": true,
       "requires": {
         "@types/json-schema": "^7.0.7",
-        "@typescript-eslint/scope-manager": "4.28.1",
-        "@typescript-eslint/types": "4.28.1",
-        "@typescript-eslint/typescript-estree": "4.28.1",
+        "@typescript-eslint/scope-manager": "4.28.2",
+        "@typescript-eslint/types": "4.28.2",
+        "@typescript-eslint/typescript-estree": "4.28.2",
         "eslint-scope": "^5.1.1",
         "eslint-utils": "^3.0.0"
       },
       }
     },
     "@typescript-eslint/parser": {
-      "version": "4.28.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.28.1.tgz",
-      "integrity": "sha512-UjrMsgnhQIIK82hXGaD+MCN8IfORS1CbMdu7VlZbYa8LCZtbZjJA26De4IPQB7XYZbL8gJ99KWNj0l6WD0guJg==",
+      "version": "4.28.2",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.28.2.tgz",
+      "integrity": "sha512-Q0gSCN51eikAgFGY+gnd5p9bhhCUAl0ERMiDKrTzpSoMYRubdB8MJrTTR/BBii8z+iFwz8oihxd0RAdP4l8w8w==",
       "dev": true,
       "requires": {
-        "@typescript-eslint/scope-manager": "4.28.1",
-        "@typescript-eslint/types": "4.28.1",
-        "@typescript-eslint/typescript-estree": "4.28.1",
+        "@typescript-eslint/scope-manager": "4.28.2",
+        "@typescript-eslint/types": "4.28.2",
+        "@typescript-eslint/typescript-estree": "4.28.2",
         "debug": "^4.3.1"
       },
       "dependencies": {
         "debug": {
-          "version": "4.3.1",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
-          "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
+          "version": "4.3.2",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz",
+          "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==",
           "dev": true,
           "requires": {
             "ms": "2.1.2"
       }
     },
     "@typescript-eslint/scope-manager": {
-      "version": "4.28.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.28.1.tgz",
-      "integrity": "sha512-o95bvGKfss6705x7jFGDyS7trAORTy57lwJ+VsYwil/lOUxKQ9tA7Suuq+ciMhJc/1qPwB3XE2DKh9wubW8YYA==",
+      "version": "4.28.2",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.28.2.tgz",
+      "integrity": "sha512-MqbypNjIkJFEFuOwPWNDjq0nqXAKZvDNNs9yNseoGBB1wYfz1G0WHC2AVOy4XD7di3KCcW3+nhZyN6zruqmp2A==",
       "dev": true,
       "requires": {
-        "@typescript-eslint/types": "4.28.1",
-        "@typescript-eslint/visitor-keys": "4.28.1"
+        "@typescript-eslint/types": "4.28.2",
+        "@typescript-eslint/visitor-keys": "4.28.2"
       }
     },
     "@typescript-eslint/types": {
-      "version": "4.28.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.28.1.tgz",
-      "integrity": "sha512-4z+knEihcyX7blAGi7O3Fm3O6YRCP+r56NJFMNGsmtdw+NCdpG5SgNz427LS9nQkRVTswZLhz484hakQwB8RRg==",
+      "version": "4.28.2",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.28.2.tgz",
+      "integrity": "sha512-Gr15fuQVd93uD9zzxbApz3wf7ua3yk4ZujABZlZhaxxKY8ojo448u7XTm/+ETpy0V0dlMtj6t4VdDvdc0JmUhA==",
       "dev": true
     },
     "@typescript-eslint/typescript-estree": {
-      "version": "4.28.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.28.1.tgz",
-      "integrity": "sha512-GhKxmC4sHXxHGJv8e8egAZeTZ6HI4mLU6S7FUzvFOtsk7ZIDN1ksA9r9DyOgNqowA9yAtZXV0Uiap61bIO81FQ==",
+      "version": "4.28.2",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.28.2.tgz",
+      "integrity": "sha512-86lLstLvK6QjNZjMoYUBMMsULFw0hPHJlk1fzhAVoNjDBuPVxiwvGuPQq3fsBMCxuDJwmX87tM/AXoadhHRljg==",
       "dev": true,
       "requires": {
-        "@typescript-eslint/types": "4.28.1",
-        "@typescript-eslint/visitor-keys": "4.28.1",
+        "@typescript-eslint/types": "4.28.2",
+        "@typescript-eslint/visitor-keys": "4.28.2",
         "debug": "^4.3.1",
         "globby": "^11.0.3",
         "is-glob": "^4.0.1",
           "dev": true
         },
         "debug": {
-          "version": "4.3.1",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
-          "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
+          "version": "4.3.2",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz",
+          "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==",
           "dev": true,
           "requires": {
             "ms": "2.1.2"
       }
     },
     "@typescript-eslint/visitor-keys": {
-      "version": "4.28.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.28.1.tgz",
-      "integrity": "sha512-K4HMrdFqr9PFquPu178SaSb92CaWe2yErXyPumc8cYWxFmhgJsNY9eSePmO05j0JhBvf2Cdhptd6E6Yv9HVHcg==",
+      "version": "4.28.2",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.28.2.tgz",
+      "integrity": "sha512-aT2B4PLyyRDUVUafXzpZFoc0C9t0za4BJAKP5sgWIhG+jHECQZUEjuQSCIwZdiJJ4w4cgu5r3Kh20SOdtEBl0w==",
       "dev": true,
       "requires": {
-        "@typescript-eslint/types": "4.28.1",
+        "@typescript-eslint/types": "4.28.2",
         "eslint-visitor-keys": "^2.0.0"
       }
     },
       "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
       "dev": true
     },
+    "basic-ftp": {
+      "version": "4.6.6",
+      "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-4.6.6.tgz",
+      "integrity": "sha512-5nTclY5mVxjeFoq9cBTzgQvcM1UfeGTd4hCCvb8AgC88wHMDQCD4UX5hZo2jaiSh96Nf+gFy1joNI9iH639bvQ=="
+    },
     "bcrypt-pbkdf": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
         "request-promise": "^4.2.4",
         "tar": "^4.4.10",
         "unzip-stream": "^0.3.0"
+      },
+      "dependencies": {
+        "chownr": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
+          "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
+          "dev": true
+        },
+        "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,
+          "requires": {
+            "minipass": "^2.6.0"
+          }
+        },
+        "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,
+          "requires": {
+            "safe-buffer": "^5.1.2",
+            "yallist": "^3.0.0"
+          }
+        },
+        "minizlib": {
+          "version": "1.3.3",
+          "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz",
+          "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==",
+          "dev": true,
+          "requires": {
+            "minipass": "^2.9.0"
+          }
+        },
+        "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"
+          }
+        },
+        "tar": {
+          "version": "4.4.13",
+          "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz",
+          "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==",
+          "dev": true,
+          "requires": {
+            "chownr": "^1.1.1",
+            "fs-minipass": "^1.2.5",
+            "minipass": "^2.8.6",
+            "minizlib": "^1.2.1",
+            "mkdirp": "^0.5.0",
+            "safe-buffer": "^5.1.2",
+            "yallist": "^3.0.3"
+          }
+        },
+        "yallist": {
+          "version": "3.1.1",
+          "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+          "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+          "dev": true
+        }
       }
     },
     "bit-twiddle": {
       "dev": true
     },
     "chokidar": {
-      "version": "3.5.1",
-      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz",
-      "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==",
+      "version": "3.5.2",
+      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz",
+      "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==",
       "dev": true,
       "requires": {
-        "anymatch": "~3.1.1",
+        "anymatch": "~3.1.2",
         "braces": "~3.0.2",
-        "fsevents": "~2.3.1",
-        "glob-parent": "~5.1.0",
+        "fsevents": "~2.3.2",
+        "glob-parent": "~5.1.2",
         "is-binary-path": "~2.1.0",
         "is-glob": "~4.0.1",
         "normalize-path": "~3.0.0",
-        "readdirp": "~3.5.0"
+        "readdirp": "~3.6.0"
       }
     },
     "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.1.5",
-      "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.1.5.tgz",
-      "integrity": "sha512-RePCE4leIhBlmrqiYTvaqEeGYg7qpSl4etaIabKtdOQVi+mSTIBBklGUwIr79GXYnl3LpMwmDw4KeR2stNc6FA==",
+      "version": "1.1.6-beta.0",
+      "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.1.6-beta.0.tgz",
+      "integrity": "sha512-q3cA8TSMyqW7wcPSYWzbO/rMahnXgzs4SLG/UIWXdEsnXTFPZkEkWAdNgPiHig2OzxgpPLOh4WwsmClDxndwHw==",
       "dev": true
     },
     "commist": {
         "safer-buffer": "^2.1.0"
       }
     },
+    "ejs": {
+      "version": "3.1.6",
+      "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.6.tgz",
+      "integrity": "sha512-9lt9Zse4hPucPkoP7FHDF0LQAlGyF9JVpnClFLFH3aSSbxmyoqINRpp/9wePWJTUl4KOQwRL72Iw3InHPDkoGw==",
+      "dev": true,
+      "requires": {
+        "jake": "^10.6.1"
+      }
+    },
     "electron-to-chromium": {
       "version": "1.3.742",
       "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.742.tgz",
       }
     },
     "eslint": {
-      "version": "7.29.0",
-      "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.29.0.tgz",
-      "integrity": "sha512-82G/JToB9qIy/ArBzIWG9xvvwL3R86AlCjtGw+A29OMZDqhTybz/MByORSukGxeI+YPCR4coYyITKk8BFH9nDA==",
+      "version": "7.30.0",
+      "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.30.0.tgz",
+      "integrity": "sha512-VLqz80i3as3NdloY44BQSJpFw534L9Oh+6zJOUaViV4JPd+DaHwutqP7tcpkW3YiXbK6s05RZl7yl7cQn+lijg==",
       "dev": true,
       "requires": {
         "@babel/code-frame": "7.12.11",
         "@eslint/eslintrc": "^0.4.2",
+        "@humanwhocodes/config-array": "^0.5.0",
         "ajv": "^6.10.0",
         "chalk": "^4.0.0",
         "cross-spawn": "^7.0.2",
       }
     },
     "eslint-plugin-jsdoc": {
-      "version": "35.4.1",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-35.4.1.tgz",
-      "integrity": "sha512-lnpu2Bj+ta2eAqwCWnb6f3Xjc78TWKo/oMCpDH5NfpPhYnePNtGZJzoAMgU5uo9BQqmXJ8pql8aiodOhg82ofw==",
+      "version": "35.4.3",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-35.4.3.tgz",
+      "integrity": "sha512-hBEn+VNjVX0IKoZ2OdZs0Z1fU8CqZkBSzLqD8ZpwZEamrdi2TUgKvujvETe8gXYQ/67hpRtbR5iPFTgmWpRevw==",
       "dev": true,
       "requires": {
-        "@es-joy/jsdoccomment": "^0.8.0",
-        "comment-parser": "1.1.5",
-        "debug": "^4.3.1",
+        "@es-joy/jsdoccomment": "^0.9.0-alpha.1",
+        "comment-parser": "1.1.6-beta.0",
+        "debug": "^4.3.2",
         "esquery": "^1.4.0",
         "jsdoc-type-pratt-parser": "^1.0.4",
         "lodash": "^4.17.21",
       },
       "dependencies": {
         "debug": {
-          "version": "4.3.1",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
-          "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
+          "version": "4.3.2",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz",
+          "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==",
           "dev": true,
           "requires": {
             "ms": "2.1.2"
         "moment": "^2.11.2"
       }
     },
+    "filelist": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.2.tgz",
+      "integrity": "sha512-z7O0IS8Plc39rTCq6i6iHxk43duYOn8uFJiWSewIq0Bww1RNybVHSCjahmcC87ZqAm4OTvFzlzeGu3XAzG1ctQ==",
+      "dev": true,
+      "requires": {
+        "minimatch": "^3.0.4"
+      }
+    },
     "fill-range": {
       "version": "7.0.1",
       "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
       "dev": true
     },
     "flatted": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz",
-      "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==",
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.0.tgz",
+      "integrity": "sha512-XprP7lDrVT+kE2c2YlfiV+IfS9zxukiIOvNamPNsImNhXadSsQEbosItdL9bUQlCZXR13SvPk20BjWSWLA7m4A==",
       "dev": true
     },
     "fn.name": {
       }
     },
     "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": {
         "istanbul-lib-report": "^3.0.0"
       }
     },
+    "jake": {
+      "version": "10.8.2",
+      "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.2.tgz",
+      "integrity": "sha512-eLpKyrfG3mzvGE2Du8VoPbeSkRry093+tyNjdYaBbJS9v17knImYGNXQCUV0gLxQtF82m3E8iRb/wdSQZLoq7A==",
+      "dev": true,
+      "requires": {
+        "async": "0.9.x",
+        "chalk": "^2.4.2",
+        "filelist": "^1.0.1",
+        "minimatch": "^3.0.4"
+      },
+      "dependencies": {
+        "async": {
+          "version": "0.9.2",
+          "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz",
+          "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=",
+          "dev": true
+        }
+      }
+    },
     "jest-diff": {
       "version": "27.0.6",
       "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.0.6.tgz",
       "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=",
       "dev": true
     },
+    "markdown": {
+      "version": "0.3.1",
+      "resolved": "https://registry.npmjs.org/markdown/-/markdown-0.3.1.tgz",
+      "integrity": "sha1-XZXBqGDRSFNSl4MqFX5L5Z6YgNQ=",
+      "dev": true
+    },
     "mbt": {
       "version": "1.2.1",
       "resolved": "https://registry.npmjs.org/mbt/-/mbt-1.2.1.tgz",
         }
       }
     },
+    "mime": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+      "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
+      "dev": true
+    },
     "mime-db": {
       "version": "1.44.0",
       "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.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": {
       "version": "1.0.4",
       "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
-      "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
-      "dev": true
+      "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="
     },
     "mkdirp-classic": {
       "version": "0.5.3",
       "dev": true
     },
     "mocha": {
-      "version": "9.0.1",
-      "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.0.1.tgz",
-      "integrity": "sha512-9zwsavlRO+5csZu6iRtl3GHImAbhERoDsZwdRkdJ/bE+eVplmoxNKE901ZJ9LdSchYBjSCPbjKc5XvcAri2ylw==",
+      "version": "9.0.2",
+      "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.0.2.tgz",
+      "integrity": "sha512-FpspiWU+UT9Sixx/wKimvnpkeW0mh6ROAKkIaPokj3xZgxeRhcna/k5X57jJghEr8X+Cgu/Vegf8zCX5ugSuTA==",
       "dev": true,
       "requires": {
         "@ungap/promise-all-settled": "1.1.2",
         "ansi-colors": "4.1.1",
         "browser-stdout": "1.3.1",
-        "chokidar": "3.5.1",
+        "chokidar": "3.5.2",
         "debug": "4.3.1",
         "diff": "5.0.0",
         "escape-string-regexp": "4.0.0",
         "minimatch": "3.0.4",
         "ms": "2.1.3",
         "nanoid": "3.1.23",
-        "serialize-javascript": "5.0.1",
+        "serialize-javascript": "6.0.0",
         "strip-json-comments": "3.1.1",
         "supports-color": "8.1.1",
         "which": "2.0.2",
         "wide-align": "1.1.3",
-        "workerpool": "6.1.4",
+        "workerpool": "6.1.5",
         "yargs": "16.2.0",
         "yargs-parser": "20.2.4",
         "yargs-unparser": "2.0.0"
           "dev": true
         },
         "serialize-javascript": {
-          "version": "5.0.1",
-          "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz",
-          "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==",
+          "version": "6.0.0",
+          "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz",
+          "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==",
           "dev": true,
           "requires": {
             "randombytes": "^2.1.0"
       "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ=="
     },
     "mongodb": {
-      "version": "3.6.9",
-      "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.9.tgz",
-      "integrity": "sha512-1nSCKgSunzn/CXwgOWgbPHUWOO5OfERcuOWISmqd610jn0s8BU9K4879iJVabqgpPPbA6hO7rG48eq+fGED3Mg==",
+      "version": "3.6.10",
+      "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.10.tgz",
+      "integrity": "sha512-fvIBQBF7KwCJnDZUnFFy4WqEFP8ibdXeFANnylW19+vOwdjOAvqIzPdsNCEMT6VKTHnYu4K64AWRih0mkFms6Q==",
       "requires": {
         "bl": "^2.2.1",
         "bson": "^1.1.4",
         "word-wrap": "~1.2.3"
       }
     },
+    "options": {
+      "version": "0.0.6",
+      "resolved": "https://registry.npmjs.org/options/-/options-0.0.6.tgz",
+      "integrity": "sha1-7CLTEoBrtT5zF3Pnza788cZDEo8=",
+      "dev": true
+    },
     "ora": {
       "version": "5.4.0",
       "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.0.tgz",
       }
     },
     "readdirp": {
-      "version": "3.5.0",
-      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz",
-      "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==",
+      "version": "3.6.0",
+      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+      "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
       "dev": true,
       "requires": {
         "picomatch": "^2.2.1"
         "inherits": "^2.0.1"
       }
     },
+    "robohydra": {
+      "version": "0.6.9",
+      "resolved": "https://registry.npmjs.org/robohydra/-/robohydra-0.6.9.tgz",
+      "integrity": "sha512-kSLoeDBfnGnIkln+nmyfWPHsQSOPz9K/Lb0CDWvCj70Af6K4R5qM54q6DW/IeUUxJY7pI6zi5rHNQZCZnWgfgw==",
+      "dev": true,
+      "requires": {
+        "commander": "^2.19.0",
+        "ejs": "3.1.6",
+        "markdown": "^0.3.1",
+        "mime": "^1.0.0",
+        "qs": "^6.3.1",
+        "ws": "1.1.5"
+      },
+      "dependencies": {
+        "ws": {
+          "version": "1.1.5",
+          "resolved": "https://registry.npmjs.org/ws/-/ws-1.1.5.tgz",
+          "integrity": "sha512-o3KqipXNUdS7wpQzBHSe180lBGO60SoK0yVo3CYJgb2MkobuWuBX6dhkYP5ORCLd55y+SaflMOV5fqAB53ux4w==",
+          "dev": true,
+          "requires": {
+            "options": ">=0.0.5",
+            "ultron": "1.0.x"
+          }
+        }
+      }
+    },
     "rollup": {
-      "version": "2.52.3",
-      "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.52.3.tgz",
-      "integrity": "sha512-QF3Sju8Kl2z0osI4unyOLyUudyhOMK6G0AeqJWgfiyigqLAlnNrfBcDWDx+f1cqn+JU2iIYVkDrgQ6/KtwEfrg==",
+      "version": "2.53.0",
+      "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.53.0.tgz",
+      "integrity": "sha512-spgrY78Toh+m0+zaOoeaayJKuzFuWy6o1PdFIBMVwRcuxT0xCOX9A5rChyKe+2ruL4lePKWUMImS4mMW1QAkmQ==",
       "dev": true,
       "requires": {
         "fsevents": "~2.3.2"
       "dev": true
     },
     "tar": {
-      "version": "4.4.13",
-      "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz",
-      "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==",
-      "dev": true,
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.0.tgz",
+      "integrity": "sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA==",
       "requires": {
-        "chownr": "^1.1.1",
-        "fs-minipass": "^1.2.5",
-        "minipass": "^2.8.6",
-        "minizlib": "^1.2.1",
-        "mkdirp": "^0.5.0",
-        "safe-buffer": "^5.1.2",
-        "yallist": "^3.0.3"
+        "chownr": "^2.0.0",
+        "fs-minipass": "^2.0.0",
+        "minipass": "^3.0.0",
+        "minizlib": "^2.1.1",
+        "mkdirp": "^1.0.3",
+        "yallist": "^4.0.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"
-          }
-        },
         "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=="
         }
       }
     },
       "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw=="
     },
     "ts-node": {
-      "version": "10.0.0",
-      "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.0.0.tgz",
-      "integrity": "sha512-ROWeOIUvfFbPZkoDis0L/55Fk+6gFQNZwwKPLinacRl6tsxstTF1DbAcLKkovwnpKMVvOMHP1TIbnwXwtLg1gg==",
+      "version": "10.1.0",
+      "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.1.0.tgz",
+      "integrity": "sha512-6szn3+J9WyG2hE+5W8e0ruZrzyk1uFLYye6IGMBadnOzDh8aP7t8CbFpsfCiEx2+wMixAhjFt7lOZC4+l+WbEA==",
       "dev": true,
       "requires": {
         "@tsconfig/node10": "^1.0.7",
       }
     },
     "typescript": {
-      "version": "4.3.4",
-      "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.4.tgz",
-      "integrity": "sha512-uauPG7XZn9F/mo+7MrsRjyvbxFpzemRjKEZXS4AK83oP2KKOJPvb+9cO/gmnv8arWZvhnjVOXz7B49m1l0e9Ew==",
+      "version": "4.3.5",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz",
+      "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==",
       "dev": true
     },
     "uglify-js": {
       "dev": true,
       "optional": true
     },
+    "ultron": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz",
+      "integrity": "sha1-rOEWq1V80Zc4ak6I9GhTeMiy5Po=",
+      "dev": true
+    },
     "umd": {
       "version": "3.0.3",
       "resolved": "https://registry.npmjs.org/umd/-/umd-3.0.3.tgz",
       "dev": true
     },
     "workerpool": {
-      "version": "6.1.4",
-      "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.4.tgz",
-      "integrity": "sha512-jGWPzsUqzkow8HoAvqaPWTUPCrlPJaJ5tY8Iz7n1uCz3tTp6s3CDG0FF1NsX42WNlkRSW6Mr+CDZGnNoSsKa7g==",
+      "version": "6.1.5",
+      "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.5.tgz",
+      "integrity": "sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw==",
       "dev": true
     },
     "wrap-ansi": {
       }
     },
     "ws": {
-      "version": "7.5.1",
-      "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.1.tgz",
-      "integrity": "sha512-2c6faOUH/nhoQN6abwMloF7Iyl0ZS2E9HGtsiLrWn0zOOMWlhtDmdf/uihDt6jnuCxgtwGBNy6Onsoy2s2O2Ow=="
+      "version": "7.5.3",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.3.tgz",
+      "integrity": "sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg=="
     },
     "xdg-basedir": {
       "version": "3.0.0",
index fa61637c67867f9aced38e3aca415bf2d4652213..9cce1eb11e262a65f0ca206993182c3fc651f273 100644 (file)
     "lint": "cross-env TIMING=1 eslint --ext .js,.ts src",
     "lint:fix": "cross-env TIMING=1 eslint --fix --ext .js,.ts src",
     "import-sort": "npx import-sort-cli --write 'src/**/*.ts{,x}'",
-    "tsc": "tsc",
     "pretest": "npm run build:dev",
     "test": "cross-env TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\"}' nyc mocha test/**/*Test.ts",
     "coverage": "nyc report --reporter=lcov",
     "coverage:html": "nyc report --reporter=html",
     "clinic:clean": "clinic clean",
     "npm-check": "npm-check",
+    "robohydra": "robohydra test/robohydra/config.json",
     "git:sinit": "git submodule update --init --recursive --force",
     "git:sdiff": "git diff && git submodule foreach 'git diff'",
     "git:supdate": "git submodule update --remote --recursive --merge",
     "release": "release-it"
   },
   "dependencies": {
-    "mongodb": "^3.6.9",
+    "basic-ftp": "^4.6.6",
+    "mongodb": "^3.6.10",
     "poolifier": "^2.0.2",
     "source-map-support": "^0.5.19",
+    "tar": "^6.1.0",
     "tslib": "^2.3.0",
     "uuid": "^8.3.2",
     "winston": "^3.3.3",
     "winston-daily-rotate-file": "^4.5.5",
-    "ws": "^7.5.1"
+    "ws": "^7.5.3"
   },
   "optionalDependencies": {
     "bufferutil": "^4.0.3",
   "devDependencies": {
     "@istanbuljs/nyc-config-typescript": "^1.0.1",
     "@rollup/plugin-json": "^4.1.0",
-    "@types/mocha": "^8.2.2",
-    "@types/mochawesome": "^6.2.0",
-    "@types/node": "^14.17.4",
-    "@types/uuid": "^8.3.0",
-    "@types/ws": "^7.4.5",
-    "@typescript-eslint/eslint-plugin": "^4.28.1",
-    "@typescript-eslint/parser": "^4.28.1",
+    "@types/mocha": "^8.2.3",
+    "@types/mochawesome": "^6.2.1",
+    "@types/node": "^14.17.5",
+    "@types/tar": "^4.0.5",
+    "@types/uuid": "^8.3.1",
+    "@types/ws": "^7.4.6",
+    "@typescript-eslint/eslint-plugin": "^4.28.2",
+    "@typescript-eslint/parser": "^4.28.2",
     "auto-changelog": "^2.3.0",
     "clinic": "^9.0.0",
     "cross-env": "^7.0.3",
-    "eslint": "^7.29.0",
+    "eslint": "^7.30.0",
     "eslint-plugin-import": "^2.23.4",
-    "eslint-plugin-jsdoc": "^35.4.1",
+    "eslint-plugin-jsdoc": "^35.4.3",
     "eslint-plugin-node": "^11.1.0",
     "expect": "^27.0.6",
     "mbt": "^1.2.1",
-    "mocha": "^9.0.1",
+    "mocha": "^9.0.2",
     "mochawesome": "^6.2.2",
     "npm-check": "^5.9.2",
     "nyc": "^15.1.0",
     "release-it": "^14.10.0",
-    "rollup": "^2.52.3",
+    "robohydra": "^0.6.9",
+    "rollup": "^2.53.0",
     "rollup-plugin-analyzer": "^4.0.0",
     "rollup-plugin-copy": "^3.4.0",
     "rollup-plugin-delete": "^2.0.0",
     "rollup-plugin-istanbul": "^3.0.0",
     "rollup-plugin-terser": "^7.0.2",
     "rollup-plugin-typescript2": "^0.30.0",
-    "ts-node": "^10.0.0",
-    "typescript": "^4.3.4"
+    "ts-node": "^10.1.0",
+    "typescript": "^4.3.5"
   }
 }
index 46683b3b1f9900d71aaf90b136efe296947936eb..00f0d3cddd21d51d63c829b75c757e6490eb1b19 100644 (file)
@@ -20,7 +20,7 @@ export default {
     preserveModulesRoot: 'src',
     ...!isDevelopmentBuild && { plugins: [terser({ numWorkers: 2 })] }
   },
-  external: ['crypto', 'perf_hooks', 'fs', 'path', 'poolifier', 'uuid', 'ws', 'winston-daily-rotate-file', 'winston/lib/winston/transports', 'winston', 'worker_threads'],
+  external: ['basic-ftp', 'crypto', 'perf_hooks', 'fs', 'path', 'poolifier', 'tar', 'url', 'uuid', 'ws', 'winston-daily-rotate-file', 'winston/lib/winston/transports', 'winston', 'worker_threads'],
   plugins: [
     json(),
     typescript({
index 6281db138c29805f3695bf751b40917a306721e7..2dc38e36a569dc1266e48d85e7e87ed0d3f18528 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 6281db138c29805f3695bf751b40917a306721e7
+Subproject commit 2dc38e36a569dc1266e48d85e7e87ed0d3f18528
index 71de426c4af701b5572320882b93015fc2a2e423..8e307a9ed5faa2b1ec2d9380cd1a68cab7d3f35b 100644 (file)
@@ -228,7 +228,10 @@ export default class ChargingStation {
     }
     const sampledValueTemplates: SampledValueTemplate[] = this.getConnector(connectorId).MeterValues;
     for (let index = 0; !Utils.isEmptyArray(sampledValueTemplates) && index < sampledValueTemplates.length; index++) {
-      if (phase && sampledValueTemplates[index]?.phase === phase && sampledValueTemplates[index]?.measurand === measurand
+      if (!Constants.SUPPORTED_MEASURANDS.includes(sampledValueTemplates[index]?.measurand)) {
+        logger.warn(`${this.logPrefix()} Unsupported MeterValues measurand ${measurand} ${phase ? `on phase ${phase} ` : ''}in template on connectorId ${connectorId}`);
+        continue;
+      } else if (phase && sampledValueTemplates[index]?.phase === phase && sampledValueTemplates[index]?.measurand === measurand
           && this.getConfigurationKey(StandardParametersKey.MeterValuesSampledData).value.includes(measurand)) {
         return sampledValueTemplates[index];
       } else if (!phase && !sampledValueTemplates[index].phase && sampledValueTemplates[index]?.measurand === measurand
@@ -547,6 +550,13 @@ export default class ChargingStation {
     }
     // OCPP parameters
     this.initOCPPParameters();
+    if (this.stationInfo.autoRegister) {
+      this.bootNotificationResponse = {
+        currentTime: new Date().toISOString(),
+        interval: this.getHeartbeatInterval() / 1000,
+        status: RegistrationStatus.ACCEPTED
+      };
+    }
     this.stationInfo.powerDivider = this.getPowerDivider();
     if (this.getEnableStatistics()) {
       this.performanceStatistics = new PerformanceStatistics(this.stationInfo.chargingStationId);
@@ -643,8 +653,13 @@ export default class ChargingStation {
     let requestPayload: Record<string, unknown>;
     let errMsg: string;
     try {
-      // Parse the message
-      [messageType, messageId, commandName, commandPayload, errorDetails] = JSON.parse(messageEvent.toString()) as IncomingRequest;
+      const request = JSON.parse(messageEvent.toString()) as IncomingRequest;
+      if (Utils.isIterable(request)) {
+        // Parse the message
+        [messageType, messageId, commandName, commandPayload, errorDetails] = request;
+      } else {
+        throw new Error('Incoming request is not iterable');
+      }
       // Check the Type of message
       switch (messageType) {
         // Incoming Message
@@ -692,7 +707,7 @@ export default class ChargingStation {
       }
     } catch (error) {
       // Log
-      logger.error('%s Incoming message %j processing error %j on request content type %j', this.logPrefix(), messageEvent, error, this.requests[messageId]);
+      logger.error('%s Incoming request message %j processing error %j on content type %j', this.logPrefix(), messageEvent, error, this.requests[messageId]);
       // Send error
       messageType !== MessageType.CALL_ERROR_MESSAGE && await this.ocppRequestService.sendError(messageId, error, commandName);
     }
@@ -926,6 +941,8 @@ export default class ChargingStation {
     if (HeartBeatInterval) {
       return Utils.convertToInt(HeartBeatInterval.value) * 1000;
     }
+    !this.stationInfo.autoRegister && logger.warn(`${this.logPrefix()} Heartbeat interval configuration key not set, using default value: ${Constants.DEFAULT_HEARTBEAT_INTERVAL}`);
+    return Constants.DEFAULT_HEARTBEAT_INTERVAL;
   }
 
   private stopHeartbeat(): void {
index e806051a449da65b5a4b989492d0050f50d760aa..98f165a6d5f37e88243ce30e4c61917a5672b2a3 100644 (file)
@@ -1,19 +1,27 @@
-import { ChangeAvailabilityRequest, ChangeConfigurationRequest, ClearChargingProfileRequest, GetConfigurationRequest, OCPP16AvailabilityType, OCPP16IncomingRequestCommand, RemoteStartTransactionRequest, RemoteStopTransactionRequest, ResetRequest, SetChargingProfileRequest, UnlockConnectorRequest } from '../../../types/ocpp/1.6/Requests';
-import { ChangeAvailabilityResponse, ChangeConfigurationResponse, ClearChargingProfileResponse, GetConfigurationResponse, SetChargingProfileResponse, UnlockConnectorResponse } from '../../../types/ocpp/1.6/Responses';
+import * as url from 'url';
+
+import { ChangeAvailabilityRequest, ChangeConfigurationRequest, ClearChargingProfileRequest, GetConfigurationRequest, GetDiagnosticsRequest, OCPP16AvailabilityType, OCPP16IncomingRequestCommand, RemoteStartTransactionRequest, RemoteStopTransactionRequest, ResetRequest, SetChargingProfileRequest, UnlockConnectorRequest } from '../../../types/ocpp/1.6/Requests';
+import { ChangeAvailabilityResponse, ChangeConfigurationResponse, ClearChargingProfileResponse, GetConfigurationResponse, GetDiagnosticsResponse, SetChargingProfileResponse, UnlockConnectorResponse } from '../../../types/ocpp/1.6/Responses';
 import { ChargingProfilePurposeType, OCPP16ChargingProfile } from '../../../types/ocpp/1.6/ChargingProfile';
+import { Client, FTPResponse } from 'basic-ftp';
 import { OCPP16AuthorizationStatus, OCPP16StopTransactionReason } from '../../../types/ocpp/1.6/Transaction';
 
 import Constants from '../../../utils/Constants';
 import { DefaultResponse } from '../../../types/ocpp/Responses';
 import { ErrorType } from '../../../types/ocpp/ErrorType';
+import { IncomingRequestCommand } from '../../../types/ocpp/Requests';
 import { MessageType } from '../../../types/ocpp/MessageType';
 import { OCPP16ChargePointStatus } from '../../../types/ocpp/1.6/ChargePointStatus';
+import { OCPP16DiagnosticsStatus } from '../../../types/ocpp/1.6/DiagnosticsStatus';
 import { OCPP16StandardParametersKey } from '../../../types/ocpp/1.6/Configuration';
 import { OCPPConfigurationKey } from '../../../types/ocpp/Configuration';
 import OCPPError from '../../OcppError';
 import OCPPIncomingRequestService from '../OCPPIncomingRequestService';
 import Utils from '../../../utils/Utils';
+import fs from 'fs';
 import logger from '../../../utils/Logger';
+import path from 'path';
+import tar from 'tar';
 
 export default class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
   public async handleRequest(messageId: string, commandName: OCPP16IncomingRequestCommand, commandPayload: Record<string, unknown>): Promise<void> {
@@ -340,4 +348,53 @@ export default class OCPP16IncomingRequestService extends OCPPIncomingRequestSer
     logger.info(this.chargingStation.logPrefix() + ' Trying to remote stop a non existing transaction ' + transactionId.toString());
     return Constants.OCPP_RESPONSE_REJECTED;
   }
+
+  private async handleRequestGetDiagnostics(commandPayload: GetDiagnosticsRequest): Promise<GetDiagnosticsResponse> {
+    logger.debug(this.chargingStation.logPrefix() + ' ' + IncomingRequestCommand.GET_DIAGNOSTICS + ' request received: %j', commandPayload);
+    const uri = new url.URL(commandPayload.location);
+    if (uri.protocol.startsWith('ftp:')) {
+      let ftpClient: Client;
+      try {
+        const logFiles = fs.readdirSync(path.resolve(__dirname, '../../../../')).filter((file) => file.endsWith('.log')).map((file) => path.join('./', file));
+        const diagnosticsArchive = this.chargingStation.stationInfo.chargingStationId + '_logs.tar.gz';
+        tar.create({ gzip: true }, logFiles).pipe(fs.createWriteStream(diagnosticsArchive));
+        ftpClient = new Client();
+        const accessResponse = await ftpClient.access({
+          host: uri.host,
+          ...(uri.port !== '') && { port: Utils.convertToInt(uri.port) },
+          ...(uri.username !== '') && { user: uri.username },
+          ...(uri.password !== '') && { password: uri.password },
+        });
+        let uploadResponse: FTPResponse;
+        if (accessResponse.code === 220) {
+          // eslint-disable-next-line @typescript-eslint/no-misused-promises
+          ftpClient.trackProgress(async (info) => {
+            logger.info(`${this.chargingStation.logPrefix()} ${info.bytes / 1024} bytes transferred from diagnostics archive ${info.name}`);
+            await this.chargingStation.ocppRequestService.sendDiagnosticsStatusNotification(OCPP16DiagnosticsStatus.Uploading);
+          });
+          uploadResponse = await ftpClient.uploadFrom(path.join(path.resolve(__dirname, '../../../../'), diagnosticsArchive), uri.pathname + diagnosticsArchive);
+          if (uploadResponse.code === 226) {
+            await this.chargingStation.ocppRequestService.sendDiagnosticsStatusNotification(OCPP16DiagnosticsStatus.Uploaded);
+            if (ftpClient) {
+              ftpClient.close();
+            }
+            return { fileName: diagnosticsArchive };
+          }
+          throw Error(`Diagnostics transfer failed with error code ${accessResponse.code.toString()}${uploadResponse?.code && '|' + uploadResponse?.code.toString()}`);
+        }
+        throw Error(`Diagnostics transfer failed with error code ${accessResponse.code.toString()}${uploadResponse?.code && '|' + uploadResponse?.code.toString()}`);
+      } catch (error) {
+        await this.chargingStation.ocppRequestService.sendDiagnosticsStatusNotification(OCPP16DiagnosticsStatus.UploadFailed);
+        this.handleIncomingRequestError(IncomingRequestCommand.GET_DIAGNOSTICS, error);
+        if (ftpClient) {
+          ftpClient.close();
+        }
+        return Constants.OCPP_RESPONSE_EMPTY;
+      }
+    } else {
+      logger.error(`${this.chargingStation.logPrefix()} Unsupported protocol ${uri.protocol} to transfer the diagnostic logs archive`);
+      await this.chargingStation.ocppRequestService.sendDiagnosticsStatusNotification(OCPP16DiagnosticsStatus.UploadFailed);
+      return Constants.OCPP_RESPONSE_EMPTY;
+    }
+  }
 }
index 74bc1fbf2d1dcd66ab3cf805d829954b18ff7371..4b7186ced771678600284d2ff76e736491054cc0 100644 (file)
@@ -1,7 +1,7 @@
 import { ACElectricUtils, DCElectricUtils } from '../../../utils/ElectricUtils';
 import { AuthorizeRequest, OCPP16AuthorizeResponse, OCPP16StartTransactionResponse, OCPP16StopTransactionReason, OCPP16StopTransactionResponse, StartTransactionRequest, StopTransactionRequest } from '../../../types/ocpp/1.6/Transaction';
 import { CurrentType, Voltage } from '../../../types/ChargingStationTemplate';
-import { HeartbeatRequest, OCPP16BootNotificationRequest, OCPP16IncomingRequestCommand, OCPP16RequestCommand, StatusNotificationRequest } from '../../../types/ocpp/1.6/Requests';
+import { DiagnosticsStatusNotificationRequest, HeartbeatRequest, OCPP16BootNotificationRequest, OCPP16IncomingRequestCommand, OCPP16RequestCommand, StatusNotificationRequest } from '../../../types/ocpp/1.6/Requests';
 import { MeterValueUnit, MeterValuesRequest, OCPP16MeterValue, OCPP16MeterValueMeasurand, OCPP16MeterValuePhase } from '../../../types/ocpp/1.6/MeterValues';
 
 import Constants from '../../../utils/Constants';
@@ -11,6 +11,7 @@ import { MessageType } from '../../../types/ocpp/MessageType';
 import { OCPP16BootNotificationResponse } from '../../../types/ocpp/1.6/Responses';
 import { OCPP16ChargePointErrorCode } from '../../../types/ocpp/1.6/ChargePointErrorCode';
 import { OCPP16ChargePointStatus } from '../../../types/ocpp/1.6/ChargePointStatus';
+import { OCPP16DiagnosticsStatus } from '../../../types/ocpp/1.6/DiagnosticsStatus';
 import { OCPP16ServiceUtils } from './OCPP16ServiceUtils';
 import OCPPError from '../../OcppError';
 import OCPPRequestService from '../OCPPRequestService';
@@ -354,6 +355,13 @@ export default class OCPP16RequestService extends OCPPRequestService {
     await this.sendMessage(Utils.generateUUID(), payload, MessageType.CALL_MESSAGE, OCPP16RequestCommand.METER_VALUES);
   }
 
+  public async sendDiagnosticsStatusNotification(diagnosticsStatus: OCPP16DiagnosticsStatus): Promise<void> {
+    const payload: DiagnosticsStatusNotificationRequest = {
+      status: diagnosticsStatus
+    };
+    await this.sendMessage(Utils.generateUUID(), payload, MessageType.CALL_MESSAGE, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION);
+  }
+
   public async sendError(messageId: string, error: OCPPError, commandName: OCPP16RequestCommand | OCPP16IncomingRequestCommand): Promise<unknown> {
     // Send error
     return this.sendMessage(messageId, error, MessageType.CALL_ERROR_MESSAGE, commandName);
index 897127b6e2ffa029073db6e365c969356c4490da..9980e5d89ec7a8406ec82aae4ddf261820be5bf5 100644 (file)
@@ -1,5 +1,6 @@
 import ChargingStation from '../ChargingStation';
 import { IncomingRequestCommand } from '../../types/ocpp/Requests';
+import logger from '../../utils/Logger';
 
 export default abstract class OCPPIncomingRequestService {
   protected chargingStation: ChargingStation;
@@ -8,5 +9,10 @@ export default abstract class OCPPIncomingRequestService {
     this.chargingStation = chargingStation;
   }
 
+  public handleIncomingRequestError(commandName: IncomingRequestCommand, error: Error): void {
+    logger.error(this.chargingStation.logPrefix() + ' Incoming request command ' + commandName + ' error: %j', error);
+    throw error;
+  }
+
   public abstract handleRequest(messageId: string, commandName: IncomingRequestCommand, commandPayload: Record<string, unknown>): Promise<void>;
 }
index 2b00ccb96d97595044598a6ce61861b9353845e2..816826992eb3fa0f7f31770e1b9e01b19daad91a 100644 (file)
@@ -1,5 +1,5 @@
 import { AuthorizeResponse, StartTransactionResponse, StopTransactionReason, StopTransactionResponse } from '../../types/ocpp/Transaction';
-import { IncomingRequestCommand, RequestCommand } from '../../types/ocpp/Requests';
+import { DiagnosticsStatus, IncomingRequestCommand, RequestCommand } from '../../types/ocpp/Requests';
 
 import { BootNotificationResponse } from '../../types/ocpp/Responses';
 import { ChargePointErrorCode } from '../../types/ocpp/ChargePointErrorCode';
@@ -104,7 +104,7 @@ export default abstract class OCPPRequestService {
   }
 
   public handleRequestError(commandName: RequestCommand, error: Error): void {
-    logger.error(this.chargingStation.logPrefix() + ' Send ' + commandName + ' error: %j', error);
+    logger.error(this.chargingStation.logPrefix() + ' Request command ' + commandName + ' error: %j', error);
     throw error;
   }
 
@@ -117,5 +117,6 @@ export default abstract class OCPPRequestService {
   public abstract sendMeterValues(connectorId: number, transactionId: number, interval: number, self: OCPPRequestService): Promise<void>;
   public abstract sendTransactionBeginMeterValues(connectorId: number, transactionId: number, beginMeterValue: MeterValue): Promise<void>;
   public abstract sendTransactionEndMeterValues(connectorId: number, transactionId: number, endMeterValue: MeterValue): Promise<void>;
+  public abstract sendDiagnosticsStatusNotification(diagnosticsStatus: DiagnosticsStatus): Promise<void>;
   public abstract sendError(messageId: string, error: OCPPError, commandName: RequestCommand | IncomingRequestCommand): Promise<unknown>;
 }
index c65e94c68554c93abb127bc6a1a788472f212e24..0136ab329beb8931eede6b34524537023bd68d6a 100644 (file)
@@ -54,6 +54,7 @@ export default interface ChargingStationTemplate {
   useConnectorId0?: boolean;
   randomConnectors?: boolean;
   resetTime?: number;
+  autoRegister: boolean;
   autoReconnectMaxRetries?: number;
   reconnectExponentialDelay?: boolean;
   registrationMaxRetries?: number;
diff --git a/src/types/ocpp/1.6/DiagnosticsStatus.ts b/src/types/ocpp/1.6/DiagnosticsStatus.ts
new file mode 100644 (file)
index 0000000..c9fecce
--- /dev/null
@@ -0,0 +1,6 @@
+export enum OCPP16DiagnosticsStatus {
+  Idle = 'Idle',
+  Uploaded = 'Uploaded',
+  UploadFailed = 'UploadFailed',
+  Uploading = 'Uploading'
+}
index 9ed48049a52cb70915cf8c1955f914d42a236453..873d41a044df6c0484116cba82612010b1934690 100644 (file)
@@ -2,6 +2,7 @@ import { ChargingProfilePurposeType, OCPP16ChargingProfile } from './ChargingPro
 
 import { OCPP16ChargePointErrorCode } from './ChargePointErrorCode';
 import { OCPP16ChargePointStatus } from './ChargePointStatus';
+import { OCPP16DiagnosticsStatus } from './DiagnosticsStatus';
 import { OCPP16StandardParametersKey } from './Configuration';
 
 export enum OCPP16RequestCommand {
@@ -12,7 +13,8 @@ export enum OCPP16RequestCommand {
   AUTHORIZE = 'Authorize',
   START_TRANSACTION = 'StartTransaction',
   STOP_TRANSACTION = 'StopTransaction',
-  METER_VALUES = 'MeterValues'
+  METER_VALUES = 'MeterValues',
+  DIAGNOSTICS_STATUS_NOTIFICATION= 'DiagnosticsStatusNotification'
 }
 
 export enum OCPP16IncomingRequestCommand {
@@ -25,7 +27,8 @@ export enum OCPP16IncomingRequestCommand {
   SET_CHARGING_PROFILE = 'SetChargingProfile',
   CLEAR_CHARGING_PROFILE = 'ClearChargingProfile',
   REMOTE_START_TRANSACTION = 'RemoteStartTransaction',
-  REMOTE_STOP_TRANSACTION = 'RemoteStopTransaction'
+  REMOTE_STOP_TRANSACTION = 'RemoteStopTransaction',
+  GET_DIAGNOSTICS = 'GetDiagnostics'
 }
 
 // eslint-disable-next-line @typescript-eslint/no-empty-interface
@@ -106,3 +109,15 @@ export interface ClearChargingProfileRequest {
   chargingProfilePurpose?: ChargingProfilePurposeType;
   stackLevel?: number;
 }
+
+export interface GetDiagnosticsRequest {
+  location: string;
+  retries?: number;
+  retryInterval?: number;
+  startTime?: Date;
+  stopTime?: Date;
+}
+
+export interface DiagnosticsStatusNotificationRequest {
+  status: OCPP16DiagnosticsStatus
+}
index 317926f3fe921be2020dc59ac6c687cea0b7c1bc..68df3e91090ac8c6e2e380962c1d49e98e7596c9 100644 (file)
@@ -73,3 +73,10 @@ export enum OCPP16ClearChargingProfileStatus {
 export interface ClearChargingProfileResponse {
   status: OCPP16ClearChargingProfileStatus;
 }
+
+export interface GetDiagnosticsResponse {
+  fileName?: string;
+}
+
+// eslint-disable-next-line @typescript-eslint/no-empty-interface
+export interface DiagnosticsStatusNotificationResponse {}
index aeb6828c7c97cd1e9e1bcff8ebcf3c480a45f5de..82c7d2135aee7432a93390f4e0a24b52983dad42 100644 (file)
@@ -1,6 +1,7 @@
 import { OCPP16AvailabilityType, OCPP16BootNotificationRequest, OCPP16IncomingRequestCommand, OCPP16RequestCommand } from './1.6/Requests';
 
 import { MessageType } from './MessageType';
+import { OCPP16DiagnosticsStatus } from './1.6/DiagnosticsStatus';
 import OCPPError from '../../charging-station/OcppError';
 
 export default interface Requests {
@@ -27,6 +28,12 @@ export const IncomingRequestCommand = {
   ...OCPP16IncomingRequestCommand
 };
 
+export type DiagnosticsStatus = OCPP16DiagnosticsStatus;
+
+export const DiagnosticsStatus = {
+  ...OCPP16DiagnosticsStatus
+};
+
 export type Request = [(payload: Record<string, unknown> | string, requestPayload: Record<string, unknown>) => void, (error: OCPPError) => void, Record<string, unknown>];
 
 export type IncomingRequest = [MessageType, string, IncomingRequestCommand, Record<string, unknown>, Record<string, unknown>];
index 350170aacd1e3ca94f8425f31aecc35b14dee9c1..9327b988404932015944de790828ce3dc3162d99 100644 (file)
@@ -6,6 +6,7 @@ export default class Constants {
   static readonly ENTITY_CHARGING_STATION = 'ChargingStation';
   static readonly ENTITY_AUTOMATIC_TRANSACTION_GENERATOR = 'AutomaticTransactionGenerator';
 
+  static readonly OCPP_RESPONSE_EMPTY = Object.freeze({});
   static readonly OCPP_RESPONSE_ACCEPTED = Object.freeze({ status: DefaultStatus.ACCEPTED });
   static readonly OCPP_RESPONSE_REJECTED = Object.freeze({ status: DefaultStatus.REJECTED });
   static readonly OCPP_CONFIGURATION_RESPONSE_ACCEPTED = Object.freeze({ status: ConfigurationStatus.ACCEPTED });
@@ -25,7 +26,7 @@ export default class Constants {
   static readonly OCPP_AVAILABILITY_RESPONSE_SCHEDULED = Object.freeze({ status: AvailabilityStatus.SCHEDULED });
 
   static readonly OCPP_DEFAULT_BOOT_NOTIFICATION_INTERVAL = 60000; // Ms
-  static readonly OCPP_ERROR_TIMEOUT = 60000; // 60 sec
+  static readonly OCPP_ERROR_TIMEOUT = 60000; // Ms
 
   static readonly CHARGING_STATION_DEFAULT_RESET_TIME = 60000; // Ms
   static readonly CHARGING_STATION_ATG_WAIT_TIME = 2000; // Ms
@@ -41,6 +42,8 @@ export default class Constants {
 
   static readonly DEFAULT_CONNECTION_TIMEOUT = 30;
 
+  static readonly DEFAULT_HEARTBEAT_INTERVAL = 60000; // Ms
+
   static readonly SUPPORTED_MEASURANDS = Object.freeze([MeterValueMeasurand.STATE_OF_CHARGE, MeterValueMeasurand.VOLTAGE,
     MeterValueMeasurand.POWER_ACTIVE_IMPORT, MeterValueMeasurand.CURRENT_IMPORT, MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER]);
 
diff --git a/test/robohydra/config.json b/test/robohydra/config.json
new file mode 100644 (file)
index 0000000..71e532f
--- /dev/null
@@ -0,0 +1,9 @@
+{
+  "pluginLoadPaths": [
+    "test/robohydra/plugins"
+  ],
+  "plugins": [
+    "logger",
+    "wsServer"
+  ]
+}
diff --git a/test/robohydra/messages.txt b/test/robohydra/messages.txt
new file mode 100644 (file)
index 0000000..b71191b
--- /dev/null
@@ -0,0 +1,2 @@
+GetDiagnostics:
+  curl -d '[2,"123456789","GetDiagnostics",{"location":"ftp://localhost"}]' -H "Content-Type: application/json" -X POST http://localhost:3000/message
diff --git a/test/robohydra/plugins/wsServer/index.js b/test/robohydra/plugins/wsServer/index.js
new file mode 100644 (file)
index 0000000..d4ab6b8
--- /dev/null
@@ -0,0 +1,59 @@
+const RoboHydraHead = require('robohydra').heads.RoboHydraHead;
+const RoboHydraWebSocketHead = require('robohydra').heads.RoboHydraWebSocketHead;
+const RoboHydraWebSocketHeadProxy = require('robohydra').heads.RoboHydraWebSocketHeadProxy;
+
+exports.getBodyParts = function(conf) {
+  let wsSocket;
+  return {
+    heads: [
+      new RoboHydraHead({
+        name: 'message',
+        path: '/message',
+        method: 'POST',
+        handler: function(req, res) {
+          const msg = JSON.stringify(req.body);
+          if (wsSocket) {
+            wsSocket.send(msg);
+            res.send('Message sent');
+          } else {
+            res.send('Cannot send message, no opened websocket found');
+          }
+        }
+      }),
+
+      new RoboHydraHead({
+        name: 'close',
+        path: '/close',
+        method: 'GET',
+        handler: function(req, res) {
+          if (wsSocket) {
+            wsSocket.close();
+            res.send('Websocket closed');
+          } else {
+            res.send('Cannot close websocket, no opened websocket found');
+          }
+        }
+      }),
+
+      new RoboHydraWebSocketHeadProxy({
+        name: 'proxy',
+        mountPath: '/proxy',
+        proxyTo: 'ws://server.example.com',
+        preProcessor: function(data) {
+          console.log('From the client: ' + data);
+        },
+        postProcessor: function(data) {
+          console.log('From the server: ' + data);
+        }
+      }),
+
+      new RoboHydraWebSocketHead({
+        name: 'WS Server',
+        path: '/.*',
+        handler: function(req, socket) {
+          wsSocket = socket;
+        }
+      })
+    ]
+  };
+};