Auto: opcuaclient-7 — selective import + namespace remap

Adds OpcUaClientCurationOptions on OpcUaClientDriverOptions.Curation with:
- IncludePaths/ExcludePaths globs (* and ? only) matched against the
  slash-joined BrowsePath segments collected during BrowseRecursiveAsync.
  Empty Include = include all so existing deployments are unaffected;
  Exclude wins over Include. Pruned folders are skipped wholesale, so
  descendants don't reach the wire.
- NamespaceRemap (URI -> URI) applied to DriverAttributeInfo.FullName when
  registering variables. Index-0 / standard nodes round-trip unchanged;
  remapped nodes serialise via ExpandedNodeId so downstream clients see
  the local-side URI.
- RootAlias replaces the hard-coded "Remote" folder name at the top of
  the mirrored tree.

Closes #279
This commit is contained in:
Joseph Doherty
2026-04-25 20:20:47 -04:00
parent 1d3e9a3237
commit 02d1c85190
3 changed files with 318 additions and 7 deletions

View File

@@ -181,8 +181,66 @@ public sealed class OpcUaClientDriverOptions
/// since SHA-1 is spec-deprecated for OPC UA.
/// </summary>
public OpcUaCertificateValidationOptions CertificateValidation { get; init; } = new();
/// <summary>
/// Curation rules applied to the upstream address space during
/// <c>DiscoverAsync</c>. Lets operators trim the mirrored tree to the subset their
/// downstream clients actually need, rename namespace URIs so the local-side metadata
/// stays consistent across upstream-server swaps, and override the default
/// <c>"Remote"</c> root folder name. Defaults are empty / null which preserves the
/// pre-curation behaviour exactly — empty include = include all.
/// </summary>
public OpcUaClientCurationOptions Curation { get; init; } = new();
}
/// <summary>
/// Selective import + namespace remap rules for the OPC UA Client driver. Pure local
/// filtering inside <c>BrowseRecursiveAsync</c> + <c>EnrichAndRegisterVariablesAsync</c>;
/// no new SDK calls.
/// </summary>
/// <remarks>
/// <para>
/// <b>Glob semantics</b>: patterns are matched against the slash-joined BrowseName
/// segments accumulated during the browse pass (e.g. <c>"Server/Diagnostics/SessionsDiagnosticsArray"</c>).
/// Two wildcards are supported — <c>*</c> matches any sequence of characters
/// (including empty / slashes) and <c>?</c> matches exactly one character. No
/// character classes, no <c>**</c>, no escapes — keep the surface tight so the doc
/// + behaviour stay simple.
/// </para>
/// <para>
/// Empty <see cref="IncludePaths"/> = include all (existing behaviour).
/// <see cref="ExcludePaths"/> wins over <see cref="IncludePaths"/> when both match.
/// Folders pruned by the rules are skipped wholesale — their descendants don't get
/// browsed, which keeps the wire cost down on large servers.
/// </para>
/// </remarks>
/// <param name="IncludePaths">
/// Glob patterns matched against the BrowsePath segment list. Empty = include all
/// (default — preserves pre-curation behaviour).
/// </param>
/// <param name="ExcludePaths">
/// Glob patterns matched against the BrowsePath segment list. Wins over
/// <see cref="IncludePaths"/> — useful for "include everything under <c>Plant/*</c>
/// except <c>Plant/Diagnostics</c>" rules.
/// </param>
/// <param name="NamespaceRemap">
/// Upstream-namespace-URI → local-namespace-URI translation table applied to the
/// <c>FullName</c> field of <c>DriverAttributeInfo</c> when registering variables.
/// The driver's stored <c>FullName</c> swaps the prefix before persisting so downstream
/// clients see the remapped URI. Lookup is case-sensitive — match the upstream URI
/// exactly. Defaults to empty (no remap).
/// </param>
/// <param name="RootAlias">
/// Replaces the default <c>"Remote"</c> folder name at the top of the mirrored tree.
/// Useful when multiple OPC UA Client drivers are aggregated and operators need to
/// distinguish them in the local browse tree. Default <c>null</c> = use <c>"Remote"</c>.
/// </param>
public sealed record OpcUaClientCurationOptions(
IReadOnlyList<string>? IncludePaths = null,
IReadOnlyList<string>? ExcludePaths = null,
IReadOnlyDictionary<string, string>? NamespaceRemap = null,
string? RootAlias = null);
/// <summary>
/// Knobs governing the server-certificate validation callback. Plumbed onto
/// <see cref="OpcUaClientDriverOptions.CertificateValidation"/> rather than the top-level