1
zhang
2025-06-17 bc64b3b45cf00885696aea4cfa97230390717012
zy-acs-manager/src/main/java/com/zy/acs/manager/core/service/BaseMainService.java
File was renamed from zy-acs-manager/src/main/java/com/zy/acs/manager/core/service/MainService.java
@@ -19,14 +19,10 @@
import com.zy.acs.framework.common.Cools;
import com.zy.acs.framework.common.SnowflakeIdWorker;
import com.zy.acs.framework.exception.CoolException;
import com.zy.acs.manager.common.domain.TaskDto;
import com.zy.acs.manager.common.domain.param.HandlerPublishParam;
import com.zy.acs.manager.common.exception.BusinessException;
import com.zy.acs.manager.core.domain.AgvBackpackDto;
import com.zy.acs.manager.core.domain.Lane;
import com.zy.acs.manager.core.domain.TaskPosDto;
import com.zy.acs.manager.core.service.astart.MapDataDispatcher;
import com.zy.acs.manager.manager.controller.param.OpenBusSubmitParam;
import com.zy.acs.manager.manager.entity.*;
import com.zy.acs.manager.manager.enums.*;
import com.zy.acs.manager.manager.service.*;
@@ -41,14 +37,13 @@
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import java.util.*;
import java.util.stream.Collectors;
/**
 * Created by vincent on 2023/6/14
 */
