feat(event): 添加暂态事件波形查看与导出功能

- 新增 getTransientEventWave 接口用于查看暂态事件波形
- 新增 exportTransientEventWaves 接口用于批量导出暂态事件波形
- 添加 EventWaveExportParam 参数类支持波形导出
- 在 EventListMapper 中增加 selectTransientDetailsByIds 查询方法
- 更新事件列表查询参数支持毫秒级时间格式
- 移除事件描述模糊查询条件优化查询性能
- 添加波形导出相关的常量和工具类集成
This commit is contained in:
2026-05-18 08:45:05 +08:00
parent 90219a3daf
commit 38f910fccd
67 changed files with 1203 additions and 1760 deletions

View File

@@ -1,28 +0,0 @@
package com.njcn.gather.tool.adddata.component;
import com.njcn.gather.tool.adddata.pojo.bo.AddDataTableDefinition;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.util.List;
/**
* 表定义注册测试。
*/
class AddDataTableRegistryTest {
@Test
void shouldLoadAllThirteenTablesFromSchema() throws Exception {
AddDataTableRegistry registry = new AddDataTableRegistry();
registry.afterPropertiesSet();
List<AddDataTableDefinition> definitions = registry.getTableDefinitions();
Assertions.assertEquals(13, definitions.size());
Assertions.assertEquals("data_flicker", definitions.get(0).getTableName());
Assertions.assertEquals("data_v", definitions.get(definitions.size() - 1).getTableName());
Assertions.assertTrue(registry.getDefinition("data_v").getColumns().contains("V_THD"));
Assertions.assertEquals(4, registry.getDefinition("data_v").getPhaseCodes().size());
Assertions.assertTrue(registry.getDefinition("data_v").getPhaseCodes().contains("T"));
}
}

View File

@@ -1,33 +0,0 @@
package com.njcn.gather.tool.adddata.component;
import com.njcn.gather.tool.adddata.pojo.bo.AddDataTaskCommand;
import com.njcn.gather.tool.adddata.pojo.vo.AddDataTaskStatusVO;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.time.LocalDateTime;
import java.util.Arrays;
/**
* 补数任务状态持有器测试。
*/
class AddDataTaskStatusHolderTest {
private final AddDataTaskStatusHolder holder = new AddDataTaskStatusHolder(new AddDataTimeSlotCalculator());
@Test
void shouldReturnHourlyTimeResultsWhenCreateTask() {
AddDataTaskCommand command = new AddDataTaskCommand(
Arrays.asList("1"),
LocalDateTime.of(2026, 4, 28, 10, 7, 0),
LocalDateTime.of(2026, 4, 28, 13, 0, 0),
5);
AddDataTaskStatusVO status = holder.createWaitingTask(command);
Assertions.assertEquals(Arrays.asList(
"2026-04-28 11:00:00",
"2026-04-28 12:00:00",
"2026-04-28 13:00:00"), status.getHourlyTimeResults());
}
}

View File

@@ -1,33 +0,0 @@
package com.njcn.gather.tool.adddata.component;
import com.njcn.gather.tool.adddata.pojo.vo.AddDataTemplateVO;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* 模板注册测试。
*/
class AddDataTemplateRegistryTest {
@Test
void shouldOnlyExposeABCTPhaseCodes() {
AddDataTemplateRegistry registry = new AddDataTemplateRegistry();
List<AddDataTemplateVO> templates = registry.getTemplates();
Set<String> allowedPhaseCodes = new HashSet<String>();
allowedPhaseCodes.add("A");
allowedPhaseCodes.add("B");
allowedPhaseCodes.add("C");
allowedPhaseCodes.add("T");
for (AddDataTemplateVO template : templates) {
for (String phaseCode : template.getPhaseCodes()) {
Assertions.assertTrue(allowedPhaseCodes.contains(phaseCode));
}
}
}
}

View File

@@ -1,61 +0,0 @@
package com.njcn.gather.tool.adddata.component;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.time.LocalDateTime;
import java.util.List;
/**
* 时间槽计算测试。
*/
class AddDataTimeSlotCalculatorTest {
private final AddDataTimeSlotCalculator calculator = new AddDataTimeSlotCalculator();
@Test
void shouldAlignToNextNaturalSlot() {
LocalDateTime start = LocalDateTime.of(2026, 4, 28, 10, 7, 12);
LocalDateTime end = LocalDateTime.of(2026, 4, 28, 10, 22, 0);
List<LocalDateTime> slots = calculator.buildTimeSlots(start, end, 5);
Assertions.assertEquals(3, slots.size());
Assertions.assertEquals(LocalDateTime.of(2026, 4, 28, 10, 10, 0), slots.get(0));
Assertions.assertEquals(LocalDateTime.of(2026, 4, 28, 10, 20, 0), slots.get(2));
}
@Test
void shouldReturnEmptyWhenRangeDoesNotContainAnySlot() {
LocalDateTime start = LocalDateTime.of(2026, 4, 28, 10, 7, 0);
LocalDateTime end = LocalDateTime.of(2026, 4, 28, 10, 9, 59);
List<LocalDateTime> slots = calculator.buildTimeSlots(start, end, 10);
Assertions.assertTrue(slots.isEmpty());
}
@Test
void shouldBuildHourlySlotsFromNextNaturalHour() {
LocalDateTime start = LocalDateTime.of(2026, 4, 28, 10, 7, 0);
LocalDateTime end = LocalDateTime.of(2026, 4, 28, 13, 0, 0);
List<LocalDateTime> slots = calculator.buildHourlyTimeSlots(start, end);
Assertions.assertEquals(3, slots.size());
Assertions.assertEquals(LocalDateTime.of(2026, 4, 28, 11, 0, 0), slots.get(0));
Assertions.assertEquals(LocalDateTime.of(2026, 4, 28, 13, 0, 0), slots.get(2));
}
@Test
void shouldIncludeStartWhenAlreadyAtNaturalHour() {
LocalDateTime start = LocalDateTime.of(2026, 4, 28, 10, 0, 0);
LocalDateTime end = LocalDateTime.of(2026, 4, 28, 12, 30, 0);
List<LocalDateTime> slots = calculator.buildHourlyTimeSlots(start, end);
Assertions.assertEquals(3, slots.size());
Assertions.assertEquals(LocalDateTime.of(2026, 4, 28, 10, 0, 0), slots.get(0));
Assertions.assertEquals(LocalDateTime.of(2026, 4, 28, 12, 0, 0), slots.get(2));
}
}

