Files
wcs/WCS.BLL/Manager/TCPClientManager.cs
2024-12-06 11:12:18 +08:00

312 lines
15 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
{
/// <summary>
/// 货架类型的TCP连接管理
/// </summary>
public static List<TCPClient> TCPClients = new List<TCPClient>();
public static TcpService Service { get; set; }
public static void InitTcpClient()
{
#region Service的逻辑
Logs.Write("【InitTcpClient】开始", LogsType.StartBoot);
var moduleServices = DbHelp.db.Queryable<ShelfInfo>()
.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.tcpClient.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 == "信息化货架")
{
Logs.Write($"【信息化货架开始处理接收数据】{BitConverter.ToString(data)}", LogsType.InstructionsProcess);
Helper.ReturnDataProcess(TcpCleint, data);
Logs.Write($"【信息化货架完成处理接收数据】{BitConverter.ToString(data)}", LogsType.InstructionsProcess);
return EasyTask.CompletedTask;
}
if (tcpCleint.ShelfTypeName == "液晶货架")
{
Logs.Write($"【液晶货架开始处理接收数据】{BitConverter.ToString(data)}", LogsType.InstructionsProcess);
//Helper.ReturnDataProcess(TcpCleint, data);
Logs.Write($"【液晶货架完成处理接收数据】{BitConverter.ToString(data)}", LogsType.InstructionsProcess);
return EasyTask.CompletedTask;
}
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 += (TcpCleint.PreFixLength + TcpCleint.DataLength - 1);//每次循环index会+1 所以这里-1
//获取板子ID
var boardId = (dataTemp[TcpCleint.PreFixLength + 0] << 8) + dataTemp[TcpCleint.PreFixLength + 1];
var lightNumber = Convert.ToInt32(dataTemp[TcpCleint.PreFixLength + 3]);
//报警灯 返回来就修改对应的灯的颜色
if (dataTemp[TcpCleint.PreFixLength + 2] == 0x20)
{
var shelf = ShelfManager.Shelves.Where(t => t.ClientIp == clientIpHost)
.Where(t => t.LightId == boardId)
.FirstOrDefault();
var smartShelf = shelf as SmartShelf;
smartShelf?.WarningLightProcess(dataTemp, boardId, lightNumber);
}
//!= 0x20 货架类型协议返回
else
{
var shelf = ShelfManager.Shelves
.Where(t => t.ClientIp == clientIpHost)
.Where(t => t.ModuleIds != null && t.ModuleIds.Contains(boardId))
.FirstOrDefault();
var smartShelf = shelf as SmartShelf;
smartShelf?.ProtocolProcess(dataTemp, boardId, lightNumber);
}
Logs.Write($"【处理单条指令 结束】{BitConverter.ToString(dataTemp)}", LogsType.InstructionsProcess);
}
}
return EasyTask.CompletedTask;
};
//配置首次连接后复位操作
tcpCleint.tcpClient.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<ShelfInfo>()
.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<ShelfInfo>().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();
}
}
}