using Opc.Ua;
using Serilog;
namespace ZB.MOM.WW.OtOpcUa.Client.Shared.Helpers;
///
/// The client-side HistoryRead paging loop shared by the raw and modified reads. Continuation pages are
/// READ (releaseContinuationPoints = false) while the caller still wants values; a read stopped by the
/// caller's own cap while the server still holds a cursor releases that cursor so it does not occupy one of the
/// session's continuation-point slots until eviction.
///
internal static class HistoryReadPaging
{
private static readonly ILogger Logger = Log.ForContext(typeof(HistoryReadPaging));
/// Reads (or, with , releases) one page.
/// The cursor to resume, or for the first page.
/// to release the cursor instead of reading.
/// The caller's cancellation token.
/// The per-node result, or when the response carried none.
public delegate Task PageReader(
byte[]? continuationPoint, bool releaseContinuationPoints, CancellationToken ct);
/// Reads pages until the server finishes or the caller's cap is reached.
/// The element type extracted from each page.
/// Issues one HistoryRead for the single node.
/// Extracts the page's elements from a result.
/// The caller's cap; <= 0 means no cap (follow every continuation point).
/// The node being read, for refusal messages.
/// The caller's cancellation token.
/// Every element read.
/// A page was refused (see ).
public static async Task> ReadAllAsync(
PageReader readPage, Func> extract, int maxValues, NodeId nodeId,
CancellationToken ct)
{
var all = new List();
byte[]? continuationPoint = null;
while (true)
{
var result = await readPage(continuationPoint, false, ct).ConfigureAwait(false);
if (result == null) return all;
HistoryReadStatus.ThrowIfRefused(result.StatusCode, nodeId);
all.AddRange(extract(result));
continuationPoint = result.ContinuationPoint;
if (continuationPoint is not { Length: > 0 }) return all;
if (maxValues > 0 && all.Count >= maxValues) break;
}
// Stopped by the client's own cap: the server still holds a cursor for this session. Release it; failing to
// is not worth failing a read whose values are already in hand.
try
{
await readPage(continuationPoint, true, ct).ConfigureAwait(false);
}
catch (Exception ex) when (ex is not OperationCanceledException)
{
Logger.Warning(ex, "Releasing the HistoryRead continuation point for {NodeId} failed", nodeId);
}
return all;
}
}