终端一体化运控平台
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。
 
 
 

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