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

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