From 3ae3ce1721b1a98b17c2c602c99b7226cbd32894 Mon Sep 17 00:00:00 2001
From: zhang <zc857179121@qq.com>
Date: 星期四, 12 六月 2025 14:03:06 +0800
Subject: [PATCH] 对接开发

---
 zy-acs-manager/src/main/java/com/zy/acs/manager/core/service/MainZkdService.java             | 1120 ++++++++++++++++++++++++++++++++++++++++++++++
 zy-acs-manager/src/main/java/com/zy/acs/manager/core/third/zkd/HttpUtils.java                |   33 +
 zy-acs-framework/src/main/java/com/zy/acs/framework/common/R.java                            |    7 
 zy-acs-framework/src/main/java/com/zy/acs/framework/exception/CoolException.java             |   13 
 zy-acs-manager/src/main/java/com/zy/acs/manager/core/third/zkd/dto/AllocateTask.java         |   18 
 zy-acs-flow/src/map/tool.js                                                                  |    5 
 zy-acs-manager/pom.xml                                                                       |    7 
 zy-acs-manager/src/main/java/com/zy/acs/manager/core/third/zkd/dto/AllocateTaskResponse.java |   14 
 zy-acs-manager/src/main/java/com/zy/acs/manager/core/third/zkd/dto/Navigation.java           |   44 +
 zy-acs-manager/src/main/java/com/zy/acs/manager/common/exception/GlobalExceptionHandler.java |    7 
 zy-acs-manager/src/main/java/com/zy/acs/manager/core/service/TrafficZkdService.java          |  112 ++++
 zy-acs-manager/src/main/java/com/zy/acs/manager/manager/controller/ZkdController.java        |   27 +
 12 files changed, 1,404 insertions(+), 3 deletions(-)

