From 1566e4c7604d85737ea67fe6757e71b8185fa48e Mon Sep 17 00:00:00 2001
From: huang <1532065656@qq.com>
Date: 星期二, 18 十一月 2025 16:52:42 +0800
Subject: [PATCH] 添加设备管理页面,添加测试设备任务监控页面

---
 mes-processes/mes-plcSend/src/main/java/com/mes/device/controller/DeviceGroupController.java           |   50 
 mes-processes/mes-plcSend/src/main/java/com/mes/interaction/base/InteractionContext.java               |   49 
 mes-processes/mes-plcSend/src/main/java/com/mes/device/vo/DevicePlcVO.java                             |    7 
 mes-processes/mes-plcSend/src/main/java/com/mes/device/request/DeviceGroupRequest.java                 |   17 
 mes-processes/mes-plcSend/src/main/java/com/mes/interaction/README.md                                  |  168 +
 mes-processes/mes-plcSend/src/main/java/com/mes/device/service/DeviceInteractionService.java           |   14 
 mes-processes/mes-plcSend/src/main/java/com/mes/task/service/MultiDeviceTaskService.java               |   42 
 mes-processes/mes-plcSend/src/main/java/com/mes/device/controller/DeviceInteractionController.java     |   46 
 mes-processes/mes-plcSend/src/main/java/com/mes/device/util/ConfigJsonHelper.java                      |   71 
 mes-processes/mes-plcSend/src/main/java/com/mes/device/service/impl/DeviceInteractionServiceImpl.java  |   55 
 mes-processes/mes-plcSend/src/main/java/com/mes/device/entity/DeviceGroupRelation.java                 |   33 
 mes-processes/mes-plcSend/src/main/java/com/mes/device/vo/DeviceControlProfile.java                    |   33 
 mes-processes/mes-plcSend/src/main/java/com/mes/device/controller/DevicePlcController.java             |   20 
 mes-web/src/api/device/multiDeviceTask.js                                                              |   58 
 mes-processes/mes-plcSend/src/main/resources/application-dev.yml                                       |    2 
 mes-web/src/views/plcTest/components/MultiDeviceTest/ExecutionMonitor.vue                              |  176 +
 mes-web/src/api/device/deviceManagement.js                                                             |   60 
 mes-processes/mes-plcSend/src/main/java/com/mes/interaction/BaseDeviceLogicHandler.java                |  124 +
 mes-processes/mes-plcSend/src/main/java/com/mes/task/entity/TaskStepDetail.java                        |   89 
 mes-processes/mes-plcSend/src/main/java/com/mes/interaction/DeviceInteractionRegistry.java             |   42 
 mes-processes/mes-plcSend/src/main/java/com/mes/interaction/impl/LoadVehicleLogicHandler.java          |  372 +++
 mes-web/src/views/plcTest/MultiDeviceWorkbench.vue                                                     |   67 
 mes-processes/mes-plcSend/src/main/java/com/mes/device/entity/DeviceInteractionExecution.java          |   55 
 mes-processes/mes-plcSend/src/main/java/com/mes/device/controller/DeviceControlProfileController.java  |   10 
 mes-processes/mes-plcSend/src/main/java/com/mes/interaction/flow/GlassStorageInteraction.java          |   38 
 mes-processes/mes-plcSend/src/main/java/com/mes/device/request/DeviceGlassFeedRequest.java             |   16 
 mes-web/src/router/index.js                                                                            |   10 
 mes-processes/mes-plcSend/src/main/java/com/mes/task/service/impl/MultiDeviceTaskServiceImpl.java      |  160 +
 mes-processes/mes-plcSend/src/main/java/com/mes/device/controller/DeviceConfigController.java          |   66 
 mes-processes/mes-plcSend/src/main/java/com/mes/task/model/TaskExecutionContext.java                   |   54 
 mes-processes/mes-plcSend/src/main/java/com/mes/device/request/DevicePlcBatchRequest.java              |    7 
 mes-processes/mes-plcSend/src/main/java/com/mes/interaction/DeviceInteraction.java                     |   28 
 mes-processes/mes-plcSend/src/main/java/com/mes/device/entity/DeviceConfig.java                        |   47 
 mes-processes/mes-plcSend/src/main/java/com/mes/task/mapper/TaskStepDetailMapper.java                  |   13 
 mes-processes/mes-plcSend/src/main/java/com/mes/interaction/impl/LargeGlassLogicHandler.java           |  195 +
 mes-processes/mes-plcSend/src/main/java/com/mes/device/多设备联合测试扩展方案.md                                  | 1153 ++++++++++
 mes-processes/mes-plcSend/src/main/java/com/mes/service/impl/PlcTestWriteServiceImpl.java              |    6 
 mes-processes/mes-plcSend/src/main/java/com/mes/device/entity/DeviceGroupConfig.java                   |   37 
 mes-processes/mes-plcSend/src/main/java/com/mes/service/PlcTestWriteService.java                       |  504 +++
 mes-web/src/utils/constants.js                                                                         |    2 
 mes-processes/mes-plcSend/src/main/java/com/mes/service/impl/PlcAutoTestServiceImpl.java               |    8 
 mes-web/src/views/device/DeviceGroupList.vue                                                           |    8 
 mes-web/src/views/plcTest/components/DeviceGroup/GroupList.vue                                         |  144 +
 mes-processes/mes-plcSend/src/main/java/com/mes/task/dto/MultiDeviceTaskRequest.java                   |   32 
 mes-processes/mes-plcSend/src/main/java/com/mes/task/model/TaskExecutionResult.java                    |   42 
 mes-processes/mes-plcSend/src/main/java/com/mes/interaction/base/InteractionResult.java                |   60 
 mes-processes/mes-plcSend/src/main/java/com/mes/device/entity/DEVICE_CONFIG_FIELDS.md                  |  152 +
 mes-processes/mes-plcSend/src/main/java/com/mes/interaction/DeviceLogicHandlerFactory.java             |   77 
 mes-processes/mes-plcSend/src/main/java/com/mes/task/mapper/MultiDeviceTaskMapper.java                 |   13 
 mes-web/src/views/device/DeviceEditDialog.vue                                                          |  485 ++++
 mes-processes/mes-plcSend/src/main/java/com/mes/device/request/DeviceConfigRequest.java                |   25 
 mes-web/src/views/device/DeviceGroupEditDialog.vue                                                     |   16 
 mes-processes/mes-plcSend/src/main/java/com/mes/device/mapper/DeviceGroupRelationMapper.java           |   16 
 mes-processes/mes-plcSend/src/main/java/com/mes/service/PlcDynamicDataService.java                     |   49 
 mes-processes/mes-plcSend/src/main/java/com/mes/device/entity/DeviceInteractionLogic.java              |   45 
 mes-processes/mes-plcSend/src/main/java/com/mes/interaction/DeviceLogicHandler.java                    |   49 
 mes-processes/mes-plcSend/src/main/java/com/mes/interaction/impl/GlassStorageLogicHandler.java         |  241 ++
 mes-processes/mes-plcSend/src/main/java/com/mes/interaction/flow/LoadVehicleInteraction.java           |   43 
 mes-processes/mes-plcSend/src/main/java/com/mes/service/impl/PlcDynamicDataServiceImpl.java            |  299 ++
 mes-processes/mes-plcSend/src/main/java/com/mes/task/dto/MultiDeviceTaskQuery.java                     |   26 
 mes-processes/mes-plcSend/src/main/java/com/mes/task/dto/TaskParameters.java                           |   50 
 mes-processes/mes-plcSend/src/main/java/com/mes/device/service/impl/DevicePlcOperationServiceImpl.java |   17 
 mes-processes/mes-plcSend/src/main/java/com/mes/task/entity/MultiDeviceTask.java                       |   85 
 /dev/null                                                                                              |  238 --
 mes-processes/mes-plcSend/src/main/java/com/mes/task/service/TaskExecutionEngine.java                  |  378 +++
 mes-processes/mes-plcSend/src/main/java/com/mes/task/controller/MultiDeviceTaskController.java         |   62 
 mes-processes/mes-plcSend/src/main/java/com/mes/interaction/flow/LargeGlassInteraction.java            |   52 
 mes-processes/mes-plcSend/src/main/java/com/mes/config/MybatisMetaObjectHandler.java                   |   37 
 mes-web/src/views/plcTest/components/MultiDeviceTest/TaskOrchestration.vue                             |  135 +
 69 files changed, 6,297 insertions(+), 583 deletions(-)

diff --git a/mes-processes/mes-plcSend/README_PLC_ADDRESS_MAPPING.md b/mes-processes/mes-plcSend/README_PLC_ADDRESS_MAPPING.md
deleted file mode 100644
index ad7b7cd..0000000
--- a/mes-processes/mes-plcSend/README_PLC_ADDRESS_MAPPING.md
+++ /dev/null
@@ -1,238 +0,0 @@
-# PLC鍦板潃鏄犲皠閰嶇疆鍔熻兘璇存槑
-
-## 姒傝堪
-
-鏈姛鑳戒紭鍖栦簡PLC鍦板潃鏄犲皠閰嶇疆绠$悊锛屾敮鎸佸姩鎬侀厤缃笉鍚岄」鐩殑PLC鍦板潃鏄犲皠锛屾棤闇�涓轰笉鍚岄」鐩垱寤轰笉鍚岀殑YAML閰嶇疆鏂囦欢銆傜郴缁熸敮鎸佷粠鏁版嵁搴撳拰閰嶇疆鏂囦欢涓ょ鏂瑰紡鍔犺浇PLC鍦板潃鏄犲皠閰嶇疆锛屼紭鍏堜娇鐢ㄦ暟鎹簱涓殑閰嶇疆銆�
-
-## 鍔熻兘鐗圭偣
-
-1. **鍔ㄦ�侀厤缃�**锛氭敮鎸侀�氳繃API鎺ュ彛鍔ㄦ�侀厤缃笉鍚岄」鐩殑PLC鍦板潃鏄犲皠
-2. **澶氭暟鎹簮**锛氭敮鎸佷粠鏁版嵁搴撳拰閰嶇疆鏂囦欢鍔犺浇閰嶇疆锛屼紭鍏堜娇鐢ㄦ暟鎹簱閰嶇疆
-3. **缂撳瓨鏈哄埗**锛氱紦瀛樹笉鍚岄」鐩殑S7Serializer瀹炰緥锛屾彁楂樻�ц兘
-4. **瀹屾暣API**锛氭彁渚涘畬鏁寸殑REST API鎺ュ彛锛屾敮鎸佸鍒犳敼鏌ユ搷浣�
-5. **杩炴帴娴嬭瘯**锛氭敮鎸佹祴璇昉LC杩炴帴鏄惁姝e父
-
-## 鏍稿績缁勪欢
-
-### 1. PlcAddressMappingConfig
-- 浣嶇疆锛歚com.mes.config.PlcAddressMappingConfig`
-- 鍔熻兘锛氶厤缃被锛屽畾涔塒LC鍦板潃鏄犲皠閰嶇疆缁撴瀯
-- 鏀寔浠庨厤缃枃浠跺姞杞介粯璁ら厤缃�
-
-### 2. PlcAddressMapping
-- 浣嶇疆锛歚com.mes.entity.PlcAddress`
-- 鍔熻兘锛氬疄浣撶被锛屽搴旀暟鎹簱琛ㄧ粨鏋�
-- 瀛樺偍椤圭洰PLC鍦板潃鏄犲皠閰嶇疆
-
-### 3. PlcAddressMappingMapper
-- 浣嶇疆锛歚com.mes.mapper.PlcAddressMapper`
-- 鍔熻兘锛歁yBatis Mapper鎺ュ彛锛屾彁渚涙暟鎹簱鎿嶄綔
-
-### 4. PlcAddressMappingService
-- 浣嶇疆锛歚com.mes.service.PlcAddressService`
-- 鍔熻兘锛氭湇鍔$被锛屾彁渚汸LC鍦板潃鏄犲皠閰嶇疆鐨勫鍒犳敼鏌ュ姛鑳�
-- 鏀寔浠庢暟鎹簱鍜岄厤缃枃浠跺姞杞介厤缃�
-
-### 5. PlcTestWriteService锛堝凡浼樺寲锛�
-- 浣嶇疆锛歚com.mes.service.PlcTestWriteService`
-- 鍔熻兘锛歅LC娴嬭瘯鍐欏叆鏈嶅姟锛屾敮鎸佸姩鎬侀厤缃甈LC鍦板潃鏄犲皠
-- 缂撳瓨涓嶅悓椤圭洰鐨凷7Serializer瀹炰緥
-
-### 6. PlcAddressMappingController
-- 浣嶇疆锛歚com.mes.controller.PlcAddressController`
-- 鍔熻兘锛氭帶鍒跺櫒锛屾彁渚汻EST API鎺ュ彛
-
-## API鎺ュ彛
-
-### 1. 鑾峰彇鎵�鏈夐厤缃�
-```
-GET /api/plc/address-mapping/list
-```
-
-### 2. 鍒嗛〉鑾峰彇閰嶇疆
-```
-GET /api/plc/address-mapping/page?page=1&size=10&projectId=shuttle&plcIp=192.168.10.21
-```
-
-### 3. 鏍规嵁ID鑾峰彇閰嶇疆
-```
-GET /api/plc/address-mapping/{id}
-```
-
-### 4. 鏍规嵁椤圭洰ID鑾峰彇閰嶇疆
-```
-GET /api/plc/address-mapping/project/{projectId}
-```
-
-### 5. 鏍规嵁椤圭洰ID鑾峰彇椤圭洰閰嶇疆锛堝寘鍚湴鍧�鏄犲皠锛�
-```
-GET /api/plc/address-mapping/project/{projectId}/config
-```
-
-### 6. 鍒涘缓鏂伴厤缃�
-```
-POST /api/plc/address-mapping
-Content-Type: application/json
-
-{
-  "projectId": "new_project",
-  "projectName": "鏂伴」鐩�",
-  "dbArea": "DB1",
-  "beginIndex": 0,
-  "plcIp": "192.168.10.22",
-  "plcType": "S1200",
-  "description": "鏂伴」鐩厤缃�"
-}
-```
-
-### 7. 鏇存柊閰嶇疆
-```
-PUT /api/plc/address-mapping/{id}
-Content-Type: application/json
-
-{
-  "projectId": "updated_project",
-  "projectName": "鏇存柊椤圭洰",
-  "dbArea": "DB2",
-  "beginIndex": 10,
-  "plcIp": "192.168.10.23",
-  "plcType": "S1500",
-  "description": "鏇存柊椤圭洰閰嶇疆"
-}
-```
-
-### 8. 鍒犻櫎閰嶇疆
-```
-DELETE /api/plc/address-mapping/{id}
-```
-
-### 9. 鎵归噺鍒犻櫎閰嶇疆
-```
-DELETE /api/plc/address-mapping/batch
-Content-Type: application/json
-
-[1, 2, 3]
-```
-
-### 10. 娴嬭瘯PLC杩炴帴
-```
-POST /api/plc/address-mapping/{id}/test-connection
-```
-
-### 11. 閲嶆柊鍔犺浇閰嶇疆鏂囦欢
-```
-POST /api/plc/address-mapping/reload-config
-```
-
-## 閰嶇疆鏂囦欢绀轰緥
-
-鍦╜application-dev.yml`涓坊鍔犱互涓嬮厤缃細
-
-```yaml
-plc:
-  address:
-    mapping:
-      # 榛樿DB鍧楀湴鍧�鍜岃捣濮嬬储寮�
-      defaultDbArea: "DB1"
-      defaultBeginIndex: 0
-      
-      # 椤圭洰鐗瑰畾閰嶇疆
-      projects:
-        shuttle:
-          dbArea: "DB38"
-          beginIndex: 0
-          plcIp: "192.168.10.21"
-          plcType: "S1200"
-          addressMapping:
-            onlineState: "0"
-            plcRequest: "2"
-            plcReport: "4"
-            mesSend: "6"
-            mesConfirm: "8"
-            mesGlassCount: "10"
-            alarmInfo: "12"
-```
-
-## 浣跨敤绀轰緥
-
-### 1. 鍦ㄤ唬鐮佷腑浣跨敤鍔ㄦ�侀厤缃�
-
-```java
-@Service
-public class SomeService {
-    
-    @Autowired
-    private PlcTestWriteService plcTestWriteService;
-    
-    public void doSomething() {
-        // 璁剧疆褰撳墠椤圭洰
-        plcTestWriteService.setCurrentProjectId("shuttle");
-        
-        // 浣跨敤褰撳墠椤圭洰閰嶇疆鎵ц鎿嶄綔
-        plcTestWriteService.simulatePlcRequest();
-        
-        // 鎴栬�呯洿鎺ユ寚瀹氶」鐩�
-        plcTestWriteService.simulatePlcRequest("another_project");
-    }
-}
-```
-
-### 2. 閫氳繃API绠$悊閰嶇疆
-
-```bash
-# 鍒涘缓鏂伴」鐩厤缃�
-curl -X POST http://localhost:8080/api/plc/address-mapping \
-  -H "Content-Type: application/json" \
-  -d '{
-    "projectId": "new_project",
-    "projectName": "鏂伴」鐩�",
-    "dbArea": "DB1",
-    "beginIndex": 0,
-    "plcIp": "192.168.10.22",
-    "plcType": "S1200"
-  }'
-
-# 鑾峰彇椤圭洰閰嶇疆
-curl http://localhost:8080/api/plc/address-mapping/project/new_project/config
-
-# 娴嬭瘯杩炴帴
-curl -X POST http://localhost:8080/api/plc/address-mapping/1/test-connection
-```
-
-## 鏁版嵁搴撹〃缁撴瀯
-
-```sql
-CREATE TABLE `plc_address_mapping` (
-  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '涓婚敭ID',
-  `project_id` varchar(50) NOT NULL COMMENT '椤圭洰鏍囪瘑',
-  `project_name` varchar(100) DEFAULT NULL COMMENT '椤圭洰鍚嶇О',
-  `db_area` varchar(20) NOT NULL COMMENT 'DB鍧楀湴鍧�锛屽DB1',
-  `begin_index` int(11) NOT NULL DEFAULT '0' COMMENT '璧峰绱㈠紩',
-  `plc_ip` varchar(50) DEFAULT NULL COMMENT 'PLC IP鍦板潃',
-  `plc_type` varchar(20) DEFAULT 'S1200' COMMENT 'PLC绫诲瀷',
-  `address_mapping_json` text COMMENT '鍦板潃鏄犲皠JSON閰嶇疆',
-  `description` varchar(255) DEFAULT NULL COMMENT '鎻忚堪',
-  `enabled` tinyint(1) NOT NULL DEFAULT '1' COMMENT '鏄惁鍚敤锛�1-鍚敤锛�0-绂佺敤',
-  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
-  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '鏇存柊鏃堕棿',
-  `create_by` varchar(50) DEFAULT NULL COMMENT '鍒涘缓浜�',
-  `update_by` varchar(50) DEFAULT NULL COMMENT '鏇存柊浜�',
-  PRIMARY KEY (`id`),
-  UNIQUE KEY `uk_project_id` (`project_id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='PLC鍦板潃鏄犲皠閰嶇疆琛�';
-```
-
-## 娉ㄦ剰浜嬮」
-
-1. **閰嶇疆浼樺厛绾�**锛氭暟鎹簱涓殑閰嶇疆浼樺厛绾ч珮浜庨厤缃枃浠朵腑鐨勯厤缃�
-2. **缂撳瓨绠$悊**锛氬綋閰嶇疆鏇存柊鍚庯紝闇�瑕佹竻闄ゅ搴旂殑S7Serializer缂撳瓨
-3. **杩炴帴娴嬭瘯**锛氭祴璇曡繛鎺ュ姛鑳戒粎楠岃瘉鍙傛暟鏄惁姝g‘锛屼笉瀹為檯杩炴帴PLC
-4. **瀹夊叏鎬�**锛氬湪鐢熶骇鐜涓紝搴旀坊鍔犻�傚綋鐨勬潈闄愭帶鍒跺拰鍙傛暟楠岃瘉
-
-## 鎵╁睍寤鸿
-
-1. **娣诲姞閰嶇疆鐗堟湰鎺у埗**锛氭敮鎸侀厤缃殑鐗堟湰绠$悊鍜屽洖婊�
-2. **娣诲姞閰嶇疆鍙樻洿閫氱煡**锛氬綋閰嶇疆鍙樻洿鏃讹紝閫氱煡鐩稿叧鏈嶅姟
-3. **娣诲姞閰嶇疆瀵煎叆瀵煎嚭**锛氭敮鎸侀厤缃殑鎵归噺瀵煎叆瀵煎嚭
-4. **娣诲姞閰嶇疆妯℃澘**锛氭敮鎸佸垱寤洪厤缃ā鏉匡紝绠�鍖栨柊椤圭洰閰嶇疆
-5. **娣诲姞閰嶇疆楠岃瘉**锛氬寮洪厤缃弬鏁扮殑楠岃瘉瑙勫垯
\ No newline at end of file
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/config/MybatisMetaObjectHandler.java b/mes-processes/mes-plcSend/src/main/java/com/mes/config/MybatisMetaObjectHandler.java
new file mode 100644
index 0000000..1376009
--- /dev/null
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/config/MybatisMetaObjectHandler.java
@@ -0,0 +1,37 @@
+package com.mes.config;
+
+import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
+import org.apache.ibatis.reflection.MetaObject;
+import org.springframework.stereotype.Component;
+
+import java.time.LocalDateTime;
+
+/**
+ * 缁熶竴澶勭悊鍏叡瀛楁锛坈reated_time銆乽pdated_time銆乧reated_by銆乽pdated_by锛夌殑鑷姩濉厖
+ */
+@Component
+public class MybatisMetaObjectHandler implements MetaObjectHandler {
+
+    @Override
+    public void insertFill(MetaObject metaObject) {
+        LocalDateTime now = LocalDateTime.now();
+        String operator = resolveOperator();
+
+        strictInsertFill(metaObject, "createdTime", LocalDateTime.class, now);
+        strictInsertFill(metaObject, "updatedTime", LocalDateTime.class, now);
+        strictInsertFill(metaObject, "createdBy", String.class, operator);
+        strictInsertFill(metaObject, "updatedBy", String.class, operator);
+    }
+
+    @Override
+    public void updateFill(MetaObject metaObject) {
+        strictUpdateFill(metaObject, "updatedTime", LocalDateTime.class, LocalDateTime.now());
+        strictUpdateFill(metaObject, "updatedBy", String.class, resolveOperator());
+    }
+
+    private String resolveOperator() {
+        // TODO: 涔嬪悗鍙帴鍏ョ櫥褰曚笂涓嬫枃锛岃繖閲屼复鏃跺洖閫�涓� system
+        return "system";
+    }
+}
+
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/device/controller/DeviceConfigController.java b/mes-processes/mes-plcSend/src/main/java/com/mes/device/controller/DeviceConfigController.java
index 7a57917..c1f5da1 100644
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/device/controller/DeviceConfigController.java
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/device/controller/DeviceConfigController.java
@@ -1,15 +1,16 @@
 package com.mes.device.controller;
 
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.fasterxml.jackson.databind.ObjectMapper;
 import com.mes.device.entity.DeviceConfig;
 import com.mes.device.request.DeviceConfigRequest;
 import com.mes.device.service.DeviceConfigService;
 import com.mes.device.vo.DeviceConfigVO;
 import com.mes.device.vo.StatisticsVO;
 import com.mes.vo.Result;
-import io.swagger.v3.oas.annotations.Operation;
-import io.swagger.v3.oas.annotations.Parameter;
-import io.swagger.v3.oas.annotations.tags.Tag;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
@@ -27,17 +28,20 @@
 @Slf4j
 @RestController
 @RequestMapping("device/config")
-@Tag(name = "璁惧閰嶇疆绠$悊", description = "璁惧閰嶇疆绠$悊鐩稿叧鎺ュ彛")
+@Api(tags = "璁惧閰嶇疆绠$悊")
 public class DeviceConfigController {
 
     @Autowired
     private DeviceConfigService deviceConfigService;
 
+    @Autowired
+    private ObjectMapper objectMapper;
+
     /**
      * 鍒涘缓璁惧閰嶇疆
      */
     @PostMapping("/devices")
-    @Operation(summary = "鍒涘缓璁惧閰嶇疆", description = "鍒涘缓鏂扮殑璁惧閰嶇疆")
+    @ApiOperation("鍒涘缓璁惧閰嶇疆")
     public Result<DeviceConfig> createDevice(
             @Valid @RequestBody DeviceConfig deviceConfig) {
         try {
@@ -59,11 +63,23 @@
      * 鏇存柊璁惧閰嶇疆
      */
     @PostMapping("/devices/update")
-    @Operation(summary = "鏇存柊璁惧閰嶇疆", description = "鏇存柊鎸囧畾ID鐨勮澶囬厤缃�")
+    @ApiOperation("鏇存柊璁惧閰嶇疆")
     public Result<DeviceConfig> updateDevice(
             @Valid @RequestBody DeviceConfigRequest request) {
         try {
-            DeviceConfig deviceConfig = (DeviceConfig) request.getDeviceConfig();
+            DeviceConfig deviceConfig;
+            Object deviceConfigObj = request.getDeviceConfig();
+            
+            // 濡傛灉 deviceConfig 鏄� Map 绫诲瀷锛圝SON 鍙嶅簭鍒楀寲鍚庣殑 LinkedHashMap锛夛紝闇�瑕佽浆鎹负 DeviceConfig
+            if (deviceConfigObj instanceof Map) {
+                deviceConfig = objectMapper.convertValue(deviceConfigObj, DeviceConfig.class);
+            } else if (deviceConfigObj instanceof DeviceConfig) {
+                deviceConfig = (DeviceConfig) deviceConfigObj;
+            } else {
+                log.error("涓嶆敮鎸佺殑 deviceConfig 绫诲瀷: {}", deviceConfigObj != null ? deviceConfigObj.getClass() : "null");
+                return Result.error("璁惧閰嶇疆鏁版嵁鏍煎紡閿欒");
+            }
+            
             deviceConfig.setId(request.getDeviceId());
             boolean success = deviceConfigService.updateDevice(deviceConfig);
             if (success) {
@@ -75,7 +91,7 @@
             }
         } catch (Exception e) {
             log.error("鏇存柊璁惧閰嶇疆澶辫触", e);
-            return Result.error("鏇存柊璁惧閰嶇疆澶辫触");
+            return Result.error("鏇存柊璁惧閰嶇疆澶辫触: " + e.getMessage());
         }
     }
 
@@ -83,7 +99,7 @@
      * 鍒犻櫎璁惧閰嶇疆
      */
     @PostMapping("/devices/delete")
-    @Operation(summary = "鍒犻櫎璁惧閰嶇疆", description = "鍒犻櫎鎸囧畾ID鐨勮澶囬厤缃�")
+    @ApiOperation("鍒犻櫎璁惧閰嶇疆")
     public Result<Void> deleteDevice(
             @Valid @RequestBody DeviceConfigRequest request) {
         try {
@@ -99,7 +115,7 @@
      * 鏍规嵁ID鑾峰彇璁惧閰嶇疆
      */
     @PostMapping("/devices/detail")
-    @Operation(summary = "鑾峰彇璁惧閰嶇疆璇︽儏", description = "鏍规嵁ID鑾峰彇璁惧閰嶇疆鐨勮缁嗕俊鎭�")
+    @ApiOperation("鑾峰彇璁惧閰嶇疆璇︽儏")
     public Result<DeviceConfig> getDeviceById(
             @Valid @RequestBody DeviceConfigRequest request) {
         try {
@@ -115,7 +131,7 @@
      * 鍒嗛〉鏌ヨ璁惧閰嶇疆鍒楄〃
      */
     @PostMapping("/devices/list")
-    @Operation(summary = "鍒嗛〉鏌ヨ璁惧閰嶇疆", description = "鍒嗛〉鏌ヨ璁惧閰嶇疆鍒楄〃")
+    @ApiOperation("鍒嗛〉鏌ヨ璁惧閰嶇疆")
     public Result<Page<DeviceConfigVO.DeviceInfo>> getDeviceList(
             @Valid @RequestBody DeviceConfigRequest request) {
         try {
@@ -137,7 +153,7 @@
      * 鍚敤璁惧
      */
     @PostMapping("/devices/enable")
-    @Operation(summary = "鍚敤璁惧", description = "鍚敤鎸囧畾ID鐨勮澶�")
+    @ApiOperation("鍚敤璁惧")
     public Result<Void> enableDevice(
             @Valid @RequestBody DeviceConfigRequest request) {
         try {
@@ -153,7 +169,7 @@
      * 绂佺敤璁惧
      */
     @PostMapping("/devices/disable")
-    @Operation(summary = "绂佺敤璁惧", description = "绂佺敤鎸囧畾ID鐨勮澶�")
+    @ApiOperation("绂佺敤璁惧")
     public Result<Void> disableDevice(
             @Valid @RequestBody DeviceConfigRequest request) {
         try {
@@ -169,7 +185,7 @@
      * 鎵归噺鍚敤璁惧
      */
     @PostMapping("/devices/batch-enable")
-    @Operation(summary = "鎵归噺鍚敤璁惧", description = "鎵归噺鍚敤鎸囧畾ID鍒楄〃鐨勮澶�")
+    @ApiOperation("鎵归噺鍚敤璁惧")
     public Result<Void> batchEnableDevices(
             @Valid @RequestBody DeviceConfigRequest request) {
         try {
@@ -185,7 +201,7 @@
      * 鎵归噺绂佺敤璁惧
      */
     @PostMapping("/devices/batch-disable")
-    @Operation(summary = "鎵归噺绂佺敤璁惧", description = "鎵归噺绂佺敤鎸囧畾ID鍒楄〃鐨勮澶�")
+    @ApiOperation("鎵归噺绂佺敤璁惧")
     public Result<Void> batchDisableDevices(
             @Valid @RequestBody DeviceConfigRequest request) {
         try {
@@ -201,9 +217,9 @@
      * 鑾峰彇璁惧缁熻淇℃伅
      */
     @PostMapping("/statistics/devices")
-    @Operation(summary = "鑾峰彇璁惧缁熻淇℃伅", description = "鑾峰彇璁惧鐩稿叧鐨勭粺璁′俊鎭�")
+    @ApiOperation("鑾峰彇璁惧缁熻淇℃伅")
     public Result<StatisticsVO.DeviceStatistics> getDeviceStatistics(
-            @Parameter(description = "璁惧閰嶇疆璇锋眰") @RequestBody(required = false) DeviceConfigRequest request) {
+            @ApiParam("璁惧閰嶇疆璇锋眰") @RequestBody(required = false) DeviceConfigRequest request) {
         try {
             StatisticsVO.DeviceStatistics statistics = deviceConfigService.getDeviceStatistics(request != null ? request.getProjectId() : null);
             return Result.success(statistics);
@@ -217,9 +233,9 @@
      * 妫�鏌ヨ澶囩紪鐮佹槸鍚﹀凡瀛樺湪
      */
     @PostMapping("/devices/check-code")
-    @Operation(summary = "妫�鏌ヨ澶囩紪鐮�", description = "妫�鏌ヨ澶囩紪鐮佹槸鍚﹀凡瀛樺湪")
+    @ApiOperation("妫�鏌ヨ澶囩紪鐮�")
     public Result<Boolean> checkDeviceCodeExists(
-            @Parameter(description = "璁惧閰嶇疆璇锋眰") @RequestBody DeviceConfigRequest request) {
+            @ApiParam("璁惧閰嶇疆璇锋眰") @RequestBody DeviceConfigRequest request) {
         try {
             boolean exists = deviceConfigService.isDeviceCodeExists(request.getDeviceCode(), request.getDeviceId());
             return Result.success(exists);
@@ -233,7 +249,7 @@
      * 鑾峰彇璁惧绫诲瀷鍒楄〃
      */
     @PostMapping("/devices/types")
-    @Operation(summary = "鑾峰彇璁惧绫诲瀷鍒楄〃", description = "鑾峰彇鎵�鏈夊彲鐢ㄧ殑璁惧绫诲瀷")
+    @ApiOperation("鑾峰彇璁惧绫诲瀷鍒楄〃")
     public Result<List<String>> getDeviceTypes(@RequestBody(required = false) Map<String, Object> request) {
         try {
             List<String> deviceTypes = deviceConfigService.getAllDeviceTypes();
@@ -248,7 +264,7 @@
      * 鑾峰彇璁惧鐘舵�佸垪琛�
      */
     @PostMapping("/devices/statuses")
-    @Operation(summary = "鑾峰彇璁惧鐘舵�佸垪琛�", description = "鑾峰彇鎵�鏈夊彲鐢ㄧ殑璁惧鐘舵��")
+    @ApiOperation("鑾峰彇璁惧鐘舵�佸垪琛�")
     public Result<List<String>> getDeviceStatuses(@RequestBody(required = false) Map<String, Object> request) {
         try {
             List<String> deviceStatuses = deviceConfigService.getAllDeviceStatuses();
@@ -263,9 +279,9 @@
      * 鑾峰彇璁惧閰嶇疆鏍戠粨鏋�
      */
     @PostMapping("/devices/tree")
-    @Operation(summary = "鑾峰彇璁惧閰嶇疆鏍戠粨鏋�", description = "鑾峰彇璁惧鍜岃澶囩粍鐨勬爲褰㈢粨鏋勬暟鎹�")
+    @ApiOperation("鑾峰彇璁惧閰嶇疆鏍戠粨鏋�")
     public Result<List<DeviceConfigVO.DeviceTreeNode>> getDeviceTree(
-            @Parameter(description = "璁惧閰嶇疆璇锋眰") @RequestBody(required = false) DeviceConfigRequest request) {
+            @ApiParam("璁惧閰嶇疆璇锋眰") @RequestBody(required = false) DeviceConfigRequest request) {
         try {
             List<DeviceConfigVO.DeviceTreeNode> treeData = deviceConfigService.getDeviceTree(request != null ? request.getProjectId() : null);
             return Result.success(treeData);
@@ -279,7 +295,7 @@
      * 璁惧鍋ュ悍妫�鏌�
      */
     @PostMapping("/devices/health-check")
-    @Operation(summary = "璁惧鍋ュ悍妫�鏌�", description = "瀵规寚瀹氳澶囪繘琛屽仴搴锋鏌�")
+    @ApiOperation("璁惧鍋ュ悍妫�鏌�")
     public Result<DeviceConfigVO.HealthCheckResult> performHealthCheck(
             @Valid @RequestBody DeviceConfigRequest request) {
         try {
@@ -298,7 +314,7 @@
      * 2. 鐩存帴浼犲叆 plcIp / plcPort / timeout 杩涜涓�娆℃�ф祴璇�
      */
     @PostMapping("/devices/test-connection")
-    @Operation(summary = "娴嬭瘯璁惧PLC杩炴帴", description = "鏍规嵁璁惧閰嶇疆娴嬭瘯PLC杩炴帴鏄惁鍙揪")
+    @ApiOperation("娴嬭瘯璁惧PLC杩炴帴")
     public Result<String> testDeviceConnection(@RequestBody Map<String, Object> body) {
         try {
             String plcIp = null;
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/device/controller/DeviceControlProfileController.java b/mes-processes/mes-plcSend/src/main/java/com/mes/device/controller/DeviceControlProfileController.java
index 1ebc89b..6dcb2f7 100644
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/device/controller/DeviceControlProfileController.java
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/device/controller/DeviceControlProfileController.java
@@ -3,15 +3,15 @@
 import com.mes.device.service.DeviceControlProfileService;
 import com.mes.device.vo.DeviceControlProfile;
 import com.mes.vo.Result;
-import io.swagger.v3.oas.annotations.Operation;
-import io.swagger.v3.oas.annotations.tags.Tag;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
 import lombok.RequiredArgsConstructor;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
 @RestController
 @RequestMapping("device/control")
-@Tag(name = "璁惧鎺у埗鍙傛暟", description = "璁惧鎺у埗鍙傛暟閰嶇疆鎺ュ彛")
+@Api(tags = "璁惧鎺у埗鍙傛暟")
 @RequiredArgsConstructor
 @Validated
 public class DeviceControlProfileController {
@@ -19,13 +19,13 @@
     private final DeviceControlProfileService controlProfileService;
 
     @GetMapping("/{deviceId}")
-    @Operation(summary = "鑾峰彇璁惧鎺у埗鍙傛暟")
+    @ApiOperation("鑾峰彇璁惧鎺у埗鍙傛暟")
     public Result<DeviceControlProfile> getProfile(@PathVariable Long deviceId) {
         return Result.success(controlProfileService.getProfile(deviceId));
     }
 
     @PostMapping("/{deviceId}")
-    @Operation(summary = "鏇存柊璁惧鎺у埗鍙傛暟")
+    @ApiOperation("鏇存柊璁惧鎺у埗鍙傛暟")
     public Result<Void> saveProfile(@PathVariable Long deviceId,
                                     @RequestBody DeviceControlProfile profile) {
         controlProfileService.updateProfile(deviceId, profile);
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/device/controller/DeviceGroupController.java b/mes-processes/mes-plcSend/src/main/java/com/mes/device/controller/DeviceGroupController.java
index 88e657c..37ea229 100644
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/device/controller/DeviceGroupController.java
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/device/controller/DeviceGroupController.java
@@ -8,8 +8,8 @@
 import com.mes.device.vo.DeviceGroupVO;
 import com.mes.device.vo.StatisticsVO;
 import com.mes.vo.Result;
-import io.swagger.v3.oas.annotations.Operation;
-import io.swagger.v3.oas.annotations.tags.Tag;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
@@ -29,7 +29,7 @@
 @Slf4j
 @RestController
 @RequestMapping("device/group")
-@Tag(name = "璁惧缁勭鐞�", description = "璁惧缁勭鐞嗙浉鍏虫帴鍙�")
+@Api(tags = "璁惧缁勭鐞�")
 public class DeviceGroupController {
 
     @Resource
@@ -42,7 +42,7 @@
      * 鍒涘缓璁惧缁�
      */
     @PostMapping("/create")
-    @Operation(summary = "鍒涘缓璁惧缁�", description = "鍒涘缓璁惧缁勪俊鎭�")
+    @ApiOperation("鍒涘缓璁惧缁�")
     public Result<DeviceGroupConfig> createGroup(
             @Valid @RequestBody DeviceGroupRequest request) {
         try {
@@ -65,7 +65,7 @@
      * 鏇存柊璁惧缁勯厤缃�
      */
     @PostMapping("/update")
-    @Operation(summary = "鏇存柊璁惧缁勯厤缃�", description = "鏇存柊鎸囧畾ID鐨勮澶囩粍閰嶇疆")
+    @ApiOperation("鏇存柊璁惧缁勯厤缃�")
     public Result<DeviceGroupConfig> updateGroup(
             @Valid @RequestBody DeviceGroupRequest request) {
         try {
@@ -89,7 +89,7 @@
      * 鍒犻櫎璁惧缁勯厤缃�
      */
     @PostMapping("/delete")
-    @Operation(summary = "鍒犻櫎璁惧缁勯厤缃�", description = "鍒犻櫎鎸囧畾ID鐨勮澶囩粍閰嶇疆")
+    @ApiOperation("鍒犻櫎璁惧缁勯厤缃�")
     public Result<Void> deleteGroup(
             @Valid @RequestBody DeviceGroupRequest request) {
         try {
@@ -105,7 +105,7 @@
      * 鏍规嵁ID鑾峰彇璁惧缁勯厤缃�
      */
     @PostMapping("/detail")
-    @Operation(summary = "鑾峰彇璁惧缁勯厤缃鎯�", description = "鏍规嵁ID鑾峰彇璁惧缁勯厤缃殑璇︾粏淇℃伅")
+    @ApiOperation("鑾峰彇璁惧缁勯厤缃鎯�")
     public Result<DeviceGroupConfig> getGroupById(
             @Valid @RequestBody DeviceGroupRequest request) {
         try {
@@ -121,7 +121,7 @@
      * 鍒嗛〉鏌ヨ璁惧缁勫垪琛�
      */
     @PostMapping("/list")
-    @Operation(summary = "鍒嗛〉鏌ヨ璁惧缁勫垪琛�", description = "鍒嗛〉鏌ヨ璁惧缁勫垪琛�")
+    @ApiOperation("鍒嗛〉鏌ヨ璁惧缁勫垪琛�")
     public Result<Page<DeviceGroupVO.GroupInfo>> getGroupList(
             @Valid @RequestBody DeviceGroupRequest request) {
         try {
@@ -146,7 +146,7 @@
      * 鍚敤璁惧缁�
      */
     @PostMapping("/enable")
-    @Operation(summary = "鍚敤璁惧缁�", description = "鍚敤鎸囧畾璁惧缁�")
+    @ApiOperation("鍚敤璁惧缁�")
     public Result<Void> enableGroup(
             @Valid @RequestBody DeviceGroupRequest request) {
         try {
@@ -162,7 +162,7 @@
      * 绂佺敤璁惧缁�
      */
     @PostMapping("/disable")
-    @Operation(summary = "绂佺敤璁惧缁�", description = "绂佺敤鎸囧畾璁惧缁�")
+    @ApiOperation("绂佺敤璁惧缁�")
     public Result<Void> disableGroup(
             @Valid @RequestBody DeviceGroupRequest request) {
         try {
@@ -178,7 +178,7 @@
      * 鎵归噺鍚敤璁惧缁�
      */
     @PostMapping("/batch-enable")
-    @Operation(summary = "鎵归噺鍚敤璁惧缁�", description = "鎵归噺鍚敤鎸囧畾ID鍒楄〃鐨勮澶囩粍")
+    @ApiOperation("鎵归噺鍚敤璁惧缁�")
     public Result<Void> batchEnableGroups(
             @Valid @RequestBody DeviceGroupRequest request) {
         try {
@@ -194,7 +194,7 @@
      * 鎵归噺绂佺敤璁惧缁�
      */
     @PostMapping("/batch-disable")
-    @Operation(summary = "鎵归噺绂佺敤璁惧缁�", description = "鎵归噺绂佺敤鎸囧畾ID鍒楄〃鐨勮澶囩粍")
+    @ApiOperation("鎵归噺绂佺敤璁惧缁�")
     public Result<Void> batchDisableGroups(
             @Valid @RequestBody DeviceGroupRequest request) {
         try {
@@ -210,7 +210,7 @@
      * 鑾峰彇璁惧缁勭粺璁′俊鎭�
      */
     @PostMapping("/statistics/groups")
-    @Operation(summary = "鑾峰彇璁惧缁勭粺璁′俊鎭�", description = "鑾峰彇璁惧缁勭浉鍏崇殑缁熻淇℃伅")
+    @ApiOperation("鑾峰彇璁惧缁勭粺璁′俊鎭�")
     public Result<StatisticsVO.GroupStatistics> getGroupStatistics(
             @RequestBody(required = false) Map<String, Object> request) {
         try {
@@ -227,7 +227,7 @@
      * 妫�鏌ヨ澶囩粍缂栫爜鏄惁宸插瓨鍦�
      */
     @PostMapping("/check-code")
-    @Operation(summary = "妫�鏌ヨ澶囩粍缂栫爜", description = "妫�鏌ヨ澶囩粍缂栫爜鏄惁宸插瓨鍦�")
+    @ApiOperation("妫�鏌ヨ澶囩粍缂栫爜")
     public Result<Boolean> checkGroupCodeExists(
             @Valid @RequestBody DeviceGroupRequest request) {
         try {
@@ -247,7 +247,7 @@
      * 鑾峰彇璁惧缁勭被鍨嬪垪琛�
      */
     @PostMapping("/types")
-    @Operation(summary = "鑾峰彇璁惧缁勭被鍨嬪垪琛�", description = "鑾峰彇鎵�鏈夊彲鐢ㄧ殑璁惧缁勭被鍨�")
+    @ApiOperation("鑾峰彇璁惧缁勭被鍨嬪垪琛�")
     public Result<List<String>> getGroupTypes() {
         try {
             List<String> groupTypes = deviceGroupConfigService.getAllGroupTypes();
@@ -262,7 +262,7 @@
      * 鑾峰彇璁惧缁勭姸鎬佸垪琛�
      */
     @PostMapping("/statuses")
-    @Operation(summary = "鑾峰彇璁惧缁勭姸鎬佸垪琛�", description = "鑾峰彇鎵�鏈夊彲鐢ㄧ殑璁惧缁勭姸鎬�")
+    @ApiOperation("鑾峰彇璁惧缁勭姸鎬佸垪琛�")
     public Result<List<String>> getGroupStatuses() {
         try {
             List<String> groupStatuses = deviceGroupConfigService.getAllGroupStatuses();
@@ -277,7 +277,7 @@
      * 娣诲姞璁惧鍒拌澶囩粍
      */
     @PostMapping("/devices")
-    @Operation(summary = "娣诲姞璁惧鍒拌澶囩粍", description = "灏嗘寚瀹氳澶囨坊鍔犲埌璁惧缁勪腑")
+    @ApiOperation("娣诲姞璁惧鍒拌澶囩粍")
     public Result<Void> addDeviceToGroup(
             @Valid @RequestBody DeviceGroupRequest request) {
         try {
@@ -294,7 +294,7 @@
      * 浠庤澶囩粍绉婚櫎璁惧
      */
     @PostMapping("/devices/remove")
-    @Operation(summary = "浠庤澶囩粍绉婚櫎璁惧", description = "浠庤澶囩粍涓Щ闄ゆ寚瀹氳澶�")
+    @ApiOperation("浠庤澶囩粍绉婚櫎璁惧")
     public Result<Void> removeDeviceFromGroup(
             @Valid @RequestBody DeviceGroupRequest request) {
         try {
@@ -310,7 +310,7 @@
      * 鏇存柊璁惧瑙掕壊
      */
     @PostMapping("/devices/role")
-    @Operation(summary = "鏇存柊璁惧瑙掕壊", description = "鏇存柊璁惧鍦ㄨ澶囩粍涓殑瑙掕壊")
+    @ApiOperation("鏇存柊璁惧瑙掕壊")
     public Result<Void> updateDeviceRole(
             @Valid @RequestBody DeviceGroupRequest request) {
         try {
@@ -327,7 +327,7 @@
      * 鑾峰彇璁惧缁勮澶囧垪琛�
      */
     @PostMapping("/devices/list")
-    @Operation(summary = "鑾峰彇璁惧缁勮澶囧垪琛�", description = "鑾峰彇鎸囧畾璁惧缁勪笅鐨勬墍鏈夎澶�")
+    @ApiOperation("鑾峰彇璁惧缁勮澶囧垪琛�")
     public Result<List<DeviceGroupVO.DeviceInfo>> getGroupDevices(
             @Valid @RequestBody DeviceGroupRequest request) {
         try {
@@ -343,7 +343,7 @@
      * 鑾峰彇璁惧璁惧缁勫垪琛�
      */
     @PostMapping("/devices/groups")
-    @Operation(summary = "鑾峰彇璁惧璁惧缁勫垪琛�", description = "鑾峰彇鎸囧畾璁惧鎵�灞炵殑鎵�鏈夎澶囩粍")
+    @ApiOperation("鑾峰彇璁惧璁惧缁勫垪琛�")
     public Result<List<DeviceGroupVO.GroupInfo>> getDeviceGroups(
             @Valid @RequestBody DeviceGroupRequest request) {
         try {
@@ -359,7 +359,7 @@
      * 鎵归噺娣诲姞璁惧鍒拌澶囩粍
      */
     @PostMapping("/batch-add-devices")
-    @Operation(summary = "鎵归噺娣诲姞璁惧鍒拌澶囩粍", description = "鎵归噺灏嗘寚瀹氳澶囧垪琛ㄦ坊鍔犲埌璁惧缁勪腑")
+    @ApiOperation("鎵归噺娣诲姞璁惧鍒拌澶囩粍")
     public Result<Void> batchAddDevicesToGroup(
             @Valid @RequestBody DeviceGroupRequest request) {
         try {
@@ -375,7 +375,7 @@
      * 鎵归噺浠庤澶囩粍绉婚櫎璁惧
      */
     @PostMapping("/devices/batch-remove")
-    @Operation(summary = "鎵归噺浠庤澶囩粍绉婚櫎璁惧", description = "鎵归噺浠庤澶囩粍涓Щ闄ゆ寚瀹氳澶囧垪琛�")
+    @ApiOperation("鎵归噺浠庤澶囩粍绉婚櫎璁惧")
     public Result<Void> batchRemoveDevicesFromGroup(
             @Valid @RequestBody DeviceGroupRequest request) {
         try {
@@ -391,7 +391,7 @@
      * 璁惧缁勫仴搴锋鏌�
      */
     @PostMapping("/health-check")
-    @Operation(summary = "璁惧缁勫仴搴锋鏌�", description = "瀵规寚瀹氳澶囩粍杩涜鍋ュ悍妫�鏌�")
+    @ApiOperation("璁惧缁勫仴搴锋鏌�")
     public Result<DeviceGroupVO.HealthCheckResult> performGroupHealthCheck(
             @Valid @RequestBody DeviceGroupRequest request) {
         try {
@@ -407,7 +407,7 @@
      * 鑾峰彇璁惧缁勬�ц兘缁熻
      */
     @PostMapping("/performance")
-    @Operation(summary = "鑾峰彇璁惧缁勬�ц兘缁熻", description = "鑾峰彇鎸囧畾璁惧缁勭殑鎬ц兘缁熻淇℃伅")
+    @ApiOperation("鑾峰彇璁惧缁勬�ц兘缁熻")
     public Result<DeviceGroupVO.PerformanceStats> getGroupPerformance(
             @Valid @RequestBody DeviceGroupRequest request) {
         try {
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/device/controller/DeviceInteractionController.java b/mes-processes/mes-plcSend/src/main/java/com/mes/device/controller/DeviceInteractionController.java
index b5655ae..f38af62 100644
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/device/controller/DeviceInteractionController.java
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/device/controller/DeviceInteractionController.java
@@ -4,20 +4,22 @@
 import com.mes.device.service.DeviceInteractionService;
 import com.mes.device.vo.DevicePlcVO;
 import com.mes.vo.Result;
-import io.swagger.v3.oas.annotations.Operation;
-import io.swagger.v3.oas.annotations.tags.Tag;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import lombok.Data;
 import lombok.RequiredArgsConstructor;
 import org.springframework.validation.annotation.Validated;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
 
 import javax.validation.Valid;
+import javax.validation.constraints.NotNull;
+import java.util.List;
+import java.util.Map;
 
 @RestController
 @RequestMapping("device/interaction")
-@Tag(name = "璁惧浜や簰", description = "璁惧浜や簰閫昏緫鎵ц鎺ュ彛")
+@Api(tags = "璁惧浜や簰")
 @Validated
 @RequiredArgsConstructor
 public class DeviceInteractionController {
@@ -25,9 +27,37 @@
     private final DeviceInteractionService deviceInteractionService;
 
     @PostMapping("/glass-feed")
-    @Operation(summary = "鐜荤拑涓婃枡鍐欏叆")
+    @ApiOperation("鐜荤拑涓婃枡鍐欏叆")
     public Result<DevicePlcVO.OperationResult> feedGlass(@Valid @RequestBody DeviceGlassFeedRequest request) {
         return Result.success(deviceInteractionService.feedGlass(request));
     }
+
+    @PostMapping("/execute")
+    @ApiOperation("鎵ц璁惧閫昏緫鎿嶄綔")
+    public Result<DevicePlcVO.OperationResult> executeOperation(
+            @Valid @RequestBody DeviceOperationRequest request) {
+        return Result.success(deviceInteractionService.executeOperation(
+                request.getDeviceId(),
+                request.getOperation(),
+                request.getParams()
+        ));
+    }
+
+    /**
+     * 璁惧鎿嶄綔璇锋眰
+     */
+    @Data
+    public static class DeviceOperationRequest {
+        @NotNull(message = "璁惧ID涓嶈兘涓虹┖")
+        @ApiParam(value = "璁惧ID", required = true)
+        private Long deviceId;
+
+        @NotNull(message = "鎿嶄綔绫诲瀷涓嶈兘涓虹┖")
+        @ApiParam(value = "鎿嶄綔绫诲瀷锛堝锛歠eedGlass, triggerRequest, triggerReport绛夛級", required = true)
+        private String operation;
+
+        @ApiParam(value = "鎿嶄綔鍙傛暟")
+        private Map<String, Object> params;
+    }
 }
 
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/device/controller/DevicePlcController.java b/mes-processes/mes-plcSend/src/main/java/com/mes/device/controller/DevicePlcController.java
index d048db9..27aae25 100644
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/device/controller/DevicePlcController.java
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/device/controller/DevicePlcController.java
@@ -4,8 +4,8 @@
 import com.mes.device.service.DevicePlcOperationService;
 import com.mes.device.vo.DevicePlcVO;
 import com.mes.vo.Result;
-import io.swagger.v3.oas.annotations.Operation;
-import io.swagger.v3.oas.annotations.tags.Tag;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.validation.annotation.Validated;
@@ -25,54 +25,54 @@
 @RestController
 @RequiredArgsConstructor
 @RequestMapping("device/plc")
-@Tag(name = "璁惧PLC鎿嶄綔", description = "澶氳澶嘝LC鍐欏叆涓庣姸鎬佹煡璇㈡帴鍙�")
+@Api(tags = "璁惧PLC鎿嶄綔")
 public class DevicePlcController {
 
     private final DevicePlcOperationService devicePlcOperationService;
 
     @PostMapping("/requests")
-    @Operation(summary = "鎵归噺瑙﹀彂PLC璇锋眰", description = "瀵规寚瀹氳澶囧彂閫丳LC璇锋眰瀛�")
+    @ApiOperation("鎵归噺瑙﹀彂PLC璇锋眰")
     public Result<List<DevicePlcVO.OperationResult>> triggerRequests(
             @Valid @RequestBody DevicePlcBatchRequest request) {
         return Result.success(devicePlcOperationService.triggerRequest(request.getDeviceIds()));
     }
 
     @PostMapping("/reports")
-    @Operation(summary = "鎵归噺妯℃嫙PLC姹囨姤", description = "瀵规寚瀹氳澶囨ā鎷烶LC浠诲姟瀹屾垚姹囨姤")
+    @ApiOperation("鎵归噺妯℃嫙PLC姹囨姤")
     public Result<List<DevicePlcVO.OperationResult>> triggerReports(
             @Valid @RequestBody DevicePlcBatchRequest request) {
         return Result.success(devicePlcOperationService.triggerReport(request.getDeviceIds()));
     }
 
     @PostMapping("/resets")
-    @Operation(summary = "鎵归噺閲嶇疆PLC鐘舵��", description = "閲嶇疆鎸囧畾璁惧鍏宠仈PLC鐨勫叧閿瓧娈�")
+    @ApiOperation("鎵归噺閲嶇疆PLC鐘舵��")
     public Result<List<DevicePlcVO.OperationResult>> resetPlc(
             @Valid @RequestBody DevicePlcBatchRequest request) {
         return Result.success(devicePlcOperationService.resetDevices(request.getDeviceIds()));
     }
 
     @PostMapping("/groups/{groupId}/request")
-    @Operation(summary = "璁惧缁勮Е鍙慞LC璇锋眰", description = "瀵硅澶囩粍鍐呮墍鏈夎澶囧彂閫丳LC璇锋眰瀛�")
+    @ApiOperation("璁惧缁勮Е鍙慞LC璇锋眰")
     public Result<List<DevicePlcVO.OperationResult>> triggerGroupRequest(
             @PathVariable Long groupId) {
         return Result.success(devicePlcOperationService.triggerRequestByGroup(groupId));
     }
 
     @PostMapping("/groups/{groupId}/report")
-    @Operation(summary = "璁惧缁勬ā鎷烶LC姹囨姤", description = "瀵硅澶囩粍鍐呮墍鏈夎澶囨ā鎷熶换鍔″畬鎴愭眹鎶�")
+    @ApiOperation("璁惧缁勬ā鎷烶LC姹囨姤")
     public Result<List<DevicePlcVO.OperationResult>> triggerGroupReport(
             @PathVariable Long groupId) {
         return Result.success(devicePlcOperationService.triggerReportByGroup(groupId));
     }
 
     @GetMapping("/status/{deviceId}")
-    @Operation(summary = "鏌ヨ璁惧PLC鐘舵��", description = "璇诲彇鍗曞彴璁惧鐨凱LC鏁版嵁")
+    @ApiOperation("鏌ヨ璁惧PLC鐘舵��")
     public Result<DevicePlcVO.StatusInfo> readStatus(@PathVariable Long deviceId) {
         return Result.success(devicePlcOperationService.readStatus(deviceId));
     }
 
     @GetMapping("/groups/{groupId}/status")
-    @Operation(summary = "鏌ヨ璁惧缁凱LC鐘舵��", description = "璇诲彇璁惧缁勫唴鎵�鏈夎澶囩殑PLC鏁版嵁")
+    @ApiOperation("鏌ヨ璁惧缁凱LC鐘舵��")
     public Result<List<DevicePlcVO.StatusInfo>> readGroupStatus(@PathVariable Long groupId) {
         return Result.success(devicePlcOperationService.readStatusByGroup(groupId));
     }
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/device/entity/DEVICE_CONFIG_FIELDS.md b/mes-processes/mes-plcSend/src/main/java/com/mes/device/entity/DEVICE_CONFIG_FIELDS.md
new file mode 100644
index 0000000..70df7c5
--- /dev/null
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/device/entity/DEVICE_CONFIG_FIELDS.md
@@ -0,0 +1,152 @@
+# DeviceConfig 琛ㄥ瓧娈佃鏄�
+
+## 鏁版嵁搴撹〃锛歞evice_config
+
+### 瀛楁缁撴瀯璇存槑
+
+#### 1. `config_json` 鍒�
+**鐢ㄩ��**锛氬瓨鍌�**閫氱敤閰嶇疆鍙傛暟**锛堝墠绔�"閰嶇疆鍙傛暟"鍗$墖涓殑閿�煎锛�
+
+**鏁版嵁缁撴瀯**锛�
+```json
+[
+  {
+    "paramKey": "鍙傛暟鍚�1",
+    "paramValue": "鍙傛暟鍊�1",
+    "description": "鎻忚堪1"
+  },
+  {
+    "paramKey": "鍙傛暟鍚�2",
+    "paramValue": "鍙傛暟鍊�2",
+    "description": "鎻忚堪2"
+  }
+]
+```
+
+**鍓嶇浣嶇疆**锛氳澶囩紪杈戝脊绐� 鈫� "閰嶇疆鍙傛暟"鍗$墖
+
+**鐗圭偣**锛�
+- 閫氱敤鐨勯敭鍊煎閰嶇疆
+- 鍙互娣诲姞浠绘剰鍙傛暟
+- 鐢ㄤ簬瀛樺偍璁惧鐗瑰畾鐨勮嚜瀹氫箟鍙傛暟
+
+---
+
+#### 2. `extra_params` 鍒�
+**鐢ㄩ��**锛氬瓨鍌�**鎵╁睍鍙傛暟**锛堢粨鏋勫寲鐨凧SON瀵硅薄锛�
+
+**鏁版嵁缁撴瀯**锛�
+```json
+{
+  "connectionConfig": {
+    "moduleCode": "妯″潡缂栧彿",
+    "protocolType": "閫氳鍗忚",
+    "timeout": 30,
+    "retryCount": 3,
+    "heartbeatInterval": 30
+  },
+  "plcConfig": {
+    "dbArea": "DB1",
+    "beginIndex": 0,
+    "autoModeInterval": 5000,
+    "plcType": "S7-1200"
+  },
+  "plcProjectId": "椤圭洰ID",
+  "deviceLogic": {
+    // 鏍规嵁璁惧绫诲瀷涓嶅悓鑰屼笉鍚�
+    // 涓婂ぇ杞﹁澶囷細
+    "vehicleCapacity": 6000,
+    "glassIntervalMs": 1000,
+    "autoFeed": true,
+    "maxRetryCount": 5,
+    "positionMapping": {
+      "POS1": 1,
+      "POS2": 2
+    }
+    // 澶х悊鐗囪澶囷細
+    // "glassSize": 2000,
+    // "processingTime": 5000,
+    // "autoProcess": true
+    // 鐜荤拑瀛樺偍璁惧锛�
+    // "storageCapacity": 100,
+    // "retrievalMode": "FIFO",
+    // "autoStore": true,
+    // "autoRetrieve": true
+  }
+}
+```
+
+**鍓嶇浣嶇疆**锛氳澶囩紪杈戝脊绐椾腑鐨勫涓厤缃尯鍩�
+
+---
+
+### extra_params 璇︾粏璇存槑
+
+#### 2.1 `connectionConfig` - 杩炴帴閰嶇疆
+**鍓嶇浣嶇疆**锛�"杩炴帴閰嶇疆"鍗$墖
+
+| 瀛楁 | 璇存槑 | 鍓嶇鏄剧ず浣嶇疆 |
+|------|------|------------|
+| `moduleCode` | 妯″潡缂栧彿 | 杩炴帴閰嶇疆 鈫� 妯″潡缂栧彿 |
+| `protocolType` | 閫氳鍗忚 | 杩炴帴閰嶇疆 鈫� 閫氳鍗忚 |
+| `timeout` | 瓒呮椂鏃堕棿(绉�) | 杩炴帴閰嶇疆 鈫� 瓒呮椂鏃堕棿 |
+| `retryCount` | 閲嶈瘯娆℃暟 | 杩炴帴閰嶇疆 鈫� 閲嶈瘯娆℃暟 |
+| `heartbeatInterval` | 蹇冭烦闂撮殧(绉�) | 杩炴帴閰嶇疆 鈫� 蹇冭烦闂撮殧 |
+
+#### 2.2 `plcConfig` - PLC鍦板潃閰嶇疆
+**鍓嶇浣嶇疆**锛�"PLC 鍦板潃閰嶇疆"鍗$墖
+
+| 瀛楁 | 璇存槑 | 鍓嶇鏄剧ず浣嶇疆 |
+|------|------|------------|
+| `dbArea` | DB鍧� | PLC鍦板潃閰嶇疆 鈫� DB鍧� |
+| `beginIndex` | 璧峰绱㈠紩 | PLC鍦板潃閰嶇疆 鈫� 璧峰绱㈠紩 |
+| `autoModeInterval` | 鑷姩闂撮殧(ms) | PLC鍦板潃閰嶇疆 鈫� 鑷姩闂撮殧 |
+| `plcType` | PLC绫诲瀷 | 鍩烘湰淇℃伅 鈫� PLC绫诲瀷锛堜絾淇濆瓨鍦ㄨ繖閲岋級 |
+
+#### 2.3 `plcProjectId` - PLC椤圭洰ID
+**璇存槑**锛氱敤浜庢爣璇哖LC椤圭洰锛屽湪PLC鎿嶄綔鏃朵娇鐢�
+
+#### 2.4 `deviceLogic` - 璁惧閫昏緫鍙傛暟
+**鍓嶇浣嶇疆**锛�"璁惧閫昏緫閰嶇疆"鍗$墖锛堟牴鎹澶囩被鍨嬪姩鎬佹樉绀猴級
+
+**涓婂ぇ杞﹁澶�**锛�
+- `vehicleCapacity`: 杞﹁締瀹归噺
+- `glassIntervalMs`: 鐜荤拑闂撮殧(ms)
+- `autoFeed`: 鑷姩涓婃枡
+- `maxRetryCount`: 鏈�澶ч噸璇曟鏁�
+- `positionMapping`: 浣嶇疆鏄犲皠瀵硅薄
+
+**澶х悊鐗囪澶�**锛�
+- `glassSize`: 鐜荤拑灏哄
+- `processingTime`: 澶勭悊鏃堕棿(ms)
+- `autoProcess`: 鑷姩澶勭悊
+- `maxRetryCount`: 鏈�澶ч噸璇曟鏁�
+
+**鐜荤拑瀛樺偍璁惧**锛�
+- `storageCapacity`: 瀛樺偍瀹归噺
+- `retrievalMode`: 鍙栬揣妯″紡 (FIFO/LIFO/RANDOM)
+- `autoStore`: 鑷姩瀛樺偍
+- `autoRetrieve`: 鑷姩鍙栬揣
+- `maxRetryCount`: 鏈�澶ч噸璇曟鏁�
+
+---
+
+## 瀛楁瀵规瘮鎬荤粨
+
+| 瀛楁 | 瀛樺偍鍐呭 | 鍓嶇浣嶇疆 | 鐢ㄩ�� |
+|------|---------|---------|------|
+| `config_json` | 閫氱敤閿�煎鍙傛暟 | "閰嶇疆鍙傛暟"鍗$墖 | 鑷畾涔夊弬鏁帮紝鐏垫椿閰嶇疆 |
+| `extra_params.connectionConfig` | 杩炴帴鐩稿叧閰嶇疆 | "杩炴帴閰嶇疆"鍗$墖 | 閫氳杩炴帴鍙傛暟 |
+| `extra_params.plcConfig` | PLC鍦板潃閰嶇疆 | "PLC鍦板潃閰嶇疆"鍗$墖 | PLC璇诲啓鍦板潃鍙傛暟 |
+| `extra_params.deviceLogic` | 璁惧閫昏緫鍙傛暟 | "璁惧閫昏緫閰嶇疆"鍗$墖 | 璁惧涓氬姟閫昏緫鍙傛暟 |
+| `extra_params.plcProjectId` | 椤圭洰ID | 闅愯棌瀛楁 | 鍐呴儴浣跨敤 |
+
+---
+
+## 浣跨敤寤鸿
+
+1. **config_json**锛氱敤浜庡瓨鍌ㄤ笉甯哥敤鐨勩�佽嚜瀹氫箟鐨勯厤缃弬鏁�
+2. **extra_params.connectionConfig**锛氱敤浜庡瓨鍌ㄨ繛鎺ョ浉鍏崇殑鏍囧噯閰嶇疆
+3. **extra_params.plcConfig**锛氱敤浜庡瓨鍌≒LC鍦板潃鐩稿叧鐨勯厤缃�
+4. **extra_params.deviceLogic**锛氱敤浜庡瓨鍌ㄨ澶囦笟鍔¢�昏緫鐩稿叧鐨勯厤缃紙鏍规嵁璁惧绫诲瀷涓嶅悓锛�
+
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 3a8900e..86584a9 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
@@ -2,7 +2,8 @@
 
 import com.baomidou.mybatisplus.annotation.*;
 import com.fasterxml.jackson.annotation.JsonFormat;
-import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 
@@ -15,93 +16,93 @@
 @Data
 @EqualsAndHashCode(callSuper = false)
 @TableName("device_config")
-@Schema(name = "DeviceConfig", description = "璁惧閰嶇疆淇℃伅")
+@ApiModel(value = "璁惧閰嶇疆淇℃伅")
 public class DeviceConfig {
 
-    @Schema(description = "璁惧ID", example = "1")
+    @ApiModelProperty(value = "璁惧ID", example = "1")
     @TableId(value = "id", type = IdType.AUTO)
     private Long id;
 
-    @Schema(description = "璁惧鍞竴鏍囪瘑", example = "DEVICE_001")
+    @ApiModelProperty(value = "璁惧鍞竴鏍囪瘑", example = "DEVICE_001")
     @TableField("device_id")
     private String deviceId;
 
-    @Schema(description = "璁惧鍚嶇О", example = "涓婂ぇ杞﹁澶�1")
+    @ApiModelProperty(value = "璁惧鍚嶇О", example = "涓婂ぇ杞﹁澶�1")
     @TableField("device_name")
     private String deviceName;
 
-    @Schema(description = "璁惧缂栧彿", example = "DEV_001")
+    @ApiModelProperty(value = "璁惧缂栧彿", example = "DEV_001")
     @TableField("device_code")
     private String deviceCode;
 
-    @Schema(description = "璁惧绫诲瀷", example = "涓婂ぇ杞�/澶х悊鐗�/鐜荤拑瀛樺偍")
+    @ApiModelProperty(value = "璁惧绫诲瀷", example = "涓婂ぇ杞�/澶х悊鐗�/鐜荤拑瀛樺偍")
     @TableField("device_type")
     private String deviceType;
 
-    @Schema(description = "鎵�灞為」鐩甀D", example = "1")
+    @ApiModelProperty(value = "鎵�灞為」鐩甀D", example = "1")
     @TableField("project_id")
     private Long projectId;
 
-    @Schema(description = "PLC IP鍦板潃", example = "192.168.1.100")
+    @ApiModelProperty(value = "PLC IP鍦板潃", example = "192.168.1.100")
     @TableField("plc_ip")
     private String plcIp;
 
-    @Schema(description = "PLC绔彛", example = "102")
+    @ApiModelProperty(value = "PLC绔彛", example = "102")
     @TableField("plc_port")
     private Integer plcPort;
 
-    @Schema(description = "璁惧鐘舵��", example = "鍦ㄧ嚎/绂荤嚎/缁存姢涓�/鏁呴殰")
+    @ApiModelProperty(value = "璁惧鐘舵��", example = "鍦ㄧ嚎/绂荤嚎/缁存姢涓�/鏁呴殰")
     @TableField("status")
     private String status;
 
-    @Schema(description = "PLC绫诲瀷", example = "S7-1200/S7-1500")
+    @ApiModelProperty(value = "PLC绫诲瀷", example = "S7-1200/S7-1500")
     @TableField("plc_type")
     private String plcType;
 
-    @Schema(description = "妯″潡鍚嶇О", example = "涓婂ぇ杞︽ā鍧�")
+    @ApiModelProperty(value = "妯″潡鍚嶇О", example = "涓婂ぇ杞︽ā鍧�")
     @TableField("module_name")
     private String moduleName;
 
-    @Schema(description = "鏄惁涓绘帶璁惧", example = "true")
+    @ApiModelProperty(value = "鏄惁涓绘帶璁惧", example = "true")
     @TableField("is_primary")
     private Boolean isPrimary;
 
-    @Schema(description = "鏄惁鍚敤", example = "true")
+    @ApiModelProperty(value = "鏄惁鍚敤", example = "true")
     @TableField("enabled")
     private Boolean enabled;
 
-    @Schema(description = "璁惧鐗瑰畾閰嶇疆JSON", example = "{\"vehicleCapacity\": 6000, \"glassIntervalMs\": 1000}")
+    @ApiModelProperty(value = "璁惧鐗瑰畾閰嶇疆JSON", example = "{\"vehicleCapacity\": 6000, \"glassIntervalMs\": 1000}")
     @TableField("config_json")
     private String configJson;
 
-    @Schema(description = "璁惧鎻忚堪", example = "涓婂ぇ杞﹁澶�1")
+    @ApiModelProperty(value = "璁惧鎻忚堪", example = "涓婂ぇ杞﹁澶�1")
     @TableField("description")
     private String description;
 
-    @Schema(description = "鎵╁睍鍙傛暟JSON", example = "{\"timeout\": 5000, \"retries\": 3}")
+    @ApiModelProperty(value = "鎵╁睍鍙傛暟JSON", example = "{\"timeout\": 5000, \"retries\": 3}")
     @TableField("extra_params")
     private String extraParams;
 
-    @Schema(description = "鏄惁鍒犻櫎锛�0-鍚︼紝1-鏄�", example = "0")
+    @ApiModelProperty(value = "鏄惁鍒犻櫎锛�0-鍚︼紝1-鏄�", example = "0")
     @TableField("is_deleted")
     @TableLogic
     private Integer isDeleted;
 
-    @Schema(description = "鍒涘缓鏃堕棿")
+    @ApiModelProperty(value = "鍒涘缓鏃堕棿")
     @TableField(value = "created_time", fill = FieldFill.INSERT)
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
     private Date createdTime;
 
-    @Schema(description = "鏇存柊鏃堕棿")
+    @ApiModelProperty(value = "鏇存柊鏃堕棿")
     @TableField(value = "updated_time", fill = FieldFill.INSERT_UPDATE)
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
     private Date updatedTime;
 
-    @Schema(description = "鍒涘缓浜�", example = "system")
+    @ApiModelProperty(value = "鍒涘缓浜�", example = "system")
     @TableField(value = "created_by", fill = FieldFill.INSERT)
     private String createdBy;
 
-    @Schema(description = "鏇存柊浜�", example = "system")
+    @ApiModelProperty(value = "鏇存柊浜�", example = "system")
     @TableField(value = "updated_by", fill = FieldFill.INSERT_UPDATE)
     private String updatedBy;
 
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/device/entity/DeviceGroupConfig.java b/mes-processes/mes-plcSend/src/main/java/com/mes/device/entity/DeviceGroupConfig.java
index a7641bc..3427fd9 100644
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/device/entity/DeviceGroupConfig.java
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/device/entity/DeviceGroupConfig.java
@@ -2,7 +2,8 @@
 
 import com.baomidou.mybatisplus.annotation.*;
 import com.fasterxml.jackson.annotation.JsonFormat;
-import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 
@@ -15,72 +16,72 @@
 @Data
 @EqualsAndHashCode(callSuper = false)
 @TableName("device_group_config")
-@Schema(name = "DeviceGroupConfig", description = "璁惧缁勯厤缃俊鎭�")
+@ApiModel(value = "DeviceGroupConfig", description = "璁惧缁勯厤缃俊鎭�")
 public class DeviceGroupConfig {
 
-    @Schema(description = "璁惧缁処D", example = "1")
+    @ApiModelProperty(value = "璁惧缁処D", example = "1")
     @TableId(value = "id", type = IdType.AUTO)
     private Long id;
 
-    @Schema(description = "璁惧缁勫悕绉�", example = "鐢熶骇绾緼")
+    @ApiModelProperty(value = "璁惧缁勫悕绉�", example = "鐢熶骇绾緼")
     @TableField("group_name")
     private String groupName;
 
-    @Schema(description = "璁惧缁勭紪鍙�", example = "GROUP_001")
+    @ApiModelProperty(value = "璁惧缁勭紪鍙�", example = "GROUP_001")
     @TableField("group_code")
     private String groupCode;
 
-    @Schema(description = "璁惧缁勭被鍨嬶細1-鐢熶骇绾匡紝2-娴嬭瘯绾匡紝3-杈呭姪璁惧缁�", example = "1")
+    @ApiModelProperty(value = "璁惧缁勭被鍨嬶細1-鐢熶骇绾匡紝2-娴嬭瘯绾匡紝3-杈呭姪璁惧缁�", example = "1")
     @TableField("group_type")
     private Integer groupType;
 
-    @Schema(description = "鎵�灞為」鐩甀D", example = "1")
+    @ApiModelProperty(value = "鎵�灞為」鐩甀D", example = "1")
     @TableField("project_id")
     private Long projectId;
 
-    @Schema(description = "璁惧缁勭姸鎬侊細0-鍋滅敤锛�1-鍚敤锛�3-缁存姢涓�", example = "1")
+    @ApiModelProperty(value = "璁惧缁勭姸鎬侊細0-鍋滅敤锛�1-鍚敤锛�3-缁存姢涓�", example = "1")
     @TableField("status")
     private Integer status;
 
-    @Schema(description = "鏈�澶у苟鍙戣澶囨暟", example = "3")
+    @ApiModelProperty(value = "鏈�澶у苟鍙戣澶囨暟", example = "3")
     @TableField("max_concurrent_devices")
     private Integer maxConcurrentDevices;
 
-    @Schema(description = "蹇冭烦妫�娴嬮棿闅�(绉�)", example = "30")
+    @ApiModelProperty(value = "蹇冭烦妫�娴嬮棿闅�(绉�)", example = "30")
     @TableField("heartbeat_interval")
     private Integer heartbeatInterval;
 
-    @Schema(description = "閫氫俊瓒呮椂鏃堕棿(姣)", example = "5000")
+    @ApiModelProperty(value = "閫氫俊瓒呮椂鏃堕棿(姣)", example = "5000")
     @TableField("communication_timeout")
     private Integer communicationTimeout;
 
-    @Schema(description = "璁惧缁勬弿杩�", example = "鐢熶骇绾緼璁惧缁�")
+    @ApiModelProperty(value = "璁惧缁勬弿杩�", example = "鐢熶骇绾緼璁惧缁�")
     @TableField("description")
     private String description;
 
-    @Schema(description = "鎵╁睍閰嶇疆JSON", example = "{\"retryTimes\": 3, \"batchSize\": 100}")
+    @ApiModelProperty(value = "鎵╁睍閰嶇疆JSON", example = "{\"retryTimes\": 3, \"batchSize\": 100}")
     @TableField("extra_config")
     private String extraConfig;
 
-    @Schema(description = "鍒涘缓鏃堕棿")
+    @ApiModelProperty(value = "鍒涘缓鏃堕棿")
     @TableField(value = "created_time", fill = FieldFill.INSERT)
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
     private Date createdTime;
 
-    @Schema(description = "鏇存柊鏃堕棿")
+    @ApiModelProperty(value = "鏇存柊鏃堕棿")
     @TableField(value = "updated_time", fill = FieldFill.INSERT_UPDATE)
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
     private Date updatedTime;
 
-    @Schema(description = "鍒涘缓浜�", example = "system")
+    @ApiModelProperty(value = "鍒涘缓浜�", example = "system")
     @TableField(value = "created_by", fill = FieldFill.INSERT)
     private String createdBy;
 
-    @Schema(description = "鏇存柊浜�", example = "system")
+    @ApiModelProperty(value = "鏇存柊浜�", example = "system")
     @TableField(value = "updated_by", fill = FieldFill.INSERT_UPDATE)
     private String updatedBy;
 
-    @Schema(description = "鏄惁鍒犻櫎锛�0-鍚︼紝1-鏄�", example = "0")
+    @ApiModelProperty(value = "鏄惁鍒犻櫎锛�0-鍚︼紝1-鏄�", example = "0")
     @TableField("is_deleted")
     @TableLogic
     private Integer isDeleted;
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/device/entity/DeviceGroupRelation.java b/mes-processes/mes-plcSend/src/main/java/com/mes/device/entity/DeviceGroupRelation.java
index b7dedd3..4b9cb7f 100644
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/device/entity/DeviceGroupRelation.java
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/device/entity/DeviceGroupRelation.java
@@ -2,7 +2,8 @@
 
 import com.baomidou.mybatisplus.annotation.*;
 import com.fasterxml.jackson.annotation.JsonFormat;
-import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 
@@ -15,64 +16,64 @@
 @Data
 @EqualsAndHashCode(callSuper = false)
 @TableName("device_group_relation")
-@Schema(name = "DeviceGroupRelation", description = "璁惧缁勪笌璁惧鍏宠仈鍏崇郴")
+@ApiModel(value = "DeviceGroupRelation", description = "璁惧缁勪笌璁惧鍏宠仈鍏崇郴")
 public class DeviceGroupRelation {
 
-    @Schema(description = "鍏宠仈ID", example = "1")
+    @ApiModelProperty(value = "鍏宠仈ID", example = "1")
     @TableId(value = "id", type = IdType.AUTO)
     private Long id;
 
-    @Schema(description = "璁惧缁処D", example = "1")
+    @ApiModelProperty(value = "璁惧缁処D", example = "1")
     @TableField("group_id")
     private Long groupId;
 
-    @Schema(description = "璁惧ID", example = "1")
+    @ApiModelProperty(value = "璁惧ID", example = "1")
     @TableField("device_id")
     private Long deviceId;
 
-    @Schema(description = "璁惧鍦ㄧ粍鍐呯殑浼樺厛绾э細1-鏈�楂橈紝10-鏈�浣�", example = "1")
+    @ApiModelProperty(value = "璁惧鍦ㄧ粍鍐呯殑浼樺厛绾э細1-鏈�楂橈紝10-鏈�浣�", example = "1")
     @TableField("priority")
     private Integer priority;
 
-    @Schema(description = "璁惧鍦ㄧ粍鍐呯殑瑙掕壊锛�1-涓绘帶锛�2-鍗忎綔锛�3-鐩戞帶", example = "1")
+    @ApiModelProperty(value = "璁惧鍦ㄧ粍鍐呯殑瑙掕壊锛�1-涓绘帶锛�2-鍗忎綔锛�3-鐩戞帶", example = "1")
     @TableField("role")
     private Integer role;
 
-    @Schema(description = "璁惧鍦ㄨ缁勪腑鐨勭姸鎬侊細0-鏈厤缃紝1-姝e父锛�2-鏁呴殰锛�3-缁存姢", example = "1")
+    @ApiModelProperty(value = "璁惧鍦ㄨ缁勪腑鐨勭姸鎬侊細0-鏈厤缃紝1-姝e父锛�2-鏁呴殰锛�3-缁存姢", example = "1")
     @TableField("status")
     private Integer status;
 
-    @Schema(description = "杩炴帴椤哄簭锛氭暟鍊艰秺灏忚秺鍏堣繛鎺�", example = "1")
+    @ApiModelProperty(value = "杩炴帴椤哄簭锛氭暟鍊艰秺灏忚秺鍏堣繛鎺�", example = "1")
     @TableField("connection_order")
     private Integer connectionOrder;
 
-    @Schema(description = "鍏宠仈鎻忚堪", example = "涓绘帶璁惧锛岃礋璐f暣浣撳崗璋�")
+    @ApiModelProperty(value = "鍏宠仈鎻忚堪", example = "涓绘帶璁惧锛岃礋璐f暣浣撳崗璋�")
     @TableField("relation_desc")
     private String relationDesc;
 
-    @Schema(description = "鎵╁睍鍙傛暟JSON", example = "{\"timeout\": 5000, \"retryPolicy\": \"exponential\"}")
+    @ApiModelProperty(value = "鎵╁睍鍙傛暟JSON", example = "{\"timeout\": 5000, \"retryPolicy\": \"exponential\"}")
     @TableField("extra_params")
     private String extraParams;
 
-    @Schema(description = "鍒涘缓鏃堕棿")
+    @ApiModelProperty(value = "鍒涘缓鏃堕棿")
     @TableField(value = "created_time", fill = FieldFill.INSERT)
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
     private Date createdTime;
 
-    @Schema(description = "鏇存柊鏃堕棿")
+    @ApiModelProperty(value = "鏇存柊鏃堕棿")
     @TableField(value = "updated_time", fill = FieldFill.INSERT_UPDATE)
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
     private Date updatedTime;
 
-    @Schema(description = "鍒涘缓浜�", example = "system")
+    @ApiModelProperty(value = "鍒涘缓浜�", example = "system")
     @TableField(value = "created_by", fill = FieldFill.INSERT)
     private String createdBy;
 
-    @Schema(description = "鏇存柊浜�", example = "system")
+    @ApiModelProperty(value = "鏇存柊浜�", example = "system")
     @TableField(value = "updated_by", fill = FieldFill.INSERT_UPDATE)
     private String updatedBy;
 
-    @Schema(description = "鏄惁鍒犻櫎锛�0-鍚︼紝1-鏄�", example = "0")
+    @ApiModelProperty(value = "鏄惁鍒犻櫎锛�0-鍚︼紝1-鏄�", example = "0")
     @TableField("is_deleted")
     @TableLogic
     private Integer isDeleted;
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/device/entity/DeviceInteractionExecution.java b/mes-processes/mes-plcSend/src/main/java/com/mes/device/entity/DeviceInteractionExecution.java
index 4c61d26..b6265ac 100644
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/device/entity/DeviceInteractionExecution.java
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/device/entity/DeviceInteractionExecution.java
@@ -2,7 +2,8 @@
 
 import com.baomidou.mybatisplus.annotation.*;
 import com.fasterxml.jackson.annotation.JsonFormat;
-import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 
@@ -15,110 +16,110 @@
 @Data
 @EqualsAndHashCode(callSuper = false)
 @TableName("device_interaction_execution")
-@Schema(name = "DeviceInteractionExecution", description = "璁惧浜や簰鎵ц璁板綍")
+@ApiModel(value = "DeviceInteractionExecution", description = "璁惧浜や簰鎵ц璁板綍")
 public class DeviceInteractionExecution {
 
-    @Schema(description = "鎵ц璁板綍ID", example = "1")
+    @ApiModelProperty(value = "鎵ц璁板綍ID", example = "1")
     @TableId(value = "id", type = IdType.AUTO)
     private Long id;
 
-    @Schema(description = "鍏宠仈鐨勪氦浜掗�昏緫ID", example = "1")
+    @ApiModelProperty(value = "鍏宠仈鐨勪氦浜掗�昏緫ID", example = "1")
     @TableField("logic_id")
     private Long logicId;
 
-    @Schema(description = "璁惧缁処D", example = "1")
+    @ApiModelProperty(value = "璁惧缁処D", example = "1")
     @TableField("group_id")
     private Long groupId;
 
-    @Schema(description = "鎵�灞為」鐩甀D", example = "1")
+    @ApiModelProperty(value = "鎵�灞為」鐩甀D", example = "1")
     @TableField("project_id")
     private Long projectId;
 
-    @Schema(description = "鎵ц鎵规鍙�", example = "EXEC_20241030_001")
+    @ApiModelProperty(value = "鎵ц鎵规鍙�", example = "EXEC_20241030_001")
     @TableField("batch_no")
     private String batchNo;
 
-    @Schema(description = "鎵ц鐘舵�侊細0-绛夊緟锛�1-鎵ц涓紝2-鎴愬姛锛�3-澶辫触锛�4-瓒呮椂锛�5-鍙栨秷", example = "0")
+    @ApiModelProperty(value = "鎵ц鐘舵�侊細0-绛夊緟锛�1-鎵ц涓紝2-鎴愬姛锛�3-澶辫触锛�4-瓒呮椂锛�5-鍙栨秷", example = "0")
     @TableField("status")
     private Integer status;
 
-    @Schema(description = "鎵ц妯″紡锛�1-鎵嬪姩锛�2-鑷姩锛�3-瀹氭椂", example = "2")
+    @ApiModelProperty(value = "鎵ц妯″紡锛�1-鎵嬪姩锛�2-鑷姩锛�3-瀹氭椂", example = "2")
     @TableField("execution_mode")
     private Integer executionMode;
 
-    @Schema(description = "寮�濮嬫墽琛屾椂闂�")
+    @ApiModelProperty(value = "寮�濮嬫墽琛屾椂闂�")
     @TableField("start_time")
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
     private Date startTime;
 
-    @Schema(description = "缁撴潫鎵ц鏃堕棿")
+    @ApiModelProperty(value = "缁撴潫鎵ц鏃堕棿")
     @TableField("end_time")
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
     private Date endTime;
 
-    @Schema(description = "鎵ц鑰楁椂(姣)", example = "25000")
+    @ApiModelProperty(value = "鎵ц鑰楁椂(姣)", example = "25000")
     @TableField("execution_duration")
     private Long executionDuration;
 
-    @Schema(description = "鎵ц杩涘害锛�0-100", example = "80")
+    @ApiModelProperty(value = "鎵ц杩涘害锛�0-100", example = "80")
     @TableField("progress")
     private Integer progress;
 
-    @Schema(description = "褰撳墠鎵ц鐨勬楠ゅ簭鍙�", example = "3")
+    @ApiModelProperty(value = "褰撳墠鎵ц鐨勬楠ゅ簭鍙�", example = "3")
     @TableField("current_step")
     private Integer currentStep;
 
-    @Schema(description = "鎬绘楠ゆ暟", example = "10")
+    @ApiModelProperty(value = "鎬绘楠ゆ暟", example = "10")
     @TableField("total_steps")
     private Integer totalSteps;
 
-    @Schema(description = "鎴愬姛鎵ц鐨勮澶囨暟閲�", example = "3")
+    @ApiModelProperty(value = "鎴愬姛鎵ц鐨勮澶囨暟閲�", example = "3")
     @TableField("success_devices")
     private Integer successDevices;
 
-    @Schema(description = "澶辫触鐨勮澶囨暟閲�", example = "0")
+    @ApiModelProperty(value = "澶辫触鐨勮澶囨暟閲�", example = "0")
     @TableField("failed_devices")
     private Integer failedDevices;
 
-    @Schema(description = "瑙﹀彂鎵ц鐨勬搷浣滀汉", example = "admin")
+    @ApiModelProperty(value = "瑙﹀彂鎵ц鐨勬搷浣滀汉", example = "admin")
     @TableField("triggered_by")
     private String triggeredBy;
 
-    @Schema(description = "鎵ц缁撴灉鎻忚堪", example = "鎵�鏈夎澶囨垚鍔熷畬鎴愯嚜鍔ㄥ寲娴嬭瘯")
+    @ApiModelProperty(value = "鎵ц缁撴灉鎻忚堪", example = "鎵�鏈夎澶囨垚鍔熷畬鎴愯嚜鍔ㄥ寲娴嬭瘯")
     @TableField("result_message")
     private String resultMessage;
 
-    @Schema(description = "閿欒淇℃伅JSON", example = "{\"deviceId\": 2, \"error\": \"Connection timeout\"}")
+    @ApiModelProperty(value = "閿欒淇℃伅JSON", example = "{\"deviceId\": 2, \"error\": \"Connection timeout\"}")
     @TableField("error_details")
     private String errorDetails;
 
-    @Schema(description = "鎵ц鏁版嵁缁熻JSON", example = "{\"totalTime\": 25000, \"avgResponseTime\": 120}")
+    @ApiModelProperty(value = "鎵ц鏁版嵁缁熻JSON", example = "{\"totalTime\": 25000, \"avgResponseTime\": 120}")
     @TableField("execution_stats")
     private String executionStats;
 
-    @Schema(description = "鎵╁睍鍙傛暟JSON", example = "{\"testDataId\": \"TD_001\", \"environment\": \"prod\"}")
+    @ApiModelProperty(value = "鎵╁睍鍙傛暟JSON", example = "{\"testDataId\": \"TD_001\", \"environment\": \"prod\"}")
     @TableField("extra_params")
     private String extraParams;
 
-    @Schema(description = "鍒涘缓鏃堕棿")
+    @ApiModelProperty(value = "鍒涘缓鏃堕棿")
     @TableField(value = "created_time", fill = FieldFill.INSERT)
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
     private Date createdTime;
 
-    @Schema(description = "鏇存柊鏃堕棿")
+    @ApiModelProperty(value = "鏇存柊鏃堕棿")
     @TableField(value = "updated_time", fill = FieldFill.INSERT_UPDATE)
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
     private Date updatedTime;
 
-    @Schema(description = "鍒涘缓浜�", example = "system")
+    @ApiModelProperty(value = "鍒涘缓浜�", example = "system")
     @TableField(value = "created_by", fill = FieldFill.INSERT)
     private String createdBy;
 
-    @Schema(description = "鏇存柊浜�", example = "system")
+    @ApiModelProperty(value = "鏇存柊浜�", example = "system")
     @TableField(value = "updated_by", fill = FieldFill.INSERT_UPDATE)
     private String updatedBy;
 
-    @Schema(description = "鏄惁鍒犻櫎锛�0-鍚︼紝1-鏄�", example = "0")
+    @ApiModelProperty(value = "鏄惁鍒犻櫎锛�0-鍚︼紝1-鏄�", example = "0")
     @TableField("is_deleted")
     @TableLogic
     private Integer isDeleted;
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/device/entity/DeviceInteractionLogic.java b/mes-processes/mes-plcSend/src/main/java/com/mes/device/entity/DeviceInteractionLogic.java
index a16a4fa..f029282 100644
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/device/entity/DeviceInteractionLogic.java
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/device/entity/DeviceInteractionLogic.java
@@ -2,7 +2,8 @@
 
 import com.baomidou.mybatisplus.annotation.*;
 import com.fasterxml.jackson.annotation.JsonFormat;
-import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 
@@ -15,88 +16,88 @@
 @Data
 @EqualsAndHashCode(callSuper = false)
 @TableName("device_interaction_logic")
-@Schema(name = "DeviceInteractionLogic", description = "璁惧浜や簰閫昏緫閰嶇疆")
+@ApiModel(value = "DeviceInteractionLogic", description = "璁惧浜や簰閫昏緫閰嶇疆")
 public class DeviceInteractionLogic {
 
-    @Schema(description = "閫昏緫ID", example = "1")
+    @ApiModelProperty(value = "閫昏緫ID", example = "1")
     @TableId(value = "id", type = IdType.AUTO)
     private Long id;
 
-    @Schema(description = "閫昏緫鍚嶇О", example = "澶ц溅鑷姩鍖栨祴璇曢�昏緫")
+    @ApiModelProperty(value = "閫昏緫鍚嶇О", example = "澶ц溅鑷姩鍖栨祴璇曢�昏緫")
     @TableField("logic_name")
     private String logicName;
 
-    @Schema(description = "閫昏緫缂栧彿", example = "TRUCK_AUTO_TEST_001")
+    @ApiModelProperty(value = "閫昏緫缂栧彿", example = "TRUCK_AUTO_TEST_001")
     @TableField("logic_code")
     private String logicCode;
 
-    @Schema(description = "鎵�灞炴ā鍧楋細1-涓婂ぇ杞︼紝2-涓嬪ぇ杞︼紝3-杞繍锛�4-娴嬭瘯", example = "1")
+    @ApiModelProperty(value = "鎵�灞炴ā鍧楋細1-涓婂ぇ杞︼紝2-涓嬪ぇ杞︼紝3-杞繍锛�4-娴嬭瘯", example = "1")
     @TableField("module_type")
     private Integer moduleType;
 
-    @Schema(description = "鎵�灞炶澶囩粍ID", example = "1")
+    @ApiModelProperty(value = "鎵�灞炶澶囩粍ID", example = "1")
     @TableField("group_id")
     private Long groupId;
 
-    @Schema(description = "閫昏緫绫诲瀷锛�1-椤哄簭鎵ц锛�2-骞惰鎵ц锛�3-鏉′欢鎵ц锛�4-寰幆鎵ц", example = "1")
+    @ApiModelProperty(value = "閫昏緫绫诲瀷锛�1-椤哄簭鎵ц锛�2-骞惰鎵ц锛�3-鏉′欢鎵ц锛�4-寰幆鎵ц", example = "1")
     @TableField("logic_type")
     private Integer logicType;
 
-    @Schema(description = "閫昏緫鐘舵�侊細0-绂佺敤锛�1-鍚敤锛�3-璋冭瘯涓�", example = "1")
+    @ApiModelProperty(value = "閫昏緫鐘舵�侊細0-绂佺敤锛�1-鍚敤锛�3-璋冭瘯涓�", example = "1")
     @TableField("status")
     private Integer status;
 
-    @Schema(description = "璁惧缁勫唴璇ラ�昏緫鐨勪紭鍏堢骇锛�1-鏈�楂橈紝10-鏈�浣�", example = "1")
+    @ApiModelProperty(value = "璁惧缁勫唴璇ラ�昏緫鐨勪紭鍏堢骇锛�1-鏈�楂橈紝10-鏈�浣�", example = "1")
     @TableField("priority")
     private Integer priority;
 
-    @Schema(description = "鎵ц瓒呮椂鏃堕棿(姣)", example = "30000")
+    @ApiModelProperty(value = "鎵ц瓒呮椂鏃堕棿(姣)", example = "30000")
     @TableField("execution_timeout")
     private Integer executionTimeout;
 
-    @Schema(description = "閲嶈瘯娆℃暟", example = "3")
+    @ApiModelProperty(value = "閲嶈瘯娆℃暟", example = "3")
     @TableField("retry_times")
     private Integer retryTimes;
 
-    @Schema(description = "閫昏緫鎻忚堪", example = "澶ц溅鑷姩鍖栨祴璇曠殑瀹屾暣娴佺▼鎺у埗")
+    @ApiModelProperty(value = "閫昏緫鎻忚堪", example = "澶ц溅鑷姩鍖栨祴璇曠殑瀹屾暣娴佺▼鎺у埗")
     @TableField("description")
     private String description;
 
-    @Schema(description = "浜や簰姝ラJSON鏁扮粍", example = "[{\"step\": 1, \"deviceId\": 1, \"action\": \"START\", \"params\": {}}]")
+    @ApiModelProperty(value = "浜や簰姝ラJSON鏁扮粍", example = "[{\"step\": 1, \"deviceId\": 1, \"action\": \"START\", \"params\": {}}]")
     @TableField("interaction_steps")
     private String interactionSteps;
 
-    @Schema(description = "鏉′欢鍒ゆ柇閫昏緫JSON", example = "{\"conditions\": [{\"field\": \"status\", \"operator\": \"eq\", \"value\": 1}]}")
+    @ApiModelProperty(value = "鏉′欢鍒ゆ柇閫昏緫JSON", example = "{\"conditions\": [{\"field\": \"status\", \"operator\": \"eq\", \"value\": 1}]}")
     @TableField("condition_logic")
     private String conditionLogic;
 
-    @Schema(description = "鎵╁睍鍙傛暟JSON", example = "{\"parallelLimit\": 5, \"errorHandling\": \"retry\"}")
+    @ApiModelProperty(value = "鎵╁睍鍙傛暟JSON", example = "{\"parallelLimit\": 5, \"errorHandling\": \"retry\"}")
     @TableField("extra_params")
     private String extraParams;
 
-    @Schema(description = "鐗堟湰鍙�", example = "1.0.0")
+    @ApiModelProperty(value = "鐗堟湰鍙�", example = "1.0.0")
     @TableField("version")
     private String version;
 
-    @Schema(description = "鍒涘缓鏃堕棿")
+    @ApiModelProperty(value = "鍒涘缓鏃堕棿")
     @TableField(value = "created_time", fill = FieldFill.INSERT)
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
     private Date createdTime;
 
-    @Schema(description = "鏇存柊鏃堕棿")
+    @ApiModelProperty(value = "鏇存柊鏃堕棿")
     @TableField(value = "updated_time", fill = FieldFill.INSERT_UPDATE)
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
     private Date updatedTime;
 
-    @Schema(description = "鍒涘缓浜�", example = "system")
+    @ApiModelProperty(value = "鍒涘缓浜�", example = "system")
     @TableField(value = "created_by", fill = FieldFill.INSERT)
     private String createdBy;
 
-    @Schema(description = "鏇存柊浜�", example = "system")
+    @ApiModelProperty(value = "鏇存柊浜�", example = "system")
     @TableField(value = "updated_by", fill = FieldFill.INSERT_UPDATE)
     private String updatedBy;
 
-    @Schema(description = "鏄惁鍒犻櫎锛�0-鍚︼紝1-鏄�", example = "0")
+    @ApiModelProperty(value = "鏄惁鍒犻櫎锛�0-鍚︼紝1-鏄�", example = "0")
     @TableField("is_deleted")
     @TableLogic
     private Integer isDeleted;
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/device/mapper/DeviceGroupRelationMapper.java b/mes-processes/mes-plcSend/src/main/java/com/mes/device/mapper/DeviceGroupRelationMapper.java
index 6ae3625..f9eb11f 100644
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/device/mapper/DeviceGroupRelationMapper.java
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/device/mapper/DeviceGroupRelationMapper.java
@@ -1,6 +1,7 @@
 package com.mes.device.mapper;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.mes.device.entity.DeviceConfig;
 import com.mes.device.entity.DeviceGroupRelation;
 import com.mes.device.vo.DeviceGroupVO;
 import org.apache.ibatis.annotations.Mapper;
@@ -77,4 +78,19 @@
             "INNER JOIN device_group_relation dgr ON dgc.id = dgr.group_id " +
             "WHERE dgr.device_id = #{deviceId} AND dgr.is_deleted = 0 AND dgc.is_deleted = 0")
     List<DeviceGroupVO.GroupInfo> getDeviceGroups(@Param("deviceId") Long deviceId);
+
+    /**
+     * 鑾峰彇鎸夎繛鎺ラ『搴忔帓搴忕殑璁惧閰嶇疆鍒楄〃
+     *
+     * @param groupId 璁惧缁処D
+     * @return 璁惧閰嶇疆闆嗗悎
+     */
+    @Select("SELECT d.* " +
+            "FROM device_group_relation dgr " +
+            "INNER JOIN device_config d ON dgr.device_id = d.id " +
+            "WHERE dgr.group_id = #{groupId} " +
+            "  AND dgr.is_deleted = 0 " +
+            "  AND d.is_deleted = 0 " +
+            "ORDER BY IFNULL(dgr.connection_order, 0) ASC, dgr.id ASC")
+    List<DeviceConfig> getOrderedDeviceConfigs(@Param("groupId") Long groupId);
 }
\ No newline at end of file
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/device/request/DeviceConfigRequest.java b/mes-processes/mes-plcSend/src/main/java/com/mes/device/request/DeviceConfigRequest.java
index ca837d1..717a216 100644
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/device/request/DeviceConfigRequest.java
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/device/request/DeviceConfigRequest.java
@@ -1,6 +1,7 @@
 package com.mes.device.request;
 
-import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 
 import java.util.List;
@@ -12,37 +13,37 @@
  * @since 2025-07-12
  */
 @Data
-@Schema(description = "璁惧閰嶇疆鎿嶄綔璇锋眰浣�")
+@ApiModel(description = "璁惧閰嶇疆鎿嶄綔璇锋眰浣�")
 public class DeviceConfigRequest {
 
-    @Schema(description = "璁惧ID", example = "1")
+    @ApiModelProperty(value = "璁惧ID", example = "1")
     private Long deviceId;
 
-    @Schema(description = "璁惧閰嶇疆淇℃伅")
+    @ApiModelProperty(value = "璁惧閰嶇疆淇℃伅")
     private Object deviceConfig;
 
-    @Schema(description = "璁惧ID鍒楄〃")
+    @ApiModelProperty(value = "璁惧ID鍒楄〃")
     private List<Long> deviceIds;
 
-    @Schema(description = "椤圭洰ID", example = "1")
+    @ApiModelProperty(value = "椤圭洰ID", example = "1")
     private Long projectId;
 
-    @Schema(description = "璁惧绫诲瀷", example = "1")
+    @ApiModelProperty(value = "璁惧绫诲瀷", example = "1")
     private String deviceType;
 
-    @Schema(description = "璁惧鐘舵��", example = "1")
+    @ApiModelProperty(value = "璁惧鐘舵��", example = "1")
     private String deviceStatus;
 
-    @Schema(description = "鎼滅储鍏抽敭璇�", example = "璁惧1")
+    @ApiModelProperty(value = "鎼滅储鍏抽敭璇�", example = "璁惧1")
     private String keyword;
 
-    @Schema(description = "璁惧缂栫爜", example = "DEVICE001")
+    @ApiModelProperty(value = "璁惧缂栫爜", example = "DEVICE001")
     private String deviceCode;
 
-    @Schema(description = "椤电爜", example = "1")
+    @ApiModelProperty(value = "椤电爜", example = "1")
     private Integer page;
 
-    @Schema(description = "姣忛〉澶у皬", example = "10")
+    @ApiModelProperty(value = "姣忛〉澶у皬", example = "10")
     private Integer size;
 
     // 鏋勯�犲嚱鏁�
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/device/request/DeviceGlassFeedRequest.java b/mes-processes/mes-plcSend/src/main/java/com/mes/device/request/DeviceGlassFeedRequest.java
index b35348d..47a12b4 100644
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/device/request/DeviceGlassFeedRequest.java
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/device/request/DeviceGlassFeedRequest.java
@@ -1,7 +1,7 @@
 package com.mes.device.request;
 
-import io.swagger.v3.oas.annotations.media.ArraySchema;
-import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 
 import javax.validation.constraints.NotEmpty;
@@ -12,23 +12,23 @@
  * 鐜荤拑鍐欏叆璇锋眰
  */
 @Data
-@Schema(name = "DeviceGlassFeedRequest", description = "璁惧鐜荤拑鍐欏叆璇锋眰")
+@ApiModel(value = "DeviceGlassFeedRequest", description = "璁惧鐜荤拑鍐欏叆璇锋眰")
 public class DeviceGlassFeedRequest {
 
     @NotNull
-    @Schema(description = "璁惧ID", required = true)
+    @ApiModelProperty(value = "璁惧ID", required = true)
     private Long deviceId;
 
-    @ArraySchema(schema = @Schema(description = "鐜荤拑ID鍒楄〃", example = "GLS001"), minItems = 1)
+    @ApiModelProperty(value = "鐜荤拑ID鍒楄〃", example = "GLS001")
     private List<String> glassIds;
 
-    @Schema(description = "杩涚墖浣嶇疆鏍囪瘑锛堜笌鎺у埗鍙傛暟涓殑 positionMappings 瀵瑰簲锛�")
+    @ApiModelProperty(value = "杩涚墖浣嶇疆鏍囪瘑锛堜笌鎺у埗鍙傛暟涓殑 positionMappings 瀵瑰簲锛�")
     private String positionCode;
 
-    @Schema(description = "鐩存帴鎸囧畾鐨勪綅缃�硷紙浼樺厛绾ч珮浜� positionCode锛�")
+    @ApiModelProperty(value = "鐩存帴鎸囧畾鐨勪綅缃�硷紙浼樺厛绾ч珮浜� positionCode锛�")
     private Integer positionValue;
 
-    @Schema(description = "鏄惁鑷姩鍐欏叆璇锋眰瀛�", defaultValue = "true")
+    @ApiModelProperty(value = "鏄惁鑷姩鍐欏叆璇锋眰瀛�", example = "true")
     private Boolean triggerRequest = true;
 }
 
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/device/request/DeviceGroupRequest.java b/mes-processes/mes-plcSend/src/main/java/com/mes/device/request/DeviceGroupRequest.java
index 187aa9e..2c3acbd 100644
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/device/request/DeviceGroupRequest.java
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/device/request/DeviceGroupRequest.java
@@ -1,6 +1,7 @@
 package com.mes.device.request;
 
-import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 
 import java.util.List;
@@ -12,25 +13,25 @@
  * @since 2025-07-12
  */
 @Data
-@Schema(description = "璁惧缁勬搷浣滆姹備綋")
+@ApiModel(description = "璁惧缁勬搷浣滆姹備綋")
 public class DeviceGroupRequest {
 
-    @Schema(description = "璁惧缁処D", example = "1")
+    @ApiModelProperty(value = "璁惧缁処D", example = "1")
     private Long groupId;
 
-    @Schema(description = "璁惧ID", example = "1")
+    @ApiModelProperty(value = "璁惧ID", example = "1")
     private Long deviceId;
 
-    @Schema(description = "璁惧ID鍒楄〃")
+    @ApiModelProperty(value = "璁惧ID鍒楄〃")
     private List<Long> deviceIds;
 
-    @Schema(description = "璁惧缁処D鍒楄〃")
+    @ApiModelProperty(value = "璁惧缁処D鍒楄〃")
     private List<Long> groupIds;
     
-    @Schema(description = "璁惧缁勯厤缃俊鎭�")
+    @ApiModelProperty(value = "璁惧缁勯厤缃俊鎭�")
     private Object groupConfig;
 
-    @Schema(description = "璁惧瑙掕壊", example = "MEMBER")
+    @ApiModelProperty(value = "璁惧瑙掕壊", example = "MEMBER")
     private String deviceRole;
 
     // 鏋勯�犲嚱鏁�
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/device/request/DevicePlcBatchRequest.java b/mes-processes/mes-plcSend/src/main/java/com/mes/device/request/DevicePlcBatchRequest.java
index 445ccad..12b82fd 100644
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/device/request/DevicePlcBatchRequest.java
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/device/request/DevicePlcBatchRequest.java
@@ -1,6 +1,7 @@
 package com.mes.device.request;
 
-import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 
 import javax.validation.constraints.NotEmpty;
@@ -14,11 +15,11 @@
  * @since 2025-11-17
  */
 @Data
-@Schema(name = "DevicePlcBatchRequest", description = "璁惧 PLC 鎵归噺鎿嶄綔璇锋眰")
+@ApiModel(value = "DevicePlcBatchRequest", description = "璁惧 PLC 鎵归噺鎿嶄綔璇锋眰")
 public class DevicePlcBatchRequest implements Serializable {
 
     @NotEmpty(message = "璁惧ID鍒楄〃涓嶈兘涓虹┖")
-    @Schema(description = "璁惧ID鍒楄〃", required = true)
+    @ApiModelProperty(value = "璁惧ID鍒楄〃", required = true)
     private List<Long> deviceIds;
 }
 
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/device/service/DeviceInteractionService.java b/mes-processes/mes-plcSend/src/main/java/com/mes/device/service/DeviceInteractionService.java
index 6f0b4bb..6075183 100644
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/device/service/DeviceInteractionService.java
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/device/service/DeviceInteractionService.java
@@ -3,14 +3,26 @@
 import com.mes.device.request.DeviceGlassFeedRequest;
 import com.mes.device.vo.DevicePlcVO;
 
+import java.util.Map;
+
 /**
  * 璁惧浜や簰閫昏緫鏈嶅姟
  */
 public interface DeviceInteractionService {
 
     /**
-     * 鎵ц鐜荤拑涓婃枡鍐欏叆
+     * 鎵ц鐜荤拑涓婃枡鍐欏叆锛堝吋瀹规棫鎺ュ彛锛�
      */
     DevicePlcVO.OperationResult feedGlass(DeviceGlassFeedRequest request);
+
+    /**
+     * 鎵ц璁惧閫昏緫鎿嶄綔锛堟柊鎺ュ彛锛屼娇鐢ㄥ鐞嗗櫒鏋舵瀯锛�
+     * 
+     * @param deviceId 璁惧ID
+     * @param operation 鎿嶄綔绫诲瀷锛堝锛歠eedGlass, triggerRequest, triggerReport绛夛級
+     * @param params 鎿嶄綔鍙傛暟
+     * @return 鎿嶄綔缁撴灉
+     */
+    DevicePlcVO.OperationResult executeOperation(Long deviceId, String operation, Map<String, Object> params);
 }
 
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/device/service/impl/DeviceInteractionServiceImpl.java b/mes-processes/mes-plcSend/src/main/java/com/mes/device/service/impl/DeviceInteractionServiceImpl.java
index 9c6fb3f..73cb33a 100644
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/device/service/impl/DeviceInteractionServiceImpl.java
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/device/service/impl/DeviceInteractionServiceImpl.java
@@ -1,6 +1,10 @@
 package com.mes.device.service.impl;
 
+import com.mes.device.entity.DeviceConfig;
+import com.mes.interaction.DeviceLogicHandler;
+import com.mes.interaction.DeviceLogicHandlerFactory;
 import com.mes.device.request.DeviceGlassFeedRequest;
+import com.mes.device.service.DeviceConfigService;
 import com.mes.device.service.DeviceControlProfileService;
 import com.mes.device.service.DeviceInteractionService;
 import com.mes.device.service.DevicePlcOperationService;
@@ -25,9 +29,30 @@
 
     private final DeviceControlProfileService controlProfileService;
     private final DevicePlcOperationService devicePlcOperationService;
+    private final DeviceConfigService deviceConfigService;
+    private final DeviceLogicHandlerFactory handlerFactory;
 
+    /**
+     * 鎵ц鐜荤拑涓婃枡鍐欏叆锛堝吋瀹规棫鎺ュ彛锛屼繚鐣欏師鏈夐�昏緫锛�
+     */
     @Override
     public DevicePlcVO.OperationResult feedGlass(DeviceGlassFeedRequest request) {
+        // 浼樺厛浣跨敤鏂扮殑澶勭悊鍣ㄦ灦鏋�
+        DeviceConfig deviceConfig = deviceConfigService.getDeviceById(request.getDeviceId());
+        if (deviceConfig != null) {
+            DeviceLogicHandler handler = handlerFactory.getHandler(deviceConfig.getDeviceType());
+            if (handler != null) {
+                // 浣跨敤鏂版灦鏋勬墽琛�
+                Map<String, Object> params = new HashMap<>();
+                params.put("glassIds", request.getGlassIds());
+                params.put("positionCode", request.getPositionCode());
+                params.put("positionValue", request.getPositionValue());
+                params.put("triggerRequest", request.getTriggerRequest());
+                return handler.execute(deviceConfig, "feedGlass", params);
+            }
+        }
+
+        // 闄嶇骇鍒板師鏈夐�昏緫锛堝吋瀹规棫浠g爜锛�
         DeviceControlProfile profile = controlProfileService.getProfile(request.getDeviceId());
         Map<String, Object> payload = buildGlassPayload(profile, request);
         String opName = "鐜荤拑涓婃枡";
@@ -37,6 +62,36 @@
         return devicePlcOperationService.writeFields(request.getDeviceId(), payload, opName);
     }
 
+    /**
+     * 鎵ц璁惧閫昏緫鎿嶄綔锛堟柊鎺ュ彛锛屼娇鐢ㄥ鐞嗗櫒鏋舵瀯锛�
+     */
+    @Override
+    public DevicePlcVO.OperationResult executeOperation(Long deviceId, String operation, Map<String, Object> params) {
+        // 鑾峰彇璁惧閰嶇疆
+        DeviceConfig deviceConfig = deviceConfigService.getDeviceById(deviceId);
+        if (deviceConfig == null) {
+            return DevicePlcVO.OperationResult.builder()
+                    .success(false)
+                    .message("璁惧涓嶅瓨鍦�: " + deviceId)
+                    .build();
+        }
+
+        // 鑾峰彇瀵瑰簲鐨勫鐞嗗櫒
+        DeviceLogicHandler handler = handlerFactory.getHandler(deviceConfig.getDeviceType());
+        if (handler == null) {
+            return DevicePlcVO.OperationResult.builder()
+                    .success(false)
+                    .message("涓嶆敮鎸佺殑璁惧绫诲瀷: " + deviceConfig.getDeviceType())
+                    .build();
+        }
+
+        // 鎵ц鎿嶄綔
+        return handler.execute(deviceConfig, operation, params != null ? params : new HashMap<>());
+    }
+
+    /**
+     * 鏋勫缓鐜荤拑涓婃枡鏁版嵁锛堝吋瀹规棫閫昏緫锛�
+     */
     private Map<String, Object> buildGlassPayload(DeviceControlProfile profile, DeviceGlassFeedRequest request) {
         if (CollectionUtils.isEmpty(profile.getGlassSlots())) {
             throw new IllegalStateException("璁惧鏈厤缃幓鐠冩Ы浣嶄俊鎭�");
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 c3c50d1..f034b9e 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
@@ -6,6 +6,7 @@
 import com.mes.device.service.DeviceConfigService;
 import com.mes.device.service.DeviceGroupRelationService;
 import com.mes.device.service.DevicePlcOperationService;
+import com.mes.device.util.ConfigJsonHelper;
 import com.mes.device.vo.DeviceGroupVO;
 import com.mes.device.vo.DevicePlcVO;
 import com.mes.service.PlcTestWriteService;
@@ -247,13 +248,21 @@
             throw new IllegalArgumentException("璁惧淇℃伅涓虹┖");
         }
 
+        // 浼樺厛浠巆onfigJson涓幏鍙�
+        Map<String, Object> configParams = ConfigJsonHelper.parseToMap(device.getConfigJson(), objectMapper);
+        Object plcProjectId = configParams.get(PLC_PROJECT_ID_KEY);
+        if (plcProjectId != null) {
+            return String.valueOf(plcProjectId);
+        }
+
+        // 鍏舵浠庢墿灞曞弬鏁颁腑鑾峰彇锛堝吋瀹规棫閰嶇疆锛�
         String extra = device.getExtraParams();
         if (extra != null && !extra.isEmpty()) {
             try {
                 Map<String, Object> extraParams = objectMapper.readValue(extra, new TypeReference<Map<String, Object>>() {});
-                Object plcProjectId = extraParams.get(PLC_PROJECT_ID_KEY);
-                if (plcProjectId != null) {
-                    return String.valueOf(plcProjectId);
+                Object plcProjectIdFromExtra = extraParams.get(PLC_PROJECT_ID_KEY);
+                if (plcProjectIdFromExtra != null) {
+                    return String.valueOf(plcProjectIdFromExtra);
                 }
             } catch (Exception e) {
                 log.warn("瑙f瀽璁惧鎵╁睍鍙傛暟澶辫触, deviceId={}", device.getId(), e);
@@ -271,7 +280,7 @@
         throw new IllegalStateException("鏃犳硶瑙f瀽璁惧鐨� PLC 椤圭洰鏍囪瘑, deviceId=" + device.getId());
     }
 
-    private enum PlcOperationType {
+    public enum PlcOperationType {
         REQUEST("PLC璇锋眰", "PLC 璇锋眰鍙戦�佹垚鍔�", "PLC 璇锋眰鍙戦�佸け璐�"),
         REPORT("PLC姹囨姤", "PLC 姹囨姤妯℃嫙鎴愬姛", "PLC 姹囨姤妯℃嫙澶辫触"),
         RESET("PLC閲嶇疆", "PLC 鐘舵�佸凡閲嶇疆", "PLC 鐘舵�侀噸缃け璐�");
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/device/util/ConfigJsonHelper.java b/mes-processes/mes-plcSend/src/main/java/com/mes/device/util/ConfigJsonHelper.java
new file mode 100644
index 0000000..c132d55
--- /dev/null
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/device/util/ConfigJsonHelper.java
@@ -0,0 +1,71 @@
+package com.mes.device.util;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * 宸ュ叿绫伙細瑙f瀽 DeviceConfig.configJson 鐨勫绉嶆牸寮�
+ * 鍏煎瀵硅薄缁撴瀯鍜� [{paramKey,paramValue}] 鏁扮粍缁撴瀯
+ */
+@Slf4j
+public final class ConfigJsonHelper {
+
+    private static final TypeReference<Map<String, Object>> MAP_TYPE =
+            new TypeReference<Map<String, Object>>() {};
+    private static final TypeReference<List<Map<String, Object>>> LIST_TYPE =
+            new TypeReference<List<Map<String, Object>>>() {};
+
+    private ConfigJsonHelper() {
+    }
+
+    /**
+     * 灏� configJson 瑙f瀽涓� key-value 鐨� Map
+     *
+     * @param configJson   鍘熷 JSON 瀛楃涓�
+     * @param objectMapper 鍏叡 ObjectMapper
+     * @return 鍙傛暟鏄犲皠锛屼笉鍙慨鏀�
+     */
+    public static Map<String, Object> parseToMap(String configJson, ObjectMapper objectMapper) {
+        if (configJson == null || configJson.trim().isEmpty()) {
+            return Collections.emptyMap();
+        }
+        String trimmed = configJson.trim();
+        try {
+            if (trimmed.startsWith("[")) {
+                List<Map<String, Object>> items = objectMapper.readValue(trimmed, LIST_TYPE);
+                Map<String, Object> result = new LinkedHashMap<>();
+                for (Map<String, Object> item : items) {
+                    if (item == null) {
+                        continue;
+                    }
+                    Object keyObj = firstNonNull(item.get("paramKey"), item.get("key"));
+                    if (keyObj == null) {
+                        continue;
+                    }
+                    Object valueObj = firstNonNull(item.get("paramValue"), item.get("value"));
+                    result.put(String.valueOf(keyObj), valueObj);
+                }
+                return result;
+            } else if (trimmed.startsWith("{")) {
+                return objectMapper.readValue(trimmed, MAP_TYPE);
+            } else {
+                log.warn("鏈煡鐨� configJson 鏍煎紡: {}", trimmed);
+            }
+        } catch (Exception e) {
+            log.warn("瑙f瀽 configJson 澶辫触: {}", trimmed, e);
+        }
+        return Collections.emptyMap();
+    }
+
+    private static Object firstNonNull(Object first, Object second) {
+        return Objects.nonNull(first) ? first : second;
+    }
+}
+
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/device/vo/DeviceControlProfile.java b/mes-processes/mes-plcSend/src/main/java/com/mes/device/vo/DeviceControlProfile.java
index 60ee815..e3e9fd5 100644
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/device/vo/DeviceControlProfile.java
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/device/vo/DeviceControlProfile.java
@@ -1,6 +1,7 @@
 package com.mes.device.vo;
 
-import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
 import lombok.AllArgsConstructor;
 import lombok.Builder;
 import lombok.Data;
@@ -17,52 +18,52 @@
 @Builder
 @NoArgsConstructor
 @AllArgsConstructor
-@Schema(name = "DeviceControlProfile", description = "璁惧鎺у埗鍙傛暟閰嶇疆")
+@ApiModel(value = "DeviceControlProfile", description = "璁惧鎺у埗鍙傛暟閰嶇疆")
 public class DeviceControlProfile implements Serializable {
 
-    @Schema(description = "鑺傛媿/绾块�熷害锛坢m/s锛�")
+    @ApiModelProperty(value = "鑺傛媿/绾块�熷害锛坢m/s锛�")
     private Integer lineSpeed;
 
-    @Schema(description = "鐜荤拑闀垮害锛坢m锛�")
+    @ApiModelProperty(value = "鐜荤拑闀垮害锛坢m锛�")
     private Integer glassLength;
 
-    @Schema(description = "缂撳瓨鏁伴噺/妲戒綅鏁伴噺")
+    @ApiModelProperty(value = "缂撳瓨鏁伴噺/妲戒綅鏁伴噺")
     private Integer bufferCount;
 
-    @Schema(description = "鏄惁鑷姩瑙﹀彂PLC璇锋眰")
+    @ApiModelProperty(value = "鏄惁鑷姩瑙﹀彂PLC璇锋眰")
     private Boolean autoRequest;
 
-    @Schema(description = "PLC璇锋眰瀛楁鍚�", defaultValue = "plcRequest")
+    @ApiModelProperty(value = "PLC璇锋眰瀛楁鍚�")
     private String requestField = "plcRequest";
 
-    @Schema(description = "杩涚墖浣嶇疆瀛楁鍚�", defaultValue = "inPosition")
+    @ApiModelProperty(value = "杩涚墖浣嶇疆瀛楁鍚�")
     private String positionField = "inPosition";
 
-    @Schema(description = "鐜荤拑鏁伴噺瀛楁鍚�", defaultValue = "plcGlassCount")
+    @ApiModelProperty(value = "鐜荤拑鏁伴噺瀛楁鍚�")
     private String glassCountField = "plcGlassCount";
 
-    @Schema(description = "鐜荤拑ID妲戒綅瀛楁瀹氫箟")
+    @ApiModelProperty(value = "鐜荤拑ID妲戒綅瀛楁瀹氫箟")
     private List<GlassSlot> glassSlots;
 
-    @Schema(description = "浣嶇疆鏄犲皠锛屽锛歿 \"station1\":1 }")
+    @ApiModelProperty(value = "浣嶇疆鏄犲皠锛屽锛歿 \"station1\":1 }")
     private Map<String, Integer> positionMappings;
 
     @Data
     @NoArgsConstructor
     @AllArgsConstructor
     @Builder
-    @Schema(name = "GlassSlot", description = "鐜荤拑ID妲戒綅")
+    @ApiModel(value = "GlassSlot", description = "鐜荤拑ID妲戒綅")
     public static class GlassSlot implements Serializable {
-        @Schema(description = "妲戒綅搴忓彿锛屼粠1寮�濮�")
+        @ApiModelProperty(value = "妲戒綅搴忓彿锛屼粠1寮�濮�")
         private Integer order;
 
-        @Schema(description = "PLC瀛楁鍚嶏紝渚嬪 plcGlassId1")
+        @ApiModelProperty(value = "PLC瀛楁鍚嶏紝渚嬪 plcGlassId1")
         private String field;
 
-        @Schema(description = "瀛楁闀垮害锛屽瓧绗︿覆闀垮害绛�")
+        @ApiModelProperty(value = "瀛楁闀垮害锛屽瓧绗︿覆闀垮害绛�")
         private Integer length;
 
-        @Schema(description = "妲戒綅鎻忚堪")
+        @ApiModelProperty(value = "妲戒綅鎻忚堪")
         private String description;
     }
 }
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 a4099ae..873c2e0 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
@@ -1,6 +1,7 @@
 package com.mes.device.vo;
 
-import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
 import lombok.AllArgsConstructor;
 import lombok.Builder;
 import lombok.Data;
@@ -25,7 +26,7 @@
     @Builder
     @NoArgsConstructor
     @AllArgsConstructor
-    @Schema(name = "DevicePlcOperationResult", description = "PLC 鎿嶄綔缁撴灉")
+    @ApiModel(value = "DevicePlcOperationResult", description = "PLC 鎿嶄綔缁撴灉")
     public static class OperationResult implements Serializable {
         private Long deviceId;
         private String deviceName;
@@ -44,7 +45,7 @@
     @Builder
     @NoArgsConstructor
     @AllArgsConstructor
-    @Schema(name = "DevicePlcStatus", description = "PLC 鐘舵�佹暟鎹�")
+    @ApiModel(value = "DevicePlcStatus", description = "PLC 鐘舵�佹暟鎹�")
     public static class StatusInfo implements Serializable {
         private Long deviceId;
         private String deviceName;
diff --git "a/mes-processes/mes-plcSend/src/main/java/com/mes/device/\345\244\232\350\256\276\345\244\207\350\201\224\345\220\210\346\265\213\350\257\225\346\211\251\345\261\225\346\226\271\346\241\210.md" "b/mes-processes/mes-plcSend/src/main/java/com/mes/device/\345\244\232\350\256\276\345\244\207\350\201\224\345\220\210\346\265\213\350\257\225\346\211\251\345\261\225\346\226\271\346\241\210.md"
new file mode 100644
index 0000000..eb15099
--- /dev/null
+++ "b/mes-processes/mes-plcSend/src/main/java/com/mes/device/\345\244\232\350\256\276\345\244\207\350\201\224\345\220\210\346\265\213\350\257\225\346\211\251\345\261\225\346\226\271\346\241\210.md"
@@ -0,0 +1,1153 @@
+# MES Test Project 澶氳澶囪仈鍚堟祴璇曟墿灞曟柟妗�
+
+## 馃搵 椤圭洰姒傝堪
+
+鍩轰簬鐜版湁鐨凪ES Test Project锛坢es-web + mes-plcSend锛夛紝鎵╁睍鏀寔澶氳澶囪仈鍚堟祴璇曞姛鑳斤紝瀹炵幇"涓婂ぇ杞﹁澶� 鈫� 澶х悊鐗囪澶� 鈫� 鐜荤拑瀛樺偍璁惧"鐨勫畬鏁寸敓浜ф祦绋嬭嚜鍔ㄥ寲娴嬭瘯銆�
+
+## 馃幆 鏍稿績闇�姹�
+
+### 涓氬姟鍦烘櫙
+1. **涓婂ぇ杞﹀墠璇锋眰**锛氭娴嬭溅杈嗗閲忥紙6000mm鍙厤缃級銆佺幓鐠冭鏍煎尮閰嶃�佽妭鎷嶆帶鍒�
+2. **澶х悊鐗囦氦浜�**锛氫笌MES澶х悊鐗囦俊鎭瘮瀵归獙璇併�佹壒閲忓鐞嗛�昏緫
+3. **澶氳澶囧崗璋�**锛氳澶囬棿鏁版嵁浼犻�掋�佺姸鎬佸悓姝ャ�佷緷璧栫鐞�
+
+### 鎶�鏈渶姹�
+- 鏀寔澶歅LC璁惧鍦板潃鏄犲皠
+- 璁惧缁勯厤缃拰绠$悊
+- 涓茶/骞惰鎵ц妯″紡
+- 璁惧闂存暟鎹叡浜�
+- 瀹炴椂鐘舵�佺洃鎺�
+
+## 馃搧 鎵╁睍鏋舵瀯璁捐
+
+### 1. 鍚庣鎵╁睍缁撴瀯
+
+#### 1.1 鏂板鐩綍缁撴瀯
+```
+mes-plcSend/src/main/java/com/mes/
+鈹溾攢鈹� device/                          # 璁惧绠$悊灞�
+鈹�   鈹溾攢鈹� entity/
+鈹�   鈹�   鈹溾攢鈹� DeviceConfig.java        # 璁惧閰嶇疆瀹炰綋
+鈹�   鈹�   鈹溾攢鈹� DeviceGroup.java         # 璁惧缁勫疄浣�
+鈹�   鈹�   鈹斺攢鈹� DeviceStatus.java        # 璁惧鐘舵�佸疄浣�
+鈹�   鈹溾攢鈹� service/
+鈹�   鈹�   鈹溾攢鈹� DeviceService.java       # 璁惧绠$悊鏈嶅姟
+鈹�   鈹�   鈹溾攢鈹� DeviceGroupService.java  # 璁惧缁勬湇鍔�
+鈹�   鈹�   鈹斺攢鈹� DeviceCoordinationService.java # 璁惧鍗忚皟鏈嶅姟
+鈹�   鈹斺攢鈹� controller/
+鈹�       鈹溾攢鈹� DeviceController.java    # 璁惧绠$悊API
+鈹�       鈹斺攢鈹� DeviceGroupController.java # 璁惧缁勭鐞咥PI
+鈹溾攢鈹� interaction/                     # 浜や簰閫昏緫妯″潡
+鈹�   鈹溾攢鈹� base/
+鈹�   鈹�   鈹溾攢鈹� BaseInteraction.java     # 鍩虹浜や簰鎶借薄
+鈹�   鈹�   鈹溾攢鈹� InteractionContext.java  # 浜や簰涓婁笅鏂�
+鈹�   鈹�   鈹斺攢鈹� InteractionResult.java   # 浜や簰缁撴灉
+鈹�   鈹溾攢鈹� 涓婂ぇ杞�/
+鈹�   鈹�   鈹溾攢鈹� 涓婂ぇ杞nteraction.java   # 涓婂ぇ杞︿氦浜掗�昏緫
+鈹�   鈹�   鈹斺攢鈹� 涓婂ぇ杞onfig.java        # 涓婂ぇ杞﹂厤缃�
+鈹�   鈹溾攢鈹� 澶х悊鐗�/
+鈹�   鈹�   鈹溾攢鈹� 澶х悊鐗嘔nteraction.java   # 澶х悊鐗囦氦浜掗�昏緫
+鈹�   鈹�   鈹斺攢鈹� 澶х悊鐗嘋onfig.java        # 澶х悊鐗囬厤缃�
+鈹�   鈹斺攢鈹� 鐜荤拑瀛樺偍/
+鈹�       鈹溾攢鈹� 鐜荤拑瀛樺偍Interaction.java # 鐜荤拑瀛樺偍浜や簰閫昏緫
+鈹�       鈹斺攢鈹� 鐜荤拑瀛樺偍Config.java      # 鐜荤拑瀛樺偍閰嶇疆
+鈹斺攢鈹� task/                           # 浠诲姟绠$悊灞�
+    鈹溾攢鈹� entity/
+    鈹�   鈹溾攢鈹� MultiDeviceTask.java     # 澶氳澶囦换鍔″疄浣�
+    鈹�   鈹斺攢鈹� TaskStep.java            # 浠诲姟姝ラ瀹炰綋
+    鈹溾攢鈹� service/
+    鈹�   鈹溾攢鈹� MultiDeviceTaskService.java # 澶氳澶囦换鍔℃湇鍔�
+    鈹�   鈹斺攢鈹� TaskExecutionEngine.java    # 浠诲姟鎵ц寮曟搸
+    鈹斺攢鈹� controller/
+        鈹斺攢鈹� MultiDeviceTaskController.java # 澶氳澶囦换鍔PI
+```
+
+#### 1.2 鏁版嵁搴撹〃鎵╁睍
+
+```sql
+-- 璁惧閰嶇疆琛�
+CREATE TABLE device_config (
+    id BIGINT PRIMARY KEY AUTO_INCREMENT,
+    device_id VARCHAR(50) UNIQUE NOT NULL COMMENT '璁惧ID',
+    device_name VARCHAR(100) NOT NULL COMMENT '璁惧鍚嶇О',
+    device_type VARCHAR(50) NOT NULL COMMENT '璁惧绫诲瀷(涓婂ぇ杞�/澶х悊鐗�/鐜荤拑瀛樺偍)',
+    plc_ip VARCHAR(15) NOT NULL COMMENT 'PLC IP鍦板潃',
+    plc_type VARCHAR(20) NOT NULL COMMENT 'PLC绫诲瀷',
+    module_name VARCHAR(50) NOT NULL COMMENT '妯″潡鍚嶇О',
+    is_primary BOOLEAN DEFAULT FALSE COMMENT '鏄惁涓绘帶璁惧',
+    enabled BOOLEAN DEFAULT TRUE COMMENT '鏄惁鍚敤',
+    config_json TEXT COMMENT '璁惧鐗瑰畾閰嶇疆(JSON)',
+    created_time DATETIME DEFAULT CURRENT_TIMESTAMP,
+    updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
+);
+
+-- 璁惧缁勯厤缃〃
+CREATE TABLE device_group (
+    id BIGINT PRIMARY KEY AUTO_INCREMENT,
+    group_id VARCHAR(50) UNIQUE NOT NULL COMMENT '璁惧缁処D',
+    group_name VARCHAR(100) NOT NULL COMMENT '璁惧缁勫悕绉�',
+    project_id VARCHAR(50) NOT NULL COMMENT '鍏宠仈椤圭洰ID',
+    execution_mode ENUM('SERIAL', 'PARALLEL') DEFAULT 'SERIAL' COMMENT '鎵ц妯″紡',
+    execution_config JSON COMMENT '鎵ц閰嶇疆',
+    dependencies JSON COMMENT '璁惧渚濊禆鍏崇郴',
+    created_time DATETIME DEFAULT CURRENT_TIMESTAMP,
+    updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
+);
+
+-- 璁惧缁勪笌璁惧鍏崇郴琛�
+CREATE TABLE device_group_mapping (
+    id BIGINT PRIMARY KEY AUTO_INCREMENT,
+    group_id VARCHAR(50) NOT NULL,
+    device_id VARCHAR(50) NOT NULL,
+    execution_order INT NOT NULL COMMENT '鎵ц椤哄簭',
+    FOREIGN KEY (group_id) REFERENCES device_group(group_id) ON DELETE CASCADE,
+    FOREIGN KEY (device_id) REFERENCES device_config(device_id) ON DELETE CASCADE,
+    UNIQUE KEY uk_group_device (group_id, device_id)
+);
+
+-- 澶氳澶囦换鍔¤〃
+CREATE TABLE multi_device_task (
+    id BIGINT PRIMARY KEY AUTO_INCREMENT,
+    task_id VARCHAR(50) UNIQUE NOT NULL COMMENT '浠诲姟ID',
+    group_id VARCHAR(50) NOT NULL COMMENT '璁惧缁処D',
+    project_id VARCHAR(50) NOT NULL COMMENT '椤圭洰ID',
+    status ENUM('PENDING', 'RUNNING', 'COMPLETED', 'FAILED', 'CANCELLED') DEFAULT 'PENDING',
+    current_step INT DEFAULT 0 COMMENT '褰撳墠姝ラ',
+    total_steps INT DEFAULT 0 COMMENT '鎬绘楠ゆ暟',
+    start_time DATETIME COMMENT '寮�濮嬫椂闂�',
+    end_time DATETIME COMMENT '缁撴潫鏃堕棿',
+    error_message TEXT COMMENT '閿欒淇℃伅',
+    result_data JSON COMMENT '缁撴灉鏁版嵁',
+    created_time DATETIME DEFAULT CURRENT_TIMESTAMP,
+    updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
+);
+
+-- 浠诲姟姝ラ璇︽儏琛�
+CREATE TABLE task_step_detail (
+    id BIGINT PRIMARY KEY AUTO_INCREMENT,
+    task_id VARCHAR(50) NOT NULL COMMENT '浠诲姟ID',
+    step_order INT NOT NULL COMMENT '姝ラ椤哄簭',
+    device_id VARCHAR(50) NOT NULL COMMENT '璁惧ID',
+    step_name VARCHAR(100) NOT NULL COMMENT '姝ラ鍚嶇О',
+    status ENUM('PENDING', 'RUNNING', 'COMPLETED', 'FAILED', 'SKIPPED') DEFAULT 'PENDING',
+    start_time DATETIME COMMENT '姝ラ寮�濮嬫椂闂�',
+    end_time DATETIME COMMENT '姝ラ缁撴潫鏃堕棿',
+    duration_ms BIGINT COMMENT '鎵ц鑰楁椂(姣)',
+    input_data JSON COMMENT '杈撳叆鏁版嵁',
+    output_data JSON COMMENT '杈撳嚭鏁版嵁',
+    error_message TEXT COMMENT '閿欒淇℃伅',
+    retry_count INT DEFAULT 0 COMMENT '閲嶈瘯娆℃暟',
+    created_time DATETIME DEFAULT CURRENT_TIMESTAMP,
+    FOREIGN KEY (task_id) REFERENCES multi_device_task(task_id) ON DELETE CASCADE
+);
+```
+
+### 2. 鍓嶇鎵╁睍缁撴瀯
+
+#### 2.1 鏂板鍓嶇鐩綍
+```
+mes-web/src/views/plcTest/
+鈹溾攢鈹� components/
+鈹�   鈹溾攢鈹� DeviceManagement/          # 璁惧绠$悊缁勪欢
+鈹�   鈹�   鈹溾攢鈹� DeviceList.vue         # 璁惧鍒楄〃
+鈹�   鈹�   鈹溾攢鈹� DeviceConfig.vue       # 璁惧閰嶇疆
+鈹�   鈹�   鈹斺攢鈹� DeviceStatus.vue       # 璁惧鐘舵��
+鈹�   鈹溾攢鈹� DeviceGroup/              # 璁惧缁勭粍浠�
+鈹�   鈹�   鈹溾攢鈹� GroupList.vue          # 璁惧缁勫垪琛�
+鈹�   鈹�   鈹溾攢鈹� GroupConfig.vue        # 璁惧缁勯厤缃�
+鈹�   鈹�   鈹斺攢鈹� GroupTopology.vue      # 璁惧缁勬嫇鎵戝浘
+鈹�   鈹溾攢鈹� MultiDeviceTest/          # 澶氳澶囨祴璇曠粍浠�
+鈹�   鈹�   鈹溾攢鈹� TestOrchestration.vue  # 娴嬭瘯缂栨帓
+鈹�   鈹�   鈹溾攢鈹� ExecutionMonitor.vue   # 鎵ц鐩戞帶
+鈹�   鈹�   鈹斺攢鈹� ResultAnalysis.vue     # 缁撴灉鍒嗘瀽
+鈹�   鈹斺攢鈹� InteractionLogic/         # 浜や簰閫昏緫缁勪欢
+鈹�       鈹溾攢鈹� 涓婂ぇ杞onfig.vue       # 涓婂ぇ杞﹂厤缃�
+鈹�       鈹溾攢鈹� 澶х悊鐗嘋onfig.vue       # 澶х悊鐗囬厤缃�
+鈹�       鈹斺攢鈹� 鐜荤拑瀛樺偍Config.vue     # 鐜荤拑瀛樺偍閰嶇疆
+```
+
+## 馃敡 鏍稿績瀹炵幇璁捐
+
+### 3.1 璁惧绠$悊瀹炵幇
+
+#### 璁惧閰嶇疆瀹炰綋
+```java
+@Data
+@Entity
+@TableName("device_config")
+public class DeviceConfig {
+    @TableId(type = IdType.AUTO)
+    private Long id;
+    
+    @TableField("device_id")
+    private String deviceId;
+    
+    @TableField("device_name")
+    private String deviceName;
+    
+    @TableField("device_type")
+    private String deviceType; // "涓婂ぇ杞�" / "澶х悊鐗�" / "鐜荤拑瀛樺偍"
+    
+    @TableField("plc_ip")
+    private String plcIp;
+    
+    @TableField("plc_type")
+    private String plcType;
+    
+    @TableField("module_name")
+    private String moduleName;
+    
+    @TableField("is_primary")
+    private Boolean isPrimary;
+    
+    @TableField("config_json")
+    private String configJson; // 璁惧鐗瑰畾閰嶇疆
+    
+    // 閰嶇疆瑙f瀽鏂规硶
+    public <T> T getConfig(Class<T> clazz) {
+        if (StringUtils.isBlank(configJson)) {
+            return null;
+        }
+        try {
+            return JsonUtils.fromJson(configJson, clazz);
+        } catch (Exception e) {
+            log.error("瑙f瀽璁惧閰嶇疆澶辫触: {}", deviceId, e);
+            return null;
+        }
+    }
+}
+```
+
+#### 璁惧绠$悊鏈嶅姟
+```java
+@Service
+public class DeviceService {
+    
+    @Resource
+    private DeviceConfigMapper deviceConfigMapper;
+    
+    /**
+     * 娉ㄥ唽璁惧
+     */
+    @Transactional
+    public void registerDevice(DeviceConfig device) {
+        // 楠岃瘉PLC杩炴帴
+        validatePlcConnection(device.getPlcIp(), device.getPlcType());
+        
+        // 淇濆瓨璁惧閰嶇疆
+        deviceConfigMapper.insert(device);
+        
+        // 鏇存柊鍦板潃鏄犲皠
+        updatePlcAddressMapping(device);
+        
+        log.info("璁惧娉ㄥ唽鎴愬姛: {}", device.getDeviceId());
+    }
+    
+    /**
+     * 鑾峰彇璁惧鐨凱LC鍦板潃鏄犲皠
+     */
+    public Map<String, Integer> getDeviceAddressMapping(String deviceId) {
+        DeviceConfig device = getDeviceById(deviceId);
+        if (device == null) {
+            throw new RuntimeException("璁惧涓嶅瓨鍦�: " + deviceId);
+        }
+        
+        // 鏍规嵁璁惧绫诲瀷鑾峰彇瀵瑰簲鐨勫湴鍧�鏄犲皠閰嶇疆
+        String mappingKey = device.getDeviceType() + "_" + device.getModuleName();
+        return plcAddressService.getMappingByKey(mappingKey);
+    }
+}
+```
+
+### 3.2 璁惧缁勭鐞嗗疄鐜�
+
+#### 璁惧缁勫疄浣�
+```java
+@Data
+@TableName("device_group")
+public class DeviceGroup {
+    @TableId(type = IdType.AUTO)
+    private Long id;
+    
+    @TableField("group_id")
+    private String groupId;
+    
+    @TableField("group_name")
+    private String groupName;
+    
+    @TableField("project_id")
+    private String projectId;
+    
+    @TableField("execution_mode")
+    private ExecutionMode executionMode; // SERIAL / PARALLEL
+    
+    @TableField("execution_config")
+    private String executionConfig;
+    
+    @TableField("dependencies")
+    private String dependencies; // JSON鏍煎紡鐨勪緷璧栧叧绯�
+    
+    // 鑾峰彇璁惧缁勪腑鐨勬墍鏈夎澶�
+    public List<DeviceConfig> getDevices() {
+        return deviceGroupService.getDevicesByGroupId(groupId);
+    }
+    
+    // 鑾峰彇鎵ц椤哄簭
+    public List<DeviceConfig> getExecutionSequence() {
+        return deviceGroupService.getDevicesByOrder(groupId);
+    }
+}
+```
+
+### 3.3 浜や簰閫昏緫瀹炵幇
+
+#### 鍩虹浜や簰鎺ュ彛
+```java
+/**
+ * 璁惧浜や簰閫昏緫鎺ュ彛
+ */
+public interface DeviceInteraction {
+    
+    /**
+     * 鎵ц浜や簰閫昏緫
+     */
+    InteractionResult execute(InteractionContext context);
+    
+    /**
+     * 楠岃瘉鍓嶇疆鏉′欢
+     */
+    boolean validatePreCondition(InteractionContext context);
+    
+    /**
+     * 鑾峰彇璁惧绫诲瀷
+     */
+    String getDeviceType();
+    
+    /**
+     * 鑾峰彇榛樿閰嶇疆
+     */
+    DeviceInteractionConfig getDefaultConfig();
+}
+```
+
+#### 涓婂ぇ杞︿氦浜掑疄鐜�
+```java
+@Component("涓婂ぇ杞nteraction")
+public class 涓婂ぇ杞nteraction implements DeviceInteraction {
+    
+    @Override
+    public InteractionResult execute(InteractionContext context) {
+        涓婂ぇ杞onfig config = context.getConfig(涓婂ぇ杞onfig.class);
+        
+        try {
+            // 1. 楠岃瘉鍓嶇疆鏉′欢
+            if (!validatePreCondition(context)) {
+                return InteractionResult.fail("鍓嶇疆鏉′欢楠岃瘉澶辫触");
+            }
+            
+            // 2. 鑾峰彇杞﹁締瑙勬牸锛堝彲閰嶇疆锛�
+            VehicleSpec vehicle = context.getVehicleSpec();
+            double vehicleCapacity = config.getVehicleCapacity(); // 榛樿6000mm
+            
+            // 3. 妫�鏌ヨ溅杈嗗閲�
+            if (vehicle.getMaxCapacity() > vehicleCapacity) {
+                return InteractionResult.fail("杞﹁締瀹归噺瓒呭嚭闄愬埗: " + vehicle.getMaxCapacity());
+            }
+            
+            // 4. 鑾峰彇褰撳墠鐜荤拑淇℃伅
+            GlassSpec currentGlass = context.getCurrentGlass();
+            
+            // 5. 璁$畻瑁呰浇绌洪棿
+            double usedCapacity = calculateUsedCapacity(context);
+            double remainingCapacity = vehicleCapacity - usedCapacity;
+            
+            if (remainingCapacity >= currentGlass.getLength()) {
+                // 6. 鍒嗛厤杞﹁締绌洪棿
+                allocateVehicleSpace(context, currentGlass);
+                
+                // 7. 鑺傛媿鎺у埗锛堝彲閰嶇疆闂撮殧鏃堕棿锛�
+                sleep(config.getGlassIntervalMs()); // 榛樿1000ms
+                
+                // 8. 瑙﹀彂PLC鍐欏叆
+                triggerPlcWrite(context, "杞﹁締瑁呰浇", currentGlass);
+                
+                return InteractionResult.success("涓婂ぇ杞︽垚鍔�", 
+                    Map.of("remainingCapacity", remainingCapacity, 
+                           "allocatedGlass", currentGlass));
+            }
+            
+            return InteractionResult.wait("绛夊緟涓嬩竴杈嗚溅涓婂ぇ杞�", 
+                Map.of("remainingCapacity", remainingCapacity));
+                
+        } catch (Exception e) {
+            log.error("涓婂ぇ杞︿氦浜掓墽琛屽け璐�", e);
+            return InteractionResult.fail("鎵ц寮傚父: " + e.getMessage());
+        }
+    }
+    
+    @Override
+    public boolean validatePreCondition(InteractionContext context) {
+        // 楠岃瘉杞﹁締淇℃伅銆佺幓鐠冧俊鎭�丳LC杩炴帴绛�
+        return context.getVehicleSpec() != null && 
+               context.getCurrentGlass() != null &&
+               validatePlcConnection(context.getDeviceId());
+    }
+    
+    @Override
+    public String getDeviceType() {
+        return "涓婂ぇ杞�";
+    }
+}
+```
+
+#### 澶х悊鐗囦氦浜掑疄鐜�
+```java
+@Component("澶х悊鐗嘔nteraction")
+public class 澶х悊鐗嘔nteraction implements DeviceInteraction {
+    
+    @Override
+    public InteractionResult execute(InteractionContext context) {
+        澶х悊鐗嘋onfig config = context.getConfig(澶х悊鐗嘋onfig.class);
+        
+        try {
+            // 1. 鑾峰彇涓婂ぇ杞﹂樁娈典紶閫掔殑鏁版嵁
+            List<GlassSpec> glassesFromVehicle = context.getSharedData("glassesFromVehicle", List.class);
+            VehicleSpec vehicleInfo = context.getSharedData("vehicleInfo", VehicleSpec.class);
+            
+            if (glassesFromVehicle == null || glassesFromVehicle.isEmpty()) {
+                return InteractionResult.wait("绛夊緟涓婂ぇ杞︽暟鎹�");
+            }
+            
+            // 2. 涓嶮ES澶х悊鐗囦俊鎭瘮瀵归獙璇�
+            List<GlassSpec> mesGlasses = fetchMesGlassesInfo(vehicleInfo.getVehicleId());
+            
+            for (GlassSpec vehicleGlass : glassesFromVehicle) {
+                boolean matched = false;
+                for (GlassSpec mesGlass : mesGlasses) {
+                    if (isGlassMatched(vehicleGlass, mesGlass, config)) {
+                        matched = true;
+                        break;
+                    }
+                }
+                
+                if (!matched && config.isGlassMatchingEnabled()) {
+                    return InteractionResult.fail("鐜荤拑淇℃伅涓嶅尮閰�: " + vehicleGlass.getGlassId());
+                }
+            }
+            
+            // 3. 鎵归噺澶勭悊锛堝彲閰嶇疆锛�
+            if (config.isBatchProcessing()) {
+                return processBatch(glassesFromVehicle, context, config);
+            } else {
+                return processIndividual(glassesFromVehicle, context, config);
+            }
+            
+        } catch (Exception e) {
+            log.error("澶х悊鐗囦氦浜掓墽琛屽け璐�", e);
+            return InteractionResult.fail("鎵ц寮傚父: " + e.getMessage());
+        }
+    }
+    
+    private InteractionResult processBatch(List<GlassSpec> glasses, InteractionContext context, 澶х悊鐗嘋onfig config) {
+        // 鎵归噺澶勭悊閫昏緫
+        for (GlassSpec glass : glasses) {
+            // 姣忕墖鐜荤拑澶勭悊闂撮殧
+            sleep(config.getProcessingInterval()); // 榛樿2000ms
+            triggerPlcWrite(context, "澶х悊鐗囧鐞�", glass);
+        }
+        
+        // 浼犻�掑鐞嗙粨鏋滃埌涓嬩竴涓澶�
+        context.setSharedData("processedGlasses", glasses);
+        
+        return InteractionResult.success("澶х悊鐗囨壒閲忓鐞嗗畬鎴�", 
+            Map.of("processedCount", glasses.size()));
+    }
+}
+```
+
+### 3.4 澶氳澶囦换鍔℃墽琛屽紩鎿�
+
+#### 浠诲姟鎵ц寮曟搸
+```java
+@Service
+public class TaskExecutionEngine {
+    
+    @Resource
+    private DeviceGroupService deviceGroupService;
+    
+    /**
+     * 鎵ц澶氳澶囪仈鍚堟祴璇�
+     */
+    @Transactional
+    public MultiDeviceTaskResult executeMultiDeviceTask(String groupId, TaskParameters parameters) {
+        DeviceGroup group = deviceGroupService.getDeviceGroupById(groupId);
+        if (group == null) {
+            throw new RuntimeException("璁惧缁勪笉瀛樺湪: " + groupId);
+        }
+        
+        // 1. 鍒涘缓浠诲姟璁板綍
+        MultiDeviceTask task = createTaskRecord(group, parameters);
+        
+        try {
+            task.setStatus("RUNNING");
+            task.setStartTime(new Date());
+            
+            // 2. 鏋勫缓浜や簰涓婁笅鏂�
+            InteractionContext context = buildInteractionContext(group, parameters);
+            
+            // 3. 鎸夋墽琛屾ā寮忔墽琛�
+            MultiDeviceTaskResult result;
+            if (group.getExecutionMode() == ExecutionMode.SERIAL) {
+                result = executeSerialDevices(group, context, task);
+            } else {
+                result = executeParallelDevices(group, context, task);
+            }
+            
+            // 4. 鏇存柊浠诲姟缁撴灉
+            task.setStatus(result.isSuccess() ? "COMPLETED" : "FAILED");
+            task.setEndTime(new Date());
+            task.setResultData(JsonUtils.toJson(result));
+            
+            return result;
+            
+        } catch (Exception e) {
+            log.error("澶氳澶囦换鍔℃墽琛屽け璐�: {}", groupId, e);
+            
+            task.setStatus("FAILED");
+            task.setEndTime(new Date());
+            task.setErrorMessage(e.getMessage());
+            
+            return MultiDeviceTaskResult.fail("浠诲姟鎵ц澶辫触: " + e.getMessage());
+        }
+    }
+    
+    /**
+     * 涓茶璁惧鎵ц锛氫笂澶ц溅 鈫� 澶х悊鐗� 鈫� 鐜荤拑瀛樺偍
+     */
+    private MultiDeviceTaskResult executeSerialDevices(DeviceGroup group, InteractionContext context, MultiDeviceTask task) {
+        MultiDeviceTaskResult finalResult = new MultiDeviceTaskResult();
+        List<DeviceConfig> executionSequence = group.getExecutionSequence();
+        
+        for (int i = 0; i < executionSequence.size(); i++) {
+            DeviceConfig device = executionSequence.get(i);
+            TaskStep step = createTaskStep(task.getTaskId(), i + 1, device);
+            
+            try {
+                step.setStatus("RUNNING");
+                step.setStartTime(new Date());
+                
+                // 璁剧疆褰撳墠璁惧涓婁笅鏂�
+                context.setCurrentDevice(device);
+                context.setDeviceId(device.getDeviceId());
+                
+                // 鎵ц璁惧浜や簰閫昏緫
+                DeviceInteraction interaction = getInteraction(device.getDeviceType());
+                InteractionResult stepResult = interaction.execute(context);
+                
+                step.setEndTime(new Date());
+                step.setDurationMs(System.currentTimeMillis() - step.getStartTime().getTime());
+                
+                if (stepResult.isSuccess()) {
+                    step.setStatus("COMPLETED");
+                    step.setOutputData(JsonUtils.toJson(stepResult.getData()));
+                    
+                    // 浼犻�掓暟鎹埌涓嬩竴涓澶�
+                    passDataToNextDevice(context, device, stepResult);
+                    
+                    finalResult.addStepResult(device.getDeviceName(), stepResult);
+                    
+                } else {
+                    step.setStatus("FAILED");
+                    step.setErrorMessage(stepResult.getMessage());
+                    
+                    finalResult.addStepResult(device.getDeviceName(), stepResult);
+                    finalResult.fail("璁惧 " + device.getDeviceName() + " 鎵ц澶辫触: " + stepResult.getMessage());
+                    break;
+                }
+                
+            } catch (Exception e) {
+                step.setStatus("FAILED");
+                step.setErrorMessage(e.getMessage());
+                step.setEndTime(new Date());
+                
+                log.error("璁惧鎵ц寮傚父: {}", device.getDeviceName(), e);
+                finalResult.fail("璁惧 " + device.getDeviceName() + " 鎵ц寮傚父: " + e.getMessage());
+                break;
+            }
+        }
+        
+        return finalResult;
+    }
+    
+    /**
+     * 鏁版嵁浼犻�掑埌涓嬩竴涓澶�
+     */
+    private void passDataToNextDevice(InteractionContext context, DeviceConfig currentDevice, InteractionResult result) {
+        String deviceType = currentDevice.getDeviceType();
+        
+        if ("涓婂ぇ杞�".equals(deviceType)) {
+            // 涓婂ぇ杞� 鈫� 澶х悊鐗囷細浼犻�掔幓鐠冨垪琛ㄥ拰杞﹁締淇℃伅
+            context.setSharedData("glassesFromVehicle", result.getData("glasses"));
+            context.setSharedData("vehicleInfo", result.getData("vehicle"));
+            
+        } else if ("澶х悊鐗�".equals(deviceType)) {
+            // 澶х悊鐗� 鈫� 鐜荤拑瀛樺偍锛氫紶閫掑鐞嗗畬鎴愮殑鐜荤拑
+            context.setSharedData("processedGlasses", result.getData("processedGlasses"));
+        }
+    }
+}
+```
+
+## 馃帹 鍓嶇鐣岄潰璁捐
+
+### 4.1 璁惧绠$悊鐣岄潰
+
+#### 璁惧閰嶇疆椤甸潰
+```vue
+<template>
+  <div class="device-management">
+    <!-- 璁惧鍒楄〃 -->
+    <div class="device-list">
+      <el-table :data="devices" style="width: 100%">
+        <el-table-column prop="deviceName" label="璁惧鍚嶇О" />
+        <el-table-column prop="deviceType" label="璁惧绫诲瀷" />
+        <el-table-column prop="plcIp" label="PLC IP" />
+        <el-table-column prop="status" label="鐘舵��">
+          <template #default="{ row }">
+            <el-tag :type="getStatusType(row.status)">{{ row.status }}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="鎿嶄綔">
+          <template #default="{ row }">
+            <el-button @click="editDevice(row)">缂栬緫</el-button>
+            <el-button @click="testConnection(row)">娴嬭瘯杩炴帴</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+    </div>
+    
+    <!-- 璁惧閰嶇疆琛ㄥ崟 -->
+    <el-dialog v-model="showConfigDialog" title="璁惧閰嶇疆">
+      <el-form :model="deviceForm" label-width="120px">
+        <el-form-item label="璁惧ID">
+          <el-input v-model="deviceForm.deviceId" />
+        </el-form-item>
+        <el-form-item label="璁惧鍚嶇О">
+          <el-input v-model="deviceForm.deviceName" />
+        </el-form-item>
+        <el-form-item label="璁惧绫诲瀷">
+          <el-select v-model="deviceForm.deviceType">
+            <el-option label="涓婂ぇ杞﹁澶�" value="涓婂ぇ杞�" />
+            <el-option label="澶х悊鐗囪澶�" value="澶х悊鐗�" />
+            <el-option label="鐜荤拑瀛樺偍璁惧" value="鐜荤拑瀛樺偍" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="PLC閰嶇疆">
+          <div class="plc-config">
+            <el-input v-model="deviceForm.plcIp" placeholder="PLC IP鍦板潃" />
+            <el-select v-model="deviceForm.plcType" placeholder="PLC绫诲瀷">
+              <el-option label="S7-1200" value="S7-1200" />
+              <el-option label="S7-1500" value="S7-1500" />
+            </el-select>
+          </div>
+        </el-form-item>
+        
+        <!-- 璁惧鐗瑰畾閰嶇疆 -->
+        <div v-if="deviceForm.deviceType === '涓婂ぇ杞�'">
+          <el-form-item label="杞﹁締瀹归噺(mm)">
+            <el-input-number v-model="deviceForm.config.vehicleCapacity" :min="1000" :max="12000" />
+          </el-form-item>
+          <el-form-item label="鐜荤拑闂撮殧(ms)">
+            <el-input-number v-model="deviceForm.config.glassIntervalMs" :min="100" :max="10000" />
+          </el-form-item>
+        </div>
+        
+        <div v-if="deviceForm.deviceType === '澶х悊鐗�'">
+          <el-form-item label="鍚敤鐜荤拑姣斿">
+            <el-switch v-model="deviceForm.config.glassMatchingEnabled" />
+          </el-form-item>
+          <el-form-item label="鎵归噺澶勭悊">
+            <el-switch v-model="deviceForm.config.batchProcessing" />
+          </el-form-item>
+        </div>
+      </el-form>
+      
+      <template #footer>
+        <el-button @click="showConfigDialog = false">鍙栨秷</el-button>
+        <el-button type="primary" @click="saveDevice">淇濆瓨</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+export default {
+  data() {
+    return {
+      devices: [],
+      showConfigDialog: false,
+      deviceForm: {
+        deviceId: '',
+        deviceName: '',
+        deviceType: '',
+        plcIp: '',
+        plcType: '',
+        config: {
+          vehicleCapacity: 6000,      // 榛樿6绫�
+          glassIntervalMs: 1000,     // 榛樿1绉�
+          glassMatchingEnabled: true,
+          batchProcessing: true
+        }
+      }
+    }
+  },
+  methods: {
+    async loadDevices() {
+      // 鍔犺浇璁惧鍒楄〃
+      const response = await this.$api.device.getDeviceList();
+      this.devices = response.data;
+    },
+    
+    async saveDevice() {
+      try {
+        if (this.deviceForm.deviceId) {
+          await this.$api.device.updateDevice(this.deviceForm);
+        } else {
+          await this.$api.device.createDevice(this.deviceForm);
+        }
+        this.$message.success('淇濆瓨鎴愬姛');
+        this.showConfigDialog = false;
+        this.loadDevices();
+      } catch (error) {
+        this.$message.error('淇濆瓨澶辫触: ' + error.message);
+      }
+    }
+  }
+}
+</script>
+```
+
+### 4.2 璁惧缁勯厤缃晫闈�
+
+```vue
+<template>
+  <div class="device-group-management">
+    <!-- 璁惧缁勫垪琛� -->
+    <div class="group-list">
+      <div class="header">
+        <h3>璁惧缁勯厤缃�</h3>
+        <el-button type="primary" @click="createGroup">鏂板缓璁惧缁�</el-button>
+      </div>
+      
+      <div class="groups">
+        <el-card v-for="group in deviceGroups" :key="group.groupId" class="group-card">
+          <template #header>
+            <div class="card-header">
+              <span>{{ group.groupName }}</span>
+              <el-tag :type="group.executionMode === 'SERIAL' ? 'primary' : 'success'">
+                {{ group.executionMode === 'SERIAL' ? '涓茶鎵ц' : '骞惰鎵ц' }}
+              </el-tag>
+            </div>
+          </template>
+          
+          <div class="group-content">
+            <!-- 璁惧鎷撴墤鍥� -->
+            <div class="topology">
+              <div v-for="(device, index) in group.devices" :key="device.deviceId" class="device-node">
+                <div class="device-box" :class="device.deviceType">
+                  <div class="device-name">{{ device.deviceName }}</div>
+                  <div class="device-type">{{ device.deviceType }}</div>
+                </div>
+                <div v-if="index < group.devices.length - 1" class="arrow">鈫�</div>
+              </div>
+            </div>
+            
+            <!-- 渚濊禆鍏崇郴 -->
+            <div class="dependencies" v-if="group.dependencies">
+              <h4>璁惧渚濊禆鍏崇郴锛�</h4>
+              <div v-for="(deps, device) in group.dependencies" :key="device" class="dependency-item">
+                {{ device }} 渚濊禆浜�: {{ deps.join(', ') }}
+              </div>
+            </div>
+          </div>
+          
+          <template #footer>
+            <div class="card-footer">
+              <el-button @click="editGroup(group)">缂栬緫</el-button>
+              <el-button @click="startTest(group)">寮�濮嬫祴璇�</el-button>
+              <el-button @click="deleteGroup(group)">鍒犻櫎</el-button>
+            </div>
+          </template>
+        </el-card>
+      </div>
+    </div>
+    
+    <!-- 璁惧缁勯厤缃璇濇 -->
+    <el-dialog v-model="showGroupDialog" title="璁惧缁勯厤缃�" width="800px">
+      <el-form :model="groupForm" label-width="120px">
+        <el-form-item label="璁惧缁勫悕绉�">
+          <el-input v-model="groupForm.groupName" />
+        </el-form-item>
+        <el-form-item label="鍏宠仈椤圭洰">
+          <el-select v-model="groupForm.projectId">
+            <el-option v-for="project in projects" :key="project.id" 
+                      :label="project.projectName" :value="project.id" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="鎵ц妯″紡">
+          <el-radio-group v-model="groupForm.executionMode">
+            <el-radio label="SERIAL">涓茶鎵ц</el-radio>
+            <el-radio label="PARALLEL">骞惰鎵ц</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        
+        <!-- 璁惧閫夋嫨 -->
+        <el-form-item label="鍖呭惈璁惧">
+          <el-transfer 
+            v-model="selectedDevices"
+            :data="availableDevices"
+            :titles="['鍙敤璁惧', '宸查�夎澶�']"
+            @change="updateDeviceOrder" />
+        </el-form-item>
+        
+        <!-- 鎵ц椤哄簭璋冩暣 -->
+        <el-form-item label="鎵ц椤哄簭" v-if="groupForm.executionMode === 'SERIAL'">
+          <div class="execution-order">
+            <div v-for="(deviceId, index) in deviceOrder" :key="deviceId" class="order-item">
+              <span>{{ index + 1 }}. {{ getDeviceName(deviceId) }}</span>
+              <el-button-group>
+                <el-button @click="moveUp(index)" :disabled="index === 0">鈫�</el-button>
+                <el-button @click="moveDown(index)" :disabled="index === deviceOrder.length - 1">鈫�</el-button>
+              </el-button-group>
+            </div>
+          </div>
+        </el-form-item>
+      </el-form>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+export default {
+  data() {
+    return {
+      deviceGroups: [],
+      availableDevices: [],
+      selectedDevices: [],
+      deviceOrder: [],
+      showGroupDialog: false,
+      groupForm: {
+        groupId: '',
+        groupName: '',
+        projectId: '',
+        executionMode: 'SERIAL'
+      }
+    }
+  },
+  methods: {
+    async loadDeviceGroups() {
+      const response = await this.$api.deviceGroup.getGroupList();
+      this.deviceGroups = response.data;
+    },
+    
+    async startTest(group) {
+      try {
+        // 璺宠浆鍒版祴璇曟墽琛岄〉闈�
+        this.$router.push({
+          name: 'MultiDeviceTest',
+          query: { groupId: group.groupId }
+        });
+      } catch (error) {
+        this.$message.error('鍚姩娴嬭瘯澶辫触: ' + error.message);
+      }
+    }
+  }
+}
+</script>
+```
+
+### 4.3 澶氳澶囨祴璇曟墽琛岀晫闈�
+
+```vue
+<template>
+  <div class="multi-device-test">
+    <!-- 娴嬭瘯閰嶇疆 -->
+    <div class="test-config">
+      <el-card>
+        <template #header>
+          <h3>娴嬭瘯閰嶇疆 - {{ groupInfo.groupName }}</h3>
+        </template>
+        
+        <div class="config-content">
+          <div class="execution-mode">
+            <el-tag :type="groupInfo.executionMode === 'SERIAL' ? 'primary' : 'success'">
+              {{ groupInfo.executionMode === 'SERIAL' ? '涓茶鎵ц' : '骞惰鎵ц' }}
+            </el-tag>
+          </div>
+          
+          <div class="parameters">
+            <h4>娴嬭瘯鍙傛暟锛�</h4>
+            <el-form label-width="150px">
+              <el-form-item label="杞﹁締瑙勬牸">
+                <el-input-number v-model="testParams.vehicleLength" :min="1000" :max="12000" /> mm
+              </el-form-item>
+              <el-form-item label="娴嬭瘯鐜荤拑鏁伴噺">
+                <el-input-number v-model="testParams.glassCount" :min="1" :max="50" />
+              </el-form-item>
+              <el-form-item label="鎵ц闂撮殧">
+                <el-input-number v-model="testParams.executionInterval" :min="500" :max="10000" /> ms
+              </el-form-item>
+            </el-form>
+          </div>
+          
+          <div class="test-controls">
+            <el-button type="primary" size="large" @click="startTest" :loading="isRunning">
+              {{ isRunning ? '娴嬭瘯鎵ц涓�...' : '寮�濮嬫祴璇�' }}
+            </el-button>
+            <el-button @click="stopTest" :disabled="!isRunning">鍋滄娴嬭瘯</el-button>
+            <el-button @click="pauseTest" :disabled="!isRunning">鏆傚仠娴嬭瘯</el-button>
+          </div>
+        </div>
+      </el-card>
+    </div>
+    
+    <!-- 鎵ц鐩戞帶 -->
+    <div class="execution-monitor" v-if="isRunning || currentTask">
+      <el-card>
+        <template #header>
+          <h3>鎵ц鐩戞帶</h3>
+        </template>
+        
+        <div class="monitor-content">
+          <!-- 浠诲姟杩涘害 -->
+          <div class="task-progress">
+            <el-progress 
+              :percentage="getOverallProgress()" 
+              :status="getTaskStatus()" />
+            <div class="progress-info">
+              姝ラ {{ currentStep }} / {{ totalSteps }} - {{ getCurrentStepName() }}
+            </div>
+          </div>
+          
+          <!-- 璁惧鐘舵�� -->
+          <div class="device-status-grid">
+            <div v-for="device in groupInfo.devices" :key="device.deviceId" 
+                 class="device-status-card">
+              <div class="device-header">
+                <span class="device-name">{{ device.deviceName }}</span>
+                <el-tag :type="getDeviceStatusType(device.deviceId)">
+                  {{ getDeviceStatus(device.deviceId) }}
+                </el-tag>
+              </div>
+              
+              <div class="device-details" v-if="getDeviceDetails(device.deviceId)">
+                <div class="detail-item">
+                  <span>褰撳墠鐜荤拑:</span> {{ getDeviceDetails(device.deviceId).currentGlass || '-' }}
+                </div>
+                <div class="detail-item">
+                  <span>澶勭悊鏁伴噺:</span> {{ getDeviceDetails(device.deviceId).processedCount || 0 }}
+                </div>
+                <div class="detail-item">
+                  <span>鍓╀綑瀹归噺:</span> {{ getDeviceDetails(device.deviceId).remainingCapacity || '-' }} mm
+                </div>
+              </div>
+              
+              <!-- 瀹炴椂鏃ュ織 -->
+              <div class="device-logs">
+                <div v-for="log in getDeviceLogs(device.deviceId)" :key="log.timestamp" 
+                     class="log-item" :class="log.level">
+                  <span class="log-time">{{ formatTime(log.timestamp) }}</span>
+                  <span class="log-message">{{ log.message }}</span>
+                </div>
+              </div>
+            </div>
+          </div>
+          
+          <!-- 鏁版嵁娴佸彲瑙嗗寲 -->
+          <div class="data-flow">
+            <h4>鏁版嵁娴佺姸鎬侊細</h4>
+            <div class="flow-diagram">
+              <div v-for="(device, index) in groupInfo.devices" :key="device.deviceId" class="flow-node">
+                <div class="node" :class="['device-' + device.deviceType.toLowerCase(), getNodeStatus(device.deviceId)]">
+                  {{ device.deviceName }}
+                </div>
+                <div class="data-indicator" v-if="index < groupInfo.devices.length - 1">
+                  <div class="data-flow-arrow" :class="getFlowStatus(device.deviceId, index + 1)">
+                    {{ getFlowData(device.deviceId) }}
+                  </div>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+      </el-card>
+    </div>
+    
+    <!-- 缁撴灉鍒嗘瀽 -->
+    <div class="test-results" v-if="testResults">
+      <el-card>
+        <template #header>
+          <h3>娴嬭瘯缁撴灉</h3>
+        </template>
+        
+        <div class="results-content">
+          <!-- 鎬讳綋缁撴灉 -->
+          <div class="overall-result">
+            <el-result 
+              :icon="testResults.success ? 'success' : 'error'" 
+              :title="testResults.success ? '娴嬭瘯鎴愬姛' : '娴嬭瘯澶辫触'"
+              :sub-title="testResults.message">
+              <template #extra>
+                <div class="result-stats">
+                  <div class="stat-item">
+                    <span class="label">鎵ц鏃堕棿:</span>
+                    <span class="value">{{ formatDuration(testResults.duration) }}</span>
+                  </div>
+                  <div class="stat-item">
+                    <span class="label">澶勭悊鐜荤拑:</span>
+                    <span class="value">{{ testResults.processedGlassCount }} 鐗�</span>
+                  </div>
+                  <div class="stat-item">
+                    <span class="label">鎴愬姛鐜�:</span>
+                    <span class="value">{{ testResults.successRate }}%</span>
+                  </div>
+                </div>
+              </template>
+            </el-result>
+          </div>
+          
+          <!-- 璇︾粏缁撴灉 -->
+          <div class="detailed-results">
+            <h4>鍚勮澶囨墽琛岃鎯咃細</h4>
+            <el-collapse>
+              <el-collapse-item v-for="(result, deviceName) in testResults.deviceResults" 
+                               :key="deviceName" :title="deviceName">
+                <div class="device-result-detail">
+                  <div class="result-summary">
+                    <el-tag :type="result.success ? 'success' : 'error'">
+                      {{ result.success ? '鎵ц鎴愬姛' : '鎵ц澶辫触' }}
+                    </el-tag>
+                    <span class="duration">鑰楁椂: {{ formatDuration(result.duration) }}</span>
+                  </div>
+                  
+                  <div class="result-data">
+                    <h5>杈撳嚭鏁版嵁锛�</h5>
+                    <pre>{{ JSON.stringify(result.outputData, null, 2) }}</pre>
+                  </div>
+                  
+                  <div class="result-logs">
+                    <h5>鎵ц鏃ュ織锛�</h5>
+                    <div class="log-list">
+                      <div v-for="log in result.logs" :key="log.timestamp" class="log-line">
+                        <span class="log-time">{{ formatTime(log.timestamp) }}</span>
+                        <span class="log-level">{{ log.level }}</span>
+                        <span class="log-message">{{ log.message }}</span>
+                      </div>
+                    </div>
+                  </div>
+                </div>
+              </el-collapse-item>
+            </el-collapse>
+          </div>
+          
+          <!-- 瀵煎嚭缁撴灉 -->
+          <div class="export-results">
+            <el-button @click="exportResults('json')">瀵煎嚭JSON</el-button>
+            <el-button @click="exportResults('excel')">瀵煎嚭Excel</el-button>
+            <el-button @click="exportResults('pdf')">瀵煎嚭PDF鎶ュ憡</el-button>
+          </div>
+        </div>
+      </el-card>
+    </div>
+  </div>
+</template>
+```
+
+## 馃殌 瀹炴柦璁″垝
+
+### 绗竴闃舵锛氬熀纭�鏋舵瀯鎼缓锛�1-2鍛級
+
+1. **鏁版嵁搴撹〃鍒涘缓**
+   - 鍒涘缓璁惧绠$悊鐩稿叧琛�
+   - 寤虹珛琛ㄥ叧绯诲拰绱㈠紩
+   - 杩佺Щ鐜版湁鏁版嵁锛堝闇�瑕侊級
+
+2. **鍚庣鍩虹缁勪欢**
+   - 璁惧閰嶇疆瀹炰綋鍜岀鐞嗘湇鍔�
+   - 璁惧缁勭鐞嗙粍浠�
+   - 鍩虹浜や簰鎺ュ彛瀹氫箟
+
+3. **鍓嶇鍩虹鐣岄潰**
+   - 璁惧绠$悊椤甸潰
+   - 璁惧缁勯厤缃〉闈�
+   - 鍩虹缁勪欢寮�鍙�
+
+### 绗簩闃舵锛氭牳蹇冧氦浜掗�昏緫锛�2-3鍛級
+
+1. **璁惧浜や簰瀹炵幇**
+   - 涓婂ぇ杞︿氦浜掗�昏緫
+   - 澶х悊鐗囦氦浜掗�昏緫
+   - 鐜荤拑瀛樺偍浜や簰閫昏緫
+
+2. **浠诲姟鎵ц寮曟搸**
+   - 涓茶鎵ц寮曟搸
+   - 澶氳澶囧崗璋冩満鍒�
+   - 閿欒澶勭悊鍜岄噸璇曢�昏緫
+
+3. **PLC鍦板潃鏄犲皠鎵╁睍**
+   - 澶氳澶囧湴鍧�鏄犲皠鏀寔
+   - 鍦板潃閰嶇疆绠$悊
+   - PLC閫氫俊閫傞厤
+
+### 绗笁闃舵锛氬墠绔畬鍠勶紙1-2鍛級
+
+1. **娴嬭瘯鎵ц鐣岄潰**
+   - 澶氳澶囨祴璇曠紪鎺�
+   - 瀹炴椂鐩戞帶闈㈡澘
+   - 缁撴灉鍒嗘瀽灞曠ず
+
+2. **鐢ㄦ埛浜や簰浼樺寲**
+   - 閰嶇疆鍚戝
+   - 鍙鍖栬澶囨嫇鎵�
+   - 瀹炴椂鏁版嵁娴佸睍绀�
+
+### 绗洓闃舵锛氶泦鎴愭祴璇曪紙1鍛級
+
+1. **鍔熻兘娴嬭瘯**
+   - 澶氳澶囪仈鍚堟祴璇曟祦绋�
+   - 寮傚父鎯呭喌澶勭悊
+   - 鎬ц兘娴嬭瘯
+
+2. **绯荤粺闆嗘垚**
+   - 涓庣幇鏈夌郴缁熼泦鎴�
+   - 鏁版嵁杩佺Щ楠岃瘉
+   - 鐢ㄦ埛楠屾敹娴嬭瘯
+
+## 馃搳 鎶�鏈鐐规�荤粨
+
+### 鏍稿績浼樺娍
+1. **妯″潡鍖栬璁�**锛氭瘡涓澶囩被鍨嬬嫭绔嬪疄鐜帮紝渚夸簬鎵╁睍
+2. **閰嶇疆椹卞姩**锛氭墍鏈夊弬鏁板彲閰嶇疆锛屾敮鎸佷笉鍚屼笟鍔″満鏅�
+3. **鏁版嵁娴佺鐞�**锛氳澶囬棿鏁版嵁浼犻�掑拰鐘舵�佸悓姝�
+4. **鍙鍖栫洃鎺�**锛氬疄鏃舵樉绀鸿澶囩姸鎬佸拰鏁版嵁娴�
+5. **鐏垫椿鎵╁睍**锛氭敮鎸佹柊澧炶澶囩被鍨嬪拰浜や簰閫昏緫
+
+### 鎶�鏈毦鐐�
+1. **璁惧鍗忚皟**锛氱‘淇濆璁惧鎵ц鐨勬纭『搴�
+2. **鏁版嵁鍚屾**锛氳澶囬棿鏁版嵁鐨勫噯纭紶閫�
+3. **寮傚父澶勭悊**锛氬崟涓澶囧け璐ュ鏁翠釜娴佺▼鐨勫奖鍝�
+4. **鎬ц兘浼樺寲**锛氬ぇ鎵归噺鏁版嵁鐨勫鐞嗘�ц兘
+
+### 椋庨櫓鎺у埗
+1. **鍚戝悗鍏煎**锛氫笉鐮村潖鐜版湁鍗曡澶囧姛鑳�
+2. **娓愯繘寮忓崌绾�**锛氬垎闃舵瀹炴柦锛岄檷浣庨闄�
+3. **鍏呭垎娴嬭瘯**锛氭瘡涓樁娈电殑鍏ㄩ潰娴嬭瘯楠岃瘉
+4. **鍥炴粴鏈哄埗**锛氬嚭鐜伴棶棰樻椂鐨勫揩閫熷洖婊氭柟妗�
+
+---
+
+## 馃摑 缁撹
+
+閫氳繃杩欎釜鎵╁睍鏂规锛孧ES Test Project灏嗗叿澶囧畬鏁寸殑澶氳澶囪仈鍚堟祴璇曡兘鍔涳紝鏀寔澶嶆潅鐨勭敓浜ф祦绋嬭嚜鍔ㄥ寲娴嬭瘯銆傛柟妗堝熀浜庣幇鏈夋灦鏋勮璁★紝椋庨櫓鍙帶锛屽疄鏂介毦搴﹂�備腑锛岃兘澶熷緢濂藉湴婊¤冻涓氬姟闇�姹傘��
+
+寤鸿浼樺厛瀹炵幇绗竴闃舵鐨勫熀纭�鏋舵瀯锛岀劧鍚庨�愭瀹屽杽浜や簰閫昏緫鍜屽墠绔晫闈紝纭繚姣忎釜闃舵閮借兘浜や粯鍙敤鐨勫姛鑳姐��
\ No newline at end of file
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/BaseDeviceLogicHandler.java b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/BaseDeviceLogicHandler.java
new file mode 100644
index 0000000..92b9093
--- /dev/null
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/BaseDeviceLogicHandler.java
@@ -0,0 +1,124 @@
+package com.mes.interaction;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.mes.device.entity.DeviceConfig;
+import com.mes.device.service.DevicePlcOperationService;
+import com.mes.device.vo.DevicePlcVO;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 璁惧閫昏緫澶勭悊鍣ㄥ熀绫�
+ * 鎻愪緵閫氱敤鐨勫姛鑳藉疄鐜�
+ * 
+ * @author mes
+ * @since 2025-01-XX
+ */
+@Slf4j
+@RequiredArgsConstructor
+public abstract class BaseDeviceLogicHandler implements DeviceLogicHandler {
+
+    protected final DevicePlcOperationService devicePlcOperationService;
+    protected final ObjectMapper objectMapper = new ObjectMapper();
+
+    @Override
+    public DevicePlcVO.OperationResult execute(DeviceConfig deviceConfig, String operation, Map<String, Object> params) {
+        try {
+            // 楠岃瘉璁惧閰嶇疆
+            String validationError = validateLogicParams(deviceConfig);
+            if (validationError != null) {
+                return DevicePlcVO.OperationResult.builder()
+                        .success(false)
+                        .message("璁惧閫昏緫鍙傛暟楠岃瘉澶辫触: " + validationError)
+                        .build();
+            }
+
+            // 瑙f瀽璁惧閫昏緫鍙傛暟锛堜粠 extraParams.deviceLogic 涓鍙栵級
+            Map<String, Object> logicParams = parseLogicParams(deviceConfig);
+            
+            // 鎵ц鍏蜂綋鎿嶄綔锛堢敱瀛愮被瀹炵幇锛�
+            return doExecute(deviceConfig, operation, params, logicParams);
+        } catch (Exception e) {
+            log.error("鎵ц璁惧閫昏緫鎿嶄綔澶辫触, deviceId={}, operation={}", deviceConfig.getId(), operation, e);
+            return DevicePlcVO.OperationResult.builder()
+                    .success(false)
+                    .message("鎵ц澶辫触: " + e.getMessage())
+                    .build();
+        }
+    }
+
+    /**
+     * 瀛愮被瀹炵幇鍏蜂綋鐨勬搷浣滈�昏緫
+     * 
+     * @param deviceConfig 璁惧閰嶇疆
+     * @param operation 鎿嶄綔绫诲瀷
+     * @param params 杩愯鏃跺弬鏁帮紙鍔ㄦ�佷紶鍏ワ級
+     * @param logicParams 閫昏緫閰嶇疆鍙傛暟锛堜粠 extraParams.deviceLogic 瑙f瀽锛�
+     * @return 鎿嶄綔缁撴灉
+     */
+    protected abstract DevicePlcVO.OperationResult doExecute(
+            DeviceConfig deviceConfig, 
+            String operation, 
+            Map<String, Object> params,
+            Map<String, Object> logicParams
+    );
+
+    /**
+     * 瑙f瀽璁惧閫昏緫鍙傛暟锛堜粠 extraParams 涓彁鍙� deviceLogic锛�
+     */
+    protected Map<String, Object> parseLogicParams(DeviceConfig deviceConfig) {
+        String extraParams = deviceConfig.getExtraParams();
+        if (extraParams == null || extraParams.trim().isEmpty()) {
+            return new HashMap<>();
+        }
+
+        try {
+            Map<String, Object> extra = objectMapper.readValue(extraParams, new TypeReference<Map<String, Object>>() {});
+            @SuppressWarnings("unchecked")
+            Map<String, Object> deviceLogic = (Map<String, Object>) extra.get("deviceLogic");
+            return deviceLogic != null ? deviceLogic : new HashMap<>();
+        } catch (Exception e) {
+            log.warn("瑙f瀽璁惧閫昏緫鍙傛暟澶辫触, deviceId={}", deviceConfig.getId(), e);
+            return new HashMap<>();
+        }
+    }
+
+    /**
+     * 鑾峰彇閫昏緫鍙傛暟涓殑鍊硷紙甯﹂粯璁ゅ�硷級
+     */
+    @SuppressWarnings("unchecked")
+    protected <T> T getLogicParam(Map<String, Object> logicParams, String key, T defaultValue) {
+        Object value = logicParams.get(key);
+        if (value == null) {
+            return defaultValue;
+        }
+        try {
+            return (T) value;
+        } catch (ClassCastException e) {
+            log.warn("閫昏緫鍙傛暟绫诲瀷杞崲澶辫触, key={}, value={}", key, value, e);
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 鑾峰彇閫昏緫鍙傛暟涓殑鍊硷紙涓嶅甫榛樿鍊硷級
+     */
+    @SuppressWarnings("unchecked")
+    protected <T> T getLogicParam(Map<String, Object> logicParams, String key) {
+        Object value = logicParams.get(key);
+        if (value == null) {
+            return null;
+        }
+        try {
+            return (T) value;
+        } catch (ClassCastException e) {
+            log.warn("閫昏緫鍙傛暟绫诲瀷杞崲澶辫触, key={}, value={}", key, value, e);
+            return null;
+        }
+    }
+}
+
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/DeviceInteraction.java b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/DeviceInteraction.java
new file mode 100644
index 0000000..1dd6dc0
--- /dev/null
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/DeviceInteraction.java
@@ -0,0 +1,28 @@
+package com.mes.interaction;
+
+import com.mes.interaction.base.InteractionContext;
+import com.mes.interaction.base.InteractionResult;
+
+/**
+ * 澶氳澶囦氦浜掓帴鍙�
+ */
+public interface DeviceInteraction {
+
+    /**
+     * 浜や簰瀵瑰簲鐨勮澶囩被鍨�
+     */
+    String getDeviceType();
+
+    /**
+     * 鎵ц浜や簰閫昏緫
+     */
+    InteractionResult execute(InteractionContext context);
+
+    /**
+     * 鏄惁鏀寔鎸囧畾鎿嶄綔
+     */
+    default boolean supportsOperation(String operation) {
+        return true;
+    }
+}
+
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
new file mode 100644
index 0000000..fc4cc8c
--- /dev/null
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/DeviceInteractionRegistry.java
@@ -0,0 +1,42 @@
+package com.mes.interaction;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 浜や簰娉ㄥ唽涓績
+ */
+@Slf4j
+@Component
+public class DeviceInteractionRegistry {
+
+    private final Map<String, DeviceInteraction> interactionMap = new HashMap<>();
+
+    public DeviceInteractionRegistry(List<DeviceInteraction> interactions) {
+        if (interactions != null) {
+            for (DeviceInteraction interaction : interactions) {
+                if (interaction.getDeviceType() != null) {
+                    interactionMap.put(interaction.getDeviceType(), interaction);
+                    log.info("娉ㄥ唽璁惧浜や簰: {}", interaction.getDeviceType());
+                }
+            }
+        }
+    }
+
+    public DeviceInteraction getInteraction(String deviceType) {
+        if (deviceType == null) {
+            return null;
+        }
+        return interactionMap.get(deviceType);
+    }
+
+    public Map<String, DeviceInteraction> getInteractions() {
+        return Collections.unmodifiableMap(interactionMap);
+    }
+}
+
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
new file mode 100644
index 0000000..9566222
--- /dev/null
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/DeviceLogicHandler.java
@@ -0,0 +1,49 @@
+package com.mes.interaction;
+
+import com.mes.device.entity.DeviceConfig;
+import com.mes.device.vo.DevicePlcVO;
+
+import java.util.Map;
+
+/**
+ * 璁惧閫昏緫澶勭悊鍣ㄦ帴鍙�
+ * 涓嶅悓璁惧绫诲瀷瀹炵幇姝ゆ帴鍙f潵澶勭悊鍚勮嚜鐨勪笟鍔¢�昏緫
+ * 
+ * @author mes
+ * @since 2025-01-XX
+ */
+public interface DeviceLogicHandler {
+
+    /**
+     * 鑾峰彇璁惧绫诲瀷锛堢敤浜庡尮閰嶅鐞嗗櫒锛�
+     * 
+     * @return 璁惧绫诲瀷锛屽锛�"涓婂ぇ杞�"銆�"澶х悊鐗�"銆�"鐜荤拑瀛樺偍"
+     */
+    String getDeviceType();
+
+    /**
+     * 鎵ц璁惧閫昏緫鎿嶄綔
+     * 
+     * @param deviceConfig 璁惧閰嶇疆淇℃伅
+     * @param operation 鎿嶄綔绫诲瀷锛堝锛歠eedGlass, triggerRequest, triggerReport绛夛級
+     * @param params 鎿嶄綔鍙傛暟锛堣繍琛屾椂浼犲叆鐨勫姩鎬佸弬鏁帮級
+     * @return 鎿嶄綔缁撴灉
+     */
+    DevicePlcVO.OperationResult execute(DeviceConfig deviceConfig, String operation, Map<String, Object> params);
+
+    /**
+     * 楠岃瘉璁惧閫昏緫鍙傛暟閰嶇疆鏄惁鏈夋晥
+     * 
+     * @param deviceConfig 璁惧閰嶇疆
+     * @return 楠岃瘉缁撴灉锛宯ull琛ㄧず楠岃瘉閫氳繃锛屽惁鍒欒繑鍥為敊璇俊鎭�
+     */
+    String validateLogicParams(DeviceConfig deviceConfig);
+
+    /**
+     * 鑾峰彇璁惧閫昏緫鍙傛暟鐨勯粯璁ら厤缃�
+     * 
+     * @return 榛樿閰嶇疆鐨凧SON瀛楃涓�
+     */
+    String getDefaultLogicParams();
+}
+
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/DeviceLogicHandlerFactory.java b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/DeviceLogicHandlerFactory.java
new file mode 100644
index 0000000..f5eb175
--- /dev/null
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/DeviceLogicHandlerFactory.java
@@ -0,0 +1,77 @@
+package com.mes.interaction;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.PostConstruct;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 璁惧閫昏緫澶勭悊鍣ㄥ伐鍘傜被
+ * 鏍规嵁璁惧绫诲瀷鑾峰彇瀵瑰簲鐨勫鐞嗗櫒
+ * 
+ * @author mes
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Component
+public class DeviceLogicHandlerFactory {
+
+    @Autowired
+    private List<DeviceLogicHandler> handlers;
+
+    private final Map<String, DeviceLogicHandler> handlerMap = new HashMap<>();
+
+    /**
+     * 鍒濆鍖栧鐞嗗櫒鏄犲皠
+     */
+    @PostConstruct
+    public void init() {
+        if (handlers != null) {
+            for (DeviceLogicHandler handler : handlers) {
+                String deviceType = handler.getDeviceType();
+                if (deviceType != null && !deviceType.isEmpty()) {
+                    handlerMap.put(deviceType, handler);
+                    log.info("娉ㄥ唽璁惧閫昏緫澶勭悊鍣�: {} -> {}", deviceType, handler.getClass().getSimpleName());
+                }
+            }
+        }
+        log.info("璁惧閫昏緫澶勭悊鍣ㄥ垵濮嬪寲瀹屾垚锛屽叡娉ㄥ唽 {} 涓鐞嗗櫒", handlerMap.size());
+    }
+
+    /**
+     * 鏍规嵁璁惧绫诲瀷鑾峰彇瀵瑰簲鐨勫鐞嗗櫒
+     * 
+     * @param deviceType 璁惧绫诲瀷
+     * @return 璁惧閫昏緫澶勭悊鍣紝濡傛灉鏈壘鍒拌繑鍥瀗ull
+     */
+    public DeviceLogicHandler getHandler(String deviceType) {
+        if (deviceType == null || deviceType.isEmpty()) {
+            return null;
+        }
+        return handlerMap.get(deviceType);
+    }
+
+    /**
+     * 妫�鏌ユ槸鍚︽敮鎸佹寚瀹氱殑璁惧绫诲瀷
+     * 
+     * @param deviceType 璁惧绫诲瀷
+     * @return true琛ㄧず鏀寔锛宖alse琛ㄧず涓嶆敮鎸�
+     */
+    public boolean supports(String deviceType) {
+        return deviceType != null && handlerMap.containsKey(deviceType);
+    }
+
+    /**
+     * 鑾峰彇鎵�鏈夊凡娉ㄥ唽鐨勮澶囩被鍨�
+     * 
+     * @return 璁惧绫诲瀷闆嗗悎
+     */
+    public java.util.Set<String> getSupportedDeviceTypes() {
+        return handlerMap.keySet();
+    }
+}
+
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/README.md b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/README.md
new file mode 100644
index 0000000..ede551b
--- /dev/null
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/README.md
@@ -0,0 +1,168 @@
+# 璁惧閫昏緫澶勭悊鍣ㄦ灦鏋勮鏄�
+
+## 鏋舵瀯姒傝堪
+
+閲囩敤**绛栫暐妯″紡 + 宸ュ巶妯″紡**瀹炵幇璁惧閫昏緫澶勭悊锛屾敮鎸佷笉鍚岃澶囩被鍨嬬殑宸紓鍖栭�昏緫澶勭悊銆�
+
+## 鐩綍缁撴瀯
+
+```
+com.mes.interaction/
+鈹溾攢鈹� DeviceLogicHandler.java              # 澶勭悊鍣ㄦ帴鍙�
+鈹溾攢鈹� BaseDeviceLogicHandler.java          # 鍩虹鎶借薄绫�
+鈹溾攢鈹� DeviceLogicHandlerFactory.java       # 宸ュ巶绫�
+鈹斺攢鈹� impl/
+    鈹溾攢鈹� LoadVehicleLogicHandler.java     # 涓婂ぇ杞﹂�昏緫澶勭悊鍣�
+    鈹溾攢鈹� LargeGlassLogicHandler.java     # 澶х悊鐗囬�昏緫澶勭悊鍣�
+    鈹斺攢鈹� GlassStorageLogicHandler.java   # 鐜荤拑瀛樺偍閫昏緫澶勭悊鍣�
+```
+
+## 鏍稿績姒傚康
+
+### 1. 鍙傛暟閰嶇疆锛坋xtraParams.deviceLogic锛�
+
+璁惧閫昏緫鍙傛暟瀛樺偍鍦� `DeviceConfig.extraParams` 鐨� `deviceLogic` 鑺傜偣涓細
+
+```json
+{
+  "connectionConfig": { ... },
+  "plcConfig": { ... },
+  "deviceLogic": {
+    "vehicleCapacity": 6000,
+    "glassIntervalMs": 1000,
+    "autoFeed": true,
+    "positionMapping": {
+      "POS1": 1,
+      "POS2": 2
+    }
+  }
+}
+```
+
+### 2. 鎵ц娴佺▼
+
+```
+鐢ㄦ埛璋冪敤鎺ュ彛
+    鈫�
+Service 灞傝幏鍙栬澶囬厤缃紙鍖呭惈 extraParams锛�
+    鈫�
+Factory 鏍规嵁 deviceType 閫夋嫨瀵瑰簲鐨� Handler
+    鈫�
+Handler 浠� extraParams.deviceLogic 璇诲彇閰嶇疆鍙傛暟
+    鈫�
+Handler 鏍规嵁閰嶇疆鍙傛暟鍜岃繍琛屾椂鍙傛暟鎵ц閫昏緫
+    鈫�
+璋冪敤 DevicePlcOperationService.writeFields() 鍐欏叆PLC
+```
+
+## 浣跨敤鏂瑰紡
+
+### 鏂瑰紡1锛氶�氳繃 DeviceInteractionService锛堟帹鑽愶級
+
+```java
+@Autowired
+private DeviceInteractionService deviceInteractionService;
+
+// 鎵ц鎿嶄綔
+Map<String, Object> params = new HashMap<>();
+params.put("glassIds", Arrays.asList("GLS001", "GLS002"));
+params.put("positionCode", "POS1");
+
+OperationResult result = deviceInteractionService.executeOperation(
+    deviceId, 
+    "feedGlass", 
+    params
+);
+```
+
+### 鏂瑰紡2锛氱洿鎺ヤ娇鐢� Handler
+
+```java
+@Autowired
+private DeviceLogicHandlerFactory handlerFactory;
+@Autowired
+private DeviceConfigService deviceConfigService;
+
+// 鑾峰彇璁惧鍜屽鐞嗗櫒
+DeviceConfig device = deviceConfigService.getDeviceById(deviceId);
+DeviceLogicHandler handler = handlerFactory.getHandler(device.getDeviceType());
+
+// 鎵ц鎿嶄綔
+Map<String, Object> params = new HashMap<>();
+params.put("glassIds", Arrays.asList("GLS001"));
+OperationResult result = handler.execute(device, "feedGlass", params);
+```
+
+## 鏀寔鐨勬搷浣滅被鍨�
+
+### 涓婂ぇ杞︼紙LoadVehicleLogicHandler锛�
+- `feedGlass` - 鐜荤拑涓婃枡
+- `triggerRequest` - 瑙﹀彂璇锋眰
+- `triggerReport` - 瑙﹀彂姹囨姤
+- `reset` - 閲嶇疆
+
+### 澶х悊鐗囷紙LargeGlassLogicHandler锛�
+- `processGlass` - 鐜荤拑鍔犲伐
+- `triggerRequest` - 瑙﹀彂璇锋眰
+- `triggerReport` - 瑙﹀彂姹囨姤
+- `reset` - 閲嶇疆
+
+### 鐜荤拑瀛樺偍锛圙lassStorageLogicHandler锛�
+- `storeGlass` - 瀛樺偍鐜荤拑
+- `retrieveGlass` - 鍙栬揣
+- `triggerRequest` - 瑙﹀彂璇锋眰
+- `triggerReport` - 瑙﹀彂姹囨姤
+- `reset` - 閲嶇疆
+
+## 鎵╁睍鏂拌澶囩被鍨�
+
+### 姝ラ1锛氬垱寤哄鐞嗗櫒绫�
+
+```java
+@Component
+public class NewDeviceLogicHandler extends BaseDeviceLogicHandler {
+    
+    public NewDeviceLogicHandler(DevicePlcOperationService devicePlcOperationService) {
+        super(devicePlcOperationService);
+    }
+    
+    @Override
+    public String getDeviceType() {
+        return "鏂拌澶囩被鍨�";
+    }
+    
+    @Override
+    protected OperationResult doExecute(...) {
+        // 瀹炵幇鍏蜂綋閫昏緫
+    }
+}
+```
+
+### 姝ラ2锛氬湪 DeviceConfig.DeviceType 涓坊鍔犲父閲�
+
+```java
+public static final String NEW_DEVICE = "鏂拌澶囩被鍨�";
+```
+
+### 姝ラ3锛氶厤缃粯璁ゅ弬鏁�
+
+瀹炵幇 `getDefaultLogicParams()` 鏂规硶锛岃繑鍥為粯璁ょ殑JSON閰嶇疆銆�
+
+## 鍙傛暟璇存槑
+
+### 閰嶇疆鍙傛暟锛堜粠 extraParams.deviceLogic 璇诲彇锛�
+- 闈欐�侀厤缃紝鍦ㄨ澶囬厤缃椂璁剧疆
+- 瀛樺偍鍦ㄦ暟鎹簱涓�
+- 绀轰緥锛歷ehicleCapacity, glassIntervalMs
+
+### 杩愯鏃跺弬鏁帮紙浠庢柟娉曞弬鏁颁紶鍏ワ級
+- 鍔ㄦ�佸弬鏁帮紝姣忔璋冪敤鏃朵紶鍏�
+- 绀轰緥锛歡lassIds, positionCode
+
+## 娉ㄦ剰浜嬮」
+
+1. **鍙傛暟楠岃瘉**锛氭瘡涓� Handler 瀹炵幇 `validateLogicParams()` 鏂规硶杩涜鍙傛暟楠岃瘉
+2. **榛樿鍊煎鐞�**锛氫娇鐢� `getLogicParam()` 鏂规硶鑾峰彇鍙傛暟锛屾敮鎸侀粯璁ゅ��
+3. **閿欒澶勭悊**锛氭墍鏈夊紓甯搁兘浼氳鎹曡幏骞惰繑鍥� `OperationResult`
+4. **鍚戝悗鍏煎**锛氫繚鐣欎簡鍘熸湁鐨� `feedGlass()` 鏂规硶锛屼紭鍏堜娇鐢ㄦ柊鏋舵瀯锛屽け璐ユ椂闄嶇骇鍒版棫閫昏緫
+
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/base/InteractionContext.java b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/base/InteractionContext.java
new file mode 100644
index 0000000..44374df
--- /dev/null
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/base/InteractionContext.java
@@ -0,0 +1,49 @@
+package com.mes.interaction.base;
+
+import com.mes.device.entity.DeviceConfig;
+import com.mes.task.model.TaskExecutionContext;
+import com.mes.task.dto.TaskParameters;
+import lombok.Getter;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 浜や簰涓婁笅鏂�
+ */
+@Getter
+public class InteractionContext {
+
+    private final DeviceConfig currentDevice;
+    private final TaskExecutionContext taskContext;
+
+    public InteractionContext(DeviceConfig currentDevice, TaskExecutionContext taskContext) {
+        this.currentDevice = currentDevice;
+        this.taskContext = taskContext;
+    }
+
+    public TaskParameters getParameters() {
+        return taskContext.getParameters();
+    }
+
+    public Map<String, Object> getSharedData() {
+        return taskContext.getSharedData();
+    }
+
+    public void setLoadedGlassIds(List<String> glassIds) {
+        taskContext.setLoadedGlassIds(glassIds);
+    }
+
+    public void setProcessedGlassIds(List<String> glassIds) {
+        taskContext.setProcessedGlassIds(glassIds);
+    }
+
+    public List<String> getLoadedGlassIds() {
+        return taskContext.getSafeLoadedGlassIds();
+    }
+
+    public List<String> getProcessedGlassIds() {
+        return taskContext.getSafeProcessedGlassIds();
+    }
+}
+
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/base/InteractionResult.java b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/base/InteractionResult.java
new file mode 100644
index 0000000..a3353d0
--- /dev/null
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/base/InteractionResult.java
@@ -0,0 +1,60 @@
+package com.mes.interaction.base;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Getter;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 浜や簰鎵ц缁撴灉
+ */
+@Getter
+@Builder
+@AllArgsConstructor
+public class InteractionResult {
+
+    public enum Status {
+        SUCCESS, WAITING, FAILED
+        }
+
+    private final Status status;
+    private final String message;
+    @Builder.Default
+    private final Map<String, Object> data = new HashMap<>();
+
+    public static InteractionResult success(Map<String, Object> payload) {
+        return InteractionResult.builder()
+                .status(Status.SUCCESS)
+                .message("success")
+                .data(payload != null ? payload : Collections.emptyMap())
+                .build();
+    }
+
+    public static InteractionResult waitResult(String message, Map<String, Object> payload) {
+        return InteractionResult.builder()
+                .status(Status.WAITING)
+                .message(message)
+                .data(payload != null ? payload : Collections.emptyMap())
+                .build();
+    }
+
+    public static InteractionResult fail(String message) {
+        return InteractionResult.builder()
+                .status(Status.FAILED)
+                .message(message)
+                .data(Collections.emptyMap())
+                .build();
+    }
+
+    public boolean isSuccess() {
+        return status == Status.SUCCESS;
+    }
+
+    public boolean isWaiting() {
+        return status == Status.WAITING;
+    }
+}
+
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
new file mode 100644
index 0000000..e4751e7
--- /dev/null
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/flow/GlassStorageInteraction.java
@@ -0,0 +1,38 @@
+package com.mes.interaction.flow;
+
+import com.mes.device.entity.DeviceConfig;
+import com.mes.interaction.DeviceInteraction;
+import com.mes.interaction.base.InteractionContext;
+import com.mes.interaction.base.InteractionResult;
+import org.springframework.stereotype.Component;
+import org.springframework.util.CollectionUtils;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 鐜荤拑瀛樺偍浜や簰瀹炵幇
+ */
+@Component
+public class GlassStorageInteraction implements DeviceInteraction {
+
+    @Override
+    public String getDeviceType() {
+        return DeviceConfig.DeviceType.GLASS_STORAGE;
+    }
+
+    @Override
+    public InteractionResult execute(InteractionContext context) {
+        List<String> processed = context.getProcessedGlassIds();
+        if (CollectionUtils.isEmpty(processed)) {
+            return InteractionResult.waitResult("娌℃湁鍙瓨鍌ㄧ殑鐜荤拑", null);
+        }
+
+        Map<String, Object> data = new HashMap<>();
+        data.put("storedCount", processed.size());
+        data.put("storedGlasses", processed);
+        return InteractionResult.success(data);
+    }
+}
+
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
new file mode 100644
index 0000000..7a24ee6
--- /dev/null
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/flow/LargeGlassInteraction.java
@@ -0,0 +1,52 @@
+package com.mes.interaction.flow;
+
+import com.mes.device.entity.DeviceConfig;
+import com.mes.interaction.DeviceInteraction;
+import com.mes.interaction.base.InteractionContext;
+import com.mes.interaction.base.InteractionResult;
+import org.springframework.stereotype.Component;
+import org.springframework.util.CollectionUtils;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 澶х悊鐗囦氦浜掑疄鐜�
+ */
+@Component
+public class LargeGlassInteraction implements DeviceInteraction {
+
+    @Override
+    public String getDeviceType() {
+        return DeviceConfig.DeviceType.LARGE_GLASS;
+    }
+
+    @Override
+    public InteractionResult execute(InteractionContext context) {
+        Object source = context.getSharedData().get("glassesFromVehicle");
+        List<String> glassQueue = castList(source);
+        if (CollectionUtils.isEmpty(glassQueue)) {
+            return InteractionResult.waitResult("绛夊緟涓婂ぇ杞﹁緭鍑�", null);
+        }
+
+        List<String> processed = new ArrayList<>(glassQueue);
+        context.setProcessedGlassIds(processed);
+        context.getSharedData().put("processedGlasses", processed);
+
+        Map<String, Object> data = new HashMap<>();
+        data.put("processedCount", processed.size());
+        data.put("processedGlasses", processed);
+        return InteractionResult.success(data);
+    }
+
+    @SuppressWarnings("unchecked")
+    private List<String> castList(Object value) {
+        if (value instanceof List) {
+            return (List<String>) value;
+        }
+        return 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
new file mode 100644
index 0000000..675b6ab
--- /dev/null
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/flow/LoadVehicleInteraction.java
@@ -0,0 +1,43 @@
+package com.mes.interaction.flow;
+
+import com.mes.device.entity.DeviceConfig;
+import com.mes.interaction.DeviceInteraction;
+import com.mes.interaction.base.InteractionContext;
+import com.mes.interaction.base.InteractionResult;
+import org.springframework.stereotype.Component;
+import org.springframework.util.CollectionUtils;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 涓婂ぇ杞︿氦浜掑疄鐜�
+ */
+@Component
+public class LoadVehicleInteraction implements DeviceInteraction {
+
+    @Override
+    public String getDeviceType() {
+        return DeviceConfig.DeviceType.LOAD_VEHICLE;
+    }
+
+    @Override
+    public InteractionResult execute(InteractionContext context) {
+        List<String> glassIds = context.getParameters().getGlassIds();
+        if (CollectionUtils.isEmpty(glassIds)) {
+            return InteractionResult.waitResult("鏈彁渚涚幓鐠僆D锛岀瓑寰呰緭鍏�", null);
+        }
+
+        List<String> copied = new ArrayList<>(glassIds);
+        context.setLoadedGlassIds(copied);
+        context.getSharedData().put("glassesFromVehicle", copied);
+
+        Map<String, Object> data = new HashMap<>();
+        data.put("loaded", copied);
+        data.put("glassCount", copied.size());
+        return InteractionResult.success(data);
+    }
+}
+
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/impl/GlassStorageLogicHandler.java b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/impl/GlassStorageLogicHandler.java
new file mode 100644
index 0000000..6776cf5
--- /dev/null
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/impl/GlassStorageLogicHandler.java
@@ -0,0 +1,241 @@
+package com.mes.interaction.impl;
+
+import com.mes.device.entity.DeviceConfig;
+import com.mes.interaction.BaseDeviceLogicHandler;
+import com.mes.device.service.DevicePlcOperationService;
+import com.mes.device.vo.DevicePlcVO;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 鐜荤拑瀛樺偍璁惧閫昏緫澶勭悊鍣�
+ * 
+ * @author mes
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Component
+public class GlassStorageLogicHandler extends BaseDeviceLogicHandler {
+
+    public GlassStorageLogicHandler(DevicePlcOperationService devicePlcOperationService) {
+        super(devicePlcOperationService);
+    }
+
+    @Override
+    public String getDeviceType() {
+        return DeviceConfig.DeviceType.GLASS_STORAGE;
+    }
+
+    @Override
+    protected DevicePlcVO.OperationResult doExecute(
+            DeviceConfig deviceConfig,
+            String operation,
+            Map<String, Object> params,
+            Map<String, Object> logicParams) {
+
+        log.info("鎵ц鐜荤拑瀛樺偍璁惧鎿嶄綔: deviceId={}, operation={}", deviceConfig.getId(), operation);
+
+        switch (operation) {
+            case "storeGlass":
+                return handleStoreGlass(deviceConfig, params, logicParams);
+            case "retrieveGlass":
+                return handleRetrieveGlass(deviceConfig, params, logicParams);
+            case "triggerRequest":
+                return handleTriggerRequest(deviceConfig, params, logicParams);
+            case "triggerReport":
+                return handleTriggerReport(deviceConfig, params, logicParams);
+            case "reset":
+                return handleReset(deviceConfig, params, logicParams);
+            default:
+                log.warn("涓嶆敮鎸佺殑鎿嶄綔绫诲瀷: {}", operation);
+                return DevicePlcVO.OperationResult.builder()
+                        .success(false)
+                        .message("涓嶆敮鎸佺殑鎿嶄綔: " + operation)
+                        .build();
+        }
+    }
+
+    /**
+     * 澶勭悊瀛樺偍鐜荤拑鎿嶄綔
+     */
+    private DevicePlcVO.OperationResult handleStoreGlass(
+            DeviceConfig deviceConfig,
+            Map<String, Object> params,
+            Map<String, Object> logicParams) {
+
+        // 浠庨�昏緫鍙傛暟涓幏鍙栭厤缃�
+        Integer storageCapacity = getLogicParam(logicParams, "storageCapacity", 100);
+        String retrievalMode = getLogicParam(logicParams, "retrievalMode", "FIFO");
+        Boolean autoStore = getLogicParam(logicParams, "autoStore", true);
+
+        // 浠庤繍琛屾椂鍙傛暟涓幏鍙栨暟鎹�
+        String glassId = (String) params.get("glassId");
+        Integer storagePosition = (Integer) params.get("storagePosition");
+        Boolean triggerRequest = (Boolean) params.getOrDefault("triggerRequest", autoStore);
+
+        // 鏋勫缓鍐欏叆鏁版嵁
+        Map<String, Object> payload = new HashMap<>();
+        
+        if (glassId != null) {
+            payload.put("plcGlassId", glassId);
+        }
+        
+        if (storagePosition != null) {
+            payload.put("storagePosition", storagePosition);
+        }
+
+        // 鑷姩瑙﹀彂璇锋眰
+        if (triggerRequest != null && triggerRequest) {
+            payload.put("plcRequest", 1);
+        }
+
+        log.info("鐜荤拑瀛樺偍: deviceId={}, glassId={}, position={}", 
+                deviceConfig.getId(), glassId, storagePosition);
+
+        return devicePlcOperationService.writeFields(
+                deviceConfig.getId(), 
+                payload, 
+                "鐜荤拑瀛樺偍-瀛樺偍鐜荤拑"
+        );
+    }
+
+    /**
+     * 澶勭悊鍙栬揣鎿嶄綔
+     */
+    private DevicePlcVO.OperationResult handleRetrieveGlass(
+            DeviceConfig deviceConfig,
+            Map<String, Object> params,
+            Map<String, Object> logicParams) {
+
+        // 浠庨�昏緫鍙傛暟涓幏鍙栭厤缃�
+        String retrievalMode = getLogicParam(logicParams, "retrievalMode", "FIFO");
+        Boolean autoRetrieve = getLogicParam(logicParams, "autoRetrieve", true);
+
+        // 浠庤繍琛屾椂鍙傛暟涓幏鍙栨暟鎹�
+        Integer storagePosition = (Integer) params.get("storagePosition");
+        String glassId = (String) params.get("glassId");
+        Boolean triggerRequest = (Boolean) params.getOrDefault("triggerRequest", autoRetrieve);
+
+        // 鏋勫缓鍐欏叆鏁版嵁
+        Map<String, Object> payload = new HashMap<>();
+        
+        if (storagePosition != null) {
+            payload.put("retrievePosition", storagePosition);
+        }
+        
+        if (glassId != null) {
+            payload.put("retrieveGlassId", glassId);
+        }
+
+        // 鑷姩瑙﹀彂璇锋眰
+        if (triggerRequest != null && triggerRequest) {
+            payload.put("plcRequest", 1);
+        }
+
+        log.info("鐜荤拑鍙栬揣: deviceId={}, position={}, glassId={}", 
+                deviceConfig.getId(), storagePosition, glassId);
+
+        return devicePlcOperationService.writeFields(
+                deviceConfig.getId(), 
+                payload, 
+                "鐜荤拑瀛樺偍-鍙栬揣"
+        );
+    }
+
+    /**
+     * 澶勭悊瑙﹀彂璇锋眰鎿嶄綔
+     */
+    private DevicePlcVO.OperationResult handleTriggerRequest(
+            DeviceConfig deviceConfig,
+            Map<String, Object> params,
+            Map<String, Object> logicParams) {
+
+        Map<String, Object> payload = new HashMap<>();
+        payload.put("plcRequest", 1);
+        
+        log.info("鐜荤拑瀛樺偍瑙﹀彂璇锋眰: deviceId={}", deviceConfig.getId());
+        return devicePlcOperationService.writeFields(
+                deviceConfig.getId(),
+                payload,
+                "鐜荤拑瀛樺偍-瑙﹀彂璇锋眰"
+        );
+    }
+
+    /**
+     * 澶勭悊瑙﹀彂姹囨姤鎿嶄綔
+     */
+    private DevicePlcVO.OperationResult handleTriggerReport(
+            DeviceConfig deviceConfig,
+            Map<String, Object> params,
+            Map<String, Object> logicParams) {
+
+        Map<String, Object> payload = new HashMap<>();
+        payload.put("plcReport", 1);
+        
+        log.info("鐜荤拑瀛樺偍瑙﹀彂姹囨姤: deviceId={}", deviceConfig.getId());
+        return devicePlcOperationService.writeFields(
+                deviceConfig.getId(),
+                payload,
+                "鐜荤拑瀛樺偍-瑙﹀彂姹囨姤"
+        );
+    }
+
+    /**
+     * 澶勭悊閲嶇疆鎿嶄綔
+     */
+    private DevicePlcVO.OperationResult handleReset(
+            DeviceConfig deviceConfig,
+            Map<String, Object> params,
+            Map<String, Object> logicParams) {
+
+        Map<String, Object> payload = new HashMap<>();
+        payload.put("plcRequest", 0);
+        payload.put("plcReport", 0);
+        
+        log.info("鐜荤拑瀛樺偍閲嶇疆: deviceId={}", deviceConfig.getId());
+        return devicePlcOperationService.writeFields(
+                deviceConfig.getId(),
+                payload,
+                "鐜荤拑瀛樺偍-閲嶇疆"
+        );
+    }
+
+    @Override
+    public String validateLogicParams(DeviceConfig deviceConfig) {
+        Map<String, Object> logicParams = parseLogicParams(deviceConfig);
+        
+        // 楠岃瘉蹇呭~鍙傛暟
+        Integer storageCapacity = getLogicParam(logicParams, "storageCapacity", null);
+        if (storageCapacity != null && storageCapacity <= 0) {
+            return "瀛樺偍瀹归噺(storageCapacity)蹇呴』澶т簬0";
+        }
+
+        String retrievalMode = getLogicParam(logicParams, "retrievalMode", null);
+        if (retrievalMode != null && !retrievalMode.matches("FIFO|LIFO|RANDOM")) {
+            return "鍙栬揣妯″紡(retrievalMode)蹇呴』鏄疐IFO銆丩IFO鎴朢ANDOM";
+        }
+
+        return null; // 楠岃瘉閫氳繃
+    }
+
+    @Override
+    public String getDefaultLogicParams() {
+        Map<String, Object> defaultParams = new HashMap<>();
+        defaultParams.put("storageCapacity", 100);
+        defaultParams.put("retrievalMode", "FIFO");
+        defaultParams.put("autoStore", true);
+        defaultParams.put("autoRetrieve", true);
+        defaultParams.put("maxRetryCount", 3);
+        
+        try {
+            return objectMapper.writeValueAsString(defaultParams);
+        } catch (Exception e) {
+            log.error("鐢熸垚榛樿閫昏緫鍙傛暟澶辫触", e);
+            return "{}";
+        }
+    }
+}
+
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/impl/LargeGlassLogicHandler.java b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/impl/LargeGlassLogicHandler.java
new file mode 100644
index 0000000..284fefa
--- /dev/null
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/impl/LargeGlassLogicHandler.java
@@ -0,0 +1,195 @@
+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
new file mode 100644
index 0000000..7d3f18a
--- /dev/null
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/impl/LoadVehicleLogicHandler.java
@@ -0,0 +1,372 @@
+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.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 {
+
+    public LoadVehicleLogicHandler(DevicePlcOperationService devicePlcOperationService) {
+        super(devicePlcOperationService);
+    }
+
+    @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);
+            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,
+                "涓婂ぇ杞�-閲嶇疆"
+        );
+    }
+
+    @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) {
+                for (String glassId : glassIds) {
+                    result.add(new GlassInfo(glassId, null));
+                }
+            }
+        }
+        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/service/PlcDynamicDataService.java b/mes-processes/mes-plcSend/src/main/java/com/mes/service/PlcDynamicDataService.java
index 84b20e7..12eeb8e 100644
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/service/PlcDynamicDataService.java
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/service/PlcDynamicDataService.java
@@ -3,6 +3,7 @@
 import com.alibaba.fastjson.JSONObject;
 import com.github.xingshuangs.iot.common.enums.EDataType;
 import com.github.xingshuangs.iot.protocol.s7.serializer.S7Parameter;
+import com.mes.device.entity.DeviceConfig;
 import com.mes.entity.PlcAddress;
 import com.mes.s7.enhanced.EnhancedS7Serializer;
 
@@ -66,4 +67,52 @@
      * @param s7Serializer S7搴忓垪鍖栧櫒
      */
     void writePlcField(PlcAddress config, String fieldName, Object value, EnhancedS7Serializer s7Serializer);
+    
+    /**
+     * 鏍规嵁DeviceConfig閰嶇疆鍜屽瓧娈靛悕绉拌鍙朠LC鏁版嵁
+     * 
+     * @param device 璁惧閰嶇疆
+     * @param fieldNames 瑕佽鍙栫殑瀛楁鍚嶇О鍒楄〃
+     * @param s7Serializer S7搴忓垪鍖栧櫒
+     * @return 瀛楁鍚�->鍊� 鐨凪ap
+     */
+    Map<String, Object> readPlcData(DeviceConfig device, List<String> fieldNames, EnhancedS7Serializer s7Serializer);
+    
+    /**
+     * 鏍规嵁DeviceConfig閰嶇疆鍜屾暟鎹甅ap鍐欏叆PLC
+     * 
+     * @param device 璁惧閰嶇疆
+     * @param dataMap 瀛楁鍚�->鍊� 鐨凪ap
+     * @param s7Serializer S7搴忓垪鍖栧櫒
+     */
+    void writePlcData(DeviceConfig device, Map<String, Object> dataMap, EnhancedS7Serializer s7Serializer);
+    
+    /**
+     * 璇诲彇PLC鎵�鏈夊瓧娈碉紙鍩轰簬DeviceConfig锛�
+     * 
+     * @param device 璁惧閰嶇疆
+     * @param s7Serializer S7搴忓垪鍖栧櫒
+     * @return 鎵�鏈夊瓧娈电殑鍊�
+     */
+    Map<String, Object> readAllPlcData(DeviceConfig device, EnhancedS7Serializer s7Serializer);
+    
+    /**
+     * 璇诲彇鍗曚釜瀛楁锛堝熀浜嶥eviceConfig锛�
+     * 
+     * @param device 璁惧閰嶇疆
+     * @param fieldName 瀛楁鍚�
+     * @param s7Serializer S7搴忓垪鍖栧櫒
+     * @return 瀛楁鍊�
+     */
+    Object readPlcField(DeviceConfig device, String fieldName, EnhancedS7Serializer s7Serializer);
+    
+    /**
+     * 鍐欏叆鍗曚釜瀛楁锛堝熀浜嶥eviceConfig锛�
+     * 
+     * @param device 璁惧閰嶇疆
+     * @param fieldName 瀛楁鍚�
+     * @param value 瀛楁鍊�
+     * @param s7Serializer S7搴忓垪鍖栧櫒
+     */
+    void writePlcField(DeviceConfig device, String fieldName, Object value, EnhancedS7Serializer s7Serializer);
 }
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/service/PlcTestWriteService.java b/mes-processes/mes-plcSend/src/main/java/com/mes/service/PlcTestWriteService.java
index 9e53e9f..ba3905b 100644
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/service/PlcTestWriteService.java
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/service/PlcTestWriteService.java
@@ -1,14 +1,22 @@
 package com.mes.service;
 
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
 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.device.service.DeviceConfigService;
+import com.mes.device.util.ConfigJsonHelper;
 import com.mes.entity.PlcBaseData;
 import com.mes.entity.PlcAddress;
+import com.mes.service.PlcDynamicDataService;
 import com.mes.s7.enhanced.EnhancedS7Serializer;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 
 import javax.annotation.Resource;
+import java.util.Collections;
+import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 
@@ -25,6 +33,15 @@
 
     @Resource
     private PlcAddressService plcAddressService;
+    
+    @Resource
+    private DeviceConfigService deviceConfigService;
+    
+    @Resource
+    private PlcDynamicDataService plcDynamicDataService;
+    
+    private final ObjectMapper objectMapper = new ObjectMapper();
+    private static final TypeReference<Map<String, Object>> MAP_TYPE = new TypeReference<Map<String, Object>>() {};
 
     private static final int ON = 1;
     private static final int OFF = 0;
@@ -47,33 +64,43 @@
      */
     public boolean simulatePlcRequest(String projectId) {
         try {
-            // 鑾峰彇椤圭洰閰嶇疆锛堟暟鎹簱瀹炰綋锛�
             PlcAddress config = plcAddressService.getProjectConfigWithMapping(projectId);
-            
-            // 鑾峰彇瀵瑰簲鐨凷7Serializer
-            EnhancedS7Serializer s7Serializer = getSerializerForProject(projectId, config);
-            
-            // 璇诲彇褰撳墠PLC鐘舵��
-            PlcBaseData currentData = s7Serializer.read(PlcBaseData.class, config.getDbArea(), config.getBeginIndex());
-
-            if (currentData.getOnlineState() == OFF) {
-                log.info("褰撳墠PLC鑱旀満妯″紡涓�0锛屽仠姝㈣仈鏈�");
+            if (config == null) {
+                log.error("椤圭洰閰嶇疆涓嶅瓨鍦�: projectId={}", projectId);
                 return false;
-            }else if (currentData.getPlcReport() == ON){
-                log.info("褰撳墠涓婄墖PLC姹囨姤瀛椾负1锛岄噸缃负0");
-                currentData.setPlcReport(OFF);
             }
-            // 璁剧疆PLC璇锋眰瀛椾负1锛堣Е鍙慚ES浠诲姟澶勭悊锛�
-            currentData.setPlcRequest(ON);
-            s7Serializer.write(currentData, config.getDbArea(), config.getBeginIndex());
-            log.info("妯℃嫙PLC鍙戦�佽姹傚瓧鎴愬姛锛歱lcRequest=1, projectId={}, dbArea={}, beginIndex={}", 
-                    projectId, config.getDbArea(), config.getBeginIndex());
-            return true;
-            
+            EnhancedS7Serializer s7Serializer = getSerializerForProject(projectId, config);
+            if (s7Serializer == null) {
+                log.error("鏃犳硶鍒涘缓S7Serializer: projectId={}", projectId);
+                return false;
+            }
+            return simulatePlcRequestInternal(projectId, config, s7Serializer);
         } catch (Exception e) {
             log.error("妯℃嫙PLC璇锋眰瀛楀け璐�", e);
             return false;
         }
+    }
+
+    private boolean simulatePlcRequestInternal(String projectId, PlcAddress config, EnhancedS7Serializer s7Serializer) throws Exception {
+        PlcBaseData currentData = s7Serializer.read(PlcBaseData.class, config.getDbArea(), config.getBeginIndex());
+        if (currentData == null) {
+            log.error("璇诲彇PLC鏁版嵁澶辫触锛岃繑鍥瀗ull: projectId={}, dbArea={}, beginIndex={}", 
+                    projectId, config.getDbArea(), config.getBeginIndex());
+            return false;
+        }
+
+        if (currentData.getOnlineState() == OFF) {
+            log.info("褰撳墠PLC鑱旀満妯″紡涓�0锛屽仠姝㈣仈鏈�");
+            return false;
+        } else if (currentData.getPlcReport() == ON) {
+            log.info("褰撳墠涓婄墖PLC姹囨姤瀛椾负1锛岄噸缃负0");
+            currentData.setPlcReport(OFF);
+        }
+        currentData.setPlcRequest(ON);
+        s7Serializer.write(currentData, config.getDbArea(), config.getBeginIndex());
+        log.info("妯℃嫙PLC鍙戦�佽姹傚瓧鎴愬姛锛歱lcRequest=1, projectId={}, dbArea={}, beginIndex={}", 
+                projectId, config.getDbArea(), config.getBeginIndex());
+        return true;
     }
 
     /**
@@ -90,29 +117,41 @@
         try {
             // 鑾峰彇椤圭洰閰嶇疆锛堟暟鎹簱瀹炰綋锛�
             PlcAddress config = plcAddressService.getProjectConfigWithMapping(projectId);
+            if (config == null) {
+                log.error("椤圭洰閰嶇疆涓嶅瓨鍦�: projectId={}", projectId);
+                return false;
+            }
             
             // 鑾峰彇瀵瑰簲鐨凷7Serializer
             EnhancedS7Serializer s7Serializer = getSerializerForProject(projectId, config);
+            if (s7Serializer == null) {
+                log.error("鏃犳硶鍒涘缓S7Serializer: projectId={}", projectId);
+                return false;
+            }
             
-            PlcBaseData currentData = s7Serializer.read(PlcBaseData.class, config.getDbArea(), config.getBeginIndex());
-
-            // 璁剧疆PLC姹囨姤瀛椾负1锛堜换鍔″畬鎴愶級
-            currentData.setPlcReport(ON);
-            // 璇锋眰瀛楁竻0
-            currentData.setPlcRequest(OFF);
-            
-            // 璁剧疆瀹屾垚鏁伴噺绛夋暟鎹�
-            currentData.setMesGlassCount(10);
-            
-            s7Serializer.write(currentData, config.getDbArea(), config.getBeginIndex());
-            log.info("妯℃嫙PLC浠诲姟瀹屾垚姹囨姤锛歱lcReport=1, mesGlassCount=10, projectId={}, dbArea={}, beginIndex={}", 
-                    projectId, config.getDbArea(), config.getBeginIndex());
-            return true;
-            
+            return simulatePlcReportInternal(projectId, config, s7Serializer);
         } catch (Exception e) {
             log.error("妯℃嫙PLC浠诲姟瀹屾垚姹囨姤澶辫触", e);
             return false;
         }
+    }
+
+    private boolean simulatePlcReportInternal(String projectId, PlcAddress config, EnhancedS7Serializer s7Serializer) throws Exception {
+        PlcBaseData currentData = s7Serializer.read(PlcBaseData.class, config.getDbArea(), config.getBeginIndex());
+        if (currentData == null) {
+            log.error("璇诲彇PLC鏁版嵁澶辫触锛岃繑鍥瀗ull: projectId={}, dbArea={}, beginIndex={}", 
+                    projectId, config.getDbArea(), config.getBeginIndex());
+            return false;
+        }
+
+        currentData.setPlcReport(ON);
+        currentData.setPlcRequest(OFF);
+        currentData.setMesGlassCount(10);
+
+        s7Serializer.write(currentData, config.getDbArea(), config.getBeginIndex());
+        log.info("妯℃嫙PLC浠诲姟瀹屾垚姹囨姤锛歱lcReport=1, mesGlassCount=10, projectId={}, dbArea={}, beginIndex={}", 
+                projectId, config.getDbArea(), config.getBeginIndex());
+        return true;
     }
 
     /**
@@ -129,24 +168,38 @@
         try {
             // 鑾峰彇椤圭洰閰嶇疆锛堟暟鎹簱瀹炰綋锛�
             PlcAddress config = plcAddressService.getProjectConfigWithMapping(projectId);
+            if (config == null) {
+                log.error("椤圭洰閰嶇疆涓嶅瓨鍦�: projectId={}", projectId);
+                return false;
+            }
             
             // 鑾峰彇瀵瑰簲鐨凷7Serializer
             EnhancedS7Serializer s7Serializer = getSerializerForProject(projectId, config);
+            if (s7Serializer == null) {
+                log.error("鏃犳硶鍒涘缓S7Serializer: projectId={}", projectId);
+                return false;
+            }
             
-            PlcBaseData currentData = s7Serializer.read(PlcBaseData.class, config.getDbArea(), config.getBeginIndex());
-
-            // 1:鑱旀満 0:鑴辨満
-            currentData.setOnlineState(onlineState);
-            
-            s7Serializer.write(currentData, config.getDbArea(), config.getBeginIndex());
-            log.info("妯℃嫙PLC鑱旀満鐘舵�侊細onlineState={}, projectId={}, dbArea={}, beginIndex={}", 
-                    onlineState, projectId, config.getDbArea(), config.getBeginIndex());
-            return true;
-            
+            return simulateOnlineStatusInternal(onlineState, projectId, config, s7Serializer);
         } catch (Exception e) {
             log.error("妯℃嫙PLC鑱旀満鐘舵�佸け璐�", e);
             return false;
         }
+    }
+
+    private boolean simulateOnlineStatusInternal(int onlineState, String projectId, PlcAddress config, EnhancedS7Serializer s7Serializer) throws Exception {
+        PlcBaseData currentData = s7Serializer.read(PlcBaseData.class, config.getDbArea(), config.getBeginIndex());
+        if (currentData == null) {
+            log.error("璇诲彇PLC鏁版嵁澶辫触锛岃繑鍥瀗ull: projectId={}, dbArea={}, beginIndex={}", 
+                    projectId, config.getDbArea(), config.getBeginIndex());
+            return false;
+        }
+
+        currentData.setOnlineState(onlineState);
+        s7Serializer.write(currentData, config.getDbArea(), config.getBeginIndex());
+        log.info("妯℃嫙PLC鑱旀満鐘舵�侊細onlineState={}, projectId={}, dbArea={}, beginIndex={}", 
+                onlineState, projectId, config.getDbArea(), config.getBeginIndex());
+        return true;
     }
 
     /**
@@ -163,32 +216,37 @@
         try {
             // 鑾峰彇椤圭洰閰嶇疆锛堟暟鎹簱瀹炰綋锛�
             PlcAddress config = plcAddressService.getProjectConfigWithMapping(projectId);
+            if (config == null) {
+                log.error("椤圭洰閰嶇疆涓嶅瓨鍦�: projectId={}", projectId);
+                return false;
+            }
             
-            // 鑾峰彇瀵瑰簲鐨凷7Serializer
             EnhancedS7Serializer s7Serializer = getSerializerForProject(projectId, config);
-            
-            PlcBaseData resetData = new PlcBaseData();
-            
-            // 閲嶇疆鎵�鏈夊叧閿瓧娈�
-            resetData.setPlcRequest(OFF);
-            resetData.setPlcReport(OFF);
-            resetData.setMesSend(OFF);
-            resetData.setMesConfirm(OFF);
-            // 榛樿鑱旀満
-            resetData.setOnlineState(ON);
-            resetData.setMesGlassCount(0);
-            // 娓呴櫎鎶ヨ
-            resetData.setAlarmInfo(OFF);
-
-            s7Serializer.write(resetData, config.getDbArea(), config.getBeginIndex());
-            log.info("PLC鐘舵�佸凡閲嶇疆, projectId={}, dbArea={}, beginIndex={}", 
-                    projectId, config.getDbArea(), config.getBeginIndex());
-            return true;
-            
+            if (s7Serializer == null) {
+                log.error("鏃犳硶鍒涘缓S7Serializer: projectId={}", projectId);
+                return false;
+            }
+            return resetPlcInternal(projectId, config, s7Serializer);
         } catch (Exception e) {
             log.error("閲嶇疆PLC鐘舵�佸け璐�", e);
             return false;
         }
+    }
+
+    private boolean resetPlcInternal(String projectId, PlcAddress config, EnhancedS7Serializer s7Serializer) throws Exception {
+        PlcBaseData resetData = new PlcBaseData();
+        resetData.setPlcRequest(OFF);
+        resetData.setPlcReport(OFF);
+        resetData.setMesSend(OFF);
+        resetData.setMesConfirm(OFF);
+        resetData.setOnlineState(ON);
+        resetData.setMesGlassCount(0);
+        resetData.setAlarmInfo(OFF);
+
+        s7Serializer.write(resetData, config.getDbArea(), config.getBeginIndex());
+        log.info("PLC鐘舵�佸凡閲嶇疆, projectId={}, dbArea={}, beginIndex={}", 
+                projectId, config.getDbArea(), config.getBeginIndex());
+        return true;
     }
 
     /**
@@ -205,15 +263,31 @@
         try {
             // 鑾峰彇椤圭洰閰嶇疆锛堟暟鎹簱瀹炰綋锛�
             PlcAddress config = plcAddressService.getProjectConfigWithMapping(projectId);
+            if (config == null) {
+                log.error("椤圭洰閰嶇疆涓嶅瓨鍦�: projectId={}", projectId);
+                return null;
+            }
             
-            // 鑾峰彇瀵瑰簲鐨凷7Serializer
             EnhancedS7Serializer s7Serializer = getSerializerForProject(projectId, config);
+            if (s7Serializer == null) {
+                log.error("鏃犳硶鍒涘缓S7Serializer: projectId={}", projectId);
+                return null;
+            }
             
-            return s7Serializer.read(PlcBaseData.class, config.getDbArea(), config.getBeginIndex());
+            return readPlcStatusInternal(projectId, config, s7Serializer);
         } catch (Exception e) {
             log.error("璇诲彇PLC鐘舵�佸け璐�", e);
             return null;
         }
+    }
+
+    private PlcBaseData readPlcStatusInternal(String projectId, PlcAddress config, EnhancedS7Serializer s7Serializer) throws Exception {
+        PlcBaseData data = s7Serializer.read(PlcBaseData.class, config.getDbArea(), config.getBeginIndex());
+        if (data == null) {
+            log.error("璇诲彇PLC鐘舵�佽繑鍥瀗ull: projectId={}, dbArea={}, beginIndex={}", 
+                    projectId, config.getDbArea(), config.getBeginIndex());
+        }
+        return data;
     }
     
     /**
@@ -276,4 +350,300 @@
         serializerCache.clear();
         log.info("宸叉竻闄ゆ墍鏈塖7Serializer缂撳瓨");
     }
+    
+    /**
+     * 鏍规嵁璁惧ID妯℃嫙PLC鍙戦�佽姹傚瓧
+     * 
+     * @param deviceId 璁惧ID
+     * @return 鏄惁鎴愬姛
+     */
+    public boolean simulatePlcRequestByDevice(Long deviceId) {
+        DeviceConfig device = deviceConfigService.getDeviceById(deviceId);
+        if (device == null) {
+            log.error("璁惧涓嶅瓨鍦�: deviceId={}", deviceId);
+            return false;
+        }
+        try {
+            String projectId = resolveProjectId(device);
+            PlcAddress config = buildPlcAddressFromDevice(device);
+            EnhancedS7Serializer s7Serializer = getSerializerForDevice(device);
+            return simulatePlcRequestInternal(projectId, config, s7Serializer);
+        } catch (Exception e) {
+            log.error("鏍规嵁璁惧妯℃嫙PLC璇锋眰瀛楀け璐�: deviceId={}", deviceId, e);
+            return false;
+        }
+    }
+    
+    /**
+     * 鏍规嵁璁惧ID妯℃嫙PLC浠诲姟瀹屾垚姹囨姤
+     * 
+     * @param deviceId 璁惧ID
+     * @return 鏄惁鎴愬姛
+     */
+    public boolean simulatePlcReportByDevice(Long deviceId) {
+        DeviceConfig device = deviceConfigService.getDeviceById(deviceId);
+        if (device == null) {
+            log.error("璁惧涓嶅瓨鍦�: deviceId={}", deviceId);
+            return false;
+        }
+        try {
+            String projectId = resolveProjectId(device);
+            PlcAddress config = buildPlcAddressFromDevice(device);
+            EnhancedS7Serializer s7Serializer = getSerializerForDevice(device);
+            return simulatePlcReportInternal(projectId, config, s7Serializer);
+        } catch (Exception e) {
+            log.error("鏍规嵁璁惧妯℃嫙PLC姹囨姤澶辫触: deviceId={}", deviceId, e);
+            return false;
+        }
+    }
+    
+    /**
+     * 鏍规嵁璁惧ID閲嶇疆PLC鎵�鏈夌姸鎬�
+     * 
+     * @param deviceId 璁惧ID
+     * @return 鏄惁鎴愬姛
+     */
+    public boolean resetPlcByDevice(Long deviceId) {
+        DeviceConfig device = deviceConfigService.getDeviceById(deviceId);
+        if (device == null) {
+            log.error("璁惧涓嶅瓨鍦�: deviceId={}", deviceId);
+            return false;
+        }
+        try {
+            String projectId = resolveProjectId(device);
+            PlcAddress config = buildPlcAddressFromDevice(device);
+            EnhancedS7Serializer s7Serializer = getSerializerForDevice(device);
+            return resetPlcInternal(projectId, config, s7Serializer);
+        } catch (Exception e) {
+            log.error("鏍规嵁璁惧閲嶇疆PLC鐘舵�佸け璐�: deviceId={}", deviceId, e);
+            return false;
+        }
+    }
+    
+    /**
+     * 鏍规嵁璁惧ID璇诲彇PLC褰撳墠鐘舵��
+     * 
+     * @param deviceId 璁惧ID
+     * @return PLC鐘舵�佹暟鎹�
+     */
+    public Map<String, Object> readPlcStatusByDevice(Long deviceId) {
+        DeviceConfig device = deviceConfigService.getDeviceById(deviceId);
+        if (device == null) {
+            log.error("璁惧涓嶅瓨鍦�: deviceId={}", deviceId);
+            return null;
+        }
+        try {
+            String projectId = resolveProjectId(device);
+            PlcAddress config = buildPlcAddressFromDevice(device);
+            EnhancedS7Serializer s7Serializer = getSerializerForDevice(device);
+            PlcBaseData data = readPlcStatusInternal(projectId, config, s7Serializer);
+            if (data == null) {
+                return null;
+            }
+            String json = objectMapper.writeValueAsString(data);
+            return objectMapper.readValue(json, MAP_TYPE);
+        } catch (Exception e) {
+            log.error("璇诲彇璁惧PLC鐘舵�佸け璐�: deviceId={}", deviceId, e);
+            return null;
+        }
+    }
+    
+    /**
+     * 鏍规嵁璁惧ID鍐欏叆PLC瀛楁
+     * 
+     * @param deviceId 璁惧ID
+     * @param fieldValues 瀛楁鍚�->鍊� 鐨凪ap
+     * @return 鏄惁鎴愬姛
+     */
+    public boolean writeFieldsByDevice(Long deviceId, Map<String, Object> fieldValues) {
+        DeviceConfig device = deviceConfigService.getDeviceById(deviceId);
+        if (device == null) {
+            log.error("璁惧涓嶅瓨鍦�: deviceId={}", deviceId);
+            return false;
+        }
+        
+        try {
+            // 浠庤澶囬厤缃腑鑾峰彇椤圭洰鏍囪瘑
+            String projectId = resolveProjectId(device);
+            
+            // 鑾峰彇瀵瑰簲鐨凷7Serializer锛堜娇鐢ㄨ澶囬厤缃級
+            EnhancedS7Serializer s7Serializer = getSerializerForDevice(device);
+            
+            // 浣跨敤鍔ㄦ�佹暟鎹湇鍔″啓鍏ュ瓧娈碉紙鍩轰簬DeviceConfig锛�
+            plcDynamicDataService.writePlcData(device, fieldValues, s7Serializer);
+            
+            log.info("鍐欏叆PLC瀛楁鎴愬姛: deviceId={}, projectId={}, fields={}", deviceId, projectId, fieldValues.keySet());
+            return true;
+        } catch (Exception e) {
+            log.error("鍐欏叆PLC瀛楁澶辫触: deviceId={}", deviceId, e);
+            return false;
+        }
+    }
+    
+    /**
+     * 鑾峰彇璁惧瀵瑰簲鐨凷7Serializer瀹炰緥
+     * 
+     * @param device 璁惧閰嶇疆
+     * @return S7Serializer瀹炰緥
+     */
+    private EnhancedS7Serializer getSerializerForDevice(DeviceConfig device) {
+        String cacheKey = "device:" + (device.getId() != null ? device.getId() : resolveProjectId(device));
+        return serializerCache.computeIfAbsent(cacheKey, id -> {
+            // 瑙f瀽PLC绫诲瀷锛堜粎鍙栧疄浣撳瓧娈碉級
+            EPlcType plcType = EPlcType.S1200;
+            String plcTypeValue = device.getPlcType();
+            if (plcTypeValue == null || plcTypeValue.isEmpty()) {
+                log.warn("璁惧鏈厤缃甈LC绫诲瀷锛屼娇鐢ㄩ粯璁ょ被鍨婼1200, deviceId={}", device.getId());
+            } else {
+                try {
+                    plcType = EPlcType.valueOf(plcTypeValue);
+                } catch (IllegalArgumentException e) {
+                    log.warn("鏈煡鐨凱LC绫诲瀷: {}, 浣跨敤榛樿绫诲瀷 S1200", plcTypeValue);
+                }
+            }
+            
+            // 鍒涘缓S7PLC瀹炰緥锛堜粎鍙栧疄浣撳瓧娈碉級
+            String plcIp = device.getPlcIp();
+            if (plcIp == null || plcIp.isEmpty()) {
+                log.warn("璁惧鏈厤缃甈LC IP锛屼娇鐢ㄩ粯璁� 192.168.10.21, deviceId={}", device.getId());
+                plcIp = "192.168.10.21";
+            }
+            S7PLC s7Plc = new S7PLC(plcType, plcIp);
+            
+            // 鍒涘缓骞惰繑鍥濫nhancedS7Serializer瀹炰緥
+            return EnhancedS7Serializer.newInstance(s7Plc);
+        });
+    }
+    
+    private PlcAddress buildPlcAddressFromDevice(DeviceConfig device) {
+        Map<String, Object> plcConfig = getPlcConfigParams(device);
+        String dbArea = plcConfig.get("dbArea") != null ? String.valueOf(plcConfig.get("dbArea")) : "DB12";
+        int beginIndex = plcConfig.get("beginIndex") != null ? parseInteger(plcConfig.get("beginIndex")) : 0;
+
+        String plcIp = device.getPlcIp();
+        if (plcIp == null || plcIp.isEmpty()) {
+            log.warn("璁惧鏈厤缃甈LC IP锛屼娇鐢ㄩ粯璁� 192.168.10.21, deviceId={}", device.getId());
+            plcIp = "192.168.10.21";
+        }
+
+        String plcType = device.getPlcType();
+        if (plcType == null || plcType.isEmpty()) {
+            log.warn("璁惧鏈厤缃甈LC绫诲瀷锛屼娇鐢ㄩ粯璁1200, deviceId={}", device.getId());
+            plcType = EPlcType.S1200.name();
+        }
+
+        String addressMapping = resolveAddressMapping(device);
+
+        PlcAddress config = new PlcAddress();
+        config.setProjectId(resolveProjectId(device));
+        config.setDbArea(dbArea);
+        config.setBeginIndex(beginIndex);
+        config.setPlcIp(plcIp);
+        config.setPlcType(plcType);
+        config.setAddressMapping(addressMapping);
+        return config;
+    }
+
+    private String resolveAddressMapping(DeviceConfig device) {
+        Map<String, Object> mapping = ConfigJsonHelper.parseToMap(device.getConfigJson(), objectMapper);
+        if (!mapping.isEmpty()) {
+            try {
+                return objectMapper.writeValueAsString(mapping);
+            } catch (Exception e) {
+                log.warn("搴忓垪鍖朿onfigJson瀛楁鏄犲皠澶辫触, deviceId={}", device.getId(), e);
+            }
+        }
+        Map<String, Object> extraParams = parseExtraParams(device);
+        Object addressMapping = extraParams.get("addressMapping");
+        if (addressMapping instanceof String) {
+            return (String) addressMapping;
+        }
+        if (addressMapping != null) {
+            try {
+                return objectMapper.writeValueAsString(addressMapping);
+            } catch (Exception e) {
+                log.warn("搴忓垪鍖杄xtraParams.addressMapping澶辫触, deviceId={}", device.getId(), e);
+            }
+        }
+        throw new IllegalStateException("璁惧鏈厤缃甈LC瀛楁鏄犲皠, deviceId=" + device.getId());
+    }
+
+    private Map<String, Object> parseExtraParams(DeviceConfig device) {
+        if (device.getExtraParams() == null || device.getExtraParams().trim().isEmpty()) {
+            return Collections.emptyMap();
+        }
+        try {
+            return objectMapper.readValue(device.getExtraParams(), MAP_TYPE);
+        } catch (Exception e) {
+            log.warn("瑙f瀽璁惧extraParams澶辫触, deviceId={}", device.getId(), e);
+            return Collections.emptyMap();
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private Map<String, Object> getPlcConfigParams(DeviceConfig device) {
+        Map<String, Object> extraParams = parseExtraParams(device);
+        Object plcConfig = extraParams.get("plcConfig");
+        if (plcConfig instanceof Map) {
+            return (Map<String, Object>) plcConfig;
+        }
+        if (plcConfig instanceof String) {
+            try {
+                return objectMapper.readValue((String) plcConfig, MAP_TYPE);
+            } catch (Exception e) {
+                log.warn("瑙f瀽extraParams.plcConfig澶辫触, deviceId={}", device.getId(), e);
+            }
+        }
+        return Collections.emptyMap();
+    }
+
+    private int parseInteger(Object value) {
+        if (value instanceof Number) {
+            return ((Number) value).intValue();
+        }
+        try {
+            return Integer.parseInt(String.valueOf(value));
+        } catch (NumberFormatException ex) {
+            log.warn("鏃犳硶瑙f瀽鏁村瀷鍊�: {}", value);
+            return 0;
+        }
+    }
+
+    /**
+     * 浠庤澶囬厤缃腑瑙f瀽椤圭洰鏍囪瘑
+     * 
+     * @param device 璁惧閰嶇疆
+     * @return 椤圭洰鏍囪瘑
+     */
+    private String resolveProjectId(DeviceConfig device) {
+        if (device == null) {
+            throw new IllegalArgumentException("璁惧淇℃伅涓虹┖");
+        }
+        
+        // 1. 浼樺厛浣跨敤瀹炰綋涓婄殑projectId
+        if (device.getProjectId() != null) {
+            return String.valueOf(device.getProjectId());
+        }
+
+        // 2. 浠巈xtraParams涓鍙�
+        Map<String, Object> extraParams = parseExtraParams(device);
+        Object plcProjectId = extraParams.get("plcProjectId");
+        if (plcProjectId != null) {
+            return String.valueOf(plcProjectId);
+        }
+
+        // 3. 鍏煎鏃х粨鏋勶細configJson鎴杄xtraParams鍐呭祵
+        Map<String, Object> configParams = ConfigJsonHelper.parseToMap(device.getConfigJson(), objectMapper);
+        Object legacyProjectId = configParams.get("plcProjectId");
+        if (legacyProjectId != null) {
+            return String.valueOf(legacyProjectId);
+        }
+        
+        // 鏈�鍚庝娇鐢ㄨ澶囩紪鍙�
+        if (device.getDeviceCode() != null && !device.getDeviceCode().isEmpty()) {
+            return device.getDeviceCode();
+        }
+        
+        throw new IllegalStateException("鏃犳硶瑙f瀽璁惧鐨凱LC椤圭洰鏍囪瘑, deviceId=" + device.getId());
+    }
 }
\ No newline at end of file
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/service/impl/PlcAutoTestServiceImpl.java b/mes-processes/mes-plcSend/src/main/java/com/mes/service/impl/PlcAutoTestServiceImpl.java
index 5a2cfe5..74fd0a2 100644
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/service/impl/PlcAutoTestServiceImpl.java
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/service/impl/PlcAutoTestServiceImpl.java
@@ -1,7 +1,7 @@
 package com.mes.service.impl;
 
-import com.mes.service.IPlcAutoTestService;
-import com.mes.service.IPlcTestWriteService;
+import com.mes.service.PlcAutoTestService;
+import com.mes.service.PlcTestWriteService;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.scheduling.annotation.Scheduled;
@@ -18,10 +18,10 @@
  */
 @Slf4j
 @Service
-public class PlcAutoTestServiceImpl implements PlcAutoTestService {
+public class PlcAutoTestServiceImpl extends PlcAutoTestService {
 
     @Resource
-    private IPlcTestWriteService plcTestWriteService;
+    private PlcTestWriteService plcTestWriteService;
 
     // 鑷姩娴嬭瘯寮�鍏�
     @Value("${plc.auto.test.enabled:false}")
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/service/impl/PlcDynamicDataServiceImpl.java b/mes-processes/mes-plcSend/src/main/java/com/mes/service/impl/PlcDynamicDataServiceImpl.java
index 2a1b439..15a52af 100644
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/service/impl/PlcDynamicDataServiceImpl.java
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/service/impl/PlcDynamicDataServiceImpl.java
@@ -1,8 +1,12 @@
 package com.mes.service.impl;
 
 import com.alibaba.fastjson.JSONObject;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
 import com.github.xingshuangs.iot.common.enums.EDataType;
 import com.github.xingshuangs.iot.protocol.s7.serializer.S7Parameter;
+import com.mes.device.entity.DeviceConfig;
+import com.mes.device.util.ConfigJsonHelper;
 import com.mes.entity.PlcAddress;
 import com.mes.s7.enhanced.EnhancedS7Serializer;
 import com.mes.service.PlcDynamicDataService;
@@ -10,6 +14,7 @@
 import org.springframework.stereotype.Service;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -24,6 +29,9 @@
 @Slf4j
 @Service
 public class PlcDynamicDataServiceImpl implements PlcDynamicDataService {
+    
+    private final ObjectMapper objectMapper = new ObjectMapper();
+    private static final TypeReference<Map<String, Object>> MAP_TYPE = new TypeReference<Map<String, Object>>() {};
 
     /**
      * 鏍规嵁PlcAddress閰嶇疆鍜屽瓧娈靛悕绉拌鍙朠LC鏁版嵁
@@ -59,8 +67,8 @@
             
             return resultMap;
         } catch (Exception e) {
-            log.error("璇诲彇PLC鏁版嵁澶辫触锛岃妫�鏌ワ細1.PLC IP鍦板潃鏄惁姝g‘[{}] 2.PLC璁惧鏄惁鍦ㄧ嚎 3.缃戠粶杩炴帴鏄惁姝e父锛宮odule: {}, 璇︾粏閿欒: {}", 
-                config.getPlcIp(), config.getModule(), e.getMessage(), e);
+            log.error("璇诲彇PLC鏁版嵁澶辫触锛岃妫�鏌ワ細1.PLC IP鍦板潃鏄惁姝g‘[{}] 2.PLC璁惧鏄惁鍦ㄧ嚎 3.缃戠粶杩炴帴鏄惁姝e父锛岃缁嗛敊璇�: {}", 
+                config.getPlcIp(), e.getMessage(), e);
             return new HashMap<>();
         }
     }
@@ -88,8 +96,8 @@
             // 鍐欏叆PLC
             s7Serializer.write(parameters);
         } catch (Exception e) {
-            log.error("鍐欏叆PLC鏁版嵁澶辫触锛岃妫�鏌ワ細1.PLC IP鍦板潃鏄惁姝g‘[{}] 2.PLC璁惧鏄惁鍦ㄧ嚎 3.缃戠粶杩炴帴鏄惁姝e父锛宮odule: {}, 璇︾粏閿欒: {}", 
-                config.getPlcIp(), config.getModule(), e.getMessage(), e);
+            log.error("鍐欏叆PLC鏁版嵁澶辫触锛岃妫�鏌ワ細1.PLC IP鍦板潃鏄惁姝g‘[{}] 2.PLC璁惧鏄惁鍦ㄧ嚎 3.缃戠粶杩炴帴鏄惁姝e父锛岃缁嗛敊璇�: {}", 
+                config.getPlcIp(), e.getMessage(), e);
         }
     }
 
@@ -212,4 +220,287 @@
         
         return parameters;
     }
+    
+    /**
+     * 浠嶥eviceConfig涓彁鍙栧湴鍧�鏄犲皠閰嶇疆
+     * 
+     * @param device 璁惧閰嶇疆
+     * @return 鍦板潃鏄犲皠JSON瀛楃涓�
+     */
+    private String extractAddressMapping(DeviceConfig device) {
+        // configJson 鐜板湪浠呭瓨鏀惧瓧娈靛湴鍧�鏄犲皠锛堟暟缁勫舰寮忥級
+        Map<String, Object> configParams = ConfigJsonHelper.parseToMap(device.getConfigJson(), objectMapper);
+        if (!configParams.isEmpty()) {
+            try {
+                return objectMapper.writeValueAsString(configParams);
+            } catch (Exception e) {
+                log.warn("搴忓垪鍖朿onfigJson鍦板潃鏄犲皠澶辫触, deviceId={}", device.getId(), e);
+            }
+        }
+        
+        // 鍏舵浠巈xtraParams涓幏鍙栵紙鍏煎鏃х粨鏋勶級
+        Map<String, Object> extraParams = parseExtraParams(device);
+        Object addressMapping = extraParams.get("addressMapping");
+        if (addressMapping != null) {
+            if (addressMapping instanceof String) {
+                return (String) addressMapping;
+            } else {
+                try {
+                    return objectMapper.writeValueAsString(addressMapping);
+                } catch (Exception e) {
+                    log.warn("搴忓垪鍖杄xtraParams.addressMapping澶辫触, deviceId={}", device.getId(), e);
+                }
+            }
+        }
+        
+        throw new IllegalArgumentException("璁惧閰嶇疆涓湭鎵惧埌addressMapping, deviceId=" + device.getId());
+    }
+    
+    /**
+     * 浠嶥eviceConfig涓彁鍙杁bArea
+     * 
+     * @param device 璁惧閰嶇疆
+     * @return dbArea
+     */
+    private String extractDbArea(DeviceConfig device) {
+        // 浠巈xtraParams.plcConfig涓幏鍙栵紙鏂扮粨鏋勶級
+        Map<String, Object> plcConfig = getPlcConfig(device);
+        Object dbArea = plcConfig.get("dbArea");
+        if (dbArea != null) {
+            return String.valueOf(dbArea);
+        }
+        
+        // 鍏煎鏃х粨鏋勶細extraParams鏍硅妭鐐�
+        Map<String, Object> extraParams = parseExtraParams(device);
+        Object legacyDbArea = extraParams.get("dbArea");
+        if (legacyDbArea != null) {
+            return String.valueOf(legacyDbArea);
+        }
+        
+        // 榛樿鍊�
+        return "DB12";
+    }
+    
+    /**
+     * 浠嶥eviceConfig涓彁鍙朾eginIndex
+     * 
+     * @param device 璁惧閰嶇疆
+     * @return beginIndex
+     */
+    private int extractBeginIndex(DeviceConfig device) {
+        // 浠巈xtraParams.plcConfig涓幏鍙�
+        Map<String, Object> plcConfig = getPlcConfig(device);
+        Object beginIndex = plcConfig.get("beginIndex");
+        if (beginIndex != null) {
+            return parseInteger(beginIndex);
+        }
+        
+        // 鍏煎鏃х粨鏋勶細extraParams鏍硅妭鐐�
+        Map<String, Object> extraParams = parseExtraParams(device);
+        Object legacyBeginIndex = extraParams.get("beginIndex");
+        if (legacyBeginIndex != null) {
+            return parseInteger(legacyBeginIndex);
+        }
+        
+        // 榛樿鍊�
+        return 0;
+    }
+    
+    private Map<String, Object> parseExtraParams(DeviceConfig device) {
+        if (device.getExtraParams() == null || device.getExtraParams().trim().isEmpty()) {
+            return Collections.emptyMap();
+        }
+        try {
+            return objectMapper.readValue(device.getExtraParams(), MAP_TYPE);
+        } catch (Exception e) {
+            log.warn("瑙f瀽璁惧extraParams澶辫触, deviceId={}", device.getId(), e);
+            return Collections.emptyMap();
+        }
+    }
+    
+    @SuppressWarnings("unchecked")
+    private Map<String, Object> getPlcConfig(DeviceConfig device) {
+        Map<String, Object> extraParams = parseExtraParams(device);
+        Object plcConfig = extraParams.get("plcConfig");
+        if (plcConfig instanceof Map) {
+            return (Map<String, Object>) plcConfig;
+        }
+        if (plcConfig instanceof String) {
+            try {
+                return objectMapper.readValue((String) plcConfig, MAP_TYPE);
+            } catch (Exception e) {
+                log.warn("瑙f瀽extraParams.plcConfig澶辫触, deviceId={}", device.getId(), e);
+            }
+        }
+        return Collections.emptyMap();
+    }
+    
+    private int parseInteger(Object value) {
+        if (value instanceof Number) {
+            return ((Number) value).intValue();
+        }
+        try {
+            return Integer.parseInt(String.valueOf(value));
+        } catch (NumberFormatException ex) {
+            log.warn("鏃犳硶瑙f瀽鏁存暟鍊�: {}", value);
+            return 0;
+        }
+    }
+    
+    @Override
+    public Map<String, Object> readPlcData(DeviceConfig device, List<String> fieldNames, EnhancedS7Serializer s7Serializer) {
+        if (device == null) {
+            throw new IllegalArgumentException("璁惧閰嶇疆涓嶈兘涓虹┖");
+        }
+        
+        String addressMapping = extractAddressMapping(device);
+        if (addressMapping == null || addressMapping.isEmpty()) {
+            throw new IllegalArgumentException("璁惧閰嶇疆涓璦ddressMapping涓嶈兘涓虹┖");
+        }
+        
+        try {
+            // 瑙f瀽addressMapping JSON閰嶇疆
+            JSONObject addressMappingObj = JSONObject.parseObject(addressMapping);
+            
+            // 鏋勫缓S7Parameter鍒楄〃
+            String dbArea = extractDbArea(device);
+            List<S7Parameter> parameters = buildS7ParametersForDevice(device, dbArea, addressMappingObj, fieldNames);
+            
+            // 浠嶱LC璇诲彇鏁版嵁
+            List<S7Parameter> results = s7Serializer.read(parameters);
+            
+            // 灏嗙粨鏋滆浆鎹负Map
+            Map<String, Object> resultMap = new HashMap<>();
+            for (int i = 0; i < fieldNames.size() && i < results.size(); i++) {
+                String fieldName = fieldNames.get(i);
+                Object value = results.get(i).getValue();
+                resultMap.put(fieldName, value);
+            }
+            
+            return resultMap;
+        } catch (Exception e) {
+            log.error("璇诲彇PLC鏁版嵁澶辫触锛岃妫�鏌ワ細1.PLC IP鍦板潃鏄惁姝g‘[{}] 2.PLC璁惧鏄惁鍦ㄧ嚎 3.缃戠粶杩炴帴鏄惁姝e父锛宒eviceId: {}, 璇︾粏閿欒: {}", 
+                device.getPlcIp(), device.getId(), e.getMessage(), e);
+            return new HashMap<>();
+        }
+    }
+    
+    @Override
+    public void writePlcData(DeviceConfig device, Map<String, Object> dataMap, EnhancedS7Serializer s7Serializer) {
+        if (device == null) {
+            throw new IllegalArgumentException("璁惧閰嶇疆涓嶈兘涓虹┖");
+        }
+        
+        String addressMapping = extractAddressMapping(device);
+        if (addressMapping == null || addressMapping.isEmpty()) {
+            throw new IllegalArgumentException("璁惧閰嶇疆涓璦ddressMapping涓嶈兘涓虹┖");
+        }
+        
+        try {
+            // 瑙f瀽addressMapping JSON閰嶇疆
+            JSONObject addressMappingObj = JSONObject.parseObject(addressMapping);
+            
+            // 鏋勫缓S7Parameter鍒楄〃锛屽苟濉厖鍊�
+            String dbArea = extractDbArea(device);
+            List<S7Parameter> parameters = buildS7ParametersWithValuesForDevice(device, dbArea, addressMappingObj, dataMap);
+            
+            // 鍐欏叆PLC
+            s7Serializer.write(parameters);
+        } catch (Exception e) {
+            log.error("鍐欏叆PLC鏁版嵁澶辫触锛岃妫�鏌ワ細1.PLC IP鍦板潃鏄惁姝g‘[{}] 2.PLC璁惧鏄惁鍦ㄧ嚎 3.缃戠粶杩炴帴鏄惁姝e父锛宒eviceId: {}, 璇︾粏閿欒: {}", 
+                device.getPlcIp(), device.getId(), e.getMessage(), e);
+        }
+    }
+    
+    @Override
+    public Map<String, Object> readAllPlcData(DeviceConfig device, EnhancedS7Serializer s7Serializer) {
+        if (device == null) {
+            throw new IllegalArgumentException("璁惧閰嶇疆涓嶈兘涓虹┖");
+        }
+        
+        String addressMapping = extractAddressMapping(device);
+        if (addressMapping == null || addressMapping.isEmpty()) {
+            throw new IllegalArgumentException("璁惧閰嶇疆涓璦ddressMapping涓嶈兘涓虹┖");
+        }
+        
+        // 鑾峰彇鎵�鏈夊瓧娈靛悕
+        JSONObject addressMappingObj = JSONObject.parseObject(addressMapping);
+        List<String> allFields = new ArrayList<>(addressMappingObj.keySet());
+        
+        // 璇诲彇鎵�鏈夊瓧娈�
+        return readPlcData(device, allFields, s7Serializer);
+    }
+    
+    @Override
+    public Object readPlcField(DeviceConfig device, String fieldName, EnhancedS7Serializer s7Serializer) {
+        List<String> fields = new ArrayList<>();
+        fields.add(fieldName);
+        
+        Map<String, Object> result = readPlcData(device, fields, s7Serializer);
+        return result.get(fieldName);
+    }
+    
+    @Override
+    public void writePlcField(DeviceConfig device, String fieldName, Object value, EnhancedS7Serializer s7Serializer) {
+        Map<String, Object> dataMap = new HashMap<>();
+        dataMap.put(fieldName, value);
+        
+        writePlcData(device, dataMap, s7Serializer);
+    }
+    
+    /**
+     * 鏋勫缓S7Parameter鍒楄〃锛堜笉鍖呭惈鍊硷級- 鍩轰簬DeviceConfig
+     */
+    private List<S7Parameter> buildS7ParametersForDevice(DeviceConfig device, String dbArea, JSONObject addressMapping, List<String> fieldNames) {
+        List<S7Parameter> parameters = new ArrayList<>();
+        
+        for (String fieldName : fieldNames) {
+            if (!addressMapping.containsKey(fieldName)) {
+                log.warn("瀛楁 {} 鍦╝ddressMapping涓笉瀛樺湪锛岃烦杩�", fieldName);
+                continue;
+            }
+            
+            // 鑾峰彇瀛楁鐨勫亸绉诲湴鍧�
+            int offset = addressMapping.getInteger(fieldName);
+            
+            // 鏋勫缓瀹屾暣鍦板潃锛歞bArea + offset锛堝锛欴B12.2锛�
+            String fullAddress = dbArea + "." + offset;
+            
+            // 鍒涘缓S7Parameter锛岄粯璁や娇鐢║INT16绫诲瀷锛�16浣嶆棤绗﹀彿鏁存暟锛�
+            S7Parameter parameter = new S7Parameter(fullAddress, EDataType.UINT16, 1);
+            parameters.add(parameter);
+        }
+        
+        return parameters;
+    }
+    
+    /**
+     * 鏋勫缓S7Parameter鍒楄〃锛堝寘鍚�硷級- 鍩轰簬DeviceConfig
+     */
+    private List<S7Parameter> buildS7ParametersWithValuesForDevice(DeviceConfig device, String dbArea, JSONObject addressMapping, Map<String, Object> dataMap) {
+        List<S7Parameter> parameters = new ArrayList<>();
+        
+        for (Map.Entry<String, Object> entry : dataMap.entrySet()) {
+            String fieldName = entry.getKey();
+            Object value = entry.getValue();
+            
+            if (!addressMapping.containsKey(fieldName)) {
+                log.warn("瀛楁 {} 鍦╝ddressMapping涓笉瀛樺湪锛岃烦杩�", fieldName);
+                continue;
+            }
+            
+            // 鑾峰彇瀛楁鐨勫亸绉诲湴鍧�
+            int offset = addressMapping.getInteger(fieldName);
+            
+            // 鏋勫缓瀹屾暣鍦板潃
+            String fullAddress = dbArea + "." + offset;
+            
+            // 鍒涘缓S7Parameter锛岃缃��
+            S7Parameter parameter = new S7Parameter(fullAddress, EDataType.UINT16, 1);
+            parameter.setValue(value);
+            parameters.add(parameter);
+        }
+        
+        return parameters;
+    }
 }
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/service/impl/PlcTestWriteServiceImpl.java b/mes-processes/mes-plcSend/src/main/java/com/mes/service/impl/PlcTestWriteServiceImpl.java
index b0fb233..b2ebbc1 100644
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/service/impl/PlcTestWriteServiceImpl.java
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/service/impl/PlcTestWriteServiceImpl.java
@@ -2,11 +2,11 @@
 
 import com.github.xingshuangs.iot.protocol.s7.enums.EPlcType;
 import com.github.xingshuangs.iot.protocol.s7.service.S7PLC;
-import com.mes.entity.PlcBaseData;
 import com.mes.entity.PlcAddress;
+import com.mes.entity.PlcBaseData;
 import com.mes.s7.enhanced.EnhancedS7Serializer;
 import com.mes.service.PlcAddressService;
-import com.mes.service.IPlcTestWriteService;
+import com.mes.service.PlcTestWriteService;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 
@@ -23,7 +23,7 @@
  */
 @Slf4j
 @Service
-public class PlcTestWriteServiceImpl implements PlcTestWriteService {
+public class PlcTestWriteServiceImpl extends PlcTestWriteService {
 
     @Resource
     private PlcAddressService plcAddressService;
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/task/controller/MultiDeviceTaskController.java b/mes-processes/mes-plcSend/src/main/java/com/mes/task/controller/MultiDeviceTaskController.java
new file mode 100644
index 0000000..948ee05
--- /dev/null
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/task/controller/MultiDeviceTaskController.java
@@ -0,0 +1,62 @@
+package com.mes.task.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.mes.task.dto.MultiDeviceTaskQuery;
+import com.mes.task.dto.MultiDeviceTaskRequest;
+import com.mes.task.entity.MultiDeviceTask;
+import com.mes.task.entity.TaskStepDetail;
+import com.mes.task.service.MultiDeviceTaskService;
+import com.mes.vo.Result;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.RequiredArgsConstructor;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.Valid;
+import java.util.List;
+
+/**
+ * 澶氳澶囦换鍔℃帶鍒跺櫒
+ */
+@RestController
+@RequestMapping("device/task")
+@Api(tags = "澶氳澶囦换鍔$鐞�")
+@Validated
+@RequiredArgsConstructor
+public class MultiDeviceTaskController {
+
+    private final MultiDeviceTaskService multiDeviceTaskService;
+
+    @PostMapping("/start")
+    @ApiOperation("鍚姩澶氳澶囪仈鍚堟祴璇曚换鍔�")
+    public Result<MultiDeviceTask> startTask(@Valid @RequestBody MultiDeviceTaskRequest request) {
+        return Result.success(multiDeviceTaskService.startTask(request));
+    }
+
+    @PostMapping("/list")
+    @ApiOperation("鍒嗛〉鏌ヨ浠诲姟鍒楄〃")
+    public Result<Page<MultiDeviceTask>> listTasks(@RequestBody(required = false) MultiDeviceTaskQuery query) {
+        MultiDeviceTaskQuery finalQuery = query != null ? query : new MultiDeviceTaskQuery();
+        return Result.success(multiDeviceTaskService.queryTasks(finalQuery));
+    }
+
+    @GetMapping("/{taskId}")
+    @ApiOperation("鏌ヨ浠诲姟璇︽儏")
+    public Result<MultiDeviceTask> getTask(@PathVariable String taskId) {
+        return Result.success(multiDeviceTaskService.getTaskByTaskId(taskId));
+    }
+
+    @GetMapping("/{taskId}/steps")
+    @ApiOperation("鏌ヨ浠诲姟姝ラ璇︽儏")
+    public Result<List<TaskStepDetail>> getTaskSteps(@PathVariable String taskId) {
+        return Result.success(multiDeviceTaskService.getTaskSteps(taskId));
+    }
+
+    @PostMapping("/{taskId}/cancel")
+    @ApiOperation("鍙栨秷姝e湪杩愯鐨勪换鍔�")
+    public Result<Boolean> cancelTask(@PathVariable String taskId) {
+        return Result.success(multiDeviceTaskService.cancelTask(taskId));
+    }
+}
+
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/task/dto/MultiDeviceTaskQuery.java b/mes-processes/mes-plcSend/src/main/java/com/mes/task/dto/MultiDeviceTaskQuery.java
new file mode 100644
index 0000000..e4dad09
--- /dev/null
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/task/dto/MultiDeviceTaskQuery.java
@@ -0,0 +1,26 @@
+package com.mes.task.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * 澶氳澶囦换鍔℃煡璇㈡潯浠�
+ */
+@Data
+@ApiModel(value = "MultiDeviceTaskQuery", description = "澶氳澶囦换鍔″垎椤垫煡璇㈡潯浠�")
+public class MultiDeviceTaskQuery {
+
+    @ApiModelProperty(value = "璁惧缁処D", example = "1")
+    private Long groupId;
+
+    @ApiModelProperty(value = "浠诲姟鐘舵�侊紙PENDING/RUNNING/COMPLETED/FAILED/CANCELLED锛�")
+    private String status;
+
+    @ApiModelProperty(value = "椤电爜锛屼粠1寮�濮�", example = "1")
+    private Integer page = 1;
+
+    @ApiModelProperty(value = "姣忛〉鏁伴噺", example = "10")
+    private Integer size = 10;
+}
+
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/task/dto/MultiDeviceTaskRequest.java b/mes-processes/mes-plcSend/src/main/java/com/mes/task/dto/MultiDeviceTaskRequest.java
new file mode 100644
index 0000000..123da8e
--- /dev/null
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/task/dto/MultiDeviceTaskRequest.java
@@ -0,0 +1,32 @@
+package com.mes.task.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.Valid;
+import javax.validation.constraints.NotNull;
+
+/**
+ * 澶氳澶囦换鍔″惎鍔ㄨ姹�
+ */
+@Data
+@ApiModel(value = "MultiDeviceTaskRequest", description = "澶氳澶囪仈鍚堟祴璇曚换鍔″惎鍔ㄨ姹�")
+public class MultiDeviceTaskRequest {
+
+    @ApiModelProperty(value = "璁惧缁処D", example = "1", required = true)
+    @NotNull(message = "璁惧缁処D涓嶈兘涓虹┖")
+    private Long groupId;
+
+    @ApiModelProperty(value = "浠诲姟鏄剧ず鍚嶇О")
+    private String taskName;
+
+    @ApiModelProperty(value = "瑙﹀彂浜�")
+    private String triggeredBy;
+
+    @ApiModelProperty(value = "浠诲姟鍙傛暟", required = true)
+    @Valid
+    @NotNull(message = "浠诲姟鍙傛暟涓嶈兘涓虹┖")
+    private TaskParameters parameters;
+}
+
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/task/dto/TaskParameters.java b/mes-processes/mes-plcSend/src/main/java/com/mes/task/dto/TaskParameters.java
new file mode 100644
index 0000000..aaf2c7c
--- /dev/null
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/task/dto/TaskParameters.java
@@ -0,0 +1,50 @@
+package com.mes.task.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.validation.constraints.NotEmpty;
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 澶氳澶囦换鍔″弬鏁�
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@ApiModel(value = "TaskParameters", description = "澶氳澶囦换鍔℃墽琛屽弬鏁�")
+public class TaskParameters implements Serializable {
+
+    @ApiModelProperty(value = "鐜荤拑ID鍒楄〃锛堜繚鎸佹墽琛岄『搴忥級", required = true)
+    @NotEmpty(message = "鐜荤拑ID鍒楄〃涓嶈兘涓虹┖")
+    private List<String> glassIds;
+
+    @ApiModelProperty(value = "涓婂ぇ杞︿綅缃紪鐮�")
+    private String positionCode;
+
+    @ApiModelProperty(value = "涓婂ぇ杞︿綅缃��")
+    private Integer positionValue;
+
+    @ApiModelProperty(value = "澶х悊鐗囧姞宸ョ被鍨�")
+    private Integer processType;
+
+    @ApiModelProperty(value = "鐜荤拑瀛樺偍浣嶇疆")
+    private Integer storagePosition;
+
+    @ApiModelProperty(value = "鎵ц闂撮殧(姣)")
+    private Integer executionInterval;
+
+    @ApiModelProperty(value = "璁惧绾у埆鍙傛暟瑕嗙洊锛宬ey鍙互鏄澶囩被鍨嬫垨璁惧缂栫爜")
+    private Map<String, Map<String, Object>> deviceOverrides;
+
+    @ApiModelProperty(value = "棰濆閫忎紶鍙傛暟")
+    private Map<String, Object> extra;
+}
+
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/task/entity/MultiDeviceTask.java b/mes-processes/mes-plcSend/src/main/java/com/mes/task/entity/MultiDeviceTask.java
new file mode 100644
index 0000000..55c4fc0
--- /dev/null
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/task/entity/MultiDeviceTask.java
@@ -0,0 +1,85 @@
+package com.mes.task.entity;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 澶氳澶囪仈鍚堟祴璇曚换鍔″疄浣�
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@TableName("multi_device_task")
+@ApiModel(value = "MultiDeviceTask", description = "澶氳澶囪仈鍚堟祴璇曚换鍔¤褰�")
+public class MultiDeviceTask implements Serializable {
+
+    @TableId(value = "id", type = IdType.AUTO)
+    @ApiModelProperty("鑷涓婚敭")
+    private Long id;
+
+    @TableField("task_id")
+    @ApiModelProperty("浠诲姟鍞竴缂栧彿")
+    private String taskId;
+
+    @TableField("group_id")
+    @ApiModelProperty("璁惧缁処D锛堝瓧绗︿覆锛�")
+    private String groupId;
+
+    @TableField("project_id")
+    @ApiModelProperty("鎵�灞為」鐩甀D锛堝瓧绗︿覆锛�")
+    private String projectId;
+
+    @TableField("status")
+    @ApiModelProperty("浠诲姟鐘舵��")
+    private String status;
+
+    @TableField("current_step")
+    @ApiModelProperty("褰撳墠鎵ц姝ラ")
+    private Integer currentStep;
+
+    @TableField("total_steps")
+    @ApiModelProperty("鎬绘楠ゆ暟")
+    private Integer totalSteps;
+
+    @TableField("start_time")
+    @ApiModelProperty("寮�濮嬫椂闂�")
+    private Date startTime;
+
+    @TableField("end_time")
+    @ApiModelProperty("缁撴潫鏃堕棿")
+    private Date endTime;
+
+    @TableField("error_message")
+    @ApiModelProperty("閿欒淇℃伅")
+    private String errorMessage;
+
+    @TableField("result_data")
+    @ApiModelProperty("鎵ц缁撴灉鏁版嵁(JSON)")
+    private String resultData;
+
+    @TableField(value = "created_time", fill = FieldFill.INSERT)
+    @ApiModelProperty("鍒涘缓鏃堕棿")
+    private Date createdTime;
+
+    @TableField(value = "updated_time", fill = FieldFill.INSERT_UPDATE)
+    @ApiModelProperty("鏇存柊鏃堕棿")
+    private Date updatedTime;
+
+    public enum Status {
+        PENDING,
+        RUNNING,
+        COMPLETED,
+        FAILED,
+        CANCELLED
+    }
+}
+
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/task/entity/TaskStepDetail.java b/mes-processes/mes-plcSend/src/main/java/com/mes/task/entity/TaskStepDetail.java
new file mode 100644
index 0000000..73deefa
--- /dev/null
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/task/entity/TaskStepDetail.java
@@ -0,0 +1,89 @@
+package com.mes.task.entity;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 澶氳澶囦换鍔℃楠ゅ疄浣�
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@TableName("task_step_detail")
+@ApiModel(value = "TaskStepDetail", description = "澶氳澶囦换鍔℃楠よ鎯�")
+public class TaskStepDetail implements Serializable {
+
+    @TableId(value = "id", type = IdType.AUTO)
+    @ApiModelProperty("涓婚敭ID")
+    private Long id;
+
+    @TableField("task_id")
+    @ApiModelProperty("鍏宠仈浠诲姟ID")
+    private String taskId;
+
+    @TableField("step_order")
+    @ApiModelProperty("姝ラ椤哄簭")
+    private Integer stepOrder;
+
+    @TableField("device_id")
+    @ApiModelProperty("璁惧ID锛堝瓧绗︿覆锛�")
+    private String deviceId;
+
+    @TableField("step_name")
+    @ApiModelProperty("姝ラ鍚嶇О")
+    private String stepName;
+
+    @TableField("status")
+    @ApiModelProperty("姝ラ鐘舵��")
+    private String status;
+
+    @TableField("start_time")
+    @ApiModelProperty("寮�濮嬫椂闂�")
+    private Date startTime;
+
+    @TableField("end_time")
+    @ApiModelProperty("缁撴潫鏃堕棿")
+    private Date endTime;
+
+    @TableField("duration_ms")
+    @ApiModelProperty("鎵ц鑰楁椂锛堟绉掞級")
+    private Long durationMs;
+
+    @TableField("input_data")
+    @ApiModelProperty("杈撳叆鏁版嵁(JSON)")
+    private String inputData;
+
+    @TableField("output_data")
+    @ApiModelProperty("杈撳嚭鏁版嵁(JSON)")
+    private String outputData;
+
+    @TableField("error_message")
+    @ApiModelProperty("閿欒淇℃伅")
+    private String errorMessage;
+
+    @TableField("retry_count")
+    @ApiModelProperty("閲嶈瘯娆℃暟")
+    private Integer retryCount;
+
+    @TableField(value = "created_time", fill = FieldFill.INSERT)
+    @ApiModelProperty("璁板綍鍒涘缓鏃堕棿")
+    private Date createdTime;
+
+    public enum Status {
+        PENDING,
+        RUNNING,
+        COMPLETED,
+        FAILED,
+        SKIPPED
+    }
+}
+
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/task/mapper/MultiDeviceTaskMapper.java b/mes-processes/mes-plcSend/src/main/java/com/mes/task/mapper/MultiDeviceTaskMapper.java
new file mode 100644
index 0000000..140f2c9
--- /dev/null
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/task/mapper/MultiDeviceTaskMapper.java
@@ -0,0 +1,13 @@
+package com.mes.task.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.mes.task.entity.MultiDeviceTask;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 澶氳澶囦换鍔� Mapper
+ */
+@Mapper
+public interface MultiDeviceTaskMapper extends BaseMapper<MultiDeviceTask> {
+}
+
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/task/mapper/TaskStepDetailMapper.java b/mes-processes/mes-plcSend/src/main/java/com/mes/task/mapper/TaskStepDetailMapper.java
new file mode 100644
index 0000000..5131362
--- /dev/null
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/task/mapper/TaskStepDetailMapper.java
@@ -0,0 +1,13 @@
+package com.mes.task.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.mes.task.entity.TaskStepDetail;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 浠诲姟姝ラ Mapper
+ */
+@Mapper
+public interface TaskStepDetailMapper extends BaseMapper<TaskStepDetail> {
+}
+
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/task/model/TaskExecutionContext.java b/mes-processes/mes-plcSend/src/main/java/com/mes/task/model/TaskExecutionContext.java
new file mode 100644
index 0000000..364db3f
--- /dev/null
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/task/model/TaskExecutionContext.java
@@ -0,0 +1,54 @@
+package com.mes.task.model;
+
+import com.mes.task.dto.TaskParameters;
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.util.CollectionUtils;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * 澶氳澶囦换鍔℃墽琛屼笂涓嬫枃
+ */
+@Getter
+public class TaskExecutionContext {
+
+    private final TaskParameters parameters;
+
+    @Setter
+    private List<String> loadedGlassIds;
+
+    @Setter
+    private List<String> processedGlassIds;
+
+    private final Map<String, Object> sharedData = new ConcurrentHashMap<>();
+
+    public TaskExecutionContext(TaskParameters parameters) {
+        if (parameters == null) {
+            this.parameters = new TaskParameters();
+        } else {
+            this.parameters = parameters;
+        }
+        if (CollectionUtils.isEmpty(this.parameters.getGlassIds())) {
+            this.parameters.setGlassIds(new ArrayList<>());
+        }
+        this.sharedData.put("initialGlassIds", new ArrayList<>(this.parameters.getGlassIds()));
+    }
+
+    public Map<String, Object> getSharedData() {
+        return sharedData;
+    }
+
+    public List<String> getSafeLoadedGlassIds() {
+        return loadedGlassIds == null ? Collections.emptyList() : loadedGlassIds;
+    }
+
+    public List<String> getSafeProcessedGlassIds() {
+        return processedGlassIds == null ? Collections.emptyList() : processedGlassIds;
+    }
+}
+
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/task/model/TaskExecutionResult.java b/mes-processes/mes-plcSend/src/main/java/com/mes/task/model/TaskExecutionResult.java
new file mode 100644
index 0000000..11dcc1a
--- /dev/null
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/task/model/TaskExecutionResult.java
@@ -0,0 +1,42 @@
+package com.mes.task.model;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 浠诲姟鎵ц缁撴灉
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class TaskExecutionResult {
+
+    private boolean success;
+
+    private String message;
+
+    private Map<String, Object> data;
+
+    public static TaskExecutionResult success(Map<String, Object> payload) {
+        return TaskExecutionResult.builder()
+                .success(true)
+                .message("鎵ц瀹屾垚")
+                .data(payload != null ? payload : new HashMap<>())
+                .build();
+    }
+
+    public static TaskExecutionResult failure(String message, Map<String, Object> payload) {
+        return TaskExecutionResult.builder()
+                .success(false)
+                .message(message)
+                .data(payload != null ? payload : new HashMap<>())
+                .build();
+    }
+}
+
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/task/service/MultiDeviceTaskService.java b/mes-processes/mes-plcSend/src/main/java/com/mes/task/service/MultiDeviceTaskService.java
new file mode 100644
index 0000000..774b654
--- /dev/null
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/task/service/MultiDeviceTaskService.java
@@ -0,0 +1,42 @@
+package com.mes.task.service;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.mes.task.dto.MultiDeviceTaskQuery;
+import com.mes.task.dto.MultiDeviceTaskRequest;
+import com.mes.task.entity.MultiDeviceTask;
+import com.mes.task.entity.TaskStepDetail;
+
+import java.util.List;
+
+/**
+ * 澶氳澶囦换鍔℃湇鍔�
+ */
+public interface MultiDeviceTaskService extends IService<MultiDeviceTask> {
+
+    /**
+     * 鍚姩澶氳澶囨祴璇曚换鍔�
+     */
+    MultiDeviceTask startTask(MultiDeviceTaskRequest request);
+
+    /**
+     * 鏍规嵁浠诲姟缂栧彿鑾峰彇浠诲姟
+     */
+    MultiDeviceTask getTaskByTaskId(String taskId);
+
+    /**
+     * 鏌ヨ浠诲姟姝ラ
+     */
+    List<TaskStepDetail> getTaskSteps(String taskId);
+
+    /**
+     * 鍙栨秷浠诲姟
+     */
+    boolean cancelTask(String taskId);
+
+    /**
+     * 鍒嗛〉鏌ヨ浠诲姟
+     */
+    Page<MultiDeviceTask> queryTasks(MultiDeviceTaskQuery query);
+}
+
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
new file mode 100644
index 0000000..6e6bcbd
--- /dev/null
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/task/service/TaskExecutionEngine.java
@@ -0,0 +1,378 @@
+package com.mes.task.service;
+
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.mes.device.entity.DeviceConfig;
+import com.mes.device.entity.DeviceGroupConfig;
+import com.mes.interaction.DeviceInteraction;
+import com.mes.interaction.DeviceInteractionRegistry;
+import com.mes.interaction.DeviceLogicHandler;
+import com.mes.interaction.DeviceLogicHandlerFactory;
+import com.mes.interaction.base.InteractionContext;
+import com.mes.interaction.base.InteractionResult;
+import com.mes.device.service.DeviceInteractionService;
+import com.mes.task.dto.TaskParameters;
+import com.mes.task.entity.MultiDeviceTask;
+import com.mes.task.entity.TaskStepDetail;
+import com.mes.task.mapper.MultiDeviceTaskMapper;
+import com.mes.task.mapper.TaskStepDetailMapper;
+import com.mes.task.model.TaskExecutionContext;
+import com.mes.task.model.TaskExecutionResult;
+import com.mes.device.vo.DevicePlcVO;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.StringUtils;
+
+import java.util.*;
+
+/**
+ * 澶氳澶囦换鍔℃墽琛屽紩鎿�
+ */
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class TaskExecutionEngine {
+
+    private static final Map<String, String> DEFAULT_OPERATIONS = new HashMap<>();
+
+    static {
+        DEFAULT_OPERATIONS.put(DeviceConfig.DeviceType.LOAD_VEHICLE, "feedGlass");
+        DEFAULT_OPERATIONS.put(DeviceConfig.DeviceType.LARGE_GLASS, "processGlass");
+        DEFAULT_OPERATIONS.put(DeviceConfig.DeviceType.GLASS_STORAGE, "storeGlass");
+    }
+
+    private final TaskStepDetailMapper taskStepDetailMapper;
+    private final MultiDeviceTaskMapper multiDeviceTaskMapper;
+    private final DeviceInteractionService deviceInteractionService;
+    private final DeviceInteractionRegistry interactionRegistry;
+    private final DeviceLogicHandlerFactory handlerFactory;
+    private final ObjectMapper objectMapper;
+
+    public TaskExecutionResult execute(MultiDeviceTask task,
+                                       DeviceGroupConfig groupConfig,
+                                       List<DeviceConfig> devices,
+                                       TaskParameters parameters) {
+
+        if (CollectionUtils.isEmpty(devices)) {
+            return TaskExecutionResult.failure("璁惧缁勬湭閰嶇疆璁惧锛屾棤娉曟墽琛屼换鍔�", Collections.emptyMap());
+        }
+
+        TaskExecutionContext context = new TaskExecutionContext(parameters);
+        task.setTotalSteps(devices.size());
+        task.setStatus(MultiDeviceTask.Status.RUNNING.name());
+        multiDeviceTaskMapper.updateById(task);
+
+        List<Map<String, Object>> stepSummaries = new ArrayList<>();
+        boolean success = true;
+        String failureMessage = null;
+
+        for (int i = 0; i < devices.size(); i++) {
+            DeviceConfig device = devices.get(i);
+            int order = i + 1;
+            TaskStepDetail step = createStepRecord(task, device, order);
+            StepResult stepResult = executeStep(task, step, device, context);
+            stepSummaries.add(stepResult.toSummary());
+            if (!stepResult.isSuccess()) {
+                success = false;
+                failureMessage = stepResult.getMessage();
+                break;
+            }
+        }
+
+        Map<String, Object> payload = new HashMap<>();
+        payload.put("steps", stepSummaries);
+        payload.put("groupId", groupConfig.getId());
+        payload.put("deviceCount", devices.size());
+
+        if (success) {
+            return TaskExecutionResult.success(payload);
+        }
+        return TaskExecutionResult.failure(failureMessage != null ? failureMessage : "浠诲姟鎵ц澶辫触", payload);
+    }
+
+    private TaskStepDetail createStepRecord(MultiDeviceTask task, DeviceConfig device, int order) {
+        TaskStepDetail step = new TaskStepDetail();
+        step.setTaskId(task.getTaskId());
+        step.setStepOrder(order);
+        step.setDeviceId(String.valueOf(device.getId()));
+        step.setStepName(device.getDeviceName());
+        step.setStatus(TaskStepDetail.Status.PENDING.name());
+        step.setRetryCount(0);
+        taskStepDetailMapper.insert(step);
+        return step;
+    }
+
+    private StepResult executeStep(MultiDeviceTask task,
+                                   TaskStepDetail step,
+                                   DeviceConfig device,
+                                   TaskExecutionContext context) {
+        Date startTime = new Date();
+        step.setStartTime(startTime);
+        step.setStatus(TaskStepDetail.Status.RUNNING.name());
+
+        DeviceInteraction deviceInteraction = interactionRegistry.getInteraction(device.getDeviceType());
+        if (deviceInteraction != null) {
+            return executeInteractionStep(task, step, device, context, deviceInteraction);
+        }
+
+        Map<String, Object> params = buildOperationParams(device, context);
+        step.setInputData(toJson(params));
+        taskStepDetailMapper.updateById(step);
+
+        String operation = determineOperation(device, params);
+        DeviceLogicHandler handler = handlerFactory.getHandler(device.getDeviceType());
+        DevicePlcVO.OperationResult result;
+
+        try {
+            if (handler == null) {
+                result = deviceInteractionService.executeOperation(device.getId(), operation, params);
+            } else {
+                result = handler.execute(device, operation, params);
+            }
+
+            boolean opSuccess = Boolean.TRUE.equals(result.getSuccess());
+            updateStepAfterOperation(step, result, opSuccess);
+            updateTaskProgress(task, step.getStepOrder(), opSuccess);
+
+            if (opSuccess) {
+                updateContextAfterSuccess(device, context, params);
+                return StepResult.success(device.getDeviceName(), result.getMessage());
+            }
+            return StepResult.failure(device.getDeviceName(), result.getMessage());
+        } catch (Exception e) {
+            log.error("璁惧鎿嶄綔寮傚父, deviceId={}, operation={}", device.getId(), operation, e);
+            step.setStatus(TaskStepDetail.Status.FAILED.name());
+            step.setErrorMessage(e.getMessage());
+            step.setEndTime(new Date());
+            step.setDurationMs(step.getEndTime().getTime() - step.getStartTime().getTime());
+            taskStepDetailMapper.updateById(step);
+            updateTaskProgress(task, step.getStepOrder(), false);
+            return StepResult.failure(device.getDeviceName(), e.getMessage());
+        }
+    }
+
+    private StepResult executeInteractionStep(MultiDeviceTask task,
+                                              TaskStepDetail step,
+                                              DeviceConfig device,
+                                              TaskExecutionContext context,
+                                              DeviceInteraction deviceInteraction) {
+        try {
+            InteractionContext interactionContext = new InteractionContext(device, context);
+            step.setInputData(toJson(context.getParameters()));
+            InteractionResult interactionResult = deviceInteraction.execute(interactionContext);
+            boolean success = interactionResult != null && interactionResult.isSuccess();
+            updateStepAfterInteraction(step, interactionResult);
+            updateTaskProgress(task, step.getStepOrder(), success);
+
+            if (success) {
+                return StepResult.success(device.getDeviceName(), interactionResult.getMessage());
+            }
+            String message = interactionResult != null ? interactionResult.getMessage() : "浜や簰鎵ц澶辫触";
+            return StepResult.failure(device.getDeviceName(), message);
+        } catch (Exception e) {
+            log.error("浜や簰鎵ц寮傚父, deviceId={}", device.getId(), e);
+            step.setStatus(TaskStepDetail.Status.FAILED.name());
+            step.setErrorMessage(e.getMessage());
+            step.setEndTime(new Date());
+            step.setDurationMs(step.getEndTime().getTime() - step.getStartTime().getTime());
+            taskStepDetailMapper.updateById(step);
+            updateTaskProgress(task, step.getStepOrder(), false);
+            return StepResult.failure(device.getDeviceName(), e.getMessage());
+        }
+    }
+
+    private void updateStepAfterOperation(TaskStepDetail step,
+                                          DevicePlcVO.OperationResult result,
+                                          boolean success) {
+        step.setEndTime(new Date());
+        if (step.getStartTime() != null) {
+            step.setDurationMs(step.getEndTime().getTime() - step.getStartTime().getTime());
+        }
+        step.setStatus(success ? TaskStepDetail.Status.COMPLETED.name() : TaskStepDetail.Status.FAILED.name());
+        step.setErrorMessage(success ? null : result.getMessage());
+        step.setOutputData(toJson(result));
+        taskStepDetailMapper.updateById(step);
+    }
+
+    private void updateStepAfterInteraction(TaskStepDetail step,
+                                            InteractionResult result) {
+        step.setEndTime(new Date());
+        if (step.getStartTime() != null) {
+            step.setDurationMs(step.getEndTime().getTime() - step.getStartTime().getTime());
+        }
+        boolean success = result != null && result.isSuccess();
+        step.setStatus(success ? TaskStepDetail.Status.COMPLETED.name() : TaskStepDetail.Status.FAILED.name());
+        step.setErrorMessage(success ? null : (result != null ? result.getMessage() : "浜や簰鎵ц澶辫触"));
+        step.setOutputData(result != null ? toJson(result.getData()) : "{}");
+        taskStepDetailMapper.updateById(step);
+    }
+
+    private void updateTaskProgress(MultiDeviceTask task, int currentStep, boolean success) {
+        task.setCurrentStep(currentStep);
+        if (!success) {
+            task.setStatus(MultiDeviceTask.Status.FAILED.name());
+        }
+        LambdaUpdateWrapper<MultiDeviceTask> update = Wrappers.<MultiDeviceTask>lambdaUpdate()
+                .eq(MultiDeviceTask::getId, task.getId())
+                .set(MultiDeviceTask::getCurrentStep, currentStep);
+        if (!success) {
+            update.set(MultiDeviceTask::getStatus, MultiDeviceTask.Status.FAILED.name());
+        }
+        multiDeviceTaskMapper.update(null, update);
+    }
+
+    private String determineOperation(DeviceConfig device, Map<String, Object> params) {
+        if (params != null && params.containsKey("operation")) {
+            Object op = params.get("operation");
+            if (op != null) {
+                return String.valueOf(op);
+            }
+        }
+        return DEFAULT_OPERATIONS.getOrDefault(device.getDeviceType(), "feedGlass");
+    }
+
+    private Map<String, Object> buildOperationParams(DeviceConfig device, TaskExecutionContext context) {
+        Map<String, Object> params = new HashMap<>();
+        TaskParameters taskParams = context.getParameters();
+
+        switch (device.getDeviceType()) {
+            case DeviceConfig.DeviceType.LOAD_VEHICLE:
+                params.put("glassIds", new ArrayList<>(taskParams.getGlassIds()));
+                if (StringUtils.hasText(taskParams.getPositionCode())) {
+                    params.put("positionCode", taskParams.getPositionCode());
+                }
+                if (taskParams.getPositionValue() != null) {
+                    params.put("positionValue", taskParams.getPositionValue());
+                }
+                params.put("triggerRequest", true);
+                break;
+            case DeviceConfig.DeviceType.LARGE_GLASS:
+                List<String> source = context.getSafeLoadedGlassIds();
+                if (CollectionUtils.isEmpty(source)) {
+                    source = taskParams.getGlassIds();
+                }
+                if (!CollectionUtils.isEmpty(source)) {
+                    params.put("glassId", source.get(0));
+                    params.put("glassIds", new ArrayList<>(source));
+                }
+                params.put("processType", taskParams.getProcessType() != null ? taskParams.getProcessType() : 1);
+                params.put("triggerRequest", true);
+                break;
+            case DeviceConfig.DeviceType.GLASS_STORAGE:
+                List<String> processed = context.getSafeProcessedGlassIds();
+                if (CollectionUtils.isEmpty(processed)) {
+                    processed = context.getSafeLoadedGlassIds();
+                }
+                if (!CollectionUtils.isEmpty(processed)) {
+                    params.put("glassId", processed.get(0));
+                    params.put("glassIds", new ArrayList<>(processed));
+                }
+                if (taskParams.getStoragePosition() != null) {
+                    params.put("storagePosition", taskParams.getStoragePosition());
+                }
+                params.put("triggerRequest", true);
+                break;
+            default:
+                if (!CollectionUtils.isEmpty(taskParams.getExtra())) {
+                    params.putAll(taskParams.getExtra());
+                }
+        }
+
+        mergeOverrides(device, taskParams, params);
+        return params;
+    }
+
+    private void mergeOverrides(DeviceConfig device, TaskParameters taskParameters, Map<String, Object> params) {
+        if (CollectionUtils.isEmpty(taskParameters.getDeviceOverrides())) {
+            return;
+        }
+        Map<String, Object> override = taskParameters.getDeviceOverrides().get(device.getDeviceType());
+        if (override == null && StringUtils.hasText(device.getDeviceCode())) {
+            override = taskParameters.getDeviceOverrides().get(device.getDeviceCode());
+        }
+        if (override != null) {
+            params.putAll(override);
+        }
+    }
+
+    private void updateContextAfterSuccess(DeviceConfig device,
+                                           TaskExecutionContext context,
+                                           Map<String, Object> params) {
+        switch (device.getDeviceType()) {
+            case DeviceConfig.DeviceType.LOAD_VEHICLE:
+                context.setLoadedGlassIds(extractGlassIds(params));
+                break;
+            case DeviceConfig.DeviceType.LARGE_GLASS:
+                context.setProcessedGlassIds(extractGlassIds(params));
+                break;
+            default:
+                break;
+        }
+    }
+
+    private List<String> extractGlassIds(Map<String, Object> params) {
+        if (params == null) {
+            return Collections.emptyList();
+        }
+        Object glassIds = params.get("glassIds");
+        if (glassIds instanceof List) {
+            @SuppressWarnings("unchecked")
+            List<String> cast = (List<String>) glassIds;
+            return new ArrayList<>(cast);
+        }
+        Object glassId = params.get("glassId");
+        if (glassId != null) {
+            return Collections.singletonList(String.valueOf(glassId));
+        }
+        return Collections.emptyList();
+    }
+
+    private String toJson(Object value) {
+        try {
+            return objectMapper.writeValueAsString(value);
+        } catch (JsonProcessingException e) {
+            return "{}";
+        }
+    }
+
+    private static class StepResult {
+        private final boolean success;
+        private final String message;
+        private final String deviceName;
+
+        private StepResult(boolean success, String message, String deviceName) {
+            this.success = success;
+            this.message = message;
+            this.deviceName = deviceName;
+        }
+
+        public static StepResult success(String deviceName, String message) {
+            return new StepResult(true, message, deviceName);
+        }
+
+        public static StepResult failure(String deviceName, String message) {
+            return new StepResult(false, message, deviceName);
+        }
+
+        public boolean isSuccess() {
+            return success;
+        }
+
+        public String getMessage() {
+            return message;
+        }
+
+        public Map<String, Object> toSummary() {
+            Map<String, Object> summary = new HashMap<>();
+            summary.put("deviceName", deviceName);
+            summary.put("success", success);
+            summary.put("message", message);
+            return summary;
+        }
+    }
+}
+
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
new file mode 100644
index 0000000..f879887
--- /dev/null
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/task/service/impl/MultiDeviceTaskServiceImpl.java
@@ -0,0 +1,160 @@
+package com.mes.task.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.mes.device.entity.DeviceConfig;
+import com.mes.device.entity.DeviceGroupConfig;
+import com.mes.device.mapper.DeviceGroupRelationMapper;
+import com.mes.device.service.DeviceGroupConfigService;
+import com.mes.task.dto.MultiDeviceTaskQuery;
+import com.mes.task.dto.MultiDeviceTaskRequest;
+import com.mes.task.dto.TaskParameters;
+import com.mes.task.entity.MultiDeviceTask;
+import com.mes.task.entity.TaskStepDetail;
+import com.mes.task.mapper.MultiDeviceTaskMapper;
+import com.mes.task.mapper.TaskStepDetailMapper;
+import com.mes.task.model.TaskExecutionResult;
+import com.mes.task.service.MultiDeviceTaskService;
+import com.mes.task.service.TaskExecutionEngine;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.StringUtils;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * 澶氳澶囦换鍔℃湇鍔″疄鐜�
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class MultiDeviceTaskServiceImpl extends ServiceImpl<MultiDeviceTaskMapper, MultiDeviceTask>
+        implements MultiDeviceTaskService {
+
+    private final DeviceGroupConfigService deviceGroupConfigService;
+    private final DeviceGroupRelationMapper deviceGroupRelationMapper;
+    private final TaskStepDetailMapper taskStepDetailMapper;
+    private final TaskExecutionEngine taskExecutionEngine;
+    private final ObjectMapper objectMapper;
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public MultiDeviceTask startTask(MultiDeviceTaskRequest request) {
+        DeviceGroupConfig groupConfig = deviceGroupConfigService.getDeviceGroupById(request.getGroupId());
+        if (groupConfig == null) {
+            throw new IllegalArgumentException("璁惧缁勪笉瀛樺湪: " + request.getGroupId());
+        }
+        if (groupConfig.getStatus() != DeviceGroupConfig.Status.ENABLED) {
+            throw new IllegalStateException("璁惧缁勬湭鍚敤锛屾棤娉曟墽琛屼换鍔�");
+        }
+
+        List<DeviceConfig> devices = deviceGroupRelationMapper.getOrderedDeviceConfigs(groupConfig.getId());
+        if (CollectionUtils.isEmpty(devices)) {
+            throw new IllegalStateException("璁惧缁勬湭閰嶇疆浠讳綍璁惧锛屾棤娉曟墽琛屼换鍔�");
+        }
+
+        TaskParameters parameters = request.getParameters();
+        if (parameters == null || CollectionUtils.isEmpty(parameters.getGlassIds())) {
+            throw new IllegalArgumentException("鑷冲皯闇�瑕侀厤缃竴鏉$幓鐠僆D");
+        }
+
+        MultiDeviceTask task = new MultiDeviceTask();
+        task.setTaskId(generateTaskId(groupConfig));
+        task.setGroupId(String.valueOf(groupConfig.getId()));
+        task.setProjectId(String.valueOf(groupConfig.getProjectId()));
+        task.setStatus(MultiDeviceTask.Status.PENDING.name());
+        task.setCurrentStep(0);
+        task.setTotalSteps(devices.size());
+        task.setStartTime(new Date());
+        save(task);
+
+        try {
+            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());
+            task.setResultData(writeJson(result.getData()));
+            updateById(task);
+            return task;
+        } catch (Exception ex) {
+            log.error("澶氳澶囦换鍔℃墽琛屽紓甯�, taskId={}", task.getTaskId(), ex);
+            task.setStatus(MultiDeviceTask.Status.FAILED.name());
+            task.setErrorMessage(ex.getMessage());
+            task.setEndTime(new Date());
+            updateById(task);
+            throw new RuntimeException("澶氳澶囦换鍔℃墽琛屽け璐�: " + ex.getMessage(), ex);
+        }
+    }
+
+    @Override
+    public MultiDeviceTask getTaskByTaskId(String taskId) {
+        LambdaQueryWrapper<MultiDeviceTask> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(MultiDeviceTask::getTaskId, taskId);
+        return getOne(wrapper);
+    }
+
+    @Override
+    public List<TaskStepDetail> getTaskSteps(String taskId) {
+        LambdaQueryWrapper<TaskStepDetail> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(TaskStepDetail::getTaskId, taskId);
+        wrapper.orderByAsc(TaskStepDetail::getStepOrder);
+        return taskStepDetailMapper.selectList(wrapper);
+    }
+
+    @Override
+    public boolean cancelTask(String taskId) {
+        MultiDeviceTask task = getTaskByTaskId(taskId);
+        if (task == null) {
+            return false;
+        }
+        if (!MultiDeviceTask.Status.RUNNING.name().equals(task.getStatus())) {
+            return false;
+        }
+        task.setStatus(MultiDeviceTask.Status.CANCELLED.name());
+        task.setEndTime(new Date());
+        return updateById(task);
+    }
+
+    @Override
+    public Page<MultiDeviceTask> queryTasks(MultiDeviceTaskQuery query) {
+        int page = query.getPage() != null && query.getPage() > 0 ? query.getPage() : 1;
+        int size = query.getSize() != null && query.getSize() > 0 ? query.getSize() : 10;
+        Page<MultiDeviceTask> pageParam = new Page<>(page, size);
+
+        LambdaQueryWrapper<MultiDeviceTask> wrapper = new LambdaQueryWrapper<>();
+        if (query.getGroupId() != null) {
+            wrapper.eq(MultiDeviceTask::getGroupId, String.valueOf(query.getGroupId()));
+        }
+        if (StringUtils.hasText(query.getStatus())) {
+            wrapper.eq(MultiDeviceTask::getStatus, query.getStatus().toUpperCase(Locale.ROOT));
+        }
+        wrapper.orderByDesc(MultiDeviceTask::getCreatedTime);
+        return page(pageParam, wrapper);
+    }
+
+    private String generateTaskId(DeviceGroupConfig groupConfig) {
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss", Locale.CHINA);
+        return "TASK_" + groupConfig.getId() + "_" + sdf.format(new Date());
+    }
+
+    private String writeJson(Object data) {
+        if (data == null) {
+            return "{}";
+        }
+        try {
+            return objectMapper.writeValueAsString(data);
+        } catch (JsonProcessingException e) {
+            return "{}";
+        }
+    }
+}
+
diff --git a/mes-processes/mes-plcSend/src/main/resources/application-dev.yml b/mes-processes/mes-plcSend/src/main/resources/application-dev.yml
index 51d11e7..bb9e3bf 100644
--- a/mes-processes/mes-plcSend/src/main/resources/application-dev.yml
+++ b/mes-processes/mes-plcSend/src/main/resources/application-dev.yml
@@ -8,7 +8,7 @@
       strict: false #璁剧疆涓ユ牸妯″紡,榛樿false涓嶅惎鍔�. 鍚姩鍚庡湪鏈尮閰嶅埌鎸囧畾鏁版嵁婧愭椂鍊欏洖鎶涘嚭寮傚父,涓嶅惎鍔ㄤ細浣跨敤榛樿鏁版嵁婧�.
       datasource:
         northGlassMes:
-          url: jdbc:mysql://${ip}:${port}/mes_modular?serverTimezone=GMT%2b8
+          url: jdbc:mysql://${ip}:${port}/mes_test?serverTimezone=GMT%2b8
           username: root
           password: beibo.123/
           driver-class-name: com.mysql.cj.jdbc.Driver
diff --git a/mes-web/src/api/device/deviceManagement.js b/mes-web/src/api/device/deviceManagement.js
index 13dd443..48c27ee 100644
--- a/mes-web/src/api/device/deviceManagement.js
+++ b/mes-web/src/api/device/deviceManagement.js
@@ -200,33 +200,38 @@
   /**
    * 鍒涘缓璁惧缁勯厤缃�
    */
-  create(data) {
+  create(config) {
     return request({
       url: '/api/plcSend/device/group/create',
       method: 'post',
-      data
+      data: {
+        groupConfig: config
+      }
     })
   },
 
   /**
    * 鏇存柊璁惧缁勯厤缃�
    */
-  update(data) {
+  update(id, config) {
     return request({
       url: '/api/plcSend/device/group/update',
       method: 'post',
-      data
+      data: {
+        groupId: id,
+        groupConfig: config
+      }
     })
   },
 
   /**
    * 鍒犻櫎璁惧缁勯厤缃�
    */
-  delete(data) {
+  delete(id) {
     return request({
       url: '/api/plcSend/device/group/delete',
       method: 'post',
-      data
+      data: { groupId: id }
     })
   },
 
@@ -263,44 +268,44 @@
   /**
    * 鍚敤璁惧缁�
    */
-  enable(data) {
+  enable(id) {
     return request({
       url: '/api/plcSend/device/group/enable',
       method: 'post',
-      data
+      data: { groupId: id }
     })
   },
 
   /**
    * 绂佺敤璁惧缁�
    */
-  disable(data) {
+  disable(id) {
     return request({
       url: '/api/plcSend/device/group/disable',
       method: 'post',
-      data
+      data: { groupId: id }
     })
   },
 
   /**
    * 鎵归噺鍚敤璁惧缁�
    */
-  batchEnable(data) {
+  batchEnable(groupIds) {
     return request({
       url: '/api/plcSend/device/group/batch-enable',
       method: 'post',
-      data
+      data: { groupIds }
     })
   },
 
   /**
    * 鎵归噺绂佺敤璁惧缁�
    */
-  batchDisable(data) {
+  batchDisable(groupIds) {
     return request({
       url: '/api/plcSend/device/group/batch-disable',
       method: 'post',
-      data
+      data: { groupIds }
     })
   },
 
@@ -526,6 +531,32 @@
   }
 }
 
+// 璁惧浜や簰鎿嶄綔API
+export const deviceInteractionApi = {
+  /**
+   * 鎵ц璁惧閫昏緫鎿嶄綔
+   * @param {Object} data - { deviceId, operation, params }
+   */
+  executeOperation(data) {
+    return request({
+      url: '/api/plcSend/device/interaction/execute',
+      method: 'post',
+      data
+    })
+  },
+
+  /**
+   * 鐜荤拑涓婃枡鍐欏叆
+   */
+  feedGlass(data) {
+    return request({
+      url: '/api/plcSend/device/interaction/glass-feed',
+      method: 'post',
+      data
+    })
+  }
+}
+
 // 缁熻API
 export const getDeviceStatistics = (data) => {
   return request({
@@ -547,6 +578,7 @@
   deviceConfigApi,
   deviceGroupApi,
   devicePlcApi,
+  deviceInteractionApi,
   getDeviceStatistics,
   getDeviceGroupStatistics
 }
\ No newline at end of file
diff --git a/mes-web/src/api/device/multiDeviceTask.js b/mes-web/src/api/device/multiDeviceTask.js
new file mode 100644
index 0000000..6d3f05d
--- /dev/null
+++ b/mes-web/src/api/device/multiDeviceTask.js
@@ -0,0 +1,58 @@
+import request from '@/utils/request'
+
+const BASE_URL = '/api/plcSend/device/task'
+
+export const multiDeviceTaskApi = {
+  /**
+   * 鍚姩澶氳澶囦换鍔�
+   */
+  startTask(data) {
+    return request({
+      url: `${BASE_URL}/start`,
+      method: 'post',
+      data
+    })
+  },
+
+  /**
+   * 鏌ヨ浠诲姟鍒楄〃
+   */
+  getTaskList(params) {
+    return request({
+      url: `${BASE_URL}/list`,
+      method: 'post',
+      data: params
+    })
+  },
+
+  /**
+   * 鏌ヨ浠诲姟璇︽儏
+   */
+  getTaskById(taskId) {
+    return request({
+      url: `${BASE_URL}/${taskId}`,
+      method: 'get'
+    })
+  },
+
+  /**
+   * 鏌ヨ浠诲姟姝ラ
+   */
+  getTaskSteps(taskId) {
+    return request({
+      url: `${BASE_URL}/${taskId}/steps`,
+      method: 'get'
+    })
+  },
+
+  /**
+   * 鍙栨秷浠诲姟
+   */
+  cancelTask(taskId) {
+    return request({
+      url: `${BASE_URL}/${taskId}/cancel`,
+      method: 'post'
+    })
+  }
+}
+
diff --git a/mes-web/src/router/index.js b/mes-web/src/router/index.js
index 867ce2c..382db8f 100644
--- a/mes-web/src/router/index.js
+++ b/mes-web/src/router/index.js
@@ -34,6 +34,16 @@
               name: 'plcTest',
               component: () => import('../views/plcTest/Test.vue')
             },
+            {
+              path: '/plcTest/MultiDeviceWorkbench',
+              name: 'MultiDeviceWorkbench',
+              component: () => import('../views/plcTest/MultiDeviceWorkbench.vue')
+            },
+            {
+              path: '/device/DeviceManagement',
+              name: 'DeviceManagement',
+              component: () => import('../views/device/DeviceManagement.vue')
+            }
           ]
         },
 
diff --git a/mes-web/src/utils/constants.js b/mes-web/src/utils/constants.js
index 41682c9..ad93a89 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.213";//hxl
+export const WebSocketHost = "10.153.19.49";//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/DeviceEditDialog.vue b/mes-web/src/views/device/DeviceEditDialog.vue
index 773dd73..881ab73 100644
--- a/mes-web/src/views/device/DeviceEditDialog.vue
+++ b/mes-web/src/views/device/DeviceEditDialog.vue
@@ -106,13 +106,20 @@
             </el-form-item>
 
             <el-form-item label="閫氳鍗忚" prop="protocolType">
-              <el-select v-model="deviceForm.protocolType" placeholder="閫夋嫨閫氳鍗忚" style="width: 100%;">
+              <el-select 
+                v-model="deviceForm.protocolType" 
+                placeholder="閫夋嫨閫氳鍗忚" 
+                style="width: 100%;"
+                @change="handleProtocolTypeChange"
+              >
+                <el-option label="S7 Communication" value="S7 Communication" />
                 <el-option label="Modbus TCP" value="Modbus TCP" />
                 <el-option label="OPC UA" value="OPC UA" />
                 <el-option label="EtherNet/IP" value="EtherNet/IP" />
                 <el-option label="Profinet" value="Profinet" />
                 <el-option label="鍏朵粬" value="鍏朵粬" />
               </el-select>
+              <span class="form-tip">S7绯诲垪PLC閫氬父浣跨敤S7 Communication鍗忚</span>
             </el-form-item>
 
             <el-form-item label="瓒呮椂鏃堕棿(绉�)" prop="timeout">
@@ -240,6 +247,200 @@
         </div>
       </el-card>
 
+      <!-- 璁惧閫昏緫閰嶇疆 -->
+      <el-card class="form-section" shadow="never" style="margin-top: 20px;" v-if="deviceForm.deviceType">
+        <template #header>
+          <span class="section-title">璁惧閫昏緫閰嶇疆</span>
+          <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%;"
+                />
+                <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="鑷姩涓婃枡">
+                <el-switch v-model="deviceLogicParams.autoFeed" />
+                <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>
+          <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>
+      </el-card>
+
       <!-- 鎻忚堪淇℃伅 -->
       <el-card class="form-section" shadow="never" style="margin-top: 20px;">
         <template #header>
@@ -317,6 +518,28 @@
 const saving = ref(false)
 const testing = ref(false)
 const testResult = ref(null)
+
+// 璁惧閫昏緫鍙傛暟锛堟牴鎹澶囩被鍨嬪姩鎬佹樉绀猴級
+const deviceLogicParams = reactive({
+  // 涓婂ぇ杞﹀弬鏁�
+  vehicleCapacity: 6000,
+  glassIntervalMs: 1000,
+  autoFeed: true,
+  maxRetryCount: 5,
+  positionMapping: {},
+  // 澶х悊鐗囧弬鏁�
+  glassSize: 2000,
+  processingTime: 5000,
+  autoProcess: true,
+  // 鐜荤拑瀛樺偍鍙傛暟
+  storageCapacity: 100,
+  retrievalMode: 'FIFO',
+  autoStore: true,
+  autoRetrieve: true
+})
+
+// 浣嶇疆鏄犲皠鐨勯敭鏁扮粍锛堢敤浜巚-for锛�
+const mappingKeys = ref([])
 
 // 璁惧琛ㄥ崟鏁版嵁
 const getDefaultForm = () => ({
@@ -420,6 +643,27 @@
   emit('update:modelValue', newVal)
 })
 
+// 鐩戝惉PLC绫诲瀷鍙樺寲锛岃嚜鍔ㄨ缃�氳鍗忚
+watch(() => deviceForm.plcType, (newPlcType) => {
+  // 濡傛灉閫夋嫨鐨勬槸S7绯诲垪PLC锛岃嚜鍔ㄨ缃�氳鍗忚涓篠7 Communication
+  if (newPlcType && (newPlcType.startsWith('S') || newPlcType.includes('S7'))) {
+    if (!deviceForm.protocolType || deviceForm.protocolType === '鍏朵粬') {
+      deviceForm.protocolType = 'S7 Communication'
+    }
+  }
+})
+
+// 澶勭悊閫氳鍗忚鍙樺寲
+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‘')
+    }
+  }
+}
+
 // 鏂规硶瀹氫箟
 const parseJsonSafe = (str, defaultValue = null) => {
   if (!str) return defaultValue
@@ -453,11 +697,182 @@
   deviceForm.dbArea = plcConfig.dbArea || 'DB1'
   deviceForm.beginIndex = plcConfig.beginIndex ?? 0
   deviceForm.autoModeInterval = plcConfig.autoModeInterval ?? 5000
+
+  // 鍔犺浇閰嶇疆鍙傛暟锛堜粠 configJson锛�
+  // 鍏煎涓ょ鏍煎紡锛�
+  // 1. 鏁扮粍鏍煎紡锛歔{ paramKey, paramValue, description }]
+  // 2. 瀵硅薄鏍煎紡锛堟棫鏍煎紡锛夛細{ fieldName: offset } - 鑷姩杞崲涓烘暟缁勬牸寮�
+  loadConfigParams(data?.configJson)
+
+  // 鍔犺浇璁惧閫昏緫鍙傛暟
+  const deviceLogic = extraObj.deviceLogic || {}
+  loadDeviceLogicParams(deviceLogic, data?.deviceType)
+}
+
+// 鍔犺浇閰嶇疆鍙傛暟锛堝吋瀹规棫鐨勫璞℃牸寮忥級
+const loadConfigParams = (configJson) => {
+  if (!configJson) {
+    deviceForm.configParams = []
+    return
+  }
+
+  try {
+    const parsed = typeof configJson === 'string' ? JSON.parse(configJson) : configJson
+    
+    // 濡傛灉鏄暟缁勬牸寮忥紝鐩存帴浣跨敤
+    if (Array.isArray(parsed)) {
+      deviceForm.configParams = parsed
+    } 
+    // 濡傛灉鏄璞℃牸寮忥紙瀛楁鍚� 鈫� 鍋忕Щ閲忥級锛岃浆鎹负鏁扮粍鏍煎紡
+    else if (typeof parsed === 'object' && parsed !== null) {
+      // 瀛楁鍚嶅埌涓枃鎻忚堪鐨勬槧灏�
+      const fieldDescriptionMap = {
+        'plcRequest': 'PLC璇锋眰瀛�',
+        'inPosition': '杩涚墖浣嶇疆',
+        'plcGlassId1': '鐜荤拑id1',
+        'plcGlassId2': '鐜荤拑id2',
+        'plcGlassId3': '鐜荤拑id3',
+        'plcGlassId4': '鐜荤拑id4',
+        'plcGlassId5': '鐜荤拑id5',
+        'plcGlassId6': '鐜荤拑id6',
+        'plcGlassCount': '鐜荤拑鏁伴噺',
+        'onlineState': '鑱旀満鐘舵��',
+        'plcReport': 'PLC姹囨姤',
+        'state1': '鐘舵��1',
+        'state2': '鐘舵��2',
+        'state3': '鐘舵��3',
+        'state4': '鐘舵��4',
+        'state5': '鐘舵��5',
+        'state6': '鐘舵��6',
+        'mesSend': 'MES鍙戦��',
+        'mesConfirm': 'MES纭',
+        'trainInfo': '鍒楄溅淇℃伅',
+        'start1': '璧峰1',
+        'start2': '璧峰2',
+        'start3': '璧峰3',
+        'start4': '璧峰4',
+        'start5': '璧峰5',
+        'start6': '璧峰6',
+        'target1': '鐩爣1',
+        'target2': '鐩爣2',
+        'target3': '鐩爣3',
+        'target4': '鐩爣4',
+        'target5': '鐩爣5',
+        'target6': '鐩爣6',
+        'mesWidth1': 'MES瀹藉害1',
+        'mesWidth2': 'MES瀹藉害2',
+        'mesWidth3': 'MES瀹藉害3',
+        'mesWidth4': 'MES瀹藉害4',
+        'mesWidth5': 'MES瀹藉害5',
+        'mesWidth6': 'MES瀹藉害6',
+        'mesHeight1': 'MES楂樺害1',
+        'mesHeight2': 'MES楂樺害2',
+        'mesHeight3': 'MES楂樺害3',
+        'mesHeight4': 'MES楂樺害4',
+        'mesHeight5': 'MES楂樺害5',
+        'mesHeight6': 'MES楂樺害6',
+        'mesThickness1': 'MES鍘氬害1',
+        'mesThickness2': 'MES鍘氬害2',
+        'mesThickness3': 'MES鍘氬害3',
+        'mesThickness4': 'MES鍘氬害4',
+        'mesThickness5': 'MES鍘氬害5',
+        'mesThickness6': 'MES鍘氬害6',
+        'edgeDistance1': '杈圭紭璺濈1',
+        'edgeDistance2': '杈圭紭璺濈2',
+        'edgeDistance3': '杈圭紭璺濈3',
+        'edgeDistance4': '杈圭紭璺濈4',
+        'edgeDistance5': '杈圭紭璺濈5',
+        'edgeDistance6': '杈圭紭璺濈6',
+        'targetEdgeDistance1': '鐩爣杈圭紭璺濈1',
+        'targetEdgeDistance2': '鐩爣杈圭紭璺濈2',
+        'targetEdgeDistance3': '鐩爣杈圭紭璺濈3',
+        'targetEdgeDistance4': '鐩爣杈圭紭璺濈4',
+        'targetEdgeDistance5': '鐩爣杈圭紭璺濈5',
+        'targetEdgeDistance6': '鐩爣杈圭紭璺濈6',
+        'alarmInfo': '鎶ヨ淇℃伅'
+      }
+
+      // 杞崲涓烘暟缁勬牸寮�
+      deviceForm.configParams = Object.keys(parsed).map(fieldName => ({
+        paramKey: fieldName,
+        paramValue: String(parsed[fieldName]),
+        description: fieldDescriptionMap[fieldName] || fieldName
+      }))
+    } else {
+      deviceForm.configParams = []
+    }
+  } catch (error) {
+    console.warn('瑙f瀽configJson澶辫触', error)
+    deviceForm.configParams = []
+  }
+}
+
+// 鍔犺浇璁惧閫昏緫鍙傛暟
+const loadDeviceLogicParams = (deviceLogic, deviceType) => {
+  if (deviceType === '涓婂ぇ杞�') {
+    deviceLogicParams.vehicleCapacity = deviceLogic.vehicleCapacity ?? 6000
+    deviceLogicParams.glassIntervalMs = deviceLogic.glassIntervalMs ?? 1000
+    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
+  }
+}
+
+// 浣嶇疆鏄犲皠鐩稿叧鏂规硶
+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.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
 }
 
 const addConfigParam = () => {
@@ -531,6 +946,44 @@
     plcType: deviceForm.plcType
   }
 
+    // 淇濆瓨璁惧閫昏緫鍙傛暟
+    const deviceLogic = {}
+    if (deviceForm.deviceType === '涓婂ぇ杞�') {
+      deviceLogic.vehicleCapacity = deviceLogicParams.vehicleCapacity
+      deviceLogic.glassIntervalMs = deviceLogicParams.glassIntervalMs
+      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
+    }
+
+    // 鏋勫缓 configJson锛氬皢 configParams 鏁扮粍杞崲涓� JSON 瀛楃涓�
+    // configParams 缁撴瀯: [{ paramKey: '', paramValue: '', description: '' }]
+    let configJsonValue = null
+    if (deviceForm.configParams && deviceForm.configParams.length > 0) {
+      // 杩囨护鎺夌┖鍙傛暟
+      const validParams = deviceForm.configParams.filter(
+        param => param.paramKey && param.paramKey.trim() !== ''
+      )
+      if (validParams.length > 0) {
+        configJsonValue = JSON.stringify(validParams)
+      }
+  }
+
     const saveData = {
       deviceName: deviceForm.deviceName,
       deviceCode: deviceForm.deviceCode,
@@ -542,9 +995,7 @@
       isPrimary: deviceForm.isPrimary,
       enabled: deviceForm.enabled,
       description: deviceForm.description,
-      configJson: deviceForm.configParams.length > 0 
-        ? JSON.stringify(deviceForm.configParams) 
-        : null,
+      configJson: configJsonValue,  // 淇濆瓨閰嶇疆鍙傛暟JSON
       extraParams: JSON.stringify(extraObj)
     }
 
@@ -562,6 +1013,18 @@
     handleClose()
   } catch (error) {
     console.error('淇濆瓨璁惧閰嶇疆澶辫触:', error)
+    // 濡傛灉鏄〃鍗曢獙璇侀敊璇紝鏄剧ず鏇磋缁嗙殑閿欒淇℃伅
+    if (error && typeof error === 'object' && !error.response) {
+      const errorFields = Object.keys(error)
+      if (errorFields.length > 0) {
+        const firstError = error[errorFields[0]]
+        const errorMessage = Array.isArray(firstError) 
+          ? firstError[0]?.message || firstError[0] 
+          : firstError?.message || firstError
+        ElMessage.error(`琛ㄥ崟楠岃瘉澶辫触: ${errorMessage}`)
+        return
+      }
+    }
     ElMessage.error(isEdit.value ? '鏇存柊璁惧閰嶇疆澶辫触' : '鍒涘缓璁惧閰嶇疆澶辫触')
   } finally {
     saving.value = false
@@ -647,4 +1110,18 @@
 :deep(.el-card__body) {
   padding: 20px;
 }
+
+.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>
\ No newline at end of file
diff --git a/mes-web/src/views/device/DeviceGroupEditDialog.vue b/mes-web/src/views/device/DeviceGroupEditDialog.vue
index 57dc0c9..6f360bc 100644
--- a/mes-web/src/views/device/DeviceGroupEditDialog.vue
+++ b/mes-web/src/views/device/DeviceGroupEditDialog.vue
@@ -600,19 +600,17 @@
       customParams: form.customParams
     }
     
-    let response
-    if (isEdit.value) {
-      response = await deviceGroupApi.update(props.data.id, config)
-    } else {
-      response = await deviceGroupApi.create(config)
-    }
-    
-    if (response.success) {
+    const response = isEdit.value
+      ? await deviceGroupApi.update(props.data.id, config)
+      : await deviceGroupApi.create(config)
+
+    const ok = response && (response.success || response.code === 200 || response.isSuccess)
+    if (ok) {
       ElMessage.success(isEdit.value ? '璁惧缁勬洿鏂版垚鍔�' : '璁惧缁勫垱寤烘垚鍔�')
       emit('success', isEdit.value ? 'update' : 'create')
       handleClose()
     } else {
-      ElMessage.error(response.message || (isEdit.value ? '鏇存柊澶辫触' : '鍒涘缓澶辫触'))
+      ElMessage.error(response?.message || (isEdit.value ? '鏇存柊澶辫触' : '鍒涘缓澶辫触'))
     }
   } catch (error) {
     console.error('淇濆瓨閰嶇疆澶辫触:', error)
diff --git a/mes-web/src/views/device/DeviceGroupList.vue b/mes-web/src/views/device/DeviceGroupList.vue
index 9d4163c..ae5c1ce 100644
--- a/mes-web/src/views/device/DeviceGroupList.vue
+++ b/mes-web/src/views/device/DeviceGroupList.vue
@@ -398,10 +398,10 @@
   try {
     const groupId = row.id || row.groupId
     if (row.enabled) {
-      await deviceGroupApi.enable({ groupId })
+      await deviceGroupApi.enable(groupId)
       ElMessage.success('璁惧缁勫惎鐢ㄦ垚鍔�')
     } else {
-      await deviceGroupApi.disable({ groupId })
+      await deviceGroupApi.disable(groupId)
       ElMessage.success('璁惧缁勭鐢ㄦ垚鍔�')
     }
     emit('refresh-statistics')
@@ -416,7 +416,7 @@
 const batchEnable = async () => {
   try {
     const groupIds = selectedGroups.value.map(item => item.id || item.groupId)
-    await deviceGroupApi.batchEnable({ groupIds })
+    await deviceGroupApi.batchEnable(groupIds)
     ElMessage.success(`鎴愬姛鍚敤 ${groupIds.length} 涓澶囩粍`)
     clearSelection()
     loadGroupList()
@@ -430,7 +430,7 @@
 const batchDisable = async () => {
   try {
     const groupIds = selectedGroups.value.map(item => item.id || item.groupId)
-    await deviceGroupApi.batchDisable({ groupIds })
+    await deviceGroupApi.batchDisable(groupIds)
     ElMessage.success(`鎴愬姛绂佺敤 ${groupIds.length} 涓澶囩粍`)
     clearSelection()
     loadGroupList()
diff --git a/mes-web/src/views/plcTest/MultiDeviceWorkbench.vue b/mes-web/src/views/plcTest/MultiDeviceWorkbench.vue
new file mode 100644
index 0000000..5c5c465
--- /dev/null
+++ b/mes-web/src/views/plcTest/MultiDeviceWorkbench.vue
@@ -0,0 +1,67 @@
+<template>
+  <div class="multi-device-workbench">
+    <div class="main-grid">
+      <div class="left-panel">
+        <GroupList @select="handleGroupSelect" />
+      </div>
+      <div class="right-panel">
+        <TaskOrchestration :group="selectedGroup" @task-started="refreshMonitor" />
+        <ExecutionMonitor ref="monitorRef" :group-id="selectedGroupId" class="monitor-panel" />
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { computed, ref } from 'vue'
+import GroupList from './components/DeviceGroup/GroupList.vue'
+import TaskOrchestration from './components/MultiDeviceTest/TaskOrchestration.vue'
+import ExecutionMonitor from './components/MultiDeviceTest/ExecutionMonitor.vue'
+
+const selectedGroup = ref(null)
+const monitorRef = ref(null)
+
+const selectedGroupId = computed(() => {
+  if (!selectedGroup.value) return null
+  return selectedGroup.value.id || selectedGroup.value.groupId
+})
+
+const handleGroupSelect = (group) => {
+  selectedGroup.value = group
+}
+
+const refreshMonitor = () => {
+  monitorRef.value?.fetchTasks?.()
+}
+</script>
+
+<style scoped>
+.multi-device-workbench {
+  padding: 24px;
+  min-height: 100%;
+  background: linear-gradient(180deg, #f6f9ff 0%, #f4f6fb 100%);
+}
+
+.main-grid {
+  display: grid;
+  grid-template-columns: 360px 1fr;
+  gap: 24px;
+}
+
+.right-panel {
+  display: flex;
+  flex-direction: column;
+  gap: 24px;
+}
+
+.monitor-panel {
+  flex: 1;
+}
+
+@media (max-width: 1200px) {
+  .main-grid {
+    grid-template-columns: 1fr;
+  }
+}
+</style>
+
diff --git a/mes-web/src/views/plcTest/components/DeviceGroup/GroupList.vue b/mes-web/src/views/plcTest/components/DeviceGroup/GroupList.vue
new file mode 100644
index 0000000..bcccb89
--- /dev/null
+++ b/mes-web/src/views/plcTest/components/DeviceGroup/GroupList.vue
@@ -0,0 +1,144 @@
+<template>
+  <div class="group-list-panel">
+    <div class="panel-header">
+      <div>
+        <h3>璁惧缁勫垪琛�</h3>
+        <p>閫夋嫨涓�涓澶囩粍杩涜缂栨帓娴嬭瘯</p>
+      </div>
+      <div class="actions">
+        <el-input
+          v-model="filters.keyword"
+          placeholder="鎼滅储璁惧缁勫悕绉�/缂栫爜"
+          clearable
+          @clear="fetchGroups"
+          @keyup.enter="fetchGroups"
+          class="search-input"
+        >
+          <template #prefix>
+            <el-icon><Search /></el-icon>
+          </template>
+        </el-input>
+        <el-button :loading="loading" @click="fetchGroups">
+          <el-icon><Refresh /></el-icon>
+        </el-button>
+      </div>
+    </div>
+
+    <el-table
+      v-loading="loading"
+      :data="groups"
+      height="320"
+      stripe
+      class="group-table"
+      @row-click="handleRowClick"
+    >
+      <el-table-column prop="groupName" label="璁惧缁�" min-width="160" />
+      <el-table-column prop="groupCode" label="缂栫爜" min-width="120" />
+      <el-table-column prop="status" label="鐘舵��" width="100">
+        <template #default="{ row }">
+          <el-tag :type="formatStatus(row.status).type">{{ formatStatus(row.status).label }}</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column prop="deviceCount" label="璁惧鏁伴噺" width="100" />
+      <el-table-column label="鏈�鍚庢洿鏂版椂闂�" min-width="160">
+        <template #default="{ row }">
+          {{ row.updatedTime || row.createTime || '-' }}
+        </template>
+      </el-table-column>
+    </el-table>
+  </div>
+</template>
+
+<script setup>
+import { onMounted, reactive, ref } from 'vue'
+import { Refresh, Search } from '@element-plus/icons-vue'
+import { deviceGroupApi } from '@/api/device/deviceManagement'
+
+const emit = defineEmits(['select'])
+
+const loading = ref(false)
+const groups = ref([])
+const filters = reactive({
+  keyword: ''
+})
+
+const fetchGroups = async () => {
+  try {
+    loading.value = true
+    const payload = {
+      page: 1,
+      size: 20,
+      keyword: filters.keyword
+    }
+    const { data } = await deviceGroupApi.getList(payload)
+    const records = data?.records || data?.data || data || []
+    groups.value = Array.isArray(records) ? records : []
+  } finally {
+    loading.value = false
+  }
+}
+
+const handleRowClick = (row) => {
+  emit('select', row)
+}
+
+const formatStatus = (status) => {
+  const value = typeof status === 'number' ? status : String(status || '').toUpperCase()
+  if (value === 1 || value === '鍚敤' || value === 'ENABLED') {
+    return { label: '鍚敤', type: 'success' }
+  }
+  if (value === 0 || value === '鍋滅敤' || value === 'DISABLED') {
+    return { label: '鍋滅敤', type: 'info' }
+  }
+  if (value === 2 || value === '缁存姢涓�' || value === 'MAINTENANCE') {
+    return { label: '缁存姢', type: 'warning' }
+  }
+  return { label: status || '鏈煡', type: 'default' }
+}
+
+onMounted(fetchGroups)
+</script>
+
+<style scoped>
+.group-list-panel {
+  background: #fff;
+  border-radius: 12px;
+  padding: 20px;
+  box-shadow: 0 8px 32px rgba(15, 18, 63, 0.08);
+  display: flex;
+  flex-direction: column;
+  gap: 16px;
+}
+
+.panel-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+
+.panel-header h3 {
+  margin: 0;
+  font-size: 18px;
+}
+
+.panel-header p {
+  margin: 2px 0 0;
+  color: #909399;
+  font-size: 13px;
+}
+
+.actions {
+  display: flex;
+  align-items: center;
+  gap: 12px;
+}
+
+.search-input {
+  width: 240px;
+}
+
+.group-table {
+  flex: 1;
+}
+</style>
+
diff --git a/mes-web/src/views/plcTest/components/MultiDeviceTest/ExecutionMonitor.vue b/mes-web/src/views/plcTest/components/MultiDeviceTest/ExecutionMonitor.vue
new file mode 100644
index 0000000..65133b4
--- /dev/null
+++ b/mes-web/src/views/plcTest/components/MultiDeviceTest/ExecutionMonitor.vue
@@ -0,0 +1,176 @@
+<template>
+  <div class="execution-monitor">
+    <div class="panel-header">
+      <div>
+        <h3>浠诲姟鎵ц鐩戞帶</h3>
+        <p>瀹炴椂鏌ョ湅鏈�鏂扮殑澶氳澶囦换鍔�</p>
+      </div>
+      <el-button :loading="loading" @click="fetchTasks">
+        <el-icon><Refresh /></el-icon>
+        鍒锋柊
+      </el-button>
+    </div>
+
+    <el-table
+      v-loading="loading"
+      :data="tasks"
+      height="300"
+      stripe
+      @row-click="handleRowClick"
+    >
+      <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>
+        </template>
+      </el-table-column>
+      <el-table-column prop="currentStep" label="杩涘害" width="120">
+        <template #default="{ row }">
+          {{ row.currentStep || 0 }} / {{ row.totalSteps || 0 }}
+        </template>
+      </el-table-column>
+      <el-table-column label="寮�濮嬫椂闂�" min-width="160" prop="startTime" />
+      <el-table-column label="缁撴潫鏃堕棿" min-width="160" prop="endTime" />
+    </el-table>
+
+    <el-drawer v-model="drawerVisible" size="40%" title="浠诲姟姝ラ璇︽儏">
+      <el-timeline v-loading="stepsLoading" :reverse="false">
+        <el-timeline-item
+          v-for="step in steps"
+          :key="step.id"
+          :timestamp="step.startTime || '-'"
+          :type="step.status === 'COMPLETED' ? 'success' : step.status === 'FAILED' ? 'danger' : 'primary'"
+        >
+          <div class="step-title">{{ step.stepName }}</div>
+          <div class="step-desc">鐘舵�侊細{{ step.status }}</div>
+          <div class="step-desc">鑰楁椂锛歿{ formatDuration(step.durationMs) }}</div>
+          <div class="step-desc" v-if="step.errorMessage">
+            閿欒锛歿{ step.errorMessage }}
+          </div>
+        </el-timeline-item>
+      </el-timeline>
+    </el-drawer>
+  </div>
+</template>
+
+<script setup>
+import { onMounted, ref, watch } from 'vue'
+import { ElMessage } from 'element-plus'
+import { Refresh } from '@element-plus/icons-vue'
+import { multiDeviceTaskApi } from '@/api/device/multiDeviceTask'
+
+const props = defineProps({
+  groupId: {
+    type: [String, Number],
+    default: null
+  }
+})
+
+const loading = ref(false)
+const tasks = ref([])
+const drawerVisible = ref(false)
+const stepsLoading = ref(false)
+const steps = ref([])
+const currentTaskId = ref(null)
+
+const fetchTasks = async () => {
+  try {
+    loading.value = true
+    const { data } = await multiDeviceTaskApi.getTaskList({
+      groupId: props.groupId,
+      page: 1,
+      size: 10
+    })
+    tasks.value = data?.records || data?.data || data || []
+  } catch (error) {
+    ElMessage.error(error?.message || '鍔犺浇浠诲姟鍒楄〃澶辫触')
+  } finally {
+    loading.value = false
+  }
+}
+
+const handleRowClick = async (row) => {
+  currentTaskId.value = row.taskId
+  drawerVisible.value = true
+  stepsLoading.value = true
+  try {
+    const { data } = await multiDeviceTaskApi.getTaskSteps(row.taskId)
+    steps.value = Array.isArray(data) ? data : (data?.data || [])
+  } catch (error) {
+    ElMessage.error(error?.message || '鍔犺浇浠诲姟姝ラ澶辫触')
+  } finally {
+    stepsLoading.value = false
+  }
+}
+
+const statusType = (status) => {
+  switch ((status || '').toUpperCase()) {
+    case 'COMPLETED':
+      return 'success'
+    case 'FAILED':
+      return 'danger'
+    case 'RUNNING':
+      return 'warning'
+    default:
+      return 'info'
+  }
+}
+
+const formatDuration = (ms) => {
+  if (!ms) return '-'
+  if (ms < 1000) return `${ms} ms`
+  return `${(ms / 1000).toFixed(1)} s`
+}
+
+watch(
+  () => props.groupId,
+  () => {
+    fetchTasks()
+  },
+  { immediate: true }
+)
+
+onMounted(fetchTasks)
+
+defineExpose({
+  fetchTasks
+})
+</script>
+
+<style scoped>
+.execution-monitor {
+  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: 16px;
+}
+
+.panel-header h3 {
+  margin: 0;
+}
+
+.panel-header p {
+  margin: 4px 0 0;
+  color: #909399;
+  font-size: 13px;
+}
+
+.step-title {
+  font-weight: 600;
+  margin-bottom: 4px;
+}
+
+.step-desc {
+  font-size: 13px;
+  color: #606266;
+}
+</style>
+
diff --git a/mes-web/src/views/plcTest/components/MultiDeviceTest/TaskOrchestration.vue b/mes-web/src/views/plcTest/components/MultiDeviceTest/TaskOrchestration.vue
new file mode 100644
index 0000000..606f293
--- /dev/null
+++ b/mes-web/src/views/plcTest/components/MultiDeviceTest/TaskOrchestration.vue
@@ -0,0 +1,135 @@
+<template>
+  <div class="task-orchestration">
+    <div class="panel-header">
+      <div>
+        <h3>澶氳澶囨祴璇曠紪鎺�</h3>
+        <p v-if="group">褰撳墠璁惧缁勶細{{ group.groupName }}锛坽{ group.deviceCount || '-' }} 鍙拌澶囷級</p>
+        <p v-else class="warning">璇峰厛鍦ㄥ乏渚ч�夋嫨涓�涓澶囩粍</p>
+      </div>
+      <el-button type="primary" :disabled="!group" :loading="loading" @click="handleSubmit">
+        <el-icon><Promotion /></el-icon>
+        鍚姩娴嬭瘯
+      </el-button>
+    </div>
+
+    <el-form :model="form" label-width="120px">
+      <el-form-item label="鐜荤拑ID鍒楄〃">
+        <el-input
+          v-model="glassIdsInput"
+          type="textarea"
+          :rows="4"
+          placeholder="璇疯緭鍏ョ幓鐠冩潯鐮侊紝鏀寔澶氳鎴栭�楀彿鍒嗛殧"
+        />
+      </el-form-item>
+      <el-form-item label="浣嶇疆缂栫爜">
+        <el-input v-model="form.positionCode" placeholder="渚嬪锛歅OS1" />
+      </el-form-item>
+      <el-form-item label="瀛樺偍浣嶇疆">
+        <el-input-number v-model="form.storagePosition" :min="1" :max="200" />
+      </el-form-item>
+      <el-form-item label="鎵ц闂撮殧 (ms)">
+        <el-input-number v-model="form.executionInterval" :min="100" :max="10000" />
+      </el-form-item>
+    </el-form>
+  </div>
+</template>
+
+<script setup>
+import { computed, reactive, ref, watch } from 'vue'
+import { ElMessage } from 'element-plus'
+import { Promotion } from '@element-plus/icons-vue'
+import { multiDeviceTaskApi } from '@/api/device/multiDeviceTask'
+
+const props = defineProps({
+  group: {
+    type: Object,
+    default: null
+  }
+})
+
+const emit = defineEmits(['task-started'])
+
+const form = reactive({
+  positionCode: '',
+  storagePosition: null,
+  executionInterval: 1000
+})
+
+const glassIdsInput = ref('')
+const loading = ref(false)
+
+watch(
+  () => props.group,
+  () => {
+    glassIdsInput.value = ''
+  }
+)
+
+const glassIds = computed(() => {
+  if (!glassIdsInput.value) return []
+  return glassIdsInput.value
+    .split(/[\n,锛宂/)
+    .map((item) => item.trim())
+    .filter((item) => item.length > 0)
+})
+
+const handleSubmit = async () => {
+  if (!props.group) {
+    ElMessage.warning('璇峰厛閫夋嫨璁惧缁�')
+    return
+  }
+  if (glassIds.value.length === 0) {
+    ElMessage.warning('璇疯嚦灏戣緭鍏ヤ竴涓幓鐠僆D')
+    return
+  }
+  try {
+    loading.value = true
+    await multiDeviceTaskApi.startTask({
+      groupId: props.group.id || props.group.groupId,
+      parameters: {
+        glassIds: glassIds.value,
+        positionCode: form.positionCode || null,
+        storagePosition: form.storagePosition,
+        executionInterval: form.executionInterval
+      }
+    })
+    ElMessage.success('浠诲姟宸插惎鍔�')
+    emit('task-started')
+  } catch (error) {
+    ElMessage.error(error?.message || '浠诲姟鍚姩澶辫触')
+  } finally {
+    loading.value = false
+  }
+}
+</script>
+
+<style scoped>
+.task-orchestration {
+  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: 16px;
+}
+
+.panel-header h3 {
+  margin: 0;
+}
+
+.panel-header p {
+  margin: 4px 0 0;
+  color: #909399;
+  font-size: 13px;
+}
+
+.panel-header .warning {
+  color: #f56c6c;
+}
+</style>
+

--
Gitblit v1.8.0