luxiaotao1123
2024-03-26 dca1ee5b00f00dcf8c43776eb60bdd23e5cca056
Merge branch 'Four-Way-Rack' of http://47.97.1.152:5880/r/zy-asrs-master into Four-Way-Rack
9个文件已修改
6个文件已添加
1514 ■■■■ 已修改文件
zy-asrs-wcs/src/main/java/com/zy/asrs/wcs/core/kernel/command/ShuttleCommandService.java 472 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-asrs-wcs/src/main/java/com/zy/asrs/wcs/core/model/command/ShuttleCommand.java 90 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-asrs-wcs/src/main/java/com/zy/asrs/wcs/core/model/enums/MotionCtgType.java 84 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-asrs-wcs/src/main/java/com/zy/asrs/wcs/core/model/enums/ShuttleRunDirection.java 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-asrs-wcs/src/main/java/com/zy/asrs/wcs/core/model/enums/ShuttleTaskModeType.java 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-asrs-wcs/src/main/java/com/zy/asrs/wcs/core/utils/NavigatePositionConvert.java 112 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-asrs-wcs/src/main/java/com/zy/asrs/wcs/core/utils/NavigateSolution.java 217 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-asrs-wcs/src/main/java/com/zy/asrs/wcs/core/utils/NavigateUtils.java 278 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-asrs-wcs/src/main/java/com/zy/asrs/wcs/core/utils/Utils.java 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-asrs-wcs/src/main/java/com/zy/asrs/wcs/rcs/entity/Motion.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-asrs-wcs/src/main/java/com/zy/asrs/wcs/rcs/model/protocol/ShuttleProtocol.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-asrs-wcs/src/main/java/com/zy/asrs/wcs/rcs/service/MotionCtgService.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-asrs-wcs/src/main/java/com/zy/asrs/wcs/rcs/service/impl/MotionCtgServiceImpl.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-asrs-wcs/src/main/java/com/zy/asrs/wcs/rcs/thread/ShuttleThread.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-asrs-wcs/src/main/java/com/zy/asrs/wcs/rcs/thread/impl/SurayShuttleThread.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-asrs-wcs/src/main/java/com/zy/asrs/wcs/core/kernel/command/ShuttleCommandService.java
@@ -1,102 +1,108 @@
//package com.zy.asrs.wcs.core.kernel.command;
//
//import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
//import com.zy.asrs.wcs.core.model.command.ShuttleAssignCommand;
//import com.zy.asrs.wcs.core.model.command.ShuttleCommand;
//import com.zy.asrs.wcs.core.model.enums.DeviceCtgType;
//import com.zy.asrs.wcs.core.model.enums.MotionStsType;
//import com.zy.asrs.wcs.core.service.BasShuttleService;
//import com.zy.asrs.wcs.core.service.LocService;
//import com.zy.asrs.wcs.core.service.TaskService;
//import com.zy.asrs.wcs.core.utils.NavigateMapUtils;
//import com.zy.asrs.wcs.core.utils.RedisUtil;
//import com.zy.asrs.wcs.rcs.cache.SlaveConnection;
//import com.zy.asrs.wcs.rcs.entity.Motion;
//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.MotionService;
//import com.zy.asrs.wcs.rcs.thread.ShuttleThread;
//import lombok.extern.slf4j.Slf4j;
//import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.stereotype.Service;
//
//import java.util.ArrayList;
//import java.util.List;
//import java.util.Objects;
//import java.util.Optional;
//
///**
// * Created by vincent on 2023/10/23
// */
//@Slf4j
//@Service
//public class ShuttleCommandService {
//
//    public static final Integer SHUTTLE_ADDITION_COMMAND_SPEED = 500;
//
//    @Autowired
//    private RedisUtil redisUtil;
//    @Autowired
//    private MotionService motionService;
//    @Autowired
//    private TaskService taskService;
//    @Autowired
//    private BasShuttleService basShuttleService;
//    @Autowired
//    private LocService locService;
//    @Autowired
//    private NavigateMapUtils navigateMapUtils;
//
//    // 计算
//    public Boolean accept(Motion motion) {
//        Integer deviceNo = Integer.parseInt(motion.getDevice());
//        ShuttleThread shuttleThread = (ShuttleThread) SlaveConnection.get(SlaveType.Shuttle, deviceNo);
//        ShuttleProtocol shuttleProtocol = shuttleThread.getStatus();
//        if (null == shuttleProtocol) {
//            return false;
//        }
//        if (!shuttleProtocol.getIdle()) {//设备不空闲
//            return false;
//        }
//        if (!shuttleProtocol.getPakMk()) {
//            return false;
//        }
//        if (motionService.count(new LambdaQueryWrapper<Motion>()
//                .eq(Motion::getDeviceCtg, DeviceCtgType.SHUTTLE.val())
//                .eq(Motion::getDevice, motion.getDevice())
//                .eq(Motion::getMotionSts, MotionStsType.EXECUTING.val())) > 0) {
//            return false;
//        }
//
//        ShuttleAssignCommand assignCommand = new ShuttleAssignCommand();
//        assignCommand.setShuttleNo(deviceNo.shortValue());
//        assignCommand.setTaskNo(motion.getWrkNo().shortValue());
//        assignCommand.setSourceLocNo(motion.getOrigin());
//        assignCommand.setLocNo(motion.getTarget());
//
//        List<ShuttleCommand> shuttleCommands = new ArrayList<>();
//        ShuttleTaskModeType shuttleTaskModeType = null;
//
package com.zy.asrs.wcs.core.kernel.command;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zy.asrs.framework.common.Cools;
import com.zy.asrs.framework.exception.CoolException;
import com.zy.asrs.wcs.core.entity.Loc;
import com.zy.asrs.wcs.core.model.NavigateNode;
import com.zy.asrs.wcs.core.model.command.ShuttleAssignCommand;
import com.zy.asrs.wcs.core.model.command.ShuttleCommand;
import com.zy.asrs.wcs.core.model.enums.*;
import com.zy.asrs.wcs.core.service.BasShuttleService;
import com.zy.asrs.wcs.core.service.LocService;
import com.zy.asrs.wcs.core.service.TaskService;
import com.zy.asrs.wcs.core.utils.*;
import com.zy.asrs.wcs.rcs.News;
import com.zy.asrs.wcs.rcs.cache.SlaveConnection;
import com.zy.asrs.wcs.rcs.entity.Motion;
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.MotionService;
import com.zy.asrs.wcs.rcs.thread.ShuttleThread;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
/**
 * Created by vincent on 2023/10/23
 */
