| | |
| | | /// </remarks> |
| | | /// <param name="task">要下发的任务对象</param> |
| | | /// <param name="state">机器人当前状态</param> |
| | | public async Task SendSocketRobotPickAsync(Dt_RobotTask task, RobotSocketState state) |
| | | /// <param name="isScanNG">是否扫码NG</param> |
| | | public async Task SendSocketRobotPickAsync(Dt_RobotTask task, RobotSocketState state, bool isScanNG = false) |
| | | { |
| | | // 构建取货指令,格式:Pickbattery,{源地址} |
| | | string taskString = $"Pickbattery,{task.RobotSourceAddress}"; |
| | |
| | | |
| | | // 将任务关联到状态对象 |
| | | state.CurrentTask = task; |
| | | |
| | | if (isScanNG) |
| | | { |
| | | state.IsScanNG = true; |
| | | } |
| | | |
| | | // 保持原语义:仅在状态安全写入成功后再更新任务状态 |
| | | // 这样可以确保状态和任务记录的一致性 |
| | |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 计算批次编号范围 |
| | | /// </summary> |
| | | /// <remarks> |
| | | /// 返回格式:(start, end) |
| | | /// - remaining >= 4: (currentIndex, currentIndex + 3) |
| | | /// - remaining > 1: (currentIndex, currentIndex + remaining - 1) |
| | | /// - remaining == 1: (currentIndex, 0) -- 单个物品用 0 表示 end |
| | | /// </remarks> |
| | | /// <param name="currentIndex">当前批次起始编号</param> |
| | | /// <param name="remaining">剩余数量</param> |
| | | /// <returns>(start, end) 元组</returns> |
| | | public (int Start, int End) BuildBatchRange(int currentIndex, int remaining) |
| | | { |
| | | if (remaining >= 4) |
| | | return (currentIndex, currentIndex + 3); |
| | | else if (remaining > 1) |
| | | return (currentIndex, currentIndex + remaining - 1); |
| | | else // remaining == 1 |
| | | return (currentIndex, 0); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 下发取货指令(带批次格式和总数) |
| | | /// </summary> |
| | | /// <remarks> |
| | | /// 发送顺序: |
| | | /// 1. PickTotalNum,{N} -- 真实电芯总数 |
| | | /// 2. Pickbattery,{位置},{start}-{end} -- 批次取货指令 |
| | | /// |
| | | /// 下发成功后更新任务状态为"机器人执行中"。 |
| | | /// </remarks> |
| | | /// <param name="task">要下发的任务对象</param> |
| | | /// <param name="state">机器人当前状态</param> |
| | | /// <param name="position">取货位置</param> |
| | | /// <param name="batchStart">批次起始编号</param> |
| | | /// <param name="batchEnd">批次结束编号</param> |
| | | public async Task SendPickWithBatchAsync(Dt_RobotTask task, RobotSocketState state, string position, int batchStart, int batchEnd) |
| | | { |
| | | // 先发送总数指令 |
| | | string totalNumCmd = $"PickTotalNum,{task.RobotTaskTotalNum}"; |
| | | await _socketClientGateway.SendToClientAsync(state.IPAddress, totalNumCmd); |
| | | |
| | | // 再发送批次取货指令 |
| | | string range = batchEnd == 0 ? $"{batchStart}-0" : $"{batchStart}-{batchEnd}"; |
| | | string taskString = $"Pickbattery,{position},{range}"; |
| | | |
| | | bool result = await _socketClientGateway.SendToClientAsync(state.IPAddress, taskString); |
| | | |
| | | if (result) |
| | | { |
| | | _logger.LogInformation("下发批次取货指令成功,指令: {TaskString},批次: {Range},设备: {DeviceName}", |
| | | taskString, range, state.RobotCrane?.DeviceName); |
| | | QuartzLogger.Info($"下发批次取货指令成功,指令: {taskString},批次: {range}", state.RobotCrane?.DeviceName); |
| | | |
| | | task.RobotTaskState = TaskRobotStatusEnum.RobotExecuting.GetHashCode(); |
| | | state.CurrentTask = task; |
| | | |
| | | if (_stateManager.TryUpdateStateSafely(state.IPAddress, state)) |
| | | { |
| | | await _robotTaskService.UpdateRobotTaskAsync(task); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | _logger.LogError("下发批次取货指令失败,指令: {TaskString},设备: {DeviceName}", taskString, state.RobotCrane?.DeviceName); |
| | | QuartzLogger.Error($"下发批次取货指令失败,指令: {taskString}", state.RobotCrane?.DeviceName); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 下发假电芯取货指令(带批次格式和总数) |
| | | /// </summary> |
| | | /// <remarks> |
| | | /// 发送顺序: |
| | | /// 1. PickTotalNum,{N} -- 真实电芯总数 |
| | | /// 2. Pickbattery,5,{start}-{end} -- 批次取货指令(固定从5号位取) |
| | | /// |
| | | /// 下发成功后更新任务状态为"机器人执行中"。 |
| | | /// </remarks> |
| | | /// <param name="task">要下发的任务对象</param> |
| | | /// <param name="state">机器人当前状态</param> |
| | | /// <param name="batchStart">批次起始编号</param> |
| | | /// <param name="batchEnd">批次结束编号</param> |
| | | public async Task SendFakeBatteryPickWithBatchAsync(Dt_RobotTask task, RobotSocketState state, int batchStart, int batchEnd) |
| | | { |
| | | // 先发送总数指令 |
| | | string totalNumCmd = $"PickTotalNum,{task.RobotTaskTotalNum}"; |
| | | await _socketClientGateway.SendToClientAsync(state.IPAddress, totalNumCmd); |
| | | |
| | | // 再发送批次取货指令(假电芯固定从5号位取) |
| | | string range = batchEnd == 0 ? $"{batchStart}-0" : $"{batchStart}-{batchEnd}"; |
| | | string taskString = $"Pickbattery,5,{range}"; |
| | | |
| | | bool result = await _socketClientGateway.SendToClientAsync(state.IPAddress, taskString); |
| | | |
| | | if (result) |
| | | { |
| | | _logger.LogInformation("下发假电芯批次取货指令成功,指令: {TaskString},批次: {Range},设备: {DeviceName}", |
| | | taskString, range, state.RobotCrane?.DeviceName); |
| | | QuartzLogger.Info($"下发假电芯批次取货指令成功,指令: {taskString},批次: {range}", state.RobotCrane?.DeviceName); |
| | | |
| | | task.RobotTaskState = TaskRobotStatusEnum.RobotExecuting.GetHashCode(); |
| | | state.CurrentTask = task; |
| | | |
| | | if (_stateManager.TryUpdateStateSafely(state.IPAddress, state)) |
| | | { |
| | | await _robotTaskService.UpdateRobotTaskAsync(task); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | _logger.LogError("下发假电芯批次取货指令失败,指令: {TaskString},设备: {DeviceName}", taskString, state.RobotCrane?.DeviceName); |
| | | QuartzLogger.Error($"下发假电芯批次取货指令失败,指令: {taskString}", state.RobotCrane?.DeviceName); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 下发放货指令(带批次格式和总数) |
| | | /// </summary> |
| | | /// <remarks> |
| | | /// 发送顺序: |
| | | /// 1. PutTotalNum,{N} -- 真实电芯总数 |
| | | /// 2. Putbattery,{位置},{start}-{end} -- 批次放货指令 |
| | | /// |
| | | /// 下发成功后更新任务状态为"机器人执行中"。 |
| | | /// </remarks> |
| | | /// <param name="task">要下发的任务对象</param> |
| | | /// <param name="state">机器人当前状态</param> |
| | | /// <param name="position">放货位置</param> |
| | | /// <param name="batchStart">批次起始编号</param> |
| | | /// <param name="batchEnd">批次结束编号</param> |
| | | public async Task SendPutWithBatchAsync(Dt_RobotTask task, RobotSocketState state, string position, int batchStart, int batchEnd) |
| | | { |
| | | // 先发送总数指令 |
| | | string totalNumCmd = $"PutTotalNum,{task.RobotTaskTotalNum}"; |
| | | await _socketClientGateway.SendToClientAsync(state.IPAddress, totalNumCmd); |
| | | |
| | | // 再发送批次放货指令 |
| | | string range = batchEnd == 0 ? $"{batchStart}-0" : $"{batchStart}-{batchEnd}"; |
| | | string taskString = $"Putbattery,{position},{range}"; |
| | | |
| | | bool result = await _socketClientGateway.SendToClientAsync(state.IPAddress, taskString); |
| | | |
| | | if (result) |
| | | { |
| | | _logger.LogInformation("下发放货指令成功,指令: {TaskString},批次: {Range},设备: {DeviceName}", |
| | | taskString, range, state.RobotCrane?.DeviceName); |
| | | QuartzLogger.Info($"下发放货指令成功,指令: {taskString},批次: {range}", state.RobotCrane?.DeviceName); |
| | | |
| | | task.RobotTaskState = TaskRobotStatusEnum.RobotExecuting.GetHashCode(); |
| | | state.CurrentTask = task; |
| | | |
| | | if (_stateManager.TryUpdateStateSafely(state.IPAddress, state)) |
| | | { |
| | | await _robotTaskService.UpdateRobotTaskAsync(task); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | _logger.LogError("下发放货指令失败,指令: {TaskString},设备: {DeviceName}", |
| | | taskString, state.RobotCrane?.DeviceName); |
| | | QuartzLogger.Error($"下发放货指令失败,指令: {taskString}", state.RobotCrane?.DeviceName); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 处理入库任务回传(拆盘/组盘/换盘场景) |
| | | /// </summary> |
| | | /// <remarks> |
| | |
| | | /// <param name="state">机器人当前状态</param> |
| | | /// <param name="useSourceAddress">是否使用源地址(true 表示拆盘/换盘场景,false 表示组盘/换盘场景)</param> |
| | | /// <returns>处理是否成功</returns> |
| | | public async Task<bool> HandleInboundTaskAsync(RobotSocketState state, bool useSourceAddress) |
| | | public async Task<bool> HandleInboundTaskAsync(RobotSocketState state, bool useSourceAddress, string isRoadway = null) |
| | | { |
| | | // 获取当前关联的任务 |
| | | var currentTask = state.CurrentTask; |
| | |
| | | } |
| | | |
| | | // 获取巷道代码 |
| | | string roadway = currentTask.RobotSourceAddressLineCode; |
| | | string roadway = string.Empty; |
| | | |
| | | // 根据巷道名称判断仓库 ID |
| | | // ZYRB1 -> 1, HPRB001 -> 2, 其他 -> 3 |
| | | int warehouseId = currentTask.RobotRoadway == "注液组盘机械手" ? 1 : currentTask.RobotRoadway == "HPRB001" ? 2 : 3; |
| | | |
| | | // 任务类型(0 表示未定义,稍后根据任务类型设置) |
| | | int taskType = 0; |
| | | int warehouseId = 0; |
| | | |
| | | // 源地址和目标地址(初始化) |
| | | string SourceAddress = currentTask.RobotTargetAddressLineCode; |
| | | string TargetAddress = currentTask.RobotSourceAddressLineCode; |
| | | |
| | | // 任务类型(0 表示未定义,稍后根据任务类型设置) |
| | | int taskType = 0; |
| | | |
| | | // 托盘代码(初始化为空) |
| | | string PalletCode = string.Empty; |
| | |
| | | switch (robotTaskType) |
| | | { |
| | | case RobotTaskTypeEnum.GroupPallet: |
| | | // 组盘任务不使用源地址,直接返回 false |
| | | _logger.LogDebug("HandleInboundTaskAsync:组盘任务不使用源地址"); |
| | | QuartzLogger.Debug($"HandleInboundTaskAsync:组盘任务不使用源地址", state.RobotCrane?.DeviceName ?? "Unknown"); |
| | | return false; |
| | | warehouseId = 1; |
| | | roadway = "GWSC1"; |
| | | break; |
| | | |
| | | case RobotTaskTypeEnum.ChangePallet: |
| | | // 换盘/拆盘场景:托盘需要入库 |
| | | taskType = TaskTypeEnum.InEmpty.GetHashCode(); // 空托盘入库 |
| | | PalletCode = currentTask.RobotSourceAddressPalletCode; // 使用源地址的托盘码 |
| | | if (isRoadway == "HWSC1") |
| | | { |
| | | warehouseId = 2; |
| | | roadway = "HWSC1"; |
| | | } |
| | | else if (isRoadway == "GWSC1") |
| | | { |
| | | warehouseId = 1; |
| | | roadway = "GWSC1"; |
| | | } |
| | | |
| | | break; |
| | | case RobotTaskTypeEnum.SplitPallet: |
| | | // 换盘/拆盘场景:托盘需要入库 |
| | | taskType = TaskTypeEnum.InEmpty.GetHashCode(); // 空托盘入库 |
| | | PalletCode = currentTask.RobotSourceAddressPalletCode; // 使用源地址的托盘码 |
| | | |
| | | warehouseId = 3; |
| | | roadway = "CWSC1"; |
| | | break; |
| | | } |
| | | } |
| | |
| | | switch (robotTaskType) |
| | | { |
| | | case RobotTaskTypeEnum.ChangePallet: |
| | | // 换盘/组盘场景:货物需要入库 |
| | | taskType = TaskTypeEnum.Inbound.GetHashCode(); // 成品入库 |
| | | PalletCode = currentTask.RobotTargetAddressPalletCode; // 使用目标地址的托盘码 |
| | | |
| | | if (isRoadway == "HWSC1") |
| | | { |
| | | warehouseId = 2; |
| | | roadway = "HWSC1"; |
| | | } |
| | | else if (isRoadway == "GWSC1") |
| | | { |
| | | warehouseId = 1; |
| | | roadway = "GWSC1"; |
| | | } |
| | | |
| | | break; |
| | | case RobotTaskTypeEnum.GroupPallet: |
| | | // 换盘/组盘场景:货物需要入库 |
| | | taskType = TaskTypeEnum.Inbound.GetHashCode(); // 成品入库 |
| | | PalletCode = currentTask.RobotTargetAddressPalletCode; // 使用目标地址的托盘码 |
| | | |
| | | warehouseId = 1; |
| | | roadway = "GWSC1"; |
| | | break; |
| | | |
| | | case RobotTaskTypeEnum.SplitPallet: |
| | | // 拆盘任务不使用目标地址 |
| | | _logger.LogDebug("HandleInboundTaskAsync:拆盘任务不使用目标地址"); |
| | | QuartzLogger.Debug($"HandleInboundTaskAsync:拆盘任务不使用目标地址", state.RobotCrane?.DeviceName ?? "Unknown"); |
| | | return true; |
| | | |
| | | break; |
| | | } |
| | | } |
| | | |
| | |
| | | { |
| | | PalletCode = PalletCode, // 托盘条码 |
| | | SourceAddress = SourceAddress ?? string.Empty, // 源地址 |
| | | TargetAddress = TargetAddress ?? string.Empty, // 目标地址 |
| | | Roadway = roadway, // 巷道 |
| | | TargetAddress = roadway ?? string.Empty, // 目标地址 |
| | | Roadway = roadway ?? string.Empty, // 巷道 |
| | | WarehouseId = warehouseId, // 仓库 ID |
| | | PalletType = 1, // 托盘类型(默认为1) |
| | | TaskType = taskType // 任务类型(入库/空托盘入库) |
| | |
| | | } |
| | | |
| | | // 解析返回的任务信息 |
| | | var taskInfos = JsonConvert.DeserializeObject<List<Dt_Task>>(content.Data.ToJson() ?? string.Empty) ?? new List<Dt_Task>(); |
| | | var taskInfo = taskInfos.FirstOrDefault(); |
| | | //var taskInfos = JsonConvert.DeserializeObject<List<Dt_Task>>(content.Data.ToJson() ?? string.Empty) ?? new List<Dt_Task>(); |
| | | //var taskInfo = taskInfos.FirstOrDefault(); |
| | | |
| | | // 获取源地址 |
| | | string sourceAddress = taskDTO.SourceAddress; |
| | | //// 获取源地址 |
| | | //string sourceAddress = taskDTO.SourceAddress; |
| | | |
| | | // 查找源地址对应的输送线设备 |
| | | IDevice? device = Storage.Devices.FirstOrDefault(x => x.DeviceProDTOs.Any(d => d.DeviceChildCode == sourceAddress)); |
| | | //// 查找源地址对应的输送线设备 |
| | | //IDevice? device = Storage.Devices.FirstOrDefault(x => x.DeviceProDTOs.Any(d => d.DeviceChildCode == sourceAddress)); |
| | | |
| | | if (device != null) |
| | | { |
| | | // 将设备转换为输送线类型 |
| | | CommonConveyorLine conveyorLine = (CommonConveyorLine)device; |
| | | //if (device != null) |
| | | //{ |
| | | // // 将设备转换为输送线类型 |
| | | // CommonConveyorLine conveyorLine = (CommonConveyorLine)device; |
| | | |
| | | // 设置输送线的目标地址 |
| | | conveyorLine.SetValue(ConveyorLineDBNameNew.Target, taskInfo.NextAddress, sourceAddress); |
| | | // // 设置输送线的目标地址 |
| | | // conveyorLine.SetValue(ConveyorLineDBNameNew.Target, taskInfo.NextAddress, sourceAddress); |
| | | |
| | | // 设置输送线的任务号 |
| | | conveyorLine.SetValue(ConveyorLineDBNameNew.TaskNo, taskInfo.TaskNum, sourceAddress); |
| | | // // 设置输送线的任务号 |
| | | // conveyorLine.SetValue(ConveyorLineDBNameNew.TaskNo, taskInfo.TaskNum, sourceAddress); |
| | | |
| | | // 触发输送线开始执行(写入 WCS_ACK = 1) |
| | | conveyorLine.SetValue(ConveyorLineDBNameNew.WCS_ACK, (short)1, sourceAddress); |
| | | // // 触发输送线开始执行(写入 WCS_ACK = 1) |
| | | // conveyorLine.SetValue(ConveyorLineDBNameNew.WCS_ACK, (short)1, sourceAddress); |
| | | |
| | | // 更新任务状态到下一阶段 |
| | | if (_taskService.UpdateTaskStatusToNext(taskInfo).Status) |
| | | { |
| | | _logger.LogInformation("HandleInboundTaskAsync:入库任务处理成功,任务号: {TaskNum}", taskInfo.TaskNum); |
| | | QuartzLogger.Info($"HandleInboundTaskAsync:入库任务处理成功,任务号: {taskInfo.TaskNum}", state.RobotCrane?.DeviceName ?? "Unknown"); |
| | | return true; |
| | | } |
| | | } |
| | | // // 更新任务状态到下一阶段 |
| | | // if (_taskService.UpdateTaskStatusToNext(taskInfo).Status) |
| | | // { |
| | | // _logger.LogInformation("HandleInboundTaskAsync:入库任务处理成功,任务号: {TaskNum}", taskInfo.TaskNum); |
| | | // QuartzLogger.Info($"HandleInboundTaskAsync:入库任务处理成功,任务号: {taskInfo.TaskNum}", state.RobotCrane?.DeviceName ?? "Unknown"); |
| | | // return true; |
| | | // } |
| | | //} |
| | | |
| | | return false; |
| | | } |
| | |
| | | Channel = x, |
| | | |
| | | // 电池条码:如果状态中有条码列表,取对应位置的条码;否则为空 |
| | | CellBarcode = state.CellBarcode?.Count > 0 ? state.CellBarcode[x - 1] : "" |
| | | //CellBarcode = state.CellBarcode?.Count > 0 ? state.CellBarcode[x - 1] : "" |
| | | CellBarcode = state.CellBarcode[idx].ToString() |
| | | }) |
| | | .ToList() |
| | | }; |
| | |
| | | { |
| | | return _httpClientHelper.Post<WebResponseContent>(configKey, stockDTO.ToJson()); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 调用批量拆盘确认 API |
| | | /// </summary> |
| | | /// <remarks> |
| | | /// 当拆盘任务全部取完时调用,一次性上传整个托盘的解绑数据到 MES。 |
| | | /// </remarks> |
| | | /// <param name="palletCode">源托盘号</param> |
| | | /// <param name="deviceName">设备名称,用于传递到 WMS</param> |
| | | /// <returns>HTTP 响应结果</returns> |
| | | public HttpResponseResult<WebResponseContent> PostSplitPalletConfirmAsync(string palletCode, string deviceName) |
| | | { |
| | | var request = new { PalletCode = palletCode, DeviceName = deviceName }; |
| | | return _httpClientHelper.Post<WebResponseContent>(nameof(ConfigKey.SplitPalletConfirm), request.ToJson()); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 调用批量组盘确认 API |
| | | /// </summary> |
| | | /// <remarks> |
| | | /// 当组盘任务全部放完时调用,一次性上传整个托盘的绑定数据到 MES。 |
| | | /// </remarks> |
| | | /// <param name="palletCode">目标托盘号</param> |
| | | /// <param name="deviceName">设备名称,用于传递到 WMS</param> |
| | | /// <returns>HTTP 响应结果</returns> |
| | | public HttpResponseResult<WebResponseContent> PostGroupPalletConfirmAsync(string palletCode, string deviceName) |
| | | { |
| | | var request = new { PalletCode = palletCode, DeviceName = deviceName }; |
| | | return _httpClientHelper.Post<WebResponseContent>(nameof(ConfigKey.GroupPalletConfirm), request.ToJson()); |
| | | } |
| | | } |
| | | } |