From dad0263459b30dbfa75f06dff062a0c85183517b Mon Sep 17 00:00:00 2001
From: huang <1532065656@qq.com>
Date: 星期一, 01 十二月 2025 17:01:51 +0800
Subject: [PATCH] 添加卧转立扫码设备交互逻辑,任务流转

---
 mes-processes/mes-plcSend/src/main/java/com/mes/device/service/GlassInfoService.java                                       |   10 
 mes-processes/mes-plcSend/src/main/java/com/mes/interaction/BaseDeviceLogicHandler.java                                    |    9 
 mes-processes/mes-plcSend/src/main/java/com/mes/task/entity/TaskStepDetail.java                                            |    4 
 mes-processes/mes-plcSend/src/main/java/com/mes/device/service/impl/DeviceCoordinationServiceImpl.java                     |    8 
 mes-processes/mes-plcSend/src/main/java/com/mes/interaction/vehicle/handler/LoadVehicleLogicHandler.java                   |   82 +
 mes-processes/mes-plcSend/src/main/java/com/mes/task/dto/TaskParameters.java                                               |   16 
 mes-web/src/views/plcTest/components/DeviceGroup/GroupTopology.vue                                                         |   20 
 /dev/null                                                                                                                  |  241 ------
 mes-processes/mes-plcSend/src/main/java/com/mes/task/service/impl/MultiDeviceTaskServiceImpl.java                          |   55 +
 mes-web/src/views/device/components/DeviceLogicConfig/LoadVehicleConfig.vue                                                |  114 +-
 mes-processes/mes-plcSend/src/main/java/com/mes/interaction/DeviceLogicHandlerFactory.java                                 |   32 
 mes-processes/mes-plcSend/src/main/java/com/mes/interaction/workstation/scanner/handler/HorizontalScannerLogicHandler.java |  256 +++++-
 mes-processes/mes-plcSend/src/main/java/com/mes/task/service/TaskExecutionEngine.java                                      | 1232 ++++++++++++++++++++++++++++++++-
 mes-web/src/views/device/DeviceEditDialog.vue                                                                              |   24 
 mes-processes/mes-plcSend/src/main/java/com/mes/device/entity/DeviceConfig.java                                            |    7 
 mes-processes/mes-plcSend/src/main/java/com/mes/device/service/impl/GlassInfoServiceImpl.java                              |   36 
 mes-processes/mes-plcSend/src/main/java/com/mes/device/mapper/DeviceGroupRelationMapper.java                               |    2 
 mes-processes/mes-plcSend/src/main/java/com/mes/device/vo/DeviceGroupVO.java                                               |    1 
 mes-processes/mes-plcSend/src/main/java/com/mes/task/service/impl/TaskStatusNotificationServiceImpl.java                   |   14 
 mes-web/src/views/plcTest/components/MultiDeviceTest/TaskOrchestration.vue                                                 |   57 +
 20 files changed, 1,779 insertions(+), 441 deletions(-)

diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/device/entity/DeviceConfig.java b/mes-processes/mes-plcSend/src/main/java/com/mes/device/entity/DeviceConfig.java
index e1e45ad..12734de 100644
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/device/entity/DeviceConfig.java
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/device/entity/DeviceConfig.java
@@ -35,7 +35,7 @@
     @TableField("device_code")
     private String deviceCode;
 
-    @ApiModelProperty(value = "璁惧绫诲瀷", example = "澶ц溅璁惧/澶х悊鐗囩/鍗у紡缂撳瓨")
+    @ApiModelProperty(value = "璁惧绫诲瀷", example = "澶ц溅璁惧/澶х悊鐗囩")
     @TableField("device_type")
     private String deviceType;
 
@@ -110,9 +110,8 @@
     public static final class DeviceType {
         public static final String LOAD_VEHICLE = "澶ц溅璁惧";      // 澶ц溅璁惧
         public static final String LARGE_GLASS = "澶х悊鐗囩";      // 澶х悊鐗囩
-        public static final String GLASS_STORAGE = "鍗у紡缂撳瓨";   // 鍗у紡缂撳瓨
-        public static final String WORKSTATION_SCANNER = "鍗ц浆绔嬫壂鐮�"; // 鍗ц浆绔嬫壂鐮佽澶�
-        public static final String WORKSTATION_TRANSFER = "鍗ц浆绔�";    // 鍗ц浆绔嬭澶�
+        public static final String WORKSTATION_SCANNER = "鍗ц浆绔嬫壂鐮佽澶�"; // 鍗ц浆绔嬫壂鐮佽澶�
+        public static final String WORKSTATION_TRANSFER = "鍗ц浆绔嬭澶�";    // 鍗ц浆绔嬭澶�
     }
 
     // PLC绫诲瀷甯搁噺
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/device/mapper/DeviceGroupRelationMapper.java b/mes-processes/mes-plcSend/src/main/java/com/mes/device/mapper/DeviceGroupRelationMapper.java
index 48a403a..0da6396 100644
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/device/mapper/DeviceGroupRelationMapper.java
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/device/mapper/DeviceGroupRelationMapper.java
@@ -58,7 +58,7 @@
      */
     @Select("SELECT d.id, d.device_name as deviceName, d.device_code as deviceCode, " +
             "d.device_type as deviceType, d.plc_ip as plcIp, dgr.role, d.status, " +
-            "ds.last_heartbeat as lastHeartbeat, " +
+            "ds.last_heartbeat as lastHeartbeat, d.enabled, " +
             "CASE WHEN ds.status = 'ONLINE' THEN TRUE ELSE FALSE END as isOnline " +
             "FROM device_config d " +
             "INNER JOIN device_group_relation dgr ON d.id = dgr.device_id " +
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/device/service/GlassInfoService.java b/mes-processes/mes-plcSend/src/main/java/com/mes/device/service/GlassInfoService.java
index f022bf9..827d87c 100644
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/device/service/GlassInfoService.java
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/device/service/GlassInfoService.java
@@ -60,5 +60,15 @@
      * @return 鏄惁鎴愬姛
      */
     boolean batchSaveOrUpdateGlassInfo(List<GlassInfo> glassInfos);
+
+    /**
+     * 鏌ヨ鏈�杩戞壂鐮佺殑鐜荤拑ID鍒楄〃
+     * 
+     * @param minutesAgo 鏌ヨ鏈�杩戝灏戝垎閽熷唴鐨勮褰曪紝榛樿2鍒嗛挓
+     * @param maxCount 鏈�澶ц繑鍥炴暟閲忥紝榛樿20
+     * @param workLine 宸ヤ綔绾垮彿锛堝彲閫夛紝鐢ㄤ簬杩囨护锛�
+     * @return 鐜荤拑ID鍒楄〃
+     */
+    List<String> getRecentScannedGlassIds(Integer minutesAgo, Integer maxCount, String workLine);
 }
 
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/device/service/impl/DeviceCoordinationServiceImpl.java b/mes-processes/mes-plcSend/src/main/java/com/mes/device/service/impl/DeviceCoordinationServiceImpl.java
index 4414ee3..9fb28a8 100644
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/device/service/impl/DeviceCoordinationServiceImpl.java
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/device/service/impl/DeviceCoordinationServiceImpl.java
@@ -152,14 +152,8 @@
             if (CollectionUtils.isEmpty(loadedGlassIds)) {
                 missingDependencies.add("澶ц溅璁惧鏈畬鎴愶紝缂哄皯鐜荤拑ID鍒楄〃");
             }
-        } else if (DeviceConfig.DeviceType.GLASS_STORAGE.equals(deviceType)) {
-            // 鐜荤拑瀛樺偍璁惧闇�瑕佸ぇ鐞嗙墖璁惧鍏堝畬鎴愶紙浼樺厛锛夛紝鎴栧ぇ杞﹁澶囧畬鎴�
-            List<String> processedGlassIds = context.getSafeProcessedGlassIds();
-            List<String> loadedGlassIds = context.getSafeLoadedGlassIds();
-            if (CollectionUtils.isEmpty(processedGlassIds) && CollectionUtils.isEmpty(loadedGlassIds)) {
-                missingDependencies.add("鍓嶇疆璁惧鏈畬鎴愶紝缂哄皯鐜荤拑ID鍒楄〃");
-            }
         }
+        // 鍏朵粬璁惧绫诲瀷鏆備笉闇�瑕佷緷璧栨鏌�
 
         // 妫�鏌ヨ澶囬厤缃腑鐨勪緷璧栧叧绯伙紙浠巈xtraParams涓鍙栵級
         Map<String, Object> deviceDependencies = getDeviceDependencies(device);
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/device/service/impl/GlassInfoServiceImpl.java b/mes-processes/mes-plcSend/src/main/java/com/mes/device/service/impl/GlassInfoServiceImpl.java
index 94ed660..2e81b91 100644
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/device/service/impl/GlassInfoServiceImpl.java
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/device/service/impl/GlassInfoServiceImpl.java
@@ -9,6 +9,7 @@
 import org.springframework.stereotype.Service;
 
 import java.util.Collections;
+import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -124,5 +125,40 @@
             return false;
         }
     }
+
+    @Override
+    public List<String> getRecentScannedGlassIds(Integer minutesAgo, Integer maxCount, String workLine) {
+        try {
+            // 榛樿鏌ヨ鏈�杩�2鍒嗛挓鍐呯殑璁板綍锛屾渶澶氳繑鍥�20鏉�
+            int minutes = minutesAgo != null && minutesAgo > 0 ? minutesAgo : 2;
+            int limit = maxCount != null && maxCount > 0 ? maxCount : 20;
+            
+            Date timeThreshold = new Date(System.currentTimeMillis() - minutes * 60 * 1000L);
+            
+            LambdaQueryWrapper<GlassInfo> wrapper = new LambdaQueryWrapper<>();
+            wrapper.eq(GlassInfo::getStatus, GlassInfo.Status.ACTIVE)
+                   .ge(GlassInfo::getCreatedTime, timeThreshold)
+                   .orderByDesc(GlassInfo::getCreatedTime)
+                   .last("LIMIT " + limit);
+            
+            // 濡傛灉鎸囧畾浜唚orkLine锛屽垯杩囨护description
+            if (workLine != null && !workLine.trim().isEmpty()) {
+                wrapper.like(GlassInfo::getDescription, "workLine=" + workLine);
+            }
+            
+            List<GlassInfo> recentGlasses = baseMapper.selectList(wrapper);
+            
+            // 鎻愬彇鐜荤拑ID鍒楄〃
+            return recentGlasses.stream()
+                    .map(GlassInfo::getGlassId)
+                    .filter(id -> id != null && !id.trim().isEmpty())
+                    .collect(Collectors.toList());
+            
+        } catch (Exception e) {
+            log.error("鏌ヨ鏈�杩戞壂鐮佺殑鐜荤拑ID澶辫触, minutesAgo={}, maxCount={}, workLine={}", 
+                    minutesAgo, maxCount, workLine, e);
+            return Collections.emptyList();
+        }
+    }
 }
 
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/device/vo/DeviceGroupVO.java b/mes-processes/mes-plcSend/src/main/java/com/mes/device/vo/DeviceGroupVO.java
index c8bcfda..1ec61fd 100644
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/device/vo/DeviceGroupVO.java
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/device/vo/DeviceGroupVO.java
@@ -31,6 +31,7 @@
         private String status;
         private Date lastHeartbeat;
         private Boolean isOnline;
+        private Boolean enabled;
     }
 
     /**
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/BaseDeviceLogicHandler.java b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/BaseDeviceLogicHandler.java
index 92b9093..d07dc8f 100644
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/BaseDeviceLogicHandler.java
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/BaseDeviceLogicHandler.java
@@ -28,6 +28,15 @@
     @Override
     public DevicePlcVO.OperationResult execute(DeviceConfig deviceConfig, String operation, Map<String, Object> params) {
         try {
+            // 璁板綍鍙傛暟淇℃伅锛堢敤浜庤皟璇曪級
+            if (params != null) {
+                log.debug("BaseDeviceLogicHandler.execute鎺ユ敹鍙傛暟: deviceId={}, operation={}, paramsKeys={}, params={}", 
+                        deviceConfig.getId(), operation, params.keySet(), params);
+            } else {
+                log.warn("BaseDeviceLogicHandler.execute鎺ユ敹鍙傛暟涓簄ull: deviceId={}, operation={}", 
+                        deviceConfig.getId(), operation);
+            }
+            
             // 楠岃瘉璁惧閰嶇疆
             String validationError = validateLogicParams(deviceConfig);
             if (validationError != null) {
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/DeviceLogicHandlerFactory.java b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/DeviceLogicHandlerFactory.java
index f5eb175..4f2bc92 100644
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/DeviceLogicHandlerFactory.java
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/DeviceLogicHandlerFactory.java
@@ -3,6 +3,7 @@
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
 
 import javax.annotation.PostConstruct;
 import java.util.HashMap;
@@ -33,8 +34,12 @@
         if (handlers != null) {
             for (DeviceLogicHandler handler : handlers) {
                 String deviceType = handler.getDeviceType();
-                if (deviceType != null && !deviceType.isEmpty()) {
-                    handlerMap.put(deviceType, handler);
+                if (StringUtils.hasText(deviceType)) {
+                    String normalized = normalizeDeviceType(deviceType);
+                    handlerMap.put(normalized, handler);
+                    if (!normalized.equals(deviceType)) {
+                        handlerMap.put(deviceType, handler);
+                    }
                     log.info("娉ㄥ唽璁惧閫昏緫澶勭悊鍣�: {} -> {}", deviceType, handler.getClass().getSimpleName());
                 }
             }
@@ -49,10 +54,18 @@
      * @return 璁惧閫昏緫澶勭悊鍣紝濡傛灉鏈壘鍒拌繑鍥瀗ull
      */
     public DeviceLogicHandler getHandler(String deviceType) {
-        if (deviceType == null || deviceType.isEmpty()) {
+        if (!StringUtils.hasText(deviceType)) {
             return null;
         }
-        return handlerMap.get(deviceType);
+        DeviceLogicHandler handler = handlerMap.get(deviceType);
+        if (handler != null) {
+            return handler;
+        }
+        String normalized = normalizeDeviceType(deviceType);
+        if (!normalized.equals(deviceType)) {
+            handler = handlerMap.get(normalized);
+        }
+        return handler;
     }
 
     /**
@@ -73,5 +86,16 @@
     public java.util.Set<String> getSupportedDeviceTypes() {
         return handlerMap.keySet();
     }
+
+    private String normalizeDeviceType(String deviceType) {
+        if (!StringUtils.hasText(deviceType)) {
+            return deviceType;
+        }
+        String trimmed = deviceType.trim();
+        if (trimmed.endsWith("璁惧")) {
+            return trimmed.substring(0, trimmed.length() - 2);
+        }
+        return trimmed;
+    }
 }
 
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/flow/GlassStorageInteraction.java b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/flow/GlassStorageInteraction.java
deleted file mode 100644
index 257a3fa..0000000
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/flow/GlassStorageInteraction.java
+++ /dev/null
@@ -1,80 +0,0 @@
-package com.mes.interaction.flow;
-
-import com.mes.device.entity.DeviceConfig;
-import com.mes.interaction.DeviceInteraction;
-import com.mes.interaction.base.InteractionContext;
-import com.mes.interaction.base.InteractionResult;
-import org.springframework.stereotype.Component;
-import org.springframework.util.CollectionUtils;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * 鐜荤拑瀛樺偍浜や簰瀹炵幇
- */
-@Component
-public class GlassStorageInteraction implements DeviceInteraction {
-
-    @Override
-    public String getDeviceType() {
-        return DeviceConfig.DeviceType.GLASS_STORAGE;
-    }
-
-    @Override
-    public InteractionResult execute(InteractionContext context) {
-        try {
-            // 鍓嶇疆鏉′欢楠岃瘉
-            if (context.getCurrentDevice() == null) {
-                return InteractionResult.fail("璁惧閰嶇疆涓嶅瓨鍦�");
-            }
-
-            // 浼樺厛浣跨敤澶勭悊鍚庣殑鐜荤拑ID锛屽鏋滄病鏈夊垯浣跨敤澶ц溅璁惧鐨勭幓鐠僆D
-            List<String> processed = context.getProcessedGlassIds();
-            if (CollectionUtils.isEmpty(processed)) {
-                processed = context.getLoadedGlassIds();
-                if (CollectionUtils.isEmpty(processed)) {
-                    // 灏濊瘯浠庡叡浜暟鎹幏鍙�
-                    Object processedGlasses = context.getSharedData().get("processedGlasses");
-                    if (processedGlasses instanceof List) {
-                        @SuppressWarnings("unchecked")
-                        List<String> list = (List<String>) processedGlasses;
-                        processed = list;
-                    }
-                }
-            }
-
-            if (CollectionUtils.isEmpty(processed)) {
-                return InteractionResult.waitResult("娌℃湁鍙瓨鍌ㄧ殑鐜荤拑", null);
-            }
-
-            // 楠岃瘉鐜荤拑ID
-            for (String glassId : processed) {
-                if (glassId == null || glassId.trim().isEmpty()) {
-                    return InteractionResult.fail("鐜荤拑ID涓嶈兘涓虹┖");
-                }
-            }
-
-            // 鎵ц瀛樺偍鎿嶄綔
-            context.getSharedData().put("storedGlasses", processed);
-            context.getSharedData().put("storageTime", System.currentTimeMillis());
-
-            // 鍚庣疆鏉′欢妫�鏌�
-            Object stored = context.getSharedData().get("storedGlasses");
-            if (stored == null) {
-                return InteractionResult.fail("鐜荤拑瀛樺偍澶辫触锛氬瓨鍌ㄦ暟鎹负绌�");
-            }
-
-            Map<String, Object> data = new HashMap<>();
-            data.put("storedCount", processed.size());
-            data.put("storedGlasses", processed);
-            data.put("deviceId", context.getCurrentDevice().getId());
-            data.put("deviceCode", context.getCurrentDevice().getDeviceCode());
-            return InteractionResult.success(data);
-        } catch (Exception e) {
-            return InteractionResult.fail("鐜荤拑瀛樺偍浜や簰鎵ц寮傚父: " + e.getMessage());
-        }
-    }
-}
-
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/impl/GlassStorageLogicHandler.java b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/impl/GlassStorageLogicHandler.java
deleted file mode 100644
index 6776cf5..0000000
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/impl/GlassStorageLogicHandler.java
+++ /dev/null
@@ -1,241 +0,0 @@
-package com.mes.interaction.impl;
-
-import com.mes.device.entity.DeviceConfig;
-import com.mes.interaction.BaseDeviceLogicHandler;
-import com.mes.device.service.DevicePlcOperationService;
-import com.mes.device.vo.DevicePlcVO;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Component;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * 鐜荤拑瀛樺偍璁惧閫昏緫澶勭悊鍣�
- * 
- * @author mes
- * @since 2025-01-XX
- */
-@Slf4j
-@Component
-public class GlassStorageLogicHandler extends BaseDeviceLogicHandler {
-
-    public GlassStorageLogicHandler(DevicePlcOperationService devicePlcOperationService) {
-        super(devicePlcOperationService);
-    }
-
-    @Override
-    public String getDeviceType() {
-        return DeviceConfig.DeviceType.GLASS_STORAGE;
-    }
-
-    @Override
-    protected DevicePlcVO.OperationResult doExecute(
-            DeviceConfig deviceConfig,
-            String operation,
-            Map<String, Object> params,
-            Map<String, Object> logicParams) {
-
-        log.info("鎵ц鐜荤拑瀛樺偍璁惧鎿嶄綔: deviceId={}, operation={}", deviceConfig.getId(), operation);
-
-        switch (operation) {
-            case "storeGlass":
-                return handleStoreGlass(deviceConfig, params, logicParams);
-            case "retrieveGlass":
-                return handleRetrieveGlass(deviceConfig, params, logicParams);
-            case "triggerRequest":
-                return handleTriggerRequest(deviceConfig, params, logicParams);
-            case "triggerReport":
-                return handleTriggerReport(deviceConfig, params, logicParams);
-            case "reset":
-                return handleReset(deviceConfig, params, logicParams);
-            default:
-                log.warn("涓嶆敮鎸佺殑鎿嶄綔绫诲瀷: {}", operation);
-                return DevicePlcVO.OperationResult.builder()
-                        .success(false)
-                        .message("涓嶆敮鎸佺殑鎿嶄綔: " + operation)
-                        .build();
-        }
-    }
-
-    /**
-     * 澶勭悊瀛樺偍鐜荤拑鎿嶄綔
-     */
-    private DevicePlcVO.OperationResult handleStoreGlass(
-            DeviceConfig deviceConfig,
-            Map<String, Object> params,
-            Map<String, Object> logicParams) {
-
-        // 浠庨�昏緫鍙傛暟涓幏鍙栭厤缃�
-        Integer storageCapacity = getLogicParam(logicParams, "storageCapacity", 100);
-        String retrievalMode = getLogicParam(logicParams, "retrievalMode", "FIFO");
-        Boolean autoStore = getLogicParam(logicParams, "autoStore", true);
-
-        // 浠庤繍琛屾椂鍙傛暟涓幏鍙栨暟鎹�
-        String glassId = (String) params.get("glassId");
-        Integer storagePosition = (Integer) params.get("storagePosition");
-        Boolean triggerRequest = (Boolean) params.getOrDefault("triggerRequest", autoStore);
-
-        // 鏋勫缓鍐欏叆鏁版嵁
-        Map<String, Object> payload = new HashMap<>();
-        
-        if (glassId != null) {
-            payload.put("plcGlassId", glassId);
-        }
-        
-        if (storagePosition != null) {
-            payload.put("storagePosition", storagePosition);
-        }
-
-        // 鑷姩瑙﹀彂璇锋眰
-        if (triggerRequest != null && triggerRequest) {
-            payload.put("plcRequest", 1);
-        }
-
-        log.info("鐜荤拑瀛樺偍: deviceId={}, glassId={}, position={}", 
-                deviceConfig.getId(), glassId, storagePosition);
-
-        return devicePlcOperationService.writeFields(
-                deviceConfig.getId(), 
-                payload, 
-                "鐜荤拑瀛樺偍-瀛樺偍鐜荤拑"
-        );
-    }
-
-    /**
-     * 澶勭悊鍙栬揣鎿嶄綔
-     */
-    private DevicePlcVO.OperationResult handleRetrieveGlass(
-            DeviceConfig deviceConfig,
-            Map<String, Object> params,
-            Map<String, Object> logicParams) {
-
-        // 浠庨�昏緫鍙傛暟涓幏鍙栭厤缃�
-        String retrievalMode = getLogicParam(logicParams, "retrievalMode", "FIFO");
-        Boolean autoRetrieve = getLogicParam(logicParams, "autoRetrieve", true);
-
-        // 浠庤繍琛屾椂鍙傛暟涓幏鍙栨暟鎹�
-        Integer storagePosition = (Integer) params.get("storagePosition");
-        String glassId = (String) params.get("glassId");
-        Boolean triggerRequest = (Boolean) params.getOrDefault("triggerRequest", autoRetrieve);
-
-        // 鏋勫缓鍐欏叆鏁版嵁
-        Map<String, Object> payload = new HashMap<>();
-        
-        if (storagePosition != null) {
-            payload.put("retrievePosition", storagePosition);
-        }
-        
-        if (glassId != null) {
-            payload.put("retrieveGlassId", glassId);
-        }
-
-        // 鑷姩瑙﹀彂璇锋眰
-        if (triggerRequest != null && triggerRequest) {
-            payload.put("plcRequest", 1);
-        }
-
-        log.info("鐜荤拑鍙栬揣: deviceId={}, position={}, glassId={}", 
-                deviceConfig.getId(), storagePosition, glassId);
-
-        return devicePlcOperationService.writeFields(
-                deviceConfig.getId(), 
-                payload, 
-                "鐜荤拑瀛樺偍-鍙栬揣"
-        );
-    }
-
-    /**
-     * 澶勭悊瑙﹀彂璇锋眰鎿嶄綔
-     */
-    private DevicePlcVO.OperationResult handleTriggerRequest(
-            DeviceConfig deviceConfig,
-            Map<String, Object> params,
-            Map<String, Object> logicParams) {
-
-        Map<String, Object> payload = new HashMap<>();
-        payload.put("plcRequest", 1);
-        
-        log.info("鐜荤拑瀛樺偍瑙﹀彂璇锋眰: deviceId={}", deviceConfig.getId());
-        return devicePlcOperationService.writeFields(
-                deviceConfig.getId(),
-                payload,
-                "鐜荤拑瀛樺偍-瑙﹀彂璇锋眰"
-        );
-    }
-
-    /**
-     * 澶勭悊瑙﹀彂姹囨姤鎿嶄綔
-     */
-    private DevicePlcVO.OperationResult handleTriggerReport(
-            DeviceConfig deviceConfig,
-            Map<String, Object> params,
-            Map<String, Object> logicParams) {
-
-        Map<String, Object> payload = new HashMap<>();
-        payload.put("plcReport", 1);
-        
-        log.info("鐜荤拑瀛樺偍瑙﹀彂姹囨姤: deviceId={}", deviceConfig.getId());
-        return devicePlcOperationService.writeFields(
-                deviceConfig.getId(),
-                payload,
-                "鐜荤拑瀛樺偍-瑙﹀彂姹囨姤"
-        );
-    }
-
-    /**
-     * 澶勭悊閲嶇疆鎿嶄綔
-     */
-    private DevicePlcVO.OperationResult handleReset(
-            DeviceConfig deviceConfig,
-            Map<String, Object> params,
-            Map<String, Object> logicParams) {
-
-        Map<String, Object> payload = new HashMap<>();
-        payload.put("plcRequest", 0);
-        payload.put("plcReport", 0);
-        
-        log.info("鐜荤拑瀛樺偍閲嶇疆: deviceId={}", deviceConfig.getId());
-        return devicePlcOperationService.writeFields(
-                deviceConfig.getId(),
-                payload,
-                "鐜荤拑瀛樺偍-閲嶇疆"
-        );
-    }
-
-    @Override
-    public String validateLogicParams(DeviceConfig deviceConfig) {
-        Map<String, Object> logicParams = parseLogicParams(deviceConfig);
-        
-        // 楠岃瘉蹇呭~鍙傛暟
-        Integer storageCapacity = getLogicParam(logicParams, "storageCapacity", null);
-        if (storageCapacity != null && storageCapacity <= 0) {
-            return "瀛樺偍瀹归噺(storageCapacity)蹇呴』澶т簬0";
-        }
-
-        String retrievalMode = getLogicParam(logicParams, "retrievalMode", null);
-        if (retrievalMode != null && !retrievalMode.matches("FIFO|LIFO|RANDOM")) {
-            return "鍙栬揣妯″紡(retrievalMode)蹇呴』鏄疐IFO銆丩IFO鎴朢ANDOM";
-        }
-
-        return null; // 楠岃瘉閫氳繃
-    }
-
-    @Override
-    public String getDefaultLogicParams() {
-        Map<String, Object> defaultParams = new HashMap<>();
-        defaultParams.put("storageCapacity", 100);
-        defaultParams.put("retrievalMode", "FIFO");
-        defaultParams.put("autoStore", true);
-        defaultParams.put("autoRetrieve", true);
-        defaultParams.put("maxRetryCount", 3);
-        
-        try {
-            return objectMapper.writeValueAsString(defaultParams);
-        } catch (Exception e) {
-            log.error("鐢熸垚榛樿閫昏緫鍙傛暟澶辫触", e);
-            return "{}";
-        }
-    }
-}
-
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/vehicle/handler/LoadVehicleLogicHandler.java b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/vehicle/handler/LoadVehicleLogicHandler.java
index 4a40192..2599607 100644
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/vehicle/handler/LoadVehicleLogicHandler.java
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/vehicle/handler/LoadVehicleLogicHandler.java
@@ -16,6 +16,8 @@
 import com.mes.s7.enhanced.EnhancedS7Serializer;
 import com.mes.s7.provider.S7SerializerProvider;
 import com.mes.service.PlcDynamicDataService;
+import com.mes.task.model.TaskExecutionContext;
+import com.mes.interaction.workstation.scanner.handler.HorizontalScannerLogicHandler;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
@@ -256,7 +258,23 @@
         }
         
         // 浠庨厤缃腑鑾峰彇閫熷害锛堝鏋滄湁锛�
