fix(opcuaclient): #649 review minors - CodeBits match, Bad-only exception ctor, BadNoData + Raw-resume tests, latent-impact docs

Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
This commit is contained in:
Joseph Doherty
2026-10-08 19:29:47 -04:00
parent 22e79112b6
commit b8c2136732
6 changed files with 122 additions and 8 deletions
@@ -200,9 +200,14 @@ public sealed class HistoryReadStatusException : Exception
/// <summary>Creates the exception for the upstream <paramref name="statusCode"/>.</summary>
/// <param name="statusCode">The Bad OPC UA status code to surface for the node.</param>
/// <exception cref="ArgumentException"><paramref name="statusCode"/> is not a Bad code (severity bits <c>10</c>/<c>11</c>).</exception>
public HistoryReadStatusException(uint statusCode)
: base($"The upstream server refused the history read with status 0x{statusCode:X8}.")
: base(IsBad(statusCode)
? $"The upstream server refused the history read with status 0x{statusCode:X8}."
: throw new ArgumentException($"0x{statusCode:X8} is not a Bad status code.", nameof(statusCode)))
=> StatusCode = statusCode;
private static bool IsBad(uint statusCode) => (statusCode & 0x80000000u) != 0;
}
/// <summary>
@@ -2382,9 +2382,10 @@ public sealed class OpcUaClientDriver : IDriver, ITagDiscovery, IReadable, IWrit
// The per-node status decides whether the payload means anything. BadNoData is the one Bad code
// that legitimately means "nothing in the window", so it stays an empty result.
if (StatusCode.IsBad(r.StatusCode) && r.StatusCode.Code != StatusCodes.BadNoData)
// CodeBits (not Code) so an info/overflow sub-status bit on the upstream code does not dodge the match.
if (StatusCode.IsBad(r.StatusCode) && r.StatusCode.CodeBits != StatusCodes.BadNoData)
{
if (r.StatusCode.Code == StatusCodes.BadAggregateNotSupported && aggregate is { } agg)
if (r.StatusCode.CodeBits == StatusCodes.BadAggregateNotSupported && aggregate is { } agg)
throw new HistoryAggregateNotSupportedException(agg);
throw new HistoryReadStatusException(r.StatusCode.Code);
}