使用NModBus时WriteMultipleRegister抛异常及WriteSingleRegister写入异常求助
问题分析与解决方案
一、WriteMultipleRegistersAsync 抛出 IOException 但数据写入成功
原因
每次点击按钮都新建 ModbusController 和 TcpClient,写完后未正确释放连接资源,导致设备主动断开连接时,程序尝试读取响应报文失败;或者频繁创建连接引发设备端的连接处理异常。
解决方案
复用连接而非每次新建
初始化ModbusController一次(比如在窗体加载时),而非每次点击按钮都创建新实例:private ModbusController controller; private void Form_Load(object sender, EventArgs e) { controller = new ModbusController("172.16.100.106", 502); // 设置超时时间,避免因设备响应慢导致异常 controller.ModbusMaster.Transport.ReadTimeout = 5000; } private async Task<bool> WriteToRegister() { ushort value = ushort.Parse(txtValue.Text); ushort address = (ushort)int.Parse(txtRegister.Text); ushort[] data = [value]; try { await controller.WriteData(address, data); return true; } catch (Exception ex) { MessageBox.Show(ex.ToString()); return false; } }同时在
ModbusController中暴露ModbusMaster属性,方便设置超时:public class ModbusController { private TcpClient tcpClient; public IModbusMaster ModbusMaster { get; private set; } public ModbusController(string ipAddress, int port) { tcpClient = new TcpClient(ipAddress, port); var factory = new ModbusFactory(); ModbusMaster = factory.CreateMaster(tcpClient); } public async Task WriteData(ushort startAddress, ushort[] data) { await ModbusMaster.WriteMultipleRegistersAsync(1, startAddress, data); } // 窗体关闭时释放资源 public void Dispose() { ModbusMaster?.Dispose(); tcpClient?.Close(); tcpClient?.Dispose(); } }记得在窗体关闭时调用
controller.Dispose()释放资源。使用
using确保资源自动释放
如果必须每次新建连接,用using包裹ModbusController,确保连接用完即关闭:try { using (var tempController = new ModbusController("172.16.100.106", 502)) { tempController.ModbusMaster.Transport.ReadTimeout = 5000; await tempController.WriteData(address, data); } return true; } catch (Exception ex) { MessageBox.Show(ex.ToString()); return false; }
二、WriteSingleRegister 每次仅修改一位数据
原因
这不是代码问题,而是设备寄存器的特性:
- 该寄存器可能被设计为位操作寄存器,
WriteSingleRegister(功能码0x06)的行为是按位设置而非覆盖整个寄存器值; - 设备对单寄存器写入的逻辑是按位累加(比如每次写入的值与当前寄存器值做按位或操作)。
解决方案
- 查阅设备手册:确认该寄存器的写入方式,是否支持覆盖式单寄存器写入,或者是否必须使用
WriteMultipleRegisters(功能码0x10)来完整写入寄存器值; - 继续使用
WriteMultipleRegisters:既然该方式能一次性写入目标值,只需解决上述的IOException问题即可; - 若必须用单寄存器写入:先读取当前寄存器值,计算出需要修改的位掩码,再写入对应值(但这不符合你一次性写入目标值的需求,仅作为备选)。
内容的提问来源于stack exchange,提问作者jason
相关产品推荐
相关产品推荐