-        Double speed = getLogicParam(logicParams, "vehicleSpeed", null);
+        Object speedObj = logicParams != null ? logicParams.get("vehicleSpeed") : null;
+        Double speed = null;
+        if (speedObj != null) {
+            if (speedObj instanceof Double) {
+                speed = (Double) speedObj;
+            } else if (speedObj instanceof Integer) {
+                speed = ((Integer) speedObj).doubleValue();
+            } else if (speedObj instanceof Number) {
+                speed = ((Number) speedObj).doubleValue();
+            } else {
+                try {
+                    speed = Double.parseDouble(String.valueOf(speedObj));
+                } catch (NumberFormatException e) {
+                    log.warn("鏃犳硶瑙f瀽vehicleSpeed: {}", speedObj);
+                }
+            }
+        }
         if (speed != null) {
             task.setSpeed(speed);
             task.calculateEstimatedEndTime();
@@ -284,7 +302,23 @@
 
         // 浠庨�昏緫鍙傛暟涓幏鍙栭厤缃紙浠� extraParams.deviceLogic 璇诲彇锛�
         Integer vehicleCapacity = getLogicParam(logicParams, "vehicleCapacity", 6000);
-        Integer glassIntervalMs = getLogicParam(logicParams, "glassIntervalMs", 1000);
+        // 浼樺厛浣跨敤杩愯鏃跺弬鏁颁腑鐨刧lassIntervalMs锛堜粠浠诲姟鍙傛暟浼犲叆锛夛紝濡傛灉娌℃湁鍒欎娇鐢ㄨ澶囬厤缃殑
+        Integer glassIntervalMs = null;
+        if (params.containsKey("glassIntervalMs") && params.get("glassIntervalMs") != null) {
+            Object intervalObj = params.get("glassIntervalMs");
+            if (intervalObj instanceof Number) {
+                glassIntervalMs = ((Number) intervalObj).intValue();
+            } else if (intervalObj instanceof String) {
+                try {
+                    glassIntervalMs = Integer.parseInt((String) intervalObj);
+                } catch (NumberFormatException e) {
+                    // 蹇界暐
+                }
+            }
+        }
+        if (glassIntervalMs == null) {
+            glassIntervalMs = getLogicParam(logicParams, "glassIntervalMs", 1000);
+        }
         Boolean autoFeed = getLogicParam(logicParams, "autoFeed", true);
         Integer maxRetryCount = getLogicParam(logicParams, "maxRetryCount", 5);
 
@@ -311,11 +345,20 @@
                     .build();
         }
         if (plannedGlasses.isEmpty()) {
+            // 瑁呬笉涓嬶紝閫氱煡鍗ц浆绔嬫壂鐮佽澶囨殏鍋�
+            notifyScannerPause(params, true);
+            log.warn("澶ц溅璁惧瑁呬笉涓嬶紝宸查�氱煡鍗ц浆绔嬫壂鐮佹殏鍋�: deviceId={}, glassCount={}, vehicleCapacity={}", 
+                    deviceConfig.getId(), glassInfos.size(), vehicleCapacity);
             return DevicePlcVO.OperationResult.builder()
                     .success(false)
-                    .message("褰撳墠鐜荤拑灏哄瓒呭嚭杞﹁締瀹归噺锛屾棤娉曡杞�")
+                    .message("褰撳墠鐜荤拑灏哄瓒呭嚭杞﹁締瀹归噺锛屾棤娉曡杞斤紝宸查�氱煡鍗ц浆绔嬫壂鐮佹殏鍋�")
                     .build();
         }
+        
+        // 瑁呭緱涓嬶紝纭繚鍗ц浆绔嬫壂鐮佺户缁繍琛�
+        notifyScannerPause(params, false);
+        
+        // 缁х画鎵ц鍘熸湁閫昏緫
 
         // 鏋勫缓鍐欏叆鏁版嵁
         Map<String, Object> payload = new HashMap<>();
@@ -354,16 +397,13 @@
         log.info("澶ц溅璁惧鐜荤拑涓婃枡: deviceId={}, glassCount={}, position={}, plannedGlassIds={}",
                 deviceConfig.getId(), plcSlots, positionCode, plannedGlasses);
 
-        if (glassIntervalMs != null && glassIntervalMs > 0) {
-            try {
-                Thread.sleep(glassIntervalMs);
-            } catch (InterruptedException e) {
-                Thread.currentThread().interrupt();
-            }
-        }
-
+        // 鍐欏叆PLC锛岃澶ц溅寮�濮嬭鐜荤拑
         DevicePlcVO.OperationResult result = devicePlcOperationService.writeFields(
             deviceConfig.getId(), payload, operationName);
+        
+        // 娉ㄦ剰锛歡lassIntervalMs 鐨勭瓑寰呭簲璇ュ湪鎵规涔嬮棿锛堝湪TaskExecutionEngine涓鐞嗭級锛�
+        // 鑰屼笉鏄湪杩欓噷绛夊緟锛屽洜涓鸿繖閲岀瓑寰呬細闃诲澶ц溅鐨勬甯歌鐜荤拑娴佺▼
+        // 濡傛灉闇�瑕佸湪鍐欏叆鍚庣瓑寰咃紝搴旇鍦ㄦ壒娆′箣闂寸瓑寰咃紝璁╁ぇ杞︽湁鏃堕棿澶勭悊褰撳墠鎵规鐨勭幓鐠�
         
         // 濡傛灉鎵ц鎴愬姛锛屾洿鏂颁綅缃俊鎭埌鐘舵�侊紝骞跺惎鍔ㄧ姸鎬佺洃鎺�
         if (Boolean.TRUE.equals(result.getSuccess())) {
@@ -1754,5 +1794,25 @@
             Thread.currentThread().interrupt();
         }
     }
+    
+    /**
+     * 閫氱煡鍗ц浆绔嬫壂鐮佽澶囨殏鍋滄垨缁х画
+     * @param params 鍙傛暟锛屽寘鍚玙taskContext寮曠敤
+     * @param pause true=鏆傚仠锛宖alse=缁х画
+     */
+    private void notifyScannerPause(Map<String, Object> params, boolean pause) {
+        if (params == null) {
+            return;
+        }
+        
+        Object contextObj = params.get("_taskContext");
+        if (contextObj instanceof TaskExecutionContext) {
+            TaskExecutionContext context = (TaskExecutionContext) contextObj;
+            HorizontalScannerLogicHandler.setPauseFlag(context, pause);
+            log.info("宸查�氱煡鍗ц浆绔嬫壂鐮佽澶噞}: pause={}", pause ? "鏆傚仠" : "缁х画", pause);
+        } else {
+            log.debug("鏈壘鍒癟askExecutionContext锛屾棤娉曢�氱煡鍗ц浆绔嬫壂鐮佽澶囨殏鍋�");
+        }
+    }
 }
 
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/workstation/scanner/handler/HorizontalScannerLogicHandler.java b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/workstation/scanner/handler/HorizontalScannerLogicHandler.java
index 28e3728..fd1c2a1 100644
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/workstation/scanner/handler/HorizontalScannerLogicHandler.java
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/workstation/scanner/handler/HorizontalScannerLogicHandler.java
@@ -10,12 +10,15 @@
 import com.mes.s7.enhanced.EnhancedS7Serializer;
 import com.mes.s7.provider.S7SerializerProvider;
 import com.mes.service.PlcDynamicDataService;
+import com.mes.task.model.TaskExecutionContext;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Component;
 import org.springframework.util.StringUtils;
 
 import java.time.LocalDateTime;
 import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
