From 628aa6a42e587e9f337e213f87f922fc2ab2af02 Mon Sep 17 00:00:00 2001
From: huang <1532065656@qq.com>
Date: 星期二, 02 十二月 2025 17:00:39 +0800
Subject: [PATCH] 修改卧转立扫码到卧转立任务流转,卧转立判断玻璃超时时间

---
 mes-web/src/views/plcTest/components/MultiDeviceTest/ExecutionMonitor.vue                                                    |   19 ++
 mes-processes/mes-plcSend/src/main/java/com/mes/device/service/GlassInfoService.java                                         |    5 
 mes-processes/mes-plcSend/src/main/java/com/mes/task/service/impl/MultiDeviceTaskServiceImpl.java                            |    7 +
 mes-processes/mes-plcSend/src/main/java/com/mes/interaction/workstation/scanner/handler/HorizontalScannerLogicHandler.java   |    8 +
 mes-processes/mes-plcSend/src/main/java/com/mes/task/service/TaskExecutionEngine.java                                        |  152 ++++++++++++++++++++++---
 mes-processes/mes-plcSend/src/main/java/com/mes/device/entity/GlassInfo.java                                                 |    4 
 mes-web/src/views/plcTest/MultiDeviceWorkbench.vue                                                                           |    4 
 mes-processes/mes-plcSend/src/main/java/com/mes/device/service/impl/GlassInfoServiceImpl.java                                |   50 ++++++++
 mes-processes/mes-plcSend/src/main/java/com/mes/interaction/workstation/transfer/handler/HorizontalTransferLogicHandler.java |   56 +++++++-
 mes-web/src/views/plcTest/components/MultiDeviceTest/TaskOrchestration.vue                                                   |    2 
 10 files changed, 272 insertions(+), 35 deletions(-)

diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/device/entity/GlassInfo.java b/mes-processes/mes-plcSend/src/main/java/com/mes/device/entity/GlassInfo.java
index 76e5cc1..754420b 100644
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/device/entity/GlassInfo.java
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/device/entity/GlassInfo.java
@@ -86,8 +86,10 @@
 
     // 鐘舵�佸父閲�
     public static final class Status {
-        public static final String ACTIVE = "ACTIVE";      // 娲昏穬
+        public static final String ACTIVE = "ACTIVE";      // 鍏煎鏃ф暟鎹�
         public static final String ARCHIVED = "ARCHIVED";  // 宸插綊妗�
+        public static final String PENDING = "PENDING";    // 寰呭崸杞珛澶勭悊
+        public static final String PROCESSED = "PROCESSED"; // 宸插鐞�
     }
 }
 
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 827d87c..202de49 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
@@ -70,5 +70,10 @@
      * @return 鐜荤拑ID鍒楄〃
      */
     List<String> getRecentScannedGlassIds(Integer minutesAgo, Integer maxCount, String workLine);
+
+    /**
+     * 鎵归噺鏇存柊鐜荤拑鐘舵��
+     */
+    boolean updateGlassStatus(List<String> glassIds, String status);
 }
 
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 2e81b91..5546da4 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
@@ -1,12 +1,14 @@
 package com.mes.device.service.impl;
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.mes.device.entity.GlassInfo;
 import com.mes.device.mapper.DeviceGlassInfoMapper;
 import com.mes.device.service.GlassInfoService;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
+import org.springframework.util.CollectionUtils;
 
 import java.util.Collections;
 import java.util.Date;
