波形解析相关

This commit is contained in:
2026-04-17 08:11:43 +08:00
parent 7fc8996bdf
commit dfbd552218
16 changed files with 1135 additions and 74 deletions

View File

@@ -32,12 +32,13 @@ tools/
## wave-tool 的职责
`wave-tool` 当前提供的能力主要围绕波形文本解析查看数据组装:
`wave-tool` 当前提供的能力主要围绕波形解析查看数据组装与图片生成
- 解析单列幅值波形文本
- 解析双列时间/幅值波形文
- 统计点位范围、均值、点数等摘要信息
- 按查看场景输出下采样后的点位集合
- 解析单列/双列文本波形
- 解析 COMTRADE `cfg/dat` 波形文
- 计算 RMS 与特征值
- 组装前端查看明细
- 生成瞬时、RMS、治理场景波形图片
从接口层看,当前主要围绕 `/wave/*` 路径提供能力。

205
tools/wave-tool/README.md Normal file
View File

@@ -0,0 +1,205 @@
# wave-tool 模块说明
## 模块定位
`wave-tool``tools` 下的波形处理模块,当前承担以下能力:
- 文本波形解析
- COMTRADE `cfg/dat` 文件解析
- RMS 波形计算
- 波形明细组装
- 波形特征值计算
- 波形图片生成与上传
当前实现以 Spring Bean 方式对外提供能力,并由 `entrance` 模块直接聚合。
## 代码结构
```text
wave-tool/
├── src/main/java/com/njcn/gather/tool/wave/
│ ├── bo
│ ├── component
│ ├── controller
│ ├── dto
│ ├── enums
│ ├── param
│ ├── service
│ ├── utils
│ └── vo
└── temp/
```
说明:
- `component/WaveFileComponent`
- 负责 COMTRADE 波形文件解析、RMS 计算、特征值计算
- `component/WavePicComponent`
- 负责波形图片绘制结果合成与上传
- `service/impl/WaveServiceImpl`
- 负责统一编排文本波形解析与 COMTRADE 解析链路
- `utils/WaveUtil`
- 负责前端查看明细组装
- `utils/BitConverter`
- 负责波形二进制字节转换
- `temp/`
- 保留原始参考代码,不作为正式运行入口
## 对外接口
### 1. 文本波形解析
- 路径:`POST /wave/parse`
- Content-Type`application/json`
请求体字段:
- `waveformText`
- 波形文本内容,必填
- `separator`
- 分隔符,支持 `AUTO``TAB``SPACE` 或直接传入具体字符
- `containsXAxis`
- 是否显式包含 X 轴
- `xColumnIndex`
- X 轴列下标
- `yColumnIndex`
- Y 轴列下标
- `skipHeaderLines`
- 跳过的表头行数
- `samplingInterval`
- 单列波形采样间隔
- `maxPointCount`
- 返回最大点位数
返回字段:
- `containsXAxis`
- `sourcePointCount`
- `displayPointCount`
- `ignoredLineCount`
- `sampled`
- `minX`
- `maxX`
- `minY`
- `maxY`
- `averageY`
- `points`
### 2. COMTRADE 波形解析
- 路径:`POST /wave/parseComtrade`
- Content-Type`multipart/form-data`
文件字段:
- `cfgFile`
- COMTRADE 配置文件
- `datFile`
- COMTRADE 数据文件
表单字段:
- `parseType`
- 解析类型
- `0`: 高级算法采样率 32-128
- `1`: 普通展示
- `2`: App 抽点
- `3`: 原始波形
- `ptType`
- PT 接线方式
- `0`: 星形
- `1`: 三角
- `2`: 开口三角
- `pt`
- PT 变比
- `ct`
- CT 变比
- `monitorName`
- 测点名称
- `calculateRms`
- 是否计算 RMS
- `buildDetails`
- 是否组装前端查看明细
- `calculateEigenvalue`
- 是否计算特征值
- `dynamicThreshold`
- 特征值算法是否使用浮动门槛
- `generateInstantImage`
- 是否生成瞬时波形图
- `generateRmsImage`
- 是否生成 RMS 波形图
- `generateInstantZlImage`
- 是否生成治理场景瞬时波形图
- `generateRmsZlImage`
- 是否生成治理场景 RMS 波形图
返回字段:
- `waveData`
- 波形基础数据,包含 `cfg`、原始波形、RMS、标题等
- `waveDataDetails`
- 前端查看明细
- `eigenvalues`
- 特征值结果
- `instantImagePath`
- 瞬时波形图路径
- `rmsImagePath`
- RMS 波形图路径
- `instantZlImagePath`
- 治理场景瞬时波形图路径
- `rmsZlImagePath`
- 治理场景 RMS 波形图路径
## 使用示例
### 文本波形
```json
{
"waveformText": "0,220\n1,221\n2,219",
"separator": "AUTO",
"containsXAxis": true,
"maxPointCount": 2000
}
```
### COMTRADE 波形
```bash
curl -X POST "http://localhost:8080/wave/parseComtrade" \
-F "cfgFile=@D:/data/test.cfg" \
-F "datFile=@D:/data/test.dat" \
-F "parseType=1" \
-F "ptType=0" \
-F "pt=1" \
-F "ct=1" \
-F "monitorName=测试测点" \
-F "calculateRms=true" \
-F "buildDetails=true" \
-F "calculateEigenvalue=true" \
-F "dynamicThreshold=true" \
-F "generateInstantImage=true" \
-F "generateRmsImage=true"
```
## 依赖说明
模块自身直接声明的依赖仍然较轻,核心包括:
- `com.njcn:njcn-common`
- `com.njcn:spingboot2.3.12`
但当前图片链路还依赖私有组件能力:
- `DrawPicUtil`
- `FileStorageUtil`
- `OssPath`
- `PicCommonData`
因此,若要真实运行图片生成功能,需要确保这些依赖在实际环境中可用。
## 当前限制
- 当前未执行 `mvn` 编译或测试验证
- `temp/` 中仍保留原始参考代码,后续如确认正式模块稳定,可再决定是否清理
- 图片生成依赖私有绘图与文件存储组件,当前仅完成代码接入,未做运行态验证

View File

