using Microsoft.Extensions.Logging; using WIDESEA_Core; using WIDESEAWCS_Common.TaskEnum; using WIDESEAWCS_Core.Helper; using WIDESEAWCS_Core.LogHelper; using WIDESEAWCS_ITaskInfoService; using WIDESEAWCS_Model.Models; using WIDESEAWCS_Tasks.Workflow.Abstractions; namespace WIDESEAWCS_Tasks.Workflow { /// /// 机器人任务编排器 - 负责 RobotJob 中的状态机流转和执行步骤编排 /// /// /// 迁移原 RobotJob 状态机流转支持,降低 Job 层的复杂度。 /// /// 核心职责: /// 1. 根据机器人当前状态和任务状态,决定下一步动作 /// 2. 处理取货完成后的放货指令下发 /// 3. 处理放货完成后的取货指令下发(组盘场景) /// /// 状态机流转规则: /// - 条件:RobotRunMode == 2(自动模式)且 RobotControlMode == 1(客户端控制)且 OperStatus != "Running" /// - 取货完成 -> 放货:PickFinished + RobotArmObject == 1 + RobotPickFinish -> 发送 Putbattery /// - 放货完成 -> 取货:PutFinished + Homed + RobotArmObject == 0 -> 发送 Pickbattery(组盘/换盘场景) /// public class RobotWorkflowOrchestrator : IRobotWorkflowOrchestrator { /// /// 机械手状态管理器 /// /// /// 用于读取和更新机器人的状态。 /// private readonly RobotStateManager _stateManager; /// /// 机械手客户端管理器 /// /// /// 用于向机器人客户端发送指令。 /// private readonly RobotClientManager _clientManager; /// /// 任务处理器 /// /// /// 用于执行任务相关的业务逻辑,如发送取货指令。 /// private readonly RobotTaskProcessor _taskProcessor; /// /// 机器人任务服务 /// /// /// 用于更新任务状态。 /// private readonly IRobotTaskService _robotTaskService; /// /// 日志记录器 /// private readonly ILogger _logger; /// /// 构造函数 /// /// 状态管理器 /// 客户端管理器 /// 任务处理器 /// 任务服务 /// 日志记录器 public RobotWorkflowOrchestrator( RobotStateManager stateManager, RobotClientManager clientManager, RobotTaskProcessor taskProcessor, IRobotTaskService robotTaskService, ILogger logger) { _stateManager = stateManager; _clientManager = clientManager; _taskProcessor = taskProcessor; _robotTaskService = robotTaskService; _logger = logger; } /// /// 执行任务编排流程 /// /// /// 根据机器人当前状态和任务状态,决定是否下发指令。 /// /// 执行条件: /// 1. 机器人处于自动模式(RobotRunMode == 2) /// 2. 机器人处于客户端控制模式(RobotControlMode == 1) /// 3. 机器人不在运行中(OperStatus != "Running") /// /// 流转逻辑: /// - 取货完成且手臂有货 -> 发送放货指令(Putbattery) /// - 放货完成且手臂无货 -> 发送取货指令(Pickbattery) /// /// 机器人最新状态 /// 待执行的机器人任务 /// 机器人 IP 地址 public async Task ExecuteAsync(RobotSocketState latestState, Dt_RobotTask task, string ipAddress) { // 按原方案分支判断,确保逻辑一致 // 检查是否满足自动控制条件: // 1. 运行模式为自动(2) // 2. 控制模式为客户端控制(1) // 3. 运行状态是 Running if (latestState.RobotRunMode == 2 /*&& latestState.RobotControlMode == 1*/ && latestState.OperStatus == "Running" && latestState.Homed == "Homed") { // ========== 取货完成后的放货处理 ========== // 条件: // - 当前动作是 PickFinished 或 AllPickFinished(取货完成) // - 手臂上有物料(RobotArmObject == 1) // - 任务状态为 RobotPickFinish(已记录取货完成) if ((latestState.CurrentAction == "PickFinished" || latestState.CurrentAction == "AllPickFinished") && latestState.RobotArmObject == 1 && task.RobotTaskState == TaskRobotStatusEnum.RobotPickFinish.GetHashCode()) { _logger.LogInformation("ExecuteAsync:满足放货条件,开始处理取货完成,任务号: {TaskNum}", task.RobotTaskNum); QuartzLogger.Info($"ExecuteAsync:满足放货条件,开始处理取货完成", latestState.RobotCrane?.DeviceName ?? ipAddress); // 发送放货指令 await HandlePickFinishedStateAsync(task, ipAddress); } // ========== 初始化或者放货完成后的取货处理 ========== // 条件: // - 当前动作是 PutFinished、AllPutFinished 或 null(放货完成) // - 运行状态为 Homed(已归位) // - 手臂上无物料(RobotArmObject == 0) // - 任务状态为 RobotPutFinish 或不是 RobotExecuting else if ((latestState.CurrentAction == "PutFinished" || latestState.CurrentAction == "AllPutFinished" || latestState.CurrentAction.IsNullOrEmpty()) && latestState.RobotArmObject == 0 && (task.RobotTaskState == TaskRobotStatusEnum.RobotPutFinish.GetHashCode() || task.RobotTaskState != TaskRobotStatusEnum.RobotExecuting.GetHashCode())) { _logger.LogInformation("ExecuteAsync:满足取货条件,开始处理放货完成,任务号: {TaskNum}", task.RobotTaskNum); QuartzLogger.Info($"ExecuteAsync:满足取货条件,开始处理放货完成", latestState.RobotCrane?.DeviceName ?? ipAddress); // 发送取货指令 await HandlePutFinishedStateAsync(task, ipAddress); } } } /// /// 处理取货完成后的放货指令 /// /// /// 当取货完成后,向机器人发送放货指令(Putbattery)。 /// 换盘任务使用批次格式 SendPutWithBatchAsync。 /// /// 指令格式:Putbattery,{目标地址} /// 例如:Putbattery,B01 表示将货物放置到 B01 位置 /// /// 当前任务 /// 机器人 IP 地址 private async Task HandlePickFinishedStateAsync(Dt_RobotTask task, string ipAddress) { string taskString; // 换盘任务使用批次格式 if (task.RobotTaskType == RobotTaskTypeEnum.ChangePallet.GetHashCode()) { int targetNormalCount = task.RobotTaskTotalNum; var state = _stateManager.GetState(ipAddress); int currentCompletedCount = state?.RobotTaskTotalNum ?? 0; bool isFlowA = task.RobotSourceAddressLineCode is "11001" or "11010"; // 流向A Phase 2:放假电芯到目标托盘 if (isFlowA && state?.ChangePalletPhase == 2) { int remaining = 48 - currentCompletedCount; if (remaining <= 0) return; int batchStart = targetNormalCount + 1 + (state.CurrentBatchIndex - 1); int putCount = Math.Min(4, remaining); var (start, end) = _taskProcessor.BuildBatchRange(batchStart, putCount); await _taskProcessor.SendPutWithBatchAsync(task, state, task.RobotTargetAddress, start, end); return; } // 流向B Phase 4:放假电芯到5号位 if (!isFlowA && state?.ChangePalletPhase == 4) { int fakeCount = 48 - targetNormalCount; int completedFake = Math.Max(0, currentCompletedCount - targetNormalCount); int remainingFake = fakeCount - completedFake; if (remainingFake <= 0) return; var positions = _taskProcessor.GetNextAvailableFakeBatteryPositions(Math.Min(4, remainingFake)); if (positions.Count == 0) { _logger.LogError("HandlePickFinishedStateAsync:无可用假电芯点位,任务号: {TaskNum}", task.RobotTaskNum); return; } int start = positions.Min(); int end = positions.Max(); await _taskProcessor.SendPutWithBatchAsync(task, state, "5", start, end); return; } // 流向B Phase 2:放正常电芯到目标托盘 if (!isFlowA && state?.ChangePalletPhase == 2) { int remainingNormal = targetNormalCount - currentCompletedCount; if (remainingNormal <= 0) return; int batchStart = ((currentCompletedCount - 1) / 4) * 4 + 1; int putCount = Math.Min(4, remainingNormal); var (start, end) = _taskProcessor.BuildBatchRange(batchStart, putCount); await _taskProcessor.SendPutWithBatchAsync(task, state, task.RobotTargetAddress, start, end); return; } // 默认:使用原有格式 taskString = $"Putbattery,{task.RobotTargetAddress}"; } else { // 非换盘任务:使用原有格式 taskString = $"Putbattery,{task.RobotTargetAddress}"; } bool result = await _clientManager.SendToClientAsync(ipAddress, taskString); if (result) { _logger.LogInformation("HandlePickFinishedStateAsync:下发放货指令成功,指令: {TaskString},任务号: {TaskNum}", taskString, task.RobotTaskNum); QuartzLogger.Info($"下发放货指令成功,指令: {taskString}", task.RobotRoadway); task.RobotTaskState = TaskRobotStatusEnum.RobotExecuting.GetHashCode(); var stateToUpdate = _stateManager.GetState(ipAddress); if (stateToUpdate != null) { stateToUpdate.CurrentTask = task; if (_stateManager.TryUpdateStateSafely(ipAddress, stateToUpdate)) { await _robotTaskService.UpdateRobotTaskAsync(task); } } } else { _logger.LogError("HandlePickFinishedStateAsync:下发放货指令失败,指令: {TaskString},任务号: {TaskNum}", taskString, task.RobotTaskNum); QuartzLogger.Error($"下发放货指令失败,指令: {taskString}", task.RobotRoadway); } } /// /// 处理放货完成后的取货指令 /// /// /// 当放货完成后,根据任务类型决定下一步: /// /// 1. 如果不是拆盘也不是组盘(普通任务): /// - 直接发送取货指令 /// /// 2. 如果是组盘或换盘任务: /// - 生成新的托盘条码 /// - 将条码添加到状态中 /// - 发送取货指令 /// /// 组盘任务的条码用于标识新生成的托盘, /// 后续放货时会用到这些条码信息。 /// /// 当前任务 /// 机器人 IP 地址 private async Task HandlePutFinishedStateAsync(Dt_RobotTask task, string ipAddress) { // 获取最新状态 var stateForUpdate = _stateManager.GetState(ipAddress); if (stateForUpdate == null) { _logger.LogWarning("HandlePutFinishedStateAsync:获取状态失败,IP: {IpAddress}", ipAddress); QuartzLogger.Warn($"HandlePutFinishedStateAsync:获取状态失败,IP: {ipAddress}", ipAddress); return; } // 如果状态中还没有设置任务类型标志,根据任务类型设置 if (!stateForUpdate.IsSplitPallet && !stateForUpdate.IsGroupPallet) { // 判断任务类型并设置相应的标志 stateForUpdate.IsSplitPallet = task.RobotTaskType == RobotTaskTypeEnum.SplitPallet.GetHashCode(); stateForUpdate.IsGroupPallet = task.RobotTaskType == RobotTaskTypeEnum.GroupPallet.GetHashCode() || task.RobotTaskType == RobotTaskTypeEnum.ChangePallet.GetHashCode(); } // 如果是组盘任务 if (task.RobotTaskType == RobotTaskTypeEnum.GroupPallet.GetHashCode()) { // 生成托盘条码前缀 const string prefix = "TRAY"; // 生成两个托盘条码(用于组盘操作)(测试用,后续读取线体条码) string trayBarcode1 = RobotBarcodeGenerator.GenerateTrayBarcode(prefix); string trayBarcode2 = RobotBarcodeGenerator.GenerateTrayBarcode(prefix); // 如果条码生成成功 if (!string.IsNullOrEmpty(trayBarcode1) && !string.IsNullOrEmpty(trayBarcode2)) { if(stateForUpdate.CellBarcode.Contains(trayBarcode1)|| stateForUpdate.CellBarcode.Contains(trayBarcode2)) { _logger.LogError("HandlePutFinishedStateAsync:生成的托盘条码已存在,可能存在重复,任务号: {TaskNum}", task.RobotTaskNum); QuartzLogger.Error($"生成的托盘条码已存在,可能存在重复", stateForUpdate.RobotCrane.DeviceName); // 条码重复,记录错误日志并停止后续操作(后续放货时会用到这些条码信息,供后续放货时使用,调试后可能会取消此逻辑) return; } else { _logger.LogInformation("HandlePutFinishedStateAsync:生成的托盘条码唯一,继续执行,任务号: {TaskNum}", task.RobotTaskNum); QuartzLogger.Info($"生成的托盘条码唯一,继续执行", stateForUpdate.RobotCrane.DeviceName); // 将条码添加到状态中,供后续放货时使用 stateForUpdate.CellBarcode.Add(trayBarcode1); stateForUpdate.CellBarcode.Add(trayBarcode2); } // 记录日志:生成托盘条码成功 _logger.LogInformation("HandlePutFinishedStateAsync:生成托盘条码成功: {Barcode1}+{Barcode2},任务号: {TaskNum}", trayBarcode1, trayBarcode2, task.RobotTaskNum); QuartzLogger.Info($"生成托盘条码成功: {trayBarcode1}+{trayBarcode2}", stateForUpdate.RobotCrane.DeviceName); // 发送取货指令 await _taskProcessor.SendSocketRobotPickAsync(task, stateForUpdate); } else { // 条码生成失败,记录错误日志 _logger.LogError("HandlePutFinishedStateAsync:生成托盘条码失败,任务号: {TaskNum}", task.RobotTaskNum); QuartzLogger.Error($"生成托盘条码失败", stateForUpdate.RobotCrane.DeviceName); } } else if (task.RobotTaskType == RobotTaskTypeEnum.ChangePallet.GetHashCode()) { const int targetTotal = 48; int targetNormalCount = task.RobotTaskTotalNum; int currentCompletedCount = stateForUpdate.RobotTaskTotalNum; // 判断流向(null-safe) bool isFlowA = task.RobotSourceAddressLineCode is "11001" or "11010"; // 目标数量为48:直接走原有逻辑,不进入批次模式 if (targetNormalCount == targetTotal) { await _taskProcessor.SendSocketRobotPickAsync(task, stateForUpdate); return; } // 初始化批次模式 if (stateForUpdate.ChangePalletPhase == 0) { stateForUpdate.ChangePalletPhase = 1; stateForUpdate.CurrentBatchIndex = 1; _logger.LogInformation("HandlePutFinishedStateAsync:换盘任务进入批次模式,任务号: {TaskNum},流向: {Flow}", task.RobotTaskNum, isFlowA ? "A" : "B"); } // ==================== 流向A:补假电芯到目标托盘 ==================== if (isFlowA) { // Phase 1: 取假电芯(从5号位,使用 PositionIndex) if (stateForUpdate.ChangePalletPhase == 1) { int remaining = targetTotal - currentCompletedCount; if (remaining <= 0) { stateForUpdate.ChangePalletPhase = 0; stateForUpdate.CurrentBatchIndex = 1; stateForUpdate.IsInFakeBatteryMode = false; _logger.LogInformation("HandlePutFinishedStateAsync:流向A完成,任务号: {TaskNum}", task.RobotTaskNum); return; } int pickCount = Math.Min(4, remaining); var positions = _taskProcessor.GetNextAvailableFakeBatteryPositions(pickCount); if (positions.Count == 0) { _logger.LogError("HandlePutFinishedStateAsync:无可用假电芯点位,任务号: {TaskNum}", task.RobotTaskNum); return; } await _taskProcessor.SendSocketRobotFakeBatteryPickAsync(task, stateForUpdate, positions); stateForUpdate.ChangePalletPhase = 2; } // Phase 2: 放假电芯到目标托盘(从 targetNormalCount+1 开始递增) else if (stateForUpdate.ChangePalletPhase == 2) { int remaining = targetTotal - currentCompletedCount; if (remaining <= 0) { stateForUpdate.ChangePalletPhase = 0; stateForUpdate.CurrentBatchIndex = 1; stateForUpdate.IsInFakeBatteryMode = false; _logger.LogInformation("HandlePutFinishedStateAsync:流向A完成,任务号: {TaskNum}", task.RobotTaskNum); return; } // 计算放货批次编号:从 targetNormalCount + 1 开始 int batchStart = targetNormalCount + 1 + (stateForUpdate.CurrentBatchIndex - 1); int putCount = Math.Min(4, remaining); var (start, end) = _taskProcessor.BuildBatchRange(batchStart, putCount); await _taskProcessor.SendPutWithBatchAsync(task, stateForUpdate, task.RobotTargetAddress, start, end); stateForUpdate.CurrentBatchIndex += putCount; stateForUpdate.ChangePalletPhase = 1; } } // ==================== 流向B:取正常电芯 + 回收假电芯 ==================== else { // Phase 1: 取正常电芯(从源地址,从1开始递增) if (stateForUpdate.ChangePalletPhase == 1) { int remainingNormal = targetNormalCount - currentCompletedCount; if (remainingNormal <= 0) { // 正常电芯取完,切换到 Phase 3 stateForUpdate.ChangePalletPhase = 3; stateForUpdate.CurrentBatchIndex = targetNormalCount + 1; _logger.LogInformation("HandlePutFinishedStateAsync:正常电芯全部取完,进入Phase 3回收假电芯,任务号: {TaskNum}", task.RobotTaskNum); return; } int pickCount = Math.Min(4, remainingNormal); var (start, end) = _taskProcessor.BuildBatchRange(stateForUpdate.CurrentBatchIndex, pickCount); await _taskProcessor.SendPickWithBatchAsync(task, stateForUpdate, task.RobotSourceAddress, start, end); stateForUpdate.CurrentBatchIndex += pickCount; stateForUpdate.ChangePalletPhase = 2; } // Phase 2: 放正常电芯到目标托盘(放货编号与取货编号一致) else if (stateForUpdate.ChangePalletPhase == 2) { int remainingNormal = targetNormalCount - currentCompletedCount; if (remainingNormal <= 0) { // 正常电芯放完,切换到 Phase 3 stateForUpdate.ChangePalletPhase = 3; stateForUpdate.CurrentBatchIndex = targetNormalCount + 1; _logger.LogInformation("HandlePutFinishedStateAsync:正常电芯全部放完,进入Phase 3回收假电芯,任务号: {TaskNum}", task.RobotTaskNum); return; } // 计算本批放货编号:基于 currentCompletedCount 推导批次起始 int batchStart = ((currentCompletedCount - 1) / 4) * 4 + 1; int putCount = Math.Min(4, remainingNormal); var (start, end) = _taskProcessor.BuildBatchRange(batchStart, putCount); await _taskProcessor.SendPutWithBatchAsync(task, stateForUpdate, task.RobotTargetAddress, start, end); stateForUpdate.ChangePalletPhase = 1; } // Phase 3: 取假电芯(从源地址,从 targetNormalCount+1 开始递增) else if (stateForUpdate.ChangePalletPhase == 3) { int fakeCount = targetTotal - targetNormalCount; int completedFake = Math.Max(0, currentCompletedCount - targetNormalCount); int remainingFake = fakeCount - completedFake; if (remainingFake <= 0) { stateForUpdate.ChangePalletPhase = 0; stateForUpdate.CurrentBatchIndex = 1; stateForUpdate.IsInFakeBatteryMode = false; _logger.LogInformation("HandlePutFinishedStateAsync:流向B完成,任务号: {TaskNum}", task.RobotTaskNum); return; } int pickCount = Math.Min(4, remainingFake); var (start, end) = _taskProcessor.BuildBatchRange(stateForUpdate.CurrentBatchIndex, pickCount); await _taskProcessor.SendPickWithBatchAsync(task, stateForUpdate, task.RobotSourceAddress, start, end); stateForUpdate.CurrentBatchIndex += pickCount; stateForUpdate.ChangePalletPhase = 4; } // Phase 4: 放假电芯到5号位(使用 PositionIndex) else if (stateForUpdate.ChangePalletPhase == 4) { int fakeCount = targetTotal - targetNormalCount; int completedFake = Math.Max(0, currentCompletedCount - targetNormalCount); int remainingFake = fakeCount - completedFake; if (remainingFake <= 0) { stateForUpdate.ChangePalletPhase = 0; stateForUpdate.CurrentBatchIndex = 1; stateForUpdate.IsInFakeBatteryMode = false; _logger.LogInformation("HandlePutFinishedStateAsync:流向B完成,任务号: {TaskNum}", task.RobotTaskNum); return; } var positions = _taskProcessor.GetNextAvailableFakeBatteryPositions(Math.Min(4, remainingFake)); if (positions.Count == 0) { _logger.LogError("HandlePutFinishedStateAsync:无可用假电芯点位,任务号: {TaskNum}", task.RobotTaskNum); return; } int start = positions.Min(); int end = positions.Max(); await _taskProcessor.SendPutWithBatchAsync(task, stateForUpdate, "5", start, end); stateForUpdate.ChangePalletPhase = 3; } } } else { // 非组盘任务,直接发送取货指令 await _taskProcessor.SendSocketRobotPickAsync(task, stateForUpdate); } } } }