| | |
| | | import com.zy.asrs.entity.TaskLog; |
| | | import com.zy.asrs.entity.WrkMast; |
| | | import com.zy.asrs.entity.BasDevp; |
| | | import com.zy.asrs.entity.LocCache; |
| | | import com.zy.asrs.mapper.BasDevpMapper; |
| | | import com.zy.asrs.mapper.BasStationMapper; |
| | | import com.zy.asrs.mapper.WrkMastMapper; |
| | | import com.zy.asrs.service.ApiLogService; |
| | | import com.zy.asrs.service.LocCacheService; |
| | | import com.zy.asrs.service.TaskLogService; |
| | | import com.zy.asrs.service.TaskService; |
| | | import com.zy.common.constant.ApiInterfaceConstant; |
| | |
| | | private BasDevpMapper basDevpMapper; |
| | | |
| | | @Resource |
| | | private LocCacheService locCacheService; |
| | | |
| | | @Resource |
| | | private AgvProperties agvProperties; |
| | | |
| | | /** |
| | |
| | | } |
| | | |
| | | for (Task task : taskList) { |
| | | // 如果任务没有分配站点,先分配站点 |
| | | // 如果任务状态已经是8(已呼叫AGV,正在搬运),则不再发送指令 |
| | | if (task.getWrkSts() != null && task.getWrkSts() == 8L) { |
| | | log.debug("任务ID:{}状态已是8(正在搬运),跳过发送", task.getId()); |
| | | continue; |
| | | } |
| | | |
| | | // 如果任务没有分配站点,先分配站点(只有为空时才分配,已经分配了不要清空) |
| | | String staNo = task.getStaNo(); |
| | | if (staNo == null || staNo.isEmpty()) { |
| | | Integer allocatedSite = allocateSiteForTask(task); |
| | | if (allocatedSite == null) { |
| | | log.warn("任务ID:{}无法分配站点,跳过本次发送", task.getId()); |
| | | continue; // 无法分配站点,跳过本次发送 |
| | | String errorMsg = allocateSiteForTask(task); |
| | | if (errorMsg != null) { |
| | | // 无法分配站点,只在日志中记录,不记录到任务中(app不需要知道) |
| | | log.warn("任务ID:{}无法分配站点:{}", task.getId(), errorMsg); |
| | | continue; |
| | | } |
| | | staNo = String.valueOf(allocatedSite); |
| | | task.setStaNo(staNo); |
| | | taskService.updateById(task); |
| | | // 检查是否成功分配了站点(如果返回null且staNo仍为空,说明所有站点都在搬运,等待下次再试) |
| | | staNo = task.getStaNo(); |
| | | if (staNo == null || staNo.isEmpty()) { |
| | | // 所有站点都在搬运,暂不分配,等待下次定时任务再尝试 |
| | | continue; |
| | | } |
| | | log.info("任务ID:{}已分配站点:{}", task.getId(), staNo); |
| | | } |
| | | |
| | | // 检查目标站点是否有正在搬运的同类型AGV任务(出库和入库互不干扰) |
| | | // 只有状态8(已呼叫AGV,正在搬运)的任务才会阻塞,状态7(待呼叫)的任务不阻塞 |
| | | // 这样可以避免所有任务都卡在呼叫状态,按id最小的优先呼叫 |
| | | // 检查目标站点是否有效(不为0且存在) |
| | | if (staNo != null && !staNo.isEmpty()) { |
| | | try { |
| | | Integer siteNo = Integer.parseInt(staNo); |
| | | // 检查站点是否为0(无效站点),如果是0则不发送,但不清空站点 |
| | | if (siteNo == null || siteNo == 0) { |
| | | log.warn("任务ID:{}的目标站点{}无效(为0),跳过发送,保留站点分配", task.getId(), staNo); |
| | | continue; |
| | | } |
| | | List<BasDevp> basDevpList = basDevpMapper.selectList(new EntityWrapper<BasDevp>().eq("dev_no", siteNo)); |
| | | if (basDevpList == null || basDevpList.isEmpty()) { |
| | | // 站点不存在,跳过发送,不清空站点 |
| | | log.warn("任务ID:{}的目标站点{}不存在,跳过发送,保留站点分配", task.getId(), staNo); |
| | | continue; |
| | | } |
| | | } catch (NumberFormatException e) { |
| | | // 站点格式错误,跳过发送,不清空站点 |
| | | log.warn("任务ID:{}的目标站点{}格式错误,跳过发送,保留站点分配", task.getId(), staNo); |
| | | continue; |
| | | } |
| | | } else { |
| | | // 没有站点,跳过 |
| | | continue; |
| | | } |
| | | |
| | | // 检查站点是否有状态8的同类型任务,有则跳过(不清空站点) |
| | | if (staNo != null && !staNo.isEmpty() && task.getIoType() != null) { |
| | | // 根据当前任务类型,只检查同类型的正在搬运任务(状态8) |
| | | // 入库任务(ioType < 100):只检查入库类型的正在搬运任务 |
| | |
| | | taskType = "出库"; |
| | | } |
| | | |
| | | // 只检查状态为8(已呼叫AGV,正在搬运)的同类型任务 |
| | | // 检查状态为8(已呼叫AGV,正在搬运)的同类型任务 |
| | | List<Task> transportingTasks = taskService.selectList( |
| | | new EntityWrapper<Task>() |
| | | .eq("sta_no", staNo) |
| | |
| | | ); |
| | | |
| | | if (!transportingTasks.isEmpty()) { |
| | | log.info("站点{}有{}个正在搬运的{}AGV任务,跳过本次发送,等待搬运完成。当前任务ID:{}", |
| | | log.info("站点{}有{}个正在搬运的{}AGV任务,跳过当前任务ID:{}", |
| | | staNo, transportingTasks.size(), taskType, task.getId()); |
| | | continue; // 跳过本次发送,等待下次 |
| | | continue; |
| | | } |
| | | } |
| | | |
| | |
| | | default: |
| | | } |
| | | String body = getRequest(task,namespace); |
| | | // 打印请求信息 |
| | | log.info("{}呼叫agv搬运 - 请求地址:{}", namespace, url); |
| | | |
| | | // 获取当前重试次数 |
| | | int currentRetryCount = getRetryCount(task); |
| | | int maxRetryCount = agvProperties.getCallRetry().getMaxRetryCount(); |
| | | boolean retryEnabled = agvProperties.getCallRetry().isEnabled(); |
| | | |
| | | // 如果重试次数已达到最大值,跳过本次发送 |
| | | if (retryEnabled && currentRetryCount >= maxRetryCount) { |
| | | log.warn("{}呼叫agv搬运 - 任务ID:{}已达到最大重试次数({}),停止重试", |
| | | namespace, task.getId(), maxRetryCount); |
| | | // 记录最终失败信息 |
| | | task.setErrorTime(new Date()); |
| | | task.setErrorMemo(String.format("AGV呼叫失败,已达到最大重试次数(%d次)", maxRetryCount)); |
| | | taskService.updateById(task); |
| | | continue; |
| | | } |
| | | |
| | | // 打印请求信息(包含重试次数) |
| | | if (currentRetryCount > 0) { |
| | | log.info("{}呼叫agv搬运(第{}次重试) - 请求地址:{}", namespace, currentRetryCount + 1, url); |
| | | } else { |
| | | log.info("{}呼叫agv搬运 - 请求地址:{}", namespace, url); |
| | | } |
| | | log.info("{}呼叫agv搬运 - 请求参数:{}", namespace, body); |
| | | |
| | | try { |
| | | // 使用仙工M4接口 |
| | | response = new HttpHandler.Builder() |
| | |
| | | |
| | | // 检查响应是否为空 |
| | | if (response == null || response.trim().isEmpty()) { |
| | | log.error("{}呼叫agv搬运失败 - 任务ID:{},AGV接口返回为空", namespace, task.getId()); |
| | | String errorMsg = "AGV接口返回为空"; |
| | | log.error("{}呼叫agv搬运失败 - 任务ID:{},{}", namespace, task.getId(), errorMsg); |
| | | handleCallFailure(task, namespace, errorMsg, retryEnabled, maxRetryCount, currentRetryCount); |
| | | continue; |
| | | } |
| | | |
| | | JSONObject jsonObject = JSON.parseObject(response); |
| | | if (jsonObject == null) { |
| | | log.error("{}呼叫agv搬运失败 - 任务ID:{},响应JSON解析失败,响应内容:{}", namespace, task.getId(), response); |
| | | String errorMsg = "响应JSON解析失败,响应内容:" + response; |
| | | log.error("{}呼叫agv搬运失败 - 任务ID:{},{}", namespace, task.getId(), errorMsg); |
| | | handleCallFailure(task, namespace, errorMsg, retryEnabled, maxRetryCount, currentRetryCount); |
| | | continue; |
| | | } |
| | | |
| | | Integer code = jsonObject.getInteger("code"); |
| | | if (code != null && code.equals(200)) { |
| | | // 呼叫成功,清除重试次数和错误信息 |
| | | success = true; |
| | | task.setWrkSts(8L); |
| | | task.setMemo(clearRetryInfo(task.getMemo())); // 清除重试信息 |
| | | task.setErrorTime(null); |
| | | task.setErrorMemo(null); |
| | | taskService.updateById(task); |
| | | log.info("{}呼叫agv搬运成功 - 任务ID:{}", namespace, task.getId()); |
| | | } else { |
| | | String message = jsonObject.getString("message"); |
| | | log.error("{}呼叫agv搬运失败 - 任务ID:{},错误码:{},错误信息:{}", |
| | | namespace, task.getId(), code, message); |
| | | String errorMsg = String.format("错误码:%s,错误信息:%s", code, message); |
| | | log.error("{}呼叫agv搬运失败 - 任务ID:{},{}", namespace, task.getId(), errorMsg); |
| | | handleCallFailure(task, namespace, errorMsg, retryEnabled, maxRetryCount, currentRetryCount); |
| | | } |
| | | } catch (Exception e) { |
| | | log.error("{}呼叫agv搬运异常 - 任务ID:{},请求地址:{},请求参数:{},异常信息:{}", |
| | | namespace, task.getId(), url, body, e.getMessage(), e); |
| | | String errorMsg = "异常信息:" + e.getMessage(); |
| | | log.error("{}呼叫agv搬运异常 - 任务ID:{},请求地址:{},请求参数:{},{}", |
| | | namespace, task.getId(), url, body, errorMsg, e); |
| | | handleCallFailure(task, namespace, errorMsg, retryEnabled, maxRetryCount, currentRetryCount); |
| | | } finally { |
| | | try { |
| | | // 保存接口日志 |
| | |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 处理AGV呼叫失败的情况 |
| | | * @param task 任务对象 |
| | | * @param namespace 命名空间(入库/出库/转移) |
| | | * @param errorMsg 错误信息 |
| | | * @param retryEnabled 是否启用重试 |
| | | * @param maxRetryCount 最大重试次数 |
| | | * @param currentRetryCount 当前重试次数 |
| | | */ |
| | | private void handleCallFailure(Task task, String namespace, String errorMsg, |
| | | boolean retryEnabled, int maxRetryCount, int currentRetryCount) { |
| | | if (retryEnabled && currentRetryCount < maxRetryCount) { |
| | | // 增加重试次数 |
| | | int newRetryCount = currentRetryCount + 1; |
| | | task.setMemo(updateRetryCount(task.getMemo(), newRetryCount)); |
| | | task.setErrorTime(new Date()); |
| | | task.setErrorMemo(String.format("AGV呼叫失败(第%d次重试):%s", newRetryCount, errorMsg)); |
| | | taskService.updateById(task); |
| | | log.info("{}呼叫agv搬运失败 - 任务ID:{},已重试{}次,将在下次定时任务时继续重试(最多{}次)", |
| | | namespace, task.getId(), newRetryCount, maxRetryCount); |
| | | } else { |
| | | // 不启用重试或已达到最大重试次数,停止重试 |
| | | task.setErrorTime(new Date()); |
| | | if (retryEnabled) { |
| | | task.setErrorMemo(String.format("AGV呼叫失败,已达到最大重试次数(%d次):%s", maxRetryCount, errorMsg)); |
| | | } else { |
| | | task.setErrorMemo(String.format("AGV呼叫失败(重试未启用):%s", errorMsg)); |
| | | } |
| | | taskService.updateById(task); |
| | | log.warn("{}呼叫agv搬运失败 - 任务ID:{},停止重试。错误信息:{}", |
| | | namespace, task.getId(), errorMsg); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 从memo字段中获取重试次数 |
| | | * memo格式:如果包含"retryCount:数字",则返回该数字,否则返回0 |
| | | * @param task 任务对象 |
| | | * @return 重试次数 |
| | | */ |
| | | private int getRetryCount(Task task) { |
| | | String memo = task.getMemo(); |
| | | if (memo == null || memo.trim().isEmpty()) { |
| | | return 0; |
| | | } |
| | | try { |
| | | // 查找 "retryCount:数字" 格式 |
| | | String prefix = "retryCount:"; |
| | | int index = memo.indexOf(prefix); |
| | | if (index >= 0) { |
| | | int startIndex = index + prefix.length(); |
| | | int endIndex = memo.indexOf(",", startIndex); |
| | | if (endIndex < 0) { |
| | | endIndex = memo.length(); |
| | | } |
| | | String countStr = memo.substring(startIndex, endIndex).trim(); |
| | | return Integer.parseInt(countStr); |
| | | } |
| | | } catch (Exception e) { |
| | | log.warn("解析任务ID:{}的重试次数失败,memo:{}", task.getId(), memo, e); |
| | | } |
| | | return 0; |
| | | } |
| | | |
| | | /** |
| | | * 更新memo字段中的重试次数 |
| | | * @param memo 原始memo内容 |
| | | * @param retryCount 新的重试次数 |
| | | * @return 更新后的memo内容 |
| | | */ |
| | | private String updateRetryCount(String memo, int retryCount) { |
| | | if (memo == null) { |
| | | memo = ""; |
| | | } |
| | | // 移除旧的retryCount信息 |
| | | String cleanedMemo = clearRetryInfo(memo); |
| | | // 添加新的retryCount信息 |
| | | if (cleanedMemo.isEmpty()) { |
| | | return "retryCount:" + retryCount; |
| | | } else { |
| | | return cleanedMemo + ",retryCount:" + retryCount; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 清除memo字段中的重试信息 |
| | | * @param memo 原始memo内容 |
| | | * @return 清除后的memo内容 |
| | | */ |
| | | private String clearRetryInfo(String memo) { |
| | | if (memo == null || memo.trim().isEmpty()) { |
| | | return ""; |
| | | } |
| | | // 移除 "retryCount:数字" 格式的内容 |
| | | String result = memo.replaceAll("retryCount:\\d+", "").trim(); |
| | | // 清理多余的逗号 |
| | | result = result.replaceAll("^,|,$", "").replaceAll(",,+", ","); |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | |
| | | /** |
| | | * 为任务分配站点(定时任务中调用) |
| | | * @param task 任务对象 |
| | | * @return 分配的站点编号,如果无法分配则返回null |
| | | * @return 如果无法分配站点,返回错误信息;如果分配成功,返回null并更新task的staNo |
| | | */ |
| | | private Integer allocateSiteForTask(Task task) { |
| | | private String allocateSiteForTask(Task task) { |
| | | // 根据任务的invWh(机器人组)判断是东侧还是西侧 |
| | | String robotGroup = task.getInvWh(); |
| | | List<String> targetStations; |
| | |
| | | } |
| | | |
| | | if (targetStations.isEmpty()) { |
| | | log.warn("任务ID:{}没有可用的目标站点配置", task.getId()); |
| | | return null; |
| | | String errorMsg = "没有可用的目标站点配置"; |
| | | log.warn("任务ID:{}", errorMsg, task.getId()); |
| | | return errorMsg; |
| | | } |
| | | |
| | | // 将站点字符串列表转换为整数列表 |
| | |
| | | .map(Integer::parseInt) |
| | | .collect(Collectors.toList()); |
| | | |
| | | // 判断能入站点(in_enable="Y"表示能入) |
| | | // 判断能入站点(in_enable="Y"表示能入),排除dev_no=0的无效站点 |
| | | List<Integer> sites = basDevpMapper.selectList( |
| | | new EntityWrapper<BasDevp>() |
| | | .eq("in_enable", "Y") |
| | | .in("dev_no", siteIntList) |
| | | ).stream().map(BasDevp::getDevNo).collect(Collectors.toList()); |
| | | .ne("dev_no", 0) // 排除dev_no=0的无效站点 |
| | | ).stream() |
| | | .map(BasDevp::getDevNo) |
| | | .filter(devNo -> devNo != null && devNo != 0) // 再次过滤,确保不为null或0 |
| | | .collect(Collectors.toList()); |
| | | |
| | | if (sites.isEmpty()) { |
| | | log.warn("任务ID:{}没有能入站点", task.getId()); |
| | | return null; |
| | | String errorMsg = "没有能入站点"; |
| | | log.warn("任务ID:{}", errorMsg, task.getId()); |
| | | return errorMsg; |
| | | } |
| | | |
| | | // 获取没有出库任务的站点 |
| | | List<Integer> canInSites = basDevpMapper.getCanInSites(sites); |
| | | // 先检查站点配置(canining="Y"可入),排除dev_no=0的无效站点 |
| | | List<BasDevp> devListWithConfig = basDevpMapper.selectList(new EntityWrapper<BasDevp>() |
| | | .in("dev_no", sites) |
| | | .eq("in_enable", "Y") |
| | | .eq("canining", "Y") |
| | | .ne("dev_no", 0) // 排除dev_no=0的无效站点 |
| | | ).stream() |
| | | .filter(dev -> dev.getDevNo() != null && dev.getDevNo() != 0) // 再次过滤,确保不为null或0 |
| | | .collect(Collectors.toList()); |
| | | |
| | | if (devListWithConfig.isEmpty()) { |
| | | // 站点配置不允许入库(canining != "Y"),暂不分配,等待配置开通(只在定时任务中记录日志) |
| | | log.warn("任务ID:{}没有可入站点(站点未开通可入允许:canining='Y'),暂不分配站点,等待配置开通", task.getId()); |
| | | return null; // 返回null,表示暂不分配,等待配置开通 |
| | | } |
| | | |
| | | // 获取没有出库任务的站点(从已配置可入的站点中筛选) |
| | | List<Integer> configuredSites = devListWithConfig.stream() |
| | | .map(BasDevp::getDevNo) |
| | | .collect(Collectors.toList()); |
| | | List<Integer> canInSites = basDevpMapper.getCanInSites(configuredSites); |
| | | if (canInSites.isEmpty()) { |
| | | log.warn("任务ID:{}没有可入站点(请等待出库完成)", task.getId()); |
| | | return null; |
| | | // 所有已配置可入的站点都有出库任务,暂不分配,等待下次定时任务再尝试(只在定时任务中记录日志) |
| | | log.warn("任务ID:{}没有可入站点(请等待出库完成),暂不分配站点,等待下次定时任务再尝试", task.getId()); |
| | | return null; // 返回null,表示暂不分配,等待下次定时任务再尝试 |
| | | } |
| | | |
| | | // 寻找入库任务最少的站点(且必须in_enable="Y"能入 和 canining="Y"可入) |
| | | // 寻找入库任务最少的站点(且必须in_enable="Y"能入 和 canining="Y"可入),排除dev_no=0的无效站点 |
| | | List<BasDevp> devList = basDevpMapper.selectList(new EntityWrapper<BasDevp>() |
| | | .in("dev_no", canInSites) |
| | | .eq("in_enable", "Y") |
| | | .eq("canining", "Y") |
| | | ); |
| | | .ne("dev_no", 0) // 排除dev_no=0的无效站点 |
| | | ).stream() |
| | | .filter(dev -> dev.getDevNo() != null && dev.getDevNo() != 0) // 再次过滤,确保不为null或0 |
| | | .collect(Collectors.toList()); |
| | | |
| | | if (devList.isEmpty()) { |
| | | log.warn("任务ID:{}没有可入站点(in_enable='Y'且canining='Y')", task.getId()); |
| | | return null; |
| | | // 理论上不应该到这里,因为前面已经检查过了,但为了安全起见还是保留 |
| | | String errorMsg = "没有可入站点(in_enable='Y'且canining='Y')"; |
| | | log.warn("任务ID:{}", errorMsg, task.getId()); |
| | | return errorMsg; |
| | | } |
| | | |
| | | // 入库任务数排序 |
| | | // 先按规则排序(入库任务数排序) |
| | | devList.sort(Comparator.comparing(BasDevp::getInQty)); |
| | | |
| | | // 选择站点 |
| | | BasDevp basDevp; |
| | | int minInQty = devList.get(0).getInQty(); |
| | | // 根据任务类型确定要检查的io_type列表 |
| | | Integer taskIoType = task.getIoType(); |
| | | List<Integer> checkIoTypes = null; |
| | | String taskTypeName = ""; |
| | | if (taskIoType != null) { |
| | | if (taskIoType < 100) { |
| | | // 入库任务:只检查入库类型(1, 10, 53, 57) |
| | | checkIoTypes = Arrays.asList(1, 10, 53, 57); |
| | | taskTypeName = "入库"; |
| | | } else { |
| | | // 出库任务:只检查出库类型(101, 110, 103, 107) |
| | | checkIoTypes = Arrays.asList(101, 110, 103, 107); |
| | | taskTypeName = "出库"; |
| | | } |
| | | } |
| | | |
| | | // 筛选出任务数最少的站点列表 |
| | | // 筛选出任务数最少的站点列表(按规则排序后的候选站点) |
| | | int minInQty = devList.get(0).getInQty(); |
| | | List<BasDevp> minTaskSites = devList.stream() |
| | | .filter(dev -> dev.getInQty() == minInQty) |
| | | .collect(Collectors.toList()); |
| | | |
| | | // 根据配置选择分配策略 |
| | | // 根据配置选择分配策略,确定优先分配的站点顺序 |
| | | List<BasDevp> orderedSites = new ArrayList<>(); |
| | | String strategy = agvProperties.getSiteAllocation().getStrategy(); |
| | | boolean enableRoundRobin = agvProperties.getSiteAllocation().isEnableRoundRobin(); |
| | | |
| | | if (minTaskSites.size() > 1 && enableRoundRobin && "round-robin".equals(strategy)) { |
| | | // 轮询分配 |
| | | // 轮询分配:先按轮询策略排序 |
| | | AtomicInteger counter = siteRoundRobinCounters.computeIfAbsent(groupKey, k -> new AtomicInteger(0)); |
| | | int index = counter.getAndIncrement() % minTaskSites.size(); |
| | | basDevp = minTaskSites.get(index); |
| | | log.info("使用轮询分配策略,站点组:{},轮询索引:{},选中站点:{}", groupKey, index, basDevp.getDevNo()); |
| | | int startIndex = counter.get() % minTaskSites.size(); |
| | | // 将轮询选中的站点放在最前面 |
| | | orderedSites.addAll(minTaskSites.subList(startIndex, minTaskSites.size())); |
| | | orderedSites.addAll(minTaskSites.subList(0, startIndex)); |
| | | // 添加其他站点(任务数更多的) |
| | | orderedSites.addAll(devList.stream() |
| | | .filter(dev -> dev.getInQty() > minInQty) |
| | | .collect(Collectors.toList())); |
| | | log.debug("使用轮询分配策略,站点组:{},轮询起始索引:{}", groupKey, startIndex); |
| | | } else if (minTaskSites.size() > 1 && enableRoundRobin && "random".equals(strategy)) { |
| | | // 随机分配 |
| | | Random random = new Random(); |
| | | int index = random.nextInt(minTaskSites.size()); |
| | | basDevp = minTaskSites.get(index); |
| | | log.info("使用随机分配策略,选中站点:{}", basDevp.getDevNo()); |
| | | // 随机分配:先随机排序任务数最少的站点 |
| | | List<BasDevp> shuffledMinSites = new ArrayList<>(minTaskSites); |
| | | Collections.shuffle(shuffledMinSites); |
| | | orderedSites.addAll(shuffledMinSites); |
| | | // 添加其他站点(任务数更多的) |
| | | orderedSites.addAll(devList.stream() |
| | | .filter(dev -> dev.getInQty() > minInQty) |
| | | .collect(Collectors.toList())); |
| | | log.debug("使用随机分配策略"); |
| | | } else { |
| | | // 默认:选择第一个(任务最少的) |
| | | basDevp = devList.get(0); |
| | | // 默认:按入库任务数排序(已经排序好了) |
| | | orderedSites = devList; |
| | | } |
| | | |
| | | Integer endSite = basDevp.getDevNo(); |
| | | // 依次检查每个站点是否在搬运,找到第一个空闲站点就分配 |
| | | BasDevp selectedSite = null; |
| | | for (BasDevp dev : orderedSites) { |
| | | String staNo = String.valueOf(dev.getDevNo()); |
| | | |
| | | // 如果任务类型不为空,检查该站点是否有正在搬运的同类型任务 |
| | | boolean isTransporting = false; |
| | | if (checkIoTypes != null && !checkIoTypes.isEmpty()) { |
| | | List<Task> transportingTasks = taskService.selectList( |
| | | new EntityWrapper<Task>() |
| | | .eq("sta_no", staNo) |
| | | .eq("task_type", "agv") |
| | | .eq("wrk_sts", 8L) // 只检查正在搬运状态的任务 |
| | | .in("io_type", checkIoTypes) |
| | | ); |
| | | isTransporting = !transportingTasks.isEmpty(); |
| | | |
| | | if (isTransporting) { |
| | | log.debug("站点{}有{}个正在搬运的{}AGV任务,检查下一个站点", |
| | | staNo, transportingTasks.size(), taskTypeName); |
| | | continue; // 该站点正在搬运,检查下一个站点 |
| | | } |
| | | } |
| | | |
| | | // 找到第一个空闲站点,分配 |
| | | selectedSite = dev; |
| | | log.info("任务ID:{}按规则应分配到站点{},该站点空闲,分配成功", task.getId(), staNo); |
| | | break; |
| | | } |
| | | |
| | | // 如果所有站点都在搬运,则不分配站点(只在定时任务中记录日志,不返回错误信息) |
| | | if (selectedSite == null) { |
| | | // log.warn("任务ID:{},暂不分配站点,等待空闲 - 所有候选站点都有正在搬运的{}任务", |
| | | // task.getId(), taskIoType != null && taskIoType < 100 ? "入库" : "出库"); |
| | | return null; // 返回null,表示暂不分配,等待下次定时任务再尝试 |
| | | } |
| | | |
| | | Integer endSite = selectedSite.getDevNo(); |
| | | |
| | | // 检查站点是否有效(不能为0或null) |
| | | if (endSite == null || endSite == 0) { |
| | | String errorMsg = String.format("分配的站点无效(dev_no=%s)", endSite); |
| | | log.error("任务ID:{},{}", task.getId(), errorMsg); |
| | | return errorMsg; |
| | | } |
| | | |
| | | // 入库暂存+1 |
| | | basDevpMapper.incrementInQty(endSite); |
| | | |
| | | // 更新任务的站点编号 |
| | | task.setStaNo(String.valueOf(endSite)); |
| | | taskService.updateById(task); |
| | | |
| | | log.info("任务ID:{}已分配站点:{}", task.getId(), endSite); |
| | | return endSite; |
| | | return null; // 分配成功,返回null |
| | | } |
| | | |
| | | /** |
| | |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void moveTaskToHistory(List<Task> taskList) { |
| | | |
| | | // 写入历史表 |
| | | // 写入历史表,保持ID一致 |
| | | for (Task task : taskList) { |
| | | TaskLog log = new TaskLog(); |
| | | BeanUtils.copyProperties(task, log); |
| | | // 保持ID一致,不设置为null |
| | | taskLogService.insert(log); |
| | | } |
| | | |