VppUnIntervalDataHelper.java 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. package com.usky.vpp.util;
  2. import com.usky.vpp.service.VppTsdbQueryService;
  3. import java.math.BigDecimal;
  4. import java.math.RoundingMode;
  5. import java.time.LocalDate;
  6. import java.time.LocalDateTime;
  7. import java.time.LocalTime;
  8. import java.time.format.DateTimeFormatter;
  9. import java.util.ArrayList;
  10. import java.util.Collections;
  11. import java.util.LinkedHashSet;
  12. import java.util.List;
  13. import java.util.Map;
  14. import java.util.TreeMap;
  15. /**
  16. * IntervalDataReport 明日预测曲线(15 分钟整点,按量测点类型均值/累加)。
  17. */
  18. public final class VppUnIntervalDataHelper {
  19. public static final int SLOT_MINUTES = VppUnMomentDataHelper.WINDOW_MINUTES;
  20. private static final DateTimeFormatter TIME_KEY_FMT = DateTimeFormatter.ofPattern("HH:mm");
  21. private VppUnIntervalDataHelper() {
  22. }
  23. public static LocalDate resolveForecastDate() {
  24. return LocalDate.now().plusDays(1);
  25. }
  26. public static List<LocalDateTime> buildDayQuarterHourSlots(LocalDate forecastDate) {
  27. List<LocalDateTime> slots = new ArrayList<>();
  28. if (forecastDate == null) {
  29. return slots;
  30. }
  31. LocalDateTime cursor = forecastDate.atStartOfDay();
  32. LocalDateTime dayEnd = forecastDate.plusDays(1).atStartOfDay();
  33. while (cursor.isBefore(dayEnd)) {
  34. slots.add(cursor);
  35. cursor = cursor.plusMinutes(SLOT_MINUTES);
  36. }
  37. return slots;
  38. }
  39. /**
  40. * 基于典型历史日预测 forecastDate 全天 96 个 15 分钟点。
  41. */
  42. public static List<Map<String, Object>> buildForecastCurveValues(VppTsdbQueryService tsdbQueryService,
  43. List<String> deviceUuids,
  44. List<LocalDate> referenceDates,
  45. LocalDate forecastDate,
  46. VppUnRegisterMetricDefinitions.RegisterMetric registerMetric) {
  47. List<LocalDateTime> slots = buildDayQuarterHourSlots(forecastDate);
  48. if (slots.isEmpty()) {
  49. return Collections.emptyList();
  50. }
  51. if (tsdbQueryService == null || deviceUuids == null || deviceUuids.isEmpty()
  52. || referenceDates == null || referenceDates.isEmpty() || registerMetric == null) {
  53. return buildZeroCurve(slots);
  54. }
  55. List<String> tsdbMetrics = VppUnMomentDataHelper.resolveTsdbMetrics(registerMetric);
  56. if (tsdbMetrics.isEmpty()) {
  57. return buildZeroCurve(slots);
  58. }
  59. List<String> distinctUuids = new ArrayList<>(new LinkedHashSet<>(deviceUuids));
  60. if (VppUnMomentDataHelper.resolveAggregationMode(registerMetric)
  61. == VppUnMomentDataHelper.AggregationMode.AVERAGE) {
  62. return buildAverageForecastCurve(tsdbQueryService, distinctUuids, referenceDates, slots, tsdbMetrics);
  63. }
  64. return buildAccumulativeForecastCurve(tsdbQueryService, distinctUuids, referenceDates, slots, tsdbMetrics);
  65. }
  66. private static List<Map<String, Object>> buildAverageForecastCurve(
  67. VppTsdbQueryService tsdbQueryService,
  68. List<String> deviceUuids,
  69. List<LocalDate> referenceDates,
  70. List<LocalDateTime> slots,
  71. List<String> tsdbMetrics) {
  72. Map<String, BigDecimal> avgByTime = tsdbQueryService.queryBaselineAvgByTimePoint(
  73. deviceUuids,
  74. referenceDates,
  75. LocalTime.MIN,
  76. LocalTime.of(23, 59, 59),
  77. tsdbMetrics,
  78. SLOT_MINUTES,
  79. null);
  80. List<Map<String, Object>> values = new ArrayList<>(slots.size());
  81. for (LocalDateTime slot : slots) {
  82. String timeKey = slot.format(TIME_KEY_FMT);
  83. BigDecimal value = avgByTime != null ? avgByTime.get(timeKey) : null;
  84. values.add(VppUnMessageBuilder.buildIntervalCurveValue(scale(value), slot, "good"));
  85. }
  86. return values;
  87. }
  88. private static List<Map<String, Object>> buildAccumulativeForecastCurve(
  89. VppTsdbQueryService tsdbQueryService,
  90. List<String> deviceUuids,
  91. List<LocalDate> referenceDates,
  92. List<LocalDateTime> slots,
  93. List<String> tsdbMetrics) {
  94. Map<String, List<BigDecimal>> samplesByTimeKey = new TreeMap<>();
  95. for (LocalDate referenceDate : referenceDates) {
  96. LocalDateTime dayStart = referenceDate.atStartOfDay();
  97. LocalDateTime dayEnd = referenceDate.plusDays(1).atStartOfDay();
  98. Map<String, Map<String, TreeMap<LocalDateTime, BigDecimal>>> history =
  99. tsdbQueryService.queryDeviceMetricHistory(deviceUuids, dayStart, dayEnd, tsdbMetrics);
  100. if (history == null || history.isEmpty()) {
  101. continue;
  102. }
  103. for (LocalDateTime slot : slots) {
  104. LocalDateTime slotStart = LocalDateTime.of(referenceDate, slot.toLocalTime());
  105. LocalDateTime slotEnd = slotStart.plusMinutes(SLOT_MINUTES);
  106. LocalDateTime endExclusive = slotEnd.plusSeconds(1);
  107. BigDecimal slotTotal = BigDecimal.ZERO;
  108. for (String deviceUuid : deviceUuids) {
  109. Map<String, TreeMap<LocalDateTime, BigDecimal>> metricMap = history.get(deviceUuid);
  110. if (metricMap == null || metricMap.isEmpty()) {
  111. continue;
  112. }
  113. TreeMap<LocalDateTime, BigDecimal> series = pickSeries(metricMap, tsdbMetrics);
  114. BigDecimal usage = VppEnergyUsageHelper.calcUsage(series, slotStart, endExclusive);
  115. if (usage != null) {
  116. slotTotal = slotTotal.add(usage);
  117. }
  118. }
  119. samplesByTimeKey.computeIfAbsent(slot.format(TIME_KEY_FMT), key -> new ArrayList<>())
  120. .add(slotTotal);
  121. }
  122. }
  123. List<Map<String, Object>> values = new ArrayList<>(slots.size());
  124. for (LocalDateTime slot : slots) {
  125. List<BigDecimal> samples = samplesByTimeKey.get(slot.format(TIME_KEY_FMT));
  126. values.add(VppUnMessageBuilder.buildIntervalCurveValue(
  127. scale(average(samples)), slot, "good"));
  128. }
  129. return values;
  130. }
  131. private static List<Map<String, Object>> buildZeroCurve(List<LocalDateTime> slots) {
  132. List<Map<String, Object>> values = new ArrayList<>(slots.size());
  133. for (LocalDateTime slot : slots) {
  134. values.add(VppUnMessageBuilder.buildIntervalCurveValue(BigDecimal.ZERO, slot, "good"));
  135. }
  136. return values;
  137. }
  138. private static TreeMap<LocalDateTime, BigDecimal> pickSeries(Map<String, TreeMap<LocalDateTime, BigDecimal>> metricMap,
  139. List<String> preferredMetrics) {
  140. if (metricMap == null || metricMap.isEmpty()) {
  141. return null;
  142. }
  143. if (preferredMetrics != null) {
  144. for (String metric : preferredMetrics) {
  145. TreeMap<LocalDateTime, BigDecimal> series = metricMap.get(metric);
  146. if (series != null && !series.isEmpty()) {
  147. return series;
  148. }
  149. }
  150. }
  151. for (TreeMap<LocalDateTime, BigDecimal> series : metricMap.values()) {
  152. if (series != null && !series.isEmpty()) {
  153. return series;
  154. }
  155. }
  156. return null;
  157. }
  158. private static BigDecimal average(List<BigDecimal> values) {
  159. if (values == null || values.isEmpty()) {
  160. return BigDecimal.ZERO;
  161. }
  162. BigDecimal sum = BigDecimal.ZERO;
  163. int count = 0;
  164. for (BigDecimal value : values) {
  165. if (value != null) {
  166. sum = sum.add(value);
  167. count++;
  168. }
  169. }
  170. if (count == 0) {
  171. return BigDecimal.ZERO;
  172. }
  173. return sum.divide(BigDecimal.valueOf(count), 6, RoundingMode.HALF_UP);
  174. }
  175. private static BigDecimal scale(BigDecimal value) {
  176. if (value == null) {
  177. return BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
  178. }
  179. return value.setScale(2, RoundingMode.HALF_UP);
  180. }
  181. }