C#实现ModbusTCP主站通讯(二)
- 摘要:本文描述的内容是在上一篇文章的基础上增加读写int和float的数据功能、字节序读写功能(ABCD、BADC、CDAB、DCBA)。
- 导航:mp.weixin.qq.com/s/Cq3HTUmK1_QhiXdUQBhnKw
- 作者:编程笔记in
前言
今天要说的是如何在上一篇文章《C#实现ModbusTCP通讯》案例的基础上添加新功能。
废话不多说,主要添加了int、float数据的字节序读取功能。
什么是字节序?就是字节排序,使用字面ABCD表示。
每个字母代表一个字节(A=最高位字节,D=最低位字节)。
如:大端序( 0x12345678 存储为 12 34 56 78),小端序()0x12345678 存储为 78 56 34 12)。
排序方式有如下几种:
ABCD [A, B, C, D] 大端序
DCBA [D, C, B, A] 小端序
BADC [B, A, D, C]
CDAB [C, D, A, B]
- 基本的ModbusTCP读取线圈和寄存器数据一般使用bool和ushort类型,只占用一个寄存器。
- 如果要返回int、float类型的数据需要占用 2 个寄存器,其实就是读写两个寄存器,再根据不同的字节序和数据类型转换即可。
运行环境
操作系统:Windows11
编程软件:Visual Studio 2022
Net版本:.Net Framework 4.8.0
一、预览
(一)界面、运行效果
初始化字节序是ABCD。
数据读取可以通过点击读取int、float按钮读取,需设置起始地址和长度读取。
数据的写入根据起始地址和发送内容写入。
如输入:12 点击写入按钮 (int或float)实现单个写入。输入[12,34,0,0,0]点击按钮实现写入多个数。

