using System.Net.Sockets;
using WIDESEAWCS_Common.HttpEnum;
using WIDESEAWCS_Common.TaskEnum;
using WIDESEAWCS_DTO.TaskInfo;
using WIDESEAWCS_ITaskInfoService;
using WIDESEAWCS_Model.Models;
using WIDESEAWCS_Tasks.Workflow.Abstractions;
namespace WIDESEAWCS_Tasks.Workflow
{
///
/// 前缀命令处理器
///
///
/// 迁移原 RobotMessageHandler 的 pickfinished/putfinished 分支。
///
/// 前缀命令是指以特定前缀开头的命令,后面跟随逗号分隔的参数。
/// 格式:{前缀},{参数1},{参数2},...
///
/// 当前支持的前缀命令:
/// - pickfinished: 取货完成,后面跟随完成的位置编号列表
/// - putfinished: 放货完成,后面跟随完成的位置编号列表
///
/// 这些命令通常包含取货或放货的位置信息,需要解析并更新状态。
///
public class RobotPrefixCommandHandler : IRobotPrefixCommandHandler
{
///
/// 机器人任务服务
///
///
/// 用于查询和更新任务记录。
///
private readonly IRobotTaskService _robotTaskService;
///
/// 任务处理器
///
///
/// 用于处理取货/放货完成时的业务逻辑,如调用拆盘/组盘 API。
///
private readonly RobotTaskProcessor _taskProcessor;
///
/// 状态管理器
///
///
/// 用于安全更新机器人的状态。
///
private readonly RobotStateManager _stateManager;
///
/// Socket 网关
///
///
/// 用于向客户端发送响应消息。
///
private readonly ISocketClientGateway _socketClientGateway;
///
/// 构造函数
///
/// 任务服务
/// 任务处理器
/// 状态管理器
/// Socket 网关
public RobotPrefixCommandHandler(
IRobotTaskService robotTaskService,
RobotTaskProcessor taskProcessor,
RobotStateManager stateManager,
ISocketClientGateway socketClientGateway)
{
_robotTaskService = robotTaskService;
_taskProcessor = taskProcessor;
_stateManager = stateManager;
_socketClientGateway = socketClientGateway;
}
///
/// 检查消息是否为前缀命令
///
///
/// 前缀命令必须以 "pickfinished" 或 "putfinished" 开头(不区分大小写)。
///
/// 消息内容(小写形式)
/// 如果是指缀命令返回 true
public bool IsPrefixCommand(string message)
{
// 检查消息是否以 pickfinished 或 putfinished 开头
return message.StartsWith("pickfinished") || message.StartsWith("putfinished");
}
///
/// 处理前缀命令
///
///
/// 处理流程:
/// 1. 解析消息,提取位置参数
/// 2. 查询当前任务
/// 3. 根据命令类型调用相应的处理方法
/// 4. 回写原消息到客户端
///
/// 消息格式:{命令前缀},{位置1},{位置2},...
/// 示例:pickfinished,1,2,3 表示取货完成,位置 1、2、3 的货物已取走
///
/// 原始消息内容
/// 机器人当前状态
/// TCP 客户端连接,用于发送响应
public async Task HandleAsync(string message, RobotSocketState state, TcpClient client)
{
try
{
// 按逗号分隔消息,提取命令和参数
// 例如:pickfinished,1,2,3 -> ["pickfinished", "1", "2", "3"]
var parts = message.Split(',');
// 检查消息格式是否有效:至少要有命令前缀,且状态中有当前任务
if (parts.Length < 1 || state.CurrentTask == null)
{
return;
}
// 提取命令前缀并转换为小写
var cmd = parts[0].ToLowerInvariant();
// 解析位置参数(跳过命令前缀,处理后面的数字)
// 过滤掉无法解析为数字或值为 0 的位置
int[] positions = parts.Skip(1)
.Select(p => int.TryParse(p, out int value) ? value : (int?)null) // 尝试解析为整数
.Where(v => v.HasValue && v.Value != 0) // 过滤掉 null 和 0
.Select(v => v!.Value) // 提取值(已知非 null)
.ToArray();
// 从数据库重新查询当前任务(确保获取最新状态)
var task = await _robotTaskService.Repository.QueryFirstAsync(x => x.RobotTaskId == state.CurrentTask.RobotTaskId);
// 根据命令前缀分发处理
if (cmd.StartsWith("pickfinished"))
{
// 处理取货完成
await HandlePickFinishedAsync(state, positions, task);
}
else if (cmd.StartsWith("putfinished"))
{
// 处理放货完成
await HandlePutFinishedAsync(state, positions, task);
}
// 回写原消息到客户端(保持原有行为)
await _socketClientGateway.SendMessageAsync(client, message);
}
catch (Exception ex)
{
// 捕获并记录异常,防止异常向上传播导致消息处理中断
Console.WriteLine($"RobotJob MessageReceived Error: {ex.Message}");
}
}
///
/// 处理取货完成(pickfinished)命令
///
///
/// 处理逻辑:
/// 1. 如果是拆盘任务,构建库存 DTO 并调用拆盘 API
/// 2. 更新当前动作为"取货完成"
/// 3. 记录取货完成的位置
/// 4. 更新任务状态为"机器人取货完成"
/// 5. 安全更新状态到 Redis
///
/// 机器人当前状态
/// 取货完成的位置编号数组
/// 机器人任务记录
private async Task HandlePickFinishedAsync(RobotSocketState state, int[] positions, Dt_RobotTask? task)
{
// 如果是拆盘任务
if (state.IsSplitPallet)
{
// 构建库存 DTO,包含位置信息和托盘条码
var stockDTO = RobotTaskProcessor.BuildStockDTO(state, positions);
// 记录取货完成的位置
state.LastPickPositions = positions;
// 调用拆盘 API
var result = _taskProcessor.PostSplitPalletAsync(stockDTO);
// 如果 API 调用成功
if (result.Data.Status && result.IsSuccess)
{
// 更新当前动作为"取货完成"
state.CurrentAction = "PickFinished";
}
}
else
{
// 非拆盘任务,直接更新动作
state.CurrentAction = "PickFinished";
}
// 记录取货完成的位置(无论是否拆盘都记录)
state.LastPickPositions = positions;
// 如果任务存在
if (task != null)
{
// 更新任务状态为"机器人取货完成"
task.RobotTaskState = TaskRobotStatusEnum.RobotPickFinish.GetHashCode();
// 安全更新状态到 Redis,确保更新成功后再更新数据库
if (_stateManager.TryUpdateStateSafely(state.IPAddress, state))
{
await _robotTaskService.Repository.UpdateDataAsync(task);
}
}
}
///
/// 处理放货完成(putfinished)命令
///
///
/// 处理逻辑:
/// 1. 如果是组盘任务,构建库存 DTO 并调用组盘/换盘 API
/// 2. 如果组盘成功,增加任务计数
/// 3. 更新当前动作为"放货完成"
/// 4. 更新任务状态为"机器人放货完成"
/// 5. 安全更新状态到 Redis
///
/// 机器人当前状态
/// 放货完成的位置编号数组
/// 机器人任务记录
private async Task HandlePutFinishedAsync(RobotSocketState state, int[] positions, Dt_RobotTask? task)
{
// 假设放货成功(如果后续 API 调用失败也不回退计数)
bool putSuccess = true;
// 如果是组盘任务(包含换盘)
if (state.IsGroupPallet)
{
// 记录放货完成的位置
state.LastPutPositions = positions;
// 构建库存 DTO
var stockDTO = RobotTaskProcessor.BuildStockDTO(state, positions);
// 根据任务类型决定调用哪个 API
// 换盘任务调用 ChangePalletAsync,组盘任务调用 GroupPalletAsync
var configKey = state.CurrentTask?.RobotTaskType == RobotTaskTypeEnum.ChangePallet.GetHashCode()
? nameof(ConfigKey.ChangePalletAsync)
: nameof(ConfigKey.GroupPalletAsync);
// 调用组盘/换盘 API
var result = _taskProcessor.PostGroupPalletAsync(configKey, stockDTO);
// 检查 API 返回状态
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();
// 安全更新状态到 Redis
if (_stateManager.TryUpdateStateSafely(state.IPAddress, state))
{
await _robotTaskService.Repository.UpdateDataAsync(task);
}
}
}
}
}