From: Jérôme Benoit Date: Sun, 22 Nov 2020 22:07:52 +0000 (+0100) Subject: Add exponential delay at reconnect X-Git-Tag: v1.0.1-0~186 X-Git-Url: https://git.piment-noir.org/?a=commitdiff_plain;h=032d6efcb5be418f04d38c39533e7d73d0337195;p=e-mobility-charging-stations-simulator.git Add exponential delay at reconnect The feature is still buggy ... Signed-off-by: Jérôme Benoit --- diff --git a/docker/config.json b/docker/config.json index aee90bc7..7dcbd0b1 100644 --- a/docker/config.json +++ b/docker/config.json @@ -3,7 +3,7 @@ "ws://server:8010/OCPP16/5c866e81a2d9593de43efdb4" ], "statisticsDisplayInterval": 60, - "autoReconnectTimeout": 10, + "connectionTimeout": 10, "autoReconnectMaxRetries": 10, "distributeStationsToTenantsEqually": true, "useWorkerPool": false, diff --git a/src/assets/config-template.json b/src/assets/config-template.json index 7359a304..7b3dc148 100644 --- a/src/assets/config-template.json +++ b/src/assets/config-template.json @@ -2,7 +2,7 @@ "supervisionURLs": [ "ws://localhost:8010/OCPP16/5be7fb271014d90008992f06" ], - "autoReconnectTimeout": 10, + "connectionTimeout": 10, "autoReconnectMaxRetries": 10, "statisticsDisplayInterval": 60, "distributeStationsToTenantsEqually": true, diff --git a/src/charging-station/ChargingStation.ts b/src/charging-station/ChargingStation.ts index 85045f7e..b63bd2c2 100644 --- a/src/charging-station/ChargingStation.ts +++ b/src/charging-station/ChargingStation.ts @@ -42,9 +42,9 @@ export default class ChargingStation { private _wsConnection: WebSocket; private _hasStopped: boolean; private _hasSocketRestarted: boolean; + private _connectionTimeout: number; private _autoReconnectRetryCount: number; private _autoReconnectMaxRetries: number; - private _autoReconnectTimeout: number; private _requests: Requests; private _messageQueue: string[]; private _automaticTransactionGeneration: AutomaticTransactionGenerator; @@ -63,9 +63,9 @@ export default class ChargingStation { this._hasStopped = false; this._hasSocketRestarted = false; + this._connectionTimeout = Configuration.getConnectionTimeout() * 1000; // Ms, zero for disabling this._autoReconnectRetryCount = 0; this._autoReconnectMaxRetries = Configuration.getAutoReconnectMaxRetries(); // -1 for unlimited - this._autoReconnectTimeout = Configuration.getAutoReconnectTimeout() * 1000; // Ms, zero for disabling this._requests = {} as Requests; this._messageQueue = [] as string[]; @@ -295,7 +295,7 @@ export default class ChargingStation { return !Utils.isUndefined(this._stationInfo.voltageOut) ? Utils.convertToInt(this._stationInfo.voltageOut) : defaultVoltageOut; } - _getTransactionidTag(transactionId: number): string { + _getTransactionIdTag(transactionId: number): string { for (const connector in this._connectors) { if (this.getConnector(Utils.convertToInt(connector)).transactionId === transactionId) { return this.getConnector(Utils.convertToInt(connector)).idTag; @@ -322,6 +322,10 @@ export default class ChargingStation { return supervisionUrls as string; } + _getReconnectExponentialDelay(): boolean { + return !Utils.isUndefined(this._stationInfo.reconnectExponentialDelay) ? this._stationInfo.reconnectExponentialDelay : false; + } + _getAuthorizeRemoteTxRequests(): boolean { const authorizeRemoteTxRequests = this._getConfigurationKey('AuthorizeRemoteTxRequests'); return authorizeRemoteTxRequests ? Utils.convertToBoolean(authorizeRemoteTxRequests.value) : false; @@ -499,8 +503,14 @@ export default class ChargingStation { } } - _openWSConnection(): void { - this._wsConnection = new WebSocket(this._wsConnectionUrl, 'ocpp' + Constants.OCPP_VERSION_16); + _openWSConnection(options?: WebSocket.ClientOptions): void { + if (Utils.isUndefined(options)) { + options = {} as WebSocket.ClientOptions; + } + if (Utils.isUndefined(options.handshakeTimeout)) { + options.handshakeTimeout = this._connectionTimeout; + } + this._wsConnection = new WebSocket(this._wsConnectionUrl, 'ocpp' + Constants.OCPP_VERSION_16, options); logger.info(this._logPrefix() + ' Will communicate through URL ' + this._supervisionUrl); } @@ -537,7 +547,7 @@ export default class ChargingStation { this._hasStopped = true; } - _reconnect(error): void { + async _reconnect(error): Promise { logger.error(this._logPrefix() + ' Socket: abnormally closed: %j', error); // Stop heartbeat this._stopHeartbeat(); @@ -548,16 +558,15 @@ export default class ChargingStation { !this._automaticTransactionGeneration.timeToStop) { this._automaticTransactionGeneration.stop().catch(() => { }); } - if (this._autoReconnectTimeout !== 0 && - (this._autoReconnectRetryCount < this._autoReconnectMaxRetries || this._autoReconnectMaxRetries === -1)) { - logger.error(`${this._logPrefix()} Socket: connection retry with timeout ${this._autoReconnectTimeout}ms`); + if (this._autoReconnectRetryCount < this._autoReconnectMaxRetries || this._autoReconnectMaxRetries === -1) { this._autoReconnectRetryCount++; - setTimeout(() => { - logger.error(this._logPrefix() + ' Socket: reconnecting try #' + this._autoReconnectRetryCount.toString()); - this._openWSConnection(); - }, this._autoReconnectTimeout); - } else if (this._autoReconnectTimeout !== 0 || this._autoReconnectMaxRetries !== -1) { - logger.error(`${this._logPrefix()} Socket: max retries reached (${this._autoReconnectRetryCount}) or retry disabled (${this._autoReconnectTimeout})`); + const reconnectDelay = (this._getReconnectExponentialDelay() ? Utils.exponentialDelay(this._autoReconnectRetryCount) : this._connectionTimeout); + logger.error(`${this._logPrefix()} Socket: connection retry in ${Utils.roundTo(reconnectDelay, 2)}ms, timeout ${reconnectDelay - 100}ms`); + await Utils.sleep(reconnectDelay); + logger.error(this._logPrefix() + ' Socket: reconnecting try #' + this._autoReconnectRetryCount.toString()); + this._openWSConnection({ handshakeTimeout : reconnectDelay - 100 }); + } else if (this._autoReconnectMaxRetries !== -1) { + logger.error(`${this._logPrefix()} Socket: max retries reached (${this._autoReconnectRetryCount}) or retry disabled (${this._autoReconnectMaxRetries})`); } } @@ -578,15 +587,15 @@ export default class ChargingStation { }); } } - this._hasSocketRestarted = false; this._autoReconnectRetryCount = 0; + this._hasSocketRestarted = false; } - onError(errorEvent): void { + async onError(errorEvent): Promise { switch (errorEvent.code) { case 'ECONNREFUSED': this._hasSocketRestarted = true; - this._reconnect(errorEvent); + await this._reconnect(errorEvent); break; default: logger.error(this._logPrefix() + ' Socket error: %j', errorEvent); @@ -594,7 +603,7 @@ export default class ChargingStation { } } - onClose(closeEvent): void { + async onClose(closeEvent): Promise { switch (closeEvent) { case 1000: // Normal close case 1005: @@ -603,7 +612,7 @@ export default class ChargingStation { break; default: // Abnormal close this._hasSocketRestarted = true; - this._reconnect(closeEvent); + await this._reconnect(closeEvent); break; } } @@ -732,7 +741,7 @@ export default class ChargingStation { } async sendStopTransaction(transactionId: number, reason: StopTransactionReason = StopTransactionReason.NONE): Promise { - const idTag = this._getTransactionidTag(transactionId); + const idTag = this._getTransactionIdTag(transactionId); try { const payload = { transactionId, diff --git a/src/types/ChargingStationTemplate.ts b/src/types/ChargingStationTemplate.ts index 255fb8d0..5c2081db 100644 --- a/src/types/ChargingStationTemplate.ts +++ b/src/types/ChargingStationTemplate.ts @@ -40,6 +40,7 @@ export default interface ChargingStationTemplate { useConnectorId0?: boolean; randomConnectors?: boolean; resetTime?: number; + reconnectExponentialDelay?: boolean; enableStatistics?: boolean; voltageOut?: number; Configuration?: ChargingStationConfiguration; diff --git a/src/types/ConfigurationData.ts b/src/types/ConfigurationData.ts index bf4420ec..371e9c5d 100644 --- a/src/types/ConfigurationData.ts +++ b/src/types/ConfigurationData.ts @@ -7,7 +7,7 @@ export default interface ConfigurationData { supervisionURLs?: string[]; stationTemplateURLs: StationTemplateURL[]; statisticsDisplayInterval?: number; - autoReconnectTimeout?: number; + connectionTimeout?: number; autoReconnectMaxRetries?: number; distributeStationsToTenantsEqually?: boolean; useWorkerPool?: boolean; diff --git a/src/utils/Configuration.ts b/src/utils/Configuration.ts index ed5eb2d4..fef4d93a 100644 --- a/src/utils/Configuration.ts +++ b/src/utils/Configuration.ts @@ -11,9 +11,10 @@ export default class Configuration { return Utils.objectHasOwnProperty(Configuration.getConfig(), 'statisticsDisplayInterval') ? Configuration.getConfig().statisticsDisplayInterval : 60; } - static getAutoReconnectTimeout(): number { + static getConnectionTimeout(): number { + Configuration.deprecateConfigurationKey('autoReconnectTimeout', 'Use \'connectionTimeout\' instead'); // Read conf - return Utils.objectHasOwnProperty(Configuration.getConfig(), 'autoReconnectTimeout') ? Configuration.getConfig().autoReconnectTimeout : 10; + return Utils.objectHasOwnProperty(Configuration.getConfig(), 'connectionTimeout') ? Configuration.getConfig().connectionTimeout : 10; } static getAutoReconnectMaxRetries(): number { diff --git a/src/utils/Utils.ts b/src/utils/Utils.ts index d6fe05d4..a1f28155 100644 --- a/src/utils/Utils.ts +++ b/src/utils/Utils.ts @@ -176,4 +176,14 @@ export default class Utils { } static insertAt = (str: string, subStr: string, pos: number): string => `${str.slice(0, pos)}${subStr}${str.slice(pos)}`; + + /** + * @param {number} [retryNumber=0] + * @return {number} - delay in milliseconds + */ + static exponentialDelay(retryNumber = 0): number { + const delay = Math.pow(2, retryNumber) * 100; + const randomSum = delay * 0.2 * Math.random(); // 0-20% of the delay + return delay + randomSum; + } }