@@ -1,7 +1,13 @@
package com.njcn.gather.tool.wave.component;
import com.njcn.gather.tool.wave.pojo.dto.EigenvalueDTO;
import com.njcn.gather.tool.wave.pojo.dto.WaveCycleVectorDTO;
import com.njcn.gather.tool.wave.pojo.dto.WaveHarmonicDTO;
import com.njcn.gather.tool.wave.pojo.dto.WavePhaseVectorDTO;
import com.njcn.gather.tool.wave.pojo.dto.WaveVectorGroupDTO;
import com.njcn.gather.tool.wave.pojo.dto.WaveDataDTO;
import com.njcn.gather.tool.wave.pojo.param.WaveComtradeParseParam;
import com.njcn.gather.tool.wave.pojo.vo.WaveComtradeVectorResultVO;
import java.io.InputStream;
import java.nio.file.Files;
@@ -20,6 +26,8 @@ public final class WaveFileComponentTestMain {
private static final String DEFAULT_DAT_PATH = "D:\\00-B7-8D-00-E4-09\\PQMonitor_PQM2_006970_20260320_175033_734.DAT";
/** 默认解析类型1 表示普通展示。 */
private static final int DEFAULT_PARSE_TYPE = 1;
/** 向量调试固定使用原始波形。 */
private static final int VECTOR_PARSE_TYPE = 3;
/** 默认使用浮动门槛计算特征值。 */
private static final boolean DEFAULT_DYNAMIC_THRESHOLD = true;
@@ -53,6 +61,141 @@ public final class WaveFileComponentTestMain {
List<EigenvalueDTO> eigenvalues = waveFileComponent.getEigenvalue(validWaveDataDTO, DEFAULT_DYNAMIC_THRESHOLD);
printEigenvalueResult(eigenvalues);
}
runParseComtradeVectorTest(cfgPath, datPath);
}
/**
* 调试 parseComtradeVector 逻辑。
*/
private static void runParseComtradeVectorTest(Path cfgPath, Path datPath) throws Exception {
System.out.println("=== parseComtradeVector 结果 ===");
WaveFileComponent waveFileComponent = new WaveFileComponent();
WaveVectorComponent waveVectorComponent = new WaveVectorComponent();
WaveComtradeParseParam param = new WaveComtradeParseParam();
param.setParseType(VECTOR_PARSE_TYPE);
param.setPtType(0);
param.setPt(1D);
param.setCt(1D);
param.setMonitorName("main方法调试测点");
try (InputStream cfgStream = Files.newInputStream(cfgPath);
InputStream datStream = Files.newInputStream(datPath)) {
WaveDataDTO waveDataDTO = waveFileComponent.getComtrade(cfgStream, datStream, VECTOR_PARSE_TYPE);
applyWaveMetadata(waveDataDTO, param);
List<WaveVectorGroupDTO> vectorGroups = waveVectorComponent.calculateVectors(waveDataDTO);
WaveComtradeVectorResultVO result = new WaveComtradeVectorResultVO();
result.setMonitorName(waveDataDTO.getMonitorName());
result.setTime(waveDataDTO.getTime());
result.setSamplePerCycle(waveDataDTO.getComtradeCfgDTO() == null ? null : waveDataDTO.getComtradeCfgDTO().getFinalSampleRate());
result.setCycleCount(resolveCycleCount(vectorGroups));
result.setVectorGroups(vectorGroups);
printParseComtradeVectorResult(result);
}
}
/**
* 给向量调试结果补齐 PT/CT/测点信息。
*/
private static void applyWaveMetadata(WaveDataDTO waveDataDTO, WaveComtradeParseParam param) {
waveDataDTO.setPt(param.getPt() == null || param.getPt() <= 0 ? 1D : param.getPt());
waveDataDTO.setCt(param.getCt() == null || param.getCt() <= 0 ? 1D : param.getCt());
waveDataDTO.setPtType(param.getPtType() == null ? 0 : param.getPtType());
waveDataDTO.setMonitorName(param.getMonitorName() == null || param.getMonitorName().trim().isEmpty()
? "未命名测点"
: param.getMonitorName().trim());
}
/**
* 打印 parseComtradeVector 的关键摘要。
*/
private static void printParseComtradeVectorResult(WaveComtradeVectorResultVO result) {
System.out.println("测点名称: " + result.getMonitorName());
System.out.println("事件时间: " + result.getTime());
System.out.println("每周波采样点数: " + result.getSamplePerCycle());
System.out.println("可计算周波数: " + result.getCycleCount());
System.out.println("分组数量: " + sizeOf(result.getVectorGroups()));
if (result.getVectorGroups() == null || result.getVectorGroups().isEmpty()) {
return;
}
for (WaveVectorGroupDTO group : result.getVectorGroups()) {
System.out.println("-- 分组: channelName=" + group.getChannelName()
+ ", unit=" + group.getUnit()
+ ", phaseCount=" + group.getPhaseCount()
+ ", phaseNames=" + group.getPhaseNames());
if (group.getVectorSeries() == null || group.getVectorSeries().isEmpty()) {
continue;
}
WaveCycleVectorDTO firstCycle = group.getVectorSeries().get(0);
System.out.println(" 首周波: cycleIndex=" + firstCycle.getCycleIndex() + ", time=" + firstCycle.getTime());
printPhaseVectorSummary(firstCycle.getPhaseVectors());
if (firstCycle.getPositiveSequence() != null) {
System.out.println(" 正序: amplitude=" + firstCycle.getPositiveSequence().getAmplitude()
+ ", rms=" + firstCycle.getPositiveSequence().getRms()
+ ", phaseAngle=" + firstCycle.getPositiveSequence().getPhaseAngle());
}
if (firstCycle.getNegativeSequence() != null) {
System.out.println(" 负序: amplitude=" + firstCycle.getNegativeSequence().getAmplitude()
+ ", rms=" + firstCycle.getNegativeSequence().getRms()
+ ", phaseAngle=" + firstCycle.getNegativeSequence().getPhaseAngle());
}
if (firstCycle.getZeroSequence() != null) {
System.out.println(" 零序: amplitude=" + firstCycle.getZeroSequence().getAmplitude()
+ ", rms=" + firstCycle.getZeroSequence().getRms()
+ ", phaseAngle=" + firstCycle.getZeroSequence().getPhaseAngle());
}
if (firstCycle.getUnbalance() != null) {
System.out.println(" 不平衡度: negative=" + firstCycle.getUnbalance().getNegativeUnbalanceRate()
+ ", zero=" + firstCycle.getUnbalance().getZeroUnbalanceRate());
}
}
}
/**
* 打印单相电能质量结果摘要。
*/
private static void printPhaseVectorSummary(List<WavePhaseVectorDTO> phaseVectors) {
if (phaseVectors == null || phaseVectors.isEmpty()) {
return;
}
for (WavePhaseVectorDTO phaseVector : phaseVectors) {
System.out.println(" 相别=" + phaseVector.getPhaseName()
+ ", totalRms=" + phaseVector.getTotalRms()
+ ", fundamentalAmplitude=" + phaseVector.getFundamentalAmplitude()
+ ", fundamentalRms=" + phaseVector.getFundamentalRms()
+ ", fundamentalPhaseAngle=" + phaseVector.getFundamentalPhaseAngle()
+ ", harmonicDistortionRate=" + phaseVector.getHarmonicDistortionRate());
printHarmonicSummary("电压谐波", phaseVector.getHarmonicVoltageContentRates());
printHarmonicSummary("电流谐波", phaseVector.getHarmonicCurrentAmplitudes());
}
}
/**
* 打印前几个谐波结果,避免控制台过长。
*/
private static void printHarmonicSummary(String label, List<WaveHarmonicDTO> harmonics) {
if (harmonics == null || harmonics.isEmpty()) {
return;
}
int limit = Math.min(3, harmonics.size());
for (int i = 0; i < limit; i++) {
WaveHarmonicDTO harmonic = harmonics.get(i);
System.out.println(" " + label + "-第" + harmonic.getHarmonicOrder() + "次: amplitude=" + harmonic.getAmplitude()
+ ", rms=" + harmonic.getRms()
+ ", rate=" + harmonic.getRate());
}
}
/**
* 解析向量结果中的可计算周波数。
*/
private static Integer resolveCycleCount(List<WaveVectorGroupDTO> vectorGroups) {
if (vectorGroups == null || vectorGroups.isEmpty() || vectorGroups.get(0).getVectorSeries() == null) {
return 0;
}
return vectorGroups.get(0).getVectorSeries().size();
}
/**

View File

@@ -0,0 +1,422 @@
package com.njcn.gather.tool.wave.component;
import cn.hutool.core.util.StrUtil;
import com.njcn.common.pojo.exception.BusinessException;
import com.njcn.gather.tool.wave.pojo.dto.WaveCycleVectorDTO;
import com.njcn.gather.tool.wave.pojo.dto.WaveDataDTO;
import com.njcn.gather.tool.wave.pojo.dto.WaveHarmonicDTO;
import com.njcn.gather.tool.wave.pojo.dto.WavePhaseVectorDTO;
import com.njcn.gather.tool.wave.pojo.dto.WaveSequenceUnbalanceDTO;
import com.njcn.gather.tool.wave.pojo.dto.WaveSequenceVectorDTO;
import com.njcn.gather.tool.wave.pojo.dto.WaveVectorGroupDTO;
import com.njcn.gather.tool.wave.pojo.enums.WaveFileResponseEnum;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 波形向量计算组件。
*/
@Component
public class WaveVectorComponent {
/** 最低谐波次数。 */
private static final int MIN_HARMONIC_ORDER = 1;
/** 最高谐波次数。 */
private static final int MAX_HARMONIC_ORDER = 50;
/**
* 基于原始波形按周波计算向量结果。
*/
public List<WaveVectorGroupDTO> calculateVectors(WaveDataDTO waveDataDTO) {
if (waveDataDTO == null || waveDataDTO.getComtradeCfgDTO() == null) {
throw new BusinessException(WaveFileResponseEnum.WAVE_DATA_INVALID);
}
List<List<Float>> listWaveData = waveDataDTO.getListWaveData();
Integer samplePerCycle = waveDataDTO.getComtradeCfgDTO().getFinalSampleRate();
if (listWaveData == null || listWaveData.isEmpty() || samplePerCycle == null || samplePerCycle <= 0 || listWaveData.size() < samplePerCycle) {
throw new BusinessException(WaveFileResponseEnum.WAVE_DATA_INVALID);
}
int phaseCount = waveDataDTO.getIPhasic() == null || waveDataDTO.getIPhasic() <= 0 ? 1 : waveDataDTO.getIPhasic();
int totalValueColumnCount = listWaveData.get(0).size() - 1;
if (totalValueColumnCount < phaseCount) {
throw new BusinessException(WaveFileResponseEnum.WAVE_DATA_INVALID);
}
int groupCount = totalValueColumnCount / phaseCount;
int cycleCount = listWaveData.size() / samplePerCycle;
List<WaveVectorGroupDTO> vectorGroups = new ArrayList<>();
for (int groupIndex = 0; groupIndex < groupCount; groupIndex++) {
int titleIndex = groupIndex * phaseCount + 1;
WaveVectorGroupDTO groupDTO = new WaveVectorGroupDTO();
groupDTO.setChannelName(resolveChannelName(waveDataDTO.getChannelNames(), titleIndex, groupIndex));
groupDTO.setUnit(resolveVectorUnit(waveDataDTO, titleIndex));
groupDTO.setPhaseCount(phaseCount);
groupDTO.setPhaseNames(resolvePhaseNames(waveDataDTO, titleIndex, phaseCount));
groupDTO.setVectorSeries(buildVectorSeries(waveDataDTO, groupIndex, phaseCount, samplePerCycle, cycleCount));
vectorGroups.add(groupDTO);
}
return vectorGroups;
}
/**
* 构建单组通道的周波结果序列。
*/
private List<WaveCycleVectorDTO> buildVectorSeries(WaveDataDTO waveDataDTO, int groupIndex, int phaseCount,
int samplePerCycle, int cycleCount) {
List<WaveCycleVectorDTO> vectorSeries = new ArrayList<>();
int titleIndex = groupIndex * phaseCount + 1;
List<String> phaseNames = resolvePhaseNames(waveDataDTO, titleIndex, phaseCount);
float ratio = resolveVectorRatio(waveDataDTO, titleIndex);
for (int cycleIndex = 0; cycleIndex < cycleCount; cycleIndex++) {
int startIndex = cycleIndex * samplePerCycle;
vectorSeries.add(buildCycleVector(waveDataDTO.getListWaveData(), phaseNames, groupIndex, phaseCount,
samplePerCycle, startIndex, cycleIndex, ratio, resolveVectorUnit(waveDataDTO, titleIndex)));
}
return vectorSeries;
}
/**
* 计算单个周波的电能质量结果。
*/
private WaveCycleVectorDTO buildCycleVector(List<List<Float>> listWaveData, List<String> phaseNames, int groupIndex,
int phaseCount, int samplePerCycle, int startIndex, int cycleIndex,
float ratio, String unit) {
WaveCycleVectorDTO cycleVectorDTO = new WaveCycleVectorDTO();
cycleVectorDTO.setCycleIndex(cycleIndex);
cycleVectorDTO.setTime(resolveCycleTime(listWaveData, startIndex, samplePerCycle));
List<WavePhaseVectorDTO> phaseVectors = new ArrayList<>();
Map<String, HarmonicSpectrum> spectrumMap = new HashMap<>();
for (int phaseIndex = 0; phaseIndex < phaseCount; phaseIndex++) {
int columnIndex = 1 + groupIndex * phaseCount + phaseIndex;
HarmonicSpectrum spectrum = calculateSpectrum(listWaveData, startIndex, samplePerCycle, columnIndex, ratio);
String phaseName = phaseNames.get(phaseIndex);
phaseVectors.add(buildPhaseVector(phaseName, spectrum, unit));
spectrumMap.put(phaseName, spectrum);
}
cycleVectorDTO.setPhaseVectors(phaseVectors);
if (phaseCount >= 3) {
HarmonicSpectrum phaseA = findSpectrum(spectrumMap, "A");
HarmonicSpectrum phaseB = findSpectrum(spectrumMap, "B");
HarmonicSpectrum phaseC = findSpectrum(spectrumMap, "C");
ComplexValue alpha = new ComplexValue(-0.5D, Math.sqrt(3D) / 2D);
ComplexValue alphaSquare = new ComplexValue(-0.5D, -Math.sqrt(3D) / 2D);
ComplexValue positive = phaseA.fundamental.add(alpha.multiply(phaseB.fundamental)).add(alphaSquare.multiply(phaseC.fundamental)).divide(3D);
ComplexValue negative = phaseA.fundamental.add(alphaSquare.multiply(phaseB.fundamental)).add(alpha.multiply(phaseC.fundamental)).divide(3D);
ComplexValue zero = phaseA.fundamental.add(phaseB.fundamental).add(phaseC.fundamental).divide(3D);
cycleVectorDTO.setPositiveSequence(buildSequenceVector("正序", positive));
cycleVectorDTO.setNegativeSequence(buildSequenceVector("负序", negative));
cycleVectorDTO.setZeroSequence(buildSequenceVector("零序", zero));
cycleVectorDTO.setUnbalance(buildUnbalance(positive, negative, zero));
}
return cycleVectorDTO;
}
/**
* 组装单相结果。
*/
private WavePhaseVectorDTO buildPhaseVector(String phaseName, HarmonicSpectrum spectrum, String unit) {
WavePhaseVectorDTO dto = new WavePhaseVectorDTO();
dto.setPhaseName(phaseName);
dto.setTotalRms(roundValue(spectrum.totalRms));
dto.setFundamentalAmplitude(roundValue(spectrum.fundamental.magnitude()));
dto.setFundamentalRms(roundValue(spectrum.fundamental.magnitude() / Math.sqrt(2D)));
dto.setFundamentalPhaseAngle(roundValue(resolveDisplayAngle(spectrum.fundamental)));
dto.setHarmonicDistortionRate(roundValue(calculateThdRate(spectrum)));
if ("kV".equalsIgnoreCase(unit)) {
dto.setHarmonicVoltageContentRates(buildHarmonicList(spectrum, true));
} else {
dto.setHarmonicCurrentAmplitudes(buildHarmonicList(spectrum, false));
}
return dto;
}
/**
* 计算单相一个周波的频谱信息。
*/
private HarmonicSpectrum calculateSpectrum(List<List<Float>> listWaveData, int startIndex, int samplePerCycle,
int columnIndex, float ratio) {
double totalSquare = 0D;
ComplexValue[] harmonics = new ComplexValue[MAX_HARMONIC_ORDER + 1];
for (int order = MIN_HARMONIC_ORDER; order <= MAX_HARMONIC_ORDER; order++) {
double real = 0D;
double imag = 0D;
for (int i = 0; i < samplePerCycle; i++) {
List<Float> row = listWaveData.get(startIndex + i);
if (row == null || row.size() <= columnIndex) {
throw new BusinessException(WaveFileResponseEnum.WAVE_DATA_INVALID);
}
double sample = row.get(columnIndex) * ratio;
if (order == 1) {
totalSquare += sample * sample;
}
double angle = 2D * Math.PI * order * i / samplePerCycle;
real += sample * Math.cos(angle);
imag -= sample * Math.sin(angle);
}
harmonics[order] = new ComplexValue(2D * real / samplePerCycle, 2D * imag / samplePerCycle);
}
HarmonicSpectrum spectrum = new HarmonicSpectrum();
spectrum.harmonics = harmonics;
spectrum.fundamental = harmonics[1];
spectrum.totalRms = Math.sqrt(totalSquare / samplePerCycle);
return spectrum;
}
/**
* 构建谐波列表。
*/
private List<WaveHarmonicDTO> buildHarmonicList(HarmonicSpectrum spectrum, boolean buildRate) {
List<WaveHarmonicDTO> harmonics = new ArrayList<>();
double fundamentalAmplitude = spectrum.fundamental == null ? 0D : spectrum.fundamental.magnitude();
for (int order = 2; order <= MAX_HARMONIC_ORDER; order++) {
ComplexValue value = spectrum.harmonics[order];
if (value == null) {
continue;
}
WaveHarmonicDTO harmonicDTO = new WaveHarmonicDTO();
harmonicDTO.setHarmonicOrder(order);
harmonicDTO.setAmplitude(roundValue(value.magnitude()));
harmonicDTO.setRms(roundValue(value.magnitude() / Math.sqrt(2D)));
if (buildRate) {
harmonicDTO.setRate(roundValue(fundamentalAmplitude == 0D ? 0D : value.magnitude() / fundamentalAmplitude * 100D));
}
harmonics.add(harmonicDTO);
}
return harmonics;
}
/**
* 计算 THD。
*/
private double calculateThdRate(HarmonicSpectrum spectrum) {
double sum = 0D;
for (int order = 2; order <= MAX_HARMONIC_ORDER; order++) {
ComplexValue value = spectrum.harmonics[order];
if (value != null) {
double rms = value.magnitude() / Math.sqrt(2D);
sum += rms * rms;
}
}
double fundamentalRms = spectrum.fundamental == null ? 0D : spectrum.fundamental.magnitude() / Math.sqrt(2D);
if (fundamentalRms == 0D) {
return 0D;
}
return Math.sqrt(sum) / fundamentalRms * 100D;
}
/**
* 组装序分量结果。
*/
private WaveSequenceVectorDTO buildSequenceVector(String sequenceName, ComplexValue value) {
WaveSequenceVectorDTO dto = new WaveSequenceVectorDTO();
dto.setSequenceName(sequenceName);
dto.setAmplitude(roundValue(value.magnitude()));
dto.setRms(roundValue(value.magnitude() / Math.sqrt(2D)));
dto.setPhaseAngle(roundValue(resolveDisplayAngle(value)));
return dto;
}
/**
* 组装不平衡度。
*/
private WaveSequenceUnbalanceDTO buildUnbalance(ComplexValue positive, ComplexValue negative, ComplexValue zero) {
WaveSequenceUnbalanceDTO dto = new WaveSequenceUnbalanceDTO();
double positiveMagnitude = positive.magnitude();
if (positiveMagnitude == 0D) {
dto.setNegativeUnbalanceRate(0F);
dto.setZeroUnbalanceRate(0F);
return dto;
}
dto.setNegativeUnbalanceRate(roundValue(negative.magnitude() / positiveMagnitude * 100D));
dto.setZeroUnbalanceRate(roundValue(zero.magnitude() / positiveMagnitude * 100D));
return dto;
}
/**
* 查找频谱。
*/
private HarmonicSpectrum findSpectrum(Map<String, HarmonicSpectrum> spectrumMap, String phaseFlag) {
for (Map.Entry<String, HarmonicSpectrum> entry : spectrumMap.entrySet()) {
if (StrUtil.containsIgnoreCase(entry.getKey(), phaseFlag)) {
return entry.getValue();
}
}
return HarmonicSpectrum.empty();
}
/**
* 解析通道名称。
*/
private String resolveChannelName(List<String> channelNames, int titleIndex, int groupIndex) {
if (channelNames != null && channelNames.size() > titleIndex) {
return channelNames.get(titleIndex);
}
return "CH" + (groupIndex + 1);
}
/**
* 解析相别名称。
*/
private List<String> resolvePhaseNames(WaveDataDTO waveDataDTO, int titleIndex, int phaseCount) {
List<String> phaseNames = new ArrayList<>();
List<String> waveTitle = waveDataDTO.getWaveTitle();
for (int phaseIndex = 0; phaseIndex < phaseCount; phaseIndex++) {
if (waveTitle != null && waveTitle.size() > titleIndex + phaseIndex) {
String title = waveTitle.get(titleIndex + phaseIndex);
phaseNames.add(title.length() > 1 ? title.substring(1) : title);
} else {
phaseNames.add(defaultPhaseName(phaseIndex));
}
}
return phaseNames;
}
/**
* 解析向量单位。
*/
private String resolveVectorUnit(WaveDataDTO waveDataDTO, int titleIndex) {
if (waveDataDTO.getWaveTitle() != null && waveDataDTO.getWaveTitle().size() > titleIndex
&& StrUtil.startWithIgnoreCase(waveDataDTO.getWaveTitle().get(titleIndex), "U")) {
return "kV";
}
return "A";
}
/**
* 解析向量比例。
*/
private float resolveVectorRatio(WaveDataDTO waveDataDTO, int titleIndex) {
if (waveDataDTO.getWaveTitle() != null && waveDataDTO.getWaveTitle().size() > titleIndex
&& StrUtil.startWithIgnoreCase(waveDataDTO.getWaveTitle().get(titleIndex), "U")) {
return waveDataDTO.getPt() == null ? 1F : waveDataDTO.getPt().floatValue() / 1000F;
}
return waveDataDTO.getCt() == null ? 1F : waveDataDTO.getCt().floatValue();
}
/**
* 取当前周波中点时刻。
*/
private Float resolveCycleTime(List<List<Float>> listWaveData, int startIndex, int samplePerCycle) {
int middleIndex = Math.min(startIndex + samplePerCycle / 2, listWaveData.size() - 1);
List<Float> row = listWaveData.get(middleIndex);
if (row == null || row.isEmpty()) {
throw new BusinessException(WaveFileResponseEnum.WAVE_DATA_INVALID);
}
return roundValue(row.get(0));
}
/**
* 傅里叶相角换算成正弦基准显示角度。
*/
private double resolveDisplayAngle(ComplexValue vectorValue) {
return normalizeAngle(Math.toDegrees(vectorValue.angle()) + 90D);
}
/**
* 归一化角度。
*/
private double normalizeAngle(double angle) {
double normalizedAngle = angle;
while (normalizedAngle > 180D) {
normalizedAngle -= 360D;
}
while (normalizedAngle <= -180D) {
normalizedAngle += 360D;
}
return normalizedAngle;
}
/**
* 统一保留 4 位小数。
*/
private Float roundValue(double value) {
return BigDecimal.valueOf(value).setScale(4, RoundingMode.HALF_UP).floatValue();
}
/**
* 默认相名。
*/
private String defaultPhaseName(int phaseIndex) {
switch (phaseIndex) {
case 0:
return "A相";
case 1:
return "B相";
case 2:
return "C相";
default:
return "" + (phaseIndex + 1);
}
}
/**
* 频谱对象。
*/
private static final class HarmonicSpectrum {
/** 各次谐波复向量。 */
private ComplexValue[] harmonics;
/** 基波。 */
private ComplexValue fundamental;
/** 总有效值。 */
private double totalRms;
private static HarmonicSpectrum empty() {
HarmonicSpectrum spectrum = new HarmonicSpectrum();
spectrum.harmonics = new ComplexValue[MAX_HARMONIC_ORDER + 1];
spectrum.fundamental = ComplexValue.zero();
spectrum.totalRms = 0D;
return spectrum;
}
}
/**
* 复向量。
*/
private static final class ComplexValue {
/** 实部。 */
private final double real;
/** 虚部。 */
private final double imag;
private ComplexValue(double real, double imag) {
this.real = real;
this.imag = imag;
}
private static ComplexValue zero() {
return new ComplexValue(0D, 0D);
}
private ComplexValue add(ComplexValue other) {
return new ComplexValue(this.real + other.real, this.imag + other.imag);
}
private ComplexValue multiply(ComplexValue other) {
return new ComplexValue(
this.real * other.real - this.imag * other.imag,
this.real * other.imag + this.imag * other.real
);
}
private ComplexValue divide(double divisor) {
return new ComplexValue(this.real / divisor, this.imag / divisor);
}
private double magnitude() {
return Math.hypot(this.real, this.imag);
}
private double angle() {
return Math.atan2(this.imag, this.real);
}
}
}

