docs(comments): strip internal task/milestone/bundle bookkeeping from code comments
Remove project bookkeeping citations from shipped code comments across the solution: hyphenated task IDs (WP-14, StoreAndForward-025), milestone/task/ issue refs (M3, Task 4, Audit Log #23, #21), Bundle X task-bundle labels, and C/D/K/S/T phase labels. Comment text only — no code logic, string/log literals, or XML-doc structure changed. Genuine descriptions are preserved (only the citation is stripped), and technical lookalikes are retained (UTF-8, SHA-256, T00:00:00, M365, UTC-5, pre-C4/pre-C5 schema versions). Flagged by the new CommentChecker TaskReferenceInComment / TrackingReferenceInComment checks plus targeted grep passes; full solution builds clean, append-only guard tests pass.
This commit is contained in:
@@ -17,7 +17,7 @@ public record OpcUaConnectionOptions(
|
||||
int SamplingIntervalMs = 1000,
|
||||
int QueueSize = 10,
|
||||
string SecurityMode = "None",
|
||||
// DataConnectionLayer-012: secure-by-default — untrusted server certificates are
|
||||
// Secure-by-default — untrusted server certificates are
|
||||
// rejected unless an operator explicitly opts in per connection. Accepting any
|
||||
// certificate defeats the Sign / SignAndEncrypt modes against a man-in-the-middle.
|
||||
bool AutoAcceptUntrustedCerts = false,
|
||||
@@ -38,7 +38,7 @@ public record OpcUaUserIdentityOptions(
|
||||
string CertificatePassword);
|
||||
|
||||
/// <summary>
|
||||
/// WP-7: Abstraction over OPC UA client library for testability.
|
||||
/// Abstraction over OPC UA client library for testability.
|
||||
/// The real implementation would wrap an OPC UA SDK (e.g., OPC Foundation .NET Standard Library).
|
||||
/// </summary>
|
||||
public interface IOpcUaClient : IAsyncDisposable
|
||||
@@ -150,7 +150,7 @@ public interface IOpcUaClient : IAsyncDisposable
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// M7-B4 (T15): walks the server's address space breadth-first from the
|
||||
/// Walks the server's address space breadth-first from the
|
||||
/// root (ObjectsFolder), bounded by <paramref name="maxDepth"/> and
|
||||
/// <paramref name="maxResults"/>, returning the nodes whose DisplayName or
|
||||
/// root-relative path contains <paramref name="query"/> (case-insensitive
|
||||
@@ -173,7 +173,7 @@ public interface IOpcUaClient : IAsyncDisposable
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// M7-B4 (T15): shared bounded breadth-first address-space search, expressed
|
||||
/// Shared bounded breadth-first address-space search, expressed
|
||||
/// purely over an injected browse delegate so the stub and real OPC UA clients
|
||||
/// run the IDENTICAL walk (matching, continuation-paging, depth bound, result
|
||||
/// cap, node-visit ceiling, cancellation). The delegate is each client's own
|
||||
@@ -351,7 +351,7 @@ internal class StubOpcUaClient : IOpcUaClient
|
||||
string? parentNodeId, string? continuationToken = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// Canned address-space tree so DCL browse/paging flows can be exercised
|
||||
// without a live OPC UA server (T15):
|
||||
// without a live OPC UA server:
|
||||
// (root)
|
||||
// ├─ Folder1 (Object, HasChildren=true)
|
||||
// │ ├─ Tag1 (Variable) ← page 1
|
||||
|
||||
@@ -167,7 +167,7 @@ public class MxGatewayDataConnection : IDataConnection, IBrowsableDataConnection
|
||||
// condition filter, so conditionFilter is intentionally NOT forwarded
|
||||
// here: the DataConnectionActor applies it as the authoritative
|
||||
// client-side gate per source reference AND per condition type
|
||||
// (M2.4 / #8 — AlarmConditionFilter), uniform with the OPC UA path.
|
||||
// (AlarmConditionFilter), uniform with the OPC UA path.
|
||||
_ = Task.Run(() => client.RunAlarmStreamAsync(null, t => callback(t), token), token);
|
||||
}
|
||||
}
|
||||
@@ -277,7 +277,7 @@ public class MxGatewayDataConnection : IDataConnection, IBrowsableDataConnection
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
_eventLoopCts?.Cancel();
|
||||
// DataConnectionLayer-025: the DataConnectionActor disposes adapters
|
||||
// The DataConnectionActor disposes adapters
|
||||
// fire-and-forget on failover/stop without necessarily calling
|
||||
// DisconnectAsync first, so tear down the alarm stream here too — otherwise
|
||||
// the long-running RunAlarmStreamAsync task and its CTS leak on every
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace ZB.MOM.WW.ScadaBridge.DataConnectionLayer.Adapters;
|
||||
/// protocol-neutral <see cref="AlarmConditionState"/> / transition shape. Kept
|
||||
/// free of any OPC UA SDK types so it is unit-testable without a live server;
|
||||
/// the SDK field extraction lives in <c>RealOpcUaClient</c> and is exercised by
|
||||
/// the live smoke test (Task 28).
|
||||
/// the live smoke test.
|
||||
/// </summary>
|
||||
public static class OpcUaAlarmMapper
|
||||
{
|
||||
|
||||
@@ -8,7 +8,7 @@ using ZB.MOM.WW.ScadaBridge.Commons.Types.Enums;
|
||||
namespace ZB.MOM.WW.ScadaBridge.DataConnectionLayer.Adapters;
|
||||
|
||||
/// <summary>
|
||||
/// WP-7: OPC UA adapter implementing IDataConnection.
|
||||
/// OPC UA adapter implementing IDataConnection.
|
||||
/// Maps IDataConnection methods to OPC UA concepts via IOpcUaClient abstraction.
|
||||
///
|
||||
/// OPC UA mapping:
|
||||
@@ -25,13 +25,13 @@ public class OpcUaDataConnection : IDataConnection, IBrowsableDataConnection, IA
|
||||
private string _endpointUrl = string.Empty;
|
||||
private ConnectionHealth _status = ConnectionHealth.Disconnected;
|
||||
|
||||
// DataConnectionLayer-019: the previous _subscriptionHandles Dictionary was
|
||||
// The previous _subscriptionHandles Dictionary was
|
||||
// dead state — written by SubscribeAsync, removed by UnsubscribeAsync, but
|
||||
// never read anywhere. Plain Dictionary writes from thread-pool continuations
|
||||
// after await are racy (concurrent resize is undefined: InvalidOperationException,
|
||||
// bucket corruption, or silently lost entries). Bookkeeping for subscriptions
|
||||
// lives at two genuine layers: RealOpcUaClient._monitoredItems/_callbacks
|
||||
// (already ConcurrentDictionary per DCL-003) at the device adapter, and
|
||||
// (already ConcurrentDictionary) at the device adapter, and
|
||||
// DataConnectionActor._subscriptionIds at the actor — both authoritative.
|
||||
// The adapter has no need for a third copy; the field is removed rather than
|
||||
// converted to ConcurrentDictionary because there is no reader.
|
||||
@@ -49,7 +49,7 @@ public class OpcUaDataConnection : IDataConnection, IBrowsableDataConnection, IA
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
// DataConnectionLayer-013: an int flag toggled with Interlocked.Exchange so the
|
||||
// An int flag toggled with Interlocked.Exchange so the
|
||||
// "only the first caller fires Disconnected" guard in RaiseDisconnected is genuinely
|
||||
// atomic. A plain volatile bool gives visibility but not atomicity — two threads
|
||||
// (e.g. the keep-alive thread and a ReadAsync failure path) could both observe it
|
||||
@@ -161,7 +161,7 @@ public class OpcUaDataConnection : IDataConnection, IBrowsableDataConnection, IA
|
||||
{
|
||||
EnsureConnected();
|
||||
|
||||
// DataConnectionLayer-019: subscriptionId is returned directly to the
|
||||
// SubscriptionId is returned directly to the
|
||||
// caller (DataConnectionActor stores it in _subscriptionIds). No local
|
||||
// bookkeeping is kept here — see the field-deletion note above.
|
||||
return await _client!.CreateSubscriptionAsync(
|
||||
@@ -230,7 +230,7 @@ public class OpcUaDataConnection : IDataConnection, IBrowsableDataConnection, IA
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyDictionary<string, ReadResult>> ReadBatchAsync(IEnumerable<string> tagPaths, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// DataConnectionLayer-007: a single failing tag must not abort the whole batch.
|
||||
// A single failing tag must not abort the whole batch.
|
||||
// ReadAsync re-throws non-cancellation exceptions; catch them per tag and record
|
||||
// a failed ReadResult so the caller receives a complete result map for every
|
||||
// requested tag (the ReadResult shape already carries per-tag Success/error).
|
||||
@@ -269,12 +269,12 @@ public class OpcUaDataConnection : IDataConnection, IBrowsableDataConnection, IA
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyDictionary<string, WriteResult>> WriteBatchAsync(IDictionary<string, object?> values, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// DataConnectionLayer-017: a mid-batch fault must not abort the whole batch.
|
||||
// A mid-batch fault must not abort the whole batch.
|
||||
// WriteAsync calls EnsureConnected(), which throws InvalidOperationException when
|
||||
// the connection drops partway through; catch per-tag exceptions and record a
|
||||
// failed WriteResult so the caller (including WriteBatchAndWaitAsync) receives a
|
||||
// complete result map. OperationCanceledException is still propagated so a
|
||||
// cancelled batch aborts as a whole — mirrors the DCL-007 fix for ReadBatchAsync.
|
||||
// cancelled batch aborts as a whole — mirrors the ReadBatchAsync fix.
|
||||
var results = new Dictionary<string, WriteResult>();
|
||||
foreach (var (tagPath, value) in values)
|
||||
{
|
||||
|
||||
@@ -21,7 +21,7 @@ public class RealOpcUaClient : IOpcUaClient
|
||||
private ISession? _session;
|
||||
private Subscription? _subscription;
|
||||
|
||||
// DataConnectionLayer-003: these maps are read from the OPC Foundation SDK's
|
||||
// These maps are read from the OPC Foundation SDK's
|
||||
// internal publish threads (the MonitoredItem.Notification handler reads
|
||||
// _callbacks) concurrently with subscribe/disconnect mutations that run on
|
||||
// thread-pool threads. Plain Dictionary access during a concurrent resize or
|
||||
@@ -29,13 +29,13 @@ public class RealOpcUaClient : IOpcUaClient
|
||||
private readonly ConcurrentDictionary<string, MonitoredItem> _monitoredItems = new();
|
||||
private readonly ConcurrentDictionary<string, Action<string, object?, DateTime, uint>> _callbacks = new();
|
||||
|
||||
// Task-11: native alarm (A&C) event subscriptions, keyed by handle.
|
||||
// Native alarm (A&C) event subscriptions, keyed by handle.
|
||||
private readonly ConcurrentDictionary<string, MonitoredItem> _alarmItems = new();
|
||||
// Per-handle "currently inside a ConditionRefresh replay" flag → Snapshot kind.
|
||||
private readonly ConcurrentDictionary<string, bool> _alarmInRefresh = new();
|
||||
// Per-handle last (active, acked) by source reference, to derive transition kind.
|
||||
private readonly ConcurrentDictionary<string, Dictionary<string, (bool Active, bool Acked)>> _alarmLastState = new();
|
||||
// DataConnectionLayer-013: int flag toggled with Interlocked.Exchange so the
|
||||
// Int flag toggled with Interlocked.Exchange so the
|
||||
// once-only ConnectionLost guard in OnSessionKeepAlive is atomic, not just visible.
|
||||
// 0 = not fired, 1 = fired.
|
||||
private int _connectionLostFired;
|
||||
@@ -93,7 +93,7 @@ public class RealOpcUaClient : IOpcUaClient
|
||||
await appConfig.ValidateAsync(ApplicationType.Client);
|
||||
if (opts.AutoAcceptUntrustedCerts)
|
||||
{
|
||||
// DataConnectionLayer-012: this accepts ANY server certificate, defeating
|
||||
// This accepts ANY server certificate, defeating
|
||||
// certificate trust enforcement. Surface a prominent warning so an operator
|
||||
// who has opted in is aware of the man-in-the-middle exposure on the link.
|
||||
_logger.LogWarning(
|
||||
@@ -158,7 +158,7 @@ public class RealOpcUaClient : IOpcUaClient
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// T17: Probes an OPC UA endpoint configuration WITHOUT persisting it or creating a
|
||||
/// Probes an OPC UA endpoint configuration WITHOUT persisting it or creating a
|
||||
/// long-lived connection — connect, capture the server certificate if it is untrusted,
|
||||
/// then disconnect. The probe is secure-by-default and READ-ONLY: it forces
|
||||
/// <c>AutoAcceptUntrustedCertificates = false</c> and a validation hook that captures an
|
||||
@@ -196,7 +196,7 @@ public class RealOpcUaClient : IOpcUaClient
|
||||
_ => MessageSecurityMode.None
|
||||
};
|
||||
|
||||
// T17: secure-by-default — force AutoAccept=false so an untrusted server cert is
|
||||
// Secure-by-default — force AutoAccept=false so an untrusted server cert is
|
||||
// captured and rejected rather than silently accepted (defeating the whole probe).
|
||||
var appConfig = new ApplicationConfiguration
|
||||
{
|
||||
@@ -216,7 +216,7 @@ public class RealOpcUaClient : IOpcUaClient
|
||||
TransportQuotas = new TransportQuotas { OperationTimeout = config.OperationTimeoutMs }
|
||||
};
|
||||
|
||||
// T17: capture the untrusted server cert, then REJECT it (e.Accept = false). The
|
||||
// Capture the untrusted server cert, then REJECT it (e.Accept = false). The
|
||||
// validator runs on the SDK's connect thread; copying the cert is the only state we
|
||||
// keep. Never accept — this probe must not trust anything.
|
||||
appConfig.CertificateValidator.CertificateValidation += (_, e) =>
|
||||
@@ -285,7 +285,7 @@ public class RealOpcUaClient : IOpcUaClient
|
||||
}
|
||||
finally
|
||||
{
|
||||
// T17: ALWAYS dispose the probe session — never leave a connection open.
|
||||
// ALWAYS dispose the probe session — never leave a connection open.
|
||||
if (session != null)
|
||||
{
|
||||
try { await session.CloseAsync(CancellationToken.None); }
|
||||
@@ -298,7 +298,7 @@ public class RealOpcUaClient : IOpcUaClient
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// T17: Pure mapping of a probe outcome — an optional exception plus an optionally
|
||||
/// Pure mapping of a probe outcome — an optional exception plus an optionally
|
||||
/// captured untrusted server certificate — to a <see cref="VerifyEndpointResult"/>.
|
||||
/// Factored out so the classification is unit-testable WITHOUT a live OPC UA server.
|
||||
/// Precedence: a captured certificate ALWAYS yields
|
||||
@@ -455,8 +455,8 @@ public class RealOpcUaClient : IOpcUaClient
|
||||
}
|
||||
}
|
||||
|
||||
// ── Native alarm (Alarms & Conditions) subscription (Task-11) ──
|
||||
// Behavioral correctness verified against a live A&C server in Task 28; only
|
||||
// ── Native alarm (Alarms & Conditions) subscription ──
|
||||
// Behavioral correctness verified against a live A&C server; only
|
||||
// the OpcUaAlarmMapper value→state logic is unit-tested.
|
||||
|
||||
// Fixed select-clause order; parsed by index in HandleAlarmEvent.
|
||||
@@ -485,7 +485,7 @@ public class RealOpcUaClient : IOpcUaClient
|
||||
SamplingInterval = 0,
|
||||
QueueSize = 1000,
|
||||
// Server-side WhereClause is a bandwidth optimisation only — the
|
||||
// authoritative condition-type gate lives in DataConnectionActor (M2.4 / #8).
|
||||
// authoritative condition-type gate lives in DataConnectionActor.
|
||||
Filter = BuildAlarmEventFilter(AlarmConditionFilter.Parse(conditionFilter))
|
||||
};
|
||||
|
||||
@@ -519,7 +519,7 @@ public class RealOpcUaClient : IOpcUaClient
|
||||
/// <summary>
|
||||
/// Maps the standard OPC UA Alarms & Conditions type names (case-insensitive)
|
||||
/// to their well-known <see cref="ObjectTypeIds"/> NodeIds, for building the
|
||||
/// optional server-side WhereClause (M2.4 / #8). Only standard types appear
|
||||
/// optional server-side WhereClause. Only standard types appear
|
||||
/// here; vendor/custom type names cannot be mapped without browsing the server
|
||||
/// type tree, so they are handled by the client-side gate alone.
|
||||
/// <para>
|
||||
@@ -554,7 +554,7 @@ public class RealOpcUaClient : IOpcUaClient
|
||||
|
||||
/// <summary>
|
||||
/// Inverse of <see cref="KnownConditionTypeIds"/> (NodeId → friendly name), derived
|
||||
/// from it so the two cannot drift (M2.4 / #8). Used by <see cref="ResolveAlarmTypeName"/>
|
||||
/// from it so the two cannot drift. Used by <see cref="ResolveAlarmTypeName"/>
|
||||
/// to translate the event-type NodeId an OPC UA server sends back into the friendly
|
||||
/// type name the conditionFilter gate and server-side WhereClause both key off.
|
||||
/// </summary>
|
||||
@@ -563,7 +563,7 @@ public class RealOpcUaClient : IOpcUaClient
|
||||
|
||||
/// <summary>
|
||||
/// Resolves an event-type <see cref="NodeId"/> to the friendly condition-type name the
|
||||
/// <c>conditionFilter</c> gate (and the server-side WhereClause) use (M2.4 / #8).
|
||||
/// <c>conditionFilter</c> gate (and the server-side WhereClause) use.
|
||||
///
|
||||
/// <para>
|
||||
/// Standard A&C types are returned as their friendly name (e.g. <c>i=9341</c> →
|
||||
@@ -591,7 +591,7 @@ public class RealOpcUaClient : IOpcUaClient
|
||||
/// AlarmConditionType / AcknowledgeableConditionType state sub-variables we mirror,
|
||||
/// and — when <paramref name="conditionFilter"/> is non-empty and every requested
|
||||
/// type maps to a standard A&C type — a server-side <see cref="ContentFilter"/>
|
||||
/// WhereClause (OfType, OR'd) as a bandwidth optimisation (M2.4 / #8).
|
||||
/// WhereClause (OfType, OR'd) as a bandwidth optimisation.
|
||||
///
|
||||
/// <para>
|
||||
/// Conservative by design: if <em>any</em> requested type name cannot be mapped to
|
||||
@@ -798,7 +798,7 @@ public class RealOpcUaClient : IOpcUaClient
|
||||
SourceReference: sourceRef,
|
||||
SourceObjectReference: sourceObjectRef,
|
||||
// Resolve the event-type NodeId (e.g. "i=9341") to the friendly type name
|
||||
// the conditionFilter gate keys off (M2.4 / #8); NodeId-string for custom types.
|
||||
// the conditionFilter gate keys off; NodeId-string for custom types.
|
||||
AlarmTypeName: ResolveAlarmTypeName(eventType),
|
||||
Kind: kind,
|
||||
Condition: OpcUaAlarmMapper.BuildCondition(active, acked, confirmed, shelve, suppressed, severity),
|
||||
@@ -931,7 +931,7 @@ public class RealOpcUaClient : IOpcUaClient
|
||||
// huge flat folder cannot return an unbounded set. 500 leaves headroom for
|
||||
// the downstream frame-size budget (DataConnectionActor.CapBrowseChildren)
|
||||
// even with long string NodeIds; a non-empty continuation point surfaces as
|
||||
// a ContinuationToken so the caller can page via BrowseNext (T15).
|
||||
// a ContinuationToken so the caller can page via BrowseNext.
|
||||
private const uint BrowseMaxReferencesPerNode = 500u;
|
||||
|
||||
// NodeClassMask intentionally excludes ReferenceType, View, Variable-
|
||||
@@ -1050,7 +1050,7 @@ public class RealOpcUaClient : IOpcUaClient
|
||||
/// <summary>
|
||||
/// Shared "build children + B1 type enrichment + continuation token" logic
|
||||
/// used by both the initial browse and the BrowseNext paths so the
|
||||
/// enrichment is never duplicated (T15). A non-empty
|
||||
/// enrichment is never duplicated. A non-empty
|
||||
/// <paramref name="continuationPoint"/> is surfaced as a Base64
|
||||
/// <c>ContinuationToken</c> with <c>Truncated=true</c>; otherwise the result
|
||||
/// is exhausted (<c>ContinuationToken=null, Truncated=false</c>).
|
||||
@@ -1078,13 +1078,13 @@ public class RealOpcUaClient : IOpcUaClient
|
||||
// Variable child; on ANY failure we leave the three fields null and
|
||||
// return the children exactly as built above. Non-Variable nodes are
|
||||
// never read and keep null type info. Runs identically on the browse
|
||||
// and browse-next pages (T15 shares this path).
|
||||
// and browse-next pages.
|
||||
children = await EnrichVariableTypeInfoAsync(session, refs, children, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
// A non-empty continuation point means the server has more refs than
|
||||
// were returned on this page. Surface it as an opaque Base64 token the
|
||||
// caller passes back to fetch the next page via BrowseNext (T15).
|
||||
// caller passes back to fetch the next page via BrowseNext.
|
||||
var hasMore = continuationPoint != null && continuationPoint.Length > 0;
|
||||
var token = hasMore ? Convert.ToBase64String(continuationPoint!) : null;
|
||||
return new Commons.Interfaces.Protocol.BrowseChildrenResult(children, hasMore, token);
|
||||
@@ -1098,7 +1098,7 @@ public class RealOpcUaClient : IOpcUaClient
|
||||
_ => Commons.Interfaces.Protocol.BrowseNodeClass.Other
|
||||
};
|
||||
|
||||
// T16 type-info: best-effort enrichment of Variable rows with DataType,
|
||||
// Type-info: best-effort enrichment of Variable rows with DataType,
|
||||
// ValueRank, and Writable. Reads all three attributes for every Variable
|
||||
// child in ONE ReadAsync round-trip; on any failure (or zero variables)
|
||||
// returns the input children unchanged. Caller has already built the
|
||||
@@ -1210,7 +1210,7 @@ public class RealOpcUaClient : IOpcUaClient
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// T16 type-info: maps well-known OPC UA built-in <c>DataType</c> NodeIds
|
||||
/// Type-info: maps well-known OPC UA built-in <c>DataType</c> NodeIds
|
||||
/// (namespace 0 numeric ids in <see cref="DataTypeIds"/>) to friendly,
|
||||
/// CLR-flavoured names for display in the node picker. Vendor / structured
|
||||
/// DataTypes (anything not in the built-in table) fall back to the NodeId
|
||||
@@ -1277,8 +1277,8 @@ public class RealOpcUaClientFactory : IOpcUaClientFactory
|
||||
{
|
||||
private readonly OpcUaGlobalOptions _globalOptions;
|
||||
|
||||
// DataConnectionLayer-014: a real logger must be threaded through to every
|
||||
// RealOpcUaClient this factory builds, otherwise the DCL-012 auto-accept-certificate
|
||||
// A real logger must be threaded through to every
|
||||
// RealOpcUaClient this factory builds, otherwise the auto-accept-certificate
|
||||
// warning emitted in RealOpcUaClient.ConnectAsync sinks into NullLogger and is never
|
||||
// seen in production. The factory is constructed by DataConnectionFactory, which has
|
||||
// an ILoggerFactory available.
|
||||
|
||||
Reference in New Issue
Block a user