|
|
@@ -23,7 +23,9 @@ import java.util.concurrent.CompletableFuture;
|
|
|
import java.util.concurrent.ExecutionException;
|
|
|
import java.util.concurrent.ExecutorService;
|
|
|
import java.util.concurrent.Executors;
|
|
|
+import java.util.concurrent.ConcurrentHashMap;
|
|
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
|
+import java.util.concurrent.locks.ReentrantLock;
|
|
|
|
|
|
import static com.dcs.equipment.constants.ModbusConstants.*;
|
|
|
import static com.dcs.equipment.domain.ModbusTcpEnum.DATA_ADDRESS_LENGTH_MAP;
|
|
|
@@ -39,63 +41,84 @@ public class ModbusUtil {
|
|
|
|
|
|
private static final ExecutorService ModbusThreadPoolTaskExecutor = Executors.newFixedThreadPool(10);
|
|
|
|
|
|
- @Value("${dcs.device.reconnectLimit}")
|
|
|
- private static int reconnectLimit;
|
|
|
+ private static volatile int reconnectLimit = 3;
|
|
|
|
|
|
- private static Map<String, ModbusMaster> masters = new HashMap<>();
|
|
|
- private static Map<String, AtomicBoolean> reconnectingStatus = new HashMap<>();
|
|
|
+ @Value("${dcs.device.reconnectLimit:3}")
|
|
|
+ public void setReconnectLimit(int limit) { ModbusUtil.reconnectLimit = limit; }
|
|
|
+
|
|
|
+ private static final ConcurrentHashMap<String, ModbusMaster> masters = new ConcurrentHashMap<>();
|
|
|
+ private static final ConcurrentHashMap<String, AtomicBoolean> reconnectingStatus = new ConcurrentHashMap<>();
|
|
|
+ private static final ConcurrentHashMap<String, ReentrantLock> urlLocks = new ConcurrentHashMap<>();
|
|
|
+
|
|
|
+ private static ReentrantLock getUrlLock(String url) {
|
|
|
+ return urlLocks.computeIfAbsent(url, k -> new ReentrantLock());
|
|
|
+ }
|
|
|
|
|
|
public static ModbusMaster connect(String host, Integer port) throws ModbusIOException, UnknownHostException {
|
|
|
String url = host + ":" + port;
|
|
|
- logger.info("正在连接到 ModbusTCP {} 服务端", url);
|
|
|
- TcpParameters tcpParameters = new TcpParameters();
|
|
|
- InetAddress address = null;
|
|
|
- try {
|
|
|
- address = InetAddress.getByName(host);
|
|
|
- } catch (UnknownHostException e) {
|
|
|
- logger.error("ModbusTCP {} 地址解析失败: {}", url, e.getMessage());
|
|
|
- throw new UnknownHostException("ModbusTCP " + url + " 地址解析失败");
|
|
|
+ // fast path: already connected
|
|
|
+ ModbusMaster fastExisting = masters.get(url);
|
|
|
+ if (fastExisting != null && fastExisting.isConnected()) {
|
|
|
+ return fastExisting;
|
|
|
}
|
|
|
- tcpParameters.setHost(address);
|
|
|
- tcpParameters.setPort(port);
|
|
|
- tcpParameters.setKeepAlive(true);
|
|
|
- Modbus.setAutoIncrementTransactionId(true);
|
|
|
- ModbusMaster master = ModbusMasterFactory.createModbusMasterTCP(tcpParameters);
|
|
|
+ ReentrantLock lock = getUrlLock(url);
|
|
|
+ lock.lock();
|
|
|
try {
|
|
|
- master.connect();
|
|
|
- masters.put(url, master);
|
|
|
- reconnectingStatus.put(url, new AtomicBoolean(false));
|
|
|
- logger.info("与 ModbusTCP {} 连接成功", url);
|
|
|
- } catch (ModbusIOException e) {
|
|
|
- logger.error("与 ModbusTCP {} 连接失败: {}", url, e.getMessage());
|
|
|
- throw new ModbusIOException("ModbusTCP " + url + " 连接失败");
|
|
|
+ ModbusMaster existing = masters.get(url);
|
|
|
+ if (existing != null && existing.isConnected()) {
|
|
|
+ return existing;
|
|
|
+ }
|
|
|
+
|
|
|
+ logger.info("正在连接到 ModbusTCP {} 服务端", url);
|
|
|
+ TcpParameters tcpParameters = new TcpParameters();
|
|
|
+ InetAddress address;
|
|
|
+ try {
|
|
|
+ address = InetAddress.getByName(host);
|
|
|
+ } catch (UnknownHostException e) {
|
|
|
+ logger.error("ModbusTCP {} 地址解析失败: {}", url, e.getMessage());
|
|
|
+ throw new UnknownHostException("ModbusTCP " + url + " 地址解析失败");
|
|
|
+ }
|
|
|
+ tcpParameters.setHost(address);
|
|
|
+ tcpParameters.setPort(port);
|
|
|
+ tcpParameters.setKeepAlive(true);
|
|
|
+ Modbus.setAutoIncrementTransactionId(true);
|
|
|
+ ModbusMaster master = ModbusMasterFactory.createModbusMasterTCP(tcpParameters);
|
|
|
+ try {
|
|
|
+ master.connect();
|
|
|
+ masters.put(url, master);
|
|
|
+ reconnectingStatus.computeIfAbsent(url, k -> new AtomicBoolean(false));
|
|
|
+ logger.info("与 ModbusTCP {} 连接成功", url);
|
|
|
+ } catch (ModbusIOException e) {
|
|
|
+ logger.error("与 ModbusTCP {} 连接失败: {}", url, e.getMessage());
|
|
|
+ throw new ModbusIOException("ModbusTCP " + url + " 连接失败");
|
|
|
+ }
|
|
|
+ return masters.get(url);
|
|
|
+ } finally {
|
|
|
+ lock.unlock();
|
|
|
}
|
|
|
- return master;
|
|
|
}
|
|
|
|
|
|
private static void reconnect(String host, Integer port) throws DeviceIOException {
|
|
|
String url = host + ":" + port;
|
|
|
- AtomicBoolean reconnecting = reconnectingStatus.get(url);
|
|
|
+ AtomicBoolean reconnecting = reconnectingStatus.computeIfAbsent(url, k -> new AtomicBoolean(false));
|
|
|
if (reconnecting.get()) return;
|
|
|
|
|
|
if (reconnecting.compareAndSet(false, true)) {
|
|
|
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
|
|
|
try {
|
|
|
for (int i = 0; i < reconnectLimit; i++) { // 单次重连 最多尝试次数
|
|
|
- ModbusMaster master = masters.get(url);
|
|
|
logger.warn("ModbusTCP {} 连接断开,开始重连...", url);
|
|
|
try {
|
|
|
- connect(host, port);
|
|
|
+ ModbusMaster newMaster = connect(host, port);
|
|
|
+ if (newMaster != null && newMaster.isConnected()) {
|
|
|
+ logger.info("ModbusTCP {} 重连成功!", url);
|
|
|
+ break;
|
|
|
+ }
|
|
|
} catch (UnknownHostException e) {
|
|
|
throw new RuntimeException(e);
|
|
|
} catch (ModbusIOException e) {
|
|
|
if (i == reconnectLimit - 1) throw new RuntimeException(e);
|
|
|
}
|
|
|
- if (master.isConnected()) {
|
|
|
- logger.info("ModbusTCP {} 重连成功!", url);
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
try {
|
|
|
Thread.sleep(1000); // 每次重试间隔 1 秒
|
|
|
} catch (InterruptedException e) {
|
|
|
@@ -125,19 +148,23 @@ public class ModbusUtil {
|
|
|
}
|
|
|
|
|
|
public static void disconnect(String host, Integer port) {
|
|
|
- String url = host+":"+port;
|
|
|
- ModbusMaster master= masters.get(url);
|
|
|
+ String url = host + ":" + port;
|
|
|
+ ReentrantLock lock = getUrlLock(url);
|
|
|
+ lock.lock();
|
|
|
try {
|
|
|
- master.disconnect();
|
|
|
- if (masters.containsKey(url)) {
|
|
|
- masters.remove(url);
|
|
|
- }
|
|
|
- if (reconnectingStatus.containsKey(url)) {
|
|
|
- reconnectingStatus.remove(url);
|
|
|
+ ModbusMaster master = masters.get(url);
|
|
|
+ if (master != null) {
|
|
|
+ try {
|
|
|
+ master.disconnect();
|
|
|
+ } catch (ModbusIOException e) {
|
|
|
+ logger.error("与 ModbusTCP {} 断开连接失败: {}", url, e.getMessage());
|
|
|
+ }
|
|
|
}
|
|
|
+ masters.remove(url);
|
|
|
+ reconnectingStatus.remove(url);
|
|
|
logger.info("ModbusTCP {} 断开连接", url);
|
|
|
- } catch (ModbusIOException e) {
|
|
|
- logger.error("与 ModbusTCP {} 断开连接失败: {}", url, e.getMessage());
|
|
|
+ } finally {
|
|
|
+ lock.unlock();
|
|
|
}
|
|
|
}
|
|
|
@PreDestroy
|
|
|
@@ -311,8 +338,9 @@ public class ModbusUtil {
|
|
|
}
|
|
|
|
|
|
private static ModbusMaster getMaster(String host, Integer port) throws DeviceIOException {
|
|
|
- ModbusMaster master = masters.get(host+":"+port);
|
|
|
- if (master == null){
|
|
|
+ String url = host + ":" + port;
|
|
|
+ ModbusMaster master = masters.get(url);
|
|
|
+ if (master == null) {
|
|
|
try {
|
|
|
return connect(host, port);
|
|
|
} catch (ModbusIOException | UnknownHostException e) {
|
|
|
@@ -321,6 +349,7 @@ public class ModbusUtil {
|
|
|
}
|
|
|
if (!master.isConnected()) {
|
|
|
reconnect(host, port);
|
|
|
+ master = masters.get(url);
|
|
|
}
|
|
|
return master;
|
|
|
}
|