历史流式读
概述
IDE 趋势图拉历史时, 一次性把万点装进 List 会让 EDT 卡顿 + OOM。readHistoryStream 返回 Stream<DataValue>, 业务侧 forEach 拿一条处理一条。
对应规范段: Part 11 §6.5。
API
| 方法 | 类别 | 读写 | 说明 |
|---|---|---|---|
| ua.readHistoryStream(nodeId, t0, t1, batchSize) | 历史 | 读 | 返回 Stream<DataValue> |
| 参数 | 默认 | 说明 |
|---|---|---|
| nodeId | — | 历史节点 |
| startUtc | — | 起始 Instant |
| endUtc | — | 结束 Instant |
| batchSize | 1000 | 每次 RPC 最多点数 |
代码示例
import com.darra.opcua.*;
import java.time.*;
import java.util.stream.Stream;
try (DarraOpcUa ua = new DarraOpcUa("opc.tcp://localhost:4840")) {
ua.connect();
Instant t1 = Instant.now();
Instant t0 = t1.minus(Duration.ofHours(24));
// 1) 边拉边渲染
try (Stream<DataValue> s = ua.readHistoryStream("ns=2;s=Temp", t0, t1, 1000)) {
s.forEach(dv -> {
try (dv) {
chartSeries.addPoint(dv.sourceTimestamp, dv.value.asDouble());
}
});
}
// 2) LINQ 风格筛选
long badCount;
try (Stream<DataValue> s = ua.readHistoryStream("ns=2;s=Temp", t0, t1, 1000)) {
badCount = s.filter(dv -> dv.status != StatusCode.Good)
.peek(DataValue::close)
.count();
}
System.out.println("24h 内 " + badCount + " 个坏值");
}
实现限制
- 当前底层 readHistory 不暴露 ContinuationPoint, 一次 RPC 返回区间内所有点 (受 batchSize 限)
- 大区间业务侧自行切片
- 必须 close 每个 dv, 否则 native 内存泄漏
最佳实践
- try-with-resources 包 Stream
- 区间 > 1 小时建议外层切片
- UI 渲染节流: 每 100 条 refresh
跨语言对照
| C# | Python | Java | C++ | Rust | C |
|---|---|---|---|---|---|
| ReadHistoryStreamAsync | read_history_stream | readHistoryStream | ReadHistoryStreamAsync | read_history_stream | DarraUa_Session_ReadHistoryStream |