View File

@@ -1,32 +0,0 @@
package com.njcn.gather.tool.adddata.component;
import com.njcn.gather.tool.adddata.pojo.bo.AddDataTableDefinition;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.time.LocalDateTime;
import java.util.List;
/**
* 数值生成器测试。
*/
class AddDataValueGeneratorTest {
@Test
void shouldGenerateStableDataVRowWithExpectedColumnCount() throws Exception {
AddDataTableRegistry registry = new AddDataTableRegistry();
registry.afterPropertiesSet();
AddDataTableDefinition definition = registry.getDefinition("data_v");
AddDataValueGenerator generator = new AddDataValueGenerator();
List<Object> row = generator.generateRow(definition, "f04a9d62e3d24e6580e4f32b40967505", LocalDateTime.of(2026, 4, 28, 10, 10, 0), "A");
Assertions.assertEquals(definition.getColumns().size(), row.size());
Assertions.assertEquals("A", row.get(definition.getColumns().indexOf("PHASIC_TYPE")));
Double rms = (Double) row.get(definition.getColumns().indexOf("RMS"));
Double rmsMax = (Double) row.get(definition.getColumns().indexOf("RMS_MAX"));
Double rmsMin = (Double) row.get(definition.getColumns().indexOf("RMS_MIN"));
Assertions.assertTrue(rmsMax >= rms);
Assertions.assertTrue(rmsMin <= rms);
}
}

View File

@@ -32,6 +32,7 @@
project.name AS projectName,
equipment.id AS equipmentId,
equipment.name AS equipmentName,
equipment.mac AS equipmentMac,
line.line_id AS lineId,
line.name AS lineName
FROM cs_line line
@@ -55,6 +56,7 @@
project.name AS projectName,
equipment.id AS equipmentId,
equipment.name AS equipmentName,
equipment.mac AS equipmentMac,
line.line_id AS lineId,
line.name AS lineName
FROM cs_line line

View File

