fix(code-review): resolve Batch 2 open findings (AbCip, AbLegacy, Galaxy, FOCAS)
- Driver.AbCip.Contracts-001: parse 'writable' from TagConfig JSON (default true) instead of hardcoding - Driver.AbCip.Contracts-002/-003: Dt type comment; drop dead [Display]/[Range] annotations - Driver.AbCip.Contracts-004: dedicated AbCipEquipmentTagParser test class (+15) - Driver.AbCip-017: document Tick severity Low-fallback on Bad severity read - Driver.AbLegacy.Contracts-002/-003/-004: isArray-scalar remarks (+tests), MaxTagBytes/ForFamily docs - Driver.Galaxy.Browser-003 + Driver.Galaxy.Contracts-003: extract ResolveApiKey -> GalaxySecretRef (dedup) - Driver.Galaxy-019: cache buffered-interval only on Ok + ILogger warnings + ClassifyIntervalReply (+tests) - Driver.FOCAS.Contracts-002: thread WriteIdempotent through DiscoverAsync (+test)
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using ZB.MOM.WW.MxGateway.Client;
|
||||
using ZB.MOM.WW.MxGateway.Contracts.Proto;
|
||||
// Use the generated nested status enum for the SetBufferedUpdateInterval reply check.
|
||||
@@ -18,14 +20,21 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Runtime;
|
||||
public sealed class GatewayGalaxySubscriber : IGalaxySubscriber
|
||||
{
|
||||
private readonly GalaxyMxSession _session;
|
||||
private readonly ILogger _logger;
|
||||
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)
|
||||
/// <param name="logger">
|
||||
/// Optional logger; surfaces a warning when <c>SetBufferedUpdateInterval</c>
|
||||
/// soft-fails so the cadence-not-applied condition isn't silent. Null is allowed
|
||||
/// for unit-test construction and falls back to <see cref="NullLogger.Instance"/>.
|
||||
/// </param>
|
||||
public GatewayGalaxySubscriber(GalaxyMxSession session, ILogger? logger = null)
|
||||
{
|
||||
_session = session ?? throw new ArgumentNullException(nameof(session));
|
||||
_logger = logger ?? NullLogger.Instance;
|
||||
}
|
||||
|
||||
/// <summary>Subscribes to a bulk list of Galaxy references with optional buffered update interval.</summary>
|
||||
@@ -86,11 +95,33 @@ public sealed class GatewayGalaxySubscriber : IGalaxySubscriber
|
||||
},
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (reply.ProtocolStatus?.Code is not (ProtocolStatusCode.Ok or ProtocolStatusCode.MxaccessFailure))
|
||||
var code = reply.ProtocolStatus?.Code;
|
||||
|
||||
// MxaccessFailure means the COM-side SetBufferedUpdateInterval did NOT apply, so
|
||||
// we must NOT cache the requested value — caching it would suppress the retry on
|
||||
// the next subscribe at this interval. Only Ok records the value as applied.
|
||||
if (!ClassifyIntervalReply(code))
|
||||
{
|
||||
// Don't throw on a soft failure — the SubscribeBulk will still succeed at the
|
||||
// gw's default cadence, which is functional just not the requested cadence.
|
||||
// The trace span (PR 6.1) plus the warning here gives ops the signal.
|
||||
// The trace span (PR 6.1) plus this warning gives ops the signal, and leaving
|
||||
// _lastAppliedIntervalMs unchanged lets the next subscribe re-attempt the set.
|
||||
if (code == ProtocolStatusCode.MxaccessFailure)
|
||||
{
|
||||
_logger.LogWarning(
|
||||
"Galaxy SetBufferedUpdateInterval({IntervalMs}ms) soft-failed (MxaccessFailure); " +
|
||||
"buffered subscriptions on server handle {ServerHandle} will publish at the gateway's " +
|
||||
"default cadence. The requested interval was not cached, so a later subscribe will retry it.",
|
||||
intervalMs, serverHandle);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning(
|
||||
"Galaxy SetBufferedUpdateInterval({IntervalMs}ms) returned an unexpected protocol status " +
|
||||
"{Code} on server handle {ServerHandle}; treating it as not-applied and leaving the " +
|
||||
"requested interval uncached so a later subscribe retries it.",
|
||||
intervalMs, code, serverHandle);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -100,6 +131,17 @@ public sealed class GatewayGalaxySubscriber : IGalaxySubscriber
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Classifies a <c>SetBufferedUpdateInterval</c> reply: returns <c>true</c> only when
|
||||
/// the requested interval was actually applied and may be cached as last-applied.
|
||||
/// This is <see cref="ProtocolStatusCode.Ok"/> alone — <see cref="ProtocolStatusCode.MxaccessFailure"/>
|
||||
/// means the COM-side set did NOT take effect (so caching it would prevent a retry),
|
||||
/// and a <c>null</c> or any other unexpected code is treated as not-applied.
|
||||
/// </summary>
|
||||
/// <param name="code">The protocol status code from the gateway reply, or <c>null</c> when absent.</param>
|
||||
/// <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>
|
||||
|
||||
Reference in New Issue
Block a user