批量读取
能读取什么
逐个调 get 读 N 个属性就是 N 次往返。批量读取用一个 GET-Request(选择子 2) 把多个属性放进同一个 APDU,设备一次返回全部结果。
- 属性数量 ≥ 3:往返次数是主要开销,批量明显更快
- 属性之间有关联(同一相位的电压和电流):一次响应保证同一时刻的快照
- 属性只有 1~2 个:直接用
get,代码更简单
批量读取不会改变设备侧的取值时刻语义。设备仍在收到请求时采样,批量只是减少了网络往返。
数据地址表
批量读的地址就是普通 OAD,没有任何特殊之处。参见各数据类别页:
最小完整示例
一次读三相电压、电流和频率:
常用变体
必须按 OAD 分派,不能按下标
GetResponse::attributes 里每一项都带回显的 OAD(entry.attribute)。设备返回的顺序、条数都可能与请求不同 —— 有的设备会省略不支持的属性,有的会调整顺序。
按下标 attributes[0]、attributes[1] 取数据是错的。 正确做法是遍历全部项,按 entry.attribute 匹配你要的数据。
部分失败不影响其他项
批量读取的部分成功是常态:某些属性设备不支持,某些属性此刻读不到。逐项 DAR 才是正确处理方式:
请求数量上限
Limits::max_elements 默认 65536,同时限制单次 GET 的属性数。实际批量建议控制在几十个以内:
- 单个 APDU 超过
max_frame_bytes(默认 16385)时,链路层会分帧 - 响应过大时,协议层会启用 GET-NormalList 分块(GET-Next),库自动收齐所有块
分块对调用方是透明的,get_list 返回的是完整结果。但如果响应实在太大,收齐所有块的时间会超过 request_timeout,此时应减少单次请求的属性数量。
批量读记录对象
多个记录对象用 get_record_list:
响应按查询顺序返回,每个 RecordResult 仍带自己的回显 OAD 和列定义。某个查询失败时该位置的 result 是 DAR,不影响其他查询。
请求与响应报文
以下示例固定:SA 六个 00 字节、逻辑地址 0、CA=0、PIID=0、控制域请求 43/响应 C3。
一次读五个属性
请求(选择子 2 = GET-NormalList):
响应:
关键点:
- 每一项前面都有回显 OAD,这是按 OAD 分派的依据
- 每项的第二个字节是结果选择子:
00= 数据,01= DAR - 上例 5 项全部成功;若某项失败,该位置会是
01 <DAR>
混合成功与失败
把一个设备可能不支持的对象放进批量请求。请求(电压、状态字、频率):
响应(电压和频率正常,状态字 DAR=4):
关键点:结果个数仍是 3,失败项也在列表里,只是结果选择子变成 01 且紧跟一个 DAR 字节。库不会因为这一项失败而让整个 get_list 返回错误 —— 外层 Result 成功只表示"请求-响应往返完成",逐项结果要看 AttributeResult::result。
这也说明为什么必须遍历并按回显 OAD 匹配,而不能假设 attributes[i] 对应请求的第 i 项。