| | |
| | | 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; |
| | |
| | | } |
| | | |
| | | /** |
| | | * 普通 run2 的推荐排优先阶段。 |
| | | * |
| | | * 推荐排只对普通物料生效,空托盘已经切换成“按库区轮询堆垛机”的规则, |
| | | * 因此这里会直接跳过空托盘请求,避免 row 参数把空托盘重新带回旧逻辑。 |
| | | */ |
| | | private LocMast findRun2RecommendLoc(RowLastno rowLastno, RowLastnoType rowLastnoType, boolean emptyPalletRequest, |
| | | List<Integer> recommendRows, LocTypeDto locTypeDto, Integer staDescId, |
| | | Integer sourceStaNo, StartupDto startupDto, Integer preferredArea, |
| | | List<Integer> triedCrnNos) { |
| | | if (emptyPalletRequest) { |
| | | return null; |
| | | } |
| | | List<Integer> recommendCrnNos = mapRowsToCrnNos(rowLastno, recommendRows); |
| | | if (Cools.isEmpty(recommendCrnNos)) { |
| | | return null; |
| | | } |
| | | LocMast locMast = findRun2EmptyLocByCrnNos(rowLastno, rowLastnoType, recommendCrnNos, locTypeDto, |
| | | staDescId, sourceStaNo, startupDto, preferredArea, "recommend"); |
| | | triedCrnNos.addAll(recommendCrnNos); |
| | | return locMast; |
| | | } |
| | | |
| | | /** |
| | | * 普通物料 run2 找位主流程。 |
| | | * |
| | | * 执行顺序: |
| | |
| | | locTypeDto == null || locTypeDto.getLocType1() == null ? null : locTypeDto.getLocType1().intValue(), |
| | | findLocNoAttributeVo == null ? null : findLocNoAttributeVo.getMatnr()); |
| | | if (!Cools.isEmpty(stationCrnLocTypes)) { |
| | | return findRun2EmptyLocByCrnLocTypeEntries(rowLastno, rowLastnoType, stationCrnLocTypes, |
| | | // 站点优先级只是“优先尝试”,没有命中时必须继续走默认/库区回退, |
| | | // 否则会把“优先候选无位”误判成“整仓无位”。 |
| | | LocMast locMast = findRun2EmptyLocByCrnLocTypeEntries(rowLastno, rowLastnoType, stationCrnLocTypes, |
| | | locTypeDto, staDescId, sourceStaNo, startupDto, preferredArea, "station-priority"); |
| | | if (!Cools.isEmpty(locMast)) { |
| | | return locMast; |
| | | } |
| | | } |
| | | if (preferredArea == null) { |
| | | List<Integer> defaultCrnNos = new ArrayList<>(orderedCrnNos); |
| | |
| | | * 普通物料命中库位后,沿用 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; |
| | | updateCurRow = getNextRun2CurrentRow(rowLastno, updateCurRow); |
| | | if (!Cools.isEmpty(runnableCrnNos) && selectedCrnNo != null) { |
| | | updateCurRow = getNextRun2CurrentRow(rowLastno, runnableCrnNos, selectedCrnNo, updateCurRow); |
| | | } else { |
| | | updateCurRow = getNextRun2CurrentRow(rowLastno, updateCurRow); |
| | | } |
| | | rowLastno.setCurrentRow(updateCurRow); |
| | | rowLastnoService.updateById(rowLastno); |
| | | } |
| | |
| | | } |
| | | } |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | private List<Integer> mapRowsToCrnNos(RowLastno rowLastno, List<Integer> rows) { |
| | | List<Integer> result = new ArrayList<>(); |
| | | if (rowLastno == null || Cools.isEmpty(rows)) { |
| | | return result; |
| | | } |
| | | LinkedHashSet<Integer> orderedCrnNos = new LinkedHashSet<>(); |
| | | Integer rowSpan = getCrnRowSpan(rowLastno.getTypeId()); |
| | | if (rowSpan == null || rowSpan <= 0) { |
| | | rowSpan = 2; |
| | | } |
| | | int startCrnNo = rowLastno.getsCrnNo() == null ? 1 : rowLastno.getsCrnNo(); |
| | | int endCrnNo = rowLastno.geteCrnNo() == null ? startCrnNo + rowLastno.getCrnQty() - 1 : rowLastno.geteCrnNo(); |
| | | int startRow = rowLastno.getsRow() == null ? 1 : rowLastno.getsRow(); |
| | | int endRow = rowLastno.geteRow() == null ? Integer.MAX_VALUE : rowLastno.geteRow(); |
| | | for (Integer row : rows) { |
| | | if (row == null || row < startRow || row > endRow) { |
| | | continue; |
| | | } |
| | | int crnNo = startCrnNo + (row - startRow) / rowSpan; |
| | | if (crnNo >= startCrnNo && crnNo <= endCrnNo) { |
| | | orderedCrnNos.add(crnNo); |
| | | } |
| | | } |
| | | result.addAll(orderedCrnNos); |
| | | return result; |
| | | } |
| | | |
| | |
| | | } |
| | | int sRow = rowLastno.getsRow(); |
| | | int eRow = rowLastno.geteRow(); |
| | | int crnNumber = rowLastno.getCrnQty(); |
| | | int crnNumber = resolveCrnCount(rowLastno); |
| | | |
| | | |
| | | // ===============>>>> 开始执行 |
| | |
| | | * run2 入库找位主流程。 |
| | | * |
| | | * 当前方法只保留“组织流程”和“统一收口”的职责,具体策略拆成独立方法: |
| | | * 1. 普通物料:推荐排优先 -> 站点优先库区/堆垛机 -> 其它库区。 |
| | | * 1. 普通物料:按 row_lastno 自身轮询顺序 -> 站点优先库区/堆垛机 -> 其它库区。 |
| | | * 2. 空托盘:优先库区 loc_type2=1 -> 其它库区 loc_type2=1 -> loc_type1=2 兼容。 |
| | | * 3. 命中库位后分别回写普通物料游标或空托盘库区游标。 |
| | | * |
| | | * WCS 传入的推荐排不再参与 run2 选位,避免上游 row 参数把任务重新绑回固定堆垛机。 |
| | | */ |
| | | public StartupDto getLocNoRun2(Integer whsType, Integer staDescId, Integer sourceStaNo, FindLocNoAttributeVo findLocNoAttributeVo, Integer moveCrnNo, LocTypeDto locTypeDto, List<Integer> recommendRows, int times) { |
| | | |
| | |
| | | if (Cools.isEmpty(rowLastnoType)) { |
| | | throw new CoolException("数据异常,请联系管理员===》库位规则类型未知"); |
| | | } |
| | | int crnNumber = rowLastno.getCrnQty(); |
| | | int crnNumber = resolveCrnCount(rowLastno); |
| | | rowCount = crnNumber; |
| | | |
| | | curRow = rowLastno.getCurrentRow(); |
| | |
| | | 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); |
| | | if (Cools.isEmpty(locMast)) { |
| | | if (emptyPalletRequest) { |
| | | // 空托盘单独按库区轮询: |
| | | // 1. 当前库区先找 loc_type2=1 |
| | | // 2. 当前库区没有,再找其他库区 loc_type2=1 |
| | | // 3. 全部 narrow 都没有时,再退到 loc_type1=2 |
| | | emptyPalletAreaSearchResult = findEmptyPalletRun2AreaLoc(rowLastno, staDescId, sourceStaNo, startupDto, preferredArea, locTypeDto); |
| | | if (!Cools.isEmpty(emptyPalletAreaSearchResult)) { |
| | | locMast = emptyPalletAreaSearchResult.locMast; |
| | | } |
| | | } else { |
| | | locMast = findNormalRun2Loc(rowLastno, rowLastnoType, sourceStaNo, staDescId, findLocNoAttributeVo, |
| | | locTypeDto, startupDto, preferredArea, orderedCrnNos, triedCrnNos); |
| | | if (emptyPalletRequest) { |
| | | // 空托盘单独按库区轮询: |
| | | // 1. 当前库区先找 loc_type2=1 |
| | | // 2. 当前库区没有,再找其他库区 loc_type2=1 |
| | | // 3. 全部 narrow 都没有时,再退到 loc_type1=2 |
| | | emptyPalletAreaSearchResult = findEmptyPalletRun2AreaLoc(rowLastno, staDescId, sourceStaNo, startupDto, preferredArea, locTypeDto); |
| | | if (!Cools.isEmpty(emptyPalletAreaSearchResult)) { |
| | | locMast = emptyPalletAreaSearchResult.locMast; |
| | | } |
| | | } else { |
| | | locMast = findNormalRun2Loc(rowLastno, rowLastnoType, sourceStaNo, staDescId, findLocNoAttributeVo, |
| | | locTypeDto, startupDto, preferredArea, orderedCrnNos, triedCrnNos); |
| | | } |
| | | |
| | | if (!Cools.isEmpty(locMast)) { |
| | |
| | | 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")) { |
| | |
| | | } |
| | | int sRow = rowLastno.getsRow(); |
| | | int eRow = rowLastno.geteRow(); |
| | | int crnNumber = rowLastno.getCrnQty(); |
| | | int crnNumber = resolveCrnCount(rowLastno); |
| | | |
| | | // ===============>>>> 开始执行 |
| | | curRow = rowLastno.getCurrentRow(); |
| | |
| | | } |
| | | |
| | | public StartupDto getLocNoRun5(Integer whsType, Integer staDescId, Integer sourceStaNo, FindLocNoAttributeVo findLocNoAttributeVo, Integer moveCrnNo, LocTypeDto locTypeDto, List<Integer> recommendRows, int times) { |
| | | // WCS 传入的推荐排不再参与 AGV/平库选位,统一按库位排号自身轮询逻辑找位。 |
| | | |
| | | // 初始化参数 |
| | | int crnNo = 0; //堆垛机号 |
| | |
| | | } |
| | | |
| | | // 开始查找库位 ==============================>> |
| | | |
| | | if (Cools.isEmpty(locMast) && !Cools.isEmpty(recommendRows)) { |
| | | for (Integer recommendRow : recommendRows) { |
| | | if (Cools.isEmpty(recommendRow)) { |
| | | continue; |
| | | } |
| | | LocMast recommendLoc = locMastService.queryFreeLocMast(recommendRow, locTypeDto.getLocType1(), rowLastnoType.getType().longValue()); |
| | | if (!Cools.isEmpty(recommendLoc) && VersionUtils.locMoveCheckLocTypeComplete(recommendLoc, locTypeDto)) { |
| | | locMast = recommendLoc; |
| | | crnNo = recommendLoc.getCrnNo(); |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | |
| | | Integer preferredArea = findLocNoAttributeVo.getOutArea(); |
| | | |