Improve charging station registration handling.
authorJérôme Benoit <jerome.benoit@sap.com>
Thu, 31 Dec 2020 23:34:36 +0000 (00:34 +0100)
committerJérôme Benoit <jerome.benoit@sap.com>
Thu, 31 Dec 2020 23:34:36 +0000 (00:34 +0100)
Signed-off-by: Jérôme Benoit <jerome.benoit@sap.com>
package-lock.json
package.json
src/charging-station/ChargingStation.ts
src/types/ChargingStationTemplate.ts
src/types/WebSocket.ts [new file with mode: 0644]
src/utils/Constants.ts
src/utils/Utils.ts

index 6e921dec6dfa1273f7332977fd250dc3a8e46cc3..524738408c883f613b631e729386b2934a139eb0 100644 (file)
       }
     },
     "@nodelib/fs.scandir": {
-      "version": "2.1.3",
-      "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz",
-      "integrity": "sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw==",
+      "version": "2.1.4",
+      "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz",
+      "integrity": "sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA==",
       "dev": true,
       "requires": {
-        "@nodelib/fs.stat": "2.0.3",
+        "@nodelib/fs.stat": "2.0.4",
         "run-parallel": "^1.1.9"
       }
     },
     "@nodelib/fs.stat": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz",
-      "integrity": "sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA==",
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz",
+      "integrity": "sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q==",
       "dev": true
     },
     "@nodelib/fs.walk": {
-      "version": "1.2.4",
-      "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz",
-      "integrity": "sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ==",
+      "version": "1.2.6",
+      "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz",
+      "integrity": "sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow==",
       "dev": true,
       "requires": {
-        "@nodelib/fs.scandir": "2.1.3",
+        "@nodelib/fs.scandir": "2.1.4",
         "fastq": "^1.6.0"
       }
     },
       "dev": true
     },
     "@types/node": {
-      "version": "14.14.16",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.16.tgz",
-      "integrity": "sha512-naXYePhweTi+BMv11TgioE2/FXU4fSl29HAH1ffxVciNsH3rYXjNP2yM8wqmSm7jS20gM8TIklKiTen+1iVncw==",
+      "version": "14.14.17",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.17.tgz",
+      "integrity": "sha512-G0lD1/7qD60TJ/mZmhog76k7NcpLWkPVGgzkRy3CTlnFu4LUQh5v2Wa661z6vnXmD8EQrnALUyf0VRtrACYztw==",
       "dev": true
     },
     "@types/offscreencanvas": {
       }
     },
     "@typescript-eslint/eslint-plugin": {
-      "version": "4.11.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.11.0.tgz",
-      "integrity": "sha512-x4arJMXBxyD6aBXLm3W7mSDZRiABzy+2PCLJbL7OPqlp53VXhaA1HKK7R2rTee5OlRhnUgnp8lZyVIqjnyPT6g==",
+      "version": "4.11.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.11.1.tgz",
+      "integrity": "sha512-fABclAX2QIEDmTMk6Yd7Muv1CzFLwWM4505nETzRHpP3br6jfahD9UUJkhnJ/g2m7lwfz8IlswcwGGPGiq9exw==",
       "dev": true,
       "requires": {
-        "@typescript-eslint/experimental-utils": "4.11.0",
-        "@typescript-eslint/scope-manager": "4.11.0",
+        "@typescript-eslint/experimental-utils": "4.11.1",
+        "@typescript-eslint/scope-manager": "4.11.1",
         "debug": "^4.1.1",
         "functional-red-black-tree": "^1.0.1",
         "regexpp": "^3.0.0",
       }
     },
     "@typescript-eslint/experimental-utils": {
-      "version": "4.11.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.11.0.tgz",
-      "integrity": "sha512-1VC6mSbYwl1FguKt8OgPs8xxaJgtqFpjY/UzUYDBKq4pfQ5lBvN2WVeqYkzf7evW42axUHYl2jm9tNyFsb8oLg==",
+      "version": "4.11.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.11.1.tgz",
+      "integrity": "sha512-mAlWowT4A6h0TC9F+J5pdbEhjNiEMO+kqPKQ4sc3fVieKL71dEqfkKgtcFVSX3cjSBwYwhImaQ/mXQF0oaI38g==",
       "dev": true,
       "requires": {
         "@types/json-schema": "^7.0.3",
-        "@typescript-eslint/scope-manager": "4.11.0",
-        "@typescript-eslint/types": "4.11.0",
-        "@typescript-eslint/typescript-estree": "4.11.0",
+        "@typescript-eslint/scope-manager": "4.11.1",
+        "@typescript-eslint/types": "4.11.1",
+        "@typescript-eslint/typescript-estree": "4.11.1",
         "eslint-scope": "^5.0.0",
         "eslint-utils": "^2.0.0"
       }
     },
     "@typescript-eslint/parser": {
-      "version": "4.11.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.11.0.tgz",
-      "integrity": "sha512-NBTtKCC7ZtuxEV5CrHUO4Pg2s784pvavc3cnz6V+oJvVbK4tH9135f/RBP6eUA2KHiFKAollSrgSctQGmHbqJQ==",
+      "version": "4.11.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.11.1.tgz",
+      "integrity": "sha512-BJ3jwPQu1jeynJ5BrjLuGfK/UJu6uwHxJ/di7sanqmUmxzmyIcd3vz58PMR7wpi8k3iWq2Q11KMYgZbUpRoIPw==",
       "dev": true,
       "requires": {
-        "@typescript-eslint/scope-manager": "4.11.0",
-        "@typescript-eslint/types": "4.11.0",
-        "@typescript-eslint/typescript-estree": "4.11.0",
+        "@typescript-eslint/scope-manager": "4.11.1",
+        "@typescript-eslint/types": "4.11.1",
+        "@typescript-eslint/typescript-estree": "4.11.1",
         "debug": "^4.1.1"
       }
     },
     "@typescript-eslint/scope-manager": {
-      "version": "4.11.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.11.0.tgz",
-      "integrity": "sha512-6VSTm/4vC2dHM3ySDW9Kl48en+yLNfVV6LECU8jodBHQOhO8adAVizaZ1fV0QGZnLQjQ/y0aBj5/KXPp2hBTjA==",
+      "version": "4.11.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.11.1.tgz",
+      "integrity": "sha512-Al2P394dx+kXCl61fhrrZ1FTI7qsRDIUiVSuN6rTwss6lUn8uVO2+nnF4AvO0ug8vMsy3ShkbxLu/uWZdTtJMQ==",
       "dev": true,
       "requires": {
-        "@typescript-eslint/types": "4.11.0",
-        "@typescript-eslint/visitor-keys": "4.11.0"
+        "@typescript-eslint/types": "4.11.1",
+        "@typescript-eslint/visitor-keys": "4.11.1"
       }
     },
     "@typescript-eslint/types": {
-      "version": "4.11.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.11.0.tgz",
-      "integrity": "sha512-XXOdt/NPX++txOQHM1kUMgJUS43KSlXGdR/aDyEwuAEETwuPt02Nc7v+s57PzuSqMbNLclblQdv3YcWOdXhQ7g==",
+      "version": "4.11.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.11.1.tgz",
+      "integrity": "sha512-5kvd38wZpqGY4yP/6W3qhYX6Hz0NwUbijVsX2rxczpY6OXaMxh0+5E5uLJKVFwaBM7PJe1wnMym85NfKYIh6CA==",
       "dev": true
     },
     "@typescript-eslint/typescript-estree": {
-      "version": "4.11.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.11.0.tgz",
-      "integrity": "sha512-eA6sT5dE5RHAFhtcC+b5WDlUIGwnO9b0yrfGa1mIOIAjqwSQCpXbLiFmKTdRbQN/xH2EZkGqqLDrKUuYOZ0+Hg==",
+      "version": "4.11.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.11.1.tgz",
+      "integrity": "sha512-tC7MKZIMRTYxQhrVAFoJq/DlRwv1bnqA4/S2r3+HuHibqvbrPcyf858lNzU7bFmy4mLeIHFYr34ar/1KumwyRw==",
       "dev": true,
       "requires": {
-        "@typescript-eslint/types": "4.11.0",
-        "@typescript-eslint/visitor-keys": "4.11.0",
+        "@typescript-eslint/types": "4.11.1",
+        "@typescript-eslint/visitor-keys": "4.11.1",
         "debug": "^4.1.1",
         "globby": "^11.0.1",
         "is-glob": "^4.0.1",
       }
     },
     "@typescript-eslint/visitor-keys": {
-      "version": "4.11.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.11.0.tgz",
-      "integrity": "sha512-tRYKyY0i7cMk6v4UIOCjl1LhuepC/pc6adQqJk4Is3YcC6k46HvsV9Wl7vQoLbm9qADgeujiT7KdLrylvFIQ+A==",
+      "version": "4.11.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.11.1.tgz",
+      "integrity": "sha512-IrlBhD9bm4bdYcS8xpWarazkKXlE7iYb1HzRuyBP114mIaj5DJPo11Us1HgH60dTt41TCZXMaTCAW+OILIYPOg==",
       "dev": true,
       "requires": {
-        "@typescript-eslint/types": "4.11.0",
+        "@typescript-eslint/types": "4.11.1",
         "eslint-visitor-keys": "^2.0.0"
       }
     },
       }
     },
     "ws": {
-      "version": "7.4.1",
-      "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.1.tgz",
-      "integrity": "sha512-pTsP8UAfhy3sk1lSk/O/s4tjD0CRwvMnzvwr4OKGX7ZvqZtUyx4KIJB5JWbkykPoc55tixMGgTNoh3k4FkNGFQ=="
+      "version": "7.4.2",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.2.tgz",
+      "integrity": "sha512-T4tewALS3+qsrpGI/8dqNMLIVdq/g/85U98HPMa6F0m6xTbvhXU6RCQLqPH3+SlomNV/LdY6RXEbBpMH6EOJnA=="
     },
     "xdg-basedir": {
       "version": "3.0.0",
index d29aa2dfbb8e60a4de121554f0a7839ad3f0e308..538e20c8ba18b92ee9be3c288bbde2917c468c41 100644 (file)
     "winston": "^3.3.3",
     "winston-daily-rotate-file": "^4.5.0",
     "worker-threads-pool": "^2.0.0",
-    "ws": "^7.4.1"
+    "ws": "^7.4.2"
   },
   "optionalDependencies": {
     "bufferutil": "^4.0.2",
     "utf-8-validate": "^5.0.3"
   },
   "devDependencies": {
-    "@types/node": "^14.14.16",
+    "@types/node": "^14.14.17",
     "@types/uuid": "^8.3.0",
     "@types/worker-threads-pool": "^2.0.0",
     "@types/ws": "^7.4.0",
-    "@typescript-eslint/eslint-plugin": "^4.11.0",
-    "@typescript-eslint/parser": "^4.11.0",
+    "@typescript-eslint/eslint-plugin": "^4.11.1",
+    "@typescript-eslint/parser": "^4.11.1",
     "clinic": "^8.0.1",
     "cross-env": "^7.0.3",
     "eslint": "^7.16.0",
index 4c042eaa930e5ff9e82a354ad21659ccb294997c..7bdcf6e1c1215e0295605362cfb62a183db6bd8f 100644 (file)
@@ -20,6 +20,7 @@ import MeasurandValues from '../types/MeasurandValues';
 import OCPPError from './OcppError';
 import Statistics from '../utils/Statistics';
 import Utils from '../utils/Utils';
+import { WebSocketCloseEventStatusCode } from '../types/WebSocket';
 import crypto from 'crypto';
 import fs from 'fs';
 import logger from '../utils/Logger';
@@ -109,8 +110,8 @@ export default class ChargingStation {
     this._configuration = this._getTemplateChargingStationConfiguration();
     this._supervisionUrl = this._getSupervisionURL();
     this._wsConnectionUrl = this._supervisionUrl + '/' + this._stationInfo.name;
-    this._connectionTimeout = this._getConnectionTimeout() * 1000; // Ms, zero for disabling
-    this._autoReconnectMaxRetries = this._getAutoReconnectMaxRetries(); // -1 for unlimited
+    this._connectionTimeout = this._getConnectionTimeout() * 1000; // Ms, 0 for disabling
+    this._autoReconnectMaxRetries = this._getAutoReconnectMaxRetries(); // -1 for unlimited, 0 for disabling
     // Build connectors if needed
     const maxConnectors = this._getMaxNumberOfConnectors();
     if (maxConnectors <= 0) {
@@ -143,7 +144,7 @@ export default class ChargingStation {
       if ((this._stationInfo.Connectors[0] ? templateMaxConnectors - 1 : templateMaxConnectors) > 0) {
         for (let index = 1; index <= maxConnectors; index++) {
           const randConnectorID = this._stationInfo.randomConnectors ? Utils.getRandomInt(Utils.convertToInt(lastConnector), 1) : index;
-          this._connectors[index] = Utils.cloneObject<Connector>(this._stationInfo.Connectors[randConnectorID]) ;
+          this._connectors[index] = Utils.cloneObject<Connector>(this._stationInfo.Connectors[randConnectorID]);
         }
       }
     }
@@ -184,6 +185,14 @@ export default class ChargingStation {
     return Utils.logPrefix(` ${this._stationInfo.name}:`);
   }
 
+  _isWebSocketOpen(): boolean {
+    return this._wsConnection?.readyState === WebSocket.OPEN;
+  }
+
+  _isRegistered(): boolean {
+    return this._bootNotificationResponse?.status === RegistrationStatus.ACCEPTED;
+  }
+
   _getTemplateChargingStationConfiguration(): ChargingStationConfiguration {
     return this._stationInfo.Configuration ? this._stationInfo.Configuration : {} as ChargingStationConfiguration;
   }
@@ -267,6 +276,13 @@ export default class ChargingStation {
     return -1;
   }
 
+  _getRegistrationMaxRetries(): number {
+    if (!Utils.isUndefined(this._stationInfo.registrationMaxRetries)) {
+      return this._stationInfo.registrationMaxRetries;
+    }
+    return -1;
+  }
+
   _getPowerDivider(): number {
     let powerDivider = this._getNumberOfConnectors();
     if (this._stationInfo.powerSharedByConnectors) {
@@ -339,7 +355,7 @@ export default class ChargingStation {
   }
 
   _getSupervisionURL(): string {
-    const supervisionUrls = Utils.cloneObject<string|string[]>(this._stationInfo.supervisionURL ? this._stationInfo.supervisionURL : Configuration.getSupervisionURLs()) ;
+    const supervisionUrls = Utils.cloneObject<string | string[]>(this._stationInfo.supervisionURL ? this._stationInfo.supervisionURL : Configuration.getSupervisionURLs());
     let indexUrl = 0;
     if (!Utils.isEmptyArray(supervisionUrls)) {
       if (Configuration.getDistributeStationsToTenantsEqually()) {
@@ -427,7 +443,7 @@ export default class ChargingStation {
     const webSocketPingInterval: number = this._getConfigurationKey('WebSocketPingInterval') ? Utils.convertToInt(this._getConfigurationKey('WebSocketPingInterval').value) : 0;
     if (webSocketPingInterval > 0 && !this._webSocketPingSetInterval) {
       this._webSocketPingSetInterval = setInterval(() => {
-        if (this._wsConnection?.readyState === WebSocket.OPEN) {
+        if (this._isWebSocketOpen()) {
           this._wsConnection.ping((): void => { });
         }
       }, webSocketPingInterval * 1000);
@@ -543,7 +559,7 @@ export default class ChargingStation {
     if (Utils.isUndefined(options.handshakeTimeout)) {
       options.handshakeTimeout = this._connectionTimeout;
     }
-    if (this._wsConnection?.readyState === WebSocket.OPEN && forceCloseOpened) {
+    if (this._isWebSocketOpen() && forceCloseOpened) {
       this._wsConnection.close();
     }
     this._wsConnection = new WebSocket(this._wsConnectionUrl, 'ocpp' + Constants.OCPP_VERSION_16, options);
@@ -578,7 +594,7 @@ export default class ChargingStation {
         await this.sendStatusNotification(Utils.convertToInt(connector), ChargePointStatus.UNAVAILABLE);
       }
     }
-    if (this._wsConnection?.readyState === WebSocket.OPEN) {
+    if (this._isWebSocketOpen()) {
       this._wsConnection.close();
     }
     this._bootNotificationResponse = null;
@@ -586,7 +602,6 @@ export default class ChargingStation {
   }
 
   async _reconnect(error): Promise<void> {
-    logger.error(this._logPrefix() + ' Socket: abnormally closed: %j', error);
     // Stop heartbeat
     this._stopHeartbeat();
     // Stop the ATG if needed
@@ -611,52 +626,52 @@ export default class ChargingStation {
 
   async onOpen(): Promise<void> {
     logger.info(`${this._logPrefix()} Is connected to server through ${this._wsConnectionUrl}`);
-    if (!this._hasSocketRestarted || this._hasStopped) {
+    if (!this._isRegistered()) {
       // Send BootNotification
-      this._bootNotificationResponse = await this.sendBootNotification();
-    }
-    if (this._bootNotificationResponse.status === RegistrationStatus.ACCEPTED) {
-      await this._startMessageSequence();
-    } else {
+      let registrationRetryCount = 0;
       do {
-        await Utils.sleep(this._bootNotificationResponse.interval * 1000);
-        // Resend BootNotification
         this._bootNotificationResponse = await this.sendBootNotification();
-      } while (this._bootNotificationResponse.status !== RegistrationStatus.ACCEPTED);
+        if (!this._isRegistered()) {
+          registrationRetryCount++;
+          await Utils.sleep(this._bootNotificationResponse.interval * 1000);
+        }
+      } while (!this._isRegistered() && (registrationRetryCount <= this._getRegistrationMaxRetries() || this._getRegistrationMaxRetries() === -1));
     }
-    if (this._hasSocketRestarted && this._bootNotificationResponse.status === RegistrationStatus.ACCEPTED) {
-      if (!Utils.isEmptyArray(this._messageQueue)) {
-        this._messageQueue.forEach((message, index) => {
-          if (this._wsConnection?.readyState === WebSocket.OPEN) {
+    if (this._isRegistered()) {
+      await this._startMessageSequence();
+      if (this._hasSocketRestarted && this._isWebSocketOpen()) {
+        if (!Utils.isEmptyArray(this._messageQueue)) {
+          this._messageQueue.forEach((message, index) => {
             this._messageQueue.splice(index, 1);
             this._wsConnection.send(message);
-          }
-        });
+          });
+        }
       }
+    } else {
+      logger.error(`${this._logPrefix()} Registration: max retries reached (${this._getRegistrationMaxRetries()}) or retry disabled (${this._getRegistrationMaxRetries()})`);
     }
     this._autoReconnectRetryCount = 0;
     this._hasSocketRestarted = false;
   }
 
   async onError(errorEvent): Promise<void> {
-    switch (errorEvent.code) {
-      case 'ECONNREFUSED':
-        await this._reconnect(errorEvent);
-        break;
-      default:
-        logger.error(this._logPrefix() + ' Socket error: %j', errorEvent);
-        break;
-    }
+    logger.error(this._logPrefix() + ' Socket error: %j', errorEvent);
+    // pragma switch (errorEvent.code) {
+    //   case 'ECONNREFUSED':
+    //     await this._reconnect(errorEvent);
+    //     break;
+    // }
   }
 
   async onClose(closeEvent): Promise<void> {
     switch (closeEvent) {
-      case 1000: // Normal close
-      case 1005:
-        logger.info(this._logPrefix() + ' Socket normally closed: %j', closeEvent);
+      case WebSocketCloseEventStatusCode.CLOSE_NORMAL: // Normal close
+      case WebSocketCloseEventStatusCode.CLOSE_NO_STATUS:
+        logger.info(`${this._logPrefix()} Socket normally closed with status '${Utils.getWebSocketCloseEventStatusString(closeEvent)}'`);
         this._autoReconnectRetryCount = 0;
         break;
       default: // Abnormal close
+        logger.error(`${this._logPrefix()} Socket abnormally closed with status '${Utils.getWebSocketCloseEventStatusString(closeEvent)}'`);
         await this._reconnect(closeEvent);
         break;
     }
@@ -1053,8 +1068,8 @@ export default class ChargingStation {
           messageToSend = JSON.stringify([messageType, messageId, commandParams.code ? commandParams.code : Constants.OCPP_ERROR_GENERIC_ERROR, commandParams.message ? commandParams.message : '', commandParams.details ? commandParams.details : {}]);
           break;
       }
-      // Check if wsConnection is ready
-      if (this._wsConnection?.readyState === WebSocket.OPEN) {
+      // Check if wsConnection opened and charging station registered
+      if (this._isWebSocketOpen() && (this._isRegistered() || commandName === 'BootNotification')) {
         if (this.getEnableStatistics()) {
           this._statistics.addMessage(commandName, messageType);
         }
index 59eebc40c805ebfc7ba609c68c92cbae5278f599..9af61810866a8bc556fa4ef54946a290533b6757 100644 (file)
@@ -42,6 +42,7 @@ export default interface ChargingStationTemplate {
   resetTime?: number;
   connectionTimeout?: number;
   autoReconnectMaxRetries?: number;
+  registrationMaxRetries?: number;
   reconnectExponentialDelay?: boolean;
   enableStatistics?: boolean;
   voltageOut?: number;
diff --git a/src/types/WebSocket.ts b/src/types/WebSocket.ts
new file mode 100644 (file)
index 0000000..0799f7b
--- /dev/null
@@ -0,0 +1,37 @@
+export const WebSocketCloseEventStatusString: Record<number, string> = Object.freeze({
+  1000: 'Normal Closure',
+  1001: 'Going Away',
+  1002: 'Protocol Error',
+  1003: 'Unsupported Frame Data',
+  1004: 'Reserved',
+  1005: 'No Status Received',
+  1006: 'Abnormal Closure',
+  1007: 'Invalid Frame Payload Data',
+  1008: 'Policy Violation',
+  1009: 'Message Too Large',
+  1010: 'Missing Extension',
+  1011: 'Server Internal Error',
+  1012: 'Service Restart',
+  1013: 'Try Again Later',
+  1014: 'Bad Gateway',
+  1015: 'TLS Handshake'
+});
+
+export enum WebSocketCloseEventStatusCode {
+  CLOSE_NORMAL = 1000,
+  CLOSE_GOING_AWAY = 1001,
+  CLOSE_PROTOCOL_ERROR = 1002,
+  CLOSE_UNSUPPORTED = 1003,
+  CLOSE_RESERVED = 1004,
+  CLOSE_NO_STATUS = 1005,
+  CLOSE_ABNORMAL = 1006,
+  CLOSE_INVALID_PAYLOAD = 1007,
+  CLOSE_POLICY_VIOLATION = 1008,
+  CLOSE_TOO_LARGE = 1009,
+  CLOSE_MISSING_EXTENSION = 1010,
+  CLOSE_SERVER_INTERNAL_ERROR = 1011,
+  CLOSE_SERVICE_RESTART = 1012,
+  CLOSE_TRY_AGAIN_LATER = 1013,
+  CLOSE_BAD_GATEWAY = 1014,
+  CLOSE_TLS_HANDSHAKE = 1015
+}
index c61a3197480c73d8b990d2b0440919c082a06121..812b32273b0e7d4a4462c2fd123fd7b569dbf9eb 100644 (file)
@@ -4,8 +4,6 @@ export default class Constants {
   static readonly ENTITY_CHARGING_STATION = 'ChargingStation';
   static readonly ENTITY_AUTOMATIC_TRANSACTION_GENERATOR = 'AutomaticTransactionGenerator';
 
-  static readonly WS_UNSUPPORTED_DATA = 1007;
-
   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 });
index 226f54c4df5abfa54386737d2e0ffe0ed7e40ac1..9969e2294210777d281f43a3278a1dc150b16aa6 100644 (file)
@@ -1,3 +1,4 @@
+import { WebSocketCloseEventStatusString } from '../types/WebSocket';
 import { v4 as uuid } from 'uuid';
 
 export default class Utils {
@@ -156,7 +157,7 @@ export default class Utils {
   }
 
   static isNullOrUndefined(value): boolean {
-    // eslint-disable-next-line no-eq-null
+    // eslint-disable-next-line no-eq-null, eqeqeq
     if (value == null) {
       return true;
     }
@@ -188,4 +189,24 @@ export default class Utils {
     const randomSum = delay * 0.2 * Math.random(); // 0-20% of the delay
     return delay + randomSum;
   }
+
+  static getWebSocketCloseEventStatusString(code: number): string {
+    if (code >= 0 && code <= 999) {
+      return '(Unused)';
+    } else if (code >= 1016) {
+      if (code <= 1999) {
+        return '(For WebSocket standard)';
+      } else if (code <= 2999) {
+        return '(For WebSocket extensions)';
+      } else if (code <= 3999) {
+        return '(For libraries and frameworks)';
+      } else if (code <= 4999) {
+        return '(For applications)';
+      }
+    }
+    if (!Utils.isUndefined(WebSocketCloseEventStatusString[code])) {
+      return WebSocketCloseEventStatusString[code];
+    }
+    return '(Unknown)';
+  }
 }