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

730 lines
27 KiB

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