|
|
@@ -14,6 +14,7 @@ import com.usky.vpp.service.VppBaselineService;
|
|
|
import com.usky.vpp.service.VppTsdbQueryService;
|
|
|
import com.usky.vpp.service.vo.BaselinePointVO;
|
|
|
import com.usky.vpp.service.vo.SiteBaselineVO;
|
|
|
+import com.usky.vpp.service.vo.SiteDeclaredCapacityVO;
|
|
|
import com.usky.vpp.util.VppAuditHelper;
|
|
|
import com.usky.vpp.util.VppBaselineHelper;
|
|
|
import com.usky.vpp.util.VppWorkdayHelper;
|
|
|
@@ -174,6 +175,100 @@ public class VppBaselineServiceImpl implements VppBaselineService {
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 查询站点响应申报容量。
|
|
|
+ *
|
|
|
+ * <p>公式:响应时段内同时间段历史负荷均值 × 修正系数 K × 执行时段小时数 / 2。</p>
|
|
|
+ * <p>典型历史日、修正系数 K 计算规则与基线管理一致;历史均值仅取响应开始至结束时段。</p>
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public SiteDeclaredCapacityVO getSiteDeclaredCapacity(Long siteId,
|
|
|
+ String responseStartTime,
|
|
|
+ String responseEndTime) {
|
|
|
+ VppSite site = resolveSite(siteId);
|
|
|
+ LocalDateTime startTime = parseResponseStartTime(responseStartTime);
|
|
|
+ LocalDateTime endTime = parseResponseEndTime(responseEndTime, startTime);
|
|
|
+ LocalDate responseDate = startTime.toLocalDate();
|
|
|
+
|
|
|
+ SiteDeclaredCapacityVO result = new SiteDeclaredCapacityVO();
|
|
|
+ result.setSiteId(siteId);
|
|
|
+ result.setSiteName(site.getSiteName());
|
|
|
+ result.setResponseStartTime(startTime);
|
|
|
+ result.setResponseEndTime(endTime);
|
|
|
+
|
|
|
+ List<String> deviceUuids = listControllableDeviceUuids(siteId);
|
|
|
+ if (deviceUuids.isEmpty()) {
|
|
|
+ return buildMockDeclaredCapacityResult(result, siteId, responseDate, startTime, endTime);
|
|
|
+ }
|
|
|
+
|
|
|
+ Set<LocalDate> excludeDates = loadResponseHistoryDates();
|
|
|
+ boolean workday = VppBaselineHelper.isWorkdayResponse(responseDate);
|
|
|
+ result.setResponseDayType(VppWorkdayHelper.resolveDayType(responseDate));
|
|
|
+ result.setResponseDayTypeLabel(VppWorkdayHelper.resolveDayTypeLabel(responseDate));
|
|
|
+ int requiredCount = workday
|
|
|
+ ? VppBaselineHelper.WEEKDAY_REFERENCE_COUNT
|
|
|
+ : VppBaselineHelper.NON_WEEKDAY_REFERENCE_COUNT;
|
|
|
+ List<LocalDate> referenceDates = VppBaselineHelper.selectReferenceDates(
|
|
|
+ responseDate, workday, excludeDates, requiredCount);
|
|
|
+ result.setReferenceDates(referenceDates.stream()
|
|
|
+ .map(VppBaselineHelper::formatDate)
|
|
|
+ .collect(Collectors.toList()));
|
|
|
+
|
|
|
+ LocalDateTime correctionWindowStart = startTime.minusHours(VppBaselineHelper.CORRECTION_HOURS);
|
|
|
+ Map<String, BigDecimal> correctionBaselineByTime = vppTsdbQueryService.queryBaselineAvgByTimePoint(
|
|
|
+ deviceUuids,
|
|
|
+ referenceDates,
|
|
|
+ correctionWindowStart.toLocalTime(),
|
|
|
+ startTime.toLocalTime(),
|
|
|
+ VppBaselineHelper.POWER_METRICS,
|
|
|
+ VppBaselineHelper.INTERVAL_MINUTES,
|
|
|
+ null);
|
|
|
+ Map<String, BigDecimal> todayActualByTime = VppBaselineHelper.buildActualLoadByTimePoint(
|
|
|
+ vppTsdbQueryService.queryDeviceMetricHistory(
|
|
|
+ deviceUuids, responseDate.atStartOfDay(), startTime, VppBaselineHelper.POWER_METRICS),
|
|
|
+ VppBaselineHelper.INTERVAL_MINUTES);
|
|
|
+ BigDecimal correctionFactorK = VppBaselineHelper.calculateCorrectionFactor(
|
|
|
+ startTime, todayActualByTime, correctionBaselineByTime);
|
|
|
+
|
|
|
+ Map<String, BigDecimal> historicalAvgByTime = vppTsdbQueryService.queryBaselineAvgByTimePoint(
|
|
|
+ deviceUuids,
|
|
|
+ referenceDates,
|
|
|
+ startTime.toLocalTime(),
|
|
|
+ endTime.toLocalTime(),
|
|
|
+ VppBaselineHelper.POWER_METRICS,
|
|
|
+ VppBaselineHelper.INTERVAL_MINUTES,
|
|
|
+ null);
|
|
|
+ if (historicalAvgByTime.isEmpty()) {
|
|
|
+ return buildMockDeclaredCapacityResult(result, siteId, responseDate, startTime, endTime);
|
|
|
+ }
|
|
|
+
|
|
|
+ result.setCorrectionFactorK(correctionFactorK);
|
|
|
+ result.setDeclaredCapacityKw(VppBaselineHelper.calculateDeclaredCapacityKw(
|
|
|
+ historicalAvgByTime, startTime, endTime, correctionFactorK));
|
|
|
+ result.setPoints(VppBaselineHelper.buildDeclaredCapacityPoints(
|
|
|
+ historicalAvgByTime, startTime, endTime, correctionFactorK));
|
|
|
+ result.setDataSource("tsdb");
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ private SiteDeclaredCapacityVO buildMockDeclaredCapacityResult(SiteDeclaredCapacityVO result,
|
|
|
+ Long siteId,
|
|
|
+ LocalDate responseDate,
|
|
|
+ LocalDateTime startTime,
|
|
|
+ LocalDateTime endTime) {
|
|
|
+ BigDecimal correctionFactorK = VppBaselineHelper.mockCorrectionFactor(siteId, responseDate);
|
|
|
+ result.setReferenceDates(new ArrayList<>());
|
|
|
+ result.setResponseDayType(VppWorkdayHelper.resolveDayType(responseDate));
|
|
|
+ result.setResponseDayTypeLabel(VppWorkdayHelper.resolveDayTypeLabel(responseDate));
|
|
|
+ result.setCorrectionFactorK(correctionFactorK);
|
|
|
+ result.setDeclaredCapacityKw(VppBaselineHelper.mockDeclaredCapacityKw(
|
|
|
+ siteId, responseDate, startTime, endTime, correctionFactorK));
|
|
|
+ result.setPoints(VppBaselineHelper.buildMockDeclaredCapacityPoints(
|
|
|
+ siteId, responseDate, startTime, endTime, correctionFactorK));
|
|
|
+ result.setDataSource("mock");
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
private Map<String, BigDecimal> applyCorrection(Map<String, BigDecimal> originalBaselineByTime,
|
|
|
BigDecimal correctionFactorK) {
|
|
|
Map<String, BigDecimal> corrected = new TreeMap<>();
|
|
|
@@ -263,4 +358,23 @@ public class VppBaselineServiceImpl implements VppBaselineService {
|
|
|
throw new BusinessException("响应开始时间格式无效,请使用 yyyy-MM-dd HH:mm:ss");
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ private LocalDateTime parseResponseEndTime(String responseEndTime, LocalDateTime responseStartTime) {
|
|
|
+ if (!StringUtils.hasText(responseEndTime)) {
|
|
|
+ throw new BusinessException("响应结束时间不能为空");
|
|
|
+ }
|
|
|
+ LocalDateTime endTime;
|
|
|
+ try {
|
|
|
+ endTime = LocalDateTime.parse(responseEndTime.trim(), DATE_TIME_FMT);
|
|
|
+ } catch (DateTimeParseException ex) {
|
|
|
+ throw new BusinessException("响应结束时间格式无效,请使用 yyyy-MM-dd HH:mm:ss");
|
|
|
+ }
|
|
|
+ if (endTime.isBefore(responseStartTime)) {
|
|
|
+ throw new BusinessException("响应结束时间不能早于响应开始时间");
|
|
|
+ }
|
|
|
+ if (!endTime.toLocalDate().equals(responseStartTime.toLocalDate())) {
|
|
|
+ throw new BusinessException("响应结束时间需与响应开始时间在同一天");
|
|
|
+ }
|
|
|
+ return endTime;
|
|
|
+ }
|
|
|
}
|