This commit is contained in:
2026-02-16 13:41:35 +08:00
parent 71d0b0f609
commit 8806473080
11 changed files with 783 additions and 53 deletions

View File

@ -208,9 +208,7 @@ public class EmsSiteMonitorController extends BaseController{
{
startPage();
SiteBatteryDataList siteBatteryDataList = new SiteBatteryDataList();
// 簇最大最小单体id数据
List<BMSBatteryDataList> clusterBatteryDataList = iSingleSiteService.getClusterBatteryList(siteId,stackDeviceId,clusterDeviceId);
siteBatteryDataList.setClusterList(clusterBatteryDataList);
// 单体电池数据
List<BatteryDataStatsListVo> List = iSingleSiteService.getClusterDataInfoList(clusterDeviceId,siteId,stackDeviceId,batteryId);
// 对batteryList进行分页处理

View File

@ -14,7 +14,9 @@ import com.xzzn.common.utils.StringUtils;
import com.xzzn.ems.domain.EmsAlarmRecords;
import com.xzzn.ems.domain.EmsFaultProtectionPlan;
import com.xzzn.ems.domain.vo.ProtectionConstraintVo;
import com.xzzn.ems.domain.vo.ProtectionPlanVo;
import com.xzzn.ems.domain.vo.ProtectionSettingVo;
import com.xzzn.ems.domain.vo.ProtectionSettingsGroupVo;
import com.xzzn.ems.mapper.EmsAlarmRecordsMapper;
import com.xzzn.ems.mapper.EmsFaultProtectionPlanMapper;
import com.xzzn.ems.service.IEmsFaultProtectionPlanService;
@ -75,11 +77,12 @@ public class ProtectionPlanTask {
if (StringUtils.isEmpty(siteId)) {
continue;
}
List<ProtectionSettingVo> protSettings = parseProtectionSettings(plan.getProtectionSettings());
if (CollectionUtils.isEmpty(protSettings)) {
ProtectionSettingsGroupVo settingGroup = parseProtectionSettings(plan.getProtectionSettings());
if (CollectionUtils.isEmpty(settingGroup.getFaultSettings())
&& CollectionUtils.isEmpty(settingGroup.getReleaseSettings())) {
continue;
}
dealWithProtectionPlan(plan, protSettings);
dealWithProtectionPlan(plan, settingGroup);
}
refreshProtectionConstraintCache(planList);
} catch (Exception e) {
@ -88,20 +91,22 @@ public class ProtectionPlanTask {
}
@SyncAfterInsert
private void dealWithProtectionPlan(EmsFaultProtectionPlan plan, List<ProtectionSettingVo> protSettings) {
private void dealWithProtectionPlan(EmsFaultProtectionPlan plan, ProtectionSettingsGroupVo settingGroup) {
logger.info("<轮询保护方案> 站点:{}方案ID:{}", plan.getSiteId(), plan.getId());
String siteId = plan.getSiteId();
Integer isAlertAlarm = plan.getIsAlert();
List<ProtectionSettingVo> faultSettings = settingGroup.getFaultSettings();
List<ProtectionSettingVo> releaseSettings = settingGroup.getReleaseSettings();
Long status = plan.getStatus();
if (status == null) {
status = ProtPlanStatus.STOP.getCode();
}
if (Objects.equals(status, ProtPlanStatus.STOP.getCode())) {
if (checkIsNeedIssuedPlan(protSettings, siteId)) {
if (checkIsNeedIssuedPlan(faultSettings, siteId)) {
int faultDelay = safeDelaySeconds(plan.getFaultDelaySeconds(), 0);
scheduledExecutorService.schedule(() -> {
if (!checkIsNeedIssuedPlan(protSettings, siteId)) {
if (!checkIsNeedIssuedPlan(faultSettings, siteId)) {
return;
}
if (Integer.valueOf(1).equals(isAlertAlarm)) {
@ -117,7 +122,7 @@ public class ProtectionPlanTask {
return;
}
if (checkIsNeedCancelPlan(protSettings, siteId)) {
if (checkIsNeedCancelPlan(releaseSettings, siteId)) {
int releaseDelay = safeDelaySeconds(plan.getReleaseDelaySeconds(), 0);
scheduledExecutorService.schedule(() -> {
if (Integer.valueOf(1).equals(isAlertAlarm)) {
@ -146,14 +151,58 @@ public class ProtectionPlanTask {
return delay.intValue();
}
private List<ProtectionSettingVo> parseProtectionSettings(String settingsJson) {
private ProtectionSettingsGroupVo parseProtectionSettings(String settingsJson) {
if (StringUtils.isEmpty(settingsJson)) {
return ProtectionSettingsGroupVo.empty();
}
try {
if (settingsJson.trim().startsWith("[")) {
List<ProtectionSettingVo> legacy = objectMapper.readValue(
settingsJson,
new TypeReference<List<ProtectionSettingVo>>() {}
);
ProtectionSettingsGroupVo groupVo = ProtectionSettingsGroupVo.empty();
groupVo.setFaultSettings(legacy);
groupVo.setReleaseSettings(legacy);
return groupVo;
}
ProtectionSettingsGroupVo groupVo = objectMapper.readValue(settingsJson, ProtectionSettingsGroupVo.class);
if (groupVo == null) {
return ProtectionSettingsGroupVo.empty();
}
if (groupVo.getFaultSettings() == null) {
groupVo.setFaultSettings(new ArrayList<>());
}
if (groupVo.getReleaseSettings() == null) {
groupVo.setReleaseSettings(new ArrayList<>());
}
return groupVo;
} catch (Exception e) {
logger.error("解析保护前提失败json:{}", settingsJson, e);
return ProtectionSettingsGroupVo.empty();
}
}
private List<ProtectionPlanVo> parseProtectionPlan(String planJson) {
if (StringUtils.isEmpty(planJson)) {
return new ArrayList<>();
}
try {
return objectMapper.readValue(settingsJson, new TypeReference<List<ProtectionSettingVo>>() {});
if (planJson.trim().startsWith("[")) {
List<ProtectionPlanVo> plans = objectMapper.readValue(
planJson,
new TypeReference<List<ProtectionPlanVo>>() {}
);
return plans == null ? new ArrayList<>() : plans;
}
ProtectionPlanVo plan = objectMapper.readValue(planJson, ProtectionPlanVo.class);
List<ProtectionPlanVo> plans = new ArrayList<>();
if (plan != null) {
plans.add(plan);
}
return plans;
} catch (Exception e) {
logger.error("解析保护前提失败json:{}", settingsJson, e);
logger.error("解析执行保护失败json:{}", planJson, e);
return new ArrayList<>();
}
}
@ -256,9 +305,98 @@ public class ProtectionPlanTask {
vo.setPowerLimitRatio(BigDecimal.ZERO);
}
// 执行保护配置优先于描述文本配置
List<ProtectionPlanVo> protectionPlans = parseProtectionPlan(plan.getProtectionPlan());
applyCapabilityByProtectionPlan(vo, protectionPlans);
return vo;
}
private void applyCapabilityByProtectionPlan(ProtectionConstraintVo vo, List<ProtectionPlanVo> protectionPlans) {
if (CollectionUtils.isEmpty(protectionPlans)) {
return;
}
for (ProtectionPlanVo item : protectionPlans) {
if (item == null) {
continue;
}
String marker = ((item.getPointName() == null ? "" : item.getPointName()) + " "
+ (item.getPoint() == null ? "" : item.getPoint())).toLowerCase();
if (StringUtils.isEmpty(marker)) {
continue;
}
if (containsAny(marker, "降功率", "derate", "power_limit", "powerlimit")) {
BigDecimal ratio = parseDerateRatioByPlan(item);
if (ratio != null) {
vo.setPowerLimitRatio(minRatio(vo.getPowerLimitRatio(), ratio));
}
}
if (!isCapabilityEnabled(item.getValue())) {
continue;
}
if (containsAny(marker, "禁止充放电", "forbid_charge_discharge", "disable_charge_discharge")) {
vo.setAllowCharge(false);
vo.setAllowDischarge(false);
continue;
}
if (containsAny(marker, "禁止充电", "forbid_charge", "disable_charge")) {
vo.setAllowCharge(false);
}
if (containsAny(marker, "禁止放电", "forbid_discharge", "disable_discharge")) {
vo.setAllowDischarge(false);
}
if (containsAny(marker, "允许充电", "allow_charge")) {
vo.setAllowCharge(true);
}
if (containsAny(marker, "允许放电", "allow_discharge")) {
vo.setAllowDischarge(true);
}
if (containsAny(marker, "待机", "standby")) {
vo.setForceStandby(true);
}
if (containsAny(marker, "关机", "停机", "切断", "shutdown", "stop")) {
vo.setForceStop(true);
vo.setForceStandby(true);
vo.setAllowCharge(false);
vo.setAllowDischarge(false);
vo.setPowerLimitRatio(BigDecimal.ZERO);
}
}
}
private BigDecimal parseDerateRatioByPlan(ProtectionPlanVo planVo) {
BigDecimal value = planVo.getValue();
if (value == null || value.compareTo(BigDecimal.ZERO) < 0) {
return null;
}
if (value.compareTo(BigDecimal.ONE) <= 0) {
return value;
}
if (value.compareTo(new BigDecimal("100")) <= 0) {
return value.divide(new BigDecimal("100"), 4, RoundingMode.HALF_UP);
}
return null;
}
private boolean isCapabilityEnabled(BigDecimal value) {
return value == null || value.compareTo(BigDecimal.ZERO) != 0;
}
private boolean containsAny(String text, String... markers) {
if (StringUtils.isEmpty(text) || markers == null) {
return false;
}
for (String marker : markers) {
if (!StringUtils.isEmpty(marker) && text.contains(marker)) {
return true;
}
}
return false;
}
private BigDecimal parseDerateRatio(String text) {
if (StringUtils.isEmpty(text)) {
return null;

View File

@ -575,9 +575,6 @@ public class StrategyPoller {
ChargeStatus targetStatus,
EmsStrategyRuntimeConfig runtimeConfig,
ProtectionConstraintVo constraint) {
if (!Integer.valueOf(1).equals(runtimeConfig.getProtectInterveneEnable())) {
return new StrategyCommandDecision(targetStatus, safePower(targetPower));
}
if (constraint == null || nullSafeInt(constraint.getLevel()) <= 0) {
return new StrategyCommandDecision(targetStatus, safePower(targetPower));
}

View File

@ -25,6 +25,18 @@ public class BatteryDataStatsListVo {
/** SOH (%) */
private BigDecimal soh;
/** 电压映射点位ID */
private String voltagePointId;
/** 温度映射点位ID */
private String temperaturePointId;
/** SOC映射点位ID */
private String socPointId;
/** SOH映射点位ID */
private String sohPointId;
@JsonFormat(pattern = "yyyy-MM-dd")
private Date dataTimestamp;
@ -71,6 +83,38 @@ public class BatteryDataStatsListVo {
this.soh = soh;
}
public String getVoltagePointId() {
return voltagePointId;
}
public void setVoltagePointId(String voltagePointId) {
this.voltagePointId = voltagePointId;
}
public String getTemperaturePointId() {
return temperaturePointId;
}
public void setTemperaturePointId(String temperaturePointId) {
this.temperaturePointId = temperaturePointId;
}
public String getSocPointId() {
return socPointId;
}
public void setSocPointId(String socPointId) {
this.socPointId = socPointId;
}
public String getSohPointId() {
return sohPointId;
}
public void setSohPointId(String sohPointId) {
this.sohPointId = sohPointId;
}
public Date getDataTimestamp() {
return dataTimestamp;
}

View File

@ -0,0 +1,114 @@
package com.xzzn.ems.domain.vo;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
/**
* 站点保护约束(供策略轮询仲裁)
*/
public class ProtectionConstraintVo implements Serializable {
private static final long serialVersionUID = 1L;
/** 最高保护等级0-无保护1/2/3 对应故障等级 */
private Integer level;
/** 是否允许充电 */
private Boolean allowCharge;
/** 是否允许放电 */
private Boolean allowDischarge;
/** 功率上限比例0~1 */
private BigDecimal powerLimitRatio;
/** 是否强制待机 */
private Boolean forceStandby;
/** 是否强制停机 */
private Boolean forceStop;
/** 生效的方案ID列表 */
private List<Long> sourcePlanIds;
/** 更新时间戳(毫秒) */
private Long updateAt;
public Integer getLevel() {
return level;
}
public void setLevel(Integer level) {
this.level = level;
}
public Boolean getAllowCharge() {
return allowCharge;
}
public void setAllowCharge(Boolean allowCharge) {
this.allowCharge = allowCharge;
}
public Boolean getAllowDischarge() {
return allowDischarge;
}
public void setAllowDischarge(Boolean allowDischarge) {
this.allowDischarge = allowDischarge;
}
public BigDecimal getPowerLimitRatio() {
return powerLimitRatio;
}
public void setPowerLimitRatio(BigDecimal powerLimitRatio) {
this.powerLimitRatio = powerLimitRatio;
}
public Boolean getForceStandby() {
return forceStandby;
}
public void setForceStandby(Boolean forceStandby) {
this.forceStandby = forceStandby;
}
public Boolean getForceStop() {
return forceStop;
}
public void setForceStop(Boolean forceStop) {
this.forceStop = forceStop;
}
public List<Long> getSourcePlanIds() {
return sourcePlanIds;
}
public void setSourcePlanIds(List<Long> sourcePlanIds) {
this.sourcePlanIds = sourcePlanIds;
}
public Long getUpdateAt() {
return updateAt;
}
public void setUpdateAt(Long updateAt) {
this.updateAt = updateAt;
}
public static ProtectionConstraintVo empty() {
ProtectionConstraintVo vo = new ProtectionConstraintVo();
vo.setLevel(0);
vo.setAllowCharge(true);
vo.setAllowDischarge(true);
vo.setPowerLimitRatio(BigDecimal.ONE);
vo.setForceStandby(false);
vo.setForceStop(false);
vo.setSourcePlanIds(new ArrayList<>());
vo.setUpdateAt(System.currentTimeMillis());
return vo;
}
}

View File

@ -0,0 +1,38 @@
package com.xzzn.ems.domain.vo;
import java.util.ArrayList;
import java.util.List;
/**
* 告警保护方案-保护前提分组
*/
public class ProtectionSettingsGroupVo {
/** 故障保护前提 */
private List<ProtectionSettingVo> faultSettings;
/** 释放保护前提 */
private List<ProtectionSettingVo> releaseSettings;
public static ProtectionSettingsGroupVo empty() {
ProtectionSettingsGroupVo vo = new ProtectionSettingsGroupVo();
vo.setFaultSettings(new ArrayList<>());
vo.setReleaseSettings(new ArrayList<>());
return vo;
}
public List<ProtectionSettingVo> getFaultSettings() {
return faultSettings;
}
public void setFaultSettings(List<ProtectionSettingVo> faultSettings) {
this.faultSettings = faultSettings;
}
public List<ProtectionSettingVo> getReleaseSettings() {
return releaseSettings;
}
public void setReleaseSettings(List<ProtectionSettingVo> releaseSettings) {
this.releaseSettings = releaseSettings;
}
}

View File

@ -13,5 +13,7 @@ public interface EmsSiteMonitorPointMatchMapper {
int deleteBySiteId(@Param("siteId") String siteId);
int deleteBySiteIdAndDeviceId(@Param("siteId") String siteId, @Param("deviceId") String deviceId);
int insertBatch(@Param("list") List<EmsSiteMonitorPointMatch> list);
}

View File

@ -69,6 +69,7 @@ import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
/**
@ -215,9 +216,127 @@ public class EmsDeviceSettingServiceImpl implements IEmsDeviceSettingService
pcsSetting.setDeviceSettingId(devicesSetting.getId());
emsPcsSettingMapper.insertEmsPcsSetting(pcsSetting);
}
initSiteMonitorPointMappingsForNewDevice(devicesSetting);
return result;
}
private void initSiteMonitorPointMappingsForNewDevice(DevicesSettingVo devicesSetting) {
if (devicesSetting == null) {
return;
}
String siteId = StringUtils.trim(devicesSetting.getSiteId());
String deviceId = StringUtils.trim(devicesSetting.getDeviceId());
String deviceCategory = StringUtils.trim(devicesSetting.getDeviceCategory());
if (StringUtils.isAnyBlank(siteId, deviceId, deviceCategory)) {
return;
}
List<EmsSiteMonitorItem> itemList = getEnabledMonitorItems();
if (CollectionUtils.isEmpty(itemList)) {
return;
}
Set<String> targetFieldCodes = itemList.stream()
.filter(Objects::nonNull)
.filter(item -> isDeviceDimensionItem(item))
.filter(item -> StringUtils.equals(deviceCategory, resolveDeviceCategoryByItem(item)))
.map(EmsSiteMonitorItem::getFieldCode)
.filter(StringUtils::isNotBlank)
.map(String::trim)
.collect(Collectors.toSet());
if (targetFieldCodes.isEmpty()) {
return;
}
List<EmsSiteMonitorPointMatch> existMappings = emsSiteMonitorPointMatchMapper.selectBySiteId(siteId);
if (existMappings == null) {
existMappings = Collections.emptyList();
}
Set<String> deletedFieldCodes = existMappings.stream()
.filter(Objects::nonNull)
.filter(item -> DELETED_FIELD_MARK.equals(item.getDataPoint()))
.map(EmsSiteMonitorPointMatch::getFieldCode)
.filter(StringUtils::isNotBlank)
.map(String::trim)
.collect(Collectors.toSet());
targetFieldCodes.removeAll(deletedFieldCodes);
if (targetFieldCodes.isEmpty()) {
return;
}
Set<String> existsFieldAndDevice = existMappings.stream()
.filter(Objects::nonNull)
.map(item -> buildMatchKey(StringUtils.trim(item.getFieldCode()), StringUtils.trim(item.getDeviceId())))
.collect(Collectors.toSet());
String operName = StringUtils.defaultIfBlank(StringUtils.trim(devicesSetting.getCreateBy()), "system");
List<EmsSiteMonitorPointMatch> insertList = new ArrayList<>();
for (String fieldCode : targetFieldCodes) {
String key = buildMatchKey(fieldCode, deviceId);
if (existsFieldAndDevice.contains(key)) {
continue;
}
EmsSiteMonitorPointMatch template = findTemplateMappingForNewDevice(existMappings, fieldCode);
EmsSiteMonitorPointMatch insertItem = new EmsSiteMonitorPointMatch();
insertItem.setSiteId(siteId);
insertItem.setFieldCode(fieldCode);
insertItem.setDeviceId(deviceId);
insertItem.setDataPoint(template == null ? "" : StringUtils.defaultString(template.getDataPoint()));
insertItem.setFixedDataPoint(template == null ? null : StringUtils.trimToNull(template.getFixedDataPoint()));
insertItem.setUseFixedDisplay(template == null ? 0 : (template.getUseFixedDisplay() == null ? 0 : template.getUseFixedDisplay()));
insertItem.setCreateBy(operName);
insertItem.setUpdateBy(operName);
insertList.add(insertItem);
}
if (insertList.isEmpty()) {
return;
}
emsSiteMonitorPointMatchMapper.insertBatch(insertList);
redisCache.deleteObject(buildSiteMonitorPointMatchRedisKey(siteId));
clearSiteMonitorLatestCache(siteId);
projectDisplayCache.remove(siteId);
}
private EmsSiteMonitorPointMatch findTemplateMappingForNewDevice(List<EmsSiteMonitorPointMatch> mappings, String fieldCode) {
if (CollectionUtils.isEmpty(mappings) || StringUtils.isBlank(fieldCode)) {
return null;
}
String normalizedFieldCode = fieldCode.trim();
EmsSiteMonitorPointMatch fallback = null;
for (EmsSiteMonitorPointMatch item : mappings) {
if (item == null || !normalizedFieldCode.equals(StringUtils.trim(item.getFieldCode()))) {
continue;
}
if (DELETED_FIELD_MARK.equals(item.getDataPoint())) {
continue;
}
if (StringUtils.isNotBlank(item.getDeviceId())) {
return item;
}
if (fallback == null) {
fallback = item;
}
}
return fallback;
}
private String resolveDeviceCategoryByItem(EmsSiteMonitorItem item) {
if (item == null) {
return null;
}
String byMenu = StringUtils.trim(MENU_DEVICE_CATEGORY_MAP.get(StringUtils.trim(item.getMenuCode())));
if (StringUtils.isNotBlank(byMenu)) {
return byMenu;
}
String fieldCode = StringUtils.trim(item.getFieldCode());
if (StringUtils.isBlank(fieldCode) || !fieldCode.contains("__")) {
return null;
}
String menuCode = fieldCode.substring(0, fieldCode.indexOf("__"));
return StringUtils.trim(MENU_DEVICE_CATEGORY_MAP.get(StringUtils.trim(menuCode)));
}
/**
* 更新设备
* @param devicesSetting
@ -277,9 +396,26 @@ public class EmsDeviceSettingServiceImpl implements IEmsDeviceSettingService
* @return 结果
*/
@Override
@Transactional(rollbackFor = Exception.class)
public int deleteEmsDevicesSettingById(Long id){
return emsDevicesMapper.deleteEmsDevicesSettingById(id);
if (id == null) {
return 0;
}
EmsDevicesSetting existDevice = emsDevicesMapper.selectEmsDevicesSettingById(id);
int rows = emsDevicesMapper.deleteEmsDevicesSettingById(id);
if (rows <= 0 || existDevice == null) {
return rows;
}
String siteId = StringUtils.trim(existDevice.getSiteId());
String deviceId = StringUtils.trim(existDevice.getDeviceId());
if (StringUtils.isAnyBlank(siteId, deviceId)) {
return rows;
}
emsSiteMonitorPointMatchMapper.deleteBySiteIdAndDeviceId(siteId, deviceId);
redisCache.deleteObject(buildSiteMonitorPointMatchRedisKey(siteId));
clearSiteMonitorLatestCache(siteId);
projectDisplayCache.remove(siteId);
return rows;
}
@Override

View File

@ -12,6 +12,7 @@ import com.xzzn.ems.domain.EmsBatteryData;
import com.xzzn.ems.domain.EmsBatteryStack;
import com.xzzn.ems.domain.EmsCoolingData;
import com.xzzn.ems.domain.EmsDhData;
import com.xzzn.ems.domain.EmsDevicesSetting;
import com.xzzn.ems.domain.EmsEmsData;
import com.xzzn.ems.domain.EmsPcsBranchData;
import com.xzzn.ems.domain.EmsSiteMonitorPointMatch;
@ -32,6 +33,7 @@ import com.xzzn.ems.mapper.EmsSiteMonitorPointMatchMapper;
import com.xzzn.ems.mapper.EmsStrategyRunningMapper;
import com.xzzn.ems.mapper.EmsStrategyTempMapper;
import com.xzzn.ems.service.IEmsEnergyPriceConfigService;
import com.xzzn.ems.service.IEmsDeviceSettingService;
import com.xzzn.ems.service.ISingleSiteService;
import com.xzzn.ems.service.InfluxPointDataWriter;
import com.xzzn.ems.utils.DevicePointMatchDataProcessor;
@ -90,6 +92,10 @@ public class SingleSiteServiceImpl implements ISingleSiteService {
private static final Set<String> PCS_DETAIL_META_FIELDS = new HashSet<>(Arrays.asList(
"siteId", "deviceId", "deviceName", "alarmNum", "pcsBranchInfoList", "dataUpdateTime"
));
private static final Set<String> BATTERY_DETAIL_META_FIELDS = new HashSet<>(Arrays.asList(
"deviceId", "clusterDeviceId", "dataTimestamp"
));
private static final String MENU_SBJK_DTDC = "SBJK_DTDC";
private static final String CLUSTER_DATA_TEP = "温度";
@ -135,6 +141,8 @@ public class SingleSiteServiceImpl implements ISingleSiteService {
@Autowired
private IEmsEnergyPriceConfigService iEmsEnergyPriceConfigService;
@Autowired
private IEmsDeviceSettingService iEmsDeviceSettingService;
@Autowired
private DevicePointMatchDataProcessor devicePointMatchDataProcessor;
@Override
@ -1007,45 +1015,292 @@ public class SingleSiteServiceImpl implements ISingleSiteService {
public List<BatteryDataStatsListVo> getClusterDataInfoList(String clusterDeviceId,String siteId,
String stackDeviceId, String batteryId) {
List<BatteryDataStatsListVo> batteryDataStatsListVo = new ArrayList<>();
log.info("getClusterDataInfoList start, siteId={}, stackDeviceId={}, clusterDeviceId={}, batteryId={}",
siteId, stackDeviceId, clusterDeviceId, batteryId);
String targetStackDeviceId = StringUtils.trimToEmpty(stackDeviceId);
String targetClusterDeviceId = StringUtils.trimToEmpty(clusterDeviceId);
String targetBatteryId = StringUtils.trimToEmpty(batteryId);
if ("undefined".equalsIgnoreCase(targetStackDeviceId) || "null".equalsIgnoreCase(targetStackDeviceId)) {
targetStackDeviceId = "";
}
if ("undefined".equalsIgnoreCase(targetClusterDeviceId) || "null".equalsIgnoreCase(targetClusterDeviceId)) {
targetClusterDeviceId = "";
}
if ("undefined".equalsIgnoreCase(targetBatteryId) || "null".equalsIgnoreCase(targetBatteryId)) {
targetBatteryId = "";
}
log.info("getClusterDataInfoList normalized params, siteId={}, stackDeviceId={}, clusterDeviceId={}, batteryId={}",
siteId, targetStackDeviceId, targetClusterDeviceId, targetBatteryId);
if (StringUtils.isEmpty(siteId)) {
log.warn("getClusterDataInfoList early return, siteId is empty");
return batteryDataStatsListVo;
}
Map<String, Map<String, SiteMonitorProjectDisplayVo>> batteryDisplayMap =
buildBatteryDisplayMap(iEmsDeviceSettingService.getSiteMonitorProjectDisplay(siteId));
if (batteryDisplayMap.isEmpty()) {
log.warn("getClusterDataInfoList no battery display data from projectDisplay, siteId={}", siteId);
}
log.info("getClusterDataInfoList battery list source projectDisplay, siteId={}, batteryCount={}",
siteId, batteryDisplayMap.size());
Map<String, EmsDevicesSetting> batteryDeviceCache = new HashMap<>();
Map<String, EmsDevicesSetting> clusterDeviceCache = new HashMap<>();
List<String> sortedBatteryIds = new ArrayList<>(batteryDisplayMap.keySet());
Collections.sort(sortedBatteryIds);
for (String batteryDeviceId : sortedBatteryIds) {
if (StringUtils.isEmpty(batteryDeviceId)) {
continue;
}
if (StringUtils.isNotEmpty(targetBatteryId) && !batteryDeviceId.equals(targetBatteryId)) {
continue;
}
EmsDevicesSetting batterySetting = batteryDeviceCache.computeIfAbsent(
batteryDeviceId, key -> emsDevicesSettingMapper.getDeviceBySiteAndDeviceId(key, siteId));
String currentClusterDeviceId = batterySetting == null ? "" : StringUtils.trimToEmpty(batterySetting.getParentId());
if (StringUtils.isNotEmpty(targetClusterDeviceId) && !targetClusterDeviceId.equals(currentClusterDeviceId)) {
continue;
}
if (StringUtils.isNotEmpty(targetStackDeviceId)) {
if (StringUtils.isEmpty(currentClusterDeviceId)) {
continue;
}
EmsDevicesSetting clusterSetting = clusterDeviceCache.computeIfAbsent(
currentClusterDeviceId, key -> emsDevicesSettingMapper.getDeviceBySiteAndDeviceId(key, siteId));
String parentStackDeviceId = clusterSetting == null ? "" : StringUtils.trimToEmpty(clusterSetting.getParentId());
if (!targetStackDeviceId.equals(parentStackDeviceId)) {
continue;
}
}
BatteryDataStatsListVo batteryDataStatsVo = new BatteryDataStatsListVo();
fillBatteryValueFromProjectDisplay(batteryDataStatsVo, batteryDisplayMap.get(batteryDeviceId));
batteryDataStatsVo.setDeviceId(batteryDeviceId);
batteryDataStatsVo.setClusterDeviceId(currentClusterDeviceId);
batteryDataStatsListVo.add(batteryDataStatsVo);
log.info("getClusterDataInfoList battery from projectDisplay list, siteId={}, batteryDeviceId={}, clusterDeviceId={}, voltage={}, temperature={}, soc={}, soh={}, voltagePointId={}, temperaturePointId={}, socPointId={}, sohPointId={}, dataTimestamp={}",
siteId, batteryDeviceId, currentClusterDeviceId, batteryDataStatsVo.getVoltage(),
batteryDataStatsVo.getTemperature(), batteryDataStatsVo.getSoc(),
batteryDataStatsVo.getSoh(), batteryDataStatsVo.getVoltagePointId(),
batteryDataStatsVo.getTemperaturePointId(), batteryDataStatsVo.getSocPointId(),
batteryDataStatsVo.getSohPointId(), batteryDataStatsVo.getDataTimestamp());
}
if (batteryDataStatsListVo.isEmpty()) {
log.info("getClusterDataInfoList fallback to realtime cache, siteId={}, stackDeviceId={}, clusterDeviceId={}, batteryId={}",
siteId, targetStackDeviceId, targetClusterDeviceId, targetBatteryId);
appendBatteryFromRealtimeCache(siteId, targetStackDeviceId, targetClusterDeviceId, targetBatteryId,
batteryDisplayMap, batteryDataStatsListVo);
}
log.info("getClusterDataInfoList end, siteId={}, stackDeviceId={}, clusterDeviceId={}, batteryId={}, total={}",
siteId, targetStackDeviceId, targetClusterDeviceId, targetBatteryId, batteryDataStatsListVo.size());
return batteryDataStatsListVo;
}
private void appendBatteryFromRealtimeCache(String siteId,
String targetStackDeviceId,
String targetClusterDeviceId,
String targetBatteryId,
Map<String, Map<String, SiteMonitorProjectDisplayVo>> batteryDisplayMap,
List<BatteryDataStatsListVo> targetList) {
if (StringUtils.isBlank(siteId) || targetList == null) {
return;
}
List<Map<String, Object>> clusterIds = new ArrayList<>();
if (StringUtils.isEmpty(clusterDeviceId)) {
clusterIds = emsDevicesSettingMapper.getClusterIdsByFuzzyQuery(siteId, DeviceCategory.CLUSTER.getCode(),stackDeviceId);
} else {
if (StringUtils.isNotBlank(targetClusterDeviceId)) {
Map<String, Object> map = new HashMap<>();
map.put("id", clusterDeviceId);
map.put("id", targetClusterDeviceId);
clusterIds.add(map);
} else {
clusterIds = emsDevicesSettingMapper.getClusterIdsByFuzzyQuery(siteId,
DeviceCategory.CLUSTER.getCode(), StringUtils.trimToEmpty(targetStackDeviceId));
}
if (CollectionUtils.isEmpty(clusterIds)) {
return;
}
for (Map<String, Object> clusterDevice : clusterIds) {
// 从redis取单个簇详细数据
String clusterId = clusterDevice.get("id").toString();
if (clusterDevice == null || clusterDevice.get("id") == null) {
continue;
}
String clusterId = StringUtils.trimToEmpty(String.valueOf(clusterDevice.get("id")));
if (StringUtils.isBlank(clusterId)) {
continue;
}
List<EmsBatteryData> batteryDataList = redisCache.getCacheList(RedisKeyConstants.BATTERY + siteId + "_" + clusterId);
if (batteryDataList != null) {
if (CollectionUtils.isEmpty(batteryDataList)) {
continue;
}
for (EmsBatteryData batteryData : batteryDataList) {
// 判断是否需要筛选batteryId不为空时才进行匹配
if (batteryId == null || batteryId.trim().isEmpty()) {
// 空值情况:直接添加所有数据
if (batteryData == null) {
continue;
}
String currentBatteryId = StringUtils.trimToEmpty(batteryData.getDeviceId());
if (StringUtils.isNotEmpty(targetBatteryId) && !targetBatteryId.equals(currentBatteryId)) {
continue;
}
BatteryDataStatsListVo batteryDataStatsVo = new BatteryDataStatsListVo();
BeanUtils.copyProperties(batteryData, batteryDataStatsVo);
batteryDataStatsListVo.add(batteryDataStatsVo);
if (StringUtils.isBlank(batteryDataStatsVo.getClusterDeviceId())) {
batteryDataStatsVo.setClusterDeviceId(clusterId);
}
fillBatteryPointIdFromProjectDisplay(batteryDataStatsVo, batteryDisplayMap == null ? null : batteryDisplayMap.get(currentBatteryId));
targetList.add(batteryDataStatsVo);
}
}
}
private void fillBatteryDetailByLatestPointMapping(String siteId,
String deviceId,
BatteryDataStatsListVo target,
Map<String, EmsSiteMonitorPointMatch> mappingByFieldAndDevice,
Map<String, InfluxPointDataWriter.PointValue> latestPointCache) {
if (StringUtils.isAnyBlank(siteId, deviceId) || target == null || mappingByFieldAndDevice == null) {
return;
}
BeanWrapper beanWrapper = new BeanWrapperImpl(target);
Date latestDataTime = null;
for (PropertyDescriptor pd : beanWrapper.getPropertyDescriptors()) {
if (pd == null || StringUtils.isBlank(pd.getName()) || pd.getWriteMethod() == null) {
continue;
}
String fieldCode = pd.getName();
if (BATTERY_DETAIL_META_FIELDS.contains(fieldCode)) {
continue;
}
EmsSiteMonitorPointMatch pointMatch = resolvePointMatchByFieldAndDevice(mappingByFieldAndDevice, fieldCode, deviceId);
if (pointMatch == null) {
continue;
}
Object fieldValue;
Date valueTime = null;
if (USE_FIXED_DISPLAY_YES == (pointMatch.getUseFixedDisplay() == null ? 0 : pointMatch.getUseFixedDisplay())
&& StringUtils.isNotBlank(pointMatch.getFixedDataPoint())) {
fieldValue = pointMatch.getFixedDataPoint().trim();
} else {
// 有值情况:只添加匹配的数据
if (batteryId.equals(batteryData.getDeviceId())) {
BatteryDataStatsListVo batteryDataStatsVo = new BatteryDataStatsListVo();
BeanUtils.copyProperties(batteryData, batteryDataStatsVo);
batteryDataStatsListVo.add(batteryDataStatsVo);
// 找到匹配项后可提前退出当前簇的循环
break;
InfluxPointDataWriter.PointValue latestValue = getLatestPointValueByPointId(siteId, pointMatch.getDataPoint(), latestPointCache);
if (latestValue == null || latestValue.getPointValue() == null) {
continue;
}
fieldValue = latestValue.getPointValue();
valueTime = latestValue.getDataTime();
}
Object convertedValue = convertFieldValueByType(fieldValue, pd.getPropertyType());
if (convertedValue == null) {
continue;
}
beanWrapper.setPropertyValue(fieldCode, convertedValue);
if (valueTime != null && (latestDataTime == null || valueTime.after(latestDataTime))) {
latestDataTime = valueTime;
}
}
if (latestDataTime != null) {
target.setDataTimestamp(latestDataTime);
}
}
private Map<String, Map<String, SiteMonitorProjectDisplayVo>> buildBatteryDisplayMap(List<SiteMonitorProjectDisplayVo> displayList) {
Map<String, Map<String, SiteMonitorProjectDisplayVo>> result = new HashMap<>();
if (CollectionUtils.isEmpty(displayList)) {
return result;
}
//TODO 临时删除排序
// 排序
// List<BatteryDataStatsListVo> sortedList = batteryDataStatsListVo.stream()
// .sorted((u1, u2) -> Integer.parseInt(u1.getClusterDeviceId()) - Integer.parseInt(u2.getClusterDeviceId()))
// .collect(Collectors.toList());
return batteryDataStatsListVo;
for (SiteMonitorProjectDisplayVo item : displayList) {
if (item == null) {
continue;
}
String menuCode = StringUtils.trimToEmpty(item.getMenuCode());
String deviceId = StringUtils.trimToEmpty(item.getDeviceId());
String fieldCode = StringUtils.trimToEmpty(item.getFieldCode());
if (!MENU_SBJK_DTDC.equals(menuCode) || StringUtils.isEmpty(deviceId) || StringUtils.isEmpty(fieldCode)) {
continue;
}
Map<String, SiteMonitorProjectDisplayVo> fieldMap = result.computeIfAbsent(deviceId, key -> new HashMap<>());
fieldMap.put(fieldCode, item);
String normalizedFieldCode = normalizeBatteryFieldCode(fieldCode);
if (StringUtils.isNotBlank(normalizedFieldCode)) {
fieldMap.put(normalizedFieldCode, item);
}
}
return result;
}
private String normalizeBatteryFieldCode(String fieldCode) {
String normalized = StringUtils.trimToEmpty(fieldCode);
if (StringUtils.isBlank(normalized)) {
return normalized;
}
int splitIndex = normalized.lastIndexOf("__");
if (splitIndex >= 0 && splitIndex + 2 < normalized.length()) {
normalized = normalized.substring(splitIndex + 2);
}
return normalized.toLowerCase();
}
private void fillBatteryValueFromProjectDisplay(BatteryDataStatsListVo target,
Map<String, SiteMonitorProjectDisplayVo> fieldMap) {
if (target == null || fieldMap == null || fieldMap.isEmpty()) {
return;
}
SiteMonitorProjectDisplayVo voltageVo = fieldMap.get("voltage");
SiteMonitorProjectDisplayVo temperatureVo = fieldMap.get("temperature");
SiteMonitorProjectDisplayVo socVo = fieldMap.get("soc");
SiteMonitorProjectDisplayVo sohVo = fieldMap.get("soh");
target.setVoltagePointId(voltageVo == null ? null : StringUtils.trimToNull(voltageVo.getDataPoint()));
target.setTemperaturePointId(temperatureVo == null ? null : StringUtils.trimToNull(temperatureVo.getDataPoint()));
target.setSocPointId(socVo == null ? null : StringUtils.trimToNull(socVo.getDataPoint()));
target.setSohPointId(sohVo == null ? null : StringUtils.trimToNull(sohVo.getDataPoint()));
if (voltageVo != null && StringUtils.isNotBlank(voltageVo.getFieldValue())) {
target.setVoltage(StringUtils.getBigDecimal(voltageVo.getFieldValue()));
}
if (temperatureVo != null && StringUtils.isNotBlank(temperatureVo.getFieldValue())) {
target.setTemperature(StringUtils.getBigDecimal(temperatureVo.getFieldValue()));
}
if (socVo != null && StringUtils.isNotBlank(socVo.getFieldValue())) {
target.setSoc(StringUtils.getBigDecimal(socVo.getFieldValue()));
}
if (sohVo != null && StringUtils.isNotBlank(sohVo.getFieldValue())) {
target.setSoh(StringUtils.getBigDecimal(sohVo.getFieldValue()));
}
Date latest = null;
latest = pickLaterTime(latest, voltageVo == null ? null : voltageVo.getValueTime());
latest = pickLaterTime(latest, temperatureVo == null ? null : temperatureVo.getValueTime());
latest = pickLaterTime(latest, socVo == null ? null : socVo.getValueTime());
latest = pickLaterTime(latest, sohVo == null ? null : sohVo.getValueTime());
if (latest != null) {
target.setDataTimestamp(latest);
}
}
private void fillBatteryPointIdFromProjectDisplay(BatteryDataStatsListVo target,
Map<String, SiteMonitorProjectDisplayVo> fieldMap) {
if (target == null || fieldMap == null || fieldMap.isEmpty()) {
return;
}
SiteMonitorProjectDisplayVo voltageVo = fieldMap.get("voltage");
SiteMonitorProjectDisplayVo temperatureVo = fieldMap.get("temperature");
SiteMonitorProjectDisplayVo socVo = fieldMap.get("soc");
SiteMonitorProjectDisplayVo sohVo = fieldMap.get("soh");
target.setVoltagePointId(voltageVo == null ? null : StringUtils.trimToNull(voltageVo.getDataPoint()));
target.setTemperaturePointId(temperatureVo == null ? null : StringUtils.trimToNull(temperatureVo.getDataPoint()));
target.setSocPointId(socVo == null ? null : StringUtils.trimToNull(socVo.getDataPoint()));
target.setSohPointId(sohVo == null ? null : StringUtils.trimToNull(sohVo.getDataPoint()));
}
private Date pickLaterTime(Date left, Date right) {
if (left == null) {
return right;
}
if (right == null) {
return left;
}
return right.after(left) ? right : left;
}
// 获取单站的最大最小温度和电压单体数据id

View File

@ -175,6 +175,7 @@
from ems_devices_setting t
where t.site_id = #{siteId}
and t.parent_id = #{parentId}
order by t.device_id
</select>
<select id="getAllBatteryDeviceBySiteId" parameterType="String" resultType="com.xzzn.ems.domain.EmsDevicesSetting">
@ -206,9 +207,10 @@
<select id="getDeviceBySiteAndDeviceId" parameterType="String" resultMap="EmsDevicesSettingResult">
<include refid="selectEmsDevicesSettingVo"/>
where device_id = #{deviceId}
AND site_id = #{siteId}
limit 1
where site_id = #{siteId}
<if test="deviceId != null and deviceId != ''">
AND device_id = #{deviceId}
</if>
</select>
<select id="getAmmeterNameList" parameterType="String" resultType="java.util.Map">

View File

@ -31,6 +31,12 @@
where site_id = #{siteId}
</delete>
<delete id="deleteBySiteIdAndDeviceId">
delete from ems_site_monitor_point_match
where site_id = #{siteId}
and device_id = #{deviceId}
</delete>
<insert id="insertBatch">
insert into ems_site_monitor_point_match
(site_id, field_code, device_id, data_point, fixed_data_point, use_fixed_display, create_by, create_time, update_by, update_time)