zhang
2025-06-12 3ae3ce1721b1a98b17c2c602c99b7226cbd32894
对接开发
5个文件已修改
7个文件已添加
1407 ■■■■■ 已修改文件
zy-acs-flow/src/map/tool.js 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-framework/src/main/java/com/zy/acs/framework/common/R.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-framework/src/main/java/com/zy/acs/framework/exception/CoolException.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/pom.xml 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/java/com/zy/acs/manager/common/exception/GlobalExceptionHandler.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/java/com/zy/acs/manager/core/service/MainZkdService.java 1120 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/java/com/zy/acs/manager/core/service/TrafficZkdService.java 112 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/java/com/zy/acs/manager/core/third/zkd/HttpUtils.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/java/com/zy/acs/manager/core/third/zkd/dto/AllocateTask.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/java/com/zy/acs/manager/core/third/zkd/dto/AllocateTaskResponse.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/java/com/zy/acs/manager/core/third/zkd/dto/Navigation.java 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/java/com/zy/acs/manager/manager/controller/ZkdController.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-flow/src/map/tool.js
@@ -501,6 +501,11 @@
            routeList.forEach(route => {
                const startPoint = querySprite(DEVICE_TYPE.POINT, route.startCodeStr);
                const endPoint = querySprite(DEVICE_TYPE.POINT, route.endCodeStr);
                if (!startPoint || !endPoint) {
                    console.error("route", route)
                    console.error("start", startPoint)
                    console.error("end", endPoint)
                }
                const pointRoute = new PointRoute(POINT_ROUTE_DIRECTION[route.direction]);
                pointRoute.setPoint(startPoint, endPoint);
                pointRoute.clear();
zy-acs-framework/src/main/java/com/zy/acs/framework/common/R.java
@@ -42,6 +42,13 @@
        return r;
    }
    public static R error(Integer code,String msg){
        R r = error();
        r.put(MSG, msg);
        r.put(CODE, code);
        return r;
    }
    public R add(Object obj){
        this.put(DATA, obj);
        return this;
zy-acs-framework/src/main/java/com/zy/acs/framework/exception/CoolException.java
@@ -1,10 +1,16 @@
package com.zy.acs.framework.exception;
import com.zy.acs.framework.common.BaseRes;
import lombok.Data;
/**
 * 框架异常
 * Created by vincent on 2019-09-04
 */
@Data
public class CoolException extends RuntimeException {
    private Integer code;
    public CoolException(Throwable e) {
        super(e);
@@ -14,4 +20,9 @@
        super(message);
    }
}
    public CoolException(Integer code, String message) {
        super(message);
        this.code = code;
    }
}
zy-acs-manager/pom.xml
@@ -160,6 +160,13 @@
<!--            <version>1.0.0-M2.1</version>-->
<!--        </dependency>-->
        <!-- okHttp3 -->
        <dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp</artifactId>
            <version>4.9.0</version>
        </dependency>
    </dependencies>
    <build>
zy-acs-manager/src/main/java/com/zy/acs/manager/common/exception/GlobalExceptionHandler.java
@@ -1,9 +1,9 @@
package com.zy.acs.manager.common.exception;
import com.zy.acs.framework.common.R;
import com.zy.acs.framework.exception.CoolException;
import com.zy.acs.manager.common.constant.Constants;
import com.zy.acs.manager.common.utils.CommonUtil;
import com.zy.acs.framework.common.R;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.access.AccessDeniedException;
@@ -51,7 +51,10 @@
    @ExceptionHandler(CoolException.class)
    public R coolExceptionHandler(CoolException e, HttpServletResponse response) {
        CommonUtil.addCrossHeaders(response);
        return R.error(e.getMessage());
        if (e.getCode() == null) {
            return R.error(e.getMessage());
        }
        return R.error(e.getCode(), e.getMessage());
    }
    @ResponseBody
zy-acs-manager/src/main/java/com/zy/acs/manager/core/service/MainZkdService.java
New file
@@ -0,0 +1,1120 @@
package com.zy.acs.manager.core.service;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zy.acs.common.domain.AgvAction;
import com.zy.acs.common.domain.AgvActionItem;
import com.zy.acs.common.domain.AgvProtocol;
import com.zy.acs.common.domain.BaseResult;
import com.zy.acs.common.domain.protocol.AGV_11_UP;
import com.zy.acs.common.domain.protocol.AGV_70_UP;
import com.zy.acs.common.domain.protocol.IMessageBody;
import com.zy.acs.common.domain.protocol.action.*;
import com.zy.acs.common.enums.AgvBackpackType;
import com.zy.acs.common.enums.AgvCompleteType;
import com.zy.acs.common.enums.AgvDirectionType;
import com.zy.acs.common.enums.AgvSpeedType;
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.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.TaskPosDto;
import com.zy.acs.manager.core.service.astart.MapDataDispatcher;
import com.zy.acs.manager.core.third.zkd.HttpUtils;
import com.zy.acs.manager.core.third.zkd.dto.AllocateTask;
import com.zy.acs.manager.core.third.zkd.dto.AllocateTaskResponse;
import com.zy.acs.manager.core.third.zkd.dto.Navigation;
import com.zy.acs.manager.manager.entity.*;
import com.zy.acs.manager.manager.enums.*;
import com.zy.acs.manager.manager.service.*;
import com.zy.acs.manager.manager.service.impl.WebsocketServiceImpl;
import com.zy.acs.manager.manager.utils.ActionSorter;
import com.zy.acs.manager.system.service.ConfigService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
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 MainZkdService {
    @Autowired
    private BusService busService;
    @Autowired
    private TaskService taskService;
    @Autowired
    private ActionService actionService;
    @Autowired
    private StaService staService;
    @Autowired
    private LocService locService;
    @Autowired
    private AgvService agvService;
    @Autowired
    private AgvDetailService agvDetailService;
    @Autowired
    private ConfigService configService;
    @Autowired
    private ValidService validService;
    @Autowired
    private AllocateService allocateService;
    @Autowired
    private CodeService codeService;
    @Autowired
    private MapService mapService;
    @Autowired
    private SnowflakeIdWorker snowflakeIdWorker;
    @Autowired
    private CodeGapService codeGapService;
    @Autowired
    private AgvCmdService agvCmdService;
    @Autowired
    private FuncStaService funcStaService;
    @Autowired
    private MapDataDispatcher mapDataDispatcher;
    @Autowired
    private TravelService travelService;
    @Autowired
    private SegmentService segmentService;
    @Autowired
    private TrafficService trafficService;
    @Autowired
    private AgvModelService agvModelService;
    @Autowired
    private LaneService laneService;
    @Autowired
    private ActionSorter actionSorter;
    /**
     * 任务分配给车辆 ( 车辆此时是空闲且静止的 )
     */
    @Transactional
    public synchronized void allocateTaskByZkd(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());
            List<AllocateTask> allocateTasks = new ArrayList<>();
            AllocateTask allocateTask = null;
            String startCode = null;
            String endCode = null;
            Loc oriLoc = null;
            Loc destLoc = null;
            Sta oriSta = null;
            Sta destSta = null;
            for (Task task : taskList) {
                allocateTask = new AllocateTask();
                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()).getData();
                        endCode = codeService.getCacheById(destLoc.getCode()).getData();
                        break;
                    case LOC_TO_STA:
                        oriLoc = locService.getById(task.getOriLoc());
                        destSta = staService.getById(task.getDestSta());
                        startCode = codeService.getCacheById(oriLoc.getCode()).getData();
                        endCode = codeService.getCacheById(destSta.getCode()).getData();
                        break;
                    case STA_TO_LOC:
                        oriSta = staService.getById(task.getOriSta());
                        destLoc = locService.getById(task.getDestLoc());
                        startCode = codeService.getCacheById(oriSta.getCode()).getData();
                        endCode = codeService.getCacheById(destLoc.getCode()).getData();
                        break;
                    case STA_TO_STA:
                        oriSta = staService.getById(task.getOriSta());
                        destSta = staService.getById(task.getDestSta());
                        startCode = codeService.getCacheById(oriSta.getCode()).getData();
                        endCode = codeService.getCacheById(destSta.getCode()).getData();
                        break;
                    default:
                        throw new BusinessException(task.getSeqNum() + "任务类型错误");
                }
                allocateTask.setTaskId(task.getId() + "");
                allocateTask.setStart(startCode);
                allocateTask.setEnd(endCode);
                allocateTask.setType("1");
                allocateTask.setPriority(task.getPriority());
            }
            allocateTasks.add(allocateTask);
            String post = HttpUtils.post("http://localhost:8080/zy-acs-manager/api/v1/zkd/allocateTask", JSON.toJSONString(allocateTasks));
            List<AllocateTaskResponse> allocateTaskResponses = JSON.parseArray(post, AllocateTaskResponse.class);
            for (AllocateTaskResponse allocateTaskResponse : allocateTaskResponses) {
                Task task = taskService.getById(allocateTaskResponse.getTaskId());
                task.setAgvId(Long.parseLong(allocateTaskResponse.getAgvId()));
                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.allocateTaskByZkd", 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, Navigation navigation) {
        try {
            // 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(navigation));
            //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();
        }
    }
    /**
     * 根据分片生成动作 ( 车辆可能已经做过一些任务了,正在等待下一段任务 )
     */
    @Transactional
    public synchronized void generateAction(Long agvId, List<Segment> segmentList, List<String> pathList, Date algoStartTime) {
        try {
            if (Cools.isEmpty(agvId, segmentList)) {
                return;
            }
            Date now = new Date();
            long actionPrepareSts = ActionStsType.PREPARE.val();
//            JSONObject storeDirection = configService.getVal("storeDirection", JSONObject.class);
            int angleOffsetVal = configService.getVal("mapAngleOffsetVal", Integer.class);
            String agvNo = agvService.getAgvNo(agvId);
//            if (!agvService.judgeEnable(agvId)) {
//                throw new CoolException("AGV[" + agvNo + "]当前不可用...");
//            }
            AgvModel agvModel = agvModelService.getByAgvId(agvId);
            Double workDirection = agvModel.getWorkDirection();
            AgvSpeedType agvSpeedType = AgvSpeedType.query(agvModel.getTravelSpeed());
            assert agvSpeedType != null;
            AgvDetail agvDetail = agvDetailService.selectMajorByAgvId(agvId);
            List<Action> actionList = new ArrayList<>();
            // start node
            Code lastCode = codeService.getCacheById(agvDetail.getRecentCode());
            Double lastDirection = MapService.mapToNearest(agvDetail.getAgvAngle());
            if (!lastCode.getData().equals(pathList.get(0))) {
                throw new CoolException("AGV[" + agvNo + "]定位偏移...");
            }
            boolean first = true;
            for (Segment segment : segmentList) {
                // 分段所属的Task
                Task task = taskService.getById(segment.getTaskId());
                // 节点条码
                Code code = codeService.getCacheById(segment.getEndNode());
                // 需要走行
                if (!lastCode.getData().equals(code.getData())) {
                    // 走行路径节点
//                    List<String> pathList = mapService.checkoutPath(agv.getUuid(), lastCode, code);
                    List<String> pathListPart = pathList.subList(pathList.indexOf(lastCode.getData()), pathList.indexOf(code.getData()) + 1);
                    for (int i = 0; i < pathListPart.size(); i++) {
                        if (i == 0) {
                            continue;
                        }
                        String next = pathListPart.get(i);
                        Code nextCode = codeService.getCacheByData(next);
                        Double nextDirection = mapService.calculateDirection(lastCode, nextCode, angleOffsetVal);
                        // 第一步:如果下一个方向正好是作业方向的相反方向,则重置下一个方向为作业方向,标记 reverse = true
                        boolean reverse = false;
                        if (nextDirection.equals((workDirection + 180) % 360)) {
                            nextDirection = workDirection;
                            reverse = true;
                        }
                        // 第二步:判断当前节点是否可以旋转
                        if (!lastCode.getCornerBool()) {
                            // 如果是作业方向,但是小车在巷道内方向错误,则停止
                            if (reverse && !lastDirection.equals(nextDirection)) {
//                                throw new CoolException(agvNo + "号小车方向错误,请推至转弯点手动调整");
                            }
                            // 如果不是作业方向,判断是否相反方向,如果反方向则倒退行走
                            if (nextDirection.equals((lastDirection + 180) % 360)) {
                                nextDirection = lastDirection;
                                reverse = true;
                            }
                        } else {
                            if (!lastDirection.equals(nextDirection)) {
                                // 如果下个节点方向与当前agv方向相反,则倒退行走,但是如果当前agv方向正好与工作方向相反,则旋转至工作方向
                                if (nextDirection.equals((lastDirection + 180) % 360) && !workDirection.equals((lastDirection + 180) % 360)) {
                                    nextDirection = lastDirection;
                                    reverse = true;
                                } else {
                                    // turn
                                    actionList.add(new Action(
                                            null,    // 编号s
                                            task.getBusId(),    // 总线
                                            task.getId(),    // 任务
                                            null,    // 动作号
                                            null,    // 优先级
                                            ActionTypeType.TurnCorner.desc,    // 名称
                                            mapService.isTurnCorner(lastCode.getData()) ? 1D : 0D,    // 属性值
                                            lastCode.getData(),    // 地面码
                                            String.valueOf(nextDirection),   // 动作参数
                                            ActionTypeType.TurnCorner.val(),    // 动作类型
                                            actionPrepareSts,    // 动作进度
                                            agvId,    // AGV
                                            now    // 工作时间
                                    ));
                                    lastDirection = nextDirection;
                                }
                            }
                        }
                        // 第一个动作一定是 turn
                        if (actionList.isEmpty()) {
                            // turn
                            actionList.add(new Action(
                                    null,    // 编号
                                    task.getBusId(),    // 总线
                                    task.getId(),    // 任务
                                    null,    // 动作号
                                    null,    // 优先级
                                    ActionTypeType.TurnCorner.desc,    // 名称
                                    mapService.isTurnCorner(lastCode.getData()) ? 1D : 0D,    // 属性值
                                    lastCode.getData(),    // 地面码
                                    String.valueOf(nextDirection),   // 动作参数
                                    ActionTypeType.TurnCorner.val(),    // 动作类型
                                    actionPrepareSts,    // 动作进度
                                    agvId,    // AGV
                                    now    // 工作时间
                            ));
                            lastDirection = nextDirection;
                        }
                        // run
                        ActionTypeType actionType = ActionTypeType.StraightAheadTurnable;
                        if (reverse) {
                            actionType = ActionTypeType.StraightBackTurnable;
                        }
                        CodeGap gap = codeGapService.findByCodeOfBoth(lastCode.getId(), nextCode.getId());
                        actionList.add(new Action(
                                null,    // 编号
                                task.getBusId(),    // 总线
                                task.getId(),    // 任务
                                null,    // 动作号
                                null,    // 优先级
                                actionType.desc,    // 名称
                                (double) agvSpeedType.val,    // 属性值
                                lastCode.getData(),    // 地面码
                                String.valueOf(gap.getDistance()),   // 动作参数
                                actionType.val(),    // 动作类型
                                actionPrepareSts,    // 动作进度
                                agvId,    // AGV
                                now    // 工作时间
                        ));
                        lastCode = nextCode;
                    }
                }
                // 初始方向值补丁
                if (first) {
                    if (Cools.isEmpty(actionList) || !actionList.get(0).getActionType().equals(ActionTypeType.TurnCorner.val())) {
                        // turn
                        actionList.add(new Action(
                                null,    // 编号
                                task.getBusId(),    // 总线
                                task.getId(),    // 任务
                                null,    // 动作号
                                null,    // 优先级
                                ActionTypeType.TurnCorner.desc,    // 名称
                                mapService.isTurnCorner(lastCode.getData()) ? 1D : 0D,    // 属性值
                                lastCode.getData(),    // 地面码
                                String.valueOf(workDirection),   // 动作参数
                                ActionTypeType.TurnCorner.val(),    // 动作类型
                                actionPrepareSts,    // 动作进度
                                agvId,    // AGV
                                now    // 工作时间
                        ));
                        lastDirection = workDirection;
                    }
                    first = false;
                }
                // 作业点动作
                AgvDirectionType agvDirectionType;
                Double staWorkDirection;
                AgvBackpackType backpackType = AgvBackpackType.query(segment.getBackpack());
                switch (Objects.requireNonNull(TaskPosDto.queryPosType(segment.getPosType()))) {
                    case ORI_LOC:
                        assert backpackType != null;
                        // 检验方向
                        if (!lastDirection.equals(workDirection)) {
                            throw new CoolException(agvNo + "号小车方向错误,请推至转弯点手动调整");
                        }
                        // 货架取货
                        Loc oriLoc = locService.getById(task.getOriLoc());
                        // 计算左右方向
                        agvDirectionType = mapService.calculateAgvWorkDirectionByShelf(oriLoc, lastCode);
                        actionList.add(new Action(
                                null,    // 编号
                                task.getBusId(),    // 总线
                                task.getId(),    // 任务
                                null,    // 动作号
                                null,    // 优先级
                                ActionTypeType.ReadyTakeFromShelvesLoc.desc,    // 名称
                                (double) agvDirectionType.val,    // 属性值
                                lastCode.getData(),    // 地面码
                                String.valueOf(oriLoc.getOffset()),   // 动作参数
                                ActionTypeType.ReadyTakeFromShelvesLoc.val(),    // 动作类型
                                actionPrepareSts,    // 动作进度
                                agvId,    // AGV
                                now    // 工作时间
                        ));
                        // 暂存点放货
                        actionList.add(new Action(
                                null,    // 编号
                                task.getBusId(),    // 总线
                                task.getId(),    // 任务
                                null,    // 动作号
                                null,    // 优先级
                                ActionTypeType.ReadyReleaseToAgvSite.desc,    // 名称
                                (double) backpackType.lev,    // 属性值
                                lastCode.getData(),    // 地面码
                                String.valueOf(backpackType.height),   // 动作参数
                                ActionTypeType.ReadyReleaseToAgvSite.val(),    // 动作类型
                                actionPrepareSts,    // 动作进度
                                agvId,    // AGV
                                now    // 工作时间
                        ));
                        break;
                    case DEST_LOC:
                        assert backpackType != null;
                        // 检验方向
                        if (!lastDirection.equals(workDirection)) {
                            throw new CoolException(agvNo + "号小车方向错误,请推至转弯点手动调整");
                        }
                        // 暂存点取货货
                        actionList.add(new Action(
                                null,    // 编号
                                task.getBusId(),    // 总线
                                task.getId(),    // 任务
                                null,    // 动作号
                                null,    // 优先级
                                ActionTypeType.ReadyTakeFromAgvSite.desc,    // 名称
                                (double) backpackType.lev,    // 属性值
                                lastCode.getData(),    // 地面码
                                String.valueOf(backpackType.height),   // 动作参数
                                ActionTypeType.ReadyTakeFromAgvSite.val(),    // 动作类型
                                actionPrepareSts,    // 动作进度
                                agvId,    // AGV
                                now    // 工作时间
                        ));
                        // 货架放货
                        Loc destLoc = locService.getById(task.getDestLoc());
                        // 计算左右方向
                        agvDirectionType = mapService.calculateAgvWorkDirectionByShelf(destLoc, lastCode);
                        actionList.add(new Action(
                                null,    // 编号
                                task.getBusId(),    // 总线
                                task.getId(),    // 任务
                                null,    // 动作号
                                null,    // 优先级
                                ActionTypeType.ReadyReleaseToShelvesLoc.desc,    // 名称
                                (double) agvDirectionType.val,    // 属性值
                                lastCode.getData(),    // 地面码
                                String.valueOf(destLoc.getOffset()),   // 动作参数
                                ActionTypeType.ReadyReleaseToShelvesLoc.val(),    // 动作类型
                                actionPrepareSts,    // 动作进度
                                agvId,    // AGV
                                now    // 工作时间
                        ));
                        break;
                    case ORI_STA:
                        // 站点取货
                        Sta oriSta = staService.getById(task.getOriSta());
                        Double oriStaWorkDirection = mapService.getStaAngle(oriSta, workDirection);
                        // 检验方向
                        if (!lastDirection.equals(oriStaWorkDirection)) {
                            if (!lastCode.getCornerBool()) {
                                throw new CoolException(agvNo + "号小车方向错误,请推至转弯点手动调整");
                            }
                            // turn
                            actionList.add(new Action(
                                    null,    // 编号
                                    task.getBusId(),    // 总线
                                    task.getId(),    // 任务
                                    null,    // 动作号
                                    null,    // 优先级
                                    ActionTypeType.TurnCorner.desc,    // 名称
                                    mapService.isTurnCorner(lastCode.getData()) ? 1D : 0D,    // 属性值
                                    lastCode.getData(),    // 地面码
                                    String.valueOf(oriStaWorkDirection),   // 动作参数
                                    ActionTypeType.TurnCorner.val(),    // 动作类型
                                    actionPrepareSts,    // 动作进度
                                    agvId,    // AGV
                                    now    // 工作时间
                            ));
                            lastDirection = oriStaWorkDirection;
                        }
                        // 计算货叉工作方向
                        staWorkDirection = mapService.calculateAgvWorkDirectionByStation(oriStaWorkDirection, lastDirection);
                        actionList.add(new Action(
                                null,    // 编号
                                task.getBusId(),    // 总线
                                task.getId(),    // 任务
                                null,    // 动作号
                                null,    // 优先级
                                ActionTypeType.ReadyTakeFromConveyorSta.desc,    // 名称
                                staWorkDirection,    // 属性值
                                lastCode.getData(),    // 地面码
                                String.valueOf(oriSta.getOffset()),   // 动作参数
                                ActionTypeType.ReadyTakeFromConveyorSta.val(),    // 动作类型
                                actionPrepareSts,    // 动作进度
                                agvId,    // AGV
                                now    // 工作时间
                        ));
                        // 暂存点放货
                        assert backpackType != null;
                        actionList.add(new Action(
                                null,    // 编号
                                task.getBusId(),    // 总线
                                task.getId(),    // 任务
                                null,    // 动作号
                                null,    // 优先级
                                ActionTypeType.ReadyReleaseToAgvSite.desc,    // 名称
                                (double) backpackType.lev,    // 属性值
                                lastCode.getData(),    // 地面码
                                String.valueOf(backpackType.height),   // 动作参数
                                ActionTypeType.ReadyReleaseToAgvSite.val(),    // 动作类型
                                actionPrepareSts,    // 动作进度
                                agvId,    // AGV
                                now    // 工作时间
                        ));
                        break;
                    case DEST_STA:
                        // 站点放货
                        Sta destSta = staService.getById(task.getDestSta());
                        Double destStaWorkDirection = mapService.getStaAngle(destSta, workDirection);
                        // 检验方向
                        if (!lastDirection.equals(destStaWorkDirection)) {
                            if (!lastCode.getCornerBool()) {
                                throw new CoolException(agvNo + "号小车方向错误,请推至转弯点手动调整");
                            }
                            // turn
                            actionList.add(new Action(
                                    null,    // 编号
                                    task.getBusId(),    // 总线
                                    task.getId(),    // 任务
                                    null,    // 动作号
                                    null,    // 优先级
                                    ActionTypeType.TurnCorner.desc,    // 名称
                                    mapService.isTurnCorner(lastCode.getData()) ? 1D : 0D,    // 属性值
                                    lastCode.getData(),    // 地面码
                                    String.valueOf(destStaWorkDirection),   // 动作参数
                                    ActionTypeType.TurnCorner.val(),    // 动作类型
                                    actionPrepareSts,    // 动作进度
                                    agvId,    // AGV
                                    now    // 工作时间
                            ));
                            lastDirection = destStaWorkDirection;
                        }
                        // 暂存点取货
                        assert backpackType != null;
                        actionList.add(new Action(
                                null,    // 编号
                                task.getBusId(),    // 总线
                                task.getId(),    // 任务
                                null,    // 动作号
                                null,    // 优先级
                                ActionTypeType.ReadyTakeFromAgvSite.desc,    // 名称
                                (double) backpackType.lev,    // 属性值
                                lastCode.getData(),    // 地面码
                                String.valueOf(backpackType.height),   // 动作参数
                                ActionTypeType.ReadyTakeFromAgvSite.val(),    // 动作类型
                                actionPrepareSts,    // 动作进度
                                agvId,    // AGV
                                now    // 工作时间
                        ));
                        // 计算货叉工作方向
                        staWorkDirection = mapService.calculateAgvWorkDirectionByStation(destStaWorkDirection, lastDirection);
                        actionList.add(new Action(
                                null,    // 编号
                                task.getBusId(),    // 总线
                                task.getId(),    // 任务
                                null,    // 动作号
                                null,    // 优先级
                                ActionTypeType.ReadyReleaseToConveyorSta.desc,    // 名称
                                staWorkDirection,    // 属性值
                                lastCode.getData(),    // 地面码
                                String.valueOf(destSta.getOffset()),   // 动作参数
                                ActionTypeType.ReadyReleaseToConveyorSta.val(),    // 动作类型
                                actionPrepareSts,    // 动作进度
                                agvId,    // AGV
                                now    // 工作时间
                        ));
                        break;
                    case TO_CHARGE:
                        // 检验方向
                        FuncSta chargeFuncSta = funcStaService.query(lastCode.getId(), FuncStaType.CHARGE.toString());
                        Double chargeDirection = Double.parseDouble(chargeFuncSta.getAngle());
                        if (!lastDirection.equals(chargeDirection)) {
                            actionList.add(new Action(
                                    null,    // 编号
                                    null,    // 总线
                                    task.getId(),    // 任务
                                    null,    // 动作号
                                    null,    // 优先级
                                    ActionTypeType.TurnCorner.desc,    // 名称
                                    mapService.isTurnCorner(lastCode.getData()) ? 1D : 0D,    // 属性值
                                    lastCode.getData(),    // 地面码
                                    String.valueOf(chargeDirection),   // 动作参数
                                    ActionTypeType.TurnCorner.val(),    // 动作类型
                                    actionPrepareSts,    // 动作进度
                                    agvId,    // AGV
                                    now    // 工作时间
                            ));
                            lastDirection = chargeDirection;
                        }
                        // charge
                        actionList.add(new Action(
                                null,    // 编号
                                null,    // 总线
                                task.getId(),    // 任务
                                null,    // 动作号
                                null,    // 优先级
                                ActionTypeType.DockingCharge.desc,    // 名称
                                null,    // 属性值
                                lastCode.getData(),    // 地面码
                                null,   // 动作参数
                                ActionTypeType.DockingCharge.val(),    // 动作类型
                                actionPrepareSts,    // 动作进度
                                agvId,    // AGV
                                now    // 工作时间
                        ));
                        break;
                    case TO_STANDBY:
//                        FuncSta standByFuncSta = funcStaService.query(agvId, lastCode.getId(), 2);
//                        Double standByDirection = Double.parseDouble(standByFuncSta.getAngle());
//                        if (!lastDirection.equals(standByDirection)) {
//                            actionList.add(new Action(
//                                    null,    // 编号
//                                    null,    // 总线
//                                    task.getId(),    // 任务
//                                    null,    // 动作号
//                                    null,    // 优先级
//                                    ActionTypeType.TurnCorner.desc,    // 名称
//                                    null,    // 属性值
//                                    lastCode.getData(),    // 地面码
//                                    String.valueOf(standByDirection),   // 动作参数
//                                    ActionTypeType.TurnCorner.val(),    // 动作类型
//                                    actionPrepareSts,    // 动作进度
//                                    agvId,    // AGV
//                                    now    // 工作时间
//                            ));
//                            lastDirection = standByDirection;
//                        }
                        break;
                    case MOVE:
                        break;
                    default:
                        break;
                }
            }
            // finish
            actionList.add(new Action(
                    null,    // 编号
                    null,    // 总线
                    null,    // 任务
                    null,    // 动作号
                    null,    // 优先级
                    ActionTypeType.FinishPath.desc,    // 名称
                    null,    // 属性值
                    lastCode.getData(),    // 地面码
                    null,   // 动作参数
                    ActionTypeType.FinishPath.val(),    // 动作类型
                    actionPrepareSts,    // 动作进度
                    agvId,    // AGV
                    now    // 工作时间
            ));
            List<Action> newActionList = actionSorter.optimizeSort(actionList);
            String groupId = String.valueOf(snowflakeIdWorker.nextId()).substring(3);
            // save action
            int i = newActionList.size();
            for (Action action : newActionList) {
//                action.setUuid(String.valueOf(snowflakeIdWorker.nextId()).substring(3));
                action.setGroupId(groupId);
                action.setPriority(i);
                i -= 1;
            }
            if (!actionService.saveBatch(newActionList)) {
                throw new BusinessException("group[" + groupId + "] 动作保存失败");
            }
            // update segment
            for (Segment item : segmentList) {
                item.setGroupId(groupId);
                item.setState(SegmentStateType.RUNNING.toString());
                item.setUpdateTime(now);
                if (null != algoStartTime) {
                    item.setAlgoTime((int) (now.getTime() - algoStartTime.getTime()));
                }
                if (!segmentService.updateById(item)) {
                    throw new CoolException("更新Segment失败");
                }
            }
            log.info("{}号Agv动作组装完成,指令数量:{}", agvNo, newActionList.size());
        } catch (Exception e) {
            log.error("mainService.generateAction", e);
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            if (!Cools.isEmpty(pathList)) {
                pathList.remove(0);
                List<int[]> codeMatrixIdxList = mapDataDispatcher.getCodeMatrixIdxList(null, pathList);
                mapDataDispatcher.clearDynamicMatrixByCodeList(null, codeMatrixIdxList);
            }
            throw new RuntimeException("generateAction method caught an exception, rolling back transaction.", e);
        }
    }
    @Transactional
    public void publishAction(String actionGroupId) {
        try {
            Date now = new Date();
            // action
            List<Action> actionList = actionService.list(new LambdaQueryWrapper<Action>()
                    .eq(Action::getGroupId, actionGroupId).eq(Action::getActionSts, ActionStsType.PREPARE.val())
                    .orderByDesc(Action::getPriority));
            if (Cools.isEmpty(actionList)) {
                return;
            }
            Long agvId = actionList.get(0).getAgvId();
            String agvNo = agvService.getAgvNo(agvId);
            if (!agvService.judgeOnline(agvId)) {
                return;
            }
            long actionIssuedSts = ActionStsType.ISSUED.val();
            for (Action action : actionList) {
                action.setActionSts(actionIssuedSts);
                action.setStartTime(now);
                action.setIoTime(now);
                action.setUpdateTime(now);
            }
            if (!actionService.updateBatchById(actionList)) {
                throw new BusinessException("failed to update action batch !!!");
            }
            // task
            List<Long> taskIds = actionService.selectTaskIdsByGroupId(actionGroupId);
            long taskAssignSts = TaskStsType.ASSIGN.val();
            long taskProgressSts = TaskStsType.PROGRESS.val();
            for (Long taskId : taskIds) {
                Task task = taskService.getById(taskId);
                if (task.getTaskSts().equals(taskAssignSts)) {
                    task.setTaskSts(taskProgressSts);
                    task.setUpdateTime(now);
                    if (!taskService.updateById(task)) {
                        throw new BusinessException(task.getSeqNum() + "任务更新失败");
                    }
                }
            }
            AgvAction agvAction = new AgvAction(agvNo, actionGroupId);
            for (Action action : actionList) {
                switch (Objects.requireNonNull(ActionTypeType.get(action.getActionTypeEl()))) {
                    case TurnCorner:
                        agvAction.add(new AgvActionItem<>(TurnCornerAction.class)
                                .setQrCode(action.getCode())
                                .setVal(Optional.ofNullable(action.getVal()).orElse(0D).intValue())
                                .bodySync(body -> body.setAngle((short) Double.parseDouble(action.getParams())))
                        );
                        break;
                    case StraightBackUnturnable:
                        break;
                    case StraightBackTurnable:
                        agvAction.add(new AgvActionItem<>(StraightBackTurnableAction.class)
                                .setVal(action.getVal().intValue())
                                .setQrCode(action.getCode())
                                .bodySync(body -> body.setDistance((short) Double.parseDouble(action.getParams())))
                        );
                        break;
                    case StraightAheadUnturnable:
                        break;
                    case StraightAheadTurnable:
                        agvAction.add(new AgvActionItem<>(StraightAheadTurnableAction.class)
                                .setVal(action.getVal().intValue())
                                .setQrCode(action.getCode())
                                .bodySync(body -> body.setDistance((short) Double.parseDouble(action.getParams())))
                        );
                        break;
                    case ReadyTakeFromShelvesLoc:
                        agvAction.add(new AgvActionItem<>(ReadyTakeFromShelvesLoc.class)
                                .setVal(action.getVal().intValue())
                                .setQrCode(action.getCode())
                                .bodySync(body -> body.setHeight((short) Double.parseDouble(action.getParams())))
                        );
                        break;
                    case ReadyTakeFromConveyorSta:
                        agvAction.add(new AgvActionItem<>(ReadyTakeFromConveyorSta.class)
                                .setVal(action.getVal().intValue())
                                .setQrCode(action.getCode())
                                .bodySync(body -> body.setHeight((short) Double.parseDouble(action.getParams())))
                        );
                        break;
                    case ReadyTakeFromAgvSite:
                        agvAction.add(new AgvActionItem<>(ReadyTakeFromAgvSite.class)
                                .setVal(action.getVal().intValue())
                                .setQrCode(action.getCode())
                                .bodySync(body -> body.setDepth((short) Double.parseDouble(action.getParams())))
                        );
                        break;
                    case ReadyReleaseToShelvesLoc:
                        agvAction.add(new AgvActionItem<>(ReadyReleaseToShelvesLoc.class)
                                .setVal(action.getVal().intValue())
                                .setQrCode(action.getCode())
                                .bodySync(body -> body.setHeight((short) Double.parseDouble(action.getParams())))
                        );
                        break;
                    case ReadyReleaseToConveyorSta:
                        agvAction.add(new AgvActionItem<>(ReadyReleaseToConveyorSta.class)
                                .setVal(action.getVal().intValue())
                                .setQrCode(action.getCode())
                                .bodySync(body -> body.setHeight((short) Double.parseDouble(action.getParams())))
                        );
                        break;
                    case ReadyReleaseToAgvSite:
                        agvAction.add(new AgvActionItem<>(ReadyReleaseToAgvSite.class)
                                .setVal(action.getVal().intValue())
                                .setQrCode(action.getCode())
                                .bodySync(body -> body.setDepth((short) Double.parseDouble(action.getParams())))
                        );
                        break;
                    case FinishPath:
                        agvAction.add(new AgvActionItem<>(FinishPathAction.class)
                                .setQrCode(action.getCode())
                        );
                        break;
                    case DockingCharge:
                        agvAction.add(new AgvActionItem<>(DockingChargeAction.class)
                                .setQrCode(action.getCode())
                        );
                        break;
                    default:
                        break;
                }
            }
            BaseResult<?> result = agvCmdService.executeAgvActionCmd(agvAction);
            if (result.success()) {
                log.info("任务组 [{}] 动作指令已下发 ===>> 指令数量:{}", actionGroupId, actionList.size());
            } else {
                log.error("任务组 [{}] 动作指令下发失败 !!!", actionGroupId);
                throw new CoolException("任务组 [{" + actionGroupId + "}] 动作指令下发失败 !!!");
            }
        } catch (Exception e) {
            log.error("mainService.publishAction", e);
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
    }
    @Transactional
    public void upDataSubscribe(AgvProtocol protocol) {
        Date now = new Date();
        Agv agv = agvService.selectByUuid(protocol.getAgvNo());
        if (null == agv) {
            log.warn("Agv [{}] 尚未鉴权 !!!", protocol.getAgvNo());
            return;
        }
        IMessageBody msgBody = protocol.getMessageBody();
        assert msgBody != null;
        // 动作完成包
        if (msgBody instanceof AGV_11_UP) {
            AGV_11_UP agv_11_up = (AGV_11_UP) msgBody;
            String serialNo = agv_11_up.getSerialNo();
            log.info("Agv [{}] 动作完成包 ===>> {}", protocol.getAgvNo(), JSON.toJSONString(agv_11_up));
            // 入库放货
            if (agv_11_up.getCompleteType().equals(AgvCompleteType.RELEASE_FROM_SHELVES_COMPLETE)) {
                WebsocketServiceImpl.taskShelfBarcode = agv_11_up.getLocCode();
                Loc loc = locService.getOne(new LambdaQueryWrapper<Loc>().eq(Loc::getBarcode, String.valueOf(Integer.parseInt(agv_11_up.getLocCode()))));
                if (null == loc) {
                    log.warn("Agv [{}] 上报往货架放货完成时,库位码[{}]无效。", protocol.getAgvNo(), agv_11_up.getLocCode());
                } else {
                    List<Long> taskIds = actionService.selectTaskIdsByGroupId(serialNo);
                    for (Long taskId : taskIds) {
                        Task task = taskService.getById(taskId);
                        if (task.getDestLoc().equals(loc.getId())) {
                            Action action = actionService.getOne(new LambdaQueryWrapper<Action>()
                                    .eq(Action::getTaskId, task.getId())
                                    .eq(Action::getActionType, ActionTypeType.ReadyReleaseToShelvesLoc.val())
                                    .eq(Action::getActionSts, ActionStsType.ISSUED.val())
                            );
                            if (null != action) {
                                action.setActionSts(ActionStsType.FINISH.val());
                                action.setEndTime(now);
                                action.setUpdateTime(now);
                                if (!actionService.updateById(action)) {
                                    log.error("Action [{}] 更新失败 !!!", action.getPriority() + " - " + action.getName());
                                }
                            }
                        }
                    }
                }
            }
            // 出库取货
            if (agv_11_up.getCompleteType().equals(AgvCompleteType.TAKE_FROM_SHELVES_COMPLETE)) {
                WebsocketServiceImpl.taskShelfBarcode = agv_11_up.getLocCode();
                Loc loc = locService.getOne(new LambdaQueryWrapper<Loc>().eq(Loc::getBarcode, String.valueOf(Integer.parseInt(agv_11_up.getLocCode()))));
                if (null == loc) {
                    log.warn("Agv [{}] 上报从货架取货完成,库位码[{}]无效。", protocol.getAgvNo(), agv_11_up.getLocCode());
                } else {
                    List<Long> taskIds = actionService.selectTaskIdsByGroupId(serialNo);
                    for (Long taskId : taskIds) {
                        Task task = taskService.getById(taskId);
                        if (task.getOriLoc().equals(loc.getId())) {
                            Action action = actionService.getOne(new LambdaQueryWrapper<Action>()
                                    .eq(Action::getTaskId, task.getId())
                                    .eq(Action::getActionType, ActionTypeType.ReadyTakeFromShelvesLoc.val())
                                    .eq(Action::getActionSts, ActionStsType.ISSUED.val())
                            );
                            if (null != action) {
                                action.setActionSts(ActionStsType.FINISH.val());
                                action.setEndTime(now);
                                action.setUpdateTime(now);
                                if (!actionService.updateById(action)) {
                                    log.error("Action [{}] 更新失败 !!!", action.getPriority() + " - " + action.getName());
                                }
                            }
                        }
                    }
                }
            }
            // 路径完成 || 充电完成
            if (agv_11_up.getCompleteType().equals(AgvCompleteType.ENTIRE_PATH_COMPLETE)
                    || agv_11_up.getCompleteType().equals(AgvCompleteType.DOCKING_CHARGE_COMPLETE)) {
                // segment list
                List<Segment> segmentList = segmentService.list(new LambdaQueryWrapper<Segment>()
                                .eq(Segment::getGroupId, serialNo)
//                        .eq(Segment::getState, SegmentStateType.RUNNING.toString())
                                .orderByAsc(Segment::getSerial)
                );
                // settlement
                this.settleSegmentList(segmentList, serialNo);
                log.info("Agv [{}] {}作业完毕 ==========>> ", protocol.getAgvNo(), serialNo);
            }
        }
        // 料仓信息包
        if (msgBody instanceof AGV_70_UP) {
            AGV_70_UP agv_70_up = (AGV_70_UP) msgBody;
            log.info("Agv [{}] 料仓信息包 ===>> {}", protocol.getAgvNo(), JSON.toJSONString(agv_70_up));
        }
    }
    @Transactional
    public void settleSegmentList(List<Segment> segmentList, String serialNo) {
        if (Cools.isEmpty(segmentList)) {
            return;
        }
        Date now = new Date();
        // task
        for (Segment segment : segmentList) {
            boolean taskComplete = false;
            Task task = taskService.getById(segment.getTaskId());
            assert null != task;
            TaskTypeType typeType = TaskTypeType.get(task.getTaskTypeEl());
            assert null != typeType;
            TaskPosDto.PosType posType = TaskPosDto.queryPosType(segment.getPosType());
            switch (Objects.requireNonNull(posType)) {
                case ORI_STA:
                case ORI_LOC:
                    break;
                case DEST_STA:
                case DEST_LOC:
                case TO_CHARGE:
                case TO_STANDBY:
                    if (segment.getEndNode().equals(task.getDestCode())) {
                        taskComplete = true;
                    }
                    break;
                case MOVE:
                    if (segment.getEndNode().equals(task.getDestCode())) {
                        if (typeType.equals(TaskTypeType.MOVE)) {
                            taskComplete = true;
                        }
                    }
                    break;
                default:
                    break;
            }
            if (taskComplete) {
                locService.taskCallBack(task);
                task.setTaskSts(TaskStsType.COMPLETE.val());
                task.setEndTime(now);
                task.setUpdateTime(now);
                if (!taskService.updateById(task)) {
                    log.error("Task [{}] 更新失败 !!!", task.getSeqNum());
                } else {
                    log.info("Task [{}] 作业完毕 ==========>> ", task.getSeqNum());
                }
            }
        }
        // action, follow by groupId
        if (!Cools.isEmpty(serialNo)) {
            List<Action> actionList = actionService.list(new LambdaQueryWrapper<Action>()
                    .eq(Action::getGroupId, serialNo)
                    .eq(Action::getActionSts, ActionStsType.ISSUED.val())
            );
            for (Action action : actionList) {
                action.setActionSts(ActionStsType.FINISH.val());
                action.setEndTime(now);
                action.setUpdateTime(now);
                if (!actionService.updateById(action)) {
                    log.error("Action [{}] 更新失败 !!!", action.getPriority() + " - " + action.getName());
                }
            }
        }
        // segment
        for (Segment segment : segmentList) {
            segment.setState(SegmentStateType.FINISH.toString());
            segment.setUpdateTime(now);
            if (!segmentService.updateById(segment)) {
                log.error("Segment [{}] 更新失败 !!!", segment.getGroupId() + " - " + segment.getSerial());
            }
        }
        // segment call back
        segmentService.processNext(segmentList);
    }
}
zy-acs-manager/src/main/java/com/zy/acs/manager/core/service/TrafficZkdService.java
New file
@@ -0,0 +1,112 @@
package com.zy.acs.manager.core.service;
import com.zy.acs.framework.common.Cools;
import com.zy.acs.framework.common.SnowflakeIdWorker;
import com.zy.acs.manager.core.service.astart.MapDataDispatcher;
import com.zy.acs.manager.manager.entity.Agv;
import com.zy.acs.manager.manager.entity.AgvDetail;
import com.zy.acs.manager.manager.entity.Segment;
import com.zy.acs.manager.manager.entity.Travel;
import com.zy.acs.manager.manager.enums.SegmentStateType;
import com.zy.acs.manager.manager.service.*;
import com.zy.acs.manager.system.service.ConfigService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
 * Wavefront
 * Created by vincent on 6/25/2024
 */
