huang
18 小时以前 fc4e5c458094c6bf5238d7d21291325f19a57adb
mes-processes/mes-plcSend/src/main/java/com/mes/interaction/vehicle/handler/LoadVehicleLogicHandler.java
@@ -15,9 +15,8 @@
import com.mes.interaction.vehicle.model.VehicleState;
import com.mes.interaction.vehicle.model.VehicleStatus;
import com.mes.interaction.vehicle.model.VehicleTask;
import com.mes.s7.enhanced.EnhancedS7Serializer;
import com.mes.s7.provider.S7SerializerProvider;
import com.mes.service.PlcDynamicDataService;
import com.mes.plc.client.PlcClient;
import com.mes.plc.factory.PlcClientFactory;
import com.mes.task.model.TaskExecutionContext;
import com.mes.interaction.workstation.scanner.handler.HorizontalScannerLogicHandler;
import lombok.extern.slf4j.Slf4j;
@@ -56,10 +55,7 @@
    private DeviceStatusService deviceStatusService;
    
    @Autowired(required = false)
    private PlcDynamicDataService plcDynamicDataService;
    @Autowired(required = false)
    private S7SerializerProvider s7SerializerProvider;
    private PlcClientFactory plcClientFactory;
    // MES字段列表(进片和出片共用同一套协议)
    // 根据协议,使用带数字后缀的字段名(1-6对应6个玻璃位置)
@@ -112,13 +108,21 @@
    
    // 记录当前任务:deviceId -> 任务信息
    private final Map<String, MesTaskInfo> currentTasks = new ConcurrentHashMap<>();
    /**
     * 记录最近一次已完成但MES未复位的任务签名,避免重复拉起
     */
    private final Map<String, CompletedMesRecord> lastCompletedMesRecords = new ConcurrentHashMap<>();
    @Autowired
    public LoadVehicleLogicHandler(
            DevicePlcOperationService devicePlcOperationService,
            @Qualifier("deviceGlassInfoService") GlassInfoService glassInfoService) {
            @Qualifier("deviceGlassInfoService") GlassInfoService glassInfoService,
            PlcClientFactory plcClientFactory) {
        super(devicePlcOperationService);
        this.glassInfoService = glassInfoService;
        this.plcClientFactory = plcClientFactory;
        // 设置 PlcClientFactory 到基类
        this.setPlcClientFactory(plcClientFactory);
    }
    @Override
