|
|
|
@ -1,6 +1,6 @@ |
|
|
|
package com.dongjian.dashboard.back.service.common; |
|
|
|
|
|
|
|
import com.dongjian.dashboard.back.vo.device.AccumulateLineData; |
|
|
|
import com.dongjian.dashboard.back.vo.device.SubLineData; |
|
|
|
import com.dongjian.dashboard.back.vo.device.LineData; |
|
|
|
|
|
|
|
import java.time.LocalDate; |
|
|
|
@ -149,84 +149,104 @@ public class LineDataAggregator { |
|
|
|
return LocalDateTime.parse(String.valueOf(x), TimeAxisUtil.DATETIME_FMT); |
|
|
|
} |
|
|
|
|
|
|
|
/* =============================== |
|
|
|
* 按小时聚合(平均值) |
|
|
|
* =============================== */ |
|
|
|
public static void aggregateByHour(LineData lineData) { |
|
|
|
aggregateByHourOrHalfHour(lineData, new AvgCollector(), true); |
|
|
|
} |
|
|
|
public static void convertToMeasureLineDataAvg(SubLineData accumulate, LineData lineData) { |
|
|
|
if (accumulate == null || accumulate.getSubData() == null) return; |
|
|
|
|
|
|
|
/* =============================== |
|
|
|
* 按半小时聚合(平均值) |
|
|
|
* =============================== */ |
|
|
|
public static void aggregateAverageByHalfHour(LineData lineData) { |
|
|
|
aggregateByHourOrHalfHour(lineData, new AvgCollector(), false); |
|
|
|
// 1. 每个 series 做「半小时平均」
|
|
|
|
Map<String, HalfHourSeries> seriesMap = new LinkedHashMap<>(); |
|
|
|
for (Map.Entry<String, SubLineData.childLineData> entry : accumulate.getSubData().entrySet()) { |
|
|
|
seriesMap.put(entry.getKey(), aggregateAvgByHalfHour(entry.getValue())); |
|
|
|
} |
|
|
|
|
|
|
|
/* 公共聚合方法 */ |
|
|
|
private static <T> void aggregateByHourOrHalfHour( |
|
|
|
LineData lineData, |
|
|
|
HalfHourCollector<T> collector, |
|
|
|
boolean byHour |
|
|
|
) { |
|
|
|
if (lineData == null) return; |
|
|
|
// 2. today 作为 X 轴基准
|
|
|
|
HalfHourSeries today = seriesMap.get("today"); |
|
|
|
if (today == null) { |
|
|
|
throw new IllegalStateException("subData 必须包含 today"); |
|
|
|
} |
|
|
|
|
|
|
|
List<String> xData = lineData.getXData(); |
|
|
|
Map<String, List<Object>> yDataMap = lineData.getYData(); |
|
|
|
if (xData == null || xData.isEmpty() || yDataMap == null || yDataMap.isEmpty()) return; |
|
|
|
// 3. 构建最终 X(yyyy-MM-dd HH:mm:00)
|
|
|
|
List<String> finalX = new ArrayList<>(); |
|
|
|
for (String hm : today.xList) { |
|
|
|
finalX.add(today.date + " " + hm + ":00"); |
|
|
|
} |
|
|
|
|
|
|
|
Set<String> allSeriesKeys = yDataMap.keySet(); |
|
|
|
// 4. 构建临时 map,用 today.xList 对齐
|
|
|
|
Map<String, Map<String, Object>> tmpMap = new HashMap<>(); |
|
|
|
for (Map.Entry<String, HalfHourSeries> entry : seriesMap.entrySet()) { |
|
|
|
tmpMap.put(entry.getKey(), entry.getValue().toMap()); |
|
|
|
} |
|
|
|
|
|
|
|
Map<String, Map<String, T>> bucket = new TreeMap<>(); |
|
|
|
for (int i = 0; i < xData.size(); i++) { |
|
|
|
LocalDateTime time = parseTime(xData.get(i)); |
|
|
|
String key = byHour ? TimeAxisUtil.hourKey(time) : TimeAxisUtil.halfHourKey(time); |
|
|
|
// 5. 写入 LineData
|
|
|
|
lineData.setXData(finalX); |
|
|
|
lineData.setYData(LineDataAligner.alignY(today.xList, tmpMap)); |
|
|
|
lineData.setAttrCode(accumulate.getAttrCode()); |
|
|
|
|
|
|
|
for (String series : allSeriesKeys) { |
|
|
|
Object yVal = safeGet(yDataMap.get(series), i); |
|
|
|
if (yVal == null) continue; |
|
|
|
// 6. 补齐 48 半小时
|
|
|
|
fillTo48HalfHour(lineData); |
|
|
|
} |
|
|
|
|
|
|
|
bucket.computeIfAbsent(key, k -> new HashMap<>()); |
|
|
|
collector.collect(bucket.get(key), series, time, yVal); |
|
|
|
private static HalfHourSeries aggregateAvgByHalfHour(SubLineData.childLineData child) { |
|
|
|
if (child == null || child.getXData() == null || child.getYData() == null) { |
|
|
|
return new HalfHourSeries(null); |
|
|
|
} |
|
|
|
|
|
|
|
Map<String, List<Double>> bucket = new HashMap<>(); |
|
|
|
String date = null; |
|
|
|
|
|
|
|
int size = Math.min(child.getXData().size(), child.getYData().size()); |
|
|
|
|
|
|
|
for (int i = 0; i < size; i++) { |
|
|
|
Object x = child.getXData().get(i); |
|
|
|
Object y = child.getYData().get(i); |
|
|
|
if (x == null || y == null) continue; |
|
|
|
|
|
|
|
LocalDateTime time; |
|
|
|
try { |
|
|
|
time = LocalDateTime.parse(x.toString(), TimeAxisUtil.DATETIME_FMT); |
|
|
|
} catch (Exception e) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
// 构建临时 Map 用于统一对齐
|
|
|
|
Map<String, Map<String, Object>> tmpMap = new HashMap<>(); |
|
|
|
boolean isAvg = collector instanceof AvgCollector; |
|
|
|
if (date == null) { |
|
|
|
date = time.toLocalDate().toString(); |
|
|
|
} |
|
|
|
|
|
|
|
for (Map.Entry<String, Map<String, T>> entry : bucket.entrySet()) { |
|
|
|
String xKey = entry.getKey(); |
|
|
|
Map<String, T> seriesMap = entry.getValue(); |
|
|
|
for (String series : allSeriesKeys) { |
|
|
|
Object val; |
|
|
|
T tVal = seriesMap.get(series); |
|
|
|
if (isAvg && tVal instanceof List) { |
|
|
|
List<Double> list = (List<Double>) tVal; |
|
|
|
val = list.stream().mapToDouble(Double::doubleValue).average().orElse(0); |
|
|
|
} else if (!isAvg && tVal instanceof TimeValue) { |
|
|
|
val = ((TimeValue) tVal).value; |
|
|
|
} else { |
|
|
|
val = tVal; |
|
|
|
double num; |
|
|
|
try { |
|
|
|
num = Double.parseDouble(y.toString()); |
|
|
|
} catch (Exception e) { |
|
|
|
// 非数字直接丢
|
|
|
|
continue; |
|
|
|
} |
|
|
|
tmpMap.computeIfAbsent(series, k -> new HashMap<>()).put(xKey, val); |
|
|
|
|
|
|
|
String hm = TimeAxisUtil.alignToHalfHour(time.toLocalTime()); // HH:mm
|
|
|
|
bucket.computeIfAbsent(hm, k -> new ArrayList<>()).add(num); |
|
|
|
} |
|
|
|
|
|
|
|
List<String> sortedHM = new ArrayList<>(bucket.keySet()); |
|
|
|
Collections.sort(sortedHM); |
|
|
|
|
|
|
|
List<Object> values = new ArrayList<>(); |
|
|
|
for (String hm : sortedHM) { |
|
|
|
List<Double> list = bucket.get(hm); |
|
|
|
double avg = list.stream().mapToDouble(Double::doubleValue).average().orElse(0D); |
|
|
|
values.add(avg); |
|
|
|
} |
|
|
|
|
|
|
|
List<String> newX = new ArrayList<>(bucket.keySet()); |
|
|
|
lineData.setXData(newX); |
|
|
|
lineData.setYData(LineDataAligner.alignY(newX, tmpMap)); |
|
|
|
return new HalfHourSeries(date, sortedHM, values); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* =============================== |
|
|
|
* AccumulateLineData -> LineData |
|
|
|
* SubLineData -> LineData |
|
|
|
* 半小时取最后一条 |
|
|
|
* =============================== */ |
|
|
|
public static void convertToLineDataHour(AccumulateLineData accumulate, LineData lineData) { |
|
|
|
public static void convertToAccumulateLineData(SubLineData accumulate, LineData lineData) { |
|
|
|
if (accumulate == null || accumulate.getSubData() == null) return; |
|
|
|
|
|
|
|
Map<String, HalfHourSeries> seriesMap = new LinkedHashMap<>(); |
|
|
|
for (Map.Entry<String, AccumulateLineData.childLineData> entry : accumulate.getSubData().entrySet()) { |
|
|
|
for (Map.Entry<String, SubLineData.childLineData> entry : accumulate.getSubData().entrySet()) { |
|
|
|
seriesMap.put(entry.getKey(), aggregateLastByHalfHour(entry.getValue())); |
|
|
|
} |
|
|
|
|
|
|
|
@ -250,7 +270,7 @@ public class LineDataAggregator { |
|
|
|
fillTo48HalfHour(lineData); |
|
|
|
} |
|
|
|
|
|
|
|
private static HalfHourSeries aggregateLastByHalfHour(AccumulateLineData.childLineData child) { |
|
|
|
private static HalfHourSeries aggregateLastByHalfHour(SubLineData.childLineData child) { |
|
|
|
if (child == null || child.getXData() == null || child.getYData() == null) |
|
|
|
return new HalfHourSeries(null); |
|
|
|
|
|
|
|
@ -307,19 +327,19 @@ public class LineDataAggregator { |
|
|
|
/* =============================== |
|
|
|
* 全量对齐 |
|
|
|
* =============================== */ |
|
|
|
public static void convertToLineDataAll(AccumulateLineData accumulate, LineData lineData) { |
|
|
|
public static void convertToLineDataAll(SubLineData accumulate, LineData lineData) { |
|
|
|
if (accumulate == null || accumulate.getSubData() == null) return; |
|
|
|
|
|
|
|
LinkedHashSet<String> xSet = new LinkedHashSet<>(); |
|
|
|
for (AccumulateLineData.childLineData child : accumulate.getSubData().values()) { |
|
|
|
for (SubLineData.childLineData child : accumulate.getSubData().values()) { |
|
|
|
if (child.getXData() != null) xSet.addAll(child.getXData()); |
|
|
|
} |
|
|
|
List<String> mergedX = new ArrayList<>(xSet); |
|
|
|
|
|
|
|
Map<String, Map<String, Object>> tmpMap = new LinkedHashMap<>(); |
|
|
|
for (Map.Entry<String, AccumulateLineData.childLineData> entry : accumulate.getSubData().entrySet()) { |
|
|
|
for (Map.Entry<String, SubLineData.childLineData> entry : accumulate.getSubData().entrySet()) { |
|
|
|
Map<String, Object> map = new LinkedHashMap<>(); |
|
|
|
AccumulateLineData.childLineData child = entry.getValue(); |
|
|
|
SubLineData.childLineData child = entry.getValue(); |
|
|
|
if (child.getXData() != null && child.getYData() != null) { |
|
|
|
int size = Math.min(child.getXData().size(), child.getYData().size()); |
|
|
|
for (int i = 0; i < size; i++) map.put(child.getXData().get(i), child.getYData().get(i)); |
|
|
|
|