View File

@@ -7,9 +7,10 @@ import com.njcn.common.pojo.response.HttpResult;
import com.njcn.common.utils.LogUtil;
import com.njcn.gather.tool.wave.pojo.param.WaveComtradeParseParam;
import com.njcn.gather.tool.wave.pojo.param.WaveParseParam;
import com.njcn.gather.tool.wave.service.WaveService;
import com.njcn.gather.tool.wave.pojo.vo.WaveComtradeResultVO;
import com.njcn.gather.tool.wave.pojo.vo.WaveComtradeVectorResultVO;
import com.njcn.gather.tool.wave.pojo.vo.WaveParseResultVO;
import com.njcn.gather.tool.wave.service.WaveService;
import com.njcn.web.controller.BaseController;
import com.njcn.web.utils.HttpResultUtil;
import io.swagger.annotations.Api;
@@ -77,4 +78,25 @@ public class WaveController extends BaseController {
WaveComtradeResultVO result = waveService.parseComtrade(cfgFile.getInputStream(), datFile.getInputStream(), param);
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
}
/**
* 解析 COMTRADE 原始波形并计算向量结果。
*
* @param cfgFile cfg 文件
* @param datFile dat 文件
* @param param 解析参数
* @return 向量结果
* @throws IOException 读取上传文件流失败时抛出
*/
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
@ApiOperation("解析 COMTRADE 向量")
@PostMapping("/parseComtradeVector")
public HttpResult<WaveComtradeVectorResultVO> parseComtradeVector(@RequestParam("cfgFile") MultipartFile cfgFile,
@RequestParam("datFile") MultipartFile datFile,
@ModelAttribute WaveComtradeParseParam param) throws IOException {
String methodDescribe = getMethodDescribe("parseComtradeVector");
LogUtil.njcnDebug(log, "{},开始解析 COMTRADE 向量", methodDescribe);
WaveComtradeVectorResultVO result = waveService.parseComtradeVector(cfgFile.getInputStream(), datFile.getInputStream(), param);
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
}
}

