From 5eb9b455a552ffd2ea19d1027403a65766c276e0 Mon Sep 17 00:00:00 2001 From: xiaoyang <17515057146@163.com> Date: Wed, 15 Apr 2026 22:47:17 +0800 Subject: [PATCH] =?UTF-8?q?1.=E5=8D=95=E4=BD=93=E7=94=B5=E6=B1=A0=E6=89=B9?= =?UTF-8?q?=E9=87=8F=E6=96=B0=E5=A2=9E=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ems/EmsSiteConfigController.java | 8 + .../vo/SingleBatteryConfigInitRequest.java | 41 ++ .../vo/SingleBatteryConfigInitResultVo.java | 122 ++++++ .../ems/service/IEmsDeviceSettingService.java | 4 + .../impl/EmsDeviceSettingServiceImpl.java | 375 ++++++++++++++++++ .../service/impl/SingleSiteServiceImpl.java | 85 +++- 6 files changed, 633 insertions(+), 2 deletions(-) create mode 100644 ems-system/src/main/java/com/xzzn/ems/domain/vo/SingleBatteryConfigInitRequest.java create mode 100644 ems-system/src/main/java/com/xzzn/ems/domain/vo/SingleBatteryConfigInitResultVo.java diff --git a/ems-admin/src/main/java/com/xzzn/web/controller/ems/EmsSiteConfigController.java b/ems-admin/src/main/java/com/xzzn/web/controller/ems/EmsSiteConfigController.java index 3c1dd6f..78c1fcd 100644 --- a/ems-admin/src/main/java/com/xzzn/web/controller/ems/EmsSiteConfigController.java +++ b/ems-admin/src/main/java/com/xzzn/web/controller/ems/EmsSiteConfigController.java @@ -16,6 +16,8 @@ import com.xzzn.ems.domain.vo.PointDataRequest; import com.xzzn.ems.domain.vo.PointQueryResponse; import com.xzzn.ems.domain.vo.SiteDeviceListVo; import com.xzzn.ems.domain.vo.SiteMonitorProjectPointMappingSaveRequest; +import com.xzzn.ems.domain.vo.SingleBatteryConfigInitRequest; +import com.xzzn.ems.domain.vo.SingleBatteryConfigInitResultVo; import com.xzzn.ems.domain.vo.SingleBatteryMonitorImportResultVo; import com.xzzn.ems.domain.vo.SingleBatteryMonitorImportRowVo; import com.xzzn.ems.domain.vo.WorkStatusEnumMappingSaveRequest; @@ -204,6 +206,12 @@ public class EmsSiteConfigController extends BaseController { return AjaxResult.success(result); } + @PostMapping("/initializeSingleBatteryMonitorMappings") + public AjaxResult initializeSingleBatteryMonitorMappings(@RequestBody SingleBatteryConfigInitRequest request) { + SingleBatteryConfigInitResultVo result = iEmsDeviceSettingService.initializeSingleBatteryMonitorMappings(request, getUsername()); + return AjaxResult.success(result); + } + // @PreAuthorize("@ss.hasPermi('system:device:onAndOff')") @Log(title = "寮€鍏虫満", businessType = BusinessType.UPDATE) @PostMapping("/updateDeviceStatus") diff --git a/ems-system/src/main/java/com/xzzn/ems/domain/vo/SingleBatteryConfigInitRequest.java b/ems-system/src/main/java/com/xzzn/ems/domain/vo/SingleBatteryConfigInitRequest.java new file mode 100644 index 0000000..85c4343 --- /dev/null +++ b/ems-system/src/main/java/com/xzzn/ems/domain/vo/SingleBatteryConfigInitRequest.java @@ -0,0 +1,41 @@ +package com.xzzn.ems.domain.vo; + +public class SingleBatteryConfigInitRequest { + + private String siteId; + private String scopeType; + private String scopeDeviceId; + private Integer targetCount; + + public String getSiteId() { + return siteId; + } + + public void setSiteId(String siteId) { + this.siteId = siteId; + } + + public String getScopeType() { + return scopeType; + } + + public void setScopeType(String scopeType) { + this.scopeType = scopeType; + } + + public String getScopeDeviceId() { + return scopeDeviceId; + } + + public void setScopeDeviceId(String scopeDeviceId) { + this.scopeDeviceId = scopeDeviceId; + } + + public Integer getTargetCount() { + return targetCount; + } + + public void setTargetCount(Integer targetCount) { + this.targetCount = targetCount; + } +} diff --git a/ems-system/src/main/java/com/xzzn/ems/domain/vo/SingleBatteryConfigInitResultVo.java b/ems-system/src/main/java/com/xzzn/ems/domain/vo/SingleBatteryConfigInitResultVo.java new file mode 100644 index 0000000..2440a33 --- /dev/null +++ b/ems-system/src/main/java/com/xzzn/ems/domain/vo/SingleBatteryConfigInitResultVo.java @@ -0,0 +1,122 @@ +package com.xzzn.ems.domain.vo; + +public class SingleBatteryConfigInitResultVo { + + private Boolean committed; + private String siteId; + private String scopeType; + private String scopeDeviceId; + private Integer targetCount; + private Integer existingBatteryCount; + private Integer initializedBatteryCount; + private Integer insertedBatteryCount; + private Integer pointHitCount; + private Integer fixedValueFallbackCount; + private Integer insertedMappingCount; + private Integer updatedMappingCount; + private String message; + + public Boolean getCommitted() { + return committed; + } + + public void setCommitted(Boolean committed) { + this.committed = committed; + } + + public String getSiteId() { + return siteId; + } + + public void setSiteId(String siteId) { + this.siteId = siteId; + } + + public String getScopeType() { + return scopeType; + } + + public void setScopeType(String scopeType) { + this.scopeType = scopeType; + } + + public String getScopeDeviceId() { + return scopeDeviceId; + } + + public void setScopeDeviceId(String scopeDeviceId) { + this.scopeDeviceId = scopeDeviceId; + } + + public Integer getTargetCount() { + return targetCount; + } + + public void setTargetCount(Integer targetCount) { + this.targetCount = targetCount; + } + + public Integer getExistingBatteryCount() { + return existingBatteryCount; + } + + public void setExistingBatteryCount(Integer existingBatteryCount) { + this.existingBatteryCount = existingBatteryCount; + } + + public Integer getInitializedBatteryCount() { + return initializedBatteryCount; + } + + public void setInitializedBatteryCount(Integer initializedBatteryCount) { + this.initializedBatteryCount = initializedBatteryCount; + } + + public Integer getInsertedBatteryCount() { + return insertedBatteryCount; + } + + public void setInsertedBatteryCount(Integer insertedBatteryCount) { + this.insertedBatteryCount = insertedBatteryCount; + } + + public Integer getPointHitCount() { + return pointHitCount; + } + + public void setPointHitCount(Integer pointHitCount) { + this.pointHitCount = pointHitCount; + } + + public Integer getFixedValueFallbackCount() { + return fixedValueFallbackCount; + } + + public void setFixedValueFallbackCount(Integer fixedValueFallbackCount) { + this.fixedValueFallbackCount = fixedValueFallbackCount; + } + + public Integer getInsertedMappingCount() { + return insertedMappingCount; + } + + public void setInsertedMappingCount(Integer insertedMappingCount) { + this.insertedMappingCount = insertedMappingCount; + } + + public Integer getUpdatedMappingCount() { + return updatedMappingCount; + } + + public void setUpdatedMappingCount(Integer updatedMappingCount) { + this.updatedMappingCount = updatedMappingCount; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } +} diff --git a/ems-system/src/main/java/com/xzzn/ems/service/IEmsDeviceSettingService.java b/ems-system/src/main/java/com/xzzn/ems/service/IEmsDeviceSettingService.java index d8c940c..5b310c0 100644 --- a/ems-system/src/main/java/com/xzzn/ems/service/IEmsDeviceSettingService.java +++ b/ems-system/src/main/java/com/xzzn/ems/service/IEmsDeviceSettingService.java @@ -10,6 +10,8 @@ import com.xzzn.ems.domain.vo.SiteMonitorDataSaveRequest; import com.xzzn.ems.domain.vo.SiteMonitorProjectDisplayVo; import com.xzzn.ems.domain.vo.SiteMonitorProjectPointMappingSaveRequest; import com.xzzn.ems.domain.vo.SiteMonitorProjectPointMappingVo; +import com.xzzn.ems.domain.vo.SingleBatteryConfigInitRequest; +import com.xzzn.ems.domain.vo.SingleBatteryConfigInitResultVo; import com.xzzn.ems.domain.vo.SingleBatteryMonitorImportResultVo; import com.xzzn.ems.domain.vo.SingleBatteryMonitorImportRowVo; import com.xzzn.ems.domain.vo.WorkStatusEnumMappingVo; @@ -60,4 +62,6 @@ public interface IEmsDeviceSettingService public int syncSiteMonitorDataByMqtt(String siteId, String deviceId, String jsonData, Date valueTime); public SingleBatteryMonitorImportResultVo importSingleBatteryMonitorMappings(String siteId, List rows, String operName); + + public SingleBatteryConfigInitResultVo initializeSingleBatteryMonitorMappings(SingleBatteryConfigInitRequest request, String operName); } diff --git a/ems-system/src/main/java/com/xzzn/ems/service/impl/EmsDeviceSettingServiceImpl.java b/ems-system/src/main/java/com/xzzn/ems/service/impl/EmsDeviceSettingServiceImpl.java index 80b3c8c..86fa049 100644 --- a/ems-system/src/main/java/com/xzzn/ems/service/impl/EmsDeviceSettingServiceImpl.java +++ b/ems-system/src/main/java/com/xzzn/ems/service/impl/EmsDeviceSettingServiceImpl.java @@ -39,6 +39,8 @@ import com.xzzn.ems.domain.vo.SiteMonitorDataVo; import com.xzzn.ems.domain.vo.SiteMonitorProjectDisplayVo; import com.xzzn.ems.domain.vo.SiteMonitorProjectPointMappingSaveRequest; import com.xzzn.ems.domain.vo.SiteMonitorProjectPointMappingVo; +import com.xzzn.ems.domain.vo.SingleBatteryConfigInitRequest; +import com.xzzn.ems.domain.vo.SingleBatteryConfigInitResultVo; import com.xzzn.ems.domain.vo.SingleBatteryMonitorImportFailureVo; import com.xzzn.ems.domain.vo.SingleBatteryMonitorImportResultVo; import com.xzzn.ems.domain.vo.SingleBatteryMonitorImportRowVo; @@ -69,6 +71,7 @@ import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Objects; @@ -122,6 +125,9 @@ public class EmsDeviceSettingServiceImpl implements IEmsDeviceSettingService private static final String BATTERY_FIELD_TEMPERATURE = "SBJK_DTDC__temperature"; private static final String BATTERY_FIELD_SOC = "SBJK_DTDC__soc"; private static final String BATTERY_FIELD_SOH = "SBJK_DTDC__soh"; + private static final String SINGLE_BATTERY_SCOPE_CLUSTER = "cluster"; + private static final String SINGLE_BATTERY_SCOPE_STACK = "stack"; + private static final String SINGLE_BATTERY_FIELD_VALUE_ZERO = "0"; private static final Set SINGLE_BATTERY_IMPORT_FIELD_CODES = new HashSet<>(Arrays.asList( BATTERY_FIELD_VOLTAGE, BATTERY_FIELD_TEMPERATURE, BATTERY_FIELD_SOC, BATTERY_FIELD_SOH )); @@ -1081,6 +1087,375 @@ public class EmsDeviceSettingServiceImpl implements IEmsDeviceSettingService } } + private static class SingleBatteryInitScope { + private final String normalizedSiteId; + private final String scopeType; + private final String scopeDeviceId; + private final List scopeClusters; + private final String appendClusterId; + private final List allSiteDevices; + + private SingleBatteryInitScope(String normalizedSiteId, + String scopeType, + String scopeDeviceId, + List scopeClusters, + String appendClusterId, + List allSiteDevices) { + this.normalizedSiteId = normalizedSiteId; + this.scopeType = scopeType; + this.scopeDeviceId = scopeDeviceId; + this.scopeClusters = scopeClusters; + this.appendClusterId = appendClusterId; + this.allSiteDevices = allSiteDevices; + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public SingleBatteryConfigInitResultVo initializeSingleBatteryMonitorMappings(SingleBatteryConfigInitRequest request, String operName) { + SingleBatteryInitScope scope = resolveSingleBatteryInitScope(request); + SingleBatteryConfigInitResultVo result = new SingleBatteryConfigInitResultVo(); + result.setCommitted(false); + result.setSiteId(scope.normalizedSiteId); + result.setScopeType(scope.scopeType); + result.setScopeDeviceId(scope.scopeDeviceId); + result.setTargetCount(request.getTargetCount()); + result.setExistingBatteryCount(0); + result.setInitializedBatteryCount(0); + result.setInsertedBatteryCount(0); + result.setPointHitCount(0); + result.setFixedValueFallbackCount(0); + result.setInsertedMappingCount(0); + result.setUpdatedMappingCount(0); + + Map deviceMap = scope.allSiteDevices.stream() + .filter(Objects::nonNull) + .filter(item -> StringUtils.isNotBlank(item.getDeviceId())) + .collect(Collectors.toMap( + item -> StringUtils.trim(item.getDeviceId()), + item -> item, + (left, right) -> right, + LinkedHashMap::new + )); + Set scopeClusterIdSet = scope.scopeClusters.stream() + .map(EmsDevicesSetting::getDeviceId) + .filter(StringUtils::isNotBlank) + .map(String::trim) + .collect(Collectors.toCollection(LinkedHashSet::new)); + List scopeBatteryList = getScopeBatteryList(scope.allSiteDevices, scopeClusterIdSet); + result.setExistingBatteryCount(scopeBatteryList.size()); + + int targetCount = request.getTargetCount(); + if (targetCount > scopeBatteryList.size()) { + int appendCount = targetCount - scopeBatteryList.size(); + List newBatteryList = createMissingSingleBatteries( + scope.normalizedSiteId, + scope.appendClusterId, + appendCount, + deviceMap, + operName + ); + scopeBatteryList.addAll(newBatteryList); + result.setInsertedBatteryCount(newBatteryList.size()); + } + + Map> batteryByCluster = scopeBatteryList.stream() + .filter(Objects::nonNull) + .filter(item -> StringUtils.isNotBlank(item.getParentId())) + .collect(Collectors.groupingBy( + item -> StringUtils.trim(item.getParentId()), + LinkedHashMap::new, + Collectors.toList() + )); + Set pointIds = new HashSet<>(); + List taskList = new ArrayList<>(); + for (EmsDevicesSetting cluster : scope.scopeClusters) { + if (cluster == null || StringUtils.isBlank(cluster.getDeviceId())) { + continue; + } + String clusterId = StringUtils.trim(cluster.getDeviceId()); + List clusterBatteryList = batteryByCluster.getOrDefault(clusterId, Collections.emptyList()) + .stream() + .sorted(Comparator.comparing(item -> StringUtils.defaultString(item.getDeviceId()))) + .collect(Collectors.toList()); + for (int i = 0; i < clusterBatteryList.size(); i++) { + EmsDevicesSetting battery = clusterBatteryList.get(i); + String batteryId = StringUtils.trim(battery.getDeviceId()); + String serialNo = formatSingleBatterySerial(i + 1); + taskList.add(buildSingleBatteryFieldInitTask(scope.normalizedSiteId, clusterId, batteryId, BATTERY_FIELD_VOLTAGE, "DY", serialNo)); + taskList.add(buildSingleBatteryFieldInitTask(scope.normalizedSiteId, clusterId, batteryId, BATTERY_FIELD_TEMPERATURE, "WD", serialNo)); + taskList.add(buildSingleBatteryFieldInitTask(scope.normalizedSiteId, clusterId, batteryId, BATTERY_FIELD_SOC, "SOC", serialNo)); + taskList.add(buildSingleBatteryFieldInitTask(scope.normalizedSiteId, clusterId, batteryId, BATTERY_FIELD_SOH, "SOH", serialNo)); + pointIds.add(clusterId + "DTDY" + serialNo); + pointIds.add(clusterId + "DTWD" + serialNo); + pointIds.add(clusterId + "DTSOC" + serialNo); + pointIds.add(clusterId + "DTSOH" + serialNo); + } + } + + Map pointConfigMap = pointIds.isEmpty() + ? Collections.emptyMap() + : emsPointConfigMapper.selectBySiteIdAndPointIds(scope.normalizedSiteId, new ArrayList<>(pointIds)) + .stream() + .filter(Objects::nonNull) + .filter(item -> StringUtils.isNotBlank(item.getPointId())) + .collect(Collectors.toMap( + item -> StringUtils.trim(item.getPointId()), + item -> item, + (left, right) -> right, + HashMap::new + )); + + Map existingPointMatchMap = emsSiteMonitorPointMatchMapper.selectBySiteId(scope.normalizedSiteId) + .stream() + .filter(Objects::nonNull) + .filter(item -> SINGLE_BATTERY_IMPORT_FIELD_CODES.contains(StringUtils.trim(item.getFieldCode()))) + .filter(item -> StringUtils.isNotBlank(item.getDeviceId())) + .collect(Collectors.toMap( + item -> buildMatchKey(StringUtils.trim(item.getFieldCode()), StringUtils.trim(item.getDeviceId())), + item -> item, + (left, right) -> right, + LinkedHashMap::new + )); + + List insertList = new ArrayList<>(); + Set affectedBatteryIds = scopeBatteryList.stream() + .map(EmsDevicesSetting::getDeviceId) + .filter(StringUtils::isNotBlank) + .map(String::trim) + .collect(Collectors.toSet()); + for (SingleBatteryFieldInitTask task : taskList) { + boolean pointExists = pointConfigMap.containsKey(task.pointId); + if (pointExists) { + result.setPointHitCount(result.getPointHitCount() + 1); + } else { + result.setFixedValueFallbackCount(result.getFixedValueFallbackCount() + 1); + } + upsertSingleBatteryPointMatchForInit(insertList, existingPointMatchMap, result, task, pointExists, operName); + } + if (!insertList.isEmpty()) { + emsSiteMonitorPointMatchMapper.insertBatch(insertList); + } + + refreshSingleBatteryImportCache(scope.normalizedSiteId, affectedBatteryIds); + result.setInitializedBatteryCount(scopeBatteryList.size()); + result.setCommitted(true); + result.setMessage(String.format( + "初始化完成:已初始化单体 %d 个,新增单体 %d 个,命中点位 %d 条,固定值回填 %d 条", + result.getInitializedBatteryCount(), + result.getInsertedBatteryCount(), + result.getPointHitCount(), + result.getFixedValueFallbackCount() + )); + return result; + } + + private SingleBatteryInitScope resolveSingleBatteryInitScope(SingleBatteryConfigInitRequest request) { + if (request == null) { + throw new ServiceException("鍒濆鍖栬姹備笉鑳戒负绌?"); + } + String normalizedSiteId = StringUtils.trim(request.getSiteId()); + String normalizedScopeType = StringUtils.trimToEmpty(request.getScopeType()).toLowerCase(); + String normalizedScopeDeviceId = StringUtils.trim(request.getScopeDeviceId()); + Integer targetCount = request.getTargetCount(); + if (StringUtils.isBlank(normalizedSiteId)) { + throw new ServiceException("绔欑偣ID涓嶈兘涓虹┖"); + } + if (StringUtils.isBlank(normalizedScopeType) || StringUtils.isBlank(normalizedScopeDeviceId)) { + throw new ServiceException("鍒濆鍖栬寖鍥村拰璁惧ID涓嶈兘涓虹┖"); + } + if (!SINGLE_BATTERY_SCOPE_CLUSTER.equals(normalizedScopeType) && !SINGLE_BATTERY_SCOPE_STACK.equals(normalizedScopeType)) { + throw new ServiceException("鍒濆鍖栬寖鍥诲彧鏀寔 cluster 鎴?stack"); + } + if (targetCount == null || targetCount < 1) { + throw new ServiceException("鍗曚綋鏁伴噺蹇呴』澶т簬 0"); + } + + List allSiteDevices = emsDevicesMapper.selectEmsDevicesSettingList(buildSiteDeviceQuery(normalizedSiteId)); + Map deviceMap = allSiteDevices.stream() + .filter(Objects::nonNull) + .filter(item -> StringUtils.isNotBlank(item.getDeviceId())) + .collect(Collectors.toMap( + item -> StringUtils.trim(item.getDeviceId()), + item -> item, + (left, right) -> right, + LinkedHashMap::new + )); + if (SINGLE_BATTERY_SCOPE_CLUSTER.equals(normalizedScopeType)) { + EmsDevicesSetting cluster = deviceMap.get(normalizedScopeDeviceId); + if (cluster == null || !StringUtils.equals(DeviceCategory.CLUSTER.getCode(), StringUtils.trim(cluster.getDeviceCategory()))) { + throw new ServiceException(String.format("电池簇编号[%s]不存在", normalizedScopeDeviceId)); + } + return new SingleBatteryInitScope( + normalizedSiteId, + normalizedScopeType, + normalizedScopeDeviceId, + Collections.singletonList(cluster), + normalizedScopeDeviceId, + allSiteDevices + ); + } + + EmsDevicesSetting stack = deviceMap.get(normalizedScopeDeviceId); + if (stack == null || !StringUtils.equals(DeviceCategory.STACK.getCode(), StringUtils.trim(stack.getDeviceCategory()))) { + throw new ServiceException(String.format("电池堆编号[%s]不存在", normalizedScopeDeviceId)); + } + List scopeClusters = allSiteDevices.stream() + .filter(Objects::nonNull) + .filter(item -> StringUtils.equals(DeviceCategory.CLUSTER.getCode(), StringUtils.trim(item.getDeviceCategory()))) + .filter(item -> StringUtils.equals(normalizedScopeDeviceId, StringUtils.trim(item.getParentId()))) + .sorted(Comparator.comparing(item -> StringUtils.defaultString(item.getDeviceId()))) + .collect(Collectors.toList()); + if (CollectionUtils.isEmpty(scopeClusters)) { + throw new ServiceException(String.format("电池堆[%s]下未配置电池簇,无法初始化单体电池", normalizedScopeDeviceId)); + } + return new SingleBatteryInitScope( + normalizedSiteId, + normalizedScopeType, + normalizedScopeDeviceId, + scopeClusters, + StringUtils.trim(scopeClusters.get(0).getDeviceId()), + allSiteDevices + ); + } + + private List getScopeBatteryList(List allSiteDevices, Set scopeClusterIdSet) { + if (CollectionUtils.isEmpty(allSiteDevices) || CollectionUtils.isEmpty(scopeClusterIdSet)) { + return new ArrayList<>(); + } + return allSiteDevices.stream() + .filter(Objects::nonNull) + .filter(item -> StringUtils.equals(DeviceCategory.BATTERY.getCode(), StringUtils.trim(item.getDeviceCategory()))) + .filter(item -> scopeClusterIdSet.contains(StringUtils.trim(item.getParentId()))) + .collect(Collectors.toCollection(ArrayList::new)); + } + + private List createMissingSingleBatteries(String siteId, + String clusterDeviceId, + int appendCount, + Map deviceMap, + String operName) { + List result = new ArrayList<>(); + if (appendCount < 1) { + return result; + } + Set existingIds = deviceMap.keySet().stream() + .filter(StringUtils::isNotBlank) + .map(String::trim) + .collect(Collectors.toSet()); + Set clusterBatteryIds = deviceMap.values().stream() + .filter(Objects::nonNull) + .filter(item -> StringUtils.equals(DeviceCategory.BATTERY.getCode(), StringUtils.trim(item.getDeviceCategory()))) + .filter(item -> StringUtils.equals(clusterDeviceId, StringUtils.trim(item.getParentId()))) + .map(EmsDevicesSetting::getDeviceId) + .filter(StringUtils::isNotBlank) + .map(String::trim) + .collect(Collectors.toCollection(HashSet::new)); + Date now = DateUtils.getNowDate(); + int serial = 1; + while (result.size() < appendCount) { + String candidateId = formatSingleBatterySerial(serial); + serial++; + if (existingIds.contains(candidateId) || clusterBatteryIds.contains(candidateId)) { + continue; + } + EmsDevicesSetting insertDevice = new EmsDevicesSetting(); + insertDevice.setSiteId(siteId); + insertDevice.setDeviceId(candidateId); + insertDevice.setDeviceName(candidateId); + insertDevice.setDeviceType(DeviceType.TCP.name()); + insertDevice.setCommunicationStatus(CommunicationStatus.OK.getCode()); + insertDevice.setDeviceCategory(DeviceCategory.BATTERY.getCode()); + insertDevice.setDeviceStatus(DeviceRunningStatus.OFFLINE.getCode()); + insertDevice.setWorkStatus(WorkStatus.NORMAL.getCode()); + insertDevice.setParentId(clusterDeviceId); + insertDevice.setDescription("single battery initialize"); + insertDevice.setCreateBy(operName); + insertDevice.setUpdateBy(operName); + insertDevice.setCreatedAt(now); + insertDevice.setUpdatedAt(now); + emsDevicesMapper.insertEmsDevicesSetting(insertDevice); + existingIds.add(candidateId); + clusterBatteryIds.add(candidateId); + deviceMap.put(candidateId, insertDevice); + result.add(insertDevice); + } + return result; + } + + private String formatSingleBatterySerial(int serialNo) { + return String.format("%03d", serialNo); + } + + private SingleBatteryFieldInitTask buildSingleBatteryFieldInitTask(String siteId, + String clusterDeviceId, + String batteryDeviceId, + String fieldCode, + String bizCode, + String serialNo) { + String pointId = clusterDeviceId + "DT" + bizCode + serialNo; + return new SingleBatteryFieldInitTask(siteId, clusterDeviceId, batteryDeviceId, fieldCode, pointId); + } + + private void upsertSingleBatteryPointMatchForInit(List insertList, + Map existingPointMatchMap, + SingleBatteryConfigInitResultVo result, + SingleBatteryFieldInitTask task, + boolean pointExists, + String operName) { + String key = buildMatchKey(task.fieldCode, task.batteryDeviceId); + EmsSiteMonitorPointMatch existing = existingPointMatchMap.get(key); + String dataPoint = pointExists ? task.pointId : ""; + String fixedDataPoint = pointExists ? null : SINGLE_BATTERY_FIELD_VALUE_ZERO; + int useFixedDisplay = pointExists ? 0 : USE_FIXED_DISPLAY_YES; + if (existing == null) { + EmsSiteMonitorPointMatch insertItem = new EmsSiteMonitorPointMatch(); + insertItem.setSiteId(task.siteId); + insertItem.setFieldCode(task.fieldCode); + insertItem.setDeviceId(task.batteryDeviceId); + insertItem.setDataPoint(dataPoint); + insertItem.setFixedDataPoint(fixedDataPoint); + insertItem.setUseFixedDisplay(useFixedDisplay); + insertItem.setCreateBy(operName); + insertItem.setUpdateBy(operName); + insertList.add(insertItem); + result.setInsertedMappingCount(result.getInsertedMappingCount() + 1); + return; + } + boolean changed = !StringUtils.equals(StringUtils.trimToEmpty(existing.getDataPoint()), dataPoint) + || !StringUtils.equals(StringUtils.trimToEmpty(existing.getFixedDataPoint()), StringUtils.trimToEmpty(fixedDataPoint)) + || (existing.getUseFixedDisplay() == null ? 0 : existing.getUseFixedDisplay()) != useFixedDisplay; + if (!changed) { + return; + } + existing.setDataPoint(dataPoint); + existing.setFixedDataPoint(fixedDataPoint); + existing.setUseFixedDisplay(useFixedDisplay); + existing.setUpdateBy(operName); + emsSiteMonitorPointMatchMapper.updateEmsSiteMonitorPointMatch(existing); + result.setUpdatedMappingCount(result.getUpdatedMappingCount() + 1); + } + + private static class SingleBatteryFieldInitTask { + private final String siteId; + private final String clusterDeviceId; + private final String batteryDeviceId; + private final String fieldCode; + private final String pointId; + + private SingleBatteryFieldInitTask(String siteId, + String clusterDeviceId, + String batteryDeviceId, + String fieldCode, + String pointId) { + this.siteId = siteId; + this.clusterDeviceId = clusterDeviceId; + this.batteryDeviceId = batteryDeviceId; + this.fieldCode = fieldCode; + this.pointId = pointId; + } + } + @Override @Transactional(rollbackFor = Exception.class) public SingleBatteryMonitorImportResultVo importSingleBatteryMonitorMappings(String siteId, List rows, String operName) { diff --git a/ems-system/src/main/java/com/xzzn/ems/service/impl/SingleSiteServiceImpl.java b/ems-system/src/main/java/com/xzzn/ems/service/impl/SingleSiteServiceImpl.java index a7e9b63..321a61f 100644 --- a/ems-system/src/main/java/com/xzzn/ems/service/impl/SingleSiteServiceImpl.java +++ b/ems-system/src/main/java/com/xzzn/ems/service/impl/SingleSiteServiceImpl.java @@ -97,6 +97,7 @@ public class SingleSiteServiceImpl implements ISingleSiteService { "deviceId", "clusterDeviceId", "dataTimestamp" )); private static final String MENU_SBJK_DTDC = "SBJK_DTDC"; + private static final String MENU_SBJK_BMSDCC = "SBJK_BMSDCC"; private static final String CLUSTER_DATA_TEP = "温度"; @@ -1092,11 +1093,17 @@ public class SingleSiteServiceImpl implements ISingleSiteService { log.warn("getClusterDataInfoList early return, siteId is empty"); return batteryDataStatsListVo; } + List projectDisplayList = iEmsDeviceSettingService.getSiteMonitorProjectDisplay(siteId); Map> batteryDisplayMap = - buildBatteryDisplayMap(iEmsDeviceSettingService.getSiteMonitorProjectDisplay(siteId)); + buildBatteryDisplayMap(projectDisplayList); + Map> clusterDisplayMap = + buildClusterDisplayMap(projectDisplayList); if (batteryDisplayMap.isEmpty()) { log.warn("getClusterDataInfoList no battery display data from projectDisplay, siteId={}", siteId); } + if (clusterDisplayMap.isEmpty()) { + log.warn("getClusterDataInfoList no cluster display data from projectDisplay, siteId={}", siteId); + } log.info("getClusterDataInfoList battery list source projectDisplay, siteId={}, batteryCount={}", siteId, batteryDisplayMap.size()); @@ -1136,6 +1143,9 @@ public class SingleSiteServiceImpl implements ISingleSiteService { fillBatteryValueFromProjectDisplay(batteryDataStatsVo, batteryDisplayMap.get(batteryDeviceId)); batteryDataStatsVo.setDeviceId(batteryDeviceId); batteryDataStatsVo.setClusterDeviceId(currentClusterDeviceId); + // 单体电池未配置 SOC/SOH 点位时,回退使用所属电池簇的 SOC/SOH 点位和值。 + applyClusterSocSohFallback(batteryDataStatsVo, + clusterDisplayMap.get(currentClusterDeviceId), 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(), @@ -1149,7 +1159,7 @@ public class SingleSiteServiceImpl implements ISingleSiteService { log.info("getClusterDataInfoList fallback to realtime cache, siteId={}, stackDeviceId={}, clusterDeviceId={}, batteryId={}", siteId, targetStackDeviceId, targetClusterDeviceId, targetBatteryId); appendBatteryFromRealtimeCache(siteId, targetStackDeviceId, targetClusterDeviceId, targetBatteryId, - batteryDisplayMap, batteryDataStatsListVo); + batteryDisplayMap, clusterDisplayMap, batteryDataStatsListVo); } log.info("getClusterDataInfoList end, siteId={}, stackDeviceId={}, clusterDeviceId={}, batteryId={}, total={}", @@ -1162,6 +1172,7 @@ public class SingleSiteServiceImpl implements ISingleSiteService { String targetClusterDeviceId, String targetBatteryId, Map> batteryDisplayMap, + Map> clusterDisplayMap, List targetList) { if (StringUtils.isBlank(siteId) || targetList == null) { return; @@ -1204,6 +1215,9 @@ public class SingleSiteServiceImpl implements ISingleSiteService { batteryDataStatsVo.setClusterDeviceId(clusterId); } fillBatteryPointIdFromProjectDisplay(batteryDataStatsVo, batteryDisplayMap == null ? null : batteryDisplayMap.get(currentBatteryId)); + // Redis 实时缓存中的单体数据如果没有单独配置 SOC/SOH 点位,同样回退到电池簇配置。 + applyClusterSocSohFallback(batteryDataStatsVo, + clusterDisplayMap == null ? null : clusterDisplayMap.get(clusterId), clusterId); targetList.add(batteryDataStatsVo); } } @@ -1283,6 +1297,31 @@ public class SingleSiteServiceImpl implements ISingleSiteService { return result; } + private Map> buildClusterDisplayMap(List displayList) { + Map> result = new HashMap<>(); + if (CollectionUtils.isEmpty(displayList)) { + return result; + } + 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_BMSDCC.equals(menuCode) || StringUtils.isEmpty(deviceId) || StringUtils.isEmpty(fieldCode)) { + continue; + } + Map 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)) { @@ -1348,6 +1387,48 @@ public class SingleSiteServiceImpl implements ISingleSiteService { target.setSohPointId(sohVo == null ? null : StringUtils.trimToNull(sohVo.getDataPoint())); } + private void applyClusterSocSohFallback(BatteryDataStatsListVo target, + Map clusterFieldMap, + String clusterDeviceId) { + if (target == null || clusterFieldMap == null || clusterFieldMap.isEmpty()) { + return; + } + boolean useClusterSoc = StringUtils.isBlank(target.getSocPointId()); + boolean useClusterSoh = StringUtils.isBlank(target.getSohPointId()); + if (!useClusterSoc && !useClusterSoh) { + return; + } + + SiteMonitorProjectDisplayVo clusterSocVo = clusterFieldMap.get("currentsoc"); + SiteMonitorProjectDisplayVo clusterSohVo = clusterFieldMap.get("soh"); + Date latest = target.getDataTimestamp(); + + // 单体没有配置 SOC 点位时,使用所属电池簇的 currentSoc 作为兜底。 + if (useClusterSoc && clusterSocVo != null) { + target.setSocPointId(StringUtils.trimToNull(clusterSocVo.getDataPoint())); + if (StringUtils.isNotBlank(clusterSocVo.getFieldValue())) { + target.setSoc(StringUtils.getBigDecimal(clusterSocVo.getFieldValue())); + } + latest = pickLaterTime(latest, clusterSocVo.getValueTime()); + } + + // 单体没有配置 SOH 点位时,使用所属电池簇的 soh 作为兜底。 + if (useClusterSoh && clusterSohVo != null) { + target.setSohPointId(StringUtils.trimToNull(clusterSohVo.getDataPoint())); + if (StringUtils.isNotBlank(clusterSohVo.getFieldValue())) { + target.setSoh(StringUtils.getBigDecimal(clusterSohVo.getFieldValue())); + } + latest = pickLaterTime(latest, clusterSohVo.getValueTime()); + } + + if (latest != null) { + target.setDataTimestamp(latest); + } + log.info("applyClusterSocSohFallback, clusterDeviceId={}, batteryDeviceId={}, useClusterSoc={}, useClusterSoh={}, socPointId={}, sohPointId={}, soc={}, soh={}", + clusterDeviceId, target.getDeviceId(), useClusterSoc, useClusterSoh, + target.getSocPointId(), target.getSohPointId(), target.getSoc(), target.getSoh()); + } + private Date pickLaterTime(Date left, Date right) { if (left == null) { return right;