Add IDriverControl capability interface in Core.Abstractions with a RebrowseAsync(IAddressSpaceBuilder, CancellationToken) hook so operators can force a controller-side @tags re-walk without restarting the driver. AbCipDriver now implements IDriverControl. RebrowseAsync clears the UDT template cache (so stale shapes from a pre-download program don't survive) then runs the same enumerator + builder fan-out as DiscoverAsync, serialised against concurrent discovery / rebrowse via a new SemaphoreSlim. Driver.AbCip.Cli ships a `rebrowse` subcommand mirroring the existing probe / read shape: connects to a single gateway, runs RebrowseAsync against an in-memory builder, and prints discovered tag names so operators can sanity-check the controller's symbol table from a shell. Tests cover: two consecutive RebrowseAsync calls bump the enumerator's Create / Enumerate counters once each, discovered tags reach the supplied builder, the template cache is dropped on rebrowse, and the driver exposes IDriverControl. 313 AbCip unit tests + 17 CLI tests + 37 Core.Abstractions tests pass. Closes #233
28 lines
1.5 KiB
C#
28 lines
1.5 KiB
C#
namespace ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
|
|
|
/// <summary>
|
|
/// Optional control-plane capability — drivers whose backend exposes a way to refresh
|
|
/// the symbol table on-demand (without tearing the driver down) implement this so the
|
|
/// Admin UI / CLI can trigger a re-walk in response to an operator action.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Distinct from <see cref="IRediscoverable"/>: that interface is the driver telling Core
|
|
/// a refresh is needed; this one is Core asking the driver to refresh now. For drivers that
|
|
/// implement both, the typical wiring is "operator clicks Rebrowse → Core calls
|
|
/// <see cref="RebrowseAsync"/> → driver re-walks → driver fires
|
|
/// <c>OnRediscoveryNeeded</c> so the address space is rebuilt".
|
|
///
|
|
/// For AB CIP this is the "force re-walk of @tags" hook — useful after a controller
|
|
/// program download added new tags but the static config still drives the address space.
|
|
/// </remarks>
|
|
public interface IDriverControl
|
|
{
|
|
/// <summary>
|
|
/// Re-run the driver's discovery pass against live backend state and stream the
|
|
/// resulting nodes through the supplied builder. Implementations must be safe to call
|
|
/// concurrently with reads / writes; they typically serialize internally so a second
|
|
/// concurrent rebrowse waits for the first to complete rather than racing it.
|
|
/// </summary>
|
|
Task RebrowseAsync(IAddressSpaceBuilder builder, CancellationToken cancellationToken);
|
|
}
|