Task #139 — Modbus connection-layer config knobs (keep-alive / idle / reconnect)
Promotes the previously hardcoded transport-layer settings to ModbusDriverOptions so users can tune them through DriverConfig JSON without recompiling. Three new option groups: 1. KeepAlive (ModbusKeepAliveOptions): Enabled / Time / Interval / RetryCount. Defaults preserve the historical PR 53 wire output exactly (Enabled=true, Time=30s, Interval=10s, RetryCount=3). Set Enabled=false for PLCs that reject SO_KEEPALIVE. 2. IdleDisconnectTimeout (TimeSpan?): when set, the transport tracks last-PDU- success and proactively closes + reconnects on the next request after the threshold. Defends against silent NAT / firewall socket reaping. Default null = disabled (no behaviour change). 3. Reconnect (ModbusReconnectOptions): InitialDelay / MaxDelay / BackoffMultiplier for the post-drop reconnect loop. Defaults (InitialDelay=0, MaxDelay=30s, Multiplier=2.0) preserve the historical immediate-retry behaviour for the first attempt and add geometric backoff only if the reconnect itself fails. Capped at 10 attempts before propagating. ModbusTcpTransport ctor extended with optional keepAlive / idleDisconnect / reconnect parameters; existing 4-arg call sites continue to compile. Factory DTO gains parallel KeepAlive / IdleDisconnectMs / Reconnect fields with default-aware binding. 5 new ModbusConnectionOptionsTests covering the default-preservation contract (every default field matches pre-#139) and the JSON-binding round-trip for each knob group. Existing 204 unit tests still green.
This commit is contained in:
@@ -52,6 +52,20 @@ public static class ModbusDriverFactoryExtensions
|
||||
Timeout = TimeSpan.FromMilliseconds(dto.Probe?.TimeoutMs ?? 2_000),
|
||||
ProbeAddress = dto.Probe?.ProbeAddress ?? 0,
|
||||
},
|
||||
KeepAlive = dto.KeepAlive is null ? new ModbusKeepAliveOptions() : new ModbusKeepAliveOptions
|
||||
{
|
||||
Enabled = dto.KeepAlive.Enabled ?? true,
|
||||
Time = TimeSpan.FromMilliseconds(dto.KeepAlive.TimeMs ?? 30_000),
|
||||
Interval = TimeSpan.FromMilliseconds(dto.KeepAlive.IntervalMs ?? 10_000),
|
||||
RetryCount = dto.KeepAlive.RetryCount ?? 3,
|
||||
},
|
||||
IdleDisconnectTimeout = dto.IdleDisconnectMs is { } ms ? TimeSpan.FromMilliseconds(ms) : null,
|
||||
Reconnect = dto.Reconnect is null ? new ModbusReconnectOptions() : new ModbusReconnectOptions
|
||||
{
|
||||
InitialDelay = TimeSpan.FromMilliseconds(dto.Reconnect.InitialDelayMs ?? 0),
|
||||
MaxDelay = TimeSpan.FromMilliseconds(dto.Reconnect.MaxDelayMs ?? 30_000),
|
||||
BackoffMultiplier = dto.Reconnect.BackoffMultiplier ?? 2.0,
|
||||
},
|
||||
};
|
||||
|
||||
return new ModbusDriver(options, driverInstanceId);
|
||||
@@ -136,6 +150,26 @@ public static class ModbusDriverFactoryExtensions
|
||||
public bool? AutoReconnect { get; init; }
|
||||
public List<ModbusTagDto>? Tags { get; init; }
|
||||
public ModbusProbeDto? Probe { get; init; }
|
||||
|
||||
// #139 connection-layer knobs.
|
||||
public ModbusKeepAliveDto? KeepAlive { get; init; }
|
||||
public int? IdleDisconnectMs { get; init; }
|
||||
public ModbusReconnectDto? Reconnect { get; init; }
|
||||
}
|
||||
|
||||
internal sealed class ModbusKeepAliveDto
|
||||
{
|
||||
public bool? Enabled { get; init; }
|
||||
public int? TimeMs { get; init; }
|
||||
public int? IntervalMs { get; init; }
|
||||
public int? RetryCount { get; init; }
|
||||
}
|
||||
|
||||
internal sealed class ModbusReconnectDto
|
||||
{
|
||||
public int? InitialDelayMs { get; init; }
|
||||
public int? MaxDelayMs { get; init; }
|
||||
public double? BackoffMultiplier { get; init; }
|
||||
}
|
||||
|
||||
internal sealed class ModbusTagDto
|
||||
|
||||
Reference in New Issue
Block a user