洪圣文你是大傻逼
This commit is contained in:
28
tools/wave-tool/pom.xml
Normal file
28
tools/wave-tool/pom.xml
Normal file
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>com.njcn.gather</groupId>
|
||||
<artifactId>tools</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>wave-tool</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.njcn</groupId>
|
||||
<artifactId>njcn-common</artifactId>
|
||||
<version>0.0.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.njcn</groupId>
|
||||
<artifactId>spingboot2.3.12</artifactId>
|
||||
<version>2.3.12</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.njcn.gather.tool.wave.controller;
|
||||
|
||||
import com.njcn.common.pojo.annotation.OperateInfo;
|
||||
import com.njcn.common.pojo.enums.common.LogEnum;
|
||||
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||||
import com.njcn.common.pojo.response.HttpResult;
|
||||
import com.njcn.common.utils.LogUtil;
|
||||
import com.njcn.gather.tool.wave.param.WaveParseParam;
|
||||
import com.njcn.gather.tool.wave.service.WaveService;
|
||||
import com.njcn.gather.tool.wave.vo.WaveParseResultVO;
|
||||
import com.njcn.web.controller.BaseController;
|
||||
import com.njcn.web.utils.HttpResultUtil;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@Slf4j
|
||||
@Api(tags = "波形查看")
|
||||
@RestController
|
||||
@RequestMapping("/wave")
|
||||
@RequiredArgsConstructor
|
||||
public class WaveController extends BaseController {
|
||||
|
||||
private final WaveService waveService;
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||
@ApiOperation("解析波形文本")
|
||||
@ApiImplicitParam(name = "param", value = "波形解析参数", required = true, dataType = "WaveParseParam")
|
||||
@PostMapping("/parse")
|
||||
public HttpResult<WaveParseResultVO> parse(@RequestBody WaveParseParam param) {
|
||||
String methodDescribe = getMethodDescribe("parse");
|
||||
LogUtil.njcnDebug(log, "{},开始解析波形文本", methodDescribe);
|
||||
WaveParseResultVO result = waveService.parse(param);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.njcn.gather.tool.wave.param;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@Data
|
||||
@ApiModel("波形解析参数")
|
||||
public class WaveParseParam {
|
||||
|
||||
@ApiModelProperty(value = "波形文本内容,支持单列幅值、单行多值或双列时间/幅值数据", required = true)
|
||||
private String waveformText;
|
||||
|
||||
@ApiModelProperty(value = "分隔符,默认 AUTO 自动识别,支持直接传入具体字符,也支持 TAB 或 SPACE")
|
||||
private String separator;
|
||||
|
||||
@ApiModelProperty(value = "是否包含 X 轴列,true 表示文本中显式传入时间列")
|
||||
private Boolean containsXAxis;
|
||||
|
||||
@ApiModelProperty(value = "X 轴列下标,默认 0")
|
||||
private Integer xColumnIndex;
|
||||
|
||||
@ApiModelProperty(value = "Y 轴列下标,单列波形默认 0,双列波形默认 1")
|
||||
private Integer yColumnIndex;
|
||||
|
||||
@ApiModelProperty(value = "跳过的表头行数,默认 0")
|
||||
private Integer skipHeaderLines;
|
||||
|
||||
@ApiModelProperty(value = "单列波形的采样间隔,默认 1")
|
||||
private BigDecimal samplingInterval;
|
||||
|
||||
@ApiModelProperty(value = "返回的最大点位数,超过时自动下采样,默认 2000")
|
||||
private Integer maxPointCount;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.njcn.gather.tool.wave.service;
|
||||
|
||||
import com.njcn.gather.tool.wave.param.WaveParseParam;
|
||||
import com.njcn.gather.tool.wave.vo.WaveParseResultVO;
|
||||
|
||||
public interface WaveService {
|
||||
|
||||
/**
|
||||
* 解析波形文本并输出适合查看的点位结果。
|
||||
*
|
||||
* @param param 波形解析参数
|
||||
* @return 波形查看结果
|
||||
*/
|
||||
WaveParseResultVO parse(WaveParseParam param);
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
package com.njcn.gather.tool.wave.service.impl;
|
||||
|
||||
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.param.WaveParseParam;
|
||||
import com.njcn.gather.tool.wave.service.WaveService;
|
||||
import com.njcn.gather.tool.wave.vo.WaveParseResultVO;
|
||||
import com.njcn.gather.tool.wave.vo.WavePointVO;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class WaveServiceImpl implements WaveService {
|
||||
|
||||
private static final int DEFAULT_MAX_POINT_COUNT = 2000;
|
||||
private static final String AUTO_SEPARATOR = "AUTO";
|
||||
|
||||
@Override
|
||||
public WaveParseResultVO parse(WaveParseParam param) {
|
||||
if (param == null || StrUtil.isBlank(param.getWaveformText())) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "波形文本不能为空");
|
||||
}
|
||||
|
||||
boolean containsXAxis = Boolean.TRUE.equals(param.getContainsXAxis());
|
||||
int skipHeaderLines = sanitizeSkipHeaderLines(param.getSkipHeaderLines());
|
||||
int maxPointCount = sanitizeMaxPointCount(param.getMaxPointCount());
|
||||
BigDecimal samplingInterval = sanitizeSamplingInterval(param.getSamplingInterval());
|
||||
int xColumnIndex = sanitizeColumnIndex(param.getXColumnIndex(), 0);
|
||||
int yColumnIndex = sanitizeColumnIndex(param.getYColumnIndex(), containsXAxis ? 1 : 0);
|
||||
|
||||
List<WavePointVO> sourcePoints = new ArrayList<>();
|
||||
int ignoredLineCount = 0;
|
||||
int nonBlankLineIndex = 0;
|
||||
String[] lines = param.getWaveformText().split("\\r?\\n");
|
||||
for (String line : lines) {
|
||||
if (StrUtil.isBlank(line)) {
|
||||
continue;
|
||||
}
|
||||
if (nonBlankLineIndex++ < skipHeaderLines) {
|
||||
continue;
|
||||
}
|
||||
String[] columns = splitColumns(line, param.getSeparator());
|
||||
if (columns.length == 0) {
|
||||
ignoredLineCount++;
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
if (containsXAxis) {
|
||||
WavePointVO point = buildPoint(columns, xColumnIndex, yColumnIndex);
|
||||
sourcePoints.add(point);
|
||||
} else {
|
||||
sourcePoints.addAll(buildSingleColumnPoints(columns, samplingInterval, sourcePoints.size()));
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
ignoredLineCount++;
|
||||
log.debug("波形行解析失败,line={}, reason={}", line, ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
if (sourcePoints.isEmpty()) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "未解析到有效波形点位");
|
||||
}
|
||||
|
||||
List<WavePointVO> displayPoints = downSample(sourcePoints, maxPointCount);
|
||||
return buildResult(sourcePoints, displayPoints, ignoredLineCount, containsXAxis);
|
||||
}
|
||||
|
||||
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++) {
|
||||
BigDecimal yValue = parseNumber(columns[i]);
|
||||
// 单列波形默认按采样间隔自动补齐 X 轴,便于前端直接绘制。
|
||||
BigDecimal xValue = samplingInterval.multiply(BigDecimal.valueOf(startIndex + i));
|
||||
points.add(new WavePointVO(xValue, yValue));
|
||||
}
|
||||
return points;
|
||||
}
|
||||
|
||||
private String readColumn(String[] columns, int columnIndex) {
|
||||
if (columnIndex < 0 || columnIndex >= columns.length) {
|
||||
throw new IllegalArgumentException("列下标超出范围");
|
||||
}
|
||||
return columns[columnIndex];
|
||||
}
|
||||
|
||||
private BigDecimal parseNumber(String value) {
|
||||
if (StrUtil.isBlank(value)) {
|
||||
throw new IllegalArgumentException("数值为空");
|
||||
}
|
||||
return new BigDecimal(value.trim());
|
||||
}
|
||||
|
||||
private String[] splitColumns(String line, String separator) {
|
||||
String trimmedLine = line.trim();
|
||||
if (StrUtil.isBlank(trimmedLine)) {
|
||||
return new String[0];
|
||||
}
|
||||
String[] parts;
|
||||
if (StrUtil.isBlank(separator) || AUTO_SEPARATOR.equalsIgnoreCase(separator)) {
|
||||
parts = trimmedLine.split("[,;\\s]+");
|
||||
} else if ("TAB".equalsIgnoreCase(separator)) {
|
||||
parts = trimmedLine.split("\\t+");
|
||||
} else if ("SPACE".equalsIgnoreCase(separator)) {
|
||||
parts = trimmedLine.split("\\s+");
|
||||
} else {
|
||||
parts = trimmedLine.split(Pattern.quote(separator));
|
||||
}
|
||||
return Arrays.stream(parts)
|
||||
.map(String::trim)
|
||||
.filter(StrUtil::isNotBlank)
|
||||
.toArray(String[]::new);
|
||||
}
|
||||
|
||||
private List<WavePointVO> downSample(List<WavePointVO> sourcePoints, int maxPointCount) {
|
||||
if (sourcePoints.size() <= maxPointCount) {
|
||||
return sourcePoints;
|
||||
}
|
||||
|
||||
List<WavePointVO> result = new ArrayList<>();
|
||||
int step = (int) Math.ceil((double) sourcePoints.size() / maxPointCount);
|
||||
for (int i = 0; i < sourcePoints.size(); i += step) {
|
||||
result.add(sourcePoints.get(i));
|
||||
}
|
||||
WavePointVO lastPoint = sourcePoints.get(sourcePoints.size() - 1);
|
||||
if (!result.contains(lastPoint) && result.size() < maxPointCount) {
|
||||
result.add(lastPoint);
|
||||
} else if (!result.isEmpty()) {
|
||||
result.set(result.size() - 1, lastPoint);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private WaveParseResultVO buildResult(List<WavePointVO> sourcePoints, List<WavePointVO> displayPoints,
|
||||
int ignoredLineCount, boolean containsXAxis) {
|
||||
BigDecimal minX = sourcePoints.get(0).getX();
|
||||
BigDecimal maxX = sourcePoints.get(0).getX();
|
||||
BigDecimal minY = sourcePoints.get(0).getY();
|
||||
BigDecimal maxY = sourcePoints.get(0).getY();
|
||||
BigDecimal sumY = BigDecimal.ZERO;
|
||||
|
||||
for (WavePointVO point : sourcePoints) {
|
||||
if (point.getX().compareTo(minX) < 0) {
|
||||
minX = point.getX();
|
||||
}
|
||||
if (point.getX().compareTo(maxX) > 0) {
|
||||
maxX = point.getX();
|
||||
}
|
||||
if (point.getY().compareTo(minY) < 0) {
|
||||
minY = point.getY();
|
||||
}
|
||||
if (point.getY().compareTo(maxY) > 0) {
|
||||
maxY = point.getY();
|
||||
}
|
||||
sumY = sumY.add(point.getY());
|
||||
}
|
||||
|
||||
WaveParseResultVO result = new WaveParseResultVO();
|
||||
result.setContainsXAxis(containsXAxis);
|
||||
result.setSourcePointCount(sourcePoints.size());
|
||||
result.setDisplayPointCount(displayPoints.size());
|
||||
result.setIgnoredLineCount(ignoredLineCount);
|
||||
result.setSampled(sourcePoints.size() != displayPoints.size());
|
||||
result.setMinX(minX);
|
||||
result.setMaxX(maxX);
|
||||
result.setMinY(minY);
|
||||
result.setMaxY(maxY);
|
||||
result.setAverageY(sumY.divide(BigDecimal.valueOf(sourcePoints.size()), 6, RoundingMode.HALF_UP));
|
||||
result.setPoints(displayPoints);
|
||||
return result;
|
||||
}
|
||||
|
||||
private int sanitizeSkipHeaderLines(Integer skipHeaderLines) {
|
||||
if (skipHeaderLines == null || skipHeaderLines < 0) {
|
||||
return 0;
|
||||
}
|
||||
return skipHeaderLines;
|
||||
}
|
||||
|
||||
private int sanitizeMaxPointCount(Integer maxPointCount) {
|
||||
if (maxPointCount == null || maxPointCount <= 0) {
|
||||
return DEFAULT_MAX_POINT_COUNT;
|
||||
}
|
||||
return maxPointCount;
|
||||
}
|
||||
|
||||
private int sanitizeColumnIndex(Integer columnIndex, int defaultValue) {
|
||||
if (columnIndex == null || columnIndex < 0) {
|
||||
return defaultValue;
|
||||
}
|
||||
return columnIndex;
|
||||
}
|
||||
|
||||
private BigDecimal sanitizeSamplingInterval(BigDecimal samplingInterval) {
|
||||
if (samplingInterval == null || samplingInterval.compareTo(BigDecimal.ZERO) <= 0) {
|
||||
return BigDecimal.ONE;
|
||||
}
|
||||
return samplingInterval;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.njcn.gather.tool.wave.vo;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@ApiModel("波形解析结果")
|
||||
public class WaveParseResultVO {
|
||||
|
||||
@ApiModelProperty("是否包含显式 X 轴")
|
||||
private Boolean containsXAxis;
|
||||
|
||||
@ApiModelProperty("原始有效点位数")
|
||||
private Integer sourcePointCount;
|
||||
|
||||
@ApiModelProperty("返回的显示点位数")
|
||||
private Integer displayPointCount;
|
||||
|
||||
@ApiModelProperty("被忽略的无效行数")
|
||||
private Integer ignoredLineCount;
|
||||
|
||||
@ApiModelProperty("是否发生下采样")
|
||||
private Boolean sampled;
|
||||
|
||||
@ApiModelProperty("X 轴最小值")
|
||||
private BigDecimal minX;
|
||||
|
||||
@ApiModelProperty("X 轴最大值")
|
||||
private BigDecimal maxX;
|
||||
|
||||
@ApiModelProperty("Y 轴最小值")
|
||||
private BigDecimal minY;
|
||||
|
||||
@ApiModelProperty("Y 轴最大值")
|
||||
private BigDecimal maxY;
|
||||
|
||||
@ApiModelProperty("Y 轴平均值")
|
||||
private BigDecimal averageY;
|
||||
|
||||
@ApiModelProperty("用于查看的波形点位")
|
||||
private List<WavePointVO> points;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.njcn.gather.tool.wave.vo;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@ApiModel("波形点位")
|
||||
public class WavePointVO {
|
||||
|
||||
@ApiModelProperty("X 轴值")
|
||||
private BigDecimal x;
|
||||
|
||||
@ApiModelProperty("Y 轴值")
|
||||
private BigDecimal y;
|
||||
}
|
||||
Reference in New Issue
Block a user