自动化立体仓库 - WMS系统
zwl
4 天以前 a68a6de7f27e035beb2a141d895dd7e44ae9e659
完善找库位规则
1个文件已添加
3个文件已修改
296 ■■■■ 已修改文件
src/main/java/com/zy/asrs/controller/OpenController.java 150 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/entity/param/QueryTaskParam.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/utils/Utils.java 82 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/common/service/CommonService.java 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/controller/OpenController.java
@@ -4,18 +4,15 @@
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.plugins.Page;
import com.core.annotations.AppAuth;
import com.core.common.*;
import com.core.exception.CoolException;
import com.google.common.collect.Lists;
import com.zy.asrs.entity.*;
import com.zy.asrs.entity.param.*;
import com.zy.asrs.service.LocDetlService;
import com.zy.asrs.service.OpenService;
import com.zy.asrs.service.WaitPakinService;
import com.zy.asrs.service.WrkDetlService;
import com.zy.asrs.service.WrkMastLogService;
import com.zy.asrs.service.WrkMastService;
import com.zy.asrs.mapper.ReportQueryMapper;
import com.zy.asrs.service.*;
import com.zy.common.model.DetlDto;
import com.zy.common.model.LocDetlDto;
import com.zy.common.model.enums.WorkNoType;
@@ -26,6 +23,7 @@
import javax.naming.ldap.HasControls;
import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
import java.util.*;
/**
@@ -55,7 +53,11 @@
    private WrkMastService wrkMastService;
    @Autowired
    private WrkMastLogService wrkMastLogService;
    @Autowired
    private MatService matService;
    @Autowired
    private ReportQueryMapper reportQueryMapper;
//    @PostMapping("/order/matSync/default/v1")
////    @AppAuth(memo = "商品信息同步接口")
//    public synchronized R syncMatInfo(@RequestHeader(required = false) String appkey,
@@ -538,5 +540,141 @@
        }
        return openService.pakoutOrderPause(param);
    }
    /*************************************电视机程序***********************************************/
    @GetMapping("/locDetl/statistics")
    public R locDetlStatistics(){
        HashMap<String, Object> param = new HashMap<>();
        Page<LocDetl> stockStatis = locDetlService.getStockStatis(toPage(1, 100, param, LocDetl.class));
        for (LocDetl locDetl : stockStatis.getRecords()) {
            Mat mat = matService.selectByMatnr(locDetl.getMatnr());
            if (mat != null) {
                locDetl.sync(mat);
            }
        }
        return R.ok(stockStatis);
    }
    @GetMapping("/line/charts")
    public R locIoLineCharts(){
        Map<String,Object> map=new HashMap<String, Object>();
        List<AxisBean> list = new ArrayList<AxisBean>();
        List<WorkChartAxis> listChart = reportQueryMapper.getChartAxis();
        if(listChart!=null) {
            ArrayList<Integer> data1 = new ArrayList<Integer>();
            ArrayList<Integer> data2 = new ArrayList<Integer>();
            SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd");
            Calendar calendar = Calendar.getInstance();
            calendar.add(Calendar.DATE, -12);
            for(int i=0;i<12;i++) {
                boolean flag = true;
                calendar.add(Calendar.DATE, 1);
                String str = sf.format(calendar.getTime());
                for(WorkChartAxis workChart : listChart) {
                    if(str.equals(workChart.getYmd())) {
                        data1.add(workChart.getInqty());
                        data2.add(workChart.getOutqty());
                        flag = false;
                        break;
                    }
                }
                if(flag) {
                    data1.add(0);
                    data2.add(0);
                }
            }
            AxisBean inqty = new AxisBean();
            inqty.setName("入库数量");
            Integer[] array1 = new Integer[data1.size()];
            inqty.setData(data1.toArray(array1));
            list.add(inqty);
            AxisBean outqty = new AxisBean();
            outqty.setName("出库数量");
            Integer[] array2 = new Integer[data2.size()];
            outqty.setData(data2.toArray(array2));
            list.add(outqty);
        }
        map.put("rows",list);
        return R.ok(map);
    }
    /**
     * 库存信息查询接口
     */
    @GetMapping("/queryLoc")
    public synchronized R queryLoc() {
        List<Map<String, Object>> pie = new ArrayList<>();
        LocChartPie locUseRate = reportQueryMapper.getLocUseRate();
        if (locUseRate != null) {
            Map<String, Object> map = new HashMap<>();
            map.put("name", "在库");
            map.put("value", locUseRate.getFqty());
            pie.add(map);
            Map<String, Object> map1 = new HashMap<>();
            map1.put("name", "空");
            map1.put("value", locUseRate.getOqty());
            pie.add(map1);
            Map<String, Object> map2 = new HashMap<>();
            map2.put("name", "使用");
            map2.put("value", locUseRate.getUqty());
            pie.add(map2);
            Map<String, Object> map3 = new HashMap<>();
            map3.put("name", "禁用");
            map3.put("value", locUseRate.getXqty());
            pie.add(map3);
        }
        // 总库位数
        Integer total = (int) Arith.add(0, locUseRate.getFqty(), locUseRate.getOqty(), locUseRate.getUqty(), locUseRate.getXqty());
        // 使用中
        Integer used = locUseRate.getFqty() + locUseRate.getUqty();
        // 库位使用率
        double usedDivides = Arith.divides(3, used, total);
        double usedPr = Arith.multiplys(1, usedDivides, 100);
        return R.ok(
                Cools.add("pie", pie)
                        .add("stockCount", locUseRate.getFqty())
                        .add("emptyCount", locUseRate.getOqty())
                        .add("disableCount", locUseRate.getXqty())
                        .add("total", total)
                        .add("used", used)
                        .add("usedPr", usedPr)
        );
    }
    /**
     * 任务查询接口
     */
    @PostMapping("/queryTask")
    public synchronized R queryTask(@RequestBody QueryTaskParam param) {
        if (Cools.isEmpty(param)) {
            return R.parse(BaseRes.PARAM);
        }
        if (Cools.isEmpty(param.getTaskNo())) {
            return R.error("任务号[taskNo]不能为空");
        }
        WrkMast wrkMast = wrkMastService.selectOne(new EntityWrapper<WrkMast>().eq("wrk_no", param.getTaskNo()));
        if (wrkMast == null) {
            return R.error("任务不存在");
        }
        List<WrkDetl> wrkDetls = wrkDetlService.selectByWrkNo(Integer.valueOf(param.getTaskNo()));
        HashMap<String, Object> map = new HashMap<>();
        map.put("taskNo", param.getTaskNo());
        map.put("ioType", wrkMast.getIoType());
        map.put("wrkDetls", wrkDetls);
        return R.ok().add(map);
    }
}
src/main/java/com/zy/asrs/entity/param/QueryTaskParam.java
New file
@@ -0,0 +1,10 @@
package com.zy.asrs.entity.param;
import lombok.Data;
@Data
public class QueryTaskParam {
    private String taskNo;
}
src/main/java/com/zy/asrs/utils/Utils.java
@@ -152,7 +152,9 @@
     * 3. 若当前库区没有满足条件的空库位,再补充其他库区的堆垛机。
     * 4. 当 {@code locType1 = 1} 时,先返回低库位堆垛机,再把同批堆垛机的高库位追加到后面。
     * 5. 对不存在、故障、不可入以及无空库位的堆垛机直接剔除。
     * 6. 当物料为 {@code emptyPallet} 时,按空板入库优先规则重新排序。
     *
     * <p>这里是只读排序工具,不再写回 {@code asr_row_lastno.crn_qty}。
     * {@code crn_qty} 在主数据里表示“堆垛机数量”,不能再被拿来当轮询游标,否则会把整仓配置写坏。
     *
     * <p>返回结果中的每一项格式为:
     * {@code {crnNo: 堆垛机号, locType1: 库位高低类型}}
