feat(modbus-rtu): route driver + probe transports through ModbusTransportFactory
Claude-Session: https://claude.ai/code/session_01GASWkNEi68FSCtvr6rLoEW
This commit is contained in:
@@ -29,15 +29,19 @@ public sealed class ModbusDriverProbe : IDriverProbe
|
||||
/// <inheritdoc />
|
||||
public string DriverType => "Modbus";
|
||||
|
||||
/// <summary>The parsed probe target — host/port/unitId + the effective connection timeout, all read
|
||||
/// from the SAME factory DTO shape (<c>ModbusDriverConfigDto</c>) the driver factory parses, so
|
||||
/// <c>timeoutMs</c> binds identically to the factory (R2-11, 05/CONV-2; the OpcUaClient parity rule).</summary>
|
||||
internal readonly record struct ProbeTarget(string Host, int Port, byte UnitId, TimeSpan Timeout);
|
||||
/// <summary>The parsed probe target — host/port/unitId + the effective connection timeout + the wire
|
||||
/// transport mode, all read from the SAME factory DTO shape (<c>ModbusDriverConfigDto</c>) the driver
|
||||
/// factory parses, so <c>timeoutMs</c> binds identically to the factory (R2-11, 05/CONV-2; the
|
||||
/// OpcUaClient parity rule) and an <c>RtuOverTcp</c>-authored config probes over RTU framing —
|
||||
/// matching how the driver will actually run.</summary>
|
||||
internal readonly record struct ProbeTarget(string Host, int Port, byte UnitId, TimeSpan Timeout, ModbusTransportMode Transport);
|
||||
|
||||
/// <summary>Parse the driver config JSON into a <see cref="ProbeTarget"/> using the factory DTO shape.
|
||||
/// Returns a null target + an error message when the JSON is invalid or has no host/port. The effective
|
||||
/// timeout mirrors the factory (<c>TimeSpan.FromMilliseconds(dto.TimeoutMs ?? …)</c>); when the config
|
||||
/// omits <c>timeoutMs</c> the caller's <paramref name="fallbackTimeout"/> is used.</summary>
|
||||
/// omits <c>timeoutMs</c> the caller's <paramref name="fallbackTimeout"/> is used. <c>Transport</c>
|
||||
/// mirrors the factory's null-defaults-to-Tcp convention (<see cref="ModbusDriverFactoryExtensions"/>);
|
||||
/// an unrecognized value is reported as a probe-target error rather than silently falling back.</summary>
|
||||
/// <param name="configJson">The driver config JSON (factory DTO shape).</param>
|
||||
/// <param name="fallbackTimeout">The timeout used when the config omits <c>timeoutMs</c>.</param>
|
||||
/// <returns>The parsed target, or a null target with an error string.</returns>
|
||||
@@ -52,8 +56,15 @@ public sealed class ModbusDriverProbe : IDriverProbe
|
||||
if (string.IsNullOrWhiteSpace(dto.Host) || port <= 0)
|
||||
return (null, "Config has no host/port to probe.");
|
||||
|
||||
var transportMode = ModbusTransportMode.Tcp;
|
||||
if (dto.Transport is not null && !Enum.TryParse(dto.Transport, ignoreCase: true, out transportMode))
|
||||
{
|
||||
return (null, $"Config has unknown Transport '{dto.Transport}'. " +
|
||||
$"Expected one of {string.Join(", ", Enum.GetNames<ModbusTransportMode>())}");
|
||||
}
|
||||
|
||||
var timeout = dto.TimeoutMs is { } ms && ms > 0 ? TimeSpan.FromMilliseconds(ms) : fallbackTimeout;
|
||||
return (new ProbeTarget(dto.Host!, port, dto.UnitId ?? 1, timeout), null);
|
||||
return (new ProbeTarget(dto.Host!, port, dto.UnitId ?? 1, timeout, transportMode), null);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -72,9 +83,17 @@ public sealed class ModbusDriverProbe : IDriverProbe
|
||||
using var cts = CancellationTokenSource.CreateLinkedTokenSource(ct);
|
||||
cts.CancelAfter(timeout);
|
||||
|
||||
// Phase 1 — TCP connect (using ModbusTcpTransport which handles IPv4 preference).
|
||||
// Phase 1 — TCP connect, over whichever transport the config's Transport mode selects
|
||||
// (ModbusTransportFactory is the single mapping point — same switch the live driver uses).
|
||||
// autoReconnect=false: this is a one-shot probe, no retry loops.
|
||||
var transport = new ModbusTcpTransport(host, port, timeout, autoReconnect: false);
|
||||
var transport = ModbusTransportFactory.Create(new ModbusDriverOptions
|
||||
{
|
||||
Host = host,
|
||||
Port = port,
|
||||
Timeout = timeout,
|
||||
Transport = t.Transport,
|
||||
AutoReconnect = false,
|
||||
});
|
||||
await using (transport.ConfigureAwait(false))
|
||||
{
|
||||
try
|
||||
|
||||
Reference in New Issue
Block a user