466 lines
18 KiB
C#
466 lines
18 KiB
C#
using System;
|
||
using System.Collections.Concurrent;
|
||
using System.ComponentModel.Design;
|
||
using System.Reflection;
|
||
using System.Text;
|
||
using System.Text.RegularExpressions;
|
||
using TouchSocket.Core;
|
||
using TouchSocket.Sockets;
|
||
using WCS.BLL.Tool;
|
||
|
||
|
||
namespace WCS.BLL
|
||
{
|
||
/// <summary>
|
||
/// 对TouchSocket的封装 主要完成TCP的连接 状态更新 发送接收通信
|
||
/// </summary>
|
||
public class TCPClient
|
||
{
|
||
public string RemoteIPHost { get; set; } = "127.0.0.1:20002";
|
||
|
||
public string BindIPHost { get; set; } = "127.0.0.1:20003";
|
||
|
||
public string ShelfTypeName { get; set; }
|
||
public bool IsReceivedClientOnline { get; set; } = false;
|
||
public bool IsSendClientOnline { get; set; } = false;
|
||
//第一次连接是否已连接
|
||
public bool IsFirstConnected { get; set; } = false;
|
||
|
||
//can模块协议前缀
|
||
public readonly byte[] Prefix = new byte[] { 0x08, 0x00, 0x00 };
|
||
//can模块协议前缀长度
|
||
public readonly int PreFixLength = 3;
|
||
//协议数据部分长度
|
||
public readonly int DataLength = 10;
|
||
|
||
|
||
public TcpClient tcpSendClient { get; set; }
|
||
|
||
//同步发送等待客户端 等待上一条指令发送成功并收到响应之后才发送下一条指令
|
||
public IWaitingClient<TcpClient> waitClient;
|
||
|
||
public TcpClient tcpReceiveClient { get; set; }
|
||
|
||
public object receivdLockObject = new object();
|
||
public object sendLockObject = new object();
|
||
|
||
public bool Connect()
|
||
{
|
||
try
|
||
{
|
||
tcpSendClient.Connect();//调用连接,当连接不成功时,会抛出异常。
|
||
tcpReceiveClient.Connect();
|
||
return true;
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
//连接失败
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 初始化配置连接
|
||
/// </summary>
|
||
/// <param name="remoteIPHost"></param>
|
||
public TCPClient(string remoteIPHost, string bindIPHost, string shelfTypeName)
|
||
{
|
||
try
|
||
{
|
||
RemoteIPHost = remoteIPHost;
|
||
BindIPHost = bindIPHost;
|
||
ShelfTypeName = shelfTypeName;
|
||
|
||
tcpSendClient = new TcpClient();
|
||
tcpReceiveClient = new TcpClient();
|
||
|
||
|
||
if (string.IsNullOrEmpty(BindIPHost))
|
||
{
|
||
//载入配置
|
||
tcpSendClient.Setup(new TouchSocketConfig()
|
||
.SetRemoteIPHost(new IPHost(RemoteIPHost))
|
||
.ConfigurePlugins(a =>
|
||
{
|
||
//配置断线重连
|
||
a.UseReconnection(-1, true, 1000);
|
||
a.Add<HeartbeatAndReceivePlugin>();
|
||
})
|
||
.ConfigureContainer(a =>
|
||
{
|
||
//添加控制台日志注入
|
||
a.AddConsoleLogger();
|
||
}));
|
||
|
||
waitClient = tcpSendClient.CreateWaitingClient(new WaitingOptions()
|
||
{
|
||
FilterFunc = response => //设置用于筛选的fun委托,当返回为true时,才会响应返回
|
||
{
|
||
if (response.Data.Length == 13)
|
||
{
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
});
|
||
|
||
tcpReceiveClient.Setup(new TouchSocketConfig()
|
||
.SetRemoteIPHost(new IPHost(RemoteIPHost))
|
||
//.SetBindIPHost(BindIPHost)
|
||
.ConfigurePlugins(a =>
|
||
{
|
||
//配置断线重连
|
||
a.UseReconnection(-1, true, 1000);
|
||
a.Add<HeartbeatAndReceivePlugin>();
|
||
})
|
||
.ConfigureContainer(a =>
|
||
{
|
||
//添加控制台日志注入
|
||
a.AddConsoleLogger();
|
||
}));
|
||
|
||
}
|
||
else
|
||
{
|
||
//载入配置
|
||
tcpSendClient.Setup(new TouchSocketConfig()
|
||
.SetRemoteIPHost(new IPHost(RemoteIPHost))
|
||
.SetBindIPHost(new IPHost(BindIPHost))
|
||
.ConfigurePlugins(a =>
|
||
{
|
||
//配置断线重连
|
||
a.UseReconnection(-1, true, 1000);
|
||
a.Add<HeartbeatAndReceivePlugin>();
|
||
})
|
||
.ConfigureContainer(a =>
|
||
{
|
||
//添加控制台日志注入
|
||
a.AddConsoleLogger();
|
||
}));
|
||
|
||
waitClient = tcpSendClient.CreateWaitingClient(new WaitingOptions()
|
||
{
|
||
FilterFunc = response => //设置用于筛选的fun委托,当返回为true时,才会响应返回
|
||
{
|
||
if (response.Data.Length == 13)
|
||
{
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
});
|
||
|
||
tcpReceiveClient.Setup(new TouchSocketConfig()
|
||
.SetRemoteIPHost(new IPHost(RemoteIPHost))
|
||
//.SetBindIPHost(BindIPHost)
|
||
.ConfigurePlugins(a =>
|
||
{
|
||
//配置断线重连
|
||
a.UseReconnection(-1, true, 1000);
|
||
a.Add<HeartbeatAndReceivePlugin>();
|
||
})
|
||
.ConfigureContainer(a =>
|
||
{
|
||
//添加控制台日志注入
|
||
a.AddConsoleLogger();
|
||
}));
|
||
}
|
||
|
||
//添加接收事件 匹配已发送的指令
|
||
//tcpReceiveClient.Received += (client, e) =>
|
||
//{
|
||
// var data = e.ByteBlock.Buffer.Take((int)e.ByteBlock.Length).ToArray();
|
||
// Task.Run(() =>
|
||
// {
|
||
// Logs.Write($"【校验发送接收 开始】{ShelfTypeName}" + BitConverter.ToString(data), LogsType.InstructionResend);
|
||
// if (ShelfTypeName == "信息化货架")
|
||
// {
|
||
// //协议拆包
|
||
// var len = data.Length;
|
||
// //灯控制统一返回的是 FF-00-00-0A-00-02-D7-B5
|
||
// if (len == 8)
|
||
// {
|
||
// if (data[4] == 0x00 && data[0] == 0xFF && data[1] == 0x00 && (data[5] == 0x01 || data[5] == 0x02))
|
||
// {
|
||
|
||
// }
|
||
// }
|
||
// }
|
||
// else if (ShelfTypeName == "液晶标签货架")
|
||
// {
|
||
// Logs.Write($"【液晶标签货架】接收到指令{BitConverter.ToString(data)}", LogsType.InstructionResend);
|
||
// }
|
||
// //智能货架
|
||
// else
|
||
// {
|
||
// var len = data.Length;
|
||
// for (int index = 0; index < data.Length - PreFixLength; index++)
|
||
// {
|
||
// //协议拆包 通过前缀校验是否为完整数据包
|
||
// var prefixInData = data.Skip(index).Take(PreFixLength);
|
||
// var isEqual = prefixInData.SequenceEqual(Prefix);
|
||
// if (isEqual)
|
||
// {
|
||
// var dataTemp = data.Skip(index).Take(PreFixLength + DataLength).ToArray();
|
||
// if (dataTemp.Length < PreFixLength + DataLength)//拆包后不满足一条指令的长度
|
||
// {
|
||
// continue;
|
||
// }
|
||
// //获取返回指令的板子ID
|
||
// var boardId = (dataTemp[PreFixLength + 0] << 8) + dataTemp[PreFixLength + 1];
|
||
// index += (PreFixLength + DataLength - 2);//每次循环index会+1 所以这里-1
|
||
// }
|
||
// }
|
||
// }
|
||
// Logs.Write($"【校验发送接收 结束】{ShelfTypeName}" + BitConverter.ToString(data), LogsType.InstructionResend);
|
||
// });
|
||
// return null;
|
||
//};
|
||
|
||
tcpSendClient.Connected += (client, e) =>
|
||
{
|
||
this.IsSendClientOnline = true;
|
||
return EasyTask.CompletedTask;
|
||
};
|
||
|
||
tcpSendClient.Disconnected += (client, e) =>
|
||
{
|
||
this.IsSendClientOnline = false;
|
||
return EasyTask.CompletedTask;
|
||
};
|
||
|
||
tcpReceiveClient.Connected += (client, e) =>
|
||
{
|
||
this.IsReceivedClientOnline = true;
|
||
return EasyTask.CompletedTask;
|
||
};
|
||
|
||
tcpReceiveClient.Disconnected += (client, e) =>
|
||
{
|
||
this.IsReceivedClientOnline = false;
|
||
ReConnectAsync();
|
||
return EasyTask.CompletedTask;
|
||
};
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
}
|
||
}
|
||
|
||
public void ReConnectAsync()
|
||
{
|
||
if (IsSendClientOnline == false)
|
||
tcpSendClient.TryConnectAsync();
|
||
if (IsReceivedClientOnline == false)
|
||
tcpReceiveClient.TryConnectAsync();
|
||
}
|
||
|
||
|
||
public void Send(byte[] message, bool IsReSend = false)
|
||
{
|
||
try
|
||
{
|
||
var boardId = (message[3] << 8) + message[4];
|
||
|
||
lock (sendLockObject)
|
||
{
|
||
tcpReceiveClient.Send(message);
|
||
|
||
var clientIpHost = tcpReceiveClient.IP + ":" + tcpReceiveClient.Port;
|
||
Logs.Write($"【发送{clientIpHost}】{BitConverter.ToString(message)}", LogsType.Instructions);
|
||
|
||
//发送自带10ms间隔
|
||
Thread.Sleep(10);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Logs.Write("【发送指令时发生异常】" + ex.Message, LogsType.Instructions);
|
||
//因异常断连时(网线已经被断了) 手动重连一次
|
||
if (ex is NotConnectedException)
|
||
{
|
||
IsReceivedClientOnline = false;
|
||
Task.Run(() =>
|
||
{
|
||
ReConnectAsync();
|
||
});
|
||
}
|
||
throw ex;
|
||
}
|
||
}
|
||
|
||
public void SendThenReturn(byte[] message, bool IsReSend = false)
|
||
{
|
||
try
|
||
{
|
||
lock (sendLockObject)
|
||
{
|
||
var clientIpHost = tcpSendClient.IP + ":" + tcpSendClient.Port;
|
||
Logs.Write($"【同步等待发送{clientIpHost}】{BitConverter.ToString(message)}", LogsType.Instructions);
|
||
|
||
waitClient.SendThenReturn(message);
|
||
Thread.Sleep(3);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Logs.Write("【同步等待发送指令时发生异常】" + ex.Message, LogsType.Instructions);
|
||
//因异常断连时(网线已经被断了) 手动重连一次
|
||
if (ex is NotConnectedException)
|
||
{
|
||
IsSendClientOnline = false;
|
||
Task.Run(() =>
|
||
{
|
||
ReConnectAsync();
|
||
});
|
||
}
|
||
throw ex;
|
||
}
|
||
}
|
||
|
||
public void SendThenReturnList(List<byte[]> messages, bool IsReSend = false)
|
||
{
|
||
try
|
||
{
|
||
lock (sendLockObject)
|
||
{
|
||
foreach (var message in messages)
|
||
{
|
||
var clientIpHost = tcpSendClient.IP + ":" + tcpSendClient.Port;
|
||
Logs.Write($"【同步等待发送{clientIpHost}】{BitConverter.ToString(message)}", LogsType.Instructions);
|
||
waitClient.SendThenReturn(message);
|
||
Thread.Sleep(3);
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Logs.Write("【同步等待发送指令时发生异常】" + ex.Message, LogsType.Instructions);
|
||
//因异常断连时(网线已经被断了) 手动重连一次
|
||
if (ex is NotConnectedException)
|
||
{
|
||
IsSendClientOnline = false;
|
||
Task.Run(() =>
|
||
{
|
||
ReConnectAsync();
|
||
});
|
||
}
|
||
throw ex;
|
||
}
|
||
}
|
||
|
||
//生成协议明细
|
||
public byte[] GenerateMessage(int boardId, byte[] data)
|
||
{
|
||
var message = new byte[Prefix.Length + 2 + data.Length];
|
||
var boardIdData = BitConverter.GetBytes(unchecked((short)boardId));
|
||
// 检查是否需要交换字节
|
||
if (BitConverter.IsLittleEndian)
|
||
{
|
||
// 如果是小端序系统,则交换字节
|
||
byte temp = boardIdData[0];
|
||
boardIdData[0] = boardIdData[1];
|
||
boardIdData[1] = temp;
|
||
}
|
||
|
||
Buffer.BlockCopy(Prefix, 0, message, 0, Prefix.Length);
|
||
Buffer.BlockCopy(boardIdData, 0, message, Prefix.Length, boardIdData.Length);
|
||
Buffer.BlockCopy(data, 0, message, Prefix.Length + boardIdData.Length, data.Length);
|
||
return message;
|
||
}
|
||
|
||
public List<byte[]> GenerateMessageList(int boardId, byte[] data, string message)
|
||
{
|
||
var list = new List<byte[]>();
|
||
////去除不满足ASCII的数据
|
||
//message = Regex.Replace(message, @"[^\u0000-\u007F]+", string.Empty);
|
||
////总共发的数据位数
|
||
//data[1] = Convert.ToByte(message.Length);
|
||
////数据发送总帧数
|
||
//var messageCount = Math.Ceiling((decimal)message.Length / 6);
|
||
//for (int i = 0; i < messageCount; i++)
|
||
//{
|
||
// var tempmessage = string.Empty;
|
||
// //不是最后一条指令
|
||
// if (i != messageCount - 1)
|
||
// {
|
||
// tempmessage = message.Substring(i * 6, 6);
|
||
// var charArray = tempmessage.ToCharArray();
|
||
// for (int index = 0; index < 6; index++)
|
||
// {
|
||
// data[2 + index] = Convert.ToByte(charArray[index]);
|
||
// }
|
||
// }
|
||
// //最后一条指令
|
||
// else
|
||
// {
|
||
// tempmessage = message.Substring(i * 6, message.Length % 6 == 0 ? 6 : message.Length % 6);
|
||
// var charArray = tempmessage.ToCharArray();
|
||
// //先把所有数据位置为0
|
||
// for (int index = 0; index < 6; index++)
|
||
// {
|
||
// data[2 + index] = 0x00;
|
||
// }
|
||
// //有数据的置为对应的数据
|
||
// for (int index = 0; index < charArray.Length; index++)
|
||
// {
|
||
// data[2 + index] = Convert.ToByte(charArray[index]);
|
||
// }
|
||
// }
|
||
// list.Add(GenerateMessage(boardId, data));
|
||
//}
|
||
|
||
//var dataBtyes = Encoding.GetEncoding(936).GetBytes(message);
|
||
|
||
var dataBtyes = Encode.Encode.GetGb2312(message);
|
||
data[1] = Convert.ToByte(dataBtyes.Length);
|
||
var messageCount = Math.Ceiling((decimal)dataBtyes.Length / 6);
|
||
for (int i = 0; i < messageCount; i++)
|
||
{
|
||
byte[] charArray = dataBtyes.Skip(i * 6).Take(6).ToArray();
|
||
//不是最后一条指令
|
||
if (i != messageCount - 1)
|
||
{
|
||
for (int index = 0; index < 6; index++)
|
||
{
|
||
data[2 + index] = charArray[index];
|
||
}
|
||
}
|
||
//最后一条指令
|
||
else
|
||
{
|
||
//先把所有数据位置为0
|
||
for (int index = 0; index < 6; index++)
|
||
{
|
||
data[2 + index] = 0x00;
|
||
}
|
||
//有数据的置为对应的数据
|
||
for (int index = 0; index < charArray.Length; index++)
|
||
{
|
||
data[2 + index] = charArray[index];
|
||
}
|
||
}
|
||
list.Add(GenerateMessage(boardId, data));
|
||
}
|
||
|
||
return list;
|
||
}
|
||
|
||
private string CharToAscii(char chr)
|
||
{
|
||
return ((int)chr).ToString("X").PadLeft(2, '0');
|
||
}
|
||
|
||
private byte[] StrToToHexByte(string hexString)
|
||
{
|
||
hexString = hexString.Replace(" ", string.Empty);
|
||
if ((hexString.Length % 2) != 0)
|
||
hexString = "0" + hexString;
|
||
byte[] returnBytes = new byte[hexString.Length / 2];
|
||
for (int i = 0; i < returnBytes.Length; i++)
|
||
returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
|
||
return returnBytes;
|
||
}
|
||
}
|
||
}
|