huang
2025-11-17 92a5d16f0474ead87744b4ca5cb04417247a0619
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
package com.mes.job;
 
import com.mes.entity.PlcAddress;
import com.mes.entity.PlcTestTask;
import com.mes.service.PlcAddressService;
import com.mes.service.PlcTestTaskService;
import com.mes.service.PlcTestWriteService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
 
import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
 
/**
 * PLC自动测试任务调度器
 * 支持为指定项目创建和管理自动测试任务
 * 
 * 设计思路:
 * 1. 维护projectId -> 自动任务配置的映射
 * 2. 支持动态启动/停止自动测试
 * 3. 每个项目可独立配置自动测试参数
 * 4. 在PlcTestController中调用,配合AUTOMATIC模式
 * 
 * @author huang
 * @date 2025/11/04
 */
@Slf4j
@Component
public class PlcAutoTestTaskScheduler {
    
    @Resource
    private PlcTestWriteService plcTestWriteService;
    
    @Resource
    private PlcTestTaskService plcTestTaskService;
    
    @Resource
    private PlcAddressService plcAddressService;
    
    /**
     * 自动任务配置
     */
    public static class AutoTaskConfig {
        /** 项目ID */
        public String projectId;
        /** 模块名称 */
        public String module;
        /** 选中的字段 */
        public List<String> selectedFields;
        /** 执行间隔(毫秒) */
        public long intervalMs;
        /** 请求后到汇报之间的处理延迟时间(毫秒) */
        public long processDelayMs;
        /** 最大重试次数 */
        public int maxRetries;
        /** 是否启用 */
        public boolean enabled;
        /** 运行中的任务线程 */
        public volatile Thread taskThread;
        
        public AutoTaskConfig(String projectId, String module, List<String> selectedFields, long intervalMs, long processDelayMs) {
            this.projectId = projectId;
            this.module = module;
            this.selectedFields = selectedFields;
            this.intervalMs = intervalMs;
            this.processDelayMs = processDelayMs > 0 ? processDelayMs : 1000;  // 默认1秒
            this.maxRetries = 3;
            this.enabled = false;
            this.taskThread = null;
        }
    }
    
    /** projectId -> AutoTaskConfig 的映射 */
    private final Map<String, AutoTaskConfig> autoTaskConfigs = new ConcurrentHashMap<>();
    
    /**
     * 启动自动测试任务
     *
     * @param projectId      项目ID
     * @param module         模块名称
     * @param selectedFields 选中的字段列表
     * @param intervalMs     执行间隔(毫秒)
     * @param processDelayMs 请求后到汇报之间的处理延迟(毫秒)
     * @return
     */
    public PlcTestTask startAutoTest(String projectId, String module, List<String> selectedFields, long intervalMs, long processDelayMs) {
        try {
            // 检查是否已存在该项目的自动任务
            if (autoTaskConfigs.containsKey(projectId) && autoTaskConfigs.get(projectId).enabled) {
                log.warn("项目 {} 的自动测试任务已在运行,请先停止", projectId);
                return null;
            }
            
            // 创建或更新配置
            AutoTaskConfig config = new AutoTaskConfig(projectId, module, selectedFields, intervalMs, processDelayMs);
            config.enabled = true;
            
            // 创建并启动任务线程
            config.taskThread = new Thread(() -> executeAutoTestLoop(config), "AutoTest-" + projectId);
            config.taskThread.setDaemon(false);
            config.taskThread.start();
            
            autoTaskConfigs.put(projectId, config);
            log.info("启动自动测试任务,projectId: {}, module: {}, interval: {}ms", projectId, module, intervalMs);
        } catch (Exception e) {
            log.error("启动自动测试任务失败,projectId: {}", projectId, e);
        }
        return null;
    }
    
    /**
     * 停止自动测试任务
     * 
     * @param projectId 项目ID
     */
    public void stopAutoTest(String projectId) {
        try {
            AutoTaskConfig config = autoTaskConfigs.get(projectId);
            if (config == null) {
                log.warn("项目 {} 的自动测试任务不存在", projectId);
                return;
            }
            
            config.enabled = false;
            
            // 中断线程
            if (config.taskThread != null && config.taskThread.isAlive()) {
                config.taskThread.interrupt();
                try {
                    config.taskThread.join(5000); // 等待线程结束,最多5秒
                } catch (InterruptedException e) {
                    log.warn("等待自动测试任务线程结束时被中断");
                }
            }
            
            autoTaskConfigs.remove(projectId);
            log.info("停止自动测试任务,projectId: {}", projectId);
        } catch (Exception e) {
            log.error("停止自动测试任务失败,projectId: {}", projectId, e);
        }
    }
    
    /**
     * 检查自动测试任务是否运行中
     * 
     * @param projectId 项目ID
     * @return true表示运行中
     */
    public boolean isAutoTestRunning(String projectId) {
        AutoTaskConfig config = autoTaskConfigs.get(projectId);
        return config != null && config.enabled;
    }
    