@Slf4j
@Service
public class ShuttleCommandService {
    public static final Integer SHUTTLE_ADDITION_COMMAND_SPEED = 500;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private MotionService motionService;
    @Autowired
    private TaskService taskService;
    @Autowired
    private BasShuttleService basShuttleService;
    @Autowired
    private LocService locService;
    @Autowired
    private NavigateMapUtils navigateMapUtils;
    // 计算
    public Boolean accept(Motion motion) {
        Integer deviceNo = Integer.parseInt(motion.getDevice());
        ShuttleThread shuttleThread = (ShuttleThread) SlaveConnection.get(SlaveType.Shuttle, deviceNo);
        ShuttleProtocol shuttleProtocol = shuttleThread.getStatus();
        if (null == shuttleProtocol) {
            return false;
        }
        if (!shuttleProtocol.getIdle()) {//设备不空闲
            return false;
        }
        if (!shuttleProtocol.getPakMk()) {
            return false;
        }
        if (motionService.count(new LambdaQueryWrapper<Motion>()
                .eq(Motion::getDeviceCtg, DeviceCtgType.SHUTTLE.val())
                .eq(Motion::getDevice, motion.getDevice())
                .eq(Motion::getMotionSts, MotionStsType.EXECUTING.val())) > 0) {
            return false;
        }
        ShuttleAssignCommand assignCommand = new ShuttleAssignCommand();
        assignCommand.setShuttleNo(deviceNo.shortValue());
        assignCommand.setTaskNo(motion.getWrkNo().shortValue());
        assignCommand.setSourceLocNo(motion.getOrigin());
        assignCommand.setLocNo(motion.getTarget());
        List<ShuttleCommand> shuttleCommands = new ArrayList<>();
        ShuttleTaskModeType shuttleTaskModeType = null;
//        SiemensLiftThread liftThread = null;
//        LiftProtocol liftProtocol = null;
//
//        //判断小车状态
//        if (shuttleProtocol.getBusyStatusType().equals(ShuttleStatusType.IDLE)
//                && shuttleProtocol.getProtocolStatusType().equals(ShuttleProtocolStatusType.IDLE)
//                && shuttleProtocol.getTaskNo().intValue() != 0
//        ) {
//            return false;
//        }
//
//        switch (Objects.requireNonNull(MotionCtgType.get(motion.getMotionCtgEl()))){
//            case SHUTTLE_MOVE:
//                // 如果已经在当前条码则过滤
//                if (String.valueOf(shuttleProtocol.getCurrentCode()).equals(locMastService.selectById(motion.getTarget()).getQrCodeValue())) {
//                    return true;
//                }
//                shuttleCommands = this.shuttleAssignCommand(motion.getOrigin(), motion.getTarget(), NavigationMapType.NORMAL.id, assignCommand, shuttleThread);
//                shuttleTaskModeType = ShuttleTaskModeType.SHUTTLE_MOVE_LOC_NO;
//                break;
        //判断小车状态
        if (shuttleProtocol.getIdle()
                && shuttleProtocol.getProtocolStatusType().equals(ShuttleProtocolStatusType.IDLE)
                && shuttleProtocol.getTaskNo() != 0
        ) {
            return false;
        }
        switch (Objects.requireNonNull(MotionCtgType.get(motion.getMotionCtgEl()))){
            case SHUTTLE_MOVE:
                // 如果已经在当前条码则过滤
                if (String.valueOf(shuttleProtocol.getCurrentCode()).equals(locService.getOne(new LambdaQueryWrapper<Loc>()
                        .eq(Loc::getLocNo, motion.getTarget())
                        .eq(Loc::getHostId, motion.getHostId())).getCode())) {
                    return true;
                }
                shuttleCommands = this.shuttleAssignCommand(motion.getOrigin(), motion.getTarget(), NavigationMapType.NORMAL.id, assignCommand, shuttleThread);
                shuttleTaskModeType = ShuttleTaskModeType.SHUTTLE_MOVE_LOC_NO;
                break;
//            case SHUTTLE_MOVE_LIFT_PALLET://穿梭车顶升并移动
//                shuttleCommands = this.shuttleAssignCommand(motion.getOrigin(), motion.getTarget(), NavigationMapType.DFX.id, assignCommand, shuttleThread);
//                shuttleTaskModeType = ShuttleTaskModeType.PAK_IN;
@@ -345,76 +351,76 @@
//                shuttleCommands.add(shuttleThread.getChargeSwitchCommand((short) 1));
//                assignCommand.setCharge(Boolean.TRUE);
//                break;
//            default:
//                throw new CoolException(motion.getMotionCtgEl() + "没有指定任务作业流程!!!");
//        }
//
//        if (Cools.isEmpty(shuttleCommands)) {
//            return false;
//        }
//
//        assert null != shuttleTaskModeType;
//        assignCommand.setTaskMode(shuttleTaskModeType.id.shortValue());//入出库模式
//        assignCommand.setCommands(shuttleCommands);
//
//        if (motion.getOrigin() != null && motion.getTarget() != null) {
//            //所使用的路径进行锁定禁用
//            boolean lockResult = navigateMapUtils.writeNavigateNodeToRedisMap(Utils.getLev(motion.getTarget()), shuttleProtocol.getShuttleNo().intValue(), assignCommand.getNodes(), true);//所使用的路径进行锁定禁用
//            if (!lockResult) {
//                return false;//锁定失败
//            }
            default:
                throw new CoolException(motion.getMotionCtgEl() + "没有指定任务作业流程!!!");
        }
        if (Cools.isEmpty(shuttleCommands)) {
            return false;
        }
        assert null != shuttleTaskModeType;
        assignCommand.setTaskMode(shuttleTaskModeType.id.shortValue());//入出库模式
        assignCommand.setCommands(shuttleCommands);
        if (motion.getOrigin() != null && motion.getTarget() != null) {
            //所使用的路径进行锁定禁用
            boolean lockResult = navigateMapUtils.writeNavigateNodeToRedisMap(Utils.getLev(motion.getTarget()), Integer.parseInt(shuttleProtocol.getShuttleNo()), assignCommand.getNodes(), true);//所使用的路径进行锁定禁用
            if (!lockResult) {
                return false;//锁定失败
            }
//            shuttleThread.assignWork(assignCommand);
//        }else {
        }else {
//            shuttleThread.assignWork(assignCommand);
//        }
//
//        return Boolean.TRUE;
//    }
//
//    public Boolean finish(Motion motion) {
//        Integer deviceNo = Integer.parseInt(motion.getDevice());
//        ShuttleThread shuttleThread = (ShuttleThread) SlaveConnection.get(SlaveType.Shuttle, deviceNo);
//        ShuttleProtocol shuttleProtocol = shuttleThread.getShuttleProtocol();
//        if (null == shuttleProtocol) {
//            return false;
//        }
//
//        if (shuttleProtocol.getTaskNo() != 0 && shuttleProtocol.getTaskNo().intValue() != motion.getWrkNo()) {
//            return false;
//        }
//
//        //充电任务
//        if (Objects.requireNonNull(MotionCtgType.get(motion.getMotionCtgEl())).equals(MotionCtgType.SHUTTLE_CHARGE_ON)) {
//            // 复位穿梭车
//            shuttleProtocol.setTaskNo((short) 0);
//            shuttleProtocol.setProtocolStatus(ShuttleProtocolStatusType.IDLE);
//            shuttleProtocol.setPakMk(true);
//            return true;
//        }
//
//        if (!shuttleProtocol.getProtocolStatusType().equals(ShuttleProtocolStatusType.WAITING)
//            && !shuttleProtocol.getProtocolStatusType().equals(ShuttleProtocolStatusType.CHARGING_WAITING)
//        ) {
//            return false;
//        }
//
//        if (shuttleProtocol.getBusyStatusType().equals(ShuttleStatusType.BUSY)) {
//            return false;
//        }
//
        }
        return Boolean.TRUE;
    }
    public Boolean finish(Motion motion) {
        Integer deviceNo = Integer.parseInt(motion.getDevice());
        ShuttleThread shuttleThread = (ShuttleThread) SlaveConnection.get(SlaveType.Shuttle, deviceNo);
        ShuttleProtocol shuttleProtocol = shuttleThread.getStatus();
        if (null == shuttleProtocol) {
            return false;
        }
        if (shuttleProtocol.getTaskNo() != 0 && shuttleProtocol.getTaskNo().intValue() != motion.getWrkNo()) {
            return false;
        }
        //充电任务
        if (Objects.requireNonNull(MotionCtgType.get(motion.getMotionCtgEl())).equals(MotionCtgType.SHUTTLE_CHARGE_ON)) {
            // 复位穿梭车
            shuttleProtocol.setTaskNo(0);
            shuttleProtocol.setProtocolStatus(ShuttleProtocolStatusType.IDLE);
            shuttleProtocol.setPakMk(true);
            return true;
        }
        if (!shuttleProtocol.getProtocolStatusType().equals(ShuttleProtocolStatusType.WAITING)
            && !shuttleProtocol.getProtocolStatusType().equals(ShuttleProtocolStatusType.CHARGING_WAITING)
        ) {
            return false;
        }
        if (!shuttleProtocol.getIdle()) {
            return false;
        }
//        SiemensLiftThread liftThread = null;
//        LiftProtocol liftProtocol = null;
//
//        switch (Objects.requireNonNull(MotionCtgType.get(motion.getMotionCtgEl()))){
//            case SHUTTLE_MOVE:
//            case SHUTTLE_MOVE_LIFT_PALLET:
//            case SHUTTLE_MOVE_DOWN_PALLET:
//            case SHUTTLE_MOVE_TO_CONVEYOR:
//            case SHUTTLE_MOVE_FROM_CONVEYOR:
//                if (!shuttleProtocol.getCurrentLocNo().equals(motion.getTarget())) {
//                    return false;
//                }
//                break;
        switch (Objects.requireNonNull(MotionCtgType.get(motion.getMotionCtgEl()))){
            case SHUTTLE_MOVE:
            case SHUTTLE_MOVE_LIFT_PALLET:
            case SHUTTLE_MOVE_DOWN_PALLET:
            case SHUTTLE_MOVE_TO_CONVEYOR:
            case SHUTTLE_MOVE_FROM_CONVEYOR:
                if (!shuttleProtocol.getCurrentLocNo().equals(motion.getTarget())) {
                    return false;
                }
                break;
//            case SHUTTLE_MOVE_TO_LIFT:
//            case SHUTTLE_MOVE_FROM_LIFT:
//            case SHUTTLE_TRANSPORT_FROM_LIFT:
@@ -451,73 +457,73 @@
//                }
//
//                break;
//            default:
//                break;
//        }
//
//        // 复位穿梭车
//        shuttleProtocol.setTaskNo((short) 0);
//        shuttleProtocol.setProtocolStatus(ShuttleProtocolStatusType.IDLE);
//        shuttleProtocol.setPakMk(true);
//
//        return true;
//    }
//
//    public synchronized List<ShuttleCommand> shuttleAssignCommand(String startLocNo, String endLocNo, Integer mapType, ShuttleAssignCommand assignCommand, ShuttleThread shuttleThread) {
//        //获取小车移动速度
//        Integer runSpeed = Optional.ofNullable(basShuttleService.selectById(assignCommand.getShuttleNo()).getRunSpeed()).orElse(1000);
//
//        List<NavigateNode> nodeList = NavigateUtils.calc(startLocNo, endLocNo, mapType, Utils.getShuttlePoints(shuttleThread.getSlave().getId(), Utils.getLev(startLocNo)));
//        if (nodeList == null) {
//            News.error("{} dash {} can't find navigate path!", startLocNo, endLocNo);
//            return null;
//        }
//        List<NavigateNode> allNode = new ArrayList<>(nodeList);
//
//        List<ShuttleCommand> commands = new ArrayList<>();
//        //获取分段路径
//        ArrayList<ArrayList<NavigateNode>> data = NavigateUtils.getSectionPath(nodeList);
//        //将每一段路径分成command指令
//        for (ArrayList<NavigateNode> nodes : data) {
//            //开始路径
//            NavigateNode startPath = nodes.get(0);
//
//            //中间路径
//            NavigateNode middlePath = null;
//            //通过xy坐标小车二维码
//            Short middleCodeNum = null;
//            Integer middleToDistDistance = null;//计算中间点到目标点行走距离
//            if (nodes.size() > 10) {//中段码传倒数第三个
//                //中间路径
//                middlePath = nodes.get(nodes.size() - 3);
//                //通过xy坐标小车二维码
//                middleCodeNum = NavigatePositionConvert.xyToPosition(middlePath.getX(), middlePath.getY(), middlePath.getZ());
//                middleToDistDistance = NavigateUtils.getMiddleToDistDistance(nodes, middlePath);//计算中间点到目标点行走距离
//            } else if (nodes.size() > 5) {//中段码传倒数第二个
//                //中间路径
//                middlePath = nodes.get(nodes.size() - 2);
//                //通过xy坐标小车二维码
//                middleCodeNum = NavigatePositionConvert.xyToPosition(middlePath.getX(), middlePath.getY(), middlePath.getZ());
//                middleToDistDistance = NavigateUtils.getMiddleToDistDistance(nodes, middlePath);//计算中间点到目标点行走距离
//            }
//
//            //目标路径
//            NavigateNode endPath = nodes.get(nodes.size() - 1);
//            Integer allDistance = NavigateUtils.getCurrentPathAllDistance(nodes);//计算当前路径行走总距离
//            //通过xy坐标小车二维码
//            Short startCodeNum = NavigatePositionConvert.xyToPosition(startPath.getX(), startPath.getY(), startPath.getZ());
//            //通过xy坐标小车二维码
//            Short distCodeNum = NavigatePositionConvert.xyToPosition(endPath.getX(), endPath.getY(), endPath.getZ());
//            //获取移动命令
//            ShuttleCommand command = shuttleThread.getMoveCommand(startCodeNum, distCodeNum, allDistance, ShuttleRunDirection.get(startPath.getDirection()).id, middleCodeNum, middleToDistDistance, runSpeed);
//            command.setNodes(nodes);//将行走节点添加到每一步命令中
//            commands.add(command);
//        }
//
//        assignCommand.setNodes(allNode);//当前任务所占用的节点list
//
//        return commands;
//    }
//
//
//}
            default:
                break;
        }
        // 复位穿梭车
        shuttleProtocol.setTaskNo(0);
        shuttleProtocol.setProtocolStatus(ShuttleProtocolStatusType.IDLE);
        shuttleProtocol.setPakMk(true);
        return true;
    }
    public synchronized List<ShuttleCommand> shuttleAssignCommand(String startLocNo, String endLocNo, Integer mapType, ShuttleAssignCommand assignCommand, ShuttleThread shuttleThread) {
        //获取小车移动速度
        Integer runSpeed = Optional.ofNullable(basShuttleService.getById(assignCommand.getShuttleNo()).getRunSpeed()).orElse(1000);
        Long hostId = shuttleThread.getDevice().getHostId();
        List<NavigateNode> nodeList = NavigateUtils.calc(startLocNo, endLocNo, mapType, Utils.getShuttlePoints(Integer.parseInt(shuttleThread.getDevice().getDeviceNo()), Utils.getLev(startLocNo)));
        if (nodeList == null) {
            News.error("{} dash {} can't find navigate path!", startLocNo, endLocNo);
            return null;
        }
        List<NavigateNode> allNode = new ArrayList<>(nodeList);
        List<ShuttleCommand> commands = new ArrayList<>();
        //获取分段路径
        ArrayList<ArrayList<NavigateNode>> data = NavigateUtils.getSectionPath(nodeList);
        //将每一段路径分成command指令
        for (ArrayList<NavigateNode> nodes : data) {
            //开始路径
            NavigateNode startPath = nodes.get(0);
            //中间路径
            NavigateNode middlePath = null;
            //通过xy坐标小车二维码
            String middleCodeNum = null;
            Integer middleToDistDistance = null;//计算中间点到目标点行走距离
            if (nodes.size() > 10) {//中段码传倒数第三个
                //中间路径
                middlePath = nodes.get(nodes.size() - 3);
                //通过xy坐标小车二维码
                middleCodeNum = NavigatePositionConvert.xyToPosition(middlePath.getX(), middlePath.getY(), middlePath.getZ(), hostId);
                middleToDistDistance = NavigateUtils.getMiddleToDistDistance(nodes, middlePath);//计算中间点到目标点行走距离
            } else if (nodes.size() > 5) {//中段码传倒数第二个
                //中间路径
                middlePath = nodes.get(nodes.size() - 2);
                //通过xy坐标小车二维码
                middleCodeNum = NavigatePositionConvert.xyToPosition(middlePath.getX(), middlePath.getY(), middlePath.getZ(), hostId);
                middleToDistDistance = NavigateUtils.getMiddleToDistDistance(nodes, middlePath);//计算中间点到目标点行走距离
            }
            //目标路径
            NavigateNode endPath = nodes.get(nodes.size() - 1);
            Integer allDistance = NavigateUtils.getCurrentPathAllDistance(nodes);//计算当前路径行走总距离
            //通过xy坐标小车二维码
            String startCodeNum = NavigatePositionConvert.xyToPosition(startPath.getX(), startPath.getY(), startPath.getZ(), hostId);
            //通过xy坐标小车二维码
            String distCodeNum = NavigatePositionConvert.xyToPosition(endPath.getX(), endPath.getY(), endPath.getZ(), hostId);
            //获取移动命令
            ShuttleCommand command = shuttleThread.getMoveCommand(assignCommand.getTaskNo().intValue(), startCodeNum, distCodeNum, allDistance, ShuttleRunDirection.get(startPath.getDirection()).id.intValue(), runSpeed);
            command.setNodes(nodes);//将行走节点添加到每一步命令中
            commands.add(command);
        }
        assignCommand.setNodes(allNode);//当前任务所占用的节点list
        return commands;
    }
}
zy-asrs-wcs/src/main/java/com/zy/asrs/wcs/core/model/command/ShuttleCommand.java
@@ -14,99 +14,17 @@
    /**
     * 四向穿梭车号
     */
    private Short shuttleNo = 0;
    private Integer shuttleNo;
    /**
     * 任务号
     */
    private Short taskNo = 0;
    private Integer taskNo = 0;
    /**
     * 作业类型
     * 报文内容
     */
    private Short taskMode = 0;
    /**
     * 功能说明
     * 0、空
     * 1、正常移动
     * 2、托盘顶升
     * 3、强制移动
     * 4、查找定位点
     * 5、充电开关
     * 6、系统复位
     * 7、紧急停止
     * 8、IO控制
     * 9、行走电机强制移动(输入为脉冲指令)
     * 10、升降伺服强制移动(输入为脉冲指令)
     * 控制指令字
     */
    private Short commandWord;
    /**
     * 启始二维编号
     */
    private Short startCodeNum;
    /**
     * 中间二维编号
     */
    private Short middleCodeNum;
    /**
     * 目标二维编号
     */
    private Short distCodeNum;
    /**
     * 起点到目标点的距离长度
     */
    private Integer startToDistDistance;
    /**
     * 中间点到目标点的距离长度
     */
    private Integer middleToDistDistance;
    /**
     * 小车运行方向
     */
    private Short runDirection;
    /**
     * 托盘顶升
     */
    private Short palletLift;
    /**
     * 小车强制移动距离
     */
    private Integer forceMoveDistance;
    /**
     * 充电开关
     */
    private Short chargeSwitch;
    /**
     * 小车IO控制
     */
    private Short IOControl;
    /**
     * 小车运行速度
     */
    private Short runSpeed;
    /**
     * 小车雷达备用
     */
    private Short radarTmp;
    /**
     * 指令结束位
     */
    private Short commandEnd;
    private String body;
    /**
     * 命令是否完成,默认false未完成
zy-asrs-wcs/src/main/java/com/zy/asrs/wcs/core/model/enums/MotionCtgType.java
New file
@@ -0,0 +1,84 @@
package com.zy.asrs.wcs.core.model.enums;
import com.zy.asrs.framework.common.SpringUtils;
import com.zy.asrs.framework.exception.CoolException;
import com.zy.asrs.wcs.rcs.entity.MotionCtg;
import com.zy.asrs.wcs.rcs.service.MotionCtgService;
import static com.zy.asrs.wcs.core.model.enums.DeviceCtgType.*;
public enum MotionCtgType {
    // 输送线 --------------------------------------------
    CONVEYOR_INBOUND(CONVEYOR),
    CONVEYOR_INBOUND_TO_LIFT(CONVEYOR),
    CONVEYOR_OUTBOUND(CONVEYOR),
    CONVEYOR_OUTBOUND_FROM_LIFT(CONVEYOR),
    // 堆垛机 --------------------------------------------
    // 提升机 --------------------------------------------
    LIFT_MOVE(LIFT),
    LIFT_WITH_GOODS(LIFT),
    LIFT_WITH_SHUTTLE(LIFT),
    LIFT_WITH_GOODS_AND_SHUTTLE(LIFT),
    LIFT_TRANSPORT_TO_CONVEYOR(LIFT),
    LIFT_MISSION_COMPLETE(LIFT),
    // 穿梭车 --------------------------------------------
    SHUTTLE_MOVE(SHUTTLE),
    SHUTTLE_MOVE_LIFT_PALLET(SHUTTLE),//穿梭车顶升并移动
    SHUTTLE_MOVE_DOWN_PALLET(SHUTTLE),//穿梭车移动并托盘下降
    SHUTTLE_MOVE_FROM_LIFT(SHUTTLE),
    SHUTTLE_MOVE_TO_LIFT(SHUTTLE),
    SHUTTLE_MOVE_FROM_CONVEYOR(SHUTTLE),
    SHUTTLE_MOVE_TO_CONVEYOR(SHUTTLE),
    SHUTTLE_MOVE_FROM_LIFT_TO_CONVEYOR(SHUTTLE),
    SHUTTLE_TRANSPORT(SHUTTLE),
    SHUTTLE_TRANSPORT_FROM_LIFT(SHUTTLE),
    SHUTTLE_TRANSPORT_TO_LIFT(SHUTTLE),
    SHUTTLE_TRANSPORT_FROM_CONVEYOR(SHUTTLE),
    SHUTTLE_TRANSPORT_TO_CONVEYOR(SHUTTLE),
    SHUTTLE_CHARGE_ON(SHUTTLE),
    SHUTTLE_CHARGE_OFF(SHUTTLE),
    // AGV ----------------------------------------------
    AGV_TRANSPORT(AGV),
    ;
    public DeviceCtgType deviceCtg;
    MotionCtgType(DeviceCtgType deviceCtg) {
        this.deviceCtg = deviceCtg;
    }
    public long val() {
        MotionCtgService service = SpringUtils.getBean(MotionCtgService.class);
        MotionCtg entity = service.selectByFlag(this.toString());
        if (entity == null) {
            throw new CoolException("MotionCtgType Error!");
        }
        return entity.getId();
    }
    public static MotionCtgType get(String el) {
        for (MotionCtgType value : MotionCtgType.values()) {
            if (el.equals(value.toString())) {
                return value;
            }
        }
        return null;
    }
}
zy-asrs-wcs/src/main/java/com/zy/asrs/wcs/core/model/enums/ShuttleRunDirection.java
New file
@@ -0,0 +1,63 @@
package com.zy.asrs.wcs.core.model.enums;
/**
 * 四向穿梭车运行方向
 */