@@ -321,21 +323,10 @@
        if (areaRowLastno == null) {
            return new ArrayList<>(crnNos);
        }
        Integer startCrnNo = resolveAreaStartCrnNo(areaRowLastno, rowLastno);
        Integer endCrnNo = resolveAreaEndCrnNo(areaRowLastno, rowLastno);
        if (startCrnNo != null && endCrnNo != null && startCrnNo <= endCrnNo) {
            for (int crnNo = startCrnNo; crnNo <= endCrnNo; crnNo++) {
                addAreaCrnNo(crnNos, crnNo, 1, endCrnNo);
            }
            for (int crnNo = areaRowLastno.getsCrnNo(); crnNo <= startCrnNo; crnNo++) {
                addAreaCrnNo(crnNos, crnNo, 1, endCrnNo);
            }
            Integer nextCrnQty = startCrnNo + 1;
            if (areaRowLastno.geteCrnNo() != null && nextCrnQty > areaRowLastno.geteCrnNo()) {
                nextCrnQty = areaRowLastno.getsCrnNo() == null ? 1 : areaRowLastno.getsCrnNo();
            }
            areaRowLastno.setCrnQty(nextCrnQty);
            SpringUtils.getBean(RowLastnoService.class).updateById(areaRowLastno);
        Integer startCrnNo = resolveAreaStartCrnNo(areaRowLastno);
        List<Integer> orderedCrnNos = getOrderedCrnNos(areaRowLastno, startCrnNo);
        if (!orderedCrnNos.isEmpty()) {
            crnNos.addAll(orderedCrnNos);
        }
        if (crnNos.isEmpty()) {
@@ -362,34 +353,47 @@
        return defaultRowLastno;
    }
    private static Integer resolveAreaStartCrnNo(RowLastno areaRowLastno, RowLastno defaultRowLastno) {
        if (areaRowLastno != null && areaRowLastno.getCrnQty() != null && areaRowLastno.getCrnQty() > 0) {
            return areaRowLastno.getCrnQty();
        }
        if (areaRowLastno != null && areaRowLastno.getsCrnNo() != null && areaRowLastno.getsCrnNo() > 0) {
            return areaRowLastno.getsCrnNo();
        }
        if (defaultRowLastno != null && defaultRowLastno.getsCrnNo() != null && defaultRowLastno.getsCrnNo() > 0) {
            return defaultRowLastno.getsCrnNo();
        }
        return 1;
    }
    private static Integer resolveAreaEndCrnNo(RowLastno areaRowLastno, RowLastno defaultRowLastno) {
        if (areaRowLastno != null && areaRowLastno.geteCrnNo() != null && areaRowLastno.geteCrnNo() > 0) {
            return areaRowLastno.geteCrnNo();
        }
    private static Integer resolveAreaStartCrnNo(RowLastno areaRowLastno) {
        if (areaRowLastno == null) {
        return null;
    }
        Integer startCrnNo = areaRowLastno.getsCrnNo();
        Integer endCrnNo = areaRowLastno.geteCrnNo();
        Integer currentRow = areaRowLastno.getCurrentRow();
        Integer rowSpan = getCrnRowSpan(areaRowLastno.getTypeId());
        if (startCrnNo == null || startCrnNo <= 0) {
            return 1;
        }
        if (endCrnNo == null || endCrnNo < startCrnNo || currentRow == null || rowSpan == null || rowSpan <= 0) {
            return startCrnNo;
        }
        int startRow = areaRowLastno.getsRow() == null ? 1 : areaRowLastno.getsRow();
        int offset = Math.max(currentRow - startRow, 0) / rowSpan;
        int resolvedCrnNo = startCrnNo + offset;
        if (resolvedCrnNo < startCrnNo || resolvedCrnNo > endCrnNo) {
            return startCrnNo;
        }
        return resolvedCrnNo;
    }
    private static void addAreaCrnNo(LinkedHashSet<Integer> crnNos, Integer crnNo, Integer startCrnNo, Integer endCrnNo) {
        if (crnNos == null || crnNo == null || startCrnNo == null || endCrnNo == null) {
            return;
    private static List<Integer> getOrderedCrnNos(RowLastno rowLastno, Integer startCrnNo) {
        List<Integer> orderedCrnNos = new ArrayList<>();
        if (rowLastno == null) {
            return orderedCrnNos;
        }
        if (crnNo < startCrnNo || crnNo > endCrnNo) {
            return;
        int start = rowLastno.getsCrnNo() == null ? 1 : rowLastno.getsCrnNo();
        int end = rowLastno.geteCrnNo() == null ? start + ((rowLastno.getCrnQty() == null ? 1 : rowLastno.getCrnQty()) - 1) : rowLastno.geteCrnNo();
        int first = startCrnNo == null ? start : startCrnNo;
        if (first < start || first > end) {
            first = start;
        }
        crnNos.add(crnNo);
        for (int crnNo = first; crnNo <= end; crnNo++) {
            orderedCrnNos.add(crnNo);
        }
        for (int crnNo = start; crnNo < first; crnNo++) {
            orderedCrnNos.add(crnNo);
        }
        return orderedCrnNos;
    }
    private static List<Integer> getAllCrnNos(RowLastno rowLastno) {
src/main/java/com/zy/common/service/CommonService.java
@@ -538,6 +538,30 @@
        return orderedCrnNos;
    }
    /**
     * 优先按 s_crn_no/e_crn_no 反推真实堆垛机数量。
     *
     * 之前有逻辑错误把 asr_row_lastno.crn_qty 当成轮询游标写回,
     * 导致“堆垛机数量”字段被污染。后续找位不能再直接信任 crn_qty,
     * 否则会少扫堆垛机,表现出来就是任务长期偏向某几台堆垛机。
     */
    private int resolveCrnCount(RowLastno rowLastno) {
        if (rowLastno == null) {
            return 0;
        }
        if (rowLastno.getsCrnNo() != null && rowLastno.geteCrnNo() != null && rowLastno.geteCrnNo() >= rowLastno.getsCrnNo()) {
            return rowLastno.geteCrnNo() - rowLastno.getsCrnNo() + 1;
        }
        if (rowLastno.getCrnQty() != null && rowLastno.getCrnQty() > 0) {
            return rowLastno.getCrnQty();
        }
        Integer rowSpan = getCrnRowSpan(rowLastno.getTypeId());
        if (rowSpan != null && rowSpan > 0 && rowLastno.getsRow() != null && rowLastno.geteRow() != null && rowLastno.geteRow() >= rowLastno.getsRow()) {
            return (rowLastno.geteRow() - rowLastno.getsRow() + 1 + rowSpan - 1) / rowSpan;
        }
        return 0;
    }
    private Integer getCrnStartRow(RowLastno rowLastno, Integer crnNo) {
        if (rowLastno == null || crnNo == null) {
            return null;
@@ -729,6 +753,11 @@
     *
     * 推荐排只对普通物料生效,空托盘已经切换成“按库区轮询堆垛机”的规则,
     * 因此这里会直接跳过空托盘请求,避免 row 参数把空托盘重新带回旧逻辑。
     *
     * 另外,单排推荐不再作为“强绑定单台堆垛机”处理。
     * 现场上游经常只传一个推荐排,例如 row=[1],如果这里直接短路命中,
     * 满板任务就会长期压在同一台堆垛机上。现在只有当推荐排能覆盖多台堆垛机时,
     * 才把它当作真正的优先候选集合。
     */
    private LocMast findRun2RecommendLoc(RowLastno rowLastno, RowLastnoType rowLastnoType, boolean emptyPalletRequest,
                                         List<Integer> recommendRows, LocTypeDto locTypeDto, Integer staDescId,
@@ -738,7 +767,7 @@
            return null;
        }
        List<Integer> recommendCrnNos = mapRowsToCrnNos(rowLastno, recommendRows);
        if (Cools.isEmpty(recommendCrnNos)) {
        if (Cools.isEmpty(recommendCrnNos) || recommendCrnNos.size() <= 1) {
            return null;
        }
        LocMast locMast = findRun2EmptyLocByCrnNos(rowLastno, rowLastnoType, recommendCrnNos, locTypeDto,
@@ -791,11 +820,25 @@
     * 普通物料命中库位后,沿用 run2 原有的全仓轮询游标推进方式。
     */
    private void advanceNormalRun2Cursor(RowLastno rowLastno, int curRow) {
        advanceNormalRun2Cursor(rowLastno, curRow, null, null);
    }
    /**
     * 普通物料游标优先按“本次真正可用的堆垛机集合”推进。
     *
     * 这样即使库区定义里存在不存在的堆垛机,或者路径主数据只覆盖部分堆垛机,
     * 满板任务也会在真实可作业的堆垛机之间轮询,不会因为理论堆垛机号的空洞而长期回落到同一台。
     */
    private void advanceNormalRun2Cursor(RowLastno rowLastno, int curRow, List<Integer> runnableCrnNos, Integer selectedCrnNo) {
        if (rowLastno == null) {
            return;
        }
        int updateCurRow = curRow == 0 ? (rowLastno.getsRow() == null ? 1 : rowLastno.getsRow()) : curRow;
        if (!Cools.isEmpty(runnableCrnNos) && selectedCrnNo != null) {
            updateCurRow = getNextRun2CurrentRow(rowLastno, runnableCrnNos, selectedCrnNo, updateCurRow);
        } else {
        updateCurRow = getNextRun2CurrentRow(rowLastno, updateCurRow);
        }
        rowLastno.setCurrentRow(updateCurRow);
        rowLastnoService.updateById(rowLastno);
    }
@@ -1471,7 +1514,7 @@
        }
        int sRow = rowLastno.getsRow();
        int eRow = rowLastno.geteRow();
        int crnNumber = rowLastno.getCrnQty();
        int crnNumber = resolveCrnCount(rowLastno);
        // ===============>>>> 开始执行
@@ -1691,7 +1734,7 @@
        if (Cools.isEmpty(rowLastnoType)) {
            throw new CoolException("数据异常,请联系管理员===》库位规则类型未知");
        }
        int crnNumber = rowLastno.getCrnQty();
        int crnNumber = resolveCrnCount(rowLastno);
        rowCount = crnNumber;
        curRow = rowLastno.getCurrentRow();
@@ -1701,6 +1744,7 @@
        Run2AreaSearchResult emptyPalletAreaSearchResult = null;
        List<Integer> orderedCrnNos = getOrderedCrnNos(rowLastno, crnNo);
        List<Integer> orderedRunnableCrnNos = getOrderedRunnableRun2CrnNos(rowLastno, staDescId, sourceStaNo, orderedCrnNos);
        List<Integer> triedCrnNos = new ArrayList<>();
        locMast = findRun2RecommendLoc(rowLastno, rowLastnoType, emptyPalletRequest, recommendRows, locTypeDto,
                staDescId, sourceStaNo, startupDto, preferredArea, triedCrnNos);
@@ -1727,7 +1771,7 @@
        if (emptyPalletRequest) {
            advanceEmptyPalletRun2Cursor(emptyPalletAreaSearchResult, locMast);
        } else {
            advanceNormalRun2Cursor(rowLastno, curRow);
            advanceNormalRun2Cursor(rowLastno, curRow, orderedRunnableCrnNos, locMast == null ? null : locMast.getCrnNo());
        }
        if (Cools.isEmpty(locMast) || !locMast.getLocSts().equals("O")) {
@@ -1789,7 +1833,7 @@
        }
        int sRow = rowLastno.getsRow();
        int eRow = rowLastno.geteRow();
        int crnNumber = rowLastno.getCrnQty();
        int crnNumber = resolveCrnCount(rowLastno);
        // ===============>>>> 开始执行
        curRow = rowLastno.getCurrentRow();