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;
///
/// 假电芯位置服务
///
///
/// 用于释放假电芯点位。
///
private readonly IFakeBatteryPositionService _fakeBatteryPositionService;
///
/// 构造函数
///
/// 任务服务
/// 任务处理器
/// 状态管理器
/// Socket 网关
/// 假电芯位置服务
public RobotPrefixCommandHandler(
IRobotTaskService robotTaskService,
RobotTaskProcessor taskProcessor,
RobotStateManager stateManager,
ISocketClientGateway socketClientGateway,
IFakeBatteryPositionService fakeBatteryPositionService)
{
_robotTaskService = robotTaskService;
_taskProcessor = taskProcessor;
_stateManager = stateManager;
_socketClientGateway = socketClientGateway;
_fakeBatteryPositionService = fakeBatteryPositionService;
}
///
/// 检查消息是否为前缀命令
///
///
/// 前缀命令必须以 "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. 换盘任务 Phase3 取假电芯时不调用拆盘 API
/// 3. 更新当前动作为"取货完成"
/// 4. 记录取货完成的位置
/// 5. 更新任务状态为"机器人取货完成"
/// 6. 安全更新状态到 Redis
///
/// 机器人当前状态
/// 取货完成的位置编号数组
/// 机器人任务记录
private async Task HandlePickFinishedAsync(RobotSocketState state, int[] positions, Dt_RobotTask? task)
{
// 记录取货完成的位置
state.LastPickPositions = positions;
// 换盘任务 Phase3 取假电芯:不调用拆盘 API
if (state.CurrentTask?.RobotTaskType == RobotTaskTypeEnum.ChangePallet.GetHashCode()
&& state.ChangePalletPhase == 3)
{
state.CurrentAction = "PickFinished";
}
// 拆盘任务
else if (state.IsSplitPallet)
{
// 构建库存 DTO,包含位置信息和托盘条码
var stockDTO = RobotTaskProcessor.BuildStockDTO(state, positions);
// 调用拆盘 API
var result = _taskProcessor.PostSplitPalletAsync(stockDTO);
// 如果 API 调用成功
if (result.Data.Status && result.IsSuccess)
{
state.CurrentAction = "PickFinished";
}
}
else
{
// 非拆盘任务,直接更新动作
state.CurrentAction = "PickFinished";
}
// 如果任务存在
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. 换盘任务根据阶段区分处理:流向A Phase2 不调用 API;流向B Phase2 正常调用;Phase4 调用 MarkAsAvailable
/// 3. 如果组盘成功,增加任务计数
/// 4. 更新当前动作为"放货完成"
/// 5. 更新任务状态为"机器人放货完成"
/// 6. 安全更新状态到 Redis
///
/// 机器人当前状态
/// 放货完成的位置编号数组
/// 机器人任务记录
private async Task HandlePutFinishedAsync(RobotSocketState state, int[] positions, Dt_RobotTask? task)
{
// 假设放货成功(如果后续 API 调用失败也不回退计数)
bool putSuccess = true;
// 如果是组盘任务(包含换盘)
if (state.IsGroupPallet)
{
// 记录放货完成的位置
state.LastPutPositions = positions;
// 判断是否为换盘任务
var isChangePallet = state.CurrentTask?.RobotTaskType == RobotTaskTypeEnum.ChangePallet.GetHashCode();
var isFlowA = state.CurrentTask?.RobotSourceAddressLineCode is "11001" or "11010";
if (isChangePallet)
{
// 换盘任务:根据阶段区分处理
if (state.ChangePalletPhase == 2)
{
if (isFlowA)
{
// 流向A Phase2:放假电芯到目标托盘,不调用 API,不递增计数
// 仅更新状态
}
else
{
// 流向B Phase2:放正常电芯,递增计数
state.RobotTaskTotalNum += positions.Length;
if (task != null)
task.RobotTaskTotalNum -= positions.Length;
// 构建库存 DTO 并调用 ChangePalletAsync API
var stockDTO = RobotTaskProcessor.BuildStockDTO(state, positions);
var result = _taskProcessor.PostGroupPalletAsync(nameof(ConfigKey.ChangePalletAsync), stockDTO);
putSuccess = result.Data.Status && result.IsSuccess;
}
}
else if (state.ChangePalletPhase == 4)
{
// 流向B Phase4:放假电芯到5号位,不调用 API,不递增计数,释放点位
_fakeBatteryPositionService.MarkAsAvailable(positions.ToList());
}
else
{
// 非批次模式:正常递增计数并调用 API
state.RobotTaskTotalNum += positions.Length;
if (task != null)
task.RobotTaskTotalNum -= positions.Length;
var stockDTO = RobotTaskProcessor.BuildStockDTO(state, positions);
var result = _taskProcessor.PostGroupPalletAsync(nameof(ConfigKey.GroupPalletAsync), stockDTO);
putSuccess = result.Data.Status && result.IsSuccess;
}
}
else
{
// 组盘任务:原有逻辑
var stockDTO = RobotTaskProcessor.BuildStockDTO(state, positions);
var result = _taskProcessor.PostGroupPalletAsync(nameof(ConfigKey.GroupPalletAsync), stockDTO);
putSuccess = result.Data.Status && result.IsSuccess;
// 增加任务计数
state.RobotTaskTotalNum += positions.Length;
if (task != null)
task.RobotTaskTotalNum -= positions.Length;
}
}
// 如果放货成功
if (putSuccess)
{
// 更新当前动作为"放货完成"
state.CurrentAction = "PutFinished";
// 非组盘任务时增加计数(组盘任务已在上面递增)
if (!state.IsGroupPallet)
{
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);
}
}
}
}
}