#
Junjie
2025-04-07 901f9ca15fb0ce3bcf2ebf956c569c260050c561
zy-asrs-wcs/src/main/java/com/zy/asrs/wcs/core/utils/ShuttleDispatcher.java
@@ -1,38 +1,31 @@
package com.zy.asrs.wcs.core.utils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zy.asrs.framework.common.Cools;
import com.zy.asrs.framework.common.SnowflakeIdWorker;
import com.zy.asrs.framework.exception.CoolException;
import com.zy.asrs.wcs.core.entity.BasShuttle;
import com.zy.asrs.wcs.core.entity.Motion;
import com.zy.asrs.wcs.core.entity.Task;
import com.zy.asrs.wcs.core.entity.TaskCtg;
import com.zy.asrs.wcs.core.entity.*;
import com.zy.asrs.wcs.core.kernel.AnalyzeService;
import com.zy.asrs.wcs.core.model.NavigateNode;
import com.zy.asrs.wcs.core.model.enums.LiftCodeType;
import com.zy.asrs.wcs.core.model.enums.NavigationMapType;
import com.zy.asrs.wcs.core.model.enums.TaskStsType;
import com.zy.asrs.wcs.core.service.BasShuttleService;
import com.zy.asrs.wcs.core.service.TaskCtgService;
import com.zy.asrs.wcs.core.service.TaskService;
import com.zy.asrs.wcs.core.model.enums.*;
import com.zy.asrs.wcs.core.service.*;
import com.zy.asrs.wcs.rcs.News;
import com.zy.asrs.wcs.rcs.cache.SlaveConnection;
import com.zy.asrs.wcs.rcs.entity.Device;
import com.zy.asrs.wcs.rcs.entity.DeviceType;
import com.zy.asrs.wcs.rcs.model.enums.ShuttleProtocolStatusType;
import com.zy.asrs.wcs.rcs.model.enums.SlaveType;
import com.zy.asrs.wcs.rcs.model.protocol.ShuttleProtocol;
import com.zy.asrs.wcs.rcs.service.DeviceService;
import com.zy.asrs.wcs.rcs.service.DeviceTypeService;
import com.zy.asrs.wcs.core.service.MotionService;
import com.zy.asrs.wcs.rcs.thread.LiftThread;
import com.zy.asrs.wcs.rcs.thread.ShuttleThread;
import com.zy.asrs.wcs.system.entity.Dict;
import com.zy.asrs.wcs.system.service.DictService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
import java.util.List;
import java.util.*;
/**
 * Created by vincent on 2023/10/12
@@ -49,8 +42,6 @@
    private LiftDispatcher liftDispatcher;
    @Autowired
    private SnowflakeIdWorker snowflakeIdWorker;
//    @Autowired
//    private CommonService commonService;
    @Autowired
    private AnalyzeService analyzeService;
    @Autowired
@@ -58,31 +49,128 @@
    @Autowired
    private DeviceService deviceService;
    @Autowired
    private DeviceTypeService deviceTypeService;
    @Autowired
    private BasShuttleService basShuttleService;
    @Autowired
    private TaskCtgService taskCtgService;
    @Autowired
    private ShuttleStandbyService shuttleStandbyService;
    @Autowired
    private DictService dictService;
    @Autowired
    private NavigateUtils navigateUtils;
    public ShuttleThread queryShuttleWhichConvenient(Task task, Integer liftNo) {
        String locNo = taskService.judgeInbound(task) ? task.getDestLoc() : task.getOriginLoc();
    /**
     * 调度车辆
     */
    public Device dispatchShuttle(Task task, String locNo) {
        ArrayList<ShuttleThread> sameLev = new ArrayList<>();//相同楼层的穿梭车
        ArrayList<ShuttleThread> diffLev = new ArrayList<>();//不同楼层的穿梭车
        ShuttleThread resThread = null;
        Integer finalDistance = ShuttleDispatcher.INF;
        DeviceType deviceType = deviceTypeService.getOne(new LambdaQueryWrapper<DeviceType>()
                .eq(DeviceType::getHostId, task.getHostId())
                .eq(DeviceType::getStatus, 1)
                .eq(DeviceType::getFlag, String.valueOf(SlaveType.Shuttle)));
        if (deviceType == null) {
            throw new CoolException("设备类型不存在");
        }
        int lev = Utils.getLev(locNo);
        List<Device> list = deviceService.list(new LambdaQueryWrapper<Device>()
                .eq(Device::getDeviceType, deviceType.getId())
                .eq(Device::getDeviceType, DeviceCtgType.SHUTTLE.val())
                .eq(Device::getHostId, task.getHostId())
                .eq(Device::getStatus, 1));
        //获取同层小车
        List<Device> currentLevDevices = new ArrayList<>();
        //获取跨层小车
        HashMap<Integer, List<Device>> diffLevDeviceMap = new HashMap<>();
        for (Device device : list) {
            //获取四向穿梭车线程
            ShuttleThread shuttleThread = (ShuttleThread) SlaveConnection.get(SlaveType.Shuttle, device.getId().intValue());
            ShuttleProtocol shuttleProtocol = shuttleThread.getStatus();
            if (shuttleProtocol == null || shuttleProtocol.getShuttleNo() == null) {
                continue;
            }
            if (shuttleProtocol.getCurrentLocNo() == null) {
                continue;
            }
            int shuttleLev = Utils.getLev(shuttleProtocol.getCurrentLocNo());
            if (shuttleLev == lev) {
                currentLevDevices.add(device);
            } else {
                List<Device> devices = null;
                if (diffLevDeviceMap.containsKey(shuttleLev)) {
                    devices = diffLevDeviceMap.get(shuttleLev);
                } else {
                    devices = new ArrayList<>();
                }
                devices.add(device);
                diffLevDeviceMap.put(shuttleLev, devices);
            }
        }
        //搜索同层
        resThread = this.searchCurrentLevShuttle(currentLevDevices, locNo);
        //同层没有搜索到合适小车,跨楼层搜索
        if (resThread == null) {
            resThread = this.searchDiffLevShuttle(diffLevDeviceMap, locNo, task);
        }
        if (resThread != null) {
            Task result = generateMoveTask(resThread.getDevice(), locNo);
            if (result != null) {
                return resThread.getDevice();
            }
        }
        News.info("{}目标库位没有搜索到可用穿梭车", locNo);
        return null;
    }
    public synchronized ShuttleThread searchIdleShuttle(Task task) {
        String locNo = taskService.judgeInbound(task) ? task.getDestLoc() : task.getOriginLoc();
        ShuttleThread resThread = null;
        int lev = Utils.getLev(locNo);
        List<Device> list = deviceService.list(new LambdaQueryWrapper<Device>()
                .eq(Device::getDeviceType, DeviceCtgType.SHUTTLE.val())
                .eq(Device::getHostId, task.getHostId())
                .eq(Device::getStatus, 1));
        //获取同层小车
        List<Device> currentLevDevices = new ArrayList<>();
        //获取跨层小车
        HashMap<Integer,List<Device>> diffLevDeviceMap = new HashMap<>();
        for (Device device : list) {
            //获取四向穿梭车线程
            ShuttleThread shuttleThread = (ShuttleThread) SlaveConnection.get(SlaveType.Shuttle, device.getId().intValue());
            ShuttleProtocol shuttleProtocol = shuttleThread.getStatus();
            if (shuttleProtocol == null || shuttleProtocol.getShuttleNo() == null) {
                continue;
            }
            int shuttleLev = Utils.getLev(shuttleProtocol.getCurrentLocNo());
            if (shuttleLev == lev) {
                currentLevDevices.add(device);
            }else {
                List<Device> devices = null;
                if(diffLevDeviceMap.containsKey(shuttleLev)) {
                    devices = diffLevDeviceMap.get(shuttleLev);
                }else {
                    devices = new ArrayList<>();
                }
                devices.add(device);
                diffLevDeviceMap.put(shuttleLev, devices);
            }
        }
        //搜索同层
        resThread = this.searchCurrentLevShuttle(currentLevDevices, locNo);
        //同层没有搜索到合适小车,跨楼层搜索
        if(resThread == null) {
            resThread = this.searchDiffLevShuttle(diffLevDeviceMap, locNo, task);
        }
        return resThread;
    }
    private synchronized ShuttleThread searchCurrentLevShuttle(List<Device> devices, String locNo) {
        ShuttleThread resThread = null;
        Integer finalDistance = ShuttleDispatcher.INF;
        for (Device device : devices) {
            if (taskService.hasBusyOutboundByShuttle(Integer.parseInt(device.getDeviceNo()))) {
                continue;
            }
@@ -97,6 +185,21 @@
                continue;
            }
            BasShuttle basShuttle = basShuttleService.getOne(new LambdaQueryWrapper<BasShuttle>()
                    .eq(BasShuttle::getShuttleNo, device.getDeviceNo())
                    .eq(BasShuttle::getHostId, device.getHostId()));
            if (basShuttle == null) {
                continue;//小车基础数据不存在
            }
            if (!Cools.isEmpty(basShuttle.getDisableLev())) {
                List<Integer> disableLev = JSON.parseArray(basShuttle.getDisableLev(), Integer.class);
                //检查小车是否禁用该楼层
                if (disableLev.contains(Utils.getLev(locNo))) {
                    continue;//小车禁用该楼层跳过该车
                }
            }
            //检测是否存在充电任务
            Task taskCharge = taskService.selectChargeWorking(Integer.valueOf(device.getDeviceNo()));
            if (taskCharge != null) {
@@ -104,6 +207,12 @@
            }
            // 有没有被其他任务调度
            List<Task> taskList = taskService.selectWorkingByShuttle(Integer.valueOf(device.getDeviceNo()), null);
            if (!taskList.isEmpty()) {
                continue;
            }
            int currentLev = Utils.getLev(shuttleProtocol.getCurrentLocNo());//小车当前层高
            String currentLocNo = shuttleProtocol.getCurrentLocNo();//小车当前库位号
@@ -112,32 +221,22 @@
                break;
            }
            String targetLocNo = LiftCodeType.getStandbyLocNo(liftNo, currentLev);//默认到提升机待机位
            // 同楼层直接计算到目标库位
            if (currentLev == Utils.getLev(locNo)) {
                targetLocNo = locNo;
            }
            //当前穿梭车线程到当前车子所在楼层的提升机口距离
            List<NavigateNode> currentShuttlePath = NavigateUtils.calc(
            //当前穿梭车线程到当前车子所在楼层的目标库位距离
            List<NavigateNode> currentShuttlePath = navigateUtils.calcWhiteList(
                    currentLocNo
                    , targetLocNo
                    , NavigationMapType.NORMAL.id
                    , locNo
                    , NavigationMapType.DFX.id
                    , Utils.getShuttlePoints(Integer.parseInt(shuttleThread.getDevice().getDeviceNo()), currentLev)
            );//搜索空闲穿梭车,使用正常通道地图
            if (currentShuttlePath == null) {
                continue;
            }
            Integer currDistance = NavigateUtils.getOriginPathAllDistance(currentShuttlePath);//计算当前路径行走总距离
            // 不同楼层权重
            if (currentLev != Utils.getLev(locNo)) {
                currDistance += WEIGHT;
            }
            Integer currDistance = navigateUtils.getOriginPathAllDistance(currentShuttlePath);//计算当前路径行走总距离
            // 挂载任务权重
            List<Task> tasks = taskService.selectWorkingByShuttle(Integer.valueOf(device.getDeviceNo()));
            List<Task> tasks = taskService.selectWorkingByShuttle(Integer.valueOf(device.getDeviceNo()), null);
            if (!Cools.isEmpty(tasks)) {
                currDistance += tasks.size() * WEIGHT;
            }
@@ -151,6 +250,117 @@
        return resThread;
    }
    private synchronized ShuttleThread searchDiffLevShuttle(HashMap<Integer,List<Device>> devicesMap, String locNo, Task task) {
        ShuttleThread resThread = null;
        Integer finalDistance = ShuttleDispatcher.INF;
        //检测目标楼层车数量是否小于允许的最大数量
        boolean checkDispatchMaxNum = checkDispatchMaxNum(Utils.getLev(locNo), task.getHostId());
        for (Map.Entry<Integer, List<Device>> entry : devicesMap.entrySet()) {
            Integer lev = entry.getKey();
            List<Device> devices = entry.getValue();
            for (Device device : devices) {
                if (taskService.hasBusyOutboundByShuttle(Integer.parseInt(device.getDeviceNo()))) {
                    continue;
                }
                //获取四向穿梭车线程
                ShuttleThread shuttleThread = (ShuttleThread) SlaveConnection.get(SlaveType.Shuttle, device.getId().intValue());
                ShuttleProtocol shuttleProtocol = shuttleThread.getStatus();
                if (shuttleProtocol == null || shuttleProtocol.getShuttleNo() == null) {
                    continue;
                }
                if (!shuttleThread.isIdle()) {
                    continue;
                }
                BasShuttle basShuttle = basShuttleService.getOne(new LambdaQueryWrapper<BasShuttle>()
                        .eq(BasShuttle::getShuttleNo, device.getDeviceNo())
                        .eq(BasShuttle::getHostId, device.getHostId()));
                if (basShuttle == null) {
                    continue;//小车基础数据不存在
                }
                if (!Cools.isEmpty(basShuttle.getDisableLev())) {
                    List<Integer> disableLev = JSON.parseArray(basShuttle.getDisableLev(), Integer.class);
                    //检查小车是否禁用该楼层
                    if (disableLev.contains(Utils.getLev(locNo))) {
                        continue;//小车禁用该楼层跳过该车
                    }
                }
                //检测是否存在充电任务
                Task taskCharge = taskService.selectChargeWorking(Integer.valueOf(device.getDeviceNo()));
                if (taskCharge != null) {
                    continue;
                }
                // 有没有被其他任务调度
                List<Task> taskList = taskService.selectWorkingByShuttle(Integer.valueOf(device.getDeviceNo()), null);
                if (!taskList.isEmpty()) {
                    continue;
                }
                int currentLev = Utils.getLev(shuttleProtocol.getCurrentLocNo());//小车当前层高
                String currentLocNo = shuttleProtocol.getCurrentLocNo();//小车当前库位号
                if (!checkDispatchMaxNum) {
                    News.info("{}任务,{}层,已经达到当前楼层调度车辆最大值", task.getTaskNo(), Utils.getLev(locNo));
                    continue;
                }
                //获取距离小车位置最近的空闲可换层提升机
                LiftThread liftThread = liftDispatcher.searchIdleLift(currentLocNo, task.getHostId(), true);
                if (liftThread == null) {
                    continue;
                }
                Device recentTransferLift = liftThread.getDevice();
                //获取小车楼层提升机待机位
                ShuttleStandby shuttleStandby = shuttleStandbyService.getOne(new LambdaQueryWrapper<ShuttleStandby>()
                        .eq(ShuttleStandby::getDeviceId, recentTransferLift.getId())
                        .eq(ShuttleStandby::getDeviceLev, currentLev)
                        .eq(ShuttleStandby::getStatus, 1));
                String targetLocNo = shuttleStandby.getDeviceLoc();
                //当前穿梭车线程到当前车子所在楼层的提升机待机位距离
                List<NavigateNode> currentShuttlePath = navigateUtils.calc(
                        currentLocNo
                        , targetLocNo
                        , NavigationMapType.DFX.id
                        , Utils.getShuttlePoints(Integer.parseInt(shuttleThread.getDevice().getDeviceNo()), currentLev)
                );//搜索空闲穿梭车,使用正常通道地图
                if (currentShuttlePath == null) {
                    continue;
                }
                Integer currDistance = navigateUtils.getOriginPathAllDistance(currentShuttlePath);//计算当前路径行走总距离
                // 不同楼层权重
                if (currentLev != Utils.getLev(locNo)) {
                    currDistance += WEIGHT;
                }
                // 挂载任务权重
                List<Task> tasks = taskService.selectWorkingByShuttle(Integer.valueOf(device.getDeviceNo()), null);
                if (!Cools.isEmpty(tasks)) {
                    currDistance += tasks.size() * WEIGHT;
                }
                if (currDistance < finalDistance) {
                    finalDistance = currDistance;
                    resThread = shuttleThread;
                }
            }
            if (resThread != null) {
                break;
            }
        }
        return resThread;
    }
    //生成迁移任务
    public synchronized Task generateMoveTask(Device device, String locNo) {
        // 已有迁移任务
@@ -160,7 +370,7 @@
        //获取迁移任务类型
        TaskCtg taskCtg = taskCtgService.getOne(new LambdaQueryWrapper<TaskCtg>()
                .eq(TaskCtg::getFlag, "MOVE")
                .eq(TaskCtg::getFlag, String.valueOf(TaskCtgType.MOVE))
                .eq(TaskCtg::getStatus, 1));
        if (taskCtg == null) {
            return null;
@@ -171,19 +381,21 @@
            return null;
        }
        //获取避让位置
        String standByLocNo = this.searchStandByLocNo(Integer.valueOf(device.getDeviceNo()), device.getHostId(), shuttleThread.getStatus().getCurrentLocNo());
        ShuttleProtocol shuttleProtocol = shuttleThread.getStatus();
        if (shuttleProtocol == null) {
            return null;
        }
        Task task = new Task();
        task.setUuid(String.valueOf(snowflakeIdWorker.nextId()));
        task.setTaskNo(String.valueOf(Utils.getTaskNo("MOVE")));
        task.setTaskSts(TaskStsType.NEW_MOVE.getId());
        task.setTaskSts(TaskStsType.NEW_MOVE.sts);
        task.setTaskCtg(taskCtg.getId());
        task.setPriority(10);
        task.setOriginSite(null);
        task.setOriginLoc(null);
        task.setOriginLoc(shuttleProtocol.getCurrentLocNo());
        task.setDestSite(null);
        task.setDestLoc(standByLocNo); // 避让位置
        task.setDestLoc(locNo); // 迁移位置
        task.setIoTime(new Date());
        task.setStartTime(new Date());
        task.setHostId(device.getHostId());
@@ -196,7 +408,7 @@
            News.error("保存{}号四向穿梭车迁移任务失败!!!", device.getDeviceNo());
            return null;
        }
        motionService.batchInsert(motionList, task.getUuid(), Integer.valueOf(task.getTaskNo()));
        motionService.batchInsert(motionList, task.getUuid(), Integer.valueOf(task.getTaskNo()), device.getHostId());
        task.setTaskSts(TaskStsType.ANALYZE_MOVE.sts);
