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

602 lines
21 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 short ReadShort(string address)
  191. {
  192. var res = Read(address);
  193. if (res != null && res is short[] shorts && shorts.Length == 1)
  194. {
  195. return shorts[0];
  196. }
  197. return 0;
  198. }
  199. public object Read(string address, ushort len = 1, byte slaveAddress = 1)
  200. {
  201. if (address == null || tcpClient == null) return default(object);
  202. ushort startAddress = (ushort)GetAddress(address);
  203. CommandType commandType = CommandType.Coils;
  204. try
  205. {
  206. if (address.ToUpper().Contains("M"))
  207. {
  208. commandType = CommandType.Coils;
  209. return master.ReadCoils(slaveAddress, startAddress, len);
  210. }
  211. else if (address.ToUpper().Contains("VW") || address.ToUpper().Contains("LW") || address.ToUpper().Contains("D"))
  212. {
  213. commandType = CommandType.HoldingRegisters;
  214. return master.ReadHoldingRegisters(slaveAddress, startAddress, len);
  215. }
  216. else if (address.ToUpper().Contains("I"))
  217. {
  218. commandType = CommandType.Inputs;
  219. return master.ReadInputs(slaveAddress, startAddress, len);
  220. }
  221. }
  222. catch (Exception ex)
  223. {
  224. MessageLog.GetInstance.ShowEx($"读取地址:【{address}:= {startAddress}】,读取类型:【{commandType.ToString()}】出错,{ex.ToString()}");
  225. ExceptionHandling(ex);
  226. }
  227. return default(object);
  228. }
  229. public void Write<T>(string address, T value, byte slaveAddress = 1)
  230. {
  231. if (address == null || tcpClient == null) return;
  232. ushort startAddress = (ushort)GetAddress(address);
  233. CommandType commandType = CommandType.Coils;
  234. try
  235. {
  236. if (address.ToUpper().Contains("M"))
  237. {
  238. commandType = CommandType.Coils;
  239. if (value is bool boolValue)
  240. master.WriteSingleCoil(slaveAddress, startAddress, boolValue);
  241. else if (value is bool[] boolsValue)
  242. master.WriteMultipleCoils(slaveAddress, startAddress, boolsValue);
  243. }
  244. else if (address.ToUpper().Contains("VW") || address.ToUpper().Contains("LW") || address.ToUpper().Contains("D"))
  245. {
  246. commandType = CommandType.HoldingRegisters;
  247. if (value is ushort ushortValue)
  248. master.WriteSingleRegister(slaveAddress, startAddress, ushortValue);
  249. else if (value is ushort[] ushortsValue)
  250. {
  251. int len = 100;
  252. if (ushortsValue.Length > len)
  253. {
  254. List<ushort[]> ushortLists = new List<ushort[]>();
  255. for (int i = 0; i < ushortsValue.Length / len; i++)
  256. {
  257. ushortLists.Add(ushortsValue.Skip(0).Take(len).ToArray());
  258. }
  259. int y = ushortsValue.Length % len;
  260. if (y > 0)
  261. {
  262. ushortLists.Add(ushortsValue.Skip(ushortsValue.Length - y).Take(y).ToArray());
  263. }
  264. foreach (var item in ushortLists)
  265. {
  266. master.WriteMultipleRegisters(slaveAddress, startAddress, item);
  267. startAddress += (ushort)item.Length;
  268. }
  269. }
  270. else
  271. {
  272. master.WriteMultipleRegisters(slaveAddress, startAddress, ushortsValue);
  273. }
  274. }
  275. }
  276. else if (address.ToUpper().Contains("I"))
  277. {
  278. commandType = CommandType.Inputs;
  279. }
  280. }
  281. catch (Exception ex)
  282. {
  283. MessageLog.GetInstance.ShowEx($"写入地址:【{address}:= {startAddress}】,写入类型:【{commandType.ToString()}】出错,{ex.ToString()}");
  284. ExceptionHandling(ex);
  285. }
  286. }
  287. #region 字符串数据读写
  288. /// <summary>
  289. /// 赋值string
  290. /// </summary>
  291. /// <param name="StartAddress"></param>
  292. /// <param name="value"></param>
  293. /// <returns></returns>
  294. public void SetString(string StartAddress, string value)
  295. {
  296. //获取指定的编码不存在的时候需要安装 System.Text.Encoding.CodePages nuget包
  297. //然后使用下面的代码就可以获取了
  298. Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
  299. var bytes = Encoding.GetEncoding("gb2312")?.GetBytes(value);
  300. Write(StartAddress, bytes.BytesToUshorts());
  301. }
  302. /// <summary>
  303. /// 获取string
  304. /// </summary>
  305. /// <param name="StartAddress"></param>
  306. /// <param name="len"></param>
  307. /// <returns></returns>
  308. public string GetString(string StartAddress, ushort len)
  309. {
  310. var res = Read(StartAddress, len);
  311. if (res != null && res is ushort[] ushorts)
  312. {
  313. Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
  314. return Encoding.GetEncoding("gb2312")?.GetString(ushorts.UshortsToBytes()).Trim(new char[] { '\0' });
  315. }
  316. return String.Empty;
  317. }
  318. #endregion
  319. #region 浮点数数据读写
  320. /// <summary>
  321. /// 赋值Real类型数据
  322. /// </summary>
  323. /// <param name="StartAddress"></param>
  324. /// <param name="value"></param>
  325. public void SetReal(string StartAddress, float value)
  326. {
  327. var bytes = BitConverter.GetBytes(value);
  328. Write(StartAddress, bytes.BytesToUshorts());
  329. }
  330. /// <summary>
  331. /// 获取float类型数据
  332. /// </summary>
  333. /// <param name="StartAddress"></param>
  334. /// <returns></returns>
  335. public float GetReal(string StartAddress)
  336. {
  337. var res = Read(StartAddress, 2);
  338. if (res != null && res is ushort[] ushorts)
  339. {
  340. return BitConverter.ToSingle(ushorts.UshortsToBytes(), 0);
  341. }
  342. return 0;
  343. }
  344. #endregion
  345. #region 整数数据读写
  346. /// <summary>
  347. /// 赋值Real类型数据
  348. /// </summary>
  349. /// <param name="StartAddress"></param>
  350. /// <param name="value"></param>
  351. public void SetUint(string StartAddress, uint value)
  352. {
  353. var bytes = BitConverter.GetBytes(value);
  354. Write(StartAddress, bytes.BytesToUshorts());
  355. }
  356. /// <summary>
  357. /// 获取float类型数据
  358. /// </summary>
  359. /// <param name="StartAddress"></param>
  360. /// <returns></returns>
  361. public uint GetUint(string StartAddress)
  362. {
  363. var res = Read(StartAddress, 2);
  364. if (res != null && res is ushort[] ushorts)
  365. {
  366. return BitConverter.ToUInt32(ushorts.UshortsToBytes(), 0);
  367. }
  368. return 0;
  369. }
  370. #endregion
  371. #region 批量数据读取
  372. /// <summary>
  373. /// 读取多个线圈
  374. /// </summary>
  375. /// <param name="startAddress"></param>
  376. /// <param name="num"></param>
  377. /// <param name="slaveAddress"></param>
  378. /// <returns></returns>
  379. public bool[] ReadCoils(ushort startAddress, ushort num, byte slaveAddress = 1)
  380. {
  381. return master.ReadCoils(slaveAddress, startAddress, num);
  382. }
  383. /// <summary>
  384. /// 读取多个输入线圈
  385. /// </summary>
  386. /// <param name="startAddress"></param>
  387. /// <param name="num"></param>
  388. /// <param name="slaveAddress"></param>
  389. /// <returns></returns>
  390. public bool[] ReadInputs(ushort startAddress, ushort num, byte slaveAddress = 1)
  391. {
  392. return master.ReadInputs(slaveAddress, startAddress, num);
  393. }
  394. /// <summary>
  395. /// 读取多个保持寄存器
  396. /// </summary>
  397. /// <param name="startAddress"></param>
  398. /// <param name="num"></param>
  399. /// <param name="slaveAddress"></param>
  400. /// <returns></returns>
  401. public ushort[] ReadHoldingRegisters(ushort startAddress, ushort num, byte slaveAddress = 1)
  402. {
  403. return master.ReadHoldingRegisters(slaveAddress, startAddress, num);
  404. }
  405. /// <summary>
  406. /// 读取多个输入寄存器
  407. /// </summary>
  408. /// <param name="startAddress"></param>
  409. /// <param name="num"></param>
  410. /// <param name="slaveAddress"></param>
  411. /// <returns></returns>
  412. public ushort[] ReadInputRegisters(ushort startAddress, ushort num, byte slaveAddress = 1)
  413. {
  414. return master.ReadInputRegisters(slaveAddress, startAddress, num);
  415. }
  416. #endregion
  417. #region 批量数据写入
  418. /// <summary>
  419. /// 写入多个线圈
  420. /// </summary>
  421. /// <param name="startAddress"></param>
  422. /// <param name="value"></param>
  423. /// <param name="slaveAddress"></param>
  424. public void WriteMultipleCoils(ushort startAddress, bool[] value, byte slaveAddress = 1)
  425. {
  426. master.WriteMultipleCoils(slaveAddress, startAddress, value);
  427. }
  428. /// <summary>
  429. /// 写入多个寄存器
  430. /// </summary>
  431. /// <param name="startAddress"></param>
  432. /// <param name="value"></param>
  433. /// <param name="slaveAddress"></param>
  434. public void WriteMultipleRegisters(ushort startAddress, ushort[] value, byte slaveAddress = 1)
  435. {
  436. master.WriteMultipleRegisters(slaveAddress, startAddress, value);
  437. }
  438. #endregion
  439. #region 单个数据读取
  440. /// <summary>
  441. /// 读取单个线圈
  442. /// </summary>
  443. /// <param name="startAddress"></param>
  444. /// <param name="slaveAddress"></param>
  445. /// <returns></returns>
  446. public bool ReadCoils(ushort startAddress, byte slaveAddress = 1)
  447. {
  448. var result = master.ReadCoils(slaveAddress, startAddress, 1);
  449. if (result == null) return false;
  450. if (result.Length == 1) return result[0];
  451. return false;
  452. //return master.ReadCoils(slaveAddress, startAddress, 1)[0];
  453. }
  454. /// <summary>
  455. /// 读取单个输入线圈
  456. /// </summary>
  457. /// <param name="startAddress"></param>
  458. /// <param name="slaveAddress"></param>
  459. /// <returns></returns>
  460. public bool ReadInputs(ushort startAddress, byte slaveAddress = 1)
  461. {
  462. var result = master.ReadInputs(slaveAddress, startAddress, 1);
  463. if (result == null) return false;
  464. if (result.Length == 1) return result[0];
  465. return false;
  466. //return master.ReadInputs(slaveAddress, startAddress, 1)[0];
  467. }
  468. /// <summary>
  469. /// 读取单个保持寄存器
  470. /// </summary>
  471. /// <param name="startAddress"></param>
  472. /// <param name="slaveAddress"></param>
  473. /// <returns></returns>
  474. public ushort ReadHoldingRegisters(ushort startAddress, byte slaveAddress = 1)
  475. {
  476. if (tcpClient == null) return 0;
  477. ushort[] result = null;
  478. try
  479. {
  480. result = master.ReadHoldingRegisters(slaveAddress, startAddress, 1);
  481. }
  482. catch (Exception ex)
  483. {
  484. MessageLog.GetInstance.Show(ex.ToString());
  485. tcpClient = null;
  486. Connect();
  487. }
  488. if (result == null) return 0;
  489. if (result.Length == 1) return result[0];
  490. return 0;
  491. //return master.ReadHoldingRegisters(slaveAddress, startAddress, 1)[0];
  492. }
  493. /// <summary>
  494. /// 读取单个输入寄存器
  495. /// </summary>
  496. /// <param name="startAddress"></param>
  497. /// <param name="slaveAddress"></param>
  498. /// <returns></returns>
  499. public ushort ReadInputRegisters(ushort startAddress, byte slaveAddress = 1)
  500. {
  501. var result = master.ReadInputRegisters(slaveAddress, startAddress, 1);
  502. if (result == null) return 0;
  503. if (result.Length == 1) return result[0];
  504. return 0;
  505. //return master.ReadInputRegisters(slaveAddress, startAddress, 1)[0];
  506. }
  507. #endregion
  508. #region 单个数据写入
  509. /// <summary>
  510. /// 写入单个线圈
  511. /// </summary>
  512. /// <param name="startAddress"></param>
  513. /// <param name="value"></param>
  514. /// <param name="slaveAddress"></param>
  515. public void WriteSingleCoil(ushort startAddress, bool value, byte slaveAddress = 1)
  516. {
  517. master.WriteSingleCoil(slaveAddress, startAddress, value);
  518. }
  519. /// <summary>
  520. /// 写入单个寄存器
  521. /// </summary>
  522. /// <param name="startAddress"></param>
  523. /// <param name="value"></param>
  524. /// <param name="slaveAddress"></param>
  525. public void WriteSingleRegister(ushort startAddress, ushort value, byte slaveAddress = 1)
  526. {
  527. master.WriteSingleRegister(slaveAddress, startAddress, value);
  528. }
  529. #endregion
  530. }
  531. public enum CommandType
  532. {
  533. /// <summary>
  534. /// 线圈操作
  535. /// </summary>
  536. Coils,
  537. /// <summary>
  538. /// 输入线圈操作
  539. /// </summary>
  540. Inputs,
  541. /// <summary>
  542. /// 保持寄存器操作
  543. /// </summary>
  544. HoldingRegisters,
  545. /// <summary>
  546. /// 输入寄存器操作
  547. /// </summary>
  548. InputRegisters,
  549. }
  550. }