修正 modbus 超时问题

This commit is contained in:
2026-01-20 17:19:35 +08:00
parent 8716d43879
commit a31a1a1caa
4 changed files with 256 additions and 190 deletions

View File

@ -1,66 +1,62 @@
package com.xzzn.common.core.modbus; package com.xzzn.common.core.modbus;
import com.serotonin.modbus4j.ModbusFactory;
import com.serotonin.modbus4j.ModbusMaster; import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.ip.IpParameters;
import com.xzzn.common.core.modbus.domain.DeviceConfig; import com.xzzn.common.core.modbus.domain.DeviceConfig;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.annotation.PreDestroy;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
/**
* Modbus连接管理器
* 不使用连接池,每次请求创建新连接,使用完立即销毁
* 避免复用已被服务端断开的连接导致Connection reset
*/
@Component @Component
public class Modbus4jConnectionManager { public class Modbus4jConnectionManager {
private static final Logger logger = LoggerFactory.getLogger(Modbus4jConnectionManager.class); private static final Logger logger = LoggerFactory.getLogger(Modbus4jConnectionManager.class);
private final Map<String, GenericObjectPool<ModbusMaster>> connectionPools = new ConcurrentHashMap<>(); private final ModbusFactory modbusFactory = new ModbusFactory();
/**
* 创建新连接
*/
public ModbusMaster borrowMaster(DeviceConfig config) throws Exception { public ModbusMaster borrowMaster(DeviceConfig config) throws Exception {
String poolKey = getPoolKey(config); IpParameters params = new IpParameters();
GenericObjectPool<ModbusMaster> pool = connectionPools.computeIfAbsent(poolKey, key -> { params.setHost(config.getHost());
PooledModbusMasterFactory factory = new PooledModbusMasterFactory(config.getHost(), config.getPort()); params.setPort(config.getPort());
GenericObjectPoolConfig<ModbusMaster> poolConfig = new GenericObjectPoolConfig<>(); params.setEncapsulated(false);
poolConfig.setMaxTotal(20); // 池中最大连接数
poolConfig.setMinIdle(4); // 最小空闲连接数 ModbusMaster master = modbusFactory.createTcpMaster(params, true);
poolConfig.setTestOnBorrow(true); // 借用时测试连接有效性 master.init();
poolConfig.setTestOnReturn(true); // 归还时测试连接有效性 logger.debug("创建新Modbus连接: {}:{}", config.getHost(), config.getPort());
poolConfig.setTestWhileIdle(true); // 空闲时测试连接有效性 return master;
poolConfig.setMaxWaitMillis(3000); // 获取连接的最大等待时间3秒
poolConfig.setTimeBetweenEvictionRunsMillis(30000); // 30秒检查一次空闲连接
poolConfig.setMinEvictableIdleTimeMillis(60000); // 空闲超过60秒的连接可以被驱逐
return new GenericObjectPool<>(factory, poolConfig);
});
return pool.borrowObject();
} }
/**
* 关闭连接
*/
public void returnMaster(DeviceConfig config, ModbusMaster master) { public void returnMaster(DeviceConfig config, ModbusMaster master) {
if (master == null) { destroyMaster(master, config);
return; }
}
String poolKey = getPoolKey(config); /**
GenericObjectPool<ModbusMaster> pool = connectionPools.get(poolKey); * 废弃连接与returnMaster相同
if (pool != null) { */
pool.returnObject(master); public void invalidateMaster(DeviceConfig config, ModbusMaster master) {
destroyMaster(master, config);
}
private void destroyMaster(ModbusMaster master, DeviceConfig config) {
if (master != null) {
try {
master.destroy();
logger.debug("已关闭Modbus连接: {}:{}", config.getHost(), config.getPort());
} catch (Exception e) {
logger.warn("关闭Modbus连接异常: {}:{}", config.getHost(), config.getPort(), e);
}
} }
} }
private String getPoolKey(DeviceConfig config) {
return config.getHost() + ":" + config.getPort();
}
@PreDestroy
public void shutdown() {
connectionPools.values().forEach(GenericObjectPool::close);
}
} }

View File

