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.

581 regels
20 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;
  9. using System.Threading.Tasks;
  10. using System.Windows;
  11. namespace ModbusDemo.Device
  12. {
  13. public class ModbusRTU : IModbusRTU
  14. {
  15. //串口
  16. private SerialPort _serialPort;
  17. //用于操作数据库
  18. private ModbusDbContext _modbusDbContext;
  19. //TODO,修改
  20. private SerialPortAdapter _portAdapter;
  21. public ModbusRTU(SerialPort serialPort, ModbusDbContext modbusDbContext)
  22. {
  23. _serialPort = serialPort;
  24. _modbusDbContext = modbusDbContext;
  25. _portAdapter = new SerialPortAdapter(serialPort);
  26. }
  27. /// <summary>
  28. /// 用来发送读写线圈的指令
  29. /// </summary>
  30. /// <param name="slaveAddress"></param>
  31. /// <param name="startAddress"></param>
  32. /// <param name="numberOfPoints"></param>
  33. /// <returns></returns>
  34. public bool[] ReadCoil(byte slaveAddress, ushort startAddress, ushort numberOfPoints)
  35. {
  36. bool[] resultValue = null;
  37. if (!_serialPort.IsOpen)
  38. {
  39. MessageBox.Show("串口没有链接,请先链接");
  40. return resultValue;
  41. }
  42. List<byte> sendByteList = new List<byte>();
  43. //设置从站地址
  44. sendByteList.Add(slaveAddress);
  45. //设置功能码
  46. sendByteList.Add(0x01);
  47. //设置起始地址的高位和低位
  48. sendByteList.Add(BitConverter.GetBytes(startAddress)[1]);
  49. sendByteList.Add(BitConverter.GetBytes(startAddress)[0]);
  50. //设置读取几个线圈的高位和低位
  51. sendByteList.Add(BitConverter.GetBytes(numberOfPoints)[1]);
  52. sendByteList.Add(BitConverter.GetBytes(numberOfPoints)[0]);
  53. //获取CRC校验码
  54. byte[] getCRC = sendByteList.ToArray();
  55. byte[] resultCRC = CRCUitl.CalculateCRC(getCRC, getCRC.Length);
  56. sendByteList.Add(resultCRC[0]);
  57. sendByteList.Add(resultCRC[1]);
  58. byte[] sendByte = sendByteList.ToArray();
  59. int maxRetries = 3; // 最大重试次数
  60. int attempt = 0;
  61. byte[] response = null;
  62. List<byte> responseData = new List<byte>();
  63. _serialPort.Write(sendByte, 0, sendByte.Length);
  64. while (attempt < maxRetries)
  65. {
  66. try
  67. {
  68. //使用时间去轮询查询数据
  69. DateTime startTime = DateTime.Now;
  70. while ((DateTime.Now - startTime).TotalMilliseconds < _serialPort.ReadTimeout)
  71. {
  72. int bytesToRead = _serialPort.BytesToRead;
  73. if (bytesToRead > 0)
  74. {
  75. byte[] buffer = new byte[bytesToRead];
  76. int bytesRead = _serialPort.Read(buffer, 0, bytesToRead);
  77. responseData.AddRange(buffer.Take(bytesRead));
  78. }
  79. //延迟20毫秒,节约cpu资源
  80. Task.Delay(20);
  81. }
  82. response = responseData.ToArray();
  83. if (response[0] == 0x00)
  84. {
  85. continue;
  86. }
  87. resultValue = ParseCoilresponse(sendByte, response, numberOfPoints);
  88. return resultValue;
  89. }
  90. catch (Exception)
  91. {
  92. attempt++;
  93. }
  94. }
  95. try
  96. {
  97. //将操作失败的指令,插入数据库
  98. string RequestStr = ByteArrayToString(sendByte);
  99. ModbusLog m = new ModbusLog();
  100. m.OperationType = "读线圈";
  101. m.ResponseData = "";
  102. m.RequestData = RequestStr;
  103. _modbusDbContext.Add(m);
  104. _modbusDbContext.SaveChanges();
  105. }
  106. catch (Exception)
  107. {
  108. MessageBox.Show("0x15:数据库插入错误");
  109. }
  110. return resultValue;
  111. }
  112. /// <summary>
  113. /// 用来解析返回的数据
  114. /// </summary>
  115. /// <param name="sendByte"></param>
  116. /// <param name="response"></param>
  117. /// <param name="numberOfPoints"></param>
  118. /// <returns></returns>
  119. public bool[] ParseCoilresponse(byte[] sendByte, byte[] response, ushort numberOfPoints)
  120. {
  121. try
  122. {
  123. //将操作的指令,插入数据库
  124. string RequestStr = ByteArrayToString(sendByte);
  125. string responseStr = ByteArrayToString(response);
  126. ModbusLog m = new ModbusLog();
  127. m.OperationType = "读线圈";
  128. m.ResponseData = responseStr;
  129. m.RequestData = RequestStr;
  130. _modbusDbContext.Add(m);
  131. _modbusDbContext.SaveChanges();
  132. }
  133. catch (Exception)
  134. {
  135. MessageBox.Show("0x15:数据库插入错误");
  136. }
  137. //判断发送回来的数据是否正确
  138. if (CheckData.CheckResponse(response))
  139. {
  140. List<byte> responseList = new List<byte>(response);
  141. //移除前两位
  142. responseList.RemoveRange(0, 3);
  143. //移除后两位校验码
  144. responseList.RemoveRange(responseList.Count - 2, 2);
  145. responseList.Reverse();
  146. //数组反转之后,转为二进制字符
  147. List<string> StringList = responseList.Select(m => Convert.ToString(m, 2)).ToList();
  148. var result = "";
  149. foreach (var item in StringList)
  150. {
  151. result += item.PadLeft(8, '0');
  152. }
  153. //先将字符串转为数组,再反转,再转为字节数组
  154. char[] chars = result.ToArray().Reverse<char>().ToArray();
  155. bool[] ultimately = new bool[numberOfPoints];
  156. for (int i = 0; i < numberOfPoints; i++)
  157. {
  158. if (chars[i] == '1')
  159. {
  160. ultimately[i] = true;
  161. }
  162. else
  163. {
  164. ultimately[i] = false;
  165. }
  166. }
  167. return ultimately;
  168. }
  169. else
  170. {
  171. return null;
  172. }
  173. }
  174. /// <summary>
  175. /// 写入多个线圈操作
  176. /// </summary>
  177. /// <param name="slaveAddress">从站地址</param>
  178. /// <param name="startAddress">起始地址</param>
  179. /// <param name="data">写入的数据</param>
  180. public void WriteCoil(byte slaveAddress, ushort startAddress, bool[] data)
  181. {
  182. if (!_serialPort.IsOpen)
  183. {
  184. MessageBox.Show("串口没有链接,请先链接");
  185. return;
  186. }
  187. List<byte> sendByteList = new List<byte>();
  188. //设置从站地址
  189. sendByteList.Add(slaveAddress);
  190. //设置功能码
  191. sendByteList.Add(0x0F);
  192. //设置起始地址的高位和低位
  193. sendByteList.Add(BitConverter.GetBytes(startAddress)[1]);
  194. sendByteList.Add(BitConverter.GetBytes(startAddress)[0]);
  195. //设置写入几个线圈
  196. sendByteList.Add(BitConverter.GetBytes(data.Length)[1]);
  197. sendByteList.Add(BitConverter.GetBytes(data.Length)[0]);
  198. // 计算字节数
  199. int byteCount = (data.Length + 7) / 8;
  200. sendByteList.Add((byte)byteCount);
  201. // 初始化字节数组
  202. byte[] coilBytes = new byte[byteCount];
  203. // 把bool数组转换为字节
  204. for (int i = 0; i < data.Length; i++)
  205. {
  206. int byteIndex = i / 8;
  207. int bitIndex = i % 8;
  208. if (data[i])
  209. {
  210. coilBytes[byteIndex] |= (byte)(1 << bitIndex);
  211. }
  212. }
  213. sendByteList.AddRange(coilBytes);
  214. //获取CRC校验码
  215. byte[] getCRC = sendByteList.ToArray();
  216. byte[] resultCRC = CRCUitl.CalculateCRC(getCRC, getCRC.Length);
  217. sendByteList.Add(resultCRC[0]);
  218. sendByteList.Add(resultCRC[1]);
  219. byte[] sendByte = sendByteList.ToArray();
  220. int maxRetries = 3; // 最大重试次数
  221. int attempt = 0;
  222. byte[] response = null;
  223. List<byte> responseData = new List<byte>();
  224. _serialPort.Write(sendByte, 0, sendByte.Length);
  225. while (attempt < maxRetries)
  226. {
  227. try
  228. {
  229. //使用时间去轮询查询数据
  230. DateTime startTime = DateTime.Now;
  231. while ((DateTime.Now - startTime).TotalMilliseconds < _serialPort.ReadTimeout)
  232. {
  233. int bytesToRead = _serialPort.BytesToRead;
  234. if (bytesToRead > 0)
  235. {
  236. byte[] buffer = new byte[bytesToRead];
  237. int bytesRead = _serialPort.Read(buffer, 0, bytesToRead);
  238. responseData.AddRange(buffer.Take(bytesRead));
  239. }
  240. //延迟20毫秒,节约cpu资源
  241. Task.Delay(20);
  242. }
  243. response = responseData.ToArray();
  244. if (response[0] != 0x00)
  245. {
  246. break;
  247. }
  248. }
  249. catch (Exception)
  250. {
  251. attempt++;
  252. }
  253. }
  254. try
  255. {
  256. //将操作的指令,插入数据库
  257. string RequestStr = ByteArrayToString(sendByte);
  258. string responseStr = ByteArrayToString(response);
  259. ModbusLog m = new ModbusLog();
  260. m.OperationType = "写线圈";
  261. m.ResponseData = responseStr;
  262. m.RequestData = RequestStr;
  263. _modbusDbContext.Add(m);
  264. _modbusDbContext.SaveChanges();
  265. }
  266. catch (Exception)
  267. {
  268. MessageBox.Show("0x15:数据库插入错误");
  269. }
  270. //判断发送回来的数据是否正确
  271. CheckData.CheckResponse(response);
  272. }
  273. /// <summary>
  274. /// 读寄存器操作
  275. /// </summary>
  276. /// <param name="slaveAddress"></param>
  277. /// <param name="startAddress"></param>
  278. /// <param name="numberOfPoints"></param>
  279. /// <returns></returns>
  280. public ushort[] ReadRegisters(byte slaveAddress, ushort startAddress, ushort numberOfPoints)
  281. {
  282. ushort[] resultValue = null;
  283. if (!_serialPort.IsOpen)
  284. {
  285. MessageBox.Show("串口没有链接,请先链接");
  286. return resultValue;
  287. }
  288. List<byte> sendByteList = new List<byte>();
  289. //设置从站地址
  290. sendByteList.Add(slaveAddress);
  291. //设置功能码
  292. sendByteList.Add(0x03);
  293. //设置起始地址的高位和低位
  294. sendByteList.Add(BitConverter.GetBytes(startAddress)[1]);
  295. sendByteList.Add(BitConverter.GetBytes(startAddress)[0]);
  296. //设置读取几个线圈的高位和低位
  297. sendByteList.Add(BitConverter.GetBytes(numberOfPoints)[1]);
  298. sendByteList.Add(BitConverter.GetBytes(numberOfPoints)[0]);
  299. //获取CRC校验码
  300. byte[] getCRC = sendByteList.ToArray();
  301. byte[] resultCRC = CRCUitl.CalculateCRC(getCRC, getCRC.Length);
  302. sendByteList.Add(resultCRC[0]);
  303. sendByteList.Add(resultCRC[1]);
  304. byte[] sendByte = sendByteList.ToArray();
  305. int maxRetries = 3; // 最大重试次数
  306. int attempt = 0;
  307. List<byte> responseData = new List<byte>();
  308. byte[] response = null;
  309. _serialPort.Write(sendByte, 0, sendByte.Length);
  310. while (attempt < maxRetries)
  311. {
  312. try
  313. {
  314. //使用时间去轮询查询数据
  315. DateTime startTime = DateTime.Now;
  316. while ((DateTime.Now - startTime).TotalMilliseconds < _serialPort.ReadTimeout)
  317. {
  318. int bytesToRead = _serialPort.BytesToRead;
  319. if (bytesToRead > 0)
  320. {
  321. byte[] buffer = new byte[bytesToRead];
  322. int bytesRead = _serialPort.Read(buffer, 0, bytesToRead);
  323. responseData.AddRange(buffer.Take(bytesRead));
  324. }
  325. //延迟20毫秒,节约cpu资源
  326. Task.Delay(20);
  327. }
  328. response = responseData.ToArray();
  329. if (response[0] == 0x00)
  330. {
  331. continue;
  332. }
  333. resultValue = ParseRegistersresponse(sendByte, response, numberOfPoints);
  334. return resultValue;
  335. }
  336. catch (Exception)
  337. {
  338. attempt++;
  339. }
  340. }
  341. //将操作失败的指令,插入数据库
  342. string RequestStr = ByteArrayToString(sendByte);
  343. try
  344. {
  345. ModbusLog m = new ModbusLog();
  346. m.OperationType = "读寄存器";
  347. m.ResponseData = "";
  348. m.RequestData = RequestStr;
  349. _modbusDbContext.Add(m);
  350. _modbusDbContext.SaveChanges();
  351. }
  352. catch (Exception)
  353. {
  354. MessageBox.Show("0x15:数据库插入错误");
  355. }
  356. return resultValue;
  357. }
  358. /// <summary>
  359. /// 解析读取到的操作
  360. /// </summary>
  361. /// <param name="sendByte"></param>
  362. /// <param name="response"></param>
  363. /// <param name="numberOfPoints"></param>
  364. /// <returns></returns>
  365. public ushort[] ParseRegistersresponse(byte[] sendByte, byte[] response, ushort numberOfPoints)
  366. {
  367. try
  368. {
  369. //将操作的指令,插入数据库
  370. string RequestStr = ByteArrayToString(sendByte);
  371. string responseStr = ByteArrayToString(response);
  372. ModbusLog m = new ModbusLog();
  373. m.OperationType = "读寄存器";
  374. m.ResponseData = responseStr;
  375. m.RequestData = RequestStr;
  376. _modbusDbContext.Add(m);
  377. _modbusDbContext.SaveChanges();
  378. }
  379. catch (Exception)
  380. {
  381. MessageBox.Show("0x15:数据库插入错误");
  382. }
  383. //判断发送回来的数据是否正确
  384. if (CheckData.CheckResponse(response))
  385. {
  386. ushort[] registers = new ushort[numberOfPoints];
  387. for (int i = 0; i < numberOfPoints; i++)
  388. {
  389. int pos = 3 + i * 2;
  390. // 大端序转换 (高位在前)
  391. registers[i] = (ushort)((response[pos] << 8) | response[pos + 1]);
  392. }
  393. return registers;
  394. }
  395. else
  396. {
  397. return null;
  398. }
  399. }
  400. /// <summary>
  401. /// 写寄存器
  402. /// </summary>
  403. /// <param name="slaveAddress"></param>
  404. /// <param name="startAddress"></param>
  405. /// <param name="data"></param>
  406. public void WriteRegisters(byte slaveAddress, ushort startAddress, ushort[] data)
  407. {
  408. if (!_serialPort.IsOpen)
  409. {
  410. MessageBox.Show("串口没有链接,请先链接");
  411. return;
  412. }
  413. List<byte> sendByteList = new List<byte>();
  414. //设置从站地址
  415. sendByteList.Add(slaveAddress);
  416. //设置功能码
  417. sendByteList.Add(0x10);
  418. //设置起始地址的高位和低位
  419. sendByteList.Add(BitConverter.GetBytes(startAddress)[1]);
  420. sendByteList.Add(BitConverter.GetBytes(startAddress)[0]);
  421. //设置写入几个线圈
  422. sendByteList.Add(BitConverter.GetBytes(data.Length)[1]);
  423. sendByteList.Add(BitConverter.GetBytes(data.Length)[0]);
  424. // 计算字节数
  425. int byteCount = (data.Length + 7) / 8;
  426. sendByteList.Add((byte)byteCount);
  427. foreach (ushort value in data)
  428. {
  429. byte[] valueBytes = BitConverter.GetBytes(value);
  430. // 大端序:高字节在前,低字节在后
  431. sendByteList.Add(valueBytes[1]);
  432. sendByteList.Add(valueBytes[0]);
  433. }
  434. //获取CRC校验码
  435. byte[] getCRC = sendByteList.ToArray();
  436. byte[] resultCRC = CRCUitl.CalculateCRC(getCRC, getCRC.Length);
  437. sendByteList.Add(resultCRC[0]);
  438. sendByteList.Add(resultCRC[1]);
  439. byte[] sendByte = sendByteList.ToArray();
  440. int maxRetries = 3; // 最大重试次数
  441. int attempt = 0;
  442. byte[] response = null;
  443. List<byte> responseData = new List<byte>();
  444. _serialPort.Write(sendByte, 0, sendByte.Length);
  445. while (attempt < maxRetries)
  446. {
  447. try
  448. {
  449. //使用时间去轮询查询数据
  450. DateTime startTime = DateTime.Now;
  451. while ((DateTime.Now - startTime).TotalMilliseconds < _serialPort.ReadTimeout)
  452. {
  453. int bytesToRead = _serialPort.BytesToRead;
  454. if (bytesToRead > 0)
  455. {
  456. byte[] buffer = new byte[bytesToRead];
  457. int bytesRead = _serialPort.Read(buffer, 0, bytesToRead);
  458. responseData.AddRange(buffer.Take(bytesRead));
  459. }
  460. //延迟20毫秒,节约cpu资源
  461. Task.Delay(20);
  462. }
  463. response = responseData.ToArray();
  464. if (response[0] != 0x00)
  465. {
  466. break;
  467. }
  468. }
  469. catch (Exception)
  470. {
  471. attempt++;
  472. }
  473. }
  474. try
  475. {
  476. //将操作的指令,插入数据库
  477. string RequestStr = ByteArrayToString(sendByte);
  478. string responseStr = ByteArrayToString(response);
  479. ModbusLog m = new ModbusLog();
  480. m.OperationType = "写寄存器";
  481. m.ResponseData = responseStr;
  482. m.RequestData = RequestStr;
  483. _modbusDbContext.Add(m);
  484. _modbusDbContext.SaveChanges();
  485. }
  486. catch (Exception)
  487. {
  488. MessageBox.Show("0x15:数据库插入错误");
  489. }
  490. //判断发送回来的数据是否正确
  491. CheckData.CheckResponse(response);
  492. }
  493. /// <summary>
  494. /// 将数组转换为字符串
  495. /// </summary>
  496. /// <param name="byteArray"></param>
  497. /// <returns></returns>
  498. static string ByteArrayToString(byte[] byteArray)
  499. {
  500. if (byteArray == null || byteArray.Length == 0)
  501. return string.Empty;
  502. StringBuilder sb = new StringBuilder();
  503. // 处理第一个元素(无前导空格)
  504. sb.Append(byteArray[0].ToString("X2"));
  505. // 处理后续元素(每个前面加空格)
  506. for (int i = 1; i < byteArray.Length; i++)
  507. {
  508. sb.Append(' ');
  509. sb.Append(byteArray[i].ToString("X2"));
  510. }
  511. return sb.ToString();
  512. }
  513. }
  514. }