using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Net; using System.ComponentModel; using Misc; namespace FLY.Modbus { public class ClientTCP:IModbusClient, INotifyPropertyChanged { private int maxConCurrTrans = 1; /// <summary> /// 最大同时处理交易数 /// </summary> public int MaxConCurrTrans { set { maxConCurrTrans = value; } get { return maxConCurrTrans; } } /// <summary> /// 当前交易数 /// </summary> public int ConCurrTrans { get { return Transactions.Count(); } } TCPManager.TCPCConn mTCPCConn; /// <summary> /// 错误事件 /// </summary> public event Action<Pack_Proto> ErrorEvent; /// <summary> /// 连接成功? /// </summary> public bool IsConnected { get { return mTCPCConn.IsConnected; } } /// <summary> /// 标识,modbus tcp的参数 /// </summary> public byte UnitID = 1; /// <summary> /// /// </summary> /// <param name="ip"></param> public ClientTCP(IPAddress ip):this(new IPEndPoint(ip,502)) { } /// <summary> /// /// </summary> /// <param name="ep"></param> public ClientTCP(IPEndPoint ep):this(ep,1) { } public override string ToString() { return mTCPCConn.RemoteEP.ToString(); } /// <summary> /// /// </summary> /// <param name="ep"></param> /// <param name="unitid"></param> public ClientTCP(IPEndPoint ep,byte unitid) { mTCPCConn = new TCPManager.TCPCConn(ep, ParsePacketInClient); mTCPCConn.Enable = true; mTCPCConn.ConnectEvent += new Action<TCPManager.TCPConn>(mTCPCConn_ConnectEvent); MaxConCurrTrans = 1; UnitID = unitid; } void mTCPCConn_ConnectEvent(TCPManager.TCPConn obj) { //清空全部tranid Transactions.Clear(); NotifyPropertyChanged("IsConnected"); } /// <summary> /// 从异端接收的数据,解释数据 /// </summary> /// <param name="conn"></param> /// <param name="inBuf"></param> /// <returns></returns> int ParsePacketInClient(TCPManager.TCPConn conn, byte[] inBuf) { //格式—————————————— //MBAP //域 长度 描述 //事务标识符 2byte MODBUS请求/响应的识别码 //协议标识符 2byte 0=MODBUS协议 //——长度— 2byte 以下字节的数量 //单元标识符 1byte 串行链路或其它总线过来的识别码 //PDU //功能码—— 1byte //数据 nbyte int startIndex=0; do { Pack_Proto p = new Pack_Proto(); int rlen; if (!p.TryParse(inBuf, startIndex, inBuf.Length, out rlen)) { //处理失败 return startIndex; } ParsePacket(conn, p); startIndex += rlen; } while (startIndex < inBuf.Length); return startIndex; } void ParsePacket(TCPManager.TCPConn conn, Pack_Proto p) { if (Misc.MyBase.CHECKBIT(p.func, 7)) { //出错!!! if (ErrorEvent != null) ErrorEvent(p); //根据tranid 找回 Transaction Transactions.RemoveAll(_t => _t.tranid == p.tranid); } //if (p.func == 0x01 || p.func == 0x03) { //根据tranid 找回 Transaction var tlist = from _t in Transactions where _t.tranid == p.tranid select _t; if (tlist.Count() > 0) { Transaction t = tlist.First(); t.ParsePacket(p); //if( (p.func == 0x05) || (p.func==0x01)) // Misc.Log.LogMessage("ReadCoilsAction", 0, "Receive Transid = {0} func= {1}",t.tranid, p.func); Transactions.RemoveAll(_t => _t.tranid == p.tranid); } } } class Transaction { public UInt16 tranid; public object AsyncDelegate; public object AsyncState; public Action<Pack_Proto> ParsePacket; } List<Transaction> Transactions = new List<Transaction>(); UInt16 tranid = 12; UInt16 GetFreeTranID() { tranid++; return tranid; } /// <summary> /// 读多个 COIL /// </summary> /// <param name="addr"></param> /// <param name="cnt"></param> /// <param name="AsyncDelegate"></param> /// <param name="AsyncState"></param> /// <returns></returns> public ModbusClient_Errno Do_01(UInt16 addr, UInt16 cnt, Action<bool[],object> AsyncDelegate, object AsyncState) { if (Transactions.Count() >= MaxConCurrTrans) return ModbusClient_Errno.TOO_MUCH_TRANS; List<byte> data = new List<byte>(); data.AddRange(addr.GetBytes_Big_endian()); data.AddRange(cnt.GetBytes_Big_endian()); Transaction t = new Transaction(); t.tranid = GetFreeTranID(); t.AsyncDelegate = AsyncDelegate; t.AsyncState = AsyncState; t.ParsePacket = new Action<Pack_Proto>(delegate(Pack_Proto p) { if (p.func != 0x01) { //有问题!!!触发异常事件 return; } List<bool> blist = new List<bool>(); int byte_len = p.buf[0]; int index = 1; for (int i = 0; i < byte_len; i++) { for (int j = 0; j < 8; j++) { if (Misc.MyBase.CHECKBIT(p.buf[index], j)) blist.Add(true); else blist.Add(false); if (blist.Count() >= cnt) { //完成 i = byte_len; break; } } index++; } Transactions.Remove(t); //Misc.Log.LogMessage("ReadCoilsAction", 0, "Receive Coils Transid = "+p.tranid.ToString()); ((Action<bool[], object>)t.AsyncDelegate)(blist.ToArray(), t.AsyncState); }); Transactions.Add(t); //Misc.Log.LogMessage("ReadCoilsAction", 0, "Send Coils Transid = " + t.tranid.ToString()); mTCPCConn.SendPacket( new Pack_Proto() { tranid = t.tranid, unitid = UnitID, func = 0x01, buf = data.ToArray() }.ToBytes()); return ModbusClient_Errno.OK; } /// <summary> /// 读多个REG /// </summary> /// <param name="addr"></param> /// <param name="cnt"></param> /// <param name="AsyncDelegate"></param> /// <param name="AsyncState"></param> /// <returns></returns> public ModbusClient_Errno Do_03(UInt16 addr, UInt16 cnt, Action<UInt16[], object> AsyncDelegate, object AsyncState) { if (Transactions.Count() >= MaxConCurrTrans) return ModbusClient_Errno.TOO_MUCH_TRANS; List<byte> data = new List<byte>(); data.AddRange(addr.GetBytes_Big_endian()); data.AddRange(cnt.GetBytes_Big_endian()); Transaction t = new Transaction(); t.tranid = GetFreeTranID(); t.AsyncDelegate = AsyncDelegate; t.AsyncState = AsyncState; t.ParsePacket = new Action<Pack_Proto>(delegate(Pack_Proto p) { if (p.func != 0x03) { //有问题!!!触发异常事件 return; } List<UInt16> blist = new List<UInt16>(); int index = 1; while(index<p.buf.Count()) { blist.Add(COMMON.ToUInt16_Big_endian(p.buf, index)); index += 2; } Transactions.Remove(t); ((Action<UInt16[], object>)t.AsyncDelegate)(blist.ToArray(), t.AsyncState); }); Transactions.Add(t); mTCPCConn.SendPacket( new Pack_Proto() { tranid = t.tranid, unitid = UnitID, func = 0x03, buf = data.ToArray() }.ToBytes()); return ModbusClient_Errno.OK; } /// <summary> /// Write Single Coil /// </summary> /// <param name="addr"></param> /// <param name="dat"></param> /// <returns></returns> public ModbusClient_Errno Do_05(UInt16 addr, bool dat) { if (Transactions.Count() >= MaxConCurrTrans) return ModbusClient_Errno.TOO_MUCH_TRANS; List<byte> data = new List<byte>(); data.AddRange(addr.GetBytes_Big_endian()); if (dat) data.Add(0xff); else data.Add(0); data.Add(0); Transaction t = new Transaction(); t.tranid = GetFreeTranID(); t.AsyncDelegate = null; t.AsyncState = null; t.ParsePacket = new Action<Pack_Proto>(delegate(Pack_Proto p) { if (p.func != 0x05) { //有问题!!!触发异常事件 return; } Transactions.Remove(t); }); Transactions.Add(t); mTCPCConn.SendPacket( new Pack_Proto() { tranid = t.tranid, unitid = UnitID, func = 0x05, buf = data.ToArray() }.ToBytes()); return ModbusClient_Errno.OK; } /// <summary> /// Write Single Coil,带返回 /// </summary> /// <param name="addr"></param> /// <param name="dat"></param> /// <param name="AsyncDelegate"></param> /// <param name="AsyncState"></param> /// <returns></returns> public ModbusClient_Errno Do_05(UInt16 addr, bool dat, Action<object> AsyncDelegate, object AsyncState) { if (Transactions.Count() >= MaxConCurrTrans) return ModbusClient_Errno.TOO_MUCH_TRANS; List<byte> data = new List<byte>(); data.AddRange(addr.GetBytes_Big_endian()); if (dat) data.Add(0xff); else data.Add(0); data.Add(0); Transaction t = new Transaction(); t.tranid = GetFreeTranID(); t.AsyncDelegate = AsyncDelegate; t.AsyncState = AsyncState; t.ParsePacket = new Action<Pack_Proto>(delegate(Pack_Proto p) { if (p.func != 0x05) { //有问题!!!触发异常事件 return; } Transactions.Remove(t); ((Action<object>)t.AsyncDelegate)(t.AsyncState); }); Transactions.Add(t); mTCPCConn.SendPacket( new Pack_Proto() { tranid = t.tranid, unitid = UnitID, func = 0x05, buf = data.ToArray() }.ToBytes()); return ModbusClient_Errno.OK; } /// <summary> /// 写多个coil /// </summary> /// <param name="addr"></param> /// <param name="datas"></param> /// <returns></returns> public ModbusClient_Errno Do_0F(UInt16 addr, bool[] datas) { if (Transactions.Count() >= MaxConCurrTrans) return ModbusClient_Errno.TOO_MUCH_TRANS; List<byte> data = new List<byte>(); data.AddRange(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[i]) Misc.MyBase.SIGNBIT(ref b, j); j++; if (j == 8) { data.Add(b); b = 0; j = 0; } } if (j != 0) data.Add(b); Transaction t = new Transaction(); t.tranid = GetFreeTranID(); t.AsyncDelegate = null; t.AsyncState = null; t.ParsePacket = new Action<Pack_Proto>(delegate(Pack_Proto p) { if (p.func != 0x10) { //有问题!!!触发异常事件 return; } Transactions.Remove(t); }); Transactions.Add(t); mTCPCConn.SendPacket( new Pack_Proto() { tranid = t.tranid, unitid = UnitID, func = 0x0F, buf = data.ToArray() }.ToBytes()); return ModbusClient_Errno.OK; } /// <summary> /// 写多个coil,带返回 /// </summary> /// <param name="addr"></param> /// <param name="datas"></param> /// <param name="AsyncDelegate"></param> /// <param name="AsyncState"></param> /// <returns></returns> public ModbusClient_Errno Do_0F(UInt16 addr, bool[] datas, Action<object> AsyncDelegate, object AsyncState) { if (Transactions.Count() >= MaxConCurrTrans) return ModbusClient_Errno.TOO_MUCH_TRANS; List<byte> data = new List<byte>(); data.AddRange(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[i]) Misc.MyBase.SIGNBIT(ref b, j); j++; if (j == 8) { data.Add(b); b = 0; j = 0; } } if (j != 0) data.Add(b); Transaction t = new Transaction(); t.tranid = GetFreeTranID(); t.AsyncDelegate = AsyncDelegate; t.AsyncState = AsyncState; t.ParsePacket = new Action<Pack_Proto>(delegate(Pack_Proto p) { if (p.func != 0x0f) { //有问题!!!触发异常事件 return; } Transactions.Remove(t); ((Action<object>)t.AsyncDelegate)(t.AsyncState); }); Transactions.Add(t); mTCPCConn.SendPacket( new Pack_Proto() { tranid = t.tranid, unitid = UnitID, func = 0x0F, buf = data.ToArray() }.ToBytes()); return ModbusClient_Errno.OK; } /// <summary> /// 写多个REG /// </summary> /// <param name="addr"></param> /// <param name="datas"></param> /// <returns></returns> public ModbusClient_Errno Do_10(UInt16 addr, UInt16[] datas) { if (Transactions.Count() >= MaxConCurrTrans) return ModbusClient_Errno.TOO_MUCH_TRANS; List<byte> data = new List<byte>(); data.AddRange(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[i].GetBytes_Big_endian()); } Transaction t = new Transaction(); t.tranid = GetFreeTranID(); t.AsyncDelegate = null; t.AsyncState = null; t.ParsePacket = new Action<Pack_Proto>(delegate(Pack_Proto p) { if (p.func != 0x10) { //有问题!!!触发异常事件 return; } Transactions.Remove(t); }); Transactions.Add(t); mTCPCConn.SendPacket( new Pack_Proto() { tranid = t.tranid, unitid = UnitID, func = 0x10, buf = data.ToArray() }.ToBytes()); return ModbusClient_Errno.OK; } /// <summary> /// 写多个REG,带返回 /// </summary> /// <param name="addr"></param> /// <param name="datas"></param> /// <param name="AsyncDelegate"></param> /// <param name="AsyncState"></param> /// <returns></returns> public ModbusClient_Errno Do_10(UInt16 addr, UInt16[] datas, Action<object> AsyncDelegate, object AsyncState) { if (Transactions.Count() >= MaxConCurrTrans) return ModbusClient_Errno.TOO_MUCH_TRANS; List<byte> data = new List<byte>(); data.AddRange(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[i].GetBytes_Big_endian()); } Transaction t = new Transaction(); t.tranid = GetFreeTranID(); t.AsyncDelegate = AsyncDelegate; t.AsyncState = AsyncState; t.ParsePacket = new Action<Pack_Proto>(delegate(Pack_Proto p) { if (p.func != 0x10) { //有问题!!!触发异常事件 return; } Transactions.Remove(t); ((Action<object>)t.AsyncDelegate)( t.AsyncState); }); Transactions.Add(t); mTCPCConn.SendPacket( new Pack_Proto() { tranid = t.tranid, unitid = UnitID, func = 0x10, buf = data.ToArray() }.ToBytes()); return ModbusClient_Errno.OK; } /// <summary> /// /// </summary> /// <param name="propertyname"></param> protected void NotifyPropertyChanged(string propertyname) { if (PropertyChanged != null) { PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyname)); } } /// <summary> /// /// </summary> public event PropertyChangedEventHandler PropertyChanged; } }