C#实现ModbusRTU Master从站串口调试工具源码 功能如下 -Visual Studio 2019开发 -.NET Framwork4.8框架 -支持功能码:03,04,05,06,15,16 -支持读写数据类型:ushort16,short16,int32,uint32,float32 -支持数据格式: Big-endian :ABCD, Little-endian :DCBA, Big-endian byte swap :BADC, Little-endian byte swap :CDAB -使用datagridview表格实时更新接收数据,一键回车修改数据,数据展现更合理,并且可以写注释 -源码已经写好注释,接收数据和接收报文都是采用事件接收,调用非常方便,界面带有记忆功能,实时显示收发报文

这个Modbus调试工具折腾了我两周时间,核心就解决一个问题——让串口通讯数据能像聊天记录一样直观展示。先说说数据解析这部分的门道,Modbus那堆字节序转换能把人绕晕,特别是处理32位浮点数的时候。

看看这段字节序处理的核心代码:

switch (format)
{
    case DataFormat.BigEndian:
        return new[] { bytes[0], bytes[1], bytes[2], bytes[3] };
    case DataFormat.LittleEndian:
        return new[] { bytes[3], bytes[2], bytes[1], bytes[0] };
    case DataFormat.BigEndianByteSwap:
        return new[] { bytes[1], bytes[0], bytes[3], bytes[2] };
    case DataFormat.LittleEndianByteSwap:
        return new[] { bytes[2], bytes[3], bytes[0], bytes[1] };
}

当时写这个switch的时候,差点把咖啡当眼药水滴了。比如BADC格式其实是把两个寄存器的字节各自交换,再拼起来。调试时发现个坑:当读取浮点数时,必须确保两个寄存器的读取顺序正确,否则解析出来的数值能差出几个数量级。

界面部分最满意的是这个DataGridView的数据绑定:

dataGridView.AutoGenerateColumns = false;
var addressCol = new DataGridViewTextBoxColumn { DataPropertyName = "Address"};
var valueCol = new DataGridViewTextBoxColumn { DataPropertyName = "Value"};
var commentCol = new DataGridViewTextBoxColumn { DataPropertyName = "Comment"};
dataGridView.Columns.AddRange(addressCol, valueCol, commentCol);

特意关闭了自动生成列功能,手动指定绑定关系。这样在用户敲回车修改数据时,能直接触发数据源的更新事件,不需要再手动同步数据。不过要吐槽DataGridView的验证机制,最后不得不用CellEndEdit事件来处理即时提交。

事件驱动的接收机制是项目的亮点,看看报文接收的委托定义:

public delegate void MessageReceivedHandler(byte[] rawData);
public event MessageReceivedHandler OnMessageReceived;

在串口数据到达事件里直接触发这个自定义事件,上层业务模块只需要注册监听,完全不用操心数据拼接。测试时发现个有意思的现象——当连续快速发送报文时,用事件队列的方式比直接处理更稳定,不容易丢包。

配置记忆功能用了最朴实的方案:

Properties.Settings.Default.WindowPosition = this.Location;
Properties.Settings.Default.ComPort = cboPorts.Text;
Properties.Settings.Default.Save();

每次窗口移动或关闭时自动保存设置,虽然简单但实用。有次用户反馈说重启软件后界面布局乱了,最后发现是他用了多显示器办公,把软件拖到副屏后关闭导致的坐标系问题。

源码里特意加了这种注释:

// 注意:西门子PLC的浮点数存储格式是CDAB
// 遇到解析异常时先检查字节序设置
ParseFloat(rawData, DataFormat.LittleEndianByteSwap);

毕竟Modbus设备厂家的实现五花八门,这种实战踩坑经验比文档管用多了。后来收到用户反馈说注释救了他半天调试时间,感觉这些备注加得值了。

收发报文的显示处理有个细节——超过3秒未更新时自动清空显示区域。这个设计起初被吐槽太激进,但实测发现长时间运行的报文堆积会严重影响性能。最后折中方案是保留200条最新报文,既保证可追溯性又不吃内存。

工具里藏了个彩蛋:连续发送错误指令五次会弹出"冷静下,喝杯茶?"的提示框。有用户以为这是Modbus协议自带的特殊功能,笑死。其实就是在错误计数器到达5时触发了个MessageBox.Show而已,算是个开发过程中的小趣味吧。

这个工具现在已经成为我们团队调试设备的标配,后来还扩展了TCP支持。最大的收获是意识到:好的工具不仅要功能齐全,更要让数据流动变得肉眼可见。下次考虑加个数据波形图功能,应该会更有意思。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