public enum ShuttleRunDirection {
    TOP((short)2, "货物方向-"),
    BOTTOM((short)1, "货物方向+"),
    LEFT((short)3, "过道方向-"),
    RIGHT((short)4, "过道方向+"),
    ;
    public Short id;
    public String desc;
    ShuttleRunDirection(Short id, String desc) {
        this.id = id;
        this.desc = desc;
    }
    public static ShuttleRunDirection get(String direction) {
        if (null == direction) {
            return null;
        }
        if (direction.equals("top")) {
            return TOP;
        } else if (direction.equals("bottom")) {
            return BOTTOM;
        } else if (direction.equals("left")) {
            return LEFT;
        } else if (direction.equals("right")) {
            return RIGHT;
        }else {
            return null;
        }
    }
    public static ShuttleRunDirection get(Short id) {
        if (null == id) {
            return null;
        }
        for (ShuttleRunDirection type : ShuttleRunDirection.values()) {
            if (type.id.equals(id.shortValue())) {
                return type;
            }
        }
        return null;
    }
    public static ShuttleRunDirection get(ShuttleRunDirection type) {
        if (null == type) {
            return null;
        }
        for (ShuttleRunDirection shuttleRunDirection : ShuttleRunDirection.values()) {
            if (shuttleRunDirection == type) {
                return shuttleRunDirection;
            }
        }
        return null;
    }
}
zy-asrs-wcs/src/main/java/com/zy/asrs/wcs/core/model/enums/ShuttleTaskModeType.java
New file
@@ -0,0 +1,63 @@
package com.zy.asrs.wcs.core.model.enums;
public enum ShuttleTaskModeType {
    INIT(0, "初始"),    // 初始
    PAK_IN(1, "入库"),
    PAK_OUT(2, "出库"),
    PALLET_LIFT(3, "托盘顶升"),
    PALLET_DOWN(4, "托盘下降"),
    X_NEGATIVE(5, "X-方向"),
    X_POSITIVE(6, "X+方向"),
    Y_POSITIVE(7, "Y+方向"),
    Y_NEGATIVE(8, "Y-方向"),
    RESET(9, "状态复位"),
    SEARCH_LEFT(10, "正方向(右)寻库位"),
    SEARCH_RIGHT(11, "负方向(左)寻库位"),
    SEARCH_TOP(12, "负方向(前)寻库位"),
    SEARCH_BOTTOM(13, "负方向(后)寻库位"),
    MOVE_LOC_NO(14, "移动到目标库位"),
    CHARGE(15, "充电"),
    MOVE_LIFT(16, "移动到提升机"),
    AVOID(17, "避让任务"),
    TRANSPORT(18, "搬运任务"),
    SHUTTLE_MOVE_LOC_NO(19, "小车移库任务"),
    SHUTTLE_LOC_TO_LOC(20, "库位移转"),
    SHUTTLE_CONTROL(21, "小车管制"),
    SHUTTLE_CANCEL_CONTROL(22, "小车取消管制"),
    MOVE_LOC_NO_WRK_MAST(23, "移动到目标库位(生成移动任务)"),
    ;
    public Integer id;
    public String desc;
    ShuttleTaskModeType(Integer id, String desc) {
        this.id = id;
        this.desc = desc;
    }
    public static ShuttleTaskModeType get(Short id) {
        if (null == id) {
            return null;
        }
        for (ShuttleTaskModeType type : ShuttleTaskModeType.values()) {
            if (type.id.equals(id.intValue())) {
                return type;
            }
        }
        return null;
    }
    public static ShuttleTaskModeType get(ShuttleTaskModeType type) {
        if (null == type) {
            return null;
        }
        for (ShuttleTaskModeType shuttleTaskModeType : ShuttleTaskModeType.values()) {
            if (shuttleTaskModeType == type) {
                return shuttleTaskModeType;
            }
        }
        return null;
    }
}
zy-asrs-wcs/src/main/java/com/zy/asrs/wcs/core/utils/NavigatePositionConvert.java
New file
@@ -0,0 +1,112 @@
package com.zy.asrs.wcs.core.utils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zy.asrs.framework.common.SpringUtils;
import com.zy.asrs.wcs.core.entity.Loc;
import com.zy.asrs.wcs.core.model.NavigateNode;
import com.zy.asrs.wcs.core.service.LocService;
/**
 * 库位编号和A*算法的xy轴转换工具类
 */
