docs(xmldoc): fix genuine doc issues surfaced by /fixdocs (17 fixes)
v2-ci / build (push) Successful in 3m11s
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests) (push) Failing after 3m51s
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests) (push) Failing after 1m50s
v2-ci / unit-tests (push) Failing after 8m37s

- CapabilityInvoker: 3 methods that implement IDriverCapabilityInvoker now use
  <inheritdoc /> instead of duplicating the interface docs (MissingInheritDoc).
- TwinCAT NativeRegistration ctor: added the 8 missing <param> tags (MissingParam).
- Stripped the arch-review bookkeeping IDs STAB-1 (IS7Plc, S7Driver) + STAB-2
  (TwinCATDriver x4) from comments, keeping the descriptive text.

Left the other 86 flagged items untouched — they are checker false positives on
legitimate technical content: interface-implemented events / IEqualityComparer /
IDisposable members whose <inheritdoc/> IS correct (10 InheritDocMisused), and domain
terms the heuristic misreads as tracking IDs (IEEE-754, S7-1200/300/400/1500, PLC-5,
D2-260, S7/Melsec memory addresses, OPC UA Part 9, phase-N design-doc filename refs,
UNS level-N descriptions, and prose like 'the #1 bug source' / failover 'pick #3').
This commit is contained in:
Joseph Doherty
2026-07-10 06:23:12 -04:00
parent b67bd9e8b5
commit 3b5ef439d4
4 changed files with 17 additions and 32 deletions
@@ -57,13 +57,7 @@ public sealed class CapabilityInvoker : IDriverCapabilityInvoker
_statusTracker = statusTracker; _statusTracker = statusTracker;
} }
/// <summary>Execute a capability call returning a value, honoring the per-capability pipeline.</summary> /// <inheritdoc />
/// <typeparam name="TResult">Return type of the underlying driver call.</typeparam>
/// <param name="capability">The driver capability being executed.</param>
/// <param name="hostName">The host name for logging and status tracking.</param>
/// <param name="callSite">The async function to execute.</param>
/// <param name="cancellationToken">Cancellation token for the operation.</param>
/// <returns>The result of the underlying driver call.</returns>
public async ValueTask<TResult> ExecuteAsync<TResult>( public async ValueTask<TResult> ExecuteAsync<TResult>(
DriverCapability capability, DriverCapability capability,
string hostName, string hostName,
@@ -87,12 +81,7 @@ public sealed class CapabilityInvoker : IDriverCapabilityInvoker
} }
} }
/// <summary>Execute a void-returning capability call, honoring the per-capability pipeline.</summary> /// <inheritdoc />
/// <param name="capability">The driver capability being executed.</param>
/// <param name="hostName">The host name for logging and status tracking.</param>
/// <param name="callSite">The async function to execute.</param>
/// <param name="cancellationToken">Cancellation token for the operation.</param>
/// <returns>A task that represents the asynchronous operation.</returns>
public async ValueTask ExecuteAsync( public async ValueTask ExecuteAsync(
DriverCapability capability, DriverCapability capability,
string hostName, string hostName,
@@ -116,19 +105,7 @@ public sealed class CapabilityInvoker : IDriverCapabilityInvoker
} }
} }
/// <summary> /// <inheritdoc />
/// Execute a <see cref="DriverCapability.Write"/> call honoring <see cref="WriteIdempotentAttribute"/>
/// semantics — if <paramref name="isIdempotent"/> is <c>false</c>, retries are disabled regardless
/// of the tag-level configuration (the pipeline for a non-idempotent write never retries).
/// If <c>true</c>, the call runs through the capability's pipeline which may
/// retry when the tier configuration permits.
/// </summary>
/// <typeparam name="TResult">Return type of the underlying driver call.</typeparam>
/// <param name="hostName">The host name for logging and status tracking.</param>
/// <param name="isIdempotent">Whether the write operation is idempotent.</param>
/// <param name="callSite">The async function to execute.</param>
/// <param name="cancellationToken">Cancellation token for the operation.</param>
/// <returns>The result of the underlying driver write call.</returns>
public async ValueTask<TResult> ExecuteWriteAsync<TResult>( public async ValueTask<TResult> ExecuteWriteAsync<TResult>(
string hostName, string hostName,
bool isIdempotent, bool isIdempotent,
@@ -11,7 +11,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.S7;
/// <remarks> /// <remarks>
/// The seam exists so <see cref="S7Driver.EnsureConnectedAsync"/> can dispose a dead handle and /// The seam exists so <see cref="S7Driver.EnsureConnectedAsync"/> can dispose a dead handle and
/// re-open a fresh one after a transient PLC reboot / network blip — the reconnect path /// re-open a fresh one after a transient PLC reboot / network blip — the reconnect path
/// (STAB-1) — and so that path is unit-testable via a fake <see cref="IS7PlcFactory"/> without a /// — and so that path is unit-testable via a fake <see cref="IS7PlcFactory"/> without a
/// live PLC (S7.Net ships no in-process fake and <see cref="Plc"/> is <c>sealed</c>). Mirrors /// live PLC (S7.Net ships no in-process fake and <see cref="Plc"/> is <c>sealed</c>). Mirrors
/// the TwinCAT driver's <c>ITwinCATClient</c> / <c>ITwinCATClientFactory</c> pattern. /// the TwinCAT driver's <c>ITwinCATClient</c> / <c>ITwinCATClientFactory</c> pattern.
/// </remarks> /// </remarks>
@@ -1172,7 +1172,7 @@ public sealed class S7Driver
/// <summary> /// <summary>
/// Returns a live S7 connection, lazily disposing a dead handle and re-opening a fresh one /// Returns a live S7 connection, lazily disposing a dead handle and re-opening a fresh one
/// when the previous connection dropped (STAB-1 reconnect path). The fast path returns the /// when the previous connection dropped (the reconnect path). The fast path returns the
/// current <see cref="Plc"/> when it is still connected and not marked dead; otherwise it /// current <see cref="Plc"/> when it is still connected and not marked dead; otherwise it
/// builds + opens a new connection via <see cref="_plcFactory"/> with the same /// builds + opens a new connection via <see cref="_plcFactory"/> with the same
/// timeout-linked CTS the initial connect uses. /// timeout-linked CTS the initial connect uses.
@@ -447,7 +447,7 @@ public sealed class TwinCATDriver : IDriver, IReadable, IWritable, ITagDiscovery
// true, otherwise fall through to the shared PollGroupEngine. Native path registers one // true, otherwise fall through to the shared PollGroupEngine. Native path registers one
// notification per tag against the target's PLC runtime — the PLC pushes changes on its own // notification per tag against the target's PLC runtime — the PLC pushes changes on its own
// cycle so we skip the poll loop entirely. Each registration stores its REPLAYABLE INTENT on // cycle so we skip the poll loop entirely. Each registration stores its REPLAYABLE INTENT on
// the owning device so EnsureConnectedAsync can re-register it after a reconnect (STAB-2). // the owning device so EnsureConnectedAsync can re-register it after a reconnect.
public async Task<ISubscriptionHandle> SubscribeAsync( public async Task<ISubscriptionHandle> SubscribeAsync(
IReadOnlyList<string> fullReferences, TimeSpan publishingInterval, CancellationToken cancellationToken) IReadOnlyList<string> fullReferences, TimeSpan publishingInterval, CancellationToken cancellationToken)
{ {
@@ -577,6 +577,14 @@ public sealed class TwinCATDriver : IDriver, IReadable, IWritable, ITagDiscovery
private ITwinCATNotificationHandle? _handle; private ITwinCATNotificationHandle? _handle;
/// <summary>Initializes a new instance of the <see cref="NativeRegistration"/> class.</summary> /// <summary>Initializes a new instance of the <see cref="NativeRegistration"/> class.</summary>
/// <param name="id">Globally-unique registration id (registry key on the device).</param>
/// <param name="device">The device this notification is registered against.</param>
/// <param name="reference">Driver-side full reference the pushed value is reported under.</param>
/// <param name="symbolName">ADS symbol name (resolved from the symbol path).</param>
/// <param name="dataType">Declared data type.</param>
/// <param name="bitIndex">Bit index for a BOOL-within-word symbol; null otherwise.</param>
/// <param name="interval">Requested notification interval.</param>
/// <param name="onChange">Value-change callback (closes over the owning subscription handle + reference).</param>
public NativeRegistration(long id, DeviceState device, string reference, string symbolName, public NativeRegistration(long id, DeviceState device, string reference, string symbolName,
TwinCATDataType dataType, int? bitIndex, TimeSpan interval, Action<string, object?> onChange) TwinCATDataType dataType, int? bitIndex, TimeSpan interval, Action<string, object?> onChange)
{ {
@@ -627,7 +635,7 @@ public sealed class TwinCATDriver : IDriver, IReadable, IWritable, ITagDiscovery
// A locally-connected client whose wire probe FAILS is wire-dead but port-open: // A locally-connected client whose wire probe FAILS is wire-dead but port-open:
// force a recycle so the next tick rebuilds + replays native notifications. Without // force a recycle so the next tick rebuilds + replays native notifications. Without
// this the IsConnected fast-path would keep handing back the dead client forever // this the IsConnected fast-path would keep handing back the dead client forever
// (the STAB-2 compounding bug — native pushes would never recover). // (the compounding bug — native pushes would never recover).
if (!success) if (!success)
await RecycleClientAsync(state).ConfigureAwait(false); await RecycleClientAsync(state).ConfigureAwait(false);
} }
@@ -716,7 +724,7 @@ public sealed class TwinCATDriver : IDriver, IReadable, IWritable, ITagDiscovery
/// The connect-or-reconnect core, assuming the caller already holds /// The connect-or-reconnect core, assuming the caller already holds
/// <see cref="DeviceState.ConnectGate"/>. When it installs a NEW client (a genuine /// <see cref="DeviceState.ConnectGate"/>. When it installs a NEW client (a genuine
/// reconnect, not a fast-path reuse) it replays every stored native-notification intent /// reconnect, not a fast-path reuse) it replays every stored native-notification intent
/// onto it — otherwise native ADS pushes would silently stop after a drop (STAB-2). Both /// onto it — otherwise native ADS pushes would silently stop after a drop. Both
/// the initial subscribe (<see cref="RegisterNotificationAsync"/>) and the probe loop call /// the initial subscribe (<see cref="RegisterNotificationAsync"/>) and the probe loop call
/// through here under the same gate, so a replay can never race a register. /// through here under the same gate, so a replay can never race a register.
/// </summary> /// </summary>
@@ -907,7 +915,7 @@ public sealed class TwinCATDriver : IDriver, IReadable, IWritable, ITagDiscovery
/// Live native-notification registrations against this device, keyed by registration id. /// Live native-notification registrations against this device, keyed by registration id.
/// Holds the <b>replayable intent</b> (symbol / type / bit / interval / callback), not just /// Holds the <b>replayable intent</b> (symbol / type / bit / interval / callback), not just
/// an opaque handle, so that when <see cref="TwinCATDriver.EnsureConnectedAsync"/> swaps in /// an opaque handle, so that when <see cref="TwinCATDriver.EnsureConnectedAsync"/> swaps in
/// a fresh client after a drop it can re-register every notification onto it (STAB-2). /// a fresh client after a drop it can re-register every notification onto it.
/// Populated by <c>SubscribeAsync</c>, drained by <c>UnsubscribeAsync</c> / teardown. /// Populated by <c>SubscribeAsync</c>, drained by <c>UnsubscribeAsync</c> / teardown.
/// </summary> /// </summary>
public ConcurrentDictionary<long, NativeRegistration> NativeRegistrations { get; } = new(); public ConcurrentDictionary<long, NativeRegistration> NativeRegistrations { get; } = new();