using SqlSugar; using System; using System.Collections.Generic; using System.Linq; using System.Net.Sockets; using System.Security.Cryptography.X509Certificates; using System.Text; using System.Threading.Tasks; using TouchSocket.Core; using TouchSocket.Sockets; using WCS.BLL.Config; using WCS.BLL.HardWare; using WCS.BLL.Tool; using WCS.DAL.Db; using WCS.DAL.DbModels; namespace WCS.BLL.Manager { public static class TCPClientManager { /// /// 货架类型的TCP连接管理 /// public static List TCPClients = new List(); public static TcpService Service { get; set; } public static void InitTcpClient() { #region 模块是Service的逻辑 Logs.Write("【InitTcpClient】开始", LogsType.StartBoot); var moduleServices = DbHelp.db.Queryable() .WhereIF(!string.IsNullOrEmpty(LocalFile.Config.GroupName), t => t.GroupName == LocalFile.Config.GroupName) .Where(t => t.IsService) .Select(t => new { IP = t.ClientIp, ShelfTypeName = t.ShelfTypeName, Port = t.Port, }) .Distinct() .ToList(); if (moduleServices != null && moduleServices.Count > 0) { Logs.Write($"【InitTcpClient】需要连接的服务端地址如下:\r\n{string.Join(";", moduleServices)}", LogsType.StartBoot); foreach (var cleientInDB in moduleServices) { Task.Run(() => { var tcpCleint = new TCPClient(cleientInDB.IP, cleientInDB.Port, cleientInDB.ShelfTypeName); tcpCleint.tcpReceiveClient.Received += (client, e) => { var clientIpHost = client.IP + ":" + client.Port; var TcpCleint = TCPClientManager.GetTCPClientByIPHost(clientIpHost); if (TcpCleint == null) { return EasyTask.CompletedTask; } var data = e.ByteBlock.Buffer.Take((int)e.ByteBlock.Length).ToArray(); Logs.Write($"【接收{clientIpHost}】{BitConverter.ToString(data)}", LogsType.Instructions); e.ByteBlock.Clear(); var len = data.Length; if (tcpCleint.ShelfTypeName == "液晶标签货架") { for (int index = 0; index < data.Length - TcpCleint.PreFixLength; index++) { //协议拆包 通过前缀校验是否为完整数据包 var prefixInData = data.Skip(index).Take(TcpCleint.PreFixLength); var isEqual = prefixInData.SequenceEqual(TcpCleint.Prefix); if (isEqual) { var dataTemp = data.Skip(index).Take(TcpCleint.PreFixLength + TcpCleint.DataLength).ToArray(); if (dataTemp.Length < TcpCleint.PreFixLength + TcpCleint.DataLength)//拆包后不满足一条指令的长度 { continue; } Logs.Write($"【处理单条指令 开始】{BitConverter.ToString(dataTemp)}", LogsType.InstructionsProcess); //每次循环index会+1 所以这里-1 index += (TcpCleint.PreFixLength + TcpCleint.DataLength - 1); //获取板子ID var boardId = (dataTemp[TcpCleint.PreFixLength + 0] << 8) + dataTemp[TcpCleint.PreFixLength + 1]; //报警灯 返回来就修改对应的灯的颜色 if (dataTemp[TcpCleint.PreFixLength + 2] == 0x20) { var shelf = ShelfManager.Shelves.Where(t => t.ClientIp == clientIpHost) .Where(t => t.LightId == boardId) .FirstOrDefault(); var mxl4Shelf = shelf as MXL4Shelf; mxl4Shelf?.WarningLightProcess(dataTemp, boardId); } //!= 0x20 货架类型协议返回 else { var shelf = ShelfManager.Shelves .Where(t => t.ClientIp == clientIpHost) .Where(t => t.ModuleIds != null && t.ModuleIds.Contains(boardId)) .FirstOrDefault(); var mxl4Shelf = shelf as MXL4Shelf; mxl4Shelf?.ProtocolProcess(dataTemp, boardId); } Logs.Write($"【处理单条指令 结束】{BitConverter.ToString(dataTemp)}", LogsType.InstructionsProcess); } } } return EasyTask.CompletedTask; }; //配置首次连接后复位操作 tcpCleint.tcpSendClient.Connected += (client, e) => { Logs.Write($"【TcpClient】{client.IP}完成连接,端口号{client.Port}", LogsType.StartBoot); var clientIpHost = client.IP + ":" + client.Port; var TcpCleint = TCPClientManager.GetTCPClientByIPHost(clientIpHost); if (TcpCleint == null) { return EasyTask.CompletedTask; } ////首次连接 if (TcpCleint.IsFirstConnected == false) { Logs.Write($"【InitTcpClient】{clientIpHost}完成首次连接", LogsType.StartBoot); Console.WriteLine($"【InitTcpClient】{clientIpHost}完成首次连接"); //InitStatus(TcpCleint); TcpCleint.IsFirstConnected = true; //获取剩余未完成连接的tcp var noFirstConnectedTcps = TCPClientManager.TCPClients.Where(t => t.IsFirstConnected == false) .Select(t => t.RemoteIPHost) .ToList(); Logs.Write($"【InitTcpClient】剩余未完成连接的TCP为{string.Join(";", noFirstConnectedTcps)}", LogsType.StartBoot); } return EasyTask.CompletedTask; }; lock (TCPClients)//避免添加失败的情况 { TCPClients.Add(tcpCleint); } tcpCleint.Connect(); }); } //启动线程监听所有TCP是否已经完成首次连接 Task.Run(() => { while (true) { try { Thread.Sleep(1000); var noFirstConnectedClients = TCPClientManager.TCPClients.Where(t => t.IsFirstConnected == false) .ToList(); if (noFirstConnectedClients.Count == 0) { break; } else { Console.WriteLine($"存在tcp未完成首次连接,继续重连!"); noFirstConnectedClients.ForEach(t => { t.ReConnectAsync(); }); } } catch (Exception ex) { } } }); Logs.Write("【InitTcpClient】完成 后台继续连接", LogsType.StartBoot); } #endregion #region 模块是Client的逻辑 var moduleClients = DbHelp.db.Queryable() .WhereIF(!string.IsNullOrEmpty(LocalFile.Config.GroupName), t => t.GroupName == LocalFile.Config.GroupName) .Where(t => t.IsService == false) .Select(t => new { IP = t.ClientIp, ShelfTypeName = t.ShelfTypeName, Port = t.Port, }) .Distinct() .ToList(); //有客户端作客户端时才需要初始化一个服务端 if (moduleClients != null && moduleClients.Count > 0) { Service = new TcpService(); Service.Connecting = (client, e) => { return EasyTask.CompletedTask; };//有客户端正在连接 Service.Connected = (client, e) => { return EasyTask.CompletedTask; };//有客户端成功连接 Service.Disconnecting = (client, e) => { return EasyTask.CompletedTask; };//有客户端正在断开连接,只有当主动断开时才有效。 Service.Disconnected = (client, e) => { return EasyTask.CompletedTask; };//有客户端断开连接 Service.Received = (client, e) => { //从客户端收到信息 var mes = Encoding.UTF8.GetString(e.ByteBlock.Buffer, 0, e.ByteBlock.Len);//注意:数据长度是byteBlock.Len client.Logger.Info($"已从{client.Id}接收到信息:{mes}"); //client.Send(mes);//将收到的信息直接返回给发送方 return EasyTask.CompletedTask; }; Service.Setup(new TouchSocketConfig()//载入配置 .SetListenIPHosts("tcp://0.0.0.0:9999", 7790)//同时监听两个地址 .ConfigureContainer(a =>//容器的配置顺序应该在最前面 { a.AddConsoleLogger();//添加一个控制台日志注入(注意:在maui中控制台日志不可用) }) .ConfigurePlugins(a => { //a.Add();//此处可以添加插件 })); Service.Start(); } #endregion } //后台启动时给所有板子、警示灯发送复位操作 保持状态一致 public static void InitStatus() { Task.Run(() => { try { Thread.Sleep(1000); //给所有板子发复位 TCPClients.ForEach(tcpClient => { //板子复位 new SmartShelfModule() { BoardId = 2047 }.Reset(tcpClient); //报警灯复位 new WarningLight().CloseLight(tcpClient); }); } catch (Exception ex) { Logs.Write($"启动时复位异常,{ex.Message}"); } }); } //后台启动时给所有板子、警示灯发送复位操作 保持状态一致 public static void InitStatus(TCPClient tcpClient) { var shelfInfo = DbHelp.db.Queryable().Where(t => t.ClientIp == tcpClient.RemoteIPHost).ToList(); if (shelfInfo.Count != 0) { if (shelfInfo[0].ShelfTypeName == "信息化货架") { return; } } Task.Run(() => { try { Thread.Sleep(1000); //板子复位 new SmartShelfModule() { BoardId = 2047 }.Reset(tcpClient); //报警灯复位 new WarningLight().CloseLight(tcpClient); } catch (Exception ex) { Logs.Write($"[{tcpClient.RemoteIPHost}]:启动时复位异常,{ex.Message}"); } }); } public static TCPClient GetTCPClientByIPHost(string IpHost) { return TCPClients.Where(t => t.RemoteIPHost == IpHost).FirstOrDefault(); } } }