Commit db7ad245 authored by wanghao's avatar wanghao

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

parent f13c55b9
......@@ -17,275 +17,495 @@ import com.zehong.system.modbus.handler.dto.DeviceStatusReaderDto;
import com.zehong.system.modbus.util.Modbus4jUtils;
import com.zehong.system.service.ITEquipmentAlarmDataService;
import org.apache.commons.lang3.StringUtils;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.net.Socket;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Predicate;
/**
* @author lenovo
* @date 2025/6/25
* @description 上电以后 两分钟执行一次的逻辑
* 设备通信Job(修复版:显式暴露错误,避免Trigger变ERROR)
*/
@Component
@DisallowConcurrentExecution
@DisallowConcurrentExecution // 禁止同一任务并行执行(必须保留)
public class DeviceCommunicationJob implements Job {
private static final Logger log = LoggerFactory.getLogger(DeviceCommunicationJob.class);
// -------------------------- 常量配置(统一管理,避免魔法值)--------------------------
// 超时控制:必须小于Cron周期(假设Cron为5分钟,这里留1分钟缓冲)
private static final int TOTAL_TASK_TIMEOUT_SEC = 240; // 任务总超时:4分钟
private static final int SINGLE_PORT_TIMEOUT_SEC = 60; // 单个端口超时:1分钟
private static final int SINGLE_DEVICE_TIMEOUT_SEC = 10; // 单个设备超时:10秒
// Modbus配置:取消内置重试,统一用自定义重试
private static final int MODBUS_CONN_TIMEOUT_MS = 3000; // 连接超时:3秒
private static final int CUSTOM_RETRY_TIMES = 1; // 自定义重试次数:1次
private static final int RETRY_DELAY_MS = 200; // 重试间隔:200ms
// Modbus寄存器配置
private static final int REG_START_ADDR = 0;
private static final int REG_READ_COUNT = 10;
// 工厂(单例)
private static final ModbusFactory modbusFactory = new ModbusFactory();
public static final int MAX_RETRIES = 1; // 仅1次重试(减少总耗时)
// 常量配置(优化重试,减少耗时)
public static final int START_ADDRESS = 0;
public static final int REGISTER_COUNT = 10;
// -------------------------- 依赖注入 --------------------------
@Resource
private ITEquipmentAlarmDataService alarmDataService;
// 移除静态线程池,使用单线程顺序执行
private final ExecutorService sequentialExecutor = Executors.newSingleThreadExecutor();
@Resource
private TStoreyInfoMapper tStoreyInfoMapper;
@Autowired
private ModbusResultHandler resultHandler;
// -------------------------- 核心执行逻辑 --------------------------
@Override
public void execute(JobExecutionContext context) {
// 绝对确保任何异常都不会传播到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) {
public void execute(JobExecutionContext context) throws JobExecutionException {
// 关键:不隐藏任何致命异常,让Quartz感知错误(但捕获后包装为JobExecutionException)
long taskStartTime = System.currentTimeMillis();
String storeyIdStr = getStoreyIdFromContext(context);
ExecutorService deviceExecutor = null; // 线程池:每次任务新建,结束后关闭
try {
JobDataMap data = context.getJobDetail().getJobDataMap();
String fStoreyIdStr = data != null ? data.getString("fStoreyId") : "unknown";
// 1. 初始化线程池(每次任务新建,避免泄漏)
deviceExecutor = new ThreadPoolExecutor(
3, 3, // 核心=最大=3(对应3个端口,1:1映射)
0, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(20), // 队列20个(足够72个设备)
r -> new Thread(r, "modbus-port-pool-" + storeyIdStr), // 线程名含设备ID,便于追踪
new ThreadPoolExecutor.AbortPolicy() // 队列满时抛异常,显式暴露问题
);
// 2. 执行核心逻辑(带总超时)
boolean executeSuccess = executeWithTotalTimeout(context, deviceExecutor, taskStartTime);
if (!executeSuccess) {
// 任务超时:主动抛出异常,让Quartz记录但不标记Trigger为ERROR(需配合Misfire策略)
throw new JobExecutionException(
"DeviceCommunicationJob执行超时(>" + TOTAL_TASK_TIMEOUT_SEC + "秒)",
false // 第二个参数为false:不立即暂停Trigger
);
}
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);
log.info("DeviceCommunicationJob执行成功:fStoreyId={},总耗时={}ms",
storeyIdStr, System.currentTimeMillis() - taskStartTime);
} catch (JobExecutionException e) {
// 主动抛出的任务异常:Quartz会记录日志,但Trigger状态仍为NORMAL(需配置Misfire)
log.error("DeviceCommunicationJob执行异常(已主动抛出,避免Trigger变ERROR):fStoreyId={}",
storeyIdStr, e);
throw e; // 必须抛出,让Quartz感知,但通过第二个参数控制不暂停Trigger
} catch (Throwable e) {
// 未预期的致命异常:包装为JobExecutionException,显式暴露
String errMsg = "DeviceCommunicationJob发生未预期致命异常:fStoreyId=" + storeyIdStr;
log.error(errMsg, e);
recordAlarm(null, storeyIdStr, errMsg + ":" + e.getMessage());
// 抛出时第二个参数设为false,避免Trigger直接变ERROR
throw new JobExecutionException(errMsg, e, false);
} finally {
// 关键:无论成功/失败,关闭线程池,避免资源泄漏
if (deviceExecutor != null && !deviceExecutor.isShutdown()) {
deviceExecutor.shutdownNow(); // 强制关闭,中断所有未完成任务
try {
// 等待线程池关闭,避免JVM退出前资源未释放
if (!deviceExecutor.awaitTermination(5, TimeUnit.SECONDS)) {
log.warn("线程池关闭超时,强制终止:fStoreyId={}", storeyIdStr);
}
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
log.error("线程池关闭被中断:fStoreyId={}", storeyIdStr, ie);
}
}
log.debug("DeviceCommunicationJob最终清理完成:fStoreyId={}", storeyIdStr);
}
}
private void executeWithFullProtection(JobExecutionContext context) {
// 移除所有CompletableFuture和多线程,简化执行流程
String fStoreyIdStr = null;
try {
JobDataMap data = context.getJobDetail().getJobDataMap();
fStoreyIdStr = data.getString("fStoreyId");
if (StringUtils.isBlank(fStoreyIdStr)) {
log.warn("fStoreyId为空,跳过执行");
return;
}
/**
* 带总超时的核心执行逻辑
*/
private boolean executeWithTotalTimeout(JobExecutionContext context, ExecutorService deviceExecutor, long taskStartTime) throws Exception {
String storeyIdStr = getStoreyIdFromContext(context);
TStoreyInfo storeyInfo = validateAndGetStoreyInfo(storeyIdStr);
Long fStoreyId = Long.parseLong(fStoreyIdStr);
TStoreyInfo tStoreyInfo = tStoreyInfoMapper.selectTStoreyInfoById(fStoreyId);
// 用自定义线程池执行端口通信(避免混用CompletableFuture默认线程池)
CountDownLatch portLatch = new CountDownLatch(3); // 3个端口,计数器3
AtomicInteger portErrorCount = new AtomicInteger(0);
if (tStoreyInfo == null || StringUtils.isBlank(tStoreyInfo.getfIp())) {
log.warn("设备信息无效,跳过执行");
return;
// 端口501通信
deviceExecutor.submit(() -> {
try {
executeSinglePort(storeyInfo.getfIp(), 501,
Arrays.asList(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),
storeyIdStr, storeyInfo);
} catch (Exception e) {
portErrorCount.incrementAndGet();
log.error("端口501通信异常:fStoreyId={}", storeyIdStr, e);
} finally {
portLatch.countDown();
}
});
// 简化执行:只执行最基本的通信测试
executeSimpleCommunicationTest(tStoreyInfo, fStoreyId);
// 端口502通信
deviceExecutor.submit(() -> {
try {
executeSinglePort(storeyInfo.getfIp(), 502,
Arrays.asList(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),
storeyIdStr, storeyInfo);
} catch (Exception e) {
portErrorCount.incrementAndGet();
log.error("端口502通信异常:fStoreyId={}", storeyIdStr, e);
} finally {
portLatch.countDown();
}
});
} catch (NumberFormatException e) {
log.warn("参数格式错误: {}", fStoreyIdStr, e);
} catch (Exception e) {
log.error("通信任务执行异常", e);
// 不抛出!只记录日志
// 端口503通信
deviceExecutor.submit(() -> {
try {
executeSinglePort(storeyInfo.getfIp(), 503,
Arrays.asList(55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72),
storeyIdStr, storeyInfo);
} catch (Exception e) {
portErrorCount.incrementAndGet();
log.error("端口503通信异常:fStoreyId={}", storeyIdStr, e);
} finally {
portLatch.countDown();
}
});
// 等待所有端口完成,带总超时
boolean allPortCompleted = portLatch.await(SINGLE_PORT_TIMEOUT_SEC, TimeUnit.SECONDS);
long totalCost = System.currentTimeMillis() - taskStartTime;
// 检查总耗时是否超任务超时
if (totalCost > TOTAL_TASK_TIMEOUT_SEC * 1000) {
log.warn("任务总耗时超阈值:fStoreyId={},耗时={}ms(阈值={}ms)",
storeyIdStr, totalCost, TOTAL_TASK_TIMEOUT_SEC * 1000);
recordAlarm(storeyInfo, "任务总超时:" + totalCost + "ms(阈值" + TOTAL_TASK_TIMEOUT_SEC + "秒)");
return false;
}
// 检查端口错误数
if (portErrorCount.get() > 0) {
String errMsg = "部分端口通信失败:fStoreyId=" + storeyIdStr + ",失败端口数=" + portErrorCount.get();
log.error(errMsg);
recordAlarm(storeyInfo, errMsg);
// 此处不抛出异常,仅记录告警,避免Trigger变ERROR
}
return allPortCompleted;
}
private void executeSimpleCommunicationTest(TStoreyInfo tStoreyInfo, Long fStoreyId) {
String ip = tStoreyInfo.getfIp();
// 只测试第一个设备,简化逻辑
/**
* 验证参数并获取设备信息(参数错误直接抛出异常)
*/
private TStoreyInfo validateAndGetStoreyInfo(String storeyIdStr) throws JobExecutionException {
// 1. 校验storeyIdStr
if (StringUtils.isBlank(storeyIdStr)) {
String errMsg = "fStoreyId参数为空,任务终止";
log.error(errMsg);
recordAlarm(null, "unknown", errMsg);
throw new JobExecutionException(errMsg, false);
}
// 2. 转换storeyId
Long storeyId;
try {
ModbusMaster master = createModbusMaster(ip, 501);
int[] result = readDeviceRegistersOnce(master, 1); // 只读第一个设备
master.destroy();
storeyId = Long.parseLong(storeyIdStr);
} catch (NumberFormatException e) {
String errMsg = "fStoreyId格式错误:" + storeyIdStr;
log.error(errMsg, e);
recordAlarm(null, storeyIdStr, errMsg);
throw new JobExecutionException(errMsg, e, false);
}
log.info("设备通信测试成功: fStoreyId={}, 状态值={}", fStoreyId,
result.length > 1 ? result[1] : "无数据");
// 3. 查询设备信息
TStoreyInfo storeyInfo = tStoreyInfoMapper.selectTStoreyInfoById(storeyId);
if (storeyInfo == null) {
String errMsg = "未查询到设备信息:fStoreyId=" + storeyId;
log.error(errMsg);
recordAlarm(null, storeyIdStr, errMsg);
// 清理无效任务(避免后续重复执行)
throw new JobExecutionException(errMsg, false);
}
} catch (Exception e) {
log.warn("设备通信测试失败: fStoreyId={}, IP={}", fStoreyId, ip, e);
// 不抛出异常!
// 4. 校验设备IP
if (StringUtils.isBlank(storeyInfo.getfIp())) {
String errMsg = "设备IP为空:fStoreyId=" + storeyId;
log.error(errMsg);
recordAlarm(storeyInfo, errMsg);
throw new JobExecutionException(errMsg, false);
}
return storeyInfo;
}
private void executeJobSafely(JobExecutionContext context) {
try {
// 1. 提取参数
JobDataMap data = context.getJobDetail().getJobDataMap();
String fStoreyIdStr = data.getString("fStoreyId");
if (StringUtils.isBlank(fStoreyIdStr)) {
log.warn("fStoreyId参数为空");
return;
}
/**
* 单个端口通信(含设备级超时和重试)
*/
private void executeSinglePort(String ip, int port, List<Integer> deviceIds,
String storeyIdStr, TStoreyInfo storeyInfo) throws Exception {
log.info("开始端口通信:ip={},port={},设备数={},fStoreyId={}",
ip, port, deviceIds.size(), storeyIdStr);
Long fStoreyId = Long.parseLong(fStoreyIdStr);
TStoreyInfo tStoreyInfo = tStoreyInfoMapper.selectTStoreyInfoById(fStoreyId);
CountDownLatch deviceLatch = new CountDownLatch(deviceIds.size());
AtomicInteger deviceErrorCount = new AtomicInteger(0);
if (tStoreyInfo == null || StringUtils.isBlank(tStoreyInfo.getfIp())) {
log.warn("设备信息无效: fStoreyId={}", fStoreyId);
return;
}
for (int deviceId : deviceIds) {
int finalDeviceId = deviceId;
// 每个设备用独立线程执行,带超时
Executors.newSingleThreadExecutor(r ->
new Thread(r, "modbus-device-" + storeyIdStr + "-" + port + "-" + finalDeviceId)
).submit(() -> {
try {
// 单个设备通信(带超时)
executeSingleDeviceWithTimeout(ip, port, finalDeviceId, storeyIdStr);
} catch (Exception e) {
deviceErrorCount.incrementAndGet();
log.error("设备通信异常:ip={},port={},deviceId={},fStoreyId={}",
ip, port, finalDeviceId, storeyIdStr, e);
recordAlarm(storeyInfo, "设备" + finalDeviceId + "通信异常:" + e.getMessage());
} finally {
deviceLatch.countDown();
}
});
}
// 2. 顺序执行三个端口(不要并行)
executePortSequentially(tStoreyInfo, fStoreyId);
// 等待该端口所有设备完成,带超时
boolean allDeviceCompleted = deviceLatch.await(SINGLE_DEVICE_TIMEOUT_SEC * deviceIds.size() / 10, TimeUnit.SECONDS);
if (!allDeviceCompleted) {
String errMsg = "端口" + port + "部分设备超时:ip=" + ip + ",未完成设备数=" + deviceLatch.getCount() + ",fStoreyId=" + storeyIdStr;
log.error(errMsg);
recordAlarm(storeyInfo, errMsg);
throw new Exception(errMsg); // 抛出异常,标记该端口失败
}
} catch (Exception e) {
log.error("executeJobSafely内部异常", e);
// 这里不抛出异常,只是记录日志
if (deviceErrorCount.get() > 0) {
String errMsg = "端口" + port + "部分设备通信失败:ip=" + ip + ",失败数=" + deviceErrorCount.get() + ",fStoreyId=" + storeyIdStr;
log.error(errMsg);
throw new Exception(errMsg); // 抛出异常,标记该端口失败
}
log.info("端口通信完成:ip={},port={},fStoreyId={}", ip, port, storeyIdStr);
}
private void executePortSequentially(TStoreyInfo tStoreyInfo, Long fStoreyId) {
String ip = tStoreyInfo.getfIp();
// 顺序执行,避免并发问题
executeSinglePortSafely(ip, 501, getOffsets501(), fStoreyId, tStoreyInfo);
executeSinglePortSafely(ip, 502, getOffsets502(), fStoreyId, tStoreyInfo);
executeSinglePortSafely(ip, 503, getOffsets503(), fStoreyId, tStoreyInfo);
/**
* 单个设备通信(带超时和重试)
*/
private void executeSingleDeviceWithTimeout(String ip, int port, int deviceId, String storeyIdStr) throws Exception {
// 单个设备通信超时:SINGLE_DEVICE_TIMEOUT_SEC秒
Future<?> deviceFuture = Executors.newSingleThreadExecutor().submit(() -> {
try {
// 带重试的设备通信
int[] result = readDeviceWithRetry(ip, port, deviceId, storeyIdStr);
// 处理结果(若有)
if (resultHandler != null) {
resultHandler.accept(new DeviceStatusReaderDto(ip, port, deviceId, result));
}
} catch (Exception e) {
throw new RuntimeException("设备" + deviceId + "通信失败", e);
}
});
try {
deviceFuture.get(SINGLE_DEVICE_TIMEOUT_SEC, TimeUnit.SECONDS);
} catch (TimeoutException e) {
deviceFuture.cancel(true);
String errMsg = "设备" + deviceId + "通信超时(>" + SINGLE_DEVICE_TIMEOUT_SEC + "秒):ip=" + ip + ",port=" + port;
throw new Exception(errMsg, e);
} catch (Exception e) {
String errMsg = "设备" + deviceId + "通信异常:ip=" + ip + ",port=" + port;
throw new Exception(errMsg, e);
}
}
private void executeSinglePortSafely(String ip, int port, List<Integer> deviceIds,
Long fStoreyId, TStoreyInfo tStoreyInfo) {
log.info("开始执行端口{}: ip={}, 设备数={}", port, ip, deviceIds.size());
/**
* 带重试的设备寄存器读取(取消Modbus内置重试,统一自定义重试)
*/
private int[] readDeviceWithRetry(String ip, int port, int deviceId, String storeyIdStr) throws Exception {
ModbusMaster master = null;
int[] result = null;
boolean readSuccess = false;
for (int deviceId : deviceIds) {
for (int retry = 0; retry <= CUSTOM_RETRY_TIMES; retry++) {
try {
executeSingleDeviceSafely(ip, port, deviceId, fStoreyId, tStoreyInfo);
// 每个设备执行后短暂休息,避免资源竞争
Thread.sleep(50);
// 1. 创建Modbus连接(取消内置重试,master.setRetries(0))
master = createModbusMaster(ip, port);
// 2. 读取寄存器
result = readDeviceRegisters(master, deviceId);
// 3. 检查结果(若有停止条件)
Predicate<int[]> stopCondition = ModbusResultHandler.createDefaultStopCondition();
if (stopCondition != null && stopCondition.test(result)) {
readSuccess = true;
log.debug("设备读取成功(满足停止条件):ip={},port={},deviceId={},重试次数={},fStoreyId={}",
ip, port, deviceId, retry, storeyIdStr);
break;
} else if (retry < CUSTOM_RETRY_TIMES) {
log.debug("设备读取结果不满足条件,准备重试:ip={},port={},deviceId={},重试次数={},fStoreyId={}",
ip, port, deviceId, retry, storeyIdStr);
Thread.sleep(RETRY_DELAY_MS);
}
} catch (Exception e) {
log.error("设备{}执行异常: ip={}, port={}", deviceId, ip, port, e);
// 继续执行下一个设备,不中断整个任务
if (retry < CUSTOM_RETRY_TIMES) {
log.warn("设备读取异常,准备重试:ip={},port={},deviceId={},重试次数={},fStoreyId={}",
ip, port, deviceId, retry, storeyIdStr, e);
Thread.sleep(RETRY_DELAY_MS);
} else {
String errMsg = "设备读取重试耗尽(共" + CUSTOM_RETRY_TIMES + "次):ip=" + ip + ",port=" + port + ",deviceId=" + deviceId;
log.error(errMsg, e);
throw new Exception(errMsg, e); // 重试耗尽,抛出异常
}
} finally {
// 每次重试后销毁连接,避免资源泄漏
destroyModbusMaster(master, deviceId);
}
}
log.info("端口{}执行完成: ip={}", port, ip);
if (!readSuccess) {
String errMsg = "设备读取未满足停止条件(重试" + CUSTOM_RETRY_TIMES + "次):ip=" + ip + ",port=" + port + ",deviceId=" + deviceId;
log.error(errMsg);
throw new Exception(errMsg);
}
return result;
}
private void executeSingleDeviceSafely(String ip, int port, int deviceId,
Long fStoreyId, TStoreyInfo tStoreyInfo) {
ModbusMaster master = null;
try {
// 创建连接
master = createModbusMaster(ip, port);
// -------------------------- Modbus工具方法(显式抛出异常)--------------------------
/**
* 创建Modbus连接(取消内置重试,统一自定义重试)
*/
private ModbusMaster createModbusMaster(String ip, int port) throws ModbusInitException {
IpParameters params = new IpParameters();
params.setHost(ip);
params.setPort(port);
// 读取数据(简化重试逻辑)
int[] result = readDeviceRegistersOnce(master, deviceId);
TcpMaster master = (TcpMaster) modbusFactory.createTcpMaster(params, true);
master.setTimeout(MODBUS_CONN_TIMEOUT_MS);
master.setRetries(0); // 取消内置重试,避免和自定义重试冲突
master.init();
log.debug("Modbus连接创建成功:ip={},port={},master={}", ip, port, master);
return master;
}
// 处理结果
if (resultHandler != null) {
resultHandler.accept(new DeviceStatusReaderDto(ip, port, deviceId, result));
}
/**
* 读取设备寄存器(异常直接抛出)
*/
private int[] readDeviceRegisters(ModbusMaster master, int deviceId) throws ModbusTransportException {
ReadHoldingRegistersRequest request = Modbus4jUtils.getReadHoldingRegistersRequest(
deviceId, REG_START_ADDR, REG_READ_COUNT);
ModbusResponse response = master.send(request);
log.debug("设备{}通信成功: ip={}, port={}", deviceId, ip, port);
if (!(response instanceof ReadHoldingRegistersResponse)) {
throw new IllegalArgumentException("无效Modbus响应类型:" + response.getClass().getName() + ",deviceId=" + deviceId);
}
ReadHoldingRegistersResponse regResp = (ReadHoldingRegistersResponse) response;
short[] signedVals = regResp.getShortData();
int[] unsignedVals = new int[signedVals.length];
for (int i = 0; i < signedVals.length; i++) {
unsignedVals[i] = signedVals[i] & 0xFFFF; // 转换为无符号整数
}
log.trace("设备寄存器读取结果:deviceId={},值={}", deviceId, Arrays.toString(unsignedVals));
return unsignedVals;
}
/**
* 销毁Modbus连接(反射失败直接抛出异常,显式暴露问题)
*/
private void destroyModbusMaster(ModbusMaster master, int deviceId) {
if (master == null) return;
try {
// 反射获取Socket(若失败,直接抛异常,不隐藏)
Socket socket = getUnderlyingSocket(master);
if (socket != null && !socket.isClosed()) {
socket.close();
log.trace("设备{}:Modbus底层Socket已关闭", deviceId);
}
} catch (Exception e) {
log.warn("设备{}通信失败: ip={}, port={}, 错误: {}",
deviceId, ip, port, e.getMessage());
// 不抛出异常,只记录警告
String errMsg = "设备" + deviceId + ":销毁Modbus Socket异常(可能导致连接泄漏)";
log.error(errMsg, e);
// 此处不抛出异常(避免中断销毁流程),但记录严重告警
recordAlarm(null, String.valueOf(deviceId), errMsg + ":" + e.getMessage());
} finally {
// 确保资源释放
if (master != null) {
try {
master.destroy();
} catch (Exception e) {
log.debug("释放Modbus连接异常: deviceId={}", deviceId, e);
}
try {
master.destroy();
log.trace("设备{}:ModbusMaster已销毁", deviceId);
} catch (Exception e) {
log.error("设备{}:销毁ModbusMaster异常", deviceId, e);
}
}
}
/**
* 简化版:只读取一次,不重试
* 反射获取TcpMaster的Socket(失败直接抛出异常,显式暴露问题)
*/
private int[] readDeviceRegistersOnce(ModbusMaster master, int deviceId)
throws ModbusTransportException {
ReadHoldingRegistersRequest request =
Modbus4jUtils.getReadHoldingRegistersRequest(deviceId, START_ADDRESS, REGISTER_COUNT);
ModbusResponse response = master.send(request);
if (!(response instanceof ReadHoldingRegistersResponse)) {
throw new IllegalArgumentException("Invalid response type");
private Socket getUnderlyingSocket(ModbusMaster master) throws Exception {
if (!(master instanceof TcpMaster)) {
throw new IllegalArgumentException("ModbusMaster不是TcpMaster类型:" + master.getClass().getName());
}
ReadHoldingRegistersResponse regResponse = (ReadHoldingRegistersResponse) response;
short[] signedValues = regResponse.getShortData();
TcpMaster tcpMaster = (TcpMaster) master;
try {
// 反射获取socket字段(根据你的TcpMaster源码确认字段名,必须正确)
Field socketField = TcpMaster.class.getDeclaredField("socket");
socketField.setAccessible(true);
Socket socket = (Socket) socketField.get(tcpMaster);
// 转换为无符号整数
int[] unsignedValues = new int[signedValues.length];
for (int i = 0; i < signedValues.length; i++) {
unsignedValues[i] = signedValues[i] & 0xFFFF;
if (socket == null) {
log.warn("TcpMaster的socket字段为null(未建立连接)");
}
return socket;
} catch (NoSuchFieldException e) {
throw new Exception("反射获取Socket失败:TcpMaster中不存在'socket'字段(版本不匹配)", e);
} catch (IllegalAccessException e) {
throw new Exception("反射获取Socket失败:无访问权限(可能被安全管理器拦截)", e);
}
return unsignedValues;
}
private void recordAlarm(JobExecutionContext context, String alarmData) {
// -------------------------- 辅助方法(日志/告警)--------------------------
/**
* 从JobContext中获取fStoreyId(失败返回unknown)
*/
private String getStoreyIdFromContext(JobExecutionContext context) {
try {
JobDataMap data = context.getJobDetail().getJobDataMap();
String fStoreyIdStr = data.getString("fStoreyId");
return data != null ? data.getString("fStoreyId") : "unknown";
} catch (Exception e) {
log.error("从JobContext获取fStoreyId失败", e);
return "unknown";
}
}
/**
* 记录告警(兼容设备信息为空的场景)
*/
private void recordAlarm(TStoreyInfo storeyInfo, String equipmentCode, String alarmData) {
try {
TEquipmentAlarmData alarm = new TEquipmentAlarmData();
alarm.setfAlarmType("03");
alarm.setfEquipmentCode(fStoreyIdStr != null ? fStoreyIdStr : "unknown");
alarm.setfAlarmType("03"); // 老化层告警
alarm.setfEquipmentCode(storeyInfo != null ? storeyInfo.getfStoreyCode() : equipmentCode);
alarm.setfAlarmData(alarmData);
alarm.setfCreateTime(new Date());
alarmDataService.insertTEquipmentAlarmData(alarm);
log.debug("告警记录成功:设备编码={},内容={}", alarm.getfEquipmentCode(), alarmData);
} catch (Exception e) {
log.error("记录告警失败", e);
log.error("告警记录失败:设备编码={},内容={}", equipmentCode, alarmData, e);
}
}
// 设备列表
private List<Integer> getOffsets501() {
return Arrays.asList(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);
}
private List<Integer> getOffsets502() {
return Arrays.asList(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);
}
private List<Integer> getOffsets503() {
return Arrays.asList(55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72);
}
/**
* 创建新的Modbus连接
* 简化告警记录(设备信息非空时)
*/
private ModbusMaster createModbusMaster(String ip, int port) throws ModbusInitException {
IpParameters params = new IpParameters();
params.setHost(ip);
params.setPort(port);
// 显式声明为TcpMaster,避免后续反射类型转换问题
TcpMaster master = (TcpMaster) modbusFactory.createTcpMaster(params, true);
master.setTimeout(3000); // 3秒连接超时
master.setRetries(1); // 1次重试
master.init();
log.debug("Modbus连接创建成功:IP={}, 端口={}, Master={}", ip, port, master);
return master;
private void recordAlarm(TStoreyInfo storeyInfo, String alarmData) {
recordAlarm(storeyInfo, storeyInfo != null ? storeyInfo.getfStoreyCode() : "unknown", alarmData);
}
}
}
\ No newline at end of file
......@@ -3,7 +3,6 @@ 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;
......
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