Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

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