临时修改
This commit is contained in:
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user