From fd3ddb7fead0ecac782ee6b7e33c46e6e1382b2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?LAPTOP-S9HJSOEB=5C=E6=98=8A=E5=A4=A9?= Date: Wed, 20 May 2026 18:32:15 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=87=8D=E8=BF=9E=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/s7/S7MultiPlcService.java | 115 ++++++++++++++---- 1 file changed, 93 insertions(+), 22 deletions(-) diff --git a/src/main/java/com/example/lxcameraapi/service/s7/S7MultiPlcService.java b/src/main/java/com/example/lxcameraapi/service/s7/S7MultiPlcService.java index 6065973..1351db1 100644 --- a/src/main/java/com/example/lxcameraapi/service/s7/S7MultiPlcService.java +++ b/src/main/java/com/example/lxcameraapi/service/s7/S7MultiPlcService.java @@ -109,6 +109,12 @@ public class S7MultiPlcService { // 读取间隔(ms) private static final int READ_INTERVAL = 1000; + // 重连最大重试次数 + private static final int RECONNECT_MAX_RETRIES = 5; + + // 重连间隔(ms) + private static final int RECONNECT_INTERVAL = 3000; + // 线程池 private ExecutorService executorService; @@ -189,10 +195,16 @@ public class S7MultiPlcService { try { S7Client client = pool.take(); if (!client.Connected) { - PlcData config = getPlcConfig(plcNumber); - if (config != null) { - client.ConnectTo(config.getIp(), config.getRack(), config.getSlot()); + client.Disconnect(); + // 用带重试的方法重新连接 + S7Client newClient = reconnectWithRetry(plcNumber); + if (newClient != null) { + return newClient; } + // 重连失败,把旧client放回(虽然不可用,至少不丢失池容量) + log.warn("PLC {} 获取连接时重连失败", plcNumber); + pool.offer(client); + return null; } return client; } catch (InterruptedException e) { @@ -211,16 +223,45 @@ public class S7MultiPlcService { if (client.Connected) { pool.offer(client); } else { - PlcData config = getPlcConfig(plcNumber); - if (config != null) { - S7Client newClient = new S7Client(); - int result = newClient.ConnectTo(config.getIp(), config.getRack(), config.getSlot()); - if (result == 0) { - pool.offer(newClient); - } + // 断开旧连接,尝试重连后放回池中 + client.Disconnect(); + S7Client newClient = reconnectWithRetry(plcNumber); + if (newClient != null) { + pool.offer(newClient); + } else { + log.warn("PLC {} 归还连接时重连失败,池中剩余: {}/{}", plcNumber, pool.size(), POOL_SIZE); + } + } + } + } + + /** + * 带重试的重连,尝试多次直到成功或达到上限 + * @return 新连接,失败返回null + */ + private S7Client reconnectWithRetry(String plcNumber) { + PlcData config = getPlcConfig(plcNumber); + if (config == null) return null; + + for (int i = 1; i <= RECONNECT_MAX_RETRIES; i++) { + S7Client newClient = new S7Client(); + int result = newClient.ConnectTo(config.getIp(), config.getRack(), config.getSlot()); + if (result == 0) { + log.info("PLC {} 重连成功(第{}次尝试)", plcNumber, i); + return newClient; + } + log.warn("PLC {} 重连失败(第{}/{}次),错误码: {}", plcNumber, i, RECONNECT_MAX_RETRIES, result); + if (i < RECONNECT_MAX_RETRIES) { + try { + sleep(RECONNECT_INTERVAL); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + break; } } } + log.error("PLC {} 重连失败,已达最大重试次数 {}", plcNumber, RECONNECT_MAX_RETRIES); + return null; } /** @@ -313,8 +354,30 @@ public class S7MultiPlcService { if (result != 0) { log.error("PLC {} 读取失败,错误码: {}", plcNumber, result); + // 断开坏连接,防止僵尸连接被复用 + client.Disconnect(); returnConnection(plcNumber, client); - return readDataCache.get(plcNumber); + // 重试一次 + S7Client retryClient = getConnection(plcNumber); + if (retryClient != null) { + try { + byte[] retryBuffer = new byte[readLength]; + int retryResult = retryClient.ReadArea(S7.S7AreaDB, config.getReadDataBlock(), 0, readLength, retryBuffer); + if (retryResult == 0) { + log.info("PLC {} 重试读取成功", plcNumber); + buffer = retryBuffer; + result = 0; + } else { + log.error("PLC {} 重试读取仍失败,错误码: {}", plcNumber, retryResult); + retryClient.Disconnect(); + } + } finally { + returnConnection(plcNumber, retryClient); + } + } + if (result != 0) { + return readDataCache.get(plcNumber); + } } PlcReadData data = new PlcReadData(); @@ -648,6 +711,8 @@ public class S7MultiPlcService { log.info("PLC {} 写入拍照结果成功: {}", plcNumber, photoResult); } else { log.error("PLC {} 写入拍照结果失败,错误码: {}", plcNumber, result); + // 断开坏连接,防止僵尸连接被复用 + client.Disconnect(); } returnConnection(plcNumber, client); return result == 0; @@ -704,6 +769,7 @@ public class S7MultiPlcService { List clients = new ArrayList<>(); pool.drainTo(clients); + // 1. 保留有效连接,清理无效连接 for (S7Client client : clients) { if (client != null && client.Connected) { pool.offer(client); @@ -711,20 +777,25 @@ public class S7MultiPlcService { if (client != null) { client.Disconnect(); } - PlcData config = getPlcConfig(plcNumber); - if (config != null) { - S7Client newClient = new S7Client(); - int result = newClient.ConnectTo(config.getIp(), config.getRack(), config.getSlot()); - if (result == 0) { - pool.offer(newClient); - cleaned++; - } - } + cleaned++; + } + } + + // 2. 补满连接池到POOL_SIZE(带重试) + int currentSize = pool.size(); + for (int i = currentSize; i < POOL_SIZE; i++) { + log.info("PLC {} 连接池不足({}/{}),尝试补充...", plcNumber, currentSize, POOL_SIZE); + S7Client newClient = reconnectWithRetry(plcNumber); + if (newClient != null) { + pool.offer(newClient); + } else { + log.error("PLC {} 补充连接失败,当前池大小: {}/{}", plcNumber, pool.size(), POOL_SIZE); + break; // 重试都失败了,不再继续补 } } - if (cleaned > 0) { - log.info("PLC {} 替换了 {} 个无效连接", plcNumber, cleaned); + if (cleaned > 0 || pool.size() < POOL_SIZE) { + log.info("PLC {} 清理{}个无效连接,当前池大小: {}/{}", plcNumber, cleaned, pool.size(), POOL_SIZE); } } log.info("PLC连接池清理完成");