@@ -100,8 +102,35 @@
             GlassInfo existing = baseMapper.selectByGlassId(glassInfo.getGlassId());
             if (existing != null) {
                 glassInfo.setId(existing.getId());
+                // 淇濈暀鍘熷鍒涘缓淇℃伅
+                if (glassInfo.getCreatedTime() == null) {
+                    glassInfo.setCreatedTime(existing.getCreatedTime());
+                }
+                if (glassInfo.getCreatedBy() == null) {
+                    glassInfo.setCreatedBy(existing.getCreatedBy());
+                }
+                // 鏇存柊涓哄綋鍓嶆椂闂�
+                if (glassInfo.getUpdatedTime() == null) {
+                    glassInfo.setUpdatedTime(new Date());
+                }
+                if (glassInfo.getUpdatedBy() == null) {
+                    glassInfo.setUpdatedBy("system");
+                }
                 return updateById(glassInfo);
             } else {
+                Date now = new Date();
+                if (glassInfo.getCreatedTime() == null) {
+                    glassInfo.setCreatedTime(now);
+                }
+                if (glassInfo.getUpdatedTime() == null) {
+                    glassInfo.setUpdatedTime(now);
+                }
+                if (glassInfo.getCreatedBy() == null) {
+                    glassInfo.setCreatedBy("system");
+                }
+                if (glassInfo.getUpdatedBy() == null) {
+                    glassInfo.setUpdatedBy("system");
+                }
                 return save(glassInfo);
             }
         } catch (Exception e) {
@@ -136,7 +165,7 @@
             Date timeThreshold = new Date(System.currentTimeMillis() - minutes * 60 * 1000L);
             
             LambdaQueryWrapper<GlassInfo> wrapper = new LambdaQueryWrapper<>();
-            wrapper.eq(GlassInfo::getStatus, GlassInfo.Status.ACTIVE)
+            wrapper.eq(GlassInfo::getStatus, GlassInfo.Status.PENDING)
                    .ge(GlassInfo::getCreatedTime, timeThreshold)
                    .orderByDesc(GlassInfo::getCreatedTime)
                    .last("LIMIT " + limit);
@@ -160,5 +189,24 @@
             return Collections.emptyList();
         }
     }
+
+    @Override
+    public boolean updateGlassStatus(List<String> glassIds, String status) {
+        if (CollectionUtils.isEmpty(glassIds) || status == null) {
+            return true;
+        }
+        try {
+            LambdaUpdateWrapper<GlassInfo> wrapper = new LambdaUpdateWrapper<>();
+            wrapper.in(GlassInfo::getGlassId, glassIds);
+            GlassInfo update = new GlassInfo();
+            update.setStatus(status);
+            update.setUpdatedTime(new Date());
+            update.setUpdatedBy("system");
+            return this.update(update, wrapper);
+        } catch (Exception e) {
+            log.error("鎵归噺鏇存柊鐜荤拑鐘舵�佸け璐�, glassIds={}, status={}", glassIds, status, e);
+            return false;
+        }
+    }
 }
 
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 fd1c2a1..71ad7f1 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
@@ -18,6 +18,7 @@
 import java.time.LocalDateTime;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -273,10 +274,15 @@
         if (height != null) {
             glassInfo.setGlassLength(height); // 闀�
         }
-        glassInfo.setStatus(GlassInfo.Status.ACTIVE);
+        glassInfo.setStatus(GlassInfo.Status.PENDING);
         if (workLine != null) {
             glassInfo.setDescription("workLine=" + workLine);
         }
