电表通讯协议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.mzph.cn/news/586426.shtml

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

相关文章

Strateg策略模式(组件协作)

策略模式&#xff08;组件协作&#xff09; 链接&#xff1a;策略模式实例代码 注解 目的 正常情况下&#xff0c;一个类/对象中会包含其所有可能会使用的内外方法&#xff0c;但是一般情况下&#xff0c;这些常使用的类都是由不同的父类继承、组合得来的&#xff0c;来实现…

跨境电商迎来综合竞争力比拼时代 五大趋势解读跨境2024

过去几年&#xff0c;跨境电商成为外贸出口增长的一大亮点&#xff0c;随着年底国务院办公厅《关于加快内外贸一体化发展的若干措施》的发布&#xff0c;跨境电商在促进经济发展、助力内外贸一体化发展方面的价值更加凸显。 这是跨境电商变化最快的时代&#xff0c;也是跨境电…

Getway介绍和使用

Getway 入门简介 网关搭建步骤&#xff1a; 创建项目&#xff0c;引入nacos服务发现和gateway依赖 配置application.yml&#xff0c;包括服务基本信息、nacos地址、路由 路由配置包括&#xff1a; 路由id&#xff1a;路由的唯一标示 路由目标&#xff08;uri&#xff09;…

常用环境部署(十二)——Redis搭建主从模式(一主一从)

一、主从服务器Redis安装 1、注意事项 主从服务器Redis尽量安装同一版本&#xff0c;避免兼容性造成的一些错误产生 2、Centos安装Redis 链接&#xff1a;​​​​​​常用环境部署(十)——MySQL主从同步数据搭建(一主一从)-CSDN博客 二、 主Redis配置 1、修改主Redis配置…

听说上海移动年终奖16个月!我承认我酸了!

* 你好&#xff0c;我是前端队长&#xff0c;在职场&#xff0c;玩副业&#xff0c;文末有福利! 今天&#xff0c;队长看到一篇帖子&#xff0c;有网友发帖说上海移动的年终奖发了16个月&#xff0c;我承认我酸了。 看到这里&#xff0c;我承认我也酸了。16个月是什么概念&…

【C/C++笔试练习】sort排序、STL容器、vector的特性、一级容器、迭代器失效、异常捕获、动态转换、统计每个月兔子的总数、字符串通配符

文章目录 C/C笔试练习选择部分&#xff08;1&#xff09;sort是不稳定排序&#xff08;2&#xff09;存放即有序的STL容器&#xff08;3&#xff09;连续储存的STL容器&#xff08;4&#xff09;vector的特性&#xff08;5&#xff09;一级容器&#xff08;6&#xff09;unorde…

PLC-IoT 网关开发札记(1):存档和分发 Android App

开篇记 PLC-IoT 网关是作者开发的产品&#xff0c;根据客户需求&#xff0c;立项开发手机 App&#xff0c;为用户提供一种方便、直观、友好的设备操控方式。网关运行的是嵌入式 Linux 操作系统&#xff0c;计划通过某一种通信协议&#xff08;例如 HTTP&#xff0c;MQTT或者 T…

大数定律中心极限定理

1.切比雪夫不等式 切比雪夫不等式可以对随机变量偏离期望值的概率做出估计&#xff0c;这是大数定律的推理基础。以下介绍一个对切比雪夫不等式的直观证明。 1.1 示性函数 对于随机事件A&#xff0c;我们引入一个示性函数 I A { 1 , A发生 0 , A不发生 I_A\begin{cases} 1&…

Chrome浏览器进程工作原理和机制

Chrome浏览器进程工作原理和机制 Chrome架构&#xff1a;一个页面四个进程进程和线程单进程浏览器多进程浏览器多进程浏览器解决的问题Chrome的进程模式 TCP协议&#xff1a;如何保证页面文件被完整送达浏览器IP&#xff1a;把数据包送达目的主机UDP&#xff1a;把数据包送达应…

vivado CDC约束-约束总线偏移

