跳到主要内容

历史流式读 (ReadHistoryStreamAsync)

概述

IDE 趋势图 / HMI 历史曲线拉历史时, 一次性把万点装进 List<DataValue> 会让 UI 线程卡顿 + OOM。ReadHistoryStreamAsync 返回 IAsyncEnumerable<DataValue>, 业务侧用 await foreach 拿一条处理一条, 边拉边渲染。

对应规范段: Part 11 §6.5 (HistoryRead)。

API

方法类别读写说明
ua.ReadHistoryStreamAsync(nodeId, t0, t1, batchSize, ct)历史异步流式读取 raw 历史

参数:

参数类型默认说明
nodeIdstring历史节点 NodeId
startUtcDateTime起始 UTC
endUtcDateTime结束 UTC
batchSizeuint1000每次 RPC 最多拿多少点
cancellationTokenCancellationTokendefault取消令牌

返回: IAsyncEnumerable<DataValue>, 每个元素 必须 Dispose (实现 IDisposable)。

代码示例

using DarraOpcUa_Client;

using var ua = new DarraOpcUa("opc.tcp://localhost:4840");
ua.Connect();

var t0 = DateTime.UtcNow.AddHours(-24);
var t1 = DateTime.UtcNow;

// 1) 边拉边渲染
await foreach (var dv in ua.ReadHistoryStreamAsync("ns=2;s=Temp", t0, t1))
{
using (dv)
{
chartSeries.Points.Add(dv.SourceTimestamp ?? DateTime.UtcNow, dv.Value.AsDouble);
}
}

// 2) 取消支持
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
try
{
await foreach (var dv in ua.ReadHistoryStreamAsync("ns=2;s=Temp", t0, t1, ct: cts.Token))
using (dv) Process(dv);
}
catch (OperationCanceledException)
{
Console.WriteLine("超时, 已部分加载");
}

// 3) LINQ 风格筛选 (System.Linq.Async)
var bad = await ua.ReadHistoryStreamAsync("ns=2;s=Temp", t0, t1)
.Where(dv => dv.Status != StatusCode.Good)
.CountAsync();
Console.WriteLine($"24h 内有 {bad} 个坏值");

实现限制

  • 当前底层 ReadHistory 不暴露 ContinuationPoint, 一次 RPC 返回区间内所有点 (受 batchSize 限制)
  • 大区间业务侧需自行切片 — 例如把 24 小时拆成 24 段 1 小时, 串行 await foreach
  • 取消立即返回当前已拿部分, 不抛 OperationCanceledException (除非 cts 触发在 RPC 期间)

与 ReadHistory 的对比

// 同步: 一次拿万点, UI 卡 1 秒
var vals = ua.ReadHistory("ns=2;s=Temp", t0, t1, maxValues: 50000);
foreach (var dv in vals)
using (dv) chartSeries.Points.Add(...);

// 异步流式: 边拉边渲染, UI 始终响应
await foreach (var dv in ua.ReadHistoryStreamAsync("ns=2;s=Temp", t0, t1))
using (dv) chartSeries.Points.Add(...);

最佳实践

  • 必须 using 每条 dv — 漏 Dispose 会泄漏 native 内存
  • 取消令牌 — 长查询给用户"取消"按钮
  • 分段策略 — 区间 > 1 小时建议外层手动切片, 防服务端单次返回上限触发截断
  • UI 渲染节流 — 每 100 条调用一次 chart.Refresh(), 不要每条都刷

跨语言对照

C#PythonJavaC++RustC
ReadHistoryStreamAsyncread_history_streamreadHistoryStreamReadHistoryStreamAsyncread_history_streamDarraUa_Session_ReadHistoryStream

下一步