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
{
///
/// 基于线程,所有动作都会堵塞,必须确保只有一个Task操作
///
public class ClientTCP:INotifyPropertyChanged,IModbusClient
{
///
/// 通信超时, 默认是 1s
///
public TimeSpan Timeout { get; set; } = TimeSpan.FromSeconds(6);
///
/// socket 错误信息
///
public string ErrMsg { get; set; }
///
/// 连接成功
///
public bool IsConnected { get; set; }
///
/// 标识,modbus tcp的参数
///
public byte UnitID = 1;
UInt16 tranid = 12;
UInt16 GetFreeTranID()
{
tranid++;
return tranid;
}
///
/// 远端地址
///
public IPEndPoint RemoteEP { get; set; }
Socket sock;
public event PropertyChangedEventHandler PropertyChanged;
public ClientTCP(IPEndPoint ep)
{
RemoteEP = ep;
}
///
/// 建立连接
///
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;
}
}
}
///
/// 停止
///
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;
}
}
}
///
/// 读多个 COIL
///
///
///
///
///
public bool Do_01(int addr, int cnt, out IEnumerable values)
{
values = null;
List data = new List();
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 blist = new List();
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;
}
///
/// 读多个REG
///
///
///
///
///
public bool Do_03(int addr, int cnt, out IEnumerable values)
{
values = null;
List data = new List();
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 blist = new List();
int index = 1;
while (index < reponse.buf.Count())
{
blist.Add(COMMON.ToUInt16_Big_endian(reponse.buf, index));
index += 2;
}
values = blist;
return true;
}
///
/// Write Single Coil
///
///
///
///
public bool Do_05(int addr, bool dat)
{
List data = new List();
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;
}
///
/// 写多个coil
///
///
///
///
public bool Do_0F(int addr, IEnumerable datas)
{
List data = new List();
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 datas)
{
List data = new List();
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;
}
}
}