huang
9 小时以前 fc4e5c458094c6bf5238d7d21291325f19a57adb
修改:逻辑交互写入使用plc通讯工厂统一接口
6个文件已修改
531 ■■■■■ 已修改文件
mes-processes/mes-plcSend/src/main/java/com/mes/interaction/BaseDeviceLogicHandler.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
mes-processes/mes-plcSend/src/main/java/com/mes/interaction/vehicle/handler/LoadVehicleLogicHandler.java 149 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
mes-processes/mes-plcSend/src/main/java/com/mes/interaction/workstation/scanner/handler/HorizontalScannerLogicHandler.java 59 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
mes-processes/mes-plcSend/src/main/java/com/mes/interaction/workstation/transfer/handler/HorizontalTransferLogicHandler.java 36 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
mes-processes/mes-plcSend/src/main/java/com/mes/plc/client/impl/ModbusPlcClient.java 27 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
mes-processes/mes-plcSend/src/main/java/com/mes/service/PlcTestWriteService.java 225 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
mes-processes/mes-plcSend/src/main/java/com/mes/interaction/BaseDeviceLogicHandler.java
@@ -5,6 +5,8 @@
import com.mes.device.entity.DeviceConfig;
import com.mes.device.service.DevicePlcOperationService;
import com.mes.device.vo.DevicePlcVO;
import com.mes.plc.client.PlcClient;
import com.mes.plc.factory.PlcClientFactory;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -24,6 +26,39 @@
    protected final DevicePlcOperationService devicePlcOperationService;
    protected final ObjectMapper objectMapper = new ObjectMapper();
    // PlcClientFactory 可选注入,如果子类需要直接使用 PlcClient
    protected PlcClientFactory plcClientFactory;
    /**
     * 设置 PlcClientFactory(用于子类注入)
     */
    public void setPlcClientFactory(PlcClientFactory plcClientFactory) {
        this.plcClientFactory = plcClientFactory;
    }
    /**
     * 获取 PLC 客户端
     *
     * @param deviceConfig 设备配置
     * @return PLC客户端实例,如果获取失败返回null
     */
    protected PlcClient getPlcClient(DeviceConfig deviceConfig) {
        if (plcClientFactory == null) {
            log.warn("PlcClientFactory未注入,无法获取PLC客户端: deviceId={}", deviceConfig.getId());
            return null;
        }
        try {
            PlcClient client = plcClientFactory.getClient(deviceConfig);
            if (client == null) {
                log.error("获取PLC客户端失败: deviceId={}", deviceConfig.getId());
            }
            return client;
        } catch (Exception e) {
            log.error("获取PLC客户端异常: deviceId={}", deviceConfig.getId(), e);
            return null;
        }
    }
    @Override
    public DevicePlcVO.OperationResult execute(DeviceConfig deviceConfig, String operation, Map<String, Object> params) {
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个玻璃位置)
@@ -120,9 +116,13 @@
    @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
@@ -1247,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;
                    }
                }
                
@@ -1310,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();
        }
        
@@ -1351,8 +1341,7 @@
            }
            
            // 读取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()
@@ -1550,7 +1539,7 @@
            // 清空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);
            
            // 更新车辆状态为执行中
@@ -1843,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;
        }
        
@@ -1899,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;
@@ -1907,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;
@@ -1916,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),正在运输到目标位置";
@@ -1925,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确认";
@@ -1949,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();
@@ -1972,7 +1957,7 @@
     * @return 是否发生了状态变化(从非目标状态变为目标状态)
     */
    private boolean updateStateIfNeeded(DeviceConfig deviceConfig,
                                     EnhancedS7Serializer serializer,
                                     PlcClient plcClient,
                                     Map<String, Object> currentStates,
                                     String stateField,
                                     int targetState,
@@ -1995,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);
@@ -2066,7 +2051,7 @@
     * 给MES汇报
     */
    private void reportToMes(DeviceConfig deviceConfig,
                             EnhancedS7Serializer serializer,
                             PlcClient plcClient,
                             MesTaskInfo taskInfo,
                             Map<String, Object> logicParams) {
        
@@ -2074,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()
@@ -2096,21 +2081,14 @@
    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)
@@ -2184,7 +2162,7 @@
                // 超时视为任务失败:清理任务状态并停止监控,避免继续累加等待时间
                try {
                    clearTaskStates(deviceConfig, serializer);
                    clearTaskStates(deviceConfig, plcClient);
                } catch (Exception e) {
                    log.warn("MES确认超时时清空任务状态失败: deviceId={}, error={}", deviceId, e.getMessage());
                }
