电表通讯协议DLT645-2007编程

1、协议

电表有个电力行业推荐标准《DLT645-2007多功能电能表通信协议》,电表都支持,通过该协议读取数据,不同的电表不需要考虑编码格式、数据地址、高低位转换等复杂情况,统一采集。

不方便的地方在于这个协议定义得有点小复杂,自己带有各种特殊性定义,编程时一堆的坑。

不少电表可以同时支持DLT645-2007和MODBUS RTU协议,但MODBUS协议在不同的电表中,地址都是不同的,需要查阅手册才能搞定。

DLT645不同的数据需要发送独立的请求,而Modbus数据地址连接的可以一次读取,各有所长和优势。

2、协议定义和报文

      协议定义和报文,在协议文档中有详细说明,主要部分节选如下:

3、数据域定义

         协议中对每个数据都定义了明确的地址和解析格式,如:

比如 00 00 00 00表示组合有功总电能。同一组的可以使用FF通配,表示读取整块的数据,00 00 FF 00表示一次读取 00 00 00 00 ~ 00 00 3F 00的所有数据。

 但需要注意的事,不是所有电表都支持块数据读取。

4、编码验证

    public class DLT645_2007{public enum CommandType{ReadData = 0x11,WriteData = 0x14,ReadAddress = 0x13,WriteAddress = 0x15}public class Dlt645Addr{public string dataIndentifier;public int dataLen;public float divisor = 1.0f;public string unit;public string dataItemName;public string dataName;public string PascalType;public Dlt645Addr(string dataIndentifier, int dataLen, float divisor, string unit, string dataItemName, string dataName= "", string PascalType = "FLOAT"){this.dataIndentifier = dataIndentifier;this.dataLen = dataLen;this.divisor = divisor;this.unit = unit;this.dataItemName = dataItemName;this.dataName = dataName;this.PascalType = PascalType;}}public Dlt645Addr []addrList ={//电能量new Dlt645Addr("00010000",4,100,"V" ,"正向有功总电量","Forth_Have_Power_Total"),// R4new Dlt645Addr("00010100",4,100,"V" ,"正向有功尖电量","Forth_Have_Power_SPIKE"),// R4 以上四个名称待核实new Dlt645Addr("00010200",4,100,"V" ,"正向有功峰电量","Forth_Have_Power_PEAK"),// R4new Dlt645Addr("00010300",4,100,"V" ,"正向有功平电量","Forth_Have_Power_FLAT"),// R4new Dlt645Addr("00010400",4,100,"V" ,"正向有功谷电量","Forth_Have_Power_VALLEY"),// R4//变量数据new Dlt645Addr("02010100",2,  10,"V","A项电压","Phase_A_Volt"),// R2new Dlt645Addr("02010200",2,  10,"V","B项电压","Phase_B_Volt"),// R2new Dlt645Addr("02010300",2,  10,"V","C项电压","Phase_C_Volt"),// R2new Dlt645Addr("02020100",3,1000,"A","A项电流","Phase_A_Elec"),// R3new Dlt645Addr("02020200",3,1000,"A","B项电流","Phase_B_Elec"),// R3new Dlt645Addr("02020300",3,1000,"A","C项电流","Phase_C_Elec"),// R3new Dlt645Addr("02030000",3,1000,"kW","瞬时总有功率功率","Instant_Have_Power_Rate_Total"),// R3new Dlt645Addr("02030000",3,1000,"kW","瞬时A相有功率功率","Instant_Phase_A_Have_Power_Rate"),// R3new Dlt645Addr("02030000",3,1000,"kW","瞬时B相有功率功率","Instant_Phase_B_Have_Power_Rate"),// R3new Dlt645Addr("02030000",3,1000,"kW","瞬时C相有功率功率","Instant_Phase_C_Have_Power_Rate"),// R3new Dlt645Addr("02040000",3,1000,"kwar","瞬时总无功率功率","Instant_None_Power_Rate_Total"),// R3new Dlt645Addr("02040000",3,1000,"kwar","瞬时A相无功率功率","Instant_Phase_A_None_Power_Rate"),// R3new Dlt645Addr("02040000",3,1000,"kwar","瞬时B相无功率功率","Instant_Phase_B_None_Power_Rate"),// R3new Dlt645Addr("02040000",3,1000,"kwar","瞬时C相无功率功率","Instant_Phase_C_None_Power_Rate"),// R3new Dlt645Addr("02050000",3,1000,"kVA","瞬时总视在功率","Instant_Apparent_Power_Rate_Total"),// R3new Dlt645Addr("02050000",3,1000,"kVA","瞬时A相视在功率","Instant_Phase_A_Apparent_Power_Rate"),// R3new Dlt645Addr("02050000",3,1000,"kVA","瞬时B相视在功率","Instant_Phase_B_Apparent_Power_Rate"),// R3new Dlt645Addr("02050000",3,1000,"kVA","瞬时C相视在功率","Instant_Phase_C_Apparent_Power_Rate"),// R3new Dlt645Addr("02060000",2,1000,"","总功率因数","Power_Rate_Factor_Total"),// R2new Dlt645Addr("02060100",2,1000,"","A相功率因数","Phase_A_Power_Rate_Factor"),// R2new Dlt645Addr("02060200",2,1000,"","B相功率因数","Phase_B_Power_Rate_Factor"),// R2new Dlt645Addr("02060300",2,1000,"","C相功率因数","Phase_C_Power_Rate_Factor"),// R2new Dlt645Addr("02070100",2,  10,"V","A相相角","Phase_A_Angle"),// R2new Dlt645Addr("02070200",2,  10,"V","B相相角","Phase_B_Angle"),// R2new Dlt645Addr("02070300",2,  10,"V","C相相角","Phase_C_Angle"),// R2new Dlt645Addr("02080100",2, 100,"%","A相电压波形失真度","Phase_A_Volt_Waveform_Distortion"),// R2new Dlt645Addr("02080200",2, 100,"%","B相电压波形失真度","Phase_B_Volt_Waveform_Distortion"),// R2new Dlt645Addr("02080300",2, 100,"%","C相电压波形失真度","Phase_C_Volt_Waveform_Distortion"),// R2new Dlt645Addr("02090100",2, 100,"%","A相电流波形失真度","Phase_A_Elec_Waveform_Distortion"),// R2new Dlt645Addr("02090200",2, 100,"%","B相电流波形失真度","Phase_B_Elec_Waveform_Distortion"),// R2new Dlt645Addr("02090300",2, 100,"%","C相电流波形失真度","Phase_C_Elec_Waveform_Distortion")// R2//最大需量//事件记录数//负荷记录//参变量//安全认证};Dictionary<string,Dlt645Addr> dictAddr = new Dictionary<string, Dlt645Addr> ();//是否需要唤醒 ,0xFEprivate bool isNeedWakeUp = false;// 创建串口对象SerialPort serialPort = null;int rxCount = 0;byte[] rxdata = new byte[512];Stopwatch swRead = new Stopwatch ();public DLT645_2007(){foreach(var addr in addrList){dictAddr[addr.dataIndentifier] = addr; }}//SerialPort("COM3", 9600, Parity.None, 8, StopBits.One);public bool OpenDevice(string portName, int baudRate= 2400, Parity parity = Parity.None, int dataBits=8, StopBits stopBits = StopBits.One){try{serialPort = new SerialPort(portName, baudRate, parity, dataBits, stopBits);serialPort.Open();serialPort.ReadTimeout = 500;serialPort.DataReceived += SerialPort_DataReceived;return true;}catch(Exception ex){return false;}}private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e){byte[] rxTemp = new byte[1024];int rdCount = serialPort.Read(rxTemp, 0, rxTemp.Length);if (rdCount > 0){for(int  i = 0; i < rdCount; i++){if (rxTemp[i] == 0xfe)continue;rxdata[rxCount++] = rxTemp[i];if (rxTemp[i] == 0x16){ParseData();}}}swRead.Stop();}public void ParseDataTest(string data){data = data.Replace(" ", "");rxCount = data.Length;for(int i = 0; i < rxCount;i+=2){rxdata[i/2] = Convert.ToByte(data.Substring(i, 2), 16);}ParseData();}private void ParseData(){//校验和标识检查if(rxCount < 9 || rxdata[0] != 0x68 || rxdata[7] != 0x68){Console.WriteLine($"校验和标识检查失败,len:{rxCount},{rxdata[0]:X},{rxdata[7]:X}");rxCount = 0;return;}//响应命令检查if (rxdata[8] != 0x91 && rxdata[8] != 0xB1 ){Console.WriteLine($"响应命令检查失败,{rxdata[8]:X}");rxCount = 0;return;}string addrT = BitConverter.ToString(rxdata, 1, 6).Replace("-","");string addr = addrT.Substring(8,2)+ addrT.Substring(6, 2) + addrT.Substring(4, 2) + addrT.Substring(2, 2) + addrT.Substring(0, 2);string value = "";int dataLen = rxdata[9];for(int k = 0; k < dataLen; k++){rxdata[10 + k] -= 0x33;value += string.Format("{0:X2}", rxdata[10 + k]);}string index = value.Substring(6, 2)+ value.Substring(4, 2)+ value.Substring(2, 2)+ value.Substring(0, 2);var dlt645 = dictAddr[index];string value2 = "";for(int k = dlt645.dataLen-1; k >= 0; k --){value2 += value.Substring(8 + k * 2, 2);}float fv = float.Parse(value2); Console.WriteLine($"表号:{addr},{dlt645.dataItemName},{dlt645.dataIndentifier},{fv/dlt645.divisor} [{dlt645.unit}]");rxCount = 0;}public void CloseDevice(){serialPort.Close();serialPort = null;}public void Read(List<string> addrMeter, string data){if (isNeedWakeUp){byte[] reqHe = { 0xFE, 0xFE };serialPort.Write(reqHe, 0, reqHe.Length);}byte []req = BuildRequest(addrMeter[0], data, CommandType.ReadData);serialPort.Write(req,0,req.Length);swRead.Restart();}public byte[] BuildRequest(string addrMeter, string data, CommandType commandType){// 构建请求帧byte[] request = new byte[12 + 4];int index = 0;request[index++] = 0x68;  // 帧起始符 68H//地址域 A0~A5,地址域是用来表示电表地址,低位在前,高位在后request[index++] = Convert.ToByte(addrMeter.Substring(10, 2), 16);  // 地址域 A0request[index++] = Convert.ToByte(addrMeter.Substring(8, 2), 16);  // 地址域 A1request[index++] = Convert.ToByte(addrMeter.Substring(6, 2), 16);  // 地址域 A2request[index++] = Convert.ToByte(addrMeter.Substring(4, 2), 16);  // 地址域 A3request[index++] = Convert.ToByte(addrMeter.Substring(2, 2), 16);  // 地址域 A4request[index++] = Convert.ToByte(addrMeter.Substring(0, 2), 16);  // 地址域 A5request[index++] = 0x68;  // 帧起始符request[index++] = 0x11;  // 控制码request[index++] = (byte)(4);// Convert.ToByte((data.Count * 4).ToString(), 16);  // 数据域长度for (int i = 3;i >=0; i --){string sV = data.Substring(i*2, 2);if (sV.Equals("FF")){request[index++] = 0xff;}else{request[index++] = (byte)(Convert.ToByte(sV, 16) + 0x33);  // 数据}}byte crc = GetCRC(request, 0, request.Length-2);  // 校验码request[index++] = Convert.ToByte(crc.ToString("X"), 16);request[index++] = 0x16;  // 结束符return request;}private byte GetCRC(byte[] data, int start, int length){int I = 0;for (int k = 0; k < length; k++){I += data[start + k];}return (byte)(I % 256);}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/310607.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

4.33 构建onnx结构模型-Expand

前言 构建onnx方式通常有两种&#xff1a; 1、通过代码转换成onnx结构&#xff0c;比如pytorch —> onnx 2、通过onnx 自定义结点&#xff0c;图&#xff0c;生成onnx结构 本文主要是简单学习和使用两种不同onnx结构&#xff0c; 下面以 Expand 结点进行分析 方式 方法一…

基于电商场景的高并发RocketMQ实战-Consumer端队列负载均衡分配机制、并发消费以及消费进度提交

&#x1f308;&#x1f308;&#x1f308;&#x1f308;&#x1f308;&#x1f308;&#x1f308;&#x1f308; 【11来了】文章导读地址&#xff1a;点击查看文章导读&#xff01; &#x1f341;&#x1f341;&#x1f341;&#x1f341;&#x1f341;&#x1f341;&#x1f3…

lv13 内核模块参数和依赖

1 模块传参 1.1 模块参数设置 将指定的全局变量设置成模块参数 module_param(name,type,perm);//将指定的全局变量设置成模块参数 /* name:全局变量名 type&#xff1a;使用符号 实际类型 传参方式bool bool insmod xxx.ko 变量名0 …

Kafka集群详解

Kafka介绍Kafka集群介绍Kafka集群特点Kafka集群搭建在这里插入图片描述Kafka集群如何进行故障切换Kafka集群Leader的选举Kafka集群如何快速横向拓展Kafka集群搭建最佳实践Kafka集群可以使用单节点Zookeeper吗Kafka集群的消费者信息保存在那里Kafka集群的Topic的分区数的设置规则…

区块链的三难困境是什么,如何解决?

人们需要保持社交、工作和睡眠之间的平衡&#xff0c;并且努力和谐相处。同样的概念也反映在区块链的三难困境中。 区块链三难困境是一个术语&#xff0c;指的是现有区块链的局限性&#xff1a;可扩展性、安全性和去中心化。这是一个存在了几十年的设计问题&#xff0c;其问题的…

使用SecoClient软件连接L2TP

secoclient软件是华为防火墙与友商设备进行微屁恩对接的一款软件,运行在windows下可以替代掉win系统自带的连接功能,因为win系统自带的连接功能总是不可用而且我照着网上查到的各种方法调试了很久都调不好,导致我一度怀疑是我的服务没搭建好,浪费了大把时间去研究其他搭建方案 …

鸿蒙 Window 环境的搭建

鸿蒙操作系统是国内自研的新一代的智能终端操作系统&#xff0c;支持多种终端设备部署&#xff0c;能够适配不同类别的硬件资源和功能需求。是一款面向万物互联的全场景分布式操作系统。 下载、安装与配置 DevEco Studio支持Windows系统和macOS系统 Windows系统配置华为官方推…

web网站的工作流程和开发模式

web网站的工作流程和开发模式 基于Java Script封装的高级技术&#xff1a;Vue、Element、Nginx(前端程序部署的服务器) 初识Web前端 Web标准

Cisco模拟器-企业网络部署

某企业园区网有&#xff1a;2个分厂&#xff08;分别是&#xff1a;零件分厂、总装分厂&#xff09;1个总厂网络中心 1个总厂会议室&#xff1b; &#xff08;1&#xff09;每个分厂有自己的路由器&#xff0c;均各有&#xff1a;1个楼宇分厂网络中心 每个楼宇均包含&#x…

数据库 范式

概念 一个低一级范式的关系模式通过模式分解可以转换成若干个高一级范式的关系集合&#xff0c;这种过程就叫规范化。 关系数据库中的关系是要满足一定要求的&#xff0c;满足不同程度要求的位不同范式。 部分依赖&完全依赖 定义&#xff1a;在关系 R(U) 中&#xff0c…

【CFP-专栏2】计算机类SCI优质期刊汇总(含IEEE/Top)

一、计算机区块链类SCI-IEEE 【期刊概况】IF:4.0-5.0, JCR2区&#xff0c;中科院2区&#xff1b; 【大类学科】计算机科学&#xff1b; 【检索情况】SCI在检&#xff1b; 【录用周期】3-5个月左右录用&#xff1b; 【截稿时间】12.31截稿&#xff1b; 【接收领域】区块链…

第3课 使用FFmpeg获取并播放音频流

本课对应源文件下载链接&#xff1a; https://download.csdn.net/download/XiBuQiuChong/88680079 FFmpeg作为一套庞大的音视频处理开源工具&#xff0c;其源码有太多值得研究的地方。但对于大多数初学者而言&#xff0c;如何快速利用相关的API写出自己想要的东西才是迫切需要…