#
Junjie
4 天以前 377e207801eec0014b806394166a68caa52561ab
#
4个文件已添加
16个文件已修改
1487 ■■■■■ 已修改文件
src/main/java/com/zy/asrs/controller/BasStationOptController.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/controller/ConsoleController.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/controller/StationController.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/domain/vo/StationLatestDataVo.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/core/ServerBootstrap.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/core/enums/StationCommandType.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/core/model/command/StationCommand.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/core/model/protocol/StationProtocol.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/core/network/ZyStationConnectDriver.java 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/core/network/entity/ZyStationStatusEntity.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/core/network/fake/ZyStationFakeConnect.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/core/network/fake/ZyStationFakeSegConnect.java 733 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/core/network/real/ZyStationV3RealConnect.java 237 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/core/plugin/FakeProcess.java 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/core/thread/StationThread.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/core/thread/impl/ZyStationThread.java 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/core/thread/impl/ZyStationV3Thread.java 390 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/core/utils/DualCrnOperateProcessUtils.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/core/utils/StationOperateProcessUtils.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/components/DevpCard.js 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/controller/BasStationOptController.java
@@ -43,6 +43,7 @@
        convert(param, wrapper);
        allLike(BasStationOpt.class, param.keySet(), wrapper, condition);
        if (!Cools.isEmpty(orderByField)){wrapper.orderBy(humpToLine(orderByField), "asc".equals(orderByType));}
        wrapper.orderBy("send_time", false);
        return R.ok(basStationOptService.selectPage(new Page<>(curr, limit), wrapper));
    }
src/main/java/com/zy/asrs/controller/ConsoleController.java
@@ -116,6 +116,7 @@
                vo.setEmptyMk(stationProtocol.isEmptyMk()); // 是否空板
                vo.setFullPlt(stationProtocol.isFullPlt()); // 是否满板
                vo.setRunBlock(stationProtocol.isRunBlock());// 运行堵塞
                vo.setEnableIn(stationProtocol.isEnableIn());// 启动入库
                vo.setPalletHeight(stationProtocol.getPalletHeight()); // 托盘高度
                vo.setError(stationProtocol.getError()); // 错误码
                vo.setBarcode(stationProtocol.getBarcode()); // 条码
src/main/java/com/zy/asrs/controller/StationController.java
@@ -1,10 +1,9 @@
package com.zy.asrs.controller;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.zy.asrs.entity.BasDevp;
import com.zy.asrs.service.BasDevpService;
import com.zy.core.enums.StationCommandType;
import com.zy.core.model.StationObjModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
@@ -68,7 +67,7 @@
            return R.error("线程不存在");
        }
        StationCommand command = stationThread.getMoveCommand(taskNo, stationId, targetStationId, 0);
        StationCommand command = stationThread.getCommand(StationCommandType.MOVE, taskNo, stationId, targetStationId, 0);
        MessageQueue.offer(SlaveType.Devp, devpNo, new Task(2, command));
        return R.ok();
    }
src/main/java/com/zy/asrs/domain/vo/StationLatestDataVo.java
@@ -35,6 +35,9 @@
    // 运行阻塞
    private boolean runBlock;
    // 启动入库
    private boolean enableIn;
    // 托盘高度
    private Integer palletHeight;
src/main/java/com/zy/core/ServerBootstrap.java
@@ -11,6 +11,7 @@
import com.zy.core.thread.impl.ZySiemensCrnThread;
import com.zy.core.thread.impl.ZySiemensDualCrnThread;
import com.zy.core.thread.impl.ZyStationThread;
import com.zy.core.thread.impl.ZyStationV3Thread;
import com.zy.core.thread.impl.ZyRgvThread;
import lombok.extern.slf4j.Slf4j;
@@ -134,6 +135,8 @@
                ThreadHandler thread = null;
                if (deviceConfig.getThreadImpl().equals("ZyStationThread")) {
                    thread = new ZyStationThread(deviceConfig, redisUtil);
                } else if (deviceConfig.getThreadImpl().equals("ZyStationV3Thread")) {
                    thread = new ZyStationV3Thread(deviceConfig, redisUtil);
                } else {
                    throw new CoolException("未知的线程实现");
                }
src/main/java/com/zy/core/enums/StationCommandType.java
New file
@@ -0,0 +1,18 @@
package com.zy.core.enums;
import lombok.Getter;
@Getter
public enum StationCommandType {
    MOVE("MOVE", "移动"),
    WRITE_INFO("WRITE_INFO", "信息写入"),
    RESET("RESET", "复位");
    private final String code;
    private final String desc;
    StationCommandType(String code, String desc) {
        this.code = code;
        this.desc = desc;
    }
}
src/main/java/com/zy/core/model/command/StationCommand.java
@@ -1,6 +1,9 @@
package com.zy.core.model.command;
import lombok.Data;
import java.util.List;
import com.zy.core.enums.StationCommandType;
@Data
public class StationCommand {
@@ -14,4 +17,8 @@
    // 托盘大小,如无特殊情况,默认0
    private Integer palletSize;
    private List<Integer> navigatePath;
    private StationCommandType commandType;
}
src/main/java/com/zy/core/model/protocol/StationProtocol.java
@@ -40,6 +40,9 @@
    // 运行阻塞
    private boolean runBlock;
    // 启动入库
    private boolean enableIn;
    // 托盘高度
    private Integer palletHeight;
