diff --git a/ems-common/src/main/java/com/xzzn/common/core/modbus/ModbusProcessor.java b/ems-common/src/main/java/com/xzzn/common/core/modbus/ModbusProcessor.java index a096002..b30f749 100644 --- a/ems-common/src/main/java/com/xzzn/common/core/modbus/ModbusProcessor.java +++ b/ems-common/src/main/java/com/xzzn/common/core/modbus/ModbusProcessor.java @@ -28,6 +28,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; @@ -51,12 +52,12 @@ public class ModbusProcessor { public boolean writeDataToDevice(DeviceConfig config) { logger.info("writeDataToDevice: {}", JSON.toJSONString(config)); ModbusMaster master = null; - boolean result = true; + boolean result; try { master = connectionManager.borrowMaster(config); - // 设置了Modbus通信的超时时间为3000毫秒(3秒)。当主设备与从设备通信时,若在3秒内未收到响应,则认为通信超时并抛出异常。这有助于避免长时间等待无响应的设备。 - master.setTimeout(5000); - writeTagValue(master, config, config.getWriteTags()); + // 设置了Modbus通信的超时时间为1000毫秒(1秒)。当主设备与从设备通信时,若在1秒内未收到响应,则认为通信超时并抛出异常。这有助于避免长时间等待无响应的设备。 + master.setTimeout(1000); + result = writeTagValue(master, config, config.getWriteTags()); } catch (Exception e) { logger.error("Failed to borrow connection or write to devices '{}'", config.getDeviceName(), e); result = false; @@ -70,7 +71,33 @@ public class ModbusProcessor { return result; } - public void writeTagValue(ModbusMaster master, DeviceConfig config, List tags) { + public boolean writeDataToDeviceWithRetry(DeviceConfig config) { + logger.info("writeDataToDevice: {}", JSON.toJSONString(config)); + ModbusMaster master = null; + boolean result; + + try { + master = connectionManager.borrowMaster(config); + master.setTimeout(1000); // 设置超时时间 + + // 使用重试装饰器 + ModbusMaster finalMaster = master; + result = RetryableModbusOperation.executeWithRetry(() -> { + return writeTagValue(finalMaster, config, config.getWriteTags()); + }, 1); // 最大重试1次(共2次尝试) + + } catch (Exception e) { + logger.error("Failed to borrow connection or write to devices '{}'", config.getDeviceName(), e); + result = false; + } finally { + if (master != null) { + connectionManager.returnMaster(config, master); + } + } + return result; + } + + public boolean writeTagValue(ModbusMaster master, DeviceConfig config, List tags) { tags.forEach(tag -> { Map type = ModBusType.REGISTER_TYPE; int firstDigit = Integer.parseInt(tag.getAddress().substring(0, 1)); @@ -79,7 +106,8 @@ public class ModbusProcessor { logger.info("Register type: {}, address: {}, firstDigit: {}", registerType, tag.getAddress(), firstDigit); switch (registerType) { case COIL: { - writeCoilRequest(master, config.getSlaveId(), address, Boolean.parseBoolean(String.valueOf(tag.getValue()))); + boolean result = writeCoilRequest(master, config.getSlaveId(), address, Boolean.parseBoolean(String.valueOf(tag.getValue()))); + tag.setWrite(result); break; } case HOLDING_REGISTER: { @@ -89,7 +117,8 @@ public class ModbusProcessor { if (doubleValue < -32768 || doubleValue > 32767) { logger.warn("Value {} out of range for 16-bit signed register at address {}", doubleValue, address); } - writeRegisterRequest(master, config.getSlaveId(), address, (int) doubleValue); + boolean result = writeRegisterRequest(master, config.getSlaveId(), address, (int) doubleValue); + tag.setWrite(result); break; } default: @@ -97,9 +126,15 @@ public class ModbusProcessor { break; } }); + + List collect = tags.stream().filter(tag -> !Objects.equals(tag.isWrite(), true)).collect(Collectors.toList()); + if (!collect.isEmpty()) { + return false; + } + return true; } - public static void writeCoilRequest(ModbusMaster master, int slaveId, int address, boolean value) { + public static boolean writeCoilRequest(ModbusMaster master, int slaveId, int address, boolean value) { try { WriteCoilRequest request = new WriteCoilRequest(slaveId, address, value); WriteCoilResponse response = (WriteCoilResponse)master.send(request); @@ -107,10 +142,12 @@ public class ModbusProcessor { logger.info("Write coil failed: " + response.getExceptionMessage()); } else { logger.info("Write coil successful"); + return true; } } catch (Exception e) { logger.error("Failed to write coil value '{}' to address '{}'", value, address, e); } + return false; } public static void writeCoilsRequest(ModbusMaster master, int slaveId, int address, boolean[] values) { @@ -127,7 +164,7 @@ public class ModbusProcessor { } } - public static void writeRegisterRequest(ModbusMaster master, int slaveId, int address, int value) { + public static boolean writeRegisterRequest(ModbusMaster master, int slaveId, int address, int value) { try { WriteRegisterRequest request = new WriteRegisterRequest(slaveId, address, value); WriteRegisterResponse response = (WriteRegisterResponse)master.send(request); @@ -135,10 +172,12 @@ public class ModbusProcessor { logger.info("Write register failed: " + response.getExceptionMessage()); } else { logger.info("Write register successful"); + return true; } } catch (Exception e) { logger.error("Failed to write register value '{}' to address '{}'", value, address, e); } + return false; } public static void writeRegistersRequest(ModbusMaster master, int slaveId, int address, short[] values) { @@ -181,8 +220,8 @@ public class ModbusProcessor { public ModbusMaster borrowMaster(DeviceConfig config) throws Exception { ModbusMaster master = connectionManager.borrowMaster(config); - // 设置了Modbus通信的超时时间为3000毫秒(3秒)。当主设备与从设备通信时,若在3秒内未收到响应,则认为通信超时并抛出异常。这有助于避免长时间等待无响应的设备。 - master.setTimeout(10000); + // 设置了Modbus通信的超时时间为5000毫秒(5秒)。当主设备与从设备通信时,若在5秒内未收到响应,则认为通信超时并抛出异常。这有助于避免长时间等待无响应的设备。 + master.setTimeout(5000); return master; } diff --git a/ems-common/src/main/java/com/xzzn/common/core/modbus/RetryableModbusOperation.java b/ems-common/src/main/java/com/xzzn/common/core/modbus/RetryableModbusOperation.java new file mode 100644 index 0000000..b2732ee --- /dev/null +++ b/ems-common/src/main/java/com/xzzn/common/core/modbus/RetryableModbusOperation.java @@ -0,0 +1,37 @@ +package com.xzzn.common.core.modbus; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Modbus重试 + * + */ +public class RetryableModbusOperation { + private static final Logger logger = LoggerFactory.getLogger(RetryableModbusOperation.class); + + public interface ModbusOperation { + T execute() throws Exception; + } + + public static T executeWithRetry(ModbusOperation operation, int maxRetries) { + int retryCount = 0; + Exception lastException = null; + + while (retryCount <= maxRetries) { + try { + return operation.execute(); + } catch (Exception e) { + lastException = e; + retryCount++; + if (retryCount <= maxRetries) { + logger.info("Operation failed, retrying... (Attempt {})", retryCount); + } + } + } + + logger.error("Max retries ({}) reached. Last error: {}", maxRetries, + lastException != null ? lastException.getMessage() : "Unknown"); + throw new RuntimeException("Operation failed after " + maxRetries + " retries", lastException); + } +} diff --git a/ems-common/src/main/java/com/xzzn/common/core/modbus/domain/WriteTagConfig.java b/ems-common/src/main/java/com/xzzn/common/core/modbus/domain/WriteTagConfig.java index 8e82405..2a60fc4 100644 --- a/ems-common/src/main/java/com/xzzn/common/core/modbus/domain/WriteTagConfig.java +++ b/ems-common/src/main/java/com/xzzn/common/core/modbus/domain/WriteTagConfig.java @@ -3,6 +3,7 @@ package com.xzzn.common.core.modbus.domain; public class WriteTagConfig { private Object value; private String address; + private boolean isWrite; public Object getValue() { return value; @@ -19,4 +20,12 @@ public class WriteTagConfig { public void setAddress(String address) { this.address = address; } + + public boolean isWrite() { + return isWrite; + } + + public void setWrite(boolean write) { + isWrite = write; + } } diff --git a/ems-quartz/src/main/java/com/xzzn/quartz/task/ModbusPoller.java b/ems-quartz/src/main/java/com/xzzn/quartz/task/ModbusPoller.java index 43f4db0..6c572be 100644 --- a/ems-quartz/src/main/java/com/xzzn/quartz/task/ModbusPoller.java +++ b/ems-quartz/src/main/java/com/xzzn/quartz/task/ModbusPoller.java @@ -230,7 +230,7 @@ public class ModbusPoller { // 存放mqtt原始每个设备最晚一次数据,便于后面点位获取数据 redisCache.setCacheObject(RedisKeyConstants.ORIGINAL_MQTT_DATA + siteId + "_" + deviceNumber, obj); // 存放每次同步数据,失效时间(同同步时间)-用于判断是否正常同步数据和保护策略查询 - redisCache.setCacheObject(RedisKeyConstants.SYNC_DATA_ALARM + siteId + "_" + deviceNumber, obj, 1, TimeUnit.MINUTES); + redisCache.setCacheObject(RedisKeyConstants.SYNC_DATA + siteId + "_" + deviceNumber, obj, 1, TimeUnit.MINUTES); log.info("数据已成功存储在Redis: {}", deviceNumber); } catch (Exception e) { log.error("无法在设备的Redis中存储数据: {}", deviceNumber, e); diff --git a/ems-quartz/src/main/java/com/xzzn/quartz/task/ProtectionPlanTask.java b/ems-quartz/src/main/java/com/xzzn/quartz/task/ProtectionPlanTask.java index bba3473..dc65f1a 100644 --- a/ems-quartz/src/main/java/com/xzzn/quartz/task/ProtectionPlanTask.java +++ b/ems-quartz/src/main/java/com/xzzn/quartz/task/ProtectionPlanTask.java @@ -89,9 +89,7 @@ public class ProtectionPlanTask { Long planId = 0L; try { // 获取所有方案,轮询 - EmsFaultProtectionPlan emsFaultProtectionPlan = new EmsFaultProtectionPlan(); - emsFaultProtectionPlan.setSiteId("021_DDS_01"); - List planList = iEmsFaultProtectionPlanService.selectEmsFaultProtectionPlanList(emsFaultProtectionPlan); + List planList = iEmsFaultProtectionPlanService.selectEmsFaultProtectionPlanList(null); for (EmsFaultProtectionPlan plan : planList) { planId = plan.getId(); @@ -154,7 +152,7 @@ public class ProtectionPlanTask { // 是否有保护方案,有则通过modbus连接设备下发方案 String protPlanJson = plan.getProtectionPlan(); - if (protPlanJson != null && !protPlanJson.isEmpty()) { + if (protPlanJson != null && !protPlanJson.isEmpty() && !"[]".equals(protPlanJson)) { logger.info("<下发保护方案> 方案内容:{}", protPlanJson); executeProtectionActions(protPlanJson,siteId,plan.getId(),plan.getFaultLevel()); // 执行Modbus指令 } diff --git a/ems-quartz/src/main/java/com/xzzn/quartz/task/StrategyPoller.java b/ems-quartz/src/main/java/com/xzzn/quartz/task/StrategyPoller.java index 93684cc..938878f 100644 --- a/ems-quartz/src/main/java/com/xzzn/quartz/task/StrategyPoller.java +++ b/ems-quartz/src/main/java/com/xzzn/quartz/task/StrategyPoller.java @@ -161,6 +161,7 @@ public class StrategyPoller { } // 判断当前时间是否在时间段内 if (!isTimeInRange(LocalTime.now(), emsStrategyTemp.getStartTime(), emsStrategyTemp.getEndTime())) { + logger.info("当前站点: {}, 策略: {}, 时间段:{} 不在时间段内", siteId, strategyId, emsStrategyTemp.getStartTime() + "-" + emsStrategyTemp.getEndTime()); continue; } // 查询PCS设备信息 @@ -420,10 +421,14 @@ public class StrategyPoller { boolean result = modbusProcessor.writeDataToDevice(deviceConfig); if (!result) { logger.info("当前站点: {}, PCS设备: {} modbus控制设备{}指令发送失败", siteId, deviceId, chargeStatus.getInfo()); + continue; } else { if (ChargeStatus.STANDBY.equals(chargeStatus)) { // 待机,先写功率值,再关机 - switchDevice(pcsDevice, pcsSetting, WorkStatus.STOP); + if (!switchDevice(pcsDevice, pcsSetting, WorkStatus.STOP)) { + logger.info("当前站点: {}, PCS设备: {} modbus控制设备{}指令发送失败", siteId, deviceId, WorkStatus.STOP.getInfo()); + continue; + } } } // 记录策略执行日志 @@ -432,18 +437,19 @@ public class StrategyPoller { } //设备开关机 - private void switchDevice(EmsDevicesSetting pcsDevice, EmsPcsSetting pcsSetting, WorkStatus workStatus) { + private boolean switchDevice(EmsDevicesSetting pcsDevice, EmsPcsSetting pcsSetting, WorkStatus workStatus) { String siteId = pcsDevice.getSiteId(); String deviceId = pcsDevice.getDeviceId(); pcsDevice.setWorkStatus(workStatus.getCode()); DeviceConfig deviceConfig = getDeviceConfig(siteId, deviceId, pcsDevice, pcsSetting , null, 1); if (deviceConfig == null) { - return; + return false; } boolean result = modbusProcessor.writeDataToDevice(deviceConfig); if (!result) { logger.info("当前站点: {}, PCS设备: {} modbus控制设备{}指令发送失败", siteId, deviceConfig, workStatus.getInfo()); } + return result; } // 判断当前时间是否在时间范围内