No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.

298 líneas
11 KiB

  1. using ModbusDemo.Uitls;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.IO.Ports;
  5. using System.Linq;
  6. using System.Text;
  7. using System.Threading.Tasks;
  8. using System.Windows;
  9. namespace ModbusDemo.Device
  10. {
  11. public class ModbusRTU : IModbusRTU
  12. {
  13. private SerialPort _serialPort;
  14. public ModbusRTU(SerialPort serialPort)
  15. {
  16. _serialPort = serialPort;
  17. }
  18. /// <summary>
  19. /// 用来发送读写线圈的指令
  20. /// </summary>
  21. /// <param name="slaveAddress"></param>
  22. /// <param name="startAddress"></param>
  23. /// <param name="numberOfPoints"></param>
  24. /// <returns></returns>
  25. public bool[] ReadCoil(byte slaveAddress, ushort startAddress, ushort numberOfPoints)
  26. {
  27. if (!_serialPort.IsOpen)
  28. {
  29. MessageBox.Show("串口没有链接,请先链接");
  30. return null;
  31. }
  32. List<byte> sendByteList = new List<byte>();
  33. //设置从站地址
  34. sendByteList.Add(slaveAddress);
  35. //设置功能码
  36. sendByteList.Add(0x01);
  37. //设置起始地址的高位和低位
  38. sendByteList.Add(BitConverter.GetBytes(startAddress)[1]);
  39. sendByteList.Add(BitConverter.GetBytes(startAddress)[0]);
  40. //设置读取几个线圈的高位和低位
  41. sendByteList.Add(BitConverter.GetBytes(numberOfPoints)[1]);
  42. sendByteList.Add(BitConverter.GetBytes(numberOfPoints)[0]);
  43. //获取CRC校验码
  44. byte[] getCRC = sendByteList.ToArray();
  45. byte[] resultCRC = CRCUitl.CalculateCRC(getCRC, getCRC.Length);
  46. sendByteList.Add(resultCRC[0]);
  47. sendByteList.Add(resultCRC[1]);
  48. byte[] sendByte = sendByteList.ToArray();
  49. if (_serialPort.IsOpen)
  50. {
  51. // 清空接收缓冲区(避免残留数据干扰)
  52. _serialPort.DiscardInBuffer();
  53. _serialPort.Write(sendByte, 0, sendByte.Length);
  54. Thread.Sleep(300);
  55. byte[] response = new byte[_serialPort.BytesToRead];
  56. _serialPort.Read(response, 0, _serialPort.BytesToRead);
  57. return ParseCoilresponse(response, numberOfPoints);
  58. }
  59. return null;
  60. }
  61. public bool[] ParseCoilresponse(byte[] response, ushort numberOfPoints)
  62. {
  63. //判断发送回来的数据是否正确
  64. CheckData.CheckResponse(response);
  65. if (!CRCUitl.ValidateCRC(response))
  66. {
  67. MessageBox.Show("0x14:CRC校验错误");
  68. return null;
  69. }
  70. List<byte> responseList = new List<byte>(response);
  71. //移除前两位
  72. responseList.RemoveRange(0, 3);
  73. //移除后两位校验码
  74. responseList.RemoveRange(responseList.Count - 2, 2);
  75. responseList.Reverse();
  76. //数组反转之后,转为二进制字符
  77. List<string> StringList = responseList.Select(m => Convert.ToString(m, 2)).ToList();
  78. var result = "";
  79. foreach (var item in StringList)
  80. {
  81. result += item.PadLeft(8, '0');
  82. }
  83. //先将字符串转为数组,再反转,再转为字节数组
  84. char[] chars = result.ToArray().Reverse<char>().ToArray();
  85. bool[] ultimately = new bool[numberOfPoints];
  86. for (int i = 0; i < numberOfPoints; i++)
  87. {
  88. if (chars[i] == '1')
  89. {
  90. ultimately[i] = true;
  91. }
  92. else
  93. {
  94. ultimately[i] = false;
  95. }
  96. }
  97. return ultimately;
  98. }
  99. /// <summary>
  100. /// 写入多个线圈操作
  101. /// </summary>
  102. /// <param name="slaveAddress">从站地址</param>
  103. /// <param name="startAddress">起始地址</param>
  104. /// <param name="data">写入的数据</param>
  105. public void WriteCoil(byte slaveAddress, ushort startAddress, bool[] data)
  106. {
  107. List<byte> sendByteList = new List<byte>();
  108. //设置从站地址
  109. sendByteList.Add(slaveAddress);
  110. //设置功能码
  111. sendByteList.Add(0x0F);
  112. //设置起始地址的高位和低位
  113. sendByteList.Add(BitConverter.GetBytes(startAddress)[1]);
  114. sendByteList.Add(BitConverter.GetBytes(startAddress)[0]);
  115. //设置写入几个线圈
  116. sendByteList.Add(BitConverter.GetBytes(data.Length)[1]);
  117. sendByteList.Add(BitConverter.GetBytes(data.Length)[0]);
  118. // 计算字节数
  119. int byteCount = (data.Length + 7) / 8;
  120. sendByteList.Add((byte)byteCount);
  121. // 初始化字节数组
  122. byte[] coilBytes = new byte[byteCount];
  123. // 把bool数组转换为字节
  124. for (int i = 0; i < data.Length; i++)
  125. {
  126. int byteIndex = i / 8;
  127. int bitIndex = i % 8;
  128. if (data[i])
  129. {
  130. coilBytes[byteIndex] |= (byte)(1 << bitIndex);
  131. }
  132. }
  133. sendByteList.AddRange(coilBytes);
  134. //获取CRC校验码
  135. byte[] getCRC = sendByteList.ToArray();
  136. byte[] resultCRC = CRCUitl.CalculateCRC(getCRC, getCRC.Length);
  137. sendByteList.Add(resultCRC[0]);
  138. sendByteList.Add(resultCRC[1]);
  139. byte[] sendByte = sendByteList.ToArray();
  140. if (_serialPort.IsOpen)
  141. {
  142. // 清空接收缓冲区(避免残留数据干扰)
  143. _serialPort.DiscardInBuffer();
  144. _serialPort.Write(sendByte, 0, sendByte.Length);
  145. Thread.Sleep(300);
  146. byte[] response = new byte[_serialPort.BytesToRead];
  147. _serialPort.Read(response, 0, _serialPort.BytesToRead);
  148. //判断发送回来的数据是否正确
  149. CheckData.CheckResponse(response);
  150. if (!CRCUitl.ValidateCRC(response))
  151. {
  152. MessageBox.Show("0x14:CRC校验错误");
  153. }
  154. }
  155. }
  156. public ushort[] ReadRegisters(byte slaveAddress, ushort startAddress, ushort numberOfPoints)
  157. {
  158. if (!_serialPort.IsOpen)
  159. {
  160. MessageBox.Show("串口没有链接,请先链接");
  161. return null;
  162. }
  163. List<byte> sendByteList = new List<byte>();
  164. //设置从站地址
  165. sendByteList.Add(slaveAddress);
  166. //设置功能码
  167. sendByteList.Add(0x03);
  168. //设置起始地址的高位和低位
  169. sendByteList.Add(BitConverter.GetBytes(startAddress)[1]);
  170. sendByteList.Add(BitConverter.GetBytes(startAddress)[0]);
  171. //设置读取几个线圈的高位和低位
  172. sendByteList.Add(BitConverter.GetBytes(numberOfPoints)[1]);
  173. sendByteList.Add(BitConverter.GetBytes(numberOfPoints)[0]);
  174. //获取CRC校验码
  175. byte[] getCRC = sendByteList.ToArray();
  176. byte[] resultCRC = CRCUitl.CalculateCRC(getCRC, getCRC.Length);
  177. sendByteList.Add(resultCRC[0]);
  178. sendByteList.Add(resultCRC[1]);
  179. byte[] sendByte = sendByteList.ToArray();
  180. if (_serialPort.IsOpen)
  181. {
  182. // 清空接收缓冲区(避免残留数据干扰)
  183. _serialPort.DiscardInBuffer();
  184. _serialPort.Write(sendByte, 0, sendByte.Length);
  185. Thread.Sleep(300);
  186. byte[] response = new byte[_serialPort.BytesToRead];
  187. _serialPort.Read(response, 0, _serialPort.BytesToRead);
  188. return ParseRegistersresponse(response, numberOfPoints);
  189. }
  190. return null;
  191. }
  192. public ushort[] ParseRegistersresponse(byte[] response, ushort numberOfPoints)
  193. {
  194. //判断发送回来的数据是否正确
  195. CheckData.CheckResponse(response);
  196. if (!CRCUitl.ValidateCRC(response))
  197. {
  198. MessageBox.Show("0x14:CRC校验错误");
  199. return null;
  200. }
  201. ushort[] registers = new ushort[numberOfPoints];
  202. for (int i = 0; i < numberOfPoints; i++)
  203. {
  204. int pos = 3 + i * 2;
  205. // 大端序转换 (高位在前)
  206. registers[i] = (ushort)((response[pos] << 8) | response[pos + 1]);
  207. }
  208. return registers;
  209. }
  210. public void WriteRegisters(byte slaveAddress, ushort startAddress, ushort[] data)
  211. {
  212. List<byte> sendByteList = new List<byte>();
  213. //设置从站地址
  214. sendByteList.Add(slaveAddress);
  215. //设置功能码
  216. sendByteList.Add(0x10);
  217. //设置起始地址的高位和低位
  218. sendByteList.Add(BitConverter.GetBytes(startAddress)[1]);
  219. sendByteList.Add(BitConverter.GetBytes(startAddress)[0]);
  220. //设置写入几个线圈
  221. sendByteList.Add(BitConverter.GetBytes(data.Length)[1]);
  222. sendByteList.Add(BitConverter.GetBytes(data.Length)[0]);
  223. // 计算字节数
  224. int byteCount = (data.Length + 7) / 8;
  225. sendByteList.Add((byte)byteCount);
  226. foreach (ushort value in data)
  227. {
  228. byte[] valueBytes = BitConverter.GetBytes(value);
  229. // 大端序:高字节在前,低字节在后
  230. sendByteList.Add(valueBytes[1]);
  231. sendByteList.Add(valueBytes[0]);
  232. }
  233. //获取CRC校验码
  234. byte[] getCRC = sendByteList.ToArray();
  235. byte[] resultCRC = CRCUitl.CalculateCRC(getCRC, getCRC.Length);
  236. sendByteList.Add(resultCRC[0]);
  237. sendByteList.Add(resultCRC[1]);
  238. byte[] sendByte = sendByteList.ToArray();
  239. if (_serialPort.IsOpen)
  240. {
  241. // 清空接收缓冲区(避免残留数据干扰)
  242. _serialPort.DiscardInBuffer();
  243. _serialPort.Write(sendByte, 0, sendByte.Length);
  244. Thread.Sleep(300);
  245. byte[] response = new byte[_serialPort.BytesToRead];
  246. _serialPort.Read(response, 0, _serialPort.BytesToRead);
  247. //判断发送回来的数据是否正确
  248. CheckData.CheckResponse(response);
  249. if (!CRCUitl.ValidateCRC(response))
  250. {
  251. MessageBox.Show("0x14:CRC校验错误");
  252. }
  253. }
  254. }
  255. }
  256. }