using NLog;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Windows.Threading;
using static GeneralGommunication.Modbus_Transaction;
namespace GeneralGommunication
{
///
/// 异步modbus ASCII
/// 3个线程会调用它:
/// 1.接收数据线程调用 RecMsg, func解码后,需要转为Dispatcher的线程触发func回复通知;
/// func解码后,也会触发SendMsgEvent事件;
/// 2.外部调用func,触发SendMsgEvent事件;
/// 3.超时定时器,触发SendMsgEvent事件;
///
public class ModbusAsciiAsync : IModbusAsciiAsync
{
public Logger logger = NLog.LogManager.GetCurrentClassLogger();
public event PropertyChangedEventHandler PropertyChanged;
///
/// 包出错次数
///
public int ErrCnt { get; protected set; }
///
/// 通讯速度测量模块
///
public CommSpeedMeasuring Csm => csm;
///
/// 有数据需要发送
///
public event SendDataEventHandler SendMsgEvent;
public event DeviceConnectEventHander DeviceConnectEvent;
List currPack = new List();
///
/// 指令队列,必须等上1条指令回复了,才能发下条指令
///
List Transactions;
///
/// 当前正在等待回复的指令
///
Modbus_Transaction currTran;
CommSpeedMeasuring csm = new CommSpeedMeasuring();
///
/// currTran 重发次数
///
int retryCnt = 0;
List deviceConnected = new List();
///
/// 对于全部 有返回的函数调用,都使用Dispatcher,使线程同步
///
Dispatcher dispatcher;
DispatcherTimer timerTimeOut;//等待回复超时
public ModbusAsciiAsync()
{
Transactions = new List();
timerTimeOut = new DispatcherTimer();
timerTimeOut.Interval = TimeSpan.FromSeconds(0.2);
timerTimeOut.Tick += TimerTimeOut_Tick;
dispatcher = timerTimeOut.Dispatcher;
}
///
/// 通讯中
///
public bool IsBusy()
{
lock (Transactions)
{
if (currTran != null)
return true;
if (Transactions.Count() > 0)
return true;
return false;
}
}
///
/// 检查发生列表是否包含某条指令
///
///
///
///
public bool Contain(int deviceNo, string desription)
{
lock (Transactions)
{
if (currTran != null)
{
if (currTran.deviceNo == deviceNo && currTran.desription == desription)
{
return true;
}
}
if (Transactions.Count() > 0)
{
return Transactions.Any(t => t.deviceNo == deviceNo && t.desription == desription);
}
else
{
return false;
}
}
}
private void TimerTimeOut_Tick(object sender, EventArgs e)
{
timerTimeOut.Stop();
RetrySendMsg();
}
public bool IsConnected(byte deviceNo)
{
if (deviceConnected.Contains(deviceNo))
return true;
return false;
}
void RetrySendMsg()
{
bool isNeedToSendMsg = false;
bool isDisconnected = false;
int deviceNo = 0;
lock (Transactions)
{
if (currTran == null)//没有数据需要发送
return;
//大于1秒也没回复,异常
//重试2次
retryCnt++;
if (retryCnt >= 3)
{
//已经发送了2次,放弃
AckErrNoRec($"第{retryCnt}次重试失败 放弃");
if (deviceConnected.Contains(currTran.deviceNo))
{
//之前连接是好的。
deviceConnected.Remove(currTran.deviceNo);
isDisconnected = true;
deviceNo = currTran.deviceNo;
}
//删除发送给这个设备的全部指令
Transactions.RemoveAll(t => t.deviceNo == currTran.deviceNo);
currTran = null;
retryCnt = 0;
if (Transactions.Count() != 0) //还要其它需要发送
{
isNeedToSendMsg = true;
}
}
else
{
AckErrNoRec($"第{retryCnt + 1}次重试");
//重新把指令放回队列
Transactions.Insert(0, currTran);
currTran = null;
//再发一次指令
isNeedToSendMsg = true;
}
}
if (isDisconnected) {
TriggerDeviceConnectEvent(new DeviceConnectEventArgs(){ deviceNo = deviceNo, isConnected = false, errMsg = "无回复"});
}
if (isNeedToSendMsg)
{
var buf = GetSendMsg();
TriggerSendMsgEvent(buf);
}
}
void TriggerDeviceConnectEvent(DeviceConnectEventArgs e) {
dispatcher.BeginInvoke(DeviceConnectEvent, this, e);
}
void TriggerSendMsgEvent(byte[] data)
{
dispatcher.BeginInvoke(SendMsgEvent, this, data);
}
void TriggerCurrTranAsyncDelegate()
{
//线程同步执行
dispatcher.BeginInvoke(currTran.asyncDelegate, currTran.asyncContext, currTran.retData);
}
public void RecMsg(byte[] recBuf)
{
//停止超时检查
timerTimeOut.Stop();
csm.IncRec(recBuf.Count());
bool isFinished = false;
bool isError = false;
lock (Transactions)
{
if (currTran == null)
{
//没有请求。。。
return;
}
if (currTran.EnableLog)
{
AckDebug_Rec(recBuf);
}
//这次需要判断的位置
int index = currPack.Count();
currPack.AddRange(recBuf);
for (int i = 0; i < recBuf.Count(); i++)
{
if (ParsePack(index + i, out isError))
{
//处理完,删除全部接收
currPack.Clear();
isFinished = true;
break;
}
}
}
if (isFinished)
{
if (isError)
{
//异常,需要重发
RetrySendMsg();
}
else
{
//成功解析出包
retryCnt = 0; //复位重发次数
ParsePack2();
}
return;
}
else
{
//还没接收完!!!!
timerTimeOut.Start();
}
}
///
/// 获取 发送队列 第1条msg
///
///
byte[] GetSendMsg()
{
timerTimeOut.Stop();
//TODO 要处理 TimeOut / ParseFuncPack / GetSendMsg 线性同步问题
lock (Transactions)
{
if (currTran == null)
{
//当前没有指令正在发送
if (Transactions.Count() == 0)//队列没有需要发送的指令
return null;
currTran = Transactions.First();
Transactions.RemoveAt(0);
}
else
{
//发送出去中,等待回复
return null;
}
//调试: 打印发送信息
if (currTran.EnableLog)
{
ReqDebug();
}
//开始计时
timerTimeOut.Start();
return currTran.sendBuf.ToArray();
}
}
///
/// 复位全部状态,通常由于通讯模块检测到连接断开导致的
///
public void ResetMsg()
{
lock (Transactions)
{
Transactions.Clear();
currTran = null;
}
retryCnt = 0;
timerTimeOut.Stop();
csm.Reset();
//触发通知
var isConneced = deviceConnected.ToArray();
deviceConnected.Clear();
foreach (var deviceNo in isConneced)
{
//触发通知
TriggerDeviceConnectEvent(new DeviceConnectEventArgs(){deviceNo = deviceNo,isConnected = false,errMsg= "链路断开" });
}
}
void ReqDebug()
{
if (logger.IsDebugEnabled)
{
string msg = currTran.sendBuf.ToString_Bytes2Hex();
string msg_ascii = currTran.sendBuf.ToString_Bytes2Ascii();
logger.Debug($"REQ {currTran.deviceNo:00} {currTran.desription} | {msg}| {msg_ascii}");
}
}
void AckDebug_Rec(IEnumerable recBuf)
{
if (logger.IsDebugEnabled)
{
string msg = recBuf.ToString_Bytes2Hex();
string msg_ascii = recBuf.ToString_Bytes2Ascii();
logger.Debug($"rec {currTran.deviceNo:00} {currTran.desription} | {msg}| {msg_ascii}");
}
ErrCnt++;
}
void AckErrNoRec(string errMsg)
{
if (logger.IsErrorEnabled)
{
logger.Error($"ACK {currTran.deviceNo:00} {currTran.desription} {errMsg}");
}
ErrCnt++;
}
void AckErr(string errMsg)
{
if (logger.IsErrorEnabled)
{
string msg = currPack.ToString_Bytes2Hex();
string msg_ascii = currPack.ToString_Bytes2Ascii();
logger.Error($"ACK {currTran.deviceNo:00} {currTran.desription} {errMsg} | {msg}| {msg_ascii}");
}
ErrCnt++;
}
void AckDebug(string debugMsg)
{
if (logger.IsDebugEnabled)
{
string msg = currTran.funcData.ToString_Bytes2Hex();
logger.Debug($"ACK {currTran.deviceNo:00} {currTran.desription} {debugMsg} | {msg}");
}
ErrCnt++;
}
///
/// 每个接收byte解析, 处理完,返回true;
/// 还需要更多byte,返回false
///
/// 当前处理的currPack[index]
bool ParsePack(int index, out bool isError)
{
isError = false;
if (index == 0)
{
if (currPack[index] != (byte)':')
{
//包头错,可以删除
AckErr("包头错");
isError = true;
return true;
}
}
else
{
//匹配 0x0d,0x0a
if (currPack[index] == 0x0a)
{
if (currPack[index - 1] != 0x0d)
{
//结尾出错, 可以删除
AckErr("包尾错");
isError = true;
return true;
}
//: \r\n 已经判断完
int len = index + 1;
if (len < 10)
{
AckErr("字节数太少");
isError = true;
return true;
}
byte? addr = currPack.Ascii2Hex(1);
if (addr == null)
{
AckErr("地址错");
isError = true;
return true;
}
if (addr != currTran.deviceNo)
{
//不是回复这里的
//AckErr("地址错");
isError = true;
return true;
}
//可以解析了
//转为hex
byte[] recBuf = currPack.Ascii2Hex(1, len - 3); //:,\d,\a 3个不要的字符
if (recBuf == null)
{
AckErr($"不能翻译为 ASCII");
isError = true;
return true;
}
byte lrc = recBuf.LRC8(0, recBuf.Count() - 1);
byte rec_lrc = recBuf[recBuf.Count() - 1];
if (lrc != rec_lrc)
{
AckErr($"LRC 校验出错 接收:{rec_lrc:X4} 计算:{lrc:X2}");
isError = true;
return true;
}
if (recBuf[1] != currTran.func)
{
AckErr($"错误码:{currPack[1]} 异常码:{currPack[2]}");
isError = true;
return true;
}
//if (len != currTran.expRecBytes)
//{
// AckErr("字节数异常 期待{currTran.expRecBytes}Bytes 实际收到{len}");
// return true;
//}
//成功解析出一个包
currTran.funcData.AddRange(recBuf.Skip(2).Take(recBuf.Count() - 3));//不要 devNo, func, lrc 这 3个byte
csm.IncPack(1);
return true;
}
else if (currPack[index - 1] == 0x0d)
{
AckErr("包尾错");
isError = true;
return true;
}
}
return false;
}
void ParsePack2()
{
//触发连接通知
int deviceNo;
lock (Transactions) {
deviceNo = currTran.deviceNo;
}
if (!deviceConnected.Contains(deviceNo))
{
deviceConnected.Add(deviceNo);
TriggerDeviceConnectEvent(new DeviceConnectEventArgs() { deviceNo = deviceNo, isConnected = true });
}
lock (Transactions)
{
switch (currTran.func)
{
case 0x03:
case 0x04:
{
List values = new List();
//index=0 为字节数
int index = 1;
while (index < currTran.funcData.Count())
{
values.Add(currTran.funcData.ToUInt16_Big_Endian(index));
index += 2;
}
currTran.retData = currTran.parse16FuncPack(values);
if (currTran.EnableLog)
{
if (currTran.retData != null)
{
string msg = $"{Newtonsoft.Json.JsonConvert.SerializeObject(currTran.retData)}";
AckDebug(msg);
}
}
}
break;
case 0x06:
if (currTran.EnableLog)
{
AckDebug("OK");
}
break;
case 0x10:
if (currTran.EnableLog)
{
AckDebug("OK");
}
break;
default:
//不受支持的功能
{
currTran.retData = currTran.funcData;
if (currTran.EnableLog)
{
AckDebug("OK");
}
}
break;
}
}
//有很多指令是没有回复数据的, 回调只是通知 指令已经执行了而已
//调用回调
if (currTran.asyncDelegate != null)
{
TriggerCurrTranAsyncDelegate();
}
bool isNeedToSendMsg = false;
lock (Transactions)
{
//空出当前交易位置
currTran = null;
if (Transactions.Count() > 0)
{
//队列还有需要发送的指令,通知外部获取指令发送
isNeedToSendMsg = true;
}
}
if (isNeedToSendMsg) {
var buf = GetSendMsg();
TriggerSendMsgEvent(buf);
}
}
public void AddTran(Modbus_Transaction tran)
{
bool isNeedToSendMsg = false;
//放入 交易队列
lock (Transactions)
{
Transactions.Add(tran);
if (currTran == null)
{
//当前没有指令正在发送
isNeedToSendMsg = true;
}
}
if (isNeedToSendMsg)
{
var buf = GetSendMsg();
TriggerSendMsgEvent(buf);
}
}
///
/// 读多个Holding REGs
///
///
///
///
///
///
///
///
///
public Modbus_Transaction Do_03(int deviceNo, int addr, int cnt,
string desription,
ParseU16FuncPackHandler parseU16FuncPack,
CallBackHandler asyncDelegate, object asyncContext)
{
//发送
//: (1byte)
//devNo (1 ascii = 2 byte)
//func (1 ascii = 2 byte)
//addr (2 ascii = 4 byte)
//regCnt (2 ascii = 4 byte)
//lrc (1 ascii = 2 byte)
//0x0d,0x0a (2 byte)
//接收
//: (1 byte)
//devNo (1 ascii = 2 byte)
//func (1 ascii = 2 byte)
//byteCnt (1 ascii = 2 byte)
//reg*byteCnt/2 (2 * byteCnt/2 ascii = 4*N byte)
//lrc (1 ascii = 2 byte)
//0x0d,0x0a (2 byte)
int expRecBytes = (cnt * 2 + 4) * 2 + 3;
Modbus_Transaction tran = new Modbus_Transaction()
{
deviceNo = deviceNo,
func = 0x03,
addr = addr,
cnt = cnt,
expRecBytes = expRecBytes,
desription = desription,
parse16FuncPack = parseU16FuncPack,
asyncDelegate = asyncDelegate,
asyncContext = asyncContext
};
tran.funcData.AddRange(((UInt16)addr).GetBytes_Big_endian());
tran.funcData.AddRange(((UInt16)cnt).GetBytes_Big_endian());
ToSendBuf(tran);
return tran;
}
///
/// 读多个Input REGs
///
///
///
///
///
///
///
///
///
public Modbus_Transaction Do_04(int deviceNo, int addr, int cnt,
string desription,
ParseU16FuncPackHandler parseU16FuncPack,
CallBackHandler asyncDelegate, object asyncContext)
{
//发送
//: (1byte)
//devNo (1 ascii = 2 byte)
//func (1 ascii = 2 byte)
//addr (2 ascii = 4 byte)
//regCnt (2 ascii = 4 byte)
//lrc (1 ascii = 2 byte)
//0x0d,0x0a (2 byte)
//接收
//: (1 byte)
//devNo (1 ascii = 2 byte)
//func (1 ascii = 2 byte)
//byteCnt (1 ascii = 2 byte)
//reg*byteCnt/2 (2 * byteCnt/2 ascii = 4*N byte)
//lrc (1 ascii = 2 byte)
//0x0d,0x0a (2 byte)
int expRecBytes = (cnt * 2 + 4) * 2 + 3;
Modbus_Transaction tran = new Modbus_Transaction()
{
deviceNo = deviceNo,
func = 0x04,
addr = addr,
cnt = cnt,
expRecBytes = expRecBytes,
desription = desription,
parse16FuncPack = parseU16FuncPack,
asyncDelegate = asyncDelegate,
asyncContext = asyncContext
};
tran.funcData.AddRange(((UInt16)addr).GetBytes_Big_endian());
tran.funcData.AddRange(((UInt16)cnt).GetBytes_Big_endian());
ToSendBuf(tran);
return tran;
}
public void ToSendBuf(Modbus_Transaction tran)
{
var data = new List();
data.Add((byte)tran.deviceNo);
data.Add(tran.func);
data.AddRange(tran.funcData);
byte lrc = data.LRC8(0, data.Count());
data.Add(lrc);
tran.funcData.Clear();
//ASCII TO HEX
byte[] recBuf = data.HexToAscii();
tran.sendBuf.Add((byte)':');
tran.sendBuf.AddRange(recBuf);
tran.sendBuf.Add(0x0d);
tran.sendBuf.Add(0x0a);
}
///
/// 写单个 Holding REG
///
///
///
///
///
///
///
///
public Modbus_Transaction Do_06(int deviceNo, int addr, UInt16 value,
string desription,
CallBackHandler asyncDelegate, object asyncContext)
{
//发送
//: (1byte)
//devNo (1 ascii = 2 byte)
//func (1 ascii = 2 byte)
//addr (2 ascii = 4 byte)
//reg (2 ascii = 4 byte)
//lrc (1 ascii = 2 byte)
//0x0d,0x0a (2 byte)
//接收
//: (1 byte)
//devNo (1 ascii = 2 byte)
//func (1 ascii = 2 byte)
//addr (2 ascii = 4 byte)
//reg (2 ascii = 4 byte)
//lrc (1 ascii = 2 byte)
//0x0d,0x0a (2 byte)
int expRecBytes = (7 * 2) + 3;
Modbus_Transaction tran = new Modbus_Transaction()
{
deviceNo = deviceNo,
func = 0x06,
addr = addr,
expRecBytes = expRecBytes,
desription = desription,
asyncDelegate = asyncDelegate,
asyncContext = asyncContext
};
tran.funcData.AddRange(((UInt16)addr).GetBytes_Big_endian());
tran.funcData.AddRange(((UInt16)value).GetBytes_Big_endian());
ToSendBuf(tran);
return tran;
}
///
/// 写多个 Holding REG
///
///
///
///
///
///
///
///
public Modbus_Transaction Do_10(int deviceNo, int addr, IEnumerable datas,
string desription,
CallBackHandler asyncDelegate, object asyncContext)
{
//发送
//: (1byte)
//devNo (1 ascii = 2 byte)
//func (1 ascii = 2 byte)
//addr (2 ascii = 4 byte)
//regCnt (2 ascii = 4 byte)
//reg*regCnt (2*regCnt ascii = 2*regCnt*2 byte)
//lrc (1 ascii = 2 byte)
//0x0d,0x0a (2 byte)
//接收
//: (1 byte)
//devNo (1 ascii = 2 byte)
//func (1 ascii = 2 byte)
//addr (2 ascii = 4 byte)
//regCnt (2 ascii = 4 byte)
//lrc (1 ascii = 2 byte)
//0x0d,0x0a (2 byte)
int expRecBytes = (7 * 2) + 3;
Modbus_Transaction tran = new Modbus_Transaction()
{
deviceNo = deviceNo,
func = 0x10,
addr = addr,
expRecBytes = expRecBytes,
desription = desription,
asyncDelegate = asyncDelegate,
asyncContext = asyncContext
};
tran.funcData.AddRange(((UInt16)tran.addr).GetBytes_Big_endian());
tran.funcData.AddRange(((UInt16)datas.Count()).GetBytes_Big_endian());
tran.funcData.Add((byte)(datas.Count() * 2));
for (int i = 0; i < datas.Count(); i++)
{
tran.funcData.AddRange(datas.ElementAt(i).GetBytes_Big_endian());
}
ToSendBuf(tran);
return tran;
}
}
}