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 selectedFields; /** 执行间隔(毫秒) */ public long intervalMs; /** 请求后到汇报之间的处理延迟时间(毫秒) */ public long processDelayMs; /** 最大重试次数 */ public int maxRetries; /** 是否启用 */ public boolean enabled; /** 运行中的任务线程 */ public volatile Thread taskThread; public AutoTaskConfig(String projectId, String module, List 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 autoTaskConfigs = new ConcurrentHashMap<>(); /** * 启动自动测试任务 * * @param projectId 项目ID * @param module 模块名称 * @param selectedFields 选中的字段列表 * @param intervalMs 执行间隔(毫秒) * @param processDelayMs 请求后到汇报之间的处理延迟(毫秒) * @return */ public PlcTestTask startAutoTest(String projectId, String module, List 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 projectIds = new ArrayList<>(autoTaskConfigs.keySet()); for (String projectId : projectIds) { stopAutoTest(projectId); } log.info("已停止所有自动测试任务"); } /** * 获取所有运行中的自动任务 * * @return projectId列表 */ public List getRunningAutoTests() { List result = new ArrayList<>(); for (Map.Entry entry : autoTaskConfigs.entrySet()) { if (entry.getValue().enabled) { result.add(entry.getKey()); } } return result; } }