}
},
"@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",
"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",
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';
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) {
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]);
}
}
}
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;
}
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) {
}
_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()) {
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);
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);
await this.sendStatusNotification(Utils.convertToInt(connector), ChargePointStatus.UNAVAILABLE);
}
}
- if (this._wsConnection?.readyState === WebSocket.OPEN) {
+ if (this._isWebSocketOpen()) {
this._wsConnection.close();
}
this._bootNotificationResponse = null;
}
async _reconnect(error): Promise<void> {
- logger.error(this._logPrefix() + ' Socket: abnormally closed: %j', error);
// Stop heartbeat
this._stopHeartbeat();
// Stop the ATG if needed
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;
}
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);
}
resetTime?: number;
connectionTimeout?: number;
autoReconnectMaxRetries?: number;
+ registrationMaxRetries?: number;
reconnectExponentialDelay?: boolean;
enableStatistics?: boolean;
voltageOut?: number;
--- /dev/null
+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
+}
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 });
+import { WebSocketCloseEventStatusString } from '../types/WebSocket';
import { v4 as uuid } from 'uuid';
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;
}
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)';
+ }
}