View File

@@ -0,0 +1,44 @@
package com.njcn.gather.tool.wave.pojo.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
* 单个周波的电能质量结果。
*/
@Data
@ApiModel("单个周波的电能质量结果")
public class WaveCycleVectorDTO implements Serializable {
/** 周波序号,从 0 开始。 */
@ApiModelProperty("周波序号,从 0 开始")
private Integer cycleIndex;
/** 当前周波中点时刻,单位毫秒。 */
@ApiModelProperty("当前周波中点时刻,单位毫秒")
private Float time;
/** 各相结果。 */
@ApiModelProperty("各相结果")
private List<WavePhaseVectorDTO> phaseVectors;
/** 正序分量。 */
@ApiModelProperty("正序分量")
private WaveSequenceVectorDTO positiveSequence;
/** 负序分量。 */
@ApiModelProperty("负序分量")
private WaveSequenceVectorDTO negativeSequence;
/** 零序分量。 */
@ApiModelProperty("零序分量")
private WaveSequenceVectorDTO zeroSequence;
/** 不平衡度。 */
@ApiModelProperty("不平衡度")
private WaveSequenceUnbalanceDTO unbalance;
}

View File

@@ -0,0 +1,31 @@
package com.njcn.gather.tool.wave.pojo.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
/**
* 单次谐波结果。
*/
@Data
@ApiModel("单次谐波结果")
public class WaveHarmonicDTO implements Serializable {
/** 谐波次数。 */
@ApiModelProperty("谐波次数")
private Integer harmonicOrder;
/** 谐波幅值。 */
@ApiModelProperty("谐波幅值")
private Float amplitude;
/** 谐波有效值。 */
@ApiModelProperty("谐波有效值")
private Float rms;
/** 谐波占基波比率,百分比。 */
@ApiModelProperty("谐波占基波比率,百分比")
private Float rate;
}