@Slf4j
@Component
public class TrafficZkdService {
    @Autowired
    private AgvService agvService;
    @Autowired
    private AgvDetailService agvDetailService;
    @Autowired
    private CodeService codeService;
    @Autowired
    private TravelService travelService;
    @Autowired
    private SegmentService segmentService;
    @Autowired
    private MainService mainService;
    @Autowired
    private MainLockWrapService mainLockWrapService;
    @Autowired
    private MapService mapService;
    @Autowired
    private MapDataDispatcher mapDataDispatcher;
    @Autowired
    private SnowflakeIdWorker snowflakeIdWorker;
    @Autowired
    private AgvModelService agvModelService;
    @Autowired
    private RetreatNavigateService retreatNavigateService;
    @Autowired
    private ConfigService configService;
    @Autowired
    private JamService jamService;
    @Autowired
    private AvoidWaveCalculator avoidWaveCalculator;
    @Autowired
    private TaskService taskService;
    @Autowired
    private FuncStaService funcStaService;
    @Transactional
    public synchronized void trigger(Segment segment) {
        try {
            Date now = new Date();
            // temporary -----------------
            Integer algoExtensionTime = configService.getVal("algoExtensionTime", Integer.class);
            if (null != algoExtensionTime && algoExtensionTime > 0) {
                Thread.sleep(algoExtensionTime);
            }
            // ---------------------------
            Travel travel = travelService.getById(segment.getTravelId());
            Agv agv = agvService.getById(travel.getAgvId());
            AgvDetail agvDetail = agvDetailService.selectByAgvId(travel.getAgvId());
            long endNode = segment.getEndNode();
            if (!Cools.isEmpty(segmentService.getByAgvAndState(agv.getId(), SegmentStateType.RUNNING.toString()))) {
                return;
            }
            List<Segment> waitingSegList = segmentService.getByAgvAndState(agv.getId(), SegmentStateType.WAITING.toString());
            if (!Cools.isEmpty(waitingSegList)) {
                for (Segment waitingSeg : waitingSegList) {
                    if (!waitingSeg.getId().equals(segment.getId())) {
//                        log.error("AGV[{}] 任务异常,服务器错误!!!", agv.getUuid());
                        return;
                    }
                }
            }
            List<Segment> segmentList = new ArrayList<>();
            segmentList.add(segment);
            mainService.generateAction(segment.getAgvId(), segmentList, pathList, now);
        } catch (Exception e) {
            log.error("TrafficService.trigger", e);
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
    }
}
zy-acs-manager/src/main/java/com/zy/acs/manager/core/third/zkd/HttpUtils.java
New file
@@ -0,0 +1,33 @@
package com.zy.acs.manager.core.third.zkd;
import okhttp3.*;
import java.io.IOException;
public class HttpUtils {
    public static String post(String url, String data) {
        OkHttpClient client = new OkHttpClient();
        // 创建RequestBody,指定媒体类型(Content-Type)为application/json; charset=utf-8
        RequestBody body = RequestBody.create(data, MediaType.get("application/json; charset=utf-8"));
        // 创建Request对象
        Request request = new Request.Builder()
                .url(url) // 替换为你的API URL
                .post(body) // 使用POST方法并设置请求体
                .build();
        // 发送请求并获取响应
        try (Response response = client.newCall(request).execute()) {
            if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
            // 获取响应体并处理,例如转换为字符串或JSON对象等
            String responseData = response.body().string();
            System.out.println(responseData); // 打印响应体数据
            return responseData;
        } catch (IOException e) {
            e.printStackTrace(); // 处理异常情况,例如网络问题或服务器错误等
        }
        return null;
    }
}
zy-acs-manager/src/main/java/com/zy/acs/manager/core/third/zkd/dto/AllocateTask.java
New file
@@ -0,0 +1,18 @@
package com.zy.acs.manager.core.third.zkd.dto;
import lombok.Data;
import java.io.Serializable;
@Data
public class AllocateTask implements Serializable {
    private String taskId;
    private String start;
    private String end;
    private String type;
    private Integer priority;
}
zy-acs-manager/src/main/java/com/zy/acs/manager/core/third/zkd/dto/AllocateTaskResponse.java
New file
@@ -0,0 +1,14 @@
package com.zy.acs.manager.core.third.zkd.dto;
import lombok.Data;
import java.io.Serializable;
@Data
public class AllocateTaskResponse implements Serializable {
    private String taskId;
    private String agvId;
}
zy-acs-manager/src/main/java/com/zy/acs/manager/core/third/zkd/dto/Navigation.java
New file
@@ -0,0 +1,44 @@
package com.zy.acs.manager.core.third.zkd.dto;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
@Data
public class Navigation implements Serializable {
    private String agvId;
    private List<CodeDTO> CodeList;
    @Data
    public class CodeDTO implements Serializable {
        /**
         * 码值
         */
        private String code;
        /**
         * 方向
         */
        private String direction;
        /**
         * 类型,避让、任务、充电、去待机位
         */
        private String type;
        /**
         * 任务编号
         */
        private String taskId;
        /**
         * 动作类型
         */
        private String posType;
        /**
         * 第几层背篓
         */
        private Integer lev;
    }
}
zy-acs-manager/src/main/java/com/zy/acs/manager/manager/controller/ZkdController.java
New file
@@ -0,0 +1,27 @@
package com.zy.acs.manager.manager.controller;
import com.zy.acs.manager.core.third.zkd.dto.Navigation;
import com.zy.acs.manager.manager.service.CodeService;
import com.zy.acs.manager.system.controller.BaseController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api")
public class ZkdController extends BaseController {
    @Autowired
    private CodeService codeService;
    @RequestMapping("/zkd/navigation/v1")
    public Object navigation(@RequestBody Navigation navigation) {
        String agvId = navigation.getAgvId();
        navigation.getCodeList().forEach(codeDTO -> {
        });
        return null;
    }
}