Auto: opcuaclient-8 — type definition mirroring

Adds an opt-in pass-3 walk of the upstream TypesFolder (i=86) so the OPC UA
Client driver can mirror upstream type definitions into the local address
space. Honours the curation rules from PR-7 (#359). Structural mirror only —
binary-encoding priming via LoadDataTypeSystem is tracked as a follow-up
because that helper was removed from the public ISession surface in
OPCFoundation.NetStandard 1.5.378+.

- IAddressSpaceBuilder.RegisterTypeNode (default no-op for back-compat)
- MirroredTypeNodeInfo + MirroredTypeKind in Core.Abstractions
- OpcUaClientDriverOptions.MirrorTypeDefinitions (default false)
- DiscoverAsync pass-3: FetchTypeTreeAsync + recursive HasSubtype walk per
  branch (ObjectTypes/VariableTypes/DataTypes/ReferenceTypes), best-effort
  IsAbstract read, IncludePaths/ExcludePaths still applied
- 6 new unit tests; all 153 OpcUaClient unit tests pass

Closes #280
This commit is contained in:
Joseph Doherty
2026-04-25 20:38:17 -04:00
parent 5e164dc965
commit cc21281cbb
4 changed files with 457 additions and 0 deletions

View File

@@ -191,6 +191,40 @@ public sealed class OpcUaClientDriverOptions
/// pre-curation behaviour exactly — empty include = include all.
/// </summary>
public OpcUaClientCurationOptions Curation { get; init; } = new();
/// <summary>
/// When <c>true</c>, <c>DiscoverAsync</c> runs an additional pass that walks the upstream
/// <c>TypesFolder</c> (<c>i=86</c>) — ObjectTypes (<c>i=88</c>), VariableTypes
/// (<c>i=89</c>), DataTypes (<c>i=90</c>), ReferenceTypes (<c>i=91</c>) — and projects the
/// discovered type-definition nodes into the local address space via
/// <c>IAddressSpaceBuilder.RegisterTypeNode</c>. Default <c>false</c> — opt-in so
/// existing deployments don't suddenly see a flood of type nodes after upgrade. Enable
/// when downstream clients need the upstream type system to render structured values or
/// decode custom event fields.
/// </summary>
/// <remarks>
/// <para>
/// The type-mirror pass uses <c>Session.FetchTypeTreeAsync</c> on each of the four
/// root type nodes so the SDK's local TypeTree cache is populated efficiently (one
/// batched browse per root rather than per-node round trips). This PR ships the
/// <i>structural</i> mirror only — every type node is registered with its identity,
/// super-type chain, and IsAbstract flag, but structured-type binary encodings are
/// NOT primed. (The OPCFoundation SDK removed
/// <c>ISession.LoadDataTypeSystem(NodeId, CancellationToken)</c> from the public
/// surface in 1.5.378+; loading binary encodings now requires per-node walks of
/// <c>HasEncoding</c> + dictionary nodes which is tracked as a follow-up.) Clients
/// that need structured-type decoding can still consume
/// <c>Variant&lt;ExtensionObject&gt;</c> on the wire.
/// </para>
/// <para>
/// <see cref="OpcUaClientCurationOptions.IncludePaths"/> +
/// <see cref="OpcUaClientCurationOptions.ExcludePaths"/> still apply to the type
/// walk; paths are slash-joined under their root (e.g.
/// <c>"ObjectTypes/BaseObjectType/SomeType"</c>). Most operators want all types so
/// empty include = include all is the right default.
/// </para>
/// </remarks>
public bool MirrorTypeDefinitions { get; init; } = false;
}
/// <summary>