View File

@@ -0,0 +1,27 @@
package com.njcn.gather.tool.wave.pojo.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
/**
* 相位差结果。
*/
@Data
@ApiModel("相位差结果")
public class WavePhaseDifferenceDTO implements Serializable {
/** A 相与 B 相相位差。 */
@ApiModelProperty("A 相与 B 相相位差")
private Float ab;
/** B 相与 C 相相位差。 */
@ApiModelProperty("B 相与 C 相相位差")
private Float bc;
/** C 相与 A 相相位差。 */
@ApiModelProperty("C 相与 A 相相位差")
private Float ca;
}

View File

@@ -0,0 +1,48 @@
package com.njcn.gather.tool.wave.pojo.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
* 单相电能质量结果。
*/
@Data
@ApiModel("单相电能质量结果")
public class WavePhaseVectorDTO implements Serializable {
/** 相别名称。 */
@ApiModelProperty("相别名称")
private String phaseName;
/** 总有效值。 */
@ApiModelProperty("总有效值")
private Float totalRms;
/** 基波幅值。 */
@ApiModelProperty("基波幅值")
private Float fundamentalAmplitude;
/** 基波有效值。 */
@ApiModelProperty("基波有效值")
private Float fundamentalRms;
/** 基波相角,单位度。 */
@ApiModelProperty("基波相角,单位度")
private Float fundamentalPhaseAngle;
/** 谐波电压含有率。 */
@ApiModelProperty("谐波电压含有率")
private List<WaveHarmonicDTO> harmonicVoltageContentRates;
/** 谐波电流幅值。 */
@ApiModelProperty("谐波电流幅值")
private List<WaveHarmonicDTO> harmonicCurrentAmplitudes;
/** 谐波畸变率,百分比。 */
@ApiModelProperty("谐波畸变率,百分比")
private Float harmonicDistortionRate;
}

