src/main/java/com/zy/asrs/controller/OpenController.java
@@ -140,6 +140,27 @@ } /** * 执行订单出库 */ @PostMapping("/order/pakout/execute/default/v1") @AppAuth(memo = "执行订单出库") public synchronized R pakoutOrderExecute(@RequestHeader(required = false) String appkey, @RequestBody OpenOrderPakoutExecuteParam param, HttpServletRequest request) { auth(appkey, param, request); if (Cools.isEmpty(param)) { return R.parse(BaseRes.PARAM); } if (Cools.isEmpty(param.getOrderId())) { return R.error("出库单号[orderId]不能为空"); } if (Cools.isEmpty(param.getExecute())) { return R.error("执行动作[execute]不能为空"); } return openService.pakoutOrderExecute(param); } /** * pause out order */ @PostMapping("/order/pakout/pause/default/v1") @@ -499,3 +520,4 @@ return R.ok(); } } src/main/java/com/zy/asrs/controller/WorkController.java
@@ -10,6 +10,7 @@ import com.zy.asrs.entity.param.StockOutParam; import com.zy.asrs.service.BasDevpService; import com.zy.asrs.service.WorkService; import com.zy.asrs.utils.Utils; import com.zy.common.model.StartupDto; import com.zy.common.web.BaseController; import org.springframework.beans.factory.annotation.Autowired; @@ -47,6 +48,14 @@ return R.ok().add(basDevpService.getAvailableEmptyInSite()); } @RequestMapping("/test/station/storage/crn/list") @ManagerAuth(memo = "测试站点库区堆垛机顺序") public R testStationStorageCrnList(@RequestParam Integer stationId, @RequestParam(required = false) Integer locType1, @RequestParam(required = false) String matnr) { List<Map<String, Integer>> stationStorageAreaName = Utils.getStationStorageAreaName(stationId, locType1, matnr); return R.ok().add(stationStorageAreaName); } @RequestMapping("/available/take/site") @ManagerAuth() public R availableTakeSite(){ @@ -185,3 +194,4 @@ } } src/main/java/com/zy/asrs/entity/BasDevp.java
@@ -14,6 +14,7 @@ import java.io.Serializable; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; @Data @TableName("asr_bas_devp") @@ -140,7 +141,7 @@ @TableField("io_time") private Date ioTime; @ApiModelProperty(value= "") @ApiModelProperty(value= "绑定库区") private String area; @ApiModelProperty(value= "") @@ -226,6 +227,23 @@ return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(this.ioTime); } public String getArea$() { if (Cools.isEmpty(this.area)) { return ""; } String normalized = this.area.trim(); String upper = normalized.toUpperCase(Locale.ROOT); if ("1".equals(normalized) || "A".equals(upper) || "A区".equals(upper) || "A库".equals(upper) || "A库区".equals(upper)) { return "A库区"; } if ("2".equals(normalized) || "B".equals(upper) || "B区".equals(upper) || "B库".equals(upper) || "B库区".equals(upper)) { return "B库区"; } if ("3".equals(normalized) || "C".equals(upper) || "C区".equals(upper) || "C库".equals(upper) || "C库区".equals(upper)) { return "C库区"; } return normalized; } public String getLocType1$() { if (null == this.locType1){ return null; } switch (this.locType1){ @@ -301,3 +319,4 @@ } } src/main/java/com/zy/asrs/service/OpenService.java
@@ -31,6 +31,11 @@ R pakoutOrderPause(OpenOrderPakoutPauseParam param); /** * execute out order */ R pakoutOrderExecute(OpenOrderPakoutExecuteParam param); /** * 入库单回写 */ List<OpenOrderCompeteResult> pakoutOrderComplete(OpenOrderCompleteParam param); @@ -76,3 +81,4 @@ */ R outOrder(OutTaskParam param); } src/main/java/com/zy/asrs/service/impl/OpenServiceImpl.java
@@ -21,6 +21,8 @@ import com.zy.common.constant.ArmConstant; import com.zy.common.model.DetlDto; import com.zy.common.model.LocDetlDto; import com.zy.common.model.LocDto; import com.zy.common.model.TaskDto; import com.zy.common.model.enums.WorkNoType; import com.zy.common.service.CommonService; import com.zy.common.utils.HttpHandler; @@ -92,6 +94,10 @@ private WrkMastService wrkMastService; @Autowired private WcsApiService wcsApiService; @Autowired private WorkService workService; @Autowired private BasCrnpService basCrnpService; @Override @Transactional @@ -420,6 +426,27 @@ return R.ok("pause out success").add(result); } @Override @Transactional public R pakoutOrderExecute(OpenOrderPakoutExecuteParam param) { if (param == null || Cools.isEmpty(param.getOrderId())) { throw new CoolException("orderId is empty"); } if (param.getExecute() == null) { throw new CoolException("execute is empty"); } if (Objects.equals(param.getExecute(), 1)) { return createPakoutTasks(param.getOrderId()); } if (Objects.equals(param.getExecute(), 2)) { OpenOrderPakoutPauseParam pauseParam = new OpenOrderPakoutPauseParam(); pauseParam.setOrderNo(param.getOrderId()); pauseParam.setReason("OPEN_API_PAUSE"); return pakoutOrderPause(pauseParam); } throw new CoolException("execute only supports 1 or 2"); } private List<WrkMast> findActiveOutboundTasks(String orderNo) { List<WrkDetl> wrkDetls = wrkDetlService.selectList(new EntityWrapper<WrkDetl>().eq("order_no", orderNo)); if (wrkDetls == null || wrkDetls.isEmpty()) { @@ -434,13 +461,116 @@ } List<WrkMast> wrkMasts = wrkMastService.selectList(new EntityWrapper<WrkMast>() .in("wrk_no", wrkNos) .in("io_type", Arrays.asList(101, 108, 110)) .in("io_type", Arrays.asList(101, 103, 107, 108, 110)) .lt("wrk_sts", 14L)); if (wrkMasts == null || wrkMasts.isEmpty()) { return Collections.emptyList(); } wrkMasts.sort(Comparator.comparing(WrkMast::getWrkNo)); return wrkMasts; } private R createPakoutTasks(String orderNo) { Order order = orderService.selectByNo(orderNo); if (order == null) { throw new CoolException("order not found: " + orderNo); } if (order.getSettle() != 1L && order.getSettle() != 2L) { throw new CoolException("该订单已处理"); } List<OrderDetl> orderDetls = orderDetlService.selectByOrderId(order.getId()); if (Cools.isEmpty(orderDetls)) { throw new CoolException("订单明细为空"); } Set<String> exist = new HashSet<>(); List<LocDto> locDtos = new ArrayList<>(); List<String> lackDetails = new ArrayList<>(); for (OrderDetl orderDetl : orderDetls) { double issued = Optional.ofNullable(orderDetl.getAnfme()).orElse(0.0D) - Optional.ofNullable(orderDetl.getWorkQty()).orElse(0.0D); if (issued <= 0.0D) { continue; } List<LocDetl> locDetls = locDetlService.queryStockAll(null, exist, orderDetl.getMatnr(), orderDetl.getBatch(), orderDetl.getBrand(), orderDetl.getStandby1(), orderDetl.getStandby2(), orderDetl.getStandby3(), orderDetl.getBoxType1(), orderDetl.getBoxType2()); for (LocDetl locDetl : locDetls) { if (issued <= 0.0D) { break; } LocMast locMast = locMastService.selectOne(new EntityWrapper<LocMast>().eq("loc_no", locDetl.getLocNo())); if (locMast == null) { continue; } BasCrnp basCrnp = basCrnpService.selectOne(new EntityWrapper<BasCrnp>().eq("crn_no", locMast.getCrnNo())); if (basCrnp == null || !"Y".equalsIgnoreCase(basCrnp.getOutEnable())) { continue; } double allocateQty = issued >= locDetl.getAnfme() ? locDetl.getAnfme() : issued; LocDto locDto = new LocDto(locDetl.getLocNo(), locDetl.getMatnr(), locDetl.getMaktx(), locDetl.getBatch(), orderDetl.getOrderNo(), allocateQty); locDto.setFrozen(locDetl.getFrozen()); locDto.setFrozenLoc(locMast.getFrozen()); locDto.setBrand(orderDetl.getBrand()); locDto.setStandby1(orderDetl.getStandby1()); locDto.setStandby2(orderDetl.getStandby2()); locDto.setStandby3(orderDetl.getStandby3()); locDto.setBoxType1(orderDetl.getBoxType1()); locDto.setBoxType2(orderDetl.getBoxType2()); locDto.setBoxType3(orderDetl.getBoxType3()); locDto.setStaNos(staDescService.queryOutStaNosByLocNo(locDetl.getLocNo(), allocateQty >= locDetl.getAnfme() ? 101 : 103)); if (Cools.isEmpty(locDto.getStaNos())) { continue; } locDtos.add(locDto); exist.add(locDetl.getLocNo()); issued -= allocateQty; } if (issued > 0.0D) { lackDetails.add(buildLackDetail(orderDetl, issued)); } } List<LocDto> availableLocDtos = new ArrayList<>(); for (LocDto locDto : locDtos) { if (locDto.getFrozen() != 1 && locDto.getFrozenLoc() != 1) { availableLocDtos.add(locDto); } } if (Cools.isEmpty(availableLocDtos)) { throw new CoolException(Cools.isEmpty(lackDetails) ? "未生成任何出库任务" : "库存不足"); } List<TaskDto> taskDtos = new ArrayList<>(); for (LocDto locDto : availableLocDtos) { TaskDto taskDto = new TaskDto(locDto.getLocNo(), locDto.getStaNo(), locDto); if (TaskDto.has(taskDtos, taskDto)) { TaskDto dto = TaskDto.find(taskDtos, taskDto); if (dto != null) { dto.getLocDtos().addAll(taskDto.getLocDtos()); } } else { taskDtos.add(taskDto); } } for (TaskDto taskDto : taskDtos) { BasDevp staNo = basDevpService.checkSiteStatus(taskDto.getStaNo()); workService.stockOut(staNo, taskDto, 9527L); } Map<String, Object> result = new LinkedHashMap<>(); result.put("orderId", orderNo); result.put("createdTaskCount", taskDtos.size()); result.put("allocatedDetailCount", availableLocDtos.size()); result.put("lackDetailCount", lackDetails.size()); result.put("lackDetails", lackDetails); return R.ok("execute out success").add(result); } private String buildLackDetail(OrderDetl orderDetl, double lackQty) { return orderDetl.getMatnr() + "|batch=" + (orderDetl.getBatch() == null ? "" : orderDetl.getBatch()) + "|lack=" + lackQty; } private boolean needNotifyWcsStop(WrkMast wrkMast) { @@ -1253,3 +1383,4 @@ return R.ok(); } } src/main/java/com/zy/asrs/service/impl/WorkServiceImpl.java
@@ -692,7 +692,12 @@ // 判断是否是盘点单 String orderNo = taskDto.getLocDtos().get(0).getOrderNo(); OrderPakout orderPakout = orderPakOutService.selectByNo(orderNo); int ioType = orderPakout.getDocType() == 8 ? 107 : (taskDto.isAll() ? 101 : 103); Order order = orderPakout == null ? orderService.selectByNo(orderNo) : null; if (orderPakout == null && order == null) { throw new CoolException("订单不存在:" + orderNo); } Long docType = orderPakout != null ? orderPakout.getDocType() : order.getDocType(); int ioType = Objects.equals(docType, 8L) ? 107 : (taskDto.isAll() ? 101 : 103); StaDesc staDesc = staDescService.queryCrnStnAuto(ioType, locMast.getCrnNo(), staNo.getDevNo()); List<LocMast> list = locMastMapper.selectList( new EntityWrapper<LocMast>() @@ -755,9 +760,14 @@ // 生成工作档明细 for (LocDto locDto : taskDto.getLocDtos()) { if (locDto.getAnfme()==null || locDto.getAnfme() <= 0.0D) { continue; } // OrderDetl orderDetl = orderDetlService.selectItem(locDto.getOrderNo(), locDto.getMatnr(), locDto.getBatch()); OrderDetl orderDetl = OrderInAndOutUtil.selectItem(Boolean.FALSE, locDto.getOrderNo(), locDto.getMatnr(), locDto.getBatch(),locDto.getBrand() OrderDetl orderDetl = orderPakout != null ? OrderInAndOutUtil.selectItem(Boolean.FALSE, locDto.getOrderNo(), locDto.getMatnr(), locDto.getBatch(),locDto.getBrand() ,locDto.getStandby1(),locDto.getStandby2(),locDto.getStandby3(),locDto.getBoxType1(),locDto.getBoxType2(),locDto.getBoxType3()) : orderDetlService.selectItem(locDto.getOrderNo(), locDto.getMatnr(), locDto.getBatch(),locDto.getBrand() ,locDto.getStandby1(),locDto.getStandby2(),locDto.getStandby3(),locDto.getBoxType1(),locDto.getBoxType2(),locDto.getBoxType3()); if (orderDetl == null) { throw new CoolException("订单明细不存在:" + locDto.getOrderNo() + ", " + locDto.getMatnr()); } // if (orderDetl == null) { //// orderDetl = orderDetlService.selectItem(locDto.getOrderNo(), locDto.getMatnr(), null); // orderDetl = OrderInAndOutUtil.selectItem(Boolean.FALSE, locDto.getOrderNo(), locDto.getMatnr(), null); @@ -792,11 +802,22 @@ // throw new CoolException("修改订单明细数量失败"); // } // orderService.updateSettle(orderDetl.getOrderId(), 2L, userId); OrderInAndOutUtil.increaseWorkQty(Boolean.FALSE,orderDetl.getOrderId(), orderDetl.getMatnr(), orderDetl.getBatch(), orderDetl.getBrand(),orderDetl.getStandby1(),orderDetl.getStandby2(),orderDetl.getStandby3(), orderDetl.getBoxType1(),orderDetl.getBoxType2(),orderDetl.getBoxType3() , locDto.getAnfme()); OrderInAndOutUtil.updateOrder(Boolean.FALSE,orderDetl.getOrderId(), 2L, userId); if (orderPakout != null) { OrderInAndOutUtil.increaseWorkQty(Boolean.FALSE,orderDetl.getOrderId(), orderDetl.getMatnr(), orderDetl.getBatch(), orderDetl.getBrand(),orderDetl.getStandby1(),orderDetl.getStandby2(),orderDetl.getStandby3(), orderDetl.getBoxType1(),orderDetl.getBoxType2(),orderDetl.getBoxType3() , locDto.getAnfme()); OrderInAndOutUtil.updateOrder(Boolean.FALSE,orderDetl.getOrderId(), 2L, userId); } else { if (!orderDetlService.increaseWorkQty(orderDetl.getOrderId(), orderDetl.getMatnr(), orderDetl.getBatch(), orderDetl.getBrand(), orderDetl.getStandby1(), orderDetl.getStandby2(), orderDetl.getStandby3(), orderDetl.getBoxType1(), orderDetl.getBoxType2(), orderDetl.getBoxType3(), locDto.getAnfme())) { throw new CoolException("修改订单明细作业数量失败"); } if (!orderService.updateSettle(orderDetl.getOrderId(), 2L, userId)) { throw new CoolException("修改订单状态失败"); } } } //修改agv备料区状态 if(locMastRgv.getLocSts().equals("O")){ @@ -1786,3 +1807,4 @@ } } src/main/java/com/zy/asrs/utils/Utils.java
@@ -5,10 +5,16 @@ import com.core.common.Cools; import com.core.common.SpringUtils; import com.core.exception.CoolException; import com.zy.asrs.entity.BasCrnp; import com.zy.asrs.entity.BasDevp; import com.zy.asrs.entity.LocMast; import com.zy.asrs.entity.RowLastno; import com.zy.asrs.service.BasCrnpService; import com.zy.asrs.service.BasDevpService; import com.zy.asrs.service.LocMastService; import com.zy.asrs.service.RowLastnoService; import com.zy.common.CodeBuilder; import com.zy.common.entity.Parameter; import com.zy.common.model.LocDetlDto; import com.zy.common.properties.SlaveProperties; import com.zy.common.service.CommonService; @@ -19,7 +25,11 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Locale; /** * Created by vincent on 2020/8/27 @@ -120,6 +130,441 @@ } } public static Integer getStationStorageArea(Integer stationId) { if (stationId == null || stationId <= 0) { return null; } BasDevpService basDevpService = SpringUtils.getBean(BasDevpService.class); BasDevp station = basDevpService.selectById(stationId); if (station == null) { return null; } return parseStorageArea(station.getArea()); } /** * 生成入库找库位时的堆垛机优先顺序。 * * <p>处理规则: * 1. 先根据入库站点查询所属库区。 * 2. 先提取该库区内的堆垛机,并按可用空库位过滤不可用堆垛机。 * 3. 若当前库区没有满足条件的空库位,再补充其他库区的堆垛机。 * 4. 当 {@code locType1 = 1} 时,先返回低库位堆垛机,再把同批堆垛机的高库位追加到后面。 * 5. 对不存在、故障、不可入以及无空库位的堆垛机直接剔除。 * 6. 当物料为 {@code emptyPallet} 时,按空板入库优先规则重新排序。 * * <p>返回结果中的每一项格式为: * {@code {crnNo: 堆垛机号, locType1: 库位高低类型}} * * @param stationId 入库站点 * @param locType1 目标库位高低类型,1=低库位,2=高库位 * @param matnr 物料编码,传入 {@code emptyPallet} 时使用空板排序规则 * @return 按优先级排好序的堆垛机列表 */ public static List<Map<String, Integer>> getStationStorageAreaName(Integer stationId, Integer locType1, String matnr) { List<Map<String, Integer>> result = new ArrayList<>(); // 先定位入库站点所属库区。 Integer storageArea = getStationStorageArea(stationId); Integer whsType = GetWhsType(stationId); if (storageArea == null || whsType == null || whsType <= 0) { return result; } RowLastnoService rowLastnoService = SpringUtils.getBean(RowLastnoService.class); RowLastno rowLastno = rowLastnoService.selectById(whsType); if (rowLastno == null) { return result; } BasCrnpService basCrnpService = SpringUtils.getBean(BasCrnpService.class); LocMastService locMastService = SpringUtils.getBean(LocMastService.class); boolean emptyPallet = "emptyPallet".equalsIgnoreCase(matnr); // 先取当前库区对应的堆垛机。 List<Integer> preferredCrnNos = getAreaCrnNos(storageArea, rowLastno); List<Integer> preferredAvailableCrnNos = getAvailableCrnNos(preferredCrnNos, locType1, emptyPallet, basCrnpService, locMastService); appendCrnLocTypeEntries(result, preferredAvailableCrnNos, locType1, locMastService); // 当前库区没有可用容量时,再补充其他库区堆垛机。 if (!hasAvailableCapacity(preferredCrnNos, locType1, basCrnpService, locMastService)) { List<Integer> otherAreaCrnNos = getOtherAreaCrnNos(storageArea, rowLastno); List<Integer> otherAvailableCrnNos = getAvailableCrnNos(otherAreaCrnNos, locType1, emptyPallet, basCrnpService, locMastService); appendCrnLocTypeEntries(result, otherAvailableCrnNos, locType1, locMastService); } return result; } private static void appendCrnLocTypeEntries(List<Map<String, Integer>> result, List<Integer> crnNos, Integer locType1, LocMastService locMastService) { Short normalizedLocType1 = normalizeLocType1(locType1); if (normalizedLocType1 == null) { appendCrnLocTypeEntries(result, crnNos, (short) 1, locMastService); appendCrnLocTypeEntries(result, crnNos, (short) 2, locMastService); return; } appendCrnLocTypeEntries(result, crnNos, normalizedLocType1, locMastService); if (normalizedLocType1 == 1) { appendCrnLocTypeEntries(result, crnNos, (short) 2, locMastService); } } private static void appendCrnLocTypeEntries(List<Map<String, Integer>> result, List<Integer> crnNos, Short targetLocType1, LocMastService locMastService) { if (targetLocType1 == null || Cools.isEmpty(crnNos)) { return; } for (Integer crnNo : crnNos) { if (!hasAvailableLoc(crnNo, targetLocType1, locMastService) || containsCrnLocType(result, crnNo, targetLocType1)) { continue; } Map<String, Integer> item = new LinkedHashMap<>(); item.put("crnNo", crnNo); item.put("locType1", targetLocType1.intValue()); result.add(item); } } private static boolean containsCrnLocType(List<Map<String, Integer>> result, Integer crnNo, Short locType1) { for (Map<String, Integer> item : result) { if (item == null) { continue; } if (crnNo.equals(item.get("crnNo")) && locType1.intValue() == item.get("locType1")) { return true; } } return false; } private static boolean hasAvailableCapacity(List<Integer> crnNos, Integer locType1, BasCrnpService basCrnpService, LocMastService locMastService) { return !getAvailableCrnNos(crnNos, locType1, false, basCrnpService, locMastService).isEmpty(); } private static List<Integer> getAvailableCrnNos(List<Integer> candidateCrnNos, Integer locType1, boolean emptyPallet, BasCrnpService basCrnpService, LocMastService locMastService) { LinkedHashSet<Integer> availableCrnNos = new LinkedHashSet<>(); if (Cools.isEmpty(candidateCrnNos)) { return new ArrayList<>(); } for (Integer crnNo : candidateCrnNos) { if (crnNo == null || !basCrnpService.checkSiteError(crnNo, true)) { continue; } if (!hasAvailableLocForRequest(crnNo, locType1, locMastService)) { continue; } availableCrnNos.add(crnNo); } List<Integer> result = new ArrayList<>(availableCrnNos); return result; } private static int compareEmptyPalletCrn(Integer leftCrnNo, Integer rightCrnNo, BasCrnpService basCrnpService) { int leftPriority = getEmptyPalletPriority(basCrnpService.selectById(leftCrnNo)); int rightPriority = getEmptyPalletPriority(basCrnpService.selectById(rightCrnNo)); if (leftPriority != rightPriority) { return Integer.compare(rightPriority, leftPriority); } return Integer.compare(leftCrnNo, rightCrnNo); } private static int getEmptyPalletPriority(BasCrnp basCrnp) { if (basCrnp == null) { return -1; } return "Y".equalsIgnoreCase(basCrnp.getEmpIn()) ? 1 : 0; } private static boolean hasAvailableLocForRequest(Integer crnNo, Integer locType1, LocMastService locMastService) { Short normalizedLocType1 = normalizeLocType1(locType1); if (normalizedLocType1 == null) { return hasAvailableLoc(crnNo, (short) 1, locMastService) || hasAvailableLoc(crnNo, (short) 2, locMastService); } if (hasAvailableLoc(crnNo, normalizedLocType1, locMastService)) { return true; } return normalizedLocType1 == 1 && hasAvailableLoc(crnNo, (short) 2, locMastService); } private static boolean hasAvailableLoc(Integer crnNo, Short locType1, LocMastService locMastService) { if (crnNo == null || locType1 == null) { return false; } return locMastService.selectCount(new EntityWrapper<LocMast>() .eq("crn_no", crnNo) .eq("loc_sts", "O") .eq("loc_type1", locType1)) > 0; } private static Short normalizeLocType1(Integer locType1) { if (locType1 == null || (locType1 != 1 && locType1 != 2)) { return null; } return locType1.shortValue(); } private static List<Integer> getOtherAreaCrnNos(Integer preferredArea, RowLastno rowLastno) { LinkedHashSet<Integer> otherAreaCrnNos = new LinkedHashSet<>(); for (int area = 1; area <= 3; area++) { if (preferredArea != null && preferredArea == area) { continue; } otherAreaCrnNos.addAll(getAreaCrnNos(area, rowLastno)); } if (otherAreaCrnNos.isEmpty()) { otherAreaCrnNos.addAll(getAllCrnNos(rowLastno)); otherAreaCrnNos.removeAll(getAreaCrnNos(preferredArea, rowLastno)); } return new ArrayList<>(otherAreaCrnNos); } private static List<Integer> getAreaCrnNos(Integer area, RowLastno rowLastno) { LinkedHashSet<Integer> crnNos = new LinkedHashSet<>(); RowLastno areaRowLastno = findAreaRowLastno(area, rowLastno); if (areaRowLastno == null) { return new ArrayList<>(crnNos); } Integer startCrnNo = resolveAreaStartCrnNo(areaRowLastno, rowLastno); Integer endCrnNo = resolveAreaEndCrnNo(areaRowLastno, rowLastno); if (startCrnNo != null && endCrnNo != null && startCrnNo <= endCrnNo) { for (int crnNo = startCrnNo; crnNo <= endCrnNo; crnNo++) { addAreaCrnNo(crnNos, crnNo, 1, endCrnNo); } for (int crnNo = areaRowLastno.getsCrnNo(); crnNo <= startCrnNo; crnNo++) { addAreaCrnNo(crnNos, crnNo, 1, endCrnNo); } Integer nextCrnQty = startCrnNo + 1; if (areaRowLastno.geteCrnNo() != null && nextCrnQty > areaRowLastno.geteCrnNo()) { nextCrnQty = areaRowLastno.getsCrnNo() == null ? 1 : areaRowLastno.getsCrnNo(); } areaRowLastno.setCrnQty(nextCrnQty); SpringUtils.getBean(RowLastnoService.class).updateById(areaRowLastno); } if (crnNos.isEmpty()) { crnNos.addAll(getFallbackAreaCrnNos(area, rowLastno)); } return new ArrayList<>(crnNos); } private static RowLastno findAreaRowLastno(Integer area, RowLastno defaultRowLastno) { if (area == null) { return defaultRowLastno; } RowLastnoService rowLastnoService = SpringUtils.getBean(RowLastnoService.class); List<RowLastno> typeMatched = rowLastnoService.selectList(new EntityWrapper<RowLastno>() .eq("type_id", area)); if (!Cools.isEmpty(typeMatched)) { return typeMatched.get(0); } List<RowLastno> whsMatched = rowLastnoService.selectList(new EntityWrapper<RowLastno>() .eq("whs_type", area)); if (!Cools.isEmpty(whsMatched)) { return whsMatched.get(0); } return defaultRowLastno; } private static Integer resolveAreaStartCrnNo(RowLastno areaRowLastno, RowLastno defaultRowLastno) { if (areaRowLastno != null && areaRowLastno.getCrnQty() != null && areaRowLastno.getCrnQty() > 0) { return areaRowLastno.getCrnQty(); } if (areaRowLastno != null && areaRowLastno.getsCrnNo() != null && areaRowLastno.getsCrnNo() > 0) { return areaRowLastno.getsCrnNo(); } if (defaultRowLastno != null && defaultRowLastno.getsCrnNo() != null && defaultRowLastno.getsCrnNo() > 0) { return defaultRowLastno.getsCrnNo(); } return 1; } private static Integer resolveAreaEndCrnNo(RowLastno areaRowLastno, RowLastno defaultRowLastno) { if (areaRowLastno != null && areaRowLastno.geteCrnNo() != null && areaRowLastno.geteCrnNo() > 0) { return areaRowLastno.geteCrnNo(); } return null; } private static void addAreaCrnNo(LinkedHashSet<Integer> crnNos, Integer crnNo, Integer startCrnNo, Integer endCrnNo) { if (crnNos == null || crnNo == null || startCrnNo == null || endCrnNo == null) { return; } if (crnNo < startCrnNo || crnNo > endCrnNo) { return; } crnNos.add(crnNo); } private static List<Integer> getAllCrnNos(RowLastno rowLastno) { List<Integer> crnNos = new ArrayList<>(); if (rowLastno == null) { return crnNos; } int startCrnNo = rowLastno.getsCrnNo() == null ? 1 : rowLastno.getsCrnNo(); int endCrnNo = rowLastno.geteCrnNo() == null ? startCrnNo + ((rowLastno.getCrnQty() == null ? 1 : rowLastno.getCrnQty()) - 1) : rowLastno.geteCrnNo(); for (int crnNo = startCrnNo; crnNo <= endCrnNo; crnNo++) { crnNos.add(crnNo); } return crnNos; } private static List<Integer> getFallbackAreaCrnNos(Integer area, RowLastno rowLastno) { List<Integer> allCrnNos = getAllCrnNos(rowLastno); List<Integer> result = new ArrayList<>(); if (Cools.isEmpty(allCrnNos) || area == null || area < 1 || area > 3) { return result; } int total = allCrnNos.size(); int baseSize = total / 3; int remainder = total % 3; int startIndex = 0; for (int currentArea = 1; currentArea < area; currentArea++) { startIndex += baseSize + (currentArea <= remainder ? 1 : 0); } int currentSize = baseSize + (area <= remainder ? 1 : 0); int endIndex = Math.min(startIndex + currentSize, total); for (int index = startIndex; index < endIndex; index++) { result.add(allCrnNos.get(index)); } return result; } private static 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() == null ? 1 : 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 static Integer getCrnRowSpan(Integer typeId) { if (typeId == null) { return null; } switch (typeId) { case 1: return 4; case 2: return 2; default: return null; } } private static String getRun2AreaRowsConfig(Integer area) { Parameter parameter = Parameter.get(); if (parameter == null || area == null) { return null; } switch (area) { case 1: return parameter.getRun2Area1Rows(); case 2: return parameter.getRun2Area2Rows(); case 3: return parameter.getRun2Area3Rows(); default: return null; } } private static List<Integer> parseAreaRows(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) { addAreaRow(orderedRows, row, rowLastno); } continue; } addAreaRow(orderedRows, safeParseInt(segment), rowLastno); } rows.addAll(orderedRows); return rows; } private static void addAreaRow(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 static Integer safeParseInt(String value) { if (Cools.isEmpty(value)) { return null; } try { return Integer.parseInt(value.trim()); } catch (NumberFormatException ignored) { return null; } } private static Integer parseStorageArea(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; } public static String zerofill(String msg, Integer count) { if (msg.length() == count) { return msg; @@ -774,3 +1219,11 @@ return row + "-" + boy + "-" + lev; } } src/main/java/com/zy/common/entity/Parameter.java
@@ -63,4 +63,22 @@ // erp上报 private String erpReport; // AGV area A row config private String agvArea1Rows; // AGV area B row config private String agvArea2Rows; // AGV area C row config private String agvArea3Rows; // run2 area A row config private String run2Area1Rows; // run2 area B row config private String run2Area2Rows; // run2 area C row config private String run2Area3Rows; } src/main/java/com/zy/common/service/CommonService.java
@@ -12,6 +12,7 @@ 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; @@ -26,7 +27,9 @@ 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; @@ -138,36 +141,596 @@ 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); 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<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, @@ -719,6 +1282,10 @@ } 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; @@ -732,71 +1299,72 @@ 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); @@ -806,7 +1374,6 @@ 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>() @@ -1359,8 +1926,10 @@ } } if (Cools.isEmpty(locMast) && sourceStaNo != 4006) {//si'lou'p四楼盘点选择区域 List<LocMast> locMasts = locMastService.selectList(new EntityWrapper<LocMast>() 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()) .orderBy("lev1", true).orderBy("bay1", true)); // 最浅库位 @@ -1377,104 +1946,21 @@ } } } } 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 (found) { break; // ??????????? } 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. @@ -1517,3 +2003,4 @@ } } src/main/java/com/zy/common/web/WcsController.java
@@ -79,7 +79,7 @@ return R.ok(dto1); } List<WrkMast> wrkMasts = wrkMastService.selectList(new EntityWrapper<WrkMast>().eq("io_type", 1)); if (!Cools.isEmpty(wrkMasts)&&wrkMasts.size()>30) { if (!Cools.isEmpty(wrkMasts)&&wrkMasts.size()>100) { return R.error("限行"); } waitPakins = waitPakinService.selectList(new EntityWrapper<WaitPakin>().eq("zpallet", param.getBarcode())); src/main/java/com/zy/system/controller/ConfigController.java
@@ -71,8 +71,8 @@ configService.insert(config); } else { configService.updateById(config); Parameter.reset(); } Parameter.reset(); return R.ok(); } @@ -85,6 +85,7 @@ } } configService.insert(config); Parameter.reset(); return R.ok(); } @@ -111,6 +112,7 @@ return R.error(); } configService.deleteBatchIds(Arrays.asList(ids)); Parameter.reset(); return R.ok(); } src/main/webapp/static/js/basDevp/basDevp.js
@@ -80,6 +80,7 @@ ,{field: 'locType1$', align: 'center',title: '高低'} ,{field: 'barcode', align: 'center',title: '条形码'} ,{field: 'inQty', align: 'center',title: '入库暂存'} ,{field: 'area$', align: 'center',title: '绑定库区'} // ,{field: 'row1', align: 'center',title: ''} // ,{field: 'ioTime$', align: 'center',title: ''} // ,{field: 'area', align: 'center',title: ''} @@ -645,3 +646,4 @@ $("#search").click(); } }); src/main/webapp/views/basDevp/basDevp_detail.html
@@ -150,10 +150,10 @@ <input id="ioTime$" class="layui-input" type="text" autocomplete="off"> </div> </div> <div class="layui-inline" style="width:31%;display: none"> <label class="layui-form-label">:</label> <div class="layui-inline" style="width:31%;"> <label class="layui-form-label">绑定库区:</label> <div class="layui-input-inline"> <input id="area" class="layui-input" type="text"> <input id="area" class="layui-input" type="text" placeholder="空=不限制,支持 1/2/3 或 A/B/C"> </div> </div> <div class="layui-inline" style="width:31%;display: none"> src/main/webapp/views/config/config_detail.html
@@ -69,7 +69,8 @@ </div> <div id="prompt"> 温馨提示:请仔细填写相关信息,<span class="extrude"><span class="not-null">*</span> 为必填选项。</span> 温馨提示:请仔细填写相关信息,<span class="extrude"><span class="not-null">*</span> 为必填选项。</span><br> 库区排配置可维护编码:AreaCrnNo1/AreaCrnNo2/AreaCrnNo3值支持 1-28这样格式的堆垛机号 </div> </form> </div>