refactor(wave): 优化波形数据处理逻辑
- 移除波形数据抽点窗口大小配置参数wave.sample.interval - 使用NPush参数替代固定的抽点窗口大小计算方式 - 修改图像生成方法的参数传递方式,直接传入nPush参数 - 调整波形数据抽点算法,移除MinMax抽点逻辑 - 优化数据段合并处理逻辑,简化null点插入策略 - 改进关键时间点处理,移除固定的关键时间点集合 - 调整波形数据范围计算,使用动态的nPush值代替固定毫秒值
This commit is contained in:
@@ -126,11 +126,6 @@ public class CsEventPOServiceImpl extends ServiceImpl<CsEventPOMapper, CsEventPO
|
||||
private final EventAnalysisService eventAnalysisService;
|
||||
private final EventCauseFeignClient eventCauseFeignClient;
|
||||
private final MsgSendFeignClient msgSendFeignClient;
|
||||
|
||||
/** 波形数据抽点窗口大小,每N个点为一个窗口进行MinMax抽点,可通过Nacos配置wave.sample.interval调整 */
|
||||
@Value("${wave.sample.interval:0}")
|
||||
private int waveSampleInterval;
|
||||
|
||||
/** 两段数据间隔区域补null点数量,可通过Nacos配置wave.gap.null.points调整 */
|
||||
@Value("${wave.gap.null.points:5}")
|
||||
private int waveGapNullPoints;
|
||||
@@ -704,23 +699,24 @@ public class CsEventPOServiceImpl extends ServiceImpl<CsEventPOMapper, CsEventPO
|
||||
if (StrUtil.isBlank(eventDetail.getInstantPics())) {
|
||||
//获取波形数据。然后绘图
|
||||
WaveDataDTO waveDataDTO = this.analyseWave(eventId, iType ,true);
|
||||
Integer nPush = Objects.isNull(waveDataDTO.getComtradeCfgDTO().getNPush())? 100: waveDataDTO.getComtradeCfgDTO().getNPush();
|
||||
//数据筛选,如果是双路电压的话,会存在2个波形数据
|
||||
List<WaveDataDetail> waveDataDetails = WaveUtil.filterWaveData(waveDataDTO);
|
||||
//单通道处理
|
||||
if (ObjectUtils.isNotEmpty(waveDataDetails) && waveDataDetails.size() == 2) {
|
||||
String instantPath = wavePicComponent.generateImageShun(waveDataDTO,waveDataDetails);
|
||||
String instantPath = wavePicComponent.generateImageShun(nPush,waveDataDetails,eventDetail.getPersistTime()*1000);
|
||||
eventDetail.setInstantPics(instantPath);
|
||||
if (StrUtil.isBlank(eventDetail.getRmsPics())) {
|
||||
String rmsPath = wavePicComponent.generateImageRms(waveDataDTO,waveDataDetails);
|
||||
String rmsPath = wavePicComponent.generateImageRms(nPush,waveDataDetails,eventDetail.getPersistTime()*1000);
|
||||
eventDetail.setRmsPics(rmsPath);
|
||||
}
|
||||
}
|
||||
//双通道处理
|
||||
else if (ObjectUtils.isNotEmpty(waveDataDetails) && waveDataDetails.size() == 4) {
|
||||
String instantPath = wavePicComponent.generateInstantImageZl(waveDataDetails);
|
||||
String instantPath = wavePicComponent.generateInstantImageZl(nPush,waveDataDetails,eventDetail.getPersistTime()*1000);
|
||||
eventDetail.setInstantPics(instantPath);
|
||||
if (StrUtil.isBlank(eventDetail.getRmsPics())) {
|
||||
String rmsPath = wavePicComponent.generateRmsImageZl(waveDataDetails);
|
||||
String rmsPath = wavePicComponent.generateRmsImageZl(nPush,waveDataDetails,eventDetail.getPersistTime()*1000);
|
||||
eventDetail.setRmsPics(rmsPath);
|
||||
}
|
||||
}
|
||||
@@ -735,24 +731,20 @@ public class CsEventPOServiceImpl extends ServiceImpl<CsEventPOMapper, CsEventPO
|
||||
if (persistTime == null || persistTime <= 0) {
|
||||
return;
|
||||
}
|
||||
Integer nPush = waveDataDTO.getComtradeCfgDTO().getNPush();
|
||||
if (Objects.isNull(nPush)) {
|
||||
nPush = 100;
|
||||
}
|
||||
|
||||
double persistTimeMs = persistTime * 1000;
|
||||
|
||||
// 开始段:波形起始前后,-40ms到100ms
|
||||
double startSegBegin = -100;
|
||||
double startSegEnd = 100;
|
||||
// 开始段:波形起始前后
|
||||
double startSegBegin = -nPush;
|
||||
double startSegEnd = nPush;
|
||||
|
||||
// 结束段:恢复点前后各40ms
|
||||
double endSegBegin = persistTimeMs - 40;
|
||||
double endSegEnd = persistTimeMs + 40;
|
||||
|
||||
// 关键时间点(抽点时必须保留,不可被抽掉)
|
||||
Set<Double> keyTimes = new HashSet<>();
|
||||
keyTimes.add(startSegBegin); // 开头-100
|
||||
keyTimes.add(0.0); // 波形开始时间点0
|
||||
keyTimes.add(persistTimeMs); // 持续时间点
|
||||
keyTimes.add(endSegBegin); // 持续时间点往前-40
|
||||
keyTimes.add(endSegEnd); // 持续时间点往后+40
|
||||
// 结束段:恢复点前后
|
||||
double endSegBegin = persistTimeMs - nPush;
|
||||
double endSegEnd = persistTimeMs + nPush;
|
||||
|
||||
List<List<Float>> originalWaveData = waveDataDTO.getListWaveData();
|
||||
List<List<Float>> originalRmsData = waveDataDTO.getListRmsData();
|
||||
@@ -769,152 +761,56 @@ public class CsEventPOServiceImpl extends ServiceImpl<CsEventPOMapper, CsEventPO
|
||||
List<List<Float>> filteredRmsData;
|
||||
|
||||
if (isMerged) {
|
||||
// 两段重叠或相邻,合并为一段连续数据
|
||||
// 两段重叠或相邻,合并为一段连续数据,不做抽点
|
||||
double mergedEnd = Math.max(startSegEnd, endSegEnd);
|
||||
List<List<Float>> segWaveData = filterByTimeRange(originalWaveData, startSegBegin, mergedEnd);
|
||||
List<List<Float>> segRmsData = filterByTimeRange(originalRmsData, startSegBegin, mergedEnd);
|
||||
|
||||
// 合并为1段:MinMax抽点 + 保留关键节点
|
||||
filteredWaveData = downsampleMinMaxWithKeyPoints(segWaveData, keyTimes, waveSampleInterval);
|
||||
filteredRmsData = downsampleMinMaxWithKeyPoints(segRmsData, keyTimes, waveSampleInterval);
|
||||
filteredWaveData = filterByTimeRange(originalWaveData, startSegBegin, mergedEnd);
|
||||
filteredRmsData = filterByTimeRange(originalRmsData, startSegBegin, mergedEnd);
|
||||
} else {
|
||||
// 两段分离,分别MinMax抽点 + 保留关键节点
|
||||
// 两段分离,各自按时间范围裁剪,不做抽点
|
||||
List<List<Float>> seg1Wave = filterByTimeRange(originalWaveData, startSegBegin, startSegEnd);
|
||||
List<List<Float>> seg2Wave = filterByTimeRange(originalWaveData, endSegBegin, endSegEnd);
|
||||
List<List<Float>> seg1Rms = filterByTimeRange(originalRmsData, startSegBegin, startSegEnd);
|
||||
List<List<Float>> seg2Rms = filterByTimeRange(originalRmsData, endSegBegin, endSegEnd);
|
||||
|
||||
List<List<Float>> ds1Wave = downsampleMinMaxWithKeyPoints(seg1Wave, keyTimes, waveSampleInterval);
|
||||
List<List<Float>> ds2Wave = downsampleMinMaxWithKeyPoints(seg2Wave, keyTimes, waveSampleInterval);
|
||||
List<List<Float>> ds1Rms = downsampleMinMaxWithKeyPoints(seg1Rms, keyTimes, waveSampleInterval);
|
||||
List<List<Float>> ds2Rms = downsampleMinMaxWithKeyPoints(seg2Rms, keyTimes, waveSampleInterval);
|
||||
|
||||
// 两段之间补固定数量的null点,时间均匀分布
|
||||
double gapStart = getTimeValue(ds1Wave.get(ds1Wave.size() - 1));
|
||||
double gapEnd = getTimeValue(ds2Wave.get(0));
|
||||
List<List<Float>> gapWaveData = createNullGapData(gapStart, gapEnd, waveGapNullPoints, waveColumnCount);
|
||||
List<List<Float>> gapRmsData = createNullGapData(gapStart, gapEnd, waveGapNullPoints, rmsColumnCount);
|
||||
|
||||
// 合并:段1 + null间隔 + 段2
|
||||
filteredWaveData = new ArrayList<>();
|
||||
filteredWaveData.addAll(ds1Wave);
|
||||
filteredWaveData.addAll(gapWaveData);
|
||||
filteredWaveData.addAll(ds2Wave);
|
||||
|
||||
filteredRmsData = new ArrayList<>();
|
||||
filteredRmsData.addAll(ds1Rms);
|
||||
filteredRmsData.addAll(gapRmsData);
|
||||
filteredRmsData.addAll(ds2Rms);
|
||||
}
|
||||
|
||||
// 段1非空才加入
|
||||
if (!seg1Wave.isEmpty()) {
|
||||
filteredWaveData.addAll(seg1Wave);
|
||||
}
|
||||
if (!seg1Rms.isEmpty()) {
|
||||
filteredRmsData.addAll(seg1Rms);
|
||||
}
|
||||
// 两段都非空时才补null间隔
|
||||
boolean waveGapNeeded = !seg1Wave.isEmpty() && !seg2Wave.isEmpty();
|
||||
boolean rmsGapNeeded = !seg1Rms.isEmpty() && !seg2Rms.isEmpty();
|
||||
|
||||
if (waveGapNeeded) {
|
||||
double waveGapStart = getTimeValue(seg1Wave.get(seg1Wave.size() - 1));
|
||||
double waveGapEnd = getTimeValue(seg2Wave.get(0));
|
||||
List<List<Float>> gapWaveData = createNullGapData(waveGapStart, waveGapEnd, waveGapNullPoints, waveColumnCount);
|
||||
filteredWaveData.addAll(gapWaveData);
|
||||
}
|
||||
if (rmsGapNeeded) {
|
||||
double rmsGapStart = getTimeValue(seg1Rms.get(seg1Rms.size() - 1));
|
||||
double rmsGapEnd = getTimeValue(seg2Rms.get(0));
|
||||
List<List<Float>> gapRmsData = createNullGapData(rmsGapStart, rmsGapEnd, waveGapNullPoints, rmsColumnCount);
|
||||
filteredRmsData.addAll(gapRmsData);
|
||||
}
|
||||
|
||||
// 段2非空才加入
|
||||
if (!seg2Wave.isEmpty()) {
|
||||
filteredWaveData.addAll(seg2Wave);
|
||||
}
|
||||
if (!seg2Rms.isEmpty()) {
|
||||
filteredRmsData.addAll(seg2Rms);
|
||||
}
|
||||
}
|
||||
waveDataDTO.setListWaveData(filteredWaveData);
|
||||
waveDataDTO.setListRmsData(filteredRmsData);
|
||||
}
|
||||
|
||||
/**
|
||||
* MinMax抽点:每N个点为一个窗口,保留窗口内峰值点、谷值点、首点及关键时间点
|
||||
* 峰值=参考列最大值的数据行,谷值=参考列最小值的数据行
|
||||
* 这样正弦波的峰谷特征得以保留,波形形状不会被破坏
|
||||
*
|
||||
* @param dataList 已按时间范围筛选的数据
|
||||
* @param keyTimes 关键时间点集合(ms)
|
||||
* @param sampleInterval 窗口大小(N)
|
||||
*/
|
||||
private List<List<Float>> downsampleMinMaxWithKeyPoints(List<List<Float>> dataList, Set<Double> keyTimes, int sampleInterval) {
|
||||
if (dataList.isEmpty()) {
|
||||
return dataList;
|
||||
}
|
||||
|
||||
// 找到关键时间点对应的数据索引
|
||||
Set<Integer> keyIndices = new HashSet<>();
|
||||
for (Double keyTime : keyTimes) {
|
||||
int closestIdx = findClosestIndex(dataList, keyTime);
|
||||
if (closestIdx >= 0) {
|
||||
keyIndices.add(closestIdx);
|
||||
}
|
||||
}
|
||||
|
||||
List<List<Float>> result = new ArrayList<>();
|
||||
int size = dataList.size();
|
||||
// 使用第1列(第一个值列,通常是A相电压)作为峰谷检测参考列
|
||||
int refCol = 1;
|
||||
|
||||
for (int windowStart = 0; windowStart < size; windowStart += sampleInterval) {
|
||||
int windowEnd = Math.min(windowStart + sampleInterval, size);
|
||||
|
||||
// 在窗口内找参考列的最大值点(峰值)和最小值点(谷值)
|
||||
int maxIdx = windowStart;
|
||||
int minIdx = windowStart;
|
||||
float maxVal = getFloatValue(dataList.get(windowStart), refCol);
|
||||
float minVal = getFloatValue(dataList.get(windowStart), refCol);
|
||||
|
||||
for (int i = windowStart + 1; i < windowEnd; i++) {
|
||||
float val = getFloatValue(dataList.get(i), refCol);
|
||||
if (val > maxVal) {
|
||||
maxVal = val;
|
||||
maxIdx = i;
|
||||
}
|
||||
if (val < minVal) {
|
||||
minVal = val;
|
||||
minIdx = i;
|
||||
}
|
||||
}
|
||||
|
||||
// 保留:窗口首点 + 峰值 + 谷值 + 窗口内的关键时间点
|
||||
Set<Integer> keepSet = new HashSet<>();
|
||||
keepSet.add(windowStart);
|
||||
keepSet.add(maxIdx);
|
||||
keepSet.add(minIdx);
|
||||
for (int i = windowStart; i < windowEnd; i++) {
|
||||
if (keyIndices.contains(i)) {
|
||||
keepSet.add(i);
|
||||
}
|
||||
}
|
||||
|
||||
// 按原始顺序输出(索引顺序即时间顺序)
|
||||
List<Integer> sortedKeep = new ArrayList<>(keepSet);
|
||||
Collections.sort(sortedKeep);
|
||||
for (int idx : sortedKeep) {
|
||||
result.add(dataList.get(idx));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 安全获取Float值,null时返回0
|
||||
*/
|
||||
private float getFloatValue(List<Float> data, int colIdx) {
|
||||
if (colIdx >= data.size()) {
|
||||
return 0f;
|
||||
}
|
||||
Float val = data.get(colIdx);
|
||||
return val != null ? val : 0f;
|
||||
}
|
||||
|
||||
/**
|
||||
* 找到与目标时间最接近的数据点索引
|
||||
* 仅在容差1.0ms内匹配,超出容差视为该段数据中无此关键点
|
||||
*/
|
||||
private int findClosestIndex(List<List<Float>> dataList, double targetTime) {
|
||||
int closestIdx = -1;
|
||||
double minDiff = Double.MAX_VALUE;
|
||||
for (int i = 0; i < dataList.size(); i++) {
|
||||
double time = getTimeValue(dataList.get(i));
|
||||
double diff = Math.abs(time - targetTime);
|
||||
if (diff < minDiff) {
|
||||
minDiff = diff;
|
||||
closestIdx = i;
|
||||
}
|
||||
}
|
||||
// 容差1.0ms,超出则认为该段不含此关键点
|
||||
if (minDiff > 1.0) {
|
||||
return -1;
|
||||
}
|
||||
return closestIdx;
|
||||
}
|
||||
|
||||
private double getTimeValue(List<Float> data) {
|
||||
return data.get(0).doubleValue();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user