fix(opcua): reverse HistoryRead-Raw paging no longer stops after the first page
A reverse read (start > end) with a small NumValuesPerNode resumed through the tie-cluster path, which drained the cluster and stepped FORWARD to T + 1 tick - outside a reverse window - so the read ended with GoodNoData after one sample. SliceTieCluster now steps down to T - 1 tick for a reverse read (terminal at the excluded end), and the node manager slices the over-fetched cluster in the read's own direction so the skip count is not applied from the wrong end. Forward paging is unchanged. Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
This commit is contained in:
@@ -205,14 +205,29 @@ internal static class HistoryPaging
|
||||
/// remains, or <c>null</c> when the window is exhausted (terminal — no continuation point).</param>
|
||||
/// <param name="nextSkip">The next page's boundary skip count: the running emitted-tie total while the
|
||||
/// cluster drains, else <c>0</c> after advancing past it.</param>
|
||||
/// <param name="reverse">
|
||||
/// <c>true</c> for a reverse read (Part 11: <c>start > end</c>, samples in descending time, window
|
||||
/// <c>(end, start]</c>). A drained cluster then steps DOWN to <c>T − 1 tick</c>, which is lossless for the
|
||||
/// same reason <c>T + 1 tick</c> is going forward, and the read continues only while that is still strictly
|
||||
/// after <paramref name="endUtc"/> (the reverse window excludes <c>end</c>; a resume at exactly <c>end</c>
|
||||
/// would turn into a <c>start == end</c> point read and return the excluded sample). Forward is unchanged.
|
||||
/// </param>
|
||||
public static void SliceTieCluster(
|
||||
int clusterCount, int skip, uint cap, DateTime boundaryT, DateTime endUtc,
|
||||
out int sliceStart, out int sliceCount, out DateTime? nextStartUtc, out int nextSkip)
|
||||
out int sliceStart, out int sliceCount, out DateTime? nextStartUtc, out int nextSkip,
|
||||
bool reverse = false)
|
||||
{
|
||||
sliceStart = Math.Min(skip, clusterCount);
|
||||
sliceCount = Math.Min((int)cap, clusterCount - sliceStart);
|
||||
var emitted = sliceStart + sliceCount;
|
||||
if (emitted < clusterCount) { nextStartUtc = boundaryT; nextSkip = emitted; }
|
||||
else if (reverse)
|
||||
{
|
||||
// DateTime.MinValue cannot step down; nothing can lie below it anyway.
|
||||
var more = boundaryT > DateTime.MinValue && boundaryT.AddTicks(-1) > endUtc;
|
||||
nextStartUtc = more ? boundaryT.AddTicks(-1) : null;
|
||||
nextSkip = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
var next = boundaryT.AddTicks(1);
|
||||
|
||||
Reference in New Issue
Block a user