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

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