| | |
| | | public ReturnT<String> agvDoOut(Task task) { |
| | | if (task.getIoType().equals(101)) { |
| | | Date now = new Date(); |
| | | |
| | | // 检查sourceLocNo是否为空 |
| | | if (task.getSourceLocNo() == null || task.getSourceLocNo().isEmpty()) { |
| | | // log.warn("任务ID:{}的sourceLocNo为空,跳过库位操作(可能是从站点入库的任务)", task.getId()); |
| | | task.setWrkSts(15L); |
| | | if (!taskService.updateById(task)) { |
| | | throw new CoolException("任务状态修改失败!!"); |
| | | } |
| | | return SUCCESS; |
| | | } |
| | | |
| | | // 查询库位信息 |
| | | LocCache locMast = locCacheService.selectOne(new EntityWrapper<LocCache>().eq("loc_no", task.getSourceLocNo())); |
| | | if (Objects.isNull(locMast)) { |
| | | throw new RuntimeException("数据错误:库位信息不能为空!!"); |
| | | // 如果查询不到库位,可能是sourceLocNo是站点号而不是库位号 |
| | | // 检查BasDevp表中是否存在该站点 |
| | | try { |
| | | Integer siteNo = Integer.parseInt(task.getSourceLocNo()); |
| | | List<BasDevp> basDevpList = basDevpMapper.selectList(new EntityWrapper<BasDevp>().eq("dev_no", siteNo)); |
| | | if (basDevpList != null && !basDevpList.isEmpty()) { |
| | | // sourceLocNo是站点号,这是从站点入库的任务,不需要处理库位 |
| | | // log.info("任务ID:{}的sourceLocNo是站点号:{},跳过库位操作(从站点入库任务)", task.getId(), task.getSourceLocNo()); |
| | | task.setWrkSts(15L); |
| | | if (!taskService.updateById(task)) { |
| | | throw new CoolException("任务状态修改失败!!"); |
| | | } |
| | | return SUCCESS; |
| | | } |
| | | } catch (NumberFormatException e) { |
| | | // sourceLocNo不是数字,可能是库位号格式错误 |
| | | log.warn("任务ID:{}的sourceLocNo:{}不是有效的数字格式", task.getId(), task.getSourceLocNo()); |
| | | } |
| | | // 既不是库位也不是站点,抛出异常 |
| | | throw new RuntimeException("数据错误:库位信息不能为空!!任务ID:" + task.getId() + ",sourceLocNo:" + task.getSourceLocNo()); |
| | | } |
| | | List<TaskDetl> wrkDetls101 = taskDetlService.selectList(new EntityWrapper<TaskDetl>().eq("wrk_no", task.getWrkNo())); |
| | | if (wrkDetls101.isEmpty()) { |
| | |
| | | .eq("barcode", completedTask.getBarcode()) |
| | | .in("io_type", 110, 101) // 空板出库或全板出库 |
| | | .eq("wrk_sts", 7) // 待呼叫AGV状态 |
| | | .andNew("(is_deleted = 0)") |
| | | ); |
| | | if (!existingTasks.isEmpty()) { |
| | | log.info("任务ID:{}的托盘码:{}已存在空托/满托出库任务,跳过生成", completedTask.getId(), completedTask.getBarcode()); |
| | |
| | | } |
| | | |
| | | // 分配缓存库位:只查找WA开头的库位(CA开头只做入库,WA开头才会被出库分配缓存区) |
| | | // 使用新的分配逻辑:按列优先级(第三列→第二列→第一列)分配 |
| | | String cacheAreaPrefix = agvProperties.getLocationPrefix().getCacheArea(); |
| | | LocCache cacheLoc = locCacheService.selectOne(new EntityWrapper<LocCache>() |
| | | .eq("whs_type", targetWhsType) // 根据出库站点判断的whs_type |
| | | .like("loc_no", cacheAreaPrefix + "%") // 只查找WA开头的库位(从配置读取) |
| | | .eq("frozen", 0) |
| | | .eq("loc_sts", LocStsType.LOC_STS_TYPE_O.type) // O.闲置 |
| | | .ne("full_plt", isEmptyPallet ? "Y" : "N") // 空托不选满板库位,满托不选空板库位 |
| | | .orderAsc(Arrays.asList("row1", "bay1", "lev1")) |
| | | .last("OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY")); |
| | | LocCache cacheLoc = allocateCacheLocationByPriority(targetWhsType, cacheAreaPrefix, isEmptyPallet); |
| | | |
| | | if (cacheLoc == null) { |
| | | log.warn("{}侧没有可用的{}缓存库位,不生成{}AGV任务,任务ID:{}", |
| | |
| | | .eq("task_type", "agv") |
| | | .eq("wrk_sts", 8L) // 只检查正在搬运状态的任务 |
| | | .in("io_type", 101, 110) // 出库到缓存区的任务类型 |
| | | .andNew("(is_deleted = 0)") |
| | | ); |
| | | |
| | | // 如果有正在搬运的任务,且工作档未完成,则不分配缓存库位 |
| | |
| | | |
| | | log.info("成功生成{}任务,任务ID:{},工作号:{},源站点:{},目标站点:{},缓存库位:{}", |
| | | isEmptyPallet ? "空托出库" : "满托出库", cacheTask.getId(), workNo, outboundStaNo, cacheStaNo, cacheLoc.getLocNo()); |
| | | } |
| | | |
| | | /** |
| | | * 按优先级分配缓存库位 |
| | | * 优先级规则: |
| | | * 1. 优先分配第三列(bay1=3),且该排的1、2、3列都是空的 |
| | | * 2. 如果所有第三列都有货,则分配第二列(bay1=2),且该排的1、2列都是空的 |
| | | * 3. 如果所有排的第二第三列都满了,则分配第一列(bay1=1) |
| | | * 4. 如果所有第一列都满了,再检查第二列和第三列 |
| | | * 5. 层(lev1)从第一层开始 |
| | | * |
| | | * @param whsType 库区类型 |
| | | * @param cacheAreaPrefix 缓存区库位前缀(如"WA") |
| | | * @param isEmptyPallet 是否空托 |
| | | * @return 分配的缓存库位,如果无法分配则返回null |
| | | */ |
| | | private LocCache allocateCacheLocationByPriority(Long whsType, String cacheAreaPrefix, boolean isEmptyPallet) { |
| | | // 查询所有符合条件的空库位 |
| | | List<LocCache> allLocations = locCacheService.selectList(new EntityWrapper<LocCache>() |
| | | .eq("whs_type", whsType) |
| | | .like("loc_no", cacheAreaPrefix + "%") |
| | | .eq("frozen", 0) |
| | | .eq("loc_sts", LocStsType.LOC_STS_TYPE_O.type) // O.闲置 |
| | | .ne("full_plt", isEmptyPallet ? "Y" : "N") // 空托不选满板库位,满托不选空板库位 |
| | | ); |
| | | |
| | | if (allLocations == null || allLocations.isEmpty()) { |
| | | return null; |
| | | } |
| | | |
| | | // 按row1分组 |
| | | Map<Integer, List<LocCache>> locationsByRow = allLocations.stream() |
| | | .filter(loc -> loc.getRow1() != null) |
| | | .collect(Collectors.groupingBy(LocCache::getRow1)); |
| | | |
| | | if (locationsByRow.isEmpty()) { |
| | | return null; |
| | | } |
| | | |
| | | // 对每个排,检查1、2、3列的状态 |
| | | // 列状态:true表示该列有空库位,false表示该列已满 |
| | | Map<Integer, Map<Integer, Boolean>> rowColumnStatus = new HashMap<>(); |
| | | for (Map.Entry<Integer, List<LocCache>> entry : locationsByRow.entrySet()) { |
| | | Integer row = entry.getKey(); |
| | | List<LocCache> rowLocs = entry.getValue(); |
| | | |
| | | Map<Integer, Boolean> columnStatus = new HashMap<>(); |
| | | // 检查第1、2、3列是否有空库位 |
| | | for (int bay = 1; bay <= 3; bay++) { |
| | | final int bayFinal = bay; // 创建final副本供lambda使用 |
| | | boolean hasEmpty = rowLocs.stream() |
| | | .anyMatch(loc -> loc.getBay1() != null && loc.getBay1() == bayFinal); |
| | | columnStatus.put(bay, hasEmpty); |
| | | } |
| | | rowColumnStatus.put(row, columnStatus); |
| | | } |
| | | |
| | | // 优先级1:分配第三列(bay1=3),且该排的1、2、3列都是空的 |
| | | for (Map.Entry<Integer, List<LocCache>> entry : locationsByRow.entrySet()) { |
| | | Integer row = entry.getKey(); |
| | | Map<Integer, Boolean> columnStatus = rowColumnStatus.get(row); |
| | | |
| | | // 检查该排的1、2、3列是否都是空的 |
| | | if (Boolean.TRUE.equals(columnStatus.get(1)) && |
| | | Boolean.TRUE.equals(columnStatus.get(2)) && |
| | | Boolean.TRUE.equals(columnStatus.get(3))) { |
| | | // 分配该排的第三列,从第一层开始 |
| | | List<LocCache> bay3Locs = entry.getValue().stream() |
| | | .filter(loc -> loc.getBay1() != null && loc.getBay1() == 3) |
| | | .sorted(Comparator.comparing(loc -> loc.getLev1() != null ? loc.getLev1() : 0)) |
| | | .collect(Collectors.toList()); |
| | | |
| | | if (!bay3Locs.isEmpty()) { |
| | | log.debug("优先级1:分配排{}的第三列,库位:{}", row, bay3Locs.get(0).getLocNo()); |
| | | return bay3Locs.get(0); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 优先级2:分配第二列(bay1=2),且该排的1、2列都是空的(第三列可能已满) |
| | | for (Map.Entry<Integer, List<LocCache>> entry : locationsByRow.entrySet()) { |
| | | Integer row = entry.getKey(); |
| | | Map<Integer, Boolean> columnStatus = rowColumnStatus.get(row); |
| | | |
| | | // 检查该排的1、2列是否都是空的 |
| | | if (Boolean.TRUE.equals(columnStatus.get(1)) && |
| | | Boolean.TRUE.equals(columnStatus.get(2))) { |
| | | // 分配该排的第二列,从第一层开始 |
| | | List<LocCache> bay2Locs = entry.getValue().stream() |
| | | .filter(loc -> loc.getBay1() != null && loc.getBay1() == 2) |
| | | .sorted(Comparator.comparing(loc -> loc.getLev1() != null ? loc.getLev1() : 0)) |
| | | .collect(Collectors.toList()); |
| | | |
| | | if (!bay2Locs.isEmpty()) { |
| | | log.debug("优先级2:分配排{}的第二列,库位:{}", row, bay2Locs.get(0).getLocNo()); |
| | | return bay2Locs.get(0); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 优先级3:分配第一列(bay1=1),所有排的第二第三列都满了 |
| | | for (Map.Entry<Integer, List<LocCache>> entry : locationsByRow.entrySet()) { |
| | | Integer row = entry.getKey(); |
| | | Map<Integer, Boolean> columnStatus = rowColumnStatus.get(row); |
| | | |
| | | // 检查该排的第一列是否有空库位 |
| | | if (Boolean.TRUE.equals(columnStatus.get(1))) { |
| | | // 分配该排的第一列,从第一层开始 |
| | | List<LocCache> bay1Locs = entry.getValue().stream() |
| | | .filter(loc -> loc.getBay1() != null && loc.getBay1() == 1) |
| | | .sorted(Comparator.comparing(loc -> loc.getLev1() != null ? loc.getLev1() : 0)) |
| | | .collect(Collectors.toList()); |
| | | |
| | | if (!bay1Locs.isEmpty()) { |
| | | log.debug("优先级3:分配排{}的第一列,库位:{}", row, bay1Locs.get(0).getLocNo()); |
| | | return bay1Locs.get(0); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 优先级4:如果所有第一列都满了,再检查第二列和第三列(不要求该排的1、2列都是空的) |
| | | // 先检查第二列 |
| | | for (Map.Entry<Integer, List<LocCache>> entry : locationsByRow.entrySet()) { |
| | | Integer row = entry.getKey(); |
| | | Map<Integer, Boolean> columnStatus = rowColumnStatus.get(row); |
| | | |
| | | // 检查该排的第二列是否有空库位 |
| | | if (Boolean.TRUE.equals(columnStatus.get(2))) { |
| | | List<LocCache> bay2Locs = entry.getValue().stream() |
| | | .filter(loc -> loc.getBay1() != null && loc.getBay1() == 2) |
| | | .sorted(Comparator.comparing(loc -> loc.getLev1() != null ? loc.getLev1() : 0)) |
| | | .collect(Collectors.toList()); |
| | | |
| | | if (!bay2Locs.isEmpty()) { |
| | | log.debug("优先级4:分配排{}的第二列,库位:{}", row, bay2Locs.get(0).getLocNo()); |
| | | return bay2Locs.get(0); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 优先级5:最后检查第三列(不要求该排的1、2、3列都是空的) |
| | | for (Map.Entry<Integer, List<LocCache>> entry : locationsByRow.entrySet()) { |
| | | Integer row = entry.getKey(); |
| | | Map<Integer, Boolean> columnStatus = rowColumnStatus.get(row); |
| | | |
| | | // 检查该排的第三列是否有空库位 |
| | | if (Boolean.TRUE.equals(columnStatus.get(3))) { |
| | | List<LocCache> bay3Locs = entry.getValue().stream() |
| | | .filter(loc -> loc.getBay1() != null && loc.getBay1() == 3) |
| | | .sorted(Comparator.comparing(loc -> loc.getLev1() != null ? loc.getLev1() : 0)) |
| | | .collect(Collectors.toList()); |
| | | |
| | | if (!bay3Locs.isEmpty()) { |
| | | log.debug("优先级5:分配排{}的第三列,库位:{}", row, bay3Locs.get(0).getLocNo()); |
| | | return bay3Locs.get(0); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 如果所有列都满了,返回null |
| | | return null; |
| | | } |
| | | |
| | | /** |
| | |
| | | .eq("task_type", "agv") |
| | | .eq("wrk_sts", 8L) // 只检查正在搬运状态的任务 |
| | | .in("io_type", checkIoTypes) |
| | | .andNew("(is_deleted = 0)") |
| | | ); |
| | | |
| | | if (!transportingTasks.isEmpty()) { |
| | |
| | | |
| | | // 如果所有站点都在搬运,则不分配站点 |
| | | if (selectedSite == null) { |
| | | log.warn("所有缓存区站点都有正在搬运的出库任务,暂不分配站点,等待空闲"); |
| | | // log.warn("所有缓存区站点都有正在搬运的出库任务,暂不分配站点,等待空闲"); |
| | | return null; |
| | | } |
| | | |
| | |
| | | if (ioType == 1 || ioType == 10 || ioType == 53 || ioType == 57) { |
| | | // ioType == 1 需要处理组托数据 |
| | | if (ioType == 1) { |
| | | // 检查locNo是否为空 |
| | | if (wrkMast.getLocNo() == null || wrkMast.getLocNo().isEmpty()) { |
| | | log.warn("任务ID:{}的locNo为空,跳过库位操作(可能是从站点入库的任务)", wrkMast.getId()); |
| | | // 更新任务状态为5(库存更新完成) |
| | | wrkMast.setWrkSts(5L); |
| | | wrkMast.setModiTime(new Date()); |
| | | if (!taskService.updateById(wrkMast)) { |
| | | throw new CoolException("任务状态修改失败!!"); |
| | | } |
| | | return SUCCESS; |
| | | } |
| | | |
| | | LocCache locCache = locCacheService.selectOne(new EntityWrapper<LocCache>().eq("loc_no", wrkMast.getLocNo())); |
| | | if (Objects.isNull(locCache)) { |
| | | throw new CoolException("数据错误,库位不存在!!"); |
| | | // 如果查询不到库位,可能是locNo是站点号而不是库位号 |
| | | // 检查BasDevp表中是否存在该站点 |
| | | try { |
| | | Integer siteNo = Integer.parseInt(wrkMast.getLocNo()); |
| | | List<BasDevp> basDevpList = basDevpMapper.selectList(new EntityWrapper<BasDevp>().eq("dev_no", siteNo)); |
| | | if (basDevpList != null && !basDevpList.isEmpty()) { |
| | | // locNo是站点号,这是从站点入库的任务,不需要处理库位明细 |
| | | log.info("任务ID:{}的locNo是站点号:{},跳过库位操作(从站点入库任务)", wrkMast.getId(), wrkMast.getLocNo()); |
| | | // 更新任务状态为5(库存更新完成) |
| | | wrkMast.setWrkSts(5L); |
| | | wrkMast.setModiTime(new Date()); |
| | | if (!taskService.updateById(wrkMast)) { |
| | | throw new CoolException("任务状态修改失败!!"); |
| | | } |
| | | return SUCCESS; |
| | | } |
| | | } catch (NumberFormatException e) { |
| | | // locNo不是数字,可能是库位号格式错误 |
| | | log.warn("任务ID:{}的locNo:{}不是有效的数字格式", wrkMast.getId(), wrkMast.getLocNo()); |
| | | } |
| | | // 既不是库位也不是站点,抛出异常 |
| | | throw new CoolException("数据错误,库位不存在!!任务ID:" + wrkMast.getId() + ",locNo:" + wrkMast.getLocNo()); |
| | | } |
| | | if (!locCache.getLocSts().equals(LocStsType.LOC_STS_TYPE_S.type)) { |
| | | throw new CoolException("当前库位状态为:" + LocStsType.LOC_STS_TYPE_S.type + "." + LocStsType.LOC_STS_TYPE_S.desc + ",不是出库预约状态"); |
| | |
| | | } |
| | | }); |
| | | |
| | | locCache.setLocSts(LocStsType.LOC_STS_TYPE_F.type); |
| | | // 根据fullPlt设置库位状态:满托设置为"F"(在库),空托设置为"D"(空桶/空栈板) |
| | | boolean isFullPlt = wrkMast.getFullPlt() != null && wrkMast.getFullPlt().equals("Y"); |
| | | locCache.setLocSts(isFullPlt ? LocStsType.LOC_STS_TYPE_F.type : LocStsType.LOC_STS_TYPE_D.type); |
| | | locCache.setFullPlt(isFullPlt ? "Y" : "N"); |
| | | locCache.setModiTime(new Date()); |
| | | locCache.setBarcode(wrkMast.getBarcode()); |
| | | locCache.setModiTime(new Date()); |
| | | locCache.setIoTime(new Date()); |
| | | if (!locCacheService.updateById(locCache)) { |
| | | throw new CoolException("库位状态修改失败!"); |
| | | } |
| | | } else if (ioType == 10 || ioType == 53 || ioType == 57) { |
| | | // 空托入库或其他入库类型,也需要更新缓存库位状态 |
| | | LocCache locCache = locCacheService.selectOne(new EntityWrapper<LocCache>().eq("loc_no", wrkMast.getLocNo())); |
| | | if (locCache != null) { |
| | | // 根据fullPlt设置库位状态:满托设置为"F"(在库),空托设置为"D"(空桶/空栈板) |
| | | // ioType == 10 是空托入库,默认设置为"D" |
| | | boolean isFullPlt = (ioType != 10) && (wrkMast.getFullPlt() != null && wrkMast.getFullPlt().equals("Y")); |
| | | locCache.setLocSts(isFullPlt ? LocStsType.LOC_STS_TYPE_F.type : LocStsType.LOC_STS_TYPE_D.type); |
| | | locCache.setFullPlt(isFullPlt ? "Y" : "N"); |
| | | locCache.setModiTime(new Date()); |
| | | locCache.setBarcode(wrkMast.getBarcode()); |
| | | locCache.setIoTime(new Date()); |
| | | if (!locCacheService.updateById(locCache)) { |
| | | throw new CoolException("库位状态修改失败!"); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 更新任务状态为5(库存更新完成) |