feat(dcl): bounded recursive OPC UA address-space search adapter (T15)

This commit is contained in:
Joseph Doherty
2026-06-18 02:45:01 -04:00
parent 9ec2450ad5
commit c00c8241b3
5 changed files with 308 additions and 1 deletions
@@ -0,0 +1,43 @@
namespace ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Protocol;
/// <summary>
/// M7-B4 (T15): optional capability for an <see cref="IDataConnection"/>
/// implementation that supports a bounded recursive search of the server's
/// address space. A complement to <see cref="IBrowsableDataConnection"/>:
/// where browse walks one level at a time on user demand, search walks the
/// tree itself (bounded by depth + result caps) and returns the nodes whose
/// DisplayName or root-relative path contains a query substring.
///
/// Consumed only by management/UI flows (e.g. the OPC UA tag picker's
/// "find a tag" box on the instance config page) — never by Instance Actors
/// on the hot path.
/// </summary>
public interface IAddressSpaceSearchable
{
/// <summary>
/// Walks the server's address space breadth-first from the root, bounded by
/// <paramref name="maxDepth"/> and <paramref name="maxResults"/>, returning
/// the nodes whose DisplayName or root-relative path contains
/// <paramref name="query"/> (case-insensitive substring match).
/// </summary>
/// <param name="query">Case-insensitive substring to match against each node's DisplayName and its root-relative path. An empty/whitespace query matches nothing (the walk is skipped).</param>
/// <param name="maxDepth">Maximum number of levels below the root to descend (Object nodes deeper than this are not expanded). Must be non-negative.</param>
/// <param name="maxResults">Maximum number of matches to return; when reached the walk stops early and <see cref="AddressSpaceSearchResult.CapReached"/> is set true.</param>
/// <param name="cancellationToken">Cancellation token; on cancellation the implementation should throw <see cref="OperationCanceledException"/>.</param>
/// <returns>A task that resolves to the matches found and a flag indicating whether a bound (result cap or node-visit ceiling) cut the walk short.</returns>
Task<AddressSpaceSearchResult> SearchAddressSpaceAsync(
string query,
int maxDepth,
int maxResults,
CancellationToken cancellationToken = default);
}
/// <param name="Node">The matched address-space node, carrying its NodeId, DisplayName, NodeClass and (B1) type info.</param>
/// <param name="Path">The matched node's root-relative path: the slash-joined DisplayName chain from the root down to the node (e.g. <c>"/Folder1/Tag1"</c>).</param>
public record AddressSpaceMatch(BrowseNode Node, string Path);
/// <param name="Matches">The matched nodes, in breadth-first discovery order.</param>
/// <param name="CapReached">True when a bound cut the walk short — either <c>maxResults</c> matches were collected or the implementation's node-visit ceiling was hit — so more matches may exist than were returned.</param>
public record AddressSpaceSearchResult(
IReadOnlyList<AddressSpaceMatch> Matches,
bool CapReached);