C#解析DL/T645电力数据
DL/T 645协议详细解析
DL/T 645是我国电力行业的标准,主要适用于电表与计算机之间的通讯。
帧格式
帧起始符 68H
标识一帧信息的开始,其值为 68H=01101000B
地址域 A0~A5
地址域由 6 个字节构成,每字节 2 位 BCD 码,地址长度可达12位十进制数。每块表具有唯一的通信地址,且与物理层信道无关。当使用的地址码长度不足 6 字节时,高位用“0”补足。通信地址999999999999H为广播地址,只针对特殊命令有效,如广播校时和广播冻结等。广播命令不要求从站应答。地址域支持缩位寻址,即从若干低位起,剩余高位补AAH作为通配符进行读表操作,从站应答帧的地址域返回实际通信地址。地址域传输时低字节在前,高字节在后
控制码 C
数据域长度 L
L 为数据域的字节数。读数据时 L≤200,写数据时 L≤50,L=0 表示无数据域。
数据域 DATA
数据域包括数据标识、密码、操作者代码、数据、帧序号等,其结构随控制码的功能而改变。传输时发送方按字节进行加 33H 处理,接收方按字节进行减 33H 处理。
校验码 CS
从第一个帧起始符开始到校验码之前的所有各字节的模 256 的和,即各字节二进制算术和,不计超过 256 的溢出值。
结束符 16H
645常用控制码与返回
控制码 | 含义 | 正常返回 | 异常返回 |
---|---|---|---|
11 | 读数据 | 91 | D1 |
1A | 电表清零 | 9A | DA |
12 | 读后续数据 | B2 | D2 |
17 | 更改通讯速率 | 97 | D7 |
16 | 冻结 | 96 | D6 |
1B | 事件清零(默认事件总清0) | 9B | DB |
14 | 写数据 | 94 | D4 |
1C | 拉合闸 | 9C | DC |
示例解析:
当前指令是查询表地址777123000000的(当前)组合有功总电能
FEFEFEFE6877712300000068110433333333AD16
前置:fefefefe
起始符:68
地址域:777123000000
起始符:68
控制码:11
长度:04
数据域:33333333
校验码:ad
结束符:16
电表返回:
FEFEFEFE6877712300000068910833333333343333330D16
前置:fefefefe
起始符:68
地址域:777123000000
起始符:68
控制码返回:91 正常
长度:08
数据域:3333333334333333 其中数据表示为33333333 当前组合有功电能 值为 43333333 结果为:0.01
校验码:ad
结束符:16
.net简单封装一下解析
下面是主要的代码了
/// <summary>
/// 数据解析
/// </summary>
/// <param name="infos"></param>
public static Dlt645Analysis DL645Analysis(Span<byte> infos)
{
Dlt645Analysis analysis = new Dlt645Analysis();
Span<byte> data;
//去除FE
if (infos[0] == 254)
{
int codeIndexInfos = 1;
data = infos.Slice(1);
while (infos[codeIndexInfos] == 254)
{
data = data.Slice(1);
codeIndexInfos++;
}
}
else
{
data = infos;
}
string address = "";
//解析地址域
Span<byte> addressbyte = data.Slice(1, 6);
foreach (var item in addressbyte)
{
address += item.ToString("x2");
}
analysis.Address = address;
string type = data[8].ToString("x2").ToUpper();
bool suncess = type switch
{
//读数据
"91" => AnalysisRead(analysis, data, true),
_ => false
};
return analysis;
}
private void button1_Click(object sender, EventArgs e)
{
List<byte> list = new List<byte>();
for (int i = 0; i < txt645.Text.Length; i = i + 2)
{
var value = txt645.Text.Substring(i, 2);
list.Add((byte)(Convert.ToInt32(value, 16)));
}
var resultData = DL645Agreement.DL645Analysis(list.ToArray());
if (!resultData.Sucess)
{
txtIsSucess.Text = "返回失败";
return;
}
string infoType = resultData.Data.Substring(0, 8).ToUpper();//数据标识
string infoData = resultData.Data.Substring(8);//数据值
txtIsSucess.Text = "返回成功";
txtLabel.Text = infoType;
int length = Convert.ToInt32(txtLength.Text);
int intLength = Convert.ToInt32(txtInt.Text);
string[] spandata = new string[infoData.Length / 2];
for (int i = 0, index = 0; i < infoData.Length; i = i + 2, index++)
{
spandata[index] = infoData.Substring(i, 2);
}
spandata = spandata.Reverse().ToArray();
//数量
int count = spandata.Length / (length / 2);
string[] realinfo = new string[count];
for (int i = 0; i < count; i++)
{
string value = "";
for (int j = (length / 2) * i; j < (length / 2) * (i + 1); j++)
{
value += (Convert.ToInt32(spandata[j], 16) - 51).ToString("X2").PadLeft(2, '0');
}
if (length != intLength)
{
value = value.Insert(intLength, ".");
}
realinfo[i] = value;
}
txtAddress.Text = resultData.Address;
txtData.Text = string.Join(",", realinfo);
}