using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Linq; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; using System.Threading.Tasks; namespace FLY.Modbus.WithThread { /// <summary> /// 基于线程,所有动作都会堵塞,必须确保只有一个Task操作 /// </summary> public class ClientTCP : INotifyPropertyChanged, IModbusClient { /// <summary> /// 通信超时, 默认是 1s /// </summary> public TimeSpan Timeout { get; set; } = TimeSpan.FromSeconds(6); /// <summary> /// socket 错误信息 /// </summary> public string ErrMsg { get; set; } /// <summary> /// 连接成功 /// </summary> public bool IsConnected { get; set; } /// <summary> /// 标识,modbus tcp的参数 /// </summary> public byte UnitID = 1; UInt16 tranid = 12; UInt16 GetFreeTranID() { tranid++; return tranid; } /// <summary> /// 远端地址 /// </summary> public IPEndPoint RemoteEP { get; set; } public string RemoteAddr { get { if (RemoteEP != null) { return RemoteEP.ToString(); } return null; } } Socket sock; public event PropertyChangedEventHandler PropertyChanged; public ClientTCP(IPEndPoint ep) { RemoteEP = ep; } /// <summary> /// 建立连接 /// </summary> public bool Connect() { lock (this) { if (IsConnected) return true; sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); try { sock.Connect(RemoteEP); IsConnected = true; return true; } catch (Exception e) { ErrMsg = e.Message; return false; } } } /// <summary> /// 停止 /// </summary> public void Close() { lock (this) { if (!IsConnected) return; sock.Close(); IsConnected = false; } } //会堵塞,必须使用一个线程执行 bool TranExecute(Pack_Proto request, out Pack_Proto reponse) { reponse = new Pack_Proto(); if (!IsConnected) return false; lock (this) { if (!IsConnected) return false; bool isError = false; try { sock.Send(request.ToBytes()); } catch (Exception e) { ErrMsg = e.Message; isError = true; goto _end; } byte[] recvBytes = new byte[sock.ReceiveBufferSize];//回复的指令不可能大于2K sock.ReceiveTimeout = (int)Timeout.TotalMilliseconds; int offset = 0; //读取数据 while (true) { int size = recvBytes.Length - offset; if (size <= 0) { //异常 ErrMsg = "TimeOut"; isError = true; goto _end; } int bytes=0; try { bytes = sock.Receive(recvBytes, offset, size, SocketFlags.None);//从客户端接受信息,可能会分了多次接收 } catch (SocketException e) { //异常 ErrMsg = e.Message; isError = true; goto _end; } if (bytes == 0) { //超时,或已经断开连接 ErrMsg = "TimeOut"; isError = true; goto _end; } //肯定只回复一条,只需要翻译一条 if (!reponse.TryParse(recvBytes, 0, bytes, out int rlen)) { //处理失败,数据不够,继续接收 offset = bytes; } else { //完成!!!! break; } } _end: if (isError) { sock.Close(); IsConnected = false; return false; } else { return true; } } } /// <summary> /// 读多个 COIL /// </summary> /// <param name="addr"></param> /// <param name="cnt"></param> /// <param name="values"></param> /// <returns></returns> public bool Do_01(int addr, int cnt, out IEnumerable<bool> values) { values = null; List<byte> data = new List<byte>(); data.AddRange(((UInt16)addr).GetBytes_Big_endian()); data.AddRange(((UInt16)cnt).GetBytes_Big_endian()); Pack_Proto request = new Pack_Proto() { tranid = GetFreeTranID(), unitid = UnitID, func = 0x01, buf = data.ToArray() }; if (!TranExecute(request, out Pack_Proto reponse)) return false; if (reponse.func != 0x01) { //有问题!!!触发异常事件 ErrMsg = "返回信息 reponse.func != 0x01"; return false; } List<bool> blist = new List<bool>(); int byte_len = reponse.buf[0]; int index = 1; for (int i = 0; i < byte_len; i++) { for (int j = 0; j < 8; j++) { if (Misc.MyBase.CHECKBIT(reponse.buf[index], j)) blist.Add(true); else blist.Add(false); if (blist.Count() >= cnt) { //完成 i = byte_len; break; } } index++; } values = blist; return true; } /// <summary> /// 读多个REG /// </summary> /// <param name="addr"></param> /// <param name="cnt"></param> /// <param name="values"></param> /// <returns></returns> public bool Do_03(int addr, int cnt, out IEnumerable<ushort> values) { values = null; List<byte> data = new List<byte>(); data.AddRange(((UInt16)addr).GetBytes_Big_endian()); data.AddRange(((UInt16)cnt).GetBytes_Big_endian()); Pack_Proto request = new Pack_Proto() { tranid = GetFreeTranID(), unitid = UnitID, func = 0x03, buf = data.ToArray() }; if (!TranExecute(request, out Pack_Proto reponse)) return false; if (reponse.func != 0x03) { //有问题!!!触发异常事件 ErrMsg = "返回信息 reponse.func != 0x03"; return false; } List<UInt16> blist = new List<UInt16>(); int index = 1; while (index < reponse.buf.Count()) { blist.Add(COMMON.ToUInt16_Big_endian(reponse.buf, index)); index += 2; } values = blist; return true; } /// <summary> /// Write Single Coil /// </summary> /// <param name="addr"></param> /// <param name="dat"></param> /// <returns></returns> public bool Do_05(int addr, bool dat) { List<byte> data = new List<byte>(); data.AddRange(((UInt16)addr).GetBytes_Big_endian()); if (dat) data.Add(0xff); else data.Add(0); data.Add(0); Pack_Proto request = new Pack_Proto() { tranid = GetFreeTranID(), unitid = UnitID, func = 0x05, buf = data.ToArray() }; if (!TranExecute(request, out Pack_Proto reponse)) return false; if (reponse.func != 0x05) { //有问题!!!触发异常事件 ErrMsg = "返回信息 reponse.func != 0x05"; return false; } return true; } /// <summary> /// 写多个coil /// </summary> /// <param name="addr"></param> /// <param name="datas"></param> /// <returns></returns> public bool Do_0F(int addr, IEnumerable<bool> datas) { List<byte> data = new List<byte>(); data.AddRange(((UInt16)addr).GetBytes_Big_endian()); data.AddRange(((UInt16)datas.Count()).GetBytes_Big_endian()); data.Add((byte)(Math.Ceiling(datas.Count() / 8.0))); byte b = 0; int j = 0; for (int i = 0; i < datas.Count(); i++) { if (datas.ElementAt(i)) Misc.MyBase.SIGNBIT(ref b, j); j++; if (j == 8) { data.Add(b); b = 0; j = 0; } } if (j != 0) data.Add(b); Pack_Proto request = new Pack_Proto() { tranid = GetFreeTranID(), unitid = UnitID, func = 0x0F, buf = data.ToArray() }; if (!TranExecute(request, out Pack_Proto reponse)) return false; if (reponse.func != 0x0F) { //有问题!!!触发异常事件 ErrMsg = "返回信息 reponse.func != 0x0F"; return false; } return true; } public bool Do_10(int addr, IEnumerable<ushort> datas) { List<byte> data = new List<byte>(); data.AddRange(((UInt16)addr).GetBytes_Big_endian()); data.AddRange(((UInt16)datas.Count()).GetBytes_Big_endian()); data.Add((byte)(datas.Count() * 2)); for (int i = 0; i < datas.Count(); i++) { data.AddRange(datas.ElementAt(i).GetBytes_Big_endian()); } Pack_Proto request = new Pack_Proto() { tranid = GetFreeTranID(), unitid = UnitID, func = 0x10, buf = data.ToArray() }; if (!TranExecute(request, out Pack_Proto reponse)) return false; if (reponse.func != 0x10) { //有问题!!!触发异常事件 ErrMsg = "返回信息 reponse.func != 0x10"; return false; } return true; } } }