@@ -208,10 +420,66 @@
        return task;
    }
    //生成手动取放货任务
    public synchronized Task generateManuaTakeMoveTask(Device device, String sourceLocNo, String locNo) {
        // 已有手动任务
        if (taskService.selectManualWorking(Integer.valueOf(device.getDeviceNo())) != null) {
            return null;
        }
        //获取手动任务类型
        TaskCtg taskCtg = taskCtgService.getOne(new LambdaQueryWrapper<TaskCtg>()
                .eq(TaskCtg::getFlag, String.valueOf(TaskCtgType.MANUAL))
                .eq(TaskCtg::getStatus, 1));
        if (taskCtg == null) {
            return null;
        }
        ShuttleThread shuttleThread = (ShuttleThread) SlaveConnection.get(SlaveType.Shuttle, device.getId().intValue());
        if (shuttleThread == null) {
            return null;
        }
        Task task = new Task();
        task.setUuid(String.valueOf(snowflakeIdWorker.nextId()));
        task.setTaskNo(String.valueOf(Utils.getTaskNo("MANUAL")));
        task.setTaskSts(TaskStsType.NEW_MANUAL.sts);
        task.setTaskCtg(taskCtg.getId());
        task.setPriority(10);
        task.setOriginSite(null);
        task.setOriginLoc(sourceLocNo);
        task.setDestSite("takeMove");
        task.setDestLoc(locNo);
        task.setIoTime(new Date());
        task.setStartTime(new Date());
        task.setStatus(1);
        task.setMemo("manual");
        task.setShuttleNo(Integer.valueOf(device.getDeviceNo()));
        task.setRecordLoc("Y");//记录库存信息
        task.setHostId(device.getHostId());
        // generate motion list
        List<Motion> motionList = analyzeService.generateShuttleManualMotion(task);
        if (Cools.isEmpty(motionList)) {
            News.error("保存{}号四向穿梭车手动任务失败!!!", device.getDeviceNo());
            return null;
        }
        motionService.batchInsert(motionList, task.getUuid(), Integer.valueOf(task.getTaskNo()), device.getHostId());
        task.setTaskSts(TaskStsType.ANALYZE_MANUAL.sts);
        if (!taskService.save(task)) {
            News.error("保存{}号四向穿梭车手动任务失败!!!", device.getDeviceNo());
            return null;
        }
        return task;
    }
    /**
     * 搜索避让库位,通过小车号和目标库位
     * 搜索可用库位,通过小车号和目标库位
     */
    public String searchStandByLocNo(Integer shuttleNo, Long hostId, String locNo) {
    public String searchAvailableLocNo(Integer shuttleNo, Long hostId, String currentLocNo, List<String> locNos) {
        BasShuttle basShuttle = basShuttleService.getOne(new LambdaQueryWrapper<BasShuttle>()
                .eq(BasShuttle::getShuttleNo, shuttleNo)
                .eq(BasShuttle::getHostId, hostId));
@@ -219,29 +487,18 @@
            throw new CoolException("小车基础数据不存在");
        }
        String idleLoc = basShuttle.getIdleLoc();
        if (Cools.isEmpty(idleLoc)) {
            throw new CoolException("小车避让数据不存在");
        if (locNos.isEmpty()) {
            throw new CoolException("当前层无避让位置");
        }
        int lev = Utils.getLev(locNo);//当前楼层
        JSONArray standbyLoc = JSON.parseArray(idleLoc);
        if (lev > standbyLoc.size()) {
            throw new CoolException("避让数据异常");
        }
        Object object = standbyLoc.get(lev - 1);
        List<String> locs = JSON.parseArray(object.toString(), String.class);
        if (locs.isEmpty()) {
            throw new CoolException("避让数据为空");
        }
        int lev = Utils.getLev(currentLocNo);
        Integer finalDistance = ShuttleDispatcher.INF;
        String recentLoc = null;
        for (String loc : locs) {
        for (String loc : locNos) {
            //当前穿梭车到避让位计算
            List<NavigateNode> currentShuttlePath = NavigateUtils.calc(
                    locNo
            List<NavigateNode> currentShuttlePath = navigateUtils.calc(
                    currentLocNo
                    , loc
                    , NavigationMapType.NORMAL.id
                    , Utils.getShuttlePoints(shuttleNo, lev)
@@ -250,7 +507,7 @@
                continue;
            }
            Integer currDistance = NavigateUtils.getOriginPathAllDistance(currentShuttlePath);//计算当前路径行走总距离
            Integer currDistance = navigateUtils.getOriginPathAllDistance(currentShuttlePath);//计算当前路径行走总距离
            if (currDistance < finalDistance) {
                finalDistance = currDistance;
                recentLoc = loc;
@@ -264,5 +521,140 @@
        return recentLoc;
    }
    /**
     * 检测目标楼层车数量是否小于允许的最大数量
     * true: 小于最大数量  false: 大于或等于最大数量
     */
    public boolean checkDispatchMaxNum(Integer lev, Long hostId) {
        Dict dict = dictService.getOne(new LambdaQueryWrapper<Dict>().eq(Dict::getFlag, "dispatchShuttleMaxNum"));
        if (dict == null) {
            return false;
        }
        ArrayList<Integer> shuttleNos = new ArrayList<>();
        List<Device> list = deviceService.list(new LambdaQueryWrapper<Device>()
                .eq(Device::getDeviceType, DeviceCtgType.SHUTTLE.val())
                .eq(Device::getHostId, hostId)
                .eq(Device::getStatus, 1));
        for (Device device : list) {
            //获取四向穿梭车线程
            ShuttleThread shuttleThread = (ShuttleThread) SlaveConnection.get(SlaveType.Shuttle, device.getId().intValue());
            if (shuttleThread == null) {
                continue;
            }
            ShuttleProtocol shuttleProtocol = shuttleThread.getStatus();
            if (shuttleProtocol == null) {
                continue;
            }
            if (shuttleProtocol.getProtocolStatusType().equals(ShuttleProtocolStatusType.OFFLINE)) {
                continue;
            }
            if(!shuttleThread.isDeviceIdle()) {
                continue;
            }
            if(shuttleProtocol.getCurrentLocNo() == null) {
                continue;
            }
            if (Utils.getLev(shuttleProtocol.getCurrentLocNo()) == lev) {
                if (shuttleProtocol.getHasCharge()) {
                    continue;//充电中
                }
                shuttleNos.add(shuttleProtocol.getShuttleNo());//目标楼层有车,添加进list
            }
        }
        //搜索是否存在前往目标楼层的小车移动工作档
        for (Task task : taskService.list(new LambdaQueryWrapper<Task>()
                .in(Task::getTaskSts, TaskStsType.NEW_MOVE.sts, TaskStsType.ANALYZE_MOVE.sts, TaskStsType.EXECUTE_MOVE.sts, TaskStsType.COMPLETE_MOVE.sts))) {
            if (task.getOriginLoc() == null || task.getDestLoc() == null) {
                continue;
            }
            int sourceLev = Utils.getLev(task.getOriginLoc());//工作档源楼层
            int targetLev = Utils.getLev(task.getDestLoc());//工作档目标楼层
            if (sourceLev == targetLev) {
                continue;//工作档楼层和目标楼层相同,跳过
            }
            if (targetLev == lev) {
                //工作档目标楼层和实际楼层相同,数量增加
                if (!shuttleNos.contains(task.getShuttleNo())) {
                    shuttleNos.add(task.getShuttleNo());
                }
                continue;
            }
        }
        //搜索是否存在前往目标楼层的小车工作档
        for (Task task : taskService.list(new LambdaQueryWrapper<Task>()
                .in(Task::getTaskSts, TaskStsType.NEW_INBOUND.sts, TaskStsType.ANALYZE_INBOUND.sts, TaskStsType.EXECUTE_INBOUND.sts, TaskStsType.COMPLETE_INBOUND.sts
                        , TaskStsType.NEW_OUTBOUND.sts, TaskStsType.ANALYZE_OUTBOUND.sts, TaskStsType.EXECUTE_OUTBOUND.sts, TaskStsType.COMPLETE_OUTBOUND.sts))) {
            String locNo = taskService.judgeInbound(task) ? task.getDestLoc() : task.getOriginLoc();
            if (Utils.getLev(locNo) != lev) {
                continue;
            }
            if (task.getShuttleNo() == null) {
                continue;
            }
            if (!shuttleNos.contains(task.getShuttleNo())) {
                shuttleNos.add(task.getShuttleNo());
            }
        }
        return shuttleNos.size() < Integer.parseInt(dict.getValue());
    }
    //分析出库路径待机库位
    public String analyzeOutPathWaitLoc(String startLoc, String targetLoc, Device shuttleDevice) {
        //计算路径并分解成两段动作
        List<NavigateNode> nodeList = navigateUtils.calc(startLoc, targetLoc, NavigationMapType.DFX.id, Utils.getShuttlePoints(Integer.parseInt(shuttleDevice.getDeviceNo()), Utils.getLev(startLoc)));
        if (nodeList == null) {
            News.error("{} dash {} can't find navigate path!", startLoc, targetLoc);
            return null;
        }
        //获取分段路径
        ArrayList<ArrayList<NavigateNode>> data = navigateUtils.getSectionPath(nodeList);
        if (data.size() <= 1) {
            return startLoc;//两点之间只有一段路径,在起点位置等待
        }
        //取出倒数第二段路径
        ArrayList<NavigateNode> navigateNodes = data.get(data.size() - 2);
        NavigateNode startNode = navigateNodes.get(0);
        String lastPathStartLoc = Utils.getLocNo(startNode.getX(), startNode.getY(), startNode.getZ());
        return lastPathStartLoc;
    }
    //获取小车坐标
    public String findShuttleLocNo(Integer shuttleNo, Long hostId) {
        Device device = deviceService.getOne(new LambdaQueryWrapper<Device>()
                .eq(Device::getDeviceType, DeviceCtgType.SHUTTLE.val())
                .eq(Device::getDeviceNo, shuttleNo)
                .eq(Device::getHostId, hostId)
                .eq(Device::getStatus, 1));
        if(device == null) {
            return null;
        }
        //获取四向穿梭车线程
        ShuttleThread shuttleThread = (ShuttleThread) SlaveConnection.get(SlaveType.Shuttle, device.getId().intValue());
        ShuttleProtocol shuttleProtocol = shuttleThread.getStatus();
        if (shuttleProtocol == null || shuttleProtocol.getShuttleNo() == null) {
            return null;
        }
        return shuttleProtocol.getCurrentLocNo();
    }
}