Commit f13c55b9 authored by wanghao's avatar wanghao

1 指令指令完成,但是 没有检测到机械臂完成。

parent d34ebdc1
...@@ -81,7 +81,8 @@ spring: ...@@ -81,7 +81,8 @@ spring:
# 是否使用属性文件存储JobDataMaps # 是否使用属性文件存储JobDataMaps
useProperties: true useProperties: true
# 错过触发阈值(毫秒) # 错过触发阈值(毫秒)
misfireThreshold: 60000 misfireThreshold: 300000
tolerateJobFailure: true
# 是否启用集群功能 # 是否启用集群功能
isClustered: false isClustered: false
# 集群检查间隔(毫秒) # 集群检查间隔(毫秒)
......
...@@ -58,24 +58,77 @@ public class DeviceCommunicationJob implements Job { ...@@ -58,24 +58,77 @@ public class DeviceCommunicationJob implements Job {
@Override @Override
public void execute(JobExecutionContext context) { public void execute(JobExecutionContext context) {
log.info("=== DeviceCommunicationJob 开始执行 ==="); // 绝对确保任何异常都不会传播到Quartz
try {
log.info("=== DeviceCommunicationJob 开始执行 ===");
executeWithFullProtection(context);
log.info("=== DeviceCommunicationJob 执行完成 ===");
} catch (Throwable e) { // 捕获Throwable而不是Exception
// 关键:记录错误但不传播到Quartz
log.error("=== 任务执行异常(已完全捕获,不影响触发器) ===", e);
safeRecordAlarm(context, "任务异常已捕获: " + e.getClass().getSimpleName());
}
}
private void safeRecordAlarm(JobExecutionContext context, String message) {
try {
JobDataMap data = context.getJobDetail().getJobDataMap();
String fStoreyIdStr = data != null ? data.getString("fStoreyId") : "unknown";
TEquipmentAlarmData alarm = new TEquipmentAlarmData();
alarm.setfAlarmType("03");
alarm.setfEquipmentCode(fStoreyIdStr);
alarm.setfAlarmData(message);
alarm.setfCreateTime(new Date());
alarmDataService.insertTEquipmentAlarmData(alarm);
} catch (Exception e) {
log.error("记录告警失败", e);
}
}
private void executeWithFullProtection(JobExecutionContext context) {
// 移除所有CompletableFuture和多线程,简化执行流程
String fStoreyIdStr = null;
try { try {
// 使用单线程顺序执行,避免并发问题 JobDataMap data = context.getJobDetail().getJobDataMap();
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> { fStoreyIdStr = data.getString("fStoreyId");
executeJobSafely(context);
}, sequentialExecutor); if (StringUtils.isBlank(fStoreyIdStr)) {
log.warn("fStoreyId为空,跳过执行");
// 设置总超时 return;
future.get(180, TimeUnit.SECONDS); // 3分钟总超时 }
log.info("=== DeviceCommunicationJob 正常完成 ===");
Long fStoreyId = Long.parseLong(fStoreyIdStr);
} catch (TimeoutException e) { TStoreyInfo tStoreyInfo = tStoreyInfoMapper.selectTStoreyInfoById(fStoreyId);
log.error("任务执行超时", e);
recordAlarm(context, "任务执行超时(3分钟)"); if (tStoreyInfo == null || StringUtils.isBlank(tStoreyInfo.getfIp())) {
log.warn("设备信息无效,跳过执行");
return;
}
// 简化执行:只执行最基本的通信测试
executeSimpleCommunicationTest(tStoreyInfo, fStoreyId);
} catch (NumberFormatException e) {
log.warn("参数格式错误: {}", fStoreyIdStr, e);
} catch (Exception e) {
log.error("通信任务执行异常", e);
// 不抛出!只记录日志
}
}
private void executeSimpleCommunicationTest(TStoreyInfo tStoreyInfo, Long fStoreyId) {
String ip = tStoreyInfo.getfIp();
// 只测试第一个设备,简化逻辑
try {
ModbusMaster master = createModbusMaster(ip, 501);
int[] result = readDeviceRegistersOnce(master, 1); // 只读第一个设备
master.destroy();
log.info("设备通信测试成功: fStoreyId={}, 状态值={}", fStoreyId,
result.length > 1 ? result[1] : "无数据");
} catch (Exception e) { } catch (Exception e) {
log.error("任务执行异常(已捕获,不会影响触发器)", e); log.warn("设备通信测试失败: fStoreyId={}, IP={}", fStoreyId, ip, e);
recordAlarm(context, "任务执行异常: " + e.getMessage()); // 不抛出异常!
} }
} }
private void executeJobSafely(JobExecutionContext context) { private void executeJobSafely(JobExecutionContext context) {
......
...@@ -6,6 +6,7 @@ import org.slf4j.Logger; ...@@ -6,6 +6,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.time.Instant; import java.time.Instant;
import java.time.temporal.ChronoUnit; import java.time.temporal.ChronoUnit;
...@@ -35,6 +36,12 @@ public class DeviceTaskScheduler { ...@@ -35,6 +36,12 @@ public class DeviceTaskScheduler {
// 常量:Cron周期(3分钟)、任务有效期(7天) // 常量:Cron周期(3分钟)、任务有效期(7天)
private static final String CRON_EXPRESSION = "0 0/5 * * * ?"; private static final String CRON_EXPRESSION = "0 0/5 * * * ?";
private static final int TASK_VALID_DAYS = 7; private static final int TASK_VALID_DAYS = 7;
@PostConstruct
public void init() throws SchedulerException {
scheduler.getListenerManager().addJobListener(new QuartzJobListener());
}
/** /**
* 创建设备监控任务(入口:增加调度器健康检查、任务去重) * 创建设备监控任务(入口:增加调度器健康检查、任务去重)
*/ */
...@@ -81,6 +88,15 @@ public class DeviceTaskScheduler { ...@@ -81,6 +88,15 @@ public class DeviceTaskScheduler {
private void createHourlyCommunicationJob(Long fStoreyId) throws SchedulerException { private void createHourlyCommunicationJob(Long fStoreyId) throws SchedulerException {
String jobId = "COMM_" + fStoreyId; String jobId = "COMM_" + fStoreyId;
JobKey jobKey = new JobKey(jobId, JOB_GROUP); JobKey jobKey = new JobKey(jobId, JOB_GROUP);
TriggerKey triggerKey = new TriggerKey(jobId + "_TRIGGER", TRIGGER_GROUP);
// 先删除旧的触发器和任务(彻底清理)
if (scheduler.checkExists(triggerKey)) {
scheduler.unscheduleJob(triggerKey);
}
if (scheduler.checkExists(jobKey)) {
scheduler.deleteJob(jobKey);
}
JobDetail job = JobBuilder.newJob(DeviceCommunicationJob.class) JobDetail job = JobBuilder.newJob(DeviceCommunicationJob.class)
.withIdentity(jobKey) .withIdentity(jobKey)
...@@ -88,26 +104,17 @@ public class DeviceTaskScheduler { ...@@ -88,26 +104,17 @@ public class DeviceTaskScheduler {
.storeDurably(false) .storeDurably(false)
.build(); .build();
// 使用更宽松的misfire策略 // 关键修复:使用StartAt而不是StartNow
CronTrigger trigger = TriggerBuilder.newTrigger() CronTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity(jobId + "_TRIGGER", TRIGGER_GROUP) .withIdentity(triggerKey)
.forJob(jobKey) .forJob(jobKey)
.withSchedule(CronScheduleBuilder.cronSchedule(CRON_EXPRESSION) .withSchedule(CronScheduleBuilder.cronSchedule("0 0/3 * * * ?") // 改为3分钟
.withMisfireHandlingInstructionDoNothing()) // 错过就忽略 .withMisfireHandlingInstructionDoNothing())
.startNow() .startAt(new Date()) // 显式设置开始时间:cite[1]
.build(); .build();
// 清理旧任务时更温和 Date nextFireTime = scheduler.scheduleJob(job, trigger);
if (scheduler.checkExists(jobKey)) { log.info("通信任务[{}]创建成功,下次执行:{}", jobId, nextFireTime);
scheduler.deleteJob(jobKey);
try {
Thread.sleep(100); // 短暂等待确保清理完成
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
scheduler.scheduleJob(job, trigger);
} }
/** /**
...@@ -251,5 +258,35 @@ public class DeviceTaskScheduler { ...@@ -251,5 +258,35 @@ public class DeviceTaskScheduler {
finalTrigger != null ? finalTrigger.getNextFireTime() : "不存在", finalTrigger != null ? finalTrigger.getNextFireTime() : "不存在",
finalTrigger != null ? finalTrigger.getEndTime() : "不存在"); finalTrigger != null ? finalTrigger.getEndTime() : "不存在");
} }
/**
* 监控并修复ERROR状态的触发器
*/
public void monitorAndRepairTriggers() throws SchedulerException {
for (String groupName : scheduler.getTriggerGroupNames()) {
for (TriggerKey triggerKey : scheduler.getTriggerKeys(GroupMatcher.triggerGroupEquals(groupName))) {
Trigger.TriggerState state = scheduler.getTriggerState(triggerKey);
if (state == Trigger.TriggerState.ERROR) {
log.warn("发现ERROR状态触发器: {}", triggerKey);
repairErrorTrigger(triggerKey);
}
}
}
}
private void repairErrorTrigger(TriggerKey triggerKey) throws SchedulerException {
Trigger oldTrigger = scheduler.getTrigger(triggerKey);
JobKey jobKey = oldTrigger.getJobKey();
// 重新创建触发器
Trigger newTrigger = TriggerBuilder.newTrigger()
.withIdentity(triggerKey)
.forJob(jobKey)
.withSchedule(CronScheduleBuilder.cronSchedule("0 0/3 * * * ?")
.withMisfireHandlingInstructionDoNothing())
.startAt(new Date()) // 关键:使用当前时间
.build();
scheduler.rescheduleJob(triggerKey, newTrigger);
log.info("触发器修复完成: {}", triggerKey);
}
} }
package com.zehong.system.task;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobListener;
import org.quartz.SchedulerException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
/**
* @author lenovo
* @date 2025/9/24
* @description TODO
*/
@Component
public class QuartzJobListener implements JobListener {
private static final Logger log = LoggerFactory.getLogger(QuartzJobListener.class);
@Override
public String getName() {
return "DeviceCommunicationJobListener";
}
@Override
public void jobToBeExecuted(JobExecutionContext context) {
log.info("Job即将执行: {}", context.getJobDetail().getKey());
}
@Override
public void jobExecutionVetoed(JobExecutionContext context) {
log.warn("Job执行被否决: {}", context.getJobDetail().getKey());
}
@Override
public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) {
if (jobException != null) {
log.error("Job执行异常: {}", context.getJobDetail().getKey(), jobException);
} else {
log.info("Job执行完成: {}", context.getJobDetail().getKey());
}
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment