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
@@ -61,8 +61,17 @@ internal sealed class EventPump : IAsyncDisposable
private Task? _dispatchLoop;
private bool _disposed;
/// <summary>Occurs when a data change event is received from the Galaxy subscriber.</summary>
public event EventHandler<DataChangeEventArgs>? OnDataChange;
/// <summary>Initializes a new instance of the EventPump class.</summary>
/// <param name="subscriber">The Galaxy subscriber to consume events from.</param>
/// <param name="registry">The subscription registry for resolving subscribers.</param>
/// <param name="logger">The logger instance; if null, uses NullLogger.</param>
/// <param name="handleFactory">The factory for creating subscription handles; if null, uses GalaxySubscriptionHandle.</param>
/// <param name="channelCapacity">The bounded channel capacity for buffering events.</param>
/// <param name="clientName">The client name for metric tagging; if null, uses "&lt;unknown&gt;".</param>
/// <param name="onStreamFault">Optional callback invoked when the stream faults.</param>
public EventPump(
IGalaxySubscriber subscriber,
SubscriptionRegistry registry,
@@ -234,6 +243,8 @@ internal sealed class EventPump : IAsyncDisposable
ServerTimestampUtc: DateTime.UtcNow);
}
/// <summary>Disposes the event pump and cancels all running tasks.</summary>
/// <returns>A value task representing the asynchronous disposal operation.</returns>
public async ValueTask DisposeAsync()
{
if (_disposed) return;
@@ -11,6 +11,8 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Runtime;
/// </summary>
internal sealed class GalaxyAlarmSubscriptionHandle : IAlarmSubscriptionHandle
{
/// <summary>Initializes a new instance of the GalaxyAlarmSubscriptionHandle class.</summary>
/// <param name="diagnosticId">The diagnostic ID for the subscription.</param>
public GalaxyAlarmSubscriptionHandle(string diagnosticId)
{
DiagnosticId = diagnosticId;
@@ -30,12 +30,16 @@ public sealed class GalaxyMxSession : IAsyncDisposable
private int _serverHandle;
private bool _disposed;
/// <summary>Initializes a new instance of the GalaxyMxSession class.</summary>
/// <param name="options">MX Access configuration options.</param>
/// <param name="logger">Optional logger instance; uses NullLogger if not provided.</param>
public GalaxyMxSession(GalaxyMxAccessOptions options, ILogger? logger = null)
{
_options = options ?? throw new ArgumentNullException(nameof(options));
_logger = logger ?? NullLogger.Instance;
}
/// <summary>Gets a value indicating whether the session is connected.</summary>
public bool IsConnected => _session is not null;
/// <summary>
@@ -49,6 +53,8 @@ public sealed class GalaxyMxSession : IAsyncDisposable
/// configured client name. Idempotent — second calls are no-ops while
/// <see cref="IsConnected"/> is true.
/// </summary>
/// <param name="clientOptions">The MX gateway client options.</param>
/// <param name="cancellationToken">The cancellation token.</param>
public async Task ConnectAsync(MxGatewayClientOptions clientOptions, CancellationToken cancellationToken)
{
ObjectDisposedException.ThrowIf(_disposed, this);
@@ -67,6 +73,8 @@ public sealed class GalaxyMxSession : IAsyncDisposable
/// fake). Skips the gateway-client construction so tests can drive the session
/// surface without spinning a real gRPC channel. Caller retains client ownership.
/// </summary>
/// <param name="session">The MX gateway session to attach.</param>
/// <param name="serverHandle">The server handle value to use.</param>
internal void AttachForTests(MxGatewaySession session, int serverHandle)
{
ObjectDisposedException.ThrowIf(_disposed, this);
@@ -80,6 +88,7 @@ public sealed class GalaxyMxSession : IAsyncDisposable
/// </summary>
public MxGatewaySession? Session => _session;
/// <summary>Disposes the session and underlying gateway client resources.</summary>
public async ValueTask DisposeAsync()
{
if (_disposed) return;
@@ -8,5 +8,6 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Runtime;
/// </summary>
internal sealed record GalaxySubscriptionHandle(long SubscriptionId) : ISubscriptionHandle
{
/// <summary>Gets the diagnostic identifier for the subscription.</summary>
public string DiagnosticId => $"galaxy-sub-{SubscriptionId}";
}
@@ -25,6 +25,8 @@ internal static class GalaxyTelemetry
/// Tag a span with a failure reason and set its status to <c>Error</c>. Helper
/// so the decorators don't repeat the four-line idiom on every catch block.
/// </summary>
/// <param name="activity">The activity to tag with error information.</param>
/// <param name="ex">The exception to record.</param>
public static void RecordError(this Activity? activity, Exception ex)
{
if (activity is null) return;
@@ -23,12 +23,20 @@ internal sealed class GatewayGalaxyAlarmAcknowledger : IGalaxyAlarmAcknowledger
private readonly MxGatewayClient _client;
private readonly ILogger _logger;
/// <summary>Initializes a new instance of the <see cref="GatewayGalaxyAlarmAcknowledger"/> class.</summary>
/// <param name="client">The MX gateway client used to send acknowledgments.</param>
/// <param name="logger">A logger for diagnostic output.</param>
public GatewayGalaxyAlarmAcknowledger(MxGatewayClient client, ILogger logger)
{
_client = client ?? throw new ArgumentNullException(nameof(client));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
/// <summary>Acknowledges an alarm via the gateway.</summary>
/// <param name="alarmFullReference">The full reference path of the alarm to acknowledge.</param>
/// <param name="comment">An operator-supplied comment attached to the acknowledgement.</param>
/// <param name="operatorUser">The name of the operator performing the acknowledgement.</param>
/// <param name="cancellationToken">A token to cancel the asynchronous operation.</param>
public async Task AcknowledgeAsync(
string alarmFullReference,
string comment,
@@ -37,6 +37,8 @@ internal sealed class GatewayGalaxyAlarmFeed : IGalaxyAlarmFeed
/// Opens a <c>StreamAlarms</c> feed. Matches the method group
/// <c>MxGatewayClient.StreamAlarmsAsync</c>.
/// </summary>
/// <param name="request">The stream request parameters.</param>
/// <param name="cancellationToken">A cancellation token.</param>
internal delegate IAsyncEnumerable<AlarmFeedMessage> AlarmStreamFactory(
StreamAlarmsRequest request, CancellationToken cancellationToken);
@@ -65,8 +67,15 @@ internal sealed class GatewayGalaxyAlarmFeed : IGalaxyAlarmFeed
private Task? _loop;
private bool _disposed;
/// <summary>Occurs when an alarm transition (raise, acknowledge, clear) is received from the Galaxy feed.</summary>
public event EventHandler<GalaxyAlarmTransition>? OnAlarmTransition;
/// <summary>Initializes a new instance of the <see cref="GatewayGalaxyAlarmFeed"/> class.</summary>
/// <param name="streamFactory">A factory delegate that opens the alarm stream.</param>
/// <param name="logger">An optional logger for diagnostic output.</param>
/// <param name="clientName">An optional client name for tagging log entries.</param>
/// <param name="alarmFilterPrefix">An optional prefix to filter alarms in the stream.</param>
/// <param name="reconnectDelay">An optional delay before reconnecting after a stream fault.</param>
public GatewayGalaxyAlarmFeed(
AlarmStreamFactory streamFactory,
ILogger? logger = null,
@@ -81,6 +90,7 @@ internal sealed class GatewayGalaxyAlarmFeed : IGalaxyAlarmFeed
_clientTag = new KeyValuePair<string, object?>("galaxy.client", clientName ?? "<unknown>");
}
/// <summary>Starts the alarm feed by opening the stream and processing messages in a background task.</summary>
public void Start()
{
ObjectDisposedException.ThrowIf(_disposed, this);
@@ -250,6 +260,7 @@ internal sealed class GatewayGalaxyAlarmFeed : IGalaxyAlarmFeed
_ => GalaxyAlarmTransitionKind.Unspecified,
};
/// <summary>Releases the alarm feed resources and stops the background stream task.</summary>
public async ValueTask DisposeAsync()
{
if (_disposed) return;
@@ -28,6 +28,10 @@ public sealed class GatewayGalaxyDataWriter : IGalaxyDataWriter
private readonly ConcurrentDictionary<string, int> _itemHandles =
new(StringComparer.OrdinalIgnoreCase);
/// <summary>Initializes a new Galaxy data writer.</summary>
/// <param name="session">The MXAccess gateway session.</param>
/// <param name="writeUserId">The user ID for write operations.</param>
/// <param name="logger">Optional logger for tracing.</param>
public GatewayGalaxyDataWriter(GalaxyMxSession session, int writeUserId, ILogger? logger = null)
{
_session = session ?? throw new ArgumentNullException(nameof(session));
@@ -35,6 +39,11 @@ public sealed class GatewayGalaxyDataWriter : IGalaxyDataWriter
_logger = logger ?? NullLogger.Instance;
}
/// <summary>Writes values to Galaxy tags through the gateway.</summary>
/// <param name="writes">The write requests.</param>
/// <param name="securityResolver">Function to resolve security classification per tag.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>The write results per request.</returns>
public async Task<IReadOnlyList<WriteResult>> WriteAsync(
IReadOnlyList<WriteRequest> writes,
Func<string, SecurityClassification> securityResolver,
@@ -21,11 +21,18 @@ public sealed class GatewayGalaxySubscriber : IGalaxySubscriber
private readonly Lock _intervalLock = new();
private int _lastAppliedIntervalMs = -1; // -1 = never applied; 0 = explicit "use gw default"
/// <summary>Initializes a new instance of GatewayGalaxySubscriber.</summary>
/// <param name="session">The Galaxy MX session to use for subscription operations.</param>
public GatewayGalaxySubscriber(GalaxyMxSession session)
{
_session = session ?? throw new ArgumentNullException(nameof(session));
}
/// <summary>Subscribes to a bulk list of Galaxy references with optional buffered update interval.</summary>
/// <param name="fullReferences">The full Galaxy tag references to subscribe to.</param>
/// <param name="bufferedUpdateIntervalMs">The buffered update interval in milliseconds.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>A task that returns a list of subscribe results.</returns>
public async Task<IReadOnlyList<SubscribeResult>> SubscribeBulkAsync(
IReadOnlyList<string> fullReferences, int bufferedUpdateIntervalMs, CancellationToken cancellationToken)
{
@@ -93,6 +100,10 @@ public sealed class GatewayGalaxySubscriber : IGalaxySubscriber
}
}
/// <summary>Unsubscribes from a bulk list of item handles.</summary>
/// <param name="itemHandles">The item handles to unsubscribe from.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>A task representing the unsubscribe operation.</returns>
public async Task UnsubscribeBulkAsync(IReadOnlyList<int> itemHandles, CancellationToken cancellationToken)
{
if (itemHandles.Count == 0) return;
@@ -106,6 +117,9 @@ public sealed class GatewayGalaxySubscriber : IGalaxySubscriber
.ConfigureAwait(false);
}
/// <summary>Streams Galaxy MX events asynchronously.</summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>An async enumerable of MX events.</returns>
public IAsyncEnumerable<MxEvent> StreamEventsAsync(CancellationToken cancellationToken)
{
var session = _session.Session
@@ -22,6 +22,9 @@ public interface IGalaxyDataReader
/// Implementations MUST return the same length as the input — partial-tag
/// failures are encoded as Bad-quality snapshots, not omitted.
/// </summary>
/// <param name="fullReferences">The list of fully-qualified tag references to read.</param>
/// <param name="cancellationToken">Cancellation token for the read operation.</param>
/// <returns>A list of data value snapshots, one per input reference in the same order.</returns>
Task<IReadOnlyList<DataValueSnapshot>> ReadAsync(
IReadOnlyList<string> fullReferences, CancellationToken cancellationToken);
}
@@ -17,10 +17,15 @@ public interface IGalaxySubscriber
/// negative — the caller treats those as per-tag failures rather than a whole-call
/// failure.
/// </summary>
/// <param name="fullReferences">The list of tag references to subscribe to.</param>
/// <param name="bufferedUpdateIntervalMs">The buffered update interval in milliseconds.</param>
/// <param name="cancellationToken">Cancellation token for the operation.</param>
Task<IReadOnlyList<SubscribeResult>> SubscribeBulkAsync(
IReadOnlyList<string> fullReferences, int bufferedUpdateIntervalMs, CancellationToken cancellationToken);
/// <summary>Unsubscribe a batch of item handles obtained from <see cref="SubscribeBulkAsync"/>.</summary>
/// <param name="itemHandles">The item handles to unsubscribe.</param>
/// <param name="cancellationToken">Cancellation token for the operation.</param>
Task UnsubscribeBulkAsync(IReadOnlyList<int> itemHandles, CancellationToken cancellationToken);
/// <summary>
@@ -28,5 +33,6 @@ public interface IGalaxySubscriber
/// <see cref="MxEvent"/> carries the gw item handle the caller correlates against
/// its <see cref="SubscriptionRegistry"/>.
/// </summary>
/// <param name="cancellationToken">Cancellation token for the stream.</param>
IAsyncEnumerable<MxEvent> StreamEventsAsync(CancellationToken cancellationToken);
}
@@ -36,6 +36,7 @@ internal static class MxAccessSeverityMapper
/// Translate a raw MXAccess severity into the four-bucket
/// <see cref="AlarmSeverity"/> + OPC UA Part 9 numeric severity tuple.
/// </summary>
/// <param name="rawMxAccessSeverity">The raw MXAccess severity value (0-999 range, clamped if out of range).</param>
public static (AlarmSeverity Bucket, int OpcUaSeverity) Map(int rawMxAccessSeverity)
{
if (rawMxAccessSeverity < 250)
@@ -13,6 +13,8 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Runtime;
/// </summary>
internal static class MxValueDecoder
{
/// <summary>Decodes a gateway MxValue into a boxed CLR object.</summary>
/// <param name="value">The MxValue to decode, or null.</param>
public static object? Decode(MxValue? value)
{
if (value is null) return null;
@@ -13,6 +13,10 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Runtime;
/// </summary>
internal static class MxValueEncoder
{
/// <summary>Encodes a CLR value as an MxValue for transmission to the gateway.</summary>
/// <param name="value">The value to encode, or null for a null MxValue.</param>
/// <returns>An MxValue instance representing the encoded value.</returns>
/// <exception cref="ArgumentException">Thrown if the value type is not supported.</exception>
public static MxValue Encode(object? value)
{
if (value is null) return new MxValue { IsNull = true };
@@ -66,6 +66,14 @@ public sealed class ReconnectSupervisor : IDisposable
/// <summary>Fires after every state transition.</summary>
public event EventHandler<StateTransition>? StateChanged;
/// <summary>
/// Initializes a new ReconnectSupervisor with the specified recovery handlers and options.
/// </summary>
/// <param name="reopen">Callback to reopen the transport connection.</param>
/// <param name="replay">Callback to replay subscriptions and state after reopening.</param>
/// <param name="options">Optional reconnection behavior configuration.</param>
/// <param name="logger">Optional logger instance; defaults to null logger if not provided.</param>
/// <param name="backoffDelay">Optional custom backoff delay calculator; uses default if not provided.</param>
public ReconnectSupervisor(
Func<CancellationToken, Task> reopen,
Func<CancellationToken, Task> replay,
@@ -92,11 +100,13 @@ public sealed class ReconnectSupervisor : IDisposable
get { lock (_stateLock) return _state != State.Healthy; }
}
/// <summary>Gets the message of the last reported transport error, if any.</summary>
public string? LastError
{
get { lock (_stateLock) return _lastError; }
}
/// <summary>Gets the UTC timestamp of the most recent state transition.</summary>
public DateTime? LastTransitionUtc
{
get { lock (_stateLock) return _lastTransitionUtc; }
@@ -108,6 +118,7 @@ public sealed class ReconnectSupervisor : IDisposable
/// first call spawns a background task that drives reopen → replay until it
/// succeeds or <see cref="Dispose"/> cancels it.
/// </summary>
/// <param name="cause">The exception that triggered the failure notification.</param>
public void ReportTransportFailure(Exception cause)
{
ArgumentNullException.ThrowIfNull(cause);
@@ -135,6 +146,7 @@ public sealed class ReconnectSupervisor : IDisposable
/// is cancelled. Returns immediately when already Healthy. Useful for tests
/// and for orchestration that wants to gate calls on recovery completing.
/// </summary>
/// <param name="cancellationToken">Token to cancel the wait operation.</param>
public async Task WaitForHealthyAsync(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested && IsDegraded)
@@ -227,6 +239,7 @@ public sealed class ReconnectSupervisor : IDisposable
}
}
/// <summary>Disposes the supervisor and cancels any in-flight recovery loop.</summary>
public void Dispose()
{
if (_disposed) return;
@@ -263,6 +276,9 @@ public sealed record ReconnectOptions(
TimeSpan? InitialBackoffOverride = null,
TimeSpan? MaxBackoffOverride = null)
{
/// <summary>Gets the initial backoff delay for reconnection attempts.</summary>
public TimeSpan InitialBackoff => InitialBackoffOverride ?? TimeSpan.FromMilliseconds(500);
/// <summary>Gets the maximum backoff delay for reconnection attempts.</summary>
public TimeSpan MaxBackoff => MaxBackoffOverride ?? TimeSpan.FromSeconds(30);
}
@@ -49,6 +49,9 @@ internal static class StatusCodeMap
/// which is what Wonderware Historian + MXAccess surface as <c>OPCITEMSTATE.qLong</c>'s
/// low byte) to the OPC UA StatusCode uint.
/// </summary>
/// <param name="q">The OPC DA quality byte to convert.</param>
/// <param name="logger">Optional logger for diagnostics on unknown quality bytes.</param>
/// <returns>The OPC UA status code.</returns>
public static uint FromQualityByte(byte q, ILogger? logger = null) => q switch
{
// Good family — top two bits 11b (192-255).
@@ -83,6 +86,9 @@ internal static class StatusCodeMap
/// the authoritative indicator. On failure, the detail byte (OPC DA quality substatus)
/// drives the specific code, with a transport-error fallback for pre-MXAccess failures.
/// </summary>
/// <param name="status">The MX status proxy to convert, or null for Good status.</param>
/// <param name="logger">Optional logger for diagnostics on unknown status codes.</param>
/// <returns>The OPC UA status code.</returns>
public static uint FromMxStatus(MxStatusProxy? status, ILogger? logger = null)
{
if (status is null) return Good;
@@ -112,6 +118,8 @@ internal static class StatusCodeMap
/// the round-trip in one place means a future change to the OPC UA bit layout cannot
/// silently desync the probe-health decode.
/// </summary>
/// <param name="statusCode">The OPC UA status code to convert.</param>
/// <returns>The OPC DA quality category byte.</returns>
public static byte ToQualityCategoryByte(uint statusCode) =>
(byte)(((statusCode >> 30) & 0x3u) switch
{
@@ -26,7 +26,9 @@ internal sealed class SubscriptionRegistry
private readonly ConcurrentDictionary<int, ImmutableHashSet<long>> _subscribersByItemHandle = new();
private long _nextSubscriptionId;
/// <summary>Gets the number of tracked subscriptions.</summary>
public int TrackedSubscriptionCount => _bySubscriptionId.Count;
/// <summary>Gets the number of tracked item handles.</summary>
public int TrackedItemHandleCount => _subscribersByItemHandle.Count;
/// <summary>Allocate a fresh subscription id. Monotonic; unique per registry lifetime.</summary>
@@ -37,6 +39,8 @@ internal sealed class SubscriptionRegistry
/// Failed tags (item handle = 0 or negative) are stored anyway so unsubscribe can
/// emit per-tag UnsubscribeBulk for the ones that did succeed.
/// </summary>
/// <param name="subscriptionId">The subscription identifier.</param>
/// <param name="bindings">The tag bindings for the subscription.</param>
public void Register(long subscriptionId, IReadOnlyList<TagBinding> bindings)
{
var entry = new SubscriptionEntry(subscriptionId, bindings);
@@ -55,6 +59,8 @@ internal sealed class SubscriptionRegistry
/// Remove a subscription. Returns the bindings the caller should pass to
/// <c>UnsubscribeBulkAsync</c>; null when the id was never registered.
/// </summary>
/// <param name="subscriptionId">The subscription identifier.</param>
/// <returns>The bindings for the subscription, or null if not found.</returns>
public IReadOnlyList<TagBinding>? Remove(long subscriptionId)
{
if (!_bySubscriptionId.TryRemove(subscriptionId, out var entry)) return null;
@@ -83,6 +89,8 @@ internal sealed class SubscriptionRegistry
/// scan of the binding list. At 50k tags / 1Hz this turns each dispatch from a
/// 50k-element scan into a single dictionary lookup.
/// </remarks>
/// <param name="itemHandle">The gateway item handle.</param>
/// <returns>A list of subscription and reference pairs for the item handle.</returns>
public IReadOnlyList<(long SubscriptionId, string FullReference)> ResolveSubscribers(int itemHandle)
{
if (!_subscribersByItemHandle.TryGetValue(itemHandle, out var bag)) return [];
@@ -117,6 +125,8 @@ internal sealed class SubscriptionRegistry
/// handles dispatch and the now-dead pre-reconnect handles are dropped. No-op when the
/// subscription id is unknown (it was unsubscribed during the reconnect window).
/// </summary>
/// <param name="subscriptionId">The subscription identifier.</param>
/// <param name="newBindings">The new tag bindings after reconnection.</param>
public void Rebind(long subscriptionId, IReadOnlyList<TagBinding> newBindings)
{
if (!_bySubscriptionId.TryGetValue(subscriptionId, out var oldEntry)) return;
@@ -151,12 +161,19 @@ internal sealed class SubscriptionRegistry
/// (Driver.Galaxy-012). Failed bindings (item handle ≤ 0) are excluded from the
/// index because the EventPump only dispatches for positive handles.
/// </summary>
/// <summary>Per-subscription bookkeeping entry.</summary>
private sealed class SubscriptionEntry
{
/// <summary>Gets the subscription identifier.</summary>
public long SubscriptionId { get; }
/// <summary>Gets the tag bindings for the subscription.</summary>
public IReadOnlyList<TagBinding> Bindings { get; }
/// <summary>Gets the index of full references by item handle.</summary>
public IReadOnlyDictionary<int, string> FullRefByItemHandle { get; }
/// <summary>Initializes a new subscription entry.</summary>
/// <param name="subscriptionId">The subscription identifier.</param>
/// <param name="bindings">The tag bindings for the subscription.</param>
public SubscriptionEntry(long subscriptionId, IReadOnlyList<TagBinding> bindings)
{
SubscriptionId = subscriptionId;
@@ -10,6 +10,10 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Runtime;
/// </summary>
internal sealed class TracedGalaxyDataWriter(IGalaxyDataWriter inner, string clientName) : IGalaxyDataWriter
{
/// <summary>Writes data to Galaxy while recording telemetry span.</summary>
/// <param name="writes">The list of write requests to process.</param>
/// <param name="securityResolver">Function to resolve security classification for tag references.</param>
/// <param name="cancellationToken">Cancellation token for the operation.</param>
public async Task<IReadOnlyList<WriteResult>> WriteAsync(
IReadOnlyList<WriteRequest> writes,
Func<string, SecurityClassification> securityResolver,
@@ -10,6 +10,10 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Runtime;
/// </summary>
internal sealed class TracedGalaxySubscriber(IGalaxySubscriber inner, string clientName) : IGalaxySubscriber
{
/// <summary>Subscribes to multiple Galaxy tags in bulk with tracing.</summary>
/// <param name="fullReferences">The full tag references to subscribe to.</param>
/// <param name="bufferedUpdateIntervalMs">The buffered update interval in milliseconds.</param>
/// <param name="cancellationToken">The cancellation token.</param>
public async Task<IReadOnlyList<SubscribeResult>> SubscribeBulkAsync(
IReadOnlyList<string> fullReferences, int bufferedUpdateIntervalMs, CancellationToken cancellationToken)
{
@@ -31,6 +35,9 @@ internal sealed class TracedGalaxySubscriber(IGalaxySubscriber inner, string cli
}
}
/// <summary>Unsubscribes from multiple Galaxy tags in bulk with tracing.</summary>
/// <param name="itemHandles">The item handles to unsubscribe from.</param>
/// <param name="cancellationToken">The cancellation token.</param>
public async Task UnsubscribeBulkAsync(IReadOnlyList<int> itemHandles, CancellationToken cancellationToken)
{
using var activity = GalaxyTelemetry.ActivitySource.StartActivity("galaxy.unsubscribe_bulk");
@@ -52,6 +59,7 @@ internal sealed class TracedGalaxySubscriber(IGalaxySubscriber inner, string cli
/// spans would dominate the trace volume at 50k tags / 1Hz; ops gets per-event
/// visibility through <see cref="EventPump"/>'s metrics in PR 6.2 instead.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
public async IAsyncEnumerable<MxEvent> StreamEventsAsync(
[EnumeratorCancellation] CancellationToken cancellationToken)
{