@ -34,6 +34,7 @@ import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import static com.xzzn.common.enums.RegisterType.COIL; import static com.xzzn.common.enums.RegisterType.COIL;
@ -43,8 +44,12 @@ import static com.xzzn.common.enums.RegisterType.DISCRETE_INPUT;
public class ModbusProcessor { public class ModbusProcessor {
private static final Logger logger = LoggerFactory.getLogger(ModbusProcessor.class); private static final Logger logger = LoggerFactory.getLogger(ModbusProcessor.class);
private int readTimeout = 5000; @Value("${modbus.read-timeout:8000}")
private int writeTimeout = 3000; private int readTimeout;
@Value("${modbus.write-timeout:5000}")
private int writeTimeout;
@Value("${modbus.read-retries:1}")
private int readRetries;
@Autowired @Autowired
private RedisCache redisCache; private RedisCache redisCache;
@ -54,20 +59,25 @@ public class ModbusProcessor {
public boolean writeDataToDevice(DeviceConfig config) { public boolean writeDataToDevice(DeviceConfig config) {
logger.info("writeDataToDevice: {}", JSON.toJSONString(config)); logger.info("writeDataToDevice: {}", JSON.toJSONString(config));
ModbusMaster master = null; ModbusMaster master = null;
boolean result; boolean result = false;
boolean hasError = false;
try { try {
master = connectionManager.borrowMaster(config); master = connectionManager.borrowMaster(config);
// 设置了Modbus通信的超时时间为3000毫秒3秒。当主设备与从设备通信时若在3秒内未收到响应则认为通信超时并抛出异常。这有助于避免长时间等待无响应的设备。
master.setTimeout(writeTimeout); master.setTimeout(writeTimeout);
result = writeTagValue(master, config, config.getWriteTags()); result = writeTagValue(master, config, config.getWriteTags());
} catch (Exception e) { } catch (Exception e) {
logger.error("Failed to borrow connection or write to devices '{}'", config.getDeviceName(), e); logger.error("Failed to borrow connection or write to devices '{}'", config.getDeviceName(), e);
result = false; hasError = true;
} }
finally { finally {
// 关键:无论成功与否,都必须将连接归还到池中
if (master != null) { if (master != null) {
connectionManager.returnMaster(config, master); if (hasError) {
// 发生异常时废弃连接,下次重新创建
connectionManager.invalidateMaster(config, master);
} else {
// 正常时归还连接
connectionManager.returnMaster(config, master);
}
} }
} }
return result; return result;
@ -224,16 +234,14 @@ public class ModbusProcessor {
ModbusMaster master = connectionManager.borrowMaster(config); ModbusMaster master = connectionManager.borrowMaster(config);
// 设置了Modbus通信的超时时间为5000毫秒5秒。当主设备与从设备通信时若在5秒内未收到响应则认为通信超时并抛出异常。这有助于避免长时间等待无响应的设备。 // 设置了Modbus通信的超时时间为5000毫秒5秒。当主设备与从设备通信时若在5秒内未收到响应则认为通信超时并抛出异常。这有助于避免长时间等待无响应的设备。
master.setTimeout(readTimeout); master.setTimeout(readTimeout);
master.setRetries(readRetries);
return master; return master;
} }
public Map<String, Object> readDataFromDevice(DeviceConfig config, ModbusMaster master) { public Map<String, Object> readDataFromDevice(DeviceConfig config, ModbusMaster master) {
Map<String, Object> deviceData = new HashMap<>(); Map<String, Object> deviceData = new HashMap<>();
// ModbusMaster master = null; // 将master的声明提前 boolean hasError = false;
try { try {
// master = connectionManager.borrowMaster(config);
// 设置了Modbus通信的超时时间为3000毫秒3秒。当主设备与从设备通信时若在3秒内未收到响应则认为通信超时并抛出异常。这有助于避免长时间等待无响应的设备。
// master.setTimeout(5000);
BatchResults<String> results = readTagValues(master, config.getSlaveId(), config.getTags()); BatchResults<String> results = readTagValues(master, config.getSlaveId(), config.getTags());
for (TagConfig tag : config.getTags()) { for (TagConfig tag : config.getTags()) {
if (Objects.equals(tag.getDataType(), "FOUR_BYTE_FLOAT_DBCA")){ if (Objects.equals(tag.getDataType(), "FOUR_BYTE_FLOAT_DBCA")){
@ -243,28 +251,22 @@ public class ModbusProcessor {
}else { }else {
deviceData.put(tag.getKey(), results.getValue(tag.getKey())); deviceData.put(tag.getKey(), results.getValue(tag.getKey()));
} }
// try {
// Object value = readTagValue(master, config.getSlaveId(), tag);
// if (value != null) {
// deviceData.put(tag.getKey(), value);
// }
// } catch (Exception e) {
// logger.error("Failed to read tag '{}' from devices '{}'", tag.getKey(), config.getDeviceName(), e);
// }
} }
} catch (Exception e) { } catch (Exception e) {
logger.error("Failed read from devices '{}'", config.getDeviceName(), e); logger.error("Failed read from devices '{}'", config.getDeviceName(), e);
hasError = true;
} }
finally { finally {
// 关键:无论成功与否,都必须将连接归还到池中
if (master != null) { if (master != null) {
connectionManager.returnMaster(config, master); if (hasError) {
// 发生异常时废弃连接,下次重新创建
connectionManager.invalidateMaster(config, master);
} else {
// 正常时归还连接
connectionManager.returnMaster(config, master);
}
} }
} }
// String deviceNumber = config.getDeviceNumber();
// redisCache.setCacheObject(deviceNumber, deviceData);
return deviceData; return deviceData;
} }
@ -340,77 +342,101 @@ public class ModbusProcessor {
private BatchResults<String> readTagValues(ModbusMaster master, int slaveId, List<TagConfig> tags) throws Exception { private BatchResults<String> readTagValues(ModbusMaster master, int slaveId, List<TagConfig> tags) throws Exception {
try { try {
BatchRead<String> batch = new BatchRead<>(); BatchResults<String> results = sendBatchRead(master, slaveId, tags);
tags.forEach(tag -> { logBatchResults(tags, results);
Map<Integer, RegisterType> type = ModBusType.REGISTER_TYPE;
Map<String, Integer> DATA_LENGTH = ModBusType.LENGTH;
int firstDigit = Integer.parseInt(tag.getAddress().substring(0, 1));
int address = 0;
int addressLength = tag.getAddress().length();
int exp = (int) Math.pow(10, addressLength-1);
if (firstDigit != 0){
int digit = Integer.parseInt(tag.getAddress());
address = digit % (exp);
}else {
address = Integer.parseInt(tag.getAddress());
}
RegisterType registerType = type.get(firstDigit);
int dataLength = DATA_LENGTH.get(tag.getDataType());
switch (registerType) {
case COIL: {
BaseLocator<Boolean> loc = BaseLocator.coilStatus(slaveId, address);
batch.addLocator(tag.getKey(), loc);
break;
}
case DISCRETE_INPUT: {
BaseLocator<Boolean> loc = BaseLocator.inputStatus(slaveId, address);
batch.addLocator(tag.getKey(), loc);
break;
}
case HOLDING_REGISTER: {
// logger.info("HOLDING_REGISTER: {}",tag.getAddress());
if (dataLength == 28){
BaseLocator<Number> locator = BaseLocator.holdingRegister(slaveId, address, 4);
batch.addLocator(tag.getKey(), locator);
}else {
BaseLocator<Number> loc = BaseLocator.holdingRegister(slaveId, address, dataLength);
batch.addLocator(tag.getKey(), loc);
}
break;
}
case INPUT_REGISTER: {
// logger.info("INPUT_REGISTER: {}",tag.getAddress());
BaseLocator<Number> loc = BaseLocator.inputRegister(slaveId, address, dataLength);
batch.addLocator(tag.getKey(), loc);
break;
}
}
});
BatchResults<String> results = master.send(batch);
List<String> logInfoList = new ArrayList<>();
for (TagConfig tag : tags){
StringBuilder logInfo = new StringBuilder();
logInfo.append(tag.getAddress());
if (tag.getBit()!=null){
// logger.info("批处理读取寄存器成功: {}",tag.getAddress() +"(" + tag.getBit() + "):" + results.getValue(tag.getKey()));
logInfo.append("(" + tag.getBit() + "):");
}else {
// logger.info("批处理读取寄存器成功: {}",tag.getAddress() + ":" + results.getValue(tag.getKey()));
logInfo.append(":");
}
logInfo.append(results.getValue(tag.getKey()));
logInfoList.add(logInfo.toString());
}
logger.info("批处理读取寄存器成功: {}", JSON.toJSONString(logInfoList));
return results; return results;
} catch (Exception e){ } catch (Exception e){
if (isTimeoutException(e)) {
logger.warn("批量读取超时尝试降级为单点读取slaveId: {}", slaveId);
BatchResults<String> fallback = new BatchResults<>();
for (TagConfig tag : tags) {
Object value = readTagValue(master, slaveId, tag);
fallback.addResult(tag.getKey(), value);
}
logBatchResults(tags, fallback);
return fallback;
}
logger.error("Failed to read master '{}'", slaveId, e); logger.error("Failed to read master '{}'", slaveId, e);
throw new Exception(e); throw new Exception(e);
} }
} }
private BatchResults<String> sendBatchRead(ModbusMaster master, int slaveId, List<TagConfig> tags) throws Exception {
BatchRead<String> batch = new BatchRead<>();
tags.forEach(tag -> {
Map<Integer, RegisterType> type = ModBusType.REGISTER_TYPE;
Map<String, Integer> DATA_LENGTH = ModBusType.LENGTH;
int firstDigit = Integer.parseInt(tag.getAddress().substring(0, 1));
int address = 0;
int addressLength = tag.getAddress().length();
int exp = (int) Math.pow(10, addressLength - 1);
if (firstDigit != 0) {
int digit = Integer.parseInt(tag.getAddress());
address = digit % (exp);
} else {
address = Integer.parseInt(tag.getAddress());
}
RegisterType registerType = type.get(firstDigit);
int dataLength = DATA_LENGTH.get(tag.getDataType());
switch (registerType) {
case COIL: {
BaseLocator<Boolean> loc = BaseLocator.coilStatus(slaveId, address);
batch.addLocator(tag.getKey(), loc);
break;
}
case DISCRETE_INPUT: {
BaseLocator<Boolean> loc = BaseLocator.inputStatus(slaveId, address);
batch.addLocator(tag.getKey(), loc);
break;
}
case HOLDING_REGISTER: {
if (dataLength == 28) {
BaseLocator<Number> locator = BaseLocator.holdingRegister(slaveId, address, 4);
batch.addLocator(tag.getKey(), locator);
} else {
BaseLocator<Number> loc = BaseLocator.holdingRegister(slaveId, address, dataLength);
batch.addLocator(tag.getKey(), loc);
}
break;
}
case INPUT_REGISTER: {
BaseLocator<Number> loc = BaseLocator.inputRegister(slaveId, address, dataLength);
batch.addLocator(tag.getKey(), loc);
break;
}
}
});
return master.send(batch);
}
private void logBatchResults(List<TagConfig> tags, BatchResults<String> results) {
List<String> logInfoList = new ArrayList<>();
for (TagConfig tag : tags) {
StringBuilder logInfo = new StringBuilder();
logInfo.append(tag.getAddress());
if (tag.getBit() != null) {
logInfo.append("(").append(tag.getBit()).append("):");
} else {
logInfo.append(":");
}
logInfo.append(results.getValue(tag.getKey()));
logInfoList.add(logInfo.toString());
}
logger.info("批处理读取寄存器成功: {}", JSON.toJSONString(logInfoList));
}
private boolean isTimeoutException(Throwable e) {
Throwable current = e;
while (current != null) {
if (current instanceof com.serotonin.modbus4j.sero.messaging.TimeoutException) {
return true;
}
current = current.getCause();
}
return false;
}
public static float convertValueToFloat(Object value) { public static float convertValueToFloat(Object value) {
if (!(value instanceof Number)) { if (!(value instanceof Number)) {
throw new IllegalArgumentException("Input must be a Number"); throw new IllegalArgumentException("Input must be a Number");

View File

@ -31,9 +31,11 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javax.annotation.Resource;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.eclipse.paho.client.mqttv3.MqttException; import org.eclipse.paho.client.mqttv3.MqttException;
@ -56,10 +58,14 @@ public class ModbusPoller {
private final ObjectMapper objectMapper = new ObjectMapper(); private final ObjectMapper objectMapper = new ObjectMapper();
private final Map<String, Integer> deviceFailureCounts = new ConcurrentHashMap<>(); private final Map<String, Integer> deviceFailureCounts = new ConcurrentHashMap<>();
@Resource(name = "modbusExecutor")
private ExecutorService modbusExecutor;
@Autowired @Autowired
private ModbusProcessor modbusProcessor; private ModbusProcessor modbusProcessor;
@Autowired @Autowired
private IEmsAlarmRecordsService iEmsAlarmRecordsService; private IEmsAlarmRecordsService iEmsAlarmRecordsService;
@Autowired @Autowired
private ISysJobService iSysJobService; private ISysJobService iSysJobService;
@Autowired @Autowired
@ -99,8 +105,8 @@ public class ModbusPoller {
return; return;
} }
// 按 host:port 分组 // 按主机IP分组同一网关的不同端口也归为一组避免并发访问导致Connection Reset
Map<String, List<DeviceConfig>> groupedConfigs = new HashMap<>(); Map<String, List<DeviceConfig>> groupedByHost = new HashMap<>();
for (Path filePath : jsonFiles) { for (Path filePath : jsonFiles) {
DeviceConfig config = null; DeviceConfig config = null;
try { try {
@ -110,51 +116,44 @@ public class ModbusPoller {
continue; continue;
} }
if (config.isEnabled()) { if (config.isEnabled()) {
String key = config.getHost() + ":" + config.getPort(); // 只按主机IP分组确保同一网关的所有端口串行访问
groupedConfigs.computeIfAbsent(key, k -> new ArrayList<>()).add(config); String hostKey = config.getHost();
groupedByHost.computeIfAbsent(hostKey, k -> new ArrayList<>()).add(config);
} }
} }
int interval = getScheduledTaskInterval(); // 使用单线程 executor 串行执行所有主机的 Modbus 操作
// 为每个 host:port 启动一个任务 // 将所有主机的设备按顺序串行处理,避免任何并发访问
for (Map.Entry<String, List<DeviceConfig>> entry : groupedConfigs.entrySet()) { modbusExecutor.submit(() -> {
String groupKey = entry.getKey(); for (Map.Entry<String, List<DeviceConfig>> entry : groupedByHost.entrySet()) {
List<DeviceConfig> configs = entry.getValue(); String hostKey = entry.getKey();
try { List<DeviceConfig> configs = entry.getValue();
CompletableFuture.runAsync(() -> { for (DeviceConfig config : configs) {
for (DeviceConfig config : configs) { try {
try { scheduledStart(config);
scheduledStart(config); // 每次读取后等待200ms给Modbus网关足够的处理时间
} catch (Exception e) { Thread.sleep(200);
log.error("采集设备数据异常: {}", config.getDeviceName(), e); } catch (InterruptedException ie) {
} Thread.currentThread().interrupt();
} log.warn("Modbus轮询被中断");
}) return;
.exceptionally(e -> { } catch (Exception e) {
log.error("采集设备数据{}轮询异常", groupKey, e); log.error("采集设备数据异常: {}", config.getDeviceName(), e);
return null; }
}) }
.thenRun(() -> log.info("采集设备数据{}轮询任务执行完成", groupKey)); log.info("采集设备数据{}轮询任务执行完成", hostKey);
} catch (Exception e) {
log.error("采集设备数据{}任务失败", groupKey, e);
} }
} });
} }
public void scheduledStart(DeviceConfig config) { public void scheduledStart(DeviceConfig config) {
if (config.isEnabled()) { if (config.isEnabled()) {
log.info("Reading data from devices: {}", config.getDeviceName()); log.info("Reading data from devices: {}", config.getDeviceName());
ModbusMaster master = null;
try { // 带重试的读取最多重试2次
master = modbusProcessor.borrowMaster(config); Map<String, Object> data = readWithRetry(config, 2);
} catch (Exception e) {
log.error("Failed to borrow connection '{}'", config.getDeviceName(), e);
// 处理设备连接失败的情况,更新设备状态为离线,添加报警记录
addDeviceOfflineRecord(siteId, config.getDeviceNumber());
return;
}
List<String> rawValuEmptyList = new ArrayList<>(); List<String> rawValuEmptyList = new ArrayList<>();
Map<String, Object> data = modbusProcessor.readDataFromDevice(config, master);
// 在这里处理采集到的数据空 // 在这里处理采集到的数据空
config.getTags().forEach(tag -> { config.getTags().forEach(tag -> {
Object rawValue = data.get(tag.getKey()); Object rawValue = data.get(tag.getKey());
@ -191,6 +190,51 @@ public class ModbusPoller {
} }
} }
/**
* 带重试的读取方法
*/
private Map<String, Object> readWithRetry(DeviceConfig config, int maxRetries) {
Map<String, Object> data = new HashMap<>();
for (int attempt = 0; attempt <= maxRetries; attempt++) {
try {
ModbusMaster master = modbusProcessor.borrowMaster(config);
data = modbusProcessor.readDataFromDevice(config, master);
// 如果读取成功(有数据),直接返回
if (!data.isEmpty()) {
if (attempt > 0) {
log.info("设备 {} 第 {} 次重试成功", config.getDeviceName(), attempt);
}
return data;
}
// 读取返回空数据,等待后重试
if (attempt < maxRetries) {
log.warn("设备 {} 读取返回空数据等待1秒后重试 ({}/{})",
config.getDeviceName(), attempt + 1, maxRetries);
Thread.sleep(1000);
}
} catch (Exception e) {
log.error("设备 {} 读取异常 ({}/{}): {}",
config.getDeviceName(), attempt + 1, maxRetries, e.getMessage());
if (attempt < maxRetries) {
try {
Thread.sleep(1000);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
break;
}
}
}
}
// 所有重试都失败
log.error("设备 {} 读取失败,已重试 {} 次", config.getDeviceName(), maxRetries);
return data;
}
private void processingData(Map<String, Object> data, String deviceNumber) { private void processingData(Map<String, Object> data, String deviceNumber) {
if (CollectionUtils.isEmpty(data)) { if (CollectionUtils.isEmpty(data)) {
// 增加失败计数 // 增加失败计数
@ -270,4 +314,4 @@ public class ModbusPoller {
return Math.toIntExact(CronUtils.getNextExecutionIntervalMillis(sysJobs.get(0).getCronExpression())); return Math.toIntExact(CronUtils.getNextExecutionIntervalMillis(sysJobs.get(0).getCronExpression()));
} }
} }

View File

@ -39,8 +39,10 @@ import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import javax.annotation.Resource;
import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -85,28 +87,26 @@ public class StrategyPoller {
@Autowired @Autowired
private ModbusProcessor modbusProcessor; private ModbusProcessor modbusProcessor;
@Resource(name = "modbusExecutor")
private ExecutorService modbusExecutor;
public void pollAllDevices() { public void pollAllDevices() {
logger.info("开始执行运行策略数据轮询..."); logger.info("开始执行运行策略数据轮询...");
List<StrategyRunningVo> strategyRunningVoList = emsStrategyRunningMapper.getPendingPollerStrategy(null); List<StrategyRunningVo> strategyRunningVoList = emsStrategyRunningMapper.getPendingPollerStrategy(null);
strategyRunningVoList.forEach(strategyVo -> { strategyRunningVoList.forEach(strategyVo -> {
Long strategyId = strategyVo.getId(); Long strategyId = strategyVo.getId();
if (strategyLocks.putIfAbsent(strategyId, true) == null) { if (strategyLocks.putIfAbsent(strategyId, true) == null) {
try { // 使用共享的modbusExecutor串行执行避免与ModbusPoller并发访问导致通讯故障
CompletableFuture.runAsync(() -> { modbusExecutor.submit(() -> {
processData(strategyVo); try {
}) processData(strategyVo);
.exceptionally(e -> { } catch (Exception e) {
logger.error("运行策略{}轮询异常", strategyVo.getId(), e); logger.error("运行策略{}轮询异常", strategyVo.getId(), e);
return null; } finally {
}) logger.info("运行策略{}轮询任务执行完成,释放锁", strategyVo.getId());
.thenRun(() -> { strategyLocks.remove(strategyId);
logger.info("运行策略{}轮询任务执行完成,释放锁", strategyVo.getId()); }
strategyLocks.remove(strategyId); });
});
} catch (Exception e) {
logger.error("运行策略{}任务失败", strategyVo.getId(), e);
strategyLocks.remove(strategyId);
}
} else { } else {
logger.info("策略{}已在处理中,跳过重复执行", strategyId); logger.info("策略{}已在处理中,跳过重复执行", strategyId);
} }