From 19f59c243e8df97c8b9fd9dba4e758be8235d68b Mon Sep 17 00:00:00 2001
From: huang <1532065656@qq.com>
Date: 星期二, 25 十一月 2025 17:02:54 +0800
Subject: [PATCH] 添加卧转立扫码、卧转立、大车、大理片笼基础任务流转逻辑

---
 mes-web/src/views/plcTest/components/MultiDeviceTest/ResultAnalysis.vue                                                      |  638 ++++
 mes-processes/mes-plcSend/ARCHITECTURE.md                                                                                    |  536 +++
 mes-processes/mes-plcSend/src/main/java/com/mes/interaction/largeglass/config/LargeGlassConfig.java                          |   37 
 mes-processes/mes-plcSend/src/main/java/com/mes/interaction/vehicle/coordination/VehicleCoordinationService.java             |  189 +
 mes-processes/mes-plcSend/src/main/java/com/mes/device/vo/DevicePlcVO.java                                                   |    3 
 mes-web/src/utils/constants.js                                                                                               |    2 
 mes-processes/mes-plcSend/README.md                                                                                          |  645 ++++
 mes-processes/mes-plcSend/src/main/java/com/mes/device/service/impl/DeviceCoordinationServiceImpl.java                       |   10 
 mes-processes/mes-plcSend/src/main/java/com/mes/interaction/vehicle/handler/LoadVehicleLogicHandler.java                     | 1719 +++++++++++
 mes-processes/mes-plcSend/src/main/java/com/mes/interaction/vehicle/model/VehicleTask.java                                   |   81 
 mes-processes/mes-plcSend/src/main/java/com/mes/interaction/vehicle/flow/LoadVehicleInteraction.java                         |  225 +
 mes-processes/mes-plcSend/src/main/java/com/mes/interaction/vehicle/model/VehiclePosition.java                               |   63 
 mes-processes/mes-plcSend/src/main/java/com/mes/interaction/vehicle/model/VehicleStatus.java                                 |  113 
 mes-processes/mes-plcSend/src/main/java/com/mes/interaction/workstation/base/WorkstationBaseHandler.java                     |   80 
 mes-processes/mes-plcSend/src/main/java/com/mes/config/TaskExecutorConfig.java                                               |   63 
 mes-processes/mes-plcSend/src/main/java/com/mes/interaction/workstation/config/WorkstationLogicConfig.java                   |   32 
 mes-processes/mes-plcSend/src/main/java/com/mes/s7/provider/S7SerializerProvider.java                                        |   90 
 mes-web/src/views/device/DeviceEditDialog.vue                                                                                |  385 --
 mes-processes/mes-plcSend/src/main/java/com/mes/interaction/DeviceLogicHandler.java                                          |    2 
 mes-web/src/views/device/components/DeviceLogicConfig/WorkstationScannerConfig.vue                                           |   84 
 mes-web/src/views/device/components/DeviceLogicConfig/index.js                                                               |   34 
 mes-processes/mes-plcSend/src/main/java/com/mes/device/service/impl/DeviceConfigServiceImpl.java                             |   48 
 mes-web/src/views/plcTest/components/MultiDeviceTest/ExecutionMonitor.vue                                                    |  482 +++
 mes-processes/mes-plcSend/src/main/java/com/mes/interaction/DeviceInteractionRegistry.java                                   |    1 
 mes-web/src/views/plcTest/MultiDeviceWorkbench.vue                                                                           |  105 
 mes-processes/mes-plcSend/src/main/java/com/mes/interaction/largeglass/model/GridRange.java                                  |   30 
 mes-processes/mes-plcSend/src/main/java/com/mes/interaction/workstation/transfer/handler/HorizontalTransferLogicHandler.java |  481 +++
 mes-web/src/views/device/DeviceConfigList.vue                                                                                |    4 
 mes-processes/mes-plcSend/src/main/java/com/mes/device/service/impl/DevicePlcOperationServiceImpl.java                       |   20 
 mes-processes/mes-plcSend/src/main/java/com/mes/interaction/flow/GlassStorageInteraction.java                                |    2 
 mes-web/src/views/plcTest/components/DeviceGroup/GroupTopology.vue                                                           |  478 +++
 /dev/null                                                                                                                    |  451 ---
 mes-processes/mes-plcSend/src/main/java/com/mes/task/service/impl/MultiDeviceTaskServiceImpl.java                            |   42 
 mes-web/src/views/device/components/DeviceLogicConfig/LargeGlassConfig.vue                                                   |  177 +
 mes-web/src/views/device/components/DeviceLogicConfig/LoadVehicleConfig.vue                                                  |  324 ++
 mes-processes/mes-plcSend/src/main/java/com/mes/interaction/vehicle/model/VehiclePath.java                                   |   91 
 mes-processes/mes-plcSend/src/main/java/com/mes/interaction/workstation/scanner/handler/HorizontalScannerLogicHandler.java   |  161 +
 mes-processes/mes-plcSend/src/main/java/com/mes/task/service/TaskExecutionEngine.java                                        |    2 
 mes-processes/mes-plcSend/src/main/java/com/mes/device/service/DevicePlcOperationService.java                                |    2 
 mes-processes/mes-plcSend/src/main/java/com/mes/interaction/flow/LargeGlassInteraction.java                                  |    4 
 mes-processes/mes-plcSend/src/main/java/com/mes/task/controller/TaskStatusNotificationController.java                        |   11 
 mes-processes/mes-plcSend/src/main/java/com/mes/device/entity/DeviceConfig.java                                              |   16 
 mes-processes/mes-plcSend/src/main/java/com/mes/interaction/vehicle/coordination/VehicleStatusManager.java                   |  199 +
 mes-processes/mes-plcSend/src/main/java/com/mes/interaction/vehicle/model/VehicleState.java                                  |   30 
 mes-web/src/views/device/components/DeviceLogicConfig/WorkstationTransferConfig.vue                                          |  146 
 mes-processes/mes-plcSend/src/main/java/com/mes/interaction/largeglass/handler/LargeGlassLogicHandler.java                   |  379 ++
 mes-web/src/views/plcTest/components/MultiDeviceTest/TaskOrchestration.vue                                                   |  199 +
 47 files changed, 8,066 insertions(+), 850 deletions(-)

diff --git a/mes-processes/mes-plcSend/ARCHITECTURE.md b/mes-processes/mes-plcSend/ARCHITECTURE.md
new file mode 100644
index 0000000..4d41c1d
--- /dev/null
+++ b/mes-processes/mes-plcSend/ARCHITECTURE.md
@@ -0,0 +1,536 @@
+# 椤圭洰鏋舵瀯璇存槑鏂囨。
+
+## 馃搵 椤圭洰涓讳綋鏋舵瀯
+
+### 鏍稿績涓讳綋锛歍askExecutionEngine锛堜换鍔℃墽琛屽紩鎿庯級
+
+**浣嶇疆**锛歚com.mes.task.service.TaskExecutionEngine`
+
+**鑱岃矗**锛氬璁惧浠诲姟鎵ц鐨勬牳蹇冨紩鎿庯紝璐熻矗锛�
+- 涓茶/骞惰鎵ц妯″紡鎺у埗
+- 璁惧姝ラ鎵ц璋冨害
+- 璁惧鍗忚皟妫�鏌�
+- 閿欒澶勭悊鍜岄噸璇�
+- 瀹炴椂鐘舵�侀�氱煡
+
+## 馃攧 璋冪敤娴佺▼
+
+### 1. 浠诲姟鍚姩娴佺▼
+
+```
+鐢ㄦ埛璇锋眰 (Controller)
+    鈫�
+MultiDeviceTaskServiceImpl.startTask()
+    鈹溾攢 楠岃瘉璁惧缁�
+    鈹溾攢 鑾峰彇璁惧鍒楄〃
+    鈹溾攢 鍒涘缓浠诲姟璁板綍 (PENDING)
+    鈹斺攢 寮傛鎵ц executeTaskAsync()
+        鈫�
+    TaskExecutionEngine.execute()
+        鈹溾攢 璁惧鍗忚皟妫�鏌� (DeviceCoordinationService)
+        鈹溾攢 纭畾鎵ц妯″紡 (涓茶/骞惰)
+        鈹斺攢 鎵ц璁惧姝ラ
+```
+
+### 2. 璁惧姝ラ鎵ц娴佺▼
+
+```
+TaskExecutionEngine.executeStep()
+    鈫�
+妫�鏌ユ槸鍚︽湁 DeviceInteraction
+    鈹溾攢 鏈� 鈫� executeInteractionStepWithRetry()
+    鈹�       鈫�
+    鈹�   DeviceInteraction.execute(InteractionContext)
+    鈹�       鈫�
+    鈹�   鍏蜂綋璁惧浜や簰瀹炵幇锛堝 LoadVehicleInteraction锛�
+    鈹�       鈫�
+    鈹�   璋冪敤 DeviceInteractionService
+    鈹�       鈫�
+    鈹�   鏈�缁堣皟鐢� DeviceLogicHandler
+    鈹�
+    鈹斺攢 鏃� 鈫� 鐩存帴璋冪敤 DeviceLogicHandler
+            鈫�
+        DeviceLogicHandlerFactory.getHandler(deviceType)
+            鈫�
+        BaseDeviceLogicHandler.execute()
+            鈫�
+        瀛愮被瀹炵幇 doExecute()
+```
+
+### 3. 涓ょ鎵ц璺緞
+
+#### 璺緞A锛氶�氳繃 DeviceInteraction锛堟帹鑽愮敤浜庡鏉傝澶囷級
+
+```
+TaskExecutionEngine
+    鈫�
+DeviceInteraction.execute(InteractionContext)
+    鈫�
+鍏蜂綋瀹炵幇绫伙紙濡� LoadVehicleInteraction锛�
+    鈹溾攢 鐘舵�佹鏌�
+    鈹溾攢 璁惧閫夋嫨锛堝瀹炰緥鍗忚皟锛�
+    鈹溾攢 鏁版嵁鍑嗗
+    鈹斺攢 璋冪敤 DeviceInteractionService
+        鈫�
+    DeviceLogicHandler.execute()
+```
+
+**閫傜敤鍦烘櫙**锛�
+- 闇�瑕佸瀹炰緥鍗忚皟鐨勮澶囷紙濡傚ぇ杞﹁澶囷級
+- 闇�瑕佸鏉傚墠缃鏌ョ殑璁惧
+- 闇�瑕佹暟鎹浆鎹㈠拰鍑嗗鐨勮澶�
+
+#### 璺緞B锛氱洿鎺ヨ皟鐢� DeviceLogicHandler锛堢畝鍗曡澶囷級
+
+```
+TaskExecutionEngine
+    鈫�
+DeviceLogicHandlerFactory.getHandler(deviceType)
+    鈫�
+BaseDeviceLogicHandler.execute()
+    鈹溾攢 鍙傛暟瑙f瀽
+    鈹溾攢 閫昏緫鍙傛暟鎻愬彇 (extraParams.deviceLogic)
+    鈹斺攢 瀛愮被瀹炵幇 doExecute()
+```
+
+**閫傜敤鍦烘櫙**锛�
+- 绠�鍗曡澶囷紝閫昏緫鍗曚竴
+- 涓嶉渶瑕佸鏉傚崗璋冪殑璁惧
+
+## 馃彈锔� 鍒嗗眰鏋舵瀯
+
+### 绗竴灞傦細Controller 灞傦紙API鍏ュ彛锛�
+
+**浣嶇疆**锛歚com.mes.task.controller.*`
+
+**鑱岃矗**锛�
+- 鎺ユ敹HTTP璇锋眰
+- 鍙傛暟楠岃瘉
+- 璋冪敤Service灞�
+
+**涓昏绫�**锛�
+- `MultiDeviceTaskController` - 浠诲姟绠$悊API
+- `TaskStatusNotificationController` - SSE瀹炴椂閫氱煡API
+
+### 绗簩灞傦細Service 灞傦紙涓氬姟閫昏緫锛�
+
+**浣嶇疆**锛歚com.mes.task.service.*`
+
+**鑱岃矗**锛�
+- 涓氬姟閫昏緫澶勭悊
+- 浜嬪姟绠$悊
+- 璋冪敤鎵ц寮曟搸
+
+**涓昏绫�**锛�
+- `MultiDeviceTaskServiceImpl` - 浠诲姟鏈嶅姟瀹炵幇
+  - 鍒涘缓浠诲姟璁板綍
+  - 寮傛鎵ц浠诲姟
+  - 浠诲姟鐘舵�佺鐞�
+  
+- `TaskExecutionEngine` - **鏍稿績鎵ц寮曟搸**
+  - 璁惧鍗忚皟妫�鏌�
+  - 鎵ц妯″紡鍒ゆ柇锛堜覆琛�/骞惰锛�
+  - 姝ラ鎵ц璋冨害
+  - 閲嶈瘯鏈哄埗
+
+### 绗笁灞傦細Interaction 灞傦紙璁惧浜や簰锛�
+
+**浣嶇疆**锛歚com.mes.interaction.*`
+
+**鑱岃矗**锛�
+- 璁惧浜や簰閫昏緫灏佽
+- 澶氳澶囧崗璋�
+- 鏁版嵁浼犻��
+
+**涓昏缁勪欢**锛�
+
+#### 3.1 DeviceInteraction锛堣澶囦氦浜掓帴鍙o級
+
+**鎺ュ彛**锛歚com.mes.interaction.DeviceInteraction`
+
+**瀹炵幇绫荤ず渚�**锛�
+- `LoadVehicleInteraction` - 澶ц溅璁惧浜や簰
+- `LargeGlassInteraction` - 澶х悊鐗囩浜や簰
+- `GlassStorageInteraction` - 鐜荤拑瀛樺偍浜や簰
+
+**娉ㄥ唽鏈哄埗**锛�
+- `DeviceInteractionRegistry` - 鑷姩娉ㄥ唽鎵�鏈� `@Component` 鐨� `DeviceInteraction` 瀹炵幇
+
+#### 3.2 DeviceLogicHandler锛堣澶囬�昏緫澶勭悊鍣級
+
+**鎺ュ彛**锛歚com.mes.interaction.DeviceLogicHandler`
+
+**鍩虹被**锛歚BaseDeviceLogicHandler`
+- 鎻愪緵閫氱敤鍔熻兘锛�
+  - 鍙傛暟瑙f瀽
+  - 閫昏緫鍙傛暟鎻愬彇锛堜粠 `extraParams.deviceLogic`锛�
+  - 閿欒澶勭悊
+
+**瀹炵幇绫荤ず渚�**锛�
+- `LoadVehicleLogicHandler` - 澶ц溅璁惧閫昏緫
+- `HorizontalScannerLogicHandler` - 鍗ц浆绔嬫壂鐮侀�昏緫
+- `HorizontalTransferLogicHandler` - 鍗ц浆绔嬮�昏緫
+- `LargeGlassLogicHandler` - 澶х悊鐗囩閫昏緫
+
+**娉ㄥ唽鏈哄埗**锛�
+- `DeviceLogicHandlerFactory` - 鑷姩娉ㄥ唽鎵�鏈� `DeviceLogicHandler` 瀹炵幇
+- 閫氳繃 `@PostConstruct` 鍒濆鍖栨槧灏勮〃
+
+### 绗洓灞傦細Coordination 灞傦紙璁惧鍗忚皟锛�
+
+**浣嶇疆**锛歚com.mes.device.service.*` 鍜� `com.mes.interaction.*.coordination.*`
+
+**鑱岃矗**锛�
+- 璁惧闂存暟鎹紶閫�
+- 璁惧鐘舵�佸悓姝�
+- 璁惧渚濊禆绠$悊
+- 澶氬疄渚嬪崗璋�
+
+**涓昏绫�**锛�
+- `DeviceCoordinationService` - 閫氱敤璁惧鍗忚皟鏈嶅姟
+- `VehicleCoordinationService` - 澶ц溅璁惧鍗忚皟鏈嶅姟
+- `VehicleStatusManager` - 澶ц溅鐘舵�佺鐞嗗櫒
+
+### 绗簲灞傦細PLC 鎿嶄綔灞傦紙纭欢閫氫俊锛�
+
+**浣嶇疆**锛歚com.mes.device.service.DevicePlcOperationService`
+
+**鑱岃矗**锛�
+- PLC璇诲啓鎿嶄綔
+- 鍦板潃鏄犲皠
+- 閫氫俊绠$悊
+
+## 馃摝 濡備綍娣诲姞鏂拌澶�
+
+### 姝ラ1锛氬畾涔夎澶囩被鍨嬪父閲�
+
+鍦� `DeviceConfig.DeviceType` 涓坊鍔狅細
+
+```java
+public static final class DeviceType {
+    public static final String NEW_DEVICE = "鏂拌澶囩被鍨�";
+}
+```
+
+### 姝ラ2锛氬垱寤鸿澶囬�昏緫澶勭悊鍣紙蹇呴』锛�
+
+**浣嶇疆**锛歚com.mes.interaction.*.handler.NewDeviceLogicHandler`
+
+```java
+@Component
+public class NewDeviceLogicHandler extends BaseDeviceLogicHandler {
+    
+    @Override
+    public String getDeviceType() {
+        return DeviceConfig.DeviceType.NEW_DEVICE;
+    }
+    
+    @Override
+    protected DevicePlcVO.OperationResult doExecute(
+            DeviceConfig deviceConfig,
+            String operation,
+            Map<String, Object> params,
+            Map<String, Object> logicParams) {
+        
+        // 1. 浠� logicParams 涓幏鍙栭厤缃弬鏁�
+        Integer timeout = getLogicParam(logicParams, "timeout", 5000);
+        String mode = getLogicParam(logicParams, "mode", "default");
+        
+        // 2. 浠� params 涓幏鍙栬繍琛屾椂鍙傛暟
+        String glassId = (String) params.get("glassId");
+        
+        // 3. 鎵ц璁惧閫昏緫
+        // ... 鍏蜂綋瀹炵幇
+        
+        // 4. 璋冪敤PLC鎿嶄綔
+        Map<String, Object> plcParams = new HashMap<>();
+        plcParams.put("field1", value1);
+        DevicePlcVO.OperationResult result = 
+            devicePlcOperationService.writePlcData(deviceConfig, plcParams);
+        
+        return result;
+    }
+    
+    @Override
+    protected Map<String, Object> getDefaultLogicParams() {
+        Map<String, Object> defaults = new HashMap<>();
+        defaults.put("timeout", 5000);
+        defaults.put("mode", "default");
+        return defaults;
+    }
+}
+```
+
+**鑷姩娉ㄥ唽**锛�
+- 瀹炵幇 `DeviceLogicHandler` 鎺ュ彛
+- 娣诲姞 `@Component` 娉ㄨВ
+- `DeviceLogicHandlerFactory` 浼氳嚜鍔ㄥ彂鐜板苟娉ㄥ唽
+
+### 姝ラ3锛氬垱寤鸿澶囦氦浜掔被锛堝彲閫夛紝澶嶆潅璁惧鎺ㄨ崘锛�
+
+**浣嶇疆**锛歚com.mes.interaction.*.flow.NewDeviceInteraction`
+
+```java
+@Component
+public class NewDeviceInteraction implements DeviceInteraction {
+    
+    @Override
+    public String getDeviceType() {
+        return DeviceConfig.DeviceType.NEW_DEVICE;
+    }
+    
+    @Override
+    public InteractionResult execute(InteractionContext context) {
+        DeviceConfig device = context.getCurrentDevice();
+        
+        // 1. 鍓嶇疆鏉′欢妫�鏌�
+        if (device == null) {
+            return InteractionResult.fail("璁惧閰嶇疆涓嶅瓨鍦�");
+        }
+        
+        // 2. 鏁版嵁鍑嗗
+        List<String> glassIds = context.getParameters().getGlassIds();
+        
+        // 3. 璋冪敤璁惧閫昏緫澶勭悊鍣�
+        DeviceInteractionService service = ...;
+        Map<String, Object> params = new HashMap<>();
+        params.put("glassIds", glassIds);
+        
+        DevicePlcVO.OperationResult result = 
+            service.executeDeviceOperation(device, "operationName", params);
+        
+        if (result.isSuccess()) {
+            // 4. 鏁版嵁浼犻�掑埌涓嬩竴涓澶�
+            context.getSharedData().put("processedGlasses", glassIds);
+            return InteractionResult.success("鎵ц鎴愬姛", result.getData());
+        } else {
+            return InteractionResult.fail(result.getMessage());
+        }
+    }
+}
+```
+
+**鑷姩娉ㄥ唽**锛�
+- 瀹炵幇 `DeviceInteraction` 鎺ュ彛
+- 娣诲姞 `@Component` 娉ㄨВ
+- `DeviceInteractionRegistry` 浼氳嚜鍔ㄥ彂鐜板苟娉ㄥ唽
+
+### 姝ラ4锛氶厤缃澶囬�昏緫鍙傛暟锛堝墠绔級
+
+鍦ㄨ澶囬厤缃殑 `extraParams.deviceLogic` 涓厤缃細
+
+```json
+{
+  "deviceLogic": {
+    "timeout": 5000,
+    "mode": "default",
+    "customParam1": "value1"
+  }
+}
+```
+
+### 姝ラ5锛氳澶囩粍缁囨柟寮忛�夋嫨
+
+#### 绠�鍗曡澶囷紙鎺ㄨ崘鏀惧湪閫氱敤鍖咃級
+
+```
+interaction/
+鈹斺攢鈹� impl/
+    鈹斺攢鈹� NewDeviceLogicHandler.java
+```
+
+#### 澶嶆潅璁惧锛堥渶瑕佸崗璋冦�佺姸鎬佺鐞嗙瓑锛�
+
+```
+interaction/
+鈹斺攢鈹� newdevice/
+    鈹溾攢鈹� handler/
+    鈹�   鈹斺攢鈹� NewDeviceLogicHandler.java
+    鈹溾攢鈹� flow/
+    鈹�   鈹斺攢鈹� NewDeviceInteraction.java
+    鈹溾攢鈹� coordination/  (鍙��)
+    鈹�   鈹斺攢鈹� NewDeviceCoordinationService.java
+    鈹斺攢鈹� model/  (鍙��)
+        鈹斺攢鈹� NewDeviceStatus.java
+```
+
+## 馃攳 鎵ц娴佺▼璇﹁В
+
+### 涓茶鎵ц妯″紡
+
+```
+TaskExecutionEngine.execute()
+    鈫�
+for (姣忎釜璁惧) {
+    1. 鍒涘缓姝ラ璁板綍 (TaskStepDetail)
+    2. executeStep()
+        鈫�
+    3. 妫�鏌ユ槸鍚︽湁 DeviceInteraction
+        鈹溾攢 鏈� 鈫� DeviceInteraction.execute()
+        鈹�       鈫�
+        鈹�   璋冪敤 DeviceLogicHandler
+        鈹�
+        鈹斺攢 鏃� 鈫� 鐩存帴璋冪敤 DeviceLogicHandler
+                鈫�
+            BaseDeviceLogicHandler.execute()
+                鈫�
+            瀛愮被 doExecute()
+    4. 鏇存柊姝ラ鐘舵��
+    5. 浼犻�掓暟鎹埌涓嬩竴涓澶�
+}
+```
+
+### 骞惰鎵ц妯″紡
+
+```
+TaskExecutionEngine.execute()
+    鈫�
+鍒涘缓绾跨▼姹犱换鍔″垪琛�
+    鈫�
+for (姣忎釜璁惧) {
+    鎻愪氦鍒扮嚎绋嬫睜鎵ц
+        鈫�
+    executeStep() (鍚屼笂)
+}
+    鈫�
+绛夊緟鎵�鏈変换鍔″畬鎴�
+    鈫�
+姹囨�荤粨鏋�
+```
+
+## 馃摑 鍏抽敭鎺ュ彛璇存槑
+
+### DeviceLogicHandler
+
+**鎺ュ彛鏂规硶**锛�
+```java
+DevicePlcVO.OperationResult execute(
+    DeviceConfig deviceConfig,
+    String operation,
+    Map<String, Object> params
+);
+```
+
+**璋冪敤鏃舵満**锛�
+- 浠诲姟鎵ц寮曟搸鐩存帴璋冪敤
+- 鎴栭�氳繃 DeviceInteraction 闂存帴璋冪敤
+
+### DeviceInteraction
+
+**鎺ュ彛鏂规硶**锛�
+```java
+InteractionResult execute(InteractionContext context);
+```
+
+**璋冪敤鏃舵満**锛�
+- 浠诲姟鎵ц寮曟搸浼樺厛妫�鏌ユ槸鍚︽湁 DeviceInteraction
+- 濡傛灉鏈夛紝浼樺厛浣跨敤 DeviceInteraction
+- 濡傛灉娌℃湁锛岀洿鎺ヨ皟鐢� DeviceLogicHandler
+
+### InteractionContext
+
+**鍖呭惈鍐呭**锛�
+- `currentDevice` - 褰撳墠璁惧閰嶇疆
+- `taskContext` - 浠诲姟涓婁笅鏂�
+- `parameters` - 浠诲姟鍙傛暟
+- `sharedData` - 璁惧闂村叡浜暟鎹�
+
+## 馃幆 鏈�浣冲疄璺�
+
+### 1. 浣曟椂浣跨敤 DeviceInteraction锛�
+
+**浣跨敤鍦烘櫙**锛�
+- 鉁� 闇�瑕佸瀹炰緥鍗忚皟锛堝澶ц溅璁惧锛�
+- 鉁� 闇�瑕佸鏉傚墠缃鏌�
+- 鉁� 闇�瑕佹暟鎹浆鎹㈠拰鍑嗗
+- 鉁� 闇�瑕佺姸鎬佺鐞�
+
+**涓嶄娇鐢ㄥ満鏅�**锛�
+- 鉂� 绠�鍗曡澶囷紝閫昏緫鍗曚竴
+- 鉂� 涓嶉渶瑕佸崗璋冪殑璁惧
+
+### 2. 鍙傛暟璁捐
+
+**logicParams锛堥�昏緫鍙傛暟锛�**锛�
+- 浠� `extraParams.deviceLogic` 涓鍙�
+- 璁惧閰嶇疆鏃惰缃紝杩愯鏃朵笉鍙�
+- 濡傦細瓒呮椂鏃堕棿銆佹ā寮忋�佸閲忕瓑
+
+**params锛堣繍琛屾椂鍙傛暟锛�**锛�
+- 浠诲姟鎵ц鏃跺姩鎬佷紶鍏�
+- 濡傦細鐜荤拑ID銆佷綅缃�佹暟閲忕瓑
+
+### 3. 閿欒澶勭悊
+
+**鍦� BaseDeviceLogicHandler 涓�**锛�
+- 鑷姩鎹曡幏寮傚父
+- 杩斿洖缁熶竴鐨勯敊璇牸寮�
+
+**鍦ㄥ瓙绫讳腑**锛�
+- 鍙互鎶涘嚭涓氬姟寮傚父
+- 浼氳鍩虹被鎹曡幏骞惰浆鎹�
+
+### 4. 鏁版嵁浼犻��
+
+**閫氳繃 InteractionContext.sharedData**锛�
+```java
+// 鍦ㄨ澶嘇涓缃�
+context.getSharedData().put("glassIds", glassIds);
+
+// 鍦ㄨ澶嘊涓幏鍙�
+List<String> glassIds = (List<String>) context.getSharedData().get("glassIds");
+```
+
+## 馃摎 鐩稿叧鏂囦欢浣嶇疆
+
+### 鏍稿績鏂囦欢
+- `TaskExecutionEngine.java` - 浠诲姟鎵ц寮曟搸
+- `MultiDeviceTaskServiceImpl.java` - 浠诲姟鏈嶅姟
+- `DeviceLogicHandlerFactory.java` - 澶勭悊鍣ㄥ伐鍘�
+- `DeviceInteractionRegistry.java` - 浜や簰娉ㄥ唽涓績
+
+### 鍩虹被
+- `BaseDeviceLogicHandler.java` - 閫昏緫澶勭悊鍣ㄥ熀绫�
+- `DeviceInteraction.java` - 浜や簰鎺ュ彛
+
+### 绀轰緥瀹炵幇
+- `LoadVehicleLogicHandler.java` - 澶ц溅璁惧閫昏緫澶勭悊鍣�
+- `LoadVehicleInteraction.java` - 澶ц溅璁惧浜や簰
+- `HorizontalScannerLogicHandler.java` - 鍗ц浆绔嬫壂鐮佸鐞嗗櫒
+
+## 馃敡 璋冭瘯鎶�宸�
+
+### 1. 鏌ョ湅娉ㄥ唽鐨勮澶囧鐞嗗櫒
+
+```java
+@Autowired
+private DeviceLogicHandlerFactory factory;
+
+// 鏌ョ湅鎵�鏈夊凡娉ㄥ唽鐨勮澶囩被鍨�
+Set<String> types = factory.getSupportedDeviceTypes();
+```
+
+### 2. 鏌ョ湅娉ㄥ唽鐨勮澶囦氦浜�
+
+```java
+@Autowired
+private DeviceInteractionRegistry registry;
+
+// 鏌ョ湅鎵�鏈夊凡娉ㄥ唽鐨勪氦浜�
+Map<String, DeviceInteraction> interactions = registry.getInteractions();
+```
+
+### 3. 鏃ュ織杈撳嚭
+
+- 浠诲姟鎵ц锛氭煡鐪� `TaskExecutionEngine` 鏃ュ織
+- 璁惧鎵ц锛氭煡鐪嬪叿浣� Handler 鏃ュ織
+- 鍗忚皟鏈嶅姟锛氭煡鐪� `DeviceCoordinationService` 鏃ュ織
+
+## 鉁� 鎬荤粨
+
+1. **椤圭洰涓讳綋**锛歚TaskExecutionEngine` 鏄牳蹇冩墽琛屽紩鎿�
+2. **璋冪敤娴佺▼**锛欳ontroller 鈫� Service 鈫� Engine 鈫� Interaction/Handler 鈫� PLC
+3. **鍒嗗眰鏋舵瀯**锛欳ontroller 鈫� Service 鈫� Interaction 鈫� Coordination 鈫� PLC
+4. **娣诲姞璁惧**锛氬疄鐜� `DeviceLogicHandler`锛堝繀椤伙級+ `DeviceInteraction`锛堝彲閫夛級
+5. **鑷姩娉ㄥ唽**锛氶�氳繃 Spring 鐨� `@Component` 鍜屽伐鍘傜被鑷姩鍙戠幇鍜屾敞鍐�
+
diff --git a/mes-processes/mes-plcSend/README.md b/mes-processes/mes-plcSend/README.md
new file mode 100644
index 0000000..0e37557
--- /dev/null
+++ b/mes-processes/mes-plcSend/README.md
@@ -0,0 +1,645 @@
+# MES PLC Send 椤圭洰鏂囨。
+
+## 馃搵 椤圭洰姒傝堪
+
+MES PLC Send 鏄竴涓熀浜� Spring Boot 鐨勫璁惧鑱斿悎娴嬭瘯绯荤粺锛屾敮鎸� PLC 璁惧绠$悊銆佽澶囩粍閰嶇疆銆佸璁惧浠诲姟缂栨帓鍜屾墽琛屻�傜郴缁熷疄鐜颁簡"妯℃澘 + 瀹炰緥"鐨勮璁℃ā寮忥紝鏀寔涓�涓澶囩被鍨嬫ā鏉垮搴斿涓澶囧疄渚嬶紝瀹炵幇浜嗚澶囬棿鐨勫崗璋冨拰鏁版嵁浼犻�掋��
+
+**鎶�鏈爤**锛�
+- Spring Boot 2.x
+- MyBatis-Plus
+- S7NetPlus锛圥LC閫氫俊锛�
+- MySQL
+- Server-Sent Events (SSE) 瀹炴椂鎺ㄩ��
+
+**鏈嶅姟绔彛**锛�10018
+
+## 馃幆 鏍稿績鍔熻兘
+
+### 1. 璁惧绠$悊
+- 璁惧閰嶇疆绠$悊锛圥LC IP銆佽澶囩被鍨嬨�佹ā鍧楀悕绉扮瓑锛�
+- 璁惧鐘舵�佺洃鎺э紙鍦ㄧ嚎/绂荤嚎/蹇欑/閿欒/缁存姢涓級
+- 鏀寔5绉嶈澶囩被鍨嬶細
+  - **澶ц溅璁惧** (`LOAD_VEHICLE`)锛氭敮鎸佸瀹炰緥鍗忚皟锛岃嚜鍔ㄧ姸鎬佺鐞嗭紝MES浠诲姟澶勭悊
+  - **澶х悊鐗囩** (`LARGE_GLASS`)锛氭牸瀛愯寖鍥撮厤缃紝閫昏緫鍒ゆ柇
+  - **鍗ц浆绔嬫壂鐮�** (`WORKSTATION_SCANNER`)锛氬畾鏃舵壂鎻忥紝MES鏁版嵁璇诲彇
+  - **鍗ц浆绔�** (`WORKSTATION_TRANSFER`)锛�30s缂撳啿鍒ゅ畾锛屾壒閲忓鐞�
+  - **鍗у紡缂撳瓨** (`GLASS_STORAGE`)锛氱幓鐠冨瓨鍌ㄧ鐞嗭紙宸插疄鐜帮紝浣嗗綋鍓嶄笉浣跨敤锛�
+
+### 2. 璁惧缁勭鐞�
+- 璁惧缁勯厤缃紙鏀寔鏈�澶у苟鍙戣澶囨暟鎺у埗锛�
+- 璁惧缁勬嫇鎵戝彲瑙嗗寲
+- 璁惧渚濊禆鍏崇郴绠$悊锛堜紭鍏堢骇銆佽鑹层�佽繛鎺ラ『搴忥級
+- 璁惧缁勪换鍔$紪鎺�
+
+### 3. 澶氳澶囦换鍔℃墽琛�
+- **涓茶鎵ц**锛氭寜璁惧杩炴帴椤哄簭鎵ц
+- **骞惰鎵ц**锛氭敮鎸佸璁惧骞惰鎵ц锛岄�氳繃 `max_concurrent_devices` 鎺у埗骞跺彂鏁�
+- **瀹炴椂鐩戞帶**锛氬熀浜� SSE锛圫erver-Sent Events锛夌殑瀹炴椂鐘舵�佹帹閫�
+- **閿欒澶勭悊**锛氳嚜鍔ㄩ噸璇曟満鍒讹紝鏀寔鎸囨暟閫�閬�
+- **鏁版嵁浼犻��**锛氳澶囬棿鏁版嵁鍏变韩鍜岀姸鎬佸悓姝�
+
+### 4. 璁惧鍗忚皟鏈嶅姟
+- 璁惧闂存暟鎹紶閫掓満鍒�
+- 璁惧鐘舵�佸悓姝�
+- 璁惧渚濊禆绠$悊
+- 澶氬疄渚嬪崗璋冿紙濡傚杞﹀崗璋冿級
+
+## 馃彈锔� 鏋舵瀯璁捐
+
+### 鐩綍缁撴瀯
+
+```
+mes-plcSend/
+鈹溾攢鈹� device/                          # 璁惧绠$悊灞�
+鈹�   鈹溾攢鈹� entity/                      # 瀹炰綋绫�
+鈹�   鈹�   鈹溾攢鈹� DeviceConfig.java        # 璁惧閰嶇疆
+鈹�   鈹�   鈹溾攢鈹� DeviceGroupConfig.java   # 璁惧缁勯厤缃�
+鈹�   鈹�   鈹溾攢鈹� DeviceGroupRelation.java # 璁惧缁勫叧绯�
+鈹�   鈹�   鈹溾攢鈹� DeviceStatus.java        # 璁惧鐘舵��
+鈹�   鈹�   鈹斺攢鈹� GlassInfo.java           # 鐜荤拑淇℃伅
+鈹�   鈹溾攢鈹� service/                     # 鏈嶅姟灞�
+鈹�   鈹�   鈹溾攢鈹� DeviceConfigService.java
+鈹�   鈹�   鈹溾攢鈹� DeviceGroupConfigService.java
+鈹�   鈹�   鈹溾攢鈹� DeviceCoordinationService.java  # 璁惧鍗忚皟鏈嶅姟
+鈹�   鈹�   鈹斺攢鈹� GlassInfoService.java
+鈹�   鈹斺攢鈹� controller/                  # 鎺у埗鍣�
+鈹�       鈹溾攢鈹� DeviceConfigController.java
+鈹�       鈹溾攢鈹� DeviceGroupController.java
+鈹�       鈹斺攢鈹� DevicePlcController.java
+鈹�
+鈹溾攢鈹� interaction/                     # 浜や簰閫昏緫灞�
+鈹�   鈹溾攢鈹� base/                        # 鍩虹鎺ュ彛
+鈹�   鈹�   鈹溾攢鈹� BaseDeviceLogicHandler.java
+鈹�   鈹�   鈹溾攢鈹� DeviceInteraction.java
+鈹�   鈹�   鈹斺攢鈹� InteractionContext.java
+鈹�   鈹�
+鈹�   鈹溾攢鈹� vehicle/                     # 澶ц溅璁惧涓撶敤鍖�
+鈹�   鈹�   鈹溾攢鈹� handler/
+鈹�   鈹�   鈹�   鈹斺攢鈹� LoadVehicleLogicHandler.java  # 鍏变韩閫昏緫澶勭悊鍣�
+鈹�   鈹�   鈹溾攢鈹� flow/
+鈹�   鈹�   鈹�   鈹斺攢鈹� LoadVehicleInteraction.java
+鈹�   鈹�   鈹溾攢鈹� coordination/
+鈹�   鈹�   鈹�   鈹溾攢鈹� VehicleStatusManager.java      # 鐘舵�佺鐞嗗櫒
+鈹�   鈹�   鈹�   鈹斺攢鈹� VehicleCoordinationService.java # 鍗忚皟鏈嶅姟
+鈹�   鈹�   鈹斺攢鈹� model/
+鈹�   鈹�       鈹溾攢鈹� VehicleStatus.java
+鈹�   鈹�       鈹溾攢鈹� VehiclePosition.java
+鈹�   鈹�       鈹溾攢鈹� VehicleState.java
+鈹�   鈹�       鈹溾攢鈹� VehiclePath.java
+鈹�   鈹�       鈹斺攢鈹� VehicleTask.java
+鈹�   鈹�
+鈹�   鈹溾攢鈹� workstation/                 # 鍗ц浆绔嬭澶囧寘
+鈹�   鈹�   鈹溾攢鈹� base/
+鈹�   鈹�   鈹�   鈹斺攢鈹� WorkstationBaseHandler.java
+鈹�   鈹�   鈹溾攢鈹� config/
+鈹�   鈹�   鈹�   鈹斺攢鈹� WorkstationLogicConfig.java
+鈹�   鈹�   鈹溾攢鈹� scanner/
+鈹�   鈹�   鈹�   鈹斺攢鈹� handler/
+鈹�   鈹�   鈹�       鈹斺攢鈹� HorizontalScannerLogicHandler.java
+鈹�   鈹�   鈹斺攢鈹� transfer/
+鈹�   鈹�       鈹斺攢鈹� handler/
+鈹�   鈹�           鈹斺攢鈹� HorizontalTransferLogicHandler.java
+鈹�   鈹�
+鈹�   鈹溾攢鈹� largeglass/                  # 澶х悊鐗囩璁惧鍖�
+鈹�   鈹�   鈹溾攢鈹� handler/
+鈹�   鈹�   鈹�   鈹斺攢鈹� LargeGlassLogicHandler.java
+鈹�   鈹�   鈹溾攢鈹� config/
+鈹�   鈹�   鈹�   鈹斺攢鈹� LargeGlassConfig.java
+鈹�   鈹�   鈹斺攢鈹� model/
+鈹�   鈹�       鈹斺攢鈹� GridRange.java
+鈹�   鈹�
+鈹�   鈹溾攢鈹� flow/                        # 浜や簰娴佺▼锛堥�氱敤锛�
+鈹�   鈹�   鈹溾攢鈹� GlassStorageInteraction.java
+鈹�   鈹�   鈹斺攢鈹� LargeGlassInteraction.java
+鈹�   鈹�
+鈹�   鈹斺攢鈹� impl/                        # 绠�鍗曡澶囧疄鐜�
+鈹�       鈹斺攢鈹� GlassStorageLogicHandler.java
+鈹�
+鈹溾攢鈹� task/                            # 浠诲姟绠$悊灞�
+鈹�   鈹溾攢鈹� entity/
+鈹�   鈹�   鈹溾攢鈹� MultiDeviceTask.java     # 澶氳澶囦换鍔�
+鈹�   鈹�   鈹斺攢鈹� TaskStepDetail.java      # 浠诲姟姝ラ璇︽儏
+鈹�   鈹溾攢鈹� service/
+鈹�   鈹�   鈹溾攢鈹� MultiDeviceTaskService.java
+鈹�   鈹�   鈹溾攢鈹� TaskExecutionEngine.java  # 浠诲姟鎵ц寮曟搸
+鈹�   鈹�   鈹斺攢鈹� TaskStatusNotificationService.java  # SSE鎺ㄩ�佹湇鍔�
+鈹�   鈹溾攢鈹� controller/
+鈹�   鈹�   鈹溾攢鈹� MultiDeviceTaskController.java
+鈹�   鈹�   鈹斺攢鈹� TaskStatusNotificationController.java
+鈹�   鈹斺攢鈹� model/
+鈹�       鈹溾攢鈹� RetryPolicy.java         # 閲嶈瘯绛栫暐
+鈹�       鈹溾攢鈹� TaskExecutionContext.java
+鈹�       鈹斺攢鈹� TaskExecutionResult.java
+鈹�
+鈹溾攢鈹� service/                         # PLC鏈嶅姟灞�
+鈹�   鈹溾攢鈹� PlcDynamicDataService.java   # PLC鍔ㄦ�佹暟鎹湇鍔�
+鈹�   鈹斺攢鈹� PlcTestWriteService.java
+鈹�
+鈹斺攢鈹� s7/                              # S7閫氫俊
+    鈹斺攢鈹� provider/
+        鈹斺攢鈹� S7SerializerProvider.java
+```
+
+### 鏍稿績璁捐妯″紡
+
+#### 1. 妯℃澘 + 瀹炰緥妯″紡
+
+**璁捐鐞嗗康**锛氫竴涓澶囩被鍨嬫ā鏉匡紙鍏变韩閫昏緫锛�+ 澶氫釜璁惧瀹炰緥锛堢嫭绔嬬姸鎬侊級
+
+```
+LoadVehicleLogicHandler (鍏变韩閫昏緫澶勭悊鍣�)
+    鈫�
+鈹屸攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�  鈹屸攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�  鈹屸攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+鈹� 澶ц溅瀹炰緥1鈹�  鈹� 澶ц溅瀹炰緥2鈹�  鈹� 澶ц溅瀹炰緥3鈹�
+鈹� 鐘舵�佺嫭绔� 鈹�  鈹� 鐘舵�佺嫭绔� 鈹�  鈹� 鐘舵�佺嫭绔� 鈹�
+鈹斺攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�  鈹斺攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�  鈹斺攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+```
+
+**浼樺娍**锛�
+- 鍏变韩閫昏緫锛氭墍鏈夊疄渚嬩娇鐢ㄥ悓涓�涓�昏緫澶勭悊鍣�
+- 鐙珛鐘舵�侊細姣忎釜瀹炰緥鏈夌嫭绔嬬殑杩愯鏃剁姸鎬�
+- 鐏垫椿閰嶇疆锛氳澶囩粍鍙互鍖呭惈浠绘剰鏁伴噺鐨勫疄渚�
+- 鏄撲簬鎵╁睍锛氭柊澧炲疄渚嬪彧闇�鍦ㄦ暟鎹簱娣诲姞璁板綍
+
+#### 2. 娣峰悎鍒嗗眰鏋舵瀯
+
+- **澶嶆潅璁惧**锛堝澶ц溅锛夛細鐙珛鍖咃紝鍖呭惈鍗忚皟鏈嶅姟銆佺姸鎬佺鐞嗙瓑
+- **绠�鍗曡澶�**锛堝鐜荤拑瀛樺偍锛夛細鏀惧湪閫氱敤鍖呬腑
+
+## 馃敡 鏍稿績缁勪欢璇存槑
+
+### 1. 澶ц溅璁惧锛圠oadVehicle锛�
+
+#### 鍔熻兘鐗规��
+- **绌洪棽鐘舵�佺洃鎺�**锛氭病鏈変换鍔℃椂锛宍plcRequest` 淇濇寔涓� 1
+- **MES浠诲姟璇诲彇**锛氬綋 `mesSend=1` 鏃讹紝璇诲彇 MES 鍙傛暟锛堢幓鐠僆D銆佽捣濮嬩綅缃�佺洰鏍囦綅缃瓑锛�
+- **浣嶇疆鏄犲皠**锛氬皢 MES 浣嶇疆缂栧彿锛堝 900銆�901锛夋槧灏勪负瀹為檯缃戞牸浣嶇疆锛堝 100銆�500锛�
+- **鏃堕棿璁$畻**锛氭牴鎹溅杈嗛�熷害锛坓rids/second锛夈�佸綋鍓嶄綅缃�佺洰鏍囦綅缃绠� `gotime` 鍜� `cartime`
+- **鐘舵�佺鐞�**锛歚state1~6` 鐘舵�佹祦杞紙0鈫�1鈫�2锛夛紝鑷姩瑙﹀彂 MES 姹囨姤
+- **鑷姩鍗忚皟**锛氬綋 `state=1`锛堜笂杞﹀畬鎴愶級鏃讹紝鑷姩灏�"鍗ц浆绔�"璁惧鐨� `plcRequest` 璁剧疆涓� 0
+- **鍑虹墖閫昏緫**锛氭敮鎸佽繘鐗囧拰鍑虹墖浠诲姟锛屾牴鎹� `startSlot` 鍜� `outboundSlotRanges` 鑷姩鍒ゆ柇浠诲姟绫诲瀷
+
+#### 閰嶇疆鍙傛暟锛坋xtraParams.deviceLogic锛�
+```json
+{
+  "vehicleCapacity": 6,
+  "vehicleSpeed": 1.0,
+  "minRange": 1,
+  "maxRange": 100,
+  "homePosition": 50,
+  "idleMonitorIntervalMs": 1000,
+  "taskMonitorIntervalMs": 1000,
+  "mesConfirmTimeoutMs": 30000,
+  "positionMapping": {
+    "900": 100,
+    "901": 500
+  },
+  "outboundSlotRanges": [
+    {"start": 1000, "end": 2000}
+  ],
+  "gridPositionMapping": {
+    "1000": 80
+  }
+}
+```
+
+#### 鐘舵�佹祦杞�
+```
+IDLE (绌洪棽) 鈫� EXECUTING (鎵ц涓�) 鈫� IDLE (绌洪棽)
+```
+
+### 2. 鍗ц浆绔嬫壂鐮侊紙HorizontalScanner锛�
+
+#### 鍔熻兘鐗规��
+- **瀹氭椂鎵弿**锛氬彲閰嶇疆鎵弿闂撮殧锛堥粯璁� 10s锛�
+- **MES鏁版嵁璇诲彇**锛氬綋 `mesSend=1` 鏃讹紝璇诲彇鐜荤拑淇℃伅锛坄mesGlassId`銆乣mesWidth`銆乣mesHeight`銆乣workLine`锛�
+- **鏁版嵁钀藉簱**锛氬皢鐜荤拑淇℃伅淇濆瓨鍒� `glass_info` 琛�
+- **鑷姩纭**锛氳鍙栧悗鑷姩灏� `mesSend` 鍐欏洖 0
+
+#### 閰嶇疆鍙傛暟锛坋xtraParams.deviceLogic锛�
+```json
+{
+  "scanIntervalMs": 10000,
+  "workLine": "LINE_001",
+  "autoConfirm": true
+}
+```
+
+### 3. 鍗ц浆绔嬶紙HorizontalTransfer锛�
+
+#### 鍔熻兘鐗规��
+- **30s缂撳啿鍒ゅ畾**锛氱瓑寰� 30s锛屽鏋滄病鏈変笅涓�鐗囩幓鐠冩壂鐮侊紝鍒欒涓烘槸鏈�鍚庝竴鐗�
+- **瀹归噺鍒ゆ柇**锛氬垽鏂兘鍚︽斁涓嬬浜岀墖鐜荤拑
+- **鎵归噺澶勭悊**锛氬皢澶氱墖鐜荤拑缁勮鎴愭壒娆�
+- **PLC鍐欏叆**锛氬啓鍏� `plcGlassId1~6`銆乣plcGlassCount`銆乣inPosition`銆乣plcRequest`
+
+#### 閰嶇疆鍙傛暟锛坋xtraParams.deviceLogic锛�
+```json
+{
+  "scanIntervalMs": 10000,
+  "bufferTimeoutMs": 30000,
+  "vehicleCapacity": 2,
+  "monitorIntervalMs": 1000,
+  "workLine": "LINE_001",
+  "positionValue": 100
+}
+```
+
+### 4. 澶х悊鐗囩锛圠argeGlass锛�
+
+#### 鍔熻兘鐗规��
+- **鏍煎瓙鑼冨洿閰嶇疆**锛氭敮鎸佸琛屾牸瀛愰厤缃紙濡傜涓�琛� 1~52 鏍硷紝绗簩琛� 53~101 鏍硷級
+- **鏍煎瓙灏哄閰嶇疆**锛氭瘡鏍肩殑闀裤�佸銆佸帤鍙厤缃�
+- **閫昏緫鍒ゆ柇**锛氱敤浜庝綅缃獙璇佸拰鏍煎瓙绠$悊锛屼笉娑夊強 PLC 鍐欏叆
+
+#### 閰嶇疆鍙傛暟锛坋xtraParams.deviceLogic锛�
+```json
+{
+  "gridRanges": [
+    {"row": 1, "start": 1, "end": 52},
+    {"row": 2, "start": 53, "end": 101}
+  ],
+  "gridLength": 2000,
+  "gridWidth": 1500,
+  "gridThickness": 5
+}
+```
+
+## 馃殌 浣跨敤鎸囧崡
+
+### 1. 璁惧閰嶇疆
+
+#### 鍒涘缓璁惧
+```java
+DeviceConfig device = new DeviceConfig();
+device.setDeviceId("DEVICE_001");
+device.setDeviceCode("DEV_001");
+device.setDeviceName("澶ц溅璁惧1");
+device.setDeviceType(DeviceConfig.DeviceType.LOAD_VEHICLE);
+device.setPlcIp("192.168.1.101");
+device.setPlcPort(102);
+device.setPlcType(DeviceConfig.PlcType.S7_1200);
+device.setModuleName("DB1");
+device.setProjectId(1L);
+device.setEnabled(true);
+// 璁剧疆閫昏緫鍙傛暟
+device.setExtraParams(extraParams);
+deviceConfigService.createDevice(device);
+```
+
+#### 閰嶇疆璁惧閫昏緫鍙傛暟
+鍦� `extraParams.deviceLogic` 涓厤缃澶囩壒瀹氱殑閫昏緫鍙傛暟锛屽杞﹁締閫熷害銆佷綅缃槧灏勭瓑銆�
+
+### 2. 璁惧缁勯厤缃�
+
+#### 鍒涘缓璁惧缁�
+```java
+DeviceGroupConfig group = new DeviceGroupConfig();
+group.setGroupCode("GROUP_001");
+group.setGroupName("鐢熶骇绾緼");
+group.setGroupType(1);  // 1-鐢熶骇绾匡紝2-娴嬭瘯绾匡紝3-杈呭姪璁惧缁�
+group.setProjectId(1L);
+group.setStatus(1);  // 0-鍋滅敤锛�1-鍚敤锛�3-缁存姢涓�
+group.setMaxConcurrentDevices(3);  // 鏈�澶у苟鍙戣澶囨暟
+group.setHeartbeatInterval(30);
+group.setCommunicationTimeout(5000);
+deviceGroupService.createGroup(group);
+```
+
+#### 娣诲姞璁惧鍒拌澶囩粍
+```java
+// 娣诲姞璁惧锛岃缃紭鍏堢骇銆佽鑹层�佽繛鎺ラ『搴�
+deviceGroupService.addDevicesToGroup(groupId, deviceIds, priorities, roles, connectionOrders);
+```
+
+### 3. 浠诲姟鎵ц
+
+#### 鍚姩澶氳澶囦换鍔�
+```java
+MultiDeviceTaskRequest request = new MultiDeviceTaskRequest();
+request.setGroupId(groupId);
+request.setTaskName("娴嬭瘯浠诲姟");
+request.setProjectId("PROJECT_001");
+request.setParameters(taskParameters);
+MultiDeviceTask task = multiDeviceTaskService.startTask(request);
+```
+
+#### API 绔偣
+- `POST /device/task/start` - 鍚姩浠诲姟
+- `POST /device/task/list` - 鏌ヨ浠诲姟鍒楄〃
+- `GET /device/task/{taskId}` - 鏌ヨ浠诲姟璇︽儏
+- `GET /device/task/{taskId}/steps` - 鏌ヨ浠诲姟姝ラ璇︽儏
+- `POST /device/task/{taskId}/cancel` - 鍙栨秷浠诲姟
+
+#### 瀹炴椂鐩戞帶锛圫SE锛�
+鍓嶇閫氳繃 SSE 杩炴帴瀹炴椂鎺ユ敹浠诲姟鐘舵�佹洿鏂帮細
+```javascript
+// 鐩戝惉鎸囧畾浠诲姟
+const eventSource = new EventSource('/task/notification/sse?taskId=xxx');
+eventSource.addEventListener('taskStatus', (event) => {
+  const data = JSON.parse(event.data);
+  // 澶勭悊浠诲姟鐘舵�佹洿鏂�
+});
+eventSource.addEventListener('stepUpdate', (event) => {
+  const data = JSON.parse(event.data);
+  // 澶勭悊姝ラ鏇存柊
+});
+
+// 鐩戝惉鎵�鏈変换鍔�
+const eventSourceAll = new EventSource('/task/notification/sse/all');
+```
+
+**SSE 绔偣**锛�
+- `GET /task/notification/sse?taskId=xxx` - 鐩戝惉鎸囧畾浠诲姟
+- `GET /task/notification/sse/all` - 鐩戝惉鎵�鏈変换鍔�
+- `POST /task/notification/close/{taskId}` - 鍏抽棴鎸囧畾浠诲姟鐨凷SE杩炴帴
+- `POST /task/notification/close/all` - 鍏抽棴鎵�鏈塖SE杩炴帴
+
+## 馃搳 鏁版嵁搴撹璁�
+
+### 鏍稿績琛ㄧ粨鏋�
+
+#### device_config锛堣澶囬厤缃〃锛�
+- `id`锛氫富閿紙BIGINT锛�
+- `device_id`锛氳澶囧敮涓�鏍囪瘑锛圴ARCHAR(50)锛屽敮涓�锛�
+- `device_code`锛氳澶囩紪鐮侊紙VARCHAR(50)锛屽敮涓�锛�
+- `device_name`锛氳澶囧悕绉帮紙VARCHAR(100)锛�
+- `device_type`锛氳澶囩被鍨嬶紙VARCHAR(50)锛�
+- `project_id`锛氭墍灞為」鐩甀D锛圔IGINT锛�
+- `plc_ip`锛歅LC IP鍦板潃锛圴ARCHAR(15)锛�
+- `plc_port`锛歅LC绔彛锛圛NT锛�
+- `plc_type`锛歅LC绫诲瀷锛圴ARCHAR(20)锛�
+- `module_name`锛氭ā鍧楀悕绉帮紙VARCHAR(50)锛�
+- `status`锛氳澶囩姸鎬侊紙VARCHAR(20)锛�
+- `is_primary`锛氭槸鍚︿富鎺ц澶囷紙BOOLEAN锛�
+- `enabled`锛氭槸鍚﹀惎鐢紙BOOLEAN锛�
+- `config_json`锛氳澶囩壒瀹氶厤缃紙TEXT锛孞SON鏍煎紡锛�
+- `extra_params`锛氭墿灞曞弬鏁帮紙JSON锛�
+- `description`锛氳澶囨弿杩帮紙VARCHAR(200)锛�
+- `is_deleted`锛氭槸鍚﹀垹闄わ紙INT锛�0-鍚︼紝1-鏄級
+- `created_time`銆乣updated_time`锛氬垱寤�/鏇存柊鏃堕棿
+- `created_by`銆乣updated_by`锛氬垱寤�/鏇存柊浜�
+
+#### device_group_config锛堣澶囩粍閰嶇疆琛級
+- `id`锛氫富閿紙BIGINT锛�
+- `group_code`锛氳澶囩粍缂栫爜锛圴ARCHAR(50)锛屽敮涓�锛�
+- `group_name`锛氳澶囩粍鍚嶇О锛圴ARCHAR(100)锛�
+- `group_type`锛氳澶囩粍绫诲瀷锛圛NT锛�1-鐢熶骇绾匡紝2-娴嬭瘯绾匡紝3-杈呭姪璁惧缁勶級
+- `project_id`锛氭墍灞為」鐩甀D锛圔IGINT锛�
+- `status`锛氳澶囩粍鐘舵�侊紙INT锛�0-鍋滅敤锛�1-鍚敤锛�3-缁存姢涓級
+- `max_concurrent_devices`锛氭渶澶у苟鍙戣澶囨暟锛圛NT锛�
+- `heartbeat_interval`锛氬績璺虫娴嬮棿闅旓紙INT锛岀锛�
+- `communication_timeout`锛氶�氫俊瓒呮椂鏃堕棿锛圛NT锛屾绉掞級
+- `description`锛氳澶囩粍鎻忚堪锛圴ARCHAR(200)锛�
+- `extra_config`锛氭墿灞曢厤缃紙JSON锛�
+- `is_deleted`锛氭槸鍚﹀垹闄わ紙INT锛�
+- `created_time`銆乣updated_time`锛氬垱寤�/鏇存柊鏃堕棿
+- `created_by`銆乣updated_by`锛氬垱寤�/鏇存柊浜�
+
+#### device_group_relation锛堣澶囩粍鍏崇郴琛級
+- `id`锛氫富閿紙BIGINT锛�
+- `group_id`锛氳澶囩粍ID锛圔IGINT锛�
+- `device_id`锛氳澶嘔D锛圔IGINT锛�
+- `priority`锛氳澶囧湪缁勫唴鐨勪紭鍏堢骇锛圛NT锛�1-鏈�楂橈紝10-鏈�浣庯級
+- `role`锛氳澶囧湪缁勫唴鐨勮鑹诧紙INT锛�1-涓绘帶锛�2-鍗忎綔锛�3-鐩戞帶锛�
+- `status`锛氳澶囧湪璇ョ粍涓殑鐘舵�侊紙INT锛�0-鏈厤缃紝1-姝e父锛�2-鏁呴殰锛�3-缁存姢锛�
+- `connection_order`锛氳繛鎺ラ『搴忥紙INT锛屾暟鍊艰秺灏忚秺鍏堣繛鎺ワ級
+- `relation_desc`锛氬叧鑱旀弿杩帮紙VARCHAR(200)锛�
+- `extra_params`锛氭墿灞曞弬鏁帮紙JSON锛�
+- `is_deleted`锛氭槸鍚﹀垹闄わ紙INT锛�
+- `created_time`銆乣updated_time`锛氬垱寤�/鏇存柊鏃堕棿
+- `created_by`銆乣updated_by`锛氬垱寤�/鏇存柊浜�
+
+#### multi_device_task锛堝璁惧浠诲姟琛級
+- `id`锛氫富閿紙BIGINT锛�
+- `task_id`锛氫换鍔″敮涓�鏍囪瘑锛圴ARCHAR(50)锛屽敮涓�锛�
+- `group_id`锛氳澶囩粍ID锛圴ARCHAR(50)锛�
+- `project_id`锛氶」鐩甀D锛圴ARCHAR(50)锛�
+- `status`锛氫换鍔$姸鎬侊紙ENUM锛歅ENDING, RUNNING, COMPLETED, FAILED, CANCELLED锛�
+- `current_step`锛氬綋鍓嶆墽琛屾楠わ紙INT锛�
+- `total_steps`锛氭�绘楠ゆ暟锛圛NT锛�
+- `start_time`锛氬紑濮嬫椂闂达紙DATETIME锛�
+- `end_time`锛氱粨鏉熸椂闂达紙DATETIME锛�
+- `error_message`锛氶敊璇俊鎭紙TEXT锛�
+- `result_data`锛氱粨鏋滄暟鎹紙JSON锛�
+- `created_time`銆乣updated_time`锛氬垱寤�/鏇存柊鏃堕棿
+
+#### task_step_detail锛堜换鍔℃楠よ鎯呰〃锛�
+- `id`锛氫富閿紙BIGINT锛�
+- `task_id`锛氫换鍔D锛圴ARCHAR(50)锛�
+- `step_order`锛氭楠ら『搴忥紙INT锛�
+- `device_id`锛氳澶嘔D锛圴ARCHAR(50)锛�
+- `step_name`锛氭楠ゅ悕绉帮紙VARCHAR(100)锛�
+- `status`锛氭楠ょ姸鎬侊紙ENUM锛歅ENDING, RUNNING, COMPLETED, FAILED, SKIPPED锛�
+- `start_time`锛氭楠ゅ紑濮嬫椂闂达紙DATETIME锛�
+- `end_time`锛氭楠ょ粨鏉熸椂闂达紙DATETIME锛�
+- `duration_ms`锛氭墽琛岃�楁椂锛圔IGINT锛屾绉掞級
+- `input_data`锛氳緭鍏ユ暟鎹紙JSON锛�
+- `output_data`锛氳緭鍑烘暟鎹紙JSON锛�
+- `error_message`锛氶敊璇俊鎭紙TEXT锛�
+- `retry_count`锛氶噸璇曟鏁帮紙INT锛�
+- `created_time`锛氬垱寤烘椂闂达紙DATETIME锛�
+
+#### glass_info锛堢幓鐠冧俊鎭〃锛�
+- `id`锛氫富閿紙BIGINT锛�
+- `glass_id`锛氱幓鐠僆D锛圴ARCHAR(50)锛�
+- `width`锛氬搴︼紙DECIMAL锛�
+- `height`锛氶珮搴︼紙DECIMAL锛�
+- `work_line`锛氫骇绾跨紪鍙凤紙VARCHAR(50)锛�
+- `scan_time`锛氭壂鐮佹椂闂达紙DATETIME锛�
+- `status`锛氱姸鎬侊紙VARCHAR(20)锛�
+- `created_time`銆乣updated_time`锛氬垱寤�/鏇存柊鏃堕棿
+
+#### device_status锛堣澶囩姸鎬佺洃鎺ц〃锛�
+- `id`锛氫富閿紙BIGINT锛�
+- `device_id`锛氳澶嘔D锛圴ARCHAR(50)锛�
+- `task_id`锛氬叧鑱斾换鍔D锛圴ARCHAR(50)锛屽彲閫夛級
+- `status`锛氳澶囩姸鎬侊紙ENUM锛歄NLINE, OFFLINE, BUSY, ERROR, MAINTENANCE锛�
+- `last_heartbeat`锛氭渶鍚庡績璺虫椂闂达紙DATETIME锛�
+- `cpu_usage`锛欳PU浣跨敤鐜囷紙DECIMAL(5,2)锛�
+- `memory_usage`锛氬唴瀛樹娇鐢ㄧ巼锛圖ECIMAL(5,2)锛�
+- `plc_connection_status`锛歅LC杩炴帴鐘舵�侊紙ENUM锛欳ONNECTED, DISCONNECTED, ERROR锛�
+- `current_operation`锛氬綋鍓嶆搷浣滐紙VARCHAR(100)锛�
+- `operation_progress`锛氭搷浣滆繘搴︼紙DECIMAL(5,2)锛�0-100锛�
+- `alert_message`锛氬憡璀︿俊鎭紙TEXT锛�
+- `created_time`锛氳褰曟椂闂达紙DATETIME锛�
+
+## 馃攧 浠诲姟鎵ц娴佺▼
+
+### 涓茶鎵ц娴佺▼
+```
+1. 鍒涘缓浠诲姟璁板綍锛坰tatus = PENDING锛�
+2. 鑾峰彇璁惧缁勪腑鐨勮澶囧垪琛紙鎸� connection_order 鎺掑簭锛�
+3. 渚濇鎵ц姣忎釜璁惧锛�
+   a. 妫�鏌ュ墠缃潯浠�
+   b. 鏇存柊姝ラ鐘舵�佷负 RUNNING
+   c. 鎵ц璁惧浜や簰閫昏緫
+   d. 浼犻�掓暟鎹埌涓嬩竴涓澶�
+   e. 鏇存柊姝ラ鐘舵�佷负 COMPLETED
+4. 鎵�鏈夎澶囨墽琛屽畬鎴愬悗锛屾洿鏂颁换鍔$姸鎬佷负 COMPLETED
+```
+
+### 骞惰鎵ц娴佺▼
+```
+1. 鍒涘缓浠诲姟璁板綍锛坰tatus = PENDING锛�
+2. 鑾峰彇璁惧缁勪腑鐨勮澶囧垪琛�
+3. 浣跨敤绾跨▼姹犲苟琛屾墽琛岃澶囷細
+   a. 浣跨敤 max_concurrent_devices 鎺у埗骞跺彂鏁�
+   b. 姣忎釜璁惧鐙珛鎵ц
+   c. 绛夊緟鎵�鏈夎澶囧畬鎴�
+4. 鎵�鏈夎澶囨墽琛屽畬鎴愬悗锛屾洿鏂颁换鍔$姸鎬佷负 COMPLETED
+```
+
+## 鈿欙笍 閰嶇疆璇存槑
+
+### 搴旂敤閰嶇疆锛坅pplication.yml锛�
+
+```yaml
+server:
+  port: 10018
+
+spring:
+  profiles:
+    active: dev
+  application:
+    name: plcSend
+  liquibase:
+    enabled: true
+    change-log: classpath:changelog/changelogBase.xml
+
+# PLC閰嶇疆
+s7:
+  load:
+    dbArea: DB1
+    beginIndex: 0
+  raw:
+    dbArea: DB2
+    beginIndex: 0
+
+# PLC妯℃嫙閰嶇疆
+plc:
+  simulate:
+    enabled: false
+    interval: 5000
+    failure-rate: 0
+    task-count: 10
+    task-type: normal
+
+# MES閰嶇疆
+mes:
+  width: 2800
+  height: 5000
+```
+
+### 璁惧閫昏緫鍙傛暟閰嶇疆
+
+姣忎釜璁惧绫诲瀷鐨勯�昏緫鍙傛暟鍦� `extraParams.deviceLogic` 涓厤缃紝鍏蜂綋鍙傛暟瑙佸悇璁惧绫诲瀷鐨勮鏄庛��
+
+## 馃洜锔� 鎵╁睍寮�鍙�
+
+### 娣诲姞鏂拌澶囩被鍨�
+
+1. **鍦� DeviceConfig.DeviceType 涓坊鍔犲父閲�**
+```java
+public static final class DeviceType {
+    public static final String NEW_DEVICE = "鏂拌澶囩被鍨�";
+}
+```
+
+2. **鍒涘缓璁惧澶勭悊鍣�**
+```java
+@Component
+public class NewDeviceLogicHandler extends BaseDeviceLogicHandler {
+    @Override
+    public String getDeviceType() {
+        return DeviceConfig.DeviceType.NEW_DEVICE;
+    }
+    
+    @Override
+    protected DevicePlcVO.OperationResult doExecute(
+            DeviceConfig deviceConfig,
+            String operation,
+            Map<String, Object> params,
+            Map<String, Object> logicParams) {
+        // 瀹炵幇璁惧閫昏緫
+    }
+}
+```
+
+3. **鍒涘缓浜や簰娴佺▼**锛堝彲閫夛級
+```java
+@Component
+public class NewDeviceInteraction implements DeviceInteraction {
+    @Override
+    public InteractionResult execute(InteractionContext context) {
+        // 瀹炵幇浜や簰娴佺▼
+    }
+}
+```
+
+### 娣诲姞璁惧鍗忚皟閫昏緫
+
+瀵逛簬闇�瑕佸瀹炰緥鍗忚皟鐨勫鏉傝澶囷紝鍙互鍒涘缓涓撶敤鍖咃細
+```
+interaction/
+鈹斺攢鈹� newdevice/
+    鈹溾攢鈹� handler/
+    鈹溾攢鈹� coordination/
+    鈹斺攢鈹� model/
+```
+
+## 馃摑 娉ㄦ剰浜嬮」
+
+### 1. 鐘舵�佺鐞�
+- 澶ц溅璁惧鐨勭姸鎬佸瓨鍌ㄥ湪鍐呭瓨涓紙`VehicleStatusManager`锛夛紝鏈嶅姟閲嶅惎鍚庝細涓㈠け
+- 濡傞渶鎸佷箙鍖栵紝鍙互鎵╁睍鏀寔鏁版嵁搴撳瓨鍌�
+
+### 2. 骞跺彂瀹夊叏
+- 鐘舵�佺鐞嗗櫒浣跨敤 `ConcurrentHashMap`锛屾敮鎸佸苟鍙戣闂�
+- 浠诲姟鎵ц浣跨敤绾跨▼姹犲拰 `max_concurrent_devices` 鎺у埗骞跺彂
+
+### 3. 閿欒澶勭悊
+- 鏀寔鑷姩閲嶈瘯鏈哄埗锛堥粯璁ゆ渶澶�3娆★級
+- 鏀寔鎸囨暟閫�閬跨瓥鐣�
+- 閿欒鍒嗙被锛氱綉缁滈敊璇�佽秴鏃堕敊璇彲閲嶈瘯锛屼笟鍔¢敊璇笉鍙噸璇�
+
+### 4. 瀹炴椂鐩戞帶
+- SSE 杩炴帴瓒呮椂鏃堕棿锛�30鍒嗛挓
+- 鏀寔鐩戝惉鎸囧畾浠诲姟鎴栨墍鏈変换鍔�
+- 杩炴帴鏂紑鍚庨渶瑕侀噸鏂拌繛鎺�
+
+### 5. 璁惧绫诲瀷璇存槑
+- **鍗у紡缂撳瓨**锛坄GLASS_STORAGE`锛夛細浠g爜涓凡瀹炵幇锛屼絾褰撳墠涓氬姟鍦烘櫙涓嶄娇鐢紝淇濈暀鐢ㄤ簬鏈潵鎵╁睍
+
+## 馃幆 宸插畬鎴愬姛鑳�
+
+### 鉁� 鏍稿績鍔熻兘
+- [x] 璁惧绠$悊锛堥厤缃�佺姸鎬佺洃鎺э級
+- [x] 璁惧缁勭鐞嗭紙骞跺彂鎺у埗銆佷紭鍏堢骇銆佽鑹诧級
+- [x] 澶氳澶囦换鍔℃墽琛屽紩鎿庯紙涓茶/骞惰锛�
+- [x] 璁惧鍗忚皟鏈嶅姟锛堟暟鎹紶閫掋�佺姸鎬佸悓姝ワ級
+- [x] 瀹炴椂鐩戞帶鎺ㄩ�侊紙SSE锛�
+- [x] 閿欒澶勭悊鍜岄噸璇曟満鍒�
+
+### 鉁� 璁惧绫诲瀷鏀寔
+- [x] 澶ц溅璁惧锛堝瀹炰緥鍗忚皟銆佺姸鎬佺鐞嗐�丮ES浠诲姟澶勭悊銆佽繘鐗�/鍑虹墖锛�
+- [x] 鍗ц浆绔嬫壂鐮侊紙瀹氭椂鎵弿銆佹暟鎹惤搴擄級
+- [x] 鍗ц浆绔嬶紙30s缂撳啿銆佹壒閲忓鐞嗭級
+- [x] 澶х悊鐗囩锛堟牸瀛愰厤缃�侀�昏緫鍒ゆ柇锛�
+- [x] 鍗у紡缂撳瓨锛堝凡瀹炵幇锛屽綋鍓嶄笉浣跨敤锛�
+
+### 鉁� API 绔偣
+- [x] 璁惧绠$悊 API锛坄/device/*`锛�
+- [x] 璁惧缁勭鐞� API锛坄/device/group/*`锛�
+- [x] 澶氳澶囦换鍔� API锛坄/device/task/*`锛�
+- [x] SSE 瀹炴椂閫氱煡 API锛坄/task/notification/*`锛�
+
+## 馃摎 鐩稿叧鏂囨。
+
+- 鏁版嵁搴撹縼绉昏剼鏈細`src/main/resources/db/migration/`
+- API 鏂囨。锛氶�氳繃 Swagger 璁块棶 `/swagger-ui.html`
+- 鍓嶇鏂囨。锛氳 `mes-web` 椤圭洰
+
+## 馃摓 鑱旂郴鏂瑰紡
+
+濡傛湁闂鎴栧缓璁紝璇疯仈绯诲紑鍙戝洟闃熴��
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/config/TaskExecutorConfig.java b/mes-processes/mes-plcSend/src/main/java/com/mes/config/TaskExecutorConfig.java
new file mode 100644
index 0000000..eb65e79
--- /dev/null
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/config/TaskExecutorConfig.java
@@ -0,0 +1,63 @@
+package com.mes.config;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.ThreadPoolExecutor;
+
+/**
+ * 浠诲姟鎵ц鍣ㄩ厤缃�
+ * 鐢ㄤ簬寮傛鎵ц澶氳澶囩粍浠诲姟
+ * 
+ * @author mes
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Configuration
+@EnableAsync
+public class TaskExecutorConfig {
+
+    /**
+     * 璁惧缁勪换鍔℃墽琛岀嚎绋嬫睜
+     * 姣忎釜璁惧缁勪綔涓轰竴涓嫭绔嬬嚎绋嬫墽琛�
+     */
+    @Bean(name = "deviceGroupTaskExecutor")
+    public Executor deviceGroupTaskExecutor() {
+        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
+        
+        // 鏍稿績绾跨▼鏁帮細鏀寔鍚屾椂鎵ц鐨勬牳蹇冭澶囩粍鏁伴噺
+        executor.setCorePoolSize(5);
+        
+        // 鏈�澶х嚎绋嬫暟锛氭渶澶氬悓鏃舵墽琛岀殑璁惧缁勬暟閲�
+        executor.setMaxPoolSize(20);
+        
+        // 闃熷垪瀹归噺锛氱瓑寰呮墽琛岀殑璁惧缁勪换鍔℃暟閲�
+        executor.setQueueCapacity(100);
+        
+        // 绾跨▼鍚嶅墠缂�
+        executor.setThreadNamePrefix("DeviceGroupTask-");
+        
+        // 绾跨▼绌洪棽鏃堕棿锛堢锛�
+        executor.setKeepAliveSeconds(60);
+        
+        // 鎷掔粷绛栫暐锛氬綋绾跨▼姹犲拰闃熷垪閮芥弧鏃讹紝鐢辫皟鐢ㄧ嚎绋嬫墽琛�
+        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
+        
+        // 绛夊緟鎵�鏈変换鍔$粨鏉熷悗鍐嶅叧闂嚎绋嬫睜
+        executor.setWaitForTasksToCompleteOnShutdown(true);
+        
+        // 绛夊緟鏃堕棿锛堢锛�
+        executor.setAwaitTerminationSeconds(60);
+        
+        executor.initialize();
+        
+        log.info("璁惧缁勪换鍔$嚎绋嬫睜鍒濆鍖栧畬鎴�: corePoolSize=5, maxPoolSize=20, queueCapacity=100");
+        
+        return executor;
+    }
+}
+
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 86584a9..e1e45ad 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
@@ -27,7 +27,7 @@
     @TableField("device_id")
     private String deviceId;
 
-    @ApiModelProperty(value = "璁惧鍚嶇О", example = "涓婂ぇ杞﹁澶�1")
+    @ApiModelProperty(value = "璁惧鍚嶇О", example = "澶ц溅璁惧1")
     @TableField("device_name")
     private String deviceName;
 
@@ -35,7 +35,7 @@
     @TableField("device_code")
     private String deviceCode;
 
-    @ApiModelProperty(value = "璁惧绫诲瀷", example = "涓婂ぇ杞�/澶х悊鐗�/鐜荤拑瀛樺偍")
+    @ApiModelProperty(value = "璁惧绫诲瀷", example = "澶ц溅璁惧/澶х悊鐗囩/鍗у紡缂撳瓨")
     @TableField("device_type")
     private String deviceType;
 
@@ -59,7 +59,7 @@
     @TableField("plc_type")
     private String plcType;
 
-    @ApiModelProperty(value = "妯″潡鍚嶇О", example = "涓婂ぇ杞︽ā鍧�")
+    @ApiModelProperty(value = "妯″潡鍚嶇О", example = "澶ц溅璁惧妯″潡")
     @TableField("module_name")
     private String moduleName;
 
@@ -75,7 +75,7 @@
     @TableField("config_json")
     private String configJson;
 
-    @ApiModelProperty(value = "璁惧鎻忚堪", example = "涓婂ぇ杞﹁澶�1")
+    @ApiModelProperty(value = "璁惧鎻忚堪", example = "澶ц溅璁惧1")
     @TableField("description")
     private String description;
 
@@ -108,9 +108,11 @@
 
     // 璁惧绫诲瀷甯搁噺
     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 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 = "鍗ц浆绔�";    // 鍗ц浆绔嬭澶�
     }
 
     // PLC绫诲瀷甯搁噺
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/device/service/DevicePlcOperationService.java b/mes-processes/mes-plcSend/src/main/java/com/mes/device/service/DevicePlcOperationService.java
index a0fd53a..7df295c 100644
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/device/service/DevicePlcOperationService.java
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/device/service/DevicePlcOperationService.java
@@ -1,6 +1,7 @@
 package com.mes.device.service;
 
 import com.mes.device.vo.DevicePlcVO;
+import org.springframework.stereotype.Service;
 
 import java.util.List;
 import java.util.Map;
@@ -11,6 +12,7 @@
  * @author mes
  * @since 2025-11-17
  */
+@Service
 public interface DevicePlcOperationService {
 
     DevicePlcVO.OperationResult triggerRequest(Long deviceId);
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/device/service/impl/DeviceConfigServiceImpl.java b/mes-processes/mes-plcSend/src/main/java/com/mes/device/service/impl/DeviceConfigServiceImpl.java
index 1488243..292bd8f 100644
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/device/service/impl/DeviceConfigServiceImpl.java
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/device/service/impl/DeviceConfigServiceImpl.java
@@ -135,9 +135,9 @@
         
         // 璁惧绫诲瀷杩囨护
         if (deviceType != null && !deviceType.trim().isEmpty()) {
-            String convertedDeviceType = convertDeviceTypeFromString(deviceType);
-            if (convertedDeviceType != null) {
-                wrapper.eq(DeviceConfig::getDeviceType, convertedDeviceType);
+            List<String> convertedDeviceTypes = convertDeviceTypeFromString(deviceType);
+            if (convertedDeviceTypes != null && !convertedDeviceTypes.isEmpty()) {
+                wrapper.in(DeviceConfig::getDeviceType, convertedDeviceTypes);
             }
         }
         
@@ -315,24 +315,42 @@
     /**
      * 瀛楃涓茶浆鎹负璁惧绫诲瀷
      */
-    private String convertDeviceTypeFromString(String deviceType) {
-        if (deviceType == null) return null;
+    private List<String> convertDeviceTypeFromString(String deviceType) {
+        if (deviceType == null) {
+            return Collections.emptyList();
+        }
         
-        switch (deviceType.trim().toLowerCase()) {
+        String normalized = deviceType.trim().toLowerCase();
+        switch (normalized) {
             case "load_vehicle":
             case "涓婂ぇ杞�":
+            case "涓婂ぇ杞﹁澶�":
+            case "澶ц溅璁惧":
             case "1":
-                return DeviceConfig.DeviceType.LOAD_VEHICLE;
+                return Arrays.asList(
+                    DeviceConfig.DeviceType.LOAD_VEHICLE,
+                    "澶ц溅璁惧"
+                );
             case "large_glass":
             case "澶х悊鐗�":
+            case "澶х悊鐗囩":
             case "2":
-                return DeviceConfig.DeviceType.LARGE_GLASS;
+                return Arrays.asList(
+                    DeviceConfig.DeviceType.LARGE_GLASS,
+                    "澶х悊鐗囩"
+                );
             case "glass_storage":
             case "鐜荤拑瀛樺偍":
+            case "鍗у紡缂撳瓨":
+            case "鐜荤拑瀛樺偍璁惧":
             case "3":
-                return DeviceConfig.DeviceType.GLASS_STORAGE;
+                return Arrays.asList(
+                    DeviceConfig.DeviceType.GLASS_STORAGE,
+                    "鍗у紡缂撳瓨",
+                    "鐜荤拑瀛樺偍璁惧"
+                );
             default:
-                return null;
+                return Collections.emptyList();
         }
     }
 
@@ -624,12 +642,12 @@
             }
             
             // 璁惧绫诲瀷杩囨护
-            if (deviceType != null && !deviceType.trim().isEmpty()) {
-                String convertedDeviceType = convertDeviceTypeFromString(deviceType);
-                if (convertedDeviceType != null) {
-                    wrapper.eq(DeviceConfig::getDeviceType, convertedDeviceType);
-                }
+        if (deviceType != null && !deviceType.trim().isEmpty()) {
+            List<String> convertedDeviceTypes = convertDeviceTypeFromString(deviceType);
+            if (convertedDeviceTypes != null && !convertedDeviceTypes.isEmpty()) {
+                wrapper.in(DeviceConfig::getDeviceType, convertedDeviceTypes);
             }
+        }
             
             // 璁惧鐘舵�佽繃婊�
             if (deviceStatus != null && !deviceStatus.trim().isEmpty()) {
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 44513f8..4414ee3 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
@@ -80,13 +80,13 @@
 
             // 鏍规嵁璁惧绫诲瀷锛屾彁鍙栧叧閿暟鎹苟鏇存柊涓婁笅鏂�
             if (DeviceConfig.DeviceType.LOAD_VEHICLE.equals(fromDevice.getDeviceType())) {
-                // 涓婂ぇ杞﹁澶囧畬鎴愶紝浼犻�掔幓鐠僆D鍒楄〃
+                // 澶ц溅璁惧瀹屾垚锛屼紶閫掔幓鐠僆D鍒楄〃
                 Object glassIds = data.get("glassIds");
                 if (glassIds instanceof List) {
                     @SuppressWarnings("unchecked")
                     List<String> ids = (List<String>) glassIds;
                     context.setLoadedGlassIds(new ArrayList<>(ids));
-                    log.info("涓婂ぇ杞﹁澶囨暟鎹紶閫�: fromDevice={}, toDevice={}, glassIds={}", 
+                    log.info("澶ц溅璁惧鏁版嵁浼犻��: fromDevice={}, toDevice={}, glassIds={}", 
                         fromDevice.getDeviceCode(), toDevice.getDeviceCode(), ids);
                 }
             } else if (DeviceConfig.DeviceType.LARGE_GLASS.equals(fromDevice.getDeviceType())) {
@@ -147,13 +147,13 @@
         // 妫�鏌ヨ澶囩被鍨嬬壒瀹氱殑渚濊禆
         String deviceType = device.getDeviceType();
         if (DeviceConfig.DeviceType.LARGE_GLASS.equals(deviceType)) {
-            // 澶х悊鐗囪澶囬渶瑕佷笂澶ц溅璁惧鍏堝畬鎴�
+            // 澶х悊鐗囪澶囬渶瑕佸ぇ杞﹁澶囧厛瀹屾垚
             List<String> loadedGlassIds = context.getSafeLoadedGlassIds();
             if (CollectionUtils.isEmpty(loadedGlassIds)) {
-                missingDependencies.add("涓婂ぇ杞﹁澶囨湭瀹屾垚锛岀己灏戠幓鐠僆D鍒楄〃");
+                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)) {
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/device/service/impl/DevicePlcOperationServiceImpl.java b/mes-processes/mes-plcSend/src/main/java/com/mes/device/service/impl/DevicePlcOperationServiceImpl.java
index dc65a65..ebac164 100644
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/device/service/impl/DevicePlcOperationServiceImpl.java
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/device/service/impl/DevicePlcOperationServiceImpl.java
@@ -40,15 +40,27 @@
     private final PlcTestWriteService plcTestWriteService;
     private final ObjectMapper objectMapper;
 
-    public enum PlcOperationType {
+    public static enum PlcOperationType {
+        /** PLC璇锋眰鎿嶄綔 */
         REQUEST("PLC璇锋眰", "PLC 璇锋眰鍙戦�佹垚鍔�", "PLC 璇锋眰鍙戦�佸け璐�"),
+        /** PLC姹囨姤鎿嶄綔 */
         REPORT("PLC姹囨姤", "PLC 姹囨姤妯℃嫙鎴愬姛", "PLC 姹囨姤妯℃嫙澶辫触"),
+        /** PLC閲嶇疆鎿嶄綔 */
         RESET("PLC閲嶇疆", "PLC 鐘舵�佸凡閲嶇疆", "PLC 鐘舵�侀噸缃け璐�");
 
+        /** 鎿嶄綔鏄剧ず鍚嶇О */
         private final String display;
+        /** 鎿嶄綔鎴愬姛鎻愮ず淇℃伅 */
         private final String successMsg;
+        /** 鎿嶄綔澶辫触鎻愮ず淇℃伅 */
         private final String failedMsg;
 
+        /**
+         * 鏋勯�犳柟娉�
+         * @param display 鎿嶄綔鏄剧ず鍚嶇О
+         * @param successMsg 鎴愬姛鎻愮ず淇℃伅
+         * @param failedMsg 澶辫触鎻愮ず淇℃伅
+         */
         PlcOperationType(String display, String successMsg, String failedMsg) {
             this.display = display;
             this.successMsg = successMsg;
@@ -103,7 +115,7 @@
             return DevicePlcVO.StatusInfo.builder()
                     .deviceId(deviceId)
                     .deviceName("鏈煡璁惧")
-                    .data(Collections.emptyMap())
+                    .fieldValues(Collections.emptyMap())
                     .timestamp(LocalDateTime.now())
                     .build();
         }
@@ -115,7 +127,7 @@
                     .deviceName(device.getDeviceName())
                     .deviceCode(device.getDeviceCode())
                     .projectId(String.valueOf(device.getProjectId()))
-                    .data(data)
+                    .fieldValues(data)
                     .timestamp(LocalDateTime.now())
                     .build();
         } catch (Exception e) {
@@ -125,7 +137,7 @@
                     .deviceName(device.getDeviceName())
                     .deviceCode(device.getDeviceCode())
                     .projectId(null)
-                    .data(Collections.emptyMap())
+                    .fieldValues(Collections.emptyMap())
                     .timestamp(LocalDateTime.now())
                     .build();
         }
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/device/vo/DevicePlcVO.java b/mes-processes/mes-plcSend/src/main/java/com/mes/device/vo/DevicePlcVO.java
index 873c2e0..69919ec 100644
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/device/vo/DevicePlcVO.java
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/device/vo/DevicePlcVO.java
@@ -36,6 +36,7 @@
         private Boolean success;
         private String message;
         private LocalDateTime timestamp;
+        private Map<String, Object> data;
     }
 
     /**
@@ -51,7 +52,7 @@
         private String deviceName;
         private String deviceCode;
         private String projectId;
-        private Map<String, Object> data;
+        private Map<String, Object> fieldValues;
         private LocalDateTime timestamp;
     }
 }
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/DeviceInteractionRegistry.java b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/DeviceInteractionRegistry.java
index fc4cc8c..3dc7ddb 100644
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/DeviceInteractionRegistry.java
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/DeviceInteractionRegistry.java
@@ -10,6 +10,7 @@
 
 /**
  * 浜や簰娉ㄥ唽涓績
+ * @author huang
  */
 @Slf4j
 @Component
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/DeviceLogicHandler.java b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/DeviceLogicHandler.java
index 9566222..4a39cc7 100644
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/DeviceLogicHandler.java
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/DeviceLogicHandler.java
@@ -17,7 +17,7 @@
     /**
      * 鑾峰彇璁惧绫诲瀷锛堢敤浜庡尮閰嶅鐞嗗櫒锛�
      * 
-     * @return 璁惧绫诲瀷锛屽锛�"涓婂ぇ杞�"銆�"澶х悊鐗�"銆�"鐜荤拑瀛樺偍"
+     * @return 璁惧绫诲瀷锛屽锛�"澶ц溅璁惧"銆�"澶х悊鐗囩"銆�"鍗у紡缂撳瓨"
      */
     String getDeviceType();
 
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
index 28b8569..257a3fa 100644
--- 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
@@ -30,7 +30,7 @@
                 return InteractionResult.fail("璁惧閰嶇疆涓嶅瓨鍦�");
             }
 
-            // 浼樺厛浣跨敤澶勭悊鍚庣殑鐜荤拑ID锛屽鏋滄病鏈夊垯浣跨敤涓婂ぇ杞︾殑鐜荤拑ID
+            // 浼樺厛浣跨敤澶勭悊鍚庣殑鐜荤拑ID锛屽鏋滄病鏈夊垯浣跨敤澶ц溅璁惧鐨勭幓鐠僆D
             List<String> processed = context.getProcessedGlassIds();
             if (CollectionUtils.isEmpty(processed)) {
                 processed = context.getLoadedGlassIds();
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/flow/LargeGlassInteraction.java b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/flow/LargeGlassInteraction.java
index c0ded0b..3a9f805 100644
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/flow/LargeGlassInteraction.java
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/flow/LargeGlassInteraction.java
@@ -31,14 +31,14 @@
                 return InteractionResult.fail("璁惧閰嶇疆涓嶅瓨鍦�");
             }
 
-            // 妫�鏌ヤ笂澶ц溅鏄惁瀹屾垚
+            // 妫�鏌ュぇ杞﹁澶囨槸鍚﹀畬鎴�
             Object source = context.getSharedData().get("glassesFromVehicle");
             List<String> glassQueue = castList(source);
             if (CollectionUtils.isEmpty(glassQueue)) {
                 // 涔熷皾璇曚粠涓婁笅鏂囪幏鍙�
                 glassQueue = context.getLoadedGlassIds();
                 if (CollectionUtils.isEmpty(glassQueue)) {
-                    return InteractionResult.waitResult("绛夊緟涓婂ぇ杞﹁緭鍑�", null);
+                    return InteractionResult.waitResult("绛夊緟澶ц溅璁惧杈撳嚭", null);
                 }
             }
 
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/flow/LoadVehicleInteraction.java b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/flow/LoadVehicleInteraction.java
deleted file mode 100644
index f9a7934..0000000
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/flow/LoadVehicleInteraction.java
+++ /dev/null
@@ -1,95 +0,0 @@
-package com.mes.interaction.flow;
-
-import com.mes.device.entity.DeviceConfig;
-import com.mes.device.service.DeviceInteractionService;
-import com.mes.device.vo.DevicePlcVO;
-import com.mes.interaction.DeviceInteraction;
-import com.mes.interaction.base.InteractionContext;
-import com.mes.interaction.base.InteractionResult;
-import lombok.RequiredArgsConstructor;
-import org.springframework.stereotype.Component;
-import org.springframework.util.CollectionUtils;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * 涓婂ぇ杞︿氦浜掑疄鐜�
- */
-@Component
-@RequiredArgsConstructor
-public class LoadVehicleInteraction implements DeviceInteraction {
-
-    private final DeviceInteractionService deviceInteractionService;
-
-    @Override
-    public String getDeviceType() {
-        return DeviceConfig.DeviceType.LOAD_VEHICLE;
-    }
-
-    @Override
-    public InteractionResult execute(InteractionContext context) {
-        try {
-            // 鍓嶇疆鏉′欢楠岃瘉
-            if (context.getCurrentDevice() == null) {
-                return InteractionResult.fail("璁惧閰嶇疆涓嶅瓨鍦�");
-            }
-
-            List<String> glassIds = context.getParameters().getGlassIds();
-            if (CollectionUtils.isEmpty(glassIds)) {
-                return InteractionResult.waitResult("鏈彁渚涚幓鐠僆D锛岀瓑寰呰緭鍏�", null);
-            }
-
-            // 楠岃瘉鐜荤拑ID鏍煎紡
-            for (String glassId : glassIds) {
-                if (glassId == null || glassId.trim().isEmpty()) {
-                    return InteractionResult.fail("鐜荤拑ID涓嶈兘涓虹┖");
-                }
-            }
-
-            // 鏋勫缓PLC鍐欏叆鍙傛暟
-            Map<String, Object> params = new HashMap<>();
-            params.put("glassIds", glassIds);
-            params.put("positionCode", context.getParameters().getPositionCode());
-            params.put("positionValue", context.getParameters().getPositionValue());
-            params.put("triggerRequest", true);
-
-            // 鎵ц瀹為檯鐨凱LC鍐欏叆鎿嶄綔
-            DevicePlcVO.OperationResult plcResult = deviceInteractionService.executeOperation(
-                    context.getCurrentDevice().getId(), 
-                    "feedGlass", 
-                    params
-            );
-
-            // 妫�鏌LC鍐欏叆缁撴灉
-            if (plcResult == null || !Boolean.TRUE.equals(plcResult.getSuccess())) {
-                String errorMsg = plcResult != null ? plcResult.getMessage() : "PLC鍐欏叆鎿嶄綔杩斿洖绌虹粨鏋�";
-                return InteractionResult.fail("PLC鍐欏叆澶辫触: " + errorMsg);
-            }
-
-            // 鎵ц涓婂ぇ杞︽搷浣滐紙鏁版嵁娴佽浆锛�
-            List<String> copied = new ArrayList<>(glassIds);
-            context.setLoadedGlassIds(copied);
-            context.getSharedData().put("glassesFromVehicle", copied);
-            context.getSharedData().put("loadVehicleTime", System.currentTimeMillis());
-
-            // 鍚庣疆鏉′欢妫�鏌�
-            if (context.getLoadedGlassIds().isEmpty()) {
-                return InteractionResult.fail("涓婂ぇ杞︽搷浣滃け璐ワ細鐜荤拑ID鍒楄〃涓虹┖");
-            }
-
-            Map<String, Object> data = new HashMap<>();
-            data.put("loaded", copied);
-            data.put("glassCount", copied.size());
-            data.put("deviceId", context.getCurrentDevice().getId());
-            data.put("deviceCode", context.getCurrentDevice().getDeviceCode());
-            data.put("plcResult", plcResult.getMessage());
-            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/LargeGlassLogicHandler.java b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/impl/LargeGlassLogicHandler.java
deleted file mode 100644
index 284fefa..0000000
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/impl/LargeGlassLogicHandler.java
+++ /dev/null
@@ -1,195 +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 LargeGlassLogicHandler extends BaseDeviceLogicHandler {
-
-    public LargeGlassLogicHandler(DevicePlcOperationService devicePlcOperationService) {
-        super(devicePlcOperationService);
-    }
-
-    @Override
-    public String getDeviceType() {
-        return DeviceConfig.DeviceType.LARGE_GLASS;
-    }
-
-    @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 "processGlass":
-                return handleProcessGlass(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 handleProcessGlass(
-            DeviceConfig deviceConfig,
-            Map<String, Object> params,
-            Map<String, Object> logicParams) {
-
-        // 浠庨�昏緫鍙傛暟涓幏鍙栭厤缃�
-        Integer glassSize = getLogicParam(logicParams, "glassSize", 2000);
-        Integer processingTime = getLogicParam(logicParams, "processingTime", 5000);
-        Boolean autoProcess = getLogicParam(logicParams, "autoProcess", true);
-
-        // 浠庤繍琛屾椂鍙傛暟涓幏鍙栨暟鎹�
-        String glassId = (String) params.get("glassId");
-        Integer processType = (Integer) params.get("processType");
-        Boolean triggerRequest = (Boolean) params.getOrDefault("triggerRequest", autoProcess);
-
-        // 鏋勫缓鍐欏叆鏁版嵁
-        Map<String, Object> payload = new HashMap<>();
-        
-        if (glassId != null) {
-            payload.put("plcGlassId", glassId);
-        }
-        
-        if (processType != null) {
-            payload.put("processType", processType);
-        }
-
-        // 鑷姩瑙﹀彂璇锋眰
-        if (triggerRequest != null && triggerRequest) {
-            payload.put("plcRequest", 1);
-        }
-
-        log.info("澶х悊鐗囩幓鐠冨姞宸�: deviceId={}, glassId={}, processType={}", 
-                deviceConfig.getId(), glassId, processType);
-
-        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 glassSize = getLogicParam(logicParams, "glassSize", null);
-        if (glassSize != null && glassSize <= 0) {
-            return "鐜荤拑灏哄(glassSize)蹇呴』澶т簬0";
-        }
-
-        Integer processingTime = getLogicParam(logicParams, "processingTime", null);
-        if (processingTime != null && processingTime < 0) {
-            return "澶勭悊鏃堕棿(processingTime)涓嶈兘涓鸿礋鏁�";
-        }
-
-        return null; // 楠岃瘉閫氳繃
-    }
-
-    @Override
-    public String getDefaultLogicParams() {
-        Map<String, Object> defaultParams = new HashMap<>();
-        defaultParams.put("glassSize", 2000);
-        defaultParams.put("processingTime", 5000);
-        defaultParams.put("autoProcess", 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/impl/LoadVehicleLogicHandler.java b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/impl/LoadVehicleLogicHandler.java
deleted file mode 100644
index ec1d8f2..0000000
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/impl/LoadVehicleLogicHandler.java
+++ /dev/null
@@ -1,451 +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.service.GlassInfoService;
-import com.mes.device.vo.DevicePlcVO;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Qualifier;
-import org.springframework.stereotype.Component;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-
-/**
- * 涓婂ぇ杞﹁澶囬�昏緫澶勭悊鍣�
- * 
- * @author mes
- * @since 2025-01-XX
- */
-@Slf4j
-@Component
-public class LoadVehicleLogicHandler extends BaseDeviceLogicHandler {
-
-    private final GlassInfoService glassInfoService;
-
-    public LoadVehicleLogicHandler(
-            DevicePlcOperationService devicePlcOperationService,
-            @Qualifier("deviceGlassInfoService") GlassInfoService glassInfoService) {
-        super(devicePlcOperationService);
-        this.glassInfoService = glassInfoService;
-    }
-
-    @Override
-    public String getDeviceType() {
-        return DeviceConfig.DeviceType.LOAD_VEHICLE;
-    }
-
-    @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 "feedGlass":
-                return handleFeedGlass(deviceConfig, params, logicParams);
-            case "triggerRequest":
-                return handleTriggerRequest(deviceConfig, params, logicParams);
-            case "triggerReport":
-                return handleTriggerReport(deviceConfig, params, logicParams);
-            case "reset":
-                return handleReset(deviceConfig, params, logicParams);
-            case "clearGlass":
-            case "clearPlc":
-            case "clear":
-                return handleClearGlass(deviceConfig, params, logicParams);
-            default:
-                log.warn("涓嶆敮鎸佺殑鎿嶄綔绫诲瀷: {}", operation);
-                return DevicePlcVO.OperationResult.builder()
-                        .success(false)
-                        .message("涓嶆敮鎸佺殑鎿嶄綔: " + operation)
-                        .build();
-        }
-    }
-
-    /**
-     * 澶勭悊鐜荤拑涓婃枡鎿嶄綔
-     */
-    private DevicePlcVO.OperationResult handleFeedGlass(
-            DeviceConfig deviceConfig,
-            Map<String, Object> params,
-            Map<String, Object> logicParams) {
-
-        // 浠庨�昏緫鍙傛暟涓幏鍙栭厤缃紙浠� extraParams.deviceLogic 璇诲彇锛�
-        Integer vehicleCapacity = getLogicParam(logicParams, "vehicleCapacity", 6000);
-        Integer glassIntervalMs = getLogicParam(logicParams, "glassIntervalMs", 1000);
-        Boolean autoFeed = getLogicParam(logicParams, "autoFeed", true);
-        Integer maxRetryCount = getLogicParam(logicParams, "maxRetryCount", 5);
-
-        // 浠庤繍琛屾椂鍙傛暟涓幏鍙栨暟鎹紙浠庢帴鍙h皟鐢ㄦ椂浼犲叆锛�
-        List<GlassInfo> glassInfos = extractGlassInfos(params);
-        if (glassInfos.isEmpty()) {
-            return DevicePlcVO.OperationResult.builder()
-                    .success(false)
-                    .message("鏈彁渚涙湁鏁堢殑鐜荤拑淇℃伅")
-                    .build();
-        }
-
-        String positionCode = (String) params.get("positionCode");
-        Integer positionValue = (Integer) params.get("positionValue");
-        Boolean triggerRequest = (Boolean) params.getOrDefault("triggerRequest", autoFeed);
-
-        List<GlassInfo> plannedGlasses = planGlassLoading(glassInfos, vehicleCapacity,
-                getLogicParam(logicParams, "defaultGlassLength", 2000));
-        if (plannedGlasses.isEmpty()) {
-            return DevicePlcVO.OperationResult.builder()
-                    .success(false)
-                    .message("褰撳墠鐜荤拑灏哄瓒呭嚭杞﹁締瀹归噺锛屾棤娉曡杞�")
-                    .build();
-        }
-
-        // 鏋勫缓鍐欏叆鏁版嵁
-        Map<String, Object> payload = new HashMap<>();
-        
-        // 鍐欏叆鐜荤拑ID
-        int plcSlots = Math.min(plannedGlasses.size(), 6);
-        for (int i = 0; i < plcSlots; i++) {
-            String fieldName = "plcGlassId" + (i + 1);
-            payload.put(fieldName, plannedGlasses.get(i).getGlassId());
-        }
-        payload.put("plcGlassCount", plcSlots);
-
-        // 鍐欏叆浣嶇疆淇℃伅
-        if (positionValue != null) {
-            payload.put("inPosition", positionValue);
-        } else if (positionCode != null) {
-            // 浠庝綅缃槧灏勪腑鑾峰彇浣嶇疆鍊�
-            @SuppressWarnings("unchecked")
-            Map<String, Integer> positionMapping = getLogicParam(logicParams, "positionMapping", new HashMap<>());
-            Integer mappedValue = positionMapping.get(positionCode);
-            if (mappedValue != null) {
-                payload.put("inPosition", mappedValue);
-            }
-        }
-
-        // 鑷姩瑙﹀彂璇锋眰瀛�
-        if (triggerRequest != null && triggerRequest) {
-            payload.put("plcRequest", 1);
-        }
-
-        String operationName = "涓婂ぇ杞�-鐜荤拑涓婃枡";
-        if (positionCode != null) {
-            operationName += "(" + positionCode + ")";
-        }
-
-        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();
-            }
-        }
-
-        return devicePlcOperationService.writeFields(deviceConfig.getId(), payload, operationName);
-    }
-
-    /**
-     * 澶勭悊瑙﹀彂璇锋眰鎿嶄綔
-     */
-    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,
-                "涓婂ぇ杞�-閲嶇疆"
-        );
-    }
-
-    /**
-     * 娓呯┖PLC涓殑鐜荤拑鏁版嵁
-     */
-    private DevicePlcVO.OperationResult handleClearGlass(
-            DeviceConfig deviceConfig,
-            Map<String, Object> params,
-            Map<String, Object> logicParams) {
-
-        Map<String, Object> payload = new HashMap<>();
-
-        int slotCount = getLogicParam(logicParams, "glassSlotCount", 6);
-        if (slotCount <= 0) {
-            slotCount = 6;
-        }
-
-        List<String> slotFields = resolveGlassSlotFields(logicParams, slotCount);
-        for (String field : slotFields) {
-            payload.put(field, "");
-        }
-
-        payload.put("plcGlassCount", 0);
-        payload.put("plcRequest", 0);
-        payload.put("plcReport", 0);
-
-        if (params != null && params.containsKey("positionValue")) {
-            payload.put("inPosition", params.get("positionValue"));
-        } else if (params != null && Boolean.TRUE.equals(params.get("clearPosition"))) {
-            payload.put("inPosition", 0);
-        }
-
-        log.info("娓呯┖涓婂ぇ杞LC鐜荤拑鏁版嵁: deviceId={}, clearedSlots={}", deviceConfig.getId(), slotFields.size());
-
-        return devicePlcOperationService.writeFields(
-                deviceConfig.getId(),
-                payload,
-                "涓婂ぇ杞�-娓呯┖鐜荤拑鏁版嵁"
-        );
-    }
-
-    private List<String> resolveGlassSlotFields(Map<String, Object> logicParams, int fallbackCount) {
-        List<String> fields = new ArrayList<>();
-        if (logicParams != null) {
-            Object slotFieldConfig = logicParams.get("glassSlotFields");
-            if (slotFieldConfig instanceof List) {
-                List<?> configured = (List<?>) slotFieldConfig;
-                for (Object item : configured) {
-                    if (item != null) {
-                        String fieldName = String.valueOf(item).trim();
-                        if (!fieldName.isEmpty()) {
-                            fields.add(fieldName);
-                        }
-                    }
-                }
-            }
-        }
-
-        if (fields.isEmpty()) {
-            for (int i = 1; i <= fallbackCount; i++) {
-                fields.add("plcGlassId" + i);
-            }
-        }
-        return fields;
-    }
-
-    @Override
-    public String validateLogicParams(DeviceConfig deviceConfig) {
-        Map<String, Object> logicParams = parseLogicParams(deviceConfig);
-        
-        // 楠岃瘉蹇呭~鍙傛暟
-        Integer vehicleCapacity = getLogicParam(logicParams, "vehicleCapacity", null);
-        if (vehicleCapacity == null || vehicleCapacity <= 0) {
-            return "杞﹁締瀹归噺(vehicleCapacity)蹇呴』澶т簬0";
-        }
-
-        Integer glassIntervalMs = getLogicParam(logicParams, "glassIntervalMs", null);
-        if (glassIntervalMs != null && glassIntervalMs < 0) {
-            return "鐜荤拑闂撮殧鏃堕棿(glassIntervalMs)涓嶈兘涓鸿礋鏁�";
-        }
-
-        return null; // 楠岃瘉閫氳繃
-    }
-
-    @Override
-    public String getDefaultLogicParams() {
-        Map<String, Object> defaultParams = new HashMap<>();
-        defaultParams.put("vehicleCapacity", 6000);
-        defaultParams.put("glassIntervalMs", 1000);
-        defaultParams.put("autoFeed", true);
-        defaultParams.put("maxRetryCount", 5);
-        defaultParams.put("defaultGlassLength", 2000);
-        
-        Map<String, Integer> positionMapping = new HashMap<>();
-        positionMapping.put("POS1", 1);
-        positionMapping.put("POS2", 2);
-        defaultParams.put("positionMapping", positionMapping);
-        
-        try {
-            return objectMapper.writeValueAsString(defaultParams);
-        } catch (Exception e) {
-            log.error("鐢熸垚榛樿閫昏緫鍙傛暟澶辫触", e);
-            return "{}";
-        }
-    }
-
-    @SuppressWarnings("unchecked")
-    private List<GlassInfo> extractGlassInfos(Map<String, Object> params) {
-        List<GlassInfo> result = new ArrayList<>();
-        Object rawGlassInfos = params.get("glassInfos");
-        if (rawGlassInfos instanceof List) {
-            List<?> list = (List<?>) rawGlassInfos;
-            for (Object item : list) {
-                GlassInfo info = convertToGlassInfo(item);
-                if (info != null) {
-                    result.add(info);
-                }
-            }
-        }
-
-        if (result.isEmpty()) {
-            List<String> glassIds = (List<String>) params.get("glassIds");
-            if (glassIds != null && !glassIds.isEmpty()) {
-                // 浠庢暟鎹簱鏌ヨ鐜荤拑灏哄
-                Map<String, Integer> lengthMap = glassInfoService.getGlassLengthMap(glassIds);
-                for (String glassId : glassIds) {
-                    Integer length = lengthMap.get(glassId);
-                    result.add(new GlassInfo(glassId, length));
-                }
-                log.debug("浠庢暟鎹簱鏌ヨ鐜荤拑灏哄: glassIds={}, lengthMap={}", glassIds, lengthMap);
-            }
-        }
-        return result;
-    }
-
-    private GlassInfo convertToGlassInfo(Object source) {
-        if (source instanceof GlassInfo) {
-            return (GlassInfo) source;
-        }
-        if (source instanceof Map) {
-            Map<?, ?> map = (Map<?, ?>) source;
-            Object id = map.get("glassId");
-            if (id == null) {
-                id = map.get("id");
-            }
-            if (id == null) {
-                return null;
-            }
-            Integer length = parseLength(map.get("length"));
-            if (length == null) {
-                length = parseLength(map.get("size"));
-            }
-            return new GlassInfo(String.valueOf(id), length);
-        }
-        if (source instanceof String) {
-            return new GlassInfo((String) source, null);
-        }
-        return null;
-    }
-
-    private Integer parseLength(Object value) {
-        if (value instanceof Number) {
-            return ((Number) value).intValue();
-        }
-        if (value instanceof String) {
-            try {
-                return Integer.parseInt((String) value);
-            } catch (NumberFormatException ignored) {
-            }
-        }
-        return null;
-    }
-
-    private List<GlassInfo> planGlassLoading(List<GlassInfo> source,
-                                             int vehicleCapacity,
-                                             Integer defaultGlassLength) {
-        List<GlassInfo> planned = new ArrayList<>();
-        int usedLength = 0;
-        int capacity = Math.max(vehicleCapacity, 1);
-        int fallbackLength = defaultGlassLength != null && defaultGlassLength > 0 ? defaultGlassLength : 2000;
-
-        for (GlassInfo info : source) {
-            int length = info.getLength() != null && info.getLength() > 0 ? info.getLength() : fallbackLength;
-            if (planned.isEmpty()) {
-                planned.add(info.withLength(length));
-                usedLength = length;
-                continue;
-            }
-            if (usedLength + length <= capacity) {
-                planned.add(info.withLength(length));
-                usedLength += length;
-            } else {
-                break;
-            }
-        }
-        return planned;
-    }
-
-    private static class GlassInfo {
-        private final String glassId;
-        private final Integer length;
-
-        GlassInfo(String glassId, Integer length) {
-            this.glassId = glassId;
-            this.length = length;
-        }
-
-        public String getGlassId() {
-            return glassId;
-        }
-
-        public Integer getLength() {
-            return length;
-        }
-
-        public GlassInfo withLength(Integer newLength) {
-            return new GlassInfo(this.glassId, newLength);
-        }
-
-        @Override
-        public String toString() {
-            return glassId;
-        }
-
-        @Override
-        public int hashCode() {
-            return Objects.hash(glassId, length);
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            if (this == obj) return true;
-            if (obj == null || getClass() != obj.getClass()) return false;
-            GlassInfo other = (GlassInfo) obj;
-            return Objects.equals(glassId, other.glassId) && Objects.equals(length, other.length);
-        }
-    }
-}
-
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/largeglass/config/LargeGlassConfig.java b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/largeglass/config/LargeGlassConfig.java
new file mode 100644
index 0000000..883349e
--- /dev/null
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/largeglass/config/LargeGlassConfig.java
@@ -0,0 +1,37 @@
+package com.mes.interaction.largeglass.config;
+
+import com.mes.interaction.largeglass.model.GridRange;
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 澶х悊鐗囩閰嶇疆
+ * 瀵瑰簲 extraParams.deviceLogic 涓殑瀛楁
+ */
+@Data
+public class LargeGlassConfig {
+
+    /**
+     * 鏍煎瓙鑼冨洿鍒楄〃
+     * 渚嬪锛氱涓�琛�1~52鏍硷紝绗簩琛�53~101鏍�
+     */
+    private List<GridRange> gridRanges = new ArrayList<>();
+
+    /**
+     * 姣忔牸闀垮害锛坢m锛�
+     */
+    private Integer gridLength = 2000;
+
+    /**
+     * 姣忔牸瀹藉害锛坢m锛�
+     */
+    private Integer gridWidth = 1500;
+
+    /**
+     * 姣忔牸鍘氬害锛坢m锛�
+     */
+    private Integer gridThickness = 5;
+}
+
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/largeglass/handler/LargeGlassLogicHandler.java b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/largeglass/handler/LargeGlassLogicHandler.java
new file mode 100644
index 0000000..e7bba0f
--- /dev/null
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/largeglass/handler/LargeGlassLogicHandler.java
@@ -0,0 +1,379 @@
+package com.mes.interaction.largeglass.handler;
+
+import com.mes.device.entity.DeviceConfig;
+import com.mes.device.vo.DevicePlcVO;
+import com.mes.interaction.BaseDeviceLogicHandler;
+import com.mes.device.service.DevicePlcOperationService;
+import com.mes.interaction.largeglass.config.LargeGlassConfig;
+import com.mes.interaction.largeglass.model.GridRange;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import java.time.LocalDateTime;
+import java.util.*;
+
+/**
+ * 澶х悊鐗囩璁惧閫昏緫澶勭悊鍣�
+ * 璐熻矗鏍煎瓙鑼冨洿閰嶇疆銆佹瘡鏍煎昂瀵搁厤缃�侀�昏緫鍒ゆ柇绛�
+ * 涓嶆秹鍙奝LC鍐欏叆鎿嶄綔锛屽彧鐢ㄤ簬閫昏緫鍒ゆ柇鍜岄厤缃鐞�
+ */
+@Slf4j
+@Component
+public class LargeGlassLogicHandler extends BaseDeviceLogicHandler {
+
+    public LargeGlassLogicHandler(DevicePlcOperationService devicePlcOperationService) {
+        super(devicePlcOperationService);
+    }
+
+    @Override
+    public String getDeviceType() {
+        return DeviceConfig.DeviceType.LARGE_GLASS;
+    }
+
+    @Override
+    protected DevicePlcVO.OperationResult doExecute(DeviceConfig deviceConfig,
+                                                    String operation,
+                                                    Map<String, Object> params,
+                                                    Map<String, Object> logicParams) {
+        try {
+            LargeGlassConfig config = parseLargeGlassConfig(logicParams);
+            
+            switch (operation) {
+                case "checkGrid":
+                case "validateGrid":
+                    return handleCheckGrid(deviceConfig, params, config);
+                case "getGridInfo":
+                    return handleGetGridInfo(deviceConfig, params, config);
+                case "findAvailableGrid":
+                    return handleFindAvailableGrid(deviceConfig, params, config);
+                case "getGridPosition":
+                    return handleGetGridPosition(deviceConfig, params, config);
+                default:
+                    return buildResult(deviceConfig, operation, false, 
+                            "涓嶆敮鎸佺殑鎿嶄綔: " + operation);
+            }
+        } catch (Exception e) {
+            log.error("澶х悊鐗囩澶勭悊寮傚父: deviceId={}, operation={}", 
+                    deviceConfig.getId(), operation, e);
+            return buildResult(deviceConfig, operation, false, 
+                    "澶勭悊寮傚父: " + e.getMessage());
+        }
+    }
+
+    /**
+     * 妫�鏌ユ牸瀛愭槸鍚︽湁鏁�
+     */
+    private DevicePlcVO.OperationResult handleCheckGrid(
+            DeviceConfig deviceConfig,
+            Map<String, Object> params,
+            LargeGlassConfig config) {
+        
+        Integer gridNumber = getIntegerParam(params, "gridNumber");
+        if (gridNumber == null) {
+            return buildResult(deviceConfig, "checkGrid", false, 
+                    "鏈彁渚涙牸瀛愮紪鍙�");
+        }
+        
+        GridRange gridRange = findGridRange(gridNumber, config);
+        if (gridRange == null) {
+            return buildResult(deviceConfig, "checkGrid", false, 
+                    String.format("鏍煎瓙缂栧彿 %d 涓嶅湪閰嶇疆鑼冨洿鍐�", gridNumber));
+        }
+        
+        return buildResult(deviceConfig, "checkGrid", true, 
+                String.format("鏍煎瓙缂栧彿 %d 鏈夋晥锛屼綅浜庣 %d 琛岋紝鑼冨洿 %d~%d", 
+                        gridNumber, gridRange.getRow(), gridRange.getStart(), gridRange.getEnd()));
+    }
+
+    /**
+     * 鑾峰彇鏍煎瓙淇℃伅
+     */
+    private DevicePlcVO.OperationResult handleGetGridInfo(
+            DeviceConfig deviceConfig,
+            Map<String, Object> params,
+            LargeGlassConfig config) {
+        
+        Integer gridNumber = getIntegerParam(params, "gridNumber");
+        if (gridNumber == null) {
+            return buildResult(deviceConfig, "getGridInfo", false, 
+                    "鏈彁渚涙牸瀛愮紪鍙�");
+        }
+        
+        GridRange gridRange = findGridRange(gridNumber, config);
+        if (gridRange == null) {
+            return buildResult(deviceConfig, "getGridInfo", false, 
+                    String.format("鏍煎瓙缂栧彿 %d 涓嶅湪閰嶇疆鑼冨洿鍐�", gridNumber));
+        }
+        
+        Map<String, Object> gridInfo = new HashMap<>();
+        gridInfo.put("gridNumber", gridNumber);
+        gridInfo.put("row", gridRange.getRow());
+        gridInfo.put("start", gridRange.getStart());
+        gridInfo.put("end", gridRange.getEnd());
+        gridInfo.put("length", config.getGridLength());
+        gridInfo.put("width", config.getGridWidth());
+        gridInfo.put("thickness", config.getGridThickness());
+        
+        return buildResult(deviceConfig, "getGridInfo", true, 
+                "鏍煎瓙淇℃伅鑾峰彇鎴愬姛", gridInfo);
+    }
+
+    /**
+     * 鏌ユ壘鍙敤鏍煎瓙
+     */
+    private DevicePlcVO.OperationResult handleFindAvailableGrid(
+            DeviceConfig deviceConfig,
+            Map<String, Object> params,
+            LargeGlassConfig config) {
+        
+        // 杩欓噷鍙互鏍规嵁瀹為檯闇�姹傚疄鐜版煡鎵鹃�昏緫
+        // 渚嬪锛氭煡鎵剧涓�涓彲鐢ㄦ牸瀛愩�佹煡鎵炬寚瀹氳鍙敤鏍煎瓙绛�
+        
+        List<GridRange> gridRanges = config.getGridRanges();
+        if (gridRanges == null || gridRanges.isEmpty()) {
+            return buildResult(deviceConfig, "findAvailableGrid", false, 
+                    "鏈厤缃牸瀛愯寖鍥�");
+        }
+        
+        // 绠�鍗曞疄鐜帮細杩斿洖绗竴涓牸瀛愮殑璧峰浣嶇疆
+        GridRange firstRange = gridRanges.get(0);
+        Integer availableGrid = firstRange.getStart();
+        
+        Map<String, Object> result = new HashMap<>();
+        result.put("availableGrid", availableGrid);
+        result.put("row", firstRange.getRow());
+        
+        return buildResult(deviceConfig, "findAvailableGrid", true, 
+                String.format("鎵惧埌鍙敤鏍煎瓙: %d", availableGrid), result);
+    }
+
+    /**
+     * 鏍规嵁鐩爣浣嶇疆鑾峰彇鏍煎瓙缂栧彿
+     */
+    private DevicePlcVO.OperationResult handleGetGridPosition(
+            DeviceConfig deviceConfig,
+            Map<String, Object> params,
+            LargeGlassConfig config) {
+        
+        Integer targetPosition = getIntegerParam(params, "targetPosition");
+        if (targetPosition == null) {
+            return buildResult(deviceConfig, "getGridPosition", false, 
+                    "鏈彁渚涚洰鏍囦綅缃�");
+        }
+        
+        // 鏌ユ壘鐩爣浣嶇疆鍦ㄥ摢涓牸瀛愯寖鍥村唴
+        GridRange gridRange = findGridRangeByPosition(targetPosition, config);
+        if (gridRange == null) {
+            return buildResult(deviceConfig, "getGridPosition", false, 
+                    String.format("鐩爣浣嶇疆 %d 涓嶅湪浠讳綍鏍煎瓙鑼冨洿鍐�", targetPosition));
+        }
+        
+        // 璁$畻鏍煎瓙缂栧彿锛堝亣璁句綅缃氨鏄牸瀛愮紪鍙凤紝鎴栨牴鎹疄闄呰鍒欒绠楋級
+        Integer gridNumber = targetPosition;
+        
+        Map<String, Object> result = new HashMap<>();
+        result.put("gridNumber", gridNumber);
+        result.put("row", gridRange.getRow());
+        result.put("position", targetPosition);
+        
+        return buildResult(deviceConfig, "getGridPosition", true, 
+                String.format("鐩爣浣嶇疆 %d 瀵瑰簲鏍煎瓙缂栧彿 %d锛岀 %d 琛�", 
+                        targetPosition, gridNumber, gridRange.getRow()), result);
+    }
+
+    /**
+     * 瑙f瀽澶х悊鐗囩閰嶇疆
+     */
+    private LargeGlassConfig parseLargeGlassConfig(Map<String, Object> logicParams) {
+        LargeGlassConfig config = new LargeGlassConfig();
+        
+        if (logicParams == null) {
+            return config;
+        }
+        
+        // 瑙f瀽鏍煎瓙鑼冨洿閰嶇疆
+        @SuppressWarnings("unchecked")
+        List<Map<String, Object>> gridRangesConfig = 
+                (List<Map<String, Object>>) logicParams.get("gridRanges");
+        
+        if (gridRangesConfig != null) {
+            List<GridRange> gridRanges = new ArrayList<>();
+            for (Map<String, Object> rangeConfig : gridRangesConfig) {
+                Integer row = getIntegerValue(rangeConfig.get("row"));
+                Integer start = getIntegerValue(rangeConfig.get("start"));
+                Integer end = getIntegerValue(rangeConfig.get("end"));
+                
+                if (row != null && start != null && end != null) {
+                    gridRanges.add(new GridRange(row, start, end));
+                }
+            }
+            config.setGridRanges(gridRanges);
+        }
+        
+        // 瑙f瀽姣忔牸灏哄
+        config.setGridLength(getLogicParam(logicParams, "gridLength", 2000)); // 榛樿2000mm
+        config.setGridWidth(getLogicParam(logicParams, "gridWidth", 1500));  // 榛樿1500mm
+        config.setGridThickness(getLogicParam(logicParams, "gridThickness", 5)); // 榛樿5mm
+        
+        return config;
+    }
+
+    /**
+     * 鏌ユ壘鏍煎瓙鎵�鍦ㄧ殑鏍煎瓙鑼冨洿
+     */
+    private GridRange findGridRange(Integer gridNumber, LargeGlassConfig config) {
+        List<GridRange> gridRanges = config.getGridRanges();
+        if (gridRanges == null) {
+            return null;
+        }
+        
+        for (GridRange range : gridRanges) {
+            if (gridNumber >= range.getStart() && gridNumber <= range.getEnd()) {
+                return range;
+            }
+        }
+        
+        return null;
+    }
+
+    /**
+     * 鏍规嵁浣嶇疆鏌ユ壘鏍煎瓙鑼冨洿
+     */
+    private GridRange findGridRangeByPosition(Integer position, LargeGlassConfig config) {
+        List<GridRange> gridRanges = config.getGridRanges();
+        if (gridRanges == null) {
+            return null;
+        }
+        
+        for (GridRange range : gridRanges) {
+            if (position >= range.getStart() && position <= range.getEnd()) {
+                return range;
+            }
+        }
+        
+        return null;
+    }
+
+    /**
+     * 鑾峰彇鏁存暟鍙傛暟
+     */
+    private Integer getIntegerParam(Map<String, Object> params, String key) {
+        if (params == null) {
+            return null;
+        }
+        return getIntegerValue(params.get(key));
+    }
+
+    /**
+     * 鑾峰彇鏁存暟鍊�
+     */
+    private Integer getIntegerValue(Object value) {
+        if (value instanceof Number) {
+            return ((Number) value).intValue();
+        }
+        if (value == null) {
+            return null;
+        }
+        try {
+            return Integer.parseInt(String.valueOf(value));
+        } catch (NumberFormatException e) {
+            return null;
+        }
+    }
+
+    /**
+     * 鏋勫缓鎿嶄綔缁撴灉
+     */
+    private DevicePlcVO.OperationResult buildResult(DeviceConfig deviceConfig,
+                                                    String operation,
+                                                    boolean success,
+                                                    String message) {
+        return buildResult(deviceConfig, operation, success, message, null);
+    }
+
+    /**
+     * 鏋勫缓鎿嶄綔缁撴灉锛堝甫鏁版嵁锛�
+     */
+    private DevicePlcVO.OperationResult buildResult(DeviceConfig deviceConfig,
+                                                    String operation,
+                                                    boolean success,
+                                                    String message,
+                                                    Map<String, Object> data) {
+        DevicePlcVO.OperationResult.OperationResultBuilder builder = DevicePlcVO.OperationResult.builder()
+                .deviceId(deviceConfig.getId())
+                .deviceName(deviceConfig.getDeviceName())
+                .deviceCode(deviceConfig.getDeviceCode())
+                .projectId(deviceConfig.getProjectId() != null ? 
+                        String.valueOf(deviceConfig.getProjectId()) : null)
+                .operation(operation)
+                .success(success)
+                .message(message)
+                .timestamp(LocalDateTime.now());
+        
+        if (data != null) {
+            builder.data(data);
+        }
+        
+        return builder.build();
+    }
+
+    @Override
+    public String validateLogicParams(DeviceConfig deviceConfig) {
+        Map<String, Object> logicParams = parseLogicParams(deviceConfig);
+        LargeGlassConfig config = parseLargeGlassConfig(logicParams);
+        
+        // 楠岃瘉鏍煎瓙鑼冨洿閰嶇疆
+        List<GridRange> gridRanges = config.getGridRanges();
+        if (gridRanges == null || gridRanges.isEmpty()) {
+            return "蹇呴』閰嶇疆鑷冲皯涓�涓牸瀛愯寖鍥达紙gridRanges锛�";
+        }
+        
+        // 楠岃瘉姣忔牸灏哄
+        if (config.getGridLength() == null || config.getGridLength() <= 0) {
+            return "鏍煎瓙闀垮害锛坓ridLength锛夊繀椤诲ぇ浜�0";
+        }
+        if (config.getGridWidth() == null || config.getGridWidth() <= 0) {
+            return "鏍煎瓙瀹藉害锛坓ridWidth锛夊繀椤诲ぇ浜�0";
+        }
+        if (config.getGridThickness() == null || config.getGridThickness() <= 0) {
+            return "鏍煎瓙鍘氬害锛坓ridThickness锛夊繀椤诲ぇ浜�0";
+        }
+        
+        return null; // 楠岃瘉閫氳繃
+    }
+
+    @Override
+    public String getDefaultLogicParams() {
+        Map<String, Object> defaultParams = new HashMap<>();
+        
+        // 榛樿鏍煎瓙鑼冨洿閰嶇疆锛氱涓�琛�1~52鏍硷紝绗簩琛�53~101鏍�
+        List<Map<String, Object>> gridRanges = new ArrayList<>();
+        
+        Map<String, Object> row1 = new HashMap<>();
+        row1.put("row", 1);
+        row1.put("start", 1);
+        row1.put("end", 52);
+        gridRanges.add(row1);
+        
+        Map<String, Object> row2 = new HashMap<>();
+        row2.put("row", 2);
+        row2.put("start", 53);
+        row2.put("end", 101);
+        gridRanges.add(row2);
+        
+        defaultParams.put("gridRanges", gridRanges);
+        
+        // 榛樿姣忔牸灏哄锛坢m锛�
+        defaultParams.put("gridLength", 2000);   // 闀垮害2000mm
+        defaultParams.put("gridWidth", 1500);    // 瀹藉害1500mm
+        defaultParams.put("gridThickness", 5);   // 鍘氬害5mm
+        
+        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/largeglass/model/GridRange.java b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/largeglass/model/GridRange.java
new file mode 100644
index 0000000..457243f
--- /dev/null
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/largeglass/model/GridRange.java
@@ -0,0 +1,30 @@
+package com.mes.interaction.largeglass.model;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * 鏍煎瓙鑼冨洿
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class GridRange {
+
+    /**
+     * 琛屽彿锛堢鍑犺锛�
+     */
+    private Integer row;
+
+    /**
+     * 璧峰鏍煎瓙缂栧彿
+     */
+    private Integer start;
+
+    /**
+     * 缁撴潫鏍煎瓙缂栧彿
+     */
+    private Integer end;
+}
+
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/vehicle/coordination/VehicleCoordinationService.java b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/vehicle/coordination/VehicleCoordinationService.java
new file mode 100644
index 0000000..118e7f3
--- /dev/null
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/vehicle/coordination/VehicleCoordinationService.java
@@ -0,0 +1,189 @@
+package com.mes.interaction.vehicle.coordination;
+
+import com.mes.device.entity.DeviceConfig;
+import com.mes.device.service.DeviceConfigService;
+import com.mes.device.service.DeviceGroupRelationService;
+import com.mes.device.vo.DeviceGroupVO;
+import com.mes.interaction.vehicle.model.VehiclePath;
+import com.mes.interaction.vehicle.model.VehicleStatus;
+import com.mes.interaction.vehicle.model.VehicleState;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 杞﹁締鍗忚皟鏈嶅姟
+ * 璐熻矗鍦ㄥ涓ぇ杞﹀疄渚嬩腑閫夋嫨鍜屽垎閰嶄换鍔�
+ * 
+ * @author mes
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Service
+public class VehicleCoordinationService {
+    
+    @Autowired
+    private VehicleStatusManager statusManager;
+    
+    @Autowired
+    private DeviceConfigService deviceConfigService;
+    
+    @Autowired
+    private DeviceGroupRelationService deviceGroupRelationService;
+    
+    /**
+     * 浠庤澶囩粍涓�夋嫨涓�涓彲鐢ㄧ殑澶ц溅瀹炰緥
+     * 
+     * @param groupId 璁惧缁処D
+     * @return 鍙敤鐨勮澶囬厤缃紝濡傛灉娌℃湁鍙敤杞﹁締鍒欒繑鍥瀗ull
+     */
+    public DeviceConfig selectAvailableVehicle(Long groupId) {
+        if (groupId == null) {
+            log.warn("璁惧缁処D涓虹┖锛屾棤娉曢�夋嫨杞﹁締");
+            return null;
+        }
+        
+        // 1. 鑾峰彇璁惧缁勪腑鐨勬墍鏈夎澶�
+        List<DeviceGroupVO.DeviceInfo> groupDevices = deviceGroupRelationService.getGroupDevices(groupId);
+        if (groupDevices == null || groupDevices.isEmpty()) {
+            log.warn("璁惧缁� {} 涓病鏈夎澶�", groupId);
+            return null;
+        }
+        
+        // 2. 杩囨护鍑哄ぇ杞︾被鍨嬬殑璁惧
+        List<DeviceGroupVO.DeviceInfo> vehicles = groupDevices.stream()
+            .filter(d -> DeviceConfig.DeviceType.LOAD_VEHICLE.equals(d.getDeviceType()))
+            .collect(Collectors.toList());
+        
+        if (vehicles.isEmpty()) {
+            log.warn("璁惧缁� {} 涓病鏈夊ぇ杞﹁澶�", groupId);
+            return null;
+        }
+        
+        // 3. 杩囨护鍑哄彲鐢ㄧ殑杞﹁締
+        List<DeviceGroupVO.DeviceInfo> availableVehicles = vehicles.stream()
+            .filter(v -> {
+                String deviceId = v.getId() != null ? v.getId().toString() : null;
+                if (deviceId == null) {
+                    deviceId = v.getDeviceCode();
+                }
+                return statusManager.isVehicleAvailable(deviceId);
+            })
+            .collect(Collectors.toList());
+        
+        if (availableVehicles.isEmpty()) {
+            log.warn("璁惧缁� {} 涓病鏈夊彲鐢ㄧ殑澶ц溅璁惧", groupId);
+            return null;
+        }
+        
+        // 4. 閫夋嫨绛栫暐锛堢畝鍗曠瓥鐣ワ細閫夋嫨绗竴涓彲鐢ㄧ殑锛屽彲浠ユ墿灞曚负鎸変紭鍏堢骇銆佽礋杞界瓑閫夋嫨锛�
+        DeviceGroupVO.DeviceInfo selected = availableVehicles.get(0);
+        
+        // 5. 鑾峰彇瀹屾暣鐨勮澶囬厤缃�
+        DeviceConfig deviceConfig = null;
+        if (selected.getId() != null) {
+            deviceConfig = deviceConfigService.getDeviceById(selected.getId());
+        } else if (selected.getDeviceCode() != null) {
+            deviceConfig = deviceConfigService.getDeviceByCode(selected.getDeviceCode());
+        }
+        
+        if (deviceConfig != null) {
+            log.info("閫夋嫨鍙敤杞﹁締: deviceId={}, deviceName={}", 
+                deviceConfig.getDeviceId(), deviceConfig.getDeviceName());
+        }
+        
+        return deviceConfig;
+    }
+    
+    /**
+     * 妫�鏌ヨ矾寰勫啿绐�
+     * 
+     * @param deviceId 璁惧ID
+     * @param plannedPath 璁″垝璺緞
+     * @return true琛ㄧず鏈夊啿绐侊紝false琛ㄧず鏃犲啿绐�
+     */
+    public boolean hasPathConflict(String deviceId, VehiclePath plannedPath) {
+        if (deviceId == null || plannedPath == null) {
+            return false;
+        }
+        
+        // 鑾峰彇鎵�鏈夋墽琛屼腑鐨勮溅杈�
+        List<VehicleStatus> executingVehicles = statusManager.getExecutingVehicles();
+        
+        // 鎺掗櫎鑷繁
+        executingVehicles = executingVehicles.stream()
+            .filter(v -> !v.getDeviceId().equals(deviceId))
+            .collect(Collectors.toList());
+        
+        // 妫�鏌ヨ矾寰勬槸鍚﹀啿绐�
+        for (VehicleStatus vehicle : executingVehicles) {
+            if (vehicle.getCurrentTask() != null) {
+                VehiclePath existingPath = vehicle.getCurrentTask().getPlannedPath();
+                if (existingPath != null && plannedPath.conflictsWith(existingPath)) {
+                    log.warn("妫�娴嬪埌璺緞鍐茬獊: deviceId={}, 涓� deviceId={} 鐨勮矾寰勫啿绐�", 
+                        deviceId, vehicle.getDeviceId());
+                    return true;
+                }
+            }
+        }
+        
+        return false;
+    }
+    
+    /**
+     * 鑾峰彇璁惧缁勪腑鎵�鏈夊ぇ杞﹁澶�
+     * 
+     * @param groupId 璁惧缁処D
+     * @return 澶ц溅璁惧鍒楄〃
+     */
+    public List<DeviceConfig> getVehiclesInGroup(Long groupId) {
+        if (groupId == null) {
+            return Collections.emptyList();
+        }
+        
+        List<DeviceGroupVO.DeviceInfo> groupDevices = deviceGroupRelationService.getGroupDevices(groupId);
+        if (groupDevices == null || groupDevices.isEmpty()) {
+            return Collections.emptyList();
+        }
+        
+        return groupDevices.stream()
+            .filter(d -> DeviceConfig.DeviceType.LOAD_VEHICLE.equals(d.getDeviceType()))
+            .map(d -> {
+                if (d.getId() != null) {
+                    return deviceConfigService.getDeviceById(d.getId());
+                } else if (d.getDeviceCode() != null) {
+                    return deviceConfigService.getDeviceByCode(d.getDeviceCode());
+                }
+                return null;
+            })
+            .filter(d -> d != null)
+            .collect(Collectors.toList());
+    }
+    
+    /**
+     * 鑾峰彇璁惧缁勪腑鎵�鏈夊彲鐢ㄧ殑澶ц溅璁惧
+     * 
+     * @param groupId 璁惧缁処D
+     * @return 鍙敤鐨勫ぇ杞﹁澶囧垪琛�
+     */
+    public List<DeviceConfig> getAvailableVehiclesInGroup(Long groupId) {
+        return getVehiclesInGroup(groupId).stream()
+            .filter(v -> statusManager.isVehicleAvailable(v.getDeviceId()))
+            .collect(Collectors.toList());
+    }
+    
+    /**
+     * 妫�鏌ヨ澶囩粍涓槸鍚︽湁鍙敤鐨勫ぇ杞�
+     * 
+     * @param groupId 璁惧缁処D
+     * @return true琛ㄧず鏈夊彲鐢ㄨ溅杈嗭紝false琛ㄧず娌℃湁
+     */
+    public boolean hasAvailableVehicle(Long groupId) {
+        return selectAvailableVehicle(groupId) != null;
+    }
+}
+
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/vehicle/coordination/VehicleStatusManager.java b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/vehicle/coordination/VehicleStatusManager.java
new file mode 100644
index 0000000..2cc0964
--- /dev/null
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/vehicle/coordination/VehicleStatusManager.java
@@ -0,0 +1,199 @@
+package com.mes.interaction.vehicle.coordination;
+
+import com.mes.interaction.vehicle.model.VehicleState;
+import com.mes.interaction.vehicle.model.VehicleStatus;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
+
+/**
+ * 杞﹁締鐘舵�佺鐞嗗櫒
+ * 绠$悊鎵�鏈夊ぇ杞﹁澶囧疄渚嬬殑杩愯鏃剁姸鎬�
+ * 
+ * @author huang
+ * @since 2025-11-21
+ */
+@Slf4j
+@Service
+public class VehicleStatusManager {
+    
+    /**
+     * 瀛樺偍鎵�鏈夎溅杈嗗疄渚嬬殑鐘舵�侊細deviceId -> VehicleStatus
+     */
+    private final Map<String, VehicleStatus> vehicleStatusMap = new ConcurrentHashMap<>();
+    
+    /**
+     * 鑾峰彇杞﹁締鐘舵��
+     * 濡傛灉涓嶅瓨鍦ㄥ垯鍒涘缓骞惰繑鍥炵┖闂茬姸鎬�
+     * 
+     * @param deviceId 璁惧ID
+     * @return 杞﹁締鐘舵��
+     */
+    public VehicleStatus getVehicleStatus(String deviceId) {
+        if (deviceId == null || deviceId.isEmpty()) {
+            return null;
+        }
+        return vehicleStatusMap.get(deviceId);
+    }
+    
+    /**
+     * 鑾峰彇鎴栧垱寤鸿溅杈嗙姸鎬�
+     * 
+     * @param deviceId 璁惧ID
+     * @param deviceName 璁惧鍚嶇О
+     * @return 杞﹁締鐘舵��
+     */
+    public VehicleStatus getOrCreateVehicleStatus(String deviceId, String deviceName) {
+        return vehicleStatusMap.computeIfAbsent(deviceId, 
+            k -> new VehicleStatus(deviceId, deviceName));
+    }
+    
+    /**
+     * 鏇存柊杞﹁締鐘舵��
+     * 
+     * @param deviceId 璁惧ID
+     * @param state 鏂扮姸鎬�
+     */
+    public void updateVehicleStatus(String deviceId, VehicleState state) {
+        if (deviceId == null || deviceId.isEmpty()) {
+            log.warn("璁惧ID涓虹┖锛屾棤娉曟洿鏂扮姸鎬�");
+            return;
+        }
+        
+        VehicleStatus status = vehicleStatusMap.computeIfAbsent(
+            deviceId, 
+            k -> new VehicleStatus(deviceId)
+        );
+        status.setState(state);
+        
+        log.debug("鏇存柊杞﹁締鐘舵��: deviceId={}, state={}", deviceId, state);
+    }
+    
+    /**
+     * 鏇存柊杞﹁締鐘舵�侊紙甯﹁澶囧悕绉帮級
+     * 
+     * @param deviceId 璁惧ID
+     * @param deviceName 璁惧鍚嶇О
+     * @param state 鏂扮姸鎬�
+     */
+    public void updateVehicleStatus(String deviceId, String deviceName, VehicleState state) {
+        VehicleStatus status = getOrCreateVehicleStatus(deviceId, deviceName);
+        status.setState(state);
+        log.debug("鏇存柊杞﹁締鐘舵��: deviceId={}, deviceName={}, state={}", deviceId, deviceName, state);
+    }
+    
+    /**
+     * 鑾峰彇鎵�鏈夌┖闂茬殑杞﹁締
+     * 
+     * @return 绌洪棽杞﹁締鐘舵�佸垪琛�
+     */
+    public List<VehicleStatus> getIdleVehicles() {
+        return vehicleStatusMap.values().stream()
+            .filter(v -> v.getState() == VehicleState.IDLE)
+            .collect(Collectors.toList());
+    }
+    
+    /**
+     * 鑾峰彇鎵�鏈夋墽琛屼腑鐨勮溅杈�
+     * 
+     * @return 鎵ц涓溅杈嗙姸鎬佸垪琛�
+     */
+    public List<VehicleStatus> getExecutingVehicles() {
+        return vehicleStatusMap.values().stream()
+            .filter(v -> v.getState() == VehicleState.EXECUTING)
+            .collect(Collectors.toList());
+    }
+    
+    /**
+     * 鑾峰彇鎵�鏈夌瓑寰呬腑鐨勮溅杈�
+     * 
+     * @return 绛夊緟涓溅杈嗙姸鎬佸垪琛�
+     */
+    public List<VehicleStatus> getWaitingVehicles() {
+        return vehicleStatusMap.values().stream()
+            .filter(v -> v.getState() == VehicleState.WAITING)
+            .collect(Collectors.toList());
+    }
+    
+    /**
+     * 妫�鏌ヨ溅杈嗘槸鍚﹀彲鐢�
+     * 
+     * @param deviceId 璁惧ID
+     * @return true琛ㄧず鍙敤锛宖alse琛ㄧず涓嶅彲鐢�
+     */
+    public boolean isVehicleAvailable(String deviceId) {
+        if (deviceId == null || deviceId.isEmpty()) {
+            return false;
+        }
+        
+        VehicleStatus status = vehicleStatusMap.get(deviceId);
+        return status == null || status.isAvailable();
+    }
+    
+    /**
+     * 璁剧疆杞﹁締浠诲姟
+     * 
+     * @param deviceId 璁惧ID
+     * @param task 浠诲姟
+     */
+    public void setVehicleTask(String deviceId, com.mes.interaction.vehicle.model.VehicleTask task) {
+        VehicleStatus status = vehicleStatusMap.get(deviceId);
+        if (status != null) {
+            status.setCurrentTask(task);
+            if (task != null && task.getPlannedPath() != null) {
+                status.setTargetPosition(task.getPlannedPath().getEndPosition());
+            }
+        }
+    }
+    
+    /**
+     * 娓呴櫎杞﹁締浠诲姟
+     * 
+     * @param deviceId 璁惧ID
+     */
+    public void clearVehicleTask(String deviceId) {
+        VehicleStatus status = vehicleStatusMap.get(deviceId);
+        if (status != null) {
+            status.setCurrentTask(null);
+            status.setTargetPosition(null);
+        }
+    }
+    
+    /**
+     * 绉婚櫎杞﹁締鐘舵�侊紙褰撹澶囪鍒犻櫎鏃讹級
+     * 
+     * @param deviceId 璁惧ID
+     */
+    public void removeVehicleStatus(String deviceId) {
+        vehicleStatusMap.remove(deviceId);
+        log.info("绉婚櫎杞﹁締鐘舵��: deviceId={}", deviceId);
+    }
+    
+    /**
+     * 鑾峰彇鎵�鏈夎溅杈嗙姸鎬�
+     * 
+     * @return 鎵�鏈夎溅杈嗙姸鎬佸垪琛�
+     */
+    public List<VehicleStatus> getAllVehicleStatuses() {
+        return vehicleStatusMap.values().stream()
+            .collect(Collectors.toList());
+    }
+    
+    /**
+     * 鑾峰彇鎸囧畾璁惧缁勪腑鐨勮溅杈嗙姸鎬�
+     * 
+     * @param deviceIds 璁惧ID鍒楄〃
+     * @return 杞﹁締鐘舵�佸垪琛�
+     */
+    public List<VehicleStatus> getVehicleStatuses(List<String> deviceIds) {
+        return deviceIds.stream()
+            .map(this::getVehicleStatus)
+            .filter(status -> status != null)
+            .collect(Collectors.toList());
+    }
+}
+
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/vehicle/flow/LoadVehicleInteraction.java b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/vehicle/flow/LoadVehicleInteraction.java
new file mode 100644
index 0000000..a644e2d
--- /dev/null
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/vehicle/flow/LoadVehicleInteraction.java
@@ -0,0 +1,225 @@
+package com.mes.interaction.vehicle.flow;
+
+import com.mes.device.entity.DeviceConfig;
+import com.mes.device.service.DeviceInteractionService;
+import com.mes.device.vo.DevicePlcVO;
+import com.mes.interaction.DeviceInteraction;
+import com.mes.interaction.base.InteractionContext;
+import com.mes.interaction.base.InteractionResult;
+import com.mes.interaction.vehicle.coordination.VehicleCoordinationService;
+import com.mes.interaction.vehicle.coordination.VehicleStatusManager;
+import com.mes.interaction.vehicle.model.VehicleState;
+import com.mes.interaction.vehicle.model.VehiclePosition;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.util.CollectionUtils;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 澶ц溅璁惧浜や簰瀹炵幇锛堝寮虹増锛�
+ * 闆嗘垚澶氳溅鍗忚皟鍜岀姸鎬佺鐞嗗姛鑳�
+ * 
+ * @author mes
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class LoadVehicleInteraction implements DeviceInteraction {
+
+    private final DeviceInteractionService deviceInteractionService;
+    
+    @Autowired
+    private VehicleCoordinationService coordinationService;
+    
+    @Autowired
+    private VehicleStatusManager statusManager;
+
+    @Override
+    public String getDeviceType() {
+        return DeviceConfig.DeviceType.LOAD_VEHICLE;
+    }
+
+    @Override
+    public InteractionResult execute(InteractionContext context) {
+        try {
+            // 鍓嶇疆鏉′欢楠岃瘉
+            if (context.getCurrentDevice() == null) {
+                return InteractionResult.fail("璁惧閰嶇疆涓嶅瓨鍦�");
+            }
+
+            DeviceConfig currentDevice = context.getCurrentDevice();
+            String deviceId = currentDevice.getDeviceId();
+
+            // 1. 妫�鏌ヨ溅杈嗙姸鎬侊紙濡傛灉璁惧宸叉寚瀹氾級
+            if (deviceId != null) {
+                if (!statusManager.isVehicleAvailable(deviceId)) {
+                    com.mes.interaction.vehicle.model.VehicleStatus status = 
+                        statusManager.getVehicleStatus(deviceId);
+                    String stateMsg = status != null ? status.getState().name() : "鏈煡";
+                    return InteractionResult.fail(
+                        String.format("杞﹁締 %s (%s) 褰撳墠鐘舵�佷负 %s锛屾棤娉曟墽琛屾搷浣�", 
+                            currentDevice.getDeviceName(), deviceId, stateMsg));
+                }
+            }
+
+            // 2. 濡傛灉娌℃湁鎸囧畾璁惧锛屽皾璇曚粠璁惧缁勪腑閫夋嫨鍙敤杞﹁締
+            DeviceConfig selectedDevice = currentDevice;
+            Long groupId = extractGroupId(context);
+            if (groupId != null && (deviceId == null || !statusManager.isVehicleAvailable(deviceId))) {
+                DeviceConfig availableVehicle = coordinationService.selectAvailableVehicle(groupId);
+                if (availableVehicle != null) {
+                    selectedDevice = availableVehicle;
+                    log.info("浠庤澶囩粍 {} 涓�夋嫨鍙敤杞﹁締: {}", groupId, availableVehicle.getDeviceName());
+                } else if (deviceId == null) {
+                    // 娌℃湁鍙敤杞﹁締锛岃繑鍥炵瓑寰呯粨鏋�
+                    return InteractionResult.waitResult(
+                        "璁惧缁勪腑娌℃湁鍙敤鐨勫ぇ杞﹁澶囷紝绛夊緟杞﹁締绌洪棽", null);
+                }
+            }
+
+            // 3. 楠岃瘉鐜荤拑ID
+            List<String> glassIds = context.getParameters().getGlassIds();
+            if (CollectionUtils.isEmpty(glassIds)) {
+                return InteractionResult.waitResult("鏈彁渚涚幓鐠僆D锛岀瓑寰呰緭鍏�", null);
+            }
+
+            // 楠岃瘉鐜荤拑ID鏍煎紡
+            for (String glassId : glassIds) {
+                if (glassId == null || glassId.trim().isEmpty()) {
+                    return InteractionResult.fail("鐜荤拑ID涓嶈兘涓虹┖");
+                }
+            }
+
+            // 4. 鏍囪杞﹁締涓烘墽琛屼腑
+            String selectedDeviceId = selectedDevice.getDeviceId();
+            statusManager.updateVehicleStatus(
+                selectedDeviceId, 
+                selectedDevice.getDeviceName(), 
+                VehicleState.EXECUTING);
+
+            try {
+                // 5. 鏋勫缓PLC鍐欏叆鍙傛暟
+                Map<String, Object> params = new HashMap<>();
+                params.put("glassIds", glassIds);
+                params.put("positionCode", context.getParameters().getPositionCode());
+                params.put("positionValue", context.getParameters().getPositionValue());
+                params.put("triggerRequest", true);
+
+                // 6. 鎵ц瀹為檯鐨凱LC鍐欏叆鎿嶄綔
+                DevicePlcVO.OperationResult plcResult = deviceInteractionService.executeOperation(
+                        selectedDevice.getId(), 
+                        "feedGlass", 
+                        params
+                );
+
+                // 7. 妫�鏌LC鍐欏叆缁撴灉
+                if (plcResult == null || !Boolean.TRUE.equals(plcResult.getSuccess())) {
+                    String errorMsg = plcResult != null ? plcResult.getMessage() : "PLC鍐欏叆鎿嶄綔杩斿洖绌虹粨鏋�";
+                    
+                    // 鎵ц澶辫触锛屾仮澶嶄负绌洪棽鐘舵��
+                    statusManager.updateVehicleStatus(selectedDeviceId, VehicleState.IDLE);
+                    
+                    return InteractionResult.fail("PLC鍐欏叆澶辫触: " + errorMsg);
+                }
+
+                // 8. 鏇存柊杞﹁締浣嶇疆淇℃伅锛堝鏋滄湁锛�
+                if (context.getParameters().getPositionCode() != null || 
+                    context.getParameters().getPositionValue() != null) {
+                    com.mes.interaction.vehicle.model.VehicleStatus vehicleStatus = 
+                        statusManager.getOrCreateVehicleStatus(
+                            selectedDeviceId, 
+                            selectedDevice.getDeviceName());
+                    VehiclePosition position = new VehiclePosition(
+                        context.getParameters().getPositionCode(),
+                        context.getParameters().getPositionValue());
+                    vehicleStatus.setCurrentPosition(position);
+                }
+
+                // 9. 鎵ц澶ц溅璁惧鎿嶄綔锛堟暟鎹祦杞級
+                List<String> copied = new ArrayList<>(glassIds);
+                context.setLoadedGlassIds(copied);
+                context.getSharedData().put("glassesFromVehicle", copied);
+                context.getSharedData().put("loadVehicleTime", System.currentTimeMillis());
+                context.getSharedData().put("selectedVehicleId", selectedDeviceId);
+                context.getSharedData().put("selectedVehicleName", selectedDevice.getDeviceName());
+
+                // 10. 鍚庣疆鏉′欢妫�鏌�
+                if (context.getLoadedGlassIds().isEmpty()) {
+                    statusManager.updateVehicleStatus(selectedDeviceId, VehicleState.IDLE);
+                    return InteractionResult.fail("澶ц溅璁惧鎿嶄綔澶辫触锛氱幓鐠僆D鍒楄〃涓虹┖");
+                }
+
+                // 11. 鏋勫缓杩斿洖鏁版嵁
+                Map<String, Object> data = new HashMap<>();
+                data.put("loaded", copied);
+                data.put("glassCount", copied.size());
+                data.put("deviceId", selectedDevice.getId());
+                data.put("deviceCode", selectedDevice.getDeviceCode());
+                data.put("deviceName", selectedDevice.getDeviceName());
+                data.put("deviceIdString", selectedDeviceId);
+                data.put("plcResult", plcResult.getMessage());
+                
+                // 娉ㄦ剰锛氳繖閲屼笉绔嬪嵆鎭㈠涓虹┖闂茬姸鎬侊紝鍥犱负瀹為檯鎵ц鍙兘闇�瑕佹椂闂�
+                // 鐪熸鐨勭姸鎬佹仮澶嶅簲璇ュ湪浠诲姟瀹屾垚鍚庨�氳繃鍥炶皟鎴栫姸鎬佹煡璇㈡潵鏇存柊
+                // 鎴栬�呭彲浠ラ�氳繃寮傛浠诲姟鍦ㄥ悗鍙扮洃鎺LC鐘舵�侊紝纭瀹屾垚鍚庡啀鎭㈠
+                
+                log.info("澶ц溅璁惧浜や簰鎵ц鎴愬姛: deviceId={}, deviceName={}, glassCount={}", 
+                    selectedDeviceId, selectedDevice.getDeviceName(), copied.size());
+                
+                return InteractionResult.success(data);
+                
+            } catch (Exception e) {
+                // 鍙戠敓寮傚父鏃讹紝鎭㈠涓虹┖闂茬姸鎬�
+                statusManager.updateVehicleStatus(selectedDeviceId, VehicleState.ERROR);
+                log.error("澶ц溅璁惧浜や簰鎵ц寮傚父: deviceId={}", selectedDeviceId, e);
+                throw e;
+            }
+            
+        } catch (Exception e) {
+            return InteractionResult.fail("澶ц溅璁惧浜や簰鎵ц寮傚父: " + e.getMessage());
+        }
+    }
+
+    /**
+     * 浠庝笂涓嬫枃涓彁鍙栬澶囩粍ID
+     */
+    private Long extractGroupId(InteractionContext context) {
+        // 灏濊瘯浠庡叡浜暟鎹腑鑾峰彇
+        Map<String, Object> sharedData = context.getSharedData();
+        if (sharedData != null) {
+            Object groupIdObj = sharedData.get("groupId");
+            if (groupIdObj instanceof Number) {
+                return ((Number) groupIdObj).longValue();
+            } else if (groupIdObj instanceof String) {
+                try {
+                    return Long.parseLong((String) groupIdObj);
+                } catch (NumberFormatException ignored) {
+                }
+            }
+        }
+        
+        // 灏濊瘯浠庝换鍔″弬鏁颁腑鑾峰彇
+        if (context.getParameters() != null && context.getParameters().getExtra() != null) {
+            Object groupIdObj = context.getParameters().getExtra().get("groupId");
+            if (groupIdObj instanceof Number) {
+                return ((Number) groupIdObj).longValue();
+            }
+        }
+        
+        return null;
+    }
+
+    @Override
+    public boolean supportsOperation(String operation) {
+        // 鏀寔鎵�鏈夋搷浣滐紝浣嗕富瑕佸叧娉� feedGlass
+        return true;
+    }
+}
+
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
new file mode 100644
index 0000000..aed9919
--- /dev/null
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/vehicle/handler/LoadVehicleLogicHandler.java
@@ -0,0 +1,1719 @@
+package com.mes.interaction.vehicle.handler;
+
+import com.mes.device.entity.DeviceConfig;
+import com.mes.device.service.DeviceConfigService;
+import com.mes.device.service.DeviceGroupRelationService;
+import com.mes.device.service.DevicePlcOperationService;
+import com.mes.device.service.GlassInfoService;
+import com.mes.device.vo.DeviceGroupVO;
+import com.mes.device.vo.DevicePlcVO;
+import com.mes.interaction.BaseDeviceLogicHandler;
+import com.mes.interaction.vehicle.coordination.VehicleStatusManager;
+import com.mes.interaction.vehicle.model.VehiclePosition;
+import com.mes.interaction.vehicle.model.VehicleState;
+import com.mes.interaction.vehicle.model.VehicleStatus;
+import com.mes.interaction.vehicle.model.VehicleTask;
+import com.mes.s7.enhanced.EnhancedS7Serializer;
+import com.mes.s7.provider.S7SerializerProvider;
+import com.mes.service.PlcDynamicDataService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.PreDestroy;
+import java.util.*;
+import java.util.concurrent.*;
+
+/**
+ * 澶ц溅璁惧閫昏緫澶勭悊鍣�
+ * 鎵�鏈夊ぇ杞﹁澶囧疄渚嬪叡浜繖涓鐞嗗櫒
+ * 闆嗘垚澶氬疄渚嬬姸鎬佺鐞嗗拰鍗忚皟鍔熻兘
+ * 
+ * @author huang
+ * @since 2025-11-21
+ */
+@Slf4j
+@Component
+public class LoadVehicleLogicHandler extends BaseDeviceLogicHandler {
+
+    private final GlassInfoService glassInfoService;
+    
+    @Autowired
+    private VehicleStatusManager statusManager;
+    
+    @Autowired(required = false)
+    private DeviceConfigService deviceConfigService;
+    
+    @Autowired(required = false)
+    private DeviceGroupRelationService deviceGroupRelationService;
+    
+    @Autowired(required = false)
+    private PlcDynamicDataService plcDynamicDataService;
+    
+    @Autowired(required = false)
+    private S7SerializerProvider s7SerializerProvider;
+
+    // MES瀛楁鍒楄〃锛堣繘鐗囧拰鍑虹墖鍏辩敤鍚屼竴濂楀崗璁級
+    private static final List<String> MES_FIELDS = Arrays.asList(
+            "mesSend", "mesGlassId", "mesWidth", "mesHeight", 
+            "startSlot", "targetSlot", "workLine"
+    );
+
+    // 鐩戞帶绾跨▼姹狅細鐢ㄤ簬瀹氭湡妫�鏌ュぇ杞︾姸鎬佸苟鍗忚皟鍗ц浆绔嬭澶�
+    private final ScheduledExecutorService stateMonitorExecutor = Executors.newScheduledThreadPool(5, r -> {
+        Thread t = new Thread(r, "VehicleStateMonitor");
+        t.setDaemon(true);
+        return t;
+    });
+    
+    // 绌洪棽鐩戞帶绾跨▼姹狅細鐢ㄤ簬淇濇寔plcRequest=1
+    private final ScheduledExecutorService idleMonitorExecutor = Executors.newScheduledThreadPool(3, r -> {
+        Thread t = new Thread(r, "VehicleIdleMonitor");
+        t.setDaemon(true);
+        return t;
+    });
+    
+    // 浠诲姟鐩戞帶绾跨▼姹狅細鐢ㄤ簬鐩戞帶浠诲姟鎵ц鍜岀姸鎬佸垏鎹�
+    private final ScheduledExecutorService taskMonitorExecutor = Executors.newScheduledThreadPool(5, r -> {
+        Thread t = new Thread(r, "VehicleTaskMonitor");
+        t.setDaemon(true);
+        return t;
+    });
+    
+    // 璁板綍姝e湪鐩戞帶鐨勮澶囷細deviceId -> 鐩戞帶浠诲姟
+    private final Map<String, ScheduledFuture<?>> monitoringTasks = new ConcurrentHashMap<>();
+    
+    // 璁板綍绌洪棽鐩戞帶浠诲姟锛歞eviceId -> 绌洪棽鐩戞帶浠诲姟
+    private final Map<String, ScheduledFuture<?>> idleMonitoringTasks = new ConcurrentHashMap<>();
+    
+    // 璁板綍浠诲姟鐩戞帶浠诲姟锛歞eviceId -> 浠诲姟鐩戞帶浠诲姟
+    private final Map<String, ScheduledFuture<?>> taskMonitoringTasks = new ConcurrentHashMap<>();
+    
+    // 璁板綍宸插崗璋冪殑璁惧锛歞eviceId -> 宸插崗璋冪殑state瀛楁闆嗗悎锛堥伩鍏嶉噸澶嶅崗璋冿級
+    private final Map<String, List<String>> coordinatedStates = new ConcurrentHashMap<>();
+    
+    // 璁板綍褰撳墠浠诲姟锛歞eviceId -> 浠诲姟淇℃伅
+    private final Map<String, MesTaskInfo> currentTasks = new ConcurrentHashMap<>();
+
+    @Autowired
+    public LoadVehicleLogicHandler(
+            DevicePlcOperationService devicePlcOperationService,
+            @Qualifier("deviceGlassInfoService") GlassInfoService glassInfoService) {
+        super(devicePlcOperationService);
+        this.glassInfoService = glassInfoService;
+    }
+
+    @Override
+    public String getDeviceType() {
+        return DeviceConfig.DeviceType.LOAD_VEHICLE;
+    }
+
+    @Override
+    protected DevicePlcVO.OperationResult doExecute(
+            DeviceConfig deviceConfig,
+            String operation,
+            Map<String, Object> params,
+            Map<String, Object> logicParams) {
+
+        String deviceId = deviceConfig.getDeviceId();
+        log.info("鎵ц澶ц溅璁惧鎿嶄綔: deviceId={}, deviceName={}, operation={}", 
+            deviceId, deviceConfig.getDeviceName(), operation);
+
+        // 1. 妫�鏌ヨ繖涓澶囧疄渚嬬殑鐘舵�侊紙瀵逛簬闇�瑕佺姸鎬佹鏌ョ殑鎿嶄綔锛�
+        if (needsStateCheck(operation)) {
+            VehicleStatus status = statusManager.getVehicleStatus(deviceId);
+            if (status != null && !status.isAvailable()) {
+                return DevicePlcVO.OperationResult.builder()
+                    .success(false)
+                    .message(String.format("杞﹁締 %s (%s) 褰撳墠鐘舵�佷负 %s锛屾棤娉曟墽琛屾搷浣� %s", 
+                        deviceConfig.getDeviceName(), 
+                        deviceId,
+                        status.getState(),
+                        operation))
+                    .build();
+            }
+        }
+
+        // 2. 鏍囪涓烘墽琛屼腑锛堝浜庨渶瑕佺姸鎬佺鐞嗙殑鎿嶄綔锛�
+        if (needsStateManagement(operation)) {
+            statusManager.updateVehicleStatus(deviceId, deviceConfig.getDeviceName(), VehicleState.EXECUTING);
+            
+            // 鍒涘缓浠诲姟淇℃伅
+            VehicleTask task = createVehicleTask(deviceConfig, operation, params, logicParams);
+            statusManager.setVehicleTask(deviceId, task);
+        }
+
+        try {
+            // 3. 鎵ц鍘熸湁閫昏緫
+            DevicePlcVO.OperationResult result;
+            switch (operation) {
+                case "feedGlass":
+                    result = handleFeedGlass(deviceConfig, params, logicParams);
+                    break;
+                case "triggerRequest":
+                    result = handleTriggerRequest(deviceConfig, params, logicParams);
+                    break;
+                case "triggerReport":
+                    result = handleTriggerReport(deviceConfig, params, logicParams);
+                    break;
+                case "reset":
+                    result = handleReset(deviceConfig, params, logicParams);
+                    break;
+                case "clearGlass":
+                case "clearPlc":
+                case "clear":
+                    result = handleClearGlass(deviceConfig, params, logicParams);
+                    break;
+                case "checkStateAndCoordinate":
+                    result = handleCheckStateAndCoordinate(deviceConfig, params, logicParams);
+                    break;
+                case "startIdleMonitor":
+                    result = handleStartIdleMonitor(deviceConfig, params, logicParams);
+                    break;
+                case "stopIdleMonitor":
+                    result = handleStopIdleMonitor(deviceConfig);
+                    break;
+                case "checkMesTask":
+                    result = handleCheckMesTask(deviceConfig, params, logicParams);
+                    break;
+                case "checkMesOutboundTask":
+                    // 鍑虹墖浠诲姟涔熶娇鐢ㄥ悓涓�濂楀崗璁紝閫氳繃handleCheckMesTask澶勭悊
+                    result = handleCheckMesTask(deviceConfig, params, logicParams);
+                    break;
+                case "startTaskMonitor":
+                    result = handleStartTaskMonitor(deviceConfig, params, logicParams);
+                    break;
+                case "stopTaskMonitor":
+                    result = handleStopTaskMonitor(deviceConfig);
+                    break;
+                default:
+                    log.warn("涓嶆敮鎸佺殑鎿嶄綔绫诲瀷: {}", operation);
+                    result = DevicePlcVO.OperationResult.builder()
+                            .success(false)
+                            .message("涓嶆敮鎸佺殑鎿嶄綔: " + operation)
+                            .build();
+            }
+            
+            return result;
+            
+        } catch (Exception e) {
+            log.error("鎵ц澶ц溅璁惧鎿嶄綔寮傚父: deviceId={}, operation={}", deviceId, operation, e);
+            // 鍙戠敓寮傚父鏃讹紝灏嗙姸鎬佽缃负閿欒
+            if (needsStateManagement(operation)) {
+                statusManager.updateVehicleStatus(deviceId, VehicleState.ERROR);
+            }
+            throw e;
+        } finally {
+            // 4. 鎵ц瀹屾垚鍚庢仮澶嶄负绌洪棽鐘舵�侊紙瀵逛簬闇�瑕佺姸鎬佺鐞嗙殑鎿嶄綔锛�
+            if (needsStateManagement(operation)) {
+                // 娉ㄦ剰锛氳繖閲屼笉绔嬪嵆璁剧疆涓篒DLE锛屽洜涓哄疄闄呮墽琛屽彲鑳介渶瑕佹椂闂�
+                // 鐪熸鐨勭姸鎬佹洿鏂板簲璇ュ湪浠诲姟瀹屾垚鍚庨�氳繃鍥炶皟鎴栫姸鎬佹煡璇㈡潵鏇存柊
+                // 杩欓噷鍏堜繚鎸丒XECUTING鐘舵�侊紝绛夊緟澶栭儴纭瀹屾垚鍚庡啀鏇存柊
+                log.debug("鎿嶄綔鎵ц瀹屾垚锛屼繚鎸佹墽琛屼腑鐘舵�侊紝绛夊緟澶栭儴纭: deviceId={}", deviceId);
+            }
+        }
+    }
+
+    /**
+     * 鍒ゆ柇鎿嶄綔鏄惁闇�瑕佺姸鎬佹鏌�
+     */
+    private boolean needsStateCheck(String operation) {
+        // 鎵�鏈夋搷浣滈兘闇�瑕佹鏌ョ姸鎬侊紝闄や簡鏌ヨ绫绘搷浣�
+        return !"query".equals(operation) && !"status".equals(operation);
+    }
+
+    /**
+     * 鍒ゆ柇鎿嶄綔鏄惁闇�瑕佺姸鎬佺鐞�
+     */
+    private boolean needsStateManagement(String operation) {
+        // feedGlass 闇�瑕佺姸鎬佺鐞嗭紝鍏朵粬鎿嶄綔鏍规嵁瀹為檯鎯呭喌
+        return "feedGlass".equals(operation);
+    }
+
+    /**
+     * 鍒涘缓杞﹁締浠诲姟淇℃伅
+     */
+    private VehicleTask createVehicleTask(
+            DeviceConfig deviceConfig,
+            String operation,
+            Map<String, Object> params,
+            Map<String, Object> logicParams) {
+        
+        VehicleTask task = new VehicleTask();
+        task.setTaskId(generateTaskId(deviceConfig.getDeviceId()));
+        task.setTaskName("澶ц溅璁惧-" + operation);
+        task.setOperation(operation);
+        
+        // 浠庡弬鏁颁腑鎻愬彇浣嶇疆淇℃伅
+        String positionCode = (String) params.get("positionCode");
+        Integer positionValue = (Integer) params.get("positionValue");
+        
+        if (positionCode != null || positionValue != null) {
+            VehiclePosition position = new VehiclePosition(positionCode, positionValue);
+            task.getPlannedPath().setStartPosition(position);
+            task.getPlannedPath().setEndPosition(position);
+        }
+        
+        // 浠庨厤缃腑鑾峰彇閫熷害锛堝鏋滄湁锛�
+        Double speed = getLogicParam(logicParams, "vehicleSpeed", null);
+        if (speed != null) {
+            task.setSpeed(speed);
+            task.calculateEstimatedEndTime();
+        }
+        
+        task.setParameters(new HashMap<>(params));
+        
+        return task;
+    }
+
+    /**
+     * 鐢熸垚浠诲姟ID
+     */
+    private String generateTaskId(String deviceId) {
+        return "TASK_" + deviceId + "_" + System.currentTimeMillis();
+    }
+
+    /**
+     * 澶勭悊鐜荤拑涓婃枡鎿嶄綔
+     */
+    private DevicePlcVO.OperationResult handleFeedGlass(
+            DeviceConfig deviceConfig,
+            Map<String, Object> params,
+            Map<String, Object> logicParams) {
+
+        // 浠庨�昏緫鍙傛暟涓幏鍙栭厤缃紙浠� extraParams.deviceLogic 璇诲彇锛�
+        Integer vehicleCapacity = getLogicParam(logicParams, "vehicleCapacity", 6000);
+        Integer glassIntervalMs = getLogicParam(logicParams, "glassIntervalMs", 1000);
+        Boolean autoFeed = getLogicParam(logicParams, "autoFeed", true);
+        Integer maxRetryCount = getLogicParam(logicParams, "maxRetryCount", 5);
+
+        // 浠庤繍琛屾椂鍙傛暟涓幏鍙栨暟鎹紙浠庢帴鍙h皟鐢ㄦ椂浼犲叆锛�
+        List<GlassInfo> glassInfos = extractGlassInfos(params);
+        if (glassInfos.isEmpty()) {
+            return DevicePlcVO.OperationResult.builder()
+                    .success(false)
+                    .message("鏈彁渚涙湁鏁堢殑鐜荤拑淇℃伅")
+                    .build();
+        }
+
+        String positionCode = (String) params.get("positionCode");
+        Integer positionValue = (Integer) params.get("positionValue");
+        Boolean triggerRequest = (Boolean) params.getOrDefault("triggerRequest", autoFeed);
+
+        List<GlassInfo> plannedGlasses = planGlassLoading(glassInfos, vehicleCapacity,
+                getLogicParam(logicParams, "defaultGlassLength", 2000));
+        if (plannedGlasses.isEmpty()) {
+            return DevicePlcVO.OperationResult.builder()
+                    .success(false)
+                    .message("褰撳墠鐜荤拑灏哄瓒呭嚭杞﹁締瀹归噺锛屾棤娉曡杞�")
+                    .build();
+        }
+
+        // 鏋勫缓鍐欏叆鏁版嵁
+        Map<String, Object> payload = new HashMap<>();
+        
+        // 鍐欏叆鐜荤拑ID
+        int plcSlots = Math.min(plannedGlasses.size(), 6);
+        for (int i = 0; i < plcSlots; i++) {
+            String fieldName = "plcGlassId" + (i + 1);
+            payload.put(fieldName, plannedGlasses.get(i).getGlassId());
+        }
+        payload.put("plcGlassCount", plcSlots);
+
+        // 鍐欏叆浣嶇疆淇℃伅
+        if (positionValue != null) {
+            payload.put("inPosition", positionValue);
+        } else if (positionCode != null) {
+            // 浠庝綅缃槧灏勪腑鑾峰彇浣嶇疆鍊�
+            @SuppressWarnings("unchecked")
+            Map<String, Integer> positionMapping = getLogicParam(logicParams, "positionMapping", new HashMap<>());
+            Integer mappedValue = positionMapping.get(positionCode);
+            if (mappedValue != null) {
+                payload.put("inPosition", mappedValue);
+            }
+        }
+
+        // 鑷姩瑙﹀彂璇锋眰瀛�
+        if (triggerRequest != null && triggerRequest) {
+            payload.put("plcRequest", 1);
+        }
+
+        String operationName = "澶ц溅璁惧-鐜荤拑涓婃枡";
+        if (positionCode != null) {
+            operationName += "(" + positionCode + ")";
+        }
+
+        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();
+            }
+        }
+
+        DevicePlcVO.OperationResult result = devicePlcOperationService.writeFields(
+            deviceConfig.getId(), payload, operationName);
+        
+        // 濡傛灉鎵ц鎴愬姛锛屾洿鏂颁綅缃俊鎭埌鐘舵�侊紝骞跺惎鍔ㄧ姸鎬佺洃鎺�
+        if (Boolean.TRUE.equals(result.getSuccess())) {
+            VehicleStatus status = statusManager.getOrCreateVehicleStatus(
+                deviceConfig.getDeviceId(), deviceConfig.getDeviceName());
+            if (positionCode != null || positionValue != null) {
+                VehiclePosition position = new VehiclePosition(positionCode, positionValue);
+                status.setCurrentPosition(position);
+            }
+            
+            // 鍚姩鑷姩鐘舵�佺洃鎺э紝褰� state=1 鏃惰嚜鍔ㄥ崗璋冨崸杞珛璁惧
+            startStateMonitoring(deviceConfig, logicParams);
+        }
+        
+        return result;
+    }
+
+    /**
+     * 澶勭悊瑙﹀彂璇锋眰鎿嶄綔
+     */
+    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());
+        
+        DevicePlcVO.OperationResult result = devicePlcOperationService.writeFields(
+                deviceConfig.getId(),
+                payload,
+                "澶ц溅璁惧-閲嶇疆"
+        );
+        
+        // 閲嶇疆鏃讹紝娓呴櫎浠诲姟骞舵仮澶嶄负绌洪棽鐘舵�侊紝鍋滄鐩戞帶
+        if (Boolean.TRUE.equals(result.getSuccess())) {
+            statusManager.clearVehicleTask(deviceConfig.getDeviceId());
+            statusManager.updateVehicleStatus(deviceConfig.getDeviceId(), VehicleState.IDLE);
+            stopStateMonitoring(deviceConfig.getDeviceId());
+        }
+        
+        return result;
+    }
+
+    /**
+     * 娓呯┖PLC涓殑鐜荤拑鏁版嵁
+     */
+    private DevicePlcVO.OperationResult handleClearGlass(
+            DeviceConfig deviceConfig,
+            Map<String, Object> params,
+            Map<String, Object> logicParams) {
+
+        Map<String, Object> payload = new HashMap<>();
+
+        int slotCount = getLogicParam(logicParams, "glassSlotCount", 6);
+        if (slotCount <= 0) {
+            slotCount = 6;
+        }
+
+        List<String> slotFields = resolveGlassSlotFields(logicParams, slotCount);
+        for (String field : slotFields) {
+            payload.put(field, "");
+        }
+
+        payload.put("plcGlassCount", 0);
+        payload.put("plcRequest", 0);
+        payload.put("plcReport", 0);
+
+        if (params != null && params.containsKey("positionValue")) {
+            payload.put("inPosition", params.get("positionValue"));
+        } else if (params != null && Boolean.TRUE.equals(params.get("clearPosition"))) {
+            payload.put("inPosition", 0);
+        }
+
+        log.info("娓呯┖澶ц溅璁惧PLC鐜荤拑鏁版嵁: deviceId={}, clearedSlots={}", deviceConfig.getId(), slotFields.size());
+
+        DevicePlcVO.OperationResult result = devicePlcOperationService.writeFields(
+                deviceConfig.getId(),
+                payload,
+                "澶ц溅璁惧-娓呯┖鐜荤拑鏁版嵁"
+        );
+        
+        // 娓呯┖鍚庯紝鎭㈠涓虹┖闂茬姸鎬侊紝鍋滄鐩戞帶
+        if (Boolean.TRUE.equals(result.getSuccess())) {
+            statusManager.clearVehicleTask(deviceConfig.getDeviceId());
+            statusManager.updateVehicleStatus(deviceConfig.getDeviceId(), VehicleState.IDLE);
+            stopStateMonitoring(deviceConfig.getDeviceId());
+        }
+        
+        return result;
+    }
+
+    private List<String> resolveGlassSlotFields(Map<String, Object> logicParams, int fallbackCount) {
+        List<String> fields = new ArrayList<>();
+        if (logicParams != null) {
+            Object slotFieldConfig = logicParams.get("glassSlotFields");
+            if (slotFieldConfig instanceof List) {
+                List<?> configured = (List<?>) slotFieldConfig;
+                for (Object item : configured) {
+                    if (item != null) {
+                        String fieldName = String.valueOf(item).trim();
+                        if (!fieldName.isEmpty()) {
+                            fields.add(fieldName);
+                        }
+                    }
+                }
+            }
+        }
+
+        if (fields.isEmpty()) {
+            for (int i = 1; i <= fallbackCount; i++) {
+                fields.add("plcGlassId" + i);
+            }
+        }
+        return fields;
+    }
+
+    @Override
+    public String validateLogicParams(DeviceConfig deviceConfig) {
+        Map<String, Object> logicParams = parseLogicParams(deviceConfig);
+        
+        // 楠岃瘉蹇呭~鍙傛暟
+        Integer vehicleCapacity = getLogicParam(logicParams, "vehicleCapacity", null);
+        if (vehicleCapacity == null || vehicleCapacity <= 0) {
+            return "杞﹁締瀹归噺(vehicleCapacity)蹇呴』澶т簬0";
+        }
+
+        Integer glassIntervalMs = getLogicParam(logicParams, "glassIntervalMs", null);
+        if (glassIntervalMs != null && glassIntervalMs < 0) {
+            return "鐜荤拑闂撮殧鏃堕棿(glassIntervalMs)涓嶈兘涓鸿礋鏁�";
+        }
+
+        return null; // 楠岃瘉閫氳繃
+    }
+
+    @Override
+    public String getDefaultLogicParams() {
+        Map<String, Object> defaultParams = new HashMap<>();
+        defaultParams.put("vehicleCapacity", 6000);
+        defaultParams.put("glassIntervalMs", 1000);
+        defaultParams.put("autoFeed", true);
+        defaultParams.put("maxRetryCount", 5);
+        defaultParams.put("defaultGlassLength", 2000);
+        
+        // MES浠诲姟鐩稿叧閰嶇疆
+        defaultParams.put("vehicleSpeed", 1.0); // 杞﹁締閫熷害锛堟牸/绉掞紝grid/s锛夛紝榛樿1鏍�/绉�
+        defaultParams.put("minRange", 1); // 鏈�灏忚繍鍔ㄨ窛绂伙紙鏍煎瓙锛�
+        defaultParams.put("maxRange", 100); // 鏈�澶ц繍鍔ㄨ窛绂伙紙鏍煎瓙锛夛紝渚嬪100鏍�
+        defaultParams.put("homePosition", 0); // 鍒濆浣嶇疆锛堟牸瀛愶級
+        defaultParams.put("idleMonitorIntervalMs", 2000); // 绌洪棽鐩戞帶闂撮殧锛堟绉掞級
+        defaultParams.put("taskMonitorIntervalMs", 1000); // 浠诲姟鐩戞帶闂撮殧锛堟绉掞級
+        defaultParams.put("mesConfirmTimeoutMs", 30000); // MES纭瓒呮椂锛堟绉掞級
+        
+        // 鍑虹墖浠诲姟鐩稿叧閰嶇疆
+        // outboundSlotRanges: 鍑虹墖浠诲姟鐨剆tartSlot鑼冨洿锛屼緥濡俒1, 101]琛ㄧず鏍煎瓙1~101閮芥槸鍑虹墖浠诲姟
+        // 濡傛灉涓嶉厤缃紝鍒欓�氳繃鍒ゆ柇startSlot鏄惁鍦╬ositionMapping涓潵鍖哄垎杩涚墖/鍑虹墖
+        List<Integer> outboundSlotRanges = new ArrayList<>();
+        outboundSlotRanges.add(1);   // 鏈�灏忔牸瀛愮紪鍙�
+        outboundSlotRanges.add(101); // 鏈�澶ф牸瀛愮紪鍙�
+        defaultParams.put("outboundSlotRanges", outboundSlotRanges);
+        
+        // gridPositionMapping: 鏍煎瓙缂栧彿鍒颁綅缃殑鏄犲皠琛紙鍙�夛級
+        // 濡傛灉涓嶉厤缃紝鍒欐牸瀛愮紪鍙风洿鎺ヤ綔涓轰綅缃��
+        Map<String, Integer> gridPositionMapping = new HashMap<>();
+        defaultParams.put("gridPositionMapping", gridPositionMapping);
+        
+        Map<String, Integer> positionMapping = new HashMap<>();
+        positionMapping.put("POS1", 1);
+        positionMapping.put("POS2", 2);
+        defaultParams.put("positionMapping", positionMapping);
+        
+        try {
+            return objectMapper.writeValueAsString(defaultParams);
+        } catch (Exception e) {
+            log.error("鐢熸垚榛樿閫昏緫鍙傛暟澶辫触", e);
+            return "{}";
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private List<GlassInfo> extractGlassInfos(Map<String, Object> params) {
+        List<GlassInfo> result = new ArrayList<>();
+        Object rawGlassInfos = params.get("glassInfos");
+        if (rawGlassInfos instanceof List) {
+            List<?> list = (List<?>) rawGlassInfos;
+            for (Object item : list) {
+                GlassInfo info = convertToGlassInfo(item);
+                if (info != null) {
+                    result.add(info);
+                }
+            }
+        }
+
+        if (result.isEmpty()) {
+            List<String> glassIds = (List<String>) params.get("glassIds");
+            if (glassIds != null && !glassIds.isEmpty()) {
+                // 浠庢暟鎹簱鏌ヨ鐜荤拑灏哄
+                Map<String, Integer> lengthMap = glassInfoService.getGlassLengthMap(glassIds);
+                for (String glassId : glassIds) {
+                    Integer length = lengthMap.get(glassId);
+                    result.add(new GlassInfo(glassId, length));
+                }
+                log.debug("浠庢暟鎹簱鏌ヨ鐜荤拑灏哄: glassIds={}, lengthMap={}", glassIds, lengthMap);
+            }
+        }
+        return result;
+    }
+
+    private GlassInfo convertToGlassInfo(Object source) {
+        if (source instanceof GlassInfo) {
+            return (GlassInfo) source;
+        }
+        if (source instanceof Map) {
+            Map<?, ?> map = (Map<?, ?>) source;
+            Object id = map.get("glassId");
+            if (id == null) {
+                id = map.get("id");
+            }
+            if (id == null) {
+                return null;
+            }
+            Integer length = parseLength(map.get("length"));
+            if (length == null) {
+                length = parseLength(map.get("size"));
+            }
+            return new GlassInfo(String.valueOf(id), length);
+        }
+        if (source instanceof String) {
+            return new GlassInfo((String) source, null);
+        }
+        return null;
+    }
+
+    private Integer parseLength(Object value) {
+        if (value instanceof Number) {
+            return ((Number) value).intValue();
+        }
+        if (value instanceof String) {
+            try {
+                return Integer.parseInt((String) value);
+            } catch (NumberFormatException ignored) {
+            }
+        }
+        return null;
+    }
+
+    private List<GlassInfo> planGlassLoading(List<GlassInfo> source,
+                                             int vehicleCapacity,
+                                             Integer defaultGlassLength) {
+        List<GlassInfo> planned = new ArrayList<>();
+        int usedLength = 0;
+        int capacity = Math.max(vehicleCapacity, 1);
+        int fallbackLength = defaultGlassLength != null && defaultGlassLength > 0 ? defaultGlassLength : 2000;
+
+        for (GlassInfo info : source) {
+            int length = info.getLength() != null && info.getLength() > 0 ? info.getLength() : fallbackLength;
+            if (planned.isEmpty()) {
+                planned.add(info.withLength(length));
+                usedLength = length;
+                continue;
+            }
+            if (usedLength + length <= capacity) {
+                planned.add(info.withLength(length));
+                usedLength += length;
+            } else {
+                break;
+            }
+        }
+        return planned;
+    }
+
+    /**
+     * 鍚姩鐘舵�佺洃鎺�
+     * 瀹氭湡妫�鏌ュぇ杞︾殑 state1~6锛屽綋妫�娴嬪埌 state=1 鏃惰嚜鍔ㄥ崗璋冨崸杞珛璁惧
+     */
+    private void startStateMonitoring(DeviceConfig deviceConfig, Map<String, Object> logicParams) {
+        String deviceId = deviceConfig.getDeviceId();
+        
+        // 濡傛灉宸茬粡鍦ㄧ洃鎺э紝鍏堝仠姝㈡棫鐨勭洃鎺т换鍔�
+        stopStateMonitoring(deviceId);
+        
+        // 鑾峰彇鐩戞帶閰嶇疆
+        Integer monitorIntervalMs = getLogicParam(logicParams, "stateMonitorIntervalMs", 1000);
+        Integer monitorTimeoutMs = getLogicParam(logicParams, "stateMonitorTimeoutMs", 300000);
+        if (monitorIntervalMs == null || monitorIntervalMs <= 0) {
+            monitorIntervalMs = 1000;
+        }
+        if (monitorTimeoutMs == null || monitorTimeoutMs <= 0) {
+            monitorTimeoutMs = 300000;
+        }
+        final int finalMonitorIntervalMs = monitorIntervalMs;
+        final int finalMonitorTimeoutMs = monitorTimeoutMs;
+        
+        // 鍒濆鍖栧凡鍗忚皟鐘舵�佽褰�
+        coordinatedStates.put(deviceId, new CopyOnWriteArrayList<>());
+        
+        // 璁板綍鐩戞帶寮�濮嬫椂闂�
+        final long startTime = System.currentTimeMillis();
+        
+        // 鍚姩鐩戞帶浠诲姟
+        ScheduledFuture<?> future = stateMonitorExecutor.scheduleWithFixedDelay(() -> {
+            try {
+                // 妫�鏌ヨ秴鏃�
+                if (System.currentTimeMillis() - startTime > finalMonitorTimeoutMs) {
+                    log.info("澶ц溅鐘舵�佺洃鎺ц秴鏃讹紝鍋滄鐩戞帶: deviceId={}, timeout={}ms", deviceId, finalMonitorTimeoutMs);
+                    stopStateMonitoring(deviceId);
+                    return;
+                }
+                
+                // 妫�鏌ヨ溅杈嗘槸鍚﹁繕鍦ㄦ墽琛屼换鍔�
+                VehicleStatus status = statusManager.getVehicleStatus(deviceId);
+                if (status == null || status.getState() != VehicleState.EXECUTING) {
+                    log.debug("澶ц溅鐘舵�佸凡鏀瑰彉锛屽仠姝㈢洃鎺�: deviceId={}, state={}", 
+                            deviceId, status != null ? status.getState() : "null");
+                    stopStateMonitoring(deviceId);
+                    return;
+                }
+                
+                // 鎵ц鐘舵�佹鏌ュ拰鍗忚皟
+                checkAndCoordinateState(deviceConfig);
+                
+            } catch (Exception e) {
+                log.error("澶ц溅鐘舵�佺洃鎺у紓甯�: deviceId={}", deviceId, e);
+            }
+        }, finalMonitorIntervalMs, finalMonitorIntervalMs, TimeUnit.MILLISECONDS);
+        
+        monitoringTasks.put(deviceId, future);
+        log.info("宸插惎鍔ㄥぇ杞︾姸鎬佺洃鎺�: deviceId={}, interval={}ms, timeout={}ms", 
+                deviceId, finalMonitorIntervalMs, finalMonitorTimeoutMs);
+    }
+    
+    /**
+     * 鍋滄鐘舵�佺洃鎺�
+     */
+    private void stopStateMonitoring(String deviceId) {
+        ScheduledFuture<?> future = monitoringTasks.remove(deviceId);
+        if (future != null && !future.isCancelled()) {
+            future.cancel(false);
+            log.debug("宸插仠姝㈠ぇ杞︾姸鎬佺洃鎺�: deviceId={}", deviceId);
+        }
+        coordinatedStates.remove(deviceId);
+    }
+    
+    /**
+     * 妫�鏌ュぇ杞︾姸鎬佸苟鍗忚皟鍗ц浆绔嬭澶囷紙鍐呴儴鏂规硶锛岀敱鐩戞帶绾跨▼璋冪敤锛�
+     */
+    private void checkAndCoordinateState(DeviceConfig deviceConfig) {
+        String deviceId = deviceConfig.getDeviceId();
+        List<String> alreadyCoordinated = coordinatedStates.get(deviceId);
+        if (alreadyCoordinated == null) {
+            alreadyCoordinated = new CopyOnWriteArrayList<>();
+            coordinatedStates.put(deviceId, alreadyCoordinated);
+        }
+        
+        try {
+            // 璇诲彇 state1~6 瀛楁
+            List<String> stateFields = Arrays.asList("state1", "state2", "state3", "state4", "state5", "state6");
+            Map<String, Object> stateValues = new HashMap<>();
+            
+            // 浠� PLC 璇诲彇鐘舵�佸瓧娈�
+            DevicePlcVO.StatusInfo statusInfo = devicePlcOperationService.readStatus(deviceConfig.getId());
+            if (statusInfo == null || statusInfo.getFieldValues() == null) {
+                return;
+            }
+            
+            for (String field : stateFields) {
+                Object value = statusInfo.getFieldValues().get(field);
+                if (value != null) {
+                    stateValues.put(field, value);
+                }
+            }
+            
+            // 妫�鏌ユ槸鍚︽湁浠讳綍涓�涓� state 涓� 1锛屼笖灏氭湭鍗忚皟杩�
+            List<String> newStateOneFields = new ArrayList<>();
+            for (String field : stateFields) {
+                Integer stateValue = parseInteger(stateValues.get(field));
+                if (stateValue != null && stateValue == 1 && !alreadyCoordinated.contains(field)) {
+                    newStateOneFields.add(field);
+                }
+            }
+            
+            if (newStateOneFields.isEmpty()) {
+                return; // 娌℃湁鏂扮殑 state=1锛屾棤闇�鍗忚皟
+            }
+            
+            log.info("妫�娴嬪埌澶ц溅鏂扮殑 state=1: deviceId={}, stateFields={}", deviceId, newStateOneFields);
+            
+            // 鏌ユ壘鍚岀粍鐨勫崸杞珛璁惧
+            List<DeviceConfig> transferDevices = findTransferDevicesInSameGroup(deviceConfig);
+            if (transferDevices.isEmpty()) {
+                log.warn("鏈壘鍒板悓缁勭殑鍗ц浆绔嬭澶�: deviceId={}", deviceId);
+                // 鍗充娇鎵句笉鍒拌澶囷紝涔熸爣璁颁负宸插崗璋冿紝閬垮厤閲嶅妫�鏌�
+                alreadyCoordinated.addAll(newStateOneFields);
+                return;
+            }
+            
+            // 灏嗘瘡涓崸杞珛璁惧鐨� plcRequest 缃� 0
+            boolean allSuccess = true;
+            for (DeviceConfig transferDevice : transferDevices) {
+                Map<String, Object> payload = new HashMap<>();
+                payload.put("plcRequest", 0);
+                
+                DevicePlcVO.OperationResult result = devicePlcOperationService.writeFields(
+                        transferDevice.getId(),
+                        payload,
+                        "澶ц溅鑷姩鍗忚皟-娓呯┖鍗ц浆绔嬭姹�"
+                );
+                
+                if (Boolean.TRUE.equals(result.getSuccess())) {
+                    log.info("宸茶嚜鍔ㄦ竻绌哄崸杞珛璁惧 plcRequest: vehicleDeviceId={}, transferDeviceId={}, transferDeviceName={}, stateFields={}", 
+                            deviceId, transferDevice.getId(), transferDevice.getDeviceName(), newStateOneFields);
+                } else {
+                    log.warn("鑷姩娓呯┖鍗ц浆绔嬭澶� plcRequest 澶辫触: vehicleDeviceId={}, transferDeviceId={}, message={}", 
+                            deviceId, transferDevice.getId(), result.getMessage());
+                    allSuccess = false;
+                }
+            }
+            
+            // 鏍囪涓哄凡鍗忚皟锛堟棤璁烘垚鍔熶笌鍚︼紝閬垮厤閲嶅鍗忚皟锛�
+            if (allSuccess) {
+                alreadyCoordinated.addAll(newStateOneFields);
+                log.info("澶ц溅鐘舵�佸崗璋冨畬鎴�: deviceId={}, coordinatedStateFields={}", deviceId, newStateOneFields);
+            }
+            
+        } catch (Exception e) {
+            log.error("妫�鏌ュぇ杞︾姸鎬佸苟鍗忚皟鍗ц浆绔嬭澶囧紓甯�: deviceId={}", deviceId, e);
+        }
+    }
+
+    /**
+     * 妫�鏌ュぇ杞︾姸鎬佸苟鍗忚皟鍗ц浆绔嬭澶囷紙鎵嬪姩璋冪敤鎺ュ彛锛�
+     * 褰� state1~6 涓换浣曚竴涓彉涓� 1锛堜笂杞﹀畬鎴愶級鏃讹紝灏嗗悓缁勫崸杞珛璁惧鐨� plcRequest 缃� 0
+     */
+    private DevicePlcVO.OperationResult handleCheckStateAndCoordinate(
+            DeviceConfig deviceConfig,
+            Map<String, Object> params,
+            Map<String, Object> logicParams) {
+        
+        try {
+            // 璇诲彇 state1~6 瀛楁
+            List<String> stateFields = Arrays.asList("state1", "state2", "state3", "state4", "state5", "state6");
+            Map<String, Object> stateValues = new HashMap<>();
+            
+            // 浠� PLC 璇诲彇鐘舵�佸瓧娈�
+            DevicePlcVO.StatusInfo statusInfo = devicePlcOperationService.readStatus(deviceConfig.getId());
+            if (statusInfo != null && statusInfo.getFieldValues() != null) {
+                for (String field : stateFields) {
+                    Object value = statusInfo.getFieldValues().get(field);
+                    if (value != null) {
+                        stateValues.put(field, value);
+                    }
+                }
+            }
+            
+            // 妫�鏌ユ槸鍚︽湁浠讳綍涓�涓� state 涓� 1
+            boolean hasStateOne = false;
+            List<String> stateOneFields = new ArrayList<>();
+            for (String field : stateFields) {
+                Integer stateValue = parseInteger(stateValues.get(field));
+                if (stateValue != null && stateValue == 1) {
+                    hasStateOne = true;
+                    stateOneFields.add(field);
+                }
+            }
+            
+            if (!hasStateOne) {
+                return DevicePlcVO.OperationResult.builder()
+                        .success(true)
+                        .message("褰撳墠鏃� state=1 鐨勭姸鎬侊紝鏃犻渶鍗忚皟")
+                        .build();
+            }
+            
+            log.info("妫�娴嬪埌澶ц溅 state=1: deviceId={}, stateFields={}", 
+                    deviceConfig.getId(), stateOneFields);
+            
+            // 鏌ユ壘鍚岀粍鐨勫崸杞珛璁惧
+            List<DeviceConfig> transferDevices = findTransferDevicesInSameGroup(deviceConfig);
+            if (transferDevices.isEmpty()) {
+                log.warn("鏈壘鍒板悓缁勭殑鍗ц浆绔嬭澶�: deviceId={}", deviceConfig.getId());
+                return DevicePlcVO.OperationResult.builder()
+                        .success(true)
+                        .message("鏈壘鍒板悓缁勭殑鍗ц浆绔嬭澶囷紝璺宠繃鍗忚皟")
+                        .build();
+            }
+            
+            // 灏嗘瘡涓崸杞珛璁惧鐨� plcRequest 缃� 0
+            List<DevicePlcVO.OperationResult> results = new ArrayList<>();
+            for (DeviceConfig transferDevice : transferDevices) {
+                Map<String, Object> payload = new HashMap<>();
+                payload.put("plcRequest", 0);
+                
+                DevicePlcVO.OperationResult result = devicePlcOperationService.writeFields(
+                        transferDevice.getId(),
+                        payload,
+                        "澶ц溅鍗忚皟-娓呯┖鍗ц浆绔嬭姹�"
+                );
+                results.add(result);
+                
+                if (Boolean.TRUE.equals(result.getSuccess())) {
+                    log.info("宸叉竻绌哄崸杞珛璁惧 plcRequest: transferDeviceId={}, transferDeviceName={}", 
+                            transferDevice.getId(), transferDevice.getDeviceName());
+                } else {
+                    log.warn("娓呯┖鍗ц浆绔嬭澶� plcRequest 澶辫触: transferDeviceId={}, message={}", 
+                            transferDevice.getId(), result.getMessage());
+                }
+            }
+            
+            boolean allSuccess = results.stream()
+                    .allMatch(r -> Boolean.TRUE.equals(r.getSuccess()));
+            
+            return DevicePlcVO.OperationResult.builder()
+                    .success(allSuccess)
+                    .message(String.format("宸插崗璋� %d 涓崸杞珛璁惧锛屾垚鍔�: %d", 
+                            transferDevices.size(),
+                            results.stream().mapToInt(r -> Boolean.TRUE.equals(r.getSuccess()) ? 1 : 0).sum()))
+                    .build();
+                    
+        } catch (Exception e) {
+            log.error("妫�鏌ュぇ杞︾姸鎬佸苟鍗忚皟鍗ц浆绔嬭澶囧紓甯�: deviceId={}", deviceConfig.getId(), e);
+            return DevicePlcVO.OperationResult.builder()
+                    .success(false)
+                    .message("鍗忚皟寮傚父: " + e.getMessage())
+                    .build();
+        }
+    }
+
+    /**
+     * 鏌ユ壘鍚岀粍鍐呯殑鍗ц浆绔嬭澶�
+     */
+    private List<DeviceConfig> findTransferDevicesInSameGroup(DeviceConfig vehicleDevice) {
+        List<DeviceConfig> transferDevices = new ArrayList<>();
+        
+        if (deviceGroupRelationService == null || deviceConfigService == null) {
+            log.warn("DeviceGroupRelationService 鎴� DeviceConfigService 鏈敞鍏ワ紝鏃犳硶鏌ユ壘鍚岀粍璁惧");
+            return transferDevices;
+        }
+        
+        try {
+            // 鑾峰彇澶ц溅鎵�灞炵殑璁惧缁�
+            List<DeviceGroupVO.GroupInfo> groups = deviceGroupRelationService.getDeviceGroups(vehicleDevice.getId());
+            if (groups.isEmpty()) {
+                log.debug("澶ц溅璁惧鏈姞鍏ヤ换浣曡澶囩粍: deviceId={}", vehicleDevice.getId());
+                return transferDevices;
+            }
+            
+            // 閬嶅巻鎵�鏈夎澶囩粍锛屾煡鎵惧崸杞珛璁惧
+            for (DeviceGroupVO.GroupInfo group : groups) {
+                List<DeviceGroupVO.DeviceInfo> groupDevices = deviceGroupRelationService.getGroupDevices(group.getId());
+                for (DeviceGroupVO.DeviceInfo deviceInfo : groupDevices) {
+                    // 妫�鏌ユ槸鍚︿负鍗ц浆绔嬭澶�
+                    if (DeviceConfig.DeviceType.WORKSTATION_TRANSFER.equals(deviceInfo.getDeviceType())) {
+                        DeviceConfig transferDevice = deviceConfigService.getDeviceById(deviceInfo.getId());
+                        if (transferDevice != null) {
+                            transferDevices.add(transferDevice);
+                        }
+                    }
+                }
+            }
+            
+            log.debug("鎵惧埌鍚岀粍鍗ц浆绔嬭澶�: vehicleDeviceId={}, transferDeviceCount={}", 
+                    vehicleDevice.getId(), transferDevices.size());
+            
+        } catch (Exception e) {
+            log.error("鏌ユ壘鍚岀粍鍗ц浆绔嬭澶囧紓甯�: vehicleDeviceId={}", vehicleDevice.getId(), e);
+        }
+        
+        return transferDevices;
+    }
+
+    private Integer parseInteger(Object value) {
+        if (value instanceof Number) {
+            return ((Number) value).intValue();
+        }
+        if (value == null) {
+            return null;
+        }
+        try {
+            return Integer.parseInt(String.valueOf(value));
+        } catch (NumberFormatException e) {
+            return null;
+        }
+    }
+
+    private String parseString(Object value) {
+        return value == null ? null : String.valueOf(value).trim();
+    }
+
+    private static class GlassInfo {
+        private final String glassId;
+        private final Integer length;
+
+        GlassInfo(String glassId, Integer length) {
+            this.glassId = glassId;
+            this.length = length;
+        }
+
+        public String getGlassId() {
+            return glassId;
+        }
+
+        public Integer getLength() {
+            return length;
+        }
+
+        public GlassInfo withLength(Integer newLength) {
+            return new GlassInfo(this.glassId, newLength);
+        }
+
+        @Override
+        public String toString() {
+            return glassId;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(glassId, length);
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) return true;
+            if (obj == null || getClass() != obj.getClass()) return false;
+            GlassInfo other = (GlassInfo) obj;
+            return Objects.equals(glassId, other.glassId) && Objects.equals(length, other.length);
+        }
+    }
+    
+    /**
+     * 鍚姩绌洪棽鐩戞帶锛堟病鏈変换鍔℃椂锛宲lcRequest涓�鐩翠繚鎸佷负1锛�
+     */
+    private DevicePlcVO.OperationResult handleStartIdleMonitor(
+            DeviceConfig deviceConfig,
+            Map<String, Object> params,
+            Map<String, Object> logicParams) {
+        
+        String deviceId = deviceConfig.getDeviceId();
+        
+        // 鍋滄鏃х殑鐩戞帶浠诲姟
+        handleStopIdleMonitor(deviceConfig);
+        
+        // 鑾峰彇鐩戞帶闂撮殧
+        Integer monitorIntervalMs = getLogicParam(logicParams, "idleMonitorIntervalMs", 2000); // 榛樿2绉�
+        
+        // 鍚姩鐩戞帶浠诲姟
+        ScheduledFuture<?> future = idleMonitorExecutor.scheduleWithFixedDelay(() -> {
+            try {
+                // 妫�鏌ユ槸鍚︽湁浠诲姟鍦ㄦ墽琛�
+                VehicleStatus status = statusManager.getVehicleStatus(deviceId);
+                if (status != null && status.getState() == VehicleState.EXECUTING) {
+                    // 鏈変换鍔″湪鎵ц锛屼笉璁剧疆plcRequest
+                    return;
+                }
+                
+                // 妫�鏌ユ槸鍚︽湁寰呭鐞嗙殑杩涚墖鎴栧嚭鐗囦换鍔�
+                if (plcDynamicDataService != null && s7SerializerProvider != null) {
+                    EnhancedS7Serializer serializer = s7SerializerProvider.getSerializer(deviceConfig);
+                    if (serializer != null) {
+                        // 妫�鏌ヨ繘鐗囦换鍔�
+                        Map<String, Object> mesData = plcDynamicDataService.readPlcData(
+                                deviceConfig, MES_FIELDS, serializer);
+                        Integer mesSend = parseInteger(mesData != null ? mesData.get("mesSend") : null);
+                        
+                        // 杩涚墖鍜屽嚭鐗囧叡鐢╩esSend瀛楁锛屽彧闇�妫�鏌ヤ竴娆�
+                        // 濡傛灉鏈夊緟澶勭悊鐨勪换鍔★紝涓嶈缃畃lcRequest锛堢瓑寰呬换鍔″鐞嗭級
+                        if (mesSend != null && mesSend == 1) {
+                            log.debug("澶ц溅绌洪棽鐩戞帶: deviceId={}, 妫�娴嬪埌寰呭鐞嗕换鍔★紙mesSend=1锛夛紝涓嶈缃畃lcRequest", deviceId);
+                            return;
+                        }
+                    }
+                }
+                
+                // 娌℃湁浠诲姟锛屼繚鎸乸lcRequest=1
+                Map<String, Object> payload = new HashMap<>();
+                payload.put("plcRequest", 1);
+                
+                devicePlcOperationService.writeFields(deviceConfig.getId(), payload, "澶ц溅绌洪棽鐩戞帶-淇濇寔璇锋眰");
+                log.debug("澶ц溅绌洪棽鐩戞帶: deviceId={}, 淇濇寔plcRequest=1", deviceId);
+                
+            } catch (Exception e) {
+                log.error("澶ц溅绌洪棽鐩戞帶寮傚父: deviceId={}", deviceId, e);
+            }
+        }, monitorIntervalMs, monitorIntervalMs, TimeUnit.MILLISECONDS);
+        
+        idleMonitoringTasks.put(deviceId, future);
+        log.info("宸插惎鍔ㄥぇ杞︾┖闂茬洃鎺�: deviceId={}, interval={}ms", deviceId, monitorIntervalMs);
+        
+        return DevicePlcVO.OperationResult.builder()
+                .success(true)
+                .message("绌洪棽鐩戞帶宸插惎鍔�")
+                .build();
+    }
+
+    /**
+     * 鍋滄绌洪棽鐩戞帶
+     */
+    private DevicePlcVO.OperationResult handleStopIdleMonitor(DeviceConfig deviceConfig) {
+        String deviceId = deviceConfig.getDeviceId();
+        ScheduledFuture<?> future = idleMonitoringTasks.remove(deviceId);
+        if (future != null && !future.isCancelled()) {
+            future.cancel(false);
+            log.info("宸插仠姝㈠ぇ杞︾┖闂茬洃鎺�: deviceId={}", deviceId);
+        }
+        return DevicePlcVO.OperationResult.builder()
+                .success(true)
+                .message("绌洪棽鐩戞帶宸插仠姝�")
+                .build();
+    }
+
+    /**
+     * 妫�鏌ES浠诲姟锛堝綋mesSend=1鏃讹紝璇诲彇MES鍙傛暟骞跺垱寤轰换鍔★級
+     * 杩涚墖鍜屽嚭鐗囧叡鐢ㄥ悓涓�濂楀崗璁瓧娈碉紝閫氳繃浣嶇疆淇℃伅鍒ゆ柇浠诲姟绫诲瀷
+     */
+    private DevicePlcVO.OperationResult handleCheckMesTask(
+            DeviceConfig deviceConfig,
+            Map<String, Object> params,
+            Map<String, Object> logicParams) {
+        
+        if (plcDynamicDataService == null || s7SerializerProvider == null) {
+            return DevicePlcVO.OperationResult.builder()
+                    .success(false)
+                    .message("PlcDynamicDataService鎴朣7SerializerProvider鏈敞鍏�")
+                    .build();
+        }
+        
+        String deviceId = deviceConfig.getDeviceId();
+        EnhancedS7Serializer serializer = s7SerializerProvider.getSerializer(deviceConfig);
+        if (serializer == null) {
+            return DevicePlcVO.OperationResult.builder()
+                    .success(false)
+                    .message("鑾峰彇PLC搴忓垪鍖栧櫒澶辫触")
+                    .build();
+        }
+        
+        try {
+            // 璇诲彇MES瀛楁锛堣繘鐗囧拰鍑虹墖鍏辩敤锛�
+            Map<String, Object> mesData = plcDynamicDataService.readPlcData(
+                    deviceConfig, MES_FIELDS, serializer);
+            if (mesData == null || mesData.isEmpty()) {
+                return DevicePlcVO.OperationResult.builder()
+                        .success(false)
+                        .message("璇诲彇MES瀛楁澶辫触")
+                        .build();
+            }
+            
+            Integer mesSend = parseInteger(mesData.get("mesSend"));
+            if (mesSend == null || mesSend == 0) {
+                return DevicePlcVO.OperationResult.builder()
+                        .success(true)
+                        .message("鏆傛棤MES浠诲姟锛坢esSend=0锛�")
+                        .build();
+            }
+            
+            // mesSend=1锛岃鍙栦换鍔″弬鏁�
+            String glassId = parseString(mesData.get("mesGlassId"));
+            Integer startSlot = parseInteger(mesData.get("startSlot")); // 璧峰浣嶇疆缂栧彿
+            Integer targetSlot = parseInteger(mesData.get("targetSlot")); // 鐩爣浣嶇疆缂栧彿
+            Integer workLine = parseInteger(mesData.get("workLine"));
+            
+            if (glassId == null || glassId.isEmpty()) {
+                return DevicePlcVO.OperationResult.builder()
+                        .success(false)
+                        .message("MES鏈彁渚涚幓鐠僆D")
+                        .build();
+            }
+            
+            // 鍒ゆ柇鏄繘鐗囪繕鏄嚭鐗囦换鍔�
+            // 鏂规硶锛氶�氳繃startSlot鍒ゆ柇
+            // - 濡傛灉startSlot鏄崸杞珛缂栧彿锛堝900/901锛夛紝鍒欐槸杩涚墖浠诲姟
+            // - 濡傛灉startSlot鏄牸瀛愮紪鍙凤紙鍦ㄥぇ鐞嗙墖绗艰寖鍥村唴锛夛紝鍒欐槸鍑虹墖浠诲姟
+            boolean isOutbound = isOutboundTask(startSlot, logicParams);
+            
+            // 浣嶇疆鏄犲皠
+            Integer startPosition;
+            if (isOutbound) {
+                // 鍑虹墖浠诲姟锛歴tartSlot鏄牸瀛愮紪鍙凤紝闇�瑕佹槧灏勫埌瀹為檯浣嶇疆
+                startPosition = mapOutboundPosition(startSlot, logicParams);
+            } else {
+                // 杩涚墖浠诲姟锛歴tartSlot鏄崸杞珛缂栧彿锛岄�氳繃positionMapping鏄犲皠
+                startPosition = mapPosition(startSlot, logicParams);
+            }
+            
+            // targetSlot缁熶竴閫氳繃positionMapping鏄犲皠
+            Integer targetPosition = mapPosition(targetSlot, logicParams);
+            
+            if (startPosition == null || targetPosition == null) {
+                return DevicePlcVO.OperationResult.builder()
+                        .success(false)
+                        .message(String.format("浣嶇疆鏄犲皠澶辫触: startSlot=%s, targetSlot=%s, isOutbound=%s", 
+                                startSlot, targetSlot, isOutbound))
+                        .build();
+            }
+            
+            // 璇诲彇褰撳墠浣嶇疆
+            Integer currentPosition = getCurrentPosition(deviceConfig, logicParams);
+            
+            // 璁$畻鏃堕棿
+            TimeCalculation timeCalc = calculateTime(
+                    currentPosition, startPosition, targetPosition, logicParams);
+            
+            // 鍒涘缓浠诲姟淇℃伅
+            MesTaskInfo taskInfo = new MesTaskInfo();
+            taskInfo.glassId = glassId;
+            taskInfo.startSlot = startSlot;
+            taskInfo.targetSlot = targetSlot;
+            taskInfo.startPosition = startPosition;
+            taskInfo.targetPosition = targetPosition;
+            taskInfo.currentPosition = currentPosition;
+            taskInfo.gotime = timeCalc.gotime;
+            taskInfo.cartime = timeCalc.cartime;
+            taskInfo.workLine = workLine;
+            taskInfo.createdTime = System.currentTimeMillis();
+            taskInfo.isOutbound = isOutbound;
+            
+            currentTasks.put(deviceId, taskInfo);
+            
+            // 娓呯┖plcRequest锛堣〃绀哄凡鎺ユ敹浠诲姟锛�
+            Map<String, Object> payload = new HashMap<>();
+            payload.put("plcRequest", 0);
+            plcDynamicDataService.writePlcData(deviceConfig, payload, serializer);
+            
+            // 鏇存柊杞﹁締鐘舵�佷负鎵ц涓�
+            statusManager.updateVehicleStatus(deviceId, deviceConfig.getDeviceName(), VehicleState.EXECUTING);
+            
+            // 鍚姩浠诲姟鐩戞帶
+            handleStartTaskMonitor(deviceConfig, params, logicParams);
+            
+            String taskType = isOutbound ? "鍑虹墖" : "杩涚墖";
+            log.info("MES{}浠诲姟宸插垱寤�: deviceId={}, glassId={}, startSlot={}(浣嶇疆{}鏍�), targetSlot={}(浣嶇疆{}鏍�), 璺濈{}鏍�->{}鏍�, gotime={}ms({}绉�), cartime={}ms({}绉�)", 
+                    taskType, deviceId, glassId, startSlot, startPosition, targetSlot, targetPosition,
+                    Math.abs(startPosition - currentPosition), Math.abs(targetPosition - startPosition),
+                    timeCalc.gotime, timeCalc.gotime / 1000.0, timeCalc.cartime, timeCalc.cartime / 1000.0);
+            
+            return DevicePlcVO.OperationResult.builder()
+                    .success(true)
+                    .message(String.format("MES%s浠诲姟宸插垱寤�: glassId=%s, start=%d, target=%d", 
+                            taskType, glassId, startPosition, targetPosition))
+                    .build();
+                    
+        } catch (Exception e) {
+            log.error("妫�鏌ES浠诲姟寮傚父: deviceId={}", deviceId, e);
+            return DevicePlcVO.OperationResult.builder()
+                    .success(false)
+                    .message("澶勭悊寮傚父: " + e.getMessage())
+                    .build();
+        }
+    }
+
+    /**
+     * 鍒ゆ柇鏄惁涓哄嚭鐗囦换鍔�
+     * 閫氳繃startSlot鍒ゆ柇锛�
+     * - 濡傛灉startSlot鍦╬ositionMapping涓紝涓斾笉鍦ㄥぇ鐞嗙墖绗兼牸瀛愯寖鍥村唴锛屽垯鏄繘鐗囦换鍔�
+     * - 濡傛灉startSlot涓嶅湪positionMapping涓紝鎴栧湪澶х悊鐗囩鏍煎瓙鑼冨洿鍐咃紝鍒欐槸鍑虹墖浠诲姟
+     */
+    private boolean isOutboundTask(Integer startSlot, Map<String, Object> logicParams) {
+        if (startSlot == null) {
+            return false;
+        }
+        
+        // 鏂规硶1锛氭鏌tartSlot鏄惁鍦╬ositionMapping涓紙鍗ц浆绔嬬紪鍙凤級
+        @SuppressWarnings("unchecked")
+        Map<String, Integer> positionMapping = getLogicParam(logicParams, "positionMapping", new HashMap<>());
+        if (positionMapping.containsKey(String.valueOf(startSlot))) {
+            // startSlot鍦╬ositionMapping涓紝璇存槑鏄崸杞珛缂栧彿锛屾槸杩涚墖浠诲姟
+            return false;
+        }
+        
+        // 鏂规硶2锛氭鏌tartSlot鏄惁鍦ㄥぇ鐞嗙墖绗兼牸瀛愯寖鍥村唴
+        // 閫氳繃鏌ユ壘鍚岀粍鐨勫ぇ鐞嗙墖绗艰澶囷紝妫�鏌ユ牸瀛愯寖鍥�
+        // 杩欓噷绠�鍖栧鐞嗭細濡傛灉startSlot涓嶅湪positionMapping涓紝涓旀槸鏁板瓧锛屽彲鑳芥槸鏍煎瓙缂栧彿
+        // 鍙互閫氳繃閰嶇疆鎸囧畾鏍煎瓙缂栧彿鑼冨洿锛屾垨鑰呴�氳繃鏌ユ壘鍚岀粍璁惧鍒ゆ柇
+        
+        // 鏂规硶3锛氶�氳繃閰嶇疆鎸囧畾鍑虹墖浠诲姟鐨剆tartSlot鑼冨洿
+        @SuppressWarnings("unchecked")
+        List<Integer> outboundSlotRanges = getLogicParam(logicParams, "outboundSlotRanges", null);
+        if (outboundSlotRanges != null && !outboundSlotRanges.isEmpty()) {
+            // 濡傛灉閰嶇疆浜嗗嚭鐗噑lot鑼冨洿锛屾鏌tartSlot鏄惁鍦ㄨ寖鍥村唴
+            // 渚嬪锛歔1, 101] 琛ㄧず鏍煎瓙1~101閮芥槸鍑虹墖浠诲姟
+            if (outboundSlotRanges.size() >= 2) {
+                int minSlot = outboundSlotRanges.get(0);
+                int maxSlot = outboundSlotRanges.get(1);
+                if (startSlot >= minSlot && startSlot <= maxSlot) {
+                    return true;
+                }
+            }
+        }
+        
+        // 榛樿锛氬鏋渟tartSlot涓嶅湪positionMapping涓紝涓旀槸杈冨皬鐨勬暟瀛楋紙鍙兘鏄牸瀛愮紪鍙凤級锛屽垽鏂负鍑虹墖
+        // 杩欓噷鍙互鏍规嵁瀹為檯闇�姹傝皟鏁村垽鏂�昏緫
+        // 鏆傛椂锛氬鏋渟tartSlot涓嶅湪positionMapping涓紝鍒ゆ柇涓哄嚭鐗囦换鍔�
+        return true;
+    }
+
+
+    /**
+     * 鏄犲皠鍑虹墖婧愪綅缃紙鏍煎瓙缂栧彿杞崲涓哄疄闄呬綅缃級
+     * 閫氳繃鏌ユ壘鍚岀粍鐨勫ぇ鐞嗙墖绗艰澶囬厤缃紝灏嗘牸瀛愮紪鍙疯浆鎹负浣嶇疆
+     */
+    private Integer mapOutboundPosition(Integer gridNumber, Map<String, Object> logicParams) {
+        if (gridNumber == null) {
+            return null;
+        }
+        
+        // 鏂规硶1锛氬鏋滈厤缃簡鏍煎瓙鍒颁綅缃殑鏄犲皠琛�
+        @SuppressWarnings("unchecked")
+        Map<String, Integer> gridPositionMapping = getLogicParam(logicParams, "gridPositionMapping", new HashMap<>());
+        Integer position = gridPositionMapping.get(String.valueOf(gridNumber));
+        if (position != null) {
+            return position;
+        }
+        
+        // 鏂规硶2锛氭牸瀛愮紪鍙风洿鎺ヤ綔涓轰綅缃紙濡傛灉鏍煎瓙缂栧彿灏辨槸浣嶇疆鍊硷級
+        // 渚嬪锛氭牸瀛�1瀵瑰簲浣嶇疆1鏍硷紝鏍煎瓙52瀵瑰簲浣嶇疆52鏍�
+        // 杩欓噷鍙互鏍规嵁瀹為檯闇�姹傝皟鏁存槧灏勯�昏緫
+        
+        // 鏆傛椂鐩存帴浣跨敤鏍煎瓙缂栧彿浣滀负浣嶇疆
+        log.debug("浣跨敤鏍煎瓙缂栧彿浣滀负浣嶇疆: gridNumber={}", gridNumber);
+        return gridNumber;
+    }
+
+    /**
+     * 浣嶇疆鏄犲皠锛氬皢MES缁欑殑缂栧彿锛堝900/901锛夎浆鎹负瀹為檯浣嶇疆鍊硷紙濡�100/500锛�
+     */
+    private Integer mapPosition(Integer slotNumber, Map<String, Object> logicParams) {
+        if (slotNumber == null) {
+            return null;
+        }
+        
+        // 浠庨厤缃腑鑾峰彇浣嶇疆鏄犲皠琛�
+        @SuppressWarnings("unchecked")
+        Map<String, Integer> positionMapping = getLogicParam(logicParams, "positionMapping", new HashMap<>());
+        
+        // 鏌ユ壘鏄犲皠
+        Integer position = positionMapping.get(String.valueOf(slotNumber));
+        if (position != null) {
+            return position;
+        }
+        
+        // 濡傛灉娌℃湁閰嶇疆鏄犲皠锛屽皾璇曠洿鎺ヤ娇鐢ㄧ紪鍙�
+        log.warn("浣嶇疆鏄犲皠鏈壘鍒�: slotNumber={}, 浣跨敤缂栧彿浣滀负浣嶇疆鍊�", slotNumber);
+        return slotNumber;
+    }
+
+    /**
+     * 鑾峰彇褰撳墠浣嶇疆
+     */
+    private Integer getCurrentPosition(DeviceConfig deviceConfig, Map<String, Object> logicParams) {
+        // 浠庣姸鎬佺鐞嗗櫒鑾峰彇
+        VehicleStatus status = statusManager.getVehicleStatus(deviceConfig.getDeviceId());
+        if (status != null && status.getCurrentPosition() != null) {
+            return status.getCurrentPosition().getPositionValue();
+        }
+        
+        // 浠庨厤缃腑鑾峰彇榛樿浣嶇疆
+        return getLogicParam(logicParams, "homePosition", 0);
+    }
+
+    /**
+     * 鏃堕棿璁$畻锛氭牴鎹�熷害銆佸綋鍓嶄綅缃�佺洰鏍囦綅缃绠梘otime鍜宑artime
+     * 閫熷害鍗曚綅锛氭牸/绉掞紙grid/s锛�
+     * 浣嶇疆鍜岃窛绂诲崟浣嶏細鏍煎瓙锛坓rid锛�
+     */
+    private TimeCalculation calculateTime(Integer currentPos, Integer startPos, 
+                                          Integer targetPos, Map<String, Object> logicParams) {
+        // 鑾峰彇閫熷害锛堟牸/绉掞紝grid/s锛�
+        Double speed = getLogicParam(logicParams, "vehicleSpeed", 1.0);
+        if (speed == null || speed <= 0) {
+            speed = 1.0; // 榛樿1鏍�/绉�
+        }
+        
+        // 鑾峰彇杩愬姩璺濈鑼冨洿锛堟牸瀛愶級
+        Integer minRange = getLogicParam(logicParams, "minRange", 1);
+        Integer maxRange = getLogicParam(logicParams, "maxRange", 100);
+        
+        // 璁$畻gotime锛氫粠褰撳墠浣嶇疆鍒拌捣濮嬩綅缃殑鏃堕棿锛堟绉掞級
+        // 鍏紡锛氭椂闂�(ms) = 璺濈(鏍�) / 閫熷害(鏍�/绉�) * 1000
+        long gotime = 0;
+        if (currentPos != null && startPos != null) {
+            int distance = Math.abs(startPos - currentPos); // 璺濈锛堟牸瀛愶級
+            // 闄愬埗鍦ㄨ寖鍥村唴
+            distance = Math.max(minRange, Math.min(maxRange, distance));
+            gotime = (long) (distance / speed * 1000); // 杞崲涓烘绉�
+        }
+        
+        // 璁$畻cartime锛氫粠璧峰浣嶇疆鍒扮洰鏍囦綅缃殑鏃堕棿锛堟绉掞級
+        // 鍏紡锛氭椂闂�(ms) = 璺濈(鏍�) / 閫熷害(鏍�/绉�) * 1000
+        long cartime = 0;
+        if (startPos != null && targetPos != null) {
+            int distance = Math.abs(targetPos - startPos); // 璺濈锛堟牸瀛愶級
+            // 闄愬埗鍦ㄨ寖鍥村唴
+            distance = Math.max(minRange, Math.min(maxRange, distance));
+            cartime = (long) (distance / speed * 1000); // 杞崲涓烘绉�
+        }
+        
+        return new TimeCalculation(gotime, cartime);
+    }
+
+    /**
+     * 鍚姩浠诲姟鐩戞帶锛堢洃鎺tate鐘舵�佸垏鎹㈠拰浠诲姟瀹屾垚锛�
+     */
+    private DevicePlcVO.OperationResult handleStartTaskMonitor(
+            DeviceConfig deviceConfig,
+            Map<String, Object> params,
+            Map<String, Object> logicParams) {
+        
+        String deviceId = deviceConfig.getDeviceId();
+        
+        // 鍋滄鏃х殑鐩戞帶浠诲姟
+        handleStopTaskMonitor(deviceConfig);
+        
+        MesTaskInfo taskInfo = currentTasks.get(deviceId);
+        if (taskInfo == null) {
+            return DevicePlcVO.OperationResult.builder()
+                    .success(false)
+                    .message("娌℃湁姝e湪鎵ц鐨勪换鍔�")
+                    .build();
+        }
+        
+        // 鑾峰彇鐩戞帶闂撮殧
+        Integer monitorIntervalMs = getLogicParam(logicParams, "taskMonitorIntervalMs", 1000); // 榛樿1绉�
+        
+        // 鍚姩鐩戞帶浠诲姟
+        ScheduledFuture<?> future = taskMonitorExecutor.scheduleWithFixedDelay(() -> {
+            try {
+                monitorTaskExecution(deviceConfig, taskInfo, logicParams);
+            } catch (Exception e) {
+                log.error("浠诲姟鐩戞帶寮傚父: deviceId={}", deviceId, e);
+            }
+        }, monitorIntervalMs, monitorIntervalMs, TimeUnit.MILLISECONDS);
+        
+        taskMonitoringTasks.put(deviceId, future);
+        log.info("宸插惎鍔ㄤ换鍔$洃鎺�: deviceId={}, interval={}ms", deviceId, monitorIntervalMs);
+        
+        return DevicePlcVO.OperationResult.builder()
+                .success(true)
+                .message("浠诲姟鐩戞帶宸插惎鍔�")
+                .build();
+    }
+
+    /**
+     * 鐩戞帶浠诲姟鎵ц
+     */
+    private void monitorTaskExecution(DeviceConfig deviceConfig, 
+                                      MesTaskInfo taskInfo,
+                                      Map<String, Object> logicParams) {
+        
+        if (plcDynamicDataService == null || s7SerializerProvider == null) {
+            return;
+        }
+        
+        String deviceId = deviceConfig.getDeviceId();
+        EnhancedS7Serializer serializer = s7SerializerProvider.getSerializer(deviceConfig);
+        if (serializer == null) {
+            return;
+        }
+        
+        try {
+            // 璇诲彇state1~6
+            List<String> stateFields = Arrays.asList("state1", "state2", "state3", "state4", "state5", "state6");
+            Map<String, Object> stateValues = new HashMap<>();
+            
+            DevicePlcVO.StatusInfo statusInfo = devicePlcOperationService.readStatus(deviceConfig.getId());
+            if (statusInfo != null && statusInfo.getFieldValues() != null) {
+                for (String field : stateFields) {
+                    Object value = statusInfo.getFieldValues().get(field);
+                    if (value != null) {
+                        stateValues.put(field, value);
+                    }
+                }
+            }
+            
+            // 鏍规嵁鏃堕棿璁$畻鏇存柊state鐘舵��
+            long currentTime = System.currentTimeMillis();
+            long elapsed = currentTime - taskInfo.createdTime;
+            
+            // 璁$畻鐘舵�佸垏鎹㈡椂闂寸偣
+            long state1Time = taskInfo.gotime; // 鍒拌揪璧峰浣嶇疆锛屼笂杞﹀畬鎴�
+            long state2Time = taskInfo.gotime + taskInfo.cartime; // 鍒拌揪鐩爣浣嶇疆锛岃繍杈撳畬鎴�
+            
+            // 鏇存柊state鐘舵��
+            if (elapsed >= state1Time && elapsed < state2Time) {
+                // state搴旇涓�1
+                if (taskInfo.isOutbound) {
+                    // 鍑虹墖浠诲姟锛氬埌杈炬簮浣嶇疆锛堝ぇ鐞嗙墖绗硷級锛屽彇鐗囧畬鎴�
+                    updateStateIfNeeded(deviceConfig, serializer, stateValues, 1, taskInfo);
+                } else {
+                    // 杩涚墖浠诲姟锛氬埌杈捐捣濮嬩綅缃紙鍗ц浆绔嬶級锛屼笂杞﹀畬鎴�
+                    updateStateIfNeeded(deviceConfig, serializer, stateValues, 1, taskInfo);
+                }
+            } else if (elapsed >= state2Time) {
+                // state搴旇涓�2锛堣繍杈撳畬鎴愶級
+                updateStateIfNeeded(deviceConfig, serializer, stateValues, 2, taskInfo);
+                
+                // 妫�鏌ユ槸鍚︽墍鏈塻tate閮�>=2锛屽鏋滄槸鍒欑粰MES姹囨姤
+                if (allStatesCompleted(stateValues)) {
+                    reportToMes(deviceConfig, serializer, taskInfo, logicParams);
+                }
+            }
+            
+        } catch (Exception e) {
+            log.error("鐩戞帶浠诲姟鎵ц寮傚父: deviceId={}", deviceId, e);
+        }
+    }
+
+    /**
+     * 鏇存柊state鐘舵�侊紙濡傛灉闇�瑕侊級
+     */
+    private void updateStateIfNeeded(DeviceConfig deviceConfig,
+                                     EnhancedS7Serializer serializer,
+                                     Map<String, Object> currentStates,
+                                     int targetState,
+                                     MesTaskInfo taskInfo) {
+        
+        // 杩欓噷鍙互鏍规嵁瀹為檯闇�姹傛洿鏂皊tate瀛楁
+        // 鏆傛椂鍙褰曟棩蹇楋紝瀹為檯鏇存柊鍙兘闇�瑕佹牴鎹叿浣揚LC瀛楁閰嶇疆
+        log.debug("浠诲姟鐘舵�佹洿鏂�: deviceId={}, targetState={}", 
+                deviceConfig.getDeviceId(), targetState);
+    }
+
+    /**
+     * 妫�鏌ユ槸鍚︽墍鏈塻tate閮藉凡瀹屾垚锛�>=2锛�
+     */
+    private boolean allStatesCompleted(Map<String, Object> stateValues) {
+        for (Object value : stateValues.values()) {
+            Integer state = parseInteger(value);
+            if (state == null || state < 2) {
+                return false;
+            }
+        }
+        return !stateValues.isEmpty();
+    }
+
+    /**
+     * 缁橫ES姹囨姤
+     */
+    private void reportToMes(DeviceConfig deviceConfig,
+                             EnhancedS7Serializer serializer,
+                             MesTaskInfo taskInfo,
+                             Map<String, Object> logicParams) {
+        
+        try {
+            // 璁剧疆姹囨姤瀛�
+            Map<String, Object> payload = new HashMap<>();
+            payload.put("plcReport", 1);
+            plcDynamicDataService.writePlcData(deviceConfig, payload, serializer);
+            
+            String taskType = taskInfo.isOutbound ? "鍑虹墖" : "杩涚墖";
+            log.info("宸茬粰MES姹囨姤({}浠诲姟): deviceId={}, glassId={}", 
+                    taskType, deviceConfig.getDeviceId(), taskInfo.glassId);
+            
+            // 绛夊緟MES纭
+            waitForMesConfirm(deviceConfig, serializer, taskInfo, logicParams);
+            
+        } catch (Exception e) {
+            log.error("缁橫ES姹囨姤寮傚父: deviceId={}", deviceConfig.getDeviceId(), e);
+        }
+    }
+
+    /**
+     * 绛夊緟MES纭
+     */
+    private void waitForMesConfirm(DeviceConfig deviceConfig,
+                                  EnhancedS7Serializer serializer,
+                                  MesTaskInfo taskInfo,
+                                  Map<String, Object> logicParams) {
+        
+        try {
+            // 璇诲彇纭瀛楋紙鍋囪瀛楁鍚嶄负mesConfirm锛�
+            Integer maxWaitTime = getLogicParam(logicParams, "mesConfirmTimeoutMs", 30000); // 榛樿30绉�
+            long startTime = System.currentTimeMillis();
+            
+            while (System.currentTimeMillis() - startTime < maxWaitTime) {
+                Object confirmValue = plcDynamicDataService.readPlcField(
+                        deviceConfig, "mesConfirm", serializer);
+                Integer confirm = parseInteger(confirmValue);
+                
+                if (confirm != null && confirm == 1) {
+                    // MES宸茬‘璁わ紝娓呯┖state鍜屾眹鎶ュ瓧
+                    clearTaskStates(deviceConfig, serializer);
+                    
+                    // 浠诲姟瀹屾垚锛屾仮澶嶄负绌洪棽鐘舵��
+                    statusManager.updateVehicleStatus(
+                            deviceConfig.getDeviceId(), VehicleState.IDLE);
+                    statusManager.clearVehicleTask(deviceConfig.getDeviceId());
+                    
+                    // 绉婚櫎浠诲姟
+                    currentTasks.remove(deviceConfig.getDeviceId());
+                    
+                    // 鍋滄浠诲姟鐩戞帶
+                    handleStopTaskMonitor(deviceConfig);
+                    
+                    // 鎭㈠plcRequest=1锛堝彲浠ユ帴鏀舵柊浠诲姟锛�
+                    Map<String, Object> payload = new HashMap<>();
+                    payload.put("plcRequest", 1);
+                    plcDynamicDataService.writePlcData(deviceConfig, payload, serializer);
+                    
+                    log.info("MES浠诲姟宸插畬鎴�: deviceId={}, glassId={}", 
+                            deviceConfig.getDeviceId(), taskInfo.glassId);
+                    return;
+                }
+                
+                Thread.sleep(500); // 绛夊緟500ms鍚庨噸璇�
+            }
+            
+            log.warn("绛夊緟MES纭瓒呮椂: deviceId={}, glassId={}", 
+                    deviceConfig.getDeviceId(), taskInfo.glassId);
+            
+        } catch (Exception e) {
+            log.error("绛夊緟MES纭寮傚父: deviceId={}", deviceConfig.getDeviceId(), e);
+        }
+    }
+
+    /**
+     * 娓呯┖浠诲姟鐘舵��
+     */
+    private void clearTaskStates(DeviceConfig deviceConfig, EnhancedS7Serializer serializer) {
+        try {
+            Map<String, Object> payload = new HashMap<>();
+            // 娓呯┖state1~6
+            for (int i = 1; i <= 6; i++) {
+                payload.put("state" + i, 0);
+            }
+            // 娓呯┖姹囨姤瀛�
+            payload.put("plcReport", 0);
+            plcDynamicDataService.writePlcData(deviceConfig, payload, serializer);
+        } catch (Exception e) {
+            log.error("娓呯┖浠诲姟鐘舵�佸紓甯�: deviceId={}", deviceConfig.getDeviceId(), e);
+        }
+    }
+
+    /**
+     * 鍋滄浠诲姟鐩戞帶
+     */
+    private DevicePlcVO.OperationResult handleStopTaskMonitor(DeviceConfig deviceConfig) {
+        String deviceId = deviceConfig.getDeviceId();
+        ScheduledFuture<?> future = taskMonitoringTasks.remove(deviceId);
+        if (future != null && !future.isCancelled()) {
+            future.cancel(false);
+            log.info("宸插仠姝换鍔$洃鎺�: deviceId={}", deviceId);
+        }
+        return DevicePlcVO.OperationResult.builder()
+                .success(true)
+                .message("浠诲姟鐩戞帶宸插仠姝�")
+                .build();
+    }
+
+    /**
+     * 鏃堕棿璁$畻缁撴灉
+     */
+    private static class TimeCalculation {
+        final long gotime;  // 鍒拌捣濮嬩綅缃殑鏃堕棿锛堟绉掞級
+        final long cartime; // 浠庤捣濮嬪埌鐩爣浣嶇疆鐨勬椂闂达紙姣锛�
+        
+        TimeCalculation(long gotime, long cartime) {
+            this.gotime = gotime;
+            this.cartime = cartime;
+        }
+    }
+
+    /**
+     * MES浠诲姟淇℃伅
+     */
+    private static class MesTaskInfo {
+        String glassId;
+        Integer startSlot;
+        Integer targetSlot;
+        Integer startPosition;
+        Integer targetPosition;
+        Integer currentPosition;
+        long gotime;
+        long cartime;
+        Integer workLine;
+        long createdTime;
+        boolean isOutbound = false; // 鏄惁涓哄嚭鐗囦换鍔★紙false=杩涚墖锛宼rue=鍑虹墖锛�
+    }
+
+    /**
+     * 搴旂敤鍏抽棴鏃舵竻鐞嗚祫婧�
+     */
+    @PreDestroy
+    public void destroy() {
+        log.info("姝e湪鍏抽棴澶ц溅鐩戞帶绾跨▼姹�...");
+        
+        // 鍋滄鎵�鏈夌洃鎺т换鍔�
+        for (String deviceId : new ArrayList<>(monitoringTasks.keySet())) {
+            stopStateMonitoring(deviceId);
+        }
+        for (String deviceId : new ArrayList<>(idleMonitoringTasks.keySet())) {
+            ScheduledFuture<?> future = idleMonitoringTasks.remove(deviceId);
+            if (future != null) future.cancel(false);
+        }
+        for (String deviceId : new ArrayList<>(taskMonitoringTasks.keySet())) {
+            ScheduledFuture<?> future = taskMonitoringTasks.remove(deviceId);
+            if (future != null) future.cancel(false);
+        }
+        
+        // 鍏抽棴绾跨▼姹�
+        shutdownExecutor(stateMonitorExecutor, "鐘舵�佺洃鎺�");
+        shutdownExecutor(idleMonitorExecutor, "绌洪棽鐩戞帶");
+        shutdownExecutor(taskMonitorExecutor, "浠诲姟鐩戞帶");
+        
+        log.info("澶ц溅鐩戞帶绾跨▼姹犲凡鍏抽棴");
+    }
+    
+    private void shutdownExecutor(ScheduledExecutorService executor, String name) {
+        executor.shutdown();
+        try {
+            if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
+                executor.shutdownNow();
+                if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
+                    log.warn("{}绾跨▼姹犳湭鑳芥甯稿叧闂�", name);
+                }
+            }
+        } catch (InterruptedException e) {
+            executor.shutdownNow();
+            Thread.currentThread().interrupt();
+        }
+    }
+}
+
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/vehicle/model/VehiclePath.java b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/vehicle/model/VehiclePath.java
new file mode 100644
index 0000000..ba17581
--- /dev/null
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/vehicle/model/VehiclePath.java
@@ -0,0 +1,91 @@
+package com.mes.interaction.vehicle.model;
+
+import lombok.Data;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 杞﹁締璺緞淇℃伅
+ * 
+ * @author mes
+ * @since 2025-01-XX
+ */
+@Data
+public class VehiclePath {
+    /**
+     * 璧峰浣嶇疆
+     */
+    private VehiclePosition startPosition;
+    
+    /**
+     * 鐩爣浣嶇疆
+     */
+    private VehiclePosition endPosition;
+    
+    /**
+     * 璺緞鐐瑰垪琛紙濡傛灉璺緞涓嶆槸鐩寸嚎锛�
+     */
+    private List<VehiclePosition> waypoints;
+    
+    /**
+     * 璺緞瀹藉害锛堢敤浜庡啿绐佹娴嬶級
+     */
+    private Double pathWidth;
+    
+    public VehiclePath() {
+        this.waypoints = new ArrayList<>();
+    }
+    
+    public VehiclePath(VehiclePosition start, VehiclePosition end) {
+        this.startPosition = start;
+        this.endPosition = end;
+        this.waypoints = new ArrayList<>();
+    }
+    
+    /**
+     * 妫�鏌ヨ矾寰勬槸鍚︿笌鍙︿竴鏉¤矾寰勫啿绐�
+     */
+    public boolean conflictsWith(VehiclePath other) {
+        if (other == null) {
+            return false;
+        }
+        
+        // 绠�鍗曞啿绐佹娴嬶細妫�鏌ヨ捣鐐瑰拰缁堢偣鏄惁閲嶅彔
+        if (startPosition != null && other.startPosition != null) {
+            if (positionsOverlap(startPosition, other.startPosition)) {
+                return true;
+            }
+        }
+        
+        if (endPosition != null && other.endPosition != null) {
+            if (positionsOverlap(endPosition, other.endPosition)) {
+                return true;
+            }
+        }
+        
+        // TODO: 鏇村鏉傜殑璺緞浜ゅ弶妫�娴�
+        return false;
+    }
+    
+    private boolean positionsOverlap(VehiclePosition pos1, VehiclePosition pos2) {
+        if (pos1 == null || pos2 == null) {
+            return false;
+        }
+        
+        // 濡傛灉鏈夊潗鏍囷紝浣跨敤鍧愭爣鍒ゆ柇
+        if (pos1.getX() != null && pos1.getY() != null && 
+            pos2.getX() != null && pos2.getY() != null) {
+            double distance = pos1.distanceTo(pos2);
+            double threshold = (pathWidth != null ? pathWidth : 100.0) / 2.0;
+            return distance < threshold;
+        }
+        
+        // 濡傛灉鏈変綅缃�硷紝浣跨敤浣嶇疆鍊煎垽鏂�
+        if (pos1.getPositionValue() != null && pos2.getPositionValue() != null) {
+            return pos1.getPositionValue().equals(pos2.getPositionValue());
+        }
+        
+        return false;
+    }
+}
+
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/vehicle/model/VehiclePosition.java b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/vehicle/model/VehiclePosition.java
new file mode 100644
index 0000000..07658ed
--- /dev/null
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/vehicle/model/VehiclePosition.java
@@ -0,0 +1,63 @@
+package com.mes.interaction.vehicle.model;
+
+import lombok.Data;
+
+/**
+ * 杞﹁締浣嶇疆淇℃伅
+ * 
+ * @author huang
+ * @since 2025-11-21
+ */
+@Data
+public class VehiclePosition {
+    /**
+     * X鍧愭爣
+     */
+    private Double x;
+    
+    /**
+     * Y鍧愭爣
+     */
+    private Double y;
+    
+    /**
+     * Z鍧愭爣锛堝鏋滈渶瑕侊級
+     */
+    private Double z;
+    
+    /**
+     * 浣嶇疆缂栫爜锛堝锛歅OS1, POS2锛�
+     */
+    private String positionCode;
+    
+    /**
+     * 浣嶇疆鍊硷紙PLC涓殑浣嶇疆鍊硷級
+     */
+    private Integer positionValue;
+    
+    public VehiclePosition() {
+    }
+    
+    public VehiclePosition(Double x, Double y) {
+        this.x = x;
+        this.y = y;
+    }
+    
+    public VehiclePosition(String positionCode, Integer positionValue) {
+        this.positionCode = positionCode;
+        this.positionValue = positionValue;
+    }
+    
+    /**
+     * 璁$畻鍒扮洰鏍囦綅缃殑璺濈
+     */
+    public double distanceTo(VehiclePosition target) {
+        if (x == null || y == null || target.x == null || target.y == null) {
+            return 0.0;
+        }
+        double dx = target.x - x;
+        double dy = target.y - y;
+        return Math.sqrt(dx * dx + dy * dy);
+    }
+}
+
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/vehicle/model/VehicleState.java b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/vehicle/model/VehicleState.java
new file mode 100644
index 0000000..cd6c724
--- /dev/null
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/vehicle/model/VehicleState.java
@@ -0,0 +1,30 @@
+package com.mes.interaction.vehicle.model;
+
+/**
+ * 杞﹁締鐘舵�佹灇涓�
+ * 
+ * @author mes
+ * @since 2025-01-XX
+ */
+public enum VehicleState {
+    /**
+     * 绌洪棽 - 杞﹁締鍙敤锛屽彲浠ユ帴鍙楁柊浠诲姟
+     */
+    IDLE,
+    
+    /**
+     * 鎵ц涓� - 杞﹁締姝e湪鎵ц浠诲姟锛屼笉鑳芥搷浣�
+     */
+    EXECUTING,
+    
+    /**
+     * 绛夊緟 - 杞﹁締鍦ㄦ帓闃熺瓑寰呮墽琛�
+     */
+    WAITING,
+    
+    /**
+     * 閿欒 - 杞﹁締鍑虹幇閿欒
+     */
+    ERROR
+}
+
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/vehicle/model/VehicleStatus.java b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/vehicle/model/VehicleStatus.java
new file mode 100644
index 0000000..39d10c4
--- /dev/null
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/vehicle/model/VehicleStatus.java
@@ -0,0 +1,113 @@
+package com.mes.interaction.vehicle.model;
+
+import lombok.Data;
+import java.time.LocalDateTime;
+
+/**
+ * 杞﹁締杩愯鏃剁姸鎬�
+ * 姣忎釜璁惧瀹炰緥鏈夌嫭绔嬬殑鐘舵�佸璞�
+ * 
+ * @author huang
+ * @since 2025-11-21
+ */
+@Data
+public class VehicleStatus {
+    /**
+     * 璁惧ID锛堝搴擠eviceConfig.deviceId锛�
+     */
+    private String deviceId;
+    
+    /**
+     * 璁惧鍚嶇О
+     */
+    private String deviceName;
+    
+    /**
+     * 褰撳墠鐘舵��
+     */
+    private VehicleState state;
+    
+    /**
+     * 褰撳墠浣嶇疆
+     */
+    private VehiclePosition currentPosition;
+    
+    /**
+     * 鐩爣浣嶇疆
+     */
+    private VehiclePosition targetPosition;
+    
+    /**
+     * 褰撳墠閫熷害
+     */
+    private Double speed;
+    
+    /**
+     * 浠诲姟寮�濮嬫椂闂�
+     */
+    private LocalDateTime taskStartTime;
+    
+    /**
+     * 棰勮缁撴潫鏃堕棿
+     */
+    private LocalDateTime estimatedEndTime;
+    
+    /**
+     * 褰撳墠浠诲姟
+     */
+    private VehicleTask currentTask;
+    
+    /**
+     * 鏈�鍚庢洿鏂版椂闂�
+     */
+    private LocalDateTime lastUpdateTime;
+    
+    public VehicleStatus() {
+        this.state = VehicleState.IDLE;
+        this.lastUpdateTime = LocalDateTime.now();
+    }
+    
+    public VehicleStatus(String deviceId) {
+        this();
+        this.deviceId = deviceId;
+    }
+    
+    public VehicleStatus(String deviceId, String deviceName) {
+        this(deviceId);
+        this.deviceName = deviceName;
+    }
+    
+    /**
+     * 鏇存柊鐘舵��
+     */
+    public void setState(VehicleState newState) {
+        this.state = newState;
+        this.lastUpdateTime = LocalDateTime.now();
+        
+        if (newState == VehicleState.EXECUTING && currentTask != null) {
+            this.taskStartTime = LocalDateTime.now();
+            currentTask.setStartTime(taskStartTime);
+            currentTask.calculateEstimatedEndTime();
+            this.estimatedEndTime = currentTask.getEstimatedEndTime();
+        } else if (newState == VehicleState.IDLE) {
+            this.taskStartTime = null;
+            this.estimatedEndTime = null;
+            this.currentTask = null;
+        }
+    }
+    
+    /**
+     * 妫�鏌ユ槸鍚﹀彲鐢�
+     */
+    public boolean isAvailable() {
+        return state == VehicleState.IDLE;
+    }
+    
+    /**
+     * 妫�鏌ユ槸鍚︽鍦ㄦ墽琛�
+     */
+    public boolean isExecuting() {
+        return state == VehicleState.EXECUTING;
+    }
+}
+
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/vehicle/model/VehicleTask.java b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/vehicle/model/VehicleTask.java
new file mode 100644
index 0000000..f64da91
--- /dev/null
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/vehicle/model/VehicleTask.java
@@ -0,0 +1,81 @@
+package com.mes.interaction.vehicle.model;
+
+import lombok.Data;
+import java.time.LocalDateTime;
+
+/**
+ * 杞﹁締浠诲姟淇℃伅
+ * 
+ * @author mes
+ * @since 2025-01-XX
+ */
+@Data
+public class VehicleTask {
+    /**
+     * 浠诲姟ID
+     */
+    private String taskId;
+    
+    /**
+     * 浠诲姟鍚嶇О
+     */
+    private String taskName;
+    
+    /**
+     * 鎿嶄綔绫诲瀷锛堝锛歠eedGlass, triggerRequest绛夛級
+     */
+    private String operation;
+    
+    /**
+     * 璁″垝璺緞
+     */
+    private VehiclePath plannedPath;
+    
+    /**
+     * 浠诲姟寮�濮嬫椂闂�
+     */
+    private LocalDateTime startTime;
+    
+    /**
+     * 棰勮缁撴潫鏃堕棿
+     */
+    private LocalDateTime estimatedEndTime;
+    
+    /**
+     * 杞﹁締閫熷害锛堢敤浜庤绠楅璁$粨鏉熸椂闂达級
+     */
+    private Double speed;
+    
+    /**
+     * 浠诲姟鍙傛暟
+     */
+    private java.util.Map<String, Object> parameters;
+    
+    public VehicleTask() {
+        this.parameters = new java.util.HashMap<>();
+    }
+    
+    /**
+     * 璁$畻棰勮缁撴潫鏃堕棿
+     */
+    public void calculateEstimatedEndTime() {
+        if (plannedPath == null || speed == null || speed <= 0) {
+            return;
+        }
+        
+        VehiclePosition start = plannedPath.getStartPosition();
+        VehiclePosition end = plannedPath.getEndPosition();
+        
+        if (start != null && end != null) {
+            double distance = start.distanceTo(end);
+            if (distance > 0) {
+                // 鏃堕棿 = 璺濈 / 閫熷害锛堢锛�
+                long seconds = (long) (distance / speed);
+                estimatedEndTime = startTime != null ? 
+                    startTime.plusSeconds(seconds) : 
+                    LocalDateTime.now().plusSeconds(seconds);
+            }
+        }
+    }
+}
+
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/workstation/base/WorkstationBaseHandler.java b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/workstation/base/WorkstationBaseHandler.java
new file mode 100644
index 0000000..c37bb7a
--- /dev/null
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/workstation/base/WorkstationBaseHandler.java
@@ -0,0 +1,80 @@
+package com.mes.interaction.workstation.base;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.mes.device.entity.DeviceConfig;
+import com.mes.device.service.DevicePlcOperationService;
+import com.mes.device.vo.DevicePlcVO;
+import com.mes.interaction.BaseDeviceLogicHandler;
+import com.mes.interaction.workstation.config.WorkstationLogicConfig;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import java.util.Map;
+
+/**
+ * 鍗ц浆绔嬬郴鍒楄澶囩殑閫氱敤澶勭悊鍣ㄥ熀绫�
+ * 璐熻矗瑙f瀽 workstation 閰嶇疆銆佹彁渚涘崰浣嶇殑鎵ц妯℃澘锛屽悗缁叿浣撹澶囧湪瀛愮被涓疄鐜般��
+ *
+ * @author mes
+ * @since 2025-11-24
+ */
+@Slf4j
+public abstract class WorkstationBaseHandler extends BaseDeviceLogicHandler {
+
+    protected WorkstationBaseHandler(DevicePlcOperationService devicePlcOperationService) {
+        super(devicePlcOperationService);
+    }
+
+    /**
+     * 瑙f瀽 workstation 鐩稿叧閰嶇疆
+     */
+    protected WorkstationLogicConfig parseWorkstationConfig(Map<String, Object> logicParams) {
+        WorkstationLogicConfig config = new WorkstationLogicConfig();
+        if (logicParams == null) {
+            return config;
+        }
+        config.setScanIntervalMs(getLogicParam(logicParams, "scanIntervalMs", 10_000));
+        config.setTransferDelayMs(getLogicParam(logicParams, "transferDelayMs", 30_000));
+        config.setVehicleCapacity(getLogicParam(logicParams, "vehicleCapacity", 6000));
+        config.setAutoAck(getLogicParam(logicParams, "autoAck", Boolean.TRUE));
+        return config;
+    }
+
+    /**
+     * 榛樿瀹炵幇锛氭彁绀哄皻鏈疄鐜板叿浣撻�昏緫
+     */
+    @Override
+    protected DevicePlcVO.OperationResult doExecute(com.mes.device.entity.DeviceConfig deviceConfig,
+                                                   String operation,
+                                                   java.util.Map<String, Object> params,
+                                                   java.util.Map<String, Object> logicParams) {
+        log.warn("褰撳墠璁惧閫昏緫灏氭湭瀹炵幇: deviceType={}, operation={}", deviceConfig.getDeviceType(), operation);
+        return DevicePlcVO.OperationResult.builder()
+                .success(false)
+                .message("璁惧閫昏緫寰呭疄鐜�: " + deviceConfig.getDeviceType())
+                .build();
+    }
+
+    @Override
+    public String validateLogicParams(DeviceConfig deviceConfig) {
+        // 榛樿璁や负閰嶇疆鍚堟硶锛屽叿浣撴牎楠屼氦鐢卞瓙绫诲疄鐜�
+        return null;
+    }
+
+    @Override
+    public String getDefaultLogicParams() {
+        Map<String, Object> defaults = new HashMap<>();
+        defaults.put("scanIntervalMs", 10_000);
+        defaults.put("transferDelayMs", 30_000);
+        defaults.put("vehicleCapacity", 6_000);
+        defaults.put("autoAck", true);
+        try {
+            return objectMapper.writeValueAsString(defaults);
+        } catch (JsonProcessingException e) {
+            return "{\"scanIntervalMs\":10000,\"transferDelayMs\":30000,\"vehicleCapacity\":6000,\"autoAck\":true}";
+        }
+    }
+}
+
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/workstation/config/WorkstationLogicConfig.java b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/workstation/config/WorkstationLogicConfig.java
new file mode 100644
index 0000000..a782412
--- /dev/null
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/workstation/config/WorkstationLogicConfig.java
@@ -0,0 +1,32 @@
+package com.mes.interaction.workstation.config;
+
+import lombok.Data;
+
+/**
+ * 鍗ц浆绔嬬浉鍏宠澶囩殑閫昏緫閰嶇疆
+ * 瀵瑰簲 extraParams.deviceLogic 涓殑瀛楁
+ */
+@Data
+public class WorkstationLogicConfig {
+
+    /**
+     * 鎵爜璁惧鍙戦�佽姹傜殑鏃堕棿闂撮殧锛堟绉掞級
+     */
+    private Integer scanIntervalMs = 10_000;
+
+    /**
+     * 鍗ц浆绔嬪埌澶ц溅鐨勮繍杈撴椂闂达紙姣锛�
+     */
+    private Integer transferDelayMs = 30_000;
+
+    /**
+     * 鍙杞界殑鏈�澶у搴︼紙mm锛�
+     */
+    private Integer vehicleCapacity = 6_000;
+
+    /**
+     * 鏄惁鑷姩纭 MES 鍙戦�佺殑鐜荤拑淇℃伅
+     */
+    private Boolean autoAck = Boolean.TRUE;
+}
+
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
new file mode 100644
index 0000000..3b45e80
--- /dev/null
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/workstation/scanner/handler/HorizontalScannerLogicHandler.java
@@ -0,0 +1,161 @@
+package com.mes.interaction.workstation.scanner.handler;
+
+import com.mes.device.entity.DeviceConfig;
+import com.mes.device.entity.GlassInfo;
+import com.mes.device.service.DevicePlcOperationService;
+import com.mes.device.service.GlassInfoService;
+import com.mes.device.vo.DevicePlcVO;
+import com.mes.interaction.workstation.base.WorkstationBaseHandler;
+import com.mes.interaction.workstation.config.WorkstationLogicConfig;
+import com.mes.s7.enhanced.EnhancedS7Serializer;
+import com.mes.s7.provider.S7SerializerProvider;
+import com.mes.service.PlcDynamicDataService;
+import 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.List;
+import java.util.Map;
+
+/**
+ * 鍗ц浆绔嬫壂鐮佽澶囬�昏緫澶勭悊鍣�
+ * 璐熻矗浠嶮ES鍐欏尯璇诲彇鐜荤拑灏哄锛屽苟钀藉簱 glass_info
+ */
+@Slf4j
+@Component
+public class HorizontalScannerLogicHandler extends WorkstationBaseHandler {
+
+    private static final List<String> MES_FIELDS = Arrays.asList("mesSend", "mesGlassId", "mesWidth", "mesHeight", "workLine");
+
+    private final PlcDynamicDataService plcDynamicDataService;
+    private final GlassInfoService glassInfoService;
+    private final S7SerializerProvider s7SerializerProvider;
+
+    public HorizontalScannerLogicHandler(DevicePlcOperationService devicePlcOperationService,
+                                         PlcDynamicDataService plcDynamicDataService,
+                                         GlassInfoService glassInfoService,
+                                         S7SerializerProvider s7SerializerProvider) {
+        super(devicePlcOperationService);
+        this.plcDynamicDataService = plcDynamicDataService;
+        this.glassInfoService = glassInfoService;
+        this.s7SerializerProvider = s7SerializerProvider;
+    }
+
+    @Override
+    public String getDeviceType() {
+        return DeviceConfig.DeviceType.WORKSTATION_SCANNER;
+    }
+
+    @Override
+    protected DevicePlcVO.OperationResult doExecute(DeviceConfig deviceConfig,
+                                                    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搴忓垪鍖栧櫒澶辫触");
+        }
+
+        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鍐欏尯澶辫触");
+            }
+
+            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);
+            }
+
+            // 璇诲彇鍒癕ES鏁版嵁鍚庯紝閲嶇疆mesSend锛岄伩鍏嶉噸澶嶆秷璐�
+            plcDynamicDataService.writePlcField(deviceConfig, "mesSend", 0, serializer);
+
+            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);
+        } catch (Exception e) {
+            log.error("鍗ц浆绔嬫壂鐮佸鐞嗗紓甯�, deviceId={}", deviceConfig.getId(), e);
+            return buildResult(deviceConfig, operation, false, "澶勭悊寮傚父: " + e.getMessage());
+        }
+    }
+
+    private GlassInfo buildGlassInfo(String glassId, Integer longSide, Integer shortSide, Integer workLine) {
+        GlassInfo glassInfo = new GlassInfo();
+        glassInfo.setGlassId(glassId.trim());
+        if (longSide != null) {
+            glassInfo.setGlassLength(longSide);
+        }
+        if (shortSide != null) {
+            glassInfo.setGlassWidth(shortSide);
+        }
+        glassInfo.setStatus(GlassInfo.Status.ACTIVE);
+        if (workLine != null) {
+            glassInfo.setDescription("workLine=" + workLine);
+        }
+        return glassInfo;
+    }
+
+    private Integer parseInteger(Object value) {
+        if (value instanceof Number) {
+            return ((Number) value).intValue();
+        }
+        if (value == null) {
+            return null;
+        }
+        try {
+            return Integer.parseInt(String.valueOf(value));
+        } catch (NumberFormatException e) {
+            return null;
+        }
+    }
+
+    private String parseString(Object value) {
+        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) {
+        return DevicePlcVO.OperationResult.builder()
+                .deviceId(deviceConfig.getId())
+                .deviceName(deviceConfig.getDeviceName())
+                .deviceCode(deviceConfig.getDeviceCode())
+                .projectId(deviceConfig.getProjectId() != null ? String.valueOf(deviceConfig.getProjectId()) : null)
+                .operation(operation)
+                .success(success)
+                .message(message)
+                .timestamp(LocalDateTime.now())
+                .build();
+    }
+}
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
new file mode 100644
index 0000000..85b9dc3
--- /dev/null
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/workstation/transfer/handler/HorizontalTransferLogicHandler.java
@@ -0,0 +1,481 @@
+package com.mes.interaction.workstation.transfer.handler;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.mes.device.entity.DeviceConfig;
+import com.mes.device.entity.GlassInfo;
+import com.mes.device.mapper.DeviceGlassInfoMapper;
+import com.mes.device.service.DevicePlcOperationService;
+import com.mes.device.service.GlassInfoService;
+import com.mes.device.vo.DevicePlcVO;
+import com.mes.interaction.workstation.base.WorkstationBaseHandler;
+import com.mes.interaction.workstation.config.WorkstationLogicConfig;
+import com.mes.s7.enhanced.EnhancedS7Serializer;
+import com.mes.s7.provider.S7SerializerProvider;
+import com.mes.service.PlcDynamicDataService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+import javax.annotation.PreDestroy;
+
+import java.time.LocalDateTime;
+import java.util.*;
+import java.util.concurrent.*;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.stream.Collectors;
+
+/**
+ * 鍗ц浆绔嬩富浣撹澶囬�昏緫澶勭悊鍣�
+ * 璐熻矗鐜荤拑缂撳啿銆佸閲忔牎楠屻�佹壒娆$粍瑁呫�丳LC鍐欏叆绛夐�昏緫
+ */
+@Slf4j
+@Component
+public class HorizontalTransferLogicHandler extends WorkstationBaseHandler {
+
+    private final PlcDynamicDataService plcDynamicDataService;
+    private final GlassInfoService glassInfoService;
+    private final S7SerializerProvider s7SerializerProvider;
+    
+    @Autowired(required = false)
+    private DeviceGlassInfoMapper glassInfoMapper;
+
+    // 鐜荤拑缂撳啿闃熷垪锛歞eviceId -> 鐜荤拑淇℃伅鍒楄〃
+    private final Map<String, List<GlassBufferItem>> glassBuffer = new ConcurrentHashMap<>();
+    
+    // 鏈�鍚庢壂鐮佹椂闂达細deviceId -> 鏈�鍚庢壂鐮佹椂闂存埑
+    private final Map<String, AtomicLong> lastScanTime = new ConcurrentHashMap<>();
+    
+    // 鐩戞帶浠诲姟锛歞eviceId -> 鐩戞帶浠诲姟
+    private final Map<String, ScheduledFuture<?>> monitorTasks = new ConcurrentHashMap<>();
+    
+    // 鐩戞帶绾跨▼姹�
+    private final ScheduledExecutorService monitorExecutor = Executors.newScheduledThreadPool(5, r -> {
+        Thread t = new Thread(r, "HorizontalTransferMonitor");
+        t.setDaemon(true);
+        return t;
+    });
+
+    @Autowired
+    public HorizontalTransferLogicHandler(DevicePlcOperationService devicePlcOperationService,
+                                         PlcDynamicDataService plcDynamicDataService,
+                                         @Qualifier("deviceGlassInfoService") GlassInfoService glassInfoService,
+                                         S7SerializerProvider s7SerializerProvider) {
+        super(devicePlcOperationService);
+        this.plcDynamicDataService = plcDynamicDataService;
+        this.glassInfoService = glassInfoService;
+        this.s7SerializerProvider = s7SerializerProvider;
+    }
+
+    @Override
+    public String getDeviceType() {
+        return DeviceConfig.DeviceType.WORKSTATION_TRANSFER;
+    }
+
+    @Override
+    protected DevicePlcVO.OperationResult doExecute(DeviceConfig deviceConfig,
+                                                    String operation,
+                                                    Map<String, Object> params,
+                                                    Map<String, Object> logicParams) {
+        WorkstationLogicConfig config = parseWorkstationConfig(logicParams);
+        
+        try {
+            switch (operation) {
+                case "checkAndProcess":
+                case "process":
+                    return handleCheckAndProcess(deviceConfig, config, logicParams);
+                case "startMonitor":
+                    return handleStartMonitor(deviceConfig, config, logicParams);
+                case "stopMonitor":
+                    return handleStopMonitor(deviceConfig);
+                case "clearBuffer":
+                    return handleClearBuffer(deviceConfig);
+                default:
+                    return buildResult(deviceConfig, operation, false, 
+                            "涓嶆敮鎸佺殑鎿嶄綔: " + operation);
+            }
+        } catch (Exception e) {
+            log.error("鍗ц浆绔嬩富浣撳鐞嗗紓甯�: deviceId={}, operation={}", 
+                    deviceConfig.getId(), operation, e);
+            return buildResult(deviceConfig, operation, false, 
+                    "澶勭悊寮傚父: " + e.getMessage());
+        }
+    }
+
+    /**
+     * 妫�鏌ュ苟澶勭悊鐜荤拑鎵规
+     * 浠庢暟鎹簱璇诲彇鏈�杩戞壂鐮佺殑鐜荤拑锛岃繘琛屽閲忓垽鏂紝缁勮鎵规锛屽啓鍏LC
+     */
+    private DevicePlcVO.OperationResult handleCheckAndProcess(
+            DeviceConfig deviceConfig,
+            WorkstationLogicConfig config,
+            Map<String, Object> logicParams) {
+        
+        String deviceId = deviceConfig.getDeviceId();
+        EnhancedS7Serializer serializer = s7SerializerProvider.getSerializer(deviceConfig);
+        if (serializer == null) {
+            return buildResult(deviceConfig, "checkAndProcess", false, 
+                    "鑾峰彇PLC搴忓垪鍖栧櫒澶辫触");
+        }
+
+        try {
+            // 1. 浠庢暟鎹簱鏌ヨ鏈�杩戞壂鐮佺殑鐜荤拑淇℃伅锛堟渶杩�1鍒嗛挓鍐呯殑璁板綍锛�
+            List<GlassInfo> recentGlasses = queryRecentScannedGlasses(deviceConfig, logicParams);
+            if (recentGlasses.isEmpty()) {
+                return buildResult(deviceConfig, "checkAndProcess", true, 
+                        "鏆傛棤寰呭鐞嗙殑鐜荤拑淇℃伅");
+            }
+
+            log.info("鏌ヨ鍒版渶杩戞壂鐮佺殑鐜荤拑: deviceId={}, count={}", 
+                    deviceId, recentGlasses.size());
+
+            // 2. 鏇存柊缂撳啿闃熷垪鍜屾渶鍚庢壂鐮佹椂闂�
+            updateBuffer(deviceId, recentGlasses);
+            lastScanTime.put(deviceId, new AtomicLong(System.currentTimeMillis()));
+
+            // 3. 妫�鏌ユ槸鍚﹂渶瑕佺珛鍗冲鐞嗭紙瀹归噺宸叉弧鎴�30s鍐呮棤鏂扮幓鐠冿級
+            List<GlassBufferItem> buffer = glassBuffer.get(deviceId);
+            if (buffer == null || buffer.isEmpty()) {
+                return buildResult(deviceConfig, "checkAndProcess", true, 
+                        "缂撳啿闃熷垪涓虹┖");
+            }
+
+            // 4. 鍒ゆ柇鏄惁婊¤冻澶勭悊鏉′欢
+            boolean shouldProcess = shouldProcessBatch(deviceId, buffer, config);
+            if (!shouldProcess) {
+                return buildResult(deviceConfig, "checkAndProcess", true, 
+                        "绛夊緟鏇村鐜荤拑鎴�30s瓒呮椂");
+            }
+
+            // 5. 瀹归噺鍒ゆ柇鍜屾壒娆$粍瑁�
+            List<GlassInfo> batch = assembleBatch(buffer, config.getVehicleCapacity());
+            if (batch.isEmpty()) {
+                return buildResult(deviceConfig, "checkAndProcess", false, 
+                        "鏃犳硶缁勮鏈夋晥鎵规锛堝閲忎笉瓒筹級");
+            }
+
+            // 6. 鍐欏叆PLC
+            DevicePlcVO.OperationResult writeResult = writeBatchToPlc(
+                    deviceConfig, batch, serializer, logicParams);
+            
+            if (!Boolean.TRUE.equals(writeResult.getSuccess())) {
+                return writeResult;
+            }
+
+            // 7. 浠庣紦鍐查槦鍒椾腑绉婚櫎宸插鐞嗙殑鐜荤拑
+            removeProcessedGlasses(deviceId, batch);
+
+            String msg = String.format("鎵规宸插啓鍏LC: glassCount=%d, glassIds=%s", 
+                    batch.size(), 
+                    batch.stream().map(GlassInfo::getGlassId).collect(Collectors.joining(",")));
+            return buildResult(deviceConfig, "checkAndProcess", true, msg);
+
+        } catch (Exception e) {
+            log.error("妫�鏌ュ苟澶勭悊鐜荤拑鎵规寮傚父: deviceId={}", deviceId, e);
+            return buildResult(deviceConfig, "checkAndProcess", false, 
+                    "澶勭悊寮傚父: " + e.getMessage());
+        }
+    }
+
+    /**
+     * 鏌ヨ鏈�杩戞壂鐮佺殑鐜荤拑淇℃伅
+     */
+    private List<GlassInfo> queryRecentScannedGlasses(
+            DeviceConfig deviceConfig,
+            Map<String, Object> logicParams) {
+        
+        if (glassInfoMapper == null) {
+            log.warn("GlassInfoMapper鏈敞鍏ワ紝鏃犳硶鏌ヨ鏈�杩戞壂鐮佺殑鐜荤拑");
+            return Collections.emptyList();
+        }
+        
+        try {
+            // 浠庨厤缃腑鑾峰彇workLine锛岀敤浜庤繃婊�
+            String workLine = getLogicParam(logicParams, "workLine", null);
+            
+            // 鏌ヨ鏈�杩�2鍒嗛挓鍐呯殑鐜荤拑璁板綍锛堟墿澶ф椂闂寸獥鍙o紝纭繚涓嶉仐婕忥級
+            Date twoMinutesAgo = new Date(System.currentTimeMillis() - 120000);
+            
+            LambdaQueryWrapper<GlassInfo> wrapper = new LambdaQueryWrapper<>();
+            wrapper.eq(GlassInfo::getStatus, GlassInfo.Status.ACTIVE)
+                   .ge(GlassInfo::getCreatedTime, twoMinutesAgo)
+                   .orderByDesc(GlassInfo::getCreatedTime)
+                   .last("LIMIT 20"); // 闄愬埗鏌ヨ鏁伴噺锛岄伩鍏嶈繃澶�
+            
+            // 濡傛灉閰嶇疆浜唚orkLine锛屽垯杩囨护description
+            if (workLine != null && !workLine.isEmpty()) {
+                wrapper.like(GlassInfo::getDescription, "workLine=" + workLine);
+            }
+            
+            List<GlassInfo> recentGlasses = glassInfoMapper.selectList(wrapper);
+            
+            log.debug("鏌ヨ鍒版渶杩戞壂鐮佺殑鐜荤拑: deviceId={}, workLine={}, count={}", 
+                    deviceConfig.getId(), workLine, recentGlasses.size());
+            
+            return recentGlasses;
+            
+        } catch (Exception e) {
+            log.error("鏌ヨ鏈�杩戞壂鐮佺殑鐜荤拑淇℃伅寮傚父: deviceId={}", 
+                    deviceConfig.getId(), e);
+            return Collections.emptyList();
+        }
+    }
+
+    /**
+     * 鏇存柊缂撳啿闃熷垪
+     */
+    private void updateBuffer(String deviceId, List<GlassInfo> newGlasses) {
+        List<GlassBufferItem> buffer = glassBuffer.computeIfAbsent(
+                deviceId, k -> new CopyOnWriteArrayList<>());
+        
+        Set<String> existingIds = buffer.stream()
+                .map(item -> item.glassInfo.getGlassId())
+                .collect(Collectors.toSet());
+        
+        for (GlassInfo glass : newGlasses) {
+            if (!existingIds.contains(glass.getGlassId())) {
+                buffer.add(new GlassBufferItem(glass, System.currentTimeMillis()));
+                log.debug("娣诲姞鐜荤拑鍒扮紦鍐查槦鍒�: deviceId={}, glassId={}", 
+                        deviceId, glass.getGlassId());
+            }
+        }
+    }
+
+    /**
+     * 鍒ゆ柇鏄惁搴旇澶勭悊鎵规
+     */
+    private boolean shouldProcessBatch(String deviceId, 
+                                      List<GlassBufferItem> buffer,
+                                      WorkstationLogicConfig config) {
+        // 鏉′欢1锛氱紦鍐查槦鍒楀凡婊★紙杈惧埌瀹归噺闄愬埗锛�
+        int totalLength = buffer.stream()
+                .mapToInt(item -> item.glassInfo.getGlassLength() != null ? 
+                        item.glassInfo.getGlassLength() : 0)
+                .sum();
+        if (totalLength >= config.getVehicleCapacity()) {
+            log.info("缂撳啿闃熷垪瀹归噺宸叉弧锛岃Е鍙戞壒娆″鐞�: deviceId={}, totalLength={}, capacity={}", 
+                    deviceId, totalLength, config.getVehicleCapacity());
+            return true;
+        }
+
+        // 鏉′欢2锛�30s鍐呮棤鏂扮幓鐠冩壂鐮�
+        AtomicLong lastTime = lastScanTime.get(deviceId);
+        if (lastTime != null) {
+            long elapsed = System.currentTimeMillis() - lastTime.get();
+            if (elapsed >= config.getTransferDelayMs()) {
+                log.info("30s鍐呮棤鏂扮幓鐠冩壂鐮侊紝瑙﹀彂鎵规澶勭悊: deviceId={}, elapsed={}ms", 
+                        deviceId, elapsed);
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * 缁勮鎵规锛堝閲忓垽鏂級
+     */
+    private List<GlassInfo> assembleBatch(List<GlassBufferItem> buffer, 
+                                          int vehicleCapacity) {
+        List<GlassInfo> batch = new ArrayList<>();
+        int usedLength = 0;
+        
+        for (GlassBufferItem item : buffer) {
+            GlassInfo glass = item.glassInfo;
+            int glassLength = glass.getGlassLength() != null ? 
+                    glass.getGlassLength() : 0;
+            
+            if (usedLength + glassLength <= vehicleCapacity && batch.size() < 6) {
+                batch.add(glass);
+                usedLength += glassLength;
+            } else {
+                break;
+            }
+        }
+        
+        return batch;
+    }
+
+    /**
+     * 鍐欏叆鎵规鍒癙LC
+     */
+    private DevicePlcVO.OperationResult writeBatchToPlc(
+            DeviceConfig deviceConfig,
+            List<GlassInfo> batch,
+            EnhancedS7Serializer serializer,
+            Map<String, Object> logicParams) {
+        
+        Map<String, Object> payload = new HashMap<>();
+        
+        // 鍐欏叆鐜荤拑ID锛堟渶澶�6涓級
+        int count = Math.min(batch.size(), 6);
+        for (int i = 0; i < count; i++) {
+            String fieldName = "plcGlassId" + (i + 1);
+            payload.put(fieldName, batch.get(i).getGlassId());
+        }
+        
+        // 鍐欏叆鐜荤拑鏁伴噺
+        payload.put("plcGlassCount", count);
+        
+        // 鍐欏叆浣嶇疆淇℃伅锛堝鏋滄湁閰嶇疆锛�
+        Integer inPosition = getLogicParam(logicParams, "inPosition", null);
+        if (inPosition != null) {
+            payload.put("inPosition", inPosition);
+        }
+        
+        // 鍐欏叆璇锋眰瀛楋紙瑙﹀彂澶ц溅锛�
+        payload.put("plcRequest", 1);
+        
+        try {
+            plcDynamicDataService.writePlcData(deviceConfig, payload, serializer);
+            log.info("鎵规宸插啓鍏LC: deviceId={}, glassCount={}", 
+                    deviceConfig.getId(), count);
+            return buildResult(deviceConfig, "writeBatchToPlc", true, 
+                    "鎵规鍐欏叆鎴愬姛");
+        } catch (Exception e) {
+            log.error("鍐欏叆鎵规鍒癙LC澶辫触: deviceId={}", deviceConfig.getId(), e);
+            return buildResult(deviceConfig, "writeBatchToPlc", false, 
+                    "鍐欏叆澶辫触: " + e.getMessage());
+        }
+    }
+
+    /**
+     * 浠庣紦鍐查槦鍒楃Щ闄ゅ凡澶勭悊鐨勭幓鐠�
+     */
+    private void removeProcessedGlasses(String deviceId, List<GlassInfo> processed) {
+        List<GlassBufferItem> buffer = glassBuffer.get(deviceId);
+        if (buffer == null) {
+            return;
+        }
+        
+        Set<String> processedIds = processed.stream()
+                .map(GlassInfo::getGlassId)
+                .collect(Collectors.toSet());
+        
+        buffer.removeIf(item -> processedIds.contains(item.glassInfo.getGlassId()));
+    }
+
+    /**
+     * 鍚姩鐩戞帶浠诲姟锛堝畾鏈熸鏌ュ苟澶勭悊锛�
+     */
+    private DevicePlcVO.OperationResult handleStartMonitor(
+            DeviceConfig deviceConfig,
+            WorkstationLogicConfig config,
+            Map<String, Object> logicParams) {
+        
+        String deviceId = deviceConfig.getDeviceId();
+        
+        // 鍋滄鏃х殑鐩戞帶浠诲姟
+        handleStopMonitor(deviceConfig);
+        
+        // 鑾峰彇鐩戞帶闂撮殧
+        Integer monitorIntervalMs = getLogicParam(logicParams, "monitorIntervalMs", 
+                config.getScanIntervalMs());
+        
+        // 鍚姩鐩戞帶浠诲姟
+        ScheduledFuture<?> future = monitorExecutor.scheduleWithFixedDelay(() -> {
+            try {
+                handleCheckAndProcess(deviceConfig, config, logicParams);
+            } catch (Exception e) {
+                log.error("鐩戞帶浠诲姟鎵ц寮傚父: deviceId={}", deviceId, e);
+            }
+        }, monitorIntervalMs, monitorIntervalMs, TimeUnit.MILLISECONDS);
+        
+        monitorTasks.put(deviceId, future);
+        log.info("宸插惎鍔ㄥ崸杞珛鐩戞帶浠诲姟: deviceId={}, interval={}ms", 
+                deviceId, monitorIntervalMs);
+        
+        return buildResult(deviceConfig, "startMonitor", true, 
+                "鐩戞帶浠诲姟宸插惎鍔�");
+    }
+
+    /**
+     * 鍋滄鐩戞帶浠诲姟
+     */
+    private DevicePlcVO.OperationResult handleStopMonitor(DeviceConfig deviceConfig) {
+        String deviceId = deviceConfig.getDeviceId();
+        ScheduledFuture<?> future = monitorTasks.remove(deviceId);
+        if (future != null && !future.isCancelled()) {
+            future.cancel(false);
+            log.info("宸插仠姝㈠崸杞珛鐩戞帶浠诲姟: deviceId={}", deviceId);
+        }
+        return buildResult(deviceConfig, "stopMonitor", true, "鐩戞帶浠诲姟宸插仠姝�");
+    }
+
+    /**
+     * 娓呯┖缂撳啿闃熷垪
+     */
+    private DevicePlcVO.OperationResult handleClearBuffer(DeviceConfig deviceConfig) {
+        String deviceId = deviceConfig.getDeviceId();
+        glassBuffer.remove(deviceId);
+        lastScanTime.remove(deviceId);
+        log.info("宸叉竻绌虹紦鍐查槦鍒�: deviceId={}", deviceId);
+        return buildResult(deviceConfig, "clearBuffer", true, "缂撳啿闃熷垪宸叉竻绌�");
+    }
+
+    /**
+     * 鏋勫缓鎿嶄綔缁撴灉
+     */
+    private DevicePlcVO.OperationResult buildResult(DeviceConfig deviceConfig,
+                                                    String operation,
+                                                    boolean success,
+                                                    String message) {
+        return DevicePlcVO.OperationResult.builder()
+                .deviceId(deviceConfig.getId())
+                .deviceName(deviceConfig.getDeviceName())
+                .deviceCode(deviceConfig.getDeviceCode())
+                .projectId(deviceConfig.getProjectId() != null ? 
+                        String.valueOf(deviceConfig.getProjectId()) : null)
+                .operation(operation)
+                .success(success)
+                .message(message)
+                .timestamp(LocalDateTime.now())
+                .build();
+    }
+
+    /**
+     * 搴旂敤鍏抽棴鏃舵竻鐞嗚祫婧�
+     */
+    @PreDestroy
+    public void destroy() {
+        log.info("姝e湪鍏抽棴鍗ц浆绔嬬洃鎺х嚎绋嬫睜...");
+        
+        // 鍋滄鎵�鏈夌洃鎺т换鍔�
+        for (String deviceId : new ArrayList<>(monitorTasks.keySet())) {
+            ScheduledFuture<?> future = monitorTasks.remove(deviceId);
+            if (future != null && !future.isCancelled()) {
+                future.cancel(false);
+            }
+        }
+        
+        // 鍏抽棴绾跨▼姹�
+        monitorExecutor.shutdown();
+        try {
+            if (!monitorExecutor.awaitTermination(5, TimeUnit.SECONDS)) {
+                monitorExecutor.shutdownNow();
+                if (!monitorExecutor.awaitTermination(5, TimeUnit.SECONDS)) {
+                    log.warn("鍗ц浆绔嬬洃鎺х嚎绋嬫睜鏈兘姝e父鍏抽棴");
+                }
+            }
+        } catch (InterruptedException e) {
+            monitorExecutor.shutdownNow();
+            Thread.currentThread().interrupt();
+        }
+        
+        log.info("鍗ц浆绔嬬洃鎺х嚎绋嬫睜宸插叧闂�");
+    }
+
+    /**
+     * 鐜荤拑缂撳啿椤�
+     */
+    private static class GlassBufferItem {
+        final GlassInfo glassInfo;
+        final long timestamp;
+
+        GlassBufferItem(GlassInfo glassInfo, long timestamp) {
+            this.glassInfo = glassInfo;
+            this.timestamp = timestamp;
+        }
+    }
+}
+
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/s7/provider/S7SerializerProvider.java b/mes-processes/mes-plcSend/src/main/java/com/mes/s7/provider/S7SerializerProvider.java
new file mode 100644
index 0000000..9eead5a
--- /dev/null
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/s7/provider/S7SerializerProvider.java
@@ -0,0 +1,90 @@
+package com.mes.s7.provider;
+
+import com.github.xingshuangs.iot.protocol.s7.enums.EPlcType;
+import com.github.xingshuangs.iot.protocol.s7.service.S7PLC;
+import com.mes.device.entity.DeviceConfig;
+import com.mes.s7.enhanced.EnhancedS7Serializer;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * 鎻愪緵 S7 搴忓垪鍖栧櫒鐨勫叕鍏卞伐鍘傦紝閬垮厤鍚勫閲嶅鍒涘缓/缂撳瓨
+ * @author huang
+ */
+@Slf4j
+@Component
+public class S7SerializerProvider {
+
+    private final ConcurrentMap<String, EnhancedS7Serializer> serializerCache = new ConcurrentHashMap<>();
+
+    /**
+     * 鑾峰彇鎴栧垱寤轰笌璁惧缁戝畾鐨� S7 搴忓垪鍖栧櫒
+     */
+    public EnhancedS7Serializer getSerializer(DeviceConfig deviceConfig) {
+        if (deviceConfig == null) {
+            log.error("璁惧閰嶇疆涓虹┖锛屾棤娉曡幏鍙朣7搴忓垪鍖栧櫒");
+            return null;
+        }
+
+        String cacheKey = buildCacheKey(deviceConfig);
+        return serializerCache.computeIfAbsent(cacheKey, key -> createSerializer(deviceConfig));
+    }
+
+    /**
+     * 娓呴櫎鎸囧畾璁惧瀵瑰簲鐨勭紦瀛�
+     */
+    public void clear(DeviceConfig deviceConfig) {
+        if (deviceConfig == null) {
+            return;
+        }
+        serializerCache.remove(buildCacheKey(deviceConfig));
+    }
+
+    private String buildCacheKey(DeviceConfig deviceConfig) {
+        if (deviceConfig.getId() != null) {
+            return "device:" + deviceConfig.getId();
+        }
+        if (deviceConfig.getDeviceCode() != null) {
+            return "device:" + deviceConfig.getDeviceCode();
+        }
+        return "device:" + Objects.hash(deviceConfig);
+    }
+
+    private EnhancedS7Serializer createSerializer(DeviceConfig deviceConfig) {
+        try {
+            EPlcType plcType = parsePlcType(deviceConfig.getPlcType());
+            String plcIp = deviceConfig.getPlcIp();
+            if (plcIp == null || plcIp.isEmpty()) {
+                log.warn("璁惧鏈厤缃甈LC IP锛屼娇鐢ㄩ粯璁ゅ�� 192.168.10.21, deviceId={}", deviceConfig.getId());
+                plcIp = "192.168.10.21";
+            }
+            S7PLC s7Plc = new S7PLC(plcType, plcIp);
+            EnhancedS7Serializer serializer = EnhancedS7Serializer.newInstance(s7Plc);
+            if (serializer == null) {
+                log.error("鍒涘缓EnhancedS7Serializer澶辫触: deviceId={}, plcIp={}, plcType={}",
+                        deviceConfig.getId(), plcIp, plcType);
+            }
+            return serializer;
+        } catch (Exception e) {
+            log.error("鍒涘缓S7搴忓垪鍖栧櫒寮傚父: deviceId={}", deviceConfig.getId(), e);
+            return null;
+        }
+    }
+
+    private EPlcType parsePlcType(String plcTypeValue) {
+        if (plcTypeValue == null || plcTypeValue.isEmpty()) {
+            return EPlcType.S1200;
+        }
+        try {
+            return EPlcType.valueOf(plcTypeValue);
+        } catch (IllegalArgumentException e) {
+            log.warn("鏈煡鐨凱LC绫诲瀷: {}锛屼娇鐢ㄩ粯璁ょ被鍨婼1200", plcTypeValue);
+            return EPlcType.S1200;
+        }
+    }
+}
+
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/task/controller/TaskStatusNotificationController.java b/mes-processes/mes-plcSend/src/main/java/com/mes/task/controller/TaskStatusNotificationController.java
index b5247b8..cdaa096 100644
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/task/controller/TaskStatusNotificationController.java
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/task/controller/TaskStatusNotificationController.java
@@ -16,15 +16,17 @@
  * @since 2025-01-XX
  */
 @RestController
-@RequestMapping("/api/plcSend/task/notification")
+@RequestMapping("task/notification")
 @Api(tags = "浠诲姟鐘舵�侀�氱煡")
 @RequiredArgsConstructor
+@CrossOrigin(origins = "*", maxAge = 3600)
 public class TaskStatusNotificationController {
 
     private final TaskStatusNotificationService notificationService;
 
     @GetMapping(value = "/sse", produces = "text/event-stream")
     @ApiOperation("鍒涘缓SSE杩炴帴锛岀洃鍚换鍔$姸鎬佸彉鍖�")
+    @CrossOrigin(origins = "*")
     public SseEmitter createConnection(@RequestParam(required = false) String taskId) {
         SseEmitter emitter = notificationService.createConnection(taskId);
         if (emitter == null) {
@@ -33,8 +35,15 @@
         return emitter;
     }
 
+    @RequestMapping(value = "/sse", method = RequestMethod.OPTIONS)
+    @CrossOrigin(origins = "*")
+    public void options() {
+        // 澶勭悊 OPTIONS 棰勬璇锋眰
+    }
+
     @GetMapping(value = "/sse/all", produces = "text/event-stream")
     @ApiOperation("鍒涘缓SSE杩炴帴锛岀洃鍚墍鏈変换鍔$姸鎬佸彉鍖�")
+    @CrossOrigin(origins = "*")
     public SseEmitter createConnectionForAllTasks() {
         return createConnection(null);
     }
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 2919b0f..8f995c0 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
@@ -769,7 +769,7 @@
         switch (device.getDeviceType()) {
             case DeviceConfig.DeviceType.LOAD_VEHICLE:
                 context.setLoadedGlassIds(glassIds);
-                // 鏁版嵁浼犻�掞細涓婂ぇ杞� -> 涓嬩竴涓澶�
+                // 鏁版嵁浼犻�掞細澶ц溅璁惧 -> 涓嬩竴涓澶�
                 if (!CollectionUtils.isEmpty(glassIds)) {
                     Map<String, Object> transferData = new HashMap<>();
                     transferData.put("glassIds", glassIds);
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 6792e3a..2cdac06 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
@@ -22,6 +22,7 @@
 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;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.util.CollectionUtils;
@@ -69,6 +70,7 @@
             throw new IllegalArgumentException("鑷冲皯闇�瑕侀厤缃竴鏉$幓鐠僆D");
         }
 
+        // 鍒涘缓浠诲姟璁板綍
         MultiDeviceTask task = new MultiDeviceTask();
         task.setTaskId(generateTaskId(groupConfig));
         task.setGroupId(String.valueOf(groupConfig.getId()));
@@ -79,11 +81,39 @@
         task.setStartTime(new Date());
         save(task);
 
+        // 寮傛鎵ц浠诲姟锛岀珛鍗宠繑鍥炰换鍔D
+        executeTaskAsync(task, groupConfig, devices, parameters);
+
+        log.info("璁惧缁勪换鍔″凡鍚姩锛堝紓姝ユ墽琛岋級: taskId={}, groupId={}, groupName={}", 
+            task.getTaskId(), groupConfig.getId(), groupConfig.getGroupName());
+        
+        return task;
+    }
+
+    /**
+     * 寮傛鎵ц璁惧缁勪换鍔�
+     * 姣忎釜璁惧缁勪綔涓虹嫭绔嬬嚎绋嬫墽琛岋紝浜掍笉闃诲
+     */
+    @Async("deviceGroupTaskExecutor")
+    public void executeTaskAsync(MultiDeviceTask task, 
+                                  DeviceGroupConfig groupConfig, 
+                                  List<DeviceConfig> devices, 
+                                  TaskParameters parameters) {
         try {
+            log.info("寮�濮嬫墽琛岃澶囩粍浠诲姟: taskId={}, groupId={}, deviceCount={}", 
+                task.getTaskId(), groupConfig.getId(), devices.size());
+            
+            // 鏇存柊浠诲姟鐘舵�佷负杩愯涓�
+            task.setStatus(MultiDeviceTask.Status.RUNNING.name());
+            updateById(task);
+            
             // 閫氱煡浠诲姟寮�濮�
             notificationService.notifyTaskStatus(task);
             
+            // 鎵ц浠诲姟
             TaskExecutionResult result = taskExecutionEngine.execute(task, groupConfig, devices, parameters);
+            
+            // 鏇存柊浠诲姟缁撴灉
             task.setStatus(result.isSuccess() ? MultiDeviceTask.Status.COMPLETED.name() : MultiDeviceTask.Status.FAILED.name());
             task.setErrorMessage(result.isSuccess() ? null : result.getMessage());
             task.setEndTime(new Date());
@@ -93,14 +123,20 @@
             // 閫氱煡浠诲姟瀹屾垚
             notificationService.notifyTaskStatus(task);
             
-            return task;
+            log.info("璁惧缁勪换鍔℃墽琛屽畬鎴�: taskId={}, success={}, message={}", 
+                task.getTaskId(), result.isSuccess(), result.getMessage());
+            
         } catch (Exception ex) {
-            log.error("澶氳澶囦换鍔℃墽琛屽紓甯�, taskId={}", task.getTaskId(), ex);
+            log.error("璁惧缁勪换鍔℃墽琛屽紓甯�: taskId={}, groupId={}", task.getTaskId(), groupConfig.getId(), ex);
+            
+            // 鏇存柊浠诲姟鐘舵�佷负澶辫触
             task.setStatus(MultiDeviceTask.Status.FAILED.name());
             task.setErrorMessage(ex.getMessage());
             task.setEndTime(new Date());
             updateById(task);
-            throw new RuntimeException("澶氳澶囦换鍔℃墽琛屽け璐�: " + ex.getMessage(), ex);
+            
+            // 閫氱煡浠诲姟澶辫触
+            notificationService.notifyTaskStatus(task);
         }
     }
 
diff --git a/mes-web/src/utils/constants.js b/mes-web/src/utils/constants.js
index ad93a89..d33355f 100644
--- a/mes-web/src/utils/constants.js
+++ b/mes-web/src/utils/constants.js
@@ -1,6 +1,6 @@
 // export const WebSocketHost = "10.153.19.150";
 // export const WebSocketHost = "172.17.2.7";
-export const WebSocketHost = "10.153.19.49";//hxl
+export const WebSocketHost = "10.153.19.225";//hxl
 // export const WebSocketHost = "10.153.19.2";//zt
 //export const WebSocketHost = "10.153.19.20";//wsx
 // export const WebSocketHost = "127.0.0.1";
diff --git a/mes-web/src/views/device/DeviceConfigList.vue b/mes-web/src/views/device/DeviceConfigList.vue
index 6050c82..07d95a6 100644
--- a/mes-web/src/views/device/DeviceConfigList.vue
+++ b/mes-web/src/views/device/DeviceConfigList.vue
@@ -190,8 +190,8 @@
       pageNum: pagination.page,
       pageSize: pagination.size,
       deviceType: searchForm.deviceType || undefined,
-      status: searchForm.deviceStatus || undefined,
-      deviceCode: searchForm.keyword || undefined
+      deviceStatus: searchForm.deviceStatus || undefined,
+      keyword: searchForm.keyword?.trim() || undefined
     }
     
     const response = await deviceConfigApi.getList(params)
diff --git a/mes-web/src/views/device/DeviceEditDialog.vue b/mes-web/src/views/device/DeviceEditDialog.vue
index 888cc2b..494fa61 100644
--- a/mes-web/src/views/device/DeviceEditDialog.vue
+++ b/mes-web/src/views/device/DeviceEditDialog.vue
@@ -41,9 +41,10 @@
 
             <el-form-item label="璁惧绫诲瀷" prop="deviceType">
               <el-select v-model="deviceForm.deviceType" placeholder="閫夋嫨璁惧绫诲瀷" style="width: 100%;">
-                <el-option label="涓婂ぇ杞�" value="涓婂ぇ杞�" />
-                <el-option label="澶х悊鐗�" value="澶х悊鐗�" />
-                <el-option label="鐜荤拑瀛樺偍" value="鐜荤拑瀛樺偍" />
+                <el-option label="澶ц溅璁惧" value="澶ц溅璁惧" />
+                <el-option label="澶х悊鐗囩" value="澶х悊鐗囩" />
+                <el-option label="鍗ц浆绔嬫壂鐮�" value="鍗ц浆绔嬫壂鐮�" />
+                <el-option label="鍗ц浆绔�" value="鍗ц浆绔�" />
               </el-select>
             </el-form-item>
 
@@ -51,10 +52,7 @@
               <el-select v-model="deviceForm.plcType" placeholder="閫夋嫨PLC绫诲瀷" style="width: 100%;" clearable>
                 <el-option label="瑗块棬瀛� S7-1200" value="S1200" />
                 <el-option label="瑗块棬瀛� S7-1500" value="S1500" />
-                <el-option label="瑗块棬瀛� S7-400" value="S400" />
-                <el-option label="瑗块棬瀛� S7-300" value="S300" />
-                <el-option label="瑗块棬瀛� S7-200" value="S200" />
-                <el-option label="瑗块棬瀛� S7-200 SMART" value="S200_SMART" />
+                <el-option label="Modbus 鎺у埗鍣�" value="MODBUS" />
               </el-select>
             </el-form-item>
 
@@ -254,204 +252,19 @@
           <span class="form-tip">鏍规嵁璁惧绫诲瀷閰嶇疆鐗瑰畾鐨勪笟鍔¢�昏緫鍙傛暟</span>
         </template>
 
-        <!-- 涓婂ぇ杞﹁澶囬�昏緫閰嶇疆 -->
-        <div v-if="deviceForm.deviceType === '涓婂ぇ杞�'">
-          <el-row :gutter="20">
-            <el-col :span="12">
-              <el-form-item label="杞﹁締瀹归噺">
-                <el-input-number
-                  v-model="deviceLogicParams.vehicleCapacity"
-                  :min="1"
-                  :max="10000"
-                  :step="100"
-                  style="width: 100%;"
+        <!-- 浣跨敤鍔ㄦ�佺粍浠跺姞杞藉搴旇澶囩被鍨嬬殑閰嶇疆缁勪欢 -->
+        <component
+          :is="deviceConfigComponent"
+          v-if="deviceConfigComponent"
+          v-model="deviceLogicParams"
                 />
-                <span class="form-tip">杞﹁締鏈�澶у閲�</span>
-              </el-form-item>
-            </el-col>
-            <el-col :span="12">
-              <el-form-item label="鐜荤拑闂撮殧(ms)">
-                <el-input-number
-                  v-model="deviceLogicParams.glassIntervalMs"
-                  :min="100"
-                  :max="10000"
-                  :step="100"
-                  style="width: 100%;"
-                />
-                <span class="form-tip">鐜荤拑涓婃枡闂撮殧鏃堕棿锛堟绉掞級</span>
-              </el-form-item>
-            </el-col>
-          </el-row>
-          <el-row :gutter="20">
-            <el-col :span="12">
-              <el-form-item label="榛樿鐜荤拑闀垮害(mm)">
-                <el-input-number
-                  v-model="deviceLogicParams.defaultGlassLength"
-                  :min="100"
-                  :max="10000"
-                  :step="100"
-                  style="width: 100%;"
-                />
-                <span class="form-tip">褰撶幓鐠冩湭鎻愪緵闀垮害鏃朵娇鐢ㄧ殑榛樿鍊�</span>
-              </el-form-item>
-            </el-col>
-            <el-col :span="12">
-              <el-form-item label="鑷姩涓婃枡">
-                <el-switch v-model="deviceLogicParams.autoFeed" />
-                <span class="form-tip">鏄惁鑷姩瑙﹀彂涓婃枡璇锋眰</span>
-              </el-form-item>
-            </el-col>
-          </el-row>
-          <el-row :gutter="20">
-            <el-col :span="12">
-              <el-form-item label="鏈�澶ч噸璇曟鏁�">
-                <el-input-number
-                  v-model="deviceLogicParams.maxRetryCount"
-                  :min="0"
-                  :max="10"
-                  :step="1"
-                  style="width: 100%;"
-                />
-              </el-form-item>
-            </el-col>
-          </el-row>
-          <el-form-item label="浣嶇疆鏄犲皠">
-            <div class="position-mapping">
-              <div
-                v-for="(value, key, index) in deviceLogicParams.positionMapping"
-                :key="index"
-                class="mapping-item"
-              >
-                <el-input
-                  v-model="mappingKeys[index]"
-                  placeholder="浣嶇疆浠g爜"
-                  size="small"
-                  style="width: 150px; margin-right: 10px;"
-                  @input="updatePositionMapping(index, $event, value)"
-                />
-                <el-input-number
-                  v-model="deviceLogicParams.positionMapping[mappingKeys[index] || key]"
-                  :min="0"
-                  :max="100"
-                  size="small"
-                  style="width: 120px; margin-right: 10px;"
-                />
-                <el-button
-                  type="danger"
-                  size="small"
-                  @click="removePositionMapping(key)"
-                >
-                  鍒犻櫎
-                </el-button>
-              </div>
-              <el-button type="primary" size="small" @click="addPositionMapping">
-                娣诲姞浣嶇疆鏄犲皠
-              </el-button>
-            </div>
-          </el-form-item>
-        </div>
-
-        <!-- 澶х悊鐗囪澶囬�昏緫閰嶇疆 -->
-        <div v-if="deviceForm.deviceType === '澶х悊鐗�'">
-          <el-row :gutter="20">
-            <el-col :span="12">
-              <el-form-item label="鐜荤拑灏哄">
-                <el-input-number
-                  v-model="deviceLogicParams.glassSize"
-                  :min="100"
-                  :max="5000"
-                  :step="100"
-                  style="width: 100%;"
-                />
-                <span class="form-tip">鐜荤拑灏哄锛坢m锛�</span>
-              </el-form-item>
-            </el-col>
-            <el-col :span="12">
-              <el-form-item label="澶勭悊鏃堕棿(ms)">
-                <el-input-number
-                  v-model="deviceLogicParams.processingTime"
-                  :min="1000"
-                  :max="60000"
-                  :step="1000"
-                  style="width: 100%;"
-                />
-                <span class="form-tip">鐜荤拑澶勭悊鏃堕棿锛堟绉掞級</span>
-              </el-form-item>
-            </el-col>
-          </el-row>
-          <el-row :gutter="20">
-            <el-col :span="12">
-              <el-form-item label="鑷姩澶勭悊">
-                <el-switch v-model="deviceLogicParams.autoProcess" />
-                <span class="form-tip">鏄惁鑷姩瑙﹀彂澶勭悊璇锋眰</span>
-              </el-form-item>
-            </el-col>
-            <el-col :span="12">
-              <el-form-item label="鏈�澶ч噸璇曟鏁�">
-                <el-input-number
-                  v-model="deviceLogicParams.maxRetryCount"
-                  :min="0"
-                  :max="10"
-                  :step="1"
-                  style="width: 100%;"
-                />
-              </el-form-item>
-            </el-col>
-          </el-row>
-        </div>
-
-        <!-- 鐜荤拑瀛樺偍璁惧閫昏緫閰嶇疆 -->
-        <div v-if="deviceForm.deviceType === '鐜荤拑瀛樺偍'">
-          <el-row :gutter="20">
-            <el-col :span="12">
-              <el-form-item label="瀛樺偍瀹归噺">
-                <el-input-number
-                  v-model="deviceLogicParams.storageCapacity"
-                  :min="1"
-                  :max="1000"
-                  :step="1"
-                  style="width: 100%;"
-                />
-                <span class="form-tip">鏈�澶у瓨鍌ㄦ暟閲�</span>
-              </el-form-item>
-            </el-col>
-            <el-col :span="12">
-              <el-form-item label="鍙栬揣妯″紡">
-                <el-select v-model="deviceLogicParams.retrievalMode" style="width: 100%;">
-                  <el-option label="鍏堣繘鍏堝嚭 (FIFO)" value="FIFO" />
-                  <el-option label="鍚庤繘鍏堝嚭 (LIFO)" value="LIFO" />
-                  <el-option label="闅忔満 (RANDOM)" value="RANDOM" />
-                </el-select>
-              </el-form-item>
-            </el-col>
-          </el-row>
-          <el-row :gutter="20">
-            <el-col :span="12">
-              <el-form-item label="鑷姩瀛樺偍">
-                <el-switch v-model="deviceLogicParams.autoStore" />
-                <span class="form-tip">鏄惁鑷姩瑙﹀彂瀛樺偍璇锋眰</span>
-              </el-form-item>
-            </el-col>
-            <el-col :span="12">
-              <el-form-item label="鑷姩鍙栬揣">
-                <el-switch v-model="deviceLogicParams.autoRetrieve" />
-                <span class="form-tip">鏄惁鑷姩瑙﹀彂鍙栬揣璇锋眰</span>
-              </el-form-item>
-            </el-col>
-          </el-row>
-          <el-row :gutter="20">
-            <el-col :span="12">
-              <el-form-item label="鏈�澶ч噸璇曟鏁�">
-                <el-input-number
-                  v-model="deviceLogicParams.maxRetryCount"
-                  :min="0"
-                  :max="10"
-                  :step="1"
-                  style="width: 100%;"
-                />
-              </el-form-item>
-            </el-col>
-          </el-row>
+        <div v-else class="no-config-tip">
+          <el-alert
+            :title="`璁惧绫诲瀷銆�${deviceForm.deviceType}銆嶆殏鏃犻厤缃粍浠禶"
+            type="info"
+            :closable="false"
+            show-icon
+          />
         </div>
       </el-card>
 
@@ -510,6 +323,7 @@
 import { ref, reactive, watch, computed } from 'vue'
 import { ElMessage } from 'element-plus'
 import { deviceConfigApi } from '@/api/device/deviceManagement'
+import { getDeviceConfigComponent } from './components/DeviceLogicConfig'
 
 // Props瀹氫箟
 const props = defineProps({
@@ -534,27 +348,18 @@
 const testResult = ref(null)
 
 // 璁惧閫昏緫鍙傛暟锛堟牴鎹澶囩被鍨嬪姩鎬佹樉绀猴級
-const deviceLogicParams = reactive({
-  // 涓婂ぇ杞﹀弬鏁�
-  vehicleCapacity: 6000,
-  glassIntervalMs: 1000,
-  defaultGlassLength: 2000,
-  autoFeed: true,
-  maxRetryCount: 5,
-  positionMapping: {},
-  // 澶х悊鐗囧弬鏁�
-  glassSize: 2000,
-  processingTime: 5000,
-  autoProcess: true,
-  // 鐜荤拑瀛樺偍鍙傛暟
-  storageCapacity: 100,
-  retrievalMode: 'FIFO',
-  autoStore: true,
-  autoRetrieve: true
-})
+const deviceLogicParams = reactive({})
 
-// 浣嶇疆鏄犲皠鐨勯敭鏁扮粍锛堢敤浜巚-for锛�
-const mappingKeys = ref([])
+const S7_PLC_TYPES = ['S1200', 'S1500']
+const MODBUS_PLC_TYPES = ['MODBUS']
+
+// 璁$畻灞炴�э細鏍规嵁璁惧绫诲瀷鑾峰彇瀵瑰簲鐨勯厤缃粍浠�
+const deviceConfigComponent = computed(() => {
+  if (!deviceForm.deviceType) {
+    return null
+  }
+  return getDeviceConfigComponent(deviceForm.deviceType)
+})
 
 // 璁惧琛ㄥ崟鏁版嵁
 const getDefaultForm = () => ({
@@ -660,22 +465,37 @@
 
 // 鐩戝惉PLC绫诲瀷鍙樺寲锛岃嚜鍔ㄨ缃�氳鍗忚
 watch(() => deviceForm.plcType, (newPlcType) => {
-  // 濡傛灉閫夋嫨鐨勬槸S7绯诲垪PLC锛岃嚜鍔ㄨ缃�氳鍗忚涓篠7 Communication
-  if (newPlcType && (newPlcType.startsWith('S') || newPlcType.includes('S7'))) {
-    if (!deviceForm.protocolType || deviceForm.protocolType === '鍏朵粬') {
+  if (!newPlcType) {
+    return
+  }
+
+  if (S7_PLC_TYPES.includes(newPlcType)) {
+    if (!deviceForm.protocolType || deviceForm.protocolType === '鍏朵粬' || deviceForm.protocolType === 'Modbus TCP') {
       deviceForm.protocolType = 'S7 Communication'
+    }
+    return
+  }
+
+  if (MODBUS_PLC_TYPES.includes(newPlcType)) {
+    if (!deviceForm.protocolType || deviceForm.protocolType === '鍏朵粬' || deviceForm.protocolType === 'S7 Communication') {
+      deviceForm.protocolType = 'Modbus TCP'
     }
   }
 })
 
 // 澶勭悊閫氳鍗忚鍙樺寲
 const handleProtocolTypeChange = (value) => {
-  // 濡傛灉閫夋嫨浜嗛潪S7鍗忚锛屼絾PLC绫诲瀷鏄疭7绯诲垪锛岀粰鍑烘彁绀�
-  if (value && value !== 'S7 Communication' && deviceForm.plcType) {
-    const s7Types = ['S1200', 'S1500', 'S400', 'S300', 'S200', 'S200_SMART']
-    if (s7Types.includes(deviceForm.plcType)) {
-      ElMessage.warning('S7绯诲垪PLC閫氬父浣跨敤S7 Communication鍗忚锛岃纭鍗忚閫夋嫨鏄惁姝g‘')
-    }
+  if (!deviceForm.plcType || !value) {
+    return
+  }
+
+  if (value !== 'S7 Communication' && S7_PLC_TYPES.includes(deviceForm.plcType)) {
+    ElMessage.warning('S7绯诲垪PLC閫氬父浣跨敤S7 Communication鍗忚锛岃纭鍗忚閫夋嫨鏄惁姝g‘')
+    return
+  }
+
+  if (value !== 'Modbus TCP' && MODBUS_PLC_TYPES.includes(deviceForm.plcType)) {
+    ElMessage.warning('Modbus 绫诲瀷PLC閫氬父浣跨敤 Modbus TCP 鍗忚锛岃纭鍗忚閫夋嫨鏄惁姝g‘')
   }
 }
 
@@ -824,72 +644,26 @@
 
 // 鍔犺浇璁惧閫昏緫鍙傛暟
 const loadDeviceLogicParams = (deviceLogic, deviceType) => {
-  if (deviceType === '涓婂ぇ杞�') {
-    deviceLogicParams.vehicleCapacity = deviceLogic.vehicleCapacity ?? 6000
-    deviceLogicParams.glassIntervalMs = deviceLogic.glassIntervalMs ?? 1000
-    deviceLogicParams.defaultGlassLength = deviceLogic.defaultGlassLength ?? 2000
-    deviceLogicParams.autoFeed = deviceLogic.autoFeed ?? true
-    deviceLogicParams.maxRetryCount = deviceLogic.maxRetryCount ?? 5
-    deviceLogicParams.positionMapping = deviceLogic.positionMapping || {}
-    mappingKeys.value = Object.keys(deviceLogicParams.positionMapping)
-  } else if (deviceType === '澶х悊鐗�') {
-    deviceLogicParams.glassSize = deviceLogic.glassSize ?? 2000
-    deviceLogicParams.processingTime = deviceLogic.processingTime ?? 5000
-    deviceLogicParams.autoProcess = deviceLogic.autoProcess ?? true
-    deviceLogicParams.maxRetryCount = deviceLogic.maxRetryCount ?? 3
-  } else if (deviceType === '鐜荤拑瀛樺偍') {
-    deviceLogicParams.storageCapacity = deviceLogic.storageCapacity ?? 100
-    deviceLogicParams.retrievalMode = deviceLogic.retrievalMode || 'FIFO'
-    deviceLogicParams.autoStore = deviceLogic.autoStore ?? true
-    deviceLogicParams.autoRetrieve = deviceLogic.autoRetrieve ?? true
-    deviceLogicParams.maxRetryCount = deviceLogic.maxRetryCount ?? 3
+  // 娓呯┖鐜版湁鍙傛暟
+  Object.keys(deviceLogicParams).forEach(key => {
+    delete deviceLogicParams[key]
+  })
+  
+  // 鏍规嵁璁惧绫诲瀷鍔犺浇瀵瑰簲鐨勫弬鏁�
+  if (deviceLogic && Object.keys(deviceLogic).length > 0) {
+    Object.assign(deviceLogicParams, deviceLogic)
   }
 }
 
-// 浣嶇疆鏄犲皠鐩稿叧鏂规硶
-const addPositionMapping = () => {
-  const newKey = `POS${Object.keys(deviceLogicParams.positionMapping).length + 1}`
-  deviceLogicParams.positionMapping[newKey] = 1
-  mappingKeys.value.push(newKey)
-}
-
-const removePositionMapping = (key) => {
-  delete deviceLogicParams.positionMapping[key]
-  mappingKeys.value = mappingKeys.value.filter(k => k !== key)
-}
-
-const updatePositionMapping = (index, newKey, oldValue) => {
-  const oldKey = mappingKeys.value[index]
-  if (oldKey && oldKey !== newKey) {
-    delete deviceLogicParams.positionMapping[oldKey]
-  }
-  mappingKeys.value[index] = newKey
-  if (newKey) {
-    deviceLogicParams.positionMapping[newKey] = oldValue || 1
-  }
-}
 
 const resetForm = () => {
   Object.assign(deviceForm, getDefaultForm())
   deviceFormRef.value?.clearValidate()
   
   // 閲嶇疆璁惧閫昏緫鍙傛暟
-  deviceLogicParams.vehicleCapacity = 6000
-  deviceLogicParams.glassIntervalMs = 1000
-  deviceLogicParams.defaultGlassLength = 2000
-  deviceLogicParams.autoFeed = true
-  deviceLogicParams.maxRetryCount = 5
-  deviceLogicParams.positionMapping = {}
-  mappingKeys.value = []
-  
-  deviceLogicParams.glassSize = 2000
-  deviceLogicParams.processingTime = 5000
-  deviceLogicParams.autoProcess = true
-  
-  deviceLogicParams.storageCapacity = 100
-  deviceLogicParams.retrievalMode = 'FIFO'
-  deviceLogicParams.autoStore = true
-  deviceLogicParams.autoRetrieve = true
+  Object.keys(deviceLogicParams).forEach(key => {
+    delete deviceLogicParams[key]
+  })
 }
 
 const addConfigParam = () => {
@@ -963,30 +737,9 @@
     plcType: deviceForm.plcType
   }
 
-    // 淇濆瓨璁惧閫昏緫鍙傛暟
-    const deviceLogic = {}
-    if (deviceForm.deviceType === '涓婂ぇ杞�') {
-      deviceLogic.vehicleCapacity = deviceLogicParams.vehicleCapacity
-      deviceLogic.glassIntervalMs = deviceLogicParams.glassIntervalMs
-      deviceLogic.defaultGlassLength = deviceLogicParams.defaultGlassLength
-      deviceLogic.autoFeed = deviceLogicParams.autoFeed
-      deviceLogic.maxRetryCount = deviceLogicParams.maxRetryCount
-      deviceLogic.positionMapping = deviceLogicParams.positionMapping
-    } else if (deviceForm.deviceType === '澶х悊鐗�') {
-      deviceLogic.glassSize = deviceLogicParams.glassSize
-      deviceLogic.processingTime = deviceLogicParams.processingTime
-      deviceLogic.autoProcess = deviceLogicParams.autoProcess
-      deviceLogic.maxRetryCount = deviceLogicParams.maxRetryCount
-    } else if (deviceForm.deviceType === '鐜荤拑瀛樺偍') {
-      deviceLogic.storageCapacity = deviceLogicParams.storageCapacity
-      deviceLogic.retrievalMode = deviceLogicParams.retrievalMode
-      deviceLogic.autoStore = deviceLogicParams.autoStore
-      deviceLogic.autoRetrieve = deviceLogicParams.autoRetrieve
-      deviceLogic.maxRetryCount = deviceLogicParams.maxRetryCount
-    }
-    
-    if (Object.keys(deviceLogic).length > 0) {
-      extraObj.deviceLogic = deviceLogic
+    // 淇濆瓨璁惧閫昏緫鍙傛暟锛堢洿鎺ヤ娇鐢╠eviceLogicParams锛岀敱鍚勪釜閰嶇疆缁勪欢绠$悊锛�
+    if (deviceLogicParams && Object.keys(deviceLogicParams).length > 0) {
+      extraObj.deviceLogic = { ...deviceLogicParams }
     }
 
     // 鏋勫缓 configJson锛氬皢 configParams 鏁扮粍杞崲涓� JSON 瀛楃涓�
@@ -1142,4 +895,8 @@
   border-radius: 6px;
   background-color: #fafafa;
 }
+
+.no-config-tip {
+  padding: 20px;
+}
 </style>
\ No newline at end of file
diff --git a/mes-web/src/views/device/components/DeviceLogicConfig/LargeGlassConfig.vue b/mes-web/src/views/device/components/DeviceLogicConfig/LargeGlassConfig.vue
new file mode 100644
index 0000000..3b7e68f
--- /dev/null
+++ b/mes-web/src/views/device/components/DeviceLogicConfig/LargeGlassConfig.vue
@@ -0,0 +1,177 @@
+<template>
+  <div class="large-glass-config">
+    <el-form-item label="鏍煎瓙鑼冨洿閰嶇疆">
+      <div class="grid-ranges">
+        <div
+          v-for="(range, index) in config.gridRanges"
+          :key="index"
+          class="grid-range-item"
+        >
+          <el-input-number
+            v-model="range.row"
+            :min="1"
+            :max="100"
+            :step="1"
+            style="width: 100px; margin-right: 10px;"
+            placeholder="琛屽彿"
+          />
+          <span>琛岋細</span>
+          <el-input-number
+            v-model="range.start"
+            :min="1"
+            :max="1000"
+            :step="1"
+            style="width: 120px; margin: 0 10px;"
+            placeholder="璧峰鏍煎瓙"
+          />
+          <span>~</span>
+          <el-input-number
+            v-model="range.end"
+            :min="1"
+            :max="1000"
+            :step="1"
+            style="width: 120px; margin-left: 10px;"
+            placeholder="缁撴潫鏍煎瓙"
+          />
+          <el-button
+            type="danger"
+            size="small"
+            style="margin-left: 10px;"
+            @click="removeGridRange(index)"
+          >
+            鍒犻櫎
+          </el-button>
+        </div>
+        <el-button type="primary" size="small" @click="addGridRange">
+          娣诲姞鏍煎瓙鑼冨洿
+        </el-button>
+      </div>
+      <span class="form-tip">閰嶇疆姣忚鐨勬牸瀛愯寖鍥达紝渚嬪锛氱涓�琛�1~52鏍硷紝绗簩琛�53~101鏍�</span>
+    </el-form-item>
+
+    <el-row :gutter="20">
+      <el-col :span="8">
+        <el-form-item label="姣忔牸闀垮害(mm)">
+          <el-input-number
+            v-model="config.gridLength"
+            :min="100"
+            :max="10000"
+            :step="100"
+            style="width: 100%;"
+          />
+          <span class="form-tip">姣忔牸闀垮害锛堟绫筹級</span>
+        </el-form-item>
+      </el-col>
+      <el-col :span="8">
+        <el-form-item label="姣忔牸瀹藉害(mm)">
+          <el-input-number
+            v-model="config.gridWidth"
+            :min="100"
+            :max="10000"
+            :step="100"
+            style="width: 100%;"
+          />
+          <span class="form-tip">姣忔牸瀹藉害锛堟绫筹級</span>
+        </el-form-item>
+      </el-col>
+      <el-col :span="8">
+        <el-form-item label="姣忔牸鍘氬害(mm)">
+          <el-input-number
+            v-model="config.gridThickness"
+            :min="1"
+            :max="100"
+            :step="1"
+            style="width: 100%;"
+          />
+          <span class="form-tip">姣忔牸鍘氬害锛堟绫筹級</span>
+        </el-form-item>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script setup>
+import { ref, watch } from 'vue'
+
+const props = defineProps({
+  modelValue: {
+    type: Object,
+    default: () => ({})
+  }
+})
+
+const emit = defineEmits(['update:modelValue'])
+
+// 閰嶇疆鏁版嵁
+const config = ref({
+  gridRanges: [
+    { row: 1, start: 1, end: 52 },
+    { row: 2, start: 53, end: 101 }
+  ],
+  gridLength: 2000,
+  gridWidth: 1500,
+  gridThickness: 5
+})
+
+// 鐩戝惉props鍙樺寲
+watch(() => props.modelValue, (newVal) => {
+  if (newVal && Object.keys(newVal).length > 0) {
+    config.value = {
+      gridRanges: newVal.gridRanges || [
+        { row: 1, start: 1, end: 52 },
+        { row: 2, start: 53, end: 101 }
+      ],
+      gridLength: newVal.gridLength ?? 2000,
+      gridWidth: newVal.gridWidth ?? 1500,
+      gridThickness: newVal.gridThickness ?? 5
+    }
+  }
+}, { immediate: true, deep: true })
+
+// 鐩戝惉config鍙樺寲锛屽悓姝ュ埌鐖剁粍浠�
+watch(config, (newVal) => {
+  emit('update:modelValue', { ...newVal })
+}, { deep: true })
+
+// 鏍煎瓙鑼冨洿鐩稿叧鏂规硶
+const addGridRange = () => {
+  const maxRow = config.value.gridRanges.length > 0
+    ? Math.max(...config.value.gridRanges.map(r => r.row))
+    : 0
+  const lastEnd = config.value.gridRanges.length > 0
+    ? Math.max(...config.value.gridRanges.map(r => r.end))
+    : 0
+  config.value.gridRanges.push({
+    row: maxRow + 1,
+    start: lastEnd + 1,
+    end: lastEnd + 50
+  })
+}
+
+const removeGridRange = (index) => {
+  config.value.gridRanges.splice(index, 1)
+}
+</script>
+
+<style scoped>
+.form-tip {
+  margin-left: 10px;
+  font-size: 12px;
+  color: #909399;
+}
+
+.grid-ranges {
+  width: 100%;
+}
+
+.grid-range-item {
+  display: flex;
+  align-items: center;
+  margin-bottom: 12px;
+  padding: 12px;
+  border: 1px solid #ebeef5;
+  border-radius: 6px;
+  background-color: #fafafa;
+}
+</style>
+
diff --git a/mes-web/src/views/device/components/DeviceLogicConfig/LoadVehicleConfig.vue b/mes-web/src/views/device/components/DeviceLogicConfig/LoadVehicleConfig.vue
new file mode 100644
index 0000000..6f1bd5b
--- /dev/null
+++ b/mes-web/src/views/device/components/DeviceLogicConfig/LoadVehicleConfig.vue
@@ -0,0 +1,324 @@
+<template>
+  <div class="load-vehicle-config">
+    <el-row :gutter="20">
+      <el-col :span="12">
+        <el-form-item label="杞﹁締瀹归噺(mm)">
+          <el-input-number
+            v-model="config.vehicleCapacity"
+            :min="1"
+            :max="10000"
+            :step="100"
+            style="width: 100%;"
+          />
+          <span class="form-tip">杞﹁締鏈�澶у閲�</span>
+        </el-form-item>
+      </el-col>
+      <el-col :span="12">
+        <el-form-item label="杞﹁締閫熷害(鏍�/绉�)">
+          <el-input-number
+            v-model="config.vehicleSpeed"
+            :min="0.1"
+            :max="10"
+            :step="0.1"
+            :precision="1"
+            style="width: 100%;"
+          />
+          <span class="form-tip">杞﹁締杩愬姩閫熷害锛岄粯璁�1鏍�/绉�</span>
+        </el-form-item>
+      </el-col>
+    </el-row>
+
+    <el-row :gutter="20">
+      <el-col :span="12">
+        <el-form-item label="鐜荤拑闂撮殧(ms)">
+          <el-input-number
+            v-model="config.glassIntervalMs"
+            :min="100"
+            :max="10000"
+            :step="100"
+            style="width: 100%;"
+          />
+          <span class="form-tip">鐜荤拑涓婃枡闂撮殧鏃堕棿锛堟绉掞級</span>
+        </el-form-item>
+      </el-col>
+      <el-col :span="12">
+        <el-form-item label="榛樿鐜荤拑闀垮害(mm)">
+          <el-input-number
+            v-model="config.defaultGlassLength"
+            :min="100"
+            :max="10000"
+            :step="100"
+            style="width: 100%;"
+          />
+          <span class="form-tip">褰撶幓鐠冩湭鎻愪緵闀垮害鏃朵娇鐢ㄧ殑榛樿鍊�</span>
+        </el-form-item>
+      </el-col>
+    </el-row>
+
+    <el-row :gutter="20">
+      <el-col :span="12">
+        <el-form-item label="鍒濆浣嶇疆(鏍�)">
+          <el-input-number
+            v-model="config.homePosition"
+            :min="0"
+            :max="1000"
+            :step="1"
+            style="width: 100%;"
+          />
+          <span class="form-tip">杞﹁締鍒濆浣嶇疆锛堟牸瀛愶級</span>
+        </el-form-item>
+      </el-col>
+      <el-col :span="12">
+        <el-form-item label="杩愬姩璺濈鑼冨洿">
+          <el-input-number
+            v-model="config.minRange"
+            :min="1"
+            :max="1000"
+            :step="1"
+            style="width: 48%;"
+            placeholder="鏈�灏�"
+          />
+          <span style="margin: 0 2%;">~</span>
+          <el-input-number
+            v-model="config.maxRange"
+            :min="1"
+            :max="1000"
+            :step="1"
+            style="width: 48%;"
+            placeholder="鏈�澶�"
+          />
+          <span class="form-tip">杩愬姩璺濈鑼冨洿锛堟牸瀛愶級</span>
+        </el-form-item>
+      </el-col>
+    </el-row>
+
+    <el-row :gutter="20">
+      <el-col :span="12">
+        <el-form-item label="绌洪棽鐩戞帶闂撮殧(ms)">
+          <el-input-number
+            v-model="config.idleMonitorIntervalMs"
+            :min="500"
+            :max="10000"
+            :step="100"
+            style="width: 100%;"
+          />
+          <span class="form-tip">绌洪棽鐘舵�佺洃鎺ч棿闅旓紝榛樿2000ms</span>
+        </el-form-item>
+      </el-col>
+      <el-col :span="12">
+        <el-form-item label="浠诲姟鐩戞帶闂撮殧(ms)">
+          <el-input-number
+            v-model="config.taskMonitorIntervalMs"
+            :min="500"
+            :max="10000"
+            :step="100"
+            style="width: 100%;"
+          />
+          <span class="form-tip">浠诲姟鎵ц鐩戞帶闂撮殧锛岄粯璁�1000ms</span>
+        </el-form-item>
+      </el-col>
+    </el-row>
+
+    <el-row :gutter="20">
+      <el-col :span="12">
+        <el-form-item label="MES纭瓒呮椂(ms)">
+          <el-input-number
+            v-model="config.mesConfirmTimeoutMs"
+            :min="5000"
+            :max="300000"
+            :step="1000"
+            style="width: 100%;"
+          />
+          <span class="form-tip">绛夊緟MES纭鐨勮秴鏃舵椂闂达紝榛樿30000ms</span>
+        </el-form-item>
+      </el-col>
+      <el-col :span="12">
+        <el-form-item label="鑷姩涓婃枡">
+          <el-switch v-model="config.autoFeed" />
+          <span class="form-tip">鏄惁鑷姩瑙﹀彂涓婃枡璇锋眰</span>
+        </el-form-item>
+      </el-col>
+    </el-row>
+
+    <el-row :gutter="20">
+      <el-col :span="12">
+        <el-form-item label="鏈�澶ч噸璇曟鏁�">
+          <el-input-number
+            v-model="config.maxRetryCount"
+            :min="0"
+            :max="10"
+            :step="1"
+            style="width: 100%;"
+          />
+        </el-form-item>
+      </el-col>
+    </el-row>
+
+    <el-form-item label="浣嶇疆鏄犲皠">
+      <div class="position-mapping">
+        <div
+          v-for="(value, key, index) in config.positionMapping"
+          :key="index"
+          class="mapping-item"
+        >
+          <el-input
+            v-model="mappingKeys[index]"
+            placeholder="浣嶇疆浠g爜锛堝900/901锛�"
+            size="small"
+            style="width: 150px; margin-right: 10px;"
+            @input="updatePositionMapping(index, $event, value)"
+          />
+          <el-input-number
+            v-model="config.positionMapping[mappingKeys[index] || key]"
+            :min="0"
+            :max="1000"
+            :step="1"
+            size="small"
+            style="width: 120px; margin-right: 10px;"
+            placeholder="浣嶇疆鍊硷紙鏍硷級"
+          />
+          <el-button
+            type="danger"
+            size="small"
+            @click="removePositionMapping(key)"
+          >
+            鍒犻櫎
+          </el-button>
+        </div>
+        <el-button type="primary" size="small" @click="addPositionMapping">
+          娣诲姞浣嶇疆鏄犲皠
+        </el-button>
+      </div>
+      <span class="form-tip">灏哅ES缂栧彿锛堝900/901锛夋槧灏勪负瀹為檯浣嶇疆鍊硷紙鏍煎瓙锛�</span>
+    </el-form-item>
+
+    <el-form-item label="鍑虹墖浠诲姟鏍煎瓙鑼冨洿">
+      <el-input-number
+        v-model="config.outboundSlotRanges[0]"
+        :min="1"
+        :max="1000"
+        :step="1"
+        style="width: 48%;"
+        placeholder="鏈�灏忔牸瀛愮紪鍙�"
+      />
+      <span style="margin: 0 2%;">~</span>
+      <el-input-number
+        v-model="config.outboundSlotRanges[1]"
+        :min="1"
+        :max="1000"
+        :step="1"
+        style="width: 48%;"
+        placeholder="鏈�澶ф牸瀛愮紪鍙�"
+      />
+      <span class="form-tip">鍑虹墖浠诲姟鐨剆tartSlot鑼冨洿锛屼緥濡俒1, 101]琛ㄧず鏍煎瓙1~101閮芥槸鍑虹墖浠诲姟</span>
+    </el-form-item>
+  </div>
+</template>
+
+<script setup>
+import { ref, watch } from 'vue'
+
+const props = defineProps({
+  modelValue: {
+    type: Object,
+    default: () => ({})
+  }
+})
+
+const emit = defineEmits(['update:modelValue'])
+
+// 閰嶇疆鏁版嵁
+const config = ref({
+  vehicleCapacity: 6000,
+  vehicleSpeed: 1.0,
+  glassIntervalMs: 1000,
+  defaultGlassLength: 2000,
+  homePosition: 0,
+  minRange: 1,
+  maxRange: 100,
+  idleMonitorIntervalMs: 2000,
+  taskMonitorIntervalMs: 1000,
+  mesConfirmTimeoutMs: 30000,
+  autoFeed: true,
+  maxRetryCount: 5,
+  positionMapping: {},
+  outboundSlotRanges: [1, 101]
+})
+
+// 浣嶇疆鏄犲皠鐨勯敭鏁扮粍
+const mappingKeys = ref([])
+
+// 鐩戝惉props鍙樺寲
+watch(() => props.modelValue, (newVal) => {
+  if (newVal && Object.keys(newVal).length > 0) {
+    config.value = {
+      vehicleCapacity: newVal.vehicleCapacity ?? 6000,
+      vehicleSpeed: newVal.vehicleSpeed ?? 1.0,
+      glassIntervalMs: newVal.glassIntervalMs ?? 1000,
+      defaultGlassLength: newVal.defaultGlassLength ?? 2000,
+      homePosition: newVal.homePosition ?? 0,
+      minRange: newVal.minRange ?? 1,
+      maxRange: newVal.maxRange ?? 100,
+      idleMonitorIntervalMs: newVal.idleMonitorIntervalMs ?? 2000,
+      taskMonitorIntervalMs: newVal.taskMonitorIntervalMs ?? 1000,
+      mesConfirmTimeoutMs: newVal.mesConfirmTimeoutMs ?? 30000,
+      autoFeed: newVal.autoFeed ?? true,
+      maxRetryCount: newVal.maxRetryCount ?? 5,
+      positionMapping: newVal.positionMapping || {},
+      outboundSlotRanges: newVal.outboundSlotRanges || [1, 101]
+    }
+    mappingKeys.value = Object.keys(config.value.positionMapping)
+  }
+}, { immediate: true, deep: true })
+
+// 鐩戝惉config鍙樺寲锛屽悓姝ュ埌鐖剁粍浠�
+watch(config, (newVal) => {
+  emit('update:modelValue', { ...newVal })
+}, { deep: true })
+
+// 浣嶇疆鏄犲皠鐩稿叧鏂规硶
+const addPositionMapping = () => {
+  const newKey = `POS${Object.keys(config.value.positionMapping).length + 1}`
+  config.value.positionMapping[newKey] = 1
+  mappingKeys.value.push(newKey)
+}
+
+const removePositionMapping = (key) => {
+  delete config.value.positionMapping[key]
+  mappingKeys.value = mappingKeys.value.filter(k => k !== key)
+}
+
+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
+  }
+}
+</script>
+
+<style scoped>
+.form-tip {
+  margin-left: 10px;
+  font-size: 12px;
+  color: #909399;
+}
+
+.position-mapping {
+  width: 100%;
+}
+
+.mapping-item {
+  display: flex;
+  align-items: center;
+  margin-bottom: 12px;
+  padding: 12px;
+  border: 1px solid #ebeef5;
+  border-radius: 6px;
+  background-color: #fafafa;
+}
+</style>
+
diff --git a/mes-web/src/views/device/components/DeviceLogicConfig/WorkstationScannerConfig.vue b/mes-web/src/views/device/components/DeviceLogicConfig/WorkstationScannerConfig.vue
new file mode 100644
index 0000000..e1c86c6
--- /dev/null
+++ b/mes-web/src/views/device/components/DeviceLogicConfig/WorkstationScannerConfig.vue
@@ -0,0 +1,84 @@
+<template>
+  <div class="workstation-scanner-config">
+    <el-row :gutter="20">
+      <el-col :span="12">
+        <el-form-item label="鎵爜闂撮殧(ms)">
+          <el-input-number
+            v-model="config.scanIntervalMs"
+            :min="1000"
+            :max="60000"
+            :step="1000"
+            style="width: 100%;"
+          />
+          <span class="form-tip">瀹氭椂鎵弿MES鍐欏尯鐨勬椂闂撮棿闅旓紝榛樿10000ms锛�10绉掞級</span>
+        </el-form-item>
+      </el-col>
+      <el-col :span="12">
+        <el-form-item label="浜х嚎缂栧彿">
+          <el-input-number
+            v-model="config.workLine"
+            :min="1"
+            :max="100"
+            :step="1"
+            style="width: 100%;"
+          />
+          <span class="form-tip">浜х嚎缂栧彿锛岀敤浜庤繃婊ょ幓鐠冧俊鎭�</span>
+        </el-form-item>
+      </el-col>
+    </el-row>
+
+    <el-row :gutter="20">
+      <el-col :span="12">
+        <el-form-item label="鑷姩纭">
+          <el-switch v-model="config.autoAck" />
+          <span class="form-tip">鏄惁鑷姩纭MES鍙戦�佺殑鐜荤拑淇℃伅锛堝洖鍐檓esSend=0锛�</span>
+        </el-form-item>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script setup>
+import { ref, watch } from 'vue'
+
+const props = defineProps({
+  modelValue: {
+    type: Object,
+    default: () => ({})
+  }
+})
+
+const emit = defineEmits(['update:modelValue'])
+
+// 閰嶇疆鏁版嵁
+const config = ref({
+  scanIntervalMs: 10000,
+  workLine: null,
+  autoAck: true
+})
+
+// 鐩戝惉props鍙樺寲
+watch(() => props.modelValue, (newVal) => {
+  if (newVal && Object.keys(newVal).length > 0) {
+    config.value = {
+      scanIntervalMs: newVal.scanIntervalMs ?? 10000,
+      workLine: newVal.workLine ?? null,
+      autoAck: newVal.autoAck ?? true
+    }
+  }
+}, { immediate: true, deep: true })
+
+// 鐩戝惉config鍙樺寲锛屽悓姝ュ埌鐖剁粍浠�
+watch(config, (newVal) => {
+  emit('update:modelValue', { ...newVal })
+}, { deep: true })
+</script>
+
+<style scoped>
+.form-tip {
+  margin-left: 10px;
+  font-size: 12px;
+  color: #909399;
+}
+</style>
+
diff --git a/mes-web/src/views/device/components/DeviceLogicConfig/WorkstationTransferConfig.vue b/mes-web/src/views/device/components/DeviceLogicConfig/WorkstationTransferConfig.vue
new file mode 100644
index 0000000..6b1f9d8
--- /dev/null
+++ b/mes-web/src/views/device/components/DeviceLogicConfig/WorkstationTransferConfig.vue
@@ -0,0 +1,146 @@
+<template>
+  <div class="workstation-transfer-config">
+    <el-row :gutter="20">
+      <el-col :span="12">
+        <el-form-item label="鎵爜闂撮殧(ms)">
+          <el-input-number
+            v-model="config.scanIntervalMs"
+            :min="1000"
+            :max="60000"
+            :step="1000"
+            style="width: 100%;"
+          />
+          <span class="form-tip">瀹氭椂鏌ヨ鏈�杩戞壂鐮佺幓鐠冪殑鏃堕棿闂撮殧锛岄粯璁�10000ms锛�10绉掞級</span>
+        </el-form-item>
+      </el-col>
+      <el-col :span="12">
+        <el-form-item label="缂撳啿鍒ゅ畾鏃堕棿(ms)">
+          <el-input-number
+            v-model="config.transferDelayMs"
+            :min="5000"
+            :max="120000"
+            :step="1000"
+            style="width: 100%;"
+          />
+          <span class="form-tip">30绉掑唴鏃犳柊鐜荤拑鎵爜鍒欏垽瀹氫负鏈�鍚庝竴鐗囷紝榛樿30000ms锛�30绉掞級</span>
+        </el-form-item>
+      </el-col>
+    </el-row>
+
+    <el-row :gutter="20">
+      <el-col :span="12">
+        <el-form-item label="杞﹁締瀹归噺(mm)">
+          <el-input-number
+            v-model="config.vehicleCapacity"
+            :min="1000"
+            :max="20000"
+            :step="100"
+            style="width: 100%;"
+          />
+          <span class="form-tip">鍙杞界殑鏈�澶у搴︼紙姣背锛夛紝榛樿6000mm</span>
+        </el-form-item>
+      </el-col>
+      <el-col :span="12">
+        <el-form-item label="鐩戞帶闂撮殧(ms)">
+          <el-input-number
+            v-model="config.monitorIntervalMs"
+            :min="1000"
+            :max="60000"
+            :step="1000"
+            style="width: 100%;"
+          />
+          <span class="form-tip">鎵规澶勭悊鐩戞帶闂撮殧锛岄粯璁や娇鐢╯canIntervalMs</span>
+        </el-form-item>
+      </el-col>
+    </el-row>
+
+    <el-row :gutter="20">
+      <el-col :span="12">
+        <el-form-item label="浜х嚎缂栧彿">
+          <el-input-number
+            v-model="config.workLine"
+            :min="1"
+            :max="100"
+            :step="1"
+            style="width: 100%;"
+          />
+          <span class="form-tip">浜х嚎缂栧彿锛岀敤浜庤繃婊ょ幓鐠冧俊鎭�</span>
+        </el-form-item>
+      </el-col>
+      <el-col :span="12">
+        <el-form-item label="浣嶇疆鍊�(鏍�)">
+          <el-input-number
+            v-model="config.inPosition"
+            :min="0"
+            :max="1000"
+            :step="1"
+            style="width: 100%;"
+          />
+          <span class="form-tip">鍐欏叆PLC鐨刬nPosition鍊硷紙鏍煎瓙锛�</span>
+        </el-form-item>
+      </el-col>
+    </el-row>
+
+    <el-row :gutter="20">
+      <el-col :span="12">
+        <el-form-item label="鑷姩纭">
+          <el-switch v-model="config.autoAck" />
+          <span class="form-tip">鏄惁鑷姩纭MES鍙戦�佺殑鐜荤拑淇℃伅</span>
+        </el-form-item>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script setup>
+import { ref, watch } from 'vue'
+
+const props = defineProps({
+  modelValue: {
+    type: Object,
+    default: () => ({})
+  }
+})
+
+const emit = defineEmits(['update:modelValue'])
+
+// 閰嶇疆鏁版嵁
+const config = ref({
+  scanIntervalMs: 10000,
+  transferDelayMs: 30000,
+  vehicleCapacity: 6000,
+  monitorIntervalMs: 10000,
+  workLine: null,
+  inPosition: null,
+  autoAck: true
+})
+
+// 鐩戝惉props鍙樺寲
+watch(() => props.modelValue, (newVal) => {
+  if (newVal && Object.keys(newVal).length > 0) {
+    config.value = {
+      scanIntervalMs: newVal.scanIntervalMs ?? 10000,
+      transferDelayMs: newVal.transferDelayMs ?? 30000,
+      vehicleCapacity: newVal.vehicleCapacity ?? 6000,
+      monitorIntervalMs: newVal.monitorIntervalMs ?? newVal.scanIntervalMs ?? 10000,
+      workLine: newVal.workLine ?? null,
+      inPosition: newVal.inPosition ?? null,
+      autoAck: newVal.autoAck ?? true
+    }
+  }
+}, { immediate: true, deep: true })
+
+// 鐩戝惉config鍙樺寲锛屽悓姝ュ埌鐖剁粍浠�
+watch(config, (newVal) => {
+  emit('update:modelValue', { ...newVal })
+}, { deep: true })
+</script>
+
+<style scoped>
+.form-tip {
+  margin-left: 10px;
+  font-size: 12px;
+  color: #909399;
+}
+</style>
+
diff --git a/mes-web/src/views/device/components/DeviceLogicConfig/index.js b/mes-web/src/views/device/components/DeviceLogicConfig/index.js
new file mode 100644
index 0000000..7456aa4
--- /dev/null
+++ b/mes-web/src/views/device/components/DeviceLogicConfig/index.js
@@ -0,0 +1,34 @@
+/**
+ * 璁惧閫昏緫閰嶇疆缁勪欢瀵煎嚭
+ * 缁熶竴绠$悊鎵�鏈夎澶囩被鍨嬬殑閰嶇疆缁勪欢
+ */
+
+import LoadVehicleConfig from './LoadVehicleConfig.vue'
+import LargeGlassConfig from './LargeGlassConfig.vue'
+import WorkstationScannerConfig from './WorkstationScannerConfig.vue'
+import WorkstationTransferConfig from './WorkstationTransferConfig.vue'
+
+// 璁惧绫诲瀷鍒扮粍浠剁殑鏄犲皠
+export const deviceTypeComponentMap = {
+  '澶ц溅璁惧': LoadVehicleConfig,
+  '澶х悊鐗囩': LargeGlassConfig,
+  '鍗ц浆绔嬫壂鐮�': WorkstationScannerConfig,
+  '鍗ц浆绔�': WorkstationTransferConfig,
+  // 鍏煎鏃у悕绉�
+  '涓婂ぇ杞�': LoadVehicleConfig,
+  '澶х悊鐗�': LargeGlassConfig
+}
+
+// 瀵煎嚭鎵�鏈夌粍浠�
+export {
+  LoadVehicleConfig,
+  LargeGlassConfig,
+  WorkstationScannerConfig,
+  WorkstationTransferConfig
+}
+
+// 鏍规嵁璁惧绫诲瀷鑾峰彇瀵瑰簲鐨勯厤缃粍浠�
+export function getDeviceConfigComponent(deviceType) {
+  return deviceTypeComponentMap[deviceType] || null
+}
+
diff --git a/mes-web/src/views/plcTest/MultiDeviceWorkbench.vue b/mes-web/src/views/plcTest/MultiDeviceWorkbench.vue
index 5c5c465..3472ea8 100644
--- a/mes-web/src/views/plcTest/MultiDeviceWorkbench.vue
+++ b/mes-web/src/views/plcTest/MultiDeviceWorkbench.vue
@@ -3,23 +3,56 @@
     <div class="main-grid">
       <div class="left-panel">
         <GroupList @select="handleGroupSelect" />
+        <GroupTopology
+          v-if="selectedGroup"
+          :group="selectedGroup"
+          class="topology-panel"
+        />
       </div>
       <div class="right-panel">
-        <TaskOrchestration :group="selectedGroup" @task-started="refreshMonitor" />
-        <ExecutionMonitor ref="monitorRef" :group-id="selectedGroupId" class="monitor-panel" />
+        <el-tabs v-model="activeTab" type="card" class="workbench-tabs">
+          <el-tab-pane label="浠诲姟缂栨帓" name="orchestration">
+            <TaskOrchestration
+              :group="selectedGroup"
+              @task-started="handleTaskStarted"
+            />
+          </el-tab-pane>
+          <el-tab-pane label="鎵ц鐩戞帶" name="monitor">
+            <ExecutionMonitor
+              ref="monitorRef"
+              :group-id="selectedGroupId"
+              :task-id="selectedTaskId"
+              class="monitor-panel"
+              @task-selected="handleTaskSelected"
+            />
+          </el-tab-pane>
+          <el-tab-pane label="缁撴灉鍒嗘瀽" name="analysis">
+            <ResultAnalysis
+              ref="analysisRef"
+              :task="selectedTask"
+              class="analysis-panel"
+            />
+          </el-tab-pane>
+        </el-tabs>
       </div>
     </div>
   </div>
 </template>
 
 <script setup>
-import { computed, ref } from 'vue'
+import { computed, ref, watch } from 'vue'
 import GroupList from './components/DeviceGroup/GroupList.vue'
+import GroupTopology from './components/DeviceGroup/GroupTopology.vue'
 import TaskOrchestration from './components/MultiDeviceTest/TaskOrchestration.vue'
 import ExecutionMonitor from './components/MultiDeviceTest/ExecutionMonitor.vue'
+import ResultAnalysis from './components/MultiDeviceTest/ResultAnalysis.vue'
 
 const selectedGroup = ref(null)
 const monitorRef = ref(null)
+const analysisRef = ref(null)
+const activeTab = ref('orchestration')
+const selectedTaskId = ref(null)
+const selectedTask = ref(null)
 
 const selectedGroupId = computed(() => {
   if (!selectedGroup.value) return null
@@ -28,10 +61,39 @@
 
 const handleGroupSelect = (group) => {
   selectedGroup.value = group
+  selectedTask.value = null
+  selectedTaskId.value = null
+  // 鍒囨崲鍒扮紪鎺掓爣绛鹃〉
+  activeTab.value = 'orchestration'
 }
 
-const refreshMonitor = () => {
-  monitorRef.value?.fetchTasks?.()
+const handleTaskStarted = (task) => {
+  // 浠诲姟鍚姩鍚庯紝鍒囨崲鍒扮洃鎺ф爣绛鹃〉锛堝鏋滃綋鍓嶄笉鍦ㄧ洃鎺ч〉锛�
+  if (activeTab.value !== 'monitor') {
+    activeTab.value = 'monitor'
+  }
+  // 绔嬪嵆鍒锋柊鐩戞帶鍒楄〃锛屾樉绀烘柊鍚姩鐨勪换鍔�
+  setTimeout(() => {
+    monitorRef.value?.fetchTasks?.()
+  }, 300)
+  
+  // 濡傛灉浼犲叆浜嗕换鍔′俊鎭紝鍙互鑷姩閫変腑
+  if (task && task.taskId) {
+    selectedTaskId.value = task.taskId
+  }
+}
+
+const handleTaskSelected = (task) => {
+  selectedTask.value = task
+  selectedTaskId.value = task?.taskId || null
+  // 濡傛灉浠诲姟宸插畬鎴愭垨澶辫触锛屽垏鎹㈠埌缁撴灉鍒嗘瀽鏍囩椤�
+  if (task && (task.status === 'COMPLETED' || task.status === 'FAILED')) {
+    activeTab.value = 'analysis'
+    // 鍒锋柊鍒嗘瀽鏁版嵁
+    setTimeout(() => {
+      analysisRef.value?.fetchSteps?.()
+    }, 100)
+  }
 }
 </script>
 
@@ -48,14 +110,43 @@
   gap: 24px;
 }
 
-.right-panel {
+.left-panel {
   display: flex;
   flex-direction: column;
   gap: 24px;
 }
 
-.monitor-panel {
+.topology-panel {
   flex: 1;
+  min-height: 300px;
+}
+
+.right-panel {
+  display: flex;
+  flex-direction: column;
+  min-height: 0;
+}
+
+.workbench-tabs {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+  min-height: 0;
+}
+
+.workbench-tabs :deep(.el-tabs__content) {
+  flex: 1;
+  overflow: auto;
+}
+
+.workbench-tabs :deep(.el-tab-pane) {
+  height: 100%;
+}
+
+.monitor-panel,
+.analysis-panel {
+  flex: 1;
+  min-height: 500px;
 }
 
 @media (max-width: 1200px) {
diff --git a/mes-web/src/views/plcTest/components/DeviceGroup/GroupTopology.vue b/mes-web/src/views/plcTest/components/DeviceGroup/GroupTopology.vue
new file mode 100644
index 0000000..e46b9c0
--- /dev/null
+++ b/mes-web/src/views/plcTest/components/DeviceGroup/GroupTopology.vue
@@ -0,0 +1,478 @@
+<template>
+  <div class="group-topology">
+    <div class="panel-header">
+      <div>
+        <h3>璁惧缁勬嫇鎵戝浘</h3>
+        <p v-if="group">{{ group.groupName }} - 璁惧鎵ц娴佺▼鍙鍖�</p>
+        <p v-else class="warning">璇峰厛閫夋嫨涓�涓澶囩粍</p>
+      </div>
+      <div class="action-buttons">
+        <el-button :loading="loading" @click="handleRefresh">
+          <el-icon><Refresh /></el-icon>
+          鍒锋柊
+        </el-button>
+        <el-button @click="toggleLayout">
+          <el-icon><Grid /></el-icon>
+          {{ layoutMode === 'horizontal' ? '鍨傜洿甯冨眬' : '姘村钩甯冨眬' }}
+        </el-button>
+      </div>
+    </div>
+
+    <div v-if="!group" class="empty-state">
+      <el-empty description="璇烽�夋嫨璁惧缁勬煡鐪嬫嫇鎵戝浘" />
+    </div>
+
+    <div v-else class="topology-container" :class="`layout-${layoutMode}`">
+      <template v-for="(device, index) in devices" :key="device.id || device.deviceId">
+        <div
+          class="topology-node-wrapper"
+          :class="`layout-${layoutMode}`"
+        >
+          <div
+            class="topology-node"
+            :class="getDeviceTypeClass(device.deviceType)"
+            @click="handleNodeClick(device)"
+            :title="`鐐瑰嚮鏌ョ湅璁惧璇︽儏 | 鎵ц椤哄簭: ${index + 1}`"
+          >
+            <div class="node-content">
+              <div class="node-icon">
+                <el-icon :size="24">
+                  <component :is="getDeviceIcon(device.deviceType)" />
+                </el-icon>
+              </div>
+              <div class="node-info">
+                <div class="node-name">{{ device.deviceName || device.deviceCode }}</div>
+                <div class="node-type">{{ getDeviceTypeLabel(device.deviceType) }}</div>
+                <div class="node-status">
+                  <el-tag :type="getStatusType(device.status)" size="small">
+                    {{ getStatusLabel(device.status) }}
+                  </el-tag>
+                </div>
+              </div>
+            </div>
+            <!-- 鎵ц椤哄簭鏍囪瘑锛氬彸涓婅鐨勬暟瀛楀渾鍦� -->
+            <div class="node-order" :title="`鎵ц椤哄簭: 绗� ${index + 1} 姝">
+              {{ index + 1 }}
+            </div>
+          </div>
+          <!-- 娴佺▼鏂瑰悜绠ご锛氳〃绀鸿澶囨墽琛岄『搴忓拰鏁版嵁娴佸悜 -->
+          <div
+            v-if="index < devices.length - 1"
+            class="node-arrow"
+            :title="`鏁版嵁娴佸悜: ${device.deviceName || device.deviceCode} 鈫� ${devices[index + 1]?.deviceName || devices[index + 1]?.deviceCode}`"
+          >
+            <el-icon :size="20">
+              <ArrowRight v-if="layoutMode === 'horizontal'" />
+              <ArrowDown v-else />
+            </el-icon>
+          </div>
+        </div>
+      </template>
+    </div>
+
+    <!-- 璁惧璇︽儏鍗$墖 -->
+    <el-card v-if="selectedDevice" class="device-detail-card" shadow="never">
+      <template #header>
+        <div class="card-header">
+          <span>璁惧璇︽儏</span>
+          <el-button link @click="selectedDevice = null">
+            <el-icon><Close /></el-icon>
+          </el-button>
+        </div>
+      </template>
+      <el-descriptions :column="2" border>
+        <el-descriptions-item label="璁惧鍚嶇О">
+          {{ selectedDevice.deviceName }}
+        </el-descriptions-item>
+        <el-descriptions-item label="璁惧缂栫爜">
+          {{ selectedDevice.deviceCode }}
+        </el-descriptions-item>
+        <el-descriptions-item label="璁惧绫诲瀷">
+          {{ getDeviceTypeLabel(selectedDevice.deviceType) }}
+        </el-descriptions-item>
+        <el-descriptions-item label="鐘舵��">
+          <el-tag :type="getStatusType(selectedDevice.status)">
+            {{ getStatusLabel(selectedDevice.status) }}
+          </el-tag>
+        </el-descriptions-item>
+        <el-descriptions-item label="PLC IP" v-if="selectedDevice.plcIp">
+          {{ selectedDevice.plcIp }}
+        </el-descriptions-item>
+        <el-descriptions-item label="PLC绫诲瀷" v-if="selectedDevice.plcType">
+          {{ selectedDevice.plcType }}
+        </el-descriptions-item>
+        <el-descriptions-item label="妯″潡鍚嶇О" v-if="selectedDevice.moduleName">
+          {{ selectedDevice.moduleName }}
+        </el-descriptions-item>
+        <el-descriptions-item label="鏄惁鍚敤">
+          <el-tag :type="selectedDevice.enabled ? 'success' : 'info'">
+            {{ selectedDevice.enabled ? '鍚敤' : '鍋滅敤' }}
+          </el-tag>
+        </el-descriptions-item>
+      </el-descriptions>
+    </el-card>
+  </div>
+</template>
+
+<script setup>
+import { computed, ref, watch } from 'vue'
+import { ElMessage } from 'element-plus'
+import {
+  Refresh,
+  Grid,
+  ArrowRight,
+  ArrowDown,
+  Close,
+  Files,
+  Box,
+  Folder
+} from '@element-plus/icons-vue'
+import { deviceGroupApi } from '@/api/device/deviceManagement'
+
+const props = defineProps({
+  group: {
+    type: Object,
+    default: null
+  }
+})
+
+const loading = ref(false)
+const devices = ref([])
+const layoutMode = ref('horizontal') // 'horizontal' | 'vertical'
+const selectedDevice = ref(null)
+
+const fetchDevices = async () => {
+  if (!props.group) {
+    devices.value = []
+    return
+  }
+  const groupId = props.group.id || props.group.groupId
+  if (!groupId) {
+    devices.value = []
+    return
+  }
+  try {
+    loading.value = true
+    const response = await deviceGroupApi.getGroupDevices(groupId)
+    const rawList = response?.data
+    const deviceList = Array.isArray(rawList)
+      ? rawList
+      : Array.isArray(rawList?.records)
+      ? rawList.records
+      : Array.isArray(rawList?.data)
+      ? rawList.data
+      : []
+    // 鎸夋墽琛岄『搴忔帓搴�
+    devices.value = deviceList.sort((a, b) => {
+      const orderA = a.executionOrder || a.order || 0
+      const orderB = b.executionOrder || b.order || 0
+      return orderA - orderB
+    })
+  } catch (error) {
+    ElMessage.error(error?.message || '鍔犺浇璁惧鍒楄〃澶辫触')
+    devices.value = []
+  } finally {
+    loading.value = false
+  }
+}
+
+const handleRefresh = () => {
+  fetchDevices()
+}
+
+const toggleLayout = () => {
+  layoutMode.value = layoutMode.value === 'horizontal' ? 'vertical' : 'horizontal'
+}
+
+const getDeviceTypeClass = (deviceType) => {
+  if (!deviceType) return 'type-unknown'
+  const type = deviceType.toUpperCase()
+  if (type.includes('VEHICLE') || type.includes('澶ц溅')) return 'type-vehicle'
+  if (type.includes('GLASS') || type.includes('澶х悊鐗�')) return 'type-glass'
+  if (type.includes('STORAGE') || type.includes('瀛樺偍')) return 'type-storage'
+  return 'type-unknown'
+}
+
+const getDeviceIcon = (deviceType) => {
+  if (!deviceType) return Box
+  const type = deviceType.toUpperCase()
+  if (type.includes('VEHICLE') || type.includes('澶ц溅')) return Files
+  if (type.includes('GLASS') || type.includes('澶х悊鐗�')) return Box
+  if (type.includes('STORAGE') || type.includes('瀛樺偍')) return Folder
+  return Box
+}
+
+const getDeviceTypeLabel = (deviceType) => {
+  if (!deviceType) return '鏈煡璁惧'
+  const type = deviceType.toUpperCase()
+  if (type.includes('VEHICLE') || type.includes('澶ц溅')) return '涓婂ぇ杞﹁澶�'
+  if (type.includes('GLASS') || type.includes('澶х悊鐗�')) return '澶х悊鐗囪澶�'
+  if (type.includes('STORAGE') || type.includes('瀛樺偍')) return '鐜荤拑瀛樺偍璁惧'
+  return deviceType
+}
+
+const getStatusType = (status) => {
+  if (!status) return 'info'
+  const s = String(status).toUpperCase()
+  if (s === '1' || s === '鍚敤' || s === 'ENABLED' || s === 'ONLINE') return 'success'
+  if (s === '0' || s === '鍋滅敤' || s === 'DISABLED' || s === 'OFFLINE') return 'danger'
+  if (s === '2' || s === '缁存姢' || s === 'MAINTENANCE') return 'warning'
+  return 'info'
+}
+
+const getStatusLabel = (status) => {
+  if (!status) return '鏈煡'
+  const s = String(status).toUpperCase()
+  if (s === '1' || s === '鍚敤' || s === 'ENABLED' || s === 'ONLINE') return '鍦ㄧ嚎'
+  if (s === '0' || s === '鍋滅敤' || s === 'DISABLED' || s === 'OFFLINE') return '绂荤嚎'
+  if (s === '2' || s === '缁存姢' || s === 'MAINTENANCE') return '缁存姢涓�'
+  return String(status)
+}
+
+watch(
+  () => props.group,
+  () => {
+    fetchDevices()
+    selectedDevice.value = null
+  },
+  { immediate: true }
+)
+
+// 鐐瑰嚮鑺傜偣閫夋嫨璁惧
+const handleNodeClick = (device) => {
+  selectedDevice.value = device
+}
+
+defineExpose({
+  fetchDevices
+})
+</script>
+
+<style scoped>
+.group-topology {
+  background: #fff;
+  border-radius: 12px;
+  padding: 20px;
+  box-shadow: 0 8px 32px rgba(15, 18, 63, 0.08);
+}
+
+.panel-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 20px;
+}
+
+.panel-header h3 {
+  margin: 0;
+}
+
+.panel-header p {
+  margin: 4px 0 0;
+  color: #909399;
+  font-size: 13px;
+}
+
+.panel-header .warning {
+  color: #f56c6c;
+}
+
+.action-buttons {
+  display: flex;
+  gap: 12px;
+}
+
+.empty-state {
+  padding: 60px 0;
+}
+
+.topology-container {
+  display: flex;
+  align-items: center;
+  padding: 20px 0;
+  min-height: 200px;
+  overflow-x: auto;
+  /* 骞虫粦婊氬姩 */
+  scroll-behavior: smooth;
+}
+
+.topology-container.layout-horizontal {
+  flex-direction: row;
+  justify-content: flex-start;
+  align-items: center;
+  /* 娣诲姞宸﹀彸鍐呰竟璺濓紝纭繚绗竴涓拰鏈�鍚庝竴涓妭鐐瑰畬鍏ㄥ彲瑙� */
+  padding-left: 20px;
+  padding-right: 20px;
+}
+
+.topology-container.layout-vertical {
+  flex-direction: column;
+  align-items: center;
+}
+
+/* 鑺傜偣鍖呰鍣細姘村钩甯冨眬鏃舵í鍚戞帓鍒楋紝鍨傜洿甯冨眬鏃剁旱鍚戞帓鍒� */
+.topology-node-wrapper {
+  display: flex;
+  align-items: center;
+}
+
+.topology-node-wrapper.layout-horizontal {
+  flex-direction: row;
+  align-items: center;
+}
+
+.topology-node-wrapper.layout-vertical {
+  flex-direction: column;
+  align-items: center;
+}
+
+.topology-node {
+  position: relative;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  cursor: pointer;
+  transition: transform 0.2s;
+}
+
+.topology-node:hover {
+  transform: translateY(-4px);
+}
+
+.node-content {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  padding: 20px;
+  background: #fff;
+  border-radius: 12px;
+  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+  min-width: 160px;
+  transition: all 0.3s;
+}
+
+.topology-node:hover .node-content {
+  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
+}
+
+.node-icon {
+  width: 56px;
+  height: 56px;
+  border-radius: 50%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  margin-bottom: 12px;
+  color: #fff;
+}
+
+.type-vehicle .node-icon {
+  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+}
+
+.type-glass .node-icon {
+  background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
+}
+
+.type-storage .node-icon {
+  background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
+}
+
+.type-unknown .node-icon {
+  background: linear-gradient(135deg, #909399 0%, #b1b3b8 100%);
+}
+
+.node-info {
+  text-align: center;
+  width: 100%;
+}
+
+.node-name {
+  font-size: 16px;
+  font-weight: 600;
+  color: #303133;
+  margin-bottom: 4px;
+  word-break: break-all;
+}
+
+.node-type {
+  font-size: 12px;
+  color: #909399;
+  margin-bottom: 8px;
+}
+
+.node-status {
+  display: flex;
+  justify-content: center;
+}
+
+.node-order {
+  position: absolute;
+  top: -8px;
+  right: -8px;
+  width: 24px;
+  height: 24px;
+  border-radius: 50%;
+  background: #409eff;
+  color: #fff;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  font-size: 12px;
+  font-weight: 600;
+  box-shadow: 0 2px 8px rgba(64, 158, 255, 0.4);
+}
+
+.node-arrow {
+  color: #c0c4cc;
+  flex-shrink: 0;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+/* 姘村钩甯冨眬锛氱澶村湪鑺傜偣鍙充晶 */
+.topology-node-wrapper.layout-horizontal .node-arrow {
+  margin-left: 20px;
+  margin-right: 20px;
+}
+
+/* 鍨傜洿甯冨眬锛氱澶村湪鑺傜偣涓嬫柟 */
+.topology-node-wrapper.layout-vertical .node-arrow {
+  margin-top: 15px;
+  margin-bottom: 15px;
+}
+
+.device-detail-card {
+  margin-top: 20px;
+}
+
+.device-detail-card .card-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+
+@media (max-width: 768px) {
+  .panel-header {
+    flex-direction: column;
+    align-items: flex-start;
+    gap: 12px;
+  }
+
+  .action-buttons {
+    width: 100%;
+    flex-wrap: wrap;
+  }
+
+  .topology-container.layout-horizontal {
+    flex-direction: column;
+    gap: 30px;
+  }
+
+  .node-arrow {
+    transform: rotate(90deg);
+  }
+}
+</style>
+
diff --git a/mes-web/src/views/plcTest/components/MultiDeviceTest/ExecutionMonitor.vue b/mes-web/src/views/plcTest/components/MultiDeviceTest/ExecutionMonitor.vue
index 0569fd3..1502b50 100644
--- a/mes-web/src/views/plcTest/components/MultiDeviceTest/ExecutionMonitor.vue
+++ b/mes-web/src/views/plcTest/components/MultiDeviceTest/ExecutionMonitor.vue
@@ -4,11 +4,34 @@
       <div>
         <h3>浠诲姟鎵ц鐩戞帶</h3>
         <p>瀹炴椂鏌ョ湅鏈�鏂扮殑澶氳澶囦换鍔�</p>
+        <p v-if="sseConnected" class="sse-status connected">
+          <el-icon><Connection /></el-icon>
+          瀹炴椂鐩戞帶宸茶繛鎺�
+        </p>
+        <p v-else class="sse-status disconnected">
+          <el-icon><Close /></el-icon>
+          瀹炴椂鐩戞帶鏈繛鎺�
+        </p>
       </div>
-      <el-button :loading="loading" @click="fetchTasks">
-        <el-icon><Refresh /></el-icon>
-        鍒锋柊
-      </el-button>
+      <div class="action-buttons">
+        <el-button :loading="loading" @click="fetchTasks">
+          <el-icon><Refresh /></el-icon>
+          鍒锋柊
+        </el-button>
+        <el-button
+          v-if="!sseConnected"
+          type="success"
+          @click="connectSSE"
+          :loading="sseConnecting"
+        >
+          <el-icon><VideoPlay /></el-icon>
+          寮�鍚疄鏃剁洃鎺�
+        </el-button>
+        <el-button v-else type="danger" @click="disconnectSSE">
+          <el-icon><VideoPause /></el-icon>
+          鍏抽棴瀹炴椂鐩戞帶
+        </el-button>
+      </div>
     </div>
 
     <el-table
@@ -17,17 +40,29 @@
       height="300"
       stripe
       @row-click="handleRowClick"
+      row-key="taskId"
     >
       <el-table-column prop="taskId" label="浠诲姟缂栧彿" min-width="160" />
       <el-table-column prop="groupId" label="璁惧缁処D" width="120" />
       <el-table-column prop="status" label="鐘舵��" width="120">
         <template #default="{ row }">
-          <el-tag :type="statusType(row.status)">{{ row.status }}</el-tag>
+          <el-tag :type="statusType(row.status)">
+            {{ getStatusLabel(row.status) }}
+          </el-tag>
         </template>
       </el-table-column>
-      <el-table-column prop="currentStep" label="杩涘害" width="120">
+      <el-table-column prop="currentStep" label="杩涘害" width="140">
         <template #default="{ row }">
-          {{ row.currentStep || 0 }} / {{ row.totalSteps || 0 }}
+          <div class="progress-cell">
+            <span>{{ row.currentStep || 0 }} / {{ row.totalSteps || 0 }}</span>
+            <el-progress
+              :percentage="getProgressPercentage(row)"
+              :status="getProgressStatus(row.status)"
+              :stroke-width="6"
+              :show-text="false"
+              style="margin-top: 4px;"
+            />
+          </div>
         </template>
       </el-table-column>
       <el-table-column label="寮�濮嬫椂闂�" min-width="160">
@@ -40,20 +75,61 @@
           {{ formatDateTime(row.endTime) }}
         </template>
       </el-table-column>
+      <el-table-column label="鎿嶄綔" width="120" fixed="right">
+        <template #default="{ row }">
+          <el-button
+            link
+            type="primary"
+            size="small"
+            @click.stop="handleRowClick(row)"
+          >
+            鏌ョ湅璇︽儏
+          </el-button>
+          <el-button
+            v-if="row.status === 'RUNNING'"
+            link
+            type="danger"
+            size="small"
+            @click.stop="handleCancelTask(row)"
+          >
+            鍙栨秷
+          </el-button>
+        </template>
+      </el-table-column>
     </el-table>
 
-    <el-drawer v-model="drawerVisible" size="40%" title="浠诲姟姝ラ璇︽儏">
-      <el-timeline v-loading="stepsLoading" :reverse="false">
+    <el-drawer v-model="drawerVisible" size="40%" :title="`浠诲姟姝ラ璇︽儏 - ${currentTaskId || ''}`">
+      <div class="drawer-header" v-if="currentTask">
+        <el-descriptions :column="2" border size="small">
+          <el-descriptions-item label="浠诲姟鐘舵��">
+            <el-tag :type="statusType(currentTask.status)">
+              {{ getStatusLabel(currentTask.status) }}
+            </el-tag>
+          </el-descriptions-item>
+          <el-descriptions-item label="杩涘害">
+            {{ currentTask.currentStep || 0 }} / {{ currentTask.totalSteps || 0 }}
+          </el-descriptions-item>
+        </el-descriptions>
+      </div>
+      <el-timeline v-loading="stepsLoading" :reverse="false" style="margin-top: 20px;">
         <el-timeline-item
           v-for="step in steps"
           :key="step.id"
           :timestamp="formatDateTime(step.startTime) || '-'"
-          :type="step.status === 'COMPLETED' ? 'success' : step.status === 'FAILED' ? 'danger' : 'primary'"
+          :type="getStepTimelineType(step.status)"
         >
           <div class="step-title">{{ step.stepName }}</div>
-          <div class="step-desc">鐘舵�侊細{{ step.status }}</div>
+          <div class="step-desc">
+            <el-tag :type="getStepStatusType(step.status)" size="small">
+              {{ getStepStatusLabel(step.status) }}
+            </el-tag>
+          </div>
           <div class="step-desc">鑰楁椂锛歿{ formatDuration(step.durationMs) }}</div>
-          <div class="step-desc" v-if="step.errorMessage">
+          <div class="step-desc" v-if="step.retryCount > 0">
+            閲嶈瘯娆℃暟锛歿{ step.retryCount }}
+          </div>
+          <div class="step-desc error-message" v-if="step.errorMessage">
+            <el-icon><Warning /></el-icon>
             閿欒锛歿{ step.errorMessage }}
           </div>
         </el-timeline-item>
@@ -63,14 +139,25 @@
 </template>
 
 <script setup>
-import { onMounted, ref, watch } from 'vue'
-import { ElMessage } from 'element-plus'
-import { Refresh } from '@element-plus/icons-vue'
+import { onMounted, onUnmounted, ref, watch } from 'vue'
+import { ElMessage, ElMessageBox } from 'element-plus'
+import {
+  Refresh,
+  Connection,
+  Close,
+  VideoPlay,
+  VideoPause,
+  Warning
+} from '@element-plus/icons-vue'
 import { multiDeviceTaskApi } from '@/api/device/multiDeviceTask'
 
 const props = defineProps({
   groupId: {
     type: [String, Number],
+    default: null
+  },
+  taskId: {
+    type: String,
     default: null
   }
 })
@@ -81,6 +168,13 @@
 const stepsLoading = ref(false)
 const steps = ref([])
 const currentTaskId = ref(null)
+const currentTask = ref(null)
+
+// SSE鐩稿叧
+const sseConnected = ref(false)
+const sseConnecting = ref(false)
+let eventSource = null
+const baseURL = import.meta.env.VITE_API_BASE_URL || ''
 
 const fetchTasks = async () => {
   try {
@@ -98,8 +192,223 @@
   }
 }
 
+// SSE杩炴帴
+const connectSSE = () => {
+  if (eventSource) {
+    disconnectSSE()
+  }
+  
+  sseConnecting.value = true
+  try {
+    // 鏋勫缓SSE URL - 鍚庣鍙敮鎸� taskId 鍙傛暟锛屼笉鏀寔 groupId
+    let url = `${baseURL}/api/plcSend/task/notification/sse`
+    // 濡傛灉娌℃湁鎸囧畾 taskId锛屽垯鐩戝惉鎵�鏈変换鍔★紙涓嶄紶鍙傛暟锛�
+    if (props.taskId) {
+      url += `?taskId=${encodeURIComponent(props.taskId)}`
+    }
+    // 娉ㄦ剰锛氬悗绔笉鏀寔 groupId 鍙傛暟锛屽鏋滈渶瑕佺洃鍚煇涓粍鐨勬墍鏈変换鍔★紝
+    // 闇�瑕佸湪鍓嶇鏍规嵁 groupId 鑾峰彇浠诲姟鍒楄〃锛岀劧鍚庝负姣忎釜浠诲姟鍒涘缓杩炴帴
+    // 鎴栬�呬娇鐢ㄤ笉浼犲弬鏁扮殑鏂瑰紡鐩戝惉鎵�鏈変换鍔★紝鐒跺悗鍦ㄥ墠绔繃婊�
+
+    eventSource = new EventSource(url)
+
+    eventSource.onopen = () => {
+      sseConnected.value = true
+      sseConnecting.value = false
+      ElMessage.success('瀹炴椂鐩戞帶宸茶繛鎺�')
+    }
+
+    eventSource.onerror = (error) => {
+      console.error('SSE杩炴帴閿欒:', error)
+      sseConnected.value = false
+      sseConnecting.value = false
+      if (eventSource?.readyState === EventSource.CLOSED) {
+        ElMessage.warning('瀹炴椂鐩戞帶杩炴帴宸叉柇寮�')
+        // 灏濊瘯閲嶈繛
+        setTimeout(() => {
+          if (!sseConnected.value) {
+            connectSSE()
+          }
+        }, 3000)
+      }
+    }
+
+    // 鐩戝惉杩炴帴鎴愬姛浜嬩欢
+    eventSource.addEventListener('connected', (event) => {
+      try {
+        const data = JSON.parse(event.data)
+        console.log('SSE杩炴帴鎴愬姛:', data)
+      } catch (error) {
+        console.error('瑙f瀽杩炴帴娑堟伅澶辫触:', error)
+      }
+    })
+
+    // 鐩戝惉浠诲姟鐘舵�佹洿鏂�
+    eventSource.addEventListener('taskStatus', (event) => {
+      try {
+        const data = JSON.parse(event.data)
+        // 濡傛灉鎸囧畾浜� groupId锛屽彧鏇存柊璇ョ粍鐨勪换鍔�
+        if (!props.groupId || !data.groupId || String(data.groupId) === String(props.groupId)) {
+          updateTaskFromSSE(data)
+        }
+      } catch (error) {
+        console.error('瑙f瀽浠诲姟鐘舵�佸け璐�:', error)
+      }
+    })
+
+    // 鐩戝惉姝ラ鏇存柊
+    eventSource.addEventListener('stepUpdate', (event) => {
+      try {
+        const data = JSON.parse(event.data)
+        // 濡傛灉鏁版嵁涓寘鍚� taskId锛屾鏌ユ槸鍚﹀尮閰嶅綋鍓嶆煡鐪嬬殑浠诲姟
+        if (data.taskId && data.taskId === currentTaskId.value) {
+          updateStepFromSSE(data)
+        } else if (!data.taskId) {
+          // 濡傛灉娌℃湁 taskId锛屽彲鑳芥槸姝ラ鏁版嵁鐩存帴浼犻��
+          updateStepFromSSE(data)
+        }
+      } catch (error) {
+        console.error('瑙f瀽姝ラ鏇存柊澶辫触:', error)
+      }
+    })
+
+    // 鐩戝惉姝ラ鍒楄〃鏇存柊
+    eventSource.addEventListener('stepsUpdate', (event) => {
+      try {
+        const data = JSON.parse(event.data)
+        if (data.taskId === currentTaskId.value && Array.isArray(data.steps)) {
+          steps.value = data.steps
+        }
+      } catch (error) {
+        console.error('瑙f瀽姝ラ鍒楄〃澶辫触:', error)
+      }
+    })
+  } catch (error) {
+    console.error('鍒涘缓SSE杩炴帴澶辫触:', error)
+    ElMessage.error('杩炴帴瀹炴椂鐩戞帶澶辫触: ' + error.message)
+    sseConnecting.value = false
+  }
+}
+
+const disconnectSSE = () => {
+  if (eventSource) {
+    eventSource.close()
+    eventSource = null
+  }
+  sseConnected.value = false
+  sseConnecting.value = false
+}
+
+// 浠嶴SE鏇存柊浠诲姟鐘舵��
+const updateTaskFromSSE = (data) => {
+  if (!data || !data.taskId) return
+
+  // 濡傛灉鎸囧畾浜� groupId锛屽彧澶勭悊璇ョ粍鐨勪换鍔�
+  if (props.groupId && data.groupId && String(data.groupId) !== String(props.groupId)) {
+    return
+  }
+
+  const taskIndex = tasks.value.findIndex(t => t.taskId === data.taskId)
+  if (taskIndex >= 0) {
+    // 鏇存柊浠诲姟 - 淇濈暀鍘熸湁瀛楁锛屽彧鏇存柊SSE浼犳潵鐨勫瓧娈�
+    const existingTask = tasks.value[taskIndex]
+    tasks.value[taskIndex] = {
+      ...existingTask,
+      status: data.status || existingTask.status,
+      currentStep: data.currentStep !== undefined ? data.currentStep : existingTask.currentStep,
+      totalSteps: data.totalSteps !== undefined ? data.totalSteps : existingTask.totalSteps,
+      startTime: data.startTime ? new Date(data.startTime) : existingTask.startTime,
+      endTime: data.endTime ? new Date(data.endTime) : existingTask.endTime,
+      errorMessage: data.errorMessage || existingTask.errorMessage
+    }
+    // 濡傛灉褰撳墠鏌ョ湅鐨勬槸杩欎釜浠诲姟锛屼篃鏇存柊
+    if (currentTaskId.value === data.taskId) {
+      currentTask.value = tasks.value[taskIndex]
+    }
+  } else {
+    // 鏂颁换鍔★紝娣诲姞鍒板垪琛紙闇�瑕佽浆鎹㈡椂闂存埑涓篋ate瀵硅薄锛�
+    const newTask = {
+      ...data,
+      startTime: data.startTime ? new Date(data.startTime) : null,
+      endTime: data.endTime ? new Date(data.endTime) : null
+    }
+    tasks.value.unshift(newTask)
+  }
+}
+
+// 浠嶴SE鏇存柊姝ラ
+const updateStepFromSSE = (data) => {
+  if (!data) return
+  
+  // 濡傛灉鏁版嵁涓寘鍚� taskId锛屾鏌ユ槸鍚﹀尮閰嶅綋鍓嶆煡鐪嬬殑浠诲姟
+  if (data.taskId && data.taskId !== currentTaskId.value) {
+    return
+  }
+
+  // 濡傛灉褰撳墠娌℃湁鎵撳紑浠诲姟璇︽儏锛屼笉鏇存柊姝ラ
+  if (!currentTaskId.value) {
+    return
+  }
+
+  // 浣跨敤 id 鎴� stepOrder 鏉ユ煡鎵炬楠�
+  const stepIndex = data.id 
+    ? steps.value.findIndex(s => s.id === data.id)
+    : data.stepOrder !== undefined
+    ? steps.value.findIndex(s => s.stepOrder === data.stepOrder)
+    : -1
+
+  if (stepIndex >= 0) {
+    // 鏇存柊姝ラ - 淇濈暀鍘熸湁瀛楁锛屽彧鏇存柊SSE浼犳潵鐨勫瓧娈�
+    const existingStep = steps.value[stepIndex]
+    steps.value[stepIndex] = {
+      ...existingStep,
+      status: data.status || existingStep.status,
+      startTime: data.startTime ? new Date(data.startTime) : existingStep.startTime,
+      endTime: data.endTime ? new Date(data.endTime) : existingStep.endTime,
+      durationMs: data.durationMs !== undefined ? data.durationMs : existingStep.durationMs,
+      retryCount: data.retryCount !== undefined ? data.retryCount : existingStep.retryCount,
+      errorMessage: data.errorMessage || existingStep.errorMessage
+    }
+  } else if (data.stepOrder !== undefined) {
+    // 鏂版楠わ紝娣诲姞鍒板垪琛紙闇�瑕佽浆鎹㈡椂闂存埑涓篋ate瀵硅薄锛�
+    const newStep = {
+      ...data,
+      startTime: data.startTime ? new Date(data.startTime) : null,
+      endTime: data.endTime ? new Date(data.endTime) : null
+    }
+    steps.value.push(newStep)
+    // 鎸� stepOrder 鎺掑簭
+    steps.value.sort((a, b) => (a.stepOrder || 0) - (b.stepOrder || 0))
+  }
+}
+
+const handleCancelTask = async (row) => {
+  try {
+    await ElMessageBox.confirm(
+      `纭畾瑕佸彇娑堜换鍔� ${row.taskId} 鍚楋紵`,
+      '纭鍙栨秷',
+      {
+        confirmButtonText: '纭畾',
+        cancelButtonText: '鍙栨秷',
+        type: 'warning'
+      }
+    )
+    await multiDeviceTaskApi.cancelTask(row.taskId)
+    ElMessage.success('浠诲姟宸插彇娑�')
+    fetchTasks()
+  } catch (error) {
+    if (error !== 'cancel') {
+      ElMessage.error(error?.message || '鍙栨秷浠诲姟澶辫触')
+    }
+  }
+}
+
+const emit = defineEmits(['task-selected'])
+
 const handleRowClick = async (row) => {
   currentTaskId.value = row.taskId
+  currentTask.value = row
+  emit('task-selected', row)
   drawerVisible.value = true
   stepsLoading.value = true
   try {
@@ -120,9 +429,67 @@
       return 'danger'
     case 'RUNNING':
       return 'warning'
+    case 'PENDING':
+      return 'info'
+    case 'CANCELLED':
+      return 'info'
     default:
       return 'info'
   }
+}
+
+const getStatusLabel = (status) => {
+  const s = (status || '').toUpperCase()
+  const statusMap = {
+    'COMPLETED': '宸插畬鎴�',
+    'FAILED': '澶辫触',
+    'RUNNING': '鎵ц涓�',
+    'PENDING': '绛夊緟涓�',
+    'CANCELLED': '宸插彇娑�'
+  }
+  return statusMap[s] || s || '鏈煡'
+}
+
+const getProgressPercentage = (row) => {
+  if (!row.totalSteps || row.totalSteps === 0) return 0
+  return Math.round(((row.currentStep || 0) / row.totalSteps) * 100)
+}
+
+const getProgressStatus = (status) => {
+  const s = (status || '').toUpperCase()
+  if (s === 'COMPLETED') return 'success'
+  if (s === 'FAILED') return 'exception'
+  if (s === 'RUNNING') return 'active'
+  return null
+}
+
+const getStepTimelineType = (status) => {
+  const s = (status || '').toUpperCase()
+  if (s === 'COMPLETED') return 'success'
+  if (s === 'FAILED') return 'danger'
+  if (s === 'RUNNING') return 'primary'
+  return 'info'
+}
+
+const getStepStatusType = (status) => {
+  const s = (status || '').toUpperCase()
+  if (s === 'COMPLETED') return 'success'
+  if (s === 'FAILED') return 'danger'
+  if (s === 'RUNNING') return 'warning'
+  if (s === 'PENDING') return 'info'
+  return 'default'
+}
+
+const getStepStatusLabel = (status) => {
+  const s = (status || '').toUpperCase()
+  const statusMap = {
+    'COMPLETED': '宸插畬鎴�',
+    'FAILED': '澶辫触',
+    'RUNNING': '鎵ц涓�',
+    'PENDING': '绛夊緟涓�',
+    'SKIPPED': '宸茶烦杩�'
+  }
+  return statusMap[s] || s || '鏈煡'
 }
 
 const formatDuration = (ms) => {
@@ -157,14 +524,45 @@
   () => props.groupId,
   () => {
     fetchTasks()
+    // 濡傛灉SSE宸茶繛鎺ワ紝閲嶆柊杩炴帴锛堝洜涓虹洃鍚墍鏈変换鍔★紝鍓嶇浼氳繃婊わ級
+    if (sseConnected.value) {
+      disconnectSSE()
+      // 寤惰繜閲嶈繛锛岄伩鍏嶉绻佽繛鎺�
+      setTimeout(() => {
+        connectSSE()
+      }, 500)
+    }
   },
   { immediate: true }
 )
 
-onMounted(fetchTasks)
+watch(
+  () => props.taskId,
+  () => {
+    // 濡傛灉鎸囧畾浜� taskId锛岄噸鏂拌繛鎺ヤ互鐩戝惉鐗瑰畾浠诲姟
+    if (sseConnected.value) {
+      disconnectSSE()
+      setTimeout(() => {
+        connectSSE()
+      }, 500)
+    }
+  }
+)
+
+onMounted(() => {
+  fetchTasks()
+  // 鑷姩杩炴帴SSE
+  connectSSE()
+})
+
+onUnmounted(() => {
+  disconnectSSE()
+})
 
 defineExpose({
-  fetchTasks
+  fetchTasks,
+  connectSSE,
+  disconnectSSE
 })
 </script>
 
@@ -193,6 +591,56 @@
   font-size: 13px;
 }
 
+.sse-status {
+  margin-top: 4px;
+  font-size: 12px;
+  display: flex;
+  align-items: center;
+  gap: 4px;
+}
+
+.sse-status.connected {
+  color: #67c23a;
+}
+
+.sse-status.disconnected {
+  color: #f56c6c;
+}
+
+.action-buttons {
+  display: flex;
+  gap: 12px;
+}
+
+.progress-cell {
+  display: flex;
+  flex-direction: column;
+  gap: 4px;
+}
+
+.drawer-header {
+  margin-bottom: 20px;
+}
+
+.step-title {
+  font-weight: 600;
+  margin-bottom: 4px;
+}
+
+.step-desc {
+  font-size: 13px;
+  color: #606266;
+  margin-top: 4px;
+}
+
+.step-desc.error-message {
+  color: #f56c6c;
+  display: flex;
+  align-items: center;
+  gap: 4px;
+  margin-top: 8px;
+}
+
 .step-title {
   font-weight: 600;
   margin-bottom: 4px;
diff --git a/mes-web/src/views/plcTest/components/MultiDeviceTest/ResultAnalysis.vue b/mes-web/src/views/plcTest/components/MultiDeviceTest/ResultAnalysis.vue
new file mode 100644
index 0000000..47588e3
--- /dev/null
+++ b/mes-web/src/views/plcTest/components/MultiDeviceTest/ResultAnalysis.vue
@@ -0,0 +1,638 @@
+<template>
+  <div class="result-analysis">
+    <div class="panel-header">
+      <div>
+        <h3>娴嬭瘯缁撴灉鍒嗘瀽</h3>
+        <p v-if="task">浠诲姟缂栧彿锛歿{ task.taskId }}</p>
+        <p v-else class="warning">璇烽�夋嫨涓�涓换鍔℃煡鐪嬪垎鏋愮粨鏋�</p>
+      </div>
+      <div class="action-buttons">
+        <el-button :loading="loading" @click="handleRefresh">
+          <el-icon><Refresh /></el-icon>
+          鍒锋柊
+        </el-button>
+        <el-button type="primary" :disabled="!task" @click="handleExport('json')">
+          <el-icon><Download /></el-icon>
+          瀵煎嚭JSON
+        </el-button>
+        <el-button type="success" :disabled="!task" @click="handleExport('excel')">
+          <el-icon><Document /></el-icon>
+          瀵煎嚭Excel
+        </el-button>
+      </div>
+    </div>
+
+    <div v-if="!task" class="empty-state">
+      <el-empty description="鏆傛棤浠诲姟鏁版嵁" />
+    </div>
+
+    <div v-else class="analysis-content">
+      <!-- 鎬讳綋缁撴灉 -->
+      <el-card class="overall-result-card" shadow="never">
+        <template #header>
+          <div class="card-header">
+            <span>鎬讳綋缁撴灉</span>
+            <el-tag :type="getOverallStatusType()" size="large">
+              {{ getOverallStatusLabel() }}
+            </el-tag>
+          </div>
+        </template>
+        <div class="result-stats">
+          <div class="stat-item">
+            <div class="stat-label">鎵ц鏃堕棿</div>
+            <div class="stat-value">{{ formatDuration(taskDuration) }}</div>
+          </div>
+          <div class="stat-item">
+            <div class="stat-label">鎬绘楠ゆ暟</div>
+            <div class="stat-value">{{ task.totalSteps || 0 }}</div>
+          </div>
+          <div class="stat-item">
+            <div class="stat-label">瀹屾垚姝ラ</div>
+            <div class="stat-value success">{{ completedSteps }}</div>
+          </div>
+          <div class="stat-item">
+            <div class="stat-label">澶辫触姝ラ</div>
+            <div class="stat-value danger">{{ failedSteps }}</div>
+          </div>
+          <div class="stat-item">
+            <div class="stat-label">鎴愬姛鐜�</div>
+            <div class="stat-value" :class="successRateClass">
+              {{ successRate }}%
+            </div>
+          </div>
+        </div>
+      </el-card>
+
+      <!-- 杩涘害鏉� -->
+      <el-card class="progress-card" shadow="never">
+        <template #header>
+          <span>鎵ц杩涘害</span>
+        </template>
+        <el-progress
+          :percentage="progressPercentage"
+          :status="progressStatus"
+          :stroke-width="20"
+          :format="() => `${completedSteps}/${task.totalSteps || 0}`"
+        />
+      </el-card>
+
+      <!-- 姝ラ璇︽儏 -->
+      <el-card class="steps-card" shadow="never">
+        <template #header>
+          <span>姝ラ鎵ц璇︽儏</span>
+        </template>
+        <el-table
+          v-loading="stepsLoading"
+          :data="steps"
+          stripe
+          style="width: 100%"
+        >
+          <el-table-column type="index" label="搴忓彿" width="60" />
+          <el-table-column prop="stepName" label="姝ラ鍚嶇О" min-width="150" />
+          <el-table-column prop="deviceId" label="璁惧ID" width="120" />
+          <el-table-column prop="status" label="鐘舵��" width="100">
+            <template #default="{ row }">
+              <el-tag :type="getStepStatusType(row.status)">
+                {{ getStepStatusLabel(row.status) }}
+              </el-tag>
+            </template>
+          </el-table-column>
+          <el-table-column label="鑰楁椂" width="100">
+            <template #default="{ row }">
+              {{ formatDuration(row.durationMs) }}
+            </template>
+          </el-table-column>
+          <el-table-column label="閲嶈瘯娆℃暟" width="100">
+            <template #default="{ row }">
+              {{ row.retryCount || 0 }}
+            </template>
+          </el-table-column>
+          <el-table-column label="寮�濮嬫椂闂�" min-width="160">
+            <template #default="{ row }">
+              {{ formatDateTime(row.startTime) }}
+            </template>
+          </el-table-column>
+          <el-table-column label="缁撴潫鏃堕棿" min-width="160">
+            <template #default="{ row }">
+              {{ formatDateTime(row.endTime) }}
+            </template>
+          </el-table-column>
+          <el-table-column prop="errorMessage" label="閿欒淇℃伅" min-width="200" show-overflow-tooltip />
+          <el-table-column label="鎿嶄綔" width="120" fixed="right">
+            <template #default="{ row }">
+              <el-button
+                link
+                type="primary"
+                size="small"
+                @click="viewStepDetail(row)"
+              >
+                鏌ョ湅璇︽儏
+              </el-button>
+            </template>
+          </el-table-column>
+        </el-table>
+      </el-card>
+
+      <!-- 鏁版嵁缁熻鍥捐〃 -->
+      <el-card class="chart-card" shadow="never" v-if="steps.length > 0">
+        <template #header>
+          <span>鎵ц鏃堕棿鍒嗗竷</span>
+        </template>
+        <div class="chart-container">
+          <div class="chart-item" v-for="(step, index) in steps" :key="step.id">
+            <div class="chart-bar">
+              <div
+                class="bar-fill"
+                :class="getStepStatusClass(step.status)"
+                :style="{ width: getBarWidth(step.durationMs) + '%' }"
+              >
+                <span class="bar-label">{{ formatDuration(step.durationMs) }}</span>
+              </div>
+            </div>
+            <div class="chart-label">{{ step.stepName }}</div>
+          </div>
+        </div>
+      </el-card>
+    </div>
+
+    <!-- 姝ラ璇︽儏瀵硅瘽妗� -->
+    <el-dialog
+      v-model="detailDialogVisible"
+      :title="`姝ラ璇︽儏 - ${selectedStep?.stepName || ''}`"
+      width="60%"
+    >
+      <div v-if="selectedStep" class="step-detail-content">
+        <el-descriptions :column="2" border>
+          <el-descriptions-item label="姝ラ鍚嶇О">
+            {{ selectedStep.stepName }}
+          </el-descriptions-item>
+          <el-descriptions-item label="璁惧ID">
+            {{ selectedStep.deviceId }}
+          </el-descriptions-item>
+          <el-descriptions-item label="鐘舵��">
+            <el-tag :type="getStepStatusType(selectedStep.status)">
+              {{ getStepStatusLabel(selectedStep.status) }}
+            </el-tag>
+          </el-descriptions-item>
+          <el-descriptions-item label="閲嶈瘯娆℃暟">
+            {{ selectedStep.retryCount || 0 }}
+          </el-descriptions-item>
+          <el-descriptions-item label="寮�濮嬫椂闂�">
+            {{ formatDateTime(selectedStep.startTime) }}
+          </el-descriptions-item>
+          <el-descriptions-item label="缁撴潫鏃堕棿">
+            {{ formatDateTime(selectedStep.endTime) }}
+          </el-descriptions-item>
+          <el-descriptions-item label="鑰楁椂">
+            {{ formatDuration(selectedStep.durationMs) }}
+          </el-descriptions-item>
+          <el-descriptions-item label="閿欒淇℃伅" v-if="selectedStep.errorMessage">
+            {{ selectedStep.errorMessage }}
+          </el-descriptions-item>
+        </el-descriptions>
+
+        <el-divider>杈撳叆鏁版嵁</el-divider>
+        <el-input
+          v-model="selectedStepInputData"
+          type="textarea"
+          :rows="6"
+          readonly
+        />
+
+        <el-divider>杈撳嚭鏁版嵁</el-divider>
+        <el-input
+          v-model="selectedStepOutputData"
+          type="textarea"
+          :rows="6"
+          readonly
+        />
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import { computed, ref, watch } from 'vue'
+import { ElMessage } from 'element-plus'
+import { Refresh, Download, Document } from '@element-plus/icons-vue'
+import { multiDeviceTaskApi } from '@/api/device/multiDeviceTask'
+
+const props = defineProps({
+  task: {
+    type: Object,
+    default: null
+  }
+})
+
+const loading = ref(false)
+const stepsLoading = ref(false)
+const steps = ref([])
+const detailDialogVisible = ref(false)
+const selectedStep = ref(null)
+
+const selectedStepInputData = computed(() => {
+  if (!selectedStep.value?.inputData) return ''
+  try {
+    return JSON.stringify(JSON.parse(selectedStep.value.inputData), null, 2)
+  } catch {
+    return selectedStep.value.inputData
+  }
+})
+
+const selectedStepOutputData = computed(() => {
+  if (!selectedStep.value?.outputData) return ''
+  try {
+    return JSON.stringify(JSON.parse(selectedStep.value.outputData), null, 2)
+  } catch {
+    return selectedStep.value.outputData
+  }
+})
+
+const taskDuration = computed(() => {
+  if (!props.task?.startTime || !props.task?.endTime) return 0
+  try {
+    const start = new Date(props.task.startTime)
+    const end = new Date(props.task.endTime)
+    return end.getTime() - start.getTime()
+  } catch {
+    return 0
+  }
+})
+
+const completedSteps = computed(() => {
+  return steps.value.filter(s => s.status === 'COMPLETED').length
+})
+
+const failedSteps = computed(() => {
+  return steps.value.filter(s => s.status === 'FAILED').length
+})
+
+const successRate = computed(() => {
+  if (steps.value.length === 0) return 0
+  return Math.round((completedSteps.value / steps.value.length) * 100)
+})
+
+const successRateClass = computed(() => {
+  if (successRate.value >= 90) return 'success'
+  if (successRate.value >= 70) return 'warning'
+  return 'danger'
+})
+
+const progressPercentage = computed(() => {
+  if (!props.task?.totalSteps || props.task.totalSteps === 0) return 0
+  return Math.round((completedSteps.value / props.task.totalSteps) * 100)
+})
+
+const progressStatus = computed(() => {
+  if (props.task?.status === 'COMPLETED') return 'success'
+  if (props.task?.status === 'FAILED') return 'exception'
+  return 'active'
+})
+
+const fetchSteps = async () => {
+  if (!props.task?.taskId) {
+    steps.value = []
+    return
+  }
+  try {
+    stepsLoading.value = true
+    const { data } = await multiDeviceTaskApi.getTaskSteps(props.task.taskId)
+    steps.value = Array.isArray(data) ? data : (data?.data || [])
+  } catch (error) {
+    ElMessage.error(error?.message || '鍔犺浇姝ラ璇︽儏澶辫触')
+    steps.value = []
+  } finally {
+    stepsLoading.value = false
+  }
+}
+
+const handleRefresh = () => {
+  fetchSteps()
+}
+
+const viewStepDetail = (step) => {
+  selectedStep.value = step
+  detailDialogVisible.value = true
+}
+
+const handleExport = async (format) => {
+  if (!props.task) {
+    ElMessage.warning('璇峰厛閫夋嫨浠诲姟')
+    return
+  }
+  try {
+    loading.value = true
+    // 鏋勫缓瀵煎嚭鏁版嵁
+    const exportData = {
+      task: props.task,
+      steps: steps.value,
+      statistics: {
+        totalSteps: props.task.totalSteps || 0,
+        completedSteps: completedSteps.value,
+        failedSteps: failedSteps.value,
+        successRate: successRate.value,
+        duration: taskDuration.value
+      }
+    }
+    
+    if (format === 'json') {
+      const blob = new Blob([JSON.stringify(exportData, null, 2)], {
+        type: 'application/json'
+      })
+      const url = URL.createObjectURL(blob)
+      const a = document.createElement('a')
+      a.href = url
+      a.download = `task_${props.task.taskId}_${Date.now()}.json`
+      a.click()
+      URL.revokeObjectURL(url)
+      ElMessage.success('瀵煎嚭鎴愬姛')
+    } else if (format === 'excel') {
+      // TODO: 瀹炵幇Excel瀵煎嚭
+      ElMessage.info('Excel瀵煎嚭鍔熻兘寮�鍙戜腑')
+    }
+  } catch (error) {
+    ElMessage.error('瀵煎嚭澶辫触: ' + error.message)
+  } finally {
+    loading.value = false
+  }
+}
+
+const getOverallStatusType = () => {
+  const status = props.task?.status?.toUpperCase()
+  if (status === 'COMPLETED') return 'success'
+  if (status === 'FAILED') return 'danger'
+  if (status === 'RUNNING') return 'warning'
+  return 'info'
+}
+
+const getOverallStatusLabel = () => {
+  const status = props.task?.status?.toUpperCase()
+  const statusMap = {
+    'COMPLETED': '宸插畬鎴�',
+    'FAILED': '澶辫触',
+    'RUNNING': '鎵ц涓�',
+    'PENDING': '绛夊緟涓�',
+    'CANCELLED': '宸插彇娑�'
+  }
+  return statusMap[status] || status || '鏈煡'
+}
+
+const getStepStatusType = (status) => {
+  const s = (status || '').toUpperCase()
+  if (s === 'COMPLETED') return 'success'
+  if (s === 'FAILED') return 'danger'
+  if (s === 'RUNNING') return 'warning'
+  if (s === 'PENDING') return 'info'
+  return 'default'
+}
+
+const getStepStatusLabel = (status) => {
+  const s = (status || '').toUpperCase()
+  const statusMap = {
+    'COMPLETED': '宸插畬鎴�',
+    'FAILED': '澶辫触',
+    'RUNNING': '鎵ц涓�',
+    'PENDING': '绛夊緟涓�',
+    'SKIPPED': '宸茶烦杩�'
+  }
+  return statusMap[s] || s || '鏈煡'
+}
+
+const getStepStatusClass = (status) => {
+  const s = (status || '').toUpperCase()
+  if (s === 'COMPLETED') return 'status-success'
+  if (s === 'FAILED') return 'status-danger'
+  if (s === 'RUNNING') return 'status-warning'
+  return 'status-info'
+}
+
+const getBarWidth = (durationMs) => {
+  if (!durationMs || steps.value.length === 0) return 0
+  const maxDuration = Math.max(...steps.value.map(s => s.durationMs || 0))
+  if (maxDuration === 0) return 0
+  return Math.min((durationMs / maxDuration) * 100, 100)
+}
+
+const formatDuration = (ms) => {
+  if (!ms) return '-'
+  if (ms < 1000) return `${ms} ms`
+  if (ms < 60000) return `${(ms / 1000).toFixed(1)} s`
+  const minutes = Math.floor(ms / 60000)
+  const seconds = ((ms % 60000) / 1000).toFixed(0)
+  return `${minutes}鍒�${seconds}绉抈
+}
+
+const formatDateTime = (dateTime) => {
+  if (!dateTime) return '-'
+  try {
+    const date = new Date(dateTime)
+    if (isNaN(date.getTime())) return dateTime
+    const year = date.getFullYear()
+    const month = String(date.getMonth() + 1).padStart(2, '0')
+    const day = String(date.getDate()).padStart(2, '0')
+    const hours = String(date.getHours()).padStart(2, '0')
+    const minutes = String(date.getMinutes()).padStart(2, '0')
+    const seconds = String(date.getSeconds()).padStart(2, '0')
+    return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
+  } catch {
+    return dateTime
+  }
+}
+
+watch(
+  () => props.task,
+  () => {
+    fetchSteps()
+  },
+  { immediate: true }
+)
+
+defineExpose({
+  fetchSteps
+})
+</script>
+
+<style scoped>
+.result-analysis {
+  background: #fff;
+  border-radius: 12px;
+  padding: 20px;
+  box-shadow: 0 8px 32px rgba(15, 18, 63, 0.08);
+}
+
+.panel-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 20px;
+}
+
+.panel-header h3 {
+  margin: 0;
+}
+
+.panel-header p {
+  margin: 4px 0 0;
+  color: #909399;
+  font-size: 13px;
+}
+
+.panel-header .warning {
+  color: #f56c6c;
+}
+
+.action-buttons {
+  display: flex;
+  gap: 12px;
+}
+
+.empty-state {
+  padding: 60px 0;
+}
+
+.analysis-content {
+  display: flex;
+  flex-direction: column;
+  gap: 20px;
+}
+
+.overall-result-card .card-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+
+.result-stats {
+  display: grid;
+  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
+  gap: 20px;
+}
+
+.stat-item {
+  text-align: center;
+}
+
+.stat-label {
+  font-size: 13px;
+  color: #909399;
+  margin-bottom: 8px;
+}
+
+.stat-value {
+  font-size: 24px;
+  font-weight: 600;
+  color: #303133;
+}
+
+.stat-value.success {
+  color: #67c23a;
+}
+
+.stat-value.danger {
+  color: #f56c6c;
+}
+
+.stat-value.warning {
+  color: #e6a23c;
+}
+
+.progress-card {
+  margin-top: 0;
+}
+
+.steps-card {
+  margin-top: 0;
+}
+
+.chart-card {
+  margin-top: 0;
+}
+
+.chart-container {
+  display: flex;
+  flex-direction: column;
+  gap: 12px;
+  padding: 20px 0;
+}
+
+.chart-item {
+  display: flex;
+  align-items: center;
+  gap: 12px;
+}
+
+.chart-bar {
+  flex: 1;
+  height: 32px;
+  background: #f0f2f5;
+  border-radius: 4px;
+  position: relative;
+  overflow: hidden;
+}
+
+.bar-fill {
+  height: 100%;
+  border-radius: 4px;
+  display: flex;
+  align-items: center;
+  justify-content: flex-end;
+  padding: 0 8px;
+  transition: width 0.3s;
+  min-width: 60px;
+}
+
+.bar-fill.status-success {
+  background: linear-gradient(90deg, #67c23a 0%, #85ce61 100%);
+}
+
+.bar-fill.status-danger {
+  background: linear-gradient(90deg, #f56c6c 0%, #f78989 100%);
+}
+
+.bar-fill.status-warning {
+  background: linear-gradient(90deg, #e6a23c 0%, #ebb563 100%);
+}
+
+.bar-fill.status-info {
+  background: linear-gradient(90deg, #909399 0%, #b1b3b8 100%);
+}
+
+.bar-label {
+  color: #fff;
+  font-size: 12px;
+  font-weight: 500;
+  white-space: nowrap;
+}
+
+.chart-label {
+  width: 150px;
+  font-size: 13px;
+  color: #606266;
+  text-align: right;
+  flex-shrink: 0;
+}
+
+.step-detail-content {
+  display: flex;
+  flex-direction: column;
+  gap: 20px;
+}
+
+@media (max-width: 768px) {
+  .panel-header {
+    flex-direction: column;
+    align-items: flex-start;
+    gap: 12px;
+  }
+
+  .action-buttons {
+    width: 100%;
+    flex-wrap: wrap;
+  }
+
+  .result-stats {
+    grid-template-columns: repeat(2, 1fr);
+  }
+}
+</style>
+
diff --git a/mes-web/src/views/plcTest/components/MultiDeviceTest/TaskOrchestration.vue b/mes-web/src/views/plcTest/components/MultiDeviceTest/TaskOrchestration.vue
index f3490b4..8cf1feb 100644
--- a/mes-web/src/views/plcTest/components/MultiDeviceTest/TaskOrchestration.vue
+++ b/mes-web/src/views/plcTest/components/MultiDeviceTest/TaskOrchestration.vue
@@ -5,7 +5,7 @@
         <h3>澶氳澶囨祴璇曠紪鎺�</h3>
         <p v-if="group">褰撳墠璁惧缁勶細{{ group.groupName }}锛坽{ group.deviceCount || '-' }} 鍙拌澶囷級</p>
         <p v-else class="warning">璇峰厛鍦ㄥ乏渚ч�夋嫨涓�涓澶囩粍</p>
-        <p v-if="group && loadDeviceName" class="sub-info">涓婂ぇ杞﹁澶囷細{{ loadDeviceName }}</p>
+        <p v-if="group && loadDeviceName" class="sub-info">褰撳墠璁惧锛歿{ loadDeviceName }}</p>
       </div>
       <div class="action-buttons">
         <el-button
@@ -25,23 +25,94 @@
       </div>
     </div>
 
-    <el-form :model="form" label-width="120px">
-      <el-form-item label="鐜荤拑ID鍒楄〃">
+    <el-form :model="form" label-width="120px" :rules="rules" ref="formRef">
+      <el-form-item label="鐜荤拑ID鍒楄〃" prop="glassIds" required>
         <el-input
           v-model="glassIdsInput"
           type="textarea"
           :rows="4"
-          placeholder="璇疯緭鍏ョ幓鐠冩潯鐮侊紝鏀寔澶氳鎴栭�楀彿鍒嗛殧"
+          placeholder="璇疯緭鍏ョ幓鐠冩潯鐮侊紝鏀寔澶氳鎴栭�楀彿鍒嗛殧锛屾瘡琛屼竴涓垨閫楀彿鍒嗛殧"
+          show-word-limit
+          :maxlength="5000"
         />
+        <div class="form-tip">
+          宸茶緭鍏� {{ glassIds.length }} 涓幓鐠僆D
+        </div>
       </el-form-item>
+      
+      <el-divider content-position="left">璁惧鐗瑰畾閰嶇疆</el-divider>
+      
       <el-form-item label="浣嶇疆缂栫爜">
-        <el-input v-model="form.positionCode" placeholder="渚嬪锛歅OS1" />
+        <el-input
+          v-model="form.positionCode"
+          placeholder="渚嬪锛歅OS1"
+          clearable
+        />
+        <div class="form-tip">涓婂ぇ杞﹁澶囩殑浣嶇疆缂栫爜</div>
       </el-form-item>
+      
+      <el-form-item label="浣嶇疆鍊�">
+        <el-input-number
+          v-model="form.positionValue"
+          :min="0"
+          :max="9999"
+          placeholder="浣嶇疆鏁板��"
+        />
+        <div class="form-tip">涓婂ぇ杞﹁澶囩殑浣嶇疆鏁板��</div>
+      </el-form-item>
+      
       <el-form-item label="瀛樺偍浣嶇疆">
-        <el-input-number v-model="form.storagePosition" :min="1" :max="200" />
+        <el-input-number
+          v-model="form.storagePosition"
+          :min="1"
+          :max="200"
+          placeholder="瀛樺偍浣嶇疆缂栧彿"
+        />
+        <div class="form-tip">鐜荤拑瀛樺偍璁惧鐨勫瓨鍌ㄤ綅缃�</div>
       </el-form-item>
+      
+      <el-form-item label="澶勭悊绫诲瀷">
+        <el-select v-model="form.processType" placeholder="閫夋嫨澶勭悊绫诲瀷" clearable>
+          <el-option label="鏍囧噯澶勭悊" :value="1" />
+          <el-option label="蹇�熷鐞�" :value="2" />
+          <el-option label="鎱㈤�熷鐞�" :value="3" />
+        </el-select>
+        <div class="form-tip">澶х悊鐗囪澶囩殑澶勭悊绫诲瀷</div>
+      </el-form-item>
+      
+      <el-divider content-position="left">鎵ц閰嶇疆</el-divider>
+      
       <el-form-item label="鎵ц闂撮殧 (ms)">
-        <el-input-number v-model="form.executionInterval" :min="100" :max="10000" />
+        <el-input-number
+          v-model="form.executionInterval"
+          :min="100"
+          :max="10000"
+          :step="100"
+          placeholder="璁惧鎿嶄綔闂撮殧鏃堕棿"
+        />
+        <div class="form-tip">姣忎釜璁惧鎿嶄綔涔嬮棿鐨勯棿闅旀椂闂达紙姣锛�</div>
+      </el-form-item>
+      
+      <el-form-item label="瓒呮椂鏃堕棿 (鍒嗛挓)">
+        <el-input-number
+          v-model="form.timeoutMinutes"
+          :min="1"
+          :max="60"
+          :step="1"
+          placeholder="浠诲姟瓒呮椂鏃堕棿"
+        />
+        <div class="form-tip">浠诲姟鎵ц鐨勬渶澶ц秴鏃舵椂闂�</div>
+      </el-form-item>
+      
+      <el-form-item label="閲嶈瘯娆℃暟">
+        <el-input-number
+          v-model="form.retryCount"
+          :min="0"
+          :max="10"
+          :step="1"
+          placeholder="澶辫触閲嶈瘯娆℃暟"
+        />
+        <div class="form-tip">璁惧鎿嶄綔澶辫触鏃剁殑鏈�澶ч噸璇曟鏁�</div>
       </el-form-item>
     </el-form>
   </div>
@@ -65,9 +136,41 @@
 
 const form = reactive({
   positionCode: '',
+  positionValue: null,
   storagePosition: null,
-  executionInterval: 1000
+  processType: null,
+  executionInterval: 1000,
+  timeoutMinutes: 30,
+  retryCount: 3
 })
+
+const formRef = ref(null)
+
+const rules = {
+  glassIds: [
+    {
+      validator: (rule, value, callback) => {
+        if (glassIds.value.length === 0) {
+          callback(new Error('璇疯嚦灏戣緭鍏ヤ竴涓幓鐠僆D'))
+        } else if (glassIds.value.length > 100) {
+          callback(new Error('鐜荤拑ID鏁伴噺涓嶈兘瓒呰繃100涓�'))
+        } else {
+          // 楠岃瘉鐜荤拑ID鏍煎紡
+          const invalidIds = glassIds.value.filter(id => {
+            // 绠�鍗曠殑鏍煎紡楠岃瘉锛氫笉鑳戒负绌猴紝闀垮害鍦�1-50涔嬮棿
+            return !id || id.length === 0 || id.length > 50
+          })
+          if (invalidIds.length > 0) {
+            callback(new Error(`瀛樺湪鏃犳晥鐨勭幓鐠僆D鏍煎紡锛岃妫�鏌))
+          } else {
+            callback()
+          }
+        }
+      },
+      trigger: 'blur'
+    }
+  ]
+}
 
 const glassIdsInput = ref('')
 const loading = ref(false)
@@ -133,23 +236,78 @@
     ElMessage.warning('璇峰厛閫夋嫨璁惧缁�')
     return
   }
+  
+  // 琛ㄥ崟楠岃瘉
+  if (!formRef.value) return
+  try {
+    await formRef.value.validate()
+  } catch (error) {
+    ElMessage.warning('璇锋鏌ヨ〃鍗曡緭鍏�')
+    return
+  }
+  
   if (glassIds.value.length === 0) {
     ElMessage.warning('璇疯嚦灏戣緭鍏ヤ竴涓幓鐠僆D')
     return
   }
+  
   try {
     loading.value = true
-    await multiDeviceTaskApi.startTask({
+    
+    // 鏋勫缓浠诲姟鍙傛暟
+    const parameters = {
+      glassIds: glassIds.value,
+      executionInterval: form.executionInterval || 1000
+    }
+    
+    // 娣诲姞鍙�夊弬鏁�
+    if (form.positionCode) {
+      parameters.positionCode = form.positionCode
+    }
+    if (form.positionValue !== null) {
+      parameters.positionValue = form.positionValue
+    }
+    if (form.storagePosition !== null) {
+      parameters.storagePosition = form.storagePosition
+    }
+    if (form.processType !== null) {
+      parameters.processType = form.processType
+    }
+    if (form.timeoutMinutes) {
+      parameters.timeoutMinutes = form.timeoutMinutes
+    }
+    if (form.retryCount !== null) {
+      parameters.retryCount = form.retryCount
+    }
+    
+    // 寮傛鍚姩浠诲姟锛岀珛鍗宠繑鍥烇紝涓嶉樆濉�
+    const response = await multiDeviceTaskApi.startTask({
       groupId: props.group.id || props.group.groupId,
-      parameters: {
-        glassIds: glassIds.value,
-        positionCode: form.positionCode || null,
-        storagePosition: form.storagePosition,
-        executionInterval: form.executionInterval
-      }
+      parameters
     })
-    ElMessage.success('浠诲姟宸插惎鍔�')
-    emit('task-started')
+    
+    const task = response?.data
+    if (task && task.taskId) {
+      ElMessage.success(`浠诲姟宸插惎鍔紙寮傛鎵ц锛�: ${task.taskId}`)
+      emit('task-started', task)
+      
+      // 绔嬪嵆鍒锋柊鐩戞帶鍒楄〃锛屾樉绀烘柊鍚姩鐨勪换鍔�
+      setTimeout(() => {
+        emit('task-started')
+      }, 500)
+      
+      // 閲嶇疆琛ㄥ崟锛堜繚鐣欓儴鍒嗛厤缃級锛屾柟渚跨户缁惎鍔ㄥ叾浠栬澶囩粍
+      glassIdsInput.value = ''
+      form.positionCode = ''
+      form.positionValue = null
+      form.storagePosition = null
+      form.processType = null
+      
+      // 鎻愮ず鐢ㄦ埛鍙互缁х画鍚姩鍏朵粬璁惧缁�
+      ElMessage.info('鍙互缁х画閫夋嫨鍏朵粬璁惧缁勫惎鍔ㄦ祴璇曪紝澶氫釜璁惧缁勫皢骞惰鎵ц')
+    } else {
+      ElMessage.warning('浠诲姟鍚姩鍝嶅簲寮傚父')
+    }
   } catch (error) {
     ElMessage.error(error?.message || '浠诲姟鍚姩澶辫触')
   } finally {
@@ -234,5 +392,12 @@
   gap: 12px;
   align-items: center;
 }
+
+.form-tip {
+  font-size: 12px;
+  color: #909399;
+  margin-top: 4px;
+  line-height: 1.4;
+}
 </style>
 

--
Gitblit v1.8.0