@@ -145,6 +149,10 @@
                // 因为定时器可能会重复调用,或者需要等待任务完成
                if ("feedGlass".equals(operation) && status.isExecuting()) {
                    log.debug("车辆 {} 当前状态为 EXECUTING,但允许继续执行 feedGlass(定时器重复调用)", deviceId);
                    // 允许继续执行,不返回错误
                } else if ("clearGlass".equals(operation) || "clearPlc".equals(operation) || "clear".equals(operation)) {
                    // 清除操作应该允许在任何状态下执行,因为其目的就是重置设备状态
                    log.debug("车辆 {} 当前状态为 {},但允许执行清除操作 {}", deviceId, status.getState(), operation);
                    // 允许继续执行,不返回错误
                } else {
                    return DevicePlcVO.OperationResult.builder()
@@ -215,7 +223,7 @@
                    result = handleSetOnlineState(deviceConfig, params, logicParams);
                    break;
                case "checkMesConfirm":
                    result = checkMesConfirm(deviceConfig, logicParams);
                    result = checkMesConfirm(deviceConfig, params, logicParams);
                    break;
                case "markBroken":
                    result = handleMarkBroken(deviceConfig, params, logicParams);
@@ -1239,20 +1247,17 @@
                }
                
                // 检查是否有待处理的进片或出片任务
                if (plcDynamicDataService != null && s7SerializerProvider != null) {
                    EnhancedS7Serializer serializer = s7SerializerProvider.getSerializer(deviceConfig);
                    if (serializer != null) {
                        // 检查进片任务
                        Map<String, Object> mesData = plcDynamicDataService.readPlcData(
                                deviceConfig, MES_FIELDS, serializer);
                        Integer mesSend = parseInteger(mesData != null ? mesData.get("mesSend") : null);
                        // 进片和出片共用mesSend字段,只需检查一次
                        // 如果有待处理的任务,不设置plcRequest(等待任务处理)
                        if (mesSend != null && mesSend == 1) {
                            log.debug("大车空闲监控: deviceId={}, 检测到待处理任务(mesSend=1),不设置plcRequest", deviceId);
                            return;
                        }
                PlcClient plcClient = getPlcClient(deviceConfig);
                if (plcClient != null) {
                    // 检查进片任务
                    Map<String, Object> mesData = plcClient.readData(MES_FIELDS.toArray(new String[0]));
                    Integer mesSend = parseInteger(mesData != null ? mesData.get("mesSend") : null);
                    // 进片和出片共用mesSend字段,只需检查一次
                    // 如果有待处理的任务,不设置plcRequest(等待任务处理)
                    if (mesSend != null && mesSend == 1) {
                        log.debug("大车空闲监控: deviceId={}, 检测到待处理任务(mesSend=1),不设置plcRequest", deviceId);
                        return;
                    }
                }
                
@@ -1302,19 +1307,12 @@
            Map<String, Object> params,
            Map<String, Object> logicParams) {
        
        if (plcDynamicDataService == null || s7SerializerProvider == null) {
            return DevicePlcVO.OperationResult.builder()
                    .success(false)
                    .message("PlcDynamicDataService或S7SerializerProvider未注入")
                    .build();
        }
        String deviceId = deviceConfig.getDeviceId();
        EnhancedS7Serializer serializer = s7SerializerProvider.getSerializer(deviceConfig);
        if (serializer == null) {
        PlcClient plcClient = getPlcClient(deviceConfig);
        if (plcClient == null) {
            return DevicePlcVO.OperationResult.builder()
                    .success(false)
                    .message("获取PLC序列化器失败")
                    .message("获取PLC客户端失败")
                    .build();
        }
        
@@ -1323,16 +1321,27 @@
            MesTaskInfo existingTask = currentTasks.get(deviceId);
            if (existingTask != null) {
                log.debug("设备已有任务在执行中,跳过检查MES任务: deviceId={}", deviceId);
                // 仍然返回当前任务的玻璃列表,供任务引擎记录/对账本批次
                List<String> batchIds = new ArrayList<>();
                if (existingTask.glasses != null) {
                    for (GlassTaskInfo g : existingTask.glasses) {
                        if (g != null && g.glassId != null && !g.glassId.isEmpty()) {
                            batchIds.add(g.glassId);
                        }
                    }
                }
                return DevicePlcVO.OperationResult.builder()
                        .success(true)
                        .message("任务执行中,无需重复检查MES任务")
                        .data(Collections.singletonMap("waiting", false))
                        .data(new HashMap<String, Object>() {{
                            put("waiting", false);
                            put("batchGlassIds", batchIds);
                        }})
                        .build();
            }
            
            // 读取MES字段(进片和出片共用)
            Map<String, Object> mesData = plcDynamicDataService.readPlcData(
                    deviceConfig, MES_FIELDS, serializer);
            Map<String, Object> mesData = plcClient.readData(MES_FIELDS.toArray(new String[0]));
            if (mesData == null || mesData.isEmpty()) {
                log.warn("读取MES字段失败: deviceId={}, mesData为空或null", deviceId);
                return DevicePlcVO.OperationResult.builder()
@@ -1349,11 +1358,31 @@
                waitData.put("completed", false);
                waitData.put("waiting", true);
                waitData.put("waitingReason", "mesSend=0");
                waitData.put("batchGlassIds", new ArrayList<>());
                return DevicePlcVO.OperationResult.builder()
                        .success(true)
                        .message("等待MES发送请求(mesSend=0)")
                        .data(waitData)
                        .build();
            }
            // 构建当前MES数据签名,用于判断是否为已确认但未复位的旧任务
            String mesSignature = buildMesSignature(mesData);
            CompletedMesRecord completedRecord = lastCompletedMesRecords.get(deviceId);
            if (completedRecord != null
                    && mesSignature.equals(completedRecord.signature)) {
                Integer mesConfirm = parseInteger(mesData.get("mesConfirm"));
                if (mesConfirm != null && mesConfirm == 1) {
                    Map<String, Object> waitData = new HashMap<>();
                    waitData.put("completed", true);
                    waitData.put("waiting", true);
                    waitData.put("waitingReason", "mesNotReset");
                    return DevicePlcVO.OperationResult.builder()
                            .success(true)
                            .message("MES已确认完成但未复位(mesSend/mesConfirm),等待复位后再接收新任务")
                            .data(waitData)
                            .build();
                }
            }
            
            // mesSend=1,记录日志
@@ -1453,6 +1482,7 @@
            taskInfo.cartime = timeCalc.cartime;
            taskInfo.createdTime = System.currentTimeMillis();
            taskInfo.isOutbound = isOutbound;
            taskInfo.mesSignature = mesSignature;
            
            // 从配置中读取破损玻璃索引(用于测试场景)
            // 配置格式:brokenGlassIndices: [0, 2] 表示第1个和第3个玻璃应该破损
@@ -1472,11 +1502,44 @@
            }
            
            currentTasks.put(deviceId, taskInfo);
            // 如果有多设备任务上下文,则记录本次MES下发的玻璃ID列表到上下文,供分批校验使用
            if (params != null) {
                Object ctxObj = params.get("_taskContext");
                if (ctxObj instanceof com.mes.task.model.TaskExecutionContext) {
                    com.mes.task.model.TaskExecutionContext ctx = (com.mes.task.model.TaskExecutionContext) ctxObj;
                    List<String> batchIds = new ArrayList<>();
                    for (GlassTaskInfo g : glasses) {
                        if (g != null && g.glassId != null && !g.glassId.isEmpty()) {
                            batchIds.add(g.glassId);
                        }
                    }
                    // 1. 记录当前批次的玻璃ID
                    ctx.getSharedData().put("currentMesBatchGlassIds", batchIds);
                    log.info("记录本次MES批次玻璃列表: deviceId={}, batchIds={}", deviceId, batchIds);
                    // 2. 初始化总待出片玻璃列表(仅第一次初始化,从任务参数获取)
                    if (!ctx.getSharedData().containsKey("initialGlassIds")) {
                        // 从任务参数中获取总待出片玻璃ID(核心:总列表来自任务参数,而非MES批次)
                        List<String> taskGlassIds = ctx.getParameters().getGlassIds();
                        if (taskGlassIds != null && !taskGlassIds.isEmpty()) {
                            ctx.getSharedData().put("initialGlassIds", new ArrayList<>(taskGlassIds));
                            // 初始化已出片列表为空
                            if (!ctx.getSharedData().containsKey("outboundGlassIds")) {
                                ctx.getSharedData().put("outboundGlassIds", new ArrayList<>());
                            }
                            log.info("初始化总待出片玻璃列表: deviceId={}, taskGlassIds={}", deviceId, taskGlassIds);
                        } else {
                            log.warn("任务参数中未找到总待出片玻璃ID列表: deviceId={}", deviceId);
                        }
                    }
                }
            }
            
            // 清空plcRequest(表示已接收任务)
            Map<String, Object> payload = new HashMap<>();
            payload.put("plcRequest", 0);
            plcDynamicDataService.writePlcData(deviceConfig, payload, serializer);
            plcClient.writeData(payload);
            log.info("已清空plcRequest=0: deviceId={}", deviceId);
            
            // 更新车辆状态为执行中
@@ -1509,6 +1572,14 @@
            Map<String, Object> successData = new HashMap<>();
            successData.put("waiting", false);
            successData.put("taskStarted", true);
            // 将本次MES下发的玻璃ID列表通过返回值带回(任务引擎不再依赖_taskContext写入)
            List<String> batchIdsForReturn = new ArrayList<>();
            for (GlassTaskInfo g : glasses) {
                if (g != null && g.glassId != null && !g.glassId.isEmpty()) {
                    batchIdsForReturn.add(g.glassId);
                }
            }
            successData.put("batchGlassIds", batchIdsForReturn);
            
            return DevicePlcVO.OperationResult.builder()
                    .success(true)
@@ -1761,13 +1832,9 @@
                                      MesTaskInfo taskInfo,
                                      Map<String, Object> logicParams) {
        
        if (plcDynamicDataService == null || s7SerializerProvider == null) {
            return;
        }
        String deviceId = deviceConfig.getDeviceId();
        EnhancedS7Serializer serializer = s7SerializerProvider.getSerializer(deviceConfig);
        if (serializer == null) {
        PlcClient plcClient = getPlcClient(deviceConfig);
        if (plcClient == null) {
            return;
        }
        
@@ -1817,7 +1884,7 @@
                // 优先检查是否标记为破损(state=8)
                // 检查任务信息中是否标记了该玻璃为破损
                if (taskInfo.brokenGlassIndices != null && taskInfo.brokenGlassIndices.contains(i)) {
                    updateStateIfNeeded(deviceConfig, serializer, stateValues, stateField, 8, taskInfo);
                    updateStateIfNeeded(deviceConfig, plcClient, stateValues, stateField, 8, taskInfo);
                    log.info("玻璃标记为破损: deviceId={}, stateField={}, glassIndex={}", 
                            deviceConfig.getDeviceId(), stateField, i);
                    continue;
@@ -1825,7 +1892,7 @@
                
                // 检查超时未完成(state=3)
                if (elapsed >= state3TimeoutTime && (currentState == null || currentState < 2)) {
                    updateStateIfNeeded(deviceConfig, serializer, stateValues, stateField, 3, taskInfo);
                    updateStateIfNeeded(deviceConfig, plcClient, stateValues, stateField, 3, taskInfo);
                    log.warn("任务超时未完成: deviceId={}, stateField={}, elapsed={}ms, expectedTime={}ms", 
                            deviceConfig.getDeviceId(), stateField, elapsed, state2Time);
                    continue;
@@ -1834,7 +1901,7 @@
                // 正常状态更新
                if (elapsed >= state1Time && elapsed < state2Time) {
                    // state应该为1(上车完成)
                    boolean stateChanged = updateStateIfNeeded(deviceConfig, serializer, stateValues, stateField, 1, taskInfo);
                    boolean stateChanged = updateStateIfNeeded(deviceConfig, plcClient, stateValues, stateField, 1, taskInfo);
                    if (stateChanged) {
                        hasStateOne = true;
                        currentStepDesc = "玻璃已上车(state=1),正在运输到目标位置";
@@ -1843,7 +1910,7 @@
                    }
                } else if (elapsed >= state2Time) {
                    // state应该为2(运输完成)
                    boolean stateChanged = updateStateIfNeeded(deviceConfig, serializer, stateValues, stateField, 2, taskInfo);
                    boolean stateChanged = updateStateIfNeeded(deviceConfig, plcClient, stateValues, stateField, 2, taskInfo);
                    if (stateChanged) {
                        hasStateTwo = true;
                        currentStepDesc = "玻璃已到达目标位置(state=2),等待MES确认";
@@ -1867,7 +1934,7 @@
            
            // 检查是否所有state都>=2,如果是则给MES汇报
            if (elapsed >= state2Time && allStatesCompleted(stateValues, glassCount)) {
                reportToMes(deviceConfig, serializer, taskInfo, logicParams);
                reportToMes(deviceConfig, plcClient, taskInfo, logicParams);
                // 记录MES确认开始等待的时间(只记录一次)
                if (taskInfo.mesConfirmStartTime == null) {
                    taskInfo.mesConfirmStartTime = System.currentTimeMillis();
@@ -1890,7 +1957,7 @@
     * @return 是否发生了状态变化(从非目标状态变为目标状态)
     */
    private boolean updateStateIfNeeded(DeviceConfig deviceConfig,
                                     EnhancedS7Serializer serializer,
                                     PlcClient plcClient,
                                     Map<String, Object> currentStates,
                                     String stateField,
                                     int targetState,
@@ -1913,7 +1980,7 @@
            try {
                Map<String, Object> payload = new HashMap<>();
                payload.put(stateField, targetState);
                plcDynamicDataService.writePlcData(deviceConfig, payload, serializer);
                plcClient.writeData(payload);
                
                log.info("任务状态已更新到PLC: deviceId={}, stateField={}, currentState={}, targetState={}", 
                        deviceConfig.getDeviceId(), stateField, currentState, targetState);
@@ -1984,7 +2051,7 @@
     * 给MES汇报
     */
    private void reportToMes(DeviceConfig deviceConfig,
                             EnhancedS7Serializer serializer,
                             PlcClient plcClient,
                             MesTaskInfo taskInfo,
                             Map<String, Object> logicParams) {
        
@@ -1992,7 +2059,7 @@
            // 设置汇报字
            Map<String, Object> payload = new HashMap<>();
            payload.put("plcReport", 1);
            plcDynamicDataService.writePlcData(deviceConfig, payload, serializer);
            plcClient.writeData(payload);
            
            String taskType = taskInfo.isOutbound ? "出片" : "进片";
            String glassIds = taskInfo.glasses.stream()
@@ -2012,30 +2079,27 @@
     * 返回OperationResult.data中的 completed 标志表示是否已确认完成
     */
    public DevicePlcVO.OperationResult checkMesConfirm(DeviceConfig deviceConfig,
                                                       Map<String, Object> params,
                                                       Map<String, Object> logicParams) {
        if (plcDynamicDataService == null || s7SerializerProvider == null) {
            return DevicePlcVO.OperationResult.builder()
                    .success(false)
                    .message("PlcDynamicDataService或S7SerializerProvider未注入")
                    .build();
        }
        EnhancedS7Serializer serializer = s7SerializerProvider.getSerializer(deviceConfig);
        if (serializer == null) {
            return DevicePlcVO.OperationResult.builder()
                    .success(false)
                    .message("获取PLC序列化器失败")
                    .build();
        }
        String deviceId = deviceConfig.getDeviceId();
        PlcClient plcClient = getPlcClient(deviceConfig);
        if (plcClient == null) {
            return DevicePlcVO.OperationResult.builder()
                    .success(false)
                    .message("获取PLC客户端失败")
                    .build();
        }
        MesTaskInfo taskInfo = currentTasks.get(deviceId);
        
        // 如果没有任务记录,优先尝试补偿性地检查一次MES任务(避免因时序问题一直noTask)
        if (taskInfo == null) {
            log.info("检查MES确认时未找到任务记录,尝试补偿检查MES任务: deviceId={}", deviceId);
            try {
                // 关键:补偿检查时也要透传params(包含_taskContext),
                // 否则handleCheckMesTask无法把本批次玻璃ID写入currentMesBatchGlassIds,任务引擎无法累加完成进度
                Map<String, Object> checkParams = params != null ? params : Collections.emptyMap();
                DevicePlcVO.OperationResult checkResult =
                        handleCheckMesTask(deviceConfig, Collections.emptyMap(), logicParams);
                        handleCheckMesTask(deviceConfig, checkParams, logicParams);
                if (Boolean.TRUE.equals(checkResult.getSuccess())) {
                    taskInfo = currentTasks.get(deviceId);
                    if (taskInfo != null) {
@@ -2075,9 +2139,14 @@
                data.put("completed", false);
                data.put("waiting", true);
                data.put("waitingReason", "waitingReport");
                String detail = taskInfo.currentStepDesc;
                String message = "大车任务执行中,尚未汇报,无需检查确认";
                if (detail != null && !detail.isEmpty()) {
                    message = detail + ";" + message;
                }
                return DevicePlcVO.OperationResult.builder()
                        .success(true)
                        .message("大车任务执行中,尚未汇报,无需检查确认")
                        .message(message)
                        .data(data)
                        .build();
            }
@@ -2093,7 +2162,7 @@
                // 超时视为任务失败:清理任务状态并停止监控,避免继续累加等待时间
                try {
                    clearTaskStates(deviceConfig, serializer);
                    clearTaskStates(deviceConfig, plcClient);
                } catch (Exception e) {
                    log.warn("MES确认超时时清空任务状态失败: deviceId={}, error={}", deviceId, e.getMessage());
                }
@@ -2109,15 +2178,20 @@
                        .build();
            }
            
            Object confirmValue = plcDynamicDataService.readPlcField(
                    deviceConfig, "mesConfirm", serializer);
            Integer confirm = parseInteger(confirmValue);
            Map<String, Object> confirmData = plcClient.readData("mesConfirm");
            Integer confirm = parseInteger(confirmData != null ? confirmData.get("mesConfirm") : null);
            boolean completed = confirm != null && confirm == 1;
            data.put("completed", completed);
            if (completed) {
                // MES已确认,清空state和汇报字
                clearTaskStates(deviceConfig, serializer);
                // MES已确认:本次交互完成(不在设备侧判断"是否还有更多玻璃",由任务引擎统一编排)
                clearTaskStates(deviceConfig, plcClient);
                // 记录已完成的任务签名,避免MES未复位时被重复拉起
                if (taskInfo != null && taskInfo.mesSignature != null) {
                    lastCompletedMesRecords.put(deviceId,
                            new CompletedMesRecord(taskInfo.mesSignature, System.currentTimeMillis()));
                }
                // 任务完成,恢复为空闲状态
                statusManager.updateVehicleStatus(
@@ -2133,13 +2207,13 @@
                // 恢复plcRequest=1(可以接收新任务)
                Map<String, Object> payload = new HashMap<>();
                payload.put("plcRequest", 1);
                plcDynamicDataService.writePlcData(deviceConfig, payload, serializer);
                plcClient.writeData(payload);
                log.info("MES任务已确认完成: deviceId={}", deviceConfig.getDeviceId());
                String taskType = taskInfo.isOutbound ? "出片" : "进片";
                String taskType = (taskInfo != null && taskInfo.isOutbound) ? "出片" : "进片";
                return DevicePlcVO.OperationResult.builder()
                        .success(true)
                        .message(String.format("%s任务完成:MES已确认(mesConfirm=1),已清空state和汇报字,大车空闲(plcRequest=1),可以等待下次任务", taskType))
                        .message(String.format("%s任务交互完成:MES已确认(mesConfirm=1),已清空state和汇报字,大车空闲(plcRequest=1)", taskType))
                        .data(data)
                        .build();
            }
@@ -2224,20 +2298,27 @@
        }
        
        // 立即写入PLC的state字段
        EnhancedS7Serializer serializer = s7SerializerProvider.getSerializer(deviceConfig);
        if (serializer != null) {
        PlcClient plcClient = getPlcClient(deviceConfig);
        if (plcClient != null) {
            try {
                Map<String, Object> payload = new HashMap<>();
                for (Integer index : brokenIndices) {
                    String stateField = "state" + (index + 1);
                    payload.put(stateField, 8);
                }
                plcDynamicDataService.writePlcData(deviceConfig, payload, serializer);
                log.info("已标记玻璃为破损并写入PLC: deviceId={}, brokenIndices={}",
                        deviceId, brokenIndices);
                boolean success = plcClient.writeData(payload);
                if (success) {
                    log.info("已标记玻璃为破损并写入PLC: deviceId={}, brokenIndices={}",
                            deviceId, brokenIndices);
                } else {
                    log.error("写入破损状态到PLC失败: deviceId={}, brokenIndices={}",
                            deviceId, brokenIndices);
                }
            } catch (Exception e) {
                log.error("写入破损状态到PLC失败: deviceId={}, error={}", deviceId, e.getMessage());
            }
        } else {
            log.error("获取PLC客户端失败,无法写入破损状态: deviceId={}", deviceId);
        }
        
        return DevicePlcVO.OperationResult.builder()
@@ -2249,7 +2330,7 @@
    /**
     * 清空任务状态
     */
    private void clearTaskStates(DeviceConfig deviceConfig, EnhancedS7Serializer serializer) {
    private void clearTaskStates(DeviceConfig deviceConfig, PlcClient plcClient) {
        try {
            Map<String, Object> payload = new HashMap<>();
            // 清空state1~6
@@ -2258,7 +2339,7 @@
            }
            // 清空汇报字
            payload.put("plcReport", 0);
            plcDynamicDataService.writePlcData(deviceConfig, payload, serializer);
            plcClient.writeData(payload);
        } catch (Exception e) {
            log.error("清空任务状态异常: deviceId={}", deviceConfig.getDeviceId(), e);
        }
@@ -2321,6 +2402,20 @@
        List<Integer> brokenGlassIndices = null; // 标记为破损的玻璃索引列表(0-based,用于测试场景)
        Long mesConfirmStartTime = null; // MES确认开始等待的时间(用于超时检测)
        String currentStepDesc = null; // 当前步骤描述(用于显示详细的执行状态)
        String mesSignature = null; // MES数据签名,用于避免未复位时重复拉起
    }
    /**
     * 记录已完成但MES未复位的任务信息
     */
    private static class CompletedMesRecord {
        final String signature;
        final long completedAt;
        CompletedMesRecord(String signature, long completedAt) {
            this.signature = signature;
            this.completedAt = completedAt;
        }
    }
    /**
@@ -2387,17 +2482,36 @@
    }
    private void clearDynamicTaskStates(DeviceConfig deviceConfig) {
        if (plcDynamicDataService == null || s7SerializerProvider == null) {
            return;
        }
        try {
            EnhancedS7Serializer serializer = s7SerializerProvider.getSerializer(deviceConfig);
            if (serializer != null) {
                clearTaskStates(deviceConfig, serializer);
            PlcClient plcClient = getPlcClient(deviceConfig);
            if (plcClient != null) {
                clearTaskStates(deviceConfig, plcClient);
            }
        } catch (Exception e) {
            log.warn("清空大车state字段失败: deviceId={}, error={}", deviceConfig != null ? deviceConfig.getId() : "null", e.getMessage());
        }
    }
    /**
     * 将MES数据构造成签名字符串,用于识别是否为同一批次任务
     */
    private String buildMesSignature(Map<String, Object> mesData) {
        if (mesData == null || mesData.isEmpty()) {
            return "empty";
        }
        StringBuilder sb = new StringBuilder();
        sb.append("mesSend=").append(mesData.getOrDefault("mesSend", ""))
                .append(";mesConfirm=").append(mesData.getOrDefault("mesConfirm", ""));
        for (int i = 1; i <= 6; i++) {
            sb.append(";g").append(i).append("=")
                    .append(mesData.getOrDefault("mesGlassId" + i, ""))
                    .append(",s").append(mesData.getOrDefault("start" + i, ""))
                    .append(",t").append(mesData.getOrDefault("target" + i, ""))
                    .append(",w").append(mesData.getOrDefault("mesWidth" + i, ""))
                    .append(",h").append(mesData.getOrDefault("mesHeight" + i, ""))
                    .append(",th").append(mesData.getOrDefault("mesThickness" + i, ""));
        }
        return sb.toString();
    }
}