@Slf4j
@Service("mainService")
public class MainService implements IMainService {
@Service("baseMainService")
public class BaseMainService implements IBaseMainService {
    @Autowired
    private BusService busService;
@@ -96,439 +91,6 @@
    private LaneService laneService;
    @Autowired
    private ActionSorter actionSorter;
    @SuppressWarnings("all")
    @Transactional
    public Bus generateBusAndTask(OpenBusSubmitParam busSubmitParam, String memo) {
        String errorMsg = busService.checkoutValid(busSubmitParam);
        if (!Cools.isEmpty(errorMsg)) {
            throw new BusinessException(errorMsg);
        }
        String batch = busSubmitParam.getBatch();
        List<TaskDto> taskDtoList = busSubmitParam.getTaskList();
        if (Cools.isEmpty(taskDtoList)) {
            throw new BusinessException("taskList can't be empty!");
        }
        // 优先级排序
        taskDtoList.sort((o1, o2) -> o2.getPriority() - o1.getPriority());
        // 校验
        List<Task> taskList = validService.validTaskDtoList(taskDtoList);
        // 保存总线
        Date now = new Date();
        Bus bus = new Bus();
        bus.setUuid(String.valueOf(snowflakeIdWorker.nextId()).substring(3));
        bus.setBusNo(batch);
        bus.setStartTime(now);
        bus.setBusSts(BusStsType.RECEIVE.val());
        bus.setMemo(memo);
        if (!busService.save(bus)) {
            throw new BusinessException("Internal Server Error!");
        }
        // 保存任务
        for (Task task : taskList) {
            task.setBusId(bus.getId());
            task.setTaskSts(TaskStsType.INIT.val());
            if (!taskService.save(task)) {
                throw new BusinessException("seqNum:" + task.getSeqNum() + " failed to save!");
            }
            // 修改库位状态
            Loc oriLoc = null;
            Loc destLoc = null;
            Sta oriSta = null;
            Sta destSta = null;
            switch (TaskTypeType.get(task.getTaskTypeEl())) {
                case LOC_TO_LOC:
                    oriLoc = locService.getById(task.getOriLoc());
                    if (!oriLoc.getLocSts().equals(LocStsType.STOCK.val())) {
                        throw new BusinessException("oriLoc:" + task.getOriLoc$() + " is not in STOCK status");
                    }
                    oriLoc.setLocSts(LocStsType.PAKOUT.val());
                    oriLoc.setUpdateTime(now);
                    if (!locService.updateById(oriLoc)) {
                        throw new BusinessException("oriLoc:" + task.getOriLoc$() + " failed to update");
                    }
                    destLoc = locService.getById(task.getDestLoc());
                    if (!destLoc.getLocSts().equals(LocStsType.IDLE.val())) {
                        throw new BusinessException("destLoc:" + task.getDestLoc$() + " is not in IDLE status");
                    }
                    destLoc.setLocSts(LocStsType.PAKIN.val());
                    destLoc.setUpdateTime(now);
                    if (!locService.updateById(destLoc)) {
                        throw new BusinessException("destLoc:" + task.getDestLoc$() + " failed to update");
                    }
                    break;
                case LOC_TO_STA:
                    oriLoc = locService.getById(task.getOriLoc());
                    if (!oriLoc.getLocSts().equals(LocStsType.STOCK.val())) {
                        throw new BusinessException("oriLoc:" + task.getOriLoc$() + " is not in STOCK status");
                    }
                    oriLoc.setLocSts(LocStsType.PAKOUT.val());
                    oriLoc.setUpdateTime(now);
                    if (!locService.updateById(oriLoc)) {
                        throw new BusinessException("oriLoc:" + task.getOriLoc$() + " failed to update");
                    }
                    destSta = staService.getById(task.getDestSta());
                    if (!destSta.getStaSts().equals(StaStsType.IDLE.val())) {
                        throw new BusinessException("destSta:" + task.getDestSta$() + " is not in IDLE status");
                    }
                    destSta.setStaSts(StaStsType.READY_RELEASE.val());
                    destSta.setUpdateTime(now);
                    if (!staService.updateById(destSta)) {
                        throw new BusinessException("destSta:" + task.getDestSta$() + " failed to update");
                    }
                    break;
                case STA_TO_LOC:
                    oriSta = staService.getById(task.getOriSta());
                    if (!oriSta.getStaSts().equals(StaStsType.STOCK.val())) {
                        throw new BusinessException("oriSta:" + task.getOriSta$() + " is not in STOCK status");
                    }
                    oriSta.setStaSts(StaStsType.READY_TAKE.val());
                    oriSta.setUpdateTime(now);
                    if (!staService.updateById(oriSta)) {
                        throw new BusinessException("oriSta:" + task.getOriSta$() + " failed to update");
                    }
                    destLoc = locService.getById(task.getDestLoc());
                    if (!destLoc.getLocSts().equals(LocStsType.IDLE.val())) {
                        throw new BusinessException("destLoc:" + task.getDestLoc$() + " is not in IDLE status");
                    }
                    destLoc.setLocSts(LocStsType.PAKIN.val());
                    destLoc.setUpdateTime(now);
                    if (!locService.updateById(destLoc)) {
                        throw new BusinessException("destLoc:" + task.getDestLoc$() + " failed to update");
                    }
                    break;
                case STA_TO_STA:
                    oriSta = staService.getById(task.getOriSta());
                    if (!oriSta.getStaSts().equals(StaStsType.STOCK.val())) {
                        throw new BusinessException("oriSta:" + task.getOriSta$() + " is not in STOCK status");
                    }
                    oriSta.setStaSts(StaStsType.READY_TAKE.val());
                    oriSta.setUpdateTime(now);
                    if (!staService.updateById(oriSta)) {
                        throw new BusinessException("oriSta:" + task.getOriSta$() + " failed to update");
                    }
                    destSta = staService.getById(task.getDestSta());
                    if (!destSta.getStaSts().equals(StaStsType.IDLE.val())) {
                        throw new BusinessException("destSta:" + task.getDestSta$() + " is not in IDLE status");
                    }
                    destSta.setStaSts(StaStsType.READY_RELEASE.val());
                    destSta.setUpdateTime(now);
                    if (!staService.updateById(destSta)) {
                        throw new BusinessException("destSta:" + task.getDestSta$() + " failed to update");
                    }
                    break;
                default:
                    break;
            }
        }
        return bus;
    }
    /**
     * 任务分配给车辆 ( 车辆此时是空闲且静止的 )
     */
    @Transactional
    public synchronized void allocateTask(Bus bus) {
        try {
            Date now = new Date();
            List<Task> taskList = taskService.list(new LambdaQueryWrapper<Task>()
                    .eq(Task::getBusId, bus.getId())
                    .eq(Task::getTaskSts, TaskStsType.INIT.val())
                    .orderByDesc(Task::getPriority)
            );
            if (Cools.isEmpty(taskList)) {
                bus.setBusSts(BusStsType.PROGRESS.val());
                bus.setUpdateTime(now);
                if (!busService.updateById(bus)) {
                    log.error("Bus [{}] failed to Update !!!", bus.getUuid());
                }
                return;
            }
            List<Long> taskIds = taskList.stream().map(Task::getId).distinct().collect(Collectors.toList());
            for (Task task : taskList) {
                Agv agv = allocateService.execute(task);
                if (null == agv) {
//                    log.warn("Task[{}] has an issue, because it failed to check out agv which is idle...", task.getSeqNum());
                    continue;
                }
                task.setAgvId(agv.getId());
                task.setTaskSts(TaskStsType.WAITING.val());
                task.setIoTime(now);
                task.setUpdateTime(now);
                if (!taskService.updateById(task)) {
                    throw new BusinessException("seqNum: " + task.getSeqNum() + " failed to update");
                }
            }
        } catch (Exception e) {
            log.error("mainService.infuseAgvForTask", e);
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
    }
    /**
     * 解析取放货集合任务,进行最优的排列组合顺序 ( 车辆此时是空闲且静止的 )
     * todo: {@link com.zy.acs.manager.core.HandlerController#controlAgv(String, HandlerPublishParam)}
     */
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void buildMajorTask(Long agvId, List<Task> taskList) {
        if (Cools.isEmpty(agvId, taskList)) {
            return;
        }
        try {
            // valid -----------------------------------------------
            Agv agv = agvService.getById(agvId);
            if (!agvService.judgeEnable(agv.getId(), false)) {
                return;
            }
            if (!Cools.isEmpty(taskService.selectInSts(agvId, TaskStsType.ASSIGN, TaskStsType.PROGRESS))) {
                throw new CoolException("AGV[" + agv.getUuid() + "]分配任务失败,已存在执行任务...");
            }
            if (!Cools.isEmpty(segmentService.getByAgvAndState(agv.getId(), SegmentStateType.WAITING.toString()))
                    || !Cools.isEmpty(segmentService.getByAgvAndState(agv.getId(), SegmentStateType.RUNNING.toString()))) {
                throw new CoolException("AGV[" + agv.getUuid() + "] failed to assign,because already has the segment in running...");
            }
            // execute ----------------------------------------------------
            Date now = new Date();
            // sort and sub
            taskList.sort(new Comparator<Task>() {
                @Override
                public int compare(Task o1, Task o2) {
                    return o2.getPriority() - o1.getPriority();
                }
            });
            Integer backpack = agvService.getBackpack(agv);
            if (taskList.size() > backpack) {
                taskList = taskList.subList(0, backpack);
            }
            AgvDetail agvDetail = agvDetailService.selectByAgvId(agvId);
            List<AgvBackpackDto> backpackDtoList = new ArrayList<>();
            /**
             * 同巷道归类
             * same lane for every single agvId
             *
             * key: y(val) + TaskPosDto.PosType.ORI_LOC / ORI_STA / DEST_LOC / DEST_STA
             * val: new TaskPosDto(taskId, new Double[]{code.getX(), code.getY()}, posType)
             */
            Map<String, List<TaskPosDto>> groups = new HashMap<>();
            final String sameGroupXy = configService.getVal("sameGroupXy", String.class);
            int backpackLev = 0;
            for (Task task : taskList) {
                backpackLev++;
                Code startCode = null;
                Code endCode = null;
                Loc oriLoc = null;
                Loc destLoc = null;
                Sta oriSta = null;
                Sta destSta = null;
                switch (Objects.requireNonNull(TaskTypeType.get(task.getTaskTypeEl()))) {
                    case LOC_TO_LOC:
                        oriLoc = locService.getById(task.getOriLoc());
                        destLoc = locService.getById(task.getDestLoc());
                        startCode = codeService.getCacheById(oriLoc.getCode());
                        endCode = codeService.getCacheById(destLoc.getCode());
                        TaskPosDto.packagePosGroup(groups, task, startCode, TaskPosDto.PosType.ORI_LOC, sameGroupXy);
                        TaskPosDto.packagePosGroup(groups, task, endCode, TaskPosDto.PosType.DEST_LOC, sameGroupXy);
                        break;
                    case LOC_TO_STA:
                        oriLoc = locService.getById(task.getOriLoc());
                        destSta = staService.getById(task.getDestSta());
                        startCode = codeService.getCacheById(oriLoc.getCode());
                        endCode = codeService.getCacheById(destSta.getCode());
                        TaskPosDto.packagePosGroup(groups, task, startCode, TaskPosDto.PosType.ORI_LOC, sameGroupXy);
                        TaskPosDto.packagePosGroup(groups, task, endCode, TaskPosDto.PosType.DEST_STA, sameGroupXy);
                        break;
                    case STA_TO_LOC:
                        oriSta = staService.getById(task.getOriSta());
                        destLoc = locService.getById(task.getDestLoc());
                        startCode = codeService.getCacheById(oriSta.getCode());
                        endCode = codeService.getCacheById(destLoc.getCode());
                        TaskPosDto.packagePosGroup(groups, task, startCode, TaskPosDto.PosType.ORI_STA, sameGroupXy);
                        TaskPosDto.packagePosGroup(groups, task, endCode, TaskPosDto.PosType.DEST_LOC, sameGroupXy);
                        break;
                    case STA_TO_STA:
                        oriSta = staService.getById(task.getOriSta());
                        destSta = staService.getById(task.getDestSta());
                        startCode = codeService.getCacheById(oriSta.getCode());
                        endCode = codeService.getCacheById(destSta.getCode());
                        TaskPosDto.packagePosGroup(groups, task, startCode, TaskPosDto.PosType.ORI_STA, sameGroupXy);
                        TaskPosDto.packagePosGroup(groups, task, endCode, TaskPosDto.PosType.DEST_STA, sameGroupXy);
                        break;
                    default:
                        throw new BusinessException(task.getSeqNum() + "任务类型错误");
                }
                if (backpackLev > backpack) {
                    throw new BusinessException("解析Task失败,AGV背篓已满......");
                }
                backpackDtoList.add(new AgvBackpackDto(backpackLev, task.getId()));
            }
            /**
             * 1.Map<String, List<TaskPosDto>> groups
             *
             * key: 1000 + ORIGIN
             * val: [TaskPosDto(taskId, new Double[]{code.getX(), code.getY()}, posType), TaskPosDto(taskId, new Double[]{code.getX(), code.getY()}, posType),...]
             *
             * key: 3000 + ORIGIN
             * val: [TaskPosDto(taskId, new Double[]{code.getX(), code.getY()}, posType), TaskPosDto(taskId, new Double[]{code.getX(), code.getY()}, posType),...]
             *
             * key: 2000 + ORIGIN
             * val: [TaskPosDto(taskId, new Double[]{code.getX(), code.getY()}, posType), TaskPosDto(taskId, new Double[]{code.getX(), code.getY()}, posType),...]
             *
             * key: 1000 + DESTINATION
             * val: [TaskPosDto(taskId, new Double[]{code.getX(), code.getY()}, posType), TaskPosDto(taskId, new Double[]{code.getX(), code.getY()}, posType),...]
             *
             * key: 2000 + DESTINATION
             * val: [TaskPosDto(taskId, new Double[]{code.getX(), code.getY()}, posType), TaskPosDto(taskId, new Double[]{code.getX(), code.getY()}, posType),...]
             *
             * ......
             *
             * 2.ArrayList<List<TaskPosDto>> list
             * [
             *  [TaskPosDto(taskId, new Double[]{code.getX(), code.getY()}, posType), TaskPosDto(taskId, new Double[]{code.getX(), code.getY()}, posType),...],
             *  [TaskPosDto(taskId, new Double[]{code.getX(), code.getY()}, posType), TaskPosDto(taskId, new Double[]{code.getX(), code.getY()}, posType),...],
             *  [TaskPosDto(taskId, new Double[]{code.getX(), code.getY()}, posType), TaskPosDto(taskId, new Double[]{code.getX(), code.getY()}, posType),...],
             *  ......
             * ]
             */
            /**
             * 对所有巷道进行有序排序,先是针对List为一个单位,对他们进行FirstWeight排序,相当于表1的key的数值进行有序排序
             *  List<TaskPosDto>: task list on the same lane
             *  ArrayList<List<TaskPosDto>>: all the task list by one agv
             *
             *  tip: ORI 和 DEST 永远不会存在同一个 List
             */
            ArrayList<List<TaskPosDto>> list = new ArrayList<>(groups.values());
            list.sort((o1, o2) -> {
                double o1CompVal = (o1.get(0).getFirstWeight(sameGroupXy) * 100) + o1.get(0).getPosType().compOffset;
                double o2CompVal = (o2.get(0).getFirstWeight(sameGroupXy) * 100) + o2.get(0).getPosType().compOffset;
                return (int) (o1CompVal - o2CompVal);
            });
            // 针对Dto,按照SecondWeight进行排序
            for (List<TaskPosDto> taskPosDtoList : list) {
                taskPosDtoList.sort((o1, o2) -> (int) (o1.getSecondWeight(sameGroupXy) * 100 - o2.getSecondWeight(sameGroupXy) * 100));
            }
            // re-order by agv current position
            Code currCode = codeService.getCacheById(agvDetail.getRecentCode());
            Double[] currPosition = new Double[]{currCode.getX(), currCode.getY()};
            List<List<TaskPosDto>> pickGroups = new ArrayList<>();
            List<List<TaskPosDto>> dropGroups = new ArrayList<>();
            for (List<TaskPosDto> group : list) {
                // Assume 一个任务组中所有TaskPosDto的PosType.brief相同
                TaskPosDto.PosType posType = group.get(0).getPosType();
                if (posType == TaskPosDto.PosType.ORI_LOC || posType == TaskPosDto.PosType.ORI_STA) {
                    pickGroups.add(group);
                } else if (posType == TaskPosDto.PosType.DEST_LOC || posType == TaskPosDto.PosType.DEST_STA) {
                    dropGroups.add(group);
                } else {
                    // import tip: the list must only contain ORIGIN and DESTINATION
                    log.error("the list must only contain ORIGIN and DESTINATION");
                }
            }
            currPosition = allocateService.pac(currPosition, pickGroups);
            currPosition = allocateService.pac(currPosition, dropGroups);
            List<List<TaskPosDto>> reorderedList = new ArrayList<>();
            reorderedList.addAll(pickGroups);
            reorderedList.addAll(dropGroups);
            list.clear();
            list.addAll(reorderedList);
            // generate travel
            Travel travel = new Travel();
            travel.setUuid(String.valueOf(snowflakeIdWorker.nextId()).substring(3));
            travel.setTravelId(String.valueOf(snowflakeIdWorker.nextId()).substring(3));
            travel.setAgvId(agvId);
            travel.setTaskContent(JSON.toJSONString(list));
            travel.setTaskIds(JSON.toJSONString(taskList.stream().map(Task::getId).collect(Collectors.toList())));
            travel.setState(TravelStateType.RUNNING.toString());
            if (!travelService.save(travel)) {
                throw new BusinessException("任务组保存失败");
            }
            // generate segment
            int segSerial = 0;
            List<Segment> segmentList = new ArrayList<>();
            for (List<TaskPosDto> dtoList : list) {
                for (TaskPosDto taskPosDto : dtoList) {
                    segSerial++;
                    AgvBackpackType backpackType = AgvBackpackDto.find(backpackDtoList, taskPosDto.getTaskId());
                    assert null != backpackType;
                    Segment segment = new Segment();
                    segment.setUuid(String.valueOf(snowflakeIdWorker.nextId()).substring(3));
                    segment.setTravelId(travel.getId());
                    segment.setAgvId(agvId);
                    segment.setTaskId(taskPosDto.getTaskId());
                    segment.setSerial(segSerial);
                    segment.setEndNode(taskPosDto.getCodeId());
                    segment.setPosType(taskPosDto.getPosType().toString());
                    segment.setBackpack(backpackType.lev);
                    segment.setState(SegmentStateType.INIT.toString());
                    segmentList.add(segment);
                }
            }
            for (int i = 0; i < segmentList.size(); i++) {
                Segment segment = segmentList.get(i);
                if (i == 0) {
                    segment.setState(SegmentStateType.WAITING.toString());
                }
                if (!segmentService.save(segment)) {
                    throw new BusinessException("任务组保存失败");
                }
            }
            // task
            for (Task task : taskList) {
                task.setTaskSts(TaskStsType.ASSIGN.val());
                task.setStartTime(now);
                task.setUpdateTime(now);
                if (!taskService.updateById(task)) {
                    throw new BusinessException(task.getUuid() + "任务更新失败");
                }
            }
        } catch (Exception e) {
            log.error("mainService.buildMajorTask[task]", e);
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
    }
    /**