工控 modbusTCP 报文

public class ModBusTCP { public int ushorts2int(ushort[] res) { int high = res[0]; int low = res[1]; int value = (high << 16) + low; return value; } public ushort[] int2ushorts(int res) { ushort ust1 = (ushort)(res >> 16); ushort ust2 = (ushort)res; return new ushort[] { ust1, ust2 }; } //声明一个Socket对象 private Socket tcpClient; //单元标识符 public byte SlaveId { get; set; } = 0x01; /// <summary> /// 建立连接 /// </summary> /// <param name="ip">IP地址 </param> /// <param name="port">端口号</param> /// <returns></returns> public bool Connect(string ip, int port) { //实例化Socket对象 tcpClient =new Socket(AddressFamily.InterNetwork,SocketType.Stream, ProtocolType.Tcp); try { //建立连接 三次握手 tcpClient.Connect(IPAddress.Parse(ip), port); return true; } catch (Exception) { return false; } } /// <summary> /// 断开连接 /// </summary> public void DisConnect() { // 4次握手 tcpClient?.Close(); } /// <summary> /// 读取多个数据 /// </summary> /// <param name="startAdr"></param> /// <param name="length"></param> /// <returns></returns> public byte[] ReadOutputRegisters(ushort startAdr, ushort length) { #region 1 拼接报文 List<byte> SendCommand = new List<byte>(); //ModBusTCP 报文 格式 MBAP + 功能码+ 数据部分 // MBAP 事务标识符(2字节) 协议标识符(2字节) 长度(2字节) 单元标识符(1字节) // 功能码 (1字节) // 数据部分 起始寄存器地址(2字节) 寄存器长度(2字节) //=======================报文拼接=============================== //事务标识符 设置固定 0x00 0x00 // 等价 SendCommand.Add(0x00); SendCommand.Add(0x00); SendCommand.AddRange(new byte[] { 0x00, 0x00 }); // 协议标识符 设置固定 0x00 0x00 SendCommand.AddRange(new byte[] { 0x00, 0x00 }); // 长度 SendCommand.AddRange(new byte[] {0x00,0x06}); // 单元标识符 1字节 SendCommand.Add(SlaveId); //功能码 固定 0x03 SendCommand.Add(0x03); //数据部分 起始寄存器地址(2字节) #region ushort 转换字节数组 // % / //byte Hbyte = (byte)(startAdr / 255); //byte Lbyte = (byte)(startAdr % 255); //SendCommand.AddRange(new byte[] { Hbyte,Lbyte}); #endregion //BitConverter 大小端是颠倒 byte[] temp = BitConverter.GetBytes(startAdr); SendCommand.AddRange(new byte[] { temp[1], temp[0] }); //数据部分 寄存器长度(2字节) temp = BitConverter.GetBytes(length); SendCommand.AddRange(new byte[] { temp[1], temp[0] }); #endregion #region 2 发送报文 tcpClient.Send(SendCommand.ToArray()); #endregion #region 3 接收报文 Thread.Sleep(50); int count = tcpClient.Available; byte[] buffer= new byte[count]; tcpClient.Receive(buffer,buffer.Length,SocketFlags.None); #endregion #region 4 验证报文 //Tx 发送报文: 00 C9 00 00 00 06 01 03 00 00 00 02 //Rx 接收报文: 00 C9 00 00 00 07 01 03 04 01 4D 00 01 // 校验 00 07 01 03 04 这几位 // 00 C9 00 00 00 07 01 03 04 这9位是长度固定的, + 获取寄存器长度length*2 每个寄存器占2字节 if (buffer.Length == 9+length*2) { //两位字节转换整数 result_h 高位字节 result_1 地位字节 //result = result_h * 256 + result_1 // 00 07 // 3+length*2 3来源于01 03 04 固定的 if (buffer[4] * 256 + buffer[5]==3+length*2) { // 单元标识符 和 功能码 if (buffer[6] == SlaveId && buffer[7] == 0x03) { #region 5 解析报文 // 返回的数据字节数,就是 要读取寄存器*2 byte[] result = new byte[length*2]; Array.Copy(buffer,9,result,0,length*2); return result; #endregion } } } return null; #endregion } /// <summary> /// 写入多数据 /// </summary> /// <param name="startAdr">开始寄存器地址</param> /// <param name="length">寄存器长度</param> /// <param name="dates">具体数据</param> /// <returns></returns> public void WriteResisters(ushort startAdr, ushort length,ushort datecount, ushort[] dates) { #region 1 拼接报文 List<byte> SendCommand = new List<byte>(); // 事务标识符 2字节 固定 0x00 0x00 SendCommand.AddRange(new byte[] { 0x00, 0x00 }); // 协议标识符 2字节 固定 0x00 0x00 SendCommand.AddRange(new byte[] { 0x00, 0x00 }); // 长度 2字节 7: 单元标识符1字节+功能码1字节+ 开始寄存器地址2字节 寄存器长度2字节 + 字节长度1字节 ushort[] lens=int2ushorts(2*length+7); //byte[] start1 = BitConverter.GetBytes(lens[0]); byte[] len = BitConverter.GetBytes(lens[1]); if (BitConverter.IsLittleEndian) { Array.Reverse(len); } SendCommand.AddRange(len); // 单元标识符 1字节 固定 SlaveId SendCommand.Add(SlaveId); // 功能码 写入 多个保持寄存器 SendCommand.Add(0x10); // 数据部分 开始寄存器地址 寄存器长度 字节长度 具体数据 // 开始寄存器地址 byte[] start = BitConverter.GetBytes(startAdr); //数据值 List<byte> valueBytes = new List<byte>(); byte[] value; foreach (ushort b in dates) { value= BitConverter.GetBytes(b); if (BitConverter.IsLittleEndian) { Array.Reverse(value); } valueBytes.AddRange(value); } //根据计算机大小端存储方式进行高低字节转换 if (BitConverter.IsLittleEndian) { Array.Reverse(start); } SendCommand.AddRange(start); //寄存器长度 2字节 byte[] registLen = BitConverter.GetBytes(length); if (BitConverter.IsLittleEndian) { Array.Reverse(registLen); } SendCommand.AddRange(registLen); //字节长度 1字节 SendCommand.Add((byte)(datecount%255)); //SendCommand.AddRange(new byte[] { 0,1}); SendCommand.AddRange(valueBytes); #endregion #region 2 发送报文 Thread.Sleep(50); //tcpClient.Send(SendCommand.ToArray(),0,SendCommand.Count(),SocketFlags.Partial); //tcpClient.Send(SendCommand.ToArray()); //tcpClient.Send(new byte[] { 00, 01, 00, 00, 00, 08, 0xFF, 0x05, 00, 05 , 00 ,01, 01, 01 }); //Rx: 000002 - 00 01 00 00 00 09 01 0A 00 05 00 01 01 00 0F //事务标识符2字节 1F 97 //协议标识符2字节 00 00 //长度 2字节 00 11 //单元标识符 01 //功能码 10 //起始地址 2字节 00 04 //寄存器长度 2字节 00 02 读2个寄存器 //字节数 寄存器长度*2 04 //字节数组N*2字节 00 06 00 09 //1F 97 00 00 00 11 01 10 00 04 00 02 04 00 06 00 09 // 00 01 00 00 00 11 01 10 00 03 00 02 04 00 02 01 09 //tcpClient.Send(new byte[] { 00, 01, 00, 00, 00, 11, 01, 0x10, 00, 00 ,00 ,02 ,04,01,02, 30 ,39 }); //tcpClient.Send(new byte[] { 00, 01, 00, 00, 00, 11, 01, 0x10, 00, 00 ,00 ,02 ,04,01,02, 30 ,39 }); tcpClient.Send(SendCommand.ToArray()); #endregion } }
上一篇:面试算法-167-合并 K 个升序链表-解