+        Date now = new Date();
+        glassInfo.setCreatedTime(now);
+        glassInfo.setUpdatedTime(now);
+        glassInfo.setCreatedBy("system");
+        glassInfo.setUpdatedBy("system");
         return glassInfo;
     }
 
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/workstation/transfer/handler/HorizontalTransferLogicHandler.java b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/workstation/transfer/handler/HorizontalTransferLogicHandler.java
index 85b9dc3..c942e41 100644
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/workstation/transfer/handler/HorizontalTransferLogicHandler.java
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/workstation/transfer/handler/HorizontalTransferLogicHandler.java
@@ -83,7 +83,9 @@
             switch (operation) {
                 case "checkAndProcess":
                 case "process":
-                    return handleCheckAndProcess(deviceConfig, config, logicParams);
+                    // 杩欓噷蹇呴』鎶� params 浼犺繘鍘伙紝浠ヤ究鍦ㄥ璁惧浠诲姟娴佺▼涓�
+                    // 鑳藉閫氳繃 _taskContext 灏嗗崸杞珛杈撳嚭鐨勭幓鐠僆D鍐欏叆浠诲姟涓婁笅鏂�
+                    return handleCheckAndProcess(deviceConfig, config, logicParams, params);
                 case "startMonitor":
                     return handleStartMonitor(deviceConfig, config, logicParams);
                 case "stopMonitor":
@@ -109,7 +111,8 @@
     private DevicePlcVO.OperationResult handleCheckAndProcess(
             DeviceConfig deviceConfig,
             WorkstationLogicConfig config,
-            Map<String, Object> logicParams) {
+            Map<String, Object> logicParams,
+            Map<String, Object> params) {
         
         String deviceId = deviceConfig.getDeviceId();
         EnhancedS7Serializer serializer = s7SerializerProvider.getSerializer(deviceConfig);
@@ -129,9 +132,13 @@
             log.info("鏌ヨ鍒版渶杩戞壂鐮佺殑鐜荤拑: deviceId={}, count={}", 
                     deviceId, recentGlasses.size());
 
-            // 2. 鏇存柊缂撳啿闃熷垪鍜屾渶鍚庢壂鐮佹椂闂�
-            updateBuffer(deviceId, recentGlasses);
-            lastScanTime.put(deviceId, new AtomicLong(System.currentTimeMillis()));
+            // 2. 鏇存柊缂撳啿闃熷垪锛涗粎鍦ㄦ湁鈥滄柊鐜荤拑鈥濆姞鍏ョ紦鍐叉椂鎵嶆洿鏂版渶鍚庢壂鐮佹椂闂�
+            boolean hasNewGlass = updateBuffer(deviceId, recentGlasses);
+            if (hasNewGlass) {
+                lastScanTime
+                        .computeIfAbsent(deviceId, k -> new AtomicLong())
+                        .set(System.currentTimeMillis());
+            }
 
             // 3. 妫�鏌ユ槸鍚﹂渶瑕佺珛鍗冲鐞嗭紙瀹归噺宸叉弧鎴�30s鍐呮棤鏂扮幓鐠冿級
             List<GlassBufferItem> buffer = glassBuffer.get(deviceId);
@@ -162,8 +169,34 @@
                 return writeResult;
             }
 
-            // 7. 浠庣紦鍐查槦鍒椾腑绉婚櫎宸插鐞嗙殑鐜荤拑
+            // 鍗ц浆绔嬫壒娆″凡鎴愬姛鍐欏叆PLC锛屽皢鏈壒娆$幓鐠僆D鍐欏叆浠诲姟涓婁笅鏂囷紝渚涘ぇ杞﹁繘鐗囦娇鐢�
+            try {
+                if (params != null) {
+                    Object ctxObj = params.get("_taskContext");
+                    if (ctxObj instanceof com.mes.task.model.TaskExecutionContext) {
+                        com.mes.task.model.TaskExecutionContext ctx =
+                                (com.mes.task.model.TaskExecutionContext) ctxObj;
+                        List<String> batchGlassIds = batch.stream()
+                                .map(GlassInfo::getGlassId)
+                                .filter(Objects::nonNull)
+                                .collect(Collectors.toList());
+                        if (!batchGlassIds.isEmpty()) {
+                            ctx.getSharedData().put("transferReadyGlassIds",
+                                    new java.util.ArrayList<>(batchGlassIds));
+                            log.info("鍗ц浆绔嬪凡杈撳嚭鎵规鐜荤拑鍒颁换鍔′笂涓嬫枃: deviceId={}, glassIds={}",
+                                    deviceConfig.getId(), batchGlassIds);
+                        }
+                    }
+                }
+            } catch (Exception e) {
+                log.warn("鍗ц浆绔嬪啓鍏ヤ换鍔′笂涓嬫枃transferReadyGlassIds澶辫触: deviceId={}", deviceConfig.getId(), e);
+            }
+
+            // 7. 浠庣紦鍐查槦鍒椾腑绉婚櫎宸插鐞嗙殑鐜荤拑骞舵洿鏂扮姸鎬�
             removeProcessedGlasses(deviceId, batch);
+            glassInfoService.updateGlassStatus(
+                    batch.stream().map(GlassInfo::getGlassId).collect(Collectors.toList()),
+                    GlassInfo.Status.PROCESSED);
 
             String msg = String.format("鎵规宸插啓鍏LC: glassCount=%d, glassIds=%s", 
                     batch.size(), 
@@ -197,7 +230,7 @@
             Date twoMinutesAgo = new Date(System.currentTimeMillis() - 120000);
             
             LambdaQueryWrapper<GlassInfo> wrapper = new LambdaQueryWrapper<>();
-            wrapper.eq(GlassInfo::getStatus, GlassInfo.Status.ACTIVE)
+            wrapper.in(GlassInfo::getStatus, GlassInfo.Status.PENDING, GlassInfo.Status.ACTIVE)
                    .ge(GlassInfo::getCreatedTime, twoMinutesAgo)
                    .orderByDesc(GlassInfo::getCreatedTime)
                    .last("LIMIT 20"); // 闄愬埗鏌ヨ鏁伴噺锛岄伩鍏嶈繃澶�
@@ -223,8 +256,9 @@
 
     /**
      * 鏇存柊缂撳啿闃熷垪
+     * @return 鏄惁鏈夋柊鐨勭幓鐠冭鍔犲叆缂撳啿锛堢敤浜庡垽鏂槸鍚﹀埛鏂� lastScanTime锛�
      */
-    private void updateBuffer(String deviceId, List<GlassInfo> newGlasses) {
+    private boolean updateBuffer(String deviceId, List<GlassInfo> newGlasses) {
         List<GlassBufferItem> buffer = glassBuffer.computeIfAbsent(
                 deviceId, k -> new CopyOnWriteArrayList<>());
         
@@ -232,13 +266,16 @@
                 .map(item -> item.glassInfo.getGlassId())
                 .collect(Collectors.toSet());
         
+        boolean hasNewGlass = false;
         for (GlassInfo glass : newGlasses) {
             if (!existingIds.contains(glass.getGlassId())) {
                 buffer.add(new GlassBufferItem(glass, System.currentTimeMillis()));
+                hasNewGlass = true;
                 log.debug("娣诲姞鐜荤拑鍒扮紦鍐查槦鍒�: deviceId={}, glassId={}", 
                         deviceId, glass.getGlassId());
             }
         }
+        return hasNewGlass;
     }
 
     /**
@@ -375,7 +412,8 @@
         // 鍚姩鐩戞帶浠诲姟
         ScheduledFuture<?> future = monitorExecutor.scheduleWithFixedDelay(() -> {
             try {
-                handleCheckAndProcess(deviceConfig, config, logicParams);
+                // 鐩戞帶浠诲姟涓嶅湪澶氳澶囦换鍔′笂涓嬫枃涓繍琛岋紝杩欓噷涓嶉渶瑕佷紶鍏� params/_taskContext
+                handleCheckAndProcess(deviceConfig, config, logicParams, null);
             } catch (Exception e) {
                 log.error("鐩戞帶浠诲姟鎵ц寮傚父: deviceId={}", deviceId, e);
             }
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 986a06b..2156e46 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
@@ -165,6 +165,12 @@
                     log.info("妫�娴嬪埌鎵爜璁惧锛屽噯澶囧惎鍔ㄥ畾鏃跺櫒: deviceId={}, deviceType={}, deviceName={}", 
                             device.getId(), device.getDeviceType(), device.getDeviceName());
                     TaskStepDetail step = createStepRecord(task, device, currentOrder);
+                    // 璁剧疆姝ラ涓鸿繍琛岀姸鎬侊紝骞惰缃紑濮嬫椂闂�
+                    step.setStatus(TaskStepDetail.Status.RUNNING.name());
+                    step.setStartTime(new Date());
+                    taskStepDetailMapper.updateById(step);
+                    notificationService.notifyStepUpdate(task.getTaskId(), step);
+                    
                     ScheduledFuture<?> scannerTask = startScannerTimer(task, step, device, context);
                     if (scannerTask != null) {
                         registerScheduledTask(task.getTaskId(), scannerTask);
@@ -187,6 +193,12 @@
                     log.info("妫�娴嬪埌鍗ц浆绔嬭澶囷紝鍑嗗鍚姩瀹氭椂鍣�: deviceId={}, deviceType={}, deviceName={}", 
                             device.getId(), device.getDeviceType(), device.getDeviceName());
                     TaskStepDetail step = createStepRecord(task, device, currentOrder);
+                    // 璁剧疆姝ラ涓鸿繍琛岀姸鎬侊紝骞惰缃紑濮嬫椂闂�
+                    step.setStatus(TaskStepDetail.Status.RUNNING.name());
+                    step.setStartTime(new Date());
+                    taskStepDetailMapper.updateById(step);
+                    notificationService.notifyStepUpdate(task.getTaskId(), step);
+                    
                     ScheduledFuture<?> transferTask = startTransferTimer(task, step, device, context);
                     if (transferTask != null) {
                         registerScheduledTask(task.getTaskId(), transferTask);
@@ -209,6 +221,12 @@
                     boolean isInboundVehicle = currentLoadVehicleIndex == 1; // 绗竴涓ぇ杞︽槸杩涚墖澶ц溅
                     
                     TaskStepDetail step = createStepRecord(task, device, currentOrder);
+                    // 璁剧疆姝ラ涓鸿繍琛岀姸鎬侊紝骞惰缃紑濮嬫椂闂�
+                    step.setStatus(TaskStepDetail.Status.RUNNING.name());
+                    step.setStartTime(new Date());
+                    taskStepDetailMapper.updateById(step);
+                    notificationService.notifyStepUpdate(task.getTaskId(), step);
+                    
                     ScheduledFuture<?> vehicleTask;
                     if (isInboundVehicle) {
                         // 杩涚墖澶ц溅锛氱洃鎺у閲忥紝鍔ㄦ�佸垽鏂�
@@ -242,6 +260,12 @@
                 // 4. 澶х悊鐗囩璁惧锛氬惎鍔ㄥ畾鏃跺櫒閫昏緫澶勭悊锛堜笉娑夊強PLC浜や簰锛屽彧璐熻矗閫昏緫澶勭悊锛�
                 if (isLargeGlass) {
                     TaskStepDetail step = createStepRecord(task, device, currentOrder);
+                    // 璁剧疆姝ラ涓鸿繍琛岀姸鎬侊紝骞惰缃紑濮嬫椂闂�
+                    step.setStatus(TaskStepDetail.Status.RUNNING.name());
+                    step.setStartTime(new Date());
+                    taskStepDetailMapper.updateById(step);
+                    notificationService.notifyStepUpdate(task.getTaskId(), step);
+                    
                     ScheduledFuture<?> largeGlassTask = startLargeGlassTimer(task, step, device, context);
                     if (largeGlassTask != null) {
                         registerScheduledTask(task.getTaskId(), largeGlassTask);
@@ -392,6 +416,17 @@
                         log.info("鍗ц浆绔嬫壂鐮佸畾鏃跺櫒瀹屾垚: taskId={}, deviceId={}, processed={}/{}, success={}, fail={}",
                                 task.getTaskId(), device.getId(), processedCount.get(), glassIds.size(),
                                 successCount.get(), failCount.get());
+                        // 鑻ヤ箣鍓嶆湭鍑虹幇澶辫触锛屽啀灏嗙姸鎬佺疆涓哄畬鎴�
+                        boolean alreadyFailed = TaskStepDetail.Status.FAILED.name().equals(step.getStatus());
+                        if (!alreadyFailed) {
+                            step.setStatus(TaskStepDetail.Status.COMPLETED.name());
+                            step.setSuccessMessage(String.format("宸插畬鎴愭壂鎻�: 鎴愬姛=%d, 澶辫触=%d", successCount.get(), failCount.get()));
+                            if (step.getEndTime() == null) {
+                                step.setEndTime(new Date());
+                            }
+                            taskStepDetailMapper.updateById(step);
+                            notificationService.notifyStepUpdate(task.getTaskId(), step);
+                        }
                         deviceCoordinationService.syncDeviceStatus(device,
                                 DeviceCoordinationService.DeviceStatus.COMPLETED, context);
                         return;
@@ -492,15 +527,21 @@
                     if (handler != null) {
                         DevicePlcVO.OperationResult result = handler.execute(device, "checkAndProcess", params);
                         
-                        // 鏇存柊姝ラ鐘舵��
-                        updateStepStatus(step, result);
+                        // 鏇存柊姝ラ鐘舵�侊紙鍖哄垎绛夊緟涓拰鐪熸瀹屾垚锛�
+                        updateStepStatusForTransfer(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());
+                            String message = result.getMessage();
+                            if (message != null && message.contains("鎵规宸插啓鍏LC")) {
+                                log.info("鍗ц浆绔嬭澶囧畾鏃跺櫒鎵ц鎴愬姛锛堝凡鍐欏叆PLC锛�: taskId={}, deviceId={}, message={}",
+                                        task.getTaskId(), device.getId(), message);
+                            } else {
+                                log.debug("鍗ц浆绔嬭澶囧畾鏃跺櫒绛夊緟涓�: taskId={}, deviceId={}, message={}",
+                                        task.getTaskId(), device.getId(), message);
+                            }
                         } else {
                             log.warn("鍗ц浆绔嬭澶囧畾鏃跺櫒鎵ц澶辫触: taskId={}, deviceId={}, message={}",
                                     task.getTaskId(), device.getId(), result.getMessage());
@@ -544,28 +585,27 @@
                                 task.getTaskId(), device.getId());
                         return;
                     }
-                    // 妫�鏌ユ槸鍚︽湁宸叉壂鎻忕殑鐜荤拑淇℃伅
-                    List<String> scannedGlassIds = getScannedGlassIds(context);
-                    if (CollectionUtils.isEmpty(scannedGlassIds)) {
-                        // 娌℃湁宸叉壂鎻忕殑鐜荤拑锛岀‘淇濆崸杞珛鎵爜缁х画杩愯
-                        setScannerPause(context, false);
+                    // 妫�鏌ユ槸鍚︽湁鍗ц浆绔嬩富浣撳凡杈撳嚭銆佸噯澶囦笂澶ц溅鐨勭幓鐠冧俊鎭�
+                    List<String> readyGlassIds = getTransferReadyGlassIds(context);
+                    if (CollectionUtils.isEmpty(readyGlassIds)) {
+                        // 娌℃湁鍗ц浆绔嬭緭鍑虹殑鐜荤拑锛岀户缁瓑寰�
                         return;
                     }
                     
                     // 濡傛灉鐜荤拑ID鏁伴噺娌℃湁鍙樺寲锛岃鏄庢病鏈夋柊鐨勭幓鐠冿紝缁х画绛夊緟
-                    int currentCount = scannedGlassIds.size();
+                    int currentCount = readyGlassIds.size();
                     if (currentCount == lastProcessedCount.get()) {
                         log.debug("澶ц溅璁惧瀹氭椂鍣細鐜荤拑ID鏁伴噺鏈彉鍖栵紝缁х画绛夊緟: taskId={}, deviceId={}, count={}",
                                 task.getTaskId(), device.getId(), currentCount);
                         return;
                     }
                     
-                    log.info("杩涚墖澶ц溅璁惧瀹氭椂鍣ㄦ娴嬪埌鏂扮殑鐜荤拑淇℃伅: taskId={}, deviceId={}, glassCount={}",
+                    log.info("杩涚墖澶ц溅璁惧瀹氭椂鍣ㄦ娴嬪埌鍗ц浆绔嬭緭鍑虹殑鐜荤拑淇℃伅: taskId={}, deviceId={}, glassCount={}",
                             task.getTaskId(), device.getId(), currentCount);
                     
                     // 妫�鏌ュ閲�
                     Map<String, Object> checkParams = new HashMap<>();
-                    checkParams.put("glassIds", new ArrayList<>(scannedGlassIds));
+                    checkParams.put("glassIds", new ArrayList<>(readyGlassIds));
                     checkParams.put("_taskContext", context);
                     
                     DeviceLogicHandler handler = handlerFactory.getHandler(device.getDeviceType());
@@ -579,19 +619,18 @@
                         
                         if (Boolean.TRUE.equals(result.getSuccess())) {
                             log.info("杩涚墖澶ц溅璁惧瀹氭椂鍣ㄦ墽琛屾垚鍔�: taskId={}, deviceId={}, glassCount={}",
-                                    task.getTaskId(), device.getId(), scannedGlassIds.size());
+                                    task.getTaskId(), device.getId(), readyGlassIds.size());
                             // 灏嗗凡瑁呰浇鐨勭幓鐠僆D淇濆瓨鍒板叡浜暟鎹腑锛堜緵澶х悊鐗囩浣跨敤锛�
-                            setLoadedGlassIds(context, new ArrayList<>(scannedGlassIds));
-                            // 娓呯┖宸叉壂鎻忕殑鐜荤拑ID鍒楄〃锛堝凡澶勭悊锛�
-                            clearScannedGlassIds(context);
+                            setLoadedGlassIds(context, new ArrayList<>(readyGlassIds));
+                            // 娓呯┖鍗ц浆绔嬭緭鍑虹殑鐜荤拑ID鍒楄〃锛堝凡澶勭悊锛�
+                            clearTransferReadyGlassIds(context);
                             lastProcessedCount.set(0);
                             // 纭繚鍗ц浆绔嬫壂鐮佺户缁繍琛�
                             setScannerPause(context, false);
                         } else {
-                            // 瑁呬笉涓嬶紝閫氱煡鍗ц浆绔嬫壂鐮佹殏鍋�
-                            log.warn("杩涚墖澶ц溅璁惧瀹氭椂鍣ㄥ閲忎笉瓒�: taskId={}, deviceId={}, message={}, 宸查�氱煡鍗ц浆绔嬫壂鐮佹殏鍋�",
+                            // 瑁呬笉涓嬶紝璁板綍瀹归噺涓嶈冻锛堟槸鍚﹂渶瑕佸奖鍝嶆壂鐮佺敱宸ヨ壓鍐嶅喅瀹氾級
+                            log.warn("杩涚墖澶ц溅璁惧瀹氭椂鍣ㄥ閲忎笉瓒�: taskId={}, deviceId={}, message={}",
                                     task.getTaskId(), device.getId(), result.getMessage());
-                            setScannerPause(context, true);
                             lastProcessedCount.set(currentCount); // 璁板綍褰撳墠鏁伴噺锛岄伩鍏嶉噸澶嶆鏌�
                         }
                         
@@ -942,6 +981,30 @@
             context.getSharedData().put("scannedGlassIds", new ArrayList<>());
         }
     }
+
+    /**
+     * 鑾峰彇鍗ц浆绔嬩富浣撳凡杈撳嚭銆佸噯澶囦笂澶ц溅鐨勭幓鐠僆D鍒楄〃
+     */
+    @SuppressWarnings("unchecked")
+    private List<String> getTransferReadyGlassIds(TaskExecutionContext context) {
+        if (context == null) {
+            return Collections.emptyList();
+        }
+        Object glassIds = context.getSharedData().get("transferReadyGlassIds");
+        if (glassIds instanceof List) {
+            return new ArrayList<>((List<String>) glassIds);
+        }
+        return Collections.emptyList();
+    }
+
+    /**
+     * 娓呯┖鍗ц浆绔嬩富浣撳凡杈撳嚭鐨勭幓鐠僆D鍒楄〃
+     */
+    private void clearTransferReadyGlassIds(TaskExecutionContext context) {
+        if (context != null) {
+            context.getSharedData().put("transferReadyGlassIds", new ArrayList<>());
+        }
+    }
     
     /**
      * 娉ㄥ唽瀹氭椂鍣ㄤ换鍔�
@@ -1025,15 +1088,66 @@
         if (success) {
             // 鎴愬姛鏃讹紝濡傛灉鏈夋秷鎭垯淇濆瓨锛堢敤浜庢彁绀轰俊鎭級锛屽惁鍒欐竻绌�
             step.setSuccessMessage(StringUtils.hasText(message) ? message : null);
+            // 濡傛灉鐘舵�佸彉涓哄畬鎴愶紝璁剧疆缁撴潫鏃堕棿
+            if (TaskStepDetail.Status.COMPLETED.name().equals(step.getStatus()) && step.getEndTime() == null) {
+                step.setEndTime(new Date());
+            }
         } else {
             // 澶辫触鏃朵繚瀛橀敊璇秷鎭�
             step.setErrorMessage(message);
+            // 濡傛灉鐘舵�佸彉涓哄け璐ワ紝璁剧疆缁撴潫鏃堕棿
+            if (TaskStepDetail.Status.FAILED.name().equals(step.getStatus()) && step.getEndTime() == null) {
+                step.setEndTime(new Date());
+            }
         }
         step.setOutputData(toJson(result));
         taskStepDetailMapper.updateById(step);
     }
     
     /**
+     * 鏇存柊鍗ц浆绔嬭澶囨楠ょ姸鎬侊紙鍖哄垎绛夊緟涓拰鐪熸瀹屾垚锛�
+     */
+    private void updateStepStatusForTransfer(TaskStepDetail step, DevicePlcVO.OperationResult result) {
+        if (step == null || result == null) {
+            return;
+        }
+        boolean success = Boolean.TRUE.equals(result.getSuccess());
+        String message = result.getMessage();
+        
+        // 鍒ゆ柇鏄惁鐪熸瀹屾垚锛堝彧鏈夊啓鍏LC鎵嶇畻瀹屾垚锛�
+        boolean isRealCompleted = success && message != null && message.contains("鎵规宸插啓鍏LC");
+        
+        if (isRealCompleted) {
+            // 鐪熸瀹屾垚锛氳缃负瀹屾垚鐘舵�侊紝骞惰缃粨鏉熸椂闂�
+            step.setStatus(TaskStepDetail.Status.COMPLETED.name());
+            step.setSuccessMessage(message);
+            if (step.getEndTime() == null) {
+                step.setEndTime(new Date());
+            }
+        } else if (success) {
+            // 绛夊緟涓細淇濇寔杩愯鐘舵�侊紝鍙洿鏂版秷鎭�
+            if (!TaskStepDetail.Status.RUNNING.name().equals(step.getStatus())) {
+                step.setStatus(TaskStepDetail.Status.RUNNING.name());
+            }
+            step.setSuccessMessage(message);
+            // 纭繚寮�濮嬫椂闂村凡璁剧疆
+            if (step.getStartTime() == null) {
+                step.setStartTime(new Date());
+            }
+        } else {
+            // 澶辫触锛氳缃负澶辫触鐘舵�侊紝骞惰缃粨鏉熸椂闂�
+            step.setStatus(TaskStepDetail.Status.FAILED.name());
+            step.setErrorMessage(message);
+            if (step.getEndTime() == null) {
+                step.setEndTime(new Date());
+            }
+        }
+        
+        step.setOutputData(toJson(result));
+        taskStepDetailMapper.updateById(step);
+    }
+    
+    /**
      * 鍒涘缓姝ラ鎽樿
      */
     private Map<String, Object> createStepSummary(String deviceName, boolean success, String message) {
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 c2d2e35..278a68f 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
@@ -197,9 +197,14 @@
         if (task == null) {
             return false;
         }
-        if (!MultiDeviceTask.Status.RUNNING.name().equals(task.getStatus())) {
+        // 鍏佽鍦� RUNNING 鎴� FAILED 鐘舵�佷笅鎵ц鍙栨秷鎿嶄綔
+        String status = task.getStatus();
+        boolean cancellable = MultiDeviceTask.Status.RUNNING.name().equals(status)
+                || MultiDeviceTask.Status.FAILED.name().equals(status);
+        if (!cancellable) {
             return false;
         }
+        // 鏍囪浠诲姟鍙栨秷骞跺仠姝㈡墍鏈夊畾鏃跺櫒
         taskExecutionEngine.requestTaskCancellation(taskId);
         task.setStatus(MultiDeviceTask.Status.CANCELLED.name());
         task.setEndTime(new Date());
diff --git a/mes-web/src/views/plcTest/MultiDeviceWorkbench.vue b/mes-web/src/views/plcTest/MultiDeviceWorkbench.vue
index 3472ea8..1c2dd81 100644
--- a/mes-web/src/views/plcTest/MultiDeviceWorkbench.vue
+++ b/mes-web/src/views/plcTest/MultiDeviceWorkbench.vue
@@ -80,6 +80,10 @@
   // 濡傛灉浼犲叆浜嗕换鍔′俊鎭紝鍙互鑷姩閫変腑
   if (task && task.taskId) {
     selectedTaskId.value = task.taskId
+    // 绋嶅悗鑷姩鎵撳紑璇ヤ换鍔$殑姝ラ璇︽儏鎶藉眽
+    setTimeout(() => {
+      monitorRef.value?.openTaskDrawer?.(task.taskId)
+    }, 600)
   }
 }
 
diff --git a/mes-web/src/views/plcTest/components/MultiDeviceTest/ExecutionMonitor.vue b/mes-web/src/views/plcTest/components/MultiDeviceTest/ExecutionMonitor.vue
index 1502b50..a221de2 100644
--- a/mes-web/src/views/plcTest/components/MultiDeviceTest/ExecutionMonitor.vue
+++ b/mes-web/src/views/plcTest/components/MultiDeviceTest/ExecutionMonitor.vue
@@ -86,7 +86,7 @@
             鏌ョ湅璇︽儏
           </el-button>
           <el-button
-            v-if="row.status === 'RUNNING'"
+            v-if="row.status === 'RUNNING' || row.status === 'FAILED'"
             link
             type="danger"
             size="small"
@@ -421,6 +421,20 @@
   }
 }
 
+// 鏍规嵁taskId鎵撳紑浠诲姟璇︽儏鎶藉眽锛堜緵鐖剁粍浠惰皟鐢級
+const openTaskDrawer = async (taskId) => {
+  if (!taskId) return
+  // 濡傛灉浠诲姟鍒楄〃涓虹┖锛屽厛鍔犺浇涓�娆�
+  if (!tasks.value || tasks.value.length === 0) {
+    await fetchTasks()
+  }
+  const task = tasks.value.find(t => t.taskId === taskId)
+  if (!task) {
+    return
+  }
+  await handleRowClick(task)
+}
+
 const statusType = (status) => {
   switch ((status || '').toUpperCase()) {
     case 'COMPLETED':
@@ -562,7 +576,8 @@
 defineExpose({
   fetchTasks,
   connectSSE,
-  disconnectSSE
+  disconnectSSE,
+  openTaskDrawer
 })
 </script>
 
diff --git a/mes-web/src/views/plcTest/components/MultiDeviceTest/TaskOrchestration.vue b/mes-web/src/views/plcTest/components/MultiDeviceTest/TaskOrchestration.vue
index 649cd93..0d7d3a7 100644
--- a/mes-web/src/views/plcTest/components/MultiDeviceTest/TaskOrchestration.vue
+++ b/mes-web/src/views/plcTest/components/MultiDeviceTest/TaskOrchestration.vue
@@ -110,7 +110,7 @@
 const form = reactive({
   glassIntervalSeconds: 10, // 鍗曠墖闂撮殧锛岄粯璁�10绉�
   executionInterval: 1000,
-  timeoutMinutes: 30,
+  timeoutMinutes: 1,
   retryCount: 3
 })
 

--
Gitblit v1.8.0