View File

@@ -0,0 +1,23 @@
package com.njcn.gather.tool.wave.pojo.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
/**
* 序分量不平衡度结果。
*/
@Data
@ApiModel("序分量不平衡度结果")
public class WaveSequenceUnbalanceDTO implements Serializable {
/** 负序不平衡度,百分比。 */
@ApiModelProperty("负序不平衡度,百分比")
private Float negativeUnbalanceRate;
/** 零序不平衡度,百分比。 */
@ApiModelProperty("零序不平衡度,百分比")
private Float zeroUnbalanceRate;
}

View File

@@ -0,0 +1,31 @@
package com.njcn.gather.tool.wave.pojo.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
/**
* 序分量结果。
*/
@Data
@ApiModel("序分量结果")
public class WaveSequenceVectorDTO implements Serializable {
/** 序分量名称。 */
@ApiModelProperty("序分量名称")
private String sequenceName;
/** 幅值。 */
@ApiModelProperty("幅值")
private Float amplitude;
/** 有效值。 */
@ApiModelProperty("有效值")
private Float rms;
/** 相角,单位度。 */
@ApiModelProperty("相角,单位度")
private Float phaseAngle;
}

View File

@@ -0,0 +1,36 @@
package com.njcn.gather.tool.wave.pojo.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
* 单组通道的电能质量结果。
*/
@Data
@ApiModel("单组通道的电能质量结果")
public class WaveVectorGroupDTO implements Serializable {
/** 通道名称。 */
@ApiModelProperty("通道名称")
private String channelName;
/** 单位。 */
@ApiModelProperty("单位")
private String unit;
/** 相别数量。 */
@ApiModelProperty("相别数量")
private Integer phaseCount;
/** 当前组的相别名称。 */
@ApiModelProperty("当前组的相别名称")
private List<String> phaseNames;
/** 当前组的周波结果序列。 */
@ApiModelProperty("当前组的周波结果序列")
private List<WaveCycleVectorDTO> vectorSeries;
}

