fix(driver-modbus): probe parses the factory DTO shape — timeoutMs no longer silently dropped (R2-11, 05/CONV-2)
This commit is contained in:
@@ -29,19 +29,44 @@ 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>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>
|
||||
/// <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>
|
||||
internal static (ProbeTarget? Target, string? Error) TryParseProbeTarget(string configJson, TimeSpan fallbackTimeout)
|
||||
{
|
||||
ModbusDriverFactoryExtensions.ModbusDriverConfigDto? dto;
|
||||
try { dto = JsonSerializer.Deserialize<ModbusDriverFactoryExtensions.ModbusDriverConfigDto>(configJson, _opts); }
|
||||
catch (Exception ex) { return (null, $"Config JSON is invalid: {ex.Message}"); }
|
||||
if (dto is null) return (null, "Config JSON deserialized to null.");
|
||||
|
||||
var port = dto.Port ?? 502;
|
||||
if (string.IsNullOrWhiteSpace(dto.Host) || port <= 0)
|
||||
return (null, "Config has no host/port to probe.");
|
||||
|
||||
var timeout = dto.TimeoutMs is { } ms && ms > 0 ? TimeSpan.FromMilliseconds(ms) : fallbackTimeout;
|
||||
return (new ProbeTarget(dto.Host!, port, dto.UnitId ?? 1, timeout), null);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<DriverProbeResult> ProbeAsync(string configJson, TimeSpan timeout, CancellationToken ct)
|
||||
{
|
||||
ModbusDriverOptions? opts;
|
||||
try { opts = JsonSerializer.Deserialize<ModbusDriverOptions>(configJson, _opts); }
|
||||
catch (Exception ex) { return new(false, $"Config JSON is invalid: {ex.Message}", null); }
|
||||
if (opts is null) return new(false, "Config JSON deserialized to null.", null);
|
||||
var (target, error) = TryParseProbeTarget(configJson, timeout);
|
||||
if (target is not { } t) return new(false, error!, null);
|
||||
|
||||
var (host, port) = ExtractTarget(opts);
|
||||
if (string.IsNullOrWhiteSpace(host) || port <= 0)
|
||||
return new(false, "Config has no host/port to probe.", null);
|
||||
|
||||
var unitId = opts.UnitId;
|
||||
var host = t.Host;
|
||||
var port = t.Port;
|
||||
var unitId = t.UnitId;
|
||||
// Honour the config's timeoutMs (factory parity) — the caller's timeout is the fallback.
|
||||
timeout = t.Timeout;
|
||||
|
||||
var sw = Stopwatch.StartNew();
|
||||
using var cts = CancellationTokenSource.CreateLinkedTokenSource(ct);
|
||||
@@ -93,7 +118,4 @@ public sealed class ModbusDriverProbe : IDriverProbe
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static (string host, int port) ExtractTarget(ModbusDriverOptions opts)
|
||||
=> (opts.Host, opts.Port);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user