@@ -53,60 +56,222 @@
                                                     String operation,
                                                     Map<String, Object> params,
                                                     Map<String, Object> logicParams) {
-        WorkstationLogicConfig config = parseWorkstationConfig(logicParams);
         EnhancedS7Serializer serializer = s7SerializerProvider.getSerializer(deviceConfig);
         if (serializer == null) {
-            return buildResult(deviceConfig, operation, false, "鑾峰彇PLC搴忓垪鍖栧櫒澶辫触");
+            return buildResult(deviceConfig, operation, false, "鑾峰彇PLC搴忓垪鍖栧櫒澶辫触", null);
         }
 
+        if ("clearPlc".equalsIgnoreCase(operation) || "reset".equalsIgnoreCase(operation)) {
+            return clearPlc(deviceConfig, serializer);
+        }
+
+        WorkstationLogicConfig config = parseWorkstationConfig(logicParams);
+        return executeScan(deviceConfig, config, serializer, params);
+    }
+
+    private DevicePlcVO.OperationResult executeScan(DeviceConfig deviceConfig,
+                                                    WorkstationLogicConfig config,
+                                                    EnhancedS7Serializer serializer,
+                                                    Map<String, Object> params) {
         try {
-            log.debug("鍗ц浆绔嬫壂鐮佽鍙朚ES鍐欏尯: deviceId={}, scanInterval={}ms",
-                    deviceConfig.getId(), config.getScanIntervalMs());
-            Map<String, Object> mesData = plcDynamicDataService.readPlcData(deviceConfig, MES_FIELDS, serializer);
-            if (mesData == null || mesData.isEmpty()) {
-                return buildResult(deviceConfig, operation, false, "璇诲彇MES鍐欏尯澶辫触");
+            // 浠庡弬鏁颁腑鑾峰彇鐜荤拑ID锛堝畾鏃跺櫒姣忔鍙鐞嗕竴涓級
+            String inputGlassId = null;
+            if (params != null) {
+                Object glassIdObj = params.get("glassId");
+                if (glassIdObj != null) {
+                    inputGlassId = String.valueOf(glassIdObj).trim();
+                }
             }
-
-            Integer mesSend = parseInteger(mesData.get("mesSend"));
-            if (mesSend == null || mesSend == 0) {
-                return buildResult(deviceConfig, operation, true, "鏆傛棤寰呭鐞嗙殑鐜荤拑淇℃伅");
-            }
-
-            String glassId = parseString(mesData.get("mesGlassId"));
-            if (!StringUtils.hasText(glassId)) {
-                return buildResult(deviceConfig, operation, false, "MES鍐欏尯鏈彁渚涚幓鐠僆D");
-            }
-
-            Integer longSide = convertDimension(parseInteger(mesData.get("mesWidth")));
-            Integer shortSide = convertDimension(parseInteger(mesData.get("mesHeight")));
-            Integer workLine = parseInteger(mesData.get("workLine"));
-
-            GlassInfo glassInfo = buildGlassInfo(glassId, longSide, shortSide, workLine);
-            boolean saved = glassInfoService.saveOrUpdateGlassInfo(glassInfo);
-            if (!saved) {
-                return buildResult(deviceConfig, operation, false, "淇濆瓨鐜荤拑淇℃伅澶辫触: " + glassId);
-            }
-
-            String msg = String.format("鐜荤拑[%s] 灏哄[%s x %s] 宸叉帴鏀跺苟鍏ュ簱锛寃orkLine=%s",
-                    glassId,
-                    longSide != null ? longSide + "mm" : "-",
-                    shortSide != null ? shortSide + "mm" : "-",
-                    workLine != null ? workLine : "-");
-            return buildResult(deviceConfig, operation, true, msg);
+            
+            // 鎵ц鍗曟鎵弿锛堝畾鏃跺櫒浼氬惊鐜皟鐢ㄦ鏂规硶锛�
+            return executeSingleScan(deviceConfig, config, serializer, inputGlassId, params);
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+            log.warn("鍗ц浆绔嬫壂鐮佺瓑寰匨ES鏁版嵁琚腑鏂�, deviceId={}", deviceConfig.getId(), e);
+            return buildResult(deviceConfig, "scanOnce", false, "绛夊緟MES鏁版嵁琚腑鏂�", null);
         } catch (Exception e) {
             log.error("鍗ц浆绔嬫壂鐮佸鐞嗗紓甯�, deviceId={}", deviceConfig.getId(), e);
-            return buildResult(deviceConfig, operation, false, "澶勭悊寮傚父: " + e.getMessage());
+            return buildResult(deviceConfig, "scanOnce", false, "澶勭悊寮傚父: " + e.getMessage(), null);
+        }
+    }
+    
+    /**
+     * 鎵ц鍗曟鎵弿
+     */
+    private DevicePlcVO.OperationResult executeSingleScan(DeviceConfig deviceConfig,
+                                                          WorkstationLogicConfig config,
+                                                          EnhancedS7Serializer serializer,
+                                                          String inputGlassId,
+                                                          Map<String, Object> params) throws InterruptedException {
+        // 1. 鍐欏叆plcRequest=1鍜宲lcGlassId锛堝鏋滄彁渚涗簡鐜荤拑ID锛�
+        triggerScanRequest(deviceConfig, serializer, inputGlassId);
+        
+        // 2. 绛夊緟MES鍥炲啓mesSend=1浠ュ強鐜荤拑淇℃伅
+        Map<String, Object> mesData = waitForMesData(deviceConfig, serializer, config);
+        if (mesData == null || mesData.isEmpty()) {
+            log.error("绛夊緟MES鍐欏叆鐜荤拑淇℃伅瓒呮椂: deviceId={}, timeout={}ms", 
+                    deviceConfig.getId(), config.getScanIntervalMs());
+            return buildResult(deviceConfig, "scanOnce", false,
+                    String.format("绛夊緟MES鍐欏叆鐜荤拑淇℃伅瓒呮椂(%dms)", config.getScanIntervalMs()), null);
+        }
+
+        // 3. 璇诲彇MES鍥炲啓鐨勭幓鐠冧俊鎭�
+        String glassId = parseString(mesData.get("mesGlassId"));
+        if (!StringUtils.hasText(glassId)) {
+            return buildResult(deviceConfig, "scanOnce", false, "MES鍐欏尯鏈彁渚涚幓鐠僆D", null);
+        }
+        // 璇诲彇MES灏哄鏁版嵁锛歮esWidth=琛ㄥ锛宮esHeight=闀�
+        Integer rawWidth = parseInteger(mesData.get("mesWidth"));
+        Integer rawHeight = parseInteger(mesData.get("mesHeight"));
+        Integer workLine = parseInteger(mesData.get("workLine"));
+
+        // 4. 娓呯┖plcRequest鍜宲lcGlassId锛堝彧娓呴櫎PLC瀛楁锛�
+        clearPlcRequestFields(deviceConfig, serializer);
+
+
+            // 5. 淇濆瓨鐜荤拑淇℃伅鍒版暟鎹簱
+            GlassInfo glassInfo = buildGlassInfo(glassId, rawWidth, rawHeight, workLine);
+            boolean saved = glassInfoService.saveOrUpdateGlassInfo(glassInfo);
+            if (!saved) {
+                return buildResult(deviceConfig, "scanOnce", false, "淇濆瓨鐜荤拑淇℃伅澶辫触: " + glassId, null);
+            }
+            
+            // 6. 灏嗘壂鎻忓埌鐨勭幓鐠僆D淇濆瓨鍒板叡浜暟鎹腑锛堜緵澶ц溅璁惧瀹氭椂鍣ㄨ鍙栵級
+            saveScannedGlassId(params, glassId);
+
+            String msg = String.format("鐜荤拑[%s] 灏哄[琛ㄥ:%s x 闀�:%s] 宸叉帴鏀跺苟鍏ュ簱锛寃orkLine=%s",
+                    glassId,
+                    rawWidth != null ? rawWidth + "mm" : "-",
+                    rawHeight != null ? rawHeight + "mm" : "-",
+                    workLine != null ? workLine : "-");
+            Map<String, Object> resultData = new HashMap<>();
+            resultData.put("glassIds", Collections.singletonList(glassId));
+            if (workLine != null) {
+                resultData.put("workLine", workLine);
+            }
+            return buildResult(deviceConfig, "scanOnce", true, msg, resultData);
+    }
+    
+    /**
+     * 璁剧疆鏆傚仠鏍囧織锛堜緵澶ц溅璁惧璋冪敤锛�
+     */
+    public static void setPauseFlag(TaskExecutionContext context, boolean pause) {
+        if (context != null) {
+            context.getSharedData().put("scannerPause", pause);
+        }
+    }
+    
+    /**
+     * 淇濆瓨鎵弿鍒扮殑鐜荤拑ID鍒板叡浜暟鎹腑
+     */
+    @SuppressWarnings("unchecked")
+    private void saveScannedGlassId(Map<String, Object> params, String glassId) {
+        if (params == null || !StringUtils.hasText(glassId)) {
+            return;
+        }
+        
+        Object contextObj = params.get("_taskContext");
+        if (contextObj instanceof TaskExecutionContext) {
+            TaskExecutionContext context = (TaskExecutionContext) contextObj;
+            List<String> scannedGlassIds = (List<String>) context.getSharedData()
+                    .computeIfAbsent("scannedGlassIds", k -> new java.util.ArrayList<>());
+            if (!scannedGlassIds.contains(glassId)) {
+                scannedGlassIds.add(glassId);
+                log.debug("宸蹭繚瀛樻壂鎻忓埌鐨勭幓鐠僆D鍒板叡浜暟鎹�: glassId={}", glassId);
+            }
         }
     }
 
-    private GlassInfo buildGlassInfo(String glassId, Integer longSide, Integer shortSide, Integer workLine) {
+    private DevicePlcVO.OperationResult clearPlc(DeviceConfig deviceConfig,
+                                                 EnhancedS7Serializer serializer) {
+        try {
+            // 鍙竻绌篜LC鎿嶄綔鍖哄瓧娈碉紙plcRequest銆乸lcGlassId锛夛紝涓嶆竻绌篗ES鍐欏尯瀛楁
+            Map<String, Object> resetFields = new HashMap<>();
+            resetFields.put("plcRequest", 0);
+            resetFields.put("plcGlassId", "");
+            plcDynamicDataService.writePlcData(deviceConfig, resetFields, serializer);
+            return buildResult(deviceConfig, "clearPlc", true, "宸叉竻绌篜LC鎿嶄綔鍖哄瓧娈碉紙淇濈暀MES鍐欏尯瀛楁锛�", null);
+        } catch (Exception e) {
+            log.error("鍗ц浆绔嬫壂鐮佹竻绌篜LC澶辫触, deviceId={}", deviceConfig.getId(), e);
+            return buildResult(deviceConfig, "clearPlc", false, "娓呯┖PLC澶辫触: " + e.getMessage(), null);
+        }
+    }
+
+    /**
+     * 瑙﹀彂MES璇锋眰锛氬啓鍏lcRequest=1鍜宲lcGlassId锛堝鏋滄彁渚涗簡鐜荤拑ID锛�
+     */
+    private void triggerScanRequest(DeviceConfig deviceConfig, EnhancedS7Serializer serializer, String glassId) {
+        Map<String, Object> writeFields = new HashMap<>();
+        writeFields.put("plcRequest", 1);
+        
+        if (StringUtils.hasText(glassId)) {
+            writeFields.put("plcGlassId", glassId);
+        }
+        
+        plcDynamicDataService.writePlcData(deviceConfig, writeFields, serializer);
+    }
+    
+    /**
+     * 娓呯┖PLC璇锋眰瀛楁锛氬彧娓呴櫎plcRequest鍜宲lcGlassId锛堜笉娓呴櫎MES鍐欏尯瀛楁锛�
+     */
+    private void clearPlcRequestFields(DeviceConfig deviceConfig, EnhancedS7Serializer serializer) {
+        try {
+            Map<String, Object> clearFields = new HashMap<>();
+            clearFields.put("plcRequest", 0);
+            clearFields.put("plcGlassId", "");
+            plcDynamicDataService.writePlcData(deviceConfig, clearFields, serializer);
+        } catch (Exception e) {
+            log.error("娓呯┖PLC璇锋眰瀛楁澶辫触: deviceId={}", deviceConfig.getId(), e);
+            // 涓嶆竻绌轰笉褰卞搷涓绘祦绋嬶紝鍙褰曢敊璇�
+        }
+    }
+
+    private Map<String, Object> waitForMesData(DeviceConfig deviceConfig,
+                                               EnhancedS7Serializer serializer,
+                                               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);
+            
+            if (mesData != null && !mesData.isEmpty()) {
+                Integer mesSend = parseInteger(mesData.get("mesSend"));
+                if (mesSend != null && mesSend == 1) {
+                    log.info("妫�娴嬪埌MES宸插啓鍏ユ暟鎹�: deviceId={}, mesSend=1, mesGlassId={}, mesWidth={}, mesHeight={}, workLine={}", 
+                            deviceConfig.getId(), 
+                            mesData.get("mesGlassId"),
+                            mesData.get("mesWidth"),
+                            mesData.get("mesHeight"),
+                            mesData.get("workLine"));
+                    return mesData;
+                }
+            }
+            
+            Thread.sleep(pollInterval);
+        }
+        
+        // 瓒呮椂鍓嶆渶鍚庝竴娆″皾璇曡鍙�
+        log.warn("绛夊緟MES鏁版嵁瓒呮椂: deviceId={}, timeout={}ms", deviceConfig.getId(), timeoutMs);
+        Map<String, Object> lastMesData = plcDynamicDataService.readPlcData(deviceConfig, MES_FIELDS, serializer);
+        if (lastMesData != null && !lastMesData.isEmpty()) {
+            log.warn("瓒呮椂鍓嶆渶鍚庝竴娆¤鍙栧埌鐨勬暟鎹�: deviceId={}, mesData={}", 
+                    deviceConfig.getId(), lastMesData);
+        }
+        
+        return Collections.emptyMap();
+    }
+
+    private GlassInfo buildGlassInfo(String glassId, Integer width, Integer height, Integer workLine) {
         GlassInfo glassInfo = new GlassInfo();
         glassInfo.setGlassId(glassId.trim());
-        if (longSide != null) {
-            glassInfo.setGlassLength(longSide);
+        // mesWidth=琛ㄥ -> glassWidth, mesHeight=闀� -> glassLength
+        if (width != null) {
+            glassInfo.setGlassWidth(width);  // 琛ㄥ
         }
-        if (shortSide != null) {
-            glassInfo.setGlassWidth(shortSide);
+        if (height != null) {
+            glassInfo.setGlassLength(height); // 闀�
         }
         glassInfo.setStatus(GlassInfo.Status.ACTIVE);
         if (workLine != null) {
@@ -133,17 +298,11 @@
         return value == null ? null : String.valueOf(value).trim();
     }
 
-    private Integer convertDimension(Integer raw) {
-        if (raw == null) {
-            return null;
-        }
-        return raw / 10;
-    }
-
     private DevicePlcVO.OperationResult buildResult(DeviceConfig deviceConfig,
                                                     String operation,
                                                     boolean success,
-                                                    String message) {
+                                                    String message,
+                                                    Map<String, Object> data) {
         return DevicePlcVO.OperationResult.builder()
                 .deviceId(deviceConfig.getId())
                 .deviceName(deviceConfig.getDeviceName())
@@ -153,6 +312,7 @@
                 .success(success)
                 .message(message)
                 .timestamp(LocalDateTime.now())
+                .data(data)
                 .build();
     }
 }
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/task/dto/TaskParameters.java b/mes-processes/mes-plcSend/src/main/java/com/mes/task/dto/TaskParameters.java
index aaf2c7c..cb8be2c 100644
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/task/dto/TaskParameters.java
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/task/dto/TaskParameters.java
@@ -7,7 +7,6 @@
 import lombok.Data;
 import lombok.NoArgsConstructor;
 
-import javax.validation.constraints.NotEmpty;
 import java.io.Serializable;
 import java.util.List;
 import java.util.Map;
@@ -22,8 +21,7 @@
 @ApiModel(value = "TaskParameters", description = "澶氳澶囦换鍔℃墽琛屽弬鏁�")
 public class TaskParameters implements Serializable {
 
-    @ApiModelProperty(value = "鐜荤拑ID鍒楄〃锛堜繚鎸佹墽琛岄『搴忥級", required = true)
-    @NotEmpty(message = "鐜荤拑ID鍒楄〃涓嶈兘涓虹┖")
+    @ApiModelProperty(value = "鐜荤拑ID鍒楄〃锛堜繚鎸佹墽琛岄『搴忥級锛屽彲涓虹┖琛ㄧず鐢卞悗鍙拌嚜鍔ㄨ幏鍙栨渶杩戞壂鐮佺殑鐜荤拑ID")
     private List<String> glassIds;
 
     @ApiModelProperty(value = "涓婂ぇ杞︿綅缃紪鐮�")
@@ -41,6 +39,18 @@
     @ApiModelProperty(value = "鎵ц闂撮殧(姣)")
     private Integer executionInterval;
 
+    @ApiModelProperty(value = "鍗曠墖闂撮殧(姣)锛屽涓幓鐠僆D鏃舵瘡涓幓鐠僆D涔嬮棿鐨勯棿闅旀椂闂达紝0琛ㄧず涓�娆℃�у叏閮ㄤ紶閫�")
+    private Integer glassIntervalMs;
+
+    @ApiModelProperty(value = "鏄惁鍦ㄤ换鍔″紑濮嬪墠鍏堣Е鍙戣姹�(plcRequest)锛屼粎鍦ㄦ湭鎻愪緵鐜荤拑ID鏃剁敓鏁堬紝榛樿true")
+    private Boolean triggerRequestFirst;
+
+    @ApiModelProperty(value = "浠诲姟瓒呮椂鏃堕棿锛堝垎閽燂級锛岄粯璁�30鍒嗛挓")
+    private Integer timeoutMinutes;
+
+    @ApiModelProperty(value = "閲嶈瘯娆℃暟锛岄粯璁�3娆�")
+    private Integer retryCount;
+
     @ApiModelProperty(value = "璁惧绾у埆鍙傛暟瑕嗙洊锛宬ey鍙互鏄澶囩被鍨嬫垨璁惧缂栫爜")
     private Map<String, Map<String, Object>> deviceOverrides;
 
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/task/entity/TaskStepDetail.java b/mes-processes/mes-plcSend/src/main/java/com/mes/task/entity/TaskStepDetail.java
index 73deefa..da9c996 100644
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/task/entity/TaskStepDetail.java
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/task/entity/TaskStepDetail.java
@@ -70,6 +70,10 @@
     @ApiModelProperty("閿欒淇℃伅")
     private String errorMessage;
 
+    @TableField("success_message")
+    @ApiModelProperty("鎴愬姛淇℃伅")
+    private String successMessage;
+
     @TableField("retry_count")
     @ApiModelProperty("閲嶈瘯娆℃暟")
     private Integer retryCount;
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/task/service/TaskExecutionEngine.java b/mes-processes/mes-plcSend/src/main/java/com/mes/task/service/TaskExecutionEngine.java
index 8f995c0..986a06b 100644
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/task/service/TaskExecutionEngine.java
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/task/service/TaskExecutionEngine.java
@@ -7,14 +7,16 @@
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.mes.device.entity.DeviceConfig;
 import com.mes.device.entity.DeviceGroupConfig;
+import com.mes.device.service.DeviceCoordinationService;
+import com.mes.device.service.DeviceInteractionService;
+import com.mes.device.service.GlassInfoService;
+import com.mes.device.vo.DevicePlcVO;
 import com.mes.interaction.DeviceInteraction;
 import com.mes.interaction.DeviceInteractionRegistry;
 import com.mes.interaction.DeviceLogicHandler;
 import com.mes.interaction.DeviceLogicHandlerFactory;
 import com.mes.interaction.base.InteractionContext;
 import com.mes.interaction.base.InteractionResult;
-import com.mes.device.service.DeviceCoordinationService;
-import com.mes.device.service.DeviceInteractionService;
 import com.mes.task.dto.TaskParameters;
 import com.mes.task.entity.MultiDeviceTask;
 import com.mes.task.entity.TaskStepDetail;
@@ -23,16 +25,16 @@
 import com.mes.task.model.RetryPolicy;
 import com.mes.task.model.TaskExecutionContext;
 import com.mes.task.model.TaskExecutionResult;
-import com.mes.task.service.TaskStatusNotificationService;
-import com.mes.device.vo.DevicePlcVO;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.stereotype.Component;
 import org.springframework.util.CollectionUtils;
 import org.springframework.util.StringUtils;
 
 import java.util.*;
 import java.util.concurrent.*;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * 澶氳澶囦换鍔℃墽琛屽紩鎿�
@@ -45,6 +47,8 @@
 
     private static final Map<String, String> DEFAULT_OPERATIONS = new HashMap<>();
     private static final TypeReference<Map<String, Object>> MAP_TYPE = new TypeReference<Map<String, Object>>() {};
+    private static final int SCANNER_LOOKBACK_MINUTES = 2;
+    private static final int SCANNER_LOOKBACK_LIMIT = 20;
 
     // 鎵ц妯″紡甯搁噺
     private static final String EXECUTION_MODE_SERIAL = "SERIAL";
@@ -53,7 +57,8 @@
     static {
         DEFAULT_OPERATIONS.put(DeviceConfig.DeviceType.LOAD_VEHICLE, "feedGlass");
         DEFAULT_OPERATIONS.put(DeviceConfig.DeviceType.LARGE_GLASS, "processGlass");
-        DEFAULT_OPERATIONS.put(DeviceConfig.DeviceType.GLASS_STORAGE, "storeGlass");
+        DEFAULT_OPERATIONS.put(DeviceConfig.DeviceType.WORKSTATION_SCANNER, "scanOnce");
+        DEFAULT_OPERATIONS.put(DeviceConfig.DeviceType.WORKSTATION_TRANSFER, "checkAndProcess");
     }
 
     private final TaskStepDetailMapper taskStepDetailMapper;
@@ -64,6 +69,8 @@
     private final DeviceCoordinationService deviceCoordinationService;
     private final TaskStatusNotificationService notificationService;
     private final ObjectMapper objectMapper;
+    @Qualifier("deviceGlassInfoService")
+    private final GlassInfoService glassInfoService;
 
     // 绾跨▼姹犵敤浜庡苟琛屾墽琛�
     private final ExecutorService executorService = Executors.newCachedThreadPool(r -> {
@@ -71,6 +78,18 @@
         t.setDaemon(true);
         return t;
     });
+    
+    // 瀹氭椂鍣ㄧ嚎绋嬫睜锛氱敤浜庤澶囧畾鏃舵壂鎻�
+    private final ScheduledExecutorService scheduledExecutor = Executors.newScheduledThreadPool(10, r -> {
+        Thread t = new Thread(r, "TaskExecutionEngine-Scheduled");
+        t.setDaemon(true);
+        return t;
+    });
+    
+    // 瀛樺偍姣忎釜浠诲姟鐨勫畾鏃跺櫒浠诲姟锛歵askId -> List<ScheduledFuture>
+    private final Map<String, List<ScheduledFuture<?>>> taskScheduledTasks = new ConcurrentHashMap<>();
+    // 璁板綍姝e湪杩愯浠诲姟鐨勪笂涓嬫枃锛屼究浜庡彇娑堜换鍔℃椂璁块棶
+    private final Map<String, TaskExecutionContext> runningTaskContexts = new ConcurrentHashMap<>();
 
     public TaskExecutionResult execute(MultiDeviceTask task,
                                        DeviceGroupConfig groupConfig,
@@ -82,16 +101,8 @@
         }
 
         TaskExecutionContext context = new TaskExecutionContext(parameters);
+        runningTaskContexts.put(task.getTaskId(), context);
         
-        // 璁惧鍗忚皟锛氭鏌ヤ緷璧栧叧绯诲拰鎵ц鏉′欢
-        DeviceCoordinationService.CoordinationResult coordinationResult = 
-            deviceCoordinationService.coordinateExecution(groupConfig, devices, context);
-        if (!coordinationResult.canExecute()) {
-            log.warn("璁惧鍗忚皟澶辫触: {}", coordinationResult.getMessage());
-            return TaskExecutionResult.failure(coordinationResult.getMessage(), Collections.emptyMap());
-        }
-        log.info("璁惧鍗忚皟鎴愬姛: {}", coordinationResult.getMessage());
-
         task.setTotalSteps(devices.size());
         task.setStatus(MultiDeviceTask.Status.RUNNING.name());
         multiDeviceTaskMapper.updateById(task);
@@ -120,10 +131,133 @@
             stepSummaries = new ArrayList<>();
             success = true;
             failureMessage = null;
-            for (int i = 0; i < devices.size(); i++) {
-                DeviceConfig device = devices.get(i);
-                int order = i + 1;
-                TaskStepDetail step = createStepRecord(task, device, order);
+
+            TaskParameters params = context.getParameters();
+            boolean hasGlassIds = !CollectionUtils.isEmpty(params.getGlassIds());
+            boolean triggerFirst = Boolean.TRUE.equals(params.getTriggerRequestFirst());
+
+            int currentOrder = 1;
+            // 缁熻澶ц溅璁惧鏁伴噺锛岀敤浜庡尯鍒嗚繘鐗囧ぇ杞﹀拰鍑虹墖澶ц溅
+            int loadVehicleCount = 0;
+            for (DeviceConfig device : devices) {
+                if (DeviceConfig.DeviceType.LOAD_VEHICLE.equals(device.getDeviceType())) {
+                    loadVehicleCount++;
+                }
+            }
+            int currentLoadVehicleIndex = 0;
+            
+            for (DeviceConfig device : devices) {
+                String deviceType = device.getDeviceType();
+                log.info("澶勭悊璁惧: deviceId={}, deviceType={}, deviceName={}, WORKSTATION_SCANNER甯搁噺={}, equals={}", 
+                        device.getId(), deviceType, device.getDeviceName(), 
+                        DeviceConfig.DeviceType.WORKSTATION_SCANNER,
+                        DeviceConfig.DeviceType.WORKSTATION_SCANNER.equals(deviceType));
+                boolean isLoadVehicle = DeviceConfig.DeviceType.LOAD_VEHICLE.equals(deviceType);
+                boolean isScanner = DeviceConfig.DeviceType.WORKSTATION_SCANNER.equals(deviceType) 
+                        || (deviceType != null && (deviceType.contains("鎵爜") || deviceType.contains("SCANNER")));
+                boolean isLargeGlass = DeviceConfig.DeviceType.LARGE_GLASS.equals(deviceType);
+                boolean isTransfer = DeviceConfig.DeviceType.WORKSTATION_TRANSFER.equals(deviceType);
+                log.info("璁惧绫诲瀷鍒ゆ柇: deviceId={}, isLoadVehicle={}, isScanner={}, isLargeGlass={}, isTransfer={}", 
+                        device.getId(), isLoadVehicle, isScanner, isLargeGlass, isTransfer);
+
+                // 1. 鍗ц浆绔嬫壂鐮佽澶囷細鍚姩瀹氭椂鍣ㄦ壂鎻忥紙姣�10绉掑鐞嗕竴涓幓鐠僆D锛�
+                if (isScanner) {
+                    log.info("妫�娴嬪埌鎵爜璁惧锛屽噯澶囧惎鍔ㄥ畾鏃跺櫒: deviceId={}, deviceType={}, deviceName={}", 
+                            device.getId(), device.getDeviceType(), device.getDeviceName());
+                    TaskStepDetail step = createStepRecord(task, device, currentOrder);
+                    ScheduledFuture<?> scannerTask = startScannerTimer(task, step, device, context);
+                    if (scannerTask != null) {
+                        registerScheduledTask(task.getTaskId(), scannerTask);
+                        stepSummaries.add(createStepSummary(device.getDeviceName(), true, "瀹氭椂鍣ㄥ凡鍚姩锛屾瘡10绉掓壂鎻忎竴娆�"));
+                        log.info("鎵爜璁惧瀹氭椂鍣ㄥ惎鍔ㄦ垚鍔�: deviceId={}, taskId={}", device.getId(), task.getTaskId());
+                    } else {
+                        log.warn("鎵爜璁惧瀹氭椂鍣ㄥ惎鍔ㄥけ璐ワ紝glassIds鍙兘涓虹┖: deviceId={}, taskId={}, contextParams={}", 
+                                device.getId(), task.getTaskId(), context.getParameters());
+                        stepSummaries.add(createStepSummary(device.getDeviceName(), false, "鍚姩瀹氭椂鍣ㄥけ璐�"));
+                        success = false;
+                        failureMessage = "鍗ц浆绔嬫壂鐮佽澶囧惎鍔ㄥ畾鏃跺櫒澶辫触";
+                        break;
+                    }
+                    currentOrder++;
+                    continue;
+                }
+
+                // 2. 鍗ц浆绔嬭澶囷細鍚姩瀹氭椂鍣ㄥ畾鏈熸鏌ュ苟澶勭悊锛堜腑杞澶囷級
+                if (isTransfer) {
+                    log.info("妫�娴嬪埌鍗ц浆绔嬭澶囷紝鍑嗗鍚姩瀹氭椂鍣�: deviceId={}, deviceType={}, deviceName={}", 
+                            device.getId(), device.getDeviceType(), device.getDeviceName());
+                    TaskStepDetail step = createStepRecord(task, device, currentOrder);
+                    ScheduledFuture<?> transferTask = startTransferTimer(task, step, device, context);
+                    if (transferTask != null) {
+                        registerScheduledTask(task.getTaskId(), transferTask);
+                        stepSummaries.add(createStepSummary(device.getDeviceName(), true, "瀹氭椂鍣ㄥ凡鍚姩锛屽畾鏈熸鏌ュ苟澶勭悊鐜荤拑鎵规"));
+                        log.info("鍗ц浆绔嬭澶囧畾鏃跺櫒鍚姩鎴愬姛: deviceId={}, taskId={}", device.getId(), task.getTaskId());
+                    } else {
+                        log.warn("鍗ц浆绔嬭澶囧畾鏃跺櫒鍚姩澶辫触: deviceId={}, taskId={}", device.getId(), task.getTaskId());
+                        stepSummaries.add(createStepSummary(device.getDeviceName(), false, "鍚姩瀹氭椂鍣ㄥけ璐�"));
+                        success = false;
+                        failureMessage = "鍗ц浆绔嬭澶囧惎鍔ㄥ畾鏃跺櫒澶辫触";
+                        break;
+                    }
+                    currentOrder++;
+                    continue;
+                }
+
+                // 3. 杩涚墖澶ц溅璁惧锛氬惎鍔ㄥ畾鏃跺櫒鎸佺画鐩戞帶瀹归噺锛堢涓�涓ぇ杞﹁澶囷級
+                if (isLoadVehicle) {
+                    currentLoadVehicleIndex++;
+                    boolean isInboundVehicle = currentLoadVehicleIndex == 1; // 绗竴涓ぇ杞︽槸杩涚墖澶ц溅
+                    
+                    TaskStepDetail step = createStepRecord(task, device, currentOrder);
+                    ScheduledFuture<?> vehicleTask;
+                    if (isInboundVehicle) {
+                        // 杩涚墖澶ц溅锛氱洃鎺у閲忥紝鍔ㄦ�佸垽鏂�
+                        vehicleTask = startInboundVehicleTimer(task, step, device, context);
+                        if (vehicleTask != null) {
+                            registerScheduledTask(task.getTaskId(), vehicleTask);
+                            stepSummaries.add(createStepSummary(device.getDeviceName(), true, "杩涚墖澶ц溅瀹氭椂鍣ㄥ凡鍚姩锛屾寔缁洃鎺у閲�"));
+                        } else {
+                            stepSummaries.add(createStepSummary(device.getDeviceName(), false, "鍚姩瀹氭椂鍣ㄥけ璐�"));
+                            success = false;
+                            failureMessage = "杩涚墖澶ц溅璁惧鍚姩瀹氭椂鍣ㄥけ璐�";
+                            break;
+                        }
+                    } else {
+                        // 鍑虹墖澶ц溅锛氬惎鍔ㄥ畾鏃跺櫒鐩戞帶鍑虹墖浠诲姟
+                        vehicleTask = startOutboundVehicleTimer(task, step, device, context);
+                        if (vehicleTask != null) {
+                            registerScheduledTask(task.getTaskId(), vehicleTask);
+                            stepSummaries.add(createStepSummary(device.getDeviceName(), true, "鍑虹墖澶ц溅瀹氭椂鍣ㄥ凡鍚姩锛屾寔缁洃鎺у嚭鐗囦换鍔�"));
+                        } else {
+                            stepSummaries.add(createStepSummary(device.getDeviceName(), false, "鍚姩瀹氭椂鍣ㄥけ璐�"));
+                            success = false;
+                            failureMessage = "鍑虹墖澶ц溅璁惧鍚姩瀹氭椂鍣ㄥけ璐�";
+                            break;
+                        }
+                    }
+                    currentOrder++;
+                    continue;
+                }
+
+                // 4. 澶х悊鐗囩璁惧锛氬惎鍔ㄥ畾鏃跺櫒閫昏緫澶勭悊锛堜笉娑夊強PLC浜や簰锛屽彧璐熻矗閫昏緫澶勭悊锛�
+                if (isLargeGlass) {
+                    TaskStepDetail step = createStepRecord(task, device, currentOrder);
+                    ScheduledFuture<?> largeGlassTask = startLargeGlassTimer(task, step, device, context);
+                    if (largeGlassTask != null) {
+                        registerScheduledTask(task.getTaskId(), largeGlassTask);
+                        stepSummaries.add(createStepSummary(device.getDeviceName(), true, "澶х悊鐗囩瀹氭椂鍣ㄥ凡鍚姩锛岄�昏緫澶勭悊涓�"));
+                    } else {
+                        stepSummaries.add(createStepSummary(device.getDeviceName(), false, "鍚姩瀹氭椂鍣ㄥけ璐�"));
+                        success = false;
+                        failureMessage = "澶х悊鐗囩璁惧鍚姩瀹氭椂鍣ㄥけ璐�";
+                        break;
+                    }
+                    currentOrder++;
+                    continue;
+                }
+
+                // 鍏朵粬璁惧锛氭甯告墽琛�
+                TaskStepDetail step = createStepRecord(task, device, currentOrder);
                 StepResult stepResult = executeStep(task, step, device, context);
                 stepSummaries.add(stepResult.toSummary());
                 if (!stepResult.isSuccess()) {
@@ -131,7 +265,35 @@
                     failureMessage = stepResult.getMessage();
                     break;
                 }
+                currentOrder++;
             }
+            
+            // 濡傛灉鎵�鏈夎澶囬兘鏄畾鏃跺櫒妯″紡锛屼换鍔′繚鎸佽繍琛岀姸鎬侊紝涓嶇瓑寰呭畬鎴�
+            // 瀹氭椂鍣ㄤ細鍦ㄥ悗鍙版寔缁繍琛岋紝鐩村埌鎵嬪姩鍋滄鎴栬秴鏃�
+            boolean hasScheduledTasks = !CollectionUtils.isEmpty(taskScheduledTasks.get(task.getTaskId()));
+            if (hasScheduledTasks) {
+                log.info("浠诲姟宸插惎鍔ㄦ墍鏈夊畾鏃跺櫒锛屼繚鎸佽繍琛岀姸鎬�: taskId={}, scheduledTasksCount={}", 
+                        task.getTaskId(), taskScheduledTasks.get(task.getTaskId()).size());
+                // 浠诲姟淇濇寔 RUNNING 鐘舵�侊紝瀹氭椂鍣ㄥ湪鍚庡彴杩愯
+                // 涓嶆洿鏂颁换鍔$姸鎬佷负 COMPLETED锛岃浠诲姟鎸佺画杩愯
+                Map<String, Object> payload = new HashMap<>();
+                payload.put("steps", stepSummaries);
+                payload.put("groupId", groupConfig.getId());
+                payload.put("deviceCount", devices.size());
+                payload.put("executionMode", executionMode);
+                payload.put("message", "浠诲姟宸插惎鍔紝瀹氭椂鍣ㄥ湪鍚庡彴杩愯涓�");
+                
+                // 閫氱煡浠诲姟鐘舵�侊紙淇濇寔 RUNNING锛�
+                notificationService.notifyTaskStatus(task);
+                
+                if (success) {
+                    return TaskExecutionResult.success(payload);
+                }
+                return TaskExecutionResult.failure(failureMessage != null ? failureMessage : "浠诲姟鎵ц澶辫触", payload);
+            }
+            
+            // 濡傛灉娌℃湁瀹氭椂鍣ㄤ换鍔★紝绛夊緟鎵�鏈夋楠ゅ畬鎴�
+            // 杩欑鎯呭喌閫氬父涓嶄細鍙戠敓锛屽洜涓烘墍鏈夎澶囬兘鏄畾鏃跺櫒妯″紡
         }
 
         Map<String, Object> payload = new HashMap<>();
@@ -140,8 +302,15 @@
         payload.put("deviceCount", devices.size());
         payload.put("executionMode", executionMode);
 
+        // 鍋滄鎵�鏈夊畾鏃跺櫒浠诲姟
+        stopScheduledTasks(task.getTaskId());
+        
+        boolean cancelled = isTaskCancelled(context);
         // 鏇存柊浠诲姟鏈�缁堢姸鎬�
-        if (success) {
+        if (cancelled) {
+            task.setStatus(MultiDeviceTask.Status.CANCELLED.name());
+            task.setErrorMessage("浠诲姟宸插彇娑�");
+        } else if (success) {
             task.setStatus(MultiDeviceTask.Status.COMPLETED.name());
         } else {
             task.setStatus(MultiDeviceTask.Status.FAILED.name());
@@ -157,6 +326,744 @@
             return TaskExecutionResult.success(payload);
         }
         return TaskExecutionResult.failure(failureMessage != null ? failureMessage : "浠诲姟鎵ц澶辫触", payload);
+    }
+
+    /**
+     * 璇锋眰鍙栨秷浠诲姟锛氬仠姝㈡墍鏈夊畾鏃跺櫒骞舵爣璁颁笂涓嬫枃
+     */
+    public void requestTaskCancellation(String taskId) {
+        TaskExecutionContext context = runningTaskContexts.get(taskId);
+        if (context != null) {
+            context.getSharedData().put("taskCancelled", true);
+            log.warn("宸叉爣璁颁换鍔″彇娑�: taskId={}", taskId);
+        } else {
+            log.warn("璇锋眰鍙栨秷浠诲姟浣嗘湭鎵惧埌涓婁笅鏂�: taskId={}", taskId);
+        }
+        stopScheduledTasks(taskId);
+    }
+    
+    /**
+     * 鍚姩鍗ц浆绔嬫壂鐮佽澶囧畾鏃跺櫒锛氭瘡10绉掑鐞嗕竴涓幓鐠僆D
+     */
+    private ScheduledFuture<?> startScannerTimer(MultiDeviceTask task,
+                                                 TaskStepDetail step,
+                                                 DeviceConfig device,
+                                                 TaskExecutionContext context) {
+        try {
+            TaskParameters params = context.getParameters();
+            List<String> glassIds = params.getGlassIds();
+            log.info("鍗ц浆绔嬫壂鐮佸畾鏃跺櫒鍒濆鍖�: taskId={}, deviceId={}, glassIds={}, glassIdsSize={}, isEmpty={}", 
+                    task.getTaskId(), device.getId(), glassIds, 
+                    glassIds != null ? glassIds.size() : 0, 
+                    CollectionUtils.isEmpty(glassIds));
+            if (CollectionUtils.isEmpty(glassIds)) {
+                log.warn("鍗ц浆绔嬫壂鐮佽澶囨病鏈夌幓鐠僆D锛屽畾鏃跺櫒涓嶅惎鍔�: deviceId={}", device.getId());
+                return null;
+            }
+            
+            // 鍒涘缓寰呭鐞嗙幓鐠僆D闃熷垪
+            Queue<String> glassIdQueue = new ConcurrentLinkedQueue<>(glassIds);
+            AtomicInteger processedCount = new AtomicInteger(0);
+            AtomicInteger successCount = new AtomicInteger(0);
+            AtomicInteger failCount = new AtomicInteger(0);
+            
+            final long CYCLE_INTERVAL_MS = 10_000; // 10绉掗棿闅�
+            
+            log.info("鍚姩鍗ц浆绔嬫壂鐮佸畾鏃跺櫒: taskId={}, deviceId={}, glassCount={}, interval={}s, glassIds={}",
+                    task.getTaskId(), device.getId(), glassIds.size(), CYCLE_INTERVAL_MS / 1000, glassIds);
+            
+            // 鍚姩瀹氭椂浠诲姟
+            ScheduledFuture<?> future = scheduledExecutor.scheduleWithFixedDelay(() -> {
+                try {
+                    if (isTaskCancelled(context)) {
+                        log.info("浠诲姟宸插彇娑堬紝鍋滄鍗ц浆绔嬫壂鐮佸畾鏃跺櫒: taskId={}, deviceId={}", 
+                                task.getTaskId(), device.getId());
+                        return;
+                    }
+                    // 妫�鏌ユ槸鍚﹂渶瑕佹殏鍋�
+                    if (shouldPauseScanner(context)) {
+                        log.debug("鍗ц浆绔嬫壂鐮佸畾鏃跺櫒鏆傚仠: taskId={}, deviceId={}", task.getTaskId(), device.getId());
+                        return;
+                    }
+                    
+                    // 妫�鏌ユ槸鍚﹁繕鏈夊緟澶勭悊鐨勭幓鐠僆D
+                    String glassId = glassIdQueue.poll();
+                    if (glassId == null) {
+                        log.info("鍗ц浆绔嬫壂鐮佸畾鏃跺櫒瀹屾垚: taskId={}, deviceId={}, processed={}/{}, success={}, fail={}",
+                                task.getTaskId(), device.getId(), processedCount.get(), glassIds.size(),
+                                successCount.get(), failCount.get());
+                        deviceCoordinationService.syncDeviceStatus(device,
+                                DeviceCoordinationService.DeviceStatus.COMPLETED, context);
+                        return;
+                    }
+                    
+                    int currentIndex = processedCount.incrementAndGet();
+                    log.info("鍗ц浆绔嬫壂鐮佸畾鏃跺櫒澶勭悊绗瑊}/{}涓幓鐠�: taskId={}, deviceId={}, glassId={}",
+                            currentIndex, glassIds.size(), task.getTaskId(), device.getId(), glassId);
+                    
+                    // 鎵ц鍗曟鎵弿
+                    Map<String, Object> scanParams = new HashMap<>();
+                    scanParams.put("glassId", glassId);
+                    scanParams.put("_taskContext", context);
+                    log.info("鍗ц浆绔嬫壂鐮佸畾鏃跺櫒鍑嗗鎵ц: taskId={}, deviceId={}, glassId={}, scanParams={}", 
+                            task.getTaskId(), device.getId(), glassId, scanParams);
+                    
+                    DeviceLogicHandler handler = handlerFactory.getHandler(device.getDeviceType());
+                    if (handler != null) {
+                        // 灏唋ogicParams鍚堝苟鍒皊canParams涓�
+                        Map<String, Object> logicParams = parseLogicParams(device);
+                        if (logicParams != null && !logicParams.isEmpty()) {
+                            scanParams.put("_logicParams", logicParams);
+                        }
+                        log.info("鍗ц浆绔嬫壂鐮佸畾鏃跺櫒璋冪敤handler.execute: taskId={}, deviceId={}, glassId={}, operation=scanOnce, scanParamsKeys={}, scanParams={}", 
+                                task.getTaskId(), device.getId(), glassId, scanParams.keySet(), scanParams);
+                        DevicePlcVO.OperationResult result = handler.execute(device, "scanOnce", scanParams);
+                        log.info("鍗ц浆绔嬫壂鐮佸畾鏃跺櫒handler.execute杩斿洖: taskId={}, deviceId={}, glassId={}, success={}", 
+                                task.getTaskId(), device.getId(), glassId, result.getSuccess());
+                        
+                        if (Boolean.TRUE.equals(result.getSuccess())) {
+                            successCount.incrementAndGet();
+                            log.info("鍗ц浆绔嬫壂鐮佸畾鏃跺櫒澶勭悊鎴愬姛: taskId={}, deviceId={}, glassId={}",
+                                    task.getTaskId(), device.getId(), glassId);
+                        } else {
+                            failCount.incrementAndGet();
+                            log.warn("鍗ц浆绔嬫壂鐮佸畾鏃跺櫒澶勭悊澶辫触: taskId={}, deviceId={}, glassId={}, error={}",
+                                    task.getTaskId(), device.getId(), glassId, result.getMessage());
+                        }
+                        
+                        // 鏇存柊姝ラ鐘舵��
+                        updateStepStatus(step, result);
+                        // 閫氱煡姝ラ鏇存柊锛堣鍓嶇瀹炴椂鐪嬪埌姝ラ鐘舵�侊級
+                        notificationService.notifyStepUpdate(task.getTaskId(), step);
+                        boolean opSuccess = Boolean.TRUE.equals(result.getSuccess());
+                        updateTaskProgress(task, step.getStepOrder(), opSuccess);
+                        if (!opSuccess) {
+                            deviceCoordinationService.syncDeviceStatus(device,
+                                    DeviceCoordinationService.DeviceStatus.FAILED, context);
+                        }
+                    }
+                } catch (Exception e) {
+                    log.error("鍗ц浆绔嬫壂鐮佸畾鏃跺櫒鎵ц寮傚父: taskId={}, deviceId={}", task.getTaskId(), device.getId(), e);
+                    failCount.incrementAndGet();
+                }
+            }, 0, CYCLE_INTERVAL_MS, TimeUnit.MILLISECONDS);
+            
+            deviceCoordinationService.syncDeviceStatus(device,
+                    DeviceCoordinationService.DeviceStatus.RUNNING, context);
+            return future;
+        } catch (Exception e) {
+            log.error("鍚姩鍗ц浆绔嬫壂鐮佸畾鏃跺櫒澶辫触: taskId={}, deviceId={}", task.getTaskId(), device.getId(), e);
+            return null;
+        }
+    }
+    
+    /**
+     * 鍚姩鍗ц浆绔嬭澶囧畾鏃跺櫒锛氬畾鏈熸鏌ュ苟澶勭悊鐜荤拑鎵规
+     */
+    private ScheduledFuture<?> startTransferTimer(MultiDeviceTask task,
+                                                  TaskStepDetail step,
+                                                  DeviceConfig device,
+                                                  TaskExecutionContext context) {
+        try {
+            // 浠庤澶囬厤缃腑鑾峰彇鐩戞帶闂撮殧锛岄粯璁�5绉�
+            Map<String, Object> logicParams = parseLogicParams(device);
+            Integer monitorIntervalMs = getLogicParam(logicParams, "monitorIntervalMs", 5_000);
+            
+            log.info("鍚姩鍗ц浆绔嬭澶囧畾鏃跺櫒: taskId={}, deviceId={}, interval={}ms",
+                    task.getTaskId(), device.getId(), monitorIntervalMs);
+            
+            // 鍚姩瀹氭椂浠诲姟
+            ScheduledFuture<?> future = scheduledExecutor.scheduleWithFixedDelay(() -> {
+                try {
+                    if (isTaskCancelled(context)) {
+                        log.info("浠诲姟宸插彇娑堬紝鍋滄鍗ц浆绔嬭澶囧畾鏃跺櫒: taskId={}, deviceId={}",
+                                task.getTaskId(), device.getId());
+                        return;
+                    }
+                    // 鏋勫缓鍙傛暟
+                    Map<String, Object> params = new HashMap<>();
+                    params.put("_taskContext", context);
+                    if (logicParams != null && !logicParams.isEmpty()) {
+                        params.put("_logicParams", logicParams);
+                    }
+                    
+                    // 璋冪敤handler鎵цcheckAndProcess
+                    DeviceLogicHandler handler = handlerFactory.getHandler(device.getDeviceType());
+                    if (handler != null) {
+                        DevicePlcVO.OperationResult result = handler.execute(device, "checkAndProcess", params);
+                        
+                        // 鏇存柊姝ラ鐘舵��
+                        updateStepStatus(step, result);
+                        // 閫氱煡姝ラ鏇存柊锛堣鍓嶇瀹炴椂鐪嬪埌姝ラ鐘舵�侊級
+                        notificationService.notifyStepUpdate(task.getTaskId(), step);
+                        boolean opSuccess = Boolean.TRUE.equals(result.getSuccess());
+                        updateTaskProgress(task, step.getStepOrder(), opSuccess);
+                        if (opSuccess) {
+                            log.debug("鍗ц浆绔嬭澶囧畾鏃跺櫒鎵ц鎴愬姛: taskId={}, deviceId={}, message={}",
+                                    task.getTaskId(), device.getId(), result.getMessage());
+                        } else {
+                            log.warn("鍗ц浆绔嬭澶囧畾鏃跺櫒鎵ц澶辫触: taskId={}, deviceId={}, message={}",
+                                    task.getTaskId(), device.getId(), result.getMessage());
+                            deviceCoordinationService.syncDeviceStatus(device,
+                                    DeviceCoordinationService.DeviceStatus.FAILED, context);
+                        }
+                    }
+                } catch (Exception e) {
+                    log.error("鍗ц浆绔嬭澶囧畾鏃跺櫒鎵ц寮傚父: taskId={}, deviceId={}", task.getTaskId(), device.getId(), e);
+                }
+            }, 0, monitorIntervalMs, TimeUnit.MILLISECONDS);
+            
+            deviceCoordinationService.syncDeviceStatus(device,
+                    DeviceCoordinationService.DeviceStatus.RUNNING, context);
+            return future;
+        } catch (Exception e) {
+            log.error("鍚姩鍗ц浆绔嬭澶囧畾鏃跺櫒澶辫触: taskId={}, deviceId={}", task.getTaskId(), device.getId(), e);
+            return null;
+        }
+    }
+    
+    /**
+     * 鍚姩杩涚墖澶ц溅璁惧瀹氭椂鍣細鎸佺画鐩戞帶瀹归噺锛屽姩鎬佸垽鏂�
+     */
+    private ScheduledFuture<?> startInboundVehicleTimer(MultiDeviceTask task,
+                                                        TaskStepDetail step,
+                                                        DeviceConfig device,
+                                                        TaskExecutionContext context) {
+        try {
+            final long MONITOR_INTERVAL_MS = 2_000; // 2绉掔洃鎺т竴娆�
+            final AtomicInteger lastProcessedCount = new AtomicInteger(0);
+            
+            log.info("鍚姩杩涚墖澶ц溅璁惧瀹氭椂鍣�: taskId={}, deviceId={}, interval={}s",
+                    task.getTaskId(), device.getId(), MONITOR_INTERVAL_MS / 1000);
+            
+            // 鍚姩瀹氭椂浠诲姟
+            ScheduledFuture<?> future = scheduledExecutor.scheduleWithFixedDelay(() -> {
+                try {
+                    if (isTaskCancelled(context)) {
+                        log.info("浠诲姟宸插彇娑堬紝鍋滄杩涚墖澶ц溅瀹氭椂鍣�: taskId={}, deviceId={}",
+                                task.getTaskId(), device.getId());
+                        return;
+                    }
+                    // 妫�鏌ユ槸鍚︽湁宸叉壂鎻忕殑鐜荤拑淇℃伅
+                    List<String> scannedGlassIds = getScannedGlassIds(context);
+                    if (CollectionUtils.isEmpty(scannedGlassIds)) {
+                        // 娌℃湁宸叉壂鎻忕殑鐜荤拑锛岀‘淇濆崸杞珛鎵爜缁х画杩愯
+                        setScannerPause(context, false);
+                        return;
+                    }
+                    
+                    // 濡傛灉鐜荤拑ID鏁伴噺娌℃湁鍙樺寲锛岃鏄庢病鏈夋柊鐨勭幓鐠冿紝缁х画绛夊緟
+                    int currentCount = scannedGlassIds.size();
+                    if (currentCount == lastProcessedCount.get()) {
+                        log.debug("澶ц溅璁惧瀹氭椂鍣細鐜荤拑ID鏁伴噺鏈彉鍖栵紝缁х画绛夊緟: taskId={}, deviceId={}, count={}",
+                                task.getTaskId(), device.getId(), currentCount);
+                        return;
+                    }
+                    
+                    log.info("杩涚墖澶ц溅璁惧瀹氭椂鍣ㄦ娴嬪埌鏂扮殑鐜荤拑淇℃伅: taskId={}, deviceId={}, glassCount={}",
+                            task.getTaskId(), device.getId(), currentCount);
+                    
+                    // 妫�鏌ュ閲�
+                    Map<String, Object> checkParams = new HashMap<>();
+                    checkParams.put("glassIds", new ArrayList<>(scannedGlassIds));
+                    checkParams.put("_taskContext", context);
+                    
+                    DeviceLogicHandler handler = handlerFactory.getHandler(device.getDeviceType());
+                    if (handler != null) {
+                        // 灏唋ogicParams鍚堝苟鍒癱heckParams涓�
+                        Map<String, Object> logicParams = parseLogicParams(device);
+                        if (logicParams != null && !logicParams.isEmpty()) {
+                            checkParams.put("_logicParams", logicParams);
+                        }
+                        DevicePlcVO.OperationResult result = handler.execute(device, "feedGlass", checkParams);
+                        
+                        if (Boolean.TRUE.equals(result.getSuccess())) {
+                            log.info("杩涚墖澶ц溅璁惧瀹氭椂鍣ㄦ墽琛屾垚鍔�: taskId={}, deviceId={}, glassCount={}",
+                                    task.getTaskId(), device.getId(), scannedGlassIds.size());
+                            // 灏嗗凡瑁呰浇鐨勭幓鐠僆D淇濆瓨鍒板叡浜暟鎹腑锛堜緵澶х悊鐗囩浣跨敤锛�
+                            setLoadedGlassIds(context, new ArrayList<>(scannedGlassIds));
+                            // 娓呯┖宸叉壂鎻忕殑鐜荤拑ID鍒楄〃锛堝凡澶勭悊锛�
+                            clearScannedGlassIds(context);
+                            lastProcessedCount.set(0);
+                            // 纭繚鍗ц浆绔嬫壂鐮佺户缁繍琛�
+                            setScannerPause(context, false);
+                        } else {
+                            // 瑁呬笉涓嬶紝閫氱煡鍗ц浆绔嬫壂鐮佹殏鍋�
+                            log.warn("杩涚墖澶ц溅璁惧瀹氭椂鍣ㄥ閲忎笉瓒�: taskId={}, deviceId={}, message={}, 宸查�氱煡鍗ц浆绔嬫壂鐮佹殏鍋�",
+                                    task.getTaskId(), device.getId(), result.getMessage());
+                            setScannerPause(context, true);
+                            lastProcessedCount.set(currentCount); // 璁板綍褰撳墠鏁伴噺锛岄伩鍏嶉噸澶嶆鏌�
+                        }
+                        
+                        // 鏇存柊姝ラ鐘舵��
+                        updateStepStatus(step, result);
+                        boolean opSuccess = Boolean.TRUE.equals(result.getSuccess());
+                        updateTaskProgress(task, step.getStepOrder(), opSuccess);
+                        if (!opSuccess) {
+                            deviceCoordinationService.syncDeviceStatus(device,
+                                    DeviceCoordinationService.DeviceStatus.FAILED, context);
+                        }
+                    }
+                } catch (Exception e) {
+                    log.error("杩涚墖澶ц溅璁惧瀹氭椂鍣ㄦ墽琛屽紓甯�: taskId={}, deviceId={}", task.getTaskId(), device.getId(), e);
+                }
+            }, 0, MONITOR_INTERVAL_MS, TimeUnit.MILLISECONDS);
+            
+            deviceCoordinationService.syncDeviceStatus(device,
+                    DeviceCoordinationService.DeviceStatus.RUNNING, context);
+            return future;
+        } catch (Exception e) {
+            log.error("鍚姩杩涚墖澶ц溅璁惧瀹氭椂鍣ㄥけ璐�: taskId={}, deviceId={}", task.getTaskId(), device.getId(), e);
+            return null;
+        }
+    }
+    
+    /**
+     * 鍚姩鍑虹墖澶ц溅璁惧瀹氭椂鍣細鎸佺画鐩戞帶鍑虹墖浠诲姟
+     */
+    private ScheduledFuture<?> startOutboundVehicleTimer(MultiDeviceTask task,
+                                                         TaskStepDetail step,
+                                                         DeviceConfig device,
+                                                         TaskExecutionContext context) {
+        try {
+            final long MONITOR_INTERVAL_MS = 2_000; // 2绉掔洃鎺т竴娆�
+            
+            log.info("鍚姩鍑虹墖澶ц溅璁惧瀹氭椂鍣�: taskId={}, deviceId={}, interval={}s",
+                    task.getTaskId(), device.getId(), MONITOR_INTERVAL_MS / 1000);
+            
+            // 鍚姩瀹氭椂浠诲姟
+            ScheduledFuture<?> future = scheduledExecutor.scheduleWithFixedDelay(() -> {
+                try {
+                    if (isTaskCancelled(context)) {
+                        log.info("浠诲姟宸插彇娑堬紝鍋滄鍑虹墖澶ц溅瀹氭椂鍣�: taskId={}, deviceId={}",
+                                task.getTaskId(), device.getId());
+                        return;
+                    }
+                    // 妫�鏌ユ槸鍚︽湁宸插鐞嗙殑鐜荤拑淇℃伅锛堜粠澶х悊鐗囩鏉ョ殑锛�
+                    List<String> processedGlassIds = getProcessedGlassIds(context);
+                    if (CollectionUtils.isEmpty(processedGlassIds)) {
+                        log.debug("鍑虹墖澶ц溅璁惧瀹氭椂鍣細鏆傛棤宸插鐞嗙殑鐜荤拑淇℃伅: taskId={}, deviceId={}",
+                                task.getTaskId(), device.getId());
+                        return;
+                    }
+                    
+                    log.info("鍑虹墖澶ц溅璁惧瀹氭椂鍣ㄦ娴嬪埌宸插鐞嗙殑鐜荤拑淇℃伅: taskId={}, deviceId={}, glassCount={}",
+                            task.getTaskId(), device.getId(), processedGlassIds.size());
+                    
+                    // 鎵ц鍑虹墖鎿嶄綔
+                    Map<String, Object> checkParams = new HashMap<>();
+                    checkParams.put("glassIds", new ArrayList<>(processedGlassIds));
+                    checkParams.put("_taskContext", context);
+                    
+                    DeviceLogicHandler handler = handlerFactory.getHandler(device.getDeviceType());
+                    if (handler != null) {
+                        // 灏唋ogicParams鍚堝苟鍒癱heckParams涓�
+                        Map<String, Object> logicParams = parseLogicParams(device);
+                        if (logicParams != null && !logicParams.isEmpty()) {
+                            checkParams.put("_logicParams", logicParams);
+                        }
+                        DevicePlcVO.OperationResult result = handler.execute(device, "feedGlass", checkParams);
+                        
+                        if (Boolean.TRUE.equals(result.getSuccess())) {
+                            log.info("鍑虹墖澶ц溅璁惧瀹氭椂鍣ㄦ墽琛屾垚鍔�: taskId={}, deviceId={}, glassCount={}",
+                                    task.getTaskId(), device.getId(), processedGlassIds.size());
+                            // 娓呯┖宸插鐞嗙殑鐜荤拑ID鍒楄〃锛堝凡澶勭悊锛�
+                            clearProcessedGlassIds(context);
+                        } else {
+                            log.debug("鍑虹墖澶ц溅璁惧瀹氭椂鍣ㄦ墽琛屽け璐�: taskId={}, deviceId={}, message={}",
+                                    task.getTaskId(), device.getId(), result.getMessage());
+                        }
+                        
+                        // 鏇存柊姝ラ鐘舵��
+                        updateStepStatus(step, result);
+                        boolean opSuccess = Boolean.TRUE.equals(result.getSuccess());
+                        updateTaskProgress(task, step.getStepOrder(), opSuccess);
+                        if (!opSuccess) {
+                            deviceCoordinationService.syncDeviceStatus(device,
+                                    DeviceCoordinationService.DeviceStatus.FAILED, context);
+                        }
+                    }
+                } catch (Exception e) {
+                    log.error("鍑虹墖澶ц溅璁惧瀹氭椂鍣ㄦ墽琛屽紓甯�: taskId={}, deviceId={}", task.getTaskId(), device.getId(), e);
+                }
+            }, 0, MONITOR_INTERVAL_MS, TimeUnit.MILLISECONDS);
+            
+            deviceCoordinationService.syncDeviceStatus(device,
+                    DeviceCoordinationService.DeviceStatus.RUNNING, context);
+            return future;
+        } catch (Exception e) {
+            log.error("鍚姩鍑虹墖澶ц溅璁惧瀹氭椂鍣ㄥけ璐�: taskId={}, deviceId={}", task.getTaskId(), device.getId(), e);
+            return null;
+        }
+    }
+    
+    /**
+     * 鍚姩澶х悊鐗囩璁惧瀹氭椂鍣細閫昏緫澶勭悊锛堜笉娑夊強PLC浜や簰锛屽彧璐熻矗閫昏緫澶勭悊锛屾瘮濡傚涔呯粰浠诲姟姹囨姤锛�
+     */
+    private ScheduledFuture<?> startLargeGlassTimer(MultiDeviceTask task,
+                                                    TaskStepDetail step,
+                                                    DeviceConfig device,
+                                                    TaskExecutionContext context) {
+        try {
+            // 浠庤澶囬厤缃腑鑾峰彇澶勭悊鏃堕棿锛堥粯璁�30绉掞級
+            Map<String, Object> logicParams = parseLogicParams(device);
+            Integer processTimeSeconds = getLogicParam(logicParams, "processTimeSeconds", 30);
+            final long PROCESS_TIME_MS = processTimeSeconds * 1000;
+            
+            log.info("鍚姩澶х悊鐗囩璁惧瀹氭椂鍣�: taskId={}, deviceId={}, processTime={}s",
+                    task.getTaskId(), device.getId(), processTimeSeconds);
+            
+            // 鍚姩瀹氭椂浠诲姟
+            ScheduledFuture<?> future = scheduledExecutor.scheduleWithFixedDelay(() -> {
+                try {
+                    if (isTaskCancelled(context)) {
+                        log.info("浠诲姟宸插彇娑堬紝鍋滄澶х悊鐗囩瀹氭椂鍣�: taskId={}, deviceId={}",
+                                task.getTaskId(), device.getId());
+                        return;
+                    }
+                    // 妫�鏌ユ槸鍚︽湁宸茶杞界殑鐜荤拑淇℃伅锛堜粠杩涚墖澶ц溅鏉ョ殑锛�
+                    List<String> loadedGlassIds = getLoadedGlassIds(context);
+                    if (CollectionUtils.isEmpty(loadedGlassIds)) {
+                        log.debug("澶х悊鐗囩璁惧瀹氭椂鍣細鏆傛棤宸茶杞界殑鐜荤拑淇℃伅: taskId={}, deviceId={}",
+                                task.getTaskId(), device.getId());
+                        return;
+                    }
+                    
+                    // 妫�鏌ョ幓鐠冩槸鍚﹀凡缁忓鐞嗗畬鎴愶紙閫氳繃澶勭悊鏃堕棿鍒ゆ柇锛�
+                    Long processStartTime = getProcessStartTime(context);
+                    if (processStartTime == null) {
+                        // 绗竴娆℃娴嬪埌鐜荤拑锛岃褰曞紑濮嬪鐞嗘椂闂�
+                        setProcessStartTime(context, System.currentTimeMillis());
+                        log.info("澶х悊鐗囩璁惧寮�濮嬪鐞�: taskId={}, deviceId={}, glassCount={}, processTime={}s",
+                                task.getTaskId(), device.getId(), loadedGlassIds.size(), processTimeSeconds);
+                        return;
+                    }
+                    
+                    long elapsed = System.currentTimeMillis() - processStartTime;
+                    if (elapsed < PROCESS_TIME_MS) {
+                        // 澶勭悊鏃堕棿鏈埌锛岀户缁瓑寰�
+                        log.debug("澶х悊鐗囩璁惧澶勭悊涓�: taskId={}, deviceId={}, elapsed={}s, remaining={}s",
+                                task.getTaskId(), device.getId(), elapsed / 1000, (PROCESS_TIME_MS - elapsed) / 1000);
+                        return;
+                    }
+                    
+                    // 澶勭悊鏃堕棿宸插埌锛屽畬鎴愪换鍔℃眹鎶�
+                    log.info("澶х悊鐗囩璁惧澶勭悊瀹屾垚: taskId={}, deviceId={}, glassCount={}, processTime={}s",
+                            task.getTaskId(), device.getId(), loadedGlassIds.size(), processTimeSeconds);
+                    
+                    // 灏嗗凡澶勭悊鐨勭幓鐠僆D杞Щ鍒板凡澶勭悊鍒楄〃锛堜緵鍑虹墖澶ц溅浣跨敤锛�
+                    setProcessedGlassIds(context, new ArrayList<>(loadedGlassIds));
+                    clearLoadedGlassIds(context);
+                    clearProcessStartTime(context);
+                    
+                    // 鏇存柊姝ラ鐘舵��
+                    step.setStatus(TaskStepDetail.Status.COMPLETED.name());
+                    step.setErrorMessage(null);
+                    step.setOutputData(toJson(Collections.singletonMap("glassIds", loadedGlassIds)));
+                    taskStepDetailMapper.updateById(step);
+                    
+                } catch (Exception e) {
+                    log.error("澶х悊鐗囩璁惧瀹氭椂鍣ㄦ墽琛屽紓甯�: taskId={}, deviceId={}", task.getTaskId(), device.getId(), e);
+                }
+            }, 0, 1_000, TimeUnit.MILLISECONDS); // 姣忕妫�鏌ヤ竴娆�
+            
+            return future;
+        } catch (Exception e) {
+            log.error("鍚姩澶х悊鐗囩璁惧瀹氭椂鍣ㄥけ璐�: taskId={}, deviceId={}", task.getTaskId(), device.getId(), e);
+            return null;
+        }
+    }
+    
+    /**
+     * 鑾峰彇閫昏緫鍙傛暟
+     */
+    @SuppressWarnings("unchecked")
+    private <T> T getLogicParam(Map<String, Object> logicParams, String key, T defaultValue) {
+        if (logicParams == null) {
+            return defaultValue;
+        }
+        Object value = logicParams.get(key);
+        if (value == null) {
+            return defaultValue;
+        }
+        try {
+            return (T) value;
+        } catch (ClassCastException e) {
+            return defaultValue;
+        }
+    }
+    
+    /**
+     * 鑾峰彇宸茶杞界殑鐜荤拑ID鍒楄〃
+     */
+    @SuppressWarnings("unchecked")
+    private List<String> getLoadedGlassIds(TaskExecutionContext context) {
+        if (context == null) {
+            return Collections.emptyList();
+        }
+        Object glassIds = context.getSharedData().get("loadedGlassIds");
+        if (glassIds instanceof List) {
+            return new ArrayList<>((List<String>) glassIds);
+        }
+        return Collections.emptyList();
+    }
+    
+    /**
+     * 璁剧疆宸茶杞界殑鐜荤拑ID鍒楄〃
+     */
+    private void setLoadedGlassIds(TaskExecutionContext context, List<String> glassIds) {
+        if (context != null) {
+            context.getSharedData().put("loadedGlassIds", new ArrayList<>(glassIds));
+        }
+    }
+    
+    /**
+     * 娓呯┖宸茶杞界殑鐜荤拑ID鍒楄〃
+     */
+    private void clearLoadedGlassIds(TaskExecutionContext context) {
+        if (context != null) {
+            context.getSharedData().put("loadedGlassIds", new ArrayList<>());
+        }
+    }
+    
+    /**
+     * 鑾峰彇宸插鐞嗙殑鐜荤拑ID鍒楄〃
+     */
+    @SuppressWarnings("unchecked")
+    private List<String> getProcessedGlassIds(TaskExecutionContext context) {
+        if (context == null) {
+            return Collections.emptyList();
+        }
+        Object glassIds = context.getSharedData().get("processedGlassIds");
+        if (glassIds instanceof List) {
+            return new ArrayList<>((List<String>) glassIds);
+        }
+        return Collections.emptyList();
+    }
+    
+    /**
+     * 璁剧疆宸插鐞嗙殑鐜荤拑ID鍒楄〃
+     */
+    private void setProcessedGlassIds(TaskExecutionContext context, List<String> glassIds) {
+        if (context != null) {
+            context.getSharedData().put("processedGlassIds", new ArrayList<>(glassIds));
+        }
+    }
+    
+    /**
+     * 娓呯┖宸插鐞嗙殑鐜荤拑ID鍒楄〃
+     */
+    private void clearProcessedGlassIds(TaskExecutionContext context) {
+        if (context != null) {
+            context.getSharedData().put("processedGlassIds", new ArrayList<>());
+        }
+    }
+    
+    /**
+     * 鑾峰彇澶勭悊寮�濮嬫椂闂�
+     */
+    private Long getProcessStartTime(TaskExecutionContext context) {
+        if (context == null) {
+            return null;
+        }
+        Object time = context.getSharedData().get("processStartTime");
+        if (time instanceof Number) {
+            return ((Number) time).longValue();
+        }
+        return null;
+    }
+    
+    /**
+     * 璁剧疆澶勭悊寮�濮嬫椂闂�
+     */
+    private void setProcessStartTime(TaskExecutionContext context, long time) {
+        if (context != null) {
+            context.getSharedData().put("processStartTime", time);
+        }
+    }
+    
+    /**
+     * 娓呯┖澶勭悊寮�濮嬫椂闂�
+     */
+    private void clearProcessStartTime(TaskExecutionContext context) {
+        if (context != null) {
+            context.getSharedData().remove("processStartTime");
+        }
+    }
+    
+    /**
+     * 璁剧疆鍗ц浆绔嬫壂鐮佹殏鍋滄爣蹇�
+     */
+    private void setScannerPause(TaskExecutionContext context, boolean pause) {
+        if (context != null) {
+            context.getSharedData().put("scannerPause", pause);
+        }
+    }
+
+    private boolean isTaskCancelled(TaskExecutionContext context) {
+        if (context == null) {
+            return false;
+        }
+        Object cancelled = context.getSharedData().get("taskCancelled");
+        return cancelled instanceof Boolean && (Boolean) cancelled;
+    }
+    
+    /**
+     * 妫�鏌ユ槸鍚﹂渶瑕佹殏鍋滃崸杞珛鎵爜
+     */
+    private boolean shouldPauseScanner(TaskExecutionContext context) {
+        if (context == null) {
+            return false;
+        }
+        Object pauseFlag = context.getSharedData().get("scannerPause");
+        return pauseFlag instanceof Boolean && (Boolean) pauseFlag;
+    }
+    
+    /**
+     * 鑾峰彇宸叉壂鎻忕殑鐜荤拑ID鍒楄〃
+     */
+    @SuppressWarnings("unchecked")
+    private List<String> getScannedGlassIds(TaskExecutionContext context) {
+        if (context == null) {
+            return Collections.emptyList();
+        }
+        Object glassIds = context.getSharedData().get("scannedGlassIds");
+        if (glassIds instanceof List) {
+            return new ArrayList<>((List<String>) glassIds);
+        }
+        return Collections.emptyList();
+    }
+    
+    /**
+     * 娓呯┖宸叉壂鎻忕殑鐜荤拑ID鍒楄〃
+     */
+    private void clearScannedGlassIds(TaskExecutionContext context) {
+        if (context != null) {
+            context.getSharedData().put("scannedGlassIds", new ArrayList<>());
+        }
+    }
+    
+    /**
+     * 娉ㄥ唽瀹氭椂鍣ㄤ换鍔�
+     */
+    private void registerScheduledTask(String taskId, ScheduledFuture<?> future) {
+        taskScheduledTasks.computeIfAbsent(taskId, k -> new ArrayList<>()).add(future);
+    }
+    
+    /**
+     * 鍋滄鎵�鏈夊畾鏃跺櫒浠诲姟
+     */
+    private void stopScheduledTasks(String taskId) {
+        List<ScheduledFuture<?>> futures = taskScheduledTasks.remove(taskId);
+        if (futures != null) {
+            for (ScheduledFuture<?> future : futures) {
+                if (future != null && !future.isCancelled()) {
+                    future.cancel(false);
+                }
+            }
+            log.info("宸插仠姝换鍔$殑鎵�鏈夊畾鏃跺櫒: taskId={}, count={}", taskId, futures.size());
+        }
+        runningTaskContexts.remove(taskId);
+    }
+    
+    /**
+     * 绛夊緟瀹氭椂鍣ㄤ换鍔″畬鎴愶紙甯﹁秴鏃讹級
+     */
+    private void waitForScheduledTasks(String taskId, TaskExecutionContext context) {
+        // 鑾峰彇浠诲姟瓒呮椂鏃堕棿锛堥粯璁�30鍒嗛挓锛�
+        TaskParameters params = context.getParameters();
+        long timeoutMinutes = params != null && params.getTimeoutMinutes() != null
+                ? params.getTimeoutMinutes() : 30;
+        long timeoutMs = timeoutMinutes * 60 * 1000;
+        long deadline = System.currentTimeMillis() + timeoutMs;
+        
+        log.info("绛夊緟瀹氭椂鍣ㄤ换鍔″畬鎴�: taskId={}, timeout={}鍒嗛挓", taskId, timeoutMinutes);
+        
+        while (System.currentTimeMillis() < deadline) {
+            List<ScheduledFuture<?>> futures = taskScheduledTasks.get(taskId);
+            if (futures == null || futures.isEmpty()) {
+                break;
+            }
+            
+            // 妫�鏌ユ槸鍚︽墍鏈変换鍔¢兘宸插畬鎴�
+            boolean allDone = true;
+            for (ScheduledFuture<?> future : futures) {
+                if (future != null && !future.isDone()) {
+                    allDone = false;
+                    break;
+                }
+            }
+            
+            if (allDone) {
+                break;
+            }
+            
+            try {
+                Thread.sleep(1000);
+            } catch (InterruptedException e) {
+                Thread.currentThread().interrupt();
+                break;
+            }
+        }
+        
+        log.info("瀹氭椂鍣ㄤ换鍔$瓑寰呭畬鎴�: taskId={}", taskId);
+    }
+    
+    /**
+     * 鏇存柊姝ラ鐘舵��
+     */
+    private void updateStepStatus(TaskStepDetail step, DevicePlcVO.OperationResult result) {
+        if (step == null || result == null) {
+            return;
+        }
+        boolean success = Boolean.TRUE.equals(result.getSuccess());
+        step.setStatus(success 
+                ? TaskStepDetail.Status.COMPLETED.name() 
+                : TaskStepDetail.Status.FAILED.name());
+        // 璁剧疆娑堟伅锛氭垚鍔熸椂濡傛灉鏈夋秷鎭篃淇濆瓨锛屽け璐ユ椂淇濆瓨閿欒娑堟伅
+        String message = result.getMessage();
+        if (success) {
+            // 鎴愬姛鏃讹紝濡傛灉鏈夋秷鎭垯淇濆瓨锛堢敤浜庢彁绀轰俊鎭級锛屽惁鍒欐竻绌�
+            step.setSuccessMessage(StringUtils.hasText(message) ? message : null);
+        } else {
+            // 澶辫触鏃朵繚瀛橀敊璇秷鎭�
+            step.setErrorMessage(message);
+        }
+        step.setOutputData(toJson(result));
+        taskStepDetailMapper.updateById(step);
+    }
+    
+    /**
+     * 鍒涘缓姝ラ鎽樿
+     */
+    private Map<String, Object> createStepSummary(String deviceName, boolean success, String message) {
+        Map<String, Object> summary = new HashMap<>();
+        summary.put("deviceName", deviceName);
+        summary.put("success", success);
+        summary.put("message", message);
+        return summary;
+    }
+    
+    /**
+     * 瑙f瀽璁惧閫昏緫鍙傛暟
+     */
+    @SuppressWarnings("unchecked")
+    private Map<String, Object> parseLogicParams(DeviceConfig device) {
+        String extraParams = device.getExtraParams();
+        if (!StringUtils.hasText(extraParams)) {
+            return Collections.emptyMap();
+        }
+        try {
+            Map<String, Object> extraParamsMap = objectMapper.readValue(extraParams, MAP_TYPE);
+            Object deviceLogic = extraParamsMap.get("deviceLogic");
+            if (deviceLogic instanceof Map) {
+                return (Map<String, Object>) deviceLogic;
+            }
+            return Collections.emptyMap();
+        } catch (Exception e) {
+            log.warn("瑙f瀽璁惧閫昏緫鍙傛暟澶辫触: deviceId={}", device.getId(), e);
+            return Collections.emptyMap();
+        }
     }
 
     /**
@@ -346,6 +1253,74 @@
         }
     }
 
+    /**
+     * 鍒嗘壒鎵ц澶ц溅璁惧鐜荤拑涓婃枡锛堝綋鐜荤拑ID鏁伴噺瓒呰繃6涓笖璁剧疆浜嗗崟鐗囬棿闅旀椂锛�
+     */
+    private StepResult executeLoadVehicleWithBatches(MultiDeviceTask task,
+                                                      DeviceConfig device,
+                                                      int order,
+                                                      TaskExecutionContext context,
+                                                      List<Map<String, Object>> stepSummaries) {
+        List<String> allGlassIds = context.getParameters().getGlassIds();
+        Integer glassIntervalMs = context.getParameters().getGlassIntervalMs();
+        int batchSize = 6; // 姣忔壒鏈�澶�6涓幓鐠僆D
+        
+        // 鍒嗘壒澶勭悊
+        int totalBatches = (allGlassIds.size() + batchSize - 1) / batchSize;
+        log.info("澶ц溅璁惧鍒嗘壒涓婃枡: deviceId={}, totalGlassIds={}, batchSize={}, totalBatches={}, glassIntervalMs={}",
+                device.getId(), allGlassIds.size(), batchSize, totalBatches, glassIntervalMs);
+        
+        for (int batchIndex = 0; batchIndex < totalBatches; batchIndex++) {
+            int startIndex = batchIndex * batchSize;
+            int endIndex = Math.min(startIndex + batchSize, allGlassIds.size());
+            List<String> batchGlassIds = allGlassIds.subList(startIndex, endIndex);
+            
+            // 鍒涘缓涓存椂鍙傛暟锛屽彧鍖呭惈褰撳墠鎵规鐨勭幓鐠僆D
+            TaskParameters batchParams = new TaskParameters();
+            batchParams.setGlassIds(new ArrayList<>(batchGlassIds));
+            batchParams.setGlassIntervalMs(glassIntervalMs);
+            batchParams.setPositionCode(context.getParameters().getPositionCode());
+            batchParams.setPositionValue(context.getParameters().getPositionValue());
+            
+            // 鍒涘缓涓存椂涓婁笅鏂�
+            TaskExecutionContext batchContext = new TaskExecutionContext(batchParams);
+            
+            // 鍒涘缓姝ラ璁板綍
+            TaskStepDetail step = createStepRecord(task, device, order);
+            step.setStepName(step.getStepName() + String.format(" (鎵规 %d/%d)", batchIndex + 1, totalBatches));
+            
+            // 鎵ц褰撳墠鎵规
+            StepResult stepResult = executeStep(task, step, device, batchContext);
+            stepSummaries.add(stepResult.toSummary());
+            
+            if (!stepResult.isSuccess()) {
+                log.error("澶ц溅璁惧鍒嗘壒涓婃枡澶辫触: deviceId={}, batchIndex={}/{}, error={}",
+                        device.getId(), batchIndex + 1, totalBatches, stepResult.getMessage());
+                return stepResult;
+            }
+            
+            log.info("澶ц溅璁惧鍒嗘壒涓婃枡鎴愬姛: deviceId={}, batchIndex={}/{}, glassIds={}",
+                    device.getId(), batchIndex + 1, totalBatches, batchGlassIds);
+            
+            // 濡傛灉涓嶆槸鏈�鍚庝竴鎵癸紝绛夊緟闂撮殧锛堟ā鎷熺幓鐠冩瘡鐗囪繍鍔ㄧ殑鏃堕棿锛�
+            // 杩欎釜绛夊緟璁╁ぇ杞︽湁鏃堕棿澶勭悊褰撳墠鎵规鐨勭幓鐠冿紝鐒跺悗鍐嶄紶閫掍笅涓�鎵�
+            if (batchIndex < totalBatches - 1 && glassIntervalMs != null && glassIntervalMs > 0) {
+                try {
+                    log.info("绛夊緟鍗曠墖闂撮殧锛堟ā鎷熺幓鐠冭繍鍔ㄦ椂闂达級: glassIntervalMs={}ms, 澶ц溅鍙湪姝ゆ湡闂寸户缁鐜荤拑", glassIntervalMs);
+                    Thread.sleep(glassIntervalMs);
+                } catch (InterruptedException e) {
+                    Thread.currentThread().interrupt();
+                    return StepResult.failure(device.getDeviceName(), "绛夊緟鍗曠墖闂撮殧鏃惰涓柇");
+                }
+            }
+        }
+        
+        // 鏇存柊涓婁笅鏂囦腑鐨勫凡鍔犺浇鐜荤拑ID
+        context.setLoadedGlassIds(new ArrayList<>(allGlassIds));
+        
+        return StepResult.success(device.getDeviceName(), "鍒嗘壒涓婃枡瀹屾垚锛屽叡" + totalBatches + "鎵�");
+    }
+
     private TaskStepDetail createStepRecord(MultiDeviceTask task, DeviceConfig device, int order) {
         TaskStepDetail step = new TaskStepDetail();
         step.setTaskId(task.getTaskId());
@@ -362,6 +1337,18 @@
                                    TaskStepDetail step,
                                    DeviceConfig device,
                                    TaskExecutionContext context) {
+        DeviceCoordinationService.DependencyCheckResult dependencyResult =
+                deviceCoordinationService.checkDependencies(device, context);
+        if (!dependencyResult.isSatisfied()) {
+            log.warn("璁惧渚濊禆鏈弧瓒�: deviceId={}, message={}", device.getId(), dependencyResult.getMessage());
+            step.setStatus(TaskStepDetail.Status.FAILED.name());
+            step.setErrorMessage(dependencyResult.getMessage());
+            step.setStartTime(new Date());
+            step.setEndTime(new Date());
+            taskStepDetailMapper.updateById(step);
+            updateTaskProgress(task, step.getStepOrder(), false);
+            return StepResult.failure(device.getDeviceName(), dependencyResult.getMessage());
+        }
         return executeStepWithRetry(task, step, device, context, getRetryPolicy(device));
     }
 
@@ -384,6 +1371,10 @@
         }
 
         Map<String, Object> params = buildOperationParams(device, context);
+        // 灏哻ontext寮曠敤鏀惧叆params锛屼緵璁惧澶勭悊鍣ㄤ娇鐢紙鐢ㄤ簬璁惧鍗忚皟锛�
+        params.put("_taskContext", context);
+        log.info("executeStepWithRetry鏋勫缓鍙傛暟: deviceId={}, deviceType={}, operation={}, paramsKeys={}, params={}", 
+                device.getId(), device.getDeviceType(), determineOperation(device, params), params.keySet(), params);
         step.setInputData(toJson(params));
         taskStepDetailMapper.updateById(step);
 
@@ -424,7 +1415,7 @@
                 notificationService.notifyStepUpdate(task.getTaskId(), step);
 
                 if (opSuccess) {
-                    updateContextAfterSuccess(device, context, params);
+                    updateContextAfterSuccess(device, context, params, result);
                     
                     // 鍚屾璁惧鐘舵��
                     deviceCoordinationService.syncDeviceStatus(device, 
@@ -511,6 +1502,69 @@
         
         return StepResult.failure(device.getDeviceName(), 
             String.format("鎵ц澶辫触锛堝凡閲嶈瘯%d娆★級", retryAttempt));
+    }
+
+    /**
+     * 鎵ц涓�娆$畝鍗曠殑璁惧鎿嶄綔姝ラ锛堜笉璧颁氦浜掑紩鎿庯級锛岀敤浜庤Е鍙戣姹傜瓑鍦烘櫙
+     */
+    private StepResult executeDirectOperationStep(MultiDeviceTask task,
+                                                  TaskStepDetail step,
+                                                  DeviceConfig device,
+                                                  TaskExecutionContext context,
+                                                  String operation,
+                                                  Map<String, Object> params) {
+        Date startTime = new Date();
+        step.setStartTime(startTime);
+        step.setStatus(TaskStepDetail.Status.RUNNING.name());
+        step.setRetryCount(0);
+        step.setInputData(toJson(params));
+        taskStepDetailMapper.updateById(step);
+
+        try {
+            DeviceCoordinationService.DependencyCheckResult dependencyResult =
+                    deviceCoordinationService.checkDependencies(device, context);
+            if (!dependencyResult.isSatisfied()) {
+                log.warn("鐩存帴鎿嶄綔渚濊禆鏈弧瓒�: deviceId={}, message={}", device.getId(), dependencyResult.getMessage());
+                step.setStatus(TaskStepDetail.Status.FAILED.name());
+                step.setErrorMessage(dependencyResult.getMessage());
+                step.setEndTime(new Date());
+                step.setDurationMs(step.getEndTime().getTime() - step.getStartTime().getTime());
+                taskStepDetailMapper.updateById(step);
+                updateTaskProgress(task, step.getStepOrder(), false);
+                return StepResult.failure(device.getDeviceName(), dependencyResult.getMessage());
+            }
+
+            DevicePlcVO.OperationResult result = deviceInteractionService.executeOperation(
+                    device.getId(), operation, params);
+
+            boolean opSuccess = Boolean.TRUE.equals(result.getSuccess());
+            updateStepAfterOperation(step, result, opSuccess);
+            updateTaskProgress(task, step.getStepOrder(), opSuccess);
+
+            if (opSuccess) {
+                updateContextAfterSuccess(device, context, params, result);
+                // 绠�鍗曞悓姝ヨ澶囩姸鎬佷负宸插畬鎴�
+                deviceCoordinationService.syncDeviceStatus(device,
+                        DeviceCoordinationService.DeviceStatus.COMPLETED, context);
+                return StepResult.success(device.getDeviceName(), result.getMessage());
+            } else {
+                deviceCoordinationService.syncDeviceStatus(device,
+                        DeviceCoordinationService.DeviceStatus.FAILED, context);
+                return StepResult.failure(device.getDeviceName(), result.getMessage());
+            }
+        } catch (Exception e) {
+            log.error("鐩存帴璁惧鎿嶄綔寮傚父, deviceId={}, operation={}", device.getId(), operation, e);
+            step.setStatus(TaskStepDetail.Status.FAILED.name());
+            step.setErrorMessage(e.getMessage());
+            step.setEndTime(new Date());
+            step.setDurationMs(step.getEndTime().getTime() - step.getStartTime().getTime());
+            taskStepDetailMapper.updateById(step);
+            updateTaskProgress(task, step.getStepOrder(), false);
+
+            deviceCoordinationService.syncDeviceStatus(device,
+                    DeviceCoordinationService.DeviceStatus.FAILED, context);
+            return StepResult.failure(device.getDeviceName(), e.getMessage());
+        }
     }
 
     /**
@@ -652,7 +1706,15 @@
             step.setDurationMs(step.getEndTime().getTime() - step.getStartTime().getTime());
         }
         step.setStatus(success ? TaskStepDetail.Status.COMPLETED.name() : TaskStepDetail.Status.FAILED.name());
-        step.setErrorMessage(success ? null : result.getMessage());
+        // 璁剧疆娑堟伅锛氭垚鍔熸椂濡傛灉鏈夋秷鎭篃淇濆瓨锛屽け璐ユ椂淇濆瓨閿欒娑堟伅
+        String message = result != null ? result.getMessage() : null;
+        if (success) {
+            // 鎴愬姛鏃讹紝濡傛灉鏈夋秷鎭垯淇濆瓨锛堢敤浜庢彁绀轰俊鎭級锛屽惁鍒欐竻绌�
+            step.setErrorMessage(StringUtils.hasText(message) ? message : null);
+        } else {
+            // 澶辫触鏃朵繚瀛橀敊璇秷鎭�
+            step.setErrorMessage(message);
+        }
         step.setOutputData(toJson(result));
         taskStepDetailMapper.updateById(step);
     }
@@ -671,20 +1733,48 @@
     }
 
     private void updateTaskProgress(MultiDeviceTask task, int currentStep, boolean success) {
-        task.setCurrentStep(currentStep);
         if (!success) {
             task.setStatus(MultiDeviceTask.Status.FAILED.name());
         }
+        
+        // 璁$畻宸插畬鎴愮殑姝ラ鏁帮紙鐢ㄤ簬杩涘害鏄剧ず锛�
+        int completedSteps = countCompletedSteps(task.getTaskId());
+        int progressStep = success
+                ? completedSteps
+                : Math.max(completedSteps, currentStep); // 澶辫触鏃惰嚦灏戞樉绀哄綋鍓嶆楠�
+        
         LambdaUpdateWrapper<MultiDeviceTask> update = Wrappers.<MultiDeviceTask>lambdaUpdate()
                 .eq(MultiDeviceTask::getId, task.getId())
-                .set(MultiDeviceTask::getCurrentStep, currentStep);
+                .set(MultiDeviceTask::getCurrentStep, progressStep);
         if (!success) {
             update.set(MultiDeviceTask::getStatus, MultiDeviceTask.Status.FAILED.name());
         }
         multiDeviceTaskMapper.update(null, update);
         
-        // 閫氱煡浠诲姟鐘舵�佹洿鏂�
+        // 鏇存柊浠诲姟瀵硅薄鐨勮繘搴︼紝鐢ㄤ簬閫氱煡
+        task.setCurrentStep(progressStep);
+        
+        // 閫氱煡浠诲姟鐘舵�佹洿鏂帮紙鍖呭惈杩涘害淇℃伅锛�
         notificationService.notifyTaskStatus(task);
+    }
+    
+    /**
+     * 缁熻宸插畬鎴愮殑姝ラ鏁�
+     */
+    private int countCompletedSteps(String taskId) {
+        if (taskId == null) {
+            return 0;
+        }
+        try {
+            return taskStepDetailMapper.selectCount(
+                Wrappers.<TaskStepDetail>lambdaQuery()
+                    .eq(TaskStepDetail::getTaskId, taskId)
+                    .eq(TaskStepDetail::getStatus, TaskStepDetail.Status.COMPLETED.name())
+            ).intValue();
+        } catch (Exception e) {
+            log.warn("缁熻宸插畬鎴愭楠ゆ暟澶辫触: taskId={}", taskId, e);
+            return 0;
+        }
     }
 
     private String determineOperation(DeviceConfig device, Map<String, Object> params) {
@@ -710,6 +1800,10 @@
                 if (taskParams.getPositionValue() != null) {
                     params.put("positionValue", taskParams.getPositionValue());
                 }
+                // 浼犻�掑崟鐗囬棿闅旈厤缃紝濡傛灉浠诲姟鍙傛暟涓湁璁剧疆锛屼紭鍏堜娇鐢ㄤ换鍔″弬鏁扮殑锛屽惁鍒欎娇鐢ㄨ澶囬厤缃殑
+                if (taskParams.getGlassIntervalMs() != null) {
+                    params.put("glassIntervalMs", taskParams.getGlassIntervalMs());
+                }
                 params.put("triggerRequest", true);
                 break;
             case DeviceConfig.DeviceType.LARGE_GLASS:
@@ -724,19 +1818,21 @@
                 params.put("processType", taskParams.getProcessType() != null ? taskParams.getProcessType() : 1);
                 params.put("triggerRequest", true);
                 break;
-            case DeviceConfig.DeviceType.GLASS_STORAGE:
-                List<String> processed = context.getSafeProcessedGlassIds();
-                if (CollectionUtils.isEmpty(processed)) {
-                    processed = context.getSafeLoadedGlassIds();
+            case DeviceConfig.DeviceType.WORKSTATION_SCANNER:
+                // 鍗ц浆绔嬫壂鐮佽澶囷細浠庝换鍔″弬鏁颁腑鑾峰彇鐜荤拑ID鍒楄〃锛屽彇绗竴涓綔涓哄綋鍓嶈娴嬭瘯鐨勭幓鐠僆D
+                // 娉ㄦ剰锛氭壂鐮佽澶囬�氬父閫氳繃瀹氭椂鍣ㄦ墽琛岋紝浣嗗鏋滈�氳繃executeStep鎵ц锛屼篃闇�瑕佷紶閫抔lassId
+                log.info("buildOperationParams澶勭悊鎵爜璁惧: deviceId={}, taskParams.glassIds={}, isEmpty={}", 
+                        device.getId(), taskParams.getGlassIds(), 
+                        CollectionUtils.isEmpty(taskParams.getGlassIds()));
+                if (!CollectionUtils.isEmpty(taskParams.getGlassIds())) {
+                    params.put("glassId", taskParams.getGlassIds().get(0));
+                    params.put("glassIds", new ArrayList<>(taskParams.getGlassIds()));
+                    log.info("buildOperationParams涓烘壂鐮佽澶囨坊鍔爂lassId: deviceId={}, glassId={}, glassIdsSize={}", 
+                            device.getId(), taskParams.getGlassIds().get(0), taskParams.getGlassIds().size());
+                } else {
+                    log.warn("buildOperationParams鎵爜璁惧glassIds涓虹┖: deviceId={}, taskParams.glassIds={}, taskParams={}", 
+                            device.getId(), taskParams.getGlassIds(), taskParams);
                 }
-                if (!CollectionUtils.isEmpty(processed)) {
-                    params.put("glassId", processed.get(0));
-                    params.put("glassIds", new ArrayList<>(processed));
-                }
-                if (taskParams.getStoragePosition() != null) {
-                    params.put("storagePosition", taskParams.getStoragePosition());
-                }
-                params.put("triggerRequest", true);
                 break;
             default:
                 if (!CollectionUtils.isEmpty(taskParams.getExtra())) {
@@ -763,10 +1859,14 @@
 
     private void updateContextAfterSuccess(DeviceConfig device,
                                            TaskExecutionContext context,
-                                           Map<String, Object> params) {
+                                           Map<String, Object> params,
+                                           DevicePlcVO.OperationResult result) {
         List<String> glassIds = extractGlassIds(params);
-        
+
         switch (device.getDeviceType()) {
+            case DeviceConfig.DeviceType.WORKSTATION_SCANNER:
+                handleScannerSuccess(context, result);
+                break;
             case DeviceConfig.DeviceType.LOAD_VEHICLE:
                 context.setLoadedGlassIds(glassIds);
                 // 鏁版嵁浼犻�掞細澶ц溅璁惧 -> 涓嬩竴涓澶�
@@ -792,6 +1892,23 @@
         }
     }
 
+    private void handleScannerSuccess(TaskExecutionContext context,
+                                      DevicePlcVO.OperationResult result) {
+        List<String> scannerGlassIds = extractGlassIdsFromResult(result);
+        if (CollectionUtils.isEmpty(scannerGlassIds)) {
+            String workLine = resolveWorkLineFromResult(result, context.getParameters());
+            scannerGlassIds = glassInfoService.getRecentScannedGlassIds(
+                    SCANNER_LOOKBACK_MINUTES, SCANNER_LOOKBACK_LIMIT, workLine);
+        }
+        if (!CollectionUtils.isEmpty(scannerGlassIds)) {
+            context.getParameters().setGlassIds(new ArrayList<>(scannerGlassIds));
+            context.setLoadedGlassIds(new ArrayList<>(scannerGlassIds));
+            log.info("鍗ц浆绔嬫壂鐮佽幏鍙栧埌鐜荤拑ID: {}", scannerGlassIds);
+        } else {
+            log.warn("鍗ц浆绔嬫壂鐮佹湭鑾峰彇鍒扮幓鐠僆D锛屽悗缁澶囧彲鑳芥棤娉曟墽琛�");
+        }
+    }
+
     private List<String> extractGlassIds(Map<String, Object> params) {
         if (params == null) {
             return Collections.emptyList();
@@ -809,6 +1926,45 @@
         return Collections.emptyList();
     }
 
+    @SuppressWarnings("unchecked")
+    private List<String> extractGlassIdsFromResult(DevicePlcVO.OperationResult result) {
+        if (result == null || result.getData() == null) {
+            return Collections.emptyList();
+        }
+        Object data = result.getData().get("glassIds");
+        if (data instanceof List) {
+            List<Object> raw = (List<Object>) data;
+            List<String> converted = new ArrayList<>();
+            for (Object item : raw) {
+                if (item != null) {
+                    converted.add(String.valueOf(item));
+                }
+            }
+            return converted;
+        }
+        if (data instanceof String && StringUtils.hasText((String) data)) {
+            return Collections.singletonList((String) data);
+        }
+        return Collections.emptyList();
+    }
+
+    private String resolveWorkLineFromResult(DevicePlcVO.OperationResult result,
+                                             TaskParameters parameters) {
+        if (result != null && result.getData() != null) {
+            Object workLine = result.getData().get("workLine");
+            if (workLine != null && StringUtils.hasText(String.valueOf(workLine))) {
+                return String.valueOf(workLine);
+            }
+        }
+        if (parameters != null && !CollectionUtils.isEmpty(parameters.getExtra())) {
+            Object extraWorkLine = parameters.getExtra().get("workLine");
+            if (extraWorkLine != null) {
+                return String.valueOf(extraWorkLine);
+            }
+        }
+        return null;
+    }
+
     private String toJson(Object value) {
         try {
             return objectMapper.writeValueAsString(value);
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/task/service/impl/MultiDeviceTaskServiceImpl.java b/mes-processes/mes-plcSend/src/main/java/com/mes/task/service/impl/MultiDeviceTaskServiceImpl.java
index 2cdac06..c2d2e35 100644
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/task/service/impl/MultiDeviceTaskServiceImpl.java
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/task/service/impl/MultiDeviceTaskServiceImpl.java
@@ -20,7 +20,6 @@
 import com.mes.task.service.MultiDeviceTaskService;
 import com.mes.task.service.TaskExecutionEngine;
 import com.mes.task.service.TaskStatusNotificationService;
-import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Service;
@@ -32,13 +31,13 @@
 import java.util.Date;
 import java.util.List;
 import java.util.Locale;
+import java.util.Map;
 
 /**
  * 澶氳澶囦换鍔℃湇鍔″疄鐜�
  */
 @Slf4j
 @Service
-@RequiredArgsConstructor
 public class MultiDeviceTaskServiceImpl extends ServiceImpl<MultiDeviceTaskMapper, MultiDeviceTask>
         implements MultiDeviceTaskService {
 
@@ -48,6 +47,21 @@
     private final TaskExecutionEngine taskExecutionEngine;
     private final TaskStatusNotificationService notificationService;
     private final ObjectMapper objectMapper;
+
+    public MultiDeviceTaskServiceImpl(
+            DeviceGroupConfigService deviceGroupConfigService,
+            DeviceGroupRelationMapper deviceGroupRelationMapper,
+            TaskStepDetailMapper taskStepDetailMapper,
+            TaskExecutionEngine taskExecutionEngine,
+            TaskStatusNotificationService notificationService,
+            ObjectMapper objectMapper) {
+        this.deviceGroupConfigService = deviceGroupConfigService;
+        this.deviceGroupRelationMapper = deviceGroupRelationMapper;
+        this.taskStepDetailMapper = taskStepDetailMapper;
+        this.taskExecutionEngine = taskExecutionEngine;
+        this.notificationService = notificationService;
+        this.objectMapper = objectMapper;
+    }
 
     @Override
     @Transactional(rollbackFor = Exception.class)
@@ -66,8 +80,15 @@
         }
 
         TaskParameters parameters = request.getParameters();
-        if (parameters == null || CollectionUtils.isEmpty(parameters.getGlassIds())) {
-            throw new IllegalArgumentException("鑷冲皯闇�瑕侀厤缃竴鏉$幓鐠僆D");
+        if (parameters == null) {
+            parameters = new TaskParameters();
+        }
+
+        // 榛樿鍏佽鍗ц浆绔嬫壂鐮佽澶囧湪浠诲姟鎵ц闃舵鑾峰彇鐜荤拑淇℃伅
+        boolean hasGlassIds = !CollectionUtils.isEmpty(parameters.getGlassIds());
+        if (!hasGlassIds) {
+            log.info("娴嬭瘯浠诲姟鏈彁渚涚幓鐠僆D锛屽皢鍦ㄨ澶囩粍娴佺▼涓敱鍗ц浆绔嬫壂鐮佽澶囬噰闆嗙幓鐠冧俊鎭�: groupId={}",
+                    groupConfig.getId());
         }
 
         // 鍒涘缓浠诲姟璁板綍
@@ -113,11 +134,26 @@
             // 鎵ц浠诲姟
             TaskExecutionResult result = taskExecutionEngine.execute(task, groupConfig, devices, parameters);
             
-            // 鏇存柊浠诲姟缁撴灉
+            // 妫�鏌ヤ换鍔℃暟鎹腑鏄惁鍖呭惈鎸佺画杩愯鐨勬爣璁�
+            Map<String, Object> resultData = result.getData();
+            boolean isContinuousTask = resultData != null && "浠诲姟宸插惎鍔紝瀹氭椂鍣ㄥ湪鍚庡彴杩愯涓�".equals(resultData.get("message"));
+            
+            // 濡傛灉鏄寔缁繍琛岀殑浠诲姟锛堝畾鏃跺櫒妯″紡锛夛紝淇濇寔 RUNNING 鐘舵�侊紝涓嶆洿鏂颁负 COMPLETED
+            if (isContinuousTask && result.isSuccess()) {
+                log.info("浠诲姟宸插惎鍔ㄥ畾鏃跺櫒锛屼繚鎸佽繍琛岀姸鎬�: taskId={}, message={}", 
+                    task.getTaskId(), resultData.get("message"));
+                task.setResultData(writeJson(resultData));
+                updateById(task);
+                // 閫氱煡浠诲姟鐘舵�侊紙淇濇寔 RUNNING锛�
+                notificationService.notifyTaskStatus(task);
+                return;
+            }
+            
+            // 鏇存柊浠诲姟缁撴灉锛堥潪鎸佺画杩愯鐨勪换鍔★級
             task.setStatus(result.isSuccess() ? MultiDeviceTask.Status.COMPLETED.name() : MultiDeviceTask.Status.FAILED.name());
             task.setErrorMessage(result.isSuccess() ? null : result.getMessage());
             task.setEndTime(new Date());
-            task.setResultData(writeJson(result.getData()));
+            task.setResultData(writeJson(resultData));
             updateById(task);
             
             // 閫氱煡浠诲姟瀹屾垚
@@ -164,9 +200,14 @@
         if (!MultiDeviceTask.Status.RUNNING.name().equals(task.getStatus())) {
             return false;
         }
+        taskExecutionEngine.requestTaskCancellation(taskId);
         task.setStatus(MultiDeviceTask.Status.CANCELLED.name());
         task.setEndTime(new Date());
-        return updateById(task);
+        boolean updated = updateById(task);
+        if (updated) {
+            notificationService.notifyTaskStatus(task);
+        }
+        return updated;
     }
 
     @Override
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/task/service/impl/TaskStatusNotificationServiceImpl.java b/mes-processes/mes-plcSend/src/main/java/com/mes/task/service/impl/TaskStatusNotificationServiceImpl.java
index 9924f4a..451e98a 100644
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/task/service/impl/TaskStatusNotificationServiceImpl.java
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/task/service/impl/TaskStatusNotificationServiceImpl.java
@@ -204,7 +204,12 @@
                     .name(eventName)
                     .data(createMessage("", data)));
             } catch (IOException e) {
-                log.warn("鍙戦�丼SE娑堟伅澶辫触: taskId={}, event={}", taskId, eventName, e);
+                // 瀹㈡埛绔柇寮�杩炴帴鏄甯告儏鍐碉紝浣跨敤DEBUG绾у埆
+                if (e instanceof org.apache.catalina.connector.ClientAbortException) {
+                    log.debug("瀹㈡埛绔柇寮�SSE杩炴帴: taskId={}, event={}", taskId, eventName);
+                } else {
+                    log.warn("鍙戦�丼SE娑堟伅澶辫触: taskId={}, event={}", taskId, eventName, e);
+                }
                 toRemove.add(emitter);
             }
         }
@@ -228,7 +233,12 @@
                     .name(eventName)
                     .data(createMessage("", data)));
             } catch (IOException e) {
-                log.warn("鍙戦�丼SE娑堟伅澶辫触: event={}", eventName, e);
+                // 瀹㈡埛绔柇寮�杩炴帴鏄甯告儏鍐碉紝浣跨敤DEBUG绾у埆
+                if (e instanceof org.apache.catalina.connector.ClientAbortException) {
+                    log.debug("瀹㈡埛绔柇寮�SSE杩炴帴: event={}", eventName);
+                } else {
+                    log.warn("鍙戦�丼SE娑堟伅澶辫触: event={}", eventName, e);
+                }
                 toRemove.add(emitter);
             }
         }
diff --git a/mes-web/src/views/device/DeviceEditDialog.vue b/mes-web/src/views/device/DeviceEditDialog.vue
index 088e292..2845239 100644
--- a/mes-web/src/views/device/DeviceEditDialog.vue
+++ b/mes-web/src/views/device/DeviceEditDialog.vue
@@ -354,7 +354,7 @@
 const deviceTypesLoading = ref(false)
 
 // 璁惧閫昏緫鍙傛暟锛堟牴鎹澶囩被鍨嬪姩鎬佹樉绀猴級
-const deviceLogicParams = reactive({})
+const deviceLogicParams = ref({})
 
 const S7_PLC_TYPES = ['S1200', 'S1500']
 const MODBUS_PLC_TYPES = ['MODBUS']
@@ -683,15 +683,11 @@
 }
 
 // 鍔犺浇璁惧閫昏緫鍙傛暟
-const loadDeviceLogicParams = (deviceLogic, deviceType) => {
-  // 娓呯┖鐜版湁鍙傛暟
-  Object.keys(deviceLogicParams).forEach(key => {
-    delete deviceLogicParams[key]
-  })
-  
-  // 鏍规嵁璁惧绫诲瀷鍔犺浇瀵瑰簲鐨勫弬鏁�
+const loadDeviceLogicParams = (deviceLogic) => {
   if (deviceLogic && Object.keys(deviceLogic).length > 0) {
-    Object.assign(deviceLogicParams, deviceLogic)
+    deviceLogicParams.value = { ...deviceLogic }
+  } else {
+    deviceLogicParams.value = {}
   }
 }
 
@@ -701,9 +697,7 @@
   deviceFormRef.value?.clearValidate()
   
   // 閲嶇疆璁惧閫昏緫鍙傛暟
-  Object.keys(deviceLogicParams).forEach(key => {
-    delete deviceLogicParams[key]
-  })
+  deviceLogicParams.value = {}
 }
 
 const addConfigParam = () => {
@@ -778,8 +772,10 @@
   }
 
     // 淇濆瓨璁惧閫昏緫鍙傛暟锛堢洿鎺ヤ娇鐢╠eviceLogicParams锛岀敱鍚勪釜閰嶇疆缁勪欢绠$悊锛�
-    if (deviceLogicParams && Object.keys(deviceLogicParams).length > 0) {
-      extraObj.deviceLogic = { ...deviceLogicParams }
+    if (deviceLogicParams.value && Object.keys(deviceLogicParams.value).length > 0) {
+      extraObj.deviceLogic = { ...deviceLogicParams.value }
+    } else {
+      delete extraObj.deviceLogic
     }
 
     // 鏋勫缓 configJson锛氬皢 configParams 鏁扮粍杞崲涓� JSON 瀛楃涓�
diff --git a/mes-web/src/views/device/components/DeviceLogicConfig/LoadVehicleConfig.vue b/mes-web/src/views/device/components/DeviceLogicConfig/LoadVehicleConfig.vue
index 5c2a3a7..3bdf568 100644
--- a/mes-web/src/views/device/components/DeviceLogicConfig/LoadVehicleConfig.vue
+++ b/mes-web/src/views/device/components/DeviceLogicConfig/LoadVehicleConfig.vue
@@ -9,6 +9,7 @@
             :max="10000"
             :step="100"
             style="width: 100%;"
+            @change="emitConfigUpdate"
           />
           <span class="form-tip">杞﹁締鏈�澶у閲�</span>
         </el-form-item>
@@ -22,6 +23,7 @@
             :step="0.1"
             :precision="1"
             style="width: 100%;"
+            @change="emitConfigUpdate"
           />
           <span class="form-tip">杞﹁締杩愬姩閫熷害锛岄粯璁�1鏍�/绉�</span>
         </el-form-item>
@@ -50,6 +52,7 @@
             :max="10000"
             :step="100"
             style="width: 100%;"
+            @change="emitConfigUpdate"
           />
           <span class="form-tip">褰撶幓鐠冩湭鎻愪緵闀垮害鏃朵娇鐢ㄧ殑榛樿鍊�</span>
         </el-form-item>
@@ -65,6 +68,7 @@
             :max="1000"
             :step="1"
             style="width: 100%;"
+            @change="emitConfigUpdate"
           />
           <span class="form-tip">杞﹁締鍒濆浣嶇疆锛堟牸瀛愶級</span>
         </el-form-item>
@@ -78,6 +82,7 @@
             :step="1"
             style="width: 40%;"
             placeholder="鏈�灏�"
+            @change="emitConfigUpdate"
           />
           <span style="margin: 0 2%;">~</span>
           <el-input-number
@@ -87,6 +92,7 @@
             :step="1"
             style="width: 40%;"
             placeholder="鏈�澶�"
+            @change="emitConfigUpdate"
           />
           <span class="form-tip">杩愬姩璺濈鑼冨洿锛堟牸瀛愶級</span>
         </el-form-item>
@@ -137,7 +143,7 @@
       </el-col>
       <el-col :span="12">
         <el-form-item label="鑷姩涓婃枡">
-          <el-switch v-model="config.autoFeed" />
+          <el-switch v-model="config.autoFeed" @change="emitConfigUpdate" />
           <span class="form-tip">鏄惁鑷姩瑙﹀彂涓婃枡璇锋眰</span>
         </el-form-item>
       </el-col>
@@ -152,6 +158,7 @@
             :max="10"
             :step="1"
             style="width: 100%;"
+            @change="emitConfigUpdate"
           />
         </el-form-item>
       </el-col>
@@ -160,30 +167,31 @@
     <el-form-item label="浣嶇疆鏄犲皠">
       <div class="position-mapping">
         <div
-          v-for="(value, key, index) in config.positionMapping"
-          :key="index"
+          v-for="(item, index) in positionList"
+          :key="item.id"
           class="mapping-item"
         >
           <el-input
-            v-model="mappingKeys[index]"
+            v-model="item.key"
             placeholder="浣嶇疆浠g爜锛堝900/901锛�"
             size="small"
             style="width: 150px; margin-right: 10px;"
-            @input="updatePositionMapping(index, $event, value)"
+            @change="handlePositionKeyChange(index)"
           />
           <el-input-number
-            v-model="config.positionMapping[mappingKeys[index] || key]"
+            v-model="item.value"
             :min="0"
             :max="1000"
             :step="1"
             size="small"
             style="width: 120px; margin-right: 10px;"
             placeholder="浣嶇疆鍊硷紙鏍硷級"
+            @change="handlePositionValueChange(index)"
           />
           <el-button
             type="danger"
             size="small"
-            @click="removePositionMapping(key)"
+            @click="removePositionMapping(index)"
           >
             鍒犻櫎
           </el-button>
@@ -226,8 +234,40 @@
   positionMapping: {}
 })
 
-// 浣嶇疆鏄犲皠鐨勯敭鏁扮粍
-const mappingKeys = ref([])
+// 浣嶇疆鏄犲皠缂栬緫鍒楄〃
+const positionList = ref([])
+let suppressPositionSync = false
+let suppressEmit = false
+
+const emitConfigUpdate = () => {
+  if (suppressEmit) return
+  emit('update:modelValue', { ...config.value })
+}
+
+const syncPositionListFromConfig = () => {
+  suppressPositionSync = true
+  const entries = Object.entries(config.value.positionMapping || {})
+  positionList.value = entries.map(([key, value], idx) => ({
+    id: `${key}_${idx}_${Date.now()}`,
+    key,
+    value: value ?? 1
+  }))
+  suppressPositionSync = false
+}
+
+const applyPositionListToConfig = () => {
+  if (suppressPositionSync) return
+  const mapping = {}
+  positionList.value.forEach((item) => {
+    if (item.key && item.key.trim()) {
+      mapping[item.key.trim()] = item.value ?? 1
+    }
+  })
+  config.value.positionMapping = mapping
+  emitConfigUpdate()
+}
+
+syncPositionListFromConfig()
 
 // 鏃堕棿瀛楁锛堢锛�- 鐢ㄤ簬鍓嶇鏄剧ず鍜岃緭鍏�
 const glassIntervalSeconds = ref(1.0)
@@ -238,6 +278,7 @@
 // 鐩戝惉props鍙樺寲
 watch(() => props.modelValue, (newVal) => {
   if (newVal && Object.keys(newVal).length > 0) {
+    suppressEmit = true
     config.value = {
       vehicleCapacity: newVal.vehicleCapacity ?? 6000,
       vehicleSpeed: newVal.vehicleSpeed ?? 1.0,
@@ -258,67 +299,54 @@
     idleMonitorIntervalSeconds.value = (config.value.idleMonitorIntervalMs ?? 2000) / 1000
     taskMonitorIntervalSeconds.value = (config.value.taskMonitorIntervalMs ?? 1000) / 1000
     mesConfirmTimeoutSeconds.value = (config.value.mesConfirmTimeoutMs ?? 30000) / 1000
-    mappingKeys.value = Object.keys(config.value.positionMapping)
+    syncPositionListFromConfig()
+    suppressEmit = false
   }
 }, { immediate: true, deep: true })
 
 // 鐩戝惉绉掑瓧娈靛彉鍖栵紝杞崲涓烘绉掑苟鏇存柊config
 watch(glassIntervalSeconds, (val) => {
   config.value.glassIntervalMs = Math.round(val * 1000)
-  emit('update:modelValue', { ...config.value })
+  emitConfigUpdate()
 })
 
 watch(idleMonitorIntervalSeconds, (val) => {
   config.value.idleMonitorIntervalMs = Math.round(val * 1000)
-  emit('update:modelValue', { ...config.value })
+  emitConfigUpdate()
 })
 
 watch(taskMonitorIntervalSeconds, (val) => {
   config.value.taskMonitorIntervalMs = Math.round(val * 1000)
-  emit('update:modelValue', { ...config.value })
+  emitConfigUpdate()
 })
 
 watch(mesConfirmTimeoutSeconds, (val) => {
   config.value.mesConfirmTimeoutMs = Math.round(val * 1000)
-  emit('update:modelValue', { ...config.value })
+  emitConfigUpdate()
 })
 
 // 鐩戝惉config鍏朵粬瀛楁鍙樺寲锛屽悓姝ュ埌鐖剁粍浠�
-watch(() => [
-  config.value.vehicleCapacity,
-  config.value.vehicleSpeed,
-  config.value.defaultGlassLength,
-  config.value.homePosition,
-  config.value.minRange,
-  config.value.maxRange,
-  config.value.autoFeed,
-  config.value.maxRetryCount,
-  config.value.positionMapping
-], () => {
-  emit('update:modelValue', { ...config.value })
-}, { deep: true })
-
 // 浣嶇疆鏄犲皠鐩稿叧鏂规硶
 const addPositionMapping = () => {
-  const newKey = `POS${Object.keys(config.value.positionMapping).length + 1}`
-  config.value.positionMapping[newKey] = 1
-  mappingKeys.value.push(newKey)
+  const nextIndex = positionList.value.length + 1
+  positionList.value.push({
+    id: `POS_${Date.now()}_${nextIndex}`,
+    key: '',
+    value: 1
+  })
 }
 
-const removePositionMapping = (key) => {
-  delete config.value.positionMapping[key]
-  mappingKeys.value = mappingKeys.value.filter(k => k !== key)
+const removePositionMapping = (idx) => {
+  positionList.value.splice(idx, 1)
+  applyPositionListToConfig()
 }
 
-const updatePositionMapping = (index, newKey, oldValue) => {
-  const oldKey = mappingKeys.value[index]
-  if (oldKey && oldKey !== newKey) {
-    delete config.value.positionMapping[oldKey]
-  }
-  mappingKeys.value[index] = newKey
-  if (newKey) {
-    config.value.positionMapping[newKey] = oldValue || 1
-  }
+const handlePositionKeyChange = () => {
+  applyPositionListToConfig()
+}
+
+const handlePositionValueChange = () => {
+  applyPositionListToConfig()
 }
 </script>
 
diff --git a/mes-web/src/views/plcTest/components/DeviceGroup/GroupTopology.vue b/mes-web/src/views/plcTest/components/DeviceGroup/GroupTopology.vue
index e46b9c0..8729075 100644
--- a/mes-web/src/views/plcTest/components/DeviceGroup/GroupTopology.vue
+++ b/mes-web/src/views/plcTest/components/DeviceGroup/GroupTopology.vue
@@ -105,8 +105,8 @@
           {{ selectedDevice.moduleName }}
         </el-descriptions-item>
         <el-descriptions-item label="鏄惁鍚敤">
-          <el-tag :type="selectedDevice.enabled ? 'success' : 'info'">
-            {{ selectedDevice.enabled ? '鍚敤' : '鍋滅敤' }}
+          <el-tag :type="getEnabledType(selectedDevice.enabled)">
+            {{ getEnabledLabel(selectedDevice.enabled) }}
           </el-tag>
         </el-descriptions-item>
       </el-descriptions>
@@ -229,6 +229,22 @@
   return String(status)
 }
 
+const getEnabledType = (enabled) => {
+  // 鏀寔鏁板瓧 1/0銆佸竷灏斿�� true/false銆佸瓧绗︿覆 '1'/'0'
+  if (enabled === 1 || enabled === true || enabled === '1' || String(enabled).toUpperCase() === 'TRUE') {
+    return 'success'
+  }
+  return 'info'
+}
+
+const getEnabledLabel = (enabled) => {
+  // 鏀寔鏁板瓧 1/0銆佸竷灏斿�� true/false銆佸瓧绗︿覆 '1'/'0'
+  if (enabled === 1 || enabled === true || enabled === '1' || String(enabled).toUpperCase() === 'TRUE') {
+    return '鍚敤'
+  }
+  return '鍋滅敤'
+}
+
 watch(
   () => props.group,
   () => {
diff --git a/mes-web/src/views/plcTest/components/MultiDeviceTest/TaskOrchestration.vue b/mes-web/src/views/plcTest/components/MultiDeviceTest/TaskOrchestration.vue
index 74906ce..649cd93 100644
--- a/mes-web/src/views/plcTest/components/MultiDeviceTest/TaskOrchestration.vue
+++ b/mes-web/src/views/plcTest/components/MultiDeviceTest/TaskOrchestration.vue
@@ -26,21 +26,34 @@
     </div>
 
     <el-form :model="form" label-width="120px" :rules="rules" ref="formRef">
-      <el-form-item label="鐜荤拑ID鍒楄〃" prop="glassIds" required>
+      <el-form-item label="鐜荤拑ID鍒楄〃" prop="glassIds">
         <el-input
           v-model="glassIdsInput"
           type="textarea"
           :rows="4"
-          placeholder="璇疯緭鍏ョ幓鐠冩潯鐮侊紝鏀寔澶氳鎴栭�楀彿鍒嗛殧锛屾瘡琛屼竴涓垨閫楀彿鍒嗛殧"
+          placeholder="鍙�夛細濡傛灉杈撳叆鐜荤拑ID锛屽皢浣跨敤杈撳叆鐨処D杩涜娴嬭瘯锛堜唬鏇垮崸杞珛鎵爜锛夛紱濡傛灉涓嶈緭鍏ワ紝灏嗕粠鏁版嵁搴撹鍙栨渶杩戞壂鐮佺殑鐜荤拑ID杩涜娴嬭瘯"
           show-word-limit
           :maxlength="5000"
         />
         <div class="form-tip">
-          宸茶緭鍏� {{ glassIds.length }} 涓幓鐠僆D
+          <span v-if="glassIds.length > 0">宸茶緭鍏� {{ glassIds.length }} 涓幓鐠僆D锛堟祴璇曟ā寮忥細浣跨敤杈撳叆鐨処D锛�</span>
+          <span v-else>鏈緭鍏ョ幓鐠僆D锛堟甯告ā寮忥細灏嗕粠鏁版嵁搴撹鍙栨渶杩戞壂鐮佺殑鐜荤拑ID锛�</span>
         </div>
       </el-form-item>
       
       <el-divider content-position="left">鎵ц閰嶇疆</el-divider>
+      
+      <el-form-item label="鍗曠墖闂撮殧 (绉�)">
+        <el-input-number
+          v-model="form.glassIntervalSeconds"
+          :min="0"
+          :max="60"
+          :step="0.1"
+          :precision="1"
+          placeholder="姣忎釜鐜荤拑ID涔嬮棿鐨勯棿闅旀椂闂�"
+        />
+        <div class="form-tip">澶氫釜鐜荤拑ID鏃讹紝姣忎釜鐜荤拑ID浼犻�掍箣闂寸殑闂撮殧鏃堕棿锛堢锛夛紝鐢ㄤ簬妯℃嫙鐜荤拑姣忕墖杩愬姩鐨勬椂闂淬��0琛ㄧず涓�娆℃�у叏閮ㄤ紶閫�</div>
+      </el-form-item>
       
       <el-form-item label="鎵ц闂撮殧 (ms)">
         <el-input-number
@@ -93,8 +106,9 @@
 })
 
 const emit = defineEmits(['task-started'])
-
+//閰嶇疆榛樿鍊�
 const form = reactive({
+  glassIntervalSeconds: 10, // 鍗曠墖闂撮殧锛岄粯璁�10绉�
   executionInterval: 1000,
   timeoutMinutes: 30,
   retryCount: 3
@@ -106,8 +120,10 @@
   glassIds: [
     {
       validator: (rule, value, callback) => {
+        // 濡傛灉杈撳叆浜嗙幓鐠僆D锛屽垯杩涜楠岃瘉锛涘鏋滄病鏈夎緭鍏ワ紝鍒欏厑璁革紙灏嗕粠鏁版嵁搴撹鍙栵級
         if (glassIds.value.length === 0) {
-          callback(new Error('璇疯嚦灏戣緭鍏ヤ竴涓幓鐠僆D'))
+          // 鍏佽涓虹┖锛屽皢浠庢暟鎹簱璇诲彇鏈�杩戞壂鐮佺殑鐜荤拑ID
+          callback()
         } else if (glassIds.value.length > 100) {
           callback(new Error('鐜荤拑ID鏁伴噺涓嶈兘瓒呰繃100涓�'))
         } else {
@@ -151,6 +167,8 @@
     .filter((item) => item.length > 0)
 })
 
+const normalizeType = (type) => (type || '').trim().toUpperCase()
+
 const fetchLoadDevice = async () => {
   loadDeviceId.value = null
   loadDeviceName.value = ''
@@ -172,9 +190,15 @@
       : Array.isArray(rawList?.data)
       ? rawList.data
       : []
-    const targetDevice =
-      deviceList.find((item) => (item.deviceType || '').toUpperCase() === 'LOAD_VEHICLE') ||
-      deviceList[0]
+    const scannerDevice = deviceList.find((item) => {
+      const type = normalizeType(item.deviceType)
+      return type.includes('SCANNER') || type.includes('鎵爜')
+    })
+    const loadVehicleDevice = deviceList.find((item) => {
+      const type = normalizeType(item.deviceType)
+      return type.includes('LOAD_VEHICLE') || type.includes('澶ц溅')
+    })
+    const targetDevice = scannerDevice || loadVehicleDevice || deviceList[0]
     if (targetDevice && targetDevice.id) {
       loadDeviceId.value = targetDevice.id
       loadDeviceName.value = targetDevice.deviceName || targetDevice.deviceCode || `ID: ${targetDevice.id}`
@@ -202,17 +226,18 @@
     return
   }
   
-  if (glassIds.value.length === 0) {
-    ElMessage.warning('璇疯嚦灏戣緭鍏ヤ竴涓幓鐠僆D')
-    return
-  }
-  
   try {
     loading.value = true
     
     // 鏋勫缓浠诲姟鍙傛暟
+    // 濡傛灉杈撳叆浜嗙幓鐠僆D锛屼娇鐢ㄨ緭鍏ョ殑锛涘鏋滄病鏈夎緭鍏ワ紝glassIds涓虹┖鏁扮粍锛屽悗绔細浠庢暟鎹簱璇诲彇
+    // 灏嗙杞崲涓烘绉掍紶缁欏悗绔�
+    const glassIntervalMs = form.glassIntervalSeconds != null && form.glassIntervalSeconds !== undefined 
+      ? Math.round(form.glassIntervalSeconds * 1000) 
+      : 1000
     const parameters = {
-      glassIds: glassIds.value,
+      glassIds: glassIds.value.length > 0 ? glassIds.value : [],
+      glassIntervalMs: glassIntervalMs,
       executionInterval: form.executionInterval || 1000
     }
     
@@ -261,14 +286,14 @@
     return
   }
   if (!loadDeviceId.value) {
-    ElMessage.warning('鏈壘鍒颁笂澶ц溅璁惧锛屾棤娉曟竻绌篜LC')
+    ElMessage.warning('鏈壘鍒板搴旇澶囷紝鏃犳硶娓呯┖PLC')
     return
   }
   try {
     clearLoading.value = true
     const response = await deviceInteractionApi.executeOperation({
       deviceId: loadDeviceId.value,
-      operation: 'clearGlass',
+      operation: 'clearPlc',
       params: {}
     })
     if (response?.code !== 200) {

--
Gitblit v1.8.0