|
@@ -26,6 +26,7 @@ import com.usky.vpp.service.vo.ResponseLoadPointVO;
|
|
|
import com.usky.vpp.service.vo.ResponseLoadStatRowVO;
|
|
import com.usky.vpp.service.vo.ResponseLoadStatRowVO;
|
|
|
import com.usky.vpp.service.vo.ResponseMonitorTreeNodeVO;
|
|
import com.usky.vpp.service.vo.ResponseMonitorTreeNodeVO;
|
|
|
import com.usky.vpp.util.VppAuditHelper;
|
|
import com.usky.vpp.util.VppAuditHelper;
|
|
|
|
|
+import com.usky.vpp.util.VppBaselineHelper;
|
|
|
import com.usky.vpp.util.VppCapabilityEvalHelper;
|
|
import com.usky.vpp.util.VppCapabilityEvalHelper;
|
|
|
import com.usky.vpp.util.VppResponseMonitorHelper;
|
|
import com.usky.vpp.util.VppResponseMonitorHelper;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
@@ -33,8 +34,10 @@ import org.springframework.stereotype.Service;
|
|
|
import org.springframework.util.StringUtils;
|
|
import org.springframework.util.StringUtils;
|
|
|
|
|
|
|
|
import java.math.BigDecimal;
|
|
import java.math.BigDecimal;
|
|
|
|
|
+import java.math.RoundingMode;
|
|
|
import java.time.LocalDate;
|
|
import java.time.LocalDate;
|
|
|
import java.time.LocalDateTime;
|
|
import java.time.LocalDateTime;
|
|
|
|
|
+import java.time.LocalTime;
|
|
|
import java.time.format.DateTimeFormatter;
|
|
import java.time.format.DateTimeFormatter;
|
|
|
import java.time.format.DateTimeParseException;
|
|
import java.time.format.DateTimeParseException;
|
|
|
import java.util.ArrayList;
|
|
import java.util.ArrayList;
|
|
@@ -42,6 +45,7 @@ import java.util.Arrays;
|
|
|
import java.util.Collections;
|
|
import java.util.Collections;
|
|
|
import java.util.Comparator;
|
|
import java.util.Comparator;
|
|
|
import java.util.HashMap;
|
|
import java.util.HashMap;
|
|
|
|
|
+import java.util.HashSet;
|
|
|
import java.util.LinkedHashMap;
|
|
import java.util.LinkedHashMap;
|
|
|
import java.util.List;
|
|
import java.util.List;
|
|
|
import java.util.Map;
|
|
import java.util.Map;
|
|
@@ -54,6 +58,7 @@ import java.util.stream.Collectors;
|
|
|
public class VppResponseMonitorServiceImpl implements VppResponseMonitorService {
|
|
public class VppResponseMonitorServiceImpl implements VppResponseMonitorService {
|
|
|
|
|
|
|
|
private static final DateTimeFormatter DATE_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
|
private static final DateTimeFormatter DATE_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
|
|
|
|
+ private static final int EVENT_STATUS_ENDED = 3;
|
|
|
|
|
|
|
|
@Autowired
|
|
@Autowired
|
|
|
private VppSiteMapper siteMapper;
|
|
private VppSiteMapper siteMapper;
|
|
@@ -206,26 +211,86 @@ public class VppResponseMonitorServiceImpl implements VppResponseMonitorService
|
|
|
BigDecimal declaredCapacityKw,
|
|
BigDecimal declaredCapacityKw,
|
|
|
DrContext drContext) {
|
|
DrContext drContext) {
|
|
|
List<String> deviceUuids = resolveDeviceUuids(resources, devices);
|
|
List<String> deviceUuids = resolveDeviceUuids(resources, devices);
|
|
|
- if (!deviceUuids.isEmpty()) {
|
|
|
|
|
- LocalDateTime dayStart = statDate.atStartOfDay();
|
|
|
|
|
- LocalDateTime dayEnd = statDate.atTime(23, 59, 59);
|
|
|
|
|
- LocalDate baselineDate = statDate.minusDays(VppResponseMonitorHelper.BASELINE_DAYS_AGO);
|
|
|
|
|
- List<String> powerMetrics = new ArrayList<>(VppTsdbConstants.ACTIVE_POWER_METRICS);
|
|
|
|
|
- Map<String, Map<String, TreeMap<LocalDateTime, BigDecimal>>> actualData =
|
|
|
|
|
- tsdbQueryService.queryDeviceMetricHistory(deviceUuids, dayStart, dayEnd, powerMetrics);
|
|
|
|
|
- if (VppCapabilityEvalHelper.hasTsdbPowerData(actualData, powerMetrics)) {
|
|
|
|
|
- Map<String, Map<String, TreeMap<LocalDateTime, BigDecimal>>> baselineData =
|
|
|
|
|
- tsdbQueryService.queryDeviceMetricHistory(deviceUuids,
|
|
|
|
|
- baselineDate.atStartOfDay(), baselineDate.atTime(23, 59, 59), powerMetrics);
|
|
|
|
|
- List<ResponseLoadPointVO> points = VppResponseMonitorHelper.buildCurveFromTsdb(
|
|
|
|
|
- actualData, baselineData, statDate, powerMetrics, declaredCapacityKw,
|
|
|
|
|
- drContext.executionByTime);
|
|
|
|
|
- return new CurveBuildResult(points, "tsdb");
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ if (deviceUuids.isEmpty()) {
|
|
|
|
|
+ return new CurveBuildResult(
|
|
|
|
|
+ VppResponseMonitorHelper.buildMockCurve(resources, devices, siteId, statDate, declaredCapacityKw),
|
|
|
|
|
+ "mock");
|
|
|
}
|
|
}
|
|
|
- return new CurveBuildResult(
|
|
|
|
|
- VppResponseMonitorHelper.buildMockCurve(resources, devices, siteId, statDate, declaredCapacityKw),
|
|
|
|
|
- "mock");
|
|
|
|
|
|
|
+
|
|
|
|
|
+ // 确定响应时段(有事件用事件时段,无事件用全天)
|
|
|
|
|
+ LocalDateTime responseStart;
|
|
|
|
|
+ LocalDateTime responseEnd;
|
|
|
|
|
+ if (drContext.event != null && drContext.event.getStartTime() != null) {
|
|
|
|
|
+ responseStart = drContext.event.getStartTime();
|
|
|
|
|
+ responseEnd = drContext.event.getEndTime() != null
|
|
|
|
|
+ ? drContext.event.getEndTime() : statDate.atTime(23, 59, 59);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ responseStart = statDate.atStartOfDay();
|
|
|
|
|
+ responseEnd = statDate.atTime(23, 59, 59);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 选取典型历史日(与基线管理一致:法定节假日 + 调休,工作日5天 / 非工作日3天)
|
|
|
|
|
+ Set<LocalDate> excludeDates = loadResponseHistoryDates();
|
|
|
|
|
+ boolean workday = VppBaselineHelper.isWorkdayResponse(statDate);
|
|
|
|
|
+ int requiredCount = workday
|
|
|
|
|
+ ? VppBaselineHelper.WEEKDAY_REFERENCE_COUNT
|
|
|
|
|
+ : VppBaselineHelper.NON_WEEKDAY_REFERENCE_COUNT;
|
|
|
|
|
+ List<LocalDate> referenceDates = VppBaselineHelper.selectReferenceDates(
|
|
|
|
|
+ statDate, workday, excludeDates, requiredCount);
|
|
|
|
|
+
|
|
|
|
|
+ int interval = VppResponseMonitorHelper.BUCKET_MINUTES;
|
|
|
|
|
+
|
|
|
|
|
+ // 查询原始基线(全天,多个典型历史日同时段均值)
|
|
|
|
|
+ Map<String, BigDecimal> originalBaseline = tsdbQueryService.queryBaselineAvgByTimePoint(
|
|
|
|
|
+ deviceUuids, referenceDates,
|
|
|
|
|
+ LocalTime.MIN, LocalTime.of(23, 59, 59),
|
|
|
|
|
+ VppBaselineHelper.POWER_METRICS, interval, null);
|
|
|
|
|
+ if (originalBaseline.isEmpty()) {
|
|
|
|
|
+ return new CurveBuildResult(
|
|
|
|
|
+ VppResponseMonitorHelper.buildMockCurve(resources, devices, siteId, statDate, declaredCapacityKw),
|
|
|
|
|
+ "mock");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 今日实际负荷(全天)
|
|
|
|
|
+ Map<String, BigDecimal> todayActual = VppBaselineHelper.buildActualLoadByTimePoint(
|
|
|
|
|
+ tsdbQueryService.queryDeviceMetricHistory(
|
|
|
|
|
+ deviceUuids, statDate.atStartOfDay(), statDate.atTime(23, 59, 59),
|
|
|
|
|
+ VppBaselineHelper.POWER_METRICS),
|
|
|
|
|
+ interval);
|
|
|
|
|
+
|
|
|
|
|
+ // 修正系数 K:响应开始前2小时今日实测 / 同时段原始基线,限制 [0.7, 1.2]
|
|
|
|
|
+ LocalDateTime correctionWindowStart = responseStart.minusHours(VppBaselineHelper.CORRECTION_HOURS);
|
|
|
|
|
+ Map<String, BigDecimal> correctionBaseline = tsdbQueryService.queryBaselineAvgByTimePoint(
|
|
|
|
|
+ deviceUuids, referenceDates,
|
|
|
|
|
+ correctionWindowStart.toLocalTime(), responseStart.toLocalTime(),
|
|
|
|
|
+ VppBaselineHelper.POWER_METRICS, interval, null);
|
|
|
|
|
+ BigDecimal correctionFactorK = VppBaselineHelper.calculateCorrectionFactor(
|
|
|
|
|
+ responseStart, todayActual,
|
|
|
|
|
+ correctionBaseline.isEmpty() ? originalBaseline : correctionBaseline);
|
|
|
|
|
+
|
|
|
|
|
+ // 预测基线 = 原始基线 × 修正系数 K
|
|
|
|
|
+ Map<String, BigDecimal> predictedBaseline = new TreeMap<>();
|
|
|
|
|
+ for (Map.Entry<String, BigDecimal> entry : originalBaseline.entrySet()) {
|
|
|
|
|
+ predictedBaseline.put(entry.getKey(),
|
|
|
|
|
+ entry.getValue().multiply(correctionFactorK).setScale(2, RoundingMode.HALF_UP));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // TSDB 申报容量(仅当有事件时计算)
|
|
|
|
|
+ BigDecimal tsdbDeclaredKw = BigDecimal.ZERO;
|
|
|
|
|
+ if (drContext.event != null) {
|
|
|
|
|
+ Map<String, BigDecimal> responseWindowBaseline = tsdbQueryService.queryBaselineAvgByTimePoint(
|
|
|
|
|
+ deviceUuids, referenceDates,
|
|
|
|
|
+ responseStart.toLocalTime(), responseEnd.toLocalTime(),
|
|
|
|
|
+ VppBaselineHelper.POWER_METRICS, interval, null);
|
|
|
|
|
+ tsdbDeclaredKw = VppBaselineHelper.calculateDeclaredCapacityKw(
|
|
|
|
|
+ responseWindowBaseline, responseStart, responseEnd, correctionFactorK);
|
|
|
|
|
+ }
|
|
|
|
|
+ BigDecimal finalDeclared = tsdbDeclaredKw.compareTo(BigDecimal.ZERO) > 0
|
|
|
|
|
+ ? tsdbDeclaredKw : declaredCapacityKw;
|
|
|
|
|
+
|
|
|
|
|
+ List<ResponseLoadPointVO> points = VppResponseMonitorHelper.buildCurveWithBaseline(
|
|
|
|
|
+ predictedBaseline, todayActual, statDate, finalDeclared, drContext.executionByTime, interval);
|
|
|
|
|
+ return new CurveBuildResult(points, "tsdb");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private DrContext resolveDrContext(Long siteId, LocalDate statDate, Long eventId) {
|
|
private DrContext resolveDrContext(Long siteId, LocalDate statDate, Long eventId) {
|
|
@@ -299,6 +364,23 @@ public class VppResponseMonitorServiceImpl implements VppResponseMonitorService
|
|
|
return time.withMinute(minute).withSecond(0).withNano(0);
|
|
return time.withMinute(minute).withSecond(0).withNano(0);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 查询历史已结束的 DR 事件日期,用于基线测算时排除。
|
|
|
|
|
+ */
|
|
|
|
|
+ private Set<LocalDate> loadResponseHistoryDates() {
|
|
|
|
|
+ List<VppDrEvent> events = eventMapper.selectList(
|
|
|
|
|
+ new LambdaQueryWrapper<VppDrEvent>()
|
|
|
|
|
+ .eq(VppDrEvent::getEventStatus, EVENT_STATUS_ENDED)
|
|
|
|
|
+ .eq(VppDrEvent::getDeleteFlag, VppAuditHelper.NOT_DELETED));
|
|
|
|
|
+ Set<LocalDate> dates = new HashSet<>();
|
|
|
|
|
+ for (VppDrEvent event : events) {
|
|
|
|
|
+ if (event.getStartTime() != null) {
|
|
|
|
|
+ dates.add(event.getStartTime().toLocalDate());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return dates;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
private ResponseMonitorTreeNodeVO toSiteNode(VppSite site, Long parentId) {
|
|
private ResponseMonitorTreeNodeVO toSiteNode(VppSite site, Long parentId) {
|
|
|
ResponseMonitorTreeNodeVO node = new ResponseMonitorTreeNodeVO();
|
|
ResponseMonitorTreeNodeVO node = new ResponseMonitorTreeNodeVO();
|
|
|
node.setNodeType("SITE");
|
|
node.setNodeType("SITE");
|