diff --git a/zy-acs-flow/src/map/tool.js b/zy-acs-flow/src/map/tool.js
index a22da46..5dc1e83 100644
--- a/zy-acs-flow/src/map/tool.js
+++ b/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();
diff --git a/zy-acs-framework/src/main/java/com/zy/acs/framework/common/R.java b/zy-acs-framework/src/main/java/com/zy/acs/framework/common/R.java
index 491497b..9304ad0 100644
--- a/zy-acs-framework/src/main/java/com/zy/acs/framework/common/R.java
+++ b/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;
diff --git a/zy-acs-framework/src/main/java/com/zy/acs/framework/exception/CoolException.java b/zy-acs-framework/src/main/java/com/zy/acs/framework/exception/CoolException.java
index 6401d62..d065d2c 100644
--- a/zy-acs-framework/src/main/java/com/zy/acs/framework/exception/CoolException.java
+++ b/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;
+    }
+
+}
\ No newline at end of file
diff --git a/zy-acs-manager/pom.xml b/zy-acs-manager/pom.xml
index c98e97f..2d85426 100644
--- a/zy-acs-manager/pom.xml
+++ b/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>
diff --git a/zy-acs-manager/src/main/java/com/zy/acs/manager/common/exception/GlobalExceptionHandler.java b/zy-acs-manager/src/main/java/com/zy/acs/manager/common/exception/GlobalExceptionHandler.java
index b81c59f..34fee9d 100644
--- a/zy-acs-manager/src/main/java/com/zy/acs/manager/common/exception/GlobalExceptionHandler.java
+++ b/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
diff --git a/zy-acs-manager/src/main/java/com/zy/acs/manager/core/service/MainZkdService.java b/zy-acs-manager/src/main/java/com/zy/acs/manager/core/service/MainZkdService.java
new file mode 100644
index 0000000..03ad08c
--- /dev/null
+++ b/zy-acs-manager/src/main/java/com/zy/acs/manager/core/service/MainZkdService.java
@@ -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();
+        }
+    }
+
+    /**
+     * 瑙f瀽鍙栨斁璐ч泦鍚堜换鍔�,杩涜鏈�浼樼殑鎺掑垪缁勫悎椤哄簭 ( 杞﹁締姝ゆ椂鏄┖闂蹭笖闈欐鐨� )
+     * 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();
+        }
+    }
+
+
+    /**
+     * 鏍规嵁鍒嗙墖鐢熸垚鍔ㄤ綔 ( 杞﹁締鍙兘宸茬粡鍋氳繃涓�浜涗换鍔′簡,姝e湪绛夊緟涓嬩竴娈典换鍔� )
+     */
+    @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)) {
+                                // 濡傛灉涓嬩釜鑺傜偣鏂瑰悜涓庡綋鍓峚gv鏂瑰悜鐩稿弽锛屽垯鍊掗��琛岃蛋锛屼絾鏄鏋滃綋鍓峚gv鏂瑰悜姝eソ涓庡伐浣滄柟鍚戠浉鍙嶏紝鍒欐棆杞嚦宸ヤ綔鏂瑰悜
+                                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("{}鍙稟gv鍔ㄤ綔缁勮瀹屾垚锛屾寚浠ゆ暟閲忥細{}", 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);
+    }
+
+}
diff --git a/zy-acs-manager/src/main/java/com/zy/acs/manager/core/service/TrafficZkdService.java b/zy-acs-manager/src/main/java/com/zy/acs/manager/core/service/TrafficZkdService.java
new file mode 100644
index 0000000..257e066
--- /dev/null
+++ b/zy-acs-manager/src/main/java/com/zy/acs/manager/core/service/TrafficZkdService.java
@@ -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();
+
+        }
+    }
+
+
+}
diff --git a/zy-acs-manager/src/main/java/com/zy/acs/manager/core/third/zkd/HttpUtils.java b/zy-acs-manager/src/main/java/com/zy/acs/manager/core/third/zkd/HttpUtils.java
new file mode 100644
index 0000000..6187ff0
--- /dev/null
+++ b/zy-acs-manager/src/main/java/com/zy/acs/manager/core/third/zkd/HttpUtils.java
@@ -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) // 鏇挎崲涓轰綘鐨凙PI 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;
+    }
+}
diff --git a/zy-acs-manager/src/main/java/com/zy/acs/manager/core/third/zkd/dto/AllocateTask.java b/zy-acs-manager/src/main/java/com/zy/acs/manager/core/third/zkd/dto/AllocateTask.java
new file mode 100644
index 0000000..0c498f9
--- /dev/null
+++ b/zy-acs-manager/src/main/java/com/zy/acs/manager/core/third/zkd/dto/AllocateTask.java
@@ -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;
+}
diff --git a/zy-acs-manager/src/main/java/com/zy/acs/manager/core/third/zkd/dto/AllocateTaskResponse.java b/zy-acs-manager/src/main/java/com/zy/acs/manager/core/third/zkd/dto/AllocateTaskResponse.java
new file mode 100644
index 0000000..5374127
--- /dev/null
+++ b/zy-acs-manager/src/main/java/com/zy/acs/manager/core/third/zkd/dto/AllocateTaskResponse.java
@@ -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;
+}
diff --git a/zy-acs-manager/src/main/java/com/zy/acs/manager/core/third/zkd/dto/Navigation.java b/zy-acs-manager/src/main/java/com/zy/acs/manager/core/third/zkd/dto/Navigation.java
new file mode 100644
index 0000000..d9b1128
--- /dev/null
+++ b/zy-acs-manager/src/main/java/com/zy/acs/manager/core/third/zkd/dto/Navigation.java
@@ -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;
+
+
+    }
+}
diff --git a/zy-acs-manager/src/main/java/com/zy/acs/manager/manager/controller/ZkdController.java b/zy-acs-manager/src/main/java/com/zy/acs/manager/manager/controller/ZkdController.java
new file mode 100644
index 0000000..f75282e
--- /dev/null
+++ b/zy-acs-manager/src/main/java/com/zy/acs/manager/manager/controller/ZkdController.java
@@ -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;
+    }
+}

--
Gitblit v1.9.1