diff --git a/SlnMesnac.Config/AppConfig.cs b/SlnMesnac.Config/AppConfig.cs index 84ca71a..ca3759b 100644 --- a/SlnMesnac.Config/AppConfig.cs +++ b/SlnMesnac.Config/AppConfig.cs @@ -67,6 +67,11 @@ namespace SlnMesnac.Config /// public string ManipulatorIpConfig { get; set; } + /// + /// 机械臂地址配置 + /// + public string TCPVisionConfig { get; set; } + public AppConfig Value => this; } } diff --git a/SlnMesnac.TouchSocket/AirPorthttpClient.cs b/SlnMesnac.TouchSocket/AirPorthttpClient.cs index a4e1659..8b27196 100644 --- a/SlnMesnac.TouchSocket/AirPorthttpClient.cs +++ b/SlnMesnac.TouchSocket/AirPorthttpClient.cs @@ -36,8 +36,16 @@ namespace SlnMesnac.TouchSocket private WebApiClient CreateWebApiClient(string IpHost) { var client = new WebApiClient(); - client.Connect(IpHost); - _logger.LogInformation(IpHost + "连接成功"); + try + { + client.Connect(IpHost); + _logger.LogInformation(IpHost + "连接成功"); + } + catch (Exception ex) + { + _logger.LogError("ERROR: " + ex.Message); + return null; + } return client; } @@ -50,7 +58,7 @@ namespace SlnMesnac.TouchSocket } catch (Exception ex) { - _logger.LogError("ERROR: " + ex); + _logger.LogError("ERROR: " + ex.Message); return Task.FromException(ex); } return Task.CompletedTask; diff --git a/SlnMesnac.TouchSocket/AirportTcpServer.cs b/SlnMesnac.TouchSocket/AirportTcpServer.cs new file mode 100644 index 0000000..784afc4 --- /dev/null +++ b/SlnMesnac.TouchSocket/AirportTcpServer.cs @@ -0,0 +1,19 @@ +using Microsoft.Extensions.Hosting; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace SlnMesnac.TouchSocket +{ + public class AirportTcpServer : BackgroundService + { + protected override Task ExecuteAsync(CancellationToken stoppingToken) + { + return Task.CompletedTask; + } + + + } +} diff --git a/SlnMesnac.TouchSocket/ApiServer.cs b/SlnMesnac.TouchSocket/ApiServer.cs index c5d5212..30b7e31 100644 --- a/SlnMesnac.TouchSocket/ApiServer.cs +++ b/SlnMesnac.TouchSocket/ApiServer.cs @@ -33,12 +33,12 @@ namespace SlnMesnac.TouchSocket { public ApiServer() { - SubscribeToAGVArrivalEvent(); + } public delegate void AGVArrivalStart(string message, AGVArrivalSingalEntity aGVArrivalSingalEntity); /// - /// AGV呼叫事件刷新 + /// AGV到位信号刷新 /// public event AGVArrivalStart AGVArrivalStartEvent; @@ -57,7 +57,7 @@ namespace SlnMesnac.TouchSocket public delegate void ManipulatorWorkDoneStart(string message, ManipulatorWorkDoneEntity manipulatorWorkDoneEntity); /// - /// 机械臂开始抓取事件刷新 + /// 机械臂抓取完毕事件刷新 /// public event ManipulatorWorkDoneStart ManipulatorWorkDoneEvent; diff --git a/SlnMesnac.TouchSocket/BufferDataAnalysis.cs b/SlnMesnac.TouchSocket/BufferDataAnalysis.cs new file mode 100644 index 0000000..3836c54 --- /dev/null +++ b/SlnMesnac.TouchSocket/BufferDataAnalysis.cs @@ -0,0 +1,83 @@ +using Microsoft.Extensions.Logging; +using Serilog.Core; +using SlnMesnac.Config; +using SlnMesnac.Model.AirportApiEntity; +using SlnMesnac.TouchSocket.Entity; +using System; +using System.Buffers; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using TouchSocket.Sockets; + +namespace SlnMesnac.TouchSocket +{ + + public class BufferDataAnalysis + { + public static TcpVisionEntity BufferRootAnalysis(byte[] bytes) + { + TcpVisionEntity entity; + //数据校验,从起始符开始到数据位,按字节求和得出的结果对256求余 + int checkDatalength = bytes.Length - 2; + byte checksum = bytes[checkDatalength]; + byte[] checkData = new byte[checkDatalength]; + Array.Copy(bytes, 0, checkData, 0, checkDatalength); + int sum = 0; + foreach (byte b in checkData) + { + sum += b; + } + int count = sum % 256; + if (count == checksum) + { + entity = new TcpVisionEntity(); + entity.Checksum = checksum; + } + else + { + return null; + } + int index = 2; + + //取序列号 + entity.SN = new byte[2] { bytes[index], bytes[index + 1] }; + index += 2; + + //取时间戳 + entity.Timestamp = new byte[4] { bytes[index], bytes[index + 1], bytes[index + 2], bytes[index + 3] }; + index += 4; + + //取命令字 + entity.Command = bytes[index]; + index += 1; + + //取数据位长度 + entity.DataLength = bytes[index]; + index += 1; + + //取数据位 + byte[] dataBytes = new byte[entity.DataLength]; + Array.Copy(bytes, index, dataBytes, 0, entity.DataLength); + entity.DataBytes = dataBytes; + index += entity.DataLength; + return entity; + } + + public byte[] DataCombine(byte[] bytes) + { + + return null; + } + + public static byte[] GetCurrentUnixTimestampAsBytes() + { + // 获取当前的Unix时间戳,表示自1970-01-01以来的秒数 + int unixTimestamp = (int)(DateTimeOffset.UtcNow.ToUnixTimeSeconds()); + // 将时间戳转换为字节数组 + byte[] bytes = BitConverter.GetBytes(unixTimestamp); + return bytes; + } + + } +} diff --git a/SlnMesnac.TouchSocket/Entity/TcpVisionEntity.cs b/SlnMesnac.TouchSocket/Entity/TcpVisionEntity.cs new file mode 100644 index 0000000..395b51b --- /dev/null +++ b/SlnMesnac.TouchSocket/Entity/TcpVisionEntity.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace SlnMesnac.TouchSocket.Entity +{ + public class TcpVisionEntity + { + /// + /// 序列号 + /// + public byte[] SN { get; set; } + + /// + /// 时间戳 + /// + public byte[] Timestamp { get; set; } + + /// + /// 命令字 + /// + public byte Command { get; set; } + + /// + /// 数据长度 + /// + public byte DataLength { get; set; } + + /// + /// 数据位 + /// + public byte[]? DataBytes { get; set; } + + /// + /// 校验和 + /// + public byte Checksum { get; set; } + } +} diff --git a/SlnMesnac.TouchSocket/TcpServer.cs b/SlnMesnac.TouchSocket/TcpServer.cs index 70f8afc..d61eeef 100644 --- a/SlnMesnac.TouchSocket/TcpServer.cs +++ b/SlnMesnac.TouchSocket/TcpServer.cs @@ -1,7 +1,12 @@ -using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using SlnMesnac.Config; +using SlnMesnac.TouchSocket.Entity; using System; using System.Collections.Generic; using System.Text; +using System.Threading; +using System.Threading.Tasks; using TouchSocket.Core; using TouchSocket.Sockets; @@ -29,10 +34,13 @@ using TouchSocket.Sockets; #endregion << 版 本 注 释 >> namespace SlnMesnac.TouchSocket { - public class TcpServer + public class TcpServer : BackgroundService { + private ILogger _logger; private readonly TcpService _service; + private readonly AppConfig _appConfig; + /// /// 接收客户端指令委托 /// @@ -42,26 +50,63 @@ namespace SlnMesnac.TouchSocket public delegate void RefreshClientInfo(TcpService tcpService); public event RefreshClientInfo? RefreshClientInfoEvent; - public TcpServer(ILogger logger,TcpService tcpService) + public delegate void GetVisionData(TcpVisionEntity entity); + + /// + /// 视觉系统反馈给上位机调度系统状态 + /// + public event GetVisionData? VisionSysStateEvent; + + /// + /// 视觉系统回复给上位机调度系统开始工作状态 + /// + public event GetVisionData? VisionStartWorkEvent; + + /// + /// 一次码垛完成,发送码垛结果 + /// + public event GetVisionData? StackWorkDoneEvent; + + /// + /// 视觉系统发送给机器人码垛位置定位结果 + /// + public event GetVisionData? StackRobotLocationEvent; + + /// + /// 人工异常处理完成并更新计数 + /// + public event GetVisionData? ManualExceptionDealDoneEvent; + + private string ClientID = ""; + + public TcpServer(ILogger logger,TcpService tcpService, AppConfig appConfig) { _logger = logger; _service = tcpService; + _appConfig = appConfig; + } + + protected override Task ExecuteAsync(CancellationToken stoppingToken) + { + Init(_appConfig.TCPVisionConfig); + return Task.CompletedTask; } - public void Init(int serverPort) + public void Init(string serverPort) { try { - _service.Connecting = (client, e) => { + _service.Connecting = (client, e) => { _logger.LogInformation($"客户端{client.IP}正在接入服务"); return EasyTask.CompletedTask; }; - _service.Connected = (client, e) => { + _service.Connected = (client, e) => { _logger.LogInformation($"客户端{client.IP}接入服务成功"); RefreshClientInfoEvent?.Invoke(_service); + ClientID = client.Id; return EasyTask.CompletedTask; }; - _service.Disconnected = (client, e) => { + _service.Disconnected = (client, e) => { _logger.LogInformation($"客户端{client.IP}断开连接"); RefreshClientInfoEvent?.Invoke(_service); return EasyTask.CompletedTask; @@ -69,17 +114,18 @@ namespace SlnMesnac.TouchSocket _service.Received = (client, e) => { //从客户端收到信息 - var mes = Encoding.UTF8.GetString(e.ByteBlock.Buffer, 0, e.ByteBlock.Len);//注意:数据长度是byteBlock.Len + //var mes = Encoding.UTF8.GetString(e.ByteBlock.Buffer, 0, e.ByteBlock.Len);//注意:数据长度是byteBlock.Len byte[] receivedBuffer = new byte[e.ByteBlock.Len]; Array.Copy(e.ByteBlock.Buffer, 0, receivedBuffer, 0, e.ByteBlock.Len); - ReceivedClientBufferEvent?.Invoke(receivedBuffer); - + //ReceivedClientBufferEvent?.Invoke(receivedBuffer); + DataClassify(BufferDataAnalysis.BufferRootAnalysis(receivedBuffer)); + return EasyTask.CompletedTask; }; _service.Setup(new TouchSocketConfig()//载入配置 - .SetListenIPHosts(new IPHost[] { new IPHost($"0.0.0.0:{serverPort}") }) + .SetListenIPHosts(new IPHost[] { new IPHost(serverPort) }) .ConfigureContainer(a =>//容器的配置顺序应该在最前面 { a.AddConsoleLogger(); @@ -100,15 +146,37 @@ namespace SlnMesnac.TouchSocket } /// - /// 向所有客户端发送心跳 + /// 数据类型区分 /// - public void SendHeartBeat() + /// + public void DataClassify(TcpVisionEntity entity) { - var clients = _service.SocketClients.GetClients(); - foreach (var item in clients) + if(entity == null) { - _service.Send(item.Id,"heartbeat"); + _logger.LogError("数据格式校验错误!"); + return; + } + switch(entity.Command) + { + case 10: VisionSysStateEvent?.Invoke(entity); break; + case 11: VisionStartWorkEvent?.Invoke(entity); break; + case 12: StackWorkDoneEvent?.Invoke(entity); break; + case 13: StackRobotLocationEvent?.Invoke(entity); break; + case 14: ManualExceptionDealDoneEvent?.Invoke(entity); break; + default: _logger.LogInformation("未知命令字!"); return; } } + + ///// + ///// 向所有客户端发送心跳 + ///// + //public void SendHeartBeat() + //{ + // var clients = _service.SocketClients.GetClients(); + // foreach (var item in clients) + // { + // _service.Send(item.Id,"heartbeat"); + // } + //} } } diff --git a/SlnMesnac.WPF/Startup.cs b/SlnMesnac.WPF/Startup.cs index 3014da7..0809e26 100644 --- a/SlnMesnac.WPF/Startup.cs +++ b/SlnMesnac.WPF/Startup.cs @@ -48,6 +48,9 @@ namespace SlnMesnac.WPF //注册httpClient services.AddHostedService(); + //注册TCPServer + services.AddHostedService(); + //注册RFID工厂 //services.AddRfidFactorySetup(); diff --git a/SlnMesnac.WPF/ViewModel/IndexPage/IndexContentViewModel.cs b/SlnMesnac.WPF/ViewModel/IndexPage/IndexContentViewModel.cs index 6f5c118..d7b69b8 100644 --- a/SlnMesnac.WPF/ViewModel/IndexPage/IndexContentViewModel.cs +++ b/SlnMesnac.WPF/ViewModel/IndexPage/IndexContentViewModel.cs @@ -75,11 +75,11 @@ namespace SlnMesnac.WPF.ViewModel.IndexPage private void Init() { #region 测试数据 - _StateRefreshBusiness.UpdateManipulatorStateByResposne( - new Model.AirportApiEntity.ManipulatorStateRequestEntity() - { ManipulatorNo = "1", SignalSendTime = DateTime.Now.ToString()}); - List AirportTaskItem = _taskservice.GetTaskInfos(); - TaskItems = new ObservableCollection(AirportTaskItem); + //_StateRefreshBusiness.UpdateManipulatorStateByResposne( + // new Model.AirportApiEntity.ManipulatorStateRequestEntity() + // { ManipulatorNo = "1", SignalSendTime = DateTime.Now.ToString()}); + //List AirportTaskItem = _taskservice.GetTaskInfos(); + //TaskItems = new ObservableCollection(AirportTaskItem); // TaskItems = new ObservableCollection // { // new AirportTask { TaskCode = "1", StationName = "1#站台", TaskDetails = "入库:P04->C01", Status = "任务状态:执行中" }, diff --git a/SlnMesnac.WPF/appsettings.json b/SlnMesnac.WPF/appsettings.json index a9ef935..95b9835 100644 --- a/SlnMesnac.WPF/appsettings.json +++ b/SlnMesnac.WPF/appsettings.json @@ -62,6 +62,8 @@ ], "redisConfig": "175.27.215.92:6379,password=redis@2023", "AGVIpConfig": "127.0.0.1:4523", - "ManipulatorIpConfig": "127.0.0.1:4523" + "ManipulatorIpConfig": "127.0.0.1:4523", + "TCPVisionConfig": "127.0.0.1:6000" + } }