ModbusTcpClient.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496
  1. using NModbus;
  2. using System;
  3. using System.Net.Sockets;
  4. namespace MvvmScaffoldFrame48.DLL.CommunicationTools
  5. {
  6. public class ModbusTcpClient
  7. {
  8. #region 实例
  9. private TcpClient _tcpClient;
  10. private IModbusMaster _modbusMaster;
  11. #endregion
  12. #region 连接方法
  13. /// <summary>
  14. /// 判断 TCP 连接是否已断开
  15. /// </summary>
  16. /// <returns></returns>
  17. public bool IsTcpClientConnected()
  18. {
  19. if (_tcpClient == null || _tcpClient.Client == null)
  20. return false;
  21. try
  22. {
  23. Socket socket = _tcpClient.Client;
  24. bool isReadable = socket.Poll(0, SelectMode.SelectRead);
  25. bool hasNoData = (socket.Available == 0);
  26. // 可读且无数据 = 连接已断开
  27. return !(isReadable && hasNoData);
  28. }
  29. catch (SocketException) { return false; }
  30. catch (ObjectDisposedException) { return false; }
  31. }
  32. /// <summary>
  33. /// 连接 Modbus TCP 服务器
  34. /// </summary>
  35. /// <param name="ipAddress"></param>
  36. /// <param name="port"></param>
  37. /// <returns></returns>
  38. public bool Connect(string ipAddress, int port = 502)
  39. {
  40. try
  41. {
  42. _tcpClient = new TcpClient(ipAddress, port)
  43. {
  44. SendTimeout = 2000,
  45. ReceiveTimeout = 2000
  46. };
  47. // 使用 ModbusFactory 创建 Master(新版本 API)
  48. var factory = new ModbusFactory();
  49. _modbusMaster = factory.CreateMaster(_tcpClient);
  50. return true;
  51. }
  52. catch (Exception ex)
  53. {
  54. Console.WriteLine($"连接失败: {ex.Message}");
  55. return false;
  56. }
  57. }
  58. /// <summary>
  59. /// 断开连接
  60. /// </summary>
  61. public void Disconnect()
  62. {
  63. _modbusMaster?.Dispose();
  64. _tcpClient?.Close();
  65. }
  66. #endregion
  67. #region 读取方法
  68. /// <summary>
  69. /// 读取线圈(功能码01)
  70. /// </summary>
  71. /// <param name="slaveId"></param>
  72. /// <param name="startAddress"></param>
  73. /// <param name="numRegisters"></param>
  74. /// <returns></returns>
  75. /// <exception cref="InvalidOperationException"></exception>
  76. public bool[] ReadCoilsRegister(byte slaveId, ushort startAddress, ushort numRegisters)
  77. {
  78. if (_modbusMaster == null)
  79. {
  80. Console.WriteLine($"ModbusTcpClient-ReadCoilsRegister failed:未连接到服务器");
  81. return null;
  82. }
  83. try
  84. {
  85. return _modbusMaster.ReadCoils(slaveId, startAddress, numRegisters);
  86. }
  87. catch (Exception ex)
  88. {
  89. Console.WriteLine($"读取线圈失败,ModbusTcpClient-ReadCoilsRegister failed: {ex.Message}");
  90. return null;
  91. }
  92. }
  93. /// <summary>
  94. /// 读取保持寄存器(功能码03)
  95. /// </summary>
  96. /// <param name="slaveId"></param>
  97. /// <param name="startAddress"></param>
  98. /// <param name="numRegisters"></param>
  99. /// <returns></returns>
  100. /// <exception cref="InvalidOperationException"></exception>
  101. public ushort[] ReadHoldingRegisters(byte slaveId, ushort startAddress, ushort numRegisters)
  102. {
  103. if (_modbusMaster == null)
  104. {
  105. Console.WriteLine($"ModbusTcpClient-ReadHoldingRegistersAsReal failed:未连接到服务器");
  106. return null;
  107. }
  108. try
  109. {
  110. return _modbusMaster.ReadHoldingRegisters(slaveId, startAddress, numRegisters);
  111. }
  112. catch (Exception ex)
  113. {
  114. Console.WriteLine($"读取保持寄存器失败,ModbusTcpClient-ReadHoldingRegisters failed: {ex.Message}");
  115. return null;
  116. }
  117. }
  118. /// <summary>
  119. /// 读取保持寄存器中的REAL值(功能码03)
  120. /// </summary>
  121. /// <param name="slaveId">从站ID</param>
  122. /// <param name="startAddress">起始地址</param>
  123. /// <param name="count">REAL值的数量</param>
  124. /// <param name="byteOrder">字节序 (ABCD, CDAB, BADC, DCBA)</param>
  125. /// <returns>REAL值数组</returns>
  126. public float[] ReadHoldingRegistersAsReal(byte slaveId, ushort startAddress, ushort count, string byteOrder = "BADC")
  127. {
  128. if (_modbusMaster == null)
  129. {
  130. Console.WriteLine($"ModbusTcpClient-ReadHoldingRegistersAsReal failed:未连接到服务器");
  131. return null;
  132. }
  133. try
  134. {
  135. // 每个REAL值占用2个寄存器
  136. ushort[] registers = _modbusMaster.ReadHoldingRegisters(slaveId, startAddress, (ushort)(count * 2));
  137. float[] realValues = new float[count];
  138. for (int i = 0; i < count; i++)
  139. {
  140. ushort highRegister = registers[i * 2];
  141. ushort lowRegister = registers[i * 2 + 1];
  142. byte[] bytes = ConvertToByteArray(lowRegister, highRegister, byteOrder);
  143. realValues[i] = BitConverter.ToSingle(bytes, 0);
  144. }
  145. return realValues;
  146. }
  147. catch (Exception ex)
  148. {
  149. Console.WriteLine($"读取REAL值失败,ModbusTcpClient-ReadHoldingRegistersAsReal failed:{ex.Message}");
  150. return null;
  151. }
  152. }
  153. /// <summary>
  154. /// 读取保持寄存器中的Int32值(功能码03)
  155. /// </summary>
  156. /// <param name="slaveId">从站ID</param>
  157. /// <param name="startAddress">起始地址</param>
  158. /// <param name="count">REAL值的数量</param>
  159. /// <returns>REAL值数组</returns>
  160. public UInt32[] ReadHoldingRegistersAsInt32(byte slaveId, ushort startAddress, ushort count, string byteOrder = "BADC")
  161. {
  162. if (_modbusMaster == null)
  163. {
  164. Console.WriteLine($"ModbusTcpClient-ReadHoldingRegistersAsInt32 failed:未连接到服务器");
  165. return null;
  166. }
  167. try
  168. {
  169. // 每个REAL值占用2个寄存器
  170. ushort[] registers = _modbusMaster.ReadHoldingRegisters(slaveId, startAddress, (ushort)(count * 2));
  171. UInt32[] realValues = new UInt32[count];
  172. for (int i = 0; i < count; i++)
  173. {
  174. // 获取两个连续的寄存器值
  175. ushort highRegister = registers[i * 2];
  176. ushort lowRegister = registers[i * 2 + 1];
  177. byte[] bytes = ConvertToByteArray(lowRegister, highRegister, byteOrder);
  178. // 转换为float
  179. realValues[i] = (UInt32)BitConverter.ToInt32(bytes, 0);
  180. }
  181. return realValues;
  182. }
  183. catch (Exception ex)
  184. {
  185. Console.WriteLine($"读取Int32值失败: {ex.Message}");
  186. return null;
  187. }
  188. }
  189. #endregion
  190. #region 写入方法
  191. /// <summary>
  192. /// 写入线圈(功能码05)
  193. /// </summary>
  194. /// <param name="slaveId">Slave编号</param>
  195. /// <param name="CoilsAddress">写入地址</param>
  196. /// <param name="values">写入值</param>
  197. /// <exception cref="InvalidOperationException"></exception>
  198. public bool WriteCoilsRegister(byte slaveId, ushort CoilsAddress, bool values)
  199. {
  200. bool result = false;
  201. if (_modbusMaster == null)
  202. {
  203. Console.WriteLine($"ModbusTcpClient-WriteCoilsRegister failed:未连接到服务器");
  204. result = false;
  205. }
  206. try
  207. {
  208. _modbusMaster.WriteSingleCoil(slaveId, CoilsAddress, values);
  209. result = true;
  210. }
  211. catch (Exception ex)
  212. {
  213. Console.WriteLine($"ModbusTcpClient-WriteCoilsRegister-WriteCoilsRegister failed:{ex.Message}");
  214. result = false;
  215. }
  216. return result;
  217. }
  218. /// <summary>
  219. /// 写入单个寄存器(功能码06)
  220. /// </summary>
  221. /// <param name="slaveId"></param>
  222. /// <param name="registerAddress"></param>
  223. /// <param name="value"></param>
  224. /// <exception cref="InvalidOperationException"></exception>
  225. public bool WriteSingleRegister(byte slaveId, ushort registerAddress, ushort value)
  226. {
  227. bool result = false;
  228. if (_modbusMaster == null)
  229. {
  230. Console.WriteLine($"ModbusTcpClient-WriteSingleRegister failed:未连接到服务器");
  231. result = false;
  232. return result;
  233. }
  234. try
  235. {
  236. _modbusMaster.WriteSingleRegister(slaveId, registerAddress, value);
  237. result = true;
  238. }
  239. catch (Exception ex)
  240. {
  241. Console.WriteLine($"写入单个寄存器失败,ModbusTcpClient-WriteSingleRegister failed:{ex.Message}");
  242. result = false;
  243. }
  244. return result;
  245. }
  246. /// <summary>
  247. /// 写入多个寄存器(功能码16)
  248. /// </summary>
  249. /// <param name="slaveId"></param>
  250. /// <param name="startAddress"></param>
  251. /// <param name="values"></param>
  252. /// <exception cref="InvalidOperationException"></exception>
  253. public bool WriteMultipleRegisters(byte slaveId, ushort startAddress, ushort[] values)
  254. {
  255. bool result = false;
  256. if (_modbusMaster == null)
  257. {
  258. Console.WriteLine($"ModbusTcpClient-WriteMultipleRegisters failed:未连接到服务器");
  259. return false;
  260. }
  261. try
  262. {
  263. _modbusMaster.WriteMultipleRegisters(slaveId, startAddress, values);
  264. result = true;
  265. }
  266. catch (Exception ex)
  267. {
  268. Console.WriteLine($"写入多个寄存器失败,ModbusTcpClient-WriteMultipleRegisters failed:{ex.Message}");
  269. result = false;
  270. }
  271. return result;
  272. }
  273. /// <summary>
  274. /// 写入单个REAL值到保持寄存器(功能码16)
  275. /// </summary>
  276. /// <param name="slaveId">从站ID</param>
  277. /// <param name="startAddress">起始地址</param>
  278. /// <param name="value">REAL值</param>
  279. /// <param name="byteOrder">字节序 (ABCD, CDAB, BADC, DCBA)</param>
  280. public bool WriteSingleReal(byte slaveId, ushort startAddress, float value, string byteOrder = "BADC")
  281. {
  282. bool result = false;
  283. if (_modbusMaster == null)
  284. {
  285. Console.WriteLine($"ModbusTcpClient-WriteSingleReal failed:未连接到服务器");
  286. return false;
  287. }
  288. try
  289. {
  290. // 将float转换为字节数组
  291. byte[] bytes = BitConverter.GetBytes(value);
  292. // 根据字节序重新排列字节
  293. ushort[] registers = ConvertBytesToRegisters(bytes, byteOrder);
  294. // 写入连续的寄存器
  295. result = WriteMultipleRegisters(slaveId, startAddress, registers);
  296. }
  297. catch (Exception ex)
  298. {
  299. Console.WriteLine($"写入REAL值失败,ModbusTcpClient-WriteSingleReal failed:{ex.Message}");
  300. result = false;
  301. }
  302. return result;
  303. }
  304. /// <summary>
  305. /// 写入多个REAL值到保持寄存器(功能码16)
  306. /// </summary>
  307. /// <param name="slaveId">从站ID</param>
  308. /// <param name="startAddress">起始地址</param>
  309. /// <param name="values">REAL值数组</param>
  310. /// <param name="byteOrder">字节序 (ABCD, CDAB, BADC, DCBA)</param>
  311. public bool WriteMultipleReals(byte slaveId, ushort startAddress, float[] values, string byteOrder = "BADC")
  312. {
  313. bool result = false;
  314. if (_modbusMaster == null)
  315. {
  316. Console.WriteLine($"ModbusTcpClient-WriteMultipleReals failed:未连接到服务器");
  317. return false;
  318. }
  319. if (values == null || values.Length == 0)
  320. {
  321. Console.WriteLine("写入多个REAL值到保持寄存器-值数组不能为空", 0);
  322. return false;
  323. }
  324. try
  325. {
  326. // 每个REAL值需要2个寄存器,所以总寄存器数是REAL值数量的2倍
  327. ushort[] registers = new ushort[values.Length * 2];
  328. for (int i = 0; i < values.Length; i++)
  329. {
  330. // 将每个float转换为字节数组
  331. byte[] bytes = BitConverter.GetBytes(values[i]);
  332. // 根据字节序转换为寄存器值
  333. ushort[] regPair = ConvertBytesToRegisters(bytes, byteOrder);
  334. // 将寄存器值放入数组
  335. registers[i * 2] = regPair[0];
  336. registers[i * 2 + 1] = regPair[1];
  337. }
  338. // 写入所有寄存器
  339. _modbusMaster.WriteMultipleRegisters(slaveId, startAddress, registers);
  340. result = true;
  341. }
  342. catch (Exception ex)
  343. {
  344. Console.WriteLine($"写入多个REAL值失败: {ex.Message}");
  345. result = false;
  346. }
  347. return result;
  348. }
  349. /// <summary>
  350. /// 写入单个Int32值到保持寄存器(功能码16)
  351. /// </summary>
  352. /// <param name="slaveId">从站ID</param>
  353. /// <param name="startAddress">起始地址</param>
  354. /// <param name="value">REAL值</param>
  355. /// <param name="byteOrder">字节序 (ABCD, CDAB, BADC, DCBA)</param>
  356. public bool WriteSingleInt32(byte slaveId, ushort startAddress, UInt32 value, string byteOrder = "BADC")
  357. {
  358. // 将32位整数拆分为两个16位寄存器值
  359. byte[] bytes = BitConverter.GetBytes(value);
  360. ushort[] registers = new ushort[2];
  361. ushort[] regPair = ConvertBytesToRegisters(bytes, byteOrder);
  362. // 将寄存器值放入数组
  363. registers[0] = regPair[0];
  364. registers[1] = regPair[1];
  365. return WriteMultipleRegisters(slaveId, startAddress, registers);
  366. }
  367. #endregion
  368. #region 辅助方法
  369. /// <summary>
  370. /// 将寄存器数组换为字节数组转(根据指定字节序)
  371. /// </summary>
  372. /// <param name="lowRegister">低位寄存器</param>
  373. /// <param name="highRegister">高位寄存器</param>
  374. /// <param name="byteOrder">字节序</param>
  375. /// <returns>字节数组</returns>
  376. private byte[] ConvertToByteArray(ushort lowRegister, ushort highRegister, string byteOrder)
  377. {
  378. byte[] bytes = new byte[4];
  379. switch (byteOrder.ToUpper())
  380. {
  381. case "ABCD": // 大端序
  382. bytes[0] = (byte)(highRegister >> 8);
  383. bytes[1] = (byte)(highRegister & 0xFF);
  384. bytes[2] = (byte)(lowRegister >> 8);
  385. bytes[3] = (byte)(lowRegister & 0xFF);
  386. break;
  387. case "CDAB": // 小端序
  388. bytes[0] = (byte)(lowRegister >> 8);
  389. bytes[1] = (byte)(lowRegister & 0xFF);
  390. bytes[2] = (byte)(highRegister >> 8);
  391. bytes[3] = (byte)(highRegister & 0xFF);
  392. break;
  393. case "BADC":
  394. bytes[0] = (byte)(highRegister & 0xFF);
  395. bytes[1] = (byte)(highRegister >> 8);
  396. bytes[2] = (byte)(lowRegister & 0xFF);
  397. bytes[3] = (byte)(lowRegister >> 8);
  398. break;
  399. case "DCBA":
  400. bytes[0] = (byte)(lowRegister & 0xFF);
  401. bytes[1] = (byte)(lowRegister >> 8);
  402. bytes[2] = (byte)(highRegister & 0xFF);
  403. bytes[3] = (byte)(highRegister >> 8);
  404. break;
  405. default:
  406. // 默认使用ABCD顺序
  407. bytes[0] = (byte)(highRegister >> 8);
  408. bytes[1] = (byte)(highRegister & 0xFF);
  409. bytes[2] = (byte)(lowRegister >> 8);
  410. bytes[3] = (byte)(lowRegister & 0xFF);
  411. break;
  412. }
  413. return bytes;
  414. }
  415. /// <summary>
  416. /// 将字节数组转换为寄存器数组(根据指定字节序)
  417. /// </summary>
  418. /// <param name="bytes">字节数组(4字节)</param>
  419. /// <param name="byteOrder">字节序</param>
  420. /// <returns>寄存器数组(2个ushort)</returns>
  421. private ushort[] ConvertBytesToRegisters(byte[] bytes, string byteOrder)
  422. {
  423. if (bytes.Length != 4)
  424. throw new ArgumentException("字节数组必须包含4个字节用于REAL值转换");
  425. ushort[] registers = new ushort[2];
  426. switch (byteOrder.ToUpper())
  427. {
  428. case "ABCD": // 大端序
  429. registers[0] = (ushort)((bytes[0] << 8) | bytes[1]); // 高位寄存器
  430. registers[1] = (ushort)((bytes[2] << 8) | bytes[3]); // 低位寄存器
  431. break;
  432. case "CDAB": // 小端序
  433. registers[0] = (ushort)((bytes[2] << 8) | bytes[3]); // 高位寄存器
  434. registers[1] = (ushort)((bytes[0] << 8) | bytes[1]); // 低位寄存器
  435. break;
  436. case "BADC":
  437. registers[0] = (ushort)((bytes[1] << 8) | bytes[0]); // 高位寄存器
  438. registers[1] = (ushort)((bytes[3] << 8) | bytes[2]); // 低位寄存器
  439. break;
  440. case "DCBA":
  441. registers[0] = (ushort)((bytes[3] << 8) | bytes[2]); // 高位寄存器
  442. registers[1] = (ushort)((bytes[1] << 8) | bytes[0]); // 低位寄存器
  443. break;
  444. default:
  445. // 默认使用ABCD顺序
  446. registers[0] = (ushort)((bytes[0] << 8) | bytes[1]);
  447. registers[1] = (ushort)((bytes[2] << 8) | bytes[3]);
  448. break;
  449. }
  450. return registers;
  451. }
  452. #endregion
  453. }
  454. }