View File

@@ -0,0 +1,36 @@
package com.njcn.gather.tool.wave.pojo.vo;
import com.njcn.gather.tool.wave.pojo.dto.WaveVectorGroupDTO;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
/**
* COMTRADE 向量计算结果。
*/
@Data
@ApiModel("COMTRADE 向量计算结果")
public class WaveComtradeVectorResultVO {
/** 测点名称。 */
@ApiModelProperty("测点名称")
private String monitorName;
/** 事件发生时刻。 */
@ApiModelProperty("事件发生时刻")
private String time;
/** 每周波采样点数。 */
@ApiModelProperty("每周波采样点数")
private Integer samplePerCycle;
/** 可计算周波数。 */
@ApiModelProperty("可计算周波数")
private Integer cycleCount;
/** 向量分组结果。 */
@ApiModelProperty("向量分组结果")
private List<WaveVectorGroupDTO> vectorGroups;
}

View File

@@ -3,6 +3,7 @@ package com.njcn.gather.tool.wave.service;
import com.njcn.gather.tool.wave.pojo.param.WaveComtradeParseParam;
import com.njcn.gather.tool.wave.pojo.param.WaveParseParam;
import com.njcn.gather.tool.wave.pojo.vo.WaveComtradeResultVO;
import com.njcn.gather.tool.wave.pojo.vo.WaveComtradeVectorResultVO;
import com.njcn.gather.tool.wave.pojo.vo.WaveParseResultVO;
import java.io.InputStream;
@@ -29,4 +30,14 @@ public interface WaveService {
* @return COMTRADE 解析结果
*/
WaveComtradeResultVO parseComtrade(InputStream cfgStream, InputStream datStream, WaveComtradeParseParam param);
/**
* 基于 COMTRADE 原始波形按周波计算向量结果。
*
* @param cfgStream cfg 输入流
* @param datStream dat 输入流
* @param param 解析参数
* @return 向量计算结果
*/
WaveComtradeVectorResultVO parseComtradeVector(InputStream cfgStream, InputStream datStream, WaveComtradeParseParam param);
}

View File