public class NavigatePositionConvert {
    public static String xyToPosition(int x, int y, int z, Long hostId) {
        StringBuffer sb = new StringBuffer();
        if (x < 10) {
            sb.append("0");
        }
        sb.append(x);
        if (y < 10) {
            sb.append("00");
        } else if (y < 100) {
            sb.append("0");
        }
        sb.append(y);
        if (z < 10) {
            sb.append("0");
        }
        sb.append(z);
        String position = sb.toString();//库位号
        //库位号转小车二维码
        LocService locMastService = SpringUtils.getBean(LocService.class);
        Loc locMast = locMastService.getOne(new LambdaQueryWrapper<Loc>()
                .eq(Loc::getLocNo, position)
                .eq(Loc::getHostId, hostId));
        if (locMast == null) {
//            //当前库位号查不到,可能是站点库位号
//            BasDevpService basDevpService = SpringUtils.getBean(BasDevpService.class);
//            BasDevp basDevp = basDevpService.queryByLocNo(position);
//            if (basDevp == null) {
//                return null;
//            }
//            return Short.parseShort(basDevp.getQrCodeValue());
            return null;
        }
        return locMast.getCode();
    }
    //坐标编号转xy轴
    public static int[] positionToXY(String position) {
        int col = Integer.parseInt(position.substring(0, 2));
        int row = Integer.parseInt(position.substring(2, 5));
        int[] newPosition = coverPosition(col,row);
        //返回x和y
//        return new int[]{row, col};
        return newPosition;
    }
    //WCS系统库位号转路径算法节点
    public static NavigateNode locNoToNode(String locNo) {
        int col = Integer.parseInt(locNo.substring(0, 2));
        int row = Integer.parseInt(locNo.substring(2, 5));
        int[] newPosition = coverPosition(col,row);
        NavigateNode node = new NavigateNode(col, row);
        node.setZ(Utils.getLev(locNo));
        return node;
    }
    //路径算法节点转WCS系统库位号
    public static String nodeToLocNo(NavigateNode node) {
        return xyzToLocNo(node.getX(), node.getY(), node.getZ());
    }
    //WCS坐标转WCS库位号
    public static String xyzToLocNo(int x, int y, int z) {
        String locNo = Utils.getLocNo(x, y, z);
        return locNo;
    }
    //牛眼坐标转WCS库位号
    public static String nyXyzToLocNo(int x, int y, int z) {
        int[] ints = NyXyzToWCSXyz(x, y, z);
        String locNo = Utils.getLocNo(ints[0],ints[1],ints[2]);
        return locNo;
    }
    //WCS系统坐标转牛眼坐标
    public static int[] WCSXyzToNyXyz(int x, int y, int z) {
        //WCS系统Y轴 => 牛眼X轴转换公式
        int x1 = Math.abs(y - 61) + 11;
        //WCS系统X轴 => 牛眼Y轴转换公式
        int y1 = x + 10;
        return new int[]{x1, y1, z};
    }
    //牛眼坐标转WCS系统坐标
    public static int[] NyXyzToWCSXyz(int x, int y, int z) {
        //牛眼X轴 => WCS系统Y轴公式
        int y1 = Math.abs(x - 11 - 61);
        //牛眼Y轴 => WCS系统X轴公式
        int x1 = y - 10;
        return new int[]{x1, y1, z};
    }
    public static int[] coverPosition(int col,int row) {
        return new int[]{col, row};
    }
}
zy-asrs-wcs/src/main/java/com/zy/asrs/wcs/core/utils/NavigateSolution.java
New file
@@ -0,0 +1,217 @@
package com.zy.asrs.wcs.core.utils;
import com.zy.asrs.wcs.core.model.NavigateNode;
import java.util.ArrayList;
import java.util.List;
import java.util.PriorityQueue;
/**
 * 四向库核心
 * A*算法实现类
 */
