自动化立体仓库 - WMS系统
chen.llin
2 天以前 2816415f539ef54839e331657edae7055c243ad8
src/main/java/com/zy/asrs/task/handler/WorkMastHandler.java
@@ -781,15 +781,9 @@
        }
        
        // 分配缓存库位:只查找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:{}", 
@@ -905,6 +899,167 @@
    }
    
    /**
     * 按优先级分配缓存库位
     * 优先级规则:
     * 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=空板出库)
@@ -1006,7 +1161,7 @@
        
        // 如果所有站点都在搬运,则不分配站点
        if (selectedSite == null) {
            log.warn("所有缓存区站点都有正在搬运的出库任务,暂不分配站点,等待空闲");
//            log.warn("所有缓存区站点都有正在搬运的出库任务,暂不分配站点,等待空闲");
            return null;
        }
        
@@ -1058,14 +1213,32 @@
                    }
                });
                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(库存更新完成)