电能读取
能读取什么
电能是累计量,按方向和无功类型分成若干家族,每个家族又分相别和费率:
对每个家族可以读:总量、分相(A/B/C)、分费率(费率 1~n),以及扩展精度。
有符号家族的原始值可能为负,不要用无符号类型存储。
数据地址表
每个家族按标准编号展开,总对象 + 三个相别对象:
每个电能对象收录的属性:
数组长度 = tariff_count + 1。属性 2/3/4/5 都是只读,电能对象不提供写入。
单位是 kWh/kvarh/kVAh,不是 Wh。 倍率 -2 已经是这个单位的结果。
最小完整示例
#include <dlt698/app.hpp>
#include <dlt698/standard/catalog.hpp>
#include <iostream>
int main() {
using namespace dlt698;
namespace oi = standard::oi;
// 费率数必须与设备一致;先读 400C 得到实际值再构造布局。
const standard::DeviceLayout layout{standard::Wiring::three_phase, 4, 21};
app::ClientOptions options;
options.protocol.server.bytes = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
app::Client client(options);
auto connected = client.connect_tcp("127.0.0.1", 6980);
if (!connected) {
std::cerr << connected.error().context << '\n';
return 1;
}
// 正向有功总电能:家族=0010,相别=total,费率=0(总量)
auto oad = standard::energy_oad(oi::forward_active_energy, standard::Phase::total, 0, layout);
if (!oad) {
std::cerr << oad.error().context << '\n';
return 1;
}
auto value = client.get(oad.value());
if (!value) {
std::cerr << value.error().context << '\n';
client.disconnect();
return 1;
}
if (const auto dar = std::get_if<std::uint8_t>(&value.value())) {
// DAR=4 表示设备没有提供这个对象
std::cerr << "meter does not provide it, DAR=" << unsigned(*dar) << '\n';
client.disconnect();
return 1;
}
const auto& data = std::get<model::Data>(value.value());
// 校验类型并换算倍率:线上 123456 是原始整数,1234.56 kWh 才是工程值。
auto numbers = standard::engineering_values(oad.value(), data, layout);
if (!numbers || numbers.value().empty()) {
std::cerr << "unexpected value\n";
client.disconnect();
return 1;
}
std::cout << "total forward active energy = "
<< standard::decimal_text(numbers.value()[0]) << ' '
<< standard::unit_symbol(numbers.value()[0].scaling.unit) << '\n';
// 输出:total forward active energy = 1234.56 kWh
client.disconnect();
return 0;
}
常用变体
一次读总量和所有费率
索引 0 读整个数组,engineering_values 按数组顺序输出所有数值:
auto oad = standard::make_oad(standard::oi::forward_active_energy, 2, 0, layout);
auto value = client.get(oad.value());
if (value && !std::get_if<std::uint8_t>(&value.value())) {
const auto& data = std::get<model::Data>(value.value());
auto numbers = standard::engineering_values(oad.value(), data, layout);
if (numbers) {
// 顺序:总量、费率 1、费率 2 ...
for (std::size_t i = 0; i < numbers.value().size(); ++i)
std::cout << (i == 0 ? "total" : "tariff " + std::to_string(i))
<< " = " << standard::decimal_text(numbers.value()[i]) << '\n';
}
}
分相电能
相别在 OI 里,不在索引里:
auto total = standard::energy_oad(oi::forward_active_energy, standard::Phase::total, 0, layout);
auto a = standard::energy_oad(oi::forward_active_energy, standard::Phase::a, 0, layout);
auto b = standard::energy_oad(oi::forward_active_energy, standard::Phase::b, 0, layout);
auto c = standard::energy_oad(oi::forward_active_energy, standard::Phase::c, 0, layout);
// 分别对应 0010/2/1、0011/2/1、0012/2/1、0013/2/1
auto result = client.get_list({total.value(), a.value(), b.value(), c.value()});
三相四线表的分相电能之和通常不等于总量,差额是线路损耗,不能当作数据错误。
分费率
tariff 参数从 0 开始(0 是总量),内部转换成索引 tariff + 1:
for (std::size_t tariff = 0; tariff <= layout.tariff_count; ++tariff) {
auto oad = standard::energy_oad(oi::forward_active_energy, standard::Phase::total, tariff, layout);
// tariff=0 → 0010/2/1(总量)
// tariff=1 → 0010/2/2(费率 1)
}
扩展精度
auto normal = standard::energy_oad(oi::forward_active_energy, standard::Phase::total, 0, layout, false); // 0010/2/1
auto precise = standard::energy_oad(oi::forward_active_energy, standard::Phase::total, 0, layout, true); // 0010/4/1
属性 4 是独立属性,不是所有设备都提供,不支持时返回 DAR=4。类型 UInt64,倍率 -4,decimal_text() 不经过浮点,能精确表示 4 位小数。
读取多个家族
std::vector<model::Oad> oads;
for (auto family : {oi::forward_active_energy, oi::reverse_active_energy,
oi::combination_reactive_energy_1, oi::quadrant_1_reactive_energy}) {
auto oad = standard::energy_oad(family, standard::Phase::total, 0, layout);
if (oad) oads.push_back(oad.value());
}
auto result = client.get_list(oads);
单个列表里 OAD 数量过多时响应会分块,客户端自动收齐,但总耗时会增加。
请求与响应报文
以下示例固定:SA 六个 00 字节、逻辑地址 0、CA=0、PIID=0、控制域请求 43/响应 C3。
读取正向有功总电能
请求(GET Normal,读取 0010/2/1):
68 17 00 43 05 00 00 00 00 00 00 00 72 79 05 01 00 00 10 02 01 00 0D 04 16
响应(UInt32 123456,即 1234.56 kWh):
68 1E 00 C3 05 00 00 00 00 00 00 00 9E 54 85 01 00 00 10 02 01 01 06 00 01 E2 40 00 00 5A D4 16
00 01 E2 40 = 123456,乘以 10^-2 得 1234.56 kWh。
读取整个电能数组(四费率)
请求(0010/2/0,索引 0 = 完整数组):
68 17 00 43 05 00 00 00 00 00 00 00 72 79 05 01 00 00 10 02 00 00 D5 1D 16
响应(总量 123456,四费率 10000、20000、30000、63456):
68 34 00 C3 05 00 00 00 00 00 00 00 9C A1 85 01 00 00 10 02 00 01 01 05 06 00 01 E2 40 06 00 00 27 10 06 00 00 4E 20 06 00 00 75 30 06 00 00 F7 E0 00 00 CC B3 16
解析:结果选择子 01 → 数组长度 05(5 项)→ 5 个 06(UInt32)值,顺序为总量、费率 1~4:
四者之和 634.56 + 100 + 200 + 300 = 1234.56,正好等于总量。
读取 A 相正向有功电能
请求(0011/2/1):
68 17 00 43 05 00 00 00 00 00 00 00 72 79 05 01 00 00 11 02 01 00 B6 18 16
响应(UInt32 400000,即 4000.00 kWh):
68 1E 00 C3 05 00 00 00 00 00 00 00 9E 54 85 01 00 00 11 02 01 01 06 00 06 1A 80 00 00 7E 29 16
读取第一象限无功电能
请求(0050/2/1):
68 17 00 43 05 00 00 00 00 00 00 00 72 79 05 01 00 00 50 02 01 00 BA 12 16
响应(UInt32 54321,即 543.21 kvarh):
68 1E 00 C3 05 00 00 00 00 00 00 00 9E 54 85 01 00 00 50 02 01 01 06 00 00 D4 31 00 00 77 CF 16
读取扩展精度
请求(0010/4/1):
68 17 00 43 05 00 00 00 00 00 00 00 72 79 05 01 00 00 10 04 01 00 D4 D2 16
响应(UInt64 12345678,即 1234.5678 kWh):
68 22 00 C3 05 00 00 00 00 00 00 00 AE 25 85 01 00 00 10 04 01 01 15 00 00 00 00 00 BC 61 4E 00 00 62 BB 16
15 是 UInt64 标签,后 8 字节是 12345678。
PIID 和地址变化
PIID 是请求匹配响应的标识,每帧应不同:
PIID=0 68 17 00 43 05 00 00 00 00 00 00 00 72 79 05 01 00 20 0F 02 00 00 1C 0C 16
PIID=1 68 17 00 43 05 00 00 00 00 00 00 00 72 79 05 01 01 20 0F 02 00 00 37 08 16
PIID=0x21 68 17 00 43 05 00 00 00 00 00 00 00 72 79 05 01 21 20 0F 02 00 00 57 8D 16
SA 改成 123456789012(线序 12 90 78 56 34 12)后,HCS 和 FCS 都要重算:
68 17 00 43 05 12 90 78 56 34 12 00 A3 2E 05 01 00 20 0F 02 00 00 1C 0C 16
相关配置
读取费率数的方法见电表参数读取与设置。
常见失败
下一步