using System; using System.Collections.Concurrent; using System.ComponentModel.Design; using System.Text; using TouchSocket.Core; using TouchSocket.Sockets; using WCS.BLL.Tool; namespace WCS.BLL { /// /// 对TouchSocket的封装 主要完成TCP的连接 状态更新 发送接收通信 /// 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 IsOnline { 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 ConcurrentDictionary MessageList { get; set; } = new ConcurrentDictionary(); public TcpClient tcpClient { get; set; } public object receivdLockObject = new object(); public object sendLockObject = new object(); public bool Connect() { try { tcpClient.Connect();//调用连接,当连接不成功时,会抛出异常。 return true; } catch (Exception e) { //连接失败 return false; } } /// /// 初始化配置连接 /// /// public TCPClient(string remoteIPHost, string bindIPHost, string shelfTypeName) { try { RemoteIPHost = remoteIPHost; BindIPHost = bindIPHost; ShelfTypeName = shelfTypeName; tcpClient = new TcpClient(); if (string.IsNullOrEmpty(BindIPHost)) { //载入配置 tcpClient.Setup(new TouchSocketConfig() .SetRemoteIPHost(new IPHost(RemoteIPHost)) //.SetBindIPHost(BindIPHost) .ConfigurePlugins(a => { //配置断线重连 a.UseReconnection(-1, true, 1000); a.Add(); }) .ConfigureContainer(a => { //添加控制台日志注入 a.AddConsoleLogger(); })); } else { //载入配置 tcpClient.Setup(new TouchSocketConfig() .SetRemoteIPHost(new IPHost(RemoteIPHost)) .SetBindIPHost(new IPHost(BindIPHost)) .ConfigurePlugins(a => { //配置断线重连 a.UseReconnection(-1, true, 1000); a.Add(); }) .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) .First(); 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 () => { while (true) { try { //TODO如果指令已发两次 则取消重发 await Task.Delay(100); if (MessageList.Count > 0) { var failedMessage = MessageList.Where(t => t.Value.LastSendTime < DateTime.Now.AddSeconds(-1)) .ToList(); foreach (var message in failedMessage) { 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 { } } }); } catch (Exception ex) { } } public void ReConnectAsync() { tcpClient.TryConnectAsync(); } public void Send(byte[] message, bool IsReSend = false) { try { 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); var clientIpHost = tcpClient.IP + ":" + tcpClient.Port; Logs.Write($"【发送{clientIpHost}】{BitConverter.ToString(message)}", LogsType.Instructions); //发送自带15ms间隔 Thread.Sleep(18); } } catch (Exception ex) { Logs.Write("【发送指令时发生异常】" + ex.Message, LogsType.Instructions); //因异常断连时(网线已经被断了) 手动重连一次 if (ex is NotConnectedException) { 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 class MessageDto { public int ID { get; set; } /// /// 最后一次发送时间 /// public DateTime LastSendTime { get; set; } = DateTime.Now; public DateTime CreateTime { get; set; } = DateTime.Now; /// /// 发送内容 /// public byte[] Message { get; set; } /// /// 发送次数 /// public int SendTimes { get; set; } = 0;//第几次发送 } }