自动化立体仓库 - WMS系统
*
L
6 天以前 5099dfcf8e015c35a2f81986101e08e93a7d7944
*
3个文件已修改
2个文件已添加
591 ■■■■■ 已修改文件
src/main/java/com/zy/asrs/task/OrderToSortLineScheduler.java 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/utils/GroupedLockerOptimizerUtils.java 84 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/utils/OptimizedLockerPackingUtils.java 442 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/utils/ToSortLineUtils.java 30 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/utils/param/ItemUtilParam.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/task/OrderToSortLineScheduler.java
@@ -13,6 +13,7 @@
import com.zy.asrs.task.core.ReturnT;
import com.zy.asrs.task.handler.OrderToLineHandler;
import com.zy.asrs.utils.GroupedLockerOptimizerUtils;
import com.zy.asrs.utils.OptimizedLockerPackingUtils;
import com.zy.asrs.utils.ToSortLineUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
@@ -50,7 +51,8 @@
        for (String orderNo : orderNos) {
            try{
                List<OrderDetl> orderDetls = orderDetlService.selectByOrderNo(orderNo);
                List<GroupedLockerOptimizerUtils.Item> items = new ArrayList<>();
//                List<GroupedLockerOptimizerUtils.Item> items = new ArrayList<>();
                List<OptimizedLockerPackingUtils.Item> items = new ArrayList<>();
                for (OrderDetl orderDetl:orderDetls){
                    Integer number =  basArmRulesService.getNumber(orderDetl.getWeight(),orderDetl.getVolume(),orderDetl.getManLength(),orderDetl.getWidth(),orderDetl.getHeight());
                    if (number == null) {
@@ -67,13 +69,15 @@
                    String name = ToSortLineUtils.MergerParameter(orderDetl.getMatnr(),orderDetl.getStandby1(),orderDetl.getStandby2());
                    int maxCapacity = number;
                    int stock = orderDetl.getAnfme().intValue();
                    items.add(new GroupedLockerOptimizerUtils.Item(name, maxCapacity, stock));
//                    items.add(new GroupedLockerOptimizerUtils.Item(name, maxCapacity, stock));
                    items.add(new OptimizedLockerPackingUtils.Item(name, maxCapacity, stock));
                }
                OrderToLine orderToLine = new OrderToLine();
                orderToLine.setOrderNo(orderNo);  //单据编号
                orderToLine.setCreateTime(System.currentTimeMillis());  //创建时间
                OrderToLine orderToLineR = ToSortLineUtils.GetOrderToLine(items, orderToLine);
//                OrderToLine orderToLineR = ToSortLineUtils.GetOrderToLineGro(items, orderToLine);
                OrderToLine orderToLineR = ToSortLineUtils.GetOrderToLineOpt(items, orderToLine);
                try{
                    ReturnT<String> returnT = orderToLineHandler.start(orderToLineR);
src/main/java/com/zy/asrs/utils/GroupedLockerOptimizerUtils.java
@@ -28,6 +28,7 @@
    static class Locker {
        int id;
        double remainingSpace;
        long bindingTags;
        Map<String, Integer> contents;
        Set<String> itemTypes; // 当前储物柜中的物品种类
@@ -36,6 +37,7 @@
            this.remainingSpace = 1.0;
            this.contents = new HashMap<>();
            this.itemTypes = new HashSet<>();
            this.bindingTags = System.currentTimeMillis();
        }
        public boolean canAdd(Item item, int quantity) {
@@ -122,60 +124,60 @@
        }
    }
//    public static void main(String[] args) {
//        Scanner scanner = new Scanner(System.in);
//
//        System.out.println("请输入物品种类数量:");
//        int itemTypes = scanner.nextInt();
//        scanner.nextLine();
//
//        List<Item> items = new ArrayList<>();
//
//        for (int i = 0; i < itemTypes; i++) {
//            System.out.println("\n请输入第" + (i + 1) + "种物品的信息:");
//            System.out.print("物品名称: ");
//            String name = scanner.nextLine();
//
//            System.out.print("单种存放最大数量: ");
//            int maxCapacity = scanner.nextInt();
//
//            System.out.print("库存数量: ");
//            int stock = scanner.nextInt();
//            scanner.nextLine();
//
//            items.add(new Item(name, maxCapacity, stock));
//        }
//
//        // 使用分组优先算法
//        PackingSolution solution = packItemsWithGrouping(items);
//
//        System.out.println("\n" + solution);
//
//        scanner.close();
//    }
    public static void main(String[] args) {
        int itemTypes = 5;
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入物品种类数量:");
        int itemTypes = scanner.nextInt();
        scanner.nextLine();
        List<Item> items = new ArrayList<>();
        for (int i = 0; i < itemTypes; i++) {
            String name = i+"a";
            int maxCapacity = i*10;
            int stock = i*11;
            System.out.println("\n请输入第" + (i + 1) + "种物品的信息:");
            System.out.print("物品名称: ");
            String name = scanner.nextLine();
            System.out.print("单种存放最大数量: ");
            int maxCapacity = scanner.nextInt();
            System.out.print("库存数量: ");
            int stock = scanner.nextInt();
            scanner.nextLine();
            items.add(new Item(name, maxCapacity, stock));
        }
        // 使用分组优先算法
        PackingSolution solution = packItemsWithGrouping(items);
        for (Locker locker:solution.lockers) {
            for (String mantnr : locker.contents.keySet()){
                System.out.println(mantnr+"<===>"+locker.contents.get(mantnr));
            }
        }
        System.out.println("\n" + solution);
//        scanner.close();
        scanner.close();
    }
//    public static void main(String[] args) {
//        int itemTypes = 5;
//
//        List<Item> items = new ArrayList<>();
//
//        for (int i = 0; i < itemTypes; i++) {
//            String name = i+"a";
//            int maxCapacity = i*10;
//            int stock = i*11;
//            items.add(new Item(name, maxCapacity, stock));
//        }
//
//        // 使用分组优先算法
//        PackingSolution solution = packItemsWithGrouping(items);
//        for (Locker locker:solution.lockers) {
//            for (String mantnr : locker.contents.keySet()){
//                System.out.println(mantnr+"<===>"+locker.contents.get(mantnr));
//            }
//        }
//        System.out.println("\n" + solution);
//
////        scanner.close();
//    }
    /**
     * 分组优先装箱算法
src/main/java/com/zy/asrs/utils/OptimizedLockerPackingUtils.java
New file
@@ -0,0 +1,442 @@
package com.zy.asrs.utils;
import java.util.*;
public class OptimizedLockerPackingUtils {
    public static class Item {
        String name;
        double unitSpace;
        int maxCapacity;
        int stock;
        int remaining;
        public Item(String name, int maxCapacity, int stock) {
            this.name = name;
            this.maxCapacity = maxCapacity;
            this.unitSpace = 1.0 / maxCapacity;
            this.stock = stock;
            this.remaining = stock;
        }
        @Override
        public String toString() {
            return name + "(单放" + maxCapacity + "个, 库存" + stock + "个)";
        }
    }
    static class Locker {
        int id;
        double remainingSpace;
        long bindingTags;
        Map<String, Integer> contents;
        Set<String> itemTypes;
        public Locker(int id) {
            this.id = id;
            this.remainingSpace = 1.0;
            this.contents = new HashMap<>();
            this.itemTypes = new HashSet<>();
            this.bindingTags = System.currentTimeMillis();
        }
        public boolean canAdd(Item item, int quantity) {
            return remainingSpace >= quantity * item.unitSpace;
        }
        public void addItem(Item item, int quantity) {
            double spaceUsed = quantity * item.unitSpace;
            remainingSpace -= spaceUsed;
            contents.put(item.name, contents.getOrDefault(item.name, 0) + quantity);
            itemTypes.add(item.name);
            item.remaining -= quantity;
        }
        public boolean containsItemType(String itemName) {
            return itemTypes.contains(itemName);
        }
        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder();
            sb.append("储物柜").append(id).append(": ");
            sb.append(String.format("剩余空间%.4f", remainingSpace)).append("\n");
            for (Map.Entry<String, Integer> entry : contents.entrySet()) {
                sb.append("  ").append(entry.getKey()).append(": ").append(entry.getValue()).append("个\n");
            }
            return sb.toString();
        }
    }
    static class PackingSolution {
        List<Locker> lockers;
        int totalLockersUsed;
        double overallUtilization;
        int itemTypeChanges;
        int pureLockers; // 纯种类储物柜数量
        public PackingSolution() {
            this.lockers = new ArrayList<>();
        }
        public void addLocker(Locker locker) {
            lockers.add(locker);
            totalLockersUsed = lockers.size();
        }
        public void calculateMetrics() {
            double totalUsedSpace = 0;
            itemTypeChanges = 0;
            pureLockers = 0;
            String lastItemType = null;
            for (Locker locker : lockers) {
                totalUsedSpace += (1.0 - locker.remainingSpace);
                // 统计纯种类储物柜
                if (locker.itemTypes.size() == 1) {
                    pureLockers++;
                }
                for (String itemType : locker.itemTypes) {
                    if (lastItemType != null && !lastItemType.equals(itemType)) {
                        itemTypeChanges++;
                    }
                    lastItemType = itemType;
                }
            }
            overallUtilization = totalUsedSpace / totalLockersUsed;
        }
        @Override
        public String toString() {
            calculateMetrics();
            StringBuilder sb = new StringBuilder();
            sb.append("=== 优化摆放方案 ===\n");
            sb.append("使用的储物柜数量: ").append(totalLockersUsed).append("个\n");
            sb.append("纯种类储物柜: ").append(pureLockers).append("个\n");
            sb.append("平均空间利用率: ").append(String.format("%.2f", overallUtilization * 100)).append("%\n");
            sb.append("物品种类切换次数: ").append(itemTypeChanges).append("次\n\n");
            // 先显示纯种类储物柜,再显示混合储物柜
            List<Locker> pureLockersList = new ArrayList<>();
            List<Locker> mixedLockersList = new ArrayList<>();
            for (Locker locker : lockers) {
                if (locker.itemTypes.size() == 1) {
                    pureLockersList.add(locker);
                } else {
                    mixedLockersList.add(locker);
                }
            }
            sb.append("【纯种类储物柜】:\n");
            for (Locker locker : pureLockersList) {
                sb.append(locker.toString()).append("\n");
            }
            sb.append("【混合储物柜】:\n");
            for (Locker locker : mixedLockersList) {
                sb.append(locker.toString()).append("\n");
            }
            return sb.toString();
        }
    }
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入物品种类数量:");
        int itemTypes = scanner.nextInt();
        scanner.nextLine();
        List<Item> items = new ArrayList<>();
        for (int i = 0; i < itemTypes; i++) {
            System.out.println("\n请输入第" + (i + 1) + "种物品的信息:");
            System.out.print("物品名称: ");
            String name = scanner.nextLine();
            System.out.print("单种存放最大数量: ");
            int maxCapacity = scanner.nextInt();
            System.out.print("库存数量: ");
            int stock = scanner.nextInt();
            scanner.nextLine();
            items.add(new Item(name, maxCapacity, stock));
        }
        PackingSolution solution = optimizedPacking(items);
        System.out.println("\n" + solution);
        scanner.close();
    }
    /**
     * 优化装箱算法:先集中处理同种物品,最后处理余料
     */
    public static PackingSolution optimizedPacking(List<Item> items) {
        List<Item> itemsToPack = new ArrayList<>();
        for (Item item : items) {
            itemsToPack.add(new Item(item.name, item.maxCapacity, item.stock));
        }
        PackingSolution solution = new PackingSolution();
        int lockerId = 1;
        // 第一阶段:对每种物品进行集中处理
        for (Item item : itemsToPack) {
            if (item.remaining == 0) continue;
            // 计算这种物品需要多少个完整的储物柜
            int fullLockersNeeded = item.remaining / item.maxCapacity;
            int remainder = item.remaining % item.maxCapacity;
            // 分配完整储物柜
            for (int i = 0; i < fullLockersNeeded; i++) {
                Locker locker = new Locker(lockerId++);
                locker.addItem(item, item.maxCapacity);
                solution.addLocker(locker);
            }
            // 如果有余料,暂时保留(第二阶段处理)
        }
        // 第二阶段:收集所有余料
        List<Item> remainingItems = new ArrayList<>();
        for (Item item : itemsToPack) {
            if (item.remaining > 0) {
                remainingItems.add(item);
            }
        }
        // 如果还有余料,用最佳适应算法处理
        if (!remainingItems.isEmpty()) {
            processRemainingItems(remainingItems, solution, lockerId);
        }
        return solution;
    }
    /**
     * 处理剩余物品(余料)
     */
    private static void processRemainingItems(List<Item> remainingItems, PackingSolution solution, int startLockerId) {
        int lockerId = startLockerId;
        // 按空间占用从大到小排序(优先处理大物品)
        remainingItems.sort((a, b) -> Double.compare(b.unitSpace, a.unitSpace));
        for (Item item : remainingItems) {
            while (item.remaining > 0) {
                // 优先尝试放入已有同种物品的储物柜
                Locker sameTypeLocker = findSameTypeLocker(solution, item);
                if (sameTypeLocker != null) {
                    int maxCanAdd = (int) Math.floor(sameTypeLocker.remainingSpace / item.unitSpace);
                    int actualAdd = Math.min(item.remaining, maxCanAdd);
                    sameTypeLocker.addItem(item, actualAdd);
                    continue;
                }
                // 其次尝试放入现有储物柜(最佳适应)
                Locker bestLocker = findBestLocker(solution, item);
                if (bestLocker != null) {
                    int maxCanAdd = (int) Math.floor(bestLocker.remainingSpace / item.unitSpace);
                    int actualAdd = Math.min(item.remaining, maxCanAdd);
                    bestLocker.addItem(item, actualAdd);
                    continue;
                }
                // 最后创建新储物柜
                Locker newLocker = new Locker(lockerId++);
                int maxCanAdd = (int) Math.floor(1.0 / item.unitSpace);
                int actualAdd = Math.min(item.remaining, maxCanAdd);
                newLocker.addItem(item, actualAdd);
                solution.addLocker(newLocker);
            }
        }
    }
    /**
     * 寻找已有同种物品的储物柜
     */
    private static Locker findSameTypeLocker(PackingSolution solution, Item item) {
        Locker bestLocker = null;
        double bestRemaining = -1;
        for (Locker locker : solution.lockers) {
            if (locker.containsItemType(item.name) && locker.canAdd(item, 1)) {
                if (locker.remainingSpace > bestRemaining) {
                    bestLocker = locker;
                    bestRemaining = locker.remainingSpace;
                }
            }
        }
        return bestLocker;
    }
    /**
     * 寻找最佳适应的储物柜
     */
    private static Locker findBestLocker(PackingSolution solution, Item item) {
        Locker bestLocker = null;
        double bestRemaining = Double.MAX_VALUE;
        for (Locker locker : solution.lockers) {
            if (locker.canAdd(item, 1) && locker.remainingSpace < bestRemaining) {
                bestLocker = locker;
                bestRemaining = locker.remainingSpace;
            }
        }
        return bestLocker;
    }
    /**
     * 高级优化:智能余料分配
     */
    public static PackingSolution advancedOptimizedPacking(List<Item> items) {
        List<Item> itemsToPack = new ArrayList<>();
        for (Item item : items) {
            itemsToPack.add(new Item(item.name, item.maxCapacity, item.stock));
        }
        PackingSolution solution = new PackingSolution();
        int lockerId = 1;
        // 第一阶段:集中处理每种物品
        Map<String, Integer> remainders = new HashMap<>();
        for (Item item : itemsToPack) {
            if (item.remaining == 0) continue;
            int fullLockers = item.remaining / item.maxCapacity;
            int remainder = item.remaining % item.maxCapacity;
            for (int i = 0; i < fullLockers; i++) {
                Locker locker = new Locker(lockerId++);
                locker.addItem(item, item.maxCapacity);
                solution.addLocker(locker);
            }
            if (remainder > 0) {
                remainders.put(item.name, remainder);
            }
        }
        // 第二阶段:智能余料分配
        if (!remainders.isEmpty()) {
            smartRemainderProcessing(remainders, itemsToPack, solution, lockerId);
        }
        return solution;
    }
    /**
     * 智能余料处理:尝试将余料组合成完整储物柜
     */
    private static void smartRemainderProcessing(Map<String, Integer> remainders,
                                                 List<Item> items,
                                                 PackingSolution solution,
                                                 int startLockerId) {
        int lockerId = startLockerId;
        // 尝试找到可以完美组合的余料
        boolean foundCombination;
        do {
            foundCombination = false;
            // 寻找可以填满一个储物柜的余料组合
            for (Item item1 : items) {
                if (remainders.getOrDefault(item1.name, 0) == 0) continue;
                for (Item item2 : items) {
                    if (item1.name.equals(item2.name) || remainders.getOrDefault(item2.name, 0) == 0) continue;
                    double spaceNeeded = remainders.get(item1.name) * item1.unitSpace +
                            remainders.get(item2.name) * item2.unitSpace;
                    if (spaceNeeded <= 1.0 + 1e-6) { // 考虑浮点误差
                        Locker locker = new Locker(lockerId++);
                        locker.addItem(item1, remainders.get(item1.name));
                        locker.addItem(item2, remainders.get(item2.name));
                        solution.addLocker(locker);
                        remainders.put(item1.name, 0);
                        remainders.put(item2.name, 0);
                        foundCombination = true;
                        break;
                    }
                }
                if (foundCombination) break;
            }
        } while (foundCombination);
        // 处理剩余的零散余料
        for (Item item : items) {
            int remainder = remainders.getOrDefault(item.name, 0);
            if (remainder > 0) {
                Locker bestLocker = findBestLockerForRemainder(solution, item, remainder);
                if (bestLocker != null) {
                    bestLocker.addItem(item, remainder);
                } else {
                    Locker newLocker = new Locker(lockerId++);
                    newLocker.addItem(item, remainder);
                    solution.addLocker(newLocker);
                }
            }
        }
    }
    private static Locker findBestLockerForRemainder(PackingSolution solution, Item item, int quantity) {
        Locker bestLocker = null;
        double bestFit = Double.MAX_VALUE;
        for (Locker locker : solution.lockers) {
            if (locker.canAdd(item, quantity)) {
                double remainingAfterAdd = locker.remainingSpace - quantity * item.unitSpace;
                if (remainingAfterAdd < bestFit) {
                    bestLocker = locker;
                    bestFit = remainingAfterAdd;
                }
            }
        }
        return bestLocker;
    }
    /**
     * 性能测试和比较
     */
    public static void performanceTest() {
        System.out.println("=== 性能测试 ===");
        // 测试数据
        List<Item> testItems = Arrays.asList(
                new Item("X", 20, 87),
                new Item("Y", 15, 63),
                new Item("Z", 34, 125),
                new Item("T", 25, 48),
                new Item("U", 40, 92)
        );
        System.out.println("测试物品数量: " + testItems.size());
        int totalItems = testItems.stream().mapToInt(item -> item.stock).sum();
        System.out.println("总物品数量: " + totalItems);
        long startTime = System.currentTimeMillis();
        PackingSolution solution = optimizedPacking(testItems);
        long endTime = System.currentTimeMillis();
        System.out.println("\n优化算法结果:");
        System.out.println("处理时间: " + (endTime - startTime) + "ms");
        System.out.println("储物柜数量: " + solution.totalLockersUsed);
        System.out.println("纯种类储物柜: " + solution.pureLockers);
        System.out.println("空间利用率: " + String.format("%.2f", solution.overallUtilization * 100) + "%");
    }
}
src/main/java/com/zy/asrs/utils/ToSortLineUtils.java
@@ -36,8 +36,7 @@
        return sku+sign_F+po+sign_F+upc;
    }
    public static OrderToLine GetOrderToLine(List<GroupedLockerOptimizerUtils.Item> items,OrderToLine orderToLine){
        Long bindingTag = System.currentTimeMillis();//混搭标记
    public static OrderToLine GetOrderToLineGro(List<GroupedLockerOptimizerUtils.Item> items,OrderToLine orderToLine){
        List<OrderToLine.MatList> matLists = new ArrayList<>();
        // 使用分组优先算法
        GroupedLockerOptimizerUtils.PackingSolution solution = GroupedLockerOptimizerUtils.packItemsWithGrouping(items);
@@ -52,11 +51,34 @@
                        split[2],   //barcode -> upc -> s2
                        1,
                        null,   //origin -> supplier 货源
                        bindingTag
                        locker.bindingTags
                );
                matLists.add(mat);
            }
            bindingTag++;
        }
        orderToLine.setMatList(matLists);
        return orderToLine;
    }
    public static OrderToLine GetOrderToLineOpt(List<OptimizedLockerPackingUtils.Item> items,OrderToLine orderToLine){
        List<OrderToLine.MatList> matLists = new ArrayList<>();
        // 使用分组优先算法
        OptimizedLockerPackingUtils.PackingSolution packingSolution = OptimizedLockerPackingUtils.optimizedPacking(items);
        for (OptimizedLockerPackingUtils.Locker locker:packingSolution.lockers) {
            for (String mantnr : locker.contents.keySet()){
                System.out.println(mantnr+"<===>"+locker.contents.get(mantnr));
                String[] split = mantnr.split(Pattern.quote(sign_F));
                OrderToLine.MatList mat = new OrderToLine.MatList(
                        split[0],  // matnr -> sku
                        split[1],  //  supp -> po -> s1
                        locker.contents.get(mantnr).doubleValue(),   //整料
                        split[2],   //barcode -> upc -> s2
                        1,
                        null,   //origin -> supplier 货源
                        locker.bindingTags
                );
                matLists.add(mat);
            }
        }
        orderToLine.setMatList(matLists);
        return orderToLine;
src/main/java/com/zy/asrs/utils/param/ItemUtilParam.java
New file
@@ -0,0 +1,25 @@
package com.zy.asrs.utils.param;
import lombok.Data;
@Data
public class ItemUtilParam {
    String name;
    double unitSpace;
    int maxCapacity;
    int stock;
    int remaining;
    public ItemUtilParam(String name, int maxCapacity, int stock) {
        this.name = name;
        this.maxCapacity = maxCapacity;
        this.unitSpace = 1.0 / maxCapacity;
        this.stock = stock;
        this.remaining = stock;
    }
    @Override
    public String toString() {
        return name + "(单放" + maxCapacity + "个, 库存" + stock + "个)";
    }
}