docs(xmldoc): fill missing XML docs + strip tracking-ID comments across src
v2-ci / build (push) Failing after 41s
v2-ci / unit-tests (tests/Core/ZB.MOM.WW.OtOpcUa.Cluster.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests) (push) Has been skipped
v2-ci / build (push) Failing after 41s
v2-ci / unit-tests (tests/Core/ZB.MOM.WW.OtOpcUa.Cluster.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests) (push) Has been skipped
Adds <summary>/<param>/<returns>/<inheritdoc> where missing and removes project bookkeeping IDs (task/tracking refs) from shipped code comments, so the docs read cleanly and CommentChecker is quiet except for known false positives (PLC/protocol terms, event/IEqualityComparer inheritdoc). Doc/comment-only; no logic changed; solution builds clean.
This commit is contained in:
@@ -34,7 +34,7 @@ public sealed class FocasDriver : IDriver, IReadable, IWritable, ITagDiscovery,
|
||||
private readonly EquipmentTagRefResolver<FocasTagDefinition> _resolver;
|
||||
// Per-tag-name cache of the FocasAddress parsed once at InitializeAsync. ReadAsync /
|
||||
// WriteAsync look up the pre-parsed value instead of re-parsing tag.Address on every hot
|
||||
// call — resolves Driver.FOCAS-008. ConcurrentDictionary is required because the poll loop
|
||||
// call. ConcurrentDictionary is required because the poll loop
|
||||
// (ReadAsync) and the host write thread (WriteAsync) both mutate this cache when resolver-
|
||||
// produced equipment tags are encountered for the first time.
|
||||
private readonly ConcurrentDictionary<string, FocasAddress> _parsedAddressesByTagName =
|
||||
@@ -75,15 +75,12 @@ public sealed class FocasDriver : IDriver, IReadable, IWritable, ITagDiscovery,
|
||||
OnDataChange?.Invoke(this, new DataChangeEventArgs(handle, tagRef, snapshot)));
|
||||
}
|
||||
|
||||
/// <summary>Gets the driver instance identifier.</summary>
|
||||
/// <inheritdoc />
|
||||
public string DriverInstanceId => _driverInstanceId;
|
||||
/// <summary>Gets the driver type name.</summary>
|
||||
/// <inheritdoc />
|
||||
public string DriverType => "FOCAS";
|
||||
|
||||
/// <summary>Initializes the driver with configuration and prepares device connections and polling.</summary>
|
||||
/// <param name="driverConfigJson">JSON configuration string for the driver.</param>
|
||||
/// <param name="cancellationToken">Cancellation token for the operation.</param>
|
||||
/// <returns>A task representing the asynchronous initialization operation.</returns>
|
||||
/// <inheritdoc />
|
||||
public Task InitializeAsync(string driverConfigJson, CancellationToken cancellationToken)
|
||||
{
|
||||
Volatile.Write(ref _health, new DriverHealth(DriverState.Initializing, null, null));
|
||||
@@ -91,7 +88,7 @@ public sealed class FocasDriver : IDriver, IReadable, IWritable, ITagDiscovery,
|
||||
{
|
||||
// Fail fast if the factory is a stub/unimplemented backend — the operator must
|
||||
// see an actionable error at init rather than a phantom-Healthy driver that fails
|
||||
// every read/write/subscribe silently (Driver.FOCAS-009).
|
||||
// every read/write/subscribe silently.
|
||||
_clientFactory.EnsureUsable();
|
||||
|
||||
foreach (var device in _options.Devices)
|
||||
@@ -122,7 +119,7 @@ public sealed class FocasDriver : IDriver, IReadable, IWritable, ITagDiscovery,
|
||||
}
|
||||
_tagsByName[tag.Name] = tag;
|
||||
// Cache the parsed FocasAddress so ReadAsync / WriteAsync don't re-parse on every
|
||||
// hot-path call (Driver.FOCAS-008). The address string has already been validated
|
||||
// hot-path call. The address string has already been validated
|
||||
// by FocasAddress.TryParse above; reusing the parsed record avoids per-tick allocs
|
||||
// on subscription pollers.
|
||||
_parsedAddressesByTagName[tag.Name] = parsed;
|
||||
@@ -171,19 +168,14 @@ public sealed class FocasDriver : IDriver, IReadable, IWritable, ITagDiscovery,
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>Reinitializes the driver by shutting down and restarting with new configuration.</summary>
|
||||
/// <param name="driverConfigJson">JSON configuration string for the driver.</param>
|
||||
/// <param name="cancellationToken">Cancellation token for the operation.</param>
|
||||
/// <returns>A task representing the asynchronous reinitialization operation.</returns>
|
||||
/// <inheritdoc />
|
||||
public async Task ReinitializeAsync(string driverConfigJson, CancellationToken cancellationToken)
|
||||
{
|
||||
await ShutdownAsync(cancellationToken).ConfigureAwait(false);
|
||||
await InitializeAsync(driverConfigJson, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>Shuts down the driver, cancelling all running operations and releasing resources.</summary>
|
||||
/// <param name="cancellationToken">Cancellation token for the operation.</param>
|
||||
/// <returns>A task representing the asynchronous shutdown operation.</returns>
|
||||
/// <inheritdoc />
|
||||
public async Task ShutdownAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
await _poll.DisposeAsync().ConfigureAwait(false);
|
||||
@@ -195,8 +187,7 @@ public sealed class FocasDriver : IDriver, IReadable, IWritable, ITagDiscovery,
|
||||
foreach (var state in _devices.Values)
|
||||
{
|
||||
// Cancel-then-dispose can race in tight shutdown loops; swallowing is intentional
|
||||
// but we now log the cause so a noisy shutdown leaves a Debug trace
|
||||
// (Driver.FOCAS-007).
|
||||
// but we now log the cause so a noisy shutdown leaves a Debug trace.
|
||||
try { state.ProbeCts?.Cancel(); }
|
||||
catch (Exception ex) { _logger.LogDebug(ex, "Cancelling probe CTS for {Host} failed", state.Options.HostAddress); }
|
||||
state.ProbeCts?.Dispose();
|
||||
@@ -218,13 +209,11 @@ public sealed class FocasDriver : IDriver, IReadable, IWritable, ITagDiscovery,
|
||||
Volatile.Write(ref _health, new DriverHealth(DriverState.Unknown, Volatile.Read(ref _health).LastSuccessfulRead, null));
|
||||
}
|
||||
|
||||
/// <summary>Gets the current health status of the driver.</summary>
|
||||
/// <inheritdoc />
|
||||
public DriverHealth GetHealth() => Volatile.Read(ref _health);
|
||||
/// <summary>Gets the current memory footprint of the driver.</summary>
|
||||
/// <inheritdoc />
|
||||
public long GetMemoryFootprint() => 0;
|
||||
/// <summary>Flushes optional internal caches.</summary>
|
||||
/// <param name="cancellationToken">Cancellation token for the operation.</param>
|
||||
/// <returns>A task representing the asynchronous cache flush operation.</returns>
|
||||
/// <inheritdoc />
|
||||
public Task FlushOptionalCachesAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
||||
|
||||
/// <summary>Gets the number of configured devices.</summary>
|
||||
@@ -240,12 +229,14 @@ public sealed class FocasDriver : IDriver, IReadable, IWritable, ITagDiscovery,
|
||||
|
||||
/// <summary>Test seam — returns true when a parsed <see cref="FocasAddress"/> for the given
|
||||
/// reference is present in the address cache (both authored and equipment-tag paths).</summary>
|
||||
/// <param name="reference">The tag or equipment-tag reference to check.</param>
|
||||
/// <returns><see langword="true"/> if a parsed address is cached for the reference; otherwise <see langword="false"/>.</returns>
|
||||
internal bool IsParsedAddressCached(string reference) =>
|
||||
_parsedAddressesByTagName.ContainsKey(reference);
|
||||
|
||||
// Resolves a tag definition to its parsed FocasAddress, caching the result so that
|
||||
// equipment tags (resolver-produced, not seeded at InitializeAsync) don't re-parse the
|
||||
// address string on every ReadAsync / WriteAsync hot-path call (Driver.FOCAS-008).
|
||||
// address string on every ReadAsync / WriteAsync hot-path call.
|
||||
// Throwing inside the GetOrAdd factory propagates the exception to the caller and does
|
||||
// NOT store anything in the dictionary — consistent with the existing "fail fast on a
|
||||
// malformed address" behaviour from the init-time validation of authored tags.
|
||||
@@ -257,10 +248,7 @@ public sealed class FocasDriver : IDriver, IReadable, IWritable, ITagDiscovery,
|
||||
|
||||
// ---- IReadable ----
|
||||
|
||||
/// <summary>Reads values from one or more tags asynchronously.</summary>
|
||||
/// <param name="fullReferences">A read-only list of tag references to read.</param>
|
||||
/// <param name="cancellationToken">Cancellation token for the operation.</param>
|
||||
/// <returns>A task representing the asynchronous read operation.</returns>
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<DataValueSnapshot>> ReadAsync(
|
||||
IReadOnlyList<string> fullReferences, CancellationToken cancellationToken)
|
||||
{
|
||||
@@ -329,10 +317,7 @@ public sealed class FocasDriver : IDriver, IReadable, IWritable, ITagDiscovery,
|
||||
|
||||
// ---- IWritable ----
|
||||
|
||||
/// <summary>Writes values to one or more tags asynchronously.</summary>
|
||||
/// <param name="writes">A read-only list of write requests.</param>
|
||||
/// <param name="cancellationToken">Cancellation token for the operation.</param>
|
||||
/// <returns>A task representing the asynchronous write operation.</returns>
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<WriteResult>> WriteAsync(
|
||||
IReadOnlyList<WriteRequest> writes, CancellationToken cancellationToken)
|
||||
{
|
||||
@@ -401,18 +386,14 @@ public sealed class FocasDriver : IDriver, IReadable, IWritable, ITagDiscovery,
|
||||
|
||||
// ---- ITagDiscovery ----
|
||||
|
||||
/// <summary>
|
||||
/// Retry-until-stable: the FixedTree subtree is filled in asynchronously by
|
||||
/// <see cref="FixedTreeLoopAsync"/> a couple of seconds AFTER connect, so the first
|
||||
/// post-connect <see cref="DiscoverAsync"/> pass would miss it — the host must re-run
|
||||
/// discovery until the captured node set is non-empty and stable.
|
||||
/// </summary>
|
||||
/// <inheritdoc />
|
||||
// Retry-until-stable: the FixedTree subtree is filled in asynchronously by
|
||||
// FixedTreeLoopAsync a couple of seconds AFTER connect, so the first post-connect
|
||||
// DiscoverAsync pass would miss it — the host must re-run discovery until the captured
|
||||
// node set is non-empty and stable.
|
||||
public DiscoveryRediscoverPolicy RediscoverPolicy => DiscoveryRediscoverPolicy.UntilStable;
|
||||
|
||||
/// <summary>Discovers tags and builds the OPC UA address space asynchronously.</summary>
|
||||
/// <param name="builder">The address space builder for constructing the OPC UA namespace.</param>
|
||||
/// <param name="cancellationToken">Cancellation token for the operation.</param>
|
||||
/// <returns>A task representing the asynchronous discovery operation.</returns>
|
||||
/// <inheritdoc />
|
||||
public Task DiscoverAsync(IAddressSpaceBuilder builder, CancellationToken cancellationToken)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(builder);
|
||||
@@ -589,19 +570,12 @@ public sealed class FocasDriver : IDriver, IReadable, IWritable, ITagDiscovery,
|
||||
|
||||
// ---- ISubscribable (polling overlay via shared engine) ----
|
||||
|
||||
/// <summary>Subscribes to data changes on one or more tags.</summary>
|
||||
/// <param name="fullReferences">A read-only list of tag references to subscribe to.</param>
|
||||
/// <param name="publishingInterval">The interval at which to publish data changes.</param>
|
||||
/// <param name="cancellationToken">Cancellation token for the operation.</param>
|
||||
/// <returns>A task representing the asynchronous subscription operation.</returns>
|
||||
/// <inheritdoc />
|
||||
public Task<ISubscriptionHandle> SubscribeAsync(
|
||||
IReadOnlyList<string> fullReferences, TimeSpan publishingInterval, CancellationToken cancellationToken) =>
|
||||
Task.FromResult(_poll.Subscribe(fullReferences, publishingInterval));
|
||||
|
||||
/// <summary>Unsubscribes from a previous subscription.</summary>
|
||||
/// <param name="handle">The subscription handle to unsubscribe from.</param>
|
||||
/// <param name="cancellationToken">Cancellation token for the operation.</param>
|
||||
/// <returns>A task representing the asynchronous unsubscription operation.</returns>
|
||||
/// <inheritdoc />
|
||||
public Task UnsubscribeAsync(ISubscriptionHandle handle, CancellationToken cancellationToken)
|
||||
{
|
||||
_poll.Unsubscribe(handle);
|
||||
@@ -610,7 +584,7 @@ public sealed class FocasDriver : IDriver, IReadable, IWritable, ITagDiscovery,
|
||||
|
||||
// ---- IHostConnectivityProbe ----
|
||||
|
||||
/// <summary>Gets the connectivity status of all configured devices.</summary>
|
||||
/// <inheritdoc />
|
||||
public IReadOnlyList<HostConnectivityStatus> GetHostStatuses() =>
|
||||
[.. _devices.Values.Select(s => new HostConnectivityStatus(s.Options.HostAddress, s.HostState, s.HostStateChangedUtc))];
|
||||
|
||||
@@ -623,7 +597,7 @@ public sealed class FocasDriver : IDriver, IReadable, IWritable, ITagDiscovery,
|
||||
{
|
||||
var client = await EnsureConnectedAsync(state, ct).ConfigureAwait(false);
|
||||
// Apply Probe.Timeout so a hung CNC socket gets cancelled at the configured
|
||||
// budget rather than blocking until the OS TCP timeout (Driver.FOCAS-009).
|
||||
// budget rather than blocking until the OS TCP timeout.
|
||||
// TimeSpan.Zero / negative means "no per-probe timeout" — fall back to the loop
|
||||
// cancellation token unmodified.
|
||||
var probeTimeout = _options.Probe.Timeout;
|
||||
@@ -1048,10 +1022,7 @@ public sealed class FocasDriver : IDriver, IReadable, IWritable, ITagDiscovery,
|
||||
|
||||
// ---- IAlarmSource ----
|
||||
|
||||
/// <summary>Subscribes to alarm events from the driver.</summary>
|
||||
/// <param name="sourceNodeIds">A read-only list of source node IDs to subscribe to.</param>
|
||||
/// <param name="cancellationToken">Cancellation token for the operation.</param>
|
||||
/// <returns>A task representing the asynchronous subscription operation.</returns>
|
||||
/// <inheritdoc />
|
||||
public Task<IAlarmSubscriptionHandle> SubscribeAlarmsAsync(
|
||||
IReadOnlyList<string> sourceNodeIds, CancellationToken cancellationToken)
|
||||
{
|
||||
@@ -1061,17 +1032,11 @@ public sealed class FocasDriver : IDriver, IReadable, IWritable, ITagDiscovery,
|
||||
return _alarmProjection.SubscribeAsync(sourceNodeIds, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>Unsubscribes from a previous alarm subscription.</summary>
|
||||
/// <param name="handle">The alarm subscription handle to unsubscribe from.</param>
|
||||
/// <param name="cancellationToken">Cancellation token for the operation.</param>
|
||||
/// <returns>A task representing the asynchronous unsubscription operation.</returns>
|
||||
/// <inheritdoc />
|
||||
public Task UnsubscribeAlarmsAsync(IAlarmSubscriptionHandle handle, CancellationToken cancellationToken) =>
|
||||
_alarmProjection is { } p ? p.UnsubscribeAsync(handle, cancellationToken) : Task.CompletedTask;
|
||||
|
||||
/// <summary>Acknowledges one or more alarms.</summary>
|
||||
/// <param name="acknowledgements">A read-only list of alarm acknowledgement requests.</param>
|
||||
/// <param name="cancellationToken">Cancellation token for the operation.</param>
|
||||
/// <returns>A task representing the asynchronous acknowledgement operation.</returns>
|
||||
/// <inheritdoc />
|
||||
public Task AcknowledgeAsync(
|
||||
IReadOnlyList<AlarmAcknowledgeRequest> acknowledgements, CancellationToken cancellationToken) =>
|
||||
_alarmProjection is { } p ? p.AcknowledgeAsync(acknowledgements, cancellationToken) : Task.CompletedTask;
|
||||
@@ -1113,9 +1078,7 @@ public sealed class FocasDriver : IDriver, IReadable, IWritable, ITagDiscovery,
|
||||
|
||||
// ---- IPerCallHostResolver ----
|
||||
|
||||
/// <summary>Resolves the host address for a given tag reference.</summary>
|
||||
/// <param name="fullReference">The full reference of the tag.</param>
|
||||
/// <returns>The host address for the tag reference.</returns>
|
||||
/// <inheritdoc />
|
||||
public string ResolveHost(string fullReference)
|
||||
{
|
||||
if (_tagsByName.TryGetValue(fullReference, out var def))
|
||||
@@ -1159,6 +1122,7 @@ public sealed class FocasDriver : IDriver, IReadable, IWritable, ITagDiscovery,
|
||||
/// <summary>Disposes the driver and releases all resources synchronously.</summary>
|
||||
public void Dispose() => DisposeAsync().AsTask().GetAwaiter().GetResult();
|
||||
/// <summary>Disposes the driver and releases all resources asynchronously.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
public async ValueTask DisposeAsync() => await ShutdownAsync(CancellationToken.None).ConfigureAwait(false);
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user