wanshenmean
8 天以前 adb4016b5eb5b119a899480c321be996d9bf10bd
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotMessageHandler.cs
@@ -1,275 +1,174 @@
using Microsoft.Extensions.Logging;
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_Core.LogHelper;
using WIDESEAWCS_Tasks.Workflow.Abstractions;
namespace WIDESEAWCS_Tasks
{
    /// <summary>
    /// 机械手消息处理器 - 负责处理来自TCP客户端的消息
    /// 机器人消息处理器 - 消息路由入口
    /// </summary>
    public class RobotMessageHandler
    /// <remarks>
    /// 核心职责:
    /// 1. 缓存状态读取:从 Redis 中获取机器人最新的状态
    /// 2. 命令分发:根据消息类型分发给不同的处理器
    ///    - 简单命令(如 homing、running):由 <see cref="IRobotSimpleCommandHandler"/> 处理
    ///    - 前缀命令(如 pickfinished、putfinished):由 <see cref="IRobotPrefixCommandHandler"/> 处理
    /// 3. 回包触发:将原始消息回写到客户端
    ///
    /// 这是消息处理管道的入口点,由 TcpSocketServer 的 MessageReceived 事件触发。
    /// </remarks>
    public class RobotMessageHandler : IRobotMessageRouter
    {
        private readonly TcpSocketServer _tcpSocket;
        private readonly RobotStateManager _stateManager;
        private readonly ICacheService _cache;
        private readonly IRobotTaskService _robotTaskService;
        private readonly RobotTaskProcessor _taskProcessor;
        /// <summary>
        /// Socket 客户端网关接口
        /// </summary>
        /// <remarks>
        /// 用于向客户端发送响应消息。
        /// </remarks>
        private readonly ISocketClientGateway _socketClientGateway;
        /// <summary>
        /// 机械手状态管理器
        /// </summary>
        /// <remarks>
        /// 用于读取和更新机器人的状态。
        /// </remarks>
        private readonly RobotStateManager _stateManager;
        /// <summary>
        /// 缓存服务
        /// </summary>
        /// <remarks>
        /// 直接使用缓存服务检查状态是否存在。
        /// </remarks>
        private readonly ICacheService _cache;
        /// <summary>
        /// 简单命令处理器
        /// </summary>
        /// <remarks>
        /// 处理简单的状态更新命令,如运行状态、模式切换等。
        /// </remarks>
        private readonly IRobotSimpleCommandHandler _simpleCommandHandler;
        /// <summary>
        /// 前缀命令处理器
        /// </summary>
        /// <remarks>
        /// 处理带参数的前缀命令,如 pickfinished(取货完成)、putfinished(放货完成)。
        /// </remarks>
        private readonly IRobotPrefixCommandHandler _prefixCommandHandler;
        /// <summary>
        /// 日志记录器
        /// </summary>
        private readonly ILogger<RobotJob> _logger;
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="socketClientGateway">Socket 网关</param>
        /// <param name="stateManager">状态管理器</param>
        /// <param name="cache">缓存服务</param>
        /// <param name="simpleCommandHandler">简单命令处理器</param>
        /// <param name="prefixCommandHandler">前缀命令处理器</param>
        /// <param name="logger">日志记录器</param>
        public RobotMessageHandler(
            TcpSocketServer tcpSocket,
            ISocketClientGateway socketClientGateway,
            RobotStateManager stateManager,
            ICacheService cache,
            IRobotTaskService robotTaskService,
            RobotTaskProcessor taskProcessor)
            IRobotSimpleCommandHandler simpleCommandHandler,
            IRobotPrefixCommandHandler prefixCommandHandler,
            ILogger<RobotJob> logger)
        {
            _tcpSocket = tcpSocket;
            _socketClientGateway = socketClientGateway;
            _stateManager = stateManager;
            _cache = cache;
            _robotTaskService = robotTaskService;
            _taskProcessor = taskProcessor;
            _simpleCommandHandler = simpleCommandHandler;
            _prefixCommandHandler = prefixCommandHandler;
            _logger = logger;
        }
        /// <summary>
        /// 处理接收到的消息
        /// </summary>
        /// <remarks>
        /// 处理流程:
        /// 1. 记录日志(记录原始消息内容)
        /// 2. 验证缓存中是否存在该设备的状态
        /// 3. 尝试用简单命令处理器处理(状态更新类命令)
        ///    - 如果处理成功,回写原消息并更新状态
        /// 4. 如果不是简单命令,检查是否是前缀命令(pickfinished/putfinished)
        ///    - 如果是,调用前缀命令处理器处理
        /// 5. 保持原有行为:简单命令和前缀命令都回写原消息
        ///
        /// 注意:此方法可能在 TCP 消息接收的上下文中被频繁调用,需注意性能。
        /// </remarks>
        /// <param name="message">原始消息字符串</param>
        /// <param name="isJson">消息是否为 JSON 格式(当前未使用)</param>
        /// <param name="client">TCP 客户端连接</param>
        /// <param name="state">机器人当前状态</param>
        /// <returns>响应消息,如果无需回复则返回 null</returns>
        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)
                return null;
            // 记录接收到的消息日志
            _logger.LogInformation($"接收到客户端【{state.RobotCrane.DeviceName}】发送消息【{message}】");
            QuartzLogger.Info($"接收到客户端消息【{message}】", state.RobotCrane.DeviceName);
            // 检查任务总数是否未达到上限
            if (state.RobotTaskTotalNum > RobotConst.MaxTaskTotalNum)
            {
                // 记录接收到的消息日志
                _logger.LogInformation($"接收到客户端【{state.RobotCrane.DeviceName}】发送消息【{message}】");
                QuartzLogger.Info($"接收到客户端消息【{message}】", state.RobotCrane.DeviceName);
                // 处理成功后,将原消息回写到客户端(保持原有行为)
                await _socketClientGateway.SendMessageAsync(client, message);
                return null;
            }
            // 构建缓存键,检查 Redis 中是否存在该设备的状态
            var cacheKey = $"{RedisPrefix.Code}:{RedisName.SocketDevices}:{client.Client.RemoteEndPoint}";
            // 如果缓存中不存在或状态为 null,忽略此消息
            if (!_cache.TryGetValue(cacheKey, out RobotSocketState? cachedState) || cachedState == null)
            {
                return null;
            }
            // 使用缓存中获取的状态
            var activeState = cachedState;
            // 将消息转换为小写(用于简单命令匹配)
            string messageLower = message.ToLowerInvariant();
            if (await IsSimpleCommandAsync(messageLower, state))
            // 尝试用简单命令处理器处理
            // 简单命令包括:homing、homed、running、pausing、runmode、controlmode 等
            if (await _simpleCommandHandler.HandleAsync(messageLower, activeState))
            {
                await _tcpSocket.SendMessageAsync(client, message);
                if (_stateManager.TryUpdateStateSafely(state.IPAddress, state))
                    return null;
            }
            else if (IsPrefixCommand(messageLower))
            {
                await HandlePrefixCommandAsync(message, state, client);
                // 处理成功后,将原消息回写到客户端(保持原有行为)
                await _socketClientGateway.SendMessageAsync(client, message);
                _logger.LogInformation($"发送消息【{message}】");
                QuartzLogger.Info($"发送消息:【{message}】", state.RobotCrane.DeviceName);
                // 安全更新状态到 Redis
                _stateManager.TryUpdateStateSafely(activeState.IPAddress, activeState);
                return null;
            }
            // 如果不是简单命令,检查是否是前缀命令
            // 前缀命令包括:pickfinished、putfinished(后面跟逗号分隔的位置参数)
            if (_prefixCommandHandler.IsPrefixCommand(messageLower))
            {
                // 调用前缀命令处理器
                // 前缀命令处理器会解析位置参数并更新状态
                await _prefixCommandHandler.HandleAsync(message, activeState, client);
            }
            // 默认返回 null,不产生响应消息
            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");
        }
    }
}
}