    /**
     * 获取自动任务配置
     * 
     * @param projectId 项目ID
     * @return 配置信息
     */
    public AutoTaskConfig getAutoTaskConfig(String projectId) {
        return autoTaskConfigs.get(projectId);
    }
    
    /**
     * 自动测试循环执行
     * 
     * @param config 任务配置
     */
    private void executeAutoTestLoop(AutoTaskConfig config) {
        log.info("自动测试循环已启动,projectId: {}", config.projectId);
        
        int cycleCount = 0;
        while (config.enabled && !Thread.currentThread().isInterrupted()) {
            try {
                cycleCount++;
                log.info("执行自动测试循环 #{}, projectId: {}", cycleCount, config.projectId);
                
                // 获取项目配置
                PlcAddress plcConfig = plcAddressService.getMappingByProjectId(config.projectId);
                if (plcConfig == null) {
                    log.error("无法获取项目 {} 的PLC配置", config.projectId);
                    continue;
                }
                
                // 创建自动任务记录
                PlcTestTask task = new PlcTestTask();
                task.setProjectId(config.projectId);
                task.setModule(config.module);
                task.setOperationMode("AUTOMATIC");
                task.setStatus("RUNNING");
                task.setStartTime(new Date());
                if (config.selectedFields != null) {
                    task.setSelectedFields(String.join(",", config.selectedFields));
                }
                
                // 保存任务并获取ID
                PlcTestTask savedTask = plcTestTaskService.createTask(task);
                if (savedTask == null || savedTask.getId() == null) {
                    log.error("保存任务失败,无法获取任务ID,projectId: {}", config.projectId);
                    continue;
                }
                Long id = savedTask.getId();
                
                // 执行测试步骤
                long startTime = System.currentTimeMillis();
                boolean success = true;
                String errorMessage = null;
                
                try {
                    // 步骤1:发送PLC请求
                    log.debug("步骤1:发送PLC请求,id: {}", id);
                    boolean requestSuccess = plcTestWriteService.simulatePlcRequest(config.projectId);
                    if (!requestSuccess) {
                        throw new RuntimeException("PLC请求发送失败");
                    }
                    
                    // 步骤2:等待MES处理(使用用户配置的延迟时间)
                    log.debug("步骤2:等待MES处理 {}ms", config.processDelayMs);
                    Thread.sleep(config.processDelayMs);
                    
                    // 步骤3:汇报完成
                    log.debug("步骤3:汇报完成,id: {}", id);
                    boolean reportSuccess = plcTestWriteService.simulatePlcReport(config.projectId);
                    if (!reportSuccess) {
                        throw new RuntimeException("PLC汇报失败");
                    }
                    
                    // 步骤4:确认完成
                    log.debug("步骤4:确认完成,id: {}", id);
                    
                } catch (Exception e) {
                    success = false;
                    errorMessage = e.getMessage();
                    log.error("自动测试执行出错,id: {}", id, e);
                }
                
                // 更新任务完成状态
                long duration = System.currentTimeMillis() - startTime;
                String finalStatus = success ? "SUCCESS" : "FAILED";
                plcTestTaskService.completeTask(id, finalStatus, (int) duration, 
                    "{\"cycle\": " + cycleCount + "}", errorMessage);
                
                log.info("自动测试循环 #{} 完成,id: {}, status: {}", cycleCount, id, finalStatus);
                
                // 等待下一次执行
                if (config.enabled) {
                    log.debug("等待 {}ms 后执行下一轮", config.intervalMs);
                    Thread.sleep(config.intervalMs);
                }
                
            } catch (InterruptedException e) {
                log.info("自动测试循环被中断,projectId: {}", config.projectId);
                Thread.currentThread().interrupt();
                break;
            } catch (Exception e) {
                log.error("自动测试循环执行异常,projectId: {}", config.projectId, e);
                try {
                    Thread.sleep(config.intervalMs);
                } catch (InterruptedException ie) {
                    log.info("自动测试循环被中断");
                    break;
                }
            }
        }
        
        log.info("自动测试循环已结束,projectId: {}, 共执行 {} 轮", config.projectId, cycleCount);
    }
    
    /**
     * 停止所有运行中的自动测试任务
     */
    public void stopAllAutoTests() {
        List<String> projectIds = new ArrayList<>(autoTaskConfigs.keySet());
        for (String projectId : projectIds) {
            stopAutoTest(projectId);
        }
        log.info("已停止所有自动测试任务");
    }
    
    /**
     * 获取所有运行中的自动任务
     * 
     * @return projectId列表
     */
    public List<String> getRunningAutoTests() {
        List<String> result = new ArrayList<>();
        for (Map.Entry<String, AutoTaskConfig> entry : autoTaskConfigs.entrySet()) {
            if (entry.getValue().enabled) {
                result.add(entry.getKey());
            }
        }
        return result;
    }
}