Auto: opcuaclient-6 — Discovery URL FindServers

Adds optional `DiscoveryUrl` knob to OpcUaClientDriverOptions. When set,
the driver runs `DiscoveryClient.CreateAsync` + `FindServersAsync` +
`GetEndpointsAsync` against that URL during InitializeAsync and prepends
the discovered endpoint URLs (filtered to matching SecurityPolicy +
SecurityMode) to the failover candidate list. De-duplicates URLs that
appear in both discovered and static lists (case-insensitive). Discovery
failures are non-fatal — falls back to statically configured candidates.

The doc comment notes that FindServers requires SecurityMode=None on the
discovery channel per OPC UA spec, even when the data channel uses Sign
or SignAndEncrypt.

Closes #278

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Joseph Doherty
2026-04-25 20:10:59 -04:00
parent e879b3ae90
commit 0f509fbd3a
3 changed files with 239 additions and 4 deletions

View File

@@ -39,6 +39,34 @@ public sealed class OpcUaClientDriverOptions
/// </summary>
public TimeSpan PerEndpointConnectTimeout { get; init; } = TimeSpan.FromSeconds(3);
/// <summary>
/// Optional discovery URL pointing at a Local Discovery Server (LDS) or a server's
/// own discovery endpoint. When set, the driver runs <c>FindServers</c> +
/// <c>GetEndpoints</c> against this URL during <see cref="OpcUaClientDriver.InitializeAsync"/>
/// and prepends the discovered endpoint URLs to the failover candidate list. When
/// <see cref="EndpointUrls"/> is empty (and only <see cref="EndpointUrl"/> is set as
/// a fallback), the discovered URLs replace the candidate list entirely so a
/// discovery-driven deployment can be configured without specifying any endpoints
/// up front. Discovery failures are non-fatal — the driver logs and falls back to the
/// statically configured candidates.
/// </summary>
/// <remarks>
/// <para>
/// <b>FindServers requires SecurityMode=None on the discovery channel</b> per the
/// OPC UA spec — discovery is unauthenticated even when the data channel uses
/// <c>Sign</c> or <c>SignAndEncrypt</c>. The driver opens the discovery channel
/// unsecured regardless of <see cref="SecurityMode"/>; only the resulting data
/// session is bound to the configured policy.
/// </para>
/// <para>
/// Endpoints returned by discovery are filtered to those matching
/// <see cref="SecurityPolicy"/> + <see cref="SecurityMode"/> before being added to
/// the candidate list, so a discovery sweep against a multi-policy server only
/// surfaces endpoints the driver could actually connect to.
/// </para>
/// </remarks>
public string? DiscoveryUrl { get; init; }
/// <summary>
/// Security policy to require when selecting an endpoint. Either a
/// <see cref="OpcUaSecurityPolicy"/> enum constant or a free-form string (for