@@ -2200,15 +2178,14 @@
                        .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已确认:本次交互完成(不在设备侧判断“是否还有更多玻璃”,由任务引擎统一编排)
                clearTaskStates(deviceConfig, serializer);
                // MES已确认:本次交互完成(不在设备侧判断"是否还有更多玻璃",由任务引擎统一编排)
                clearTaskStates(deviceConfig, plcClient);
                // 记录已完成的任务签名,避免MES未复位时被重复拉起
                if (taskInfo != null && taskInfo.mesSignature != null) {
@@ -2230,7 +2207,7 @@
                // 恢复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 != null && taskInfo.isOutbound) ? "出片" : "进片";
@@ -2321,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()
@@ -2346,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
@@ -2355,7 +2339,7 @@
            }
            // 清空汇报字
            payload.put("plcReport", 0);
            plcDynamicDataService.writePlcData(deviceConfig, payload, serializer);
            plcClient.writeData(payload);
        } catch (Exception e) {
            log.error("清空任务状态异常: deviceId={}", deviceConfig.getDeviceId(), e);
        }
@@ -2498,13 +2482,10 @@
    }
    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-processes/mes-plcSend/src/main/java/com/mes/interaction/workstation/scanner/handler/HorizontalScannerLogicHandler.java
@@ -7,9 +7,8 @@
import com.mes.device.vo.DevicePlcVO;
import com.mes.interaction.workstation.base.WorkstationBaseHandler;
import com.mes.interaction.workstation.config.WorkstationLogicConfig;
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 lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@@ -33,18 +32,16 @@
    private static final List<String> MES_FIELDS = Arrays.asList("mesSend", "mesGlassId", "mesWidth", "mesHeight", "workLine");
    private final PlcDynamicDataService plcDynamicDataService;
    private final GlassInfoService glassInfoService;
    private final S7SerializerProvider s7SerializerProvider;
    private final PlcClientFactory plcClientFactory;
    public HorizontalScannerLogicHandler(DevicePlcOperationService devicePlcOperationService,
                                         PlcDynamicDataService plcDynamicDataService,
                                         GlassInfoService glassInfoService,
                                         S7SerializerProvider s7SerializerProvider) {
                                         PlcClientFactory plcClientFactory) {
        super(devicePlcOperationService);
        this.plcDynamicDataService = plcDynamicDataService;
        this.glassInfoService = glassInfoService;
        this.s7SerializerProvider = s7SerializerProvider;
        this.plcClientFactory = plcClientFactory;
        this.setPlcClientFactory(plcClientFactory);
    }
    @Override
