You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

363 line
13 KiB

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