From 97cb0e121af25342787fbd18d966ff47ff5e2b5c Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Sat, 28 Mar 2026 15:36:18 +0100 Subject: [PATCH] fix: align ConnectionTimeOut semantics with OCPP spec MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit ConnectionTimeOut/EVConnectionTimeOut is the EV cable insertion timeout per OCPP 1.6 §9.1.6 and OCPP 2.0.1 TxCtrlr spec, not a WebSocket timeout. Split DEFAULT_CONNECTION_TIMEOUT into three purpose-specific constants: - DEFAULT_EV_CONNECTION_TIMEOUT (180s): OCPP cable insertion timeout - DEFAULT_WS_HANDSHAKE_TIMEOUT (30s): WebSocket handshake timeout - DEFAULT_WS_RECONNECT_DELAY (30s): WebSocket reconnection delay - DEFAULT_MESSAGE_TIMEOUT (30s): OCPP 2.0 MessageTimeout default Dissociate WebSocket handshake/reconnect from OCPP ConnectionTimeOut configuration. Initialize ConnectionTimeOut with 180s per spec. --- src/charging-station/ChargingStation.ts | 10 +++++----- .../ocpp/2.0/OCPP20VariableRegistry.ts | 2 +- src/utils/Constants.ts | 12 +++++++----- .../ocpp/2.0/OCPP20VariableManager.test.ts | 4 ++-- 4 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/charging-station/ChargingStation.ts b/src/charging-station/ChargingStation.ts index d2049f40..47e644fb 100644 --- a/src/charging-station/ChargingStation.ts +++ b/src/charging-station/ChargingStation.ts @@ -448,10 +448,10 @@ export class ChargingStation extends EventEmitter { if (getConfigurationKey(this, StandardParametersKey.ConnectionTimeOut) != null) { return convertToInt( getConfigurationKey(this, StandardParametersKey.ConnectionTimeOut)?.value ?? - Constants.DEFAULT_CONNECTION_TIMEOUT + Constants.DEFAULT_EV_CONNECTION_TIMEOUT ) } - return Constants.DEFAULT_CONNECTION_TIMEOUT + return Constants.DEFAULT_EV_CONNECTION_TIMEOUT } /** @@ -904,7 +904,7 @@ export class ChargingStation extends EventEmitter { params?: { closeOpened?: boolean; terminateOpened?: boolean } ): void { options = { - handshakeTimeout: secondsToMilliseconds(this.getConnectionTimeout()), + handshakeTimeout: secondsToMilliseconds(Constants.DEFAULT_WS_HANDSHAKE_TIMEOUT), ...this.stationInfo?.wsOptions, ...options, } @@ -2154,7 +2154,7 @@ export class ChargingStation extends EventEmitter { addConfigurationKey( this, StandardParametersKey.ConnectionTimeOut, - Constants.DEFAULT_CONNECTION_TIMEOUT.toString() + Constants.DEFAULT_EV_CONNECTION_TIMEOUT.toString() ) } this.saveOcppConfiguration() @@ -2407,7 +2407,7 @@ export class ChargingStation extends EventEmitter { const reconnectDelay = this.stationInfo?.reconnectExponentialDelay === true ? exponentialDelay(this.wsConnectionRetryCount) - : secondsToMilliseconds(this.getConnectionTimeout()) + : secondsToMilliseconds(Constants.DEFAULT_WS_RECONNECT_DELAY) const reconnectDelayWithdraw = 1000 const reconnectTimeout = reconnectDelay - reconnectDelayWithdraw > 0 ? reconnectDelay - reconnectDelayWithdraw : 0 diff --git a/src/charging-station/ocpp/2.0/OCPP20VariableRegistry.ts b/src/charging-station/ocpp/2.0/OCPP20VariableRegistry.ts index 88b7f0c1..617e235f 100644 --- a/src/charging-station/ocpp/2.0/OCPP20VariableRegistry.ts +++ b/src/charging-station/ocpp/2.0/OCPP20VariableRegistry.ts @@ -1529,7 +1529,7 @@ export const VARIABLE_REGISTRY: Record = { )]: { component: OCPP20ComponentName.OCPPCommCtrlr, dataType: DataEnumType.integer, - defaultValue: Constants.DEFAULT_CONNECTION_TIMEOUT.toString(), + defaultValue: Constants.DEFAULT_MESSAGE_TIMEOUT.toString(), description: 'Timeout (in seconds) waiting for responses to general OCPP messages.', instance: 'Default', max: 3600, diff --git a/src/utils/Constants.ts b/src/utils/Constants.ts index 75094232..18f745fa 100644 --- a/src/utils/Constants.ts +++ b/src/utils/Constants.ts @@ -27,26 +27,25 @@ export class Constants { static readonly DEFAULT_BOOT_NOTIFICATION_INTERVAL = 60000 // Ms static readonly DEFAULT_CIRCULAR_BUFFER_CAPACITY = 386 - static readonly DEFAULT_CONNECTION_TIMEOUT = 30 // Seconds static readonly DEFAULT_EV_CONNECTION_TIMEOUT = 180 // Seconds - static readonly DEFAULT_FLUCTUATION_PERCENT = 5 static readonly DEFAULT_HASH_ALGORITHM = 'sha384' static readonly DEFAULT_HEARTBEAT_INTERVAL = 60000 // Ms - static readonly DEFAULT_IDTAG = '00000000' static readonly DEFAULT_LOG_STATISTICS_INTERVAL = 60 // Seconds static readonly DEFAULT_MESSAGE_BUFFER_FLUSH_INTERVAL = 60000 // Ms + static readonly DEFAULT_MESSAGE_TIMEOUT = 30 // Seconds + static readonly DEFAULT_METER_VALUES_INTERVAL = 60000 // Ms static readonly DEFAULT_PERFORMANCE_DIRECTORY = 'performance' + static readonly DEFAULT_PERFORMANCE_RECORDS_DB_NAME = 'e-mobility-charging-stations-simulator' static readonly DEFAULT_PERFORMANCE_RECORDS_FILENAME = 'performanceRecords.json' - static readonly DEFAULT_STATION_INFO: Readonly> = Object.freeze({ automaticTransactionGeneratorPersistentConfiguration: true, autoReconnectMaxRetries: -1, @@ -86,10 +85,13 @@ export class Constants { static readonly DEFAULT_TX_UPDATED_INTERVAL = 30 // Seconds static readonly DEFAULT_UI_SERVER_HOST = 'localhost' - static readonly DEFAULT_UI_SERVER_PORT = 8080 + static readonly DEFAULT_UI_SERVER_PORT = 8080 static readonly DEFAULT_WEBSOCKET_PING_INTERVAL = 30 // Seconds + static readonly DEFAULT_WS_HANDSHAKE_TIMEOUT = 30 // Seconds + static readonly DEFAULT_WS_RECONNECT_DELAY = 30 // Seconds + static readonly EMPTY_FROZEN_OBJECT = Object.freeze({}) static readonly EMPTY_FUNCTION: () => void = Object.freeze(() => { diff --git a/tests/charging-station/ocpp/2.0/OCPP20VariableManager.test.ts b/tests/charging-station/ocpp/2.0/OCPP20VariableManager.test.ts index c2accbee..6e5fd3b4 100644 --- a/tests/charging-station/ocpp/2.0/OCPP20VariableManager.test.ts +++ b/tests/charging-station/ocpp/2.0/OCPP20VariableManager.test.ts @@ -377,7 +377,7 @@ await describe('B05 - OCPP20VariableManager', async () => { // Third variable: MessageTimeout assert.strictEqual(result[2].attributeStatus, GetVariableStatusEnumType.Accepted) assert.strictEqual(result[2].attributeType, AttributeEnumType.Actual) - assert.strictEqual(result[2].attributeValue, Constants.DEFAULT_CONNECTION_TIMEOUT.toString()) + assert.strictEqual(result[2].attributeValue, Constants.DEFAULT_MESSAGE_TIMEOUT.toString()) assert.strictEqual(result[2].component.name, OCPP20ComponentName.OCPPCommCtrlr) assert.strictEqual(result[2].component.instance, 'Default') assert.strictEqual(result[2].variable.name, OCPP20RequiredVariableName.MessageTimeout) @@ -1009,7 +1009,7 @@ await describe('B05 - OCPP20VariableManager', async () => { await it('should validate MessageTimeout positive integer >0 and reject invalid', () => { const okRes = manager.setVariables(station, [ { - attributeValue: (Constants.DEFAULT_CONNECTION_TIMEOUT + 5).toString(), + attributeValue: (Constants.DEFAULT_MESSAGE_TIMEOUT + 5).toString(), component: { instance: 'Default', name: OCPP20ComponentName.OCPPCommCtrlr }, variable: { name: OCPP20RequiredVariableName.MessageTimeout }, }, -- 2.43.0