@@ -4,16 +4,19 @@ import cn.hutool.core.util.StrUtil;
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
import com.njcn.common.pojo.exception.BusinessException;
import com.njcn.gather.tool.wave.component.WaveFileComponent;
import com.njcn.gather.tool.wave.component.WaveVectorComponent;
import com.njcn.gather.tool.wave.pojo.bo.WaveDataDetail;
import com.njcn.gather.tool.wave.pojo.dto.EigenvalueDTO;
import com.njcn.gather.tool.wave.pojo.dto.WaveDataDTO;
import com.njcn.gather.tool.wave.pojo.dto.WaveVectorGroupDTO;
import com.njcn.gather.tool.wave.pojo.param.WaveComtradeParseParam;
import com.njcn.gather.tool.wave.pojo.param.WaveParseParam;
import com.njcn.gather.tool.wave.service.WaveService;
import com.njcn.gather.tool.wave.utils.WaveUtil;
import com.njcn.gather.tool.wave.pojo.vo.WaveComtradeResultVO;
import com.njcn.gather.tool.wave.pojo.vo.WaveComtradeVectorResultVO;
import com.njcn.gather.tool.wave.pojo.vo.WaveParseResultVO;
import com.njcn.gather.tool.wave.pojo.vo.WavePointVO;
import com.njcn.gather.tool.wave.service.WaveService;
import com.njcn.gather.tool.wave.utils.WaveUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@@ -40,6 +43,8 @@ public class WaveServiceImpl implements WaveService {
private static final String AUTO_SEPARATOR = "AUTO";
/** COMTRADE 默认解析类型。 */
private static final int DEFAULT_PARSE_TYPE = 1;
/** 向量计算固定使用原始波形解析类型。 */
private static final int RAW_WAVE_PARSE_TYPE = 3;
/** PT/CT 默认变比。 */
private static final double DEFAULT_RATIO = 1D;
/** PT 默认接线方式0 表示星形。 */
@@ -47,13 +52,9 @@ public class WaveServiceImpl implements WaveService {
/** 波形文件解析组件。 */
private final WaveFileComponent waveFileComponent;
/** 波形向量计算组件。 */
private final WaveVectorComponent waveVectorComponent;
/**
* 解析文本波形。
*
* @param param 波形解析参数
* @return 文本波形结果
*/
@Override
public WaveParseResultVO parse(WaveParseParam param) {
if (param == null || StrUtil.isBlank(param.getWaveformText())) {
@@ -103,14 +104,6 @@ public class WaveServiceImpl implements WaveService {
return buildTextResult(sourcePoints, displayPoints, ignoredLineCount, containsXAxis);
}
/**
* 解析 COMTRADE 波形文件。
*
* @param cfgStream cfg 输入流
* @param datStream dat 输入流
* @param param 解析参数
* @return COMTRADE 解析结果
*/
@Override
public WaveComtradeResultVO parseComtrade(InputStream cfgStream, InputStream datStream, WaveComtradeParseParam param) {
if (cfgStream == null || datStream == null) {
@@ -132,9 +125,8 @@ public class WaveServiceImpl implements WaveService {
WaveComtradeResultVO result = new WaveComtradeResultVO();
result.setWaveData(waveDataDTO);
List<WaveDataDetail> waveDataDetails = null;
if (buildDetails) {
waveDataDetails = WaveUtil.filterWaveData(waveDataDTO);
List<WaveDataDetail> waveDataDetails = WaveUtil.filterWaveData(waveDataDTO);
result.setWaveDataDetails(waveDataDetails);
}
if (calculateEigenvalue) {
@@ -153,9 +145,40 @@ public class WaveServiceImpl implements WaveService {
}
}
/**
* 将请求中的 PT、CT、测点等参数写回波形结果。
*/
@Override
public WaveComtradeVectorResultVO parseComtradeVector(InputStream cfgStream, InputStream datStream, WaveComtradeParseParam param) {
if (cfgStream == null || datStream == null) {
throw new BusinessException(CommonResponseEnum.FAIL, "cfg 或 dat 文件不能为空");
}
WaveComtradeParseParam resolvedParam = param == null ? new WaveComtradeParseParam() : param;
try (InputStream cfgInputStream = cfgStream; InputStream datInputStream = datStream) {
WaveDataDTO waveDataDTO = waveFileComponent.getComtrade(cfgInputStream, datInputStream, RAW_WAVE_PARSE_TYPE);
applyWaveMetadata(waveDataDTO, resolvedParam);
List<WaveVectorGroupDTO> vectorGroups = waveVectorComponent.calculateVectors(waveDataDTO);
WaveComtradeVectorResultVO result = new WaveComtradeVectorResultVO();
result.setMonitorName(waveDataDTO.getMonitorName());
result.setTime(waveDataDTO.getTime());
result.setSamplePerCycle(waveDataDTO.getComtradeCfgDTO().getFinalSampleRate());
result.setCycleCount(resolveCycleCount(vectorGroups));
result.setVectorGroups(vectorGroups);
return result;
} catch (BusinessException ex) {
throw ex;
} catch (Exception ex) {
log.error("COMTRADE 向量计算失败", ex);
throw new BusinessException(CommonResponseEnum.FAIL, "COMTRADE 向量计算失败");
}
}
private Integer resolveCycleCount(List<WaveVectorGroupDTO> vectorGroups) {
if (vectorGroups == null || vectorGroups.isEmpty() || vectorGroups.get(0).getVectorSeries() == null) {
return 0;
}
return vectorGroups.get(0).getVectorSeries().size();
}
private void applyWaveMetadata(WaveDataDTO waveDataDTO, WaveComtradeParseParam param) {
waveDataDTO.setPt(defaultIfNull(param.getPt(), DEFAULT_RATIO));
waveDataDTO.setCt(defaultIfNull(param.getCt(), DEFAULT_RATIO));
@@ -163,23 +186,14 @@ public class WaveServiceImpl implements WaveService {
waveDataDTO.setMonitorName(StrUtil.blankToDefault(param.getMonitorName(), "未命名测点"));
}
/**
* 判断是否需要计算 RMS 数据。
*/
private boolean shouldCalculateRms(WaveComtradeParseParam param) {
return param.getCalculateRms() == null || param.getCalculateRms();
}
/**
* 判断是否需要构建前端查看明细。
*/
private boolean shouldBuildDetails(WaveComtradeParseParam param) {
return param.getBuildDetails() == null || param.getBuildDetails();
}
/**
* 规范化解析类型参数。
*/
private int sanitizeParseType(Integer parseType) {
if (parseType == null || parseType < 0 || parseType > 3) {
return DEFAULT_PARSE_TYPE;
@@ -187,9 +201,6 @@ public class WaveServiceImpl implements WaveService {
return parseType;
}
/**
* 为空或非正数时返回默认比例。
*/
private double defaultIfNull(Double value, double defaultValue) {
if (value == null || value <= 0) {
return defaultValue;
@@ -197,18 +208,12 @@ public class WaveServiceImpl implements WaveService {
return value;
}
/**
* 从指定列构建一个波形点。
*/
private WavePointVO buildPoint(String[] columns, int xColumnIndex, int yColumnIndex) {
BigDecimal yValue = parseNumber(readColumn(columns, yColumnIndex));
BigDecimal xValue = parseNumber(readColumn(columns, xColumnIndex));
return new WavePointVO(xValue, yValue);
}
/**
* 将单列数值按采样间隔扩展为点位列表。
*/
private List<WavePointVO> buildSingleColumnPoints(String[] columns, BigDecimal samplingInterval, int startIndex) {
List<WavePointVO> points = new ArrayList<>();
for (int i = 0; i < columns.length; i++) {
@@ -219,9 +224,6 @@ public class WaveServiceImpl implements WaveService {
return points;
}
/**
* 读取指定列内容。
*/
private String readColumn(String[] columns, int columnIndex) {
if (columnIndex < 0 || columnIndex >= columns.length) {
throw new IllegalArgumentException("列下标超出范围");
@@ -229,9 +231,6 @@ public class WaveServiceImpl implements WaveService {
return columns[columnIndex];
}
/**
* 将字符串转换为数值。
*/
private BigDecimal parseNumber(String value) {
if (StrUtil.isBlank(value)) {
throw new IllegalArgumentException("数值不能为空");
@@ -239,9 +238,6 @@ public class WaveServiceImpl implements WaveService {
return new BigDecimal(value.trim());
}
/**
* 按分隔符规则拆分文本列。
*/
private String[] splitColumns(String line, String separator) {
String trimmedLine = line.trim();
if (StrUtil.isBlank(trimmedLine)) {
@@ -263,9 +259,6 @@ public class WaveServiceImpl implements WaveService {
.toArray(String[]::new);
}
/**
* 对过多的点位执行简单下采样。
*/
private List<WavePointVO> downSample(List<WavePointVO> sourcePoints, int maxPointCount) {
if (sourcePoints.size() <= maxPointCount) {
return sourcePoints;
@@ -285,9 +278,6 @@ public class WaveServiceImpl implements WaveService {
return result;
}
/**
* 组装文本波形解析结果。
*/
private WaveParseResultVO buildTextResult(List<WavePointVO> sourcePoints, List<WavePointVO> displayPoints,
int ignoredLineCount, boolean containsXAxis) {
BigDecimal minX = sourcePoints.get(0).getX();
@@ -327,9 +317,6 @@ public class WaveServiceImpl implements WaveService {
return result;
}
/**
* 规范化跳过表头行数。
*/
private int sanitizeSkipHeaderLines(Integer skipHeaderLines) {
if (skipHeaderLines == null || skipHeaderLines < 0) {
return 0;
@@ -337,9 +324,6 @@ public class WaveServiceImpl implements WaveService {
return skipHeaderLines;
}
/**
* 规范化最大点数限制。
*/
private int sanitizeMaxPointCount(Integer maxPointCount) {
if (maxPointCount == null || maxPointCount <= 0) {
return DEFAULT_MAX_POINT_COUNT;
@@ -347,9 +331,6 @@ public class WaveServiceImpl implements WaveService {
return maxPointCount;
}
/**
* 规范化列下标。
*/
private int sanitizeColumnIndex(Integer columnIndex, int defaultValue) {
if (columnIndex == null || columnIndex < 0) {
return defaultValue;
@@ -357,9 +338,6 @@ public class WaveServiceImpl implements WaveService {
return columnIndex;
}
/**
* 规范化采样间隔。
*/
private BigDecimal sanitizeSamplingInterval(BigDecimal samplingInterval) {
if (samplingInterval == null || samplingInterval.compareTo(BigDecimal.ZERO) <= 0) {
return BigDecimal.ONE;