NumericRange — 数组 / 矩阵子段读写
概述
OPC UA Part 4 §7.27 NumericRange 表示 Value 属性的数组 / 矩阵子范围, 用于 Read / Write 时只取 / 写部分元素, 不传整段。常见编码:
| 字符串 | 含义 |
|---|---|
| "" | 整段 (All) |
| "5" | 第 5 个元素 |
| "3:7" | 第 3-7 个 (含端点) |
| "1:3,2:4" | 二维矩阵: 第一维 [1..3], 第二维 [2..4] |
| "0:5,10" | 二维: 第一维 [0..5], 第二维仅第 10 个 |
API
| 成员 | 类别 | 读写 | 说明 |
|---|---|---|---|
| NumericRange.All | 静态 | 读 | 整段 |
| NumericRange.Single(int) | 工厂 | — | 单一索引 |
| NumericRange.Span(start, end) | 工厂 | — | 区间 |
| NumericRange.Multi(params NumericRange[]) | 工厂 | — | 多维 |
| NumericRange.Parse(string) | 静态 | — | 解析字符串 |
| NumericRange.TryParse(s, out r) | 静态 | — | 安全解析 |
| range.ToString() | 方法 | — | 序列化为 OPC UA 字符串 |
| range.Start / End | 属性 | 读 | 起止下标 (-1 = 整段) |
| range.SubRanges | 属性 | 读 | 多维子范围 |
| range.IsAll | 属性 | 读 | 是否整段 |
| ua.ReadRange(nid, range, attr) | 扩展 | 读 | 读子段, 返回 DataValue |
| ua.WriteRange(nid, range, value) | 扩展 | 写 | 写子段, 返回 StatusCode |
代码示例
using DarraOpcUa_Client;
using var ua = new DarraOpcUa("opc.tcp://localhost:4840");
ua.Connect();
// 1) 读单个元素
using (var dv = ua.ReadRange("ns=2;s=Arr", NumericRange.Single(3)))
Console.WriteLine($"Arr[3] = {dv.Value}");
// 2) 读区间
using (var dv = ua.ReadRange("ns=2;s=Arr", NumericRange.Span(0, 99)))
foreach (var v in dv.Value.AsArray<double>())
Console.WriteLine(v);
// 3) 读二维矩阵子段
var range2D = NumericRange.Multi(NumericRange.Span(0, 9), NumericRange.Span(0, 9));
using (var dv = ua.ReadRange("ns=2;s=Matrix", range2D))
Console.WriteLine($"10×10 子矩阵, 共 {dv.Value.AsArray<double>().Length} 元素");
// 4) 写单个元素
var rc = ua.WriteRange("ns=2;s=Arr", NumericRange.Single(0), new Variant(42.5));
if (rc != StatusCode.Good) Console.WriteLine($"写失败: {rc}");
// 5) 字符串解析
var r = NumericRange.Parse("3:7");
Console.WriteLine(r.ToString()); // "3:7"
if (NumericRange.TryParse("0:5,10", out var r2D))
Console.WriteLine($"维度数 = {r2D.SubRanges.Length}");
// 6) 整段 — 等价于不带 range 的 Read
using (var dv = ua.ReadRange("ns=2;s=Arr", NumericRange.All))
Console.WriteLine($"全部 {dv.Value.AsArray<double>().Length} 元素");
实现说明
- Stack 不提供
*WithRangePInvoke 时,ReadRange/WriteRange抛NotImplementedException并打 Warn 日志 (不做"假装成功"兜底) range.IsAll时,ReadRange直接转发Read, 不依赖 Stack 扩展WriteRange只支持服务端 Value 是数组 / 矩阵的节点, 写 scalar 节点服务端返回BadIndexRangeInvalid
错误处理
| StatusCode | 含义 |
|---|---|
| Good | 成功 |
| BadIndexRangeInvalid | range 字符串非法 / 节点非数组 |
| BadIndexRangeNoData | range 在节点数据范围外 |
| BadTypeMismatch | 写入值与节点 DataType 不符 |
最佳实践
- 大数组只取片 — 1 万元素曲线只看尾部 100 点, 用
Span(9900, 9999)省 99% 流量 - 矩阵按行 / 列拆 —
NumericRange.Multi(Span(i, i), Span(0, cols-1))取第 i 行 - 写入只动改了的索引 — 不要把整数组重传一遍, 用
Single(i)写单点 - 不要每次都 Parse —
NumericRange.Span(0, 99)比NumericRange.Parse("0:99")快
跨语言对照
| C# | Python | Java | C++ | Rust | C |
|---|---|---|---|---|---|
| NumericRange | NumericRange | NumericRange | NumericRange | NumericRange | DarraUa_NumericRange |
| ReadRange | read_range | readRange | ReadRange | read_range | DarraUa_Session_ReadRange |
| WriteRange | write_range | writeRange | WriteRange | write_range | DarraUa_Session_WriteRange |