@@ -57,22 +54,22 @@
                                                    String operation,
                                                    Map<String, Object> params,
                                                    Map<String, Object> logicParams) {
        EnhancedS7Serializer serializer = s7SerializerProvider.getSerializer(deviceConfig);
        if (serializer == null) {
            return buildResult(deviceConfig, operation, false, "获取PLC序列化器失败", null);
        PlcClient plcClient = getPlcClient(deviceConfig);
        if (plcClient == null) {
            return buildResult(deviceConfig, operation, false, "获取PLC客户端失败", null);
        }
        if ("clearPlc".equalsIgnoreCase(operation) || "reset".equalsIgnoreCase(operation)) {
            return clearPlc(deviceConfig, serializer);
            return clearPlc(deviceConfig, plcClient);
        }
        WorkstationLogicConfig config = parseWorkstationConfig(logicParams);
        return executeScan(deviceConfig, config, serializer, params);
        return executeScan(deviceConfig, config, plcClient, params);
    }
    private DevicePlcVO.OperationResult executeScan(DeviceConfig deviceConfig,
                                                    WorkstationLogicConfig config,
                                                    EnhancedS7Serializer serializer,
                                                    PlcClient plcClient,
                                                    Map<String, Object> params) {
        try {
            // 从参数中获取玻璃ID(定时器每次只处理一个)
@@ -85,7 +82,7 @@
            }
            
            // 执行单次扫描(定时器会循环调用此方法)
            return executeSingleScan(deviceConfig, config, serializer, inputGlassId, params);
            return executeSingleScan(deviceConfig, config, plcClient, inputGlassId, params);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.warn("卧转立扫码等待MES数据被中断, deviceId={}", deviceConfig.getId(), e);
@@ -101,19 +98,19 @@
     */
    private DevicePlcVO.OperationResult executeSingleScan(DeviceConfig deviceConfig,
                                                          WorkstationLogicConfig config,
                                                          EnhancedS7Serializer serializer,
                                                          PlcClient plcClient,
                                                          String inputGlassId,
                                                          Map<String, Object> params) throws InterruptedException {
        // 1. 写入plcRequest=1和plcGlassId(如果提供了玻璃ID)
        triggerScanRequest(deviceConfig, serializer, inputGlassId);
        triggerScanRequest(deviceConfig, plcClient, inputGlassId);
        
        // 2. 等待MES回写mesSend=1以及玻璃信息
        Map<String, Object> mesData = waitForMesData(deviceConfig, serializer, config);
        Map<String, Object> mesData = waitForMesData(deviceConfig, plcClient, config);
        if (mesData == null || mesData.isEmpty()) {
            log.error("等待MES写入玻璃信息超时: deviceId={}, timeout={}ms", 
                    deviceConfig.getId(), config.getScanIntervalMs());
            // 超时也要清空plcRequest
            clearPlcRequestFields(deviceConfig, serializer);
            clearPlcRequestFields(deviceConfig, plcClient);
            return buildResult(deviceConfig, "scanOnce", false,
                    String.format("等待MES写入玻璃信息超时(%dms)", config.getScanIntervalMs()), null);
        }
@@ -122,7 +119,7 @@
        String glassId = parseString(mesData.get("mesGlassId"));
        if (!StringUtils.hasText(glassId)) {
            // MES未提供玻璃ID也要清空plcRequest
            clearPlcRequestFields(deviceConfig, serializer);
            clearPlcRequestFields(deviceConfig, plcClient);
            return buildResult(deviceConfig, "scanOnce", false, "MES写区未提供玻璃ID", null);
        }
        // 读取MES尺寸数据:mesWidth=宽,mesHeight=长
@@ -131,7 +128,7 @@
        Integer workLine = parseInteger(mesData.get("workLine"));
        // 4. 清空plcRequest和plcGlassId(只清除PLC字段)
        clearPlcRequestFields(deviceConfig, serializer);
        clearPlcRequestFields(deviceConfig, plcClient);
        // 5. 更新玻璃信息状态:将state从0改为1(已扫码交互)
        boolean updated = glassInfoService.updateGlassStateAfterScan(glassId, rawWidth, rawHeight, workLine);
@@ -188,13 +185,13 @@
    }
    private DevicePlcVO.OperationResult clearPlc(DeviceConfig deviceConfig,
                                                 EnhancedS7Serializer serializer) {
                                                 PlcClient plcClient) {
        try {
            // 只清空PLC操作区字段(plcRequest、plcGlassId),不清空MES写区字段
            Map<String, Object> resetFields = new HashMap<>();
            resetFields.put("plcRequest", 0);
            resetFields.put("plcGlassId", "");
            plcDynamicDataService.writePlcData(deviceConfig, resetFields, serializer);
            plcClient.writeData(resetFields);
            return buildResult(deviceConfig, "clearPlc", true, "已清空PLC操作区字段(保留MES写区字段)", null);
        } catch (Exception e) {
            log.error("卧转立扫码清空PLC失败, deviceId={}", deviceConfig.getId(), e);
@@ -205,7 +202,7 @@
    /**
     * 触发MES请求:写入plcRequest=1和plcGlassId(如果提供了玻璃ID)
     */
    private void triggerScanRequest(DeviceConfig deviceConfig, EnhancedS7Serializer serializer, String glassId) {
    private void triggerScanRequest(DeviceConfig deviceConfig, PlcClient plcClient, String glassId) {
        Map<String, Object> writeFields = new HashMap<>();
        writeFields.put("plcRequest", 1);
        
@@ -213,18 +210,18 @@
            writeFields.put("plcGlassId", glassId);
        }
        
        plcDynamicDataService.writePlcData(deviceConfig, writeFields, serializer);
        plcClient.writeData(writeFields);
    }
    
    /**
     * 清空PLC请求字段:只清除plcRequest和plcGlassId(不清除MES写区字段)
     */
    private void clearPlcRequestFields(DeviceConfig deviceConfig, EnhancedS7Serializer serializer) {
    private void clearPlcRequestFields(DeviceConfig deviceConfig, PlcClient plcClient) {
        try {
            Map<String, Object> clearFields = new HashMap<>();
            clearFields.put("plcRequest", 0);
            clearFields.put("plcGlassId", "");
            plcDynamicDataService.writePlcData(deviceConfig, clearFields, serializer);
            plcClient.writeData(clearFields);
        } catch (Exception e) {
            log.error("清空PLC请求字段失败: deviceId={}", deviceConfig.getId(), e);
            // 不清空不影响主流程,只记录错误
@@ -232,14 +229,14 @@
    }
    private Map<String, Object> waitForMesData(DeviceConfig deviceConfig,
                                               EnhancedS7Serializer serializer,
                                               PlcClient plcClient,
                                               WorkstationLogicConfig config) throws InterruptedException {
        long timeoutMs = Math.max(config.getScanIntervalMs(), 3_000);
        long deadline = System.currentTimeMillis() + timeoutMs;
        int pollInterval = Math.max(200, Math.min(config.getScanIntervalMs() / 5, 1_000));
        
        while (System.currentTimeMillis() < deadline) {
            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()) {
                Integer mesSend = parseInteger(mesData.get("mesSend"));
@@ -259,7 +256,7 @@
        
        // 超时前最后一次尝试读取
        log.warn("等待MES数据超时: deviceId={}, timeout={}ms", deviceConfig.getId(), timeoutMs);
        Map<String, Object> lastMesData = plcDynamicDataService.readPlcData(deviceConfig, MES_FIELDS, serializer);
        Map<String, Object> lastMesData = plcClient.readData(MES_FIELDS.toArray(new String[0]));
        if (lastMesData != null && !lastMesData.isEmpty()) {
            log.warn("超时前最后一次读取到的数据: deviceId={}, mesData={}", 
                    deviceConfig.getId(), lastMesData);
mes-processes/mes-plcSend/src/main/java/com/mes/interaction/workstation/transfer/handler/HorizontalTransferLogicHandler.java
@@ -9,9 +9,8 @@
import com.mes.device.vo.DevicePlcVO;
import com.mes.interaction.workstation.base.WorkstationBaseHandler;
import com.mes.interaction.workstation.config.WorkstationLogicConfig;
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 lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
@@ -32,9 +31,8 @@
@Component
public class HorizontalTransferLogicHandler extends WorkstationBaseHandler {
    private final PlcDynamicDataService plcDynamicDataService;
    private final GlassInfoService glassInfoService;
    private final S7SerializerProvider s7SerializerProvider;
    private final PlcClientFactory plcClientFactory;
    
    @Autowired(required = false)
    private DeviceGlassInfoMapper glassInfoMapper;
@@ -57,13 +55,13 @@
    @Autowired
    public HorizontalTransferLogicHandler(DevicePlcOperationService devicePlcOperationService,
                                         PlcDynamicDataService plcDynamicDataService,
                                         @Qualifier("deviceGlassInfoService") GlassInfoService glassInfoService,
                                         S7SerializerProvider s7SerializerProvider) {
                                         PlcClientFactory plcClientFactory) {
        super(devicePlcOperationService);
        this.plcDynamicDataService = plcDynamicDataService;
        this.glassInfoService = glassInfoService;
        this.s7SerializerProvider = s7SerializerProvider;
        this.plcClientFactory = plcClientFactory;
        // 设置 PlcClientFactory 到基类
        this.setPlcClientFactory(plcClientFactory);
    }
    @Override
@@ -116,10 +114,10 @@
            Map<String, Object> params) {
        
        String deviceId = deviceConfig.getDeviceId();
        EnhancedS7Serializer serializer = s7SerializerProvider.getSerializer(deviceConfig);
        if (serializer == null) {
        PlcClient plcClient = getPlcClient(deviceConfig);
        if (plcClient == null) {
            return buildResult(deviceConfig, "checkAndProcess", false, 
                    "获取PLC序列化器失败");
                    "获取PLC客户端失败");
        }
        try {
@@ -184,7 +182,7 @@
            // 6. 写入PLC(尝试从任务参数中获取卧转立编号)
            DevicePlcVO.OperationResult writeResult = writeBatchToPlc(
                    deviceConfig, batch, serializer, logicParams, params);
                    deviceConfig, batch, plcClient, logicParams, params);
            
            if (!Boolean.TRUE.equals(writeResult.getSuccess())) {
                return writeResult;
@@ -401,7 +399,7 @@
    private DevicePlcVO.OperationResult writeBatchToPlc(
            DeviceConfig deviceConfig,
            List<GlassInfo> batch,
            EnhancedS7Serializer serializer,
            PlcClient plcClient,
            Map<String, Object> logicParams,
            Map<String, Object> params) {
        
@@ -448,7 +446,7 @@
        payload.put("plcRequest", 1);
        
        try {
            plcDynamicDataService.writePlcData(deviceConfig, payload, serializer);
            plcClient.writeData(payload);
            log.info("批次已写入PLC: deviceId={}, glassCount={}, inPosition={}", 
                    deviceConfig.getId(), count, inPosition);
            return buildResult(deviceConfig, "writeBatchToPlc", true, 
@@ -540,9 +538,9 @@
     */
    private DevicePlcVO.OperationResult handleClearPlc(DeviceConfig deviceConfig) {
        try {
            EnhancedS7Serializer serializer = s7SerializerProvider.getSerializer(deviceConfig);
            if (serializer == null) {
                return buildResult(deviceConfig, "clearPlc", false, "获取PLC序列化器失败");
            PlcClient plcClient = getPlcClient(deviceConfig);
            if (plcClient == null) {
                return buildResult(deviceConfig, "clearPlc", false, "获取PLC客户端失败");
            }
            
            Map<String, Object> payload = new HashMap<>();
@@ -554,7 +552,7 @@
            payload.put("plcRequest", 0);
            payload.put("inPosition", 0);
            
            plcDynamicDataService.writePlcData(deviceConfig, payload, serializer);
            plcClient.writeData(payload);
            log.info("卧转立主体清空PLC字段完成: deviceId={}", deviceConfig.getId());
            return buildResult(deviceConfig, "clearPlc", true, "已清空卧转立主体PLC字段");
        } catch (Exception e) {
mes-processes/mes-plcSend/src/main/java/com/mes/plc/client/impl/ModbusPlcClient.java
@@ -1,5 +1,6 @@
package com.mes.plc.client.impl;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.mes.connect.modbus.ModbusTcpClient;
import com.mes.device.entity.DeviceConfig;
import com.mes.plc.client.PlcClient;
@@ -30,6 +31,9 @@
    // 从站地址
    private final int unitId;
    
    // 设备配置
    private final DeviceConfig device;
    // Modbus客户端实例
    private ModbusTcpClient modbusClient;
    
@@ -39,12 +43,19 @@
    // 超时时间(毫秒)
    private int timeout = 5000;
    
    // ObjectMapper用于JSON解析
    private final ObjectMapper objectMapper = new ObjectMapper();
    // 地址映射缓存:字段名 -> Modbus地址
    private Map<String, String> addressMappingCache;
    /**
     * 构造函数
     *
     * @param device 设备配置
     */
    public ModbusPlcClient(DeviceConfig device) {
        this.device = device;
        this.plcIp = device.getPlcIp();
        this.plcPort = device.getPlcPort() != null ? device.getPlcPort() : 502;
        
@@ -74,7 +85,7 @@
     */
    private Map<String, Object> parseExtraParams(String extraParamsJson) {
        if (extraParamsJson == null || extraParamsJson.isEmpty()) {
            return null;
            return new HashMap<>();
        }
        
        try {
@@ -150,6 +161,10 @@
            return Collections.emptyMap();
        }
        
        if (fields == null || fields.length == 0) {
            return readAllData();
        }
        try {
            // TODO: 实现Modbus读取指定字段数据
            // 这里暂时返回空Map,后续完善
@@ -161,12 +176,17 @@
            return Collections.emptyMap();
        }
    }
    @Override
    public boolean writeData(Map<String, Object> data) {
        if (!isConnected() && !connect()) {
            log.error("Modbus PLC未连接,无法写入数据: {}:{}", this.plcIp, this.plcPort);
            return false;
        }
        if (data == null || data.isEmpty()) {
            log.warn("写入数据为空,跳过操作: deviceId={}", device.getId());
            return true;
        }
        
        try {
@@ -180,7 +200,7 @@
            return false;
        }
    }
    @Override
    public boolean isConnected() {
        try {
@@ -208,6 +228,5 @@
    @Override
    public void setTimeout(int timeout) {
        this.timeout = timeout;
        // ModbusTcpClient不支持直接设置超时,这里仅记录超时时间
    }
}
mes-processes/mes-plcSend/src/main/java/com/mes/service/PlcTestWriteService.java
@@ -7,7 +7,8 @@
import com.mes.device.entity.DeviceConfig;
import com.mes.device.service.DeviceConfigService;
import com.mes.device.util.ConfigJsonHelper;
import com.mes.service.PlcDynamicDataService;
import com.mes.plc.client.PlcClient;
import com.mes.plc.factory.PlcClientFactory;
import com.mes.s7.enhanced.EnhancedS7Serializer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@@ -21,11 +22,9 @@
/**
 * PLC测试写入服务
 *
 *
 * 基于DeviceConfig的新API,用于模拟PLC行为进行测试
 *
 * 推荐使用:DevicePlcOperationService(生产环境)
 *
 *
 * @author huang
 * @date 2025/10/29
 */
@@ -35,26 +34,26 @@
    @Resource
    private DeviceConfigService deviceConfigService;
    @Resource
    private PlcDynamicDataService plcDynamicDataService;
    @Resource
    private com.mes.plc.factory.PlcClientFactory plcClientFactory;
    private PlcClientFactory plcClientFactory;
    private final ObjectMapper objectMapper = new ObjectMapper();
    private static final TypeReference<Map<String, Object>> MAP_TYPE = new TypeReference<Map<String, Object>>() {};
    private static final int ON = 1;
    private static final int OFF = 0;
    // 缓存不同设备的S7Serializer实例(保持兼容,逐步迁移)
    private final ConcurrentMap<String, EnhancedS7Serializer> serializerCache = new ConcurrentHashMap<>();
    /**
     * 根据设备ID模拟PLC发送请求字
     *
     *
     * @param deviceId 设备ID
     * @return 是否成功
     */
@@ -66,7 +65,7 @@
        }
        try {
            // 尝试使用新的PLC客户端工厂
            com.mes.plc.client.PlcClient plcClient = plcClientFactory.getClient(device);
            PlcClient plcClient = plcClientFactory.getClient(device);
            if (plcClient != null) {
                // 使用新的PLC客户端读取数据
                Map<String, Object> currentData = plcClient.readAllData();
@@ -77,17 +76,17 @@
                        log.info("当前PLC联机模式为0,停止联机: deviceId={}", deviceId);
                        return false;
                    }
                    // 检查汇报字,如果为1则重置为0
                    Integer plcReport = parseInteger(currentData.get("plcReport"));
                    if (plcReport != null && plcReport == ON) {
                        log.info("当前上片PLC汇报字为1,重置为0: deviceId={}", deviceId);
                        currentData.put("plcReport", OFF);
                    }
                    // 设置请求字为1
                    currentData.put("plcRequest", ON);
                    // 使用新的PLC客户端写入数据
                    boolean success = plcClient.writeData(currentData);
                    if (success) {
@@ -96,7 +95,7 @@
                    }
                }
            }
            // 如果新客户端失败,回退到旧实现(保持兼容)
            log.warn("新PLC客户端失败,回退到旧实现: deviceId={}", deviceId);
            return simulatePlcRequestByDeviceLegacy(device);
@@ -105,10 +104,10 @@
            return false;
        }
    }
    /**
     * 旧版实现:根据设备ID模拟PLC发送请求字
     *
     *
     * @param device 设备配置
     * @return 是否成功
     */
@@ -119,14 +118,14 @@
                log.error("获取S7Serializer失败: deviceId={}", device.getId());
                return false;
            }
            // 使用PlcDynamicDataService读取数据(支持addressMapping)
            Map<String, Object> currentData = plcDynamicDataService.readAllPlcData(device, s7Serializer);
            if (currentData == null || currentData.isEmpty()) {
                log.error("读取PLC数据失败,返回空: deviceId={}", device.getId());
                return false;
            }
            // 检查联机状态
            Object onlineStateObj = currentData.get("onlineState");
            Integer onlineState = null;
@@ -144,12 +143,12 @@
                    }
                }
            }
            if (onlineState != null && onlineState == OFF) {
                log.info("当前PLC联机模式为0,停止联机: deviceId={}", device.getId());
                return false;
            }
            // 检查汇报字,如果为1则重置为0
            Object plcReportObj = currentData.get("plcReport");
            Integer plcReport = null;
@@ -167,18 +166,18 @@
                    }
                }
            }
            if (plcReport != null && plcReport == ON) {
                log.info("当前上片PLC汇报字为1,重置为0: deviceId={}", device.getId());
                currentData.put("plcReport", OFF);
            }
            // 设置请求字为1
            currentData.put("plcRequest", ON);
            // 使用PlcDynamicDataService写入数据
            plcDynamicDataService.writePlcData(device, currentData, s7Serializer);
            log.info("模拟PLC发送请求字成功(旧版):plcRequest=1, deviceId={}", device.getId());
            return true;
        } catch (Exception e) {
@@ -186,10 +185,10 @@
            return false;
        }
    }
    /**
     * 根据设备ID模拟PLC任务完成汇报
     *
     *
     * @param deviceId 设备ID
     * @return 是否成功
     */
@@ -200,38 +199,75 @@
            return false;
        }
        try {
            EnhancedS7Serializer s7Serializer = getSerializerForDevice(device);
            if (s7Serializer == null) {
                log.error("获取S7Serializer失败: deviceId={}", deviceId);
                return false;
            // 尝试使用新的PLC客户端工厂
            PlcClient plcClient = plcClientFactory.getClient(device);
            if (plcClient != null) {
                // 使用新的PLC客户端读取数据
                Map<String, Object> currentData = plcClient.readAllData();
                if (currentData == null) {
                    currentData = new HashMap<>();
                }
                // 设置汇报字为1,请求字清0
                currentData.put("plcReport", ON);
                currentData.put("plcRequest", OFF);
                // 使用新的PLC客户端写入数据
                boolean success = plcClient.writeData(currentData);
                if (success) {
                    log.info("模拟PLC任务完成汇报:plcReport=1, deviceId={}", deviceId);
                    return true;
                }
            }
            // 使用PlcDynamicDataService读取数据
            Map<String, Object> currentData = plcDynamicDataService.readAllPlcData(device, s7Serializer);
            if (currentData == null || currentData.isEmpty()) {
                log.error("读取PLC数据失败,返回空: deviceId={}", deviceId);
                return false;
            }
            // 设置汇报字为1,请求字清0
            currentData.put("plcReport", ON);
            currentData.put("plcRequest", OFF);
            currentData.put("mesGlassCount", 10);
            // 使用PlcDynamicDataService写入数据
            plcDynamicDataService.writePlcData(device, currentData, s7Serializer);
            log.info("模拟PLC任务完成汇报:plcReport=1, mesGlassCount=10, deviceId={}", deviceId);
            return true;
            // 如果新客户端失败,回退到旧实现(保持兼容)
            log.warn("新PLC客户端失败,回退到旧实现: deviceId={}", deviceId);
            return simulatePlcReportByDeviceLegacy(device);
        } catch (Exception e) {
            log.error("根据设备模拟PLC汇报失败: deviceId={}", deviceId, e);
            return false;
        }
    }
    /**
     * 旧版实现:根据设备模拟PLC任务完成汇报
     *
     * @param device 设备配置
     * @return 是否成功
     */
    private boolean simulatePlcReportByDeviceLegacy(DeviceConfig device) {
        try {
            EnhancedS7Serializer s7Serializer = getSerializerForDevice(device);
            if (s7Serializer == null) {
                log.error("获取S7Serializer失败: deviceId={}", device.getId());
                return false;
            }
            // 使用PlcDynamicDataService读取数据
            Map<String, Object> currentData = plcDynamicDataService.readAllPlcData(device, s7Serializer);
            if (currentData == null || currentData.isEmpty()) {
                log.error("读取PLC数据失败,返回空: deviceId={}", device.getId());
                return false;
            }
            // 设置汇报字为1,请求字清0
            currentData.put("plcReport", ON);
            currentData.put("plcRequest", OFF);
            // 使用PlcDynamicDataService写入数据
            plcDynamicDataService.writePlcData(device, currentData, s7Serializer);
            log.info("模拟PLC任务完成汇报(旧版):plcReport=1, deviceId={}", device.getId());
            return true;
        } catch (Exception e) {
            log.error("根据设备模拟PLC汇报失败(旧版): deviceId={}", device.getId(), e);
            return false;
        }
    }
    /**
     * 根据设备ID重置PLC所有状态
     *
     *
     * @param deviceId 设备ID
     * @return 是否成功
     */
@@ -242,12 +278,49 @@
            return false;
        }
        try {
            // 尝试使用新的PLC客户端工厂
            PlcClient plcClient = plcClientFactory.getClient(device);
            if (plcClient != null) {
                // 构建重置数据
                Map<String, Object> resetData = new HashMap<>();
                resetData.put("plcRequest", OFF);
                resetData.put("plcReport", OFF);
                resetData.put("mesSend", OFF);
                resetData.put("mesConfirm", OFF);
                resetData.put("onlineState", ON);
                resetData.put("alarmInfo", OFF);
                // 使用新的PLC客户端写入数据
                boolean success = plcClient.writeData(resetData);
                if (success) {
                    log.info("PLC状态已重置, deviceId={}", deviceId);
                    return true;
                }
            }
            // 如果新客户端失败,回退到旧实现(保持兼容)
            log.warn("新PLC客户端失败,回退到旧实现: deviceId={}", deviceId);
            return resetPlcByDeviceLegacy(device);
        } catch (Exception e) {
            log.error("根据设备重置PLC状态失败: deviceId={}", deviceId, e);
            return false;
        }
    }
    /**
     * 旧版实现:根据设备重置PLC所有状态
     *
     * @param device 设备配置
     * @return 是否成功
     */
    private boolean resetPlcByDeviceLegacy(DeviceConfig device) {
        try {
            EnhancedS7Serializer s7Serializer = getSerializerForDevice(device);
            if (s7Serializer == null) {
                log.error("获取S7Serializer失败: deviceId={}", deviceId);
                log.error("获取S7Serializer失败: deviceId={}", device.getId());
                return false;
            }
            // 构建重置数据
            Map<String, Object> resetData = new HashMap<>();
            resetData.put("plcRequest", OFF);
@@ -255,23 +328,22 @@
            resetData.put("mesSend", OFF);
            resetData.put("mesConfirm", OFF);
            resetData.put("onlineState", ON);
            resetData.put("mesGlassCount", 0);
            resetData.put("alarmInfo", OFF);
            // 使用PlcDynamicDataService写入数据
            plcDynamicDataService.writePlcData(device, resetData, s7Serializer);
            log.info("PLC状态已重置, deviceId={}", deviceId);
            log.info("PLC状态已重置(旧版): deviceId={}", device.getId());
            return true;
        } catch (Exception e) {
            log.error("根据设备重置PLC状态失败: deviceId={}", deviceId, e);
            log.error("根据设备重置PLC状态失败(旧版): deviceId={}", device.getId(), e);
            return false;
        }
    }
    /**
     * 根据设备ID读取PLC当前状态
     *
     *
     * @param deviceId 设备ID
     * @return PLC状态数据
     */
@@ -282,6 +354,18 @@
            return null;
        }
        try {
            // 尝试使用新的PLC客户端工厂
            PlcClient plcClient = plcClientFactory.getClient(device);
            if (plcClient != null) {
                // 使用新的PLC客户端读取所有数据
                Map<String, Object> data = plcClient.readAllData();
                log.info("读取PLC状态成功: deviceId={}, dataSize={}", deviceId, data != null ? data.size() : 0);
                return data;
            }
            // 如果新客户端失败,回退到旧实现(保持兼容)
            log.warn("新PLC客户端失败,回退到旧实现: deviceId={}", deviceId);
            EnhancedS7Serializer s7Serializer = getSerializerForDevice(device);
            if (s7Serializer == null) {
                log.error("获取S7Serializer失败: deviceId={}", deviceId);
@@ -289,6 +373,7 @@
            }
            // 使用PlcDynamicDataService读取所有数据(支持addressMapping)
            Map<String, Object> data = plcDynamicDataService.readAllPlcData(device, s7Serializer);
            log.info("读取PLC状态成功(旧版): deviceId={}, dataSize={}", deviceId, data != null ? data.size() : 0);
            return data;
        } catch (Exception e) {
            log.error("读取设备PLC状态失败: deviceId={}", deviceId, e);
@@ -311,6 +396,20 @@
        }
        
        try {
            // 尝试使用新的PLC客户端工厂
            PlcClient plcClient = plcClientFactory.getClient(device);
            if (plcClient != null) {
                // 使用新的PLC客户端写入数据
                boolean success = plcClient.writeData(fieldValues);
                if (success) {
                    log.info("写入PLC字段成功: deviceId={}, fields={}", deviceId, fieldValues.keySet());
                    return true;
                }
            }
            // 如果新客户端失败,回退到旧实现(保持兼容)
            log.warn("新PLC客户端失败,回退到旧实现: deviceId={}", deviceId);
            // 获取对应的S7Serializer(使用设备配置)
            EnhancedS7Serializer s7Serializer = getSerializerForDevice(device);
            if (s7Serializer == null) {
@@ -321,7 +420,7 @@
            // 使用动态数据服务写入字段(基于DeviceConfig)
            plcDynamicDataService.writePlcData(device, fieldValues, s7Serializer);
            
            log.info("写入PLC字段成功: deviceId={}, fields={}", deviceId, fieldValues.keySet());
            log.info("写入PLC字段成功(旧版): deviceId={}, fields={}", deviceId, fieldValues.keySet());
            return true;
        } catch (Exception e) {
            log.error("写入PLC字段失败: deviceId={}", deviceId, e);