docs: backfill XML documentation across 756 files
v2-ci / build (push) Failing after 1m43s
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>, <typeparam>, and <inheritdoc/> tags to public
members surfaced by commentchecker — resolves 5,847 of 5,869 issues
(99.6%) across three /fixdocs passes.
This commit is contained in:
Joseph Doherty
2026-05-28 08:10:17 -04:00
parent f9fc7dd2e1
commit 64e3fbe035
756 changed files with 9876 additions and 96 deletions
@@ -29,10 +29,18 @@ public sealed class TwinCATDriver : IDriver, IReadable, IWritable, ITagDiscovery
new(StringComparer.OrdinalIgnoreCase);
private DriverHealth _health = new(DriverState.Unknown, null, null);
/// <summary>Occurs when a subscribed tag value changes.</summary>
public event EventHandler<DataChangeEventArgs>? OnDataChange;
/// <summary>Occurs when a device host connectivity status changes.</summary>
public event EventHandler<HostStatusChangedEventArgs>? OnHostStatusChanged;
/// <summary>Occurs when the Galaxy object hierarchy or TwinCAT symbol table is rediscovered.</summary>
public event EventHandler<RediscoveryEventArgs>? OnRediscoveryNeeded;
/// <summary>Initializes a new instance of the <see cref="TwinCATDriver"/> class.</summary>
/// <param name="options">Driver configuration options.</param>
/// <param name="driverInstanceId">Unique driver instance identifier.</param>
/// <param name="clientFactory">Optional ADS client factory; defaults to <see cref="AdsTwinCATClientFactory"/>.</param>
/// <param name="logger">Optional logger; defaults to <see cref="NullLogger{TwinCATDriver}"/>.</param>
public TwinCATDriver(TwinCATDriverOptions options, string driverInstanceId,
ITwinCATClientFactory? clientFactory = null,
ILogger<TwinCATDriver>? logger = null)
@@ -48,9 +56,15 @@ public sealed class TwinCATDriver : IDriver, IReadable, IWritable, ITagDiscovery
OnDataChange?.Invoke(this, new DataChangeEventArgs(handle, tagRef, snapshot)));
}
/// <summary>Gets the unique driver instance identifier.</summary>
public string DriverInstanceId => _driverInstanceId;
/// <summary>Gets the driver type name.</summary>
public string DriverType => "TwinCAT";
/// <summary>Initializes the driver with configuration and establishes device connections.</summary>
/// <param name="driverConfigJson">JSON configuration string for the driver.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>Completion task.</returns>
public Task InitializeAsync(string driverConfigJson, CancellationToken cancellationToken)
{
_health = new DriverHealth(DriverState.Initializing, null, null);
@@ -95,12 +109,19 @@ public sealed class TwinCATDriver : IDriver, IReadable, IWritable, ITagDiscovery
return Task.CompletedTask;
}
/// <summary>Reinitializes the driver by shutting down and reinitializing with new configuration.</summary>
/// <param name="driverConfigJson">JSON configuration string for the driver.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>Completion task.</returns>
public async Task ReinitializeAsync(string driverConfigJson, CancellationToken cancellationToken)
{
await ShutdownAsync(cancellationToken).ConfigureAwait(false);
await InitializeAsync(driverConfigJson, cancellationToken).ConfigureAwait(false);
}
/// <summary>Shuts down the driver and releases all device connections and subscriptions.</summary>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>Completion task.</returns>
public async Task ShutdownAsync(CancellationToken cancellationToken)
{
// Native subs first — disposing the handles is cheap + lets the client close its
@@ -133,6 +154,8 @@ public sealed class TwinCATDriver : IDriver, IReadable, IWritable, ITagDiscovery
_health = new DriverHealth(DriverState.Unknown, _health.LastSuccessfulRead, null);
}
/// <summary>Gets the current driver health status.</summary>
/// <returns>Driver health information.</returns>
public DriverHealth GetHealth() => _health;
/// <summary>
@@ -150,14 +173,24 @@ public sealed class TwinCATDriver : IDriver, IReadable, IWritable, ITagDiscovery
/// every <see cref="DiscoverAsync"/> call. This is a no-op but is deliberately present
/// so Core's cache-budget enforcement sees a compliant Tier-A driver.
/// </summary>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>Completion task.</returns>
public Task FlushOptionalCachesAsync(CancellationToken cancellationToken) => Task.CompletedTask;
/// <summary>Gets the count of configured devices.</summary>
internal int DeviceCount => _devices.Count;
/// <summary>Gets the device state for the specified host address.</summary>
/// <param name="hostAddress">The ADS host address.</param>
/// <returns>Device state or null if not found.</returns>
internal DeviceState? GetDeviceState(string hostAddress) =>
_devices.TryGetValue(hostAddress, out var s) ? s : null;
// ---- IReadable ----
/// <summary>Reads values for the specified tag references from ADS devices.</summary>
/// <param name="fullReferences">The full tag references to read.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>Data value snapshots for each reference.</returns>
public async Task<IReadOnlyList<DataValueSnapshot>> ReadAsync(
IReadOnlyList<string> fullReferences, CancellationToken cancellationToken)
{
@@ -212,6 +245,10 @@ public sealed class TwinCATDriver : IDriver, IReadable, IWritable, ITagDiscovery
// ---- IWritable ----
/// <summary>Writes values to the specified tags on ADS devices.</summary>
/// <param name="writes">The write requests to execute.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>Write results for each request.</returns>
public async Task<IReadOnlyList<WriteResult>> WriteAsync(
IReadOnlyList<WriteRequest> writes, CancellationToken cancellationToken)
{
@@ -272,6 +309,10 @@ public sealed class TwinCATDriver : IDriver, IReadable, IWritable, ITagDiscovery
// ---- ITagDiscovery ----
/// <summary>Discovers devices and tags from ADS configuration and optionally controller symbols.</summary>
/// <param name="builder">Address space builder for adding discovered nodes.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>Completion task.</returns>
public async Task DiscoverAsync(IAddressSpaceBuilder builder, CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(builder);
@@ -352,6 +393,10 @@ public sealed class TwinCATDriver : IDriver, IReadable, IWritable, ITagDiscovery
/// target's PLC runtime — the PLC pushes changes on its own cycle so we skip the poll
/// loop entirely. Unsub path disposes the handles.
/// </summary>
/// <param name="fullReferences">The full tag references to subscribe to.</param>
/// <param name="publishingInterval">The publishing interval for updates.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>Subscription handle for managing the subscription.</returns>
public async Task<ISubscriptionHandle> SubscribeAsync(
IReadOnlyList<string> fullReferences, TimeSpan publishingInterval, CancellationToken cancellationToken)
{
@@ -402,6 +447,10 @@ public sealed class TwinCATDriver : IDriver, IReadable, IWritable, ITagDiscovery
return handle;
}
/// <summary>Unsubscribes from a native or poll-based subscription.</summary>
/// <param name="handle">The subscription handle to unsubscribe.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>Completion task.</returns>
public Task UnsubscribeAsync(ISubscriptionHandle handle, CancellationToken cancellationToken)
{
if (handle is NativeSubscriptionHandle native && _nativeSubs.TryRemove(native.Id, out var sub))
@@ -415,6 +464,7 @@ public sealed class TwinCATDriver : IDriver, IReadable, IWritable, ITagDiscovery
private sealed record NativeSubscriptionHandle(long Id) : ISubscriptionHandle
{
/// <summary>Gets the diagnostic identifier for the subscription.</summary>
public string DiagnosticId => $"twincat-native-sub-{Id}";
}
@@ -424,6 +474,8 @@ public sealed class TwinCATDriver : IDriver, IReadable, IWritable, ITagDiscovery
// ---- IHostConnectivityProbe ----
/// <summary>Gets the connectivity status for all configured devices.</summary>
/// <returns>List of host connectivity statuses.</returns>
public IReadOnlyList<HostConnectivityStatus> GetHostStatuses() =>
[.. _devices.Values.Select(s => new HostConnectivityStatus(s.Options.HostAddress, s.HostState, s.HostStateChangedUtc))];
@@ -484,6 +536,9 @@ public sealed class TwinCATDriver : IDriver, IReadable, IWritable, ITagDiscovery
/// </summary>
public const string UnresolvedHostSentinel = "";
/// <summary>Resolves the device host address for the specified tag reference.</summary>
/// <param name="fullReference">The full tag reference.</param>
/// <returns>The host address or <see cref="UnresolvedHostSentinel"/> if not found.</returns>
public string ResolveHost(string fullReference)
{
if (_tagsByName.TryGetValue(fullReference, out var def))
@@ -573,6 +628,7 @@ public sealed class TwinCATDriver : IDriver, IReadable, IWritable, ITagDiscovery
/// cancel tokens, wait on task handles with a hard timeout, dispose clients — so a
/// synchronous path does the right thing without re-entering the scheduler.
/// </summary>
/// <summary>Synchronously disposes driver resources without awaiting async operations.</summary>
public void Dispose()
{
// Dispose native subscriptions first — handle disposal is sync.
@@ -602,32 +658,43 @@ public sealed class TwinCATDriver : IDriver, IReadable, IWritable, ITagDiscovery
_health = new DriverHealth(DriverState.Unknown, _health.LastSuccessfulRead, null);
}
/// <summary>Asynchronously disposes driver resources.</summary>
/// <returns>Completion task.</returns>
public async ValueTask DisposeAsync() => await ShutdownAsync(CancellationToken.None).ConfigureAwait(false);
internal sealed class DeviceState(TwinCATAmsAddress parsedAddress, TwinCATDeviceOptions options)
{
/// <summary>Gets the parsed AMS address for the device.</summary>
public TwinCATAmsAddress ParsedAddress { get; } = parsedAddress;
/// <summary>Gets the device configuration options.</summary>
public TwinCATDeviceOptions Options { get; } = options;
/// <summary>Gets or sets the active ADS client for this device.</summary>
public ITwinCATClient? Client { get; set; }
/// <summary>Serializes connect / reconnect so concurrent callers never race a client
/// create-or-dispose for this device (Driver.TwinCAT-007).</summary>
public SemaphoreSlim ConnectGate { get; } = new(1, 1);
/// <summary>Gets the lock object for synchronizing host state transitions.</summary>
public object ProbeLock { get; } = new();
/// <summary>Gets or sets the current host connectivity state.</summary>
public HostState HostState { get; set; } = HostState.Unknown;
/// <summary>Gets or sets the UTC timestamp of the last host state change.</summary>
public DateTime HostStateChangedUtc { get; set; } = DateTime.UtcNow;
/// <summary>Gets or sets the cancellation token source for the probe loop.</summary>
public CancellationTokenSource? ProbeCts { get; set; }
/// <summary>The running probe-loop task — awaited by <see cref="TwinCATDriver.ShutdownAsync"/>
/// so the loop cannot touch a disposed client (Driver.TwinCAT-009).</summary>
public Task? ProbeTask { get; set; }
/// <summary>Disposes the active ADS client if any.</summary>
public void DisposeClient()
{
Client?.Dispose();
Client = null;
}
/// <summary>Disposes the connection gate semaphore.</summary>
public void DisposeGate() => ConnectGate.Dispose();
}
}