Non puoi selezionare più di 25 argomenti Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.

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