Auto: abcip-2.5 — online tag-DB refresh trigger
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
This commit is contained in:
@@ -22,7 +22,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.AbCip;
|
||||
/// <see cref="PlcTagHandle"/> and reconnects each device.</para>
|
||||
/// </remarks>
|
||||
public sealed class AbCipDriver : IDriver, IReadable, IWritable, ITagDiscovery, ISubscribable,
|
||||
IHostConnectivityProbe, IPerCallHostResolver, IAlarmSource, IDisposable, IAsyncDisposable
|
||||
IHostConnectivityProbe, IPerCallHostResolver, IAlarmSource, IDriverControl, IDisposable, IAsyncDisposable
|
||||
{
|
||||
private readonly AbCipDriverOptions _options;
|
||||
private readonly string _driverInstanceId;
|
||||
@@ -34,6 +34,7 @@ public sealed class AbCipDriver : IDriver, IReadable, IWritable, ITagDiscovery,
|
||||
private readonly Dictionary<string, DeviceState> _devices = new(StringComparer.OrdinalIgnoreCase);
|
||||
private readonly Dictionary<string, AbCipTagDefinition> _tagsByName = new(StringComparer.OrdinalIgnoreCase);
|
||||
private readonly AbCipAlarmProjection _alarmProjection;
|
||||
private readonly SemaphoreSlim _discoverySemaphore = new(1, 1);
|
||||
private DriverHealth _health = new(DriverState.Unknown, null, null);
|
||||
|
||||
public event EventHandler<DataChangeEventArgs>? OnDataChange;
|
||||
@@ -967,6 +968,43 @@ public sealed class AbCipDriver : IDriver, IReadable, IWritable, ITagDiscovery,
|
||||
public async Task DiscoverAsync(IAddressSpaceBuilder builder, CancellationToken cancellationToken)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(builder);
|
||||
await _discoverySemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
await DiscoverCoreAsync(builder, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_discoverySemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// PR abcip-2.5 — operator-triggered rebrowse. Drops the cached UDT template shapes so
|
||||
/// the next read re-fetches them from the controller, then runs the same enumerator
|
||||
/// walk + builder fan-out that <see cref="DiscoverAsync"/> drives. Serialised against
|
||||
/// other rebrowse / discovery passes via <see cref="_discoverySemaphore"/> so two
|
||||
/// concurrent triggers don't double-issue the @tags read.
|
||||
/// </summary>
|
||||
public async Task RebrowseAsync(IAddressSpaceBuilder builder, CancellationToken cancellationToken)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(builder);
|
||||
await _discoverySemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
// Stale template shapes can outlive a controller program-download, so a rebrowse
|
||||
// is the natural moment to drop them; subsequent UDT reads re-populate on demand.
|
||||
_templateCache.Clear();
|
||||
await DiscoverCoreAsync(builder, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_discoverySemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DiscoverCoreAsync(IAddressSpaceBuilder builder, CancellationToken cancellationToken)
|
||||
{
|
||||
var root = builder.Folder("AbCip", "AbCip");
|
||||
|
||||
foreach (var device in _options.Devices)
|
||||
@@ -1076,6 +1114,7 @@ public sealed class AbCipDriver : IDriver, IReadable, IWritable, ITagDiscovery,
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
await ShutdownAsync(CancellationToken.None).ConfigureAwait(false);
|
||||
_discoverySemaphore.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user