#
vincentlu
2025-12-31 db031bf3b7ce28f8e2c912c1556a50c74b47143d
zy-acs-manager/src/main/java/com/zy/acs/manager/core/service/MainService.java
@@ -276,6 +276,9 @@
                            int backpackLev = 0;
                            int backpack = agvService.getBackpack(agvId);
                            List<Integer> usedBackpacks = segmentService.selectUsedBackpacks(currSeg.getTravelId(), agvId);
                            if (usedBackpacks.size() == backpackLev) {
                                throw new CoolException("[Agv:" + agvNo + "] allocate inbound segment failed: no idle backpack to use");
                            }
                            for (int lev = 1; lev <= backpack; lev++) {
                                if (!usedBackpacks.contains(lev)) {
                                    backpackLev = lev;
@@ -313,29 +316,108 @@
                            // place segment
                            TaskTypeType taskType = Objects.requireNonNull(TaskTypeType.get(task.getTaskTypeEl()));
                            TaskPosDto.PosType posType;
                            Long endCodeId;
                            Code endCode;
                            switch (taskType) {
                                case STA_TO_LOC:
                                    posType = TaskPosDto.PosType.DEST_LOC;
                                    Loc destLoc = locService.getById(task.getDestLoc());
                                    endCodeId = destLoc.getCode();
                                    endCode = codeService.getCacheById(destLoc.getCode());
                                    break;
                                case STA_TO_STA:
                                    posType = TaskPosDto.PosType.DEST_STA;
                                    Sta destSta = staService.getById(task.getDestSta());
                                    endCodeId = destSta.getCode();
                                    endCode = codeService.getCacheById(destSta.getCode());
                                    break;
                                default:
                                    throw new BusinessException("[Agv:" + agvNo + "] allocate inbound only supports STA_TO_LOC/STA_TO_STA");
                            }
                            // calc placeSeg serial, through code position
                            List<Segment> destSegList = segmentService.list(new LambdaQueryWrapper<Segment>()
                                    .eq(Segment::getAgvId, agvId)
                                    .eq(Segment::getTravelId, currSeg.getTravelId())
                                    .ne(Segment::getState, SegmentStateType.FINISH.toString())
                                    .in(Segment::getPosType,
                                            TaskPosDto.PosType.DEST_LOC.toString(),
                                            TaskPosDto.PosType.DEST_STA.toString())
                                    .orderByAsc(Segment::getSerial)
                            );
                            // build destPosList and sort
                            List<TaskPosDto> destPosList = new ArrayList<>();
                            for (Segment destSeg : destSegList) {
                                Code code = codeService.getCacheById(destSeg.getEndNode());
                                if (code == null) continue;
                                TaskPosDto dto = new TaskPosDto(destSeg.getTaskId(), new Double[]{code.getX(), code.getY()}, TaskPosDto.PosType.DEST_LOC);
                                dto.setCodeId(code.getId());
                                dto.setSegId(destSeg.getId());
                                destPosList.add(dto);
                            }
                            TaskPosDto newDto = new TaskPosDto(task.getId(), new Double[]{endCode.getX(), endCode.getY()}, TaskPosDto.PosType.DEST_LOC);
                            newDto.setCodeId(endCode.getId());
                            destPosList.add(newDto);
                            destPosList.sort((a, b) -> {
                                int c1 = Double.compare(a.getXy()[0], b.getXy()[0]); // X
                                if (c1 != 0) return c1;
                                int c2 = Double.compare(a.getXy()[1], b.getXy()[1]); // Y
                                if (c2 != 0) return c2;
                                // 同坐标时用 taskId 打散(避免排序不稳定)
                                return Long.compare(a.getTaskId(), b.getTaskId());
                            });
                            // get placeSeg serial
                            int placeSegSerial = 9999;
                            // query placeSeg idx
                            int idx = -1;
                            for (int i = 0; i < destPosList.size(); i++) {
                                if (Objects.equals(destPosList.get(i).getCodeId(), newDto.getCodeId())
                                        && Objects.equals(destPosList.get(i).getTaskId(), newDto.getTaskId())) {
                                    idx = i;
                                    break;
                                }
                            }
                            if (idx < 0) {
                                throw new BusinessException("failed to locate new destination in sorted list");
                            }
                            // 后继点
                            TaskPosDto afterTaskPos = ((idx + 1) < destPosList.size()) ? destPosList.get(idx + 1) : null;
                            if (null == afterTaskPos) {
                                // loadSeg is the last one
                                placeSegSerial = destSegList.get(destSegList.size() - 1).getSerial() + 1;
                            } else {
                                Segment afterSeg = destSegList.stream()
                                        .filter(destSeg -> Objects.equals(destSeg.getId(), afterTaskPos.getSegId()))
                                        .findFirst()
                                        .orElse(null);
                                if (null == afterSeg) {
                                    placeSegSerial = destSegList.get(destSegList.size() - 1).getSerial() + 1;
                                } else {
                                    placeSegSerial = afterSeg.getSerial();
                                }
                            }
//                            placeSegSerial = Math.max(placeSegSerial, currSeg.getSerial() + 1);
                            // shift serial after
                            segmentService.update(new LambdaUpdateWrapper<Segment>()
                                    .eq(Segment::getAgvId, agvId)
                                    .eq(Segment::getTravelId, currSeg.getTravelId())
                                    .ge(Segment::getSerial, placeSegSerial)
                                    .ne(Segment::getState, SegmentStateType.FINISH.toString())
                                    .setSql("serial = serial + 1")
                                    .set(Segment::getUpdateTime, now)
                            );
                            // save place segment
                            Segment placeSeg = new Segment();
                            placeSeg.setUuid(String.valueOf(snowflakeIdWorker.nextId()).substring(3));
                            placeSeg.setTravelId(currSeg.getTravelId());
                            placeSeg.setAgvId(agvId);
                            placeSeg.setTaskId(task.getId());
                            placeSeg.setSerial();
                            placeSeg.setEndNode(endCodeId);
                            placeSeg.setSerial(placeSegSerial);
                            placeSeg.setEndNode(endCode.getId());
                            placeSeg.setPosType(posType.toString());
                            placeSeg.setState(SegmentStateType.INIT.toString());
                            placeSeg.setBackpack(backpackLev);