终端一体化运控平台
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.
 
 
 

663 line
24 KiB

  1. using BPASmartClient.Helper;
  2. using BPASmartClient.Message;
  3. using NModbus;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Diagnostics;
  7. using System.Linq;
  8. using System.Net.Sockets;
  9. using System.Text;
  10. using System.Threading;
  11. using System.Threading.Tasks;
  12. namespace BPASmartClient.Modbus
  13. {
  14. public class ModbusTcp
  15. {
  16. //private volatile static ModbusTcp _Instance;
  17. //public static ModbusTcp GetInstance => _Instance ?? (_Instance = new ModbusTcp());
  18. //private ModbusTcp() { }
  19. private ModbusFactory modbusFactory;
  20. private IModbusMaster master;
  21. private TcpClient tcpClient;
  22. private ManualResetEvent mre = new ManualResetEvent(false);
  23. public string IPAdress;
  24. public int Port;
  25. /// <summary>
  26. /// 连接plc 原料设备成功
  27. /// </summary>
  28. public Action ConnectOk { get; set; }
  29. public Action ConnectFail { get; set; }
  30. public Action Disconnect { get; set; }
  31. public bool IsReconnect { get; set; } = true; //true=启用重连,false=禁用重连
  32. /// <summary>
  33. /// 判断是否连接成功
  34. /// </summary>
  35. public bool Connected => tcpClient == null ? false : tcpClient.Connected;
  36. /// <summary>
  37. /// ModbusTcp 连接设备
  38. /// </summary>
  39. /// <param name="ip">ip 地址</param>
  40. /// <param name="port">端口号,默认502</param>
  41. public void ModbusTcpConnect(string ip, int port = 502)
  42. {
  43. MessageLog.GetInstance.Show($"设备【{ip}:{port}】连接中。。。。");
  44. IPAdress = ip;
  45. Port = port;
  46. modbusFactory = new ModbusFactory();
  47. Connect();
  48. if (Connected)
  49. {
  50. master.Transport.ReadTimeout = 2000;//读取超时时间
  51. master.Transport.WriteTimeout = 2000;//写入超时时间
  52. master.Transport.Retries = 10;//重试次数
  53. ConnectOk?.Invoke();
  54. MessageLog.GetInstance.Show($"设备【{ip}:{port}】连接成功");
  55. }
  56. else
  57. {
  58. ConnectFail?.Invoke();
  59. }
  60. }
  61. public void Dispose()
  62. {
  63. master.Dispose();
  64. Disconnect?.Invoke();
  65. }
  66. private void Connect()
  67. {
  68. bool ErrorFlag = false;
  69. while (!Connected)
  70. {
  71. try
  72. {
  73. tcpClient = new TcpClient(IPAdress, Port);
  74. //mre.Reset();
  75. //tcpClient.BeginConnect(IPAdress, Port, (res) => { mre.Set(); }, null);
  76. //if (!mre.WaitOne(1500, false)) break;
  77. master = modbusFactory.CreateMaster(tcpClient);
  78. }
  79. catch (Exception ex)
  80. {
  81. if (!ErrorFlag)
  82. {
  83. MessageLog.GetInstance.ShowEx($"ModbusTcp 连接失败,IP = {IPAdress},Port = {Port}");
  84. MessageLog.GetInstance.ShowEx(ex.ToString());
  85. ErrorFlag = true;
  86. }
  87. if (!IsReconnect) break;
  88. Thread.Sleep(3000);
  89. }
  90. }
  91. if (ErrorFlag && IsReconnect) MessageLog.GetInstance.Show("ModbusTcp 重连成功!");
  92. }
  93. /// <summary>
  94. /// 获取 M 区 和 VW 区的Modbus的地址
  95. /// </summary>
  96. /// <param name="address"></param>
  97. /// <returns></returns>
  98. public int GetAddress(string address)
  99. {
  100. if (address == null) return -1;
  101. if (address.Length > 0)
  102. {
  103. if (address.ToUpper().Contains("M") && address.Length >= 4)
  104. {
  105. var res = address.Substring(1).Split('.');
  106. if (res != null && res.Length == 2)
  107. {
  108. if (int.TryParse(res[0], out int firstAddress) && int.TryParse(res[1], out int ExitAddress))
  109. {
  110. if (ExitAddress >= 0 && ExitAddress <= 7)
  111. {
  112. return (firstAddress * 8) + 320 + ExitAddress;
  113. }
  114. }
  115. }
  116. }
  117. else if (address.ToUpper().Contains("VW") && address.Length >= 3)
  118. {
  119. var res = address.Substring(2);
  120. if (res != null && int.TryParse(res, out int tempAddress))
  121. {
  122. return (tempAddress / 2) + 100;
  123. }
  124. }
  125. else if (address.ToUpper().Contains("LW") && address.Length >= 3)
  126. {
  127. var res = address.Substring(2);
  128. if (res != null && int.TryParse(res, out int LwAddress))
  129. {
  130. return LwAddress;
  131. }
  132. }
  133. else if (address.ToUpper().Contains("D") && address.Length == 5)
  134. {
  135. try
  136. {
  137. string head = (Convert.ToInt32(address.Substring(1, 1))).ToString();
  138. int num = Convert.ToInt32(address.Substring(2, 3));
  139. int len = num.ToString().Length;
  140. string tail=string.Empty;
  141. switch (len)
  142. {
  143. case 1:
  144. if ((Convert.ToInt32(address.Substring(4, 1))).ToString().Length > 1)
  145. {
  146. tail = "0" + (Convert.ToInt32(address.Substring(4, 1))).ToString();
  147. }
  148. else
  149. {
  150. tail = "00" + (Convert.ToInt32(address.Substring(4, 1))).ToString();
  151. }
  152. break;
  153. case 2:
  154. if ((Convert.ToInt32(address.Substring(3, 2))).ToString().Length >2)
  155. {
  156. tail =(Convert.ToInt32(address.Substring(3, 2))).ToString();
  157. }
  158. else
  159. {
  160. tail = "0" + (Convert.ToInt32(address.Substring(3, 2))).ToString();
  161. }
  162. break;
  163. case 3:
  164. tail = (Convert.ToInt32(address.Substring(2, 3))).ToString();
  165. break;
  166. }
  167. address = head + tail;
  168. return Convert.ToInt32(address);
  169. }
  170. catch (Exception)
  171. {
  172. //打印日志
  173. }
  174. }
  175. }
  176. return -1;
  177. }
  178. private void ExceptionHandling(Exception ex)
  179. {
  180. if (ex.InnerException is SocketException)
  181. {
  182. if (IsReconnect)
  183. {
  184. tcpClient = null;
  185. Connect();
  186. }
  187. Disconnect?.Invoke();
  188. }
  189. }
  190. //public object ReadHC(stri)
  191. public object Read(string address, ushort len = 1, byte slaveAddress = 1)
  192. {
  193. if (address == null || tcpClient == null) return default(object);
  194. ushort startAddress = (ushort)GetAddress(address);
  195. CommandType commandType = CommandType.Coils;
  196. try
  197. {
  198. if (address.ToUpper().Contains("M"))
  199. {
  200. commandType = CommandType.Coils;
  201. return master.ReadCoils(slaveAddress, startAddress, len);
  202. }
  203. else if (address.ToUpper().Contains("VW") || address.ToUpper().Contains("LW")|| address.ToUpper().Contains("D"))
  204. {
  205. commandType = CommandType.HoldingRegisters;
  206. return master.ReadHoldingRegisters(slaveAddress, startAddress, len);
  207. }
  208. else if (address.ToUpper().Contains("I"))
  209. {
  210. commandType = CommandType.Inputs;
  211. return master.ReadInputs(slaveAddress, startAddress, len);
  212. }
  213. }
  214. catch (Exception ex)
  215. {
  216. MessageLog.GetInstance.ShowEx($"读取地址:【{address}:= {startAddress}】,读取类型:【{commandType.ToString()}】出错,{ex.ToString()}");
  217. ExceptionHandling(ex);
  218. }
  219. return default(object);
  220. }
  221. #region 180项目调用
  222. public int GetAddress(string address, string target)
  223. {
  224. if (address == null) return -1;
  225. if (address.Length > 0)
  226. {
  227. if (address.ToUpper().Contains("D") && address.Length == 5)
  228. {
  229. try
  230. {
  231. string head = "4"+(Convert.ToInt32(address.Substring(1, 1)) - 1).ToString();
  232. string tail = address.Substring(2, 3);
  233. address = head + tail;
  234. return Convert.ToInt32(address);
  235. }
  236. catch (Exception)
  237. {
  238. //打印日志
  239. }
  240. }
  241. }
  242. return -1;
  243. }
  244. public object Read(string address, ushort len, string target, byte slaveAddress = 1)
  245. {
  246. if (address == null || tcpClient == null) return default(object);
  247. ushort startAddress = (ushort)GetAddress(address, target);
  248. CommandType commandType = CommandType.HoldingRegisters;
  249. try
  250. {
  251. if (address.ToUpper().Contains("D"))
  252. {
  253. commandType = CommandType.HoldingRegisters;
  254. return master.ReadHoldingRegisters(slaveAddress, startAddress, len);
  255. }
  256. }
  257. catch (Exception ex)
  258. {
  259. MessageLog.GetInstance.ShowEx($"读取地址:【{address}:= {startAddress}】,读取类型:【{commandType.ToString()}】出错,{ex.ToString()}");
  260. ExceptionHandling(ex);
  261. }
  262. return default(object);
  263. }
  264. public void Write<T>(string address, T value, string target, byte slaveAddress = 1)
  265. {
  266. if (address == null || tcpClient == null) return;
  267. ushort startAddress = (ushort)GetAddress(address,target);
  268. CommandType commandType = CommandType.Coils;
  269. try
  270. {
  271. if (address.ToUpper().Contains("D"))
  272. {
  273. commandType = CommandType.HoldingRegisters;
  274. if (value is ushort ushortValue)
  275. {
  276. master.WriteSingleRegister(slaveAddress, startAddress, ushortValue);
  277. }
  278. }
  279. }
  280. catch (Exception ex)
  281. {
  282. MessageLog.GetInstance.ShowEx($"写入地址:【{address}:= {startAddress}】,写入类型:【{commandType.ToString()}】出错,{ex.ToString()}");
  283. ExceptionHandling(ex);
  284. }
  285. }
  286. #endregion
  287. public void Write<T>(string address, T value, byte slaveAddress = 1)
  288. {
  289. if (address == null || tcpClient == null) return;
  290. ushort startAddress = (ushort)GetAddress(address);
  291. CommandType commandType = CommandType.Coils;
  292. try
  293. {
  294. if (address.ToUpper().Contains("M"))
  295. {
  296. commandType = CommandType.Coils;
  297. if (value is bool boolValue)
  298. master.WriteSingleCoil(slaveAddress, startAddress, boolValue);
  299. else if (value is bool[] boolsValue)
  300. master.WriteMultipleCoils(slaveAddress, startAddress, boolsValue);
  301. }
  302. else if (address.ToUpper().Contains("VW") || address.ToUpper().Contains("LW")|| address.ToUpper().Contains("D"))
  303. {
  304. commandType = CommandType.HoldingRegisters;
  305. if (value is ushort ushortValue)
  306. master.WriteSingleRegister(slaveAddress, startAddress, ushortValue);
  307. else if (value is ushort[] ushortsValue)
  308. {
  309. int len = 100;
  310. if (ushortsValue.Length > len)
  311. {
  312. List<ushort[]> ushortLists = new List<ushort[]>();
  313. for (int i = 0; i < ushortsValue.Length / len; i++)
  314. {
  315. ushortLists.Add(ushortsValue.Skip(0).Take(len).ToArray());
  316. }
  317. int y = ushortsValue.Length % len;
  318. if (y > 0)
  319. {
  320. ushortLists.Add(ushortsValue.Skip(ushortsValue.Length - y).Take(y).ToArray());
  321. }
  322. foreach (var item in ushortLists)
  323. {
  324. master.WriteMultipleRegisters(slaveAddress, startAddress, item);
  325. startAddress += (ushort)item.Length;
  326. }
  327. }
  328. else
  329. {
  330. master.WriteMultipleRegisters(slaveAddress, startAddress, ushortsValue);
  331. }
  332. }
  333. }
  334. else if (address.ToUpper().Contains("I"))
  335. {
  336. commandType = CommandType.Inputs;
  337. }
  338. }
  339. catch (Exception ex)
  340. {
  341. MessageLog.GetInstance.ShowEx($"写入地址:【{address}:= {startAddress}】,写入类型:【{commandType.ToString()}】出错,{ex.ToString()}");
  342. ExceptionHandling(ex);
  343. }
  344. }
  345. #region 字符串数据读写
  346. /// <summary>
  347. /// 赋值string
  348. /// </summary>
  349. /// <param name="StartAddress"></param>
  350. /// <param name="value"></param>
  351. /// <returns></returns>
  352. public void SetString(string StartAddress, string value)
  353. {
  354. //获取指定的编码不存在的时候需要安装 System.Text.Encoding.CodePages nuget包
  355. //然后使用下面的代码就可以获取了
  356. Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
  357. var bytes = Encoding.GetEncoding("gb2312")?.GetBytes(value);
  358. Write(StartAddress, bytes.BytesToUshorts());
  359. }
  360. /// <summary>
  361. /// 获取string
  362. /// </summary>
  363. /// <param name="StartAddress"></param>
  364. /// <param name="len"></param>
  365. /// <returns></returns>
  366. public string GetString(string StartAddress, ushort len)
  367. {
  368. var res = Read(StartAddress, len);
  369. if (res != null && res is ushort[] ushorts)
  370. {
  371. Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
  372. return Encoding.GetEncoding("gb2312")?.GetString(ushorts.UshortsToBytes()).Trim(new char[] { '\0' });
  373. }
  374. return String.Empty;
  375. }
  376. #endregion
  377. #region 浮点数数据读写
  378. /// <summary>
  379. /// 赋值Real类型数据
  380. /// </summary>
  381. /// <param name="StartAddress"></param>
  382. /// <param name="value"></param>
  383. public void SetReal(string StartAddress, float value)
  384. {
  385. var bytes = BitConverter.GetBytes(value);
  386. Write(StartAddress, bytes.BytesToUshorts());
  387. }
  388. /// <summary>
  389. /// 获取float类型数据
  390. /// </summary>
  391. /// <param name="StartAddress"></param>
  392. /// <returns></returns>
  393. public float GetReal(string StartAddress)
  394. {
  395. var res = Read(StartAddress, 2);
  396. if (res != null && res is ushort[] ushorts)
  397. {
  398. return BitConverter.ToSingle(ushorts.UshortsToBytes(), 0);
  399. }
  400. return 0;
  401. }
  402. #endregion
  403. #region 整数数据读写
  404. /// <summary>
  405. /// 赋值Real类型数据
  406. /// </summary>
  407. /// <param name="StartAddress"></param>
  408. /// <param name="value"></param>
  409. public void SetUint(string StartAddress, uint value)
  410. {
  411. var bytes = BitConverter.GetBytes(value);
  412. Write(StartAddress, bytes.BytesToUshorts());
  413. }
  414. /// <summary>
  415. /// 获取float类型数据
  416. /// </summary>
  417. /// <param name="StartAddress"></param>
  418. /// <returns></returns>
  419. public uint GetUint(string StartAddress)
  420. {
  421. var res = Read(StartAddress, 2);
  422. if (res != null && res is ushort[] ushorts)
  423. {
  424. return BitConverter.ToUInt32(ushorts.UshortsToBytes(), 0);
  425. }
  426. return 0;
  427. }
  428. #endregion
  429. #region 批量数据读取
  430. /// <summary>
  431. /// 读取多个线圈
  432. /// </summary>
  433. /// <param name="startAddress"></param>
  434. /// <param name="num"></param>
  435. /// <param name="slaveAddress"></param>
  436. /// <returns></returns>
  437. public bool[] ReadCoils(ushort startAddress, ushort num, byte slaveAddress = 1)
  438. {
  439. return master.ReadCoils(slaveAddress, startAddress, num);
  440. }
  441. /// <summary>
  442. /// 读取多个输入线圈
  443. /// </summary>
  444. /// <param name="startAddress"></param>
  445. /// <param name="num"></param>
  446. /// <param name="slaveAddress"></param>
  447. /// <returns></returns>
  448. public bool[] ReadInputs(ushort startAddress, ushort num, byte slaveAddress = 1)
  449. {
  450. return master.ReadInputs(slaveAddress, startAddress, num);
  451. }
  452. /// <summary>
  453. /// 读取多个保持寄存器
  454. /// </summary>
  455. /// <param name="startAddress"></param>
  456. /// <param name="num"></param>
  457. /// <param name="slaveAddress"></param>
  458. /// <returns></returns>
  459. public ushort[] ReadHoldingRegisters(ushort startAddress, ushort num, byte slaveAddress = 1)
  460. {
  461. return master.ReadHoldingRegisters(slaveAddress, startAddress, num);
  462. }
  463. /// <summary>
  464. /// 读取多个输入寄存器
  465. /// </summary>
  466. /// <param name="startAddress"></param>
  467. /// <param name="num"></param>
  468. /// <param name="slaveAddress"></param>
  469. /// <returns></returns>
  470. public ushort[] ReadInputRegisters(ushort startAddress, ushort num, byte slaveAddress = 1)
  471. {
  472. return master.ReadInputRegisters(slaveAddress, startAddress, num);
  473. }
  474. #endregion
  475. #region 批量数据写入
  476. /// <summary>
  477. /// 写入多个线圈
  478. /// </summary>
  479. /// <param name="startAddress"></param>
  480. /// <param name="value"></param>
  481. /// <param name="slaveAddress"></param>
  482. public void WriteMultipleCoils(ushort startAddress, bool[] value, byte slaveAddress = 1)
  483. {
  484. master.WriteMultipleCoils(slaveAddress, startAddress, value);
  485. }
  486. /// <summary>
  487. /// 写入多个寄存器
  488. /// </summary>
  489. /// <param name="startAddress"></param>
  490. /// <param name="value"></param>
  491. /// <param name="slaveAddress"></param>
  492. public void WriteMultipleRegisters(ushort startAddress, ushort[] value, byte slaveAddress = 1)
  493. {
  494. master.WriteMultipleRegisters(slaveAddress, startAddress, value);
  495. }
  496. #endregion
  497. #region 单个数据读取
  498. /// <summary>
  499. /// 读取单个线圈
  500. /// </summary>
  501. /// <param name="startAddress"></param>
  502. /// <param name="slaveAddress"></param>
  503. /// <returns></returns>
  504. public bool ReadCoils(ushort startAddress, byte slaveAddress = 1)
  505. {
  506. var result = master.ReadCoils(slaveAddress, startAddress, 1);
  507. if (result == null) return false;
  508. if (result.Length == 1) return result[0];
  509. return false;
  510. //return master.ReadCoils(slaveAddress, startAddress, 1)[0];
  511. }
  512. /// <summary>
  513. /// 读取单个输入线圈
  514. /// </summary>
  515. /// <param name="startAddress"></param>
  516. /// <param name="slaveAddress"></param>
  517. /// <returns></returns>
  518. public bool ReadInputs(ushort startAddress, byte slaveAddress = 1)
  519. {
  520. var result = master.ReadInputs(slaveAddress, startAddress, 1);
  521. if (result == null) return false;
  522. if (result.Length == 1) return result[0];
  523. return false;
  524. //return master.ReadInputs(slaveAddress, startAddress, 1)[0];
  525. }
  526. /// <summary>
  527. /// 读取单个保持寄存器
  528. /// </summary>
  529. /// <param name="startAddress"></param>
  530. /// <param name="slaveAddress"></param>
  531. /// <returns></returns>
  532. public ushort ReadHoldingRegisters(ushort startAddress, byte slaveAddress = 1)
  533. {
  534. if (tcpClient == null) return 0;
  535. ushort[] result = null;
  536. try
  537. {
  538. result = master.ReadHoldingRegisters(slaveAddress, startAddress, 1);
  539. }
  540. catch (Exception ex)
  541. {
  542. MessageLog.GetInstance.Show(ex.ToString());
  543. tcpClient = null;
  544. Connect();
  545. }
  546. if (result == null) return 0;
  547. if (result.Length == 1) return result[0];
  548. return 0;
  549. //return master.ReadHoldingRegisters(slaveAddress, startAddress, 1)[0];
  550. }
  551. /// <summary>
  552. /// 读取单个输入寄存器
  553. /// </summary>
  554. /// <param name="startAddress"></param>
  555. /// <param name="slaveAddress"></param>
  556. /// <returns></returns>
  557. public ushort ReadInputRegisters(ushort startAddress, byte slaveAddress = 1)
  558. {
  559. var result = master.ReadInputRegisters(slaveAddress, startAddress, 1);
  560. if (result == null) return 0;
  561. if (result.Length == 1) return result[0];
  562. return 0;
  563. //return master.ReadInputRegisters(slaveAddress, startAddress, 1)[0];
  564. }
  565. #endregion
  566. #region 单个数据写入
  567. /// <summary>
  568. /// 写入单个线圈
  569. /// </summary>
  570. /// <param name="startAddress"></param>
  571. /// <param name="value"></param>
  572. /// <param name="slaveAddress"></param>
  573. public void WriteSingleCoil(ushort startAddress, bool value, byte slaveAddress = 1)
  574. {
  575. master.WriteSingleCoil(slaveAddress, startAddress, value);
  576. }
  577. /// <summary>
  578. /// 写入单个寄存器
  579. /// </summary>
  580. /// <param name="startAddress"></param>
  581. /// <param name="value"></param>
  582. /// <param name="slaveAddress"></param>
  583. public void WriteSingleRegister(ushort startAddress, ushort value, byte slaveAddress = 1)
  584. {
  585. master.WriteSingleRegister(slaveAddress, startAddress, value);
  586. }
  587. #endregion
  588. }
  589. public enum CommandType
  590. {
  591. /// <summary>
  592. /// 线圈操作
  593. /// </summary>
  594. Coils,
  595. /// <summary>
  596. /// 输入线圈操作
  597. /// </summary>
  598. Inputs,
  599. /// <summary>
  600. /// 保持寄存器操作
  601. /// </summary>
  602. HoldingRegisters,
  603. /// <summary>
  604. /// 输入寄存器操作
  605. /// </summary>
  606. InputRegisters,
  607. }
  608. }