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

492 lines
17 KiB

  1. using BPASmartClient.Helper;
  2. using BPASmartClient.Message;
  3. using NModbus;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Linq;
  7. using System.Net.Sockets;
  8. using System.Text;
  9. using System.Threading;
  10. using System.Threading.Tasks;
  11. namespace BPASmartClient.Modbus
  12. {
  13. public class ModbusTcp
  14. {
  15. //private volatile static ModbusTcp _Instance;
  16. //public static ModbusTcp GetInstance => _Instance ?? (_Instance = new ModbusTcp());
  17. //private ModbusTcp() { }
  18. private ModbusFactory modbusFactory;
  19. private IModbusMaster master;
  20. private TcpClient tcpClient;
  21. private string IPAdress;
  22. private int Port;
  23. public Action ConnectOk { get; set; }
  24. public Action ConnectFail { get; set; }
  25. public Action Disconnect { get; set; }
  26. public bool IsReconnect { get; set; } = true; //true=启用重连,false=禁用重连
  27. /// <summary>
  28. /// 判断是否连接成功
  29. /// </summary>
  30. public bool Connected => tcpClient == null ? false : tcpClient.Connected;
  31. /// <summary>
  32. /// ModbusTcp 连接设备
  33. /// </summary>
  34. /// <param name="ip">ip 地址</param>
  35. /// <param name="port">端口号,默认502</param>
  36. public void ModbusTcpConnect(string ip, int port = 502)
  37. {
  38. MessageLog.GetInstance.Show($"设备【{ip}:{port}】连接中。。。。");
  39. IPAdress = ip;
  40. Port = port;
  41. modbusFactory = new ModbusFactory();
  42. Connect();
  43. if (Connected)
  44. {
  45. master.Transport.ReadTimeout = 2000;//读取超时时间
  46. master.Transport.WriteTimeout = 2000;//写入超时时间
  47. master.Transport.Retries = 10;//重试次数
  48. ConnectOk?.Invoke();
  49. MessageLog.GetInstance.Show($"设备【{ip}:{port}】连接成功");
  50. }
  51. else
  52. {
  53. ConnectFail?.Invoke();
  54. }
  55. }
  56. public void Dispose()
  57. {
  58. master.Dispose();
  59. Disconnect?.Invoke();
  60. }
  61. private void Connect()
  62. {
  63. bool ErrorFlag = false;
  64. while (!Connected)
  65. {
  66. try
  67. {
  68. tcpClient = new TcpClient(IPAdress, Port);
  69. master = modbusFactory.CreateMaster(tcpClient);
  70. }
  71. catch (Exception ex)
  72. {
  73. if (!ErrorFlag)
  74. {
  75. MessageLog.GetInstance.ShowEx($"ModbusTcp 连接失败,IP = {IPAdress},Port = {Port}");
  76. MessageLog.GetInstance.ShowEx(ex.ToString());
  77. ErrorFlag = true;
  78. }
  79. if (!IsReconnect) break;
  80. Thread.Sleep(3000);
  81. }
  82. }
  83. if (ErrorFlag) MessageLog.GetInstance.Show("ModbusTcp 重连成功!");
  84. }
  85. /// <summary>
  86. /// 获取 M 区 和 VW 区的Modbus的地址
  87. /// </summary>
  88. /// <param name="address"></param>
  89. /// <returns></returns>
  90. public int GetAddress(string address)
  91. {
  92. if (address == null) return -1;
  93. if (address.Length > 0)
  94. {
  95. if (address.ToUpper().Contains("M") && address.Length >= 4)
  96. {
  97. var res = address.Substring(1).Split('.');
  98. if (res != null && res.Length == 2)
  99. {
  100. if (int.TryParse(res[0], out int firstAddress) && int.TryParse(res[1], out int ExitAddress))
  101. {
  102. if (ExitAddress >= 0 && ExitAddress <= 7)
  103. {
  104. return (firstAddress * 8) + 320 + ExitAddress;
  105. }
  106. }
  107. }
  108. }
  109. else if (address.ToUpper().Contains("VW") && address.Length >= 3)
  110. {
  111. var res = address.Substring(2);
  112. if (res != null && int.TryParse(res, out int tempAddress))
  113. {
  114. return (tempAddress / 2) + 100;
  115. }
  116. }
  117. }
  118. return -1;
  119. }
  120. private void ExceptionHandling(Exception ex)
  121. {
  122. if (ex.InnerException is SocketException)
  123. {
  124. if (IsReconnect)
  125. {
  126. tcpClient = null;
  127. Connect();
  128. }
  129. Disconnect?.Invoke();
  130. }
  131. }
  132. public object Read(string address, ushort len = 1, byte slaveAddress = 1)
  133. {
  134. if (address == null || tcpClient == null) return default(object);
  135. ushort startAddress = (ushort)GetAddress(address);
  136. CommandType commandType = CommandType.Coils;
  137. try
  138. {
  139. if (address.ToUpper().Contains("M"))
  140. {
  141. commandType = CommandType.Coils;
  142. return master.ReadCoils(slaveAddress, startAddress, len);
  143. }
  144. else if (address.ToUpper().Contains("VW"))
  145. {
  146. commandType = CommandType.HoldingRegisters;
  147. return master.ReadHoldingRegisters(slaveAddress, startAddress, len);
  148. }
  149. else if (address.ToUpper().Contains("I"))
  150. {
  151. commandType = CommandType.Inputs;
  152. return master.ReadInputs(slaveAddress, startAddress, len);
  153. }
  154. }
  155. catch (Exception ex)
  156. {
  157. MessageLog.GetInstance.ShowEx($"读取地址:【{address}:= {startAddress}】,读取类型:【{commandType.ToString()}】出错,{ex.ToString()}");
  158. ExceptionHandling(ex);
  159. }
  160. return default(object);
  161. }
  162. public void Write<T>(string address, T value, byte slaveAddress = 1)
  163. {
  164. if (address == null || tcpClient == null) return;
  165. ushort startAddress = (ushort)GetAddress(address);
  166. CommandType commandType = CommandType.Coils;
  167. try
  168. {
  169. if (address.ToUpper().Contains("M"))
  170. {
  171. commandType = CommandType.Coils;
  172. if (value is bool boolValue)
  173. master.WriteSingleCoil(slaveAddress, startAddress, boolValue);
  174. else if (value is bool[] boolsValue)
  175. master.WriteMultipleCoils(slaveAddress, startAddress, boolsValue);
  176. }
  177. else if (address.ToUpper().Contains("VW"))
  178. {
  179. commandType = CommandType.HoldingRegisters;
  180. if (value is ushort ushortValue)
  181. master.WriteSingleRegister(slaveAddress, startAddress, ushortValue);
  182. else if (value is ushort[] ushortsValue)
  183. {
  184. int len = 100;
  185. if (ushortsValue.Length > len)
  186. {
  187. List<ushort[]> ushortLists = new List<ushort[]>();
  188. for (int i = 0; i < ushortsValue.Length / len; i++)
  189. {
  190. ushortLists.Add(ushortsValue.Skip(0).Take(len).ToArray());
  191. }
  192. int y = ushortsValue.Length % len;
  193. if (y > 0)
  194. {
  195. ushortLists.Add(ushortsValue.Skip(ushortsValue.Length - y).Take(y).ToArray());
  196. }
  197. foreach (var item in ushortLists)
  198. {
  199. master.WriteMultipleRegisters(slaveAddress, startAddress, item);
  200. startAddress += (ushort)item.Length;
  201. }
  202. }
  203. else
  204. {
  205. master.WriteMultipleRegisters(slaveAddress, startAddress, ushortsValue);
  206. }
  207. }
  208. }
  209. else if (address.ToUpper().Contains("I"))
  210. {
  211. commandType = CommandType.Inputs;
  212. }
  213. }
  214. catch (Exception ex)
  215. {
  216. MessageLog.GetInstance.ShowEx($"写入地址:【{address}:= {startAddress}】,写入类型:【{commandType.ToString()}】出错,{ex.ToString()}");
  217. ExceptionHandling(ex);
  218. }
  219. }
  220. #region 字符串数据读写
  221. /// <summary>
  222. /// 赋值string
  223. /// </summary>
  224. /// <param name="StartAddress"></param>
  225. /// <param name="value"></param>
  226. /// <returns></returns>
  227. public void SetString(string StartAddress, string value)
  228. {
  229. var bytes = Encoding.UTF8.GetBytes(value);
  230. Write(StartAddress, bytes.BytesToUshorts());
  231. }
  232. /// <summary>
  233. /// 获取string
  234. /// </summary>
  235. /// <param name="StartAddress"></param>
  236. /// <param name="len"></param>
  237. /// <returns></returns>
  238. public string GetString(string StartAddress, ushort len)
  239. {
  240. var res = Read(StartAddress, len);
  241. if (res != null && res is ushort[] ushorts)
  242. {
  243. return Encoding.UTF8.GetString(ushorts.UshortsToBytes()).Trim(new char[] { '\0' });
  244. }
  245. return String.Empty;
  246. }
  247. #endregion
  248. #region 浮点数数据读写
  249. /// <summary>
  250. /// 赋值Real类型数据
  251. /// </summary>
  252. /// <param name="StartAddress"></param>
  253. /// <param name="value"></param>
  254. public void SetReal(string StartAddress, float value)
  255. {
  256. var bytes = BitConverter.GetBytes(value);
  257. Write(StartAddress, bytes.BytesToUshorts());
  258. }
  259. /// <summary>
  260. /// 获取float类型数据
  261. /// </summary>
  262. /// <param name="StartAddress"></param>
  263. /// <returns></returns>
  264. public float GetReal(string StartAddress)
  265. {
  266. var res = Read(StartAddress, 2);
  267. if (res != null && res is ushort[] ushorts)
  268. {
  269. return BitConverter.ToSingle(ushorts.UshortsToBytes(), 0);
  270. }
  271. return 0;
  272. }
  273. #endregion
  274. #region 批量数据读取
  275. /// <summary>
  276. /// 读取多个线圈
  277. /// </summary>
  278. /// <param name="startAddress"></param>
  279. /// <param name="num"></param>
  280. /// <param name="slaveAddress"></param>
  281. /// <returns></returns>
  282. public bool[] ReadCoils(ushort startAddress, ushort num, byte slaveAddress = 1)
  283. {
  284. return master.ReadCoils(slaveAddress, startAddress, num);
  285. }
  286. /// <summary>
  287. /// 读取多个输入线圈
  288. /// </summary>
  289. /// <param name="startAddress"></param>
  290. /// <param name="num"></param>
  291. /// <param name="slaveAddress"></param>
  292. /// <returns></returns>
  293. public bool[] ReadInputs(ushort startAddress, ushort num, byte slaveAddress = 1)
  294. {
  295. return master.ReadInputs(slaveAddress, startAddress, num);
  296. }
  297. /// <summary>
  298. /// 读取多个保持寄存器
  299. /// </summary>
  300. /// <param name="startAddress"></param>
  301. /// <param name="num"></param>
  302. /// <param name="slaveAddress"></param>
  303. /// <returns></returns>
  304. public ushort[] ReadHoldingRegisters(ushort startAddress, ushort num, byte slaveAddress = 1)
  305. {
  306. return master.ReadHoldingRegisters(slaveAddress, startAddress, num);
  307. }
  308. /// <summary>
  309. /// 读取多个输入寄存器
  310. /// </summary>
  311. /// <param name="startAddress"></param>
  312. /// <param name="num"></param>
  313. /// <param name="slaveAddress"></param>
  314. /// <returns></returns>
  315. public ushort[] ReadInputRegisters(ushort startAddress, ushort num, byte slaveAddress = 1)
  316. {
  317. return master.ReadInputRegisters(slaveAddress, startAddress, num);
  318. }
  319. #endregion
  320. #region 批量数据写入
  321. /// <summary>
  322. /// 写入多个线圈
  323. /// </summary>
  324. /// <param name="startAddress"></param>
  325. /// <param name="value"></param>
  326. /// <param name="slaveAddress"></param>
  327. public void WriteMultipleCoils(ushort startAddress, bool[] value, byte slaveAddress = 1)
  328. {
  329. master.WriteMultipleCoils(slaveAddress, startAddress, value);
  330. }
  331. /// <summary>
  332. /// 写入多个寄存器
  333. /// </summary>
  334. /// <param name="startAddress"></param>
  335. /// <param name="value"></param>
  336. /// <param name="slaveAddress"></param>
  337. public void WriteMultipleRegisters(ushort startAddress, ushort[] value, byte slaveAddress = 1)
  338. {
  339. master.WriteMultipleRegisters(slaveAddress, startAddress, value);
  340. }
  341. #endregion
  342. #region 单个数据读取
  343. /// <summary>
  344. /// 读取单个线圈
  345. /// </summary>
  346. /// <param name="startAddress"></param>
  347. /// <param name="slaveAddress"></param>
  348. /// <returns></returns>
  349. public bool ReadCoils(ushort startAddress, byte slaveAddress = 1)
  350. {
  351. var result = master.ReadCoils(slaveAddress, startAddress, 1);
  352. if (result == null) return false;
  353. if (result.Length == 1) return result[0];
  354. return false;
  355. //return master.ReadCoils(slaveAddress, startAddress, 1)[0];
  356. }
  357. /// <summary>
  358. /// 读取单个输入线圈
  359. /// </summary>
  360. /// <param name="startAddress"></param>
  361. /// <param name="slaveAddress"></param>
  362. /// <returns></returns>
  363. public bool ReadInputs(ushort startAddress, byte slaveAddress = 1)
  364. {
  365. var result = master.ReadInputs(slaveAddress, startAddress, 1);
  366. if (result == null) return false;
  367. if (result.Length == 1) return result[0];
  368. return false;
  369. //return master.ReadInputs(slaveAddress, startAddress, 1)[0];
  370. }
  371. /// <summary>
  372. /// 读取单个保持寄存器
  373. /// </summary>
  374. /// <param name="startAddress"></param>
  375. /// <param name="slaveAddress"></param>
  376. /// <returns></returns>
  377. public ushort ReadHoldingRegisters(ushort startAddress, byte slaveAddress = 1)
  378. {
  379. if (tcpClient == null) return 0;
  380. ushort[] result = null;
  381. try
  382. {
  383. result = master.ReadHoldingRegisters(slaveAddress, startAddress, 1);
  384. }
  385. catch (Exception ex)
  386. {
  387. MessageLog.GetInstance.Show(ex.ToString());
  388. tcpClient = null;
  389. Connect();
  390. }
  391. if (result == null) return 0;
  392. if (result.Length == 1) return result[0];
  393. return 0;
  394. //return master.ReadHoldingRegisters(slaveAddress, startAddress, 1)[0];
  395. }
  396. /// <summary>
  397. /// 读取单个输入寄存器
  398. /// </summary>
  399. /// <param name="startAddress"></param>
  400. /// <param name="slaveAddress"></param>
  401. /// <returns></returns>
  402. public ushort ReadInputRegisters(ushort startAddress, byte slaveAddress = 1)
  403. {
  404. var result = master.ReadInputRegisters(slaveAddress, startAddress, 1);
  405. if (result == null) return 0;
  406. if (result.Length == 1) return result[0];
  407. return 0;
  408. //return master.ReadInputRegisters(slaveAddress, startAddress, 1)[0];
  409. }
  410. #endregion
  411. #region 单个数据写入
  412. /// <summary>
  413. /// 写入单个线圈
  414. /// </summary>
  415. /// <param name="startAddress"></param>
  416. /// <param name="value"></param>
  417. /// <param name="slaveAddress"></param>
  418. public void WriteSingleCoil(ushort startAddress, bool value, byte slaveAddress = 1)
  419. {
  420. master.WriteSingleCoil(slaveAddress, startAddress, value);
  421. }
  422. /// <summary>
  423. /// 写入单个寄存器
  424. /// </summary>
  425. /// <param name="startAddress"></param>
  426. /// <param name="value"></param>
  427. /// <param name="slaveAddress"></param>
  428. public void WriteSingleRegister(ushort startAddress, ushort value, byte slaveAddress = 1)
  429. {
  430. master.WriteSingleRegister(slaveAddress, startAddress, value);
  431. }
  432. #endregion
  433. }
  434. public enum CommandType
  435. {
  436. /// <summary>
  437. /// 线圈操作
  438. /// </summary>
  439. Coils,
  440. /// <summary>
  441. /// 输入线圈操作
  442. /// </summary>
  443. Inputs,
  444. /// <summary>
  445. /// 保持寄存器操作
  446. /// </summary>
  447. HoldingRegisters,
  448. /// <summary>
  449. /// 输入寄存器操作
  450. /// </summary>
  451. InputRegisters,
  452. }
  453. }