public class NavigateSolution {
    // -1 -> 墙壁, 1 -> 起点  2 -> 终点  3-> 母轨  4->站点
    int[][] map = {{}};
    public NavigateSolution() {
        //载入地图
        NavigateMapData mapData = new NavigateMapData();
        int[][] data = mapData.getData();
        this.map = data;
    }
    public NavigateSolution(Integer mapType, Integer lev, List<int[]> whitePoints, List<int[]> shuttlePoints) {
        //载入地图指定层高地图
        NavigateMapData mapData = new NavigateMapData(lev);
        int[][] data = mapData.getDataFromRedis(mapType, whitePoints, shuttlePoints);
        if (data == null) {
            data = mapData.getData(mapType, whitePoints, shuttlePoints);
        }
        this.map = data;
    }
    public NavigateSolution(int[][] data) {
        this.map = data;
    }
    // Open表用优先队列
    public PriorityQueue<NavigateNode> Open = new PriorityQueue<NavigateNode>();
    //Close表用普通的数组
    public ArrayList<NavigateNode> Close = new ArrayList<NavigateNode>();
    //Exist表用来存放已经出现过的结点。
    public ArrayList<NavigateNode> Exist = new ArrayList<NavigateNode>();
    public NavigateNode astarSearch(NavigateNode start, NavigateNode end) {
        //把第一个开始的结点加入到Open表中
        this.Open.add(start);
        //把出现过的结点加入到Exist表中
        this.Exist.add(start);
        //主循环
        while (Open.size() > 0) {
            //取优先队列顶部元素并且把这个元素从Open表中删除
            NavigateNode current_node = Open.poll();
            //将这个结点加入到Close表中
            Close.add(current_node);
            //对当前结点进行扩展,得到一个四周结点的数组
            ArrayList<NavigateNode> neighbour_node = extend_current_node(current_node);
            //对这个结点遍历,看是否有目标结点出现
            for (NavigateNode node : neighbour_node) {
                // G + H + E (对启发函数增加去拐点方案calcNodeExtraCost)
                int gCost = calcNodeCost(current_node, node) * calcNodeExtraCost(current_node, node, end);
                if (node.getX() == end.getX() && node.getY() == end.getY()) {//找到目标结点就返回
                    //init_node操作把这个邻居结点的父节点设置为当前结点
                    //并且计算出G, F, H等值
                    node.init_node(current_node, end);
                    return node;
                }
                //(对启发函数增加去拐点方案calcNodeExtraCost)
                if (is_exist(node)) {
                    if (gCost < node.getG()) {
                        node.setFather(current_node);
                        node.setG(gCost);
                        node.setF(node.getG() + node.getH());
                    }
                }else {
                    //没出现过的结点加入到Open表中并且设置父节点
                    //进行计算对G, F, H 等值
                    node.init_node(current_node, end);
                    node.setG(gCost);
                    node.setH(calcNodeCost(node, end));
                    node.setF(node.getG() + node.getH());
                    Open.add(node);
                    Exist.add(node);
                }
            }
        }
        //如果遍历完所有出现的结点都没有找到最终的结点,返回null
        return null;
    }
    public ArrayList<NavigateNode> extend_current_node(NavigateNode current_node) {
        //获取当前结点的x, y
        int x = current_node.getX();
        int y = current_node.getY();
        //如果当前结点的邻结点合法,就加入到neighbour_node
        ArrayList<NavigateNode> neighbour_node = new ArrayList<NavigateNode>();
//        if (map[x][y] == 0 || map[x][y] == 3) {
//            //只有子轨和母轨才能进行左右移动
//            if (is_valid(x, y + 1))
//            {
//                Node node = new Node(x, y + 1);
//                neighbour_node.add(node);
//            }
//            if (is_valid(x, y - 1))
//            {
//                Node node = new Node(x, y - 1);
//                neighbour_node.add(node);
//            }
//        }
//
//        if (map[x][y] == 3) {
//            //只有母轨才能进行上下移动
//            if (is_valid(x + 1, y))
//            {
//                Node node = new Node(x + 1, y);
//                neighbour_node.add(node);
//            }
//            if (is_valid(x - 1, y))
//            {
//                Node node = new Node(x -1, y);
//                neighbour_node.add(node);
//            }
//        }
        if (map[x][y] == 3) {
            //母轨才能进行左右移动
            if (is_valid(x, y + 1))
            {
                NavigateNode node = new NavigateNode(x, y + 1);
                neighbour_node.add(node);
            }
            if (is_valid(x, y - 1))
            {
                NavigateNode node = new NavigateNode(x, y - 1);
                neighbour_node.add(node);
            }
        }
        if (map[x][y] == 0 || map[x][y] == 3 || map[x][y] == 4 || map[x][y] == 5) {
            //子轨和母轨、输送线、充电桩才能进行上下移动
            if (is_valid(x + 1, y))
            {
                NavigateNode node = new NavigateNode(x + 1, y);
                neighbour_node.add(node);
            }
            if (is_valid(x - 1, y))
            {
                NavigateNode node = new NavigateNode(x -1, y);
                neighbour_node.add(node);
            }
        }
        return neighbour_node;
    }
    public boolean is_valid(int x, int y) {
        // 如果结点的位置小于0,则不合法
        if (map[x][y] < 0) return false;
        for (NavigateNode node : Exist) {
            //如果结点出现过,不合法
            if (node.getX() == x && node.getY() == y) {
                return false;
            }
            if (is_exist(new NavigateNode(x, y))) {
                return false;
            }
        }
        //以上情况都没有则合法
        return true;
    }
    public boolean is_exist(NavigateNode node)
    {
        for (NavigateNode exist_node : Exist) {
            if (node.getX() == exist_node.getX() && node.getY() == exist_node.getY()) {
                return true;
            }
        }
        return false;
    }
    //------------------A*启发函数------------------//
    //计算通过现在的结点的位置和最终结点的位置计算H值(曼哈顿法:坐标分别取差值相加)
    private int calcNodeCost(NavigateNode node1, NavigateNode node2) {
        return Math.abs(node2.getX() - node1.getX()) + Math.abs(node2.getY() - node1.getY());
    }
    //去除拐点算法,给直线增加优先级
    private int calcNodeExtraCost(NavigateNode currNode, NavigateNode nextNode, NavigateNode endNode) {
        // 第一个点或直线点
        if (currNode.getFather() == null || nextNode.getX() == currNode.getFather().getX()
                || nextNode.getY() == currNode.getFather().getY()) {
            return 0;
        }
        // 拐向终点的点
        if (nextNode.getX() == endNode.getX() || nextNode.getY() == endNode.getY()) {
            return 1;
        }
        // 普通拐点
        /*
        拐点判断逻辑
        拿到父节点和下一节点
        通过判断父节点和下一节点的x数据和y数据都不相同时,则表明当前坐标是一个拐点
         */
        return 2;
    }
    //------------------A*启发函数-end------------------//
}
zy-asrs-wcs/src/main/java/com/zy/asrs/wcs/core/utils/NavigateUtils.java
New file
@@ -0,0 +1,278 @@
package com.zy.asrs.wcs.core.utils;
import com.zy.asrs.wcs.core.model.MapNode;
import com.zy.asrs.wcs.core.model.NavigateNode;
import com.zy.asrs.wcs.core.model.enums.NavigationMapType;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
/**
 * A*算法使用工具
 */
