临时修改

This commit is contained in:
2026-02-13 21:41:23 +08:00
parent 21673ecd1e
commit 6253fb6b2d
29 changed files with 2277 additions and 111 deletions

View File

@ -16,6 +16,7 @@ import com.xzzn.ems.domain.EmsAmmeterData;
import com.xzzn.ems.domain.EmsBatteryStack;
import com.xzzn.ems.domain.EmsDevicesSetting;
import com.xzzn.ems.domain.EmsPcsSetting;
import com.xzzn.ems.domain.EmsStrategyRuntimeConfig;
import com.xzzn.ems.domain.EmsStrategyLog;
import com.xzzn.ems.domain.EmsStrategyTemp;
import com.xzzn.ems.domain.EmsStrategyTimeConfig;
@ -25,17 +26,20 @@ import com.xzzn.ems.mapper.EmsBatteryStackMapper;
import com.xzzn.ems.mapper.EmsDevicesSettingMapper;
import com.xzzn.ems.mapper.EmsPcsSettingMapper;
import com.xzzn.ems.mapper.EmsStrategyLogMapper;
import com.xzzn.ems.mapper.EmsStrategyRuntimeConfigMapper;
import com.xzzn.ems.mapper.EmsStrategyRunningMapper;
import com.xzzn.ems.mapper.EmsStrategyTempMapper;
import com.xzzn.ems.mapper.EmsStrategyTimeConfigMapper;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@ -57,16 +61,20 @@ public class StrategyPoller {
private static final ConcurrentHashMap<Long, Boolean> strategyLocks = new ConcurrentHashMap<>();
// SOC 上下限值默认为0%-100%
private static final BigDecimal SOC_DOWN = new BigDecimal(0);
private static final BigDecimal SOC_UP = new BigDecimal(100);
private static final BigDecimal DEFAULT_SOC_DOWN = BigDecimal.ZERO;
private static final BigDecimal DEFAULT_SOC_UP = new BigDecimal(100);
// 逆变器功率下限值默认为30kW
private static final BigDecimal ANTI_REVERSE_THRESHOLD = new BigDecimal(30);
private static final BigDecimal DEFAULT_ANTI_REVERSE_THRESHOLD = new BigDecimal(30);
// 逆变器下限值范围默认为20%
private static final BigDecimal ANTI_REVERSE_RANGE_PERCENT = new BigDecimal(20);
private static final BigDecimal DEFAULT_ANTI_REVERSE_RANGE_PERCENT = new BigDecimal(20);
// 逆变器功率上限值默认为100kW
private static final BigDecimal ANTI_REVERSE_UP = new BigDecimal(100);
private static final BigDecimal DEFAULT_ANTI_REVERSE_UP = new BigDecimal(100);
// PCS功率降幅默认为10%
private static final BigDecimal ANTI_REVERSE_POWER_DOWN_PERCENT = new BigDecimal(10);
private static final BigDecimal DEFAULT_ANTI_REVERSE_POWER_DOWN_PERCENT = new BigDecimal(10);
// 电网有功功率低于20kW时强制待机
private static final BigDecimal DEFAULT_ANTI_REVERSE_HARD_STOP_THRESHOLD = new BigDecimal(20);
// 除法精度避免BigDecimal除不尽异常
private static final int POWER_SCALE = 4;
@Autowired
private EmsStrategyRunningMapper emsStrategyRunningMapper;
@ -85,6 +93,8 @@ public class StrategyPoller {
@Autowired
private EmsStrategyLogMapper emsStrategyLogMapper;
@Autowired
private EmsStrategyRuntimeConfigMapper runtimeConfigMapper;
@Autowired
private ModbusProcessor modbusProcessor;
@Resource(name = "modbusExecutor")
@ -132,6 +142,7 @@ public class StrategyPoller {
}
private void dealStrategyCurveData(Long strategyId, String siteId) {
EmsStrategyRuntimeConfig runtimeConfig = getRuntimeConfig(siteId);
// 1.获取当前策略的所有模板
List<Map<String, String>> temps = emsStrategyTempMapper.getTempNameList(strategyId, siteId);
if (CollectionUtils.isEmpty(temps)) {
@ -174,24 +185,38 @@ public class StrategyPoller {
continue;
}
// 判断SOC上下限
if (isSocInRange(emsStrategyTemp)) {
BigDecimal avgChargeDischargePower = emsStrategyTemp.getChargeDischargePower().divide(new BigDecimal(pcsDeviceList.size()));
if (isSocInRange(emsStrategyTemp, runtimeConfig)) {
Map<Long, EmsPcsSetting> pcsSettingCache = new HashMap<>();
BigDecimal avgChargeDischargePower = emsStrategyTemp.getChargeDischargePower()
.divide(new BigDecimal(pcsDeviceList.size()), POWER_SCALE, RoundingMode.HALF_UP);
BigDecimal totalActivePower = null;
if (ChargeStatus.DISCHARGING.getCode().equals(emsStrategyTemp.getChargeStatus())) {
// 同一站点同一轮执行只读取一次电网电表,降低重复查库和数据抖动
EmsAmmeterData emsAmmeterData = emsAmmeterDataMapper.getLastData(emsStrategyTemp.getSiteId(), SiteDevice.LOAD.name());
if (emsAmmeterData != null) {
totalActivePower = emsAmmeterData.getTotalActivePower();
}
}
for (EmsDevicesSetting pcsDevice : pcsDeviceList) {
EmsPcsSetting pcsSetting = emsPcsSettingMapper.selectEmsPcsSettingByDeviceId(pcsDevice.getId());
EmsPcsSetting pcsSetting = pcsSettingCache.computeIfAbsent(
pcsDevice.getId(),
id -> emsPcsSettingMapper.selectEmsPcsSettingByDeviceId(id)
);
if (pcsSetting == null || pcsSetting.getClusterNum() < 1) {
logger.info("当前站点: {}, PCS设备: {} 未获取电池簇数量", siteId, pcsDevice.getDeviceId());
continue;
}
// 功率默认放大10倍平均功率值根据电池簇数量进行平均分配
avgChargeDischargePower = avgChargeDischargePower.multiply(new BigDecimal(10)).divide(new BigDecimal(pcsSetting.getClusterNum()));
BigDecimal strategyPower = avgChargeDischargePower.multiply(new BigDecimal(10))
.divide(new BigDecimal(pcsSetting.getClusterNum()), POWER_SCALE, RoundingMode.HALF_UP);
// 根据充电状态,处理数据
if (ChargeStatus.CHARGING.getCode().equals(emsStrategyTemp.getChargeStatus())) {
// 发送Modbus命令控制设备-充电
sendModbusCommand(Collections.singletonList(pcsDevice), pcsSetting, ChargeStatus.CHARGING, avgChargeDischargePower, emsStrategyTemp, false, null);
sendModbusCommand(Collections.singletonList(pcsDevice), pcsSetting, ChargeStatus.CHARGING, strategyPower, emsStrategyTemp, false, null);
} else if (ChargeStatus.DISCHARGING.getCode().equals(emsStrategyTemp.getChargeStatus())) {
boolean needAntiReverseFlow = false;
Integer powerDownType = null;
BigDecimal chargeDischargePower = avgChargeDischargePower;
BigDecimal chargeDischargePower = strategyPower;
// 查询策略运行日志
EmsStrategyLog lastStrategyLog = getLastStrategyLog(pcsDevice.getDeviceId(), emsStrategyTemp);
if (lastStrategyLog != null) {
@ -204,32 +229,54 @@ public class StrategyPoller {
}
// 查询电网电表的正向有功功率,36kW-50kW范围内稳定运行低于36kW降功率高于50kW增加功率
EmsAmmeterData emsAmmeterData = emsAmmeterDataMapper.getLastData(emsStrategyTemp.getSiteId(), SiteDevice.LOAD.name());
if (emsAmmeterData == null || emsAmmeterData.getTotalActivePower() == null) {
logger.info("当前站点: {}, 未获取到最新电表数据", emsStrategyTemp.getSiteId());
if (totalActivePower == null) {
logger.warn("当前站点: {}, 未获取到最新电表数据,执行保守策略并切换待机", emsStrategyTemp.getSiteId());
sendModbusCommand(Collections.singletonList(pcsDevice), pcsSetting, ChargeStatus.STANDBY, BigDecimal.ZERO, emsStrategyTemp, true, 0);
continue;
} else {
// 电网功率过低,直接待机,不再放电
if (totalActivePower.compareTo(runtimeConfig.getAntiReverseHardStopThreshold()) < 0) {
sendModbusCommand(Collections.singletonList(pcsDevice), pcsSetting, ChargeStatus.STANDBY, BigDecimal.ZERO, emsStrategyTemp, true, 0);
continue;
}
// 放电开始先按差值限幅:差值=电网功率-防逆流阈值
BigDecimal diffPower = totalActivePower.subtract(runtimeConfig.getAntiReverseThreshold());
BigDecimal targetPower = diffPower.compareTo(BigDecimal.ZERO) > 0 ? diffPower : BigDecimal.ZERO;
if (targetPower.compareTo(strategyPower) > 0) {
targetPower = strategyPower;
}
if (chargeDischargePower.compareTo(targetPower) > 0) {
chargeDischargePower = targetPower;
}
// 判断是否需要防逆流
needAntiReverseFlow = isNeedAntiReverseFlow(emsAmmeterData.getTotalActivePower());
BigDecimal power = avgChargeDischargePower.multiply(ANTI_REVERSE_POWER_DOWN_PERCENT).divide(new BigDecimal(100));
needAntiReverseFlow = isNeedAntiReverseFlow(totalActivePower, runtimeConfig);
BigDecimal power = strategyPower.multiply(runtimeConfig.getAntiReversePowerDownPercent())
.divide(new BigDecimal(100), POWER_SCALE, RoundingMode.HALF_UP);
if (needAntiReverseFlow) {
// 降功率
chargeDischargePower = chargeDischargePower.subtract(power);
powerDownType = 0;
} else {
// 判断是否需要增加功率,
if (powerDownType != null && emsAmmeterData.getTotalActivePower().compareTo(ANTI_REVERSE_UP) > 0) {
if (chargeDischargePower.compareTo(avgChargeDischargePower) == 0) {
// 功率增加到平均值则停止
if (powerDownType != null && totalActivePower.compareTo(runtimeConfig.getAntiReverseUp()) > 0) {
if (chargeDischargePower.compareTo(targetPower) >= 0) {
// 功率增加到限幅值则停止
continue;
}
// 增加功率
chargeDischargePower = chargeDischargePower.add(power);
if (chargeDischargePower.compareTo(targetPower) > 0) {
chargeDischargePower = targetPower;
}
powerDownType = 1;
needAntiReverseFlow = true;
}
}
}
if (chargeDischargePower.compareTo(BigDecimal.ZERO) < 0) {
chargeDischargePower = BigDecimal.ZERO;
}
if (BigDecimal.ZERO.compareTo(chargeDischargePower) == 0) {
// 如果已经降功率到0则设备直接待机
// 发送Modbus命令控制设备-待机
@ -297,11 +344,11 @@ public class StrategyPoller {
return emsStrategyLogMapper.getLastStrategyLog(query);
}
private boolean isNeedAntiReverseFlow(BigDecimal totalActivePower) {
private boolean isNeedAntiReverseFlow(BigDecimal totalActivePower, EmsStrategyRuntimeConfig runtimeConfig) {
// 获取当前设定的防逆流阈值(30kW)
BigDecimal threshold = ANTI_REVERSE_THRESHOLD;
BigDecimal threshold = runtimeConfig.getAntiReverseThreshold();
// 计算20%范围的上限(36kW)
BigDecimal upperLimit = threshold.multiply(ANTI_REVERSE_RANGE_PERCENT).divide(new BigDecimal(100)).add(threshold);
BigDecimal upperLimit = threshold.multiply(runtimeConfig.getAntiReverseRangePercent()).divide(new BigDecimal(100)).add(threshold);
// 判断电网电表正向有功功率是否小于36kW(接近30kW的20%范围)
return totalActivePower.compareTo(upperLimit) < 0;
@ -409,8 +456,9 @@ public class StrategyPoller {
continue;
} else {
// 充、放电,则先开机设备
switchDevice(pcsDevice, pcsSetting, WorkStatus.NORMAL);
continue;
if (!switchDevice(pcsDevice, pcsSetting, WorkStatus.NORMAL)) {
continue;
}
}
}
@ -440,14 +488,17 @@ public class StrategyPoller {
private boolean switchDevice(EmsDevicesSetting pcsDevice, EmsPcsSetting pcsSetting, WorkStatus workStatus) {
String siteId = pcsDevice.getSiteId();
String deviceId = pcsDevice.getDeviceId();
String originalWorkStatus = pcsDevice.getWorkStatus();
pcsDevice.setWorkStatus(workStatus.getCode());
DeviceConfig deviceConfig = getDeviceConfig(siteId, deviceId, pcsDevice, pcsSetting , null, 1);
if (deviceConfig == null) {
pcsDevice.setWorkStatus(originalWorkStatus);
return false;
}
boolean result = modbusProcessor.writeDataToDeviceWithRetry(deviceConfig);
if (!result) {
logger.info("当前站点: {}, PCS设备: {} modbus控制设备{}指令发送失败", siteId, deviceConfig, workStatus.getInfo());
pcsDevice.setWorkStatus(originalWorkStatus);
logger.info("当前站点: {}, PCS设备: {} modbus控制设备{}指令发送失败", siteId, deviceId, workStatus.getInfo());
}
return result;
}
@ -461,13 +512,17 @@ public class StrategyPoller {
LocalTime endLocalTime = endTime.toInstant()
.atZone(zoneId)
.toLocalTime();
return now.equals(startLocalTime) || (now.isAfter(startLocalTime) && now.isBefore(endLocalTime));
// 支持跨天时段如23:00-01:00边界采用闭区间
if (!startLocalTime.isAfter(endLocalTime)) {
return !now.isBefore(startLocalTime) && !now.isAfter(endLocalTime);
}
return !now.isBefore(startLocalTime) || !now.isAfter(endLocalTime);
}
// 判断SOC上限和下限
private boolean isSocInRange(EmsStrategyTemp emsStrategyTemp) {
BigDecimal socDown = SOC_DOWN;
BigDecimal socUp = SOC_UP;
private boolean isSocInRange(EmsStrategyTemp emsStrategyTemp, EmsStrategyRuntimeConfig runtimeConfig) {
BigDecimal socDown = runtimeConfig.getSocDown();
BigDecimal socUp = runtimeConfig.getSocUp();
if (SocLimit.ON.getCode().equals(emsStrategyTemp.getSdcLimit())) {
socDown = emsStrategyTemp.getSdcDown();
socUp = emsStrategyTemp.getSdcUp();
@ -491,4 +546,34 @@ public class StrategyPoller {
return true;
}
private EmsStrategyRuntimeConfig getRuntimeConfig(String siteId) {
EmsStrategyRuntimeConfig config = runtimeConfigMapper.selectBySiteId(siteId);
if (config == null) {
config = new EmsStrategyRuntimeConfig();
config.setSiteId(siteId);
}
if (config.getSocDown() == null) {
config.setSocDown(DEFAULT_SOC_DOWN);
}
if (config.getSocUp() == null) {
config.setSocUp(DEFAULT_SOC_UP);
}
if (config.getAntiReverseThreshold() == null) {
config.setAntiReverseThreshold(DEFAULT_ANTI_REVERSE_THRESHOLD);
}
if (config.getAntiReverseRangePercent() == null) {
config.setAntiReverseRangePercent(DEFAULT_ANTI_REVERSE_RANGE_PERCENT);
}
if (config.getAntiReverseUp() == null) {
config.setAntiReverseUp(DEFAULT_ANTI_REVERSE_UP);
}
if (config.getAntiReversePowerDownPercent() == null) {
config.setAntiReversePowerDownPercent(DEFAULT_ANTI_REVERSE_POWER_DOWN_PERCENT);
}
if (config.getAntiReverseHardStopThreshold() == null) {
config.setAntiReverseHardStopThreshold(DEFAULT_ANTI_REVERSE_HARD_STOP_THRESHOLD);
}
return config;
}
}