package com.zy.common.service; import com.alibaba.fastjson.JSON; import com.baomidou.mybatisplus.mapper.EntityWrapper; import com.baomidou.mybatisplus.mapper.Wrapper; import com.core.common.Arith; import com.core.common.Cools; import com.core.exception.CoolException; import com.zy.asrs.entity.*; import com.zy.asrs.entity.result.FindLocNoAttributeVo; import com.zy.asrs.entity.result.KeyValueVo; 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 com.zy.common.properties.SlaveProperties; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.time.LocalDate; import java.time.format.DateTimeFormatter; 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; /** * 货架核心功能 * Created by vincent on 2020/6/11 */ @Slf4j @Service public class CommonService { private static final int MIN_SPARE_LOC_COUNT = 2; @Autowired private WrkMastService wrkMastService; @Autowired private WrkLastnoService wrkLastnoService; @Autowired private RowLastnoService rowLastnoService; @Autowired private RowLastnoTypeService rowLastnoTypeService; @Autowired private BasCrnpService basCrnpService; @Autowired private StaDescService staDescService; @Autowired private BasDevpService basDevpService; @Autowired private LocMastService locMastService; @Autowired private LocDetlService locDetlService; @Autowired private SlaveProperties slaveProperties; @Autowired private WrkDetlService wrkDetlService; /** * 生成工作号 * * @param wrkMk * @return workNo(工作号) */ public int getWorkNo(Integer wrkMk) { WrkLastno wrkLastno = wrkLastnoService.selectById(wrkMk); if (Cools.isEmpty(wrkLastno)) { throw new CoolException("数据异常,请联系管理员"); } int workNo = wrkLastno.getWrkNo(); int sNo = wrkLastno.getSNo(); int eNo = wrkLastno.getENo(); workNo = workNo >= eNo ? sNo : workNo + 1; while (true) { WrkMast wrkMast = wrkMastService.selectById(workNo); if (null != wrkMast) { workNo = workNo >= eNo ? sNo : workNo + 1; } else { break; } } // 修改序号记录 if (workNo > 0) { wrkLastno.setWrkNo(workNo); wrkLastnoService.updateById(wrkLastno); } // 检验 if (workNo == 0) { throw new CoolException("生成工作号失败,请联系管理员"); } else { if (wrkMastService.selectById(workNo) != null) { throw new CoolException("生成工作号" + workNo + "在工作档中已存在"); } } return workNo; } //拆盘机处空板扫码,驱动托盘向码垛位,不入库 @Transactional public StartupDto getScanBarcodeEmptyBoard() { StartupDto startupDto = new StartupDto(); Integer staNo = 0; if (wrkMastService.selectCount(new EntityWrapper().eq("io_type", 201).eq("staNo", 216)) < 2) { staNo = 216; } if (wrkMastService.selectCount(new EntityWrapper().eq("io_type", 201).eq("staNo", 220)) < 2) { staNo = 220; } startupDto.setStaNo(staNo); return startupDto; } /** * 检索库位号 * * @param staDescId 路径ID * @param sourceStaNo 源站 * @param findLocNoAttributeVo 属性 * @param locTypeDto 类型 * @return locNo 检索到的库位号 */ @Transactional public StartupDto getLocNo(Integer staDescId, Integer sourceStaNo, FindLocNoAttributeVo findLocNoAttributeVo, LocTypeDto locTypeDto) { return getLocNo(staDescId, sourceStaNo, findLocNoAttributeVo, locTypeDto, null); } @Transactional public StartupDto getLocNo(Integer staDescId, Integer sourceStaNo, FindLocNoAttributeVo findLocNoAttributeVo, LocTypeDto locTypeDto, List recommendRows) { try { Integer whsType = Utils.GetWhsType(sourceStaNo); RowLastno rowLastno = rowLastnoService.selectById(whsType); RowLastnoType rowLastnoType = rowLastnoTypeService.selectById(rowLastno.getTypeId()); Integer preferredArea = resolvePreferredArea(sourceStaNo, findLocNoAttributeVo); if (preferredArea != null) { findLocNoAttributeVo.setOutArea(preferredArea); } /** * 库型 1: 标准堆垛机库 2: 平库 3: 穿梭板 4: 四向车 5: AGV 0: 未知 */ switch (rowLastnoType.getType()) { case 1: case 2: return getLocNoRun2(whsType, staDescId, sourceStaNo, findLocNoAttributeVo, 0, locTypeDto, recommendRows, 0); case 3: 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: 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 + " 查找库位失败"); } } 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 getAgvAreaRows(Integer area, RowLastno rowLastno) { List configuredRows = parseAgvRows(getAgvAreaRowsConfig(area), rowLastno); if (!configuredRows.isEmpty()) { return configuredRows; } return getLegacyAgvRows(rowLastno); } private List getAgvFallbackRows(RowLastno rowLastno) { LinkedHashSet 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 parseAgvRows(String configValue, RowLastno rowLastno) { List rows = new ArrayList<>(); if (rowLastno == null || Cools.isEmpty(configValue)) { return rows; } LinkedHashSet 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 getLegacyAgvRows(RowLastno rowLastno) { List rows = new ArrayList<>(); if (rowLastno == null) { return rows; } LinkedHashSet 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 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 rows, int startBay, int endBay, int curRow, int nearRow, LocTypeDto locTypeDto, boolean useDeepCheck) { for (Integer row : rows) { if (row == null) { continue; } List locMasts = locMastService.selectList(new EntityWrapper() .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 getRun2AreaRows(Integer area, RowLastno rowLastno) { List configuredRows = parseAgvRows(getRun2AreaRowsConfig(area), rowLastno); if (!configuredRows.isEmpty()) { return configuredRows; } return getLegacyAgvRows(rowLastno); } private List getRun2FallbackRows(RowLastno rowLastno) { LinkedHashSet 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 getOrderedCrnNos(RowLastno rowLastno, Integer startCrnNo) { List 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 filterCrnNosByRows(RowLastno rowLastno, List orderedCrnNos, List rows) { if (Cools.isEmpty(rows)) { return new ArrayList<>(orderedCrnNos); } LinkedHashSet rowSet = new LinkedHashSet<>(rows); List 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 mapRowsToCrnNos(RowLastno rowLastno, List rows) { List result = new ArrayList<>(); if (rowLastno == null || Cools.isEmpty(rows)) { return result; } LinkedHashSet 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() .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 candidateCrnNos, LocTypeDto locTypeDto, List crnErrorCrns, List routeBlockedCrns, List noEmptyCrns, List 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 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 crnErrorCrns = new ArrayList<>(); List routeBlockedCrns = new ArrayList<>(); List noEmptyCrns = new ArrayList<>(); List 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 openWrapper = new EntityWrapper() .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 wrapper = new EntityWrapper() .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> 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 candidateCrnNos = extractCrnNos(crnLocTypeEntries); List crnErrorCrns = new ArrayList<>(); List routeBlockedCrns = new ArrayList<>(); List noEmptyCrns = new ArrayList<>(); List 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> crnLocTypeEntries, LocTypeDto locTypeDto, Integer staDescId, Integer sourceStaNo, StartupDto startupDto, Integer preferredArea, String stage, int index, List candidateCrnNos, List crnErrorCrns, List routeBlockedCrns, List noEmptyCrns, List locTypeBlockedCrns) { if (index >= crnLocTypeEntries.size()) { logRun2NoMatch(stage, sourceStaNo, preferredArea, candidateCrnNos, locTypeDto, crnErrorCrns, routeBlockedCrns, noEmptyCrns, locTypeBlockedCrns); return null; } Map 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 extractCrnNos(List> crnLocTypeEntries) { LinkedHashSet orderedCrnNos = new LinkedHashSet<>(); if (Cools.isEmpty(crnLocTypeEntries)) { return new ArrayList<>(); } for (Map 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 wrapper = new EntityWrapper() .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 findAvailableCrnAndNearRow(RowLastno rowLastno, int curRow, int crnNumber, int times, FindLocNoAttributeVo findLocNoAttributeVo, LocTypeDto locTypeDto, RowLastnoType rowLastnoType) { int attempt = times; while (attempt < crnNumber * 4) { int[] params = Utils.LocNecessaryParameters(rowLastno, curRow, crnNumber); curRow = params[1]; int crnNo = params[2]; // if (!basCrnpService.checkSiteError(crnNo, true)) { // attempt++; // continue; // } int rowCount = params[0]; int nearRow = params[3]; // 只取数量判断,避免拉整 list int availableLocCount = locMastService.selectCount(new EntityWrapper() .eq("row1", nearRow) .eq("loc_sts", "O") .eq("whs_type", 1)); int crnCountO = wrkMastService.selectCount(new EntityWrapper() .eq("crn_no", crnNo).le("io_type", 100)); if (availableLocCount - crnCountO <= 2) { // 可以提成常量,比如 MIN_SPARE_SLOTS = 2 log.error("{}号堆垛机没有空库位!!! 尺寸规格: {}, 轮询次数:{}", crnNo, JSON.toJSONString(locTypeDto), attempt); attempt++; continue; } return Optional.of(new CrnRowInfo(crnNo, nearRow, curRow, rowCount, attempt)); } return Optional.empty(); } private Optional findBalancedCrnAndNearRow(RowLastno rowLastno, int curRow, int crnNumber, int times, FindLocNoAttributeVo findLocNoAttributeVo, LocTypeDto locTypeDto, RowLastnoType rowLastnoType) { int maxScanTimes = Math.max(crnNumber * 2, 1); int scanCurRow = curRow; Map availableLocCountCache = new HashMap<>(); Map crnTaskCountCache = new HashMap<>(); CrnRowInfo bestInfo = null; int bestTaskCount = Integer.MAX_VALUE; int bestSpareLocCount = Integer.MIN_VALUE; int bestOffset = Integer.MAX_VALUE; CrnRowInfo fallbackInfo = null; int fallbackTaskCount = Integer.MAX_VALUE; int fallbackAvailableLocCount = Integer.MIN_VALUE; int fallbackOffset = Integer.MAX_VALUE; for (int attempt = 0; attempt < maxScanTimes; attempt++) { int[] params = Utils.LocNecessaryParameters(rowLastno, scanCurRow, crnNumber); scanCurRow = params[1]; int rowCount = params[0]; int crnNo = params[2]; int nearRow = params[3]; if (attempt < times) { continue; } int availableLocCount = availableLocCountCache.computeIfAbsent(crnNo, key -> countAvailableLocForCrn(rowLastno, rowLastnoType, key, nearRow)); int crnTaskCount = crnTaskCountCache.computeIfAbsent(crnNo, key -> wrkMastService.selectCount(new EntityWrapper() .eq("crn_no", key) .le("io_type", 100))); int spareLocCount = availableLocCount - crnTaskCount; int offset = attempt - times; if (availableLocCount > 0 && isBetterCrnCandidate(crnTaskCount, availableLocCount, offset, fallbackTaskCount, fallbackAvailableLocCount, fallbackOffset)) { fallbackInfo = new CrnRowInfo(crnNo, nearRow, scanCurRow, rowCount, attempt); fallbackTaskCount = crnTaskCount; fallbackAvailableLocCount = availableLocCount; fallbackOffset = offset; } if (spareLocCount <= MIN_SPARE_LOC_COUNT) { log.warn("{}号堆垛机可用空库位余量不足,降级候选继续保留。尺寸规格:{},轮询次数:{}", crnNo, JSON.toJSONString(locTypeDto), attempt); continue; } if (isBetterCrnCandidate(crnTaskCount, spareLocCount, offset, bestTaskCount, bestSpareLocCount, bestOffset)) { bestInfo = new CrnRowInfo(crnNo, nearRow, scanCurRow, rowCount, attempt); bestTaskCount = crnTaskCount; bestSpareLocCount = spareLocCount; bestOffset = offset; } } if (bestInfo != null) { return Optional.of(bestInfo); } if (fallbackInfo != null) { log.warn("堆垛机均衡分配未找到满足余量阈值的候选,降级使用仍有空位的堆垛机: crnNo={}", fallbackInfo.getCrnNo()); } return Optional.ofNullable(fallbackInfo); } private boolean isBetterCrnCandidate(int taskCount, int spareLocCount, int offset, int bestTaskCount, int bestSpareLocCount, int bestOffset) { if (taskCount != bestTaskCount) { return taskCount < bestTaskCount; } if (spareLocCount != bestSpareLocCount) { return spareLocCount > bestSpareLocCount; } return offset < bestOffset; } private int countAvailableLocForCrn(RowLastno rowLastno, RowLastnoType rowLastnoType, int crnNo, int preferredNearRow) { List searchRows = getCrnSearchRows(rowLastno, crnNo, preferredNearRow); if (searchRows.isEmpty()) { return 0; } return locMastService.selectCount(new EntityWrapper() .in("row1", searchRows) .eq("loc_sts", "O") .eq("whs_type", rowLastnoType.getType().longValue())); } private int countAvailableSingleExtensionLocForCrn(RowLastno rowLastno, RowLastnoType rowLastnoType, int crnNo, int preferredNearRow, LocTypeDto locTypeDto) { List searchRows = getCrnSearchRows(rowLastno, crnNo, preferredNearRow); if (searchRows.isEmpty()) { return 0; } Wrapper wrapper = new EntityWrapper() .in("row1", searchRows) .eq("loc_sts", "O") .eq("whs_type", rowLastnoType.getType().longValue()); if (locTypeDto != null && locTypeDto.getLocType1() != null) { wrapper.eq("loc_type1", locTypeDto.getLocType1()); } return locMastService.selectCount(wrapper); } private Optional findBalancedSingleExtensionCrnAndNearRow(RowLastno rowLastno, int curRow, int crnNumber, int times, LocTypeDto locTypeDto, RowLastnoType rowLastnoType) { int maxScanTimes = Math.max(crnNumber * 2, 1); int scanCurRow = curRow; Map availableLocCountCache = new HashMap<>(); Map crnTaskCountCache = new HashMap<>(); CrnRowInfo bestInfo = null; int bestTaskCount = Integer.MAX_VALUE; int bestSpareLocCount = Integer.MIN_VALUE; int bestOffset = Integer.MAX_VALUE; CrnRowInfo fallbackInfo = null; int fallbackTaskCount = Integer.MAX_VALUE; int fallbackAvailableLocCount = Integer.MIN_VALUE; int fallbackOffset = Integer.MAX_VALUE; for (int attempt = 0; attempt < maxScanTimes; attempt++) { int[] params = Utils.LocNecessaryParameters(rowLastno, scanCurRow, crnNumber); scanCurRow = params[1]; int rowCount = params[0]; int crnNo = params[2]; int nearRow = params[3]; if (attempt < times) { continue; } int availableLocCount = availableLocCountCache.computeIfAbsent(crnNo, key -> countAvailableSingleExtensionLocForCrn(rowLastno, rowLastnoType, key, nearRow, locTypeDto)); int crnTaskCount = crnTaskCountCache.computeIfAbsent(crnNo, key -> wrkMastService.selectCount(new EntityWrapper() .eq("crn_no", key) .le("io_type", 100))); int spareLocCount = availableLocCount - crnTaskCount; int offset = attempt - times; if (availableLocCount > 0 && isBetterCrnCandidate(crnTaskCount, availableLocCount, offset, fallbackTaskCount, fallbackAvailableLocCount, fallbackOffset)) { fallbackInfo = new CrnRowInfo(crnNo, nearRow, scanCurRow, rowCount, attempt); fallbackTaskCount = crnTaskCount; fallbackAvailableLocCount = availableLocCount; fallbackOffset = offset; } if (spareLocCount <= MIN_SPARE_LOC_COUNT) { continue; } if (isBetterCrnCandidate(crnTaskCount, spareLocCount, offset, bestTaskCount, bestSpareLocCount, bestOffset)) { bestInfo = new CrnRowInfo(crnNo, nearRow, scanCurRow, rowCount, attempt); bestTaskCount = crnTaskCount; bestSpareLocCount = spareLocCount; bestOffset = offset; } } if (bestInfo != null) { return Optional.of(bestInfo); } return Optional.ofNullable(fallbackInfo); } private List getCrnSearchRows(RowLastno rowLastno, int crnNo, int preferredNearRow) { List searchRows = new ArrayList<>(); addSearchRow(searchRows, preferredNearRow, rowLastno); Integer rowSpan = getCrnRowSpan(rowLastno.getTypeId()); if (rowSpan == null) { return searchRows; } int crnOffset = crnNo - rowLastno.getsCrnNo(); if (crnOffset < 0) { return searchRows; } int startRow = rowLastno.getsRow() + crnOffset * rowSpan; switch (rowLastno.getTypeId()) { case 1: addSearchRow(searchRows, startRow + 1, rowLastno); addSearchRow(searchRows, startRow + 2, rowLastno); break; case 2: addSearchRow(searchRows, startRow, rowLastno); addSearchRow(searchRows, startRow + 1, rowLastno); break; default: break; } return searchRows; } private Integer getCrnRowSpan(Integer typeId) { if (typeId == null) { return null; } switch (typeId) { case 1: return 4; case 2: return 2; default: return null; } } private void addSearchRow(List searchRows, Integer row, RowLastno rowLastno) { if (row == null) { return; } if (row < rowLastno.getsRow() || row > rowLastno.geteRow()) { return; } if (!searchRows.contains(row)) { searchRows.add(row); } } private LocMast findStandardEmptyLoc(RowLastno rowLastno, RowLastnoType rowLastnoType, int crnNo, int nearRow, LocTypeDto locTypeDto) { for (Integer searchRow : getCrnSearchRows(rowLastno, crnNo, nearRow)) { List locMasts = locMastService.selectList(new EntityWrapper() .eq("row1", searchRow) .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)) { String shallowLoc = Utils.getDeepLoc(slaveProperties, locMast1.getLocNo()); LocMast locMast2 = locMastService.selectOne(new EntityWrapper() .eq("loc_no", shallowLoc) .eq("loc_sts", "O") .eq("whs_type", rowLastnoType.getType().longValue())); if (!Cools.isEmpty(locMast2)) { return locMast2; } } else if (!Cools.isEmpty(locMast1)) { return locMast1; } } if (!Utils.BooleanWhsTypeStaIoType(rowLastno)) { continue; } for (LocMast locMast1 : locMasts) { if (!VersionUtils.locMoveCheckLocTypeComplete(locMast1, locTypeDto)) { continue; } String shallowLoc = Utils.getDeepLoc(slaveProperties, locMast1.getLocNo()); LocMast locMast2 = locMastService.selectOne(new EntityWrapper() .eq("loc_no", shallowLoc) .eq("loc_sts", "O") .eq("whs_type", rowLastnoType.getType().longValue())); if (!Cools.isEmpty(locMast2)) { return locMast2; } locMast2 = locMastService.selectOne(new EntityWrapper() .eq("loc_no", shallowLoc) .eq("loc_sts", "F") .eq("whs_type", rowLastnoType.getType().longValue())); if (!Cools.isEmpty(locMast2)) { return locMast1; } locMast2 = locMastService.selectOne(new EntityWrapper() .eq("loc_no", shallowLoc) .eq("loc_sts", "D") .eq("whs_type", rowLastnoType.getType().longValue())); if (!Cools.isEmpty(locMast2)) { return locMast1; } } } return null; } private LocTypeDto buildUpwardCompatibleLocTypeDto(LocTypeDto locTypeDto) { if (locTypeDto == null || locTypeDto.getLocType1() == null || locTypeDto.getLocType1() >= 2) { return null; } LocTypeDto compatibleLocTypeDto = new LocTypeDto(); compatibleLocTypeDto.setLocType1((short) (locTypeDto.getLocType1() + 1)); compatibleLocTypeDto.setLocType2(locTypeDto.getLocType2()); compatibleLocTypeDto.setLocType3(locTypeDto.getLocType3()); compatibleLocTypeDto.setSiteId(locTypeDto.getSiteId()); return compatibleLocTypeDto; } /** * 检索库位号 * * @param whsType 类型 1:双深式货架 * @param staDescId 路径ID * @param sourceStaNo 源站 * @param findLocNoAttributeVo 属性 * @param moveCrnNo 源 * @param locTypeDto 类型 * @param times 轮询次数 * @return locNo 检索到的库位号 */ @Transactional public StartupDto getLocNoRun(Integer whsType, Integer staDescId, Integer sourceStaNo, FindLocNoAttributeVo findLocNoAttributeVo, Integer moveCrnNo, LocTypeDto locTypeDto, int times) { // 初始化参数 int crnNo = 0; //堆垛机号 int nearRow = 0; //最浅库位排 int curRow = 0; //最深库位排 int rowCount = 0; //轮询轮次 LocMast locMast = null; // 目标库位 StartupDto startupDto = new StartupDto(); RowLastno rowLastno = rowLastnoService.selectById(whsType); if (Cools.isEmpty(rowLastno)) { throw new CoolException("数据异常,请联系管理员===>库位规则未知"); } RowLastnoType rowLastnoType = rowLastnoTypeService.selectById(rowLastno.getTypeId()); if (Cools.isEmpty(rowLastnoType)) { throw new CoolException("数据异常,请联系管理员===》库位规则类型未知"); } int sRow = rowLastno.getsRow(); int eRow = rowLastno.geteRow(); int crnNumber = rowLastno.getCrnQty(); // ===============>>>> 开始执行 curRow = rowLastno.getCurrentRow(); if (!Cools.isEmpty(moveCrnNo) && moveCrnNo != 0) { crnNumber = moveCrnNo; if (times == 0) { curRow = moveCrnNo * 4 - 1; } else { curRow = moveCrnNo * 4 - 2; } } //此程序用于优化堆垛机异常时的运行时间 Optional infoOpt = findBalancedCrnAndNearRow(rowLastno, curRow, crnNumber, times, findLocNoAttributeVo, locTypeDto, rowLastnoType); if (!infoOpt.isPresent()) { infoOpt = findAvailableCrnAndNearRow(rowLastno, curRow, crnNumber, times, findLocNoAttributeVo, locTypeDto, rowLastnoType); } if (!infoOpt.isPresent()) { throw new CoolException("无可用堆垛机"); } CrnRowInfo info = infoOpt.get(); crnNo = info.getCrnNo(); nearRow = info.getNearRow(); curRow = info.getCurRow(); rowCount = info.getRowCount(); times = info.getTimes(); boolean signRule1 = false; boolean signRule2 = false; if (Utils.BooleanWhsTypeStaIoType(rowLastno)) { // 靠近摆放规则 --- 同天同规格物料 //分离版 if (!Cools.isEmpty(findLocNoAttributeVo.getMatnr()) && staDescId == 1) { signRule1 = true; } // 靠近摆放规则 --- 同天同规格物料 //互通版 if (!Cools.isEmpty(findLocNoAttributeVo.getMatnr()) && staDescId == 1) { signRule2 = true; } if (!Cools.isEmpty(findLocNoAttributeVo.getMatnr()) && (staDescId == 11 || staDescId == 111)) { signRule1 = true; } } if (signRule1) { if (nearRow != curRow) { List locMasts = locMastService.selectList(new EntityWrapper() .eq("row1", nearRow).eq("loc_sts", "O").eq("whs_type", rowLastnoType.getType().longValue())); for (LocMast locMast1 : locMasts) { //获取巷道 // List groupOutsideLocCrn = Utils.getGroupOutLocCrn(curRow,nearRow,locMast1.getLocNo(), curRow>nearRow); // LocMast locMastGro = locMastService.selectById(wrkMast.getLocNo()); //获取目标库位所在巷道最浅非空库位 LocMast locMastF = locMastService.selectLocByLocStsPakInF(curRow, nearRow, locMast1, rowLastnoType.getType().longValue()); if (!Cools.isEmpty(locMastF) && locMastF.getLocSts().equals("F")) { LocDetl locDetl = locDetlService.selectOne(new EntityWrapper().eq("loc_no", locMastF.getLocNo())); if (!Cools.isEmpty(locDetl) && findLocNoAttributeVo.beSimilar(locDetl)) { //获取目标库位所在巷道最深空库位 locMast = locMastService.selectLocByLocStsPakInO(curRow, nearRow, locMast1, rowLastnoType.getType().longValue()); break; } } } } } else if (signRule2) { List locNos = locDetlService.getSameDetlToday(findLocNoAttributeVo.getMatnr(), sRow, eRow); for (String locNo : locNos) { if (Utils.isShallowLoc(slaveProperties, locNo)) { continue; } String shallowLocNo = Utils.getShallowLoc(slaveProperties, locNo); // 检测目标库位是否为空库位 LocMast shallowLoc = locMastService.selectById(shallowLocNo); if (shallowLoc != null && shallowLoc.getLocSts().equals("O")) { if (VersionUtils.locMoveCheckLocTypeComplete(shallowLoc, locTypeDto)) { if (basCrnpService.checkSiteError(shallowLoc.getCrnNo(), true)) { locMast = shallowLoc; crnNo = locMast.getCrnNo(); break; } } } } } // 靠近摆放规则 --- 空托 //互通版 if (staDescId == 10 && Utils.BooleanWhsTypeStaIoType(rowLastno)) { List locMasts = locMastService.selectList(new EntityWrapper() .eq("loc_sts", "D").ge("row1", sRow).le("row1", eRow).eq("whs_type", rowLastnoType.getType().longValue())); if (!locMasts.isEmpty()) { for (LocMast loc : locMasts) { if (Utils.isShallowLoc(slaveProperties, loc.getLocNo())) { continue; } String shallowLocNo = Utils.getShallowLoc(slaveProperties, loc.getLocNo()); // 检测目标库位是否为空库位 LocMast shallowLoc = locMastService.selectById(shallowLocNo); if (shallowLoc != null && shallowLoc.getLocSts().equals("O")) { if (VersionUtils.locMoveCheckLocTypeComplete(shallowLoc, locTypeDto)) { if (basCrnpService.checkSiteError(shallowLoc.getCrnNo(), true)) { locMast = shallowLoc; crnNo = locMast.getCrnNo(); break; } } } } } } Wrapper wrapper = null; StaDesc staDesc = null; BasDevp staNo = null; if (Utils.BooleanWhsTypeSta(rowLastno, staDescId)) { // 获取目标站 wrapper = new EntityWrapper() .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); // throw new CoolException("入库路径不存在"); crnNo = 0; } else { staNo = basDevpService.selectById(staDesc.getCrnStn()); if (!staNo.getAutoing().equals("Y")) { log.error("目标站" + staDesc.getCrnStn() + "不可用"); // throw new CoolException("目标站"+staDesc.getCrnStn()+"不可用"); crnNo = 0; } startupDto.setStaNo(staNo.getDevNo()); } // 更新库位排号 if (Cools.isEmpty(locMast)) { rowLastno.setCurrentRow(curRow); rowLastnoService.updateById(rowLastno); } } // Search empty location ==============================>> if (staDescId == 10 && Cools.isEmpty(locMast) && crnNo != 0) { locMast = findStandardEmptyLoc(rowLastno, rowLastnoType, crnNo, nearRow, locTypeDto); } if (Cools.isEmpty(locMast) && crnNo != 0) { locMast = findStandardEmptyLoc(rowLastno, rowLastnoType, crnNo, nearRow, locTypeDto); } if (!Cools.isEmpty(locMast) && !basCrnpService.checkSiteError(crnNo, true)) { locMast = null; } // Retry search if (Cools.isEmpty(locMast) || !locMast.getLocSts().equals("O")) { // Scan next aisle first, then retry with upward-compatible locType1. if (times < rowCount * 2) { times = times + 1; return getLocNoRun(whsType, staDescId, sourceStaNo, findLocNoAttributeVo, moveCrnNo, locTypeDto, times); } LocTypeDto compatibleLocTypeDto = buildUpwardCompatibleLocTypeDto(locTypeDto); if (compatibleLocTypeDto != null) { log.warn("locType1 upward compatibility retry. source={}, target={}", JSON.toJSONString(locTypeDto), JSON.toJSONString(compatibleLocTypeDto)); return getLocNoRun(whsType, staDescId, sourceStaNo, findLocNoAttributeVo, moveCrnNo, compatibleLocTypeDto, 0); } log.error("No empty location found. spec={}, times={}", JSON.toJSONString(locTypeDto), times); throw new CoolException("\u6ca1\u6709\u7a7a\u5e93\u4f4d"); } String locNo = locMast.getLocNo(); // 生成工作号 int workNo = getWorkNo(0); // 返回dto startupDto.setWorkNo(workNo); startupDto.setCrnNo(crnNo); startupDto.setSourceStaNo(sourceStaNo); startupDto.setLocNo(locNo); return startupDto; } 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 recommendRows, int times) { int crnNo = 0; int nearRow = 0; int curRow = 0; int rowCount = 0; LocMast locMast = null; StartupDto startupDto = new StartupDto(); RowLastno rowLastno = rowLastnoService.selectById(whsType); if (Cools.isEmpty(rowLastno)) { throw new CoolException("数据异常,请联系管理员===>库位规则未知"); } 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(); List orderedCrnNos = getOrderedCrnNos(rowLastno, crnNo); List triedCrnNos = new ArrayList<>(); List 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> 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 defaultCrnNos = new ArrayList<>(orderedCrnNos); defaultCrnNos.removeAll(triedCrnNos); locMast = findRun2EmptyLocByCrnNos(rowLastno, rowLastnoType, defaultCrnNos, locTypeDto, staDescId, sourceStaNo, startupDto, preferredArea, "default"); } else { List 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 fallbackCrnNos = filterCrnNosByRows(rowLastno, orderedCrnNos, getRun2FallbackRows(rowLastno)); fallbackCrnNos.removeAll(triedCrnNos); fallbackCrnNos.removeAll(preferredCrnNos); locMast = findRun2EmptyLocByCrnNos(rowLastno, rowLastnoType, fallbackCrnNos, locTypeDto, staDescId, sourceStaNo, startupDto, preferredArea, "fallback-area"); } } } if (!Cools.isEmpty(locMast)) { crnNo = locMast.getCrnNo(); nearRow = locMast.getRow1(); } if (curRow == 0) { curRow = rowLastno.getsRow() == null ? 1 : rowLastno.getsRow(); } curRow = getNextRun2CurrentRow(rowLastno, curRow); rowLastno.setCurrentRow(curRow); rowLastnoService.updateById(rowLastno); if (Cools.isEmpty(locMast) || !locMast.getLocSts().equals("O")) { if (times < rowCount * 2) { times = times + 1; 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, recommendRows, 0); } log.error("No empty location found. spec={}, times={}, preferredArea={}, nearRow={}", JSON.toJSONString(locTypeDto), times, preferredArea, nearRow); throw new CoolException("没有空库位"); } int workNo = getWorkNo(0); startupDto.setWorkNo(workNo); startupDto.setCrnNo(crnNo); startupDto.setSourceStaNo(sourceStaNo); 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 locMasts = locMastService.selectList(new EntityWrapper() .eq("row1", searchRow) .eq("loc_sts", "O") .eq("whs_type", rowLastnoType.getType().longValue()) .orderBy("bay1", true) .orderBy("lev1", true)); for (LocMast locMast : locMasts) { if (VersionUtils.locMoveCheckLocTypeComplete(locMast, locTypeDto)) { return locMast; } } } return null; } public StartupDto getLocNoRun4(Integer whsType, Integer staDescId, Integer sourceStaNo, FindLocNoAttributeVo findLocNoAttributeVo, Integer moveCrnNo, LocTypeDto locTypeDto, int times) { // 初始化参数 int crnNo = 0; //堆垛机号 int nearRow = 0; //最浅库位排 int curRow = 0; //最深库位排 int rowCount = 0; //轮询轮次 LocMast locMast = null; // 目标库位 StartupDto startupDto = new StartupDto(); RowLastno rowLastno = rowLastnoService.selectById(whsType); if (Cools.isEmpty(rowLastno)) { throw new CoolException("数据异常,请联系管理员===>库位规则未知"); } RowLastnoType rowLastnoType = rowLastnoTypeService.selectById(rowLastno.getTypeId()); if (Cools.isEmpty(rowLastnoType)) { throw new CoolException("数据异常,请联系管理员===》库位规则类型未知"); } int sRow = rowLastno.getsRow(); int eRow = rowLastno.geteRow(); int crnNumber = rowLastno.getCrnQty(); // ===============>>>> 开始执行 curRow = rowLastno.getCurrentRow(); if (!Cools.isEmpty(moveCrnNo) && moveCrnNo != 0) { crnNumber = moveCrnNo; // if (times==0){ // curRow = moveCrnNo*4-1; // }else { // curRow = moveCrnNo*4-2; // } } //此程序用于优化堆垛机异常时的运行时间 for (int i = times; i < crnNumber * 2; i++) { int[] locNecessaryParameters = Utils.LocNecessaryParameters(rowLastno, curRow, crnNumber); rowCount = locNecessaryParameters[0]; curRow = locNecessaryParameters[1]; crnNo = locNecessaryParameters[2]; nearRow = locNecessaryParameters[3]; List locMasts = locMastService.selectList(new EntityWrapper() .eq("crn_no", crnNo).eq("loc_sts", "O").eq("whs_type", rowLastnoType.getType().longValue())); if (locMasts.size() <= 5) { nearRow = 0; times++; continue; } break; } if (crnNo == 0) { throw new CoolException("无可用库位"); } // 相似工作档案 --- 同天同规格物料 if (!Cools.isEmpty(findLocNoAttributeVo.getMatnr()) && (staDescId == 1 || staDescId == 11 || staDescId == 111)) { //查询相似工作档案 List wrkMasts = wrkMastService.selectWrkMastWrkDetl(staDescId, findLocNoAttributeVo, crnNo); int nearbay = 0; //相似工作档案 目标库位列 int nearlev = 0; //相似工作档案 目标库位层 for (WrkMast wrkMast : wrkMasts) { int curRowW = curRow; //相似工作档案 最深库位排 int nearRowW = nearRow; //相似工作档案 最浅库位排 if (Cools.isEmpty(wrkMast.getLocNo())) { continue; } //目标排为最外层排 if (Utils.getRow(wrkMast.getLocNo()) == nearRow) { continue; } //起始站不一致 if (!wrkMast.getSourceStaNo().equals(sourceStaNo)) { continue; } //相同列、层过滤 if (Utils.getBay(wrkMast.getLocNo()) == nearbay && Utils.getLev(wrkMast.getLocNo()) == nearlev) { continue; } else { nearbay = Utils.getBay(wrkMast.getLocNo()); nearlev = Utils.getLev(wrkMast.getLocNo()); } //获取目标库位所在巷道并排序 // List groupOutsideLocCrn = Utils.getGroupOutLocCrn(curRow,nearRow,wrkMast.getLocNo(), curRow>nearRow); LocMast locMastGro = locMastService.selectById(wrkMast.getLocNo()); for (int i = 0; i < crnNumber * 2; i++) { if (!(Utils.getRow(locMastGro.getLocNo()) > nearRowW && Utils.getRow(locMastGro.getLocNo()) <= curRowW) && !(Utils.getRow(locMastGro.getLocNo()) < nearRowW && Utils.getRow(locMastGro.getLocNo()) >= curRowW)) { int[] locNecessaryParameters = Utils.LocNecessaryParameters(rowLastno, curRowW, crnNumber); curRowW = locNecessaryParameters[1]; nearRowW = locNecessaryParameters[3]; } else { break; } } //获取目标库位所在巷道最浅非空库位 LocMast locMast2 = locMastService.selectLocByLocStsPakInF(curRowW, nearRowW, locMastGro, rowLastnoType.getType().longValue()); //目标库位所在巷道最浅非空库位存在&&非最外侧库位&&入库状态 if (!Cools.isEmpty(locMast2) && Utils.getRow(locMast2.getLocNo()) != nearRowW && (locMast2.getLocSts().equals("S") || locMast2.getLocSts().equals("Q"))) { //获取库存明细 WrkDetl wrkDetl = wrkDetlService.selectOne(new EntityWrapper().eq("wrk_no", wrkMast.getWrkNo())); //判断同规格物料 if (!Cools.isEmpty(wrkDetl) && findLocNoAttributeVo.beSimilar(wrkDetl)) { int row2 = 0; if (Utils.getRow(locMast2.getLocNo()) > nearRowW) { row2 = Utils.getRow(locMast2.getLocNo()) - 1; } else { row2 = Utils.getRow(locMast2.getLocNo()) + 1; } String targetLocNo = zerofill(String.valueOf(row2), 2) + locMast2.getLocNo().substring(2); locMast = locMastService.selectOne(new EntityWrapper().eq("loc_no", targetLocNo).eq("loc_sts", "O")); if (Cools.isEmpty(locMast)) { continue; } break; } } } } // 相似工作档 --- 空托 if (Cools.isEmpty(locMast) && staDescId == 10) { List wrkMasts = wrkMastService.selectList(new EntityWrapper().eq("io_type", 10).eq("crn_no", crnNo).eq("whs_type", rowLastnoType.getType().longValue())); int nearbay = 0; int nearlev = 0; for (WrkMast wrkMast : wrkMasts) { int curRowW = curRow; //相似工作档案 最深库位排 int nearRowW = nearRow; //相似工作档案 最浅库位排 if (Cools.isEmpty(wrkMast.getLocNo())) { continue; } if (Utils.getRow(wrkMast.getLocNo()) == nearRow) { continue; } //起始站不一致 if (!wrkMast.getSourceStaNo().equals(sourceStaNo)) { continue; } if (Utils.getBay(wrkMast.getLocNo()) == nearbay && Utils.getLev(wrkMast.getLocNo()) == nearlev) { continue; } else { nearbay = Utils.getBay(wrkMast.getLocNo()); nearlev = Utils.getLev(wrkMast.getLocNo()); } // List groupOutsideLocCrn = Utils.getGroupOutLocCrn(curRow,nearRow,wrkMast.getLocNo(), curRow>nearRow); LocMast locMastGro = locMastService.selectById(wrkMast.getLocNo()); for (int i = 0; i < crnNumber * 2; i++) { if (!(Utils.getRow(locMastGro.getLocNo()) > nearRowW && Utils.getRow(locMastGro.getLocNo()) <= curRowW) && !(Utils.getRow(locMastGro.getLocNo()) < nearRowW && Utils.getRow(locMastGro.getLocNo()) >= curRowW)) { int[] locNecessaryParameters = Utils.LocNecessaryParameters(rowLastno, curRowW, crnNumber); curRowW = locNecessaryParameters[1]; nearRowW = locNecessaryParameters[3]; } else { break; } } LocMast locMast2 = locMastService.selectLocByLocStsPakInF(curRowW, nearRowW, locMastGro, rowLastnoType.getType().longValue()); if (!Cools.isEmpty(locMast2) && Utils.getRow(locMast2.getLocNo()) != nearRowW && locMast2.getLocSts().equals("S")) { int row2 = 0; if (Utils.getRow(locMast2.getLocNo()) > nearRowW) { row2 = Utils.getRow(locMast2.getLocNo()) - 1; } else { row2 = Utils.getRow(locMast2.getLocNo()) + 1; } String targetLocNo = zerofill(String.valueOf(row2), 2) + locMast2.getLocNo().substring(2); locMast = locMastService.selectOne(new EntityWrapper().eq("loc_no", targetLocNo).eq("loc_sts", "O")); if (Cools.isEmpty(locMast)) { continue; } break; } } } boolean signRule1 = false; boolean signRule2 = false; if (Utils.BooleanWhsTypeStaIoType(rowLastno)) { // 靠近摆放规则 --- 同天同规格物料 //分离版 if (!Cools.isEmpty(findLocNoAttributeVo.getMatnr()) && staDescId == 1) { // signRule1 = true; } // 靠近摆放规则 --- 同天同规格物料 //互通版 if (!Cools.isEmpty(findLocNoAttributeVo.getMatnr()) && staDescId == 1) { signRule2 = true; } if (!Cools.isEmpty(findLocNoAttributeVo.getMatnr()) && (staDescId == 11 || staDescId == 111)) { signRule1 = true; } } if (signRule1) { if (nearRow != curRow) { List locMasts = locMastService.selectList(new EntityWrapper() .eq("row1", nearRow).eq("loc_sts", "O").eq("whs_type", rowLastnoType.getType().longValue())); for (LocMast locMast1 : locMasts) { //获取巷道 // List groupOutsideLocCrn = Utils.getGroupOutLocCrn(curRow,nearRow,locMast1.getLocNo(), curRow>nearRow); //获取目标库位所在巷道最浅非空库位 LocMast locMastF = locMastService.selectLocByLocStsPakInF(curRow, nearRow, locMast1, rowLastnoType.getType().longValue()); if (!Cools.isEmpty(locMastF) && locMastF.getLocSts().equals("F")) { LocDetl locDetl = locDetlService.selectOne(new EntityWrapper().eq("loc_no", locMastF.getLocNo())); if (!Cools.isEmpty(locDetl) && findLocNoAttributeVo.beSimilar(locDetl)) { //获取目标库位所在巷道最深空库位 locMast = locMastService.selectLocByLocStsPakInO(curRow, nearRow, locMast1, rowLastnoType.getType().longValue()); break; } } } } } else if (signRule2) { List locNos = locDetlService.getSameDetlToday(findLocNoAttributeVo.getMatnr(), sRow, eRow); int nearbay = 0; int nearlev = 0; for (String locNo : locNos) { int curRowW = curRow; //相似工作档案 最深库位排 int nearRowW = nearRow; //相似工作档案 最浅库位排 if (Cools.isEmpty(locNo)) { continue; } if (Utils.getRow(locNo) == nearRow) { continue; } if (Utils.getBay(locNo) == nearbay && Utils.getLev(locNo) == nearlev) { continue; } else { nearbay = Utils.getBay(locNo); nearlev = Utils.getLev(locNo); } // List groupOutsideLocCrn = Utils.getGroupOutLocCrn(curRow,nearRow,wrkMast.getLocNo(), curRow>nearRow); LocMast locMastGro = locMastService.selectById(locNo); for (int i = 0; i < crnNumber * 2; i++) { if (!(Utils.getRow(locMastGro.getLocNo()) > nearRowW && Utils.getRow(locMastGro.getLocNo()) <= curRowW) && !(Utils.getRow(locMastGro.getLocNo()) < nearRowW && Utils.getRow(locMastGro.getLocNo()) >= curRowW)) { int[] locNecessaryParameters = Utils.LocNecessaryParameters(rowLastno, curRowW, crnNumber); curRowW = locNecessaryParameters[1]; nearRowW = locNecessaryParameters[3]; } else { break; } } LocMast locMast2 = locMastService.selectLocByLocStsPakInF(curRowW, nearRowW, locMastGro, rowLastnoType.getType().longValue()); if (!Cools.isEmpty(locMast2) && Utils.getRow(locMast2.getLocNo()) != nearRowW && locMast2.getLocSts().equals("S")) { int row2 = 0; if (Utils.getRow(locMast2.getLocNo()) > nearRowW) { row2 = Utils.getRow(locMast2.getLocNo()) - 1; } else { row2 = Utils.getRow(locMast2.getLocNo()) + 1; } String targetLocNo = zerofill(String.valueOf(row2), 2) + locMast2.getLocNo().substring(2); locMast = locMastService.selectOne(new EntityWrapper().eq("loc_no", targetLocNo).eq("loc_sts", "O")); if (Cools.isEmpty(locMast)) { continue; } break; } } } // // 靠近摆放规则 --- 空托 //分离版 // if (staDescId == 10 && Utils.BooleanWhsTypeStaIoType(rowLastno)) { // List locMasts = locMastService.selectList(new EntityWrapper().eq("row1", nearRow).eq("loc_sts", "O")); // for (LocMast locMast1:locMasts){ // //获取巷道 //// List groupOutsideLocCrn = Utils.getGroupOutLocCrn(curRow,nearRow,locMast1.getLocNo(), curRow>nearRow); // //获取目标库位所在巷道最浅非空库位 // LocMast locMastF = locMastService.selectLocByLocStsPakInF(curRow,nearRow,locMast1,rowLastnoType.getType().longValue()); // if (!Cools.isEmpty(locMastF) && locMastF.getLocSts().equals("D")){ // //获取目标库位所在巷道最浅非空库位 // locMast = locMastService.selectLocByLocStsPakInO(curRow,nearRow,locMast1,rowLastnoType.getType().longValue()); // break; // } // } // } // 靠近摆放规则 --- 空托 //互通版 if (staDescId == 10 && Utils.BooleanWhsTypeStaIoType(rowLastno)) { List locMasts = locMastService.selectList(new EntityWrapper().eq("loc_sts", "D").ge("row1", sRow).le("row1", eRow)); int nearbay = 0; int nearlev = 0; for (LocMast locMastSign : locMasts) { int curRowW = curRow; //相似工作档案 最深库位排 int nearRowW = nearRow; //相似工作档案 最浅库位排 if (Cools.isEmpty(locMastSign.getLocNo())) { continue; } if (Utils.getRow(locMastSign.getLocNo()) == nearRow) { continue; } if (Utils.getBay(locMastSign.getLocNo()) == nearbay && Utils.getLev(locMastSign.getLocNo()) == nearlev) { continue; } else { nearbay = Utils.getBay(locMastSign.getLocNo()); nearlev = Utils.getLev(locMastSign.getLocNo()); } // List groupOutsideLocCrn = Utils.getGroupOutLocCrn(curRow,nearRow,wrkMast.getLocNo(), curRow>nearRow); LocMast locMastGro = locMastService.selectById(locMastSign.getLocNo()); for (int i = 0; i < crnNumber * 2; i++) { if (!(Utils.getRow(locMastGro.getLocNo()) > nearRowW && Utils.getRow(locMastGro.getLocNo()) <= curRowW) && !(Utils.getRow(locMastGro.getLocNo()) < nearRowW && Utils.getRow(locMastGro.getLocNo()) >= curRowW)) { int[] locNecessaryParameters = Utils.LocNecessaryParameters(rowLastno, curRowW, crnNumber); curRowW = locNecessaryParameters[1]; nearRowW = locNecessaryParameters[3]; } else { break; } } LocMast locMast2 = locMastService.selectLocByLocStsPakInF(curRowW, nearRowW, locMastGro, rowLastnoType.getType().longValue()); if (!Cools.isEmpty(locMast2) && Utils.getRow(locMast2.getLocNo()) != nearRowW && locMast2.getLocSts().equals("S")) { int row2 = 0; if (Utils.getRow(locMast2.getLocNo()) > nearRowW) { row2 = Utils.getRow(locMast2.getLocNo()) - 1; } else { row2 = Utils.getRow(locMast2.getLocNo()) + 1; } String targetLocNo = zerofill(String.valueOf(row2), 2) + locMast2.getLocNo().substring(2); locMast = locMastService.selectOne(new EntityWrapper().eq("loc_no", targetLocNo).eq("loc_sts", "O")); if (Cools.isEmpty(locMast)) { continue; } break; } } } Wrapper wrapper = null; StaDesc staDesc = null; BasDevp staNo = null; if (Utils.BooleanWhsTypeSta(rowLastno, staDescId)) { // 获取目标站 wrapper = new EntityWrapper() .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; } else { staNo = basDevpService.selectById(staDesc.getCrnStn()); if (!staNo.getAutoing().equals("Y")) { log.error("目标站" + staDesc.getCrnStn() + "不可用"); crnNo = 0; } startupDto.setStaNo(staNo.getDevNo()); } } // 更新库位排号 if (Utils.BooleanWhsTypeSta(rowLastno, staDescId) && Cools.isEmpty(locMast)) { rowLastno.setCurrentRow(curRow); rowLastnoService.updateById(rowLastno); } // 开始查找库位 ==============================>> // 1.按规则查找库位 if (Cools.isEmpty(locMast) && crnNo != 0) { List locMasts = locMastService.selectList(new EntityWrapper() .eq("row1", nearRow) .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.getBay1() == curRow) { locMast = locMast2; break; } } } //未找到 允许混料 if (Cools.isEmpty(locMast) && Utils.BooleanWhsTypeStaIoType(rowLastno)) { for (LocMast locMast1 : locMasts) { if (!VersionUtils.locMoveCheckLocTypeComplete(locMast1, locTypeDto)) { continue; } if (Utils.BooleanWhsTypeStaIoType(rowLastno)) { // ????????????? // List groupOutsideLocCrn = Utils.getGroupOutLocCrn(curRow,nearRow,locMast1.getLocNo(), curRow>nearRow); // ???????????????? LocMast locMast2 = locMastService.selectLocByLocStsPakInF(curRow, nearRow, locMast1, rowLastnoType.getType().longValue()); if (Cools.isEmpty(locMast2)) { LocMast locMast3 = locMastService.selectLocByLocStsPakInO(curRow, nearRow, locMast1, rowLastnoType.getType().longValue()); if (!Cools.isEmpty(locMast3)) { locMast = locMast3; break; } } else { if ((locMast2.getLocSts().equals("F") && staDescId == 1) || (locMast2.getLocSts().equals("D") && staDescId == 10)) { LocMast locMast3 = locMastService.selectLocByLocStsPakInO(curRow, nearRow, locMast1, rowLastnoType.getType().longValue()); if (!Cools.isEmpty(locMast3)) { locMast = locMast3; break; } } } } } } } // Retry search if (Cools.isEmpty(locMast) || !locMast.getLocSts().equals("O")) { // Scan next aisle first, then retry with upward-compatible locType1. if (times < rowCount * 2) { times = times + 1; return getLocNoRun4(whsType, staDescId, sourceStaNo, findLocNoAttributeVo, moveCrnNo, locTypeDto, times); } LocTypeDto compatibleLocTypeDto = buildUpwardCompatibleLocTypeDto(locTypeDto); if (compatibleLocTypeDto != null) { log.warn("locType1 upward compatibility retry. source={}, target={}", JSON.toJSONString(locTypeDto), JSON.toJSONString(compatibleLocTypeDto)); return getLocNoRun4(whsType, staDescId, sourceStaNo, findLocNoAttributeVo, moveCrnNo, compatibleLocTypeDto, 0); } log.error("No empty location found. spec={}, times={}", JSON.toJSONString(locTypeDto), times); throw new CoolException("\u6ca1\u6709\u7a7a\u5e93\u4f4d"); } String locNo = locMast.getLocNo(); // 生成工作号 int workNo = getWorkNo(0); // 返回dto startupDto.setWorkNo(workNo); startupDto.setCrnNo(crnNo); startupDto.setSourceStaNo(sourceStaNo); startupDto.setLocNo(locNo); return startupDto; } public StartupDto getLocNoRun5(Integer whsType, Integer staDescId, Integer sourceStaNo, FindLocNoAttributeVo findLocNoAttributeVo, Integer moveCrnNo, LocTypeDto locTypeDto, List recommendRows, int times) { // 初始化参数 int crnNo = 0; //堆垛机号 int nearRow = 0; //最浅库位排 int curRow = 0; //最深库位排 int rowCount = 0; //轮询轮次 LocMast locMast = null; // 目标库位 StartupDto startupDto = new StartupDto(); RowLastno rowLastno = rowLastnoService.selectById(whsType); if (Cools.isEmpty(rowLastno)) { throw new CoolException("数据异常,请联系管理员===>库位规则未知"); } RowLastnoType rowLastnoType = rowLastnoTypeService.selectById(rowLastno.getTypeId()); if (Cools.isEmpty(rowLastnoType)) { throw new CoolException("数据异常,请联系管理员===》库位规则类型未知"); } int sRow = rowLastno.getsRow(); int eRow = rowLastno.geteRow(); int crnNumber = eRow - sRow + 1; // ===============>>>> 开始执行 curRow = rowLastno.getCurrentRow(); if (!Cools.isEmpty(moveCrnNo) && moveCrnNo != 0) { crnNumber = moveCrnNo; } //此程序用于优化堆垛机异常时的运行时间 int[] locNecessaryParameters = Utils.LocNecessaryParameters(rowLastno, curRow, crnNumber); curRow = locNecessaryParameters[1]; crnNo = 6; rowCount = locNecessaryParameters[0]; nearRow = locNecessaryParameters[3]; Wrapper wrapper = null; StaDesc staDesc = null; BasDevp staNo = null; // if (Utils.BooleanWhsTypeSta(rowLastno, staDescId)) { // 获取目标站 // wrapper = new EntityWrapper() // .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); //// throw new CoolException("入库路径不存在"); // crnNo = 0; // } // else { // staNo = basDevpService.selectById(staDesc.getCrnStn()); // if (!staNo.getAutoing().equals("Y")) { // log.error("目标站" + staDesc.getCrnStn() + "不可用"); //// throw new CoolException("目标站"+staDesc.getCrnStn()+"不可用"); // crnNo = 0; // } // startupDto.setStaNo(staNo.getDevNo()); // } // } // 更新库位排号 if (Utils.BooleanWhsTypeSta(rowLastno, staDescId) && Cools.isEmpty(locMast)) { rowLastno.setCurrentRow(curRow); rowLastnoService.updateById(rowLastno); } // 开始查找库位 ==============================>> 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(); if (Cools.isEmpty(locMast) && preferredArea == null) { List locMasts = locMastService.selectList(new EntityWrapper() .eq("row1", nearRow) .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; 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(); } if (Cools.isEmpty(locMast)) { locMast = findAgvLocByRows(rowLastno, rowLastnoType, getAgvFallbackRows(rowLastno), 1, 19, curRow, nearRow, locTypeDto, true); if (!Cools.isEmpty(locMast)) { crnNo = locMast.getCrnNo(); } } } // Retry search if (Cools.isEmpty(locMast) || !locMast.getLocSts().equals("O")) { // Scan next aisle first, then retry with upward-compatible locType1. if (times < rowCount * 2) { times = times + 1; return getLocNoRun5(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 getLocNoRun5(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"); } String locNo = locMast.getLocNo(); // 生成工作号 int workNo = getWorkNo(0); // 返回dto startupDto.setWorkNo(workNo); startupDto.setCrnNo(crnNo); startupDto.setSourceStaNo(sourceStaNo); startupDto.setLocNo(locNo); return startupDto; } public static String zerofill(String msg, Integer count) { if (msg.length() == count) { return msg; } else if (msg.length() > count) { return msg.substring(0, 16); } else { StringBuilder msgBuilder = new StringBuilder(msg); for (int i = 0; i < count - msg.length(); ++i) { msgBuilder.insert(0, "0"); } return msgBuilder.toString(); } } }