using Microsoft.Extensions.Logging;
|
using System.Net;
|
using System.Net.Sockets;
|
using WIDESEAWCS_Common;
|
using WIDESEAWCS_Core.Caches;
|
using WIDESEAWCS_Core.LogHelper;
|
using WIDESEAWCS_Tasks.Workflow.Abstractions;
|
|
namespace WIDESEAWCS_Tasks
|
{
|
/// <summary>
|
/// 机器人消息处理器 - 消息路由入口
|
/// </summary>
|
/// <remarks>
|
/// 核心职责:
|
/// 1. 缓存状态读取:从 Redis 中获取机器人最新的状态
|
/// 2. 命令分发:根据消息类型分发给不同的处理器
|
/// - 简单命令(如 homing、running):由 <see cref="IRobotSimpleCommandHandler"/> 处理
|
/// - 前缀命令(如 pickfinished、putfinished):由 <see cref="IRobotPrefixCommandHandler"/> 处理
|
/// 3. 回包触发:将原始消息回写到客户端
|
///
|
/// 这是消息处理管道的入口点,由 TcpSocketServer 的 MessageReceived 事件触发。
|
/// </remarks>
|
public class RobotMessageHandler : IRobotMessageRouter
|
{
|
/// <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(
|
ISocketClientGateway socketClientGateway,
|
RobotStateManager stateManager,
|
ICacheService cache,
|
IRobotSimpleCommandHandler simpleCommandHandler,
|
IRobotPrefixCommandHandler prefixCommandHandler,
|
ILogger<RobotJob> logger)
|
{
|
_socketClientGateway = socketClientGateway;
|
_stateManager = stateManager;
|
_cache = cache;
|
_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)
|
{
|
// 记录接收到的消息日志
|
_logger.LogInformation($"接收到客户端【{state.RobotCrane.DeviceName}】发送消息【{message}】");
|
QuartzLogger.Info($"接收到客户端消息【{message}】", state.RobotCrane.DeviceName);
|
|
// 构建缓存键,检查 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();
|
|
// 尝试用简单命令处理器处理
|
// 简单命令包括:homing、homed、running、pausing、runmode、controlmode 等
|
if (await _simpleCommandHandler.HandleAsync(messageLower, activeState))
|
{
|
// 处理成功后,将原消息回写到客户端(保持原有行为)
|
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;
|
}
|
}
|
}
|