@@ -24,6 +24,8 @@ public class AddLedgerLinePathVO implements Serializable {
private String equipmentName;
private String equipmentMac;
private String lineId;
private String lineName;

View File

@@ -1,224 +0,0 @@
/*
Navicat/MySQL 初始化脚本
用途:
1. 初始化 add-ledger 台账设备相关字典。
2. 脚本可重复执行,按字典编码避免重复插入。
说明:
- dev_type 对应 cs_equipment_delivery.dev_type保存 sys_dict_data.id。
- dev_model 对应 cs_equipment_delivery.dev_model保存 sys_dict_data.id。
*/
SET NAMES utf8mb4;
-- ----------------------------
-- 字典类型:装置类型
-- ----------------------------
SET @ledger_device_type_type_id := (
SELECT `id`
FROM `sys_dict_type`
WHERE `code` = 'ledger_device_type'
AND `state` = 1
LIMIT 1
);
INSERT INTO `sys_dict_type` (
`id`, `name`, `code`, `sort`, `open_level`, `open_describe`, `remark`, `state`,
`create_by`, `create_time`, `update_by`, `update_time`
)
SELECT
'7f91c2a1e9f44b47a6e7c8b227d00101',
'装置类型',
'ledger_device_type',
1,
0,
0,
'数据台账设备装置类型',
1,
'system',
NOW(),
'system',
NOW()
WHERE @ledger_device_type_type_id IS NULL;
SET @ledger_device_type_type_id := (
SELECT `id`
FROM `sys_dict_type`
WHERE `code` = 'ledger_device_type'
AND `state` = 1
LIMIT 1
);
INSERT INTO `sys_dict_data` (
`id`, `type_id`, `name`, `code`, `sort`, `level`, `algo_describe`, `value`, `open_value`, `state`,
`create_by`, `create_time`, `update_by`, `update_time`
)
SELECT
'7f91c2a1e9f44b47a6e7c8b227d10101',
@ledger_device_type_type_id,
'直连设备',
'direct_device',
1,
0,
NULL,
NULL,
0,
1,
'system',
NOW(),
'system',
NOW()
WHERE NOT EXISTS (
SELECT 1
FROM `sys_dict_data`
WHERE `type_id` = @ledger_device_type_type_id
AND `code` = 'direct_device'
AND `state` = 1
);
INSERT INTO `sys_dict_data` (
`id`, `type_id`, `name`, `code`, `sort`, `level`, `algo_describe`, `value`, `open_value`, `state`,
`create_by`, `create_time`, `update_by`, `update_time`
)
SELECT
'7f91c2a1e9f44b47a6e7c8b227d10102',
@ledger_device_type_type_id,
'网关',
'gateway',
2,
0,
NULL,
NULL,
0,
1,
'system',
NOW(),
'system',
NOW()
WHERE NOT EXISTS (
SELECT 1
FROM `sys_dict_data`
WHERE `type_id` = @ledger_device_type_type_id
AND `code` = 'gateway'
AND `state` = 1
);
INSERT INTO `sys_dict_data` (
`id`, `type_id`, `name`, `code`, `sort`, `level`, `algo_describe`, `value`, `open_value`, `state`,
`create_by`, `create_time`, `update_by`, `update_time`
)
SELECT
'7f91c2a1e9f44b47a6e7c8b227d10103',
@ledger_device_type_type_id,
'装置',
'device',
3,
0,
NULL,
NULL,
0,
1,
'system',
NOW(),
'system',
NOW()
WHERE NOT EXISTS (
SELECT 1
FROM `sys_dict_data`
WHERE `type_id` = @ledger_device_type_type_id
AND `code` = 'device'
AND `state` = 1
);
-- ----------------------------
-- 字典类型:装置型号
-- ----------------------------
SET @ledger_device_model_type_id := (
SELECT `id`
FROM `sys_dict_type`
WHERE `code` = 'ledger_device_model'
AND `state` = 1
LIMIT 1
);
INSERT INTO `sys_dict_type` (
`id`, `name`, `code`, `sort`, `open_level`, `open_describe`, `remark`, `state`,
`create_by`, `create_time`, `update_by`, `update_time`
)
SELECT
'7f91c2a1e9f44b47a6e7c8b227d00201',
'装置型号',
'ledger_device_model',
2,
0,
0,
'数据台账设备装置型号',
1,
'system',
NOW(),
'system',
NOW()
WHERE @ledger_device_model_type_id IS NULL;
SET @ledger_device_model_type_id := (
SELECT `id`
FROM `sys_dict_type`
WHERE `code` = 'ledger_device_model'
AND `state` = 1
LIMIT 1
);
INSERT INTO `sys_dict_data` (
`id`, `type_id`, `name`, `code`, `sort`, `level`, `algo_describe`, `value`, `open_value`, `state`,
`create_by`, `create_time`, `update_by`, `update_time`
)
SELECT
'7f91c2a1e9f44b47a6e7c8b227d20101',
@ledger_device_model_type_id,
'PQS588',
'pqs588',
1,
0,
NULL,
NULL,
0,
1,
'system',
NOW(),
'system',
NOW()
WHERE NOT EXISTS (
SELECT 1
FROM `sys_dict_data`
WHERE `type_id` = @ledger_device_model_type_id
AND `code` = 'pqs588'
AND `state` = 1
);
INSERT INTO `sys_dict_data` (
`id`, `type_id`, `name`, `code`, `sort`, `level`, `algo_describe`, `value`, `open_value`, `state`,
`create_by`, `create_time`, `update_by`, `update_time`
)
SELECT
'7f91c2a1e9f44b47a6e7c8b227d20102',
@ledger_device_model_type_id,
'PQS680',
'pqs680',
2,
0,
NULL,
NULL,
0,
1,
'system',
NOW(),
'system',
NOW()
WHERE NOT EXISTS (
SELECT 1
FROM `sys_dict_data`
WHERE `type_id` = @ledger_device_model_type_id
AND `code` = 'pqs680'
AND `state` = 1
);

View File

@@ -1,43 +0,0 @@
package com.njcn.gather.tool.addledger.component;
import com.njcn.gather.tool.addledger.pojo.po.AddLedgerLedgerPO;
import com.njcn.gather.tool.addledger.pojo.vo.AddLedgerTreeNodeVO;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import java.util.List;
/**
* 台账树组装测试。
*/
class AddLedgerTreeBuilderTest {
private final AddLedgerTreeBuilder treeBuilder = new AddLedgerTreeBuilder();
@Test
void shouldBuildFourLevelTreeFromFlatLedgerNodes() {
AddLedgerLedgerPO engineering = buildLedger("engineering-1", "0", 0, "工程");
AddLedgerLedgerPO project = buildLedger("project-1", "engineering-1", 1, "项目");
AddLedgerLedgerPO equipment = buildLedger("equipment-1", "project-1", 2, "设备");
AddLedgerLedgerPO line = buildLedger("line-1", "equipment-1", 3, "测点");
List<AddLedgerTreeNodeVO> result = treeBuilder.buildTree(Arrays.asList(line, equipment, project, engineering));
Assertions.assertEquals(1, result.size());
Assertions.assertEquals("engineering-1", result.get(0).getId());
Assertions.assertEquals("project-1", result.get(0).getChildren().get(0).getId());
Assertions.assertEquals("equipment-1", result.get(0).getChildren().get(0).getChildren().get(0).getId());
Assertions.assertEquals("line-1", result.get(0).getChildren().get(0).getChildren().get(0).getChildren().get(0).getId());
}
private AddLedgerLedgerPO buildLedger(String id, String parentId, Integer level, String name) {
AddLedgerLedgerPO ledger = new AddLedgerLedgerPO();
ledger.setId(id);
ledger.setPid(parentId);
ledger.setLevel(level);
ledger.setName(name);
ledger.setSort(0);
return ledger;
}
}

View File

@@ -1,33 +0,0 @@
package com.njcn.gather.tool.addledger.util;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import java.util.List;
/**
* 台账测点线路号工具测试。
*/
class AddLedgerLineNoUtilTest {
@Test
void shouldReturnOnlyUnusedLineNosWhenCreateLine() {
List<Integer> result = AddLedgerLineNoUtil.resolveAvailableLineNos(Arrays.asList(1, 2, 20), null);
Assertions.assertEquals(17, result.size());
Assertions.assertFalse(result.contains(1));
Assertions.assertFalse(result.contains(2));
Assertions.assertFalse(result.contains(20));
Assertions.assertTrue(result.contains(3));
}
@Test
void shouldKeepCurrentLineNoAvailableWhenEditLine() {
List<Integer> result = AddLedgerLineNoUtil.resolveAvailableLineNos(Arrays.asList(1, 2, 20), 2);
Assertions.assertTrue(result.contains(2));
Assertions.assertFalse(result.contains(1));
Assertions.assertFalse(result.contains(20));
}
}

View File

@@ -196,7 +196,7 @@ curl.exe -X POST "http://localhost:8080/api/mms-mapping/get-icd-mms-json" `
说明:
- `mappingJson` 是字符串字段,字段值本身也是一段 JSON 文本。
-`saveToDisk=true` 时,响应中还会返回 `savedPath`
-`saveToDisk=true` 时,响应中还会返回 `savedPath`,落盘文件名按统一规则追加 `_yyyyMMdd`
### 5.3 FAILED

View File

@@ -1,5 +1,6 @@
package com.njcn.gather.icd.mapping.component;
import com.njcn.gather.icd.mapping.utils.GeneratedFileNameUtil;
import org.springframework.stereotype.Component;
import java.io.File;
@@ -26,7 +27,7 @@ public class FileStorageService {
if (!dir.isDirectory()) {
throw new IllegalStateException("输出路径不是目录:" + dir.getAbsolutePath());
}
File target = new File(dir, fileName);
File target = new File(dir, GeneratedFileNameUtil.appendToday(fileName));
try (FileOutputStream fos = new FileOutputStream(target)) {
fos.write(content.getBytes(StandardCharsets.UTF_8));
}

View File

@@ -8,6 +8,7 @@ import com.njcn.gather.icd.mapping.pojo.vo.IndexCandidateReportItemResponse;
import com.njcn.gather.icd.mapping.pojo.vo.IndexCandidateResponse;
import com.njcn.gather.icd.mapping.pojo.vo.MappingDocumentResponse;
import com.njcn.gather.icd.mapping.pojo.vo.XmlFileResponse;
import com.njcn.gather.icd.mapping.utils.GeneratedFileNameUtil;
import org.springframework.stereotype.Component;
@Component
@@ -90,13 +91,13 @@ public class IcdToXmlResponseConverter {
private String resolveXmlFileName(IcdToXmlGenerateResult result) {
String iedName = result.getIedName();
if (iedName == null || iedName.trim().isEmpty()) {
return DEFAULT_XML_FILE_NAME;
return GeneratedFileNameUtil.appendToday(DEFAULT_XML_FILE_NAME);
}
String safeName = iedName.replaceAll("[\\\\/:*?\"<>|]+", "_").trim();
if (safeName.isEmpty()) {
return DEFAULT_XML_FILE_NAME;
return GeneratedFileNameUtil.appendToday(DEFAULT_XML_FILE_NAME);
}
return safeName + ".xml";
return GeneratedFileNameUtil.appendToday(safeName + ".xml");
}
}

View File

@@ -9,6 +9,7 @@ import com.njcn.gather.icd.mapping.pojo.param.*;
import com.njcn.gather.icd.mapping.pojo.vo.*;
import com.njcn.gather.icd.mapping.pojo.enums.GenerateStatus;
import com.njcn.gather.icd.mapping.pojo.bo.mapping.MappingDocument;
import com.njcn.gather.icd.mapping.utils.GeneratedFileNameUtil;
import lombok.var;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
@@ -17,6 +18,8 @@ import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.List;
@@ -57,8 +60,9 @@ public class JsonToXmlConversionService {
String xmlContent = buildXmlContentFromJson(mappingJson, templateStream, ruleStreams, indexMapping);
// 3. 保存为临时文件
Path tempPath = Files.createTempFile("converted_", ".xml");
Files.write(tempPath, xmlContent.getBytes(StandardCharsets.UTF_8));
Path tempPath = Paths.get(System.getProperty("java.io.tmpdir"),
GeneratedFileNameUtil.appendToday("converted_" + java.util.UUID.randomUUID().toString().replace("-", "") + ".xml"));
Files.write(tempPath, xmlContent.getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE_NEW);
return tempPath.toString();
}

View File

@@ -12,6 +12,8 @@ import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -20,6 +22,7 @@ import com.njcn.gather.icd.mapping.pojo.bo.*;
import com.njcn.gather.icd.mapping.pojo.param.*;
import com.njcn.gather.icd.mapping.pojo.vo.*;
import com.njcn.gather.icd.mapping.pojo.enums.GenerateStatus;
import com.njcn.gather.icd.mapping.utils.GeneratedFileNameUtil;
@Component
public class RuleBasedXmlMappingService {
@@ -104,8 +107,9 @@ public class RuleBasedXmlMappingService {
xmlContent = applyRulesToXml(xmlContent, applicableRules);
Path tempPath = Files.createTempFile("rule_mapped_", ".xml");
Files.write(tempPath, xmlContent.getBytes(StandardCharsets.UTF_8));
Path tempPath = Paths.get(System.getProperty("java.io.tmpdir"),
GeneratedFileNameUtil.appendToday("rule_mapped_" + UUID.randomUUID().toString().replace("-", "") + ".xml"));
Files.write(tempPath, xmlContent.getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE_NEW);
return tempPath.toString();
}

View File

@@ -0,0 +1,33 @@
package com.njcn.gather.icd.mapping.utils;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
/**
* 生成文件名处理工具。
*/
public final class GeneratedFileNameUtil {
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd");
private GeneratedFileNameUtil() {
}
public static String appendToday(String fileName) {
return appendDate(fileName, LocalDate.now());
}
public static String appendDate(String fileName, LocalDate date) {
if (fileName == null || date == null) {
return fileName;
}
String dateText = DATE_FORMATTER.format(date);
int separatorIndex = Math.max(fileName.lastIndexOf('/'), fileName.lastIndexOf('\\'));
int dotIndex = fileName.lastIndexOf('.');
if (dotIndex > separatorIndex) {
return fileName.substring(0, dotIndex) + "_" + dateText + fileName.substring(dotIndex);
}
return fileName + "_" + dateText;
}
}

View File

@@ -1,232 +0,0 @@
package com.njcn.gather.icd.mapping.debug;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.njcn.gather.icd.mapping.component.MappingResponseConverter;
import com.njcn.gather.icd.mapping.pojo.dto.GenerateFromIcdCommand;
import com.njcn.gather.icd.mapping.pojo.dto.IndexBindingCommand;
import com.njcn.gather.icd.mapping.pojo.dto.IndexSelectionGroupCommand;
import com.njcn.gather.icd.mapping.pojo.vo.IndexCandidateReportItemResponse;
import com.njcn.gather.icd.mapping.pojo.vo.IndexCandidateResponse;
import com.njcn.gather.icd.mapping.pojo.vo.MappingTaskResponse;
import com.njcn.gather.icd.mapping.service.MappingTaskService;
import org.springframework.boot.Banner;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
/**
* `getIcdMmsJson` 本地调试入口。
*
* 使用方式:
* 1. 先修改 ICD_FILE_PATH 指向本地真实 ICD/SCD 文件。
* 2. 直接运行 main先观察第一次调试返回的 indexCandidates。
* 3. 按第一次返回结果补齐 buildSecondStepSelection() 中的绑定关系。
* 4. 把 RUN_SECOND_STEP 改成 true再次运行 main 获取最终 mappingJson。
*/
public class GetIcdMmsJsonDebugRunner {
/** 本地 ICD/SCD 文件路径,运行前请改成真实文件。 */
private static final String ICD_FILE_PATH = "D:\\Work\\工作资料\\1灿能项目资料\\01自研\\01灿能\\09灿能C端功能\\01需求文档\\灿能工具箱开发\\icd\\PQS882_VX_BJ_1(V111).icd";
/** 调试时可固定版本号,便于对比输出。 */
private static final String VERSION = "20260421";
/** 调试作者标识。 */
private static final String AUTHOR = "debug-user";
/** 是否输出格式化 JSON便于人工查看。 */
private static final boolean PRETTY_JSON = true;
/** 是否把生成结果写入磁盘。 */
private static final boolean SAVE_TO_DISK = false;
/** saveToDisk=true 时使用的输出目录。 */
private static final String OUTPUT_DIR = "D:/temp/mms-output";
/**
* 第二次正式生成开关。
* 默认先只跑第一次,确认 groupKey/reportName/dataSetName/lnInst 候选值后再打开。
*/
private static final boolean RUN_SECOND_STEP = false;
public static void main(String[] args) {
try (ConfigurableApplicationContext context = new SpringApplicationBuilder(DebugApplication.class)
.web(WebApplicationType.NONE)
.bannerMode(Banner.Mode.OFF)
.logStartupInfo(false)
.run(args)) {
MappingTaskService mappingTaskService = context.getBean(MappingTaskService.class);
MappingResponseConverter responseConverter = context.getBean(MappingResponseConverter.class);
ObjectMapper objectMapper = createObjectMapper();
MappingTaskResponse firstResponse = debugNeedIndexSelection(mappingTaskService, responseConverter);
printResponse("第一次调试:获取索引候选", firstResponse, objectMapper);
printIndexCandidatesJson(firstResponse, objectMapper);
printIndexCandidateSummary(firstResponse);
if (!RUN_SECOND_STEP) {
System.out.println("第二次调试未开启。请先根据第一次返回结果补齐 buildSecondStepSelection(),再把 RUN_SECOND_STEP 改成 true。");
return;
}
MappingTaskResponse secondResponse = debugGenerateMapping(mappingTaskService, responseConverter);
printResponse("第二次调试:生成 MMS JSON", secondResponse, objectMapper);
} catch (Exception ex) {
throw new IllegalStateException("getIcdMmsJson 调试失败:" + ex.getMessage(), ex);
}
}
/**
* 第一次调试:不传 indexSelection只获取 icdDocument 和 indexCandidates。
*/
private static MappingTaskResponse debugNeedIndexSelection(MappingTaskService mappingTaskService,
MappingResponseConverter responseConverter) {
GenerateFromIcdCommand command = buildBaseCommand();
return responseConverter.fromSubmitResult(mappingTaskService.getIcdMmsJson(command));
}
/**
* 第二次调试:补齐 indexSelection 后直接生成最终 mappingJson。
*/
private static MappingTaskResponse debugGenerateMapping(MappingTaskService mappingTaskService,
MappingResponseConverter responseConverter) {
List<IndexSelectionGroupCommand> selectionGroups = buildSecondStepSelection();
if (selectionGroups.isEmpty()) {
throw new IllegalArgumentException("第二次调试缺少 indexSelection请先补齐 buildSecondStepSelection()");
}
GenerateFromIcdCommand command = buildBaseCommand();
command.getIndexSelection().addAll(selectionGroups);
return responseConverter.fromSubmitResult(mappingTaskService.getIcdMmsJson(command));
}
/**
* 组装调试用基础命令,等价于接口中的 request 基础参数。
*/
private static GenerateFromIcdCommand buildBaseCommand() {
Path icdPath = Paths.get(ICD_FILE_PATH);
if (!Files.exists(icdPath)) {
throw new IllegalArgumentException("ICD 文件不存在:" + icdPath.toAbsolutePath());
}
try {
GenerateFromIcdCommand command = new GenerateFromIcdCommand();
command.setFileName(icdPath.getFileName().toString());
command.setFileBytes(Files.readAllBytes(icdPath));
command.setVersion(VERSION);
command.setAuthor(AUTHOR);
command.setPrettyJson(PRETTY_JSON);
command.setSaveToDisk(SAVE_TO_DISK);
command.setOutputDir(OUTPUT_DIR);
return command;
} catch (Exception ex) {
throw new IllegalArgumentException("读取 ICD 文件失败:" + ex.getMessage(), ex);
}
}
/**
* 第二次调试的索引绑定示例。
*
* 注意:
* 1. groupKey/groupDesc/reportName/dataSetName/lnInst 必须使用第一次返回的真实值。
* 2. 下面示例仅作占位,默认不参与运行。
*/
private static List<IndexSelectionGroupCommand> buildSecondStepSelection() {
List<IndexSelectionGroupCommand> groups = new ArrayList<IndexSelectionGroupCommand>();
// 示例:
// IndexSelectionGroupCommand group = createSelectionGroup("HARM__DSSTHARM", "谐波数据");
// group.getBindings().add(createBinding("brcbStHarm", "dsStHarm", "A相", "1"));
// group.getBindings().add(createBinding("brcbStHarm", "dsStHarm", "B相", "2"));
// group.getBindings().add(createBinding("brcbStHarm", "dsStHarm", "C相", "3"));
// groups.add(group);
return groups;
}
private static IndexSelectionGroupCommand createSelectionGroup(String groupKey, String groupDesc) {
IndexSelectionGroupCommand group = new IndexSelectionGroupCommand();
group.setGroupKey(groupKey);
group.setGroupDesc(groupDesc);
return group;
}
private static IndexBindingCommand createBinding(String reportName,
String dataSetName,
String label,
String lnInst) {
IndexBindingCommand binding = new IndexBindingCommand();
binding.setReportName(reportName);
binding.setDataSetName(dataSetName);
binding.setLabel(label);
binding.setLnInst(lnInst);
return binding;
}
/**
* 控制台输出候选摘要,方便把第一次返回值填回第二次调试配置。
*/
private static void printIndexCandidateSummary(MappingTaskResponse response) {
if (response == null || response.getIndexCandidates() == null || response.getIndexCandidates().isEmpty()) {
return;
}
System.out.println("===== 索引候选摘要 =====");
for (IndexCandidateResponse candidate : response.getIndexCandidates()) {
System.out.println("groupKey=" + candidate.getGroupKey() + ", groupDesc=" + candidate.getGroupDesc());
if (candidate.getReports() == null) {
continue;
}
for (IndexCandidateReportItemResponse report : candidate.getReports()) {
System.out.println(" reportName=" + report.getReportName()
+ ", dataSetName=" + report.getDataSetName()
+ ", availableLnInstValues=" + report.getAvailableLnInstValues());
}
}
}
/**
* 单独输出 indexCandidates 的 JSON便于直接复制做第二次调试绑定。
*/
private static void printIndexCandidatesJson(MappingTaskResponse response, ObjectMapper objectMapper) {
try {
System.out.println();
System.out.println("===== indexCandidates JSON =====");
if (response == null) {
System.out.println("null");
return;
}
System.out.println(objectMapper.writeValueAsString(response.getIndexCandidates()));
} catch (Exception ex) {
throw new IllegalArgumentException("print indexCandidates JSON failed: " + ex.getMessage(), ex);
}
}
private static void printResponse(String title, MappingTaskResponse response, ObjectMapper objectMapper) {
try {
System.out.println();
System.out.println("===== " + title + " =====");
System.out.println(objectMapper.writeValueAsString(response));
} catch (Exception ex) {
throw new IllegalArgumentException("打印调试响应失败:" + ex.getMessage(), ex);
}
}
private static ObjectMapper createObjectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
return objectMapper;
}
@SpringBootApplication(scanBasePackages = "com.njcn.gather.icd.mapping")
public static class DebugApplication {
}
}

View File

@@ -1,265 +0,0 @@
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;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
/**
* 本地调试 getComtrade 的命令行入口。
*/
public final class WaveFileComponentTestMain {
/** 默认测试 CFG 文件路径。 */
private static final String DEFAULT_CFG_PATH = "D:\\00-B7-8D-00-E4-09\\PQMonitor_PQM2_006970_20260320_175033_734.CFG";
/** 默认测试 DAT 文件路径。 */
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;
private WaveFileComponentTestMain() {
}
/**
* 用法:
* java com.njcn.gather.tool.wave.component.WaveFileComponentTestMain <cfgPath> <datPath> [parseType]
*/
public static void main(String[] args) throws Exception {
if (args.length < 2) {
System.out.println("未传入参数,使用默认测试文件路径");
System.out.println("cfgPath: " + DEFAULT_CFG_PATH);
System.out.println("datPath: " + DEFAULT_DAT_PATH);
}
Path cfgPath = Paths.get(args.length > 0 ? args[0] : DEFAULT_CFG_PATH);
Path datPath = Paths.get(args.length > 1 ? args[1] : DEFAULT_DAT_PATH);
int parseType = args.length > 2 ? Integer.parseInt(args[2]) : DEFAULT_PARSE_TYPE;
WaveFileComponent waveFileComponent = new WaveFileComponent();
try (InputStream cfgStream = Files.newInputStream(cfgPath);
InputStream datStream = Files.newInputStream(datPath)) {
WaveDataDTO waveDataDTO = waveFileComponent.getComtrade(cfgStream, datStream, parseType);
printComtradeResult(waveDataDTO);
WaveDataDTO validWaveDataDTO = waveFileComponent.getValidData(waveDataDTO);
printValidDataResult(validWaveDataDTO);
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();
}
/**
* 打印 getComtrade 的关键摘要,便于快速人工核对。
*/
private static void printComtradeResult(WaveDataDTO waveDataDTO) {
System.out.println("=== getComtrade 结果 ===");
System.out.println("事件时间: " + waveDataDTO.getTime());
System.out.println("相别数: " + waveDataDTO.getIPhasic());
System.out.println("标题数: " + sizeOf(waveDataDTO.getWaveTitle()));
System.out.println("通道数: " + sizeOf(waveDataDTO.getChannelNames()));
System.out.println("波形点数: " + sizeOf(waveDataDTO.getListWaveData()));
if (waveDataDTO.getComtradeCfgDTO() != null) {
System.out.println("模拟量通道数: " + waveDataDTO.getComtradeCfgDTO().getNAnalogNum());
System.out.println("开关量通道数: " + waveDataDTO.getComtradeCfgDTO().getNDigitalNum());
System.out.println("最终采样率: " + waveDataDTO.getComtradeCfgDTO().getFinalSampleRate());
}
if (waveDataDTO.getWaveTitle() != null && !waveDataDTO.getWaveTitle().isEmpty()) {
System.out.println("波形标题: " + waveDataDTO.getWaveTitle());
}
if (waveDataDTO.getListWaveData() != null && !waveDataDTO.getListWaveData().isEmpty()) {
System.out.println("首行数据: " + waveDataDTO.getListWaveData().get(0));
System.out.println("末行数据: " + waveDataDTO.getListWaveData().get(waveDataDTO.getListWaveData().size() - 1));
}
}
/**
* 打印 getValidData 的关键摘要。
*/
private static void printValidDataResult(WaveDataDTO waveDataDTO) {
System.out.println("=== getValidData 结果 ===");
System.out.println("RMS 点数: " + sizeOf(waveDataDTO.getListRmsData()));
System.out.println("RMS 最小值点数: " + sizeOf(waveDataDTO.getListRmsMinData()));
if (waveDataDTO.getListRmsData() != null && !waveDataDTO.getListRmsData().isEmpty()) {
System.out.println("RMS 首行数据: " + waveDataDTO.getListRmsData().get(0));
System.out.println("RMS 末行数据: " + waveDataDTO.getListRmsData().get(waveDataDTO.getListRmsData().size() - 1));
}
if (waveDataDTO.getListRmsMinData() != null && !waveDataDTO.getListRmsMinData().isEmpty()) {
System.out.println("RMS 最小值数据: " + waveDataDTO.getListRmsMinData());
}
}
/**
* 打印特征值结果摘要。
*/
private static void printEigenvalueResult(List<EigenvalueDTO> eigenvalues) {
System.out.println("=== getEigenvalue 结果 ===");
System.out.println("特征值数量: " + sizeOf(eigenvalues));
if (eigenvalues == null || eigenvalues.isEmpty()) {
return;
}
for (int i = 0; i < eigenvalues.size(); i++) {
EigenvalueDTO eigenvalueDTO = eigenvalues.get(i);
System.out.println("" + (i + 1) + "相: amplitude=" + eigenvalueDTO.getAmplitude()
+ ", residualVoltage=" + eigenvalueDTO.getResidualVoltage()
+ ", ratedVoltage=" + eigenvalueDTO.getRatedVoltage()
+ ", durationTime=" + eigenvalueDTO.getDurationTime());
}
}
/**
* 安全返回列表大小,避免空指针。
*/
private static int sizeOf(List<?> list) {
return list == null ? 0 : list.size();
}
}

View File

@@ -1,22 +0,0 @@
package com.njcn.gather.tool.wave.component;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
/**
* 波形文件解析组件测试。
*/
class WaveFileComponentTest {
@Test
void shouldRoundWaveTimeToThreeDecimals() {
Assertions.assertEquals(1.235F, WaveFileComponent.roundWaveTime(1.2345F));
Assertions.assertEquals(-99.988F, WaveFileComponent.roundWaveTime(-99.9876F));
}
@Test
void shouldRoundWaveAmplitudeToThreeDecimals() {
Assertions.assertEquals(220.123F, WaveFileComponent.roundWaveAmplitude(220.1234F));
Assertions.assertEquals(-12.988F, WaveFileComponent.roundWaveAmplitude(-12.9876F));
}
}

View File

@@ -1,73 +0,0 @@
package com.njcn.gather.tool.wave.component;
import com.njcn.gather.tool.wave.pojo.dto.ComtradeCfgDTO;
import com.njcn.gather.tool.wave.pojo.dto.WaveDataDTO;
import com.njcn.gather.tool.wave.pojo.dto.WaveVectorGroupDTO;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* 波形向量计算组件测试。
*/
class WaveVectorComponentTest {
@Test
void shouldScaleVoltageVectorValuesToKv() {
WaveVectorComponent component = new WaveVectorComponent();
WaveDataDTO waveDataDTO = buildThreePhaseWaveData("U", 8153.209D, 1D);
List<WaveVectorGroupDTO> groups = component.calculateVectors(waveDataDTO);
Assertions.assertEquals("kV", groups.get(0).getUnit());
Assertions.assertEquals(5.7652F, groups.get(0).getVectorSeries().get(0).getPhaseVectors().get(0).getTotalRms());
Assertions.assertEquals(8.1532F, groups.get(0).getVectorSeries().get(0).getPhaseVectors().get(0).getFundamentalAmplitude());
Assertions.assertEquals(5.7652F, groups.get(0).getVectorSeries().get(0).getPhaseVectors().get(0).getFundamentalRms());
Assertions.assertEquals(5.7652F, groups.get(0).getVectorSeries().get(0).getPositiveSequence().getRms());
}
@Test
void shouldKeepCurrentVectorValuesInAmpere() {
WaveVectorComponent component = new WaveVectorComponent();
WaveDataDTO waveDataDTO = buildThreePhaseWaveData("I", 35D, 1D);
List<WaveVectorGroupDTO> groups = component.calculateVectors(waveDataDTO);
Assertions.assertEquals("A", groups.get(0).getUnit());
Assertions.assertEquals(24.7487F, groups.get(0).getVectorSeries().get(0).getPhaseVectors().get(0).getTotalRms());
Assertions.assertEquals(35F, groups.get(0).getVectorSeries().get(0).getPhaseVectors().get(0).getFundamentalAmplitude());
Assertions.assertEquals(24.7487F, groups.get(0).getVectorSeries().get(0).getPhaseVectors().get(0).getFundamentalRms());
}
private WaveDataDTO buildThreePhaseWaveData(String titlePrefix, double amplitude, double ratio) {
int samplePerCycle = 32;
WaveDataDTO waveDataDTO = new WaveDataDTO();
ComtradeCfgDTO cfgDTO = new ComtradeCfgDTO();
cfgDTO.setFinalSampleRate(samplePerCycle);
waveDataDTO.setComtradeCfgDTO(cfgDTO);
waveDataDTO.setIPhasic(3);
waveDataDTO.setPt(ratio);
waveDataDTO.setCt(ratio);
waveDataDTO.setWaveTitle(Arrays.asList("x", titlePrefix + "A", titlePrefix + "B", titlePrefix + "C"));
waveDataDTO.setChannelNames(Arrays.asList("x", titlePrefix));
waveDataDTO.setListWaveData(buildRows(samplePerCycle, amplitude));
return waveDataDTO;
}
private List<List<Float>> buildRows(int samplePerCycle, double amplitude) {
List<List<Float>> rows = new ArrayList<>();
for (int i = 0; i < samplePerCycle; i++) {
double angle = 2D * Math.PI * i / samplePerCycle;
List<Float> row = new ArrayList<>();
row.add((float) i);
row.add((float) (amplitude * Math.sin(angle)));
row.add((float) (amplitude * Math.sin(angle - 2D * Math.PI / 3D)));
row.add((float) (amplitude * Math.sin(angle + 2D * Math.PI / 3D)));
rows.add(row);
}
return rows;
}
}

View File

@@ -1,162 +0,0 @@
package com.njcn.gather.tool.wave.service.impl;
import com.njcn.gather.tool.wave.component.WaveFileComponent;
import com.njcn.gather.tool.wave.component.WaveVectorComponent;
import com.njcn.gather.tool.wave.pojo.dto.AnalogDTO;
import com.njcn.gather.tool.wave.pojo.dto.ComtradeCfgDTO;
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.WaveComtradeResultVO;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* 波形服务测试。
*/
class WaveServiceImplTest {
@Test
void shouldUseCfgRatioWhenCfgContainsValidPtAndCt() throws Exception {
WaveServiceImpl service = new WaveServiceImpl(new WaveFileComponent(), new WaveVectorComponent());
WaveDataDTO waveDataDTO = new WaveDataDTO();
ComtradeCfgDTO cfgDTO = new ComtradeCfgDTO();
cfgDTO.setLstAnalogDTO(Arrays.asList(
buildAnalog("V", 10000F, 100F),
buildAnalog("A", 200F, 1F)
));
waveDataDTO.setComtradeCfgDTO(cfgDTO);
WaveComtradeParseParam param = new WaveComtradeParseParam();
param.setPt(1D);
param.setCt(1D);
Method method = WaveServiceImpl.class.getDeclaredMethod("applyWaveMetadata", WaveDataDTO.class, WaveComtradeParseParam.class);
method.setAccessible(true);
method.invoke(service, waveDataDTO, param);
Assertions.assertEquals(100D, waveDataDTO.getPt());
Assertions.assertEquals(200D, waveDataDTO.getCt());
}
@Test
void shouldFillComtradeSummaryFields() throws Exception {
WaveServiceImpl service = new WaveServiceImpl(new WaveFileComponent(), new WaveVectorComponent());
WaveDataDTO waveDataDTO = new WaveDataDTO();
ComtradeCfgDTO cfgDTO = new ComtradeCfgDTO();
cfgDTO.setNChannelNum(8);
cfgDTO.setNPhasic(3);
cfgDTO.setLstAnalogDTO(Arrays.asList(
buildAnalog("V", 10000F, 100F),
buildAnalog("A", 200F, 1F)
));
waveDataDTO.setComtradeCfgDTO(cfgDTO);
waveDataDTO.setIPhasic(3);
WaveComtradeResultVO result = new WaveComtradeResultVO();
Method method = WaveServiceImpl.class.getDeclaredMethod("fillComtradeSummary", WaveComtradeResultVO.class, WaveDataDTO.class);
method.setAccessible(true);
method.invoke(service, result, waveDataDTO);
Assertions.assertEquals(8, result.getTotalChannels());
Assertions.assertEquals(3, result.getPhaseCount());
Assertions.assertEquals("kV/A", result.getUnit());
}
@Test
void shouldMaskValuesInsideBottomPlatformMiddle() throws Exception {
WaveServiceImpl service = new WaveServiceImpl(new WaveFileComponent(), new WaveVectorComponent());
WaveDataDTO waveDataDTO = new WaveDataDTO();
ComtradeCfgDTO cfgDTO = new ComtradeCfgDTO();
cfgDTO.setFinalSampleRate(1);
waveDataDTO.setComtradeCfgDTO(cfgDTO);
waveDataDTO.setListWaveData(buildRows(new float[][]{
{0F, 100F, 100F, 100F},
{1F, 100F, 100F, 100F},
{2F, 100F, 100F, 100F},
{3F, 100F, 100F, 100F},
{4F, 80F, 100F, 100F},
{5F, 60F, 100F, 100F},
{6F, 60F, 100F, 100F},
{7F, 60F, 100F, 100F},
{8F, 95F, 100F, 100F},
{9F, 100F, 100F, 100F}
}));
waveDataDTO.setListRmsData(buildRows(new float[][]{
{0F, 100F, 100F, 100F},
{1F, 100F, 100F, 100F},
{2F, 100F, 100F, 100F},
{3F, 100F, 100F, 100F},
{4F, 80F, 100F, 100F},
{5F, 60F, 100F, 100F},
{6F, 60F, 100F, 100F},
{7F, 60F, 100F, 100F},
{8F, 95F, 100F, 100F},
{9F, 100F, 100F, 100F}
}));
Method method = WaveServiceImpl.class.getDeclaredMethod("applySimplifiedDisplay", WaveDataDTO.class);
method.setAccessible(true);
method.invoke(service, waveDataDTO);
Assertions.assertEquals(0F, waveDataDTO.getListWaveData().get(0).get(0));
Assertions.assertEquals(100F, waveDataDTO.getListWaveData().get(0).get(1));
Assertions.assertEquals(100F, waveDataDTO.getListWaveData().get(2).get(1));
Assertions.assertEquals(80F, waveDataDTO.getListWaveData().get(4).get(1));
Assertions.assertEquals(60F, waveDataDTO.getListWaveData().get(5).get(1));
Assertions.assertNull(waveDataDTO.getListWaveData().get(6).get(1));
Assertions.assertEquals(60F, waveDataDTO.getListWaveData().get(7).get(1));
Assertions.assertEquals(95F, waveDataDTO.getListWaveData().get(8).get(1));
Assertions.assertEquals(100F, waveDataDTO.getListWaveData().get(9).get(1));
}
@Test
void shouldKeepDataWhenSimplifiedRangeCannotBeDetected() throws Exception {
WaveServiceImpl service = new WaveServiceImpl(new WaveFileComponent(), new WaveVectorComponent());
WaveDataDTO waveDataDTO = new WaveDataDTO();
ComtradeCfgDTO cfgDTO = new ComtradeCfgDTO();
cfgDTO.setFinalSampleRate(1);
waveDataDTO.setComtradeCfgDTO(cfgDTO);
waveDataDTO.setListWaveData(buildRows(new float[][]{
{0F, 100F, 100F, 100F},
{1F, 99F, 100F, 100F},
{2F, 98F, 100F, 100F}
}));
waveDataDTO.setListRmsData(buildRows(new float[][]{
{0F, 100F, 100F, 100F},
{1F, 99F, 100F, 100F},
{2F, 98F, 100F, 100F}
}));
Method method = WaveServiceImpl.class.getDeclaredMethod("applySimplifiedDisplay", WaveDataDTO.class);
method.setAccessible(true);
method.invoke(service, waveDataDTO);
Assertions.assertEquals(99F, waveDataDTO.getListWaveData().get(1).get(1));
Assertions.assertEquals(98F, waveDataDTO.getListRmsData().get(2).get(1));
}
private AnalogDTO buildAnalog(String unit, Float primary, Float secondary) {
AnalogDTO analogDTO = new AnalogDTO();
analogDTO.setSzUnitName(unit);
analogDTO.setFPrimary(primary);
analogDTO.setFSecondary(secondary);
return analogDTO;
}
private List<List<Float>> buildRows(float[][] values) {
List<List<Float>> rows = new ArrayList<>();
for (float[] value : values) {
List<Float> row = new ArrayList<>();
for (float item : value) {
row.add(item);
}
rows.add(row);
}
return rows;
}
}