液晶标签协议对接!

This commit is contained in:
hehaibing-1996
2024-12-09 19:40:05 +08:00
parent a37918a95d
commit 1c2a34256e
11 changed files with 460 additions and 621 deletions

View File

@ -2,6 +2,7 @@
using System.Collections.Concurrent;
using System.ComponentModel.Design;
using System.Text;
using System.Text.RegularExpressions;
using TouchSocket.Core;
using TouchSocket.Sockets;
using WCS.BLL.Tool;
@ -19,8 +20,8 @@ namespace WCS.BLL
public string BindIPHost { get; set; } = "127.0.0.1:20003";
public string ShelfTypeName { get; set; }
public bool IsOnline { get; set; } = false;
public bool IsReceivedClientOnline { get; set; } = false;
public bool IsSendClientOnline { get; set; } = false;
//第一次连接是否已连接
public bool IsFirstConnected { get; set; } = false;
@ -31,13 +32,14 @@ namespace WCS.BLL
//协议数据部分长度
public readonly int DataLength = 10;
public ConcurrentDictionary<int, MessageDto> MessageList { get; set; } = new ConcurrentDictionary<int, MessageDto>();
public TcpClient tcpClient { get; set; }
public TcpClient tcpSendClient { get; set; }
//同步发送等待客户端 等待上一条指令发送成功并收到响应之后才发送下一条指令
public IWaitingClient<TcpClient> waitClient;
public TcpClient tcpReceiveClient { get; set; }
public object receivdLockObject = new object();
public object sendLockObject = new object();
@ -45,7 +47,8 @@ namespace WCS.BLL
{
try
{
tcpClient.Connect();//调用连接,当连接不成功时,会抛出异常。
tcpSendClient.Connect();//调用连接,当连接不成功时,会抛出异常。
tcpReceiveClient.Connect();
return true;
}
catch (Exception e)
@ -67,16 +70,15 @@ namespace WCS.BLL
BindIPHost = bindIPHost;
ShelfTypeName = shelfTypeName;
tcpClient = new TcpClient();
tcpSendClient = new TcpClient();
tcpReceiveClient = new TcpClient();
if (string.IsNullOrEmpty(BindIPHost))
{
//载入配置
tcpClient.Setup(new TouchSocketConfig()
tcpSendClient.Setup(new TouchSocketConfig()
.SetRemoteIPHost(new IPHost(RemoteIPHost))
//.SetBindIPHost(BindIPHost)
.ConfigurePlugins(a =>
{
//配置断线重连
@ -89,7 +91,7 @@ namespace WCS.BLL
a.AddConsoleLogger();
}));
var waitClient = tcpClient.CreateWaitingClient(new WaitingOptions()
waitClient = tcpSendClient.CreateWaitingClient(new WaitingOptions()
{
FilterFunc = response => //设置用于筛选的fun委托当返回为true时才会响应返回
{
@ -101,11 +103,26 @@ namespace WCS.BLL
}
});
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
{
//载入配置
tcpClient.Setup(new TouchSocketConfig()
tcpSendClient.Setup(new TouchSocketConfig()
.SetRemoteIPHost(new IPHost(RemoteIPHost))
.SetBindIPHost(new IPHost(BindIPHost))
.ConfigurePlugins(a =>
@ -119,124 +136,110 @@ namespace WCS.BLL
//添加控制台日志注入
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();
}));
}
//添加接收事件 匹配已发送的指令
tcpClient.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))
{
//查询当前板子是否有待验证的指令
var message = new MessageDto();
var firstMessage = MessageList.Select(t => new { Id = t.Key, Value = t.Value })
.OrderBy(t => t.Value.CreateTime)
.FirstOrDefault();
if (firstMessage != null)
{
MessageList.TryRemove(firstMessage.Id, out message);
Logs.Write($"【信息化货架】以下指令已不重发:{BitConverter.ToString(firstMessage.Value.Message)}", LogsType.InstructionResend);
}
}
}
}
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];
//查询当前板子是否有待验证的指令
var message = new MessageDto();
MessageList.TryGetValue(boardId, out message);
//功能位校验 功能位相同视为已响应指令 删除对应的指令
if (message?.Message[PreFixLength + 2] == dataTemp[PreFixLength + 2])
{
MessageList.TryRemove(boardId, out message);
}
index += (PreFixLength + DataLength - 2);//每次循环index会+1 所以这里-1
}
}
}
Logs.Write($"【校验发送接收 结束】{ShelfTypeName}" + BitConverter.ToString(data), LogsType.InstructionResend);
});
return null;
};
tcpClient.Connected += (client, e) =>
{
this.IsOnline = true;
return EasyTask.CompletedTask;
};
tcpClient.Disconnected += (client, e) =>
{
this.IsOnline = false;
return EasyTask.CompletedTask;
};
////配置数据重发机制
//Task.Run(async () =>
//tcpReceiveClient.Received += (client, e) =>
//{
// while (true)
// var data = e.ByteBlock.Buffer.Take((int)e.ByteBlock.Length).ToArray();
// Task.Run(() =>
// {
// try
// Logs.Write($"【校验发送接收 开始】{ShelfTypeName}" + BitConverter.ToString(data), LogsType.InstructionResend);
// if (ShelfTypeName == "信息化货架")
// {
// //TODO如果指令已发两次 则取消重发
// await Task.Delay(100);
// if (MessageList.Count > 0)
// //协议拆包
// var len = data.Length;
// //灯控制统一返回的是 FF-00-00-0A-00-02-D7-B5
// if (len == 8)
// {
// var failedMessage = MessageList.Where(t => t.Value.LastSendTime < DateTime.Now.AddSeconds(-1))
// .ToList();
// foreach (var message in failedMessage)
// if (data[4] == 0x00 && data[0] == 0xFF && data[1] == 0x00 && (data[5] == 0x01 || data[5] == 0x02))
// {
// Logs.Write("【指令重发】" + BitConverter.ToString(message.Value.Message) + "指令超时1s未响应", LogsType.InstructionResend);
// }
// MessageList.RemoveWhen(t => t.Value.SendTimes >= 2);
// foreach (var item in MessageList)
// {
// if (item.Value.LastSendTime < DateTime.Now.AddSeconds(-1))
// {
// Send(item.Value.Message);
// item.Value.SendTimes++;
// item.Value.LastSendTime = DateTime.Now;
// Logs.Write("【指令重发】" + BitConverter.ToString(item.Value.Message) + "已进行重发", LogsType.InstructionResend);
// }
// }
// }
// }
// catch
// 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)
{
@ -245,7 +248,10 @@ namespace WCS.BLL
public void ReConnectAsync()
{
tcpClient.TryConnectAsync();
if (IsSendClientOnline == false)
tcpSendClient.TryConnectAsync();
if (IsReceivedClientOnline == false)
tcpReceiveClient.TryConnectAsync();
}
@ -255,25 +261,15 @@ namespace WCS.BLL
{
var boardId = (message[3] << 8) + message[4];
if (boardId != 2047 && IsReSend == false)
{
MessageList.TryAdd(boardId, new MessageDto()
{
ID = boardId,
Message = message,
SendTimes = 1
});
}
lock (sendLockObject)
{
tcpClient.Send(message);
tcpReceiveClient.Send(message);
var clientIpHost = tcpClient.IP + ":" + tcpClient.Port;
Logs.Write($"【发送{clientIpHost}】{BitConverter.ToString(message)}", LogsType.Instructions);
var clientIpHost = tcpReceiveClient.IP + ":" + tcpReceiveClient.Port;
Logs.Write($"【发送{clientIpHost}】{BitConverter.ToString(message)}", LogsType.Instructions);
//发送自带15ms间隔
Thread.Sleep(18);
//发送自带10ms间隔
Thread.Sleep(10);
}
}
catch (Exception ex)
@ -282,6 +278,7 @@ namespace WCS.BLL
//因异常断连时(网线已经被断了) 手动重连一次
if (ex is NotConnectedException)
{
IsReceivedClientOnline = false;
Task.Run(() =>
{
ReConnectAsync();
@ -295,10 +292,9 @@ namespace WCS.BLL
{
try
{
var boardId = (message[3] << 8) + message[4];
lock (sendLockObject)
{
var clientIpHost = tcpClient.IP + ":" + tcpClient.Port;
var clientIpHost = tcpSendClient.IP + ":" + tcpSendClient.Port;
Logs.Write($"【同步等待发送{clientIpHost}】{BitConverter.ToString(message)}", LogsType.Instructions);
waitClient.SendThenReturn(message);
@ -311,6 +307,38 @@ namespace WCS.BLL
//因异常断连时(网线已经被断了) 手动重连一次
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();
@ -340,26 +368,64 @@ namespace WCS.BLL
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));
}
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;
}
}
//发送指令的记录
public class MessageDto
{
public int ID { get; set; }
/// <summary>
/// 最后一次发送时间
/// </summary>
public DateTime LastSendTime { get; set; } = DateTime.Now;
public DateTime CreateTime { get; set; } = DateTime.Now;
/// <summary>
/// 发送内容
/// </summary>
public byte[] Message { get; set; }
/// <summary>
/// 发送次数
/// </summary>
public int SendTimes { get; set; } = 0;//第几次发送
}
}