二、代码
(一)MainForm代码
添加按钮了读取写入int、float按钮,用于触发读取写入功能。增加字节序变更功能。
#region 写入按钮事件 private void btn_WriteInt_Click(object sender, EventArgs e) { try { int[] register = ParseArray<int>(rtbx_SendData.Text); if (register != null &®ister.Length == 1) { modbusTcp.WriteInt(modbusTcp.DataModel.ReadStartAddress, register[0]); MessageUpdate($"[{rtbx_SendData.Text}]", Color.Green, $"# 写入 Int >"); } else { modbusTcp.WriteIntArray(modbusTcp.DataModel.ReadStartAddress, register); StringBuilder appendInt = new StringBuilder(); for (int i = 0; i < register.Length; i++) { appendInt.Append(register[i] + " "); } MessageUpdate($"{appendInt.ToString()}", Color.Green, $"# 写入 Int >"); } } catch (Exception ex) { MessageUpdate($"{ex.Message}", Color.Red, $"# 写入数据 >"); } } private void btn_WriteFloat_Click(object sender, EventArgs e) { try { float[] register = ParseArray<float>(rtbx_SendData.Text); if (register != null && register.Length == 1) { modbusTcp.WriteFloat(modbusTcp.DataModel.ReadStartAddress, register[0]); MessageUpdate($"[{rtbx_SendData.Text}]", Color.Green, $"# 写入 Float >"); } else { modbusTcp.WriteFloatArray(modbusTcp.DataModel.ReadStartAddress, register); StringBuilder appendInt = new StringBuilder(); for (int i = 0; i < register.Length; i++) { appendInt.Append(register[i] + " "); } MessageUpdate($"{appendInt.ToString()}", Color.Green, $"# 写入 Float >"); } } catch (Exception ex) { MessageUpdate($"{ex.Message}", Color.Red, $"# 写入数据 >"); } } #endregion #region 读取按钮事件 private void btn_ReadInt_Click(object sender, EventArgs e) { int[] intArray = modbusTcp.ReadInt(modbusTcp.DataModel.ReadStartAddress, modbusTcp.DataModel.ReadDataLength); if (intArray == null) return; string tempArray = string.Empty; for (int i = 0; i < intArray.Length; i++) { tempArray += intArray[i]+" "; } MessageUpdate($"[{tempArray}]", Color.Green, $"# 读取 Int >"); } private void btn_ReadFloat_Click(object sender, EventArgs e) { float[] intArray = modbusTcp.ReadFloat(modbusTcp.DataModel.ReadStartAddress, modbusTcp.DataModel.ReadDataLength); if (intArray == null) return; string tempArray = string.Empty; for (int i = 0; i < intArray.Length; i++) { tempArray += intArray[i] + " "; } MessageUpdate($"[{tempArray}]", Color.Green, $"# 读取 float >"); } #endregion
(二)读写功能
创建读写单个、多个int、float数据的方法,以及一些数据转换方法。
#region 新增读写功能 #region 写入 /// <summary> /// 写整数 /// </summary> public byte[] WriteInt(ushort startAddress, int data, byte unitId = 1) { if (_socket == null) return null; _socket.ReceiveTimeout = ReceiveTimeout; try { // 拆分int byte[] byteArray = ConvertIntToBytes(data); // 发送请求 byte[] request = BuildMutiWriteRequest(0x10, startAddress, byteArray, 2, unitId); _socket.Send(request); // 请求报文 OnRequestMessage(new ModbusMessageEvents(request, true)); byte[] response = ResponseParse(1, 0x10); // 响应报文 OnResponseMessage(new ModbusMessageEvents(response, true)); return response; } catch (SocketException ex) when (ex.SocketErrorCode == SocketError.TimedOut) { throw new TimeoutException($"读取操作超时({ReceiveTimeout}ms)", ex); } } /// <summary> /// 写浮点 /// </summary> public byte[] WriteFloat(ushort startAddress, float data, byte unitId = 1) { if (_socket == null) return null; _socket.ReceiveTimeout = ReceiveTimeout; try { // 拆分float byte[] byteArray = ConvertFloatToBytes(data); // 发送请求 byte[] request = BuildMutiWriteRequest(0x10, startAddress, byteArray, 2, unitId); _socket.Send(request); // 请求报文 OnRequestMessage(new ModbusMessageEvents(request, true)); byte[] response = ResponseParse(1, 0x10); // 响应报文 OnResponseMessage(new ModbusMessageEvents(response, true)); return response; } catch (SocketException ex) when (ex.SocketErrorCode == SocketError.TimedOut) { throw new TimeoutException($"读取操作超时({ReceiveTimeout}ms)", ex); } } #endregion #region 读取 /// <summary> /// 读取保持寄存器(功能码0x03 ) /// </summary> public int[] ReadInt(ushort startAddress, int numberOfPoints, byte unitId = 1) { if (_socket == null) return null; _socket.ReceiveTimeout = ReceiveTimeout; try { //读取整数*2个数寄存器 ushort number = (ushort)(2 * numberOfPoints); //发送请求 byte[] request = BuildReadRequest(0x03, startAddress, number, unitId); _socket.Send(request); //请求报文 OnRequestMessage(new ModbusMessageEvents(request)); //响应接收 byte[] response = ResponseParse(number, 0x03); //响应报文 OnResponseMessage(new ModbusMessageEvents(response)); //验证响应 ValidateResponse(0x03, response, request); //获取读取数据 int length = 4 * numberOfPoints; byte[] byteData = new byte[length]; byte[] data = new byte[4]; Array.Copy(response,9,byteData, 0, length); int[] intArray = new int[numberOfPoints]; for (int i = 0; i < numberOfPoints; i++) { Array.Copy(byteData,i*4, data,0,4); intArray[i] = BitConverter.ToInt32(FormatOrder(data), 0); } return intArray; } catch (SocketException ex) when (ex.SocketErrorCode == SocketError.TimedOut) { throw new TimeoutException($"读取操作超时({ReceiveTimeout}ms)", ex); } } /// <summary> /// 读取保持寄存器(功能码0x03 ) /// </summary> public float[] ReadFloat(ushort startAddress, int numberOfPoints, byte unitId = 1) { if (_socket == null) return null; _socket.ReceiveTimeout = ReceiveTimeout; try { //读取整数*2个数寄存器 ushort number = (ushort)(2 * numberOfPoints); //1、发送请求 byte[] request = BuildReadRequest(0x03, startAddress, number, unitId); _socket.Send(request); //2、请求报文 OnRequestMessage(new ModbusMessageEvents(request)); //3、响应接收 byte[] response = ResponseParse(number, 0x03); //4、响应报文 OnResponseMessage(new ModbusMessageEvents(response)); //5、验证响应 ValidateResponse(0x03, response, request); //6、计算数据长度、获取读取数据 int length = 4 * numberOfPoints; byte[] byteData = new byte[length]; Array.Copy(response, 9, byteData, 0, length); //7、遍历转换结果 byte[] data = new byte[4]; float[] intArray = new float[numberOfPoints]; for (int i = 0; i < numberOfPoints; i++) { Array.Copy(byteData, i * 4, data, 0, 4); intArray[i] = BitConverter.ToSingle(FormatOrder(data), 0); } //8、返回结果 return intArray; } catch (SocketException ex) when (ex.SocketErrorCode == SocketError.TimedOut) { throw new TimeoutException($"读取操作超时({ReceiveTimeout}ms)", ex); } } #endregion
结语
- 本文例展示了使用Scoket实现ModbusTCP主站通讯的一些说明及部分代码,实现通讯中常用的两种数据类型int、floa。
- 完整案例文末连接下载。
- 如果感兴趣的话可下载案例体验、测试程序bug,性能优化,也可以自行修改封装实现更多功能,案例代码仅供参考…
![]()
- 项目地址:gitee.com/incodenotes/csharp-modbus
- 如果你觉得这篇文章对你有帮助,不妨点个赞再走呗!
- 如有其他疑问,欢迎评论区留言讨论!
- 也可以加入微信公众号 【编程笔记in】 ,一起交流学习!
本文来自博客园,作者:编程笔记in,转载请注明原文链接:https://www.cnblogs.com/whojie/articles/18864078

浙公网安备 33010602011771号