| | |
| | | import com.zy.asrs.service.*; |
| | | import com.zy.asrs.utils.Utils; |
| | | import com.zy.asrs.utils.VersionUtils; |
| | | import com.zy.common.entity.Parameter; |
| | | import com.zy.common.model.LocTypeDto; |
| | | import com.zy.common.model.Shelves; |
| | | import com.zy.common.model.StartupDto; |
| | |
| | | import java.time.temporal.ChronoUnit; |
| | | import java.util.ArrayList; |
| | | import java.util.HashMap; |
| | | import java.util.LinkedHashSet; |
| | | import java.util.List; |
| | | import java.util.Locale; |
| | | import java.util.Map; |
| | | import java.util.Optional; |
| | | |
| | |
| | | Integer whsType = Utils.GetWhsType(sourceStaNo); |
| | | RowLastno rowLastno = rowLastnoService.selectById(whsType); |
| | | RowLastnoType rowLastnoType = rowLastnoTypeService.selectById(rowLastno.getTypeId()); |
| | | if (rowLastnoType.getType() == 2) { |
| | | return getLocNoRun2(whsType, staDescId, sourceStaNo, findLocNoAttributeVo, 0, locTypeDto, 0); |
| | | Integer preferredArea = resolvePreferredArea(sourceStaNo, findLocNoAttributeVo); |
| | | if (preferredArea != null) { |
| | | findLocNoAttributeVo.setOutArea(preferredArea); |
| | | } |
| | | |
| | | /** |
| | | * 库型 1: 标准堆垛机库 2: 平库 3: 穿梭板 4: 四向车 5: AGV 0: 未知 |
| | | */ |
| | | switch (rowLastnoType.getType()) { |
| | | case 1: |
| | | return getLocNoRun(whsType, staDescId, sourceStaNo, findLocNoAttributeVo, 0, locTypeDto, 0); |
| | | case 2: |
| | | log.error("站点={} 未查询到对应的规则", sourceStaNo); |
| | | break; |
| | | return getLocNoRun2(whsType, staDescId, sourceStaNo, findLocNoAttributeVo, 0, locTypeDto, recommendRows, 0); |
| | | case 3: |
| | | log.error("站点={} 未查询到对应的规则", sourceStaNo); |
| | | break; |
| | | return getLocNoRun(whsType, staDescId, sourceStaNo, findLocNoAttributeVo, 0, locTypeDto, 0); |
| | | case 4: |
| | | return getLocNoRun4(whsType, staDescId, sourceStaNo, findLocNoAttributeVo, 4, locTypeDto, 0); |
| | | case 5: |
| | | return getLocNoRun5(whsType, staDescId, sourceStaNo, findLocNoAttributeVo, 0, locTypeDto, recommendRows, 0); |
| | | default: |
| | | log.error("站点={} 未查询到对应的规则", sourceStaNo); |
| | | break; |
| | | throw new CoolException("站点=" + sourceStaNo + " 未查询到对应的库位规则"); |
| | | } |
| | | } catch (CoolException e) { |
| | | log.error("站点={} 查找库位失败: {}", sourceStaNo, e.getMessage(), e); |
| | | throw e; |
| | | } catch (Exception e) { |
| | | log.error("站点={} 查找库位异常", sourceStaNo, e); |
| | | throw new CoolException("站点=" + sourceStaNo + " 查找库位失败"); |
| | | } |
| | | } |
| | | |
| | | } catch (Exception e) { |
| | | log.error("站点={} 未查询到对应的规则", sourceStaNo); |
| | | private Integer resolvePreferredArea(Integer sourceStaNo, FindLocNoAttributeVo findLocNoAttributeVo) { |
| | | BasDevp sourceStation = basDevpService.selectById(sourceStaNo); |
| | | Integer stationArea = parseArea(sourceStation == null ? null : sourceStation.getArea()); |
| | | if (stationArea != null) { |
| | | return stationArea; |
| | | } |
| | | Integer requestArea = findLocNoAttributeVo.getOutArea(); |
| | | if (requestArea != null && requestArea >= 1 && requestArea <= 3) { |
| | | return requestArea; |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | private Integer parseArea(String area) { |
| | | if (Cools.isEmpty(area)) { |
| | | return null; |
| | | } |
| | | String normalized = area.trim(); |
| | | if (normalized.isEmpty()) { |
| | | return null; |
| | | } |
| | | try { |
| | | int areaNo = Integer.parseInt(normalized); |
| | | return areaNo >= 1 && areaNo <= 3 ? areaNo : null; |
| | | } catch (NumberFormatException ignored) { |
| | | } |
| | | String upper = normalized.toUpperCase(Locale.ROOT); |
| | | if ("A".equals(upper) || "A区".equals(upper) || "A库".equals(upper) || "A库区".equals(upper)) { |
| | | return 1; |
| | | } |
| | | if ("B".equals(upper) || "B区".equals(upper) || "B库".equals(upper) || "B库区".equals(upper)) { |
| | | return 2; |
| | | } |
| | | if ("C".equals(upper) || "C区".equals(upper) || "C库".equals(upper) || "C库区".equals(upper)) { |
| | | return 3; |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | private String getAgvAreaRowsConfig(Integer area) { |
| | | Parameter parameter = Parameter.get(); |
| | | if (parameter == null || area == null) { |
| | | return null; |
| | | } |
| | | switch (area) { |
| | | case 1: |
| | | return parameter.getAgvArea1Rows(); |
| | | case 2: |
| | | return parameter.getAgvArea2Rows(); |
| | | case 3: |
| | | return parameter.getAgvArea3Rows(); |
| | | default: |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | private List<Integer> getAgvAreaRows(Integer area, RowLastno rowLastno) { |
| | | List<Integer> configuredRows = parseAgvRows(getAgvAreaRowsConfig(area), rowLastno); |
| | | if (!configuredRows.isEmpty()) { |
| | | return configuredRows; |
| | | } |
| | | return getLegacyAgvRows(rowLastno); |
| | | } |
| | | |
| | | private List<Integer> getAgvFallbackRows(RowLastno rowLastno) { |
| | | LinkedHashSet<Integer> rows = new LinkedHashSet<>(); |
| | | for (int area = 1; area <= 3; area++) { |
| | | rows.addAll(parseAgvRows(getAgvAreaRowsConfig(area), rowLastno)); |
| | | } |
| | | rows.addAll(getLegacyAgvRows(rowLastno)); |
| | | return new ArrayList<>(rows); |
| | | } |
| | | |
| | | private List<Integer> parseAgvRows(String configValue, RowLastno rowLastno) { |
| | | List<Integer> rows = new ArrayList<>(); |
| | | if (rowLastno == null || Cools.isEmpty(configValue)) { |
| | | return rows; |
| | | } |
| | | LinkedHashSet<Integer> orderedRows = new LinkedHashSet<>(); |
| | | String normalized = configValue.replace(",", ",") |
| | | .replace(";", ";") |
| | | .replace("、", ",") |
| | | .replaceAll("\\s+", ""); |
| | | if (normalized.isEmpty()) { |
| | | return rows; |
| | | } |
| | | for (String segment : normalized.split("[,;]")) { |
| | | if (segment == null || segment.isEmpty()) { |
| | | continue; |
| | | } |
| | | if (segment.contains("-")) { |
| | | String[] rangeParts = segment.split("-", 2); |
| | | Integer startRow = safeParseInt(rangeParts[0]); |
| | | Integer endRow = safeParseInt(rangeParts[1]); |
| | | if (startRow == null || endRow == null) { |
| | | continue; |
| | | } |
| | | int step = startRow <= endRow ? 1 : -1; |
| | | for (int row = startRow; step > 0 ? row <= endRow : row >= endRow; row += step) { |
| | | addAgvRow(orderedRows, row, rowLastno); |
| | | } |
| | | continue; |
| | | } |
| | | addAgvRow(orderedRows, safeParseInt(segment), rowLastno); |
| | | } |
| | | rows.addAll(orderedRows); |
| | | return rows; |
| | | } |
| | | |
| | | private List<Integer> getLegacyAgvRows(RowLastno rowLastno) { |
| | | List<Integer> rows = new ArrayList<>(); |
| | | if (rowLastno == null) { |
| | | return rows; |
| | | } |
| | | LinkedHashSet<Integer> orderedRows = new LinkedHashSet<>(); |
| | | int startRow = Math.min(38, rowLastno.geteRow()); |
| | | int endRow = Math.max(32, rowLastno.getsRow()); |
| | | if (startRow >= endRow) { |
| | | for (int row = startRow; row >= endRow; row--) { |
| | | addAgvRow(orderedRows, row, rowLastno); |
| | | } |
| | | } else { |
| | | for (int row = rowLastno.geteRow(); row >= rowLastno.getsRow(); row--) { |
| | | addAgvRow(orderedRows, row, rowLastno); |
| | | } |
| | | } |
| | | rows.addAll(orderedRows); |
| | | return rows; |
| | | } |
| | | |
| | | private void addAgvRow(LinkedHashSet<Integer> rows, Integer row, RowLastno rowLastno) { |
| | | if (rows == null || row == null || rowLastno == null) { |
| | | return; |
| | | } |
| | | if (row < rowLastno.getsRow() || row > rowLastno.geteRow()) { |
| | | return; |
| | | } |
| | | rows.add(row); |
| | | } |
| | | |
| | | private Integer safeParseInt(String value) { |
| | | if (Cools.isEmpty(value)) { |
| | | return null; |
| | | } |
| | | try { |
| | | return Integer.parseInt(value.trim()); |
| | | } catch (NumberFormatException ignored) { |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | private int[] getAgvAreaBayRange(Integer area) { |
| | | if (area == null) { |
| | | return new int[]{1, 19}; |
| | | } |
| | | switch (area) { |
| | | case 1: |
| | | return new int[]{1, 12}; |
| | | case 2: |
| | | return new int[]{13, 36}; |
| | | case 3: |
| | | return new int[]{37, 56}; |
| | | default: |
| | | return new int[]{1, 56}; |
| | | } |
| | | } |
| | | |
| | | private LocMast findAgvLocByRows(RowLastno rowLastno, RowLastnoType rowLastnoType, List<Integer> rows, |
| | | int startBay, int endBay, int curRow, int nearRow, |
| | | LocTypeDto locTypeDto, boolean useDeepCheck) { |
| | | for (Integer row : rows) { |
| | | if (row == null) { |
| | | continue; |
| | | } |
| | | List<LocMast> locMasts = locMastService.selectList(new EntityWrapper<LocMast>() |
| | | .eq("row1", row) |
| | | .ge("bay1", startBay) |
| | | .le("bay1", endBay) |
| | | .eq("loc_sts", "O") |
| | | .eq("loc_type1", locTypeDto.getLocType1()) |
| | | .orderBy("lev1", true) |
| | | .orderBy("bay1", true)); |
| | | for (LocMast candidate : locMasts) { |
| | | if (!VersionUtils.locMoveCheckLocTypeComplete(candidate, locTypeDto)) { |
| | | continue; |
| | | } |
| | | if (useDeepCheck) { |
| | | if (!Utils.BooleanWhsTypeStaIoType(rowLastno)) { |
| | | continue; |
| | | } |
| | | LocMast deepLoc = locMastService.selectLocByLocStsPakInO(curRow, nearRow, candidate, rowLastnoType.getType().longValue()); |
| | | if (!Cools.isEmpty(deepLoc) && deepLoc.getRow1() == curRow) { |
| | | return deepLoc; |
| | | } |
| | | continue; |
| | | } |
| | | return candidate; |
| | | } |
| | | } |
| | | return null; |
| | | } |
| | | private String getRun2AreaRowsConfig(Integer area) { |
| | | Parameter parameter = Parameter.get(); |
| | | if (parameter == null || area == null) { |
| | | return null; |
| | | } |
| | | String run2Config; |
| | | switch (area) { |
| | | case 1: |
| | | run2Config = parameter.getRun2Area1Rows(); |
| | | break; |
| | | case 2: |
| | | run2Config = parameter.getRun2Area2Rows(); |
| | | break; |
| | | case 3: |
| | | run2Config = parameter.getRun2Area3Rows(); |
| | | break; |
| | | default: |
| | | return null; |
| | | } |
| | | return Cools.isEmpty(run2Config) ? getAgvAreaRowsConfig(area) : run2Config; |
| | | } |
| | | |
| | | private List<Integer> getRun2AreaRows(Integer area, RowLastno rowLastno) { |
| | | List<Integer> configuredRows = parseAgvRows(getRun2AreaRowsConfig(area), rowLastno); |
| | | if (!configuredRows.isEmpty()) { |
| | | return configuredRows; |
| | | } |
| | | return getLegacyAgvRows(rowLastno); |
| | | } |
| | | |
| | | private List<Integer> getRun2FallbackRows(RowLastno rowLastno) { |
| | | LinkedHashSet<Integer> rows = new LinkedHashSet<>(); |
| | | for (int area = 1; area <= 3; area++) { |
| | | rows.addAll(parseAgvRows(getRun2AreaRowsConfig(area), rowLastno)); |
| | | } |
| | | rows.addAll(getLegacyAgvRows(rowLastno)); |
| | | return new ArrayList<>(rows); |
| | | } |
| | | |
| | | private Integer resolveRun2CrnNo(RowLastno rowLastno) { |
| | | if (rowLastno == null) { |
| | | return null; |
| | | } |
| | | Integer currentRow = rowLastno.getCurrentRow(); |
| | | Integer rowSpan = getCrnRowSpan(rowLastno.getTypeId()); |
| | | if (rowSpan == null || rowSpan <= 0) { |
| | | rowSpan = 2; |
| | | } |
| | | int startRow = rowLastno.getsRow() == null ? 1 : rowLastno.getsRow(); |
| | | int startCrnNo = rowLastno.getsCrnNo() == null ? 1 : rowLastno.getsCrnNo(); |
| | | if (currentRow == null) { |
| | | return startCrnNo; |
| | | } |
| | | int offset = Math.max(currentRow - startRow, 0) / rowSpan; |
| | | int crnNo = startCrnNo + offset; |
| | | Integer endCrnNo = rowLastno.geteCrnNo(); |
| | | if (endCrnNo != null && crnNo > endCrnNo) { |
| | | return startCrnNo; |
| | | } |
| | | return crnNo; |
| | | } |
| | | |
| | | private int getNextRun2CurrentRow(RowLastno rowLastno, int currentRow) { |
| | | Integer rowSpan = getCrnRowSpan(rowLastno.getTypeId()); |
| | | if (rowSpan == null || rowSpan <= 0) { |
| | | rowSpan = 2; |
| | | } |
| | | int startRow = rowLastno.getsRow() == null ? 1 : rowLastno.getsRow(); |
| | | int endRow = rowLastno.geteRow() == null ? currentRow : rowLastno.geteRow(); |
| | | int lastStartRow = Math.max(startRow, endRow - rowSpan + 1); |
| | | if (currentRow >= lastStartRow) { |
| | | return startRow; |
| | | } |
| | | return currentRow + rowSpan; |
| | | } |
| | | |
| | | private List<Integer> getOrderedCrnNos(RowLastno rowLastno, Integer startCrnNo) { |
| | | List<Integer> orderedCrnNos = new ArrayList<>(); |
| | | if (rowLastno == null) { |
| | | return orderedCrnNos; |
| | | } |
| | | int start = rowLastno.getsCrnNo() == null ? 1 : rowLastno.getsCrnNo(); |
| | | int end = rowLastno.geteCrnNo() == null ? start + rowLastno.getCrnQty() - 1 : rowLastno.geteCrnNo(); |
| | | int first = startCrnNo == null ? start : startCrnNo; |
| | | if (first < start || first > end) { |
| | | first = start; |
| | | } |
| | | for (int crnNo = first; crnNo <= end; crnNo++) { |
| | | orderedCrnNos.add(crnNo); |
| | | } |
| | | for (int crnNo = start; crnNo < first; crnNo++) { |
| | | orderedCrnNos.add(crnNo); |
| | | } |
| | | return orderedCrnNos; |
| | | } |
| | | |
| | | private List<Integer> filterCrnNosByRows(RowLastno rowLastno, List<Integer> orderedCrnNos, List<Integer> rows) { |
| | | if (Cools.isEmpty(rows)) { |
| | | return new ArrayList<>(orderedCrnNos); |
| | | } |
| | | LinkedHashSet<Integer> rowSet = new LinkedHashSet<>(rows); |
| | | List<Integer> result = new ArrayList<>(); |
| | | Integer rowSpan = getCrnRowSpan(rowLastno.getTypeId()); |
| | | if (rowSpan == null || rowSpan <= 0) { |
| | | rowSpan = 2; |
| | | } |
| | | int startCrnNo = rowLastno.getsCrnNo() == null ? 1 : rowLastno.getsCrnNo(); |
| | | int startRow = rowLastno.getsRow() == null ? 1 : rowLastno.getsRow(); |
| | | int endRow = rowLastno.geteRow() == null ? Integer.MAX_VALUE : rowLastno.geteRow(); |
| | | for (Integer crnNo : orderedCrnNos) { |
| | | if (crnNo == null || crnNo < startCrnNo) { |
| | | continue; |
| | | } |
| | | int crnOffset = crnNo - startCrnNo; |
| | | int crnStartRow = startRow + crnOffset * rowSpan; |
| | | for (int row = crnStartRow; row < crnStartRow + rowSpan && row <= endRow; row++) { |
| | | if (rowSet.contains(row)) { |
| | | result.add(crnNo); |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | 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; |
| | | } |
| | | |
| | | private Integer resolveTargetStaNo(RowLastno rowLastno, Integer staDescId, Integer sourceStaNo, Integer crnNo) { |
| | | if (!Utils.BooleanWhsTypeSta(rowLastno, staDescId)) { |
| | | return null; |
| | | } |
| | | StaDesc staDesc = staDescService.selectOne(new EntityWrapper<StaDesc>() |
| | | .eq("type_no", staDescId) |
| | | .eq("stn_no", sourceStaNo) |
| | | .eq("crn_no", crnNo)); |
| | | if (Cools.isEmpty(staDesc)) { |
| | | log.error("type_no={},stn_no={},crn_no={}", staDescId, sourceStaNo, crnNo); |
| | | return null; |
| | | } |
| | | BasDevp staNo = basDevpService.selectById(staDesc.getCrnStn()); |
| | | if (Cools.isEmpty(staNo) || !"Y".equals(staNo.getAutoing())) { |
| | | log.error("目标站{}不可用", staDesc.getCrnStn()); |
| | | return null; |
| | | } |
| | | return staNo.getDevNo(); |
| | | } |
| | | |
| | | private void logRun2NoMatch(String stage, Integer sourceStaNo, Integer preferredArea, List<Integer> candidateCrnNos, |
| | | LocTypeDto locTypeDto, List<Integer> crnErrorCrns, List<Integer> routeBlockedCrns, |
| | | List<Integer> noEmptyCrns, List<Integer> locTypeBlockedCrns) { |
| | | log.warn("run2 no location. stage={}, sourceStaNo={}, preferredArea={}, candidateCrnNos={}, crnErrorCrns={}, routeBlockedCrns={}, noEmptyCrns={}, locTypeBlockedCrns={}, spec={}", |
| | | stage, sourceStaNo, preferredArea, candidateCrnNos, crnErrorCrns, routeBlockedCrns, noEmptyCrns, locTypeBlockedCrns, |
| | | JSON.toJSONString(locTypeDto)); |
| | | } |
| | | |
| | | private LocMast findRun2EmptyLocByCrnNos(RowLastno rowLastno, RowLastnoType rowLastnoType, List<Integer> candidateCrnNos, |
| | | LocTypeDto locTypeDto, Integer staDescId, Integer sourceStaNo, StartupDto startupDto, |
| | | Integer preferredArea, String stage) { |
| | | if (Cools.isEmpty(candidateCrnNos)) { |
| | | log.warn("run2 skip empty candidate list. stage={}, sourceStaNo={}, preferredArea={}, spec={}", |
| | | stage, sourceStaNo, preferredArea, JSON.toJSONString(locTypeDto)); |
| | | return null; |
| | | } |
| | | List<Integer> crnErrorCrns = new ArrayList<>(); |
| | | List<Integer> routeBlockedCrns = new ArrayList<>(); |
| | | List<Integer> noEmptyCrns = new ArrayList<>(); |
| | | List<Integer> locTypeBlockedCrns = new ArrayList<>(); |
| | | for (Integer candidateCrnNo : candidateCrnNos) { |
| | | if (candidateCrnNo == null || !basCrnpService.checkSiteError(candidateCrnNo, true)) { |
| | | crnErrorCrns.add(candidateCrnNo); |
| | | continue; |
| | | } |
| | | Integer targetStaNo = resolveTargetStaNo(rowLastno, staDescId, sourceStaNo, candidateCrnNo); |
| | | if (Utils.BooleanWhsTypeSta(rowLastno, staDescId) && targetStaNo == null) { |
| | | routeBlockedCrns.add(candidateCrnNo); |
| | | continue; |
| | | } |
| | | Wrapper<LocMast> openWrapper = new EntityWrapper<LocMast>() |
| | | .eq("crn_no", candidateCrnNo) |
| | | .eq("loc_sts", "O") |
| | | .eq("loc_type1", locTypeDto.getLocType1()) |
| | | .orderBy("lev1") |
| | | .orderBy("bay1"); |
| | | LocMast anyOpenLoc = locMastService.selectOne(openWrapper); |
| | | if (Cools.isEmpty(anyOpenLoc)) { |
| | | noEmptyCrns.add(candidateCrnNo); |
| | | continue; |
| | | } |
| | | Wrapper<LocMast> wrapper = new EntityWrapper<LocMast>() |
| | | .eq("crn_no", candidateCrnNo) |
| | | .eq("loc_sts", "O") |
| | | .eq("loc_type1", locTypeDto.getLocType1()) |
| | | .orderBy("lev1") |
| | | .orderBy("bay1"); |
| | | if (locTypeDto != null && locTypeDto.getLocType1() != null) { |
| | | wrapper.eq("loc_type1", locTypeDto.getLocType1()); |
| | | } |
| | | LocMast candidateLoc = locMastService.selectOne(wrapper); |
| | | if (Cools.isEmpty(candidateLoc) || (locTypeDto != null && !VersionUtils.locMoveCheckLocTypeComplete(candidateLoc, locTypeDto))) { |
| | | locTypeBlockedCrns.add(candidateCrnNo); |
| | | continue; |
| | | } |
| | | if (targetStaNo != null) { |
| | | startupDto.setStaNo(targetStaNo); |
| | | } |
| | | return candidateLoc; |
| | | } |
| | | logRun2NoMatch(stage, sourceStaNo, preferredArea, candidateCrnNos, locTypeDto, crnErrorCrns, routeBlockedCrns, noEmptyCrns, locTypeBlockedCrns); |
| | | return null; |
| | | } |
| | | private LocMast findRun2EmptyLocByCrnLocTypeEntries(RowLastno rowLastno, RowLastnoType rowLastnoType, |
| | | List<Map<String, Integer>> crnLocTypeEntries, LocTypeDto locTypeDto, |
| | | Integer staDescId, Integer sourceStaNo, StartupDto startupDto, |
| | | Integer preferredArea, String stage) { |
| | | if (Cools.isEmpty(crnLocTypeEntries)) { |
| | | log.warn("run2 skip empty crn-locType list. stage={}, sourceStaNo={}, preferredArea={}, spec={}", |
| | | stage, sourceStaNo, preferredArea, JSON.toJSONString(locTypeDto)); |
| | | return null; |
| | | } |
| | | List<Integer> candidateCrnNos = extractCrnNos(crnLocTypeEntries); |
| | | List<Integer> crnErrorCrns = new ArrayList<>(); |
| | | List<Integer> routeBlockedCrns = new ArrayList<>(); |
| | | List<Integer> noEmptyCrns = new ArrayList<>(); |
| | | List<Integer> locTypeBlockedCrns = new ArrayList<>(); |
| | | return findRun2EmptyLocByCrnLocTypeEntriesRecursively(rowLastno, rowLastnoType, crnLocTypeEntries, locTypeDto, |
| | | staDescId, sourceStaNo, startupDto, preferredArea, stage, 0, candidateCrnNos, |
| | | crnErrorCrns, routeBlockedCrns, noEmptyCrns, locTypeBlockedCrns); |
| | | } |
| | | |
| | | private LocMast findRun2EmptyLocByCrnLocTypeEntriesRecursively(RowLastno rowLastno, RowLastnoType rowLastnoType, |
| | | List<Map<String, Integer>> crnLocTypeEntries, LocTypeDto locTypeDto, |
| | | Integer staDescId, Integer sourceStaNo, StartupDto startupDto, |
| | | Integer preferredArea, String stage, int index, |
| | | List<Integer> candidateCrnNos, List<Integer> crnErrorCrns, |
| | | List<Integer> routeBlockedCrns, List<Integer> noEmptyCrns, |
| | | List<Integer> locTypeBlockedCrns) { |
| | | if (index >= crnLocTypeEntries.size()) { |
| | | logRun2NoMatch(stage, sourceStaNo, preferredArea, candidateCrnNos, locTypeDto, crnErrorCrns, routeBlockedCrns, noEmptyCrns, locTypeBlockedCrns); |
| | | return null; |
| | | } |
| | | Map<String, Integer> crnLocTypeEntry = crnLocTypeEntries.get(index); |
| | | Integer candidateCrnNo = crnLocTypeEntry == null ? null : crnLocTypeEntry.get("crnNo"); |
| | | Short candidateLocType1 = crnLocTypeEntry == null || crnLocTypeEntry.get("locType1") == null |
| | | ? null : crnLocTypeEntry.get("locType1").shortValue(); |
| | | if (candidateCrnNo == null || !basCrnpService.checkSiteError(candidateCrnNo, true)) { |
| | | crnErrorCrns.add(candidateCrnNo); |
| | | return findRun2EmptyLocByCrnLocTypeEntriesRecursively(rowLastno, rowLastnoType, crnLocTypeEntries, locTypeDto, |
| | | staDescId, sourceStaNo, startupDto, preferredArea, stage, index + 1, candidateCrnNos, |
| | | crnErrorCrns, routeBlockedCrns, noEmptyCrns, locTypeBlockedCrns); |
| | | } |
| | | Integer targetStaNo = resolveTargetStaNo(rowLastno, staDescId, sourceStaNo, candidateCrnNo); |
| | | if (Utils.BooleanWhsTypeSta(rowLastno, staDescId) && targetStaNo == null) { |
| | | routeBlockedCrns.add(candidateCrnNo); |
| | | return findRun2EmptyLocByCrnLocTypeEntriesRecursively(rowLastno, rowLastnoType, crnLocTypeEntries, locTypeDto, |
| | | staDescId, sourceStaNo, startupDto, preferredArea, stage, index + 1, candidateCrnNos, |
| | | crnErrorCrns, routeBlockedCrns, noEmptyCrns, locTypeBlockedCrns); |
| | | } |
| | | LocTypeDto searchLocTypeDto = buildRun2SearchLocTypeDto(locTypeDto, candidateLocType1); |
| | | LocMast candidateLoc = findRun2OrderedEmptyLocByCrnLocType(rowLastnoType, candidateCrnNo, candidateLocType1, searchLocTypeDto); |
| | | if (Cools.isEmpty(candidateLoc)) { |
| | | noEmptyCrns.add(candidateCrnNo); |
| | | return findRun2EmptyLocByCrnLocTypeEntriesRecursively(rowLastno, rowLastnoType, crnLocTypeEntries, locTypeDto, |
| | | staDescId, sourceStaNo, startupDto, preferredArea, stage, index + 1, candidateCrnNos, |
| | | crnErrorCrns, routeBlockedCrns, noEmptyCrns, locTypeBlockedCrns); |
| | | } |
| | | if (searchLocTypeDto != null && !VersionUtils.locMoveCheckLocTypeComplete(candidateLoc, searchLocTypeDto)) { |
| | | locTypeBlockedCrns.add(candidateCrnNo); |
| | | return findRun2EmptyLocByCrnLocTypeEntriesRecursively(rowLastno, rowLastnoType, crnLocTypeEntries, locTypeDto, |
| | | staDescId, sourceStaNo, startupDto, preferredArea, stage, index + 1, candidateCrnNos, |
| | | crnErrorCrns, routeBlockedCrns, noEmptyCrns, locTypeBlockedCrns); |
| | | } |
| | | if (targetStaNo != null) { |
| | | startupDto.setStaNo(targetStaNo); |
| | | } |
| | | return candidateLoc; |
| | | } |
| | | |
| | | private List<Integer> extractCrnNos(List<Map<String, Integer>> crnLocTypeEntries) { |
| | | LinkedHashSet<Integer> orderedCrnNos = new LinkedHashSet<>(); |
| | | if (Cools.isEmpty(crnLocTypeEntries)) { |
| | | return new ArrayList<>(); |
| | | } |
| | | for (Map<String, Integer> crnLocTypeEntry : crnLocTypeEntries) { |
| | | if (crnLocTypeEntry == null || crnLocTypeEntry.get("crnNo") == null) { |
| | | continue; |
| | | } |
| | | orderedCrnNos.add(crnLocTypeEntry.get("crnNo")); |
| | | } |
| | | return new ArrayList<>(orderedCrnNos); |
| | | } |
| | | |
| | | private LocTypeDto buildRun2SearchLocTypeDto(LocTypeDto locTypeDto, Short candidateLocType1) { |
| | | if (locTypeDto == null && candidateLocType1 == null) { |
| | | return null; |
| | | } |
| | | LocTypeDto searchLocTypeDto = new LocTypeDto(); |
| | | if (locTypeDto != null) { |
| | | searchLocTypeDto.setLocType1(locTypeDto.getLocType1()); |
| | | searchLocTypeDto.setLocType2(locTypeDto.getLocType2()); |
| | | searchLocTypeDto.setLocType3(locTypeDto.getLocType3()); |
| | | searchLocTypeDto.setSiteId(locTypeDto.getSiteId()); |
| | | } |
| | | if (candidateLocType1 != null) { |
| | | searchLocTypeDto.setLocType1(candidateLocType1); |
| | | } |
| | | return searchLocTypeDto; |
| | | } |
| | | |
| | | private LocMast findRun2OrderedEmptyLocByCrnLocType(RowLastnoType rowLastnoType, Integer candidateCrnNo, |
| | | Short candidateLocType1, LocTypeDto locTypeDto) { |
| | | if (candidateCrnNo == null) { |
| | | return null; |
| | | } |
| | | Wrapper<LocMast> wrapper = new EntityWrapper<LocMast>() |
| | | .eq("crn_no", candidateCrnNo) |
| | | .eq("loc_sts", "O"); |
| | | if (candidateLocType1 != null) { |
| | | wrapper.eq("loc_type1", candidateLocType1); |
| | | } |
| | | // 单伸堆垛机按层、列递增顺序找第一个空库位。 |
| | | if (rowLastnoType != null && rowLastnoType.getType() != null && (rowLastnoType.getType() == 1 || rowLastnoType.getType() == 2)) { |
| | | wrapper.orderBy("lev1", true).orderBy("bay1", true); |
| | | } else { |
| | | wrapper.orderBy("lev1", true).orderBy("bay1", true); |
| | | } |
| | | LocMast candidateLoc = locMastService.selectOne(wrapper); |
| | | if (Cools.isEmpty(candidateLoc)) { |
| | | return null; |
| | | } |
| | | if (locTypeDto != null && !VersionUtils.locMoveCheckLocTypeComplete(candidateLoc, locTypeDto)) { |
| | | return null; |
| | | } |
| | | return candidateLoc; |
| | | } |
| | | |
| | | private Optional<CrnRowInfo> findAvailableCrnAndNearRow(RowLastno rowLastno, int curRow, int crnNumber, int times, |
| | | FindLocNoAttributeVo findLocNoAttributeVo, LocTypeDto locTypeDto, |
| | |
| | | } |
| | | |
| | | public StartupDto getLocNoRun2(Integer whsType, Integer staDescId, Integer sourceStaNo, FindLocNoAttributeVo findLocNoAttributeVo, Integer moveCrnNo, LocTypeDto locTypeDto, int times) { |
| | | return getLocNoRun2(whsType, staDescId, sourceStaNo, findLocNoAttributeVo, moveCrnNo, locTypeDto, null, times); |
| | | } |
| | | |
| | | public StartupDto getLocNoRun2(Integer whsType, Integer staDescId, Integer sourceStaNo, FindLocNoAttributeVo findLocNoAttributeVo, Integer moveCrnNo, LocTypeDto locTypeDto, List<Integer> recommendRows, int times) { |
| | | |
| | | int crnNo = 0; |
| | | int nearRow = 0; |
| | |
| | | if (Cools.isEmpty(rowLastno)) { |
| | | throw new CoolException("数据异常,请联系管理员===>库位规则未知"); |
| | | } |
| | | crnNo = rowLastno.getCurrentRow()/2+1; |
| | | RowLastnoType rowLastnoType = rowLastnoTypeService.selectById(rowLastno.getTypeId()); |
| | | if (Cools.isEmpty(rowLastnoType)) { |
| | | throw new CoolException("数据异常,请联系管理员===》库位规则类型未知"); |
| | | } |
| | | int crnNumber = rowLastno.getCrnQty(); |
| | | rowCount = crnNumber; |
| | | |
| | | curRow = rowLastno.getCurrentRow(); |
| | | crnNo = resolveRun2CrnNo(rowLastno); |
| | | Integer preferredArea = findLocNoAttributeVo.getOutArea(); |
| | | |
| | | Wrapper<StaDesc> wrapper = null; |
| | | StaDesc staDesc = null; |
| | | BasDevp staNo = null; |
| | | |
| | | if (Utils.BooleanWhsTypeSta(rowLastno, staDescId)) { |
| | | wrapper = new EntityWrapper<StaDesc>() |
| | | .eq("type_no", staDescId) |
| | | .eq("stn_no", sourceStaNo) |
| | | .eq("crn_no", crnNo); |
| | | staDesc = staDescService.selectOne(wrapper); |
| | | if (Cools.isEmpty(staDesc)) { |
| | | log.error("type_no={},stn_no={},crn_no={}", staDescId, sourceStaNo, crnNo); |
| | | crnNo = 0; |
| | | List<Integer> orderedCrnNos = getOrderedCrnNos(rowLastno, crnNo); |
| | | List<Integer> triedCrnNos = new ArrayList<>(); |
| | | List<Integer> recommendCrnNos = mapRowsToCrnNos(rowLastno, recommendRows); |
| | | if (!Cools.isEmpty(recommendCrnNos)) { |
| | | locMast = findRun2EmptyLocByCrnNos(rowLastno, rowLastnoType, recommendCrnNos, locTypeDto, staDescId, sourceStaNo, startupDto, preferredArea, "recommend"); |
| | | triedCrnNos.addAll(recommendCrnNos); |
| | | } |
| | | if (Cools.isEmpty(locMast)) { |
| | | List<Map<String, Integer>> stationCrnLocTypes = Utils.getStationStorageAreaName( |
| | | sourceStaNo, |
| | | locTypeDto == null || locTypeDto.getLocType1() == null ? null : locTypeDto.getLocType1().intValue(), |
| | | findLocNoAttributeVo == null ? null : findLocNoAttributeVo.getMatnr()); |
| | | if (!Cools.isEmpty(stationCrnLocTypes)) { |
| | | locMast = findRun2EmptyLocByCrnLocTypeEntries(rowLastno, rowLastnoType, stationCrnLocTypes, |
| | | locTypeDto, staDescId, sourceStaNo, startupDto, preferredArea, "station-priority"); |
| | | } else if (preferredArea == null) { |
| | | List<Integer> defaultCrnNos = new ArrayList<>(orderedCrnNos); |
| | | defaultCrnNos.removeAll(triedCrnNos); |
| | | locMast = findRun2EmptyLocByCrnNos(rowLastno, rowLastnoType, defaultCrnNos, locTypeDto, staDescId, sourceStaNo, startupDto, preferredArea, "default"); |
| | | } else { |
| | | staNo = basDevpService.selectById(staDesc.getCrnStn()); |
| | | if (!staNo.getAutoing().equals("Y")) { |
| | | log.error("目标站{}不可用", staDesc.getCrnStn()); |
| | | crnNo = 0; |
| | | List<Integer> preferredCrnNos = filterCrnNosByRows(rowLastno, orderedCrnNos, getRun2AreaRows(preferredArea, rowLastno)); |
| | | preferredCrnNos.removeAll(triedCrnNos); |
| | | locMast = findRun2EmptyLocByCrnNos(rowLastno, rowLastnoType, preferredCrnNos, locTypeDto, staDescId, sourceStaNo, startupDto, preferredArea, "preferred-area"); |
| | | if (Cools.isEmpty(locMast)) { |
| | | List<Integer> fallbackCrnNos = filterCrnNosByRows(rowLastno, orderedCrnNos, getRun2FallbackRows(rowLastno)); |
| | | fallbackCrnNos.removeAll(triedCrnNos); |
| | | fallbackCrnNos.removeAll(preferredCrnNos); |
| | | locMast = findRun2EmptyLocByCrnNos(rowLastno, rowLastnoType, fallbackCrnNos, locTypeDto, staDescId, sourceStaNo, startupDto, preferredArea, "fallback-area"); |
| | | } |
| | | startupDto.setStaNo(staNo.getDevNo()); |
| | | } |
| | | } |
| | | |
| | | LocMast locMast1 = locMastService.selectOne(new EntityWrapper<LocMast>() |
| | | .eq("crn_no", crnNo) |
| | | .eq("loc_sts", "O") |
| | | .orderBy("lev1") |
| | | .orderBy("bay1") |
| | | .eq("loc_type1",locTypeDto.getLocType1())); |
| | | if (!Cools.isEmpty(locMast1)) { |
| | | locMast=locMast1; |
| | | if (!Cools.isEmpty(locMast)) { |
| | | crnNo = locMast.getCrnNo(); |
| | | nearRow = locMast.getRow1(); |
| | | } |
| | | if (curRow==rowLastno.geteRow()-1) { |
| | | curRow = 1; |
| | | }else{ |
| | | curRow = curRow + 2; |
| | | if (curRow == 0) { |
| | | curRow = rowLastno.getsRow() == null ? 1 : rowLastno.getsRow(); |
| | | } |
| | | curRow = getNextRun2CurrentRow(rowLastno, curRow); |
| | | rowLastno.setCurrentRow(curRow); |
| | | rowLastnoService.updateById(rowLastno); |
| | | |
| | | if (!Cools.isEmpty(locMast) && !basCrnpService.checkSiteError(crnNo, true)) { |
| | | locMast = null; |
| | | } |
| | | |
| | | if (Cools.isEmpty(locMast) || !locMast.getLocSts().equals("O")) { |
| | | if (times < rowCount * 2) { |
| | | times = times + 1; |
| | | return getLocNoRun2(whsType, staDescId, sourceStaNo, findLocNoAttributeVo, moveCrnNo, locTypeDto, times); |
| | | return getLocNoRun2(whsType, staDescId, sourceStaNo, findLocNoAttributeVo, moveCrnNo, locTypeDto, recommendRows, times); |
| | | } |
| | | LocTypeDto compatibleLocTypeDto = buildUpwardCompatibleLocTypeDto(locTypeDto); |
| | | if (compatibleLocTypeDto != null) { |
| | | log.warn("locType1 upward compatibility retry. source={}, target={}", JSON.toJSONString(locTypeDto), JSON.toJSONString(compatibleLocTypeDto)); |
| | | return getLocNoRun2(whsType, staDescId, sourceStaNo, findLocNoAttributeVo, moveCrnNo, compatibleLocTypeDto, 0); |
| | | return getLocNoRun2(whsType, staDescId, sourceStaNo, findLocNoAttributeVo, moveCrnNo, compatibleLocTypeDto, recommendRows, 0); |
| | | } |
| | | log.error("No empty location found. spec={}, times={}", JSON.toJSONString(locTypeDto), times); |
| | | throw new CoolException("\u6ca1\u6709\u7a7a\u5e93\u4f4d"); |
| | | log.error("No empty location found. spec={}, times={}, preferredArea={}, nearRow={}", JSON.toJSONString(locTypeDto), times, preferredArea, nearRow); |
| | | throw new CoolException("没有空库位"); |
| | | } |
| | | |
| | | int workNo = getWorkNo(0); |
| | |
| | | startupDto.setLocNo(locMast.getLocNo()); |
| | | return startupDto; |
| | | } |
| | | |
| | | private LocMast findSingleExtensionEmptyLoc(RowLastno rowLastno, int crnNo, int nearRow, RowLastnoType rowLastnoType, LocTypeDto locTypeDto) { |
| | | for (Integer searchRow : getCrnSearchRows(rowLastno, crnNo, nearRow)) { |
| | | List<LocMast> locMasts = locMastService.selectList(new EntityWrapper<LocMast>() |
| | |
| | | } |
| | | } |
| | | |
| | | if (Cools.isEmpty(locMast) && sourceStaNo != 4006) {//si'lou'p四楼盘点选择区域 |
| | | Integer preferredArea = findLocNoAttributeVo.getOutArea(); |
| | | |
| | | if (Cools.isEmpty(locMast) && preferredArea == null) { |
| | | List<LocMast> locMasts = locMastService.selectList(new EntityWrapper<LocMast>() |
| | | .eq("row1", nearRow) |
| | | .eq("loc_sts", "O").eq("whs_type", rowLastnoType.getType().longValue()) |
| | |
| | | } |
| | | } |
| | | } |
| | | } else { |
| | | // 根据 findLocNoAttributeVo.getOutArea() 设置列范围 |
| | | int startBay = 1; |
| | | int endBay = 19; |
| | | |
| | | switch (findLocNoAttributeVo.getOutArea()) { |
| | | case 3: |
| | | startBay = 15; |
| | | endBay = 19; |
| | | break; |
| | | case 2: |
| | | startBay = 8; |
| | | endBay = 14; |
| | | break; |
| | | case 1: |
| | | startBay = 1; |
| | | endBay = 8; |
| | | break; |
| | | default: |
| | | break; |
| | | } else if (Cools.isEmpty(locMast)) { |
| | | int[] bayRange = getAgvAreaBayRange(preferredArea); |
| | | locMast = findAgvLocByRows(rowLastno, rowLastnoType, getAgvAreaRows(preferredArea, rowLastno), |
| | | bayRange[0], bayRange[1], curRow, nearRow, locTypeDto, false); |
| | | if (!Cools.isEmpty(locMast)) { |
| | | crnNo = locMast.getCrnNo(); |
| | | } |
| | | |
| | | // 优先从指定列范围查找 |
| | | boolean found = false; |
| | | |
| | | // 按照排号从38到32递减查找,优先查找指定列范围(如1-8、8-14、15-19) |
| | | for (int row = 38; row >= 32; row--) { |
| | | List<LocMast> locMasts = locMastService.selectList(new EntityWrapper<LocMast>() |
| | | .eq("row1", row) |
| | | .ge("bay1", startBay) |
| | | .le("bay1", endBay) |
| | | .eq("loc_sts", "O") |
| | | .eq("whs_type", rowLastnoType.getType().longValue()) |
| | | .orderBy("lev1", true) |
| | | .orderBy("bay1", true)); // 最浅库位 |
| | | |
| | | for (LocMast locMast1 : locMasts) { |
| | | if (!VersionUtils.locMoveCheckLocTypeComplete(locMast1, locTypeDto)) { |
| | | continue; |
| | | } |
| | | if(locMast1!= null){ |
| | | locMast = locMast1; |
| | | found = true; |
| | | break; |
| | | } |
| | | // if (Utils.BooleanWhsTypeStaIoType(rowLastno)) { |
| | | // // 获取目标库位所在巷道最深空库位 |
| | | // LocMast locMast2 = locMastService.selectLocByLocStsPakInO(curRow, nearRow, locMast1, rowLastnoType.getType().longValue()); |
| | | // if (!Cools.isEmpty(locMast2) && locMast2.getRow1() == curRow) { |
| | | // locMast = locMast2; |
| | | // found = true; |
| | | // break; |
| | | // } |
| | | // } |
| | | } |
| | | |
| | | if (found) { |
| | | break; // 找到目标库位后跳出循环 |
| | | } |
| | | } |
| | | |
| | | // 如果没有在优先范围内找到合适库位,继续进行全局查找(1-19列) |
| | | if (!found) { |
| | | // 从排号38到32查找所有列(1-19) |
| | | for (int row = 38; row >= 32; row--) { |
| | | List<LocMast> locMasts = locMastService.selectList(new EntityWrapper<LocMast>() |
| | | .eq("row1", row) |
| | | .ge("bay1", 1) // 查找1到19列 |
| | | .le("bay1", 19) |
| | | .eq("loc_sts", "O") |
| | | .eq("whs_type", rowLastnoType.getType().longValue()) |
| | | .orderBy("lev1", true) |
| | | .orderBy("bay1", true)); // 最浅库位 |
| | | |
| | | for (LocMast locMast1 : locMasts) { |
| | | if (!VersionUtils.locMoveCheckLocTypeComplete(locMast1, locTypeDto)) { |
| | | continue; |
| | | } |
| | | if (Utils.BooleanWhsTypeStaIoType(rowLastno)) { |
| | | // ??????????????? |
| | | LocMast locMast2 = locMastService.selectLocByLocStsPakInO(curRow, nearRow, locMast1, rowLastnoType.getType().longValue()); |
| | | if (!Cools.isEmpty(locMast2) && locMast2.getRow1() == curRow) { |
| | | locMast = locMast2; |
| | | found = true; |
| | | break; |
| | | if (Cools.isEmpty(locMast)) { |
| | | locMast = findAgvLocByRows(rowLastno, rowLastnoType, getAgvFallbackRows(rowLastno), |
| | | 1, 19, curRow, nearRow, locTypeDto, true); |
| | | if (!Cools.isEmpty(locMast)) { |
| | | crnNo = locMast.getCrnNo(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | if (found) { |
| | | break; // ??????????? |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | // Retry search |
| | | if (Cools.isEmpty(locMast) || !locMast.getLocSts().equals("O")) { |
| | | // Scan next aisle first, then retry with upward-compatible locType1. |
| | |
| | | } |
| | | |
| | | } |
| | | |