public class NavigateUtils {
    public static List<NavigateNode> calc(String startPoint, String endPoint, Integer mapType, List<int[]> shuttlePoints) {
        //通过开始编号和结束编号获取对应的xy轴坐标
        int[] startArr = NavigatePositionConvert.positionToXY(startPoint);//开始节点
        int[] endArr = NavigatePositionConvert.positionToXY(endPoint);//结束节点
        ArrayList<int[]> whiteList = new ArrayList<>();//设置计算节点的白名单
        whiteList.add(startArr);//将开始节点设置为白名单,以防被过滤
        //获取当前节点计算的层高,并赋值到每一个节点中
        int lev = Utils.getLev(startPoint);
        //初始化开始节点
        NavigateNode start = new NavigateNode(startArr[0], startArr[1]);
        //开始节点无父节点
        start.setFather(null);
        NavigateNode end = new NavigateNode(endArr[0], endArr[1]);
        NavigateSolution solution = new NavigateSolution(mapType, lev, whiteList, shuttlePoints);
        //开始节点,不纳入禁用节点内计算
        NavigateNode res_node = solution.astarSearch(start, end);
        if (res_node == null) {
            System.err.println("未找到路径");
            return null;
        }
        ArrayList<NavigateNode> list = new ArrayList<>();
        //渲染
        NavigateNode fatherNode = null;//当前循环上一节点,用于拐点计算
        while (res_node != null) {
            res_node.setDirection(null);
            res_node.setIsInflectionPoint(false);
            res_node.setZ(lev);//设置层高
            //寻找拐点
            HashMap<String, Object> result = searchInflectionPoint(res_node, fatherNode, res_node.getFather());//分别传入当前节点、父节点、下一节点
            //判断当前节点是否为拐点
            if (Boolean.parseBoolean(result.get("result").toString())) {
                //当前为拐点
                res_node.setIsInflectionPoint(true);
                //拐点方向
                res_node.setDirection(result.get("direction").toString());
            }
            list.add(res_node);
            fatherNode = res_node;//把当前节点保存成一个父节点
            res_node = res_node.getFather();//迭代操作
        }
        Collections.reverse(list);
        //将每个节点里面的fatherNode至为null(方便后续计算时父节点过多导致显示的节点太多)
        for (NavigateNode navigateNode : list) {
            //父节点设置为null,不影响计算结果,不影响后续操作。
            //此操作仅为后续排查处理提供视觉方便。
            navigateNode.setFather(null);
        }
        //起始节点计算方向
        String direction = calcDirection(list.get(0), list.get(1));
        NavigateNode startNode = list.get(0);
        startNode.setDirection(direction);
        //更新节点列表
        list.set(0, startNode);
        return list;
    }
    //判断当前节点到下一个节点是否为拐点
    public static HashMap<String,Object> searchInflectionPoint(NavigateNode currentNode, NavigateNode fatherNode, NavigateNode nextNode) {
        HashMap<String, Object> map = new HashMap<>();
        map.put("result", false);//是否为拐点,true:拐点,false:直线
        // 第一个点或直线点
        if (fatherNode == null || nextNode == null || nextNode.getX() == fatherNode.getX() || nextNode.getY() == fatherNode.getY()) {
            return map;//不是拐点直接返回
        }
        //拐点方向
        String direction = calcDirection(currentNode, fatherNode);
        map.put("result", true);//拐点
        map.put("direction", direction);//拐点方向(从当前节点视角看的方向)
        return map;
    }
    /**
     * 计算方向
     */
    public static String calcDirection(NavigateNode currentNode, NavigateNode fatherNode) {
        //拐点方向
        String direction = "";
        // 普通拐点
        //计算拐点方向
        if (fatherNode.getX() != currentNode.getX()) {
            //x轴数据有差异,判断x轴方向
            //当前节点X - 父节点X
            if (currentNode.getX() - fatherNode.getX() > 0) {
                //大于0,方向top
                direction = "top";
            }else {
                //小于0,方向bottom
                direction = "bottom";
            }
        }
        if (fatherNode.getY() != currentNode.getY()) {
            //y轴数据有差异,判断y轴方向
            //当前节点Y - 父节点Y
            if (currentNode.getY() - fatherNode.getY() > 0) {
                //大于0,方向left
                direction = "left";
            }else {
                //小于0,方向right
                direction = "right";
            }
        }
        return direction;
    }
    /**
     * 加转弯节点
     * 获取分段路径,每当有一个拐点则进行一次分段,最终返回总分段数据
     */
    public static ArrayList<ArrayList<NavigateNode>> getSectionPath(List<NavigateNode> mapList) {
        ArrayList<ArrayList<NavigateNode>> list = new ArrayList<>();
        ArrayList<NavigateNode> data = new ArrayList<>();
        String direction = mapList.get(0).getDirection();//行走方向
        for (NavigateNode navigateNode : mapList) {
            data.add(navigateNode);
            //拐点
            if (navigateNode.getIsInflectionPoint()) {
                //分割数据
                list.add(data);//添加某一段数据
                direction = navigateNode.getDirection();//更新行走方向
                data = new ArrayList<>();
                data.add(navigateNode);//将拐点的终点,更新成下一段命令的起点坐标
            } else {
                //直行线路
                navigateNode.setDirection(direction);//设置行走方向
            }
            Integer distance = getXToNextDistance(navigateNode);//获取当前点到下一点的行走距离
            navigateNode.setMoveDistance(distance);
        }
        //将最后一段数据添加进入
        list.add(data);
        return list;
    }
    //获取从x点到下一点的行走距离
    public static Integer getXToNextDistance(NavigateNode xNode) {
        NavigateMapData mapData = new NavigateMapData();
        List<List<MapNode>> lists = mapData.getJsonData(NavigationMapType.NONE.id, null, null);
        if (lists != null) {
            MapNode mapNode = lists.get(xNode.getX()).get(xNode.getY());
            if (mapNode != null) {
                switch (xNode.getDirection()) {
                    case "top":
                        return mapNode.getTop();
                    case "bottom":
                        return mapNode.getBottom();
                    case "left":
                        return mapNode.getLeft();
                    case "right":
                        return mapNode.getRight();
                }
            }
            return 0;
        }
        return 0;
    }
    /**
     * 根据原始节点结果,计算总行走距离
     */
    public static Integer getOriginPathAllDistance(List<NavigateNode> path) {
        ArrayList<ArrayList<NavigateNode>> sectionPath = NavigateUtils.getSectionPath(path);
        Integer allDistance = 0;
        for (ArrayList<NavigateNode> navigateNodes : sectionPath) {
            Integer distance = NavigateUtils.getCurrentPathAllDistance(navigateNodes);
            allDistance += distance;
        }
        return allDistance;
    }
    /**
     * 获取当前路径总行走距离
     */
    public static Integer getCurrentPathAllDistance(List<NavigateNode> path) {
        if (path.size() == 1) {
            //路径只有一条数据,则直接返回行走距离
            return path.get(0).getMoveDistance();
        }
        //总距离
        int allDistance = 0;
        for (int i = 0; i < path.size() - 1; i++) {//路径中最后一个节点不统计到总距离,最后一个节点并不会再行走
            allDistance += path.get(i).getMoveDistance();
        }
        return allDistance;
    }
    /**
     * 获取中间点到目标点行走距离
     */
    public static Integer getMiddleToDistDistance(List<NavigateNode> path, NavigateNode middlePath) {
        //最后一条节点不计算进行走距离
        NavigateNode lastPath = path.get(path.size() - 1);
        //总距离
        int allDistance = 0;
        boolean flag = false;
        for (NavigateNode navigateNode : path) {
            if (!flag && navigateNode.equals(middlePath)) {
                flag = true;
            }
            if (navigateNode.equals(lastPath)) {
                continue;//最后一条节点不计算进行走距离
            }
            if (flag) {
                allDistance += navigateNode.getMoveDistance();
            }
        }
        return allDistance;
    }
    /**
     * 检测路径是否可用(可走)
     */
    public static boolean checkPathIsAvailable(List<NavigateNode> path, Integer shuttleNo, Integer lev) {
        NavigateSolution solution = new NavigateSolution(NavigationMapType.DFX.id, lev, null, Utils.getShuttlePoints(shuttleNo, lev));//获取无白名单地图(该地图包含小车坐标)
        int[][] map = solution.map;
        for (NavigateNode node : path) {
            int value = map[node.getX()][node.getY()];
            if (value != 0 && value != 3 && value != 5) {//母轨道3、子轨道0、充电桩5
                return false;
            }
        }
        return true;
    }
    public static void main(String[] args) {
        //计算路径
        List<NavigateNode> calc = calc("1000901", "1800201", NavigationMapType.NONE.id, null);
        System.out.println(calc);
        System.out.println("------------------------");
//        List<NavigateNode> calc = calc("0501401", "0201801", "out");
        //将路径分割成多段
        ArrayList<ArrayList<NavigateNode>> data = getSectionPath(calc);
        for (ArrayList<NavigateNode> list : data) {
            Integer currentPathAllDistance = getCurrentPathAllDistance(list);//计算当前路径总距离
            System.out.println(currentPathAllDistance);
            System.out.println(list);
        }
    }
}
zy-asrs-wcs/src/main/java/com/zy/asrs/wcs/core/utils/Utils.java
@@ -1,10 +1,60 @@
package com.zy.asrs.wcs.core.utils;
import com.zy.asrs.framework.common.Cools;
import java.util.ArrayList;
import java.util.List;
public class Utils {
    /**
     * 通过库位号获取 排
     */
    public static int getRow(String locNo) {
        if (!Cools.isEmpty(locNo)) {
            return Integer.parseInt(locNo.substring(0, 2));
        }
        throw new RuntimeException("库位解析异常");
    }
    /**
     * 通过库位号获取 列
     */
    public static int getBay(String locNo) {
        if (!Cools.isEmpty(locNo)) {
            return Integer.parseInt(locNo.substring(2, 5));
        }
        throw new RuntimeException("库位解析异常");
    }
    /**
     * 通过库位号获取 层
     */
    public static int getLev(String locNo) {
        if (!Cools.isEmpty(locNo)) {
            return Integer.parseInt(locNo.substring(5, 7));
        }
        throw new RuntimeException("库位解析异常");
    }
    public static String getLocNo(Number row, Number bay, Number lev) {
        return zerofill(String.valueOf(row), 2) + zerofill(String.valueOf(bay), 3) + zerofill(String.valueOf(lev), 2);
    }
    public static String zerofill(String msg, Integer count){
        if (msg.length() == count){
            return msg;
        } else if (msg.length() > count){
            return msg.substring(0, 16);
        } else {
            StringBuilder msgBuilder = new StringBuilder(msg);
            for (int i = 0; i<count-msg.length(); i++){
                msgBuilder.insert(0,"0");
            }
            return msgBuilder.toString();
        }
    }
    //获取除白名单外的指定楼层全部穿梭车xy坐标点
    public static List<int[]> getShuttlePoints(Integer whiteShuttle, Integer lev) {
//        SlaveProperties slaveProperties = SpringUtils.getBean(SlaveProperties.class);
zy-asrs-wcs/src/main/java/com/zy/asrs/wcs/rcs/entity/Motion.java
@@ -438,5 +438,14 @@
        return null;
    }
    public String getMotionCtgEl(){
        MotionCtgService service = SpringUtils.getBean(MotionCtgService.class);
        MotionCtg entity = service.getById(this.motionCtg);
        if (!Cools.isEmpty(entity)){
            return String.valueOf(entity.getFlag());
        }
        return null;
    }
}
zy-asrs-wcs/src/main/java/com/zy/asrs/wcs/rcs/model/protocol/ShuttleProtocol.java
@@ -3,12 +3,15 @@
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zy.asrs.framework.common.Cools;
import com.zy.asrs.framework.common.SpringUtils;
import com.zy.asrs.wcs.core.entity.Loc;
import com.zy.asrs.wcs.core.service.LocService;
import com.zy.asrs.wcs.core.utils.RedisUtil;
import com.zy.asrs.wcs.rcs.constant.DeviceRedisConstant;
import com.zy.asrs.wcs.rcs.model.enums.ShuttleDeviceStatusType;
import com.zy.asrs.wcs.rcs.model.enums.ShuttleProtocolStatusType;
import com.zy.asrs.wcs.rcs.entity.Device;
import com.zy.asrs.wcs.rcs.entity.ShuttleDeviceStatus;
import com.zy.asrs.wcs.rcs.model.enums.SlaveType;
import com.zy.asrs.wcs.rcs.service.ShuttleDeviceStatusService;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
@@ -225,4 +228,25 @@
        return this.taskNo == null ? 0 : this.taskNo;
    }
    //通过当前二维码获取当前库位号
    public String getCurrentLocNo() {
        LocService locService = SpringUtils.getBean(LocService.class);
        if (locService == null) {
            return null;
        }
        if (this.currentCode == null) {
            return null;
        }
        //源库位(小车当前位置)
        Loc currentLoc = locService.getOne(new LambdaQueryWrapper<Loc>()
                .eq(Loc::getCode, this.currentCode)
                .eq(Loc::getHostId, this.device.getHostId()));
        if (currentLoc == null) {
            return null;
        }
        return currentLoc.getLocNo();
    }
}
zy-asrs-wcs/src/main/java/com/zy/asrs/wcs/rcs/service/MotionCtgService.java
@@ -5,4 +5,6 @@
public interface MotionCtgService extends IService<MotionCtg> {
    MotionCtg selectByFlag(String flag);
}
zy-asrs-wcs/src/main/java/com/zy/asrs/wcs/rcs/service/impl/MotionCtgServiceImpl.java
@@ -1,5 +1,6 @@
package com.zy.asrs.wcs.rcs.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zy.asrs.wcs.rcs.mapper.MotionCtgMapper;
import com.zy.asrs.wcs.rcs.entity.MotionCtg;
import com.zy.asrs.wcs.rcs.service.MotionCtgService;
@@ -9,4 +10,9 @@
@Service("motionCtgService")
public class MotionCtgServiceImpl extends ServiceImpl<MotionCtgMapper, MotionCtg> implements MotionCtgService {
    @Override
    public MotionCtg selectByFlag(String flag) {
        return this.getOne(new LambdaQueryWrapper<MotionCtg>().eq(MotionCtg::getFlag, flag));
    }
}
zy-asrs-wcs/src/main/java/com/zy/asrs/wcs/rcs/thread/ShuttleThread.java
@@ -1,15 +1,23 @@
package com.zy.asrs.wcs.rcs.thread;
import com.zy.asrs.wcs.core.model.command.ShuttleCommand;
import com.zy.asrs.wcs.rcs.entity.Device;
import com.zy.asrs.wcs.rcs.model.protocol.ShuttleProtocol;
public interface ShuttleThread extends ThreadHandler{
    ShuttleProtocol getStatus();//获取四向穿梭车状态
    Device getDevice();//获取设备信息
    boolean movePath();//路径下发
    boolean move();//移动
    boolean lift();//顶升
    //***************获取命令*****************
    ShuttleCommand getMoveCommand(Integer taskNo, String startCodeNum, String distCodeNum, Integer allDistance, Integer runDirection, Integer runSpeed);
}
zy-asrs-wcs/src/main/java/com/zy/asrs/wcs/rcs/thread/impl/SurayShuttleThread.java
@@ -2,9 +2,14 @@
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zy.asrs.common.utils.HttpHandler;
import com.zy.asrs.framework.common.DateUtils;
import com.zy.asrs.framework.common.SpringUtils;
import com.zy.asrs.framework.exception.CoolException;
import com.zy.asrs.wcs.core.entity.Loc;
import com.zy.asrs.wcs.core.model.command.ShuttleCommand;
import com.zy.asrs.wcs.core.service.LocService;
import com.zy.asrs.wcs.rcs.News;
import com.zy.asrs.wcs.rcs.cache.OutputQueue;
import com.zy.asrs.wcs.rcs.model.enums.ShuttleProtocolStatusType;
@@ -148,6 +153,11 @@
    }
    @Override
    public Device getDevice() {
        return this.device;
    }
    @Override
    public synchronized boolean movePath() {
        return false;
    }
@@ -162,6 +172,32 @@
        return false;
    }
    @Override
    public ShuttleCommand getMoveCommand(Integer taskNo, String startCodeNum, String distCodeNum, Integer allDistance, Integer runDirection, Integer runSpeed) {
        HashMap<String, Object> body = new HashMap<>();
        body.put("messageName", "runOrder");
        body.put("msgTime", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
        body.put("deviceNo", Integer.parseInt(this.device.getDeviceNo()));
        body.put("taskId", taskNo);
        LocService locService = SpringUtils.getBean(LocService.class);
        Loc loc = locService.getOne(new LambdaQueryWrapper<Loc>()
                .eq(Loc::getCode, distCodeNum)
                .eq(Loc::getHostId, this.device.getHostId()));
        if (loc == null) {
            throw new CoolException("库位信息不存在");
        }
        body.put("nodeX", loc.getRow());
        body.put("nodeY", loc.getBay());
        body.put("nodeZ", loc.getLev());
        ShuttleCommand command = new ShuttleCommand();
        command.setShuttleNo(Integer.parseInt(this.device.getDeviceNo()));
        command.setBody(JSON.toJSONString(body));
        return command;
    }
    //***************设备层通讯-不同厂商设备通讯方案不一致***************
    //请求登录