fix(opcuaclient): HistoryRead honours the per-node StatusCode (BadAggregateNotSupported + other Bad) instead of returning empty data (#649)

Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
This commit is contained in:
Joseph Doherty
2026-10-08 19:16:55 -04:00
parent 8801631739
commit 22e79112b6
5 changed files with 109 additions and 14 deletions
@@ -185,6 +185,26 @@ public sealed class HistoryAggregateNotSupportedException : NotSupportedExceptio
=> Aggregate = aggregate;
}
/// <summary>
/// Thrown by a history backend that is a passthrough to another OPC UA server when that server answered
/// a node with a Bad per-node <c>HistoryReadResult.StatusCode</c> other than
/// <c>BadAggregateNotSupported</c> (which has its own <see cref="HistoryAggregateNotSupportedException"/>).
/// The Server HistoryRead path surfaces <see cref="StatusCode"/> verbatim as that node's result status
/// instead of the generic <c>BadHistoryOperationUnsupported</c>, so a refused read is never mistaken for
/// an empty Good one.
/// </summary>
public sealed class HistoryReadStatusException : Exception
{
/// <summary>The raw OPC UA status code the upstream server returned for the node.</summary>
public uint StatusCode { get; }
/// <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>
public HistoryReadStatusException(uint statusCode)
: base($"The upstream server refused the history read with status 0x{statusCode:X8}.")
=> StatusCode = statusCode;
}
/// <summary>
/// One row returned by <see cref="IHistoryProvider.ReadEventsAsync"/> — a historical
/// alarm/event record, not the OPC UA live-event stream. Fields match the minimum set the