| | |
| | | using System.Net.Sockets; |
| | | using System.Net.Sockets; |
| | | using WIDESEAWCS_Common; |
| | | using WIDESEAWCS_Common.HttpEnum; |
| | | using WIDESEAWCS_Common.TaskEnum; |
| | | using WIDESEAWCS_Core.Caches; |
| | | using WIDESEAWCS_Core.Helper; |
| | | using WIDESEAWCS_DTO.Stock; |
| | | using WIDESEAWCS_DTO.TaskInfo; |
| | | using WIDESEAWCS_ITaskInfoService; |
| | | using WIDESEAWCS_Model.Models; |
| | | using WIDESEAWCS_Tasks.SocketServer; |
| | | using WIDESEAWCS_Tasks.Workflow.Abstractions; |
| | | |
| | | namespace WIDESEAWCS_Tasks |
| | | { |
| | | /// <summary> |
| | | /// 机械手消息处理器 - 负责处理来自TCP客户端的消息 |
| | | /// 机器人消息路由入口:负责缓存状态读取、命令分发和回包触发。 |
| | | /// </summary> |
| | | public class RobotMessageHandler |
| | | public class RobotMessageHandler : IRobotMessageRouter |
| | | { |
| | | private readonly TcpSocketServer _tcpSocket; |
| | | private readonly ISocketClientGateway _socketClientGateway; |
| | | private readonly RobotStateManager _stateManager; |
| | | private readonly ICacheService _cache; |
| | | private readonly IRobotTaskService _robotTaskService; |
| | | private readonly RobotTaskProcessor _taskProcessor; |
| | | private readonly IRobotSimpleCommandHandler _simpleCommandHandler; |
| | | private readonly IRobotPrefixCommandHandler _prefixCommandHandler; |
| | | |
| | | public RobotMessageHandler( |
| | | TcpSocketServer tcpSocket, |
| | | ISocketClientGateway socketClientGateway, |
| | | RobotStateManager stateManager, |
| | | ICacheService cache, |
| | | IRobotTaskService robotTaskService, |
| | | RobotTaskProcessor taskProcessor) |
| | | IRobotSimpleCommandHandler simpleCommandHandler, |
| | | IRobotPrefixCommandHandler prefixCommandHandler) |
| | | { |
| | | _tcpSocket = tcpSocket; |
| | | _socketClientGateway = socketClientGateway; |
| | | _stateManager = stateManager; |
| | | _cache = cache; |
| | | _robotTaskService = robotTaskService; |
| | | _taskProcessor = taskProcessor; |
| | | _simpleCommandHandler = simpleCommandHandler; |
| | | _prefixCommandHandler = prefixCommandHandler; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 处理接收到的消息 |
| | | /// 处理接收到的消息。保持原有行为:简单命令和前缀命令都回写原消息。 |
| | | /// </summary> |
| | | public async Task<string?> HandleMessageReceivedAsync(string message, bool isJson, TcpClient client, RobotSocketState state) |
| | | { |
| | | if (!(_cache?.TryGetValue($"{RedisPrefix.Code}:{RedisName.SocketDevices}:{client.Client.RemoteEndPoint}", out state)) ?? false) |
| | | var cacheKey = $"{RedisPrefix.Code}:{RedisName.SocketDevices}:{client.Client.RemoteEndPoint}"; |
| | | if (!_cache.TryGetValue(cacheKey, out RobotSocketState? cachedState) || cachedState == null) |
| | | { |
| | | return null; |
| | | } |
| | | |
| | | var activeState = cachedState; |
| | | string messageLower = message.ToLowerInvariant(); |
| | | |
| | | if (await IsSimpleCommandAsync(messageLower, state)) |
| | | if (await _simpleCommandHandler.HandleAsync(messageLower, activeState)) |
| | | { |
| | | await _tcpSocket.SendMessageAsync(client, message); |
| | | if (_stateManager.TryUpdateStateSafely(state.IPAddress, state)) |
| | | return null; |
| | | await _socketClientGateway.SendMessageAsync(client, message); |
| | | _stateManager.TryUpdateStateSafely(activeState.IPAddress, activeState); |
| | | return null; |
| | | } |
| | | else if (IsPrefixCommand(messageLower)) |
| | | |
| | | if (_prefixCommandHandler.IsPrefixCommand(messageLower)) |
| | | { |
| | | await HandlePrefixCommandAsync(message, state, client); |
| | | await _prefixCommandHandler.HandleAsync(message, activeState, client); |
| | | } |
| | | |
| | | return null; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 处理前缀命令(pickfinished, putfinished) |
| | | /// </summary> |
| | | private async Task HandlePrefixCommandAsync(string message, RobotSocketState state, TcpClient client) |
| | | { |
| | | try |
| | | { |
| | | var parts = message.Split(','); |
| | | if (parts.Length >= 1 && state.CurrentTask != null) |
| | | { |
| | | var cmd = parts[0].ToLowerInvariant(); |
| | | int[] positions = parts.Skip(1) |
| | | .Select(p => int.TryParse(p, out int value) ? value : (int?)null) |
| | | .Where(v => v.HasValue && v.Value != 0) |
| | | .Select(v => v!.Value) |
| | | .ToArray(); |
| | | |
| | | var task = await _robotTaskService.Repository.QueryFirstAsync(x => x.RobotTaskId == state.CurrentTask.RobotTaskId); |
| | | |
| | | if (cmd.StartsWith("pickfinished")) |
| | | { |
| | | await HandlePickFinishedAsync(state, positions, task, client); |
| | | } |
| | | else if (cmd.StartsWith("putfinished")) |
| | | { |
| | | await HandlePutFinishedAsync(state, positions, task, client); |
| | | } |
| | | |
| | | await _tcpSocket.SendMessageAsync(client, message); |
| | | } |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | Console.WriteLine($"RobotJob MessageReceived Error: {ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 处理取货完成命令 |
| | | /// </summary> |
| | | private async Task HandlePickFinishedAsync(RobotSocketState state, int[] positions, Dt_RobotTask? task, TcpClient client) |
| | | { |
| | | if (state.IsSplitPallet) |
| | | { |
| | | var stockDTO = RobotTaskProcessor.BuildStockDTO(state, positions); |
| | | state.LastPickPositions = positions; |
| | | |
| | | var result = _taskProcessor.PostSplitPalletAsync(stockDTO); |
| | | |
| | | if (result.Data.Status && result.IsSuccess) |
| | | { |
| | | state.CurrentAction = "PickFinished"; |
| | | } |
| | | } |
| | | else |
| | | { |
| | | state.CurrentAction = "PickFinished"; |
| | | } |
| | | |
| | | state.LastPickPositions = positions; |
| | | if (task != null) |
| | | { |
| | | task.RobotTaskState = TaskRobotStatusEnum.RobotPickFinish.GetHashCode(); |
| | | if (_stateManager.TryUpdateStateSafely(state.IPAddress, state)) |
| | | await _robotTaskService.Repository.UpdateDataAsync(task); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 处理放货完成命令 |
| | | /// </summary> |
| | | private async Task HandlePutFinishedAsync(RobotSocketState state, int[] positions, Dt_RobotTask? task, TcpClient client) |
| | | { |
| | | bool putSuccess = true; |
| | | if (state.IsGroupPallet) |
| | | { |
| | | state.LastPutPositions = positions; |
| | | var stockDTO = RobotTaskProcessor.BuildStockDTO(state, positions); |
| | | var configKey = state.CurrentTask?.RobotTaskType == RobotTaskTypeEnum.ChangePallet.GetHashCode() |
| | | ? nameof(ConfigKey.ChangePalletAsync) : nameof(ConfigKey.GroupPalletAsync); |
| | | |
| | | var result = _taskProcessor.PostGroupPalletAsync(configKey, stockDTO); |
| | | putSuccess = result.Data.Status && result.IsSuccess; |
| | | } |
| | | |
| | | if (putSuccess) |
| | | { |
| | | state.CurrentAction = "PutFinished"; |
| | | state.RobotTaskTotalNum += positions.Length; |
| | | if (task != null) |
| | | { |
| | | task.RobotTaskTotalNum += positions.Length; |
| | | } |
| | | } |
| | | |
| | | if (task != null) |
| | | { |
| | | task.RobotTaskState = TaskRobotStatusEnum.RobotPutFinish.GetHashCode(); |
| | | if (_stateManager.TryUpdateStateSafely(state.IPAddress, state)) |
| | | await _robotTaskService.Repository.UpdateDataAsync(task); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 机械手简单命令处理 |
| | | /// </summary> |
| | | private async Task<bool> IsSimpleCommandAsync(string message, RobotSocketState state) |
| | | { |
| | | RobotTaskTypeEnum? GetRobotTaskType() => state.CurrentTask != null ? (RobotTaskTypeEnum)state.CurrentTask.RobotTaskType : null; |
| | | switch (message) |
| | | { |
| | | case "homing": |
| | | state.OperStatus = "Homing"; |
| | | return true; |
| | | |
| | | case "homed": |
| | | state.OperStatus = "Homed"; |
| | | return true; |
| | | |
| | | case "picking": |
| | | state.CurrentAction = "Picking"; |
| | | return true; |
| | | |
| | | case "puting": |
| | | state.CurrentAction = "Putting"; |
| | | return true; |
| | | |
| | | case "allpickfinished": // 取货完成 |
| | | state.CurrentAction = "AllPickFinished"; |
| | | var robotTaskType = GetRobotTaskType(); |
| | | |
| | | if (robotTaskType == RobotTaskTypeEnum.SplitPallet || robotTaskType == RobotTaskTypeEnum.ChangePallet) |
| | | { |
| | | if (await _taskProcessor.HandleInboundTaskAsync(state, useSourceAddress: true)) |
| | | { |
| | | _taskProcessor.DeleteTask(state.CurrentTask.RobotTaskId); |
| | | return true; |
| | | } |
| | | } |
| | | return false; |
| | | |
| | | case "allputfinished": // 放货完成 |
| | | state.CurrentAction = "AllPutFinished"; |
| | | robotTaskType = GetRobotTaskType(); |
| | | |
| | | if (robotTaskType == RobotTaskTypeEnum.GroupPallet || robotTaskType == RobotTaskTypeEnum.ChangePallet) |
| | | { |
| | | if (await _taskProcessor.HandleInboundTaskAsync(state, useSourceAddress: false)) |
| | | { |
| | | _taskProcessor.DeleteTask(state.CurrentTask.RobotTaskId); |
| | | state.CurrentTask = null; |
| | | state.RobotTaskTotalNum = 0; |
| | | state.CellBarcode = new List<string>(); |
| | | return true; |
| | | } |
| | | } |
| | | return false; |
| | | |
| | | case "running": |
| | | state.OperStatus = "Running"; |
| | | return true; |
| | | |
| | | case "pausing": |
| | | state.OperStatus = "Pausing"; |
| | | return true; |
| | | |
| | | case "warming": |
| | | state.OperStatus = "Warming"; |
| | | return true; |
| | | |
| | | case "emstoping": |
| | | state.OperStatus = "Emstoping"; |
| | | return true; |
| | | |
| | | case "runmode,1": |
| | | state.RobotRunMode = 1; |
| | | return true; |
| | | |
| | | case "runmode,2": |
| | | state.RobotRunMode = 2; |
| | | return true; |
| | | |
| | | case "controlmode,1": |
| | | state.RobotControlMode = 1; |
| | | return true; |
| | | |
| | | case "controlmode,2": |
| | | state.RobotControlMode = 2; |
| | | return true; |
| | | |
| | | case "armobject,1": |
| | | state.RobotArmObject = 1; |
| | | return true; |
| | | |
| | | case "armobject,0": |
| | | state.RobotArmObject = 0; |
| | | return true; |
| | | |
| | | default: |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 判断是否为前缀命令 |
| | | /// </summary> |
| | | private static bool IsPrefixCommand(string message) |
| | | { |
| | | return message.StartsWith("pickfinished") || message.StartsWith("putfinished"); |
| | | } |
| | | } |
| | | } |