CDC限制 关于CDC约束 时钟域交叉&#xff08;CDC&#xff09;约束适用于具有不同启动和捕获时钟。根据发布和捕获时钟关系以及在CDC路径上设置的定时异常。例如同步时钟之间但被错误路径约束覆盖的CDC路径不定时&#xff0c;并且因此被视为异步CDN。异步CDC路径可以是安全的&…

vue 导出 HTML 结构为 Word 文档(.docx)-支持表格、css样式、图片

在 Web 开发中&#xff0c;有时我们希望用户能够将网页上的 HTML 内容保存为 Word 文档&#xff0c;以便更方便地分享和打印。本文将介绍如何使用 html-docx-js 和 file-saver 这两个 JavaScript 库&#xff0c;实现将 HTML 结构导出为 Word 文档的功能。 工具简介 1. html-d…

lv13 内核模块参数和依赖

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

进阶学习——Linux系统磁盘管理与文件系统

目录 一、磁盘 1.认识磁盘 2.分区 2.1MBR&#xff08;Master Boot Record&#xff09;——主引导记录 2.2GPT分区 2.3磁盘分区结构 3.文件系统 3.1文件系统组成 3.1.1XFS ext4 3.1.2swap 3.1.3FAT16、FAT32 3.1.4NTFS&#xff08;xfs&#xff09; 3.1.5EXT4 3…

[Angular] 笔记 9:list/detail 页面以及@Output

1. Output input 好比重力&#xff0c;向下传递数据&#xff0c;list 传给 detail&#xff0c;smart 组件传给 dumb 组件&#xff0c;父组件传给子组件。input 顾名思义&#xff0c;输入数据给组件。 output 与之相反&#xff0c;好比火箭&#xff0c;向上传递数据或事件。ou…

云计算IaaS、PaaS和SaaS之

提供的服务来比较如下两图 示例图 示例图

整数规划-割平面法

整数规划-割平面法 割平面法思想Gomorys割平面法原理实例 谨以此博客作为学习期间的记录。 割平面法思想 在之前&#xff0c;梳理了分支定界法的流程:分支定界法 除了分支定界法&#xff0c;割平面法也是求解整数规划的另一个利器。 我们已经知道&#xff0c;线性规划的可行域…

vscode软件安装步骤

目录 一、下载软件安装包 二、运行安装包后 一、下载软件安装包 打开vscode官方网址&#xff0c;找到下载界面 链接如下&#xff1a;Download Visual Studio Code - Mac, Linux, Windows 我是windows电脑&#xff0c;各位小伙伴自己选择合适的版本&#xff0c;点击下载按钮…

基于虚拟机ubuntu的linux和shell脚本的学习,以及SSH远程登陆实战

简介 特点 是一款操作系统,跟windows,macos一样,有下面的特点 简单和高效,一切皆文件,所有配置都通过修改文件解决,不需要繁琐的权限和设置 权限高,把所有细节都交给用户,可完全自定义 安全,所有程序只有自己执行才会启动 分类 1、debian系主要有Debian&#xff0c;Ubun…

ESP32入门六(读取引脚的模拟信号[3]:信号出现误差的原因[硬件篇])

在之前的文章中&#xff0c;我们介绍了ESP32在读取模拟信号时出现的误差的软件方面原因&#xff0c;在这一篇中&#xff0c;将会介绍并测试由于硬件或其它方面导致数据出现误差的原因。 一、厂商原因 首先&#xff0c;我们需要知道&#xff0c;在每块EPS32中&#xff0c;在出…

海凌科HLK-V2语音识别模块更新词条

简介 HLK-V20 是海凌科的离线语音识别模块, 中英文不同时支持, 只支持中文/英文, 具体识别看每次的SDK更新设置;资料下载 可以在微信公众包搜索海凌科或HI-LINK, 下载资料 感知模块->HLK-V20 模块限制 中英文被限制, 需要根据你在官网设置的SDK信息进行确定;可以仅设置3…