src/main/java/com/zy/core/network/ZyStationConnectDriver.java
@@ -9,7 +9,9 @@
import com.zy.core.network.entity.ZyStationStatusEntity;
import java.util.List;
import com.zy.core.network.fake.ZyStationFakeConnect;
import com.zy.core.network.fake.ZyStationFakeSegConnect;
import com.zy.core.network.real.ZyStationRealConnect;
import com.zy.core.network.real.ZyStationV3RealConnect;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
@@ -23,6 +25,7 @@
public class ZyStationConnectDriver implements ThreadHandler {
    private static final ZyStationFakeConnect zyStationFakeConnect = new ZyStationFakeConnect();
    private static final ZyStationFakeSegConnect zyStationFakeSegConnect = new ZyStationFakeSegConnect();
    private boolean connected = false;
    private DeviceConfig deviceConfig;
@@ -44,10 +47,19 @@
    @Override
    public boolean connect() {
        if (deviceConfig.getFake() == 0) {
            zyStationConnectApi = new ZyStationRealConnect(deviceConfig, redisUtil);
            if ("ZyStationV3Thread".equals(deviceConfig.getThreadImpl())) {
                zyStationConnectApi = new ZyStationV3RealConnect(deviceConfig, redisUtil);
            } else {
                zyStationConnectApi = new ZyStationRealConnect(deviceConfig, redisUtil);
            }
        } else {
            zyStationFakeConnect.addFakeConnect(deviceConfig, redisUtil);
            zyStationConnectApi = zyStationFakeConnect;
            if ("ZyStationV3Thread".equals(deviceConfig.getThreadImpl())) {
                zyStationFakeSegConnect.addFakeConnect(deviceConfig, redisUtil);
                zyStationConnectApi = zyStationFakeSegConnect;
            } else {
                zyStationFakeConnect.addFakeConnect(deviceConfig, redisUtil);
                zyStationConnectApi = zyStationFakeConnect;
            }
        }
        boolean connect = zyStationConnectApi.connect();
src/main/java/com/zy/core/network/entity/ZyStationStatusEntity.java
@@ -50,6 +50,9 @@
    //运行堵塞
    private boolean runBlock = false;
    //启动入库
    private boolean enableIn = false;
    @Override
    public ZyStationStatusEntity clone() {
        try {
src/main/java/com/zy/core/network/fake/ZyStationFakeConnect.java
@@ -11,6 +11,7 @@
import com.zy.common.utils.RedisUtil;
import com.zy.core.News;
import com.zy.core.enums.RedisKeyType;
import com.zy.core.enums.StationCommandType;
import com.zy.core.model.CommandResponse;
import com.zy.core.model.command.StationCommand;
import com.zy.core.network.api.ZyStationConnectApi;
@@ -116,24 +117,29 @@
        Integer taskNo = command.getTaskNo();
        Integer stationId = command.getStationId();
        Integer targetStationId = command.getTargetStaNo();
        StationCommandType commandType = command.getCommandType();
        boolean generateBarcode = false;
        if(taskNo == 0 && targetStationId == 0){
            //清空站点
            resetStation(deviceNo, stationId);
            return;
        if(commandType == StationCommandType.RESET){
            if(taskNo == 0 && targetStationId == 0){
                //清空站点
                resetStation(deviceNo, stationId);
                return;
            }
        }
        if(commandType == StationCommandType.WRITE_INFO){
            if (taskNo == 9998 && targetStationId == 0) {
                //生成出库站点仿真数据
                generateFakeOutStationData(deviceNo, stationId);
                return;
            }
        }
        //任务号属于仿真入库任务号
        if (checkTaskNoInArea(taskNo)) {
            //生成仿真数据
            generateBarcode = true;
        }
        if (taskNo == 9998 && targetStationId == 0) {
            //生成出库站点仿真数据
            generateFakeOutStationData(deviceNo, stationId);
            return;
        }
        if (taskNo > 0 && taskNo != 9999 && taskNo != 9998 && stationId == targetStationId) {
src/main/java/com/zy/core/network/fake/ZyStationFakeSegConnect.java
New file
@@ -0,0 +1,733 @@
package com.zy.core.network.fake;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.zy.asrs.entity.DeviceConfig;
import com.zy.common.model.NavigateNode;
import com.zy.common.utils.RedisUtil;
import com.zy.core.News;
import com.zy.core.enums.RedisKeyType;
import com.zy.core.enums.StationCommandType;
import com.zy.core.model.CommandResponse;
import com.zy.core.model.command.StationCommand;
import com.zy.core.network.api.ZyStationConnectApi;
import com.zy.core.network.entity.ZyStationStatusEntity;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.Map;
import java.util.function.Supplier;
public class ZyStationFakeSegConnect implements ZyStationConnectApi {
    private static int LOCK_STATION = 0;
    private HashMap<Integer, List<ZyStationStatusEntity>> deviceStatusMap = new HashMap<>();
    private HashMap<Integer, DeviceConfig> deviceConfigMap = new HashMap<>();
    private RedisUtil redisUtil;
    private final Map<Integer, BlockingQueue<StationCommand>> taskQueues = new ConcurrentHashMap<>();
    private final Map<Integer, Long> taskLastUpdateTime = new ConcurrentHashMap<>();
    private final Map<Integer, Boolean> taskRunning = new ConcurrentHashMap<>();
    private final ExecutorService executor = Executors.newCachedThreadPool();
    public void addFakeConnect(DeviceConfig deviceConfig, RedisUtil redisUtil) {
        this.redisUtil = redisUtil;
        if (deviceConfigMap.containsKey(deviceConfig.getDeviceNo())) {
            return;
        }
        deviceConfigMap.put(deviceConfig.getDeviceNo(), deviceConfig);
        deviceStatusMap.put(deviceConfig.getDeviceNo(), new CopyOnWriteArrayList<>());
    }
    @Override
    public boolean connect() {
        return true;
    }
    @Override
    public boolean disconnect() {
        executor.shutdownNow();
        return true;
    }
    @Override
    public List<ZyStationStatusEntity> getStatus(Integer deviceNo) {
        List<ZyStationStatusEntity> statusList = deviceStatusMap.get(deviceNo);
        if (statusList == null) {
            return new ArrayList<>();
        }
        DeviceConfig deviceConfig = deviceConfigMap.get(deviceNo);
        if (statusList.isEmpty()) {
            List<ZyStationStatusEntity> init = JSON.parseArray(deviceConfig.getFakeInitStatus(), ZyStationStatusEntity.class);
            if (init != null) {
                statusList.addAll(init);
                for (ZyStationStatusEntity status : statusList) {
                    status.setAutoing(true);
                    status.setLoading(false);
                    status.setInEnable(true);
                    status.setOutEnable(true);
                    status.setEmptyMk(false);
                    status.setFullPlt(false);
                    status.setRunBlock(false);
                    status.setPalletHeight(0);
                    status.setError(0);
                    status.setBarcode("");
                }
            }
        }
        return statusList;
    }
    @Override
    public CommandResponse sendCommand(Integer deviceNo, StationCommand command) {
        Integer taskNo = command.getTaskNo();
        if (taskNo == null) {
            return new CommandResponse(false, "任务号为空");
        }
        taskQueues.computeIfAbsent(taskNo, k -> new LinkedBlockingQueue<>()).offer(command);
        taskLastUpdateTime.put(taskNo, System.currentTimeMillis());
        if (taskRunning.putIfAbsent(taskNo, true) == null) {
            executor.submit(() -> runTaskLoop(deviceNo, taskNo));
        }
        return new CommandResponse(true, "命令已受理(异步执行)");
    }
    private void runTaskLoop(Integer deviceNo, Integer taskNo) {
        try {
            // 用于存储当前任务待执行的完整路径队列
            BlockingQueue<Integer> pathQueue = new LinkedBlockingQueue<>();
            // 当前是否正在执行移动
            boolean isMoving = false;
            // 当前移动到的路径索引
            int currentPathIndex = 0;
            // 完整路径记录
            List<Integer> fullPath = new ArrayList<>();
            StationCommand initialCommand = null;
            Integer finalTargetStationId = null;
            boolean generateBarcode = false;
            while (true) {
                BlockingQueue<StationCommand> commandQueue = taskQueues.get(taskNo);
                if (commandQueue == null) {
                    break;
                }
                // 尝试获取新命令,如果没有新命令则继续执行现有路径
                StationCommand command = commandQueue.poll(100, TimeUnit.MILLISECONDS);
                if (command != null) {
                    taskLastUpdateTime.put(taskNo, System.currentTimeMillis());
                    if (initialCommand == null) {
                        initialCommand = command;
                        finalTargetStationId = command.getTargetStaNo();
                        if (checkTaskNoInArea(taskNo)) {
                            generateBarcode = true;
                        }
                    }
                    List<Integer> newPath = command.getNavigatePath();
                    if (newPath != null && !newPath.isEmpty()) {
                        // 如果是第一段路径
                        if (fullPath.isEmpty()) {
                            fullPath.addAll(newPath);
                            for (Integer stationId : newPath) {
                                pathQueue.offer(stationId);
                            }
                        } else {
                            // 追加路径,需要去重衔接点
                            Integer lastStationId = fullPath.get(fullPath.size() - 1);
                            int startIndex = 0;
                            if (!newPath.isEmpty() && newPath.get(0).equals(lastStationId)) {
                                startIndex = 1;
                            }
                            for (int i = startIndex; i < newPath.size(); i++) {
                                Integer stationId = newPath.get(i);
                                fullPath.add(stationId);
                                pathQueue.offer(stationId);
                            }
                        }
                    }
                    // 处理非移动命令
                    if (command.getCommandType() != StationCommandType.MOVE) {
                        handleCommand(deviceNo, command);
                    }
                }
                // 执行移动逻辑
                if (!pathQueue.isEmpty()) {
                    // 如果刚开始,先初始化当前位置
                    if (currentPathIndex == 0 && !fullPath.isEmpty()) {
                         Integer startStationId = fullPath.get(0);
                         Integer deviceId = getDeviceNoByStationId(startStationId);
                         if (deviceId != null) {
                             initStationMove(taskNo, startStationId, deviceId, taskNo, finalTargetStationId, true, null);
                         }
                    }
                    // 取出下一个目标点
                    Integer nextStationId = pathQueue.peek(); // 这里的逻辑需要改为逐个行走
                    // 实际行走逻辑应该是在这里消费 pathQueue
                    // 为了简化,我们将 pathQueue 转为 stationMoveByPathIds 的逻辑,但这里需要改造成步进式
                    // 重新设计:runTaskLoop 负责不断消费 pathQueue 并执行单步移动
                    if (nextStationId != null) {
                        Integer currentStationId = fullPath.get(currentPathIndex);
                        if (currentStationId.equals(nextStationId)) {
                            pathQueue.poll(); // 移除已到达的点
                            continue;
                        }
                        // 执行从 currentStationId 到 nextStationId 的移动
                        Integer currentDeviceNo = getDeviceNoByStationId(currentStationId);
                        Integer nextDeviceNo = getDeviceNoByStationId(nextStationId);
                        if (currentDeviceNo != null && nextDeviceNo != null) {
                            boolean moveSuccess = stationMoveToNext(taskNo, currentStationId, currentDeviceNo, nextStationId, nextDeviceNo, taskNo, finalTargetStationId);
                            if (moveSuccess) {
                                currentPathIndex++;
                                pathQueue.poll();
                                sleep(1000); // 模拟耗时
                            } else {
                                sleep(1000); // 失败重试等待
                            }
                        } else {
                            pathQueue.poll(); // 无法执行,跳过
                        }
                    }
                } else {
                    // 队列为空,检查是否超时
                    Long lastTime = taskLastUpdateTime.get(taskNo);
                    if (lastTime != null && System.currentTimeMillis() - lastTime > 30000) {
                        // 完成任务后清理
                        if (fullPath.size() > 0 && generateBarcode) {
                             Integer lastStation = fullPath.get(fullPath.size()-1);
                             Integer lastDevice = getDeviceNoByStationId(lastStation);
                             if (lastDevice != null) {
                                 generateStationBarcode(taskNo, finalTargetStationId, lastDevice);
                             }
                        }
                        break;
                    }
                }
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            taskQueues.remove(taskNo);
            taskLastUpdateTime.remove(taskNo);
            taskRunning.remove(taskNo);
        }
    }
    @Override
    public CommandResponse sendOriginCommand(String address, short[] data) {
        return new CommandResponse(true, "原始命令已受理(异步执行)");
    }
    @Override
    public byte[] readOriginCommand(String address, int length) {
        return new byte[0];
    }
    private void handleCommand(Integer deviceNo, StationCommand command) {
        News.info("[WCS Debug] 站点仿真模拟(V3)已启动,命令数据={}", JSON.toJSONString(command));
        Integer taskNo = command.getTaskNo();
        Integer stationId = command.getStationId();
        Integer targetStationId = command.getTargetStaNo();
        boolean generateBarcode = false;
        if (command.getCommandType() == StationCommandType.RESET) {
            resetStation(deviceNo, stationId);
            return;
        }
        if (checkTaskNoInArea(taskNo)) {
            generateBarcode = true;
        }
        if (command.getCommandType() == StationCommandType.WRITE_INFO) {
            if (taskNo == 9998 && targetStationId == 0) {
                //生成出库站点仿真数据
                generateFakeOutStationData(deviceNo, stationId);
                return;
            }
        }
        if (taskNo > 0 && taskNo != 9999 && taskNo != 9998 && stationId == targetStationId) {
            generateStationData(deviceNo, taskNo, stationId, targetStationId);
        }
        List<Integer> navigatePath = command.getNavigatePath();
        if (navigatePath != null && !navigatePath.isEmpty()) {
            segmentedPathCommand(command, generateBarcode);
            return;
        }
    }
    private void generateFakeOutStationData(Integer deviceNo, Integer stationId) {
        List<ZyStationStatusEntity> statusList = deviceStatusMap.get(deviceNo);
        ZyStationStatusEntity status = statusList.stream()
                .filter(item -> item.getStationId().equals(stationId)).findFirst().orElse(null);
        if (status == null) {
            return;
        }
        synchronized (status) {
            status.setLoading(true);
        }
    }
    private void generateStationData(Integer deviceNo, Integer taskNo, Integer stationId, Integer targetStationId) {
        List<ZyStationStatusEntity> statusList = deviceStatusMap.get(deviceNo);
        ZyStationStatusEntity status = statusList.stream()
                .filter(item -> item.getStationId().equals(stationId)).findFirst().orElse(null);
        if (status == null) {
            return;
        }
        synchronized (status) {
            status.setTaskNo(taskNo);
            status.setTargetStaNo(targetStationId);
        }
    }
    private void resetStation(Integer deviceNo, Integer stationId) {
        List<ZyStationStatusEntity> statusList = deviceStatusMap.get(deviceNo);
        ZyStationStatusEntity status = statusList.stream()
                .filter(item -> item.getStationId().equals(stationId)).findFirst().orElse(null);
        if (status == null) {
            return;
        }
        synchronized (status) {
            status.setTaskNo(0);
            status.setLoading(false);
            status.setBarcode("");
        }
    }
    private void segmentedPathCommand(StationCommand command, boolean generateBarcode) {
        Integer taskNo = command.getTaskNo();
        Integer finalTargetStationId = command.getTargetStaNo();
        List<Integer> path = command.getNavigatePath();
        if (path == null || path.isEmpty()) {
            return;
        }
        Integer currentStationId = findCurrentStationIdByTask(taskNo);
        int startIdx = 0;
        if (currentStationId != null) {
            int idx = path.indexOf(currentStationId);
            if (idx >= 0) {
                startIdx = idx;
            }
        }
        Integer segmentTargetStationId = path.get(path.size() - 1);
        int endIdx = path.size() - 1;
        boolean isFinalSegment = segmentTargetStationId.equals(finalTargetStationId);
        boolean appendMode = currentStationId != null && path.indexOf(currentStationId) >= 0;
        boolean generateBarcodeFinal = generateBarcode && isFinalSegment;
        stationMoveByPathIds(path, taskNo, finalTargetStationId, false, generateBarcodeFinal, startIdx, endIdx, appendMode);
    }
    private Integer getDeviceNoByStationId(Integer stationId) {
        for (Integer devNo : deviceStatusMap.keySet()) {
            List<ZyStationStatusEntity> list = deviceStatusMap.get(devNo);
            if (list == null) {
                continue;
            }
            for (ZyStationStatusEntity e : list) {
                if (e.getStationId() != null && e.getStationId().equals(stationId)) {
                    return devNo;
                }
            }
        }
        return null;
    }
    private Integer findCurrentStationIdByTask(Integer taskNo) {
        for (Integer devNo : deviceStatusMap.keySet()) {
            List<ZyStationStatusEntity> list = deviceStatusMap.get(devNo);
            if (list == null) {
                continue;
            }
            for (ZyStationStatusEntity e : list) {
                if (e.getTaskNo() != null && e.getTaskNo().equals(taskNo) && e.isLoading()) {
                    return e.getStationId();
                }
            }
        }
        return null;
    }
    private boolean stationMoveByPathIds(List<Integer> stationPath, Integer taskNo, Integer targetStationId, boolean clearData, boolean generateBarcode, int startIdx, int endIdx, boolean appendMode) {
        Integer lastStationId = null;
        Integer targetStationDeviceNo = getDeviceNoByStationId(targetStationId);
        long executeTime = System.currentTimeMillis();
        int i = Math.max(0, startIdx);
        while (i < stationPath.size() && i <= endIdx) {
            if (Thread.currentThread().isInterrupted()) {
                return false;
            }
            Integer currentStationId = stationPath.get(i);
            Integer currentStationDeviceNo = getDeviceNoByStationId(currentStationId);
            if (currentStationDeviceNo == null) {
                return false;
            }
            Integer nextStationId = null;
            Integer nextStationDeviceNo = null;
            try {
                nextStationId = stationPath.get(i + 1);
                nextStationDeviceNo = getDeviceNoByStationId(nextStationId);
            } catch (Exception ignore) {}
            if (!checkTaskNoInArea(taskNo)) {
                boolean fakeAllowCheckBlock = true;
                Object systemConfigMapObj = redisUtil.get(RedisKeyType.SYSTEM_CONFIG_MAP.key);
                if (systemConfigMapObj != null) {
                    HashMap<String, String> systemConfigMap = (HashMap<String, String>) systemConfigMapObj;
                    if (!systemConfigMap.get("fakeAllowCheckBlock").equals("Y")) {
                        fakeAllowCheckBlock = false;
                    }
                }
                if (fakeAllowCheckBlock && System.currentTimeMillis() - executeTime > 1000 * 10) {
                    boolean result = runBlockStation(taskNo, currentStationId, currentStationDeviceNo, taskNo, currentStationId);
                    if(!result) {
                        continue;
                    }
                    return false;
                }
            }
            if (i == startIdx && !appendMode) {
                boolean result = initStationMove(taskNo, currentStationId, currentStationDeviceNo, taskNo, targetStationId, true, null);
                if (!result) {
                    continue;
                }
                sleep(1000);
                if (Thread.currentThread().isInterrupted()) {
                    return false;
                }
            }
            if (nextStationId != null && nextStationDeviceNo != null) {
                boolean result = stationMoveToNext(taskNo, currentStationId, currentStationDeviceNo, nextStationId, nextStationDeviceNo, taskNo, targetStationId);
                if (!result) {
                    continue;
                }
                lastStationId = currentStationId;
            }
            if (currentStationId.equals(targetStationId)) {
                break;
            }
            i++;
            executeTime = System.currentTimeMillis();
            sleep(1000);
            if (Thread.currentThread().isInterrupted()) {
                return false;
            }
        }
        if (generateBarcode) {
            if (lastStationId != null && targetStationDeviceNo != null) {
                while (true) {
                    if (Thread.currentThread().isInterrupted()) {
                        break;
                    }
                    boolean result = generateStationBarcode(taskNo, targetStationId, targetStationDeviceNo);
                    sleep(1000);
                    if (!result) {
                        continue;
                    }
                    break;
                }
            }
        }
        if (clearData) {
            sleep(10000);
            if (Thread.currentThread().isInterrupted()) {
                return true;
            }
            if (lastStationId != null && targetStationDeviceNo != null) {
                while (true) {
                    if (Thread.currentThread().isInterrupted()) {
                        break;
                    }
                    boolean result = clearStation(targetStationDeviceNo, taskNo, targetStationId);
                    sleep(1000);
                    if (!result) {
                        continue;
                    }
                    break;
                }
            }
        }
        return true;
    }
    private void sleep(long ms) {
        try {
            Thread.sleep(ms);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
    public synchronized boolean setLockStation(Integer uuid) {
        if (LOCK_STATION == 0) {
            LOCK_STATION = uuid;
            return true;
        }else {
            if(LOCK_STATION == uuid) {
                return true;
            }
        }
        return false;
    }
    public synchronized boolean releaseLockStation(Integer uuid) {
        if (LOCK_STATION != uuid) {
            return false;
        }
        LOCK_STATION = 0;
        return true;
    }
    public synchronized boolean updateStationData(Integer lockTaskNo, Integer stationId, Integer deviceNo, Integer taskNo, Integer targetStaNo, Boolean isLoading, String barcode, Boolean runBlock) {
        if (LOCK_STATION != lockTaskNo) {
            return false;
        }
        List<ZyStationStatusEntity> statusList = deviceStatusMap.get(deviceNo);
        if (statusList == null) {
            return false;
        }
        ZyStationStatusEntity currentStatus = statusList.stream()
                .filter(item -> item.getStationId().equals(stationId)).findFirst().orElse(null);
        if (currentStatus == null) {
            return false;
        }
        if (taskNo != null) {
            currentStatus.setTaskNo(taskNo);
        }
        if (targetStaNo != null) {
            currentStatus.setTargetStaNo(targetStaNo);
        }
        if (isLoading != null) {
            currentStatus.setLoading(isLoading);
        }
        if (barcode != null) {
            currentStatus.setBarcode(barcode);
        }
        if (runBlock != null) {
            currentStatus.setRunBlock(runBlock);
        }
        return true;
    }
    public synchronized boolean initStationMove(Integer lockTaskNo, Integer currentStationId, Integer currentStationDeviceNo, Integer taskNo, Integer targetStationId, Boolean isLoading, String barcode) {
        boolean executeResult = lockExecute(lockTaskNo, () -> {
            List<ZyStationStatusEntity> statusList = deviceStatusMap.get(currentStationDeviceNo);
            if (statusList == null) {
                return false;
            }
            ZyStationStatusEntity currentStatus = statusList.stream()
                    .filter(item -> item.getStationId().equals(currentStationId)).findFirst().orElse(null);
            if (currentStatus == null) {
                return false;
            }
            if (currentStatus.getTaskNo() > 0) {
                if (!currentStatus.getTaskNo().equals(taskNo) && currentStatus.isLoading()) {
                    return false;
                }
            }
            boolean result = updateStationData(lockTaskNo, currentStationId, currentStationDeviceNo, taskNo, targetStationId, isLoading, barcode, false);
            if (!result) {
                return false;
            }
            return true;
        });
        return executeResult;
    }
    public synchronized boolean stationMoveToNext(Integer lockTaskNo, Integer currentStationId, Integer currentStationDeviceNo, Integer nextStationId, Integer nextStationDeviceNo, Integer taskNo, Integer targetStaNo) {
        boolean executeResult = lockExecute(lockTaskNo, () -> {
            List<ZyStationStatusEntity> statusList = deviceStatusMap.get(currentStationDeviceNo);
            if (statusList == null) {
                return false;
            }
            List<ZyStationStatusEntity> nextStatusList = deviceStatusMap.get(nextStationDeviceNo);
            if (statusList == null) {
                return false;
            }
            ZyStationStatusEntity currentStatus = statusList.stream()
                    .filter(item -> item.getStationId().equals(currentStationId)).findFirst().orElse(null);
            ZyStationStatusEntity nextStatus = nextStatusList.stream()
                    .filter(item -> item.getStationId().equals(nextStationId)).findFirst().orElse(null);
            if (currentStatus == null || nextStatus == null) {
                return false;
            }
            if (nextStatus.getTaskNo() > 0 || nextStatus.isLoading()) {
                return false;
            }
            boolean result = updateStationData(lockTaskNo, nextStationId, nextStationDeviceNo, taskNo, targetStaNo, true, null, false);
            if (!result) {
                return false;
            }
            boolean result2 = updateStationData(lockTaskNo, currentStationId, currentStationDeviceNo, 0, 0, false, "", false);
            if (!result2) {
                return false;
            }
            return true;
        });
        return executeResult;
    }
    public synchronized boolean generateStationBarcode(Integer lockTaskNo, Integer currentStationId, Integer currentStationDeviceNo) {
        boolean executeResult = lockExecute(lockTaskNo, () -> {
            List<ZyStationStatusEntity> statusList = deviceStatusMap.get(currentStationDeviceNo);
            if (statusList == null) {
                return false;
            }
            ZyStationStatusEntity currentStatus = statusList.stream()
                    .filter(item -> item.getStationId().equals(currentStationId)).findFirst().orElse(null);
            if (currentStatus == null) {
                return false;
            }
            Random random = new Random();
            String barcodeTime = String.valueOf(System.currentTimeMillis());
            String barcode = String.valueOf(random.nextInt(10)) + String.valueOf(random.nextInt(10)) + barcodeTime.substring(7);
            boolean result = updateStationData(lockTaskNo, currentStationId, currentStationDeviceNo, null, null, null, barcode, null);
            if (!result) {
                return false;
            }
            return true;
        });
        return executeResult;
    }
    public synchronized boolean clearStation(Integer deviceNo, Integer lockTaskNo, Integer currentStationId) {
        boolean executeResult = lockExecute(lockTaskNo, () -> {
            List<ZyStationStatusEntity> statusList = deviceStatusMap.get(deviceNo);
            if (statusList == null) {
                return false;
            }
            ZyStationStatusEntity currentStatus = statusList.stream()
                    .filter(item -> item.getStationId().equals(currentStationId)).findFirst().orElse(null);
            if (currentStatus == null) {
                return false;
            }
            boolean result = updateStationData(deviceNo, lockTaskNo, currentStationId, 0, 0, false, "", false);
            if (!result) {
                return false;
            }
            return true;
        });
        return executeResult;
    }
    public synchronized boolean runBlockStation(Integer lockTaskNo, Integer currentStationId, Integer currentStationDeviceNo, Integer taskNo, Integer blockStationId) {
        boolean executeResult = lockExecute(lockTaskNo, () -> {
            List<ZyStationStatusEntity> statusList = deviceStatusMap.get(currentStationDeviceNo);
            if (statusList == null) {
                return false;
            }
            ZyStationStatusEntity currentStatus = statusList.stream()
                    .filter(item -> item.getStationId().equals(currentStationId)).findFirst().orElse(null);
            if (currentStatus == null) {
                return false;
            }
            boolean result = updateStationData(lockTaskNo, currentStationId, currentStationDeviceNo, taskNo, blockStationId, true, "", true);
            if (!result) {
                return false;
            }
            return true;
        });
        return executeResult;
    }
    public boolean lockExecute(Integer taskNo, Supplier<Boolean> function) {
        if (!setLockStation(taskNo)) {
            return false;
        }
        boolean result = function.get();
        releaseLockStation(taskNo);
        return result;
    }
    private boolean checkTaskNoInArea(Integer taskNo) {
        Object fakeTaskNoAreaObj = redisUtil.get(RedisKeyType.FAKE_TASK_NO_AREA.key);
        if (fakeTaskNoAreaObj == null) {
            return false;
        }
        JSONObject data = JSON.parseObject(String.valueOf(fakeTaskNoAreaObj));
        Integer start = data.getInteger("start");
        Integer end = data.getInteger("end");
        if(taskNo >= start && taskNo <= end) {
            return true;
        }
        return false;
    }
}
src/main/java/com/zy/core/network/real/ZyStationV3RealConnect.java
New file
@@ -0,0 +1,237 @@
package com.zy.core.network.real;
import HslCommunication.Core.Types.OperateResult;
import HslCommunication.Core.Types.OperateResultExOne;
import HslCommunication.Profinet.Siemens.SiemensPLCS;
import HslCommunication.Profinet.Siemens.SiemensS7Net;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.core.common.DateUtils;
import com.core.common.SpringUtils;
import com.zy.asrs.entity.BasDevp;
import com.zy.asrs.entity.DeviceConfig;
import com.zy.asrs.service.BasDevpService;
import com.zy.common.utils.RedisUtil;
import com.zy.core.News;
import com.zy.core.cache.OutputQueue;
import com.zy.core.enums.StationCommandType;
import com.zy.core.model.CommandResponse;
import com.zy.core.model.command.StationCommand;
import com.zy.core.network.api.ZyStationConnectApi;
import com.zy.core.network.entity.ZyStationStatusEntity;
import lombok.extern.slf4j.Slf4j;
import java.text.MessageFormat;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
/**
 * 输送站真实连接(PLC)
 */
@Slf4j
public class ZyStationV3RealConnect implements ZyStationConnectApi {
    private List<ZyStationStatusEntity> statusList;
    private List<ZyStationStatusEntity> barcodeStatusList;
    private SiemensS7Net siemensNet;
    private DeviceConfig deviceConfig;
    private RedisUtil redisUtil;
    private final static int taskAddressLength = 48;
    private final static int taskAddressLimit = 50;
    public ZyStationV3RealConnect(DeviceConfig deviceConfig, RedisUtil redisUtil) {
        this.deviceConfig = deviceConfig;
        this.redisUtil = redisUtil;
    }
    @Override
    public boolean connect() {
        boolean connected = false;
        siemensNet = new SiemensS7Net(SiemensPLCS.S1200, deviceConfig.getIp());
        OperateResult connect = siemensNet.ConnectServer();
        if (connect.IsSuccess) {
            connected = true;
            OutputQueue.DEVP.offer(MessageFormat.format("【{0}】输送站plc连接成功 ===>> [id:{1}] [ip:{2}] [port:{3}]",
                    DateUtils.convert(new Date()), deviceConfig.getDeviceNo(), deviceConfig.getIp(),
                    deviceConfig.getPort()));
            News.info("输送站plc连接成功 ===>> [id:{}] [ip:{}] [port:{}]",
                    deviceConfig.getDeviceNo(), deviceConfig.getIp(), deviceConfig.getPort());
        } else {
            OutputQueue.DEVP.offer(MessageFormat.format("【{0}】输送站plc连接失败!!! ===>> [id:{1}] [ip:{2}] [port:{3}]",
                    DateUtils.convert(new Date()), deviceConfig.getDeviceNo(), deviceConfig.getIp(),
                    deviceConfig.getPort()));
            News.error("输送站plc连接失败!!! ===>> [id:{}] [ip:{}] [port:{}]",
                    deviceConfig.getDeviceNo(), deviceConfig.getIp(), deviceConfig.getPort());
        }
        return connected;
    }
    @Override
    public boolean disconnect() {
        siemensNet.ConnectClose();
        return true;
    }
    @Override
    public List<ZyStationStatusEntity> getStatus(Integer deviceNo) {
        if (statusList == null) {
            BasDevpService basDevpService = SpringUtils.getBean(BasDevpService.class);
            if (basDevpService == null) {
                return Collections.emptyList();
            }
            BasDevp basDevp = basDevpService
                    .selectOne(new EntityWrapper<BasDevp>().eq("devp_no", deviceConfig.getDeviceNo()));
            if (basDevp == null) {
                return Collections.emptyList();
            }
            statusList = JSONObject.parseArray(basDevp.getStationList(), ZyStationStatusEntity.class);
            if (statusList != null) {
                statusList.sort(Comparator.comparing(ZyStationStatusEntity::getStationId));
            }
            barcodeStatusList = JSONObject.parseArray(basDevp.getBarcodeStationList(), ZyStationStatusEntity.class);
            if (barcodeStatusList != null) {
                barcodeStatusList.sort(Comparator.comparing(ZyStationStatusEntity::getStationId));
            }
        }
        OperateResultExOne<byte[]> result = siemensNet.Read("DB100.0", (short) (statusList.size() * 8));
        if (result.IsSuccess) {
            for (int i = 0; i < statusList.size(); i++) {
                ZyStationStatusEntity statusEntity = statusList.get(i); // 站点编号
                statusEntity.setTaskNo(siemensNet.getByteTransform().TransInt32(result.Content, i * 8)); // 工作号
                statusEntity.setTargetStaNo((int) siemensNet.getByteTransform().TransInt16(result.Content, i * 8 + 4)); // 目标站
                boolean[] status = siemensNet.getByteTransform().TransBool(result.Content, i * 8 + 6, 1);
                statusEntity.setAutoing(status[0]); // 自动
                statusEntity.setLoading(status[1]); // 有物
                statusEntity.setInEnable(status[2]); // 可入
                statusEntity.setOutEnable(status[3]);// 可出
                statusEntity.setEmptyMk(status[4]); // 空托盘
                statusEntity.setFullPlt(status[5]); // 满托盘
                boolean[] status2 = siemensNet.getByteTransform().TransBool(result.Content, i * 8 + 7, 1);
                statusEntity.setRunBlock(status2[1]);//重新规划路线
                statusEntity.setEnableIn(status2[3]);//启动入库
            }
        }
        // 条码扫描器
        OperateResultExOne<byte[]> result2 = siemensNet.Read("DB101.0", (short) (statusList.size() * 10));
        if (result2.IsSuccess) {
            for (int i = 0; i < barcodeStatusList.size(); i++) {
                ZyStationStatusEntity barcodeEntity = barcodeStatusList.get(i);
                ZyStationStatusEntity statusEntity = findStatusEntity(barcodeEntity.getStationId());
                if (statusEntity == null) {
                    continue;
                }
                String barcode = siemensNet.getByteTransform().TransString(result2.Content, i * 10 + 2, 8, "UTF-8");
                barcode = barcode.trim();
                barcodeEntity.setBarcode(barcode);
            }
        }
        return statusList;
    }
    @Override
    public CommandResponse sendCommand(Integer deviceNo, StationCommand command) {
        CommandResponse commandResponse = new CommandResponse(false);
        if (null == command) {
            commandResponse.setMessage("命令为空");
            return commandResponse;
        }
        if (command.getCommandType().equals(StationCommandType.MOVE)) {
            int enableCommandIdx = -1;
            while (true) {
                enableCommandIdx = getEnableCommandIdx();
                if(enableCommandIdx == -1) {
                    try {
                        Thread.sleep(300);
                    }catch (Exception e) {}
                }else {
                    break;
                }
            }
            List<Integer> pathList = command.getNavigatePath();
            short[] data = new short[22];
            data[0] = command.getTargetStaNo().shortValue();
            int dataIdx = 1;
            for (Integer path : pathList) {
                data[dataIdx++] = path.shortValue();
            }
            OperateResult writeTask = siemensNet.Write("DB23." + enableCommandIdx * taskAddressLength, command.getTaskNo());
            OperateResult writeData = siemensNet.Write("DB23." + enableCommandIdx * taskAddressLength + 4, data);
            if(writeTask.IsSuccess &&  writeData.IsSuccess) {
                log.error("写入输送线命令成功。任务号={},站点数据={}", command.getTaskNo(), JSON.toJSON(command));
                commandResponse.setResult(true);
            }else {
                log.error("写入输送线命令失败。站点编号={},站点数据={}", command.getTaskNo(), JSON.toJSON(command));
                commandResponse.setResult(false);
            }
        }
        return commandResponse;
    }
    @Override
    public CommandResponse sendOriginCommand(String address, short[] data) {
        CommandResponse commandResponse = new CommandResponse(false);
        if (null == data || data.length == 0) {
            commandResponse.setMessage("数据为空");
            return commandResponse;
        }
        OperateResult write = siemensNet.Write(address, data);
        if (write.IsSuccess) {
            log.info("写入原始命令成功。地址={},数据={}", address, JSON.toJSON(data));
            commandResponse.setResult(true);
        } else {
            log.error("写入原始命令失败。地址={},数据={}", address, JSON.toJSON(data));
            commandResponse.setResult(false);
        }
        return commandResponse;
    }
    @Override
    public byte[] readOriginCommand(String address, int length) {
        OperateResultExOne<byte[]> result = siemensNet.Read(address, (short) length);
        if (result.IsSuccess) {
            return result.Content;
        }
        return new byte[0];
    }
    private ZyStationStatusEntity findStatusEntity(Integer stationId) {
        for (ZyStationStatusEntity statusEntity : statusList) {
            if (statusEntity.getStationId().equals(stationId)) {
                return statusEntity;
            }
        }
        return null;
    }
    private int getEnableCommandIdx() {
        int useIdx = -1;
        for (int i = 0; i < taskAddressLimit; i++) {
            OperateResultExOne<byte[]> result = siemensNet.Read("DB23." + i * taskAddressLength, (short) taskAddressLength);
            int taskStatus = siemensNet.getByteTransform().TransInt16(result.Content, i * taskAddressLength + 46);
            if (taskStatus == 1) {
                continue;
            }
            useIdx = i;
            break;
        }
        return  useIdx;
    }
}
src/main/java/com/zy/core/plugin/FakeProcess.java
@@ -236,7 +236,7 @@
                        && !stationProtocol.isLoading()
                        && stationProtocol.getTaskNo() == 0
                ) {
                    StationCommand command = stationThread.getMoveCommand(commonService.getWorkNo(WrkIoType.FAKE_TASK_NO.id), stationId, entity.getBarcodeStation().getStationId(), 0);
                    StationCommand command = stationThread.getCommand(StationCommandType.MOVE, commonService.getWorkNo(WrkIoType.FAKE_TASK_NO.id), stationId, entity.getBarcodeStation().getStationId(), 0);
                    MessageQueue.offer(SlaveType.Devp, basDevp.getDevpNo(), new Task(2, command));
                    redisUtil.set(RedisKeyType.GENERATE_FAKE_IN_STATION_DATA_LIMIT.key + stationId, "lock", 5);
                }
@@ -325,7 +325,7 @@
                    taskParam.setBarcode(stationProtocol.getBarcode());
                    WrkMast wrkMast = commonService.createInTask(taskParam);
                    StationCommand command = stationThread.getMoveCommand(wrkMast.getWrkNo(), stationId, stationId, 0);
                    StationCommand command = stationThread.getCommand(StationCommandType.MOVE, wrkMast.getWrkNo(), stationId, stationId, 0);
                    if(command == null){
                        News.taskInfo(wrkMast.getWrkNo(), "获取输送线命令失败");
                        continue;
@@ -467,7 +467,7 @@
                        taskParam.setBarcode(stationProtocol.getBarcode());
                        WrkMast wrkMast = commonService.createInTask(taskParam);
                        StationCommand command = stationThread.getMoveCommand(wrkMast.getWrkNo(), stationId, stationId, 0);
                        StationCommand command = stationThread.getCommand(StationCommandType.WRITE_INFO, wrkMast.getWrkNo(), stationId, stationId, 0);
                        if(command == null){
                            News.taskInfo(wrkMast.getWrkNo(), "获取输送线命令失败");
                            continue;
@@ -528,7 +528,7 @@
                        continue;
                    }
                    StationCommand command = stationThread.getMoveCommand(0, stationObjModel.getStationId(), 0, 0);
                    StationCommand command = stationThread.getCommand(StationCommandType.RESET, 0, stationObjModel.getStationId(), 0, 0);
                    if(command == null){
                        continue;
                    }
@@ -576,11 +576,6 @@
                continue;
            }
            StationCommand command = stationThread.getMoveCommand(0, stationObjModel.getStationId(), 0, 0);
            if(command == null){
                continue;
            }
            Map<Integer, StationProtocol> statusMap = stationThread.getStatusMap();
            StationProtocol stationProtocol = statusMap.get(stationObjModel.getStationId());
            if (stationProtocol == null) {
@@ -588,6 +583,11 @@
            }
            if(stationProtocol.getTaskNo() > 0) {
                StationCommand command = stationThread.getCommand(StationCommandType.RESET, 0, stationObjModel.getStationId(), 0, 0);
                if(command == null){
                    continue;
                }
                WrkMast wrkMast = wrkMastService.selectByWorkNo(stationProtocol.getTaskNo());
                if (wrkMast == null) {
                    MessageQueue.offer(SlaveType.Devp, stationObjModel.getDeviceNo(), new Task(2, command));
@@ -693,7 +693,7 @@
                            continue;
                        }
                        //生成仿真站点数据
                        StationCommand command = stationThread.getMoveCommand(9998, wrkMast.getSourceStaNo(), 0, 0);
                        StationCommand command = stationThread.getCommand(StationCommandType.WRITE_INFO, 9998, wrkMast.getSourceStaNo(), 0, 0);
                        MessageQueue.offer(SlaveType.Devp, stationObjModel.getDeviceNo(), new Task(2, command));
                    }
                }else if(wrkMast.getWrkSts() == WrkStsType.LOC_MOVE_RUN.sts){
src/main/java/com/zy/core/thread/StationThread.java
@@ -1,6 +1,7 @@
package com.zy.core.thread;
import com.zy.core.ThreadHandler;
import com.zy.core.enums.StationCommandType;
import com.zy.core.model.CommandResponse;
import com.zy.core.model.command.StationCommand;
import com.zy.core.model.protocol.StationProtocol;
@@ -13,7 +14,7 @@
    Map<Integer, StationProtocol> getStatusMap();
    StationCommand getMoveCommand(Integer taskNo, Integer stationId, Integer targetStationId, Integer palletSize);
    StationCommand getCommand(StationCommandType commandType, Integer taskNo, Integer stationId, Integer targetStationId, Integer palletSize);
    CommandResponse sendCommand(StationCommand command);
src/main/java/com/zy/core/thread/impl/ZyStationThread.java
@@ -20,6 +20,7 @@
import com.zy.core.cache.OutputQueue;
import com.zy.core.enums.RedisKeyType;
import com.zy.core.enums.SlaveType;
import com.zy.core.enums.StationCommandType;
import com.zy.core.model.CommandResponse;
import com.zy.core.model.Task;
import com.zy.core.model.command.StationCommand;
@@ -179,12 +180,13 @@
    }
    @Override
    public StationCommand getMoveCommand(Integer taskNo, Integer stationId, Integer targetStationId, Integer palletSize) {
    public StationCommand getCommand(StationCommandType commandType, Integer taskNo, Integer stationId, Integer targetStationId, Integer palletSize) {
        StationCommand stationCommand = new StationCommand();
        stationCommand.setTaskNo(taskNo);
        stationCommand.setStationId(stationId);
        stationCommand.setTargetStaNo(targetStationId);
        stationCommand.setPalletSize(palletSize);
        stationCommand.setCommandType(commandType);
        return stationCommand;
    }
src/main/java/com/zy/core/thread/impl/ZyStationV3Thread.java
New file
@@ -0,0 +1,390 @@
package com.zy.core.thread.impl;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.core.common.DateUtils;
import com.core.common.SpringUtils;
import com.zy.asrs.entity.BasDevp;
import com.zy.asrs.entity.BasStationOpt;
import com.zy.asrs.entity.DeviceConfig;
import com.zy.asrs.entity.DeviceDataLog;
import com.zy.asrs.service.BasDevpService;
import com.zy.asrs.service.BasStationOptService;
import com.zy.asrs.utils.Utils;
import com.zy.common.model.NavigateNode;
import com.zy.common.utils.NavigateUtils;
import com.zy.common.utils.RedisUtil;
import com.zy.core.cache.MessageQueue;
import com.zy.core.cache.OutputQueue;
import com.zy.core.cache.SlaveConnection;
import com.zy.core.enums.RedisKeyType;
import com.zy.core.enums.SlaveType;
import com.zy.core.enums.StationCommandType;
import com.zy.core.model.CommandResponse;
import com.zy.core.model.Task;
import com.zy.core.model.command.StationCommand;
import com.zy.core.model.protocol.StationProtocol;
import com.zy.core.network.DeviceConnectPool;
import com.zy.core.network.ZyStationConnectDriver;
import com.zy.core.network.entity.ZyStationStatusEntity;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import java.text.MessageFormat;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Data
@Slf4j
public class ZyStationV3Thread implements Runnable, com.zy.core.thread.StationThread {
    private List<StationProtocol> statusList = new ArrayList<>();
    private DeviceConfig deviceConfig;
    private RedisUtil redisUtil;
    private ZyStationConnectDriver zyStationConnectDriver;
    private int deviceLogCollectTime = 200;
    private long deviceDataLogTime = System.currentTimeMillis();
    private ExecutorService executor = Executors.newFixedThreadPool(9999);
    public ZyStationV3Thread(DeviceConfig deviceConfig, RedisUtil redisUtil) {
        this.deviceConfig = deviceConfig;
        this.redisUtil = redisUtil;
    }
    @Override
    @SuppressWarnings("InfiniteLoopStatement")
    public void run() {
        this.connect();
        deviceLogCollectTime = Utils.getDeviceLogCollectTime();
        Thread readThread = new Thread(() -> {
            while (true) {
                try {
                    deviceLogCollectTime = Utils.getDeviceLogCollectTime();
                    readStatus();
                    Thread.sleep(100);
                } catch (Exception e) {
                    log.error("StationV3Thread Fail", e);
                }
            }
        });
        readThread.start();
        Thread processThread = new Thread(() -> {
            while (true) {
                try {
                    int step = 1;
                    Task task = MessageQueue.poll(SlaveType.Devp, deviceConfig.getDeviceNo());
                    if (task != null) {
                        step = task.getStep();
                    }
                    if (step == 2) {
                        StationCommand cmd = (StationCommand) task.getData();
                        executor.submit(() -> executeMoveWithSeg(cmd));
                    }
                    Thread.sleep(100);
                } catch (Exception e) {
                    log.error("StationV3Process Fail", e);
                }
            }
        });
        processThread.start();
    }
    private void readStatus() {
        if (zyStationConnectDriver == null) {
            return;
        }
        if (statusList.isEmpty()) {
            BasDevpService basDevpService = null;
            try {
                basDevpService = SpringUtils.getBean(BasDevpService.class);
            } catch (Exception e) {
            }
            if (basDevpService == null) {
                return;
            }
            BasDevp basDevp = basDevpService
                    .selectOne(new EntityWrapper<BasDevp>().eq("devp_no", deviceConfig.getDeviceNo()));
            if (basDevp == null) {
                return;
            }
            List<ZyStationStatusEntity> list = JSONObject.parseArray(basDevp.getStationList(), ZyStationStatusEntity.class);
            for (ZyStationStatusEntity entity : list) {
                StationProtocol stationProtocol = new StationProtocol();
                stationProtocol.setStationId(entity.getStationId());
                statusList.add(stationProtocol);
            }
        }
        List<ZyStationStatusEntity> zyStationStatusEntities = zyStationConnectDriver.getStatus();
        for (ZyStationStatusEntity statusEntity : zyStationStatusEntities) {
            for (StationProtocol stationProtocol : statusList) {
                if (stationProtocol.getStationId().equals(statusEntity.getStationId())) {
                    stationProtocol.setTaskNo(statusEntity.getTaskNo());
                    stationProtocol.setTargetStaNo(statusEntity.getTargetStaNo());
                    stationProtocol.setAutoing(statusEntity.isAutoing());
                    stationProtocol.setLoading(statusEntity.isLoading());
                    stationProtocol.setInEnable(statusEntity.isInEnable());
                    stationProtocol.setOutEnable(statusEntity.isOutEnable());
                    stationProtocol.setEmptyMk(statusEntity.isEmptyMk());
                    stationProtocol.setFullPlt(statusEntity.isFullPlt());
                    stationProtocol.setPalletHeight(statusEntity.getPalletHeight());
                    stationProtocol.setError(statusEntity.getError());
                    stationProtocol.setBarcode(statusEntity.getBarcode());
                    stationProtocol.setRunBlock(statusEntity.isRunBlock());
                    stationProtocol.setEnableIn(statusEntity.isEnableIn());
                }
            }
        }
        OutputQueue.DEVP.offer(MessageFormat.format("【{0}】[id:{1}] <<<<< 实时数据更新成功", DateUtils.convert(new Date()), deviceConfig.getDeviceNo()));
        if (System.currentTimeMillis() - deviceDataLogTime > deviceLogCollectTime) {
            DeviceDataLog deviceDataLog = new DeviceDataLog();
            deviceDataLog.setOriginData(JSON.toJSONString(zyStationStatusEntities));
            deviceDataLog.setWcsData(JSON.toJSONString(statusList));
            deviceDataLog.setType(String.valueOf(SlaveType.Devp));
            deviceDataLog.setDeviceNo(deviceConfig.getDeviceNo());
            deviceDataLog.setCreateTime(new Date());
            redisUtil.set(RedisKeyType.DEVICE_LOG_KEY.key + System.currentTimeMillis(), deviceDataLog, 60 * 60 * 24);
            deviceDataLogTime = System.currentTimeMillis();
        }
    }
    @Override
    public boolean connect() {
        zyStationConnectDriver = new ZyStationConnectDriver(deviceConfig, redisUtil);
        zyStationConnectDriver.start();
        DeviceConnectPool.put(SlaveType.Devp, deviceConfig.getDeviceNo(), zyStationConnectDriver);
        return true;
    }
    @Override
    public void close() {
        if (zyStationConnectDriver != null) {
            zyStationConnectDriver.close();
        }
        if (executor != null) {
            try { executor.shutdownNow(); } catch (Exception ignore) {}
        }
    }
    @Override
    public List<StationProtocol> getStatus() {
        return statusList;
    }
    @Override
    public Map<Integer, StationProtocol> getStatusMap() {
        Map<Integer, StationProtocol> map = new HashMap<>();
        for (StationProtocol stationProtocol : statusList) {
            map.put(stationProtocol.getStationId(), stationProtocol);
        }
        return map;
    }
    @Override
    public StationCommand getCommand(StationCommandType commandType, Integer taskNo, Integer stationId, Integer targetStationId, Integer palletSize) {
        StationCommand stationCommand = new StationCommand();
        stationCommand.setTaskNo(taskNo);
        stationCommand.setStationId(stationId);
        stationCommand.setTargetStaNo(targetStationId);
        stationCommand.setPalletSize(palletSize);
        stationCommand.setCommandType(commandType);
        if (commandType == StationCommandType.MOVE) {
            if (!stationId.equals(targetStationId)) {
                List<Integer> path = calcPathStationIds(stationId, targetStationId);
                stationCommand.setNavigatePath(path);
            }
        }
        return stationCommand;
    }
    @Override
    public CommandResponse sendCommand(StationCommand command) {
        CommandResponse commandResponse = null;
        try {
            commandResponse = zyStationConnectDriver.sendCommand(command);
            return commandResponse;
        } finally {
            BasStationOptService optService = SpringUtils.getBean(BasStationOptService.class);
            List<ZyStationStatusEntity> statusListEntity = zyStationConnectDriver.getStatus();
            ZyStationStatusEntity matched = null;
            if (statusListEntity != null) {
                for (ZyStationStatusEntity e : statusListEntity) {
                    if (e.getStationId() != null && e.getStationId().equals(command.getStationId())) {
                        matched = e;
                        break;
                    }
                }
            }
            BasStationOpt basStationOpt = new BasStationOpt(
                    command.getTaskNo(),
                    command.getStationId(),
                    new Date(),
                    String.valueOf(command.getCommandType()),
                    command.getStationId(),
                    command.getTargetStaNo(),
                    null,
                    null,
                    null,
                    JSON.toJSONString(command),
                    JSON.toJSONString(matched),
                    1,
                    JSON.toJSONString(commandResponse)
            );
            if (optService != null) {
                optService.insert(basStationOpt);
            }
        }
    }
    @Override
    public CommandResponse sendOriginCommand(String address, short[] data) {
        return zyStationConnectDriver.sendOriginCommand(address, data);
    }
    @Override
    public byte[] readOriginCommand(String address, int length) {
        return zyStationConnectDriver.readOriginCommand(address, length);
    }
    private List<Integer> calcPathStationIds(Integer startStationId, Integer targetStationId) {
        NavigateUtils navigateUtils = SpringUtils.getBean(NavigateUtils.class);
        if (navigateUtils == null) {
            return new ArrayList<>();
        }
        List<NavigateNode> nodes = navigateUtils.calcByStationId(startStationId, targetStationId);
        List<Integer> ids = new ArrayList<>();
        for (NavigateNode n : nodes) {
            JSONObject v = JSONObject.parseObject(n.getNodeValue());
            if (v != null) {
                ids.add(v.getInteger("stationId"));
            }
        }
        return ids;
    }
    private void executeMoveWithSeg(StationCommand original) {
        if(original.getCommandType() == StationCommandType.MOVE){
            List<Integer> path = original.getNavigatePath();
            if (path == null || path.isEmpty()) {
                path = calcPathStationIds(original.getStationId(), original.getTargetStaNo());
            }
            if (path == null || path.isEmpty()) {
                return;
            }
            int total = path.size();
            List<Integer> segmentTargets = new ArrayList<>();
            List<Integer> segmentEndIndices = new ArrayList<>();
            int idx = 0;
            while (idx < total) {
                int end = Math.min(idx + 20, total) - 1;
                segmentTargets.add(path.get(end));
                segmentEndIndices.add(end);
                idx = end + 1;
            }
            int segCursor = 0;
            Integer currentTarget = segmentTargets.get(segCursor);
            Integer currentEndIdx = segmentEndIndices.get(segCursor);
            Integer currentStartIdx = 0;
            StationCommand segCmd = new StationCommand();
            segCmd.setTaskNo(original.getTaskNo());
            segCmd.setStationId(original.getStationId());
            segCmd.setTargetStaNo(original.getTargetStaNo());
            segCmd.setCommandType(original.getCommandType());
            segCmd.setPalletSize(original.getPalletSize());
            segCmd.setNavigatePath(new ArrayList<>(path.subList(0, currentEndIdx + 1)));
            sendCommand(segCmd);
            boolean finished = false;
            while (!finished) {
                try {
                    Integer currentStationId = findCurrentStationByTask(original.getTaskNo());
                    if (currentStationId == null) {
                        Thread.sleep(500);
                        continue;
                    }
                    int currentIndex = path.indexOf(currentStationId);
                    if (currentIndex < 0) {
                        Thread.sleep(500);
                        continue;
                    }
                    int remaining = total - currentIndex - 1;
                    if (remaining <= 0) {
                        finished = true;
                        break;
                    }
                    int currentSegEndIndex = path.indexOf(segmentTargets.get(segCursor));
                    int currentSegStartIndex = segCursor == 0 ? 0 : path.indexOf(segmentTargets.get(segCursor - 1)) + 1;
                    int segLen = currentSegEndIndex - currentSegStartIndex + 1;
                    int remainingSegment = Math.max(0, currentSegEndIndex - currentIndex);
                    int thresholdSegment = (int) Math.ceil(segLen * 0.3);
                    if (remainingSegment <= thresholdSegment && segCursor < segmentTargets.size() - 1) {
                        segCursor++;
                        currentEndIdx = segmentEndIndices.get(segCursor);
                        currentStartIdx = segmentEndIndices.get(segCursor - 1) + 1;
                        StationCommand nextCmd = new StationCommand();
                        nextCmd.setTaskNo(original.getTaskNo());
                        nextCmd.setStationId(original.getStationId());
                        nextCmd.setTargetStaNo(original.getTargetStaNo());
                        nextCmd.setCommandType(original.getCommandType());
                        nextCmd.setPalletSize(original.getPalletSize());
                        nextCmd.setNavigatePath(new ArrayList<>(path.subList(currentStartIdx, currentEndIdx + 1)));
                        sendCommand(nextCmd);
                    }
                    Thread.sleep(500);
                } catch (Exception e) {
                    break;
                }
            }
        }else {
            sendCommand(original);
        }
    }
    private Integer findCurrentStationByTask(Integer taskNo) {
        try {
            com.zy.asrs.service.DeviceConfigService deviceConfigService = SpringUtils.getBean(com.zy.asrs.service.DeviceConfigService.class);
            if (deviceConfigService == null) {
                return null;
            }
            List<DeviceConfig> devpList = deviceConfigService.selectList(new EntityWrapper<DeviceConfig>()
                    .eq("device_type", String.valueOf(SlaveType.Devp)));
            for (DeviceConfig dc : devpList) {
                com.zy.core.thread.StationThread t = (com.zy.core.thread.StationThread) SlaveConnection.get(SlaveType.Devp, dc.getDeviceNo());
                if (t == null) {
                    continue;
                }
                Map<Integer, StationProtocol> m = t.getStatusMap();
                if (m == null || m.isEmpty()) {
                    continue;
                }
                for (StationProtocol sp : m.values()) {
                    if (sp.getTaskNo() != null && sp.getTaskNo().equals(taskNo) && sp.isLoading()) {
                        return sp.getStationId();
                    }
                }
            }
        } catch (Exception e) {
            return null;
        }
        return null;
    }
}
src/main/java/com/zy/core/utils/DualCrnOperateProcessUtils.java
@@ -578,7 +578,7 @@
                            continue;
                        }
                        //生成仿真站点数据
                        StationCommand command = stationThread.getMoveCommand(9998, wrkMast.getSourceStaNo(), 0, 0);
                        StationCommand command = stationThread.getCommand(StationCommandType.WRITE_INFO, 9998, wrkMast.getSourceStaNo(), 0, 0);
                        MessageQueue.offer(SlaveType.Devp, stationObjModel.getDeviceNo(), new Task(2, command));
                    }
                }
src/main/java/com/zy/core/utils/StationOperateProcessUtils.java
@@ -15,6 +15,7 @@
import com.zy.core.cache.SlaveConnection;
import com.zy.core.enums.RedisKeyType;
import com.zy.core.enums.SlaveType;
import com.zy.core.enums.StationCommandType;
import com.zy.core.enums.WrkStsType;
import com.zy.core.model.StationObjModel;
import com.zy.core.model.Task;
@@ -107,7 +108,7 @@
                        continue;
                    }
                    StationCommand command = stationThread.getMoveCommand(wrkMast.getWrkNo(), stationId, targetStationId, 0);
                    StationCommand command = stationThread.getCommand(StationCommandType.MOVE, wrkMast.getWrkNo(), stationId, targetStationId, 0);
                    if(command == null){
                        News.taskInfo(wrkMast.getWrkNo(), "获取输送线命令失败");
                        continue;
@@ -174,7 +175,7 @@
                        && stationProtocol.isLoading()
                        && stationProtocol.getTaskNo() == 0
                ) {
                    StationCommand command = stationThread.getMoveCommand(wrkMast.getWrkNo(), stationProtocol.getStationId(), wrkMast.getStaNo(), 0);
                    StationCommand command = stationThread.getCommand(StationCommandType.MOVE, wrkMast.getWrkNo(), stationProtocol.getStationId(), wrkMast.getStaNo(), 0);
                    if(command == null){
                        News.taskInfo(wrkMast.getWrkNo(), "获取输送线命令失败");
                        continue;
@@ -312,7 +313,7 @@
                                continue;
                            }
                            StationCommand command = stationThread.getMoveCommand(wrkMast.getWrkNo(), stationProtocol.getStationId(), targetStationId, 0);
                            StationCommand command = stationThread.getCommand(StationCommandType.MOVE, wrkMast.getWrkNo(), stationProtocol.getStationId(), targetStationId, 0);
                            if(command == null){
                                News.taskInfo(wrkMast.getWrkNo(), "获取输送线命令失败");
                                continue;
@@ -348,7 +349,7 @@
                        }
                    }else {
                        //运行堵塞,重新计算路线
                        StationCommand command = stationThread.getMoveCommand(wrkMast.getWrkNo(), stationProtocol.getStationId(), wrkMast.getStaNo(), 0);
                        StationCommand command = stationThread.getCommand(StationCommandType.MOVE, wrkMast.getWrkNo(), stationProtocol.getStationId(), wrkMast.getStaNo(), 0);
                        if(command == null){
                            News.taskInfo(wrkMast.getWrkNo(), "获取输送线命令失败");
                            continue;
src/main/webapp/components/DevpCard.js
@@ -40,6 +40,7 @@
                <el-descriptions-item label="空板信号">{{ item.emptyMk ? 'Y' : 'N' }}</el-descriptions-item>
                <el-descriptions-item label="满板信号">{{ item.fullPlt ? 'Y' : 'N' }}</el-descriptions-item>
                <el-descriptions-item label="运行阻塞">{{ item.runBlock ? 'Y' : 'N' }}</el-descriptions-item>
                <el-descriptions-item label="启动入库">{{ item.enableIn ? 'Y' : 'N' }}</el-descriptions-item>
                <el-descriptions-item label="托盘高度">{{ item.palletHeight }}</el-descriptions-item>
                <el-descriptions-item label="条码">{{ item.barcode }}</el-descriptions-item>
                <el-descriptions-item label="故障代码">{{ item.error }}</el-descriptions-item>