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:
@@ -39,6 +39,7 @@ internal static class AlarmRefBuilder
|
||||
/// <param name="fullReference">The full reference of the alarm-bearing attribute.</param>
|
||||
/// <param name="initialSeverity">The initial alarm severity level.</param>
|
||||
/// <param name="initialDescription">The initial alarm description.</param>
|
||||
/// <returns>The populated <see cref="AlarmConditionInfo"/> with all five sub-attribute references.</returns>
|
||||
public static AlarmConditionInfo Build(
|
||||
string fullReference,
|
||||
AlarmSeverity initialSeverity = AlarmSeverity.Medium,
|
||||
|
||||
@@ -14,12 +14,13 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Browse;
|
||||
/// and encode (Boolean, Int32, Int64, Float32, Float64, String, DateTime). Without it an
|
||||
/// Int64 attribute fell through to the <see cref="DriverDataType.String"/> default,
|
||||
/// creating a String address-space node while runtime reads decoded a boxed <c>long</c> —
|
||||
/// a metadata / coercion mismatch (Driver.Galaxy-002).
|
||||
/// a metadata / coercion mismatch.
|
||||
/// </remarks>
|
||||
internal static class DataTypeMap
|
||||
{
|
||||
/// <summary>Maps an MXAccess data type ID to a driver data type.</summary>
|
||||
/// <param name="mxDataType">The MXAccess data type ID.</param>
|
||||
/// <returns>The corresponding <see cref="DriverDataType"/>, or <see cref="DriverDataType.String"/> for unknown codes.</returns>
|
||||
public static DriverDataType Map(int mxDataType) => mxDataType switch
|
||||
{
|
||||
0 => DriverDataType.Boolean,
|
||||
|
||||
@@ -98,6 +98,7 @@ public sealed class DeployWatcher : IDisposable
|
||||
}
|
||||
|
||||
/// <summary>Cancels the loop and waits for it to exit cleanly.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
public async Task StopAsync()
|
||||
{
|
||||
var cts = _cts;
|
||||
|
||||
@@ -44,6 +44,7 @@ public sealed class GalaxyDiscoverer
|
||||
/// </summary>
|
||||
/// <param name="builder">The address space builder to populate with discovery results.</param>
|
||||
/// <param name="cancellationToken">The cancellation token for the operation.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
public async Task DiscoverAsync(IAddressSpaceBuilder builder, CancellationToken cancellationToken)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(builder);
|
||||
|
||||
+1
-4
@@ -22,10 +22,7 @@ public sealed class GatewayGalaxyDeployWatchSource : IGalaxyDeployWatchSource
|
||||
_client = client ?? throw new ArgumentNullException(nameof(client));
|
||||
}
|
||||
|
||||
/// <summary>Watches for deploy events asynchronously.</summary>
|
||||
/// <param name="lastSeenDeployTime">The last deploy time that was observed.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>An async enumerable of deploy events.</returns>
|
||||
/// <inheritdoc />
|
||||
public IAsyncEnumerable<DeployEvent> WatchAsync(
|
||||
DateTimeOffset? lastSeenDeployTime, CancellationToken cancellationToken)
|
||||
=> _client.WatchDeployEventsAsync(lastSeenDeployTime, cancellationToken);
|
||||
|
||||
@@ -20,10 +20,7 @@ public sealed class GatewayGalaxyHierarchySource : IGalaxyHierarchySource
|
||||
_client = client ?? throw new ArgumentNullException(nameof(client));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Discovers the Galaxy object hierarchy asynchronously via the gateway.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">Cancellation token for the operation.</param>
|
||||
/// <inheritdoc />
|
||||
public Task<IReadOnlyList<GalaxyObject>> GetHierarchyAsync(CancellationToken cancellationToken)
|
||||
=> _client.DiscoverHierarchyAsync(cancellationToken);
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ public interface IGalaxyDeployWatchSource
|
||||
/// </summary>
|
||||
/// <param name="lastSeenDeployTime">The last seen deploy time, or null to receive a bootstrap event.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>An async stream of deploy events, starting with a bootstrap event unless suppressed.</returns>
|
||||
IAsyncEnumerable<DeployEvent> WatchAsync(
|
||||
DateTimeOffset? lastSeenDeployTime, CancellationToken cancellationToken);
|
||||
}
|
||||
|
||||
@@ -16,5 +16,6 @@ public interface IGalaxyHierarchySource
|
||||
/// callers don't reimplement paging.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">Cancellation token for the operation.</param>
|
||||
/// <returns>The full materialised Galaxy object hierarchy.</returns>
|
||||
Task<IReadOnlyList<GalaxyObject>> GetHierarchyAsync(CancellationToken cancellationToken);
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ public sealed class GalaxyDriver
|
||||
private bool _alarmFeedWired;
|
||||
// List preserves insertion order so OnAlarmFeedTransition always picks the
|
||||
// earliest-registered handle — a deterministic choice that doesn't vary as
|
||||
// handles are added/removed (Driver.Galaxy-006 fix: HashSet.First() is unstable).
|
||||
// handles are added/removed (HashSet.First() is unstable).
|
||||
private readonly List<GalaxyAlarmSubscriptionHandle> _alarmSubscriptions = new();
|
||||
|
||||
// Production runtime owned by InitializeAsync (BuildProductionRuntimeAsync). The
|
||||
@@ -200,9 +200,10 @@ public sealed class GalaxyDriver
|
||||
/// <see cref="ReconnectSupervisor.ReportTransportFailure"/> drives this on a
|
||||
/// background task in production; tests prefer to invoke it directly so the
|
||||
/// <see cref="GalaxyReconnectOptions.ReplayOnSessionLost"/> branch can be
|
||||
/// asserted deterministically (Driver.Galaxy-013).
|
||||
/// asserted deterministically.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">Cancellation token for the replay operation.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
internal Task InvokeReplayForTestAsync(CancellationToken cancellationToken) =>
|
||||
ReplayAsync(cancellationToken);
|
||||
|
||||
@@ -310,7 +311,7 @@ public sealed class GalaxyDriver
|
||||
/// handles are dead once the session reopened. The faulted <see cref="EventPump"/>
|
||||
/// is recreated first so the replayed subscriptions have a live StreamEvents
|
||||
/// consumer; without that restart the replayed tags are subscribed on the gw but
|
||||
/// never reach <c>OnDataChange</c> (Driver.Galaxy-008). PR 6.x can swap this for
|
||||
/// never reach <c>OnDataChange</c>. PR 6.x can swap this for
|
||||
/// the gw's batched <c>ReplaySubscriptionsCommand</c> once it ships.
|
||||
/// </summary>
|
||||
private async Task ReplayAsync(CancellationToken cancellationToken)
|
||||
@@ -319,11 +320,11 @@ public sealed class GalaxyDriver
|
||||
var entries = _subscriptions.SnapshotEntries();
|
||||
if (entries.Count == 0) return;
|
||||
|
||||
// Driver.Galaxy-013: honor ReplayOnSessionLost. When operators opt out (false)
|
||||
// we skip the per-tag SubscribeBulk fan-out — they're delegating to the
|
||||
// gateway's session-level ReplaySubscriptions or accept post-reconnect tag
|
||||
// loss. We still restart the EventPump so a future Subscribe call lands on
|
||||
// a live consumer.
|
||||
// Honor ReplayOnSessionLost. When operators opt out (false) we skip the
|
||||
// per-tag SubscribeBulk fan-out — they're delegating to the gateway's
|
||||
// session-level ReplaySubscriptions or accept post-reconnect tag loss. We
|
||||
// still restart the EventPump so a future Subscribe call lands on a live
|
||||
// consumer.
|
||||
if (!_options.Reconnect.ReplayOnSessionLost)
|
||||
{
|
||||
RestartEventPumpForReplay();
|
||||
@@ -440,10 +441,10 @@ public sealed class GalaxyDriver
|
||||
private MxGatewayClientOptions BuildClientOptions(GalaxyGatewayOptions gw) => new()
|
||||
{
|
||||
Endpoint = new Uri(gw.Endpoint, UriKind.Absolute),
|
||||
// Driver.Galaxy-010: pass the logger so the literal-arm cleartext fallback
|
||||
// surfaces a startup warning rather than silently shipping the key. The
|
||||
// resolver lives in Driver.Galaxy.Contracts (GalaxySecretRef) so the runtime
|
||||
// driver and the AdminUI browser share one implementation.
|
||||
// Pass the logger so the literal-arm cleartext fallback surfaces a startup
|
||||
// warning rather than silently shipping the key. The resolver lives in
|
||||
// Driver.Galaxy.Contracts (GalaxySecretRef) so the runtime driver and the
|
||||
// AdminUI browser share one implementation.
|
||||
ApiKey = GalaxySecretRef.ResolveApiKey(gw.ApiKeySecretRef, _logger),
|
||||
UseTls = gw.UseTls,
|
||||
CaCertificatePath = gw.CaCertificatePath,
|
||||
@@ -459,8 +460,8 @@ public sealed class GalaxyDriver
|
||||
|
||||
// Reuse the lazily-built repository client (DiscoverAsync constructs it on demand).
|
||||
// If discovery hasn't run yet, build the client here so the watcher has a target.
|
||||
// Driver.Galaxy-009 fix: guard with ??= so if BuildDefaultHierarchySource later runs
|
||||
// it reuses this client rather than overwriting the field and leaking the first instance.
|
||||
// Guard with ??= so if BuildDefaultHierarchySource later runs it reuses this client
|
||||
// rather than overwriting the field and leaking the first instance.
|
||||
_ownedRepositoryClient ??= ZB.MOM.WW.MxGateway.Client.GalaxyRepositoryClient.Create(
|
||||
BuildClientOptions(_options.Gateway));
|
||||
|
||||
@@ -470,31 +471,26 @@ public sealed class GalaxyDriver
|
||||
|
||||
// StartAsync schedules the background loop and returns Task.CompletedTask immediately.
|
||||
// It throws InvalidOperationException synchronously if called twice (programming error).
|
||||
// Driver.Galaxy-009 fix: don't discard the return value — observe any synchronous throw.
|
||||
// Don't discard the return value — observe any synchronous throw.
|
||||
var startTask = _deployWatcher.StartAsync(CancellationToken.None);
|
||||
// The task is already completed (StartAsync is synchronous); surface any synchronous fault.
|
||||
if (startTask.IsFaulted) startTask.GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// In-place config reapply. The driver does not currently support
|
||||
/// hot-swapping <see cref="GalaxyDriverOptions"/> at runtime — changing the
|
||||
/// gateway endpoint, MxAccess client name, or reconnect policy requires
|
||||
/// tearing down the gw session, supervisor, event pump, and address space.
|
||||
/// The host stack handles that via DriverInstance restart, so this method
|
||||
/// only accepts an equivalent config (no meaningful change) and refreshes
|
||||
/// health; a non-equivalent reapply throws <see cref="NotSupportedException"/>
|
||||
/// so the caller knows the change wasn't applied (Driver.Galaxy-013:
|
||||
/// previously the method silently ignored <c>driverConfigJson</c>).
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public Task ReinitializeAsync(string driverConfigJson, CancellationToken cancellationToken)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
if (!string.IsNullOrWhiteSpace(driverConfigJson))
|
||||
{
|
||||
// In-place config reapply. The driver does not currently support hot-swapping
|
||||
// GalaxyDriverOptions at runtime — changing the gateway endpoint, MxAccess
|
||||
// client name, or reconnect policy requires tearing down the gw session,
|
||||
// supervisor, event pump, and address space. The host stack handles that via
|
||||
// DriverInstance restart, so this method only accepts an equivalent config (no
|
||||
// meaningful change) and refreshes health; a non-equivalent reapply throws
|
||||
// NotSupportedException so the caller knows the change wasn't applied.
|
||||
//
|
||||
// Materialise the incoming config and compare against the live options. We
|
||||
// refuse any change that would require a session teardown rather than
|
||||
// pretending to apply it.
|
||||
@@ -562,17 +558,13 @@ public sealed class GalaxyDriver
|
||||
public IReadOnlyList<HostConnectivityStatus> GetHostStatuses() => _hostStatuses.Snapshot();
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <remarks>
|
||||
/// Estimated footprint: 64 bytes × tracked item handles (one gw subscription entry
|
||||
/// per bound tag) + 256 bytes × tracked driver subscriptions (registry overhead per
|
||||
/// OPC UA monitored item). Returns 0 when no subscriptions are active. These
|
||||
/// constants are conservative — a 50k-tag set occupies ~3 MB and registers clearly
|
||||
/// with the server's cache-flush heuristic. Driver.Galaxy-011: the stale
|
||||
/// "PR 4.4 sets this" comment is removed; PR 4.4 shipped the SubscriptionRegistry
|
||||
/// but never wired it here.
|
||||
/// </remarks>
|
||||
public long GetMemoryFootprint()
|
||||
{
|
||||
// Estimated footprint: 64 bytes × tracked item handles (one gw subscription entry
|
||||
// per bound tag) + 256 bytes × tracked driver subscriptions (registry overhead per
|
||||
// OPC UA monitored item). Returns 0 when no subscriptions are active. These
|
||||
// constants are conservative — a 50k-tag set occupies ~3 MB and registers clearly
|
||||
// with the server's cache-flush heuristic.
|
||||
const long BytesPerItemHandle = 64L; // TagBinding + reverse-map entry
|
||||
const long BytesPerSubscription = 256L; // SubscriptionEntry overhead
|
||||
return (_subscriptions.TrackedItemHandleCount * BytesPerItemHandle)
|
||||
@@ -584,13 +576,12 @@ public sealed class GalaxyDriver
|
||||
|
||||
// ===== ITagDiscovery (PR 4.1) =====
|
||||
|
||||
/// <summary>
|
||||
/// Run-once: <see cref="DiscoverAsync"/> fetches the full Galaxy hierarchy inline and
|
||||
/// streams the complete node set within a single awaited call — there is no FOCAS-style
|
||||
/// background cache that fills in after connect. Galaxy is a heavy network driver, so the
|
||||
/// bounded post-connect retry loop is deliberately avoided; re-discovery on Galaxy
|
||||
/// redeploy is handled separately via <see cref="IRediscoverable"/> + the deploy-event watcher.
|
||||
/// </summary>
|
||||
/// <inheritdoc />
|
||||
// Run-once: DiscoverAsync fetches the full Galaxy hierarchy inline and streams the complete
|
||||
// node set within a single awaited call — there is no FOCAS-style background cache that fills
|
||||
// in after connect. Galaxy is a heavy network driver, so the bounded post-connect retry loop
|
||||
// is deliberately avoided; re-discovery on Galaxy redeploy is handled separately via
|
||||
// IRediscoverable + the deploy-event watcher.
|
||||
public DiscoveryRediscoverPolicy RediscoverPolicy => DiscoveryRediscoverPolicy.Once;
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -710,10 +701,10 @@ public sealed class GalaxyDriver
|
||||
}
|
||||
|
||||
// Register bindings so the pump knows to dispatch events for these handles.
|
||||
// Driver.Galaxy-012: index the SubscribeBulk results once and correlate to
|
||||
// references in O(1) instead of FirstOrDefault per element (O(n²) over the
|
||||
// batch). On the 50k-tag soak path this turns a 2.5G-comparison loop into
|
||||
// a single Dictionary build + linear scan.
|
||||
// Index the SubscribeBulk results once and correlate to references in O(1)
|
||||
// instead of FirstOrDefault per element (O(n²) over the batch). On the
|
||||
// 50k-tag soak path this turns a 2.5G-comparison loop into a single
|
||||
// Dictionary build + linear scan.
|
||||
var resultIndex = BuildResultIndex(results);
|
||||
var bindings = new List<TagBinding>(fullReferences.Count);
|
||||
for (var i = 0; i < fullReferences.Count; i++)
|
||||
@@ -853,8 +844,8 @@ public sealed class GalaxyDriver
|
||||
// recorded with a non-positive ItemHandle so the caller can detect partial failure
|
||||
// by inspecting the returned handle's diagnostic context — full per-tag error
|
||||
// surface lands in PR 5.3's parity tests.
|
||||
// Driver.Galaxy-012: index results once, correlate in O(1) per reference rather
|
||||
// than FirstOrDefault inside the loop (O(n²) on the 50k-tag path).
|
||||
// Index results once, correlate in O(1) per reference rather than
|
||||
// FirstOrDefault inside the loop (O(n²) on the 50k-tag path).
|
||||
var resultIndex = BuildResultIndex(results);
|
||||
var bindings = new List<TagBinding>(fullReferences.Count);
|
||||
for (var i = 0; i < fullReferences.Count; i++)
|
||||
@@ -1084,9 +1075,9 @@ public sealed class GalaxyDriver
|
||||
// by SourceNodeId (not by handle), so every active subscriber sees the same
|
||||
// transition regardless of which handle is attached here. Using the first
|
||||
// insertion-order entry is deterministic and stable as long as at least one
|
||||
// subscription remains — HashSet.First() was unstable across mutations
|
||||
// (Driver.Galaxy-006 fix). _alarmSubscriptions is a List, so [0] is always
|
||||
// the earliest-registered handle.
|
||||
// subscription remains — HashSet.First() was unstable across mutations.
|
||||
// _alarmSubscriptions is a List, so [0] is always the earliest-registered
|
||||
// handle.
|
||||
subCount = _alarmSubscriptions.Count;
|
||||
handle = subCount > 0 ? _alarmSubscriptions[0] : null;
|
||||
}
|
||||
@@ -1162,9 +1153,9 @@ public sealed class GalaxyDriver
|
||||
/// </summary>
|
||||
private IGalaxyHierarchySource BuildDefaultHierarchySource()
|
||||
{
|
||||
// Driver.Galaxy-009 fix: reuse a client that StartDeployWatcher may have already
|
||||
// created (??=) rather than always overwriting the field and leaking the first
|
||||
// instance. Both paths produce equivalent clients from the same options.
|
||||
// Reuse a client that StartDeployWatcher may have already created (??=) rather
|
||||
// than always overwriting the field and leaking the first instance. Both paths
|
||||
// produce equivalent clients from the same options.
|
||||
_ownedRepositoryClient ??= GalaxyRepositoryClient.Create(BuildClientOptions(_options.Gateway));
|
||||
return new TracedGalaxyHierarchySource(
|
||||
new GatewayGalaxyHierarchySource(_ownedRepositoryClient), _options.MxAccess.ClientName);
|
||||
@@ -1173,10 +1164,11 @@ public sealed class GalaxyDriver
|
||||
/// <summary>
|
||||
/// Asynchronous disposal. Prefer <c>await using</c> over <c>using</c> — the
|
||||
/// async path does not block the caller while awaiting EventPump / session /
|
||||
/// client shutdown (Driver.Galaxy-007: the sync path blocked on
|
||||
/// <c>GetAwaiter().GetResult()</c> for every async sub-component, risking a
|
||||
/// deadlock under thread-pool starvation).
|
||||
/// client shutdown (the sync path blocks on <c>GetAwaiter().GetResult()</c>
|
||||
/// for every async sub-component, risking a deadlock under thread-pool
|
||||
/// starvation).
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
if (_disposed) return;
|
||||
@@ -1250,26 +1242,18 @@ public sealed class GalaxyDriver
|
||||
System.Collections.Concurrent.ConcurrentDictionary<string, SecurityClassification> map)
|
||||
: IAddressSpaceBuilder
|
||||
{
|
||||
/// <summary>Creates a folder node and returns a builder for populating it.</summary>
|
||||
/// <param name="browseName">The OPC UA BrowseName of the folder.</param>
|
||||
/// <param name="displayName">The display name for the folder.</param>
|
||||
/// <inheritdoc />
|
||||
public IAddressSpaceBuilder Folder(string browseName, string displayName)
|
||||
=> new SecurityCapturingBuilder(inner.Folder(browseName, displayName), map);
|
||||
|
||||
/// <summary>Creates a variable node and captures its security classification.</summary>
|
||||
/// <param name="browseName">The OPC UA BrowseName of the variable.</param>
|
||||
/// <param name="displayName">The display name for the variable.</param>
|
||||
/// <param name="attributeInfo">The driver attribute metadata including security classification.</param>
|
||||
/// <inheritdoc />
|
||||
public IVariableHandle Variable(string browseName, string displayName, DriverAttributeInfo attributeInfo)
|
||||
{
|
||||
map[attributeInfo.FullName] = attributeInfo.SecurityClass;
|
||||
return inner.Variable(browseName, displayName, attributeInfo);
|
||||
}
|
||||
|
||||
/// <summary>Adds a property node to the current parent.</summary>
|
||||
/// <param name="browseName">The OPC UA BrowseName of the property.</param>
|
||||
/// <param name="dataType">The OPC UA data type of the property.</param>
|
||||
/// <param name="value">The property value.</param>
|
||||
/// <inheritdoc />
|
||||
public void AddProperty(string browseName, DriverDataType dataType, object? value)
|
||||
=> inner.AddProperty(browseName, dataType, value);
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ public static class GalaxyDriverFactoryExtensions
|
||||
/// <summary>Convenience for tests + standalone callers.</summary>
|
||||
/// <param name="driverInstanceId">The unique identifier for the driver instance.</param>
|
||||
/// <param name="driverConfigJson">The driver configuration in JSON format.</param>
|
||||
/// <returns>The constructed Galaxy driver instance.</returns>
|
||||
public static GalaxyDriver CreateInstance(string driverInstanceId, string driverConfigJson)
|
||||
=> CreateInstance(driverInstanceId, driverConfigJson, loggerFactory: null);
|
||||
|
||||
@@ -41,6 +42,7 @@ public static class GalaxyDriverFactoryExtensions
|
||||
/// <param name="driverInstanceId">The unique identifier for the driver instance.</param>
|
||||
/// <param name="driverConfigJson">The driver configuration in JSON format.</param>
|
||||
/// <param name="loggerFactory">The optional logger factory for creating drivers.</param>
|
||||
/// <returns>The constructed Galaxy driver instance.</returns>
|
||||
public static GalaxyDriver CreateInstance(
|
||||
string driverInstanceId, string driverConfigJson, ILoggerFactory? loggerFactory)
|
||||
{
|
||||
|
||||
@@ -145,6 +145,10 @@ public sealed class GalaxyDriverProbe : IDriverProbe
|
||||
/// <see cref="StatusCode.PermissionDenied"/>) counts as reachable because it proves a live
|
||||
/// gateway gRPC server answered.
|
||||
/// </summary>
|
||||
/// <param name="code">The gRPC status code returned by the ping RPC.</param>
|
||||
/// <param name="host">The probed host, used to compose the failure message.</param>
|
||||
/// <param name="port">The probed port, used to compose the failure message.</param>
|
||||
/// <returns>Whether the gateway should be considered reachable, plus a human-readable message.</returns>
|
||||
internal static (bool ok, string message) ClassifyRpc(StatusCode code, string host, int port) => code switch
|
||||
{
|
||||
StatusCode.OK => (true, "gateway gRPC OK"),
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Health;
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The eventual production source for this signal is the gateway's <c>StreamSessionHealth</c>
|
||||
/// RPC (mxaccessgw issue gw-6). Until that ships, the driver-side reconnect supervisor
|
||||
/// RPC. Until that ships, the driver-side reconnect supervisor
|
||||
/// (PR 4.5) calls <see cref="SetTransport"/> on transport state transitions:
|
||||
/// <see cref="HostState.Running"/> when the gw session re-Registers, <see cref="HostState.Stopped"/>
|
||||
/// when the supervisor moves to <c>TransportLost</c>. The forwarder is intentionally
|
||||
@@ -57,7 +57,7 @@ public sealed class HostConnectivityForwarder : IDisposable
|
||||
/// <summary>Disposes the forwarder and marks it as disposed.</summary>
|
||||
public void Dispose()
|
||||
{
|
||||
// No-op today; reserved for the eventual gw-6 StreamSessionHealth consumer that
|
||||
// No-op today; reserved for the eventual StreamSessionHealth consumer that
|
||||
// will own a long-running task this method tears down.
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ public sealed class HostStatusAggregator
|
||||
/// Snapshot the current host set. Suitable as the body of
|
||||
/// <c>IHostConnectivityProbe.GetHostStatuses()</c>.
|
||||
/// </summary>
|
||||
/// <returns>The current set of tracked host connectivity statuses.</returns>
|
||||
public IReadOnlyList<HostConnectivityStatus> Snapshot()
|
||||
{
|
||||
lock (_lock)
|
||||
|
||||
@@ -77,6 +77,7 @@ public sealed class PerPlatformProbeWatcher : IDisposable
|
||||
/// </summary>
|
||||
/// <param name="platformTagNames">The platform tag names to synchronize.</param>
|
||||
/// <param name="cancellationToken">A cancellation token to cancel the operation.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
public async Task SyncPlatformsAsync(
|
||||
IEnumerable<string> platformTagNames, CancellationToken cancellationToken)
|
||||
{
|
||||
|
||||
@@ -100,8 +100,8 @@ internal sealed class EventPump : IAsyncDisposable
|
||||
// false immediately, which we account for via the EventsDropped counter.
|
||||
// We deliberately do NOT use BoundedChannelFullMode.DropWrite — that
|
||||
// would silently discard the new event inside Channel<T> without
|
||||
// surfacing the drop on a counter (Driver.Galaxy-005: keep the comment
|
||||
// and the FullMode value consistent).
|
||||
// surfacing the drop on a counter (keep this comment and the FullMode
|
||||
// value consistent).
|
||||
FullMode = BoundedChannelFullMode.Wait,
|
||||
SingleReader = true,
|
||||
SingleWriter = true,
|
||||
|
||||
@@ -68,6 +68,7 @@ public sealed class GalaxyMxSession : IAsyncDisposable
|
||||
/// </summary>
|
||||
/// <param name="clientOptions">The MX gateway client options.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
public async Task ConnectAsync(MxGatewayClientOptions clientOptions, CancellationToken cancellationToken)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
@@ -107,6 +108,7 @@ public sealed class GalaxyMxSession : IAsyncDisposable
|
||||
/// </summary>
|
||||
/// <param name="clientOptions">The MX gateway client options.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
public async Task RecreateAsync(MxGatewayClientOptions clientOptions, CancellationToken cancellationToken)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
@@ -136,6 +138,7 @@ public sealed class GalaxyMxSession : IAsyncDisposable
|
||||
public MxGatewaySession? Session => _session;
|
||||
|
||||
/// <summary>Disposes the session and underlying gateway client resources.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
if (_disposed) return;
|
||||
|
||||
@@ -8,6 +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>
|
||||
/// <inheritdoc />
|
||||
public string DiagnosticId => $"galaxy-sub-{SubscriptionId}";
|
||||
}
|
||||
|
||||
+1
-5
@@ -32,11 +32,7 @@ internal sealed class GatewayGalaxyAlarmAcknowledger : IGalaxyAlarmAcknowledger
|
||||
_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>
|
||||
/// <inheritdoc />
|
||||
public async Task AcknowledgeAsync(
|
||||
string alarmFullReference,
|
||||
string comment,
|
||||
|
||||
@@ -39,6 +39,7 @@ internal sealed class GatewayGalaxyAlarmFeed : IGalaxyAlarmFeed
|
||||
/// </summary>
|
||||
/// <param name="request">The stream request parameters.</param>
|
||||
/// <param name="cancellationToken">A cancellation token.</param>
|
||||
/// <returns>The async stream of decoded alarm-feed messages.</returns>
|
||||
internal delegate IAsyncEnumerable<AlarmFeedMessage> AlarmStreamFactory(
|
||||
StreamAlarmsRequest request, CancellationToken cancellationToken);
|
||||
|
||||
@@ -90,7 +91,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>
|
||||
/// <inheritdoc />
|
||||
public void Start()
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
@@ -261,6 +262,7 @@ internal sealed class GatewayGalaxyAlarmFeed : IGalaxyAlarmFeed
|
||||
};
|
||||
|
||||
/// <summary>Releases the alarm feed resources and stops the background stream task.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
if (_disposed) return;
|
||||
|
||||
@@ -115,11 +115,7 @@ public sealed class GatewayGalaxyDataWriter : IGalaxyDataWriter
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <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>
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<WriteResult>> WriteAsync(
|
||||
IReadOnlyList<WriteRequest> writes,
|
||||
Func<string, SecurityClassification> securityResolver,
|
||||
|
||||
@@ -37,11 +37,7 @@ public sealed class GatewayGalaxySubscriber : IGalaxySubscriber
|
||||
_logger = logger ?? NullLogger.Instance;
|
||||
}
|
||||
|
||||
/// <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>
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<SubscribeResult>> SubscribeBulkAsync(
|
||||
IReadOnlyList<string> fullReferences, int bufferedUpdateIntervalMs, CancellationToken cancellationToken)
|
||||
{
|
||||
@@ -142,10 +138,7 @@ public sealed class GatewayGalaxySubscriber : IGalaxySubscriber
|
||||
/// <returns><c>true</c> if the interval should be recorded as applied; otherwise <c>false</c>.</returns>
|
||||
internal static bool ClassifyIntervalReply(ProtocolStatusCode? code) => code == ProtocolStatusCode.Ok;
|
||||
|
||||
/// <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>
|
||||
/// <inheritdoc />
|
||||
public async Task UnsubscribeBulkAsync(IReadOnlyList<int> itemHandles, CancellationToken cancellationToken)
|
||||
{
|
||||
if (itemHandles.Count == 0) return;
|
||||
@@ -159,9 +152,7 @@ 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>
|
||||
/// <inheritdoc />
|
||||
public IAsyncEnumerable<MxEvent> StreamEventsAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var session = _session.Session
|
||||
|
||||
@@ -24,6 +24,7 @@ internal interface IGalaxyAlarmAcknowledger
|
||||
/// OPC UA session by the server-side ACL layer before reaching the driver.
|
||||
/// </param>
|
||||
/// <param name="cancellationToken">Cancels the gateway RPC.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task AcknowledgeAsync(
|
||||
string alarmFullReference,
|
||||
string comment,
|
||||
|
||||
@@ -26,6 +26,7 @@ public interface IGalaxyDataWriter
|
||||
/// (the safest default — non-secured Write).
|
||||
/// </param>
|
||||
/// <param name="cancellationToken">Aborts the in-flight batch.</param>
|
||||
/// <returns>One <see cref="WriteResult"/> per request entry, in input order.</returns>
|
||||
Task<IReadOnlyList<WriteResult>> WriteAsync(
|
||||
IReadOnlyList<WriteRequest> writes,
|
||||
Func<string, SecurityClassification> securityResolver,
|
||||
|
||||
@@ -20,12 +20,14 @@ public interface IGalaxySubscriber
|
||||
/// <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>
|
||||
/// <returns>One <see cref="SubscribeResult"/> per requested reference, in input order.</returns>
|
||||
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>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task UnsubscribeBulkAsync(IReadOnlyList<int> itemHandles, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
@@ -34,5 +36,6 @@ public interface IGalaxySubscriber
|
||||
/// its <see cref="SubscriptionRegistry"/>.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">Cancellation token for the stream.</param>
|
||||
/// <returns>An asynchronous stream of <see cref="MxEvent"/>s as they arrive from the gateway.</returns>
|
||||
IAsyncEnumerable<MxEvent> StreamEventsAsync(CancellationToken cancellationToken);
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ internal static class MxAccessSeverityMapper
|
||||
/// <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>
|
||||
/// <returns>The mapped four-bucket <see cref="AlarmSeverity"/> and its OPC UA Part 9 numeric severity.</returns>
|
||||
public static (AlarmSeverity Bucket, int OpcUaSeverity) Map(int rawMxAccessSeverity)
|
||||
{
|
||||
if (rawMxAccessSeverity < 250)
|
||||
|
||||
@@ -15,6 +15,7 @@ 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>
|
||||
/// <returns>The decoded boxed CLR value, or null when <paramref name="value"/> is null or carries a null MxValue.</returns>
|
||||
public static object? Decode(MxValue? value)
|
||||
{
|
||||
if (value is null) return null;
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Runtime;
|
||||
/// <para>
|
||||
/// <b>Replay</b>: caller-supplied callback that re-establishes every active
|
||||
/// subscription. Production wraps gw's <c>ReplaySubscriptionsCommand</c>
|
||||
/// (mxaccessgw issue #0.3); when that's not available, the callback falls
|
||||
/// (tracked in the mxaccessgw backlog); when that's not available, the callback falls
|
||||
/// back to walking the SubscriptionRegistry and re-issuing SubscribeBulk for
|
||||
/// every tracked tag.
|
||||
/// </para>
|
||||
@@ -147,6 +147,7 @@ public sealed class ReconnectSupervisor : IDisposable
|
||||
/// and for orchestration that wants to gate calls on recovery completing.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">Token to cancel the wait operation.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
public async Task WaitForHealthyAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
while (!cancellationToken.IsCancellationRequested && IsDegraded)
|
||||
|
||||
@@ -19,10 +19,6 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Runtime;
|
||||
internal sealed class SubscriptionRegistry
|
||||
{
|
||||
private readonly ConcurrentDictionary<long, SubscriptionEntry> _bySubscriptionId = new();
|
||||
// Driver.Galaxy-012: use ImmutableHashSet<long> for the reverse map so removal is
|
||||
// O(log n) instead of "rebuild the entire ConcurrentBag from a LINQ filter on every
|
||||
// unsubscribe"; reads are lock-free because the immutable snapshot is published
|
||||
// atomically via ConcurrentDictionary AddOrUpdate.
|
||||
private readonly ConcurrentDictionary<int, ImmutableHashSet<long>> _subscribersByItemHandle = new();
|
||||
// Forward index for the Galaxy writer: fullRef (case-insensitive) → live item handle.
|
||||
// Maintained in lock-step with _subscribersByItemHandle; entries are cleaned up when
|
||||
@@ -38,6 +34,7 @@ internal sealed class SubscriptionRegistry
|
||||
public int TrackedItemHandleCount => _subscribersByItemHandle.Count;
|
||||
|
||||
/// <summary>Allocate a fresh subscription id. Monotonic; unique per registry lifetime.</summary>
|
||||
/// <returns>The newly allocated subscription id.</returns>
|
||||
public long NextSubscriptionId() => Interlocked.Increment(ref _nextSubscriptionId);
|
||||
|
||||
/// <summary>
|
||||
@@ -75,8 +72,6 @@ internal sealed class SubscriptionRegistry
|
||||
foreach (var binding in entry.Bindings)
|
||||
{
|
||||
if (binding.ItemHandle <= 0) continue;
|
||||
// Driver.Galaxy-012: ImmutableHashSet.Remove is O(log n) and the result is
|
||||
// published atomically — no need to rebuild from a LINQ filter.
|
||||
if (!_subscribersByItemHandle.TryGetValue(binding.ItemHandle, out var set)) continue;
|
||||
var remaining = set.Remove(subscriptionId);
|
||||
if (remaining.IsEmpty)
|
||||
@@ -97,12 +92,6 @@ internal sealed class SubscriptionRegistry
|
||||
/// Look up the (subscription id, full reference) pairs that should receive an
|
||||
/// OnDataChange for the given gw item handle. Returns empty when nobody subscribes.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Driver.Galaxy-012: O(1) per subscriber via the per-entry
|
||||
/// <c>FullRefByItemHandle</c> index, rather than a <c>FirstOrDefault</c> linear
|
||||
/// 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)
|
||||
@@ -153,6 +142,7 @@ internal sealed class SubscriptionRegistry
|
||||
}
|
||||
|
||||
/// <summary>Snapshot every active binding for diagnostic output.</summary>
|
||||
/// <returns>A flattened list of every tag binding across all tracked subscriptions.</returns>
|
||||
public IReadOnlyList<TagBinding> SnapshotAllBindings() =>
|
||||
[.. _bySubscriptionId.Values.SelectMany(entry => entry.Bindings)];
|
||||
|
||||
@@ -161,6 +151,7 @@ internal sealed class SubscriptionRegistry
|
||||
/// Used by the reconnect replay path so it can re-issue SubscribeBulk per subscription
|
||||
/// and then <see cref="Rebind"/> each one with the post-reconnect item handles.
|
||||
/// </summary>
|
||||
/// <returns>Every tracked subscription id paired with its current tag bindings.</returns>
|
||||
public IReadOnlyList<(long SubscriptionId, IReadOnlyList<TagBinding> Bindings)> SnapshotEntries() =>
|
||||
[.. _bySubscriptionId.Values.Select(entry => (entry.SubscriptionId, entry.Bindings))];
|
||||
|
||||
@@ -178,7 +169,6 @@ internal sealed class SubscriptionRegistry
|
||||
|
||||
// Drop this subscription from every reverse-map set it currently appears in. The
|
||||
// pre-reconnect item handles are stale once the gw re-issues fresh ones.
|
||||
// Driver.Galaxy-012: ImmutableHashSet.Remove is O(log n) — no LINQ rebuild.
|
||||
foreach (var binding in oldEntry.Bindings)
|
||||
{
|
||||
if (binding.ItemHandle <= 0) continue;
|
||||
@@ -210,8 +200,8 @@ internal sealed class SubscriptionRegistry
|
||||
/// <summary>
|
||||
/// Per-subscription bookkeeping. <see cref="FullRefByItemHandle"/> is an index
|
||||
/// over <see cref="Bindings"/> keyed by item handle so <c>ResolveSubscribers</c>
|
||||
/// is O(1) per subscriber instead of a linear scan of every binding
|
||||
/// (Driver.Galaxy-012). Failed bindings (item handle ≤ 0) are excluded from the
|
||||
/// is O(1) per subscriber instead of a linear scan of every binding.
|
||||
/// Failed bindings (item handle ≤ 0) are excluded from the
|
||||
/// index because the EventPump only dispatches for positive handles.
|
||||
/// </summary>
|
||||
private sealed class SubscriptionEntry
|
||||
|
||||
@@ -11,13 +11,9 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Runtime;
|
||||
internal sealed class TracedGalaxyDataWriter(IGalaxyDataWriter inner, string clientName) : IGalaxyDataWriter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
/// <remarks>No span — this is a local cache-clear operation, not a gateway round-trip.</remarks>
|
||||
public void InvalidateHandleCaches() => inner.InvalidateHandleCaches();
|
||||
|
||||
/// <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>
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<WriteResult>> WriteAsync(
|
||||
IReadOnlyList<WriteRequest> writes,
|
||||
Func<string, SecurityClassification> securityResolver,
|
||||
|
||||
@@ -10,10 +10,7 @@ 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>
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<SubscribeResult>> SubscribeBulkAsync(
|
||||
IReadOnlyList<string> fullReferences, int bufferedUpdateIntervalMs, CancellationToken cancellationToken)
|
||||
{
|
||||
@@ -35,9 +32,7 @@ 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>
|
||||
/// <inheritdoc />
|
||||
public async Task UnsubscribeBulkAsync(IReadOnlyList<int> itemHandles, CancellationToken cancellationToken)
|
||||
{
|
||||
using var activity = GalaxyTelemetry.ActivitySource.StartActivity("galaxy.unsubscribe_bulk");
|
||||
@@ -54,12 +49,10 @@ internal sealed class TracedGalaxySubscriber(IGalaxySubscriber inner, string cli
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Streaming RPC — one parent span covers the entire stream lifetime. Per-event
|
||||
/// 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>
|
||||
// Streaming RPC — one parent span covers the entire stream lifetime. Per-event
|
||||
// spans would dominate the trace volume at 50k tags / 1Hz; ops gets per-event
|
||||
// visibility through EventPump's metrics instead.
|
||||
/// <inheritdoc />
|
||||
public async IAsyncEnumerable<MxEvent> StreamEventsAsync(
|
||||
[EnumeratorCancellation] CancellationToken cancellationToken)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user