| | |
| | | } |
| | | |
| | | // 分配缓存库位:只查找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:{}", |
| | |
| | | } |
| | | |
| | | /** |
| | | * 按优先级分配缓存库位 |
| | | * 优先级规则: |
| | | * 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; |
| | | } |
| | | |
| | | /** |
| | | * 为出库到缓存区的任务分配站点(使用和入库一样的分配策略) |
| | | * @param cacheStations 缓存区站点列表 |
| | | * @param ioType 任务类型(101=全板出库,110=空板出库) |
| | |
| | | |
| | | // 如果所有站点都在搬运,则不分配站点 |
| | | if (selectedSite == null) { |
| | | log.warn("所有缓存区站点都有正在搬运的出库任务,暂不分配站点,等待空闲"); |
| | | // log.warn("所有缓存区站点都有正在搬运的出库任务,暂不分配站点,等待空闲"); |
| | | return null; |
| | | } |
| | | |
| | |
| | | } |
| | | }); |
| | | |
| | | 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(库存更新完成) |