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.

452 line
16 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. }
  218. }
  219. public ushort[] ReadRegisters(byte slaveAddress, ushort startAddress, ushort numberOfPoints)
  220. {
  221. ushort[] resultValue = null;
  222. if (!_serialPort.IsOpen)
  223. {
  224. MessageBox.Show("串口没有链接,请先链接");
  225. return resultValue;
  226. }
  227. List<byte> sendByteList = new List<byte>();
  228. //设置从站地址
  229. sendByteList.Add(slaveAddress);
  230. //设置功能码
  231. sendByteList.Add(0x03);
  232. //设置起始地址的高位和低位
  233. sendByteList.Add(BitConverter.GetBytes(startAddress)[1]);
  234. sendByteList.Add(BitConverter.GetBytes(startAddress)[0]);
  235. //设置读取几个线圈的高位和低位
  236. sendByteList.Add(BitConverter.GetBytes(numberOfPoints)[1]);
  237. sendByteList.Add(BitConverter.GetBytes(numberOfPoints)[0]);
  238. //获取CRC校验码
  239. byte[] getCRC = sendByteList.ToArray();
  240. byte[] resultCRC = CRCUitl.CalculateCRC(getCRC, getCRC.Length);
  241. sendByteList.Add(resultCRC[0]);
  242. sendByteList.Add(resultCRC[1]);
  243. byte[] sendByte = sendByteList.ToArray();
  244. int maxRetries = 3; // 最大重试次数
  245. int attempt = 0;
  246. while (attempt < maxRetries)
  247. {
  248. try
  249. {
  250. // 清空接收缓冲区(避免残留数据干扰)
  251. _serialPort.DiscardInBuffer();
  252. _serialPort.Write(sendByte, 0, sendByte.Length);
  253. Thread.Sleep(300);
  254. byte[] response = new byte[_serialPort.BytesToRead];
  255. _serialPort.Read(response, 0, _serialPort.BytesToRead);
  256. if (response[0] == 0x00)
  257. {
  258. continue;
  259. }
  260. resultValue = ParseRegistersresponse(sendByte, response, numberOfPoints);
  261. return resultValue;
  262. }
  263. catch (Exception)
  264. {
  265. attempt++;
  266. }
  267. }
  268. //将操作失败的指令,插入数据库
  269. string RequestStr = ByteArrayToString(sendByte);
  270. ModbusLog m = new ModbusLog();
  271. m.OperationType = "读寄存器";
  272. m.ResponseData = "";
  273. m.RequestData = RequestStr;
  274. _modbusDbContext.Add(m);
  275. _modbusDbContext.SaveChanges();
  276. return resultValue;
  277. }
  278. public ushort[] ParseRegistersresponse(byte[] sendByte, byte[] response, ushort numberOfPoints)
  279. {
  280. //将操作的指令,插入数据库
  281. string RequestStr = ByteArrayToString(sendByte);
  282. string responseStr = ByteArrayToString(response);
  283. ModbusLog m = new ModbusLog();
  284. m.OperationType = "读寄存器";
  285. m.ResponseData = responseStr;
  286. m.RequestData = responseStr;
  287. _modbusDbContext.Add(m);
  288. _modbusDbContext.SaveChanges();
  289. //判断发送回来的数据是否正确
  290. if (CheckData.CheckResponse(response))
  291. {
  292. ushort[] registers = new ushort[numberOfPoints];
  293. for (int i = 0; i < numberOfPoints; i++)
  294. {
  295. int pos = 3 + i * 2;
  296. // 大端序转换 (高位在前)
  297. registers[i] = (ushort)((response[pos] << 8) | response[pos + 1]);
  298. }
  299. return registers;
  300. }
  301. else
  302. {
  303. return null;
  304. }
  305. }
  306. public void WriteRegisters(byte slaveAddress, ushort startAddress, ushort[] data)
  307. {
  308. if (!_serialPort.IsOpen)
  309. {
  310. MessageBox.Show("串口没有链接,请先链接");
  311. return;
  312. }
  313. List<byte> sendByteList = new List<byte>();
  314. //设置从站地址
  315. sendByteList.Add(slaveAddress);
  316. //设置功能码
  317. sendByteList.Add(0x10);
  318. //设置起始地址的高位和低位
  319. sendByteList.Add(BitConverter.GetBytes(startAddress)[1]);
  320. sendByteList.Add(BitConverter.GetBytes(startAddress)[0]);
  321. //设置写入几个线圈
  322. sendByteList.Add(BitConverter.GetBytes(data.Length)[1]);
  323. sendByteList.Add(BitConverter.GetBytes(data.Length)[0]);
  324. // 计算字节数
  325. int byteCount = (data.Length + 7) / 8;
  326. sendByteList.Add((byte)byteCount);
  327. foreach (ushort value in data)
  328. {
  329. byte[] valueBytes = BitConverter.GetBytes(value);
  330. // 大端序:高字节在前,低字节在后
  331. sendByteList.Add(valueBytes[1]);
  332. sendByteList.Add(valueBytes[0]);
  333. }
  334. //获取CRC校验码
  335. byte[] getCRC = sendByteList.ToArray();
  336. byte[] resultCRC = CRCUitl.CalculateCRC(getCRC, getCRC.Length);
  337. sendByteList.Add(resultCRC[0]);
  338. sendByteList.Add(resultCRC[1]);
  339. byte[] sendByte = sendByteList.ToArray();
  340. int maxRetries = 3; // 最大重试次数
  341. int attempt = 0;
  342. byte[] response = null;
  343. while (attempt< maxRetries)
  344. {
  345. try
  346. {
  347. // 清空接收缓冲区(避免残留数据干扰)
  348. _serialPort.DiscardInBuffer();
  349. _serialPort.Write(sendByte, 0, sendByte.Length);
  350. Thread.Sleep(1000);
  351. response = new byte[_serialPort.BytesToRead];
  352. _serialPort.Read(response, 0, _serialPort.BytesToRead);
  353. if (response[0] != 0x00)
  354. {
  355. break;
  356. }
  357. }
  358. catch (Exception)
  359. {
  360. attempt++;
  361. }
  362. }
  363. //将操作的指令,插入数据库
  364. string RequestStr = ByteArrayToString(sendByte);
  365. string responseStr = ByteArrayToString(response);
  366. ModbusLog m = new ModbusLog();
  367. m.OperationType = "写寄存器";
  368. m.ResponseData = responseStr;
  369. m.RequestData = responseStr;
  370. _modbusDbContext.Add(m);
  371. _modbusDbContext.SaveChanges();
  372. //判断发送回来的数据是否正确
  373. CheckData.CheckResponse(response);
  374. }
  375. static string ByteArrayToString(byte[] byteArray)
  376. {
  377. if (byteArray == null || byteArray.Length == 0)
  378. return string.Empty;
  379. System.Text.StringBuilder sb = new System.Text.StringBuilder();
  380. // 处理第一个元素(无前导空格)
  381. sb.Append(byteArray[0].ToString("X2"));
  382. // 处理后续元素(每个前面加空格)
  383. for (int i = 1; i < byteArray.Length; i++)
  384. {
  385. sb.Append(' ');
  386. sb.Append(byteArray[i].ToString("X2"));
  387. }
  388. return sb.ToString();
  389. }
  390. }
  391. }