Merge remote-tracking branch 'origin/main'

This commit is contained in:
2026-04-30 09:02:06 +08:00
16 changed files with 3870 additions and 0 deletions

View File

@@ -0,0 +1,231 @@
package com.njcn.gather.icd.mapping.component;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import com.njcn.gather.icd.mapping.pojo.dto.*;
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;
@Setter
@Getter
@Component
public class IcdToXmlMappingService {
private IndexMappingConfig indexMapping;
@Data
public static class IndexMappingConfig {
private StatIndex mmxu;
private StatIndex mhai;
private StatIndex interHarmonic;
private StatIndex msqi;
private Integer mflkShort;
private Integer mflkLong;
private Integer qvvr;
@Data
public static class StatIndex {
private Integer average;
private Integer maximum;
private Integer minimum;
private Integer percentile95;
}
}
public IndexMappingConfig toIndexMapping(){
IndexMappingConfig indexMapping = new IndexMappingConfig();
return indexMapping;
}
public void setIndexMapping(IndexMappingConfig config) {
this.indexMapping = config;
}
public IndexMappingConfig buildIndexMappingFromSelection(List<IndexSelectionGroupCommand> indexSelectionGroups) {
IndexMappingConfig config = new IndexMappingConfig();
if (indexSelectionGroups == null || indexSelectionGroups.isEmpty()) {
return config;
}
for (IndexSelectionGroupCommand group : indexSelectionGroups) {
String groupKey = group.getGroupKey();
List<IndexBindingCommand> bindings = group.getBindings();
if (bindings == null || bindings.isEmpty()) {
continue;
}
if ("统计数据__DSSTATISTICDATA".equals(groupKey)) {
processStatisticDataGroup(config, bindings);
} else if ("波动闪变__DSFLICKERDATA".equals(groupKey)) {
processFlickerDataGroup(config, bindings);
} else if ("暂态事件__DSEVEQVVR".equals(groupKey)) {
processQvvrGroup(config, bindings);
}
}
return config;
}
private void processStatisticDataGroup(IndexMappingConfig config, List<IndexBindingCommand> bindings) {
for (IndexBindingCommand binding : bindings) {
String dataSetName = binding.getDataSetName();
String label = binding.getLabel();
Integer lnInst = parseLnInst(binding.getLnInst());
if (lnInst == null) {
continue;
}
if ("dsStHarm".equals(dataSetName) ) {
if (config.getMhai() == null) {
config.setMhai(new IndexMappingConfig.StatIndex());
}
mapStatIndex(config.getMhai(), label, lnInst);
} else if ("dsStIHarm".equals(dataSetName)) {
if (config.getInterHarmonic() == null) {
config.setInterHarmonic(new IndexMappingConfig.StatIndex());
}
mapStatIndex(config.getInterHarmonic(), label, lnInst);
} else if ("dsStMMXU".equals(dataSetName)) {
if (config.getMmxu() == null) {
config.setMmxu(new IndexMappingConfig.StatIndex());
}
mapStatIndex(config.getMmxu(), label, lnInst);
}else if ("dsStMSQI".equals(dataSetName)) {
if (config.getMsqi() == null) {
config.setMsqi(new IndexMappingConfig.StatIndex());
}
mapStatIndex(config.getMsqi(), label, lnInst);
} else if ("dsStatisticData".equals(dataSetName)) {
if (label != null && label.contains("间谐波")) {
if (config.getInterHarmonic() == null) {
config.setInterHarmonic(new IndexMappingConfig.StatIndex());
}
mapStatIndex(config.getInterHarmonic(), label, lnInst);
} else {
if (config.getMmxu() == null) {
config.setMmxu(new IndexMappingConfig.StatIndex());
}
mapStatIndex(config.getMmxu(), label, lnInst);
if (config.getMhai() == null) {
config.setMhai(new IndexMappingConfig.StatIndex());
}
mapStatIndex(config.getMhai(), label, lnInst);
if (config.getMsqi() == null) {
config.setMsqi(new IndexMappingConfig.StatIndex());
}
mapStatIndex(config.getMsqi(), label, lnInst);
}
}
}
}
private void processFlickerDataGroup(IndexMappingConfig config, List<IndexBindingCommand> bindings) {
for (IndexBindingCommand binding : bindings) {
String dataSetName = binding.getDataSetName();
String label = binding.getLabel();
Integer lnInst = parseLnInst(binding.getLnInst());
if (lnInst == null) {
continue;
}
if ("dsPST".equals(dataSetName)) {
if (config.getMflkShort() == null) {
config.setMflkShort(0);
}
config.setMflkShort(lnInst);
}
else if ("dsPLT".equals(dataSetName)) {
if (config.getMflkLong() == null) {
config.setMflkLong(0);
}
config.setMflkLong(lnInst);
}else {
if (config.getMflkShort() == null) {
config.setMflkShort(0);
}
config.setMflkShort(lnInst);
if (config.getMflkLong() == null) {
config.setMflkLong(0);
}
config.setMflkLong(lnInst);
}
}
}
private void processQvvrGroup(IndexMappingConfig config, List<IndexBindingCommand> bindings) {
for (IndexBindingCommand binding : bindings) {
String label = binding.getLabel();
Integer lnInst = parseLnInst(binding.getLnInst());
if (lnInst == null) {
continue;
}
if (label != null && label.startsWith("电压变动")) {
config.setQvvr(lnInst);
}
}
}
private void mapStatIndex(IndexMappingConfig.StatIndex statIndex, String label, Integer lnInst) {
if (label == null) {
return;
}
switch (label) {
case "最大值":
statIndex.setMaximum(lnInst);
break;
case "最小值":
statIndex.setMinimum(lnInst);
break;
case "平均值":
statIndex.setAverage(lnInst);
break;
case "95值":
statIndex.setPercentile95(lnInst);
break;
case "间谐波最大值":
statIndex.setMaximum(lnInst);
break;
case "间谐波最小值":
statIndex.setMinimum(lnInst);
break;
case "间谐波平均值":
statIndex.setAverage(lnInst);
break;
case "间谐波95值":
statIndex.setPercentile95(lnInst);
break;
default:
break;
}
}
private Integer parseLnInst(String lnInstStr) {
if (lnInstStr == null || lnInstStr.trim().isEmpty()) {
return null;
}
try {
return Integer.parseInt(lnInstStr.trim());
} catch (NumberFormatException e) {
return null;
}
}
}

View File

@@ -0,0 +1,78 @@
package com.njcn.gather.icd.mapping.component;
import com.njcn.gather.icd.mapping.pojo.dto.IcdToXmlGenerateCommand;
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.param.IcdToXmlGenerateRequest;
import com.njcn.gather.icd.mapping.pojo.param.IndexBindingRequest;
import com.njcn.gather.icd.mapping.pojo.param.IndexSelectionGroupRequest;
import com.njcn.gather.icd.mapping.pojo.param.JsonToXmlRequest;import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@Component
public class IcdToXmlRequestConverter {
public IcdToXmlGenerateCommand toCommand(MultipartFile icdFile, IcdToXmlGenerateRequest request) {
IcdToXmlGenerateCommand command = new IcdToXmlGenerateCommand();
try {
command.setFileBytes(icdFile.getBytes());
} catch (IOException e) {
throw new RuntimeException("读取 ICD 文件失败", e);
}
command.setFileName(icdFile.getOriginalFilename());
command.setVersion(request.getVersion());
command.setAuthor(request.getAuthor());
command.setSaveToDisk(request.isSaveToDisk());
command.setOutputDir(request.getOutputDir());
command.setIndexSelection(toIndexSelectionGroupCommands(request.getIndexSelection()));
return command;
}
/**
* 将 JsonToXmlRequest 转换为 IcdToXmlGenerateCommand。
* 注意:此方法不处理文件,仅转换索引选择结果。
*/
public IcdToXmlGenerateCommand toCommand(JsonToXmlRequest request) {
IcdToXmlGenerateCommand command = new IcdToXmlGenerateCommand();
command.setVersion("1.0");
command.setAuthor("");
command.setSaveToDisk(false);
command.setOutputDir(null);
return command;
}
private List<IndexSelectionGroupCommand> toIndexSelectionGroupCommands(List<IndexSelectionGroupRequest> indexSelection) {
List<IndexSelectionGroupCommand> indexSelectionGroupCommands = new ArrayList<>();
if (indexSelection != null) {
for (IndexSelectionGroupRequest groupRequest : indexSelection) {
IndexSelectionGroupCommand groupCommand = new IndexSelectionGroupCommand();
groupCommand.setGroupKey(groupRequest.getGroupKey());
groupCommand.setGroupDesc(groupRequest.getGroupDesc());
groupCommand.setBindings(toIndexBindingCommands(groupRequest.getBindings()));
indexSelectionGroupCommands.add(groupCommand);
}
}
return indexSelectionGroupCommands;
}
private List<IndexBindingCommand> toIndexBindingCommands(List<IndexBindingRequest> bindings) {
List<IndexBindingCommand> indexBindingCommands = new ArrayList<>();
if (bindings != null) {
for (IndexBindingRequest bindingRequest : bindings) {
IndexBindingCommand bindingCommand = new IndexBindingCommand();
bindingCommand.setReportName(bindingRequest.getReportName());
bindingCommand.setDataSetName(bindingRequest.getDataSetName());
bindingCommand.setLabel(bindingRequest.getLabel());
bindingCommand.setLnInst(bindingRequest.getLnInst());
indexBindingCommands.add(bindingCommand);
}
}
return indexBindingCommands;
}
}

View File

@@ -0,0 +1,62 @@
package com.njcn.gather.icd.mapping.component;
import com.njcn.gather.icd.mapping.pojo.bo.IcdToXmlGenerateResult;
import com.njcn.gather.icd.mapping.pojo.bo.analysis.IndexCandidate;
import com.njcn.gather.icd.mapping.pojo.bo.analysis.IndexCandidateReportItem;
import com.njcn.gather.icd.mapping.pojo.vo.IcdToXmlResponse;
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 org.springframework.stereotype.Component;
@Component
public class IcdToXmlResponseConverter {
public IcdToXmlResponse fromResult(IcdToXmlGenerateResult result) {
IcdToXmlResponse response = new IcdToXmlResponse();
response.setStatus(result.getStatus());
response.setMessage(result.getMessage());
response.setIedName(result.getIedName());
response.setLdInst(result.getLdInst());
response.setSavedPath(result.getSavedPath());
response.getProblems().addAll(result.getProblems());
if (result.getIndexAnalysis() != null && result.getIndexAnalysis().getCandidates() != null) {
for (IndexCandidate candidate : result.getIndexAnalysis().getCandidates()) {
IndexCandidateResponse candidateResponse = new IndexCandidateResponse();
candidateResponse.setGroupKey(candidate.getGroupKey());
candidateResponse.setGroupDesc(candidate.getGroupDesc());
candidateResponse.setReportCount(candidate.getReportCount());
candidateResponse.getTemplateLabels().addAll(candidate.getTemplateLabels());
if (candidate.getReports() != null) {
for (IndexCandidateReportItem item : candidate.getReports()) {
IndexCandidateReportItemResponse itemResponse = new IndexCandidateReportItemResponse();
itemResponse.setReportName(item.getReportName());
itemResponse.setDataSetName(item.getDataSetName());
itemResponse.setReportDesc(item.getReportDesc());
itemResponse.getAvailableLnInstValues().addAll(item.getAvailableLnInstValues());
candidateResponse.getReports().add(itemResponse);
}
}
response.getIndexCandidates().add(candidateResponse);
}
}
if (result.getMappingDocument() != null) {
MappingDocumentResponse doc = new MappingDocumentResponse();
doc.setVersion(result.getMappingDocument().getVersion());
doc.setAuthor(result.getMappingDocument().getAuthor());
doc.setIed(result.getMappingDocument().getIed());
doc.setLd(result.getMappingDocument().getLd());
doc.setReportCount(result.getMappingDocument().getReportMap() == null
? 0 : result.getMappingDocument().getReportMap().size());
doc.setDataSetCount(result.getMappingDocument().getDataSetList() == null
? 0 : result.getMappingDocument().getDataSetList().size());
response.setMappingDocument(doc);
}
return response;
}
}

View File

@@ -0,0 +1,852 @@
package com.njcn.gather.icd.mapping.component;
import com.njcn.gather.icd.mapping.pojo.bo.icd.*;
import com.njcn.gather.icd.mapping.utils.XmlTemplateParser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.njcn.gather.icd.mapping.pojo.dto.*;
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;
@Component
public class RuleBasedXmlMappingService {
@Autowired
private IcdParserService icdParserService;
private static final Pattern VALUE_RULE_PATTERN = Pattern.compile(
"<Value\\s+" +
"name=\"([^\"]+)\"\\s+" +
"desc=\"([^\"]+)\"\\s+" +
"type=\"([^\"]+)\"\\s+" +
"(?:DO=\"([^\"]*)\")?\\s*" +
"(?:DA=\"([^\"]*)\")?\\s*" +
"(?:BaseFlag=\"([^\"]*)\")?\\s*" +
"(?:LimitUp=\"([^\"]*)\")?\\s*" +
"(?:LimitDown=\"([^\"]*)\")?\\s*" +
"(?:Coefficient=\"([^\"]*)\")?\\s*" +
"/>", Pattern.CASE_INSENSITIVE
);
private static final Pattern XML_VALUE_PATTERN = Pattern.compile(
"<Value\\s+" +
"name=\"([^\"]+)\"\\s+" +
"desc=\"([^\"]+)\"\\s+" +
"type=\"([^\"]+)\"\\s+" +
"(?:DO=\"([^\"]*)\")?\\s*" +
"(?:DA=\"([^\"]*)\")?\\s*" +
"(?:BaseFlag=\"([^\"]*)\")?\\s*" +
"(?:LimitUp=\"([^\"]*)\")?\\s*" +
"(?:LimitDown=\"([^\"]*)\")?\\s*" +
"(?:Coefficient=\"([^\"]*)\")?\\s*" +
"/>", Pattern.CASE_INSENSITIVE
);
private static final Pattern DO_INDEX_PATTERN = Pattern.compile("(MMXU|MHAI|MSQI|MFLK|QVVR)(\\d+)");
private static final Pattern DO_NO_INDEX_PATTERN = Pattern.compile("(MMXU|MHAI|MSQI|MFLK|QVVR)\\$");
public InputStream loadDefaultXmlFile( ) throws Exception {
ClassPathResource templateResource = new ClassPathResource("template/JiangSu_Config2.xml");
if (!templateResource.exists()) {
return null;
}
return templateResource.getInputStream();
}
public List<InputStream> loadDefaultRuleFile( ) throws Exception {
ClassPathResource ruleResource = new ClassPathResource("template/默认规则.txt");
if ( !ruleResource.exists()) {
return null;
}
InputStream ruleStream = ruleResource.getInputStream();
List<InputStream> ruleStreams = new ArrayList<>();
ruleStreams.add(ruleStream);
return ruleStreams;
}
public String generateWithRuleFiles(IcdDocument icdDocument,
InputStream templateStream,
List<InputStream> ruleStreams,
IcdToXmlMappingService.IndexMappingConfig indexMapping) throws Exception {
Map<String, List<ValueRule>> mergedRules = mergeAllRules(ruleStreams, indexMapping);
Map<String, IcdMetricInfo> icdMetrics = extractIcdMetrics(icdDocument);
Map<String, ValueRule> applicableRules = findApplicableRules(mergedRules, icdMetrics);
XmlTemplateParser.ParseResult parseResult = new XmlTemplateParser().parse(templateStream);
String xmlContent = parseResult.getTemplateContent();
xmlContent = fillIedInfo(xmlContent, icdDocument);
xmlContent = fillReportControls(xmlContent, icdDocument);
xmlContent = applyRulesToXml(xmlContent, applicableRules);
Path tempPath = Files.createTempFile("rule_mapped_", ".xml");
Files.write(tempPath, xmlContent.getBytes(StandardCharsets.UTF_8));
return tempPath.toString();
}
private Map<String, List<ValueRule>> mergeAllRules(List<InputStream> ruleStreams,
IcdToXmlMappingService.IndexMappingConfig indexMapping) throws Exception {
Map<String, List<ValueRule>> mergedRules = new LinkedHashMap<>();
for (InputStream ruleStream : ruleStreams) {
Map<String, List<ValueRule>> rules = parseRuleFile(ruleStream, indexMapping);
for (Map.Entry<String, List<ValueRule>> entry : rules.entrySet()) {
String key = entry.getKey();
List<ValueRule> ruleList = entry.getValue();
mergedRules.computeIfAbsent(key, k -> new ArrayList<>()).addAll(ruleList);
}
}
return mergedRules;
}
private Map<String, List<ValueRule>> parseRuleFile(InputStream ruleStream,
IcdToXmlMappingService.IndexMappingConfig indexMapping) throws Exception {
Map<String, List<ValueRule>> rules = new HashMap<>();
BufferedReader reader = new BufferedReader(new InputStreamReader(ruleStream, StandardCharsets.UTF_8));
String line;
String currentGroup = "";
while ((line = reader.readLine()) != null) {
line = line.trim();
if (line.startsWith("<!--")) {
if (line.contains("MMXU") || line.contains("基本数据")) {
currentGroup = "MMXU";
} else if (line.contains("间谐波")) {
currentGroup = "INTER_HARMONIC";
} else if (line.contains("MHAI") && line.contains("谐波数据")) {
currentGroup = "MHAI";
} else if (line.contains("MSQI") || line.contains("序分量")) {
currentGroup = "MSQI";
} else if (line.contains("短闪") || line.contains("短时闪变")) {
currentGroup = "MFLK_SHORT";
} else if (line.contains("长闪") || line.contains("长时闪变")) {
currentGroup = "MFLK_LONG";
} else if (line.contains("QVVR") || line.contains("电压变动")) {
currentGroup = "QVVR";
}
continue;
}
if (line.isEmpty() || !line.startsWith("<Value")) {
continue;
}
Matcher matcher = VALUE_RULE_PATTERN.matcher(line);
if (matcher.find()) {
ValueRule rule = new ValueRule();
rule.setName(matcher.group(1));
rule.setDesc(matcher.group(2));
rule.setType(matcher.group(3));
String doPath = matcher.group(4) != null ? matcher.group(4) : "";
String daPath = matcher.group(5) != null ? matcher.group(5) : "";
if (indexMapping != null && !doPath.isEmpty()) {
doPath = applyIndexMapping(doPath, currentGroup, indexMapping, rule.getName(), rule.getDesc());
}
rule.setDoPath(doPath);
rule.setDaPath(daPath);
rule.setBaseFlag(matcher.group(6));
rule.setLimitUp(matcher.group(7));
rule.setLimitDown(matcher.group(8));
rule.setCoefficient(matcher.group(9));
String key = buildRuleKey(rule.getName(), rule.getDesc());
if (!key.isEmpty() && hasValidDoOrDa(rule)) {
rules.computeIfAbsent(key.toLowerCase(), k -> new ArrayList<>()).add(rule);
}
}
}
reader.close();
return rules;
}
private String applyIndexMapping(String doPath, String currentGroup,
IcdToXmlMappingService.IndexMappingConfig indexMapping,
String name, String desc) {
if (indexMapping == null) {
return doPath;
}
Matcher matcherWithIndex = DO_INDEX_PATTERN.matcher(doPath);
if (matcherWithIndex.find()) {
String lnClass = matcherWithIndex.group(1);
int originalIndex = Integer.parseInt(matcherWithIndex.group(2));
int newIndex = determineNewIndex(currentGroup, lnClass, originalIndex, indexMapping, name, desc);
if(name.contains("fluc")){
System.out.println("fluc");
}
if (newIndex >= 0) {
return matcherWithIndex.replaceAll(lnClass + newIndex);
}
return doPath;
}
Matcher matcherNoIndex = DO_NO_INDEX_PATTERN.matcher(doPath);
if (matcherNoIndex.find()) {
String lnClass = matcherNoIndex.group(1);
int newIndex = determineNewIndex(currentGroup, lnClass,0, indexMapping, name, desc);
if (newIndex >= 0) {
return matcherNoIndex.replaceAll(lnClass + newIndex + "\\$");
}
}
return doPath;
}
private int determineNewIndexByName(String currentGroup, String lnClass,
IcdToXmlMappingService.IndexMappingConfig indexMapping,
String name, String desc) {
if (indexMapping == null || name == null) {
return -1;
}
String upperName = name.toUpperCase();
String lowerDesc = (desc != null ? desc : "").toLowerCase();
boolean isMaximum = upperName.startsWith("MAX_");
boolean isMinimum = upperName.startsWith("MIN_");
boolean is95Percentile = upperName.startsWith("G_");
boolean isAverage = !isMaximum && !isMinimum && !is95Percentile;
// 判断是否为间谐波指标:通过desc是否包含"间谐波"
boolean isInterHarmonic = lowerDesc.contains("间谐波");
if ("MMXU".equals(currentGroup) || "MMXU".equals(lnClass)) {
IcdToXmlMappingService.IndexMappingConfig.StatIndex mmxu = indexMapping.getMmxu();
if (mmxu != null) {
if (isAverage && mmxu.getAverage() != null) return mmxu.getAverage();
if (isMaximum && mmxu.getMaximum() != null) return mmxu.getMaximum();
if (isMinimum && mmxu.getMinimum() != null) return mmxu.getMinimum();
if (is95Percentile && mmxu.getPercentile95() != null) return mmxu.getPercentile95();
}
}
if ("INTER_HARMONIC".equals(currentGroup) || isInterHarmonic) {
IcdToXmlMappingService.IndexMappingConfig.StatIndex interHarmonic = indexMapping.getInterHarmonic();
if (interHarmonic != null) {
if (isAverage && interHarmonic.getAverage() != null) return interHarmonic.getAverage();
if (isMaximum && interHarmonic.getMaximum() != null) return interHarmonic.getMaximum();
if (isMinimum && interHarmonic.getMinimum() != null) return interHarmonic.getMinimum();
if (is95Percentile && interHarmonic.getPercentile95() != null) return interHarmonic.getPercentile95();
}
}
if ("MHAI".equals(currentGroup) && !isInterHarmonic) {
IcdToXmlMappingService.IndexMappingConfig.StatIndex mhai = indexMapping.getMhai();
if (mhai != null) {
if (isAverage && mhai.getAverage() != null) return mhai.getAverage();
if (isMaximum && mhai.getMaximum() != null) return mhai.getMaximum();
if (isMinimum && mhai.getMinimum() != null) return mhai.getMinimum();
if (is95Percentile && mhai.getPercentile95() != null) return mhai.getPercentile95();
}
}
if ("MSQI".equals(currentGroup) || "MSQI".equals(lnClass)) {
IcdToXmlMappingService.IndexMappingConfig.StatIndex msqi = indexMapping.getMsqi();
if (msqi != null) {
if (isAverage && msqi.getAverage() != null) return msqi.getAverage();
if (isMaximum && msqi.getMaximum() != null) return msqi.getMaximum();
if (isMinimum && msqi.getMinimum() != null) return msqi.getMinimum();
if (is95Percentile && msqi.getPercentile95() != null) return msqi.getPercentile95();
}
}
if ("MFLK_SHORT".equals(currentGroup) || "MFLK".equals(lnClass)) {
if (indexMapping.getMflkShort() != null) {
return indexMapping.getMflkShort();
}
}
if ("MFLK_LONG".equals(currentGroup)) {
if (indexMapping.getMflkLong() != null) {
return indexMapping.getMflkLong();
}
}
if ("QVVR".equals(currentGroup) || "QVVR".equals(lnClass)) {
if (indexMapping.getQvvr() != null) {
return indexMapping.getQvvr();
}
}
return -1;
}
private int determineNewIndex(String currentGroup, String lnClass, int originalIndex,
IcdToXmlMappingService.IndexMappingConfig indexMapping,
String name, String desc) {
if (indexMapping == null) {
return originalIndex;
}
String lowerName = (name != null ? name : "").toLowerCase();
String lowerDesc = (desc != null ? desc : "").toLowerCase();
boolean isAverage = lowerName.contains("avg") || lowerDesc.contains("平均值");
boolean isMaximum = lowerName.startsWith("max_") || lowerDesc.contains("最大值");
boolean isMinimum = lowerName.startsWith("min_") || lowerDesc.contains("最小值");
boolean is95Percentile = lowerName.startsWith("g_") || lowerDesc.contains("95值");
// 判断是否为间谐波指标
boolean isInterHarmonic = lowerDesc.contains("间谐波");
if ("MMXU".equals(currentGroup) || "MMXU".equals(lnClass)) {
IcdToXmlMappingService.IndexMappingConfig.StatIndex mmxu = indexMapping.getMmxu();
if (mmxu != null) {
if (isAverage && mmxu.getAverage() != null) return mmxu.getAverage();
if (isMaximum && mmxu.getMaximum() != null) return mmxu.getMaximum();
if (isMinimum && mmxu.getMinimum() != null) return mmxu.getMinimum();
if (is95Percentile && mmxu.getPercentile95() != null) return mmxu.getPercentile95();
}
}
if ("INTER_HARMONIC".equals(currentGroup) || isInterHarmonic) {
IcdToXmlMappingService.IndexMappingConfig.StatIndex interHarmonic = indexMapping.getInterHarmonic();
if (interHarmonic != null) {
if (isAverage && interHarmonic.getAverage() != null) return interHarmonic.getAverage();
if (isMaximum && interHarmonic.getMaximum() != null) return interHarmonic.getMaximum();
if (isMinimum && interHarmonic.getMinimum() != null) return interHarmonic.getMinimum();
if (is95Percentile && interHarmonic.getPercentile95() != null) return interHarmonic.getPercentile95();
}
}
if ("MHAI".equals(currentGroup) && !isInterHarmonic) {
IcdToXmlMappingService.IndexMappingConfig.StatIndex mhai = indexMapping.getMhai();
if (mhai != null) {
if (isAverage && mhai.getAverage() != null) return mhai.getAverage();
if (isMaximum && mhai.getMaximum() != null) return mhai.getMaximum();
if (isMinimum && mhai.getMinimum() != null) return mhai.getMinimum();
if (is95Percentile && mhai.getPercentile95() != null) return mhai.getPercentile95();
}
}
if ("MSQI".equals(currentGroup) || "MSQI".equals(lnClass)) {
IcdToXmlMappingService.IndexMappingConfig.StatIndex msqi = indexMapping.getMsqi();
if (msqi != null) {
if (isAverage && msqi.getAverage() != null) return msqi.getAverage();
if (isMaximum && msqi.getMaximum() != null) return msqi.getMaximum();
if (isMinimum && msqi.getMinimum() != null) return msqi.getMinimum();
if (is95Percentile && msqi.getPercentile95() != null) return msqi.getPercentile95();
}
}
if ("MFLK_SHORT".equals(currentGroup)) {
if (indexMapping.getMflkShort() != null) {
return indexMapping.getMflkShort();
}
}
if ("MFLK_LONG".equals(currentGroup)) {
if (indexMapping.getMflkLong() != null) {
return indexMapping.getMflkLong();
}
}
if ("QVVR".equals(currentGroup) || "QVVR".equals(lnClass)) {
if (indexMapping.getQvvr() != null) {
return indexMapping.getQvvr();
}
}
return originalIndex;
}
private Map<String, IcdMetricInfo> extractIcdMetrics(IcdDocument icdDocument) {
Map<String, IcdMetricInfo> metrics = new HashMap<>();
if (icdDocument == null || icdDocument.getLogicalNodes() == null) {
return metrics;
}
for (LnNode lnNode : icdDocument.getLogicalNodes()) {
if (lnNode.getDoiList() == null) continue;
for (DoiNode doiNode : lnNode.getDoiList()) {
String doPath = buildDoPath(lnNode, doiNode);
List<String> daPaths = collectAllDaPaths(doiNode.getChildren(), new ArrayList<>());
for (String daPath : daPaths) {
String fullKey = buildMetricKey(doPath, daPath);
IcdMetricInfo info = new IcdMetricInfo();
info.setLnClass(lnNode.getLnClass());
info.setLnInst(lnNode.getLnInst());
info.setDoName(doiNode.getName());
info.setDoPath(doPath);
info.setDaPath(daPath);
info.setPhase(extractPhase(daPath));
info.setType(extractType(daPath));
metrics.put(fullKey.toLowerCase(), info);
}
}
}
return metrics;
}
private List<String> collectAllDaPaths(List<DoiElementNode> nodes, List<String> currentPath) {
List<String> results = new ArrayList<>();
if (nodes == null) return results;
for (DoiElementNode node : nodes) {
List<String> newPath = new ArrayList<>(currentPath);
if ("SDI".equalsIgnoreCase(node.getKind())) {
if (node.getName() != null && !node.getName().isEmpty()) {
String name = node.getName();
if (name.startsWith("phs")) {
if (name.endsWith("Har")) {
newPath.add("phs*Har");
} else {
newPath.add("phs*");
}
}
else {
newPath.add(name);
}
}
if (node.getChildren() != null && !node.getChildren().isEmpty()) {
results.addAll(collectAllDaPaths(node.getChildren(), newPath));
}
} else if ("DAI".equalsIgnoreCase(node.getKind())) {
if (node.getName() != null && !node.getName().isEmpty()) {
newPath.add(node.getName());
}
if (node.getValues() != null && !node.getValues().isEmpty()) {
for (String val : node.getValues()) {
if (val != null && !val.isEmpty()) {
newPath.add(val);
}
}
}
results.add(String.join("$", newPath));
if (node.getChildren() != null && !node.getChildren().isEmpty()) {
results.addAll(collectAllDaPaths(node.getChildren(), newPath));
}
}
}
return results;
}
private String buildDoPath(LnNode lnNode, DoiNode doiNode) {
StringBuilder sb = new StringBuilder();
if (lnNode.getLnClass() != null) {
sb.append(lnNode.getLnClass());
}
if (lnNode.getLnInst() != null) {
sb.append(lnNode.getLnInst());
}
if (doiNode.getName() != null) {
if (sb.length() > 0) {
sb.append("$");
sb.append("MX");
sb.append("$");
}
sb.append(doiNode.getName());
}
return sb.toString();
}
private String buildMetricKey(String doPath, String daPath) {
return doPath + "|" + daPath;
}
private Map<String, ValueRule> findApplicableRules(Map<String, List<ValueRule>> allRules,
Map<String, IcdMetricInfo> icdMetrics) {
Map<String, ValueRule> applicable = new HashMap<>();
System.out.println("========== 开始匹配规则 ==========");
System.out.println("规则总数: " + allRules.size());
System.out.println("ICD指标总数: " + icdMetrics.size());
int matchedCount = 0;
int unmatchedCount = 0;
for (Map.Entry<String, List<ValueRule>> entry : allRules.entrySet()) {
String ruleKey = entry.getKey();
List<ValueRule> ruleVariants = entry.getValue();
boolean foundInIcd = false;
ValueRule matchedRule = null;
System.out.println("\n正在匹配规则: " + ruleKey + " (共" + ruleVariants.size() + "个候选)");
for (int i = 0; i < ruleVariants.size(); i++) {
ValueRule rule = ruleVariants.get(i);
if (rule.getDoPath() == null || rule.getDoPath().isEmpty()) {
System.out.println(" 候选[" + (i+1) + "] 跳过: DO为空");
continue;
}
String ruleDo = normalizePath(rule.getDoPath());
String ruleDa = normalizePath(rule.getDaPath());
System.out.println(" 尝试候选[" + (i+1) + "]: DO=" + rule.getDoPath() + " DA=" + rule.getDaPath());
for (Map.Entry<String, IcdMetricInfo> icdEntry : icdMetrics.entrySet()) {
String icdKey = icdEntry.getKey();
IcdMetricInfo icdMetric = icdEntry.getValue();
String icdDo = normalizePath(icdMetric.getDoPath());
String icdDa = normalizePath(icdMetric.getDaPath());
boolean doMatch = matchesPattern(ruleDo, icdDo);
boolean daMatch = matchesPattern(ruleDa, icdDa);
if (doMatch && daMatch) {
System.out.println(" ✓ 匹配成功! ICD Key: " + icdKey);
System.out.println(" ICD DO=" + icdMetric.getDoPath() + " DA=" + icdMetric.getDaPath());
foundInIcd = true;
matchedRule = rule;
break;
}
}
if (foundInIcd) {
System.out.println(" >> 规则 " + ruleKey + " 匹配成功,使用候选[" + (i+1) + "]");
break;
}
}
if (foundInIcd && matchedRule != null) {
applicable.put(ruleKey, matchedRule);
matchedCount++;
} else {
unmatchedCount++;
if (unmatchedCount <= 15) {
System.out.println("✗ 未匹配: " + ruleKey + " (共" + ruleVariants.size() + "个候选)");
for (ValueRule rule : ruleVariants) {
System.out.println(" 候选: DO=" + rule.getDoPath() + " DA=" + rule.getDaPath());
}
}
}
}
System.out.println("\n========== 规则匹配结束 ==========");
System.out.println("匹配成功: " + matchedCount + " 条规则");
System.out.println("未匹配: " + unmatchedCount + " 条规则");
return applicable;
}
private String normalizePath(String path) {
if (path == null) return "";
return path.trim().toLowerCase();
}
private String fillIedInfo(String xmlContent, IcdDocument icdDocument) {
if (icdDocument == null) {
return xmlContent;
}
String iedName = icdDocument.getIedName();
String ldPrefix = icdDocument.getLdPrefix();
if (iedName != null && !iedName.isEmpty()) {
// 替换 <IED name="..." .../>
xmlContent = xmlContent.replaceAll(
"<IED\\s+name=\"[^\"]*\"",
"<IED name=\"" + escapeXml(iedName) + "\""
);
}
if (ldPrefix != null && !ldPrefix.isEmpty()) {
// 替换 <LDevice Prefix="..." .../>
xmlContent = xmlContent.replaceAll(
"<LDevice\\s+Prefix=\"[^\"]*\"",
"<LDevice Prefix=\"" + escapeXml(ldPrefix) + "\""
);
}
return xmlContent;
}
private String fillReportControls(String xmlContent, IcdDocument icdDocument) {
if (icdDocument == null || icdDocument.getReportControls() == null || icdDocument.getReportControls().isEmpty()) {
return xmlContent;
}
// 构建 ReportStat 内容
StringBuilder reportStatBuilder = new StringBuilder();
reportStatBuilder.append("\t\t<ReportStat>\n");
for (ReportControlNode rc : icdDocument.getReportControls()) {
if (rc.getName() != null && !rc.getName().isEmpty()) {
String name = rc.getName();
// 只处理特定的 ReportControl 名称
if (name.contains("brcbFluc") ||
name.contains("brcbStHarm") ||
name.contains("brcbStIHarm") ||
name.contains("brcbStMMXU") ||
name.contains("brcbStMSQI") ||
name.contains("brcbPLT")||
name.contains("brcbPST")||
name.contains("brcbFlicker")||
name.contains("brcbStatistic") ){
// 构建 ReportControl 字符串格式:
// LLN0$BR$brcbFlickerData,600,0,0,1,0,0,yes,1,1,1,1,1,0,1,1,1,3,1
String reportControlStr = buildReportControlString(rc);
reportStatBuilder.append("\t\t\t<Report ReportControl=\"")
.append(escapeXml(reportControlStr))
.append("\" />\n");
}
}
}
reportStatBuilder.append("\t\t</ReportStat>");
// 替换原有的 <ReportStat>...</ReportStat> 部分
xmlContent = xmlContent.replaceAll(
"<ReportStat>[\\s\\S]*?</ReportStat>",
reportStatBuilder.toString().replace("$", "\\$").replace("[", "\\[").replace("]", "\\]")
);
return xmlContent;
}
private String buildReportControlString(ReportControlNode rc) {
// 格式LLN0$BR$brcbName,intgPd,dchg,qchg,dupd,period,gi,issuffixed,seqNum,timeStamp,reasonCode,dataSet,dataRef,bufOvfl,entryID,configRef,segmentation,FlickerFlag
StringBuilder sb = new StringBuilder();
// ReportControl 名称(需要根据实际情况调整前缀)
sb.append("LLN0$").append(rc.getBuffered() != null && rc.getBuffered() ? "BR$" : "RP$").append(rc.getName());
// 这里需要根据实际 ICD 中的配置来填充其他字段
// 目前使用默认值,后续可以根据 ReportControlNode 的字段扩展
if(rc.getName().contains("PLT") || rc.getName().contains("Flicker") || rc.getName().contains("PST") || rc.getName().contains("Fluc")){
sb.append(",600"); // intgPd - 上送周期
}else{
sb.append(",60"); // intgPd - 上送周期
}
sb.append(",1"); // dchg - 数据变化触发
sb.append(",0"); // qchg - 品质变化触发
sb.append(",0"); // dupd - 数据更新触发
sb.append(",0"); // period - 周期触发
sb.append(",0"); // gi - 总召唤
sb.append(",yes"); // issuffixed
sb.append(",1"); // seqNum
sb.append(",1"); // timeStamp
sb.append(",1"); // reasonCode
sb.append(",1"); // dataSet
sb.append(",1"); // dataRef
sb.append(",0"); // bufOvfl
sb.append(",1"); // entryID
sb.append(",1"); // configRef
sb.append(",1"); // segmentation
sb.append(",3"); // 保留字段
if(rc.getName().contains("PLT") || rc.getName().contains("Flicker")){
sb.append(",1"); // FlickerFlag
}else if(rc.getName().contains("PST") || rc.getName().contains("Fluc")){
sb.append(",2"); // FlickerFlag
} else {
sb.append(",0"); // FlickerFlag
}
return sb.toString();
}
private boolean matchesPattern(String pattern, String actual) {
if (pattern == null || actual == null) {
return pattern == null && actual == null;
}
String normalizedPattern = pattern.replaceAll("\\[[^\\]]*\\]", "");
if (normalizedPattern.isEmpty() || actual.isEmpty()) {
return normalizedPattern.isEmpty() && actual.isEmpty();
}
return actual.equals(normalizedPattern);
}
private String applyRulesToXml(String xmlContent, Map<String, ValueRule> applicableRules) {
Matcher matcher = XML_VALUE_PATTERN.matcher(xmlContent);
StringBuffer result = new StringBuffer();
while (matcher.find()) {
String name = matcher.group(1);
String desc = matcher.group(2);
String type = matcher.group(3);
String key = buildRuleKey(name, desc);
ValueRule matchedRule = applicableRules.get(key.toLowerCase());
if (matchedRule != null) {
StringBuilder valueNode = new StringBuilder("<Value ");
valueNode.append("name=\"").append(escapeXml(name)).append("\" ");
valueNode.append("desc=\"").append(escapeXml(desc)).append("\" ");
valueNode.append("type=\"").append(type).append("\" ");
if ((matchedRule.getDoPath() != null) && !matchedRule.getDoPath().isEmpty()) {
valueNode.append("DO=\"").append(escapeXml(matchedRule.getDoPath())).append("\" ");
}
if (matchedRule.getDaPath() != null && !matchedRule.getDaPath().isEmpty()) {
valueNode.append("DA=\"").append(escapeXml(matchedRule.getDaPath())).append("\" ");
}
if (matchedRule.getBaseFlag() != null && !matchedRule.getBaseFlag().isEmpty()) {
valueNode.append("BaseFlag=\"").append(escapeXml(matchedRule.getBaseFlag())).append("\" ");
}
if (matchedRule.getLimitUp() != null && !matchedRule.getLimitUp().isEmpty()) {
valueNode.append("LimitUp=\"").append(escapeXml(matchedRule.getLimitUp())).append("\" ");
}
if (matchedRule.getLimitDown() != null && !matchedRule.getLimitDown().isEmpty()) {
valueNode.append("LimitDown=\"").append(escapeXml(matchedRule.getLimitDown())).append("\" ");
}
if (matchedRule.getCoefficient() != null && !matchedRule.getCoefficient().isEmpty()) {
valueNode.append("Coefficient=\"").append(escapeXml(matchedRule.getCoefficient())).append("\" ");
}
valueNode.append("/>");
matcher.appendReplacement(result, Matcher.quoteReplacement(valueNode.toString()));
}
}
matcher.appendTail(result);
return result.toString();
}
private String buildRuleKey(String name, String desc) {
if (name != null && !name.isEmpty()) {
return name;
}
return desc != null ? desc : "";
}
private boolean hasValidDoOrDa(ValueRule rule) {
return (rule.getDoPath() != null && !rule.getDoPath().isEmpty()) ||
(rule.getDaPath() != null && !rule.getDaPath().isEmpty());
}
private String extractPhase(String daPath) {
if (daPath == null) return "";
if (daPath.contains("phsA")) return "A";
if (daPath.contains("phsB")) return "B";
if (daPath.contains("phsC")) return "C";
if (daPath.contains("c1")) return "正序";
if (daPath.contains("c2")) return "负序";
if (daPath.contains("c3")) return "零序";
return "无相别";
}
private String extractType(String daPath) {
if (daPath == null) return "";
if (daPath.contains("mag")) return "mag";
if (daPath.contains("ang")) return "ang";
return "";
}
private String escapeXml(String text) {
if (text == null) return "";
return text.replace("&", "&amp;")
.replace("<", "&lt;")
.replace(">", "&gt;")
.replace("\"", "&quot;")
.replace("'", "&apos;");
}
static class ValueRule {
private String name;
private String desc;
private String type;
private String doPath;
private String daPath;
private String baseFlag;
private String limitUp;
private String limitDown;
private String coefficient;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getDesc() { return desc; }
public void setDesc(String desc) { this.desc = desc; }
public String getType() { return type; }
public void setType(String type) { this.type = type; }
public String getDoPath() { return doPath; }
public void setDoPath(String doPath) { this.doPath = doPath; }
public String getDaPath() { return daPath; }
public void setDaPath(String daPath) { this.daPath = daPath; }
public String getBaseFlag() { return baseFlag; }
public void setBaseFlag(String baseFlag) { this.baseFlag = baseFlag; }
public String getLimitUp() { return limitUp; }
public void setLimitUp(String limitUp) { this.limitUp = limitUp; }
public String getLimitDown() { return limitDown; }
public void setLimitDown(String limitDown) { this.limitDown = limitDown; }
public String getCoefficient() { return coefficient; }
public void setCoefficient(String coefficient) { this.coefficient = coefficient; }
}
static class IcdMetricInfo {
private String lnClass;
private String lnInst;
private String doName;
private String doPath;
private String daPath;
private String phase;
private String type;
public String getLnClass() { return lnClass; }
public void setLnClass(String lnClass) { this.lnClass = lnClass; }
public String getLnInst() { return lnInst; }
public void setLnInst(String lnInst) { this.lnInst = lnInst; }
public String getDoName() { return doName; }
public void setDoName(String doName) { this.doName = doName; }
public String getDoPath() { return doPath; }
public void setDoPath(String doPath) { this.doPath = doPath; }
public String getDaPath() { return daPath; }
public void setDaPath(String daPath) { this.daPath = daPath; }
public String getPhase() { return phase; }
public void setPhase(String phase) { this.phase = phase; }
public String getType() { return type; }
public void setType(String type) { this.type = type; }
}
}

View File

@@ -3,14 +3,22 @@ package com.njcn.gather.icd.mapping.controller;
import com.njcn.common.pojo.annotation.OperateInfo;
import com.njcn.common.pojo.enums.common.LogEnum;
import com.njcn.common.utils.LogUtil;
import com.njcn.gather.icd.mapping.component.IcdToXmlRequestConverter;
import com.njcn.gather.icd.mapping.component.IcdToXmlResponseConverter;
import com.njcn.gather.icd.mapping.component.MappingRequestConverter;
import com.njcn.gather.icd.mapping.component.MappingResponseConverter;
import com.njcn.gather.icd.mapping.pojo.bo.GenerateMappingResult;
import com.njcn.gather.icd.mapping.pojo.bo.IcdToXmlGenerateResult;
import com.njcn.gather.icd.mapping.pojo.dto.GenerateFromIcdCommand;
import com.njcn.gather.icd.mapping.pojo.dto.IcdToXmlGenerateCommand;
import com.njcn.gather.icd.mapping.pojo.param.GenerateMappingFromIcdRequest;
import com.njcn.gather.icd.mapping.pojo.param.IcdToXmlGenerateRequest;
import com.njcn.gather.icd.mapping.pojo.param.JsonToXmlRequest;
import com.njcn.gather.icd.mapping.pojo.param.SubmitIndexSelectionRequest;
import com.njcn.gather.icd.mapping.pojo.vo.IcdToXmlResponse;
import com.njcn.gather.icd.mapping.pojo.vo.MappingTaskResponse;
import com.njcn.gather.icd.mapping.service.MappingTaskService;
import com.njcn.gather.icd.mapping.service.impl.IcdToXmlTaskAppService;
import com.njcn.gather.icd.mapping.utils.DateUtils;
import com.njcn.web.controller.BaseController;
import io.swagger.annotations.Api;
@@ -44,6 +52,15 @@ public class MappingController extends BaseController {
/** 响应转换器,按接口阶段裁剪最小返回字段。 */
private final MappingResponseConverter responseConverter;
/** 请求参数转换器,将接口入参转换为应用层命令。 */
private final IcdToXmlResponseConverter icdResponseConverter;
/** 响应转换器,按接口阶段裁剪最小返回字段。 */
private final IcdToXmlRequestConverter icdRequestConverter;
/** 响应转换器,按接口阶段裁剪最小返回字段。 */
private final IcdToXmlTaskAppService icdToXmlTaskAppService;
/**
* 上传 ICD 文件,返回候选结果和可编辑的 ICD 解析结果。
*/
@@ -89,4 +106,27 @@ public class MappingController extends BaseController {
GenerateMappingResult result = mappingTaskService.getIcdMmsJson(command);
return responseConverter.fromSubmitResult(result);
}
/**
* 直接将 MMS JSON 转换为 XML 文件。
* 适用于已经通过 getIcdMmsJson 获得 JSON 后,单独进行 XML 转换的场景。
*/
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
@ApiOperation("MMS JSON 转 XML")
@PostMapping("/get-xml-from-json")
public IcdToXmlResponse getXmlFromJson(@Validated @RequestPart("request") JsonToXmlRequest request) {
String methodDescribe = getMethodDescribe("getXmlFromJson");
LogUtil.njcnDebug(log, "{},开始将 MMS JSON 转换为 XML", methodDescribe);
// // 将请求参数转换为 Command
// IcdToXmlGenerateCommand command =
// icdRequestConverter.toCommand(request);
//
// 调用服务生成 XML
IcdToXmlGenerateResult result =
icdToXmlTaskAppService.generateXmlFromJson(request.getMappingJson());
return icdResponseConverter.fromResult(result);
}
}

View File

@@ -0,0 +1,38 @@
package com.njcn.gather.icd.mapping.pojo.bo;
import com.njcn.gather.icd.mapping.pojo.bo.analysis.IndexAnalysis;
import com.njcn.gather.icd.mapping.pojo.bo.mapping.MappingDocument;
import com.njcn.gather.icd.mapping.pojo.enums.GenerateStatus;
import lombok.Builder;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
public class IcdToXmlGenerateResult {
private GenerateStatus status;
private String message;
private String iedName;
private String ldInst;
private IndexAnalysis indexAnalysis;
private MappingDocument mappingDocument;
private String savedPath;
private List<String> problems = new ArrayList<String>();
public GenerateStatus getStatus() { return status; }
public void setStatus(GenerateStatus status) { this.status = status; }
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
public String getIedName() { return iedName; }
public void setIedName(String iedName) { this.iedName = iedName; }
public String getLdInst() { return ldInst; }
public void setLdInst(String ldInst) { this.ldInst = ldInst; }
public IndexAnalysis getIndexAnalysis() { return indexAnalysis; }
public void setIndexAnalysis(IndexAnalysis indexAnalysis) { this.indexAnalysis = indexAnalysis; }
public MappingDocument getMappingDocument() { return mappingDocument; }
public void setMappingDocument(MappingDocument mappingDocument) { this.mappingDocument = mappingDocument; }
public String getSavedPath() { return savedPath; }
public void setSavedPath(String savedPath) { this.savedPath = savedPath; }
public List<String> getProblems() { return problems; }
public void setProblems(List<String> problems) { this.problems = problems; }
}

View File

@@ -0,0 +1,91 @@
package com.njcn.gather.icd.mapping.pojo.dto;
import lombok.Builder;
import lombok.Data;
import org.springframework.web.multipart.MultipartFile;
import java.util.ArrayList;
import java.util.List;
public class IcdToXmlGenerateCommand {
/** 原始文件名。 */
private String fileName;
/** ICD 文件字节数组。 */
private byte[] fileBytes;
/** 输出版本号。 */
private String version;
/** 作者。 */
private String author;
/** 是否保存到磁盘。 */
private boolean saveToDisk;
/** 输出目录。 */
private String outputDir;
/** 用户上送的索引选择结果。 */
private List<IndexSelectionGroupCommand> indexSelection = new ArrayList<IndexSelectionGroupCommand>();
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public byte[] getFileBytes() {
return fileBytes;
}
public void setFileBytes(byte[] fileBytes) {
this.fileBytes = fileBytes;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public boolean isSaveToDisk() {
return saveToDisk;
}
public void setSaveToDisk(boolean saveToDisk) {
this.saveToDisk = saveToDisk;
}
public String getOutputDir() {
return outputDir;
}
public void setOutputDir(String outputDir) {
this.outputDir = outputDir;
}
public List<IndexSelectionGroupCommand> getIndexSelection() {
return indexSelection;
}
public void setIndexSelection(List<IndexSelectionGroupCommand> indexSelection) {
this.indexSelection = indexSelection;
}
}

View File

@@ -0,0 +1,76 @@
package com.njcn.gather.icd.mapping.pojo.param;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import java.util.ArrayList;
import java.util.List;
public class IcdToXmlGenerateRequest {
/** 输出版本号。为空时后端默认补 1.0。 */
private String version;
/** 作者。为空时默认空字符串。 */
private String author;
/** 是否保存到磁盘。 */
private boolean saveToDisk;
/** 输出目录。saveToDisk=true 时才会用到。 */
private String outputDir;
/**
* 索引选择结果。
*
* 说明:
* 1. 每一个元素代表一个“业务分组”,例如:实时数据、统计数据、波动闪变。
* 2. 每个业务分组下面又包含多条绑定关系。
* 3. 允许为空;为空时后端返回 NEED_INDEX_SELECTION给前端候选参考项。
*/
private List<IndexSelectionGroupRequest> indexSelection = new ArrayList<IndexSelectionGroupRequest>();
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public boolean isSaveToDisk() {
return saveToDisk;
}
public void setSaveToDisk(boolean saveToDisk) {
this.saveToDisk = saveToDisk;
}
public String getOutputDir() {
return outputDir;
}
public void setOutputDir(String outputDir) {
this.outputDir = outputDir;
}
public List<IndexSelectionGroupRequest> getIndexSelection() {
return indexSelection;
}
public void setIndexSelection(List<IndexSelectionGroupRequest> indexSelection) {
this.indexSelection = indexSelection;
}
}

View File

@@ -0,0 +1,32 @@
package com.njcn.gather.icd.mapping.pojo.param;
import java.util.ArrayList;
import java.util.List;
/**
* JSON 转 XML 请求参数。
*/
public class JsonToXmlRequest {
/**
* MMS 映射 JSON 字符串。
* 由 getIcdMmsJson 接口返回的 mappingJson 字段提供。
*/
private String mappingJson;
/**
* 索引选择结果(用于重建索引映射配置)。
* 如果提供了 indexSelection则会重新构建索引映射
* 如果为空,则使用默认的索引映射配置。
*/
public String getMappingJson() {
return mappingJson;
}
public void setMappingJson(String mappingJson) {
this.mappingJson = mappingJson;
}
}

View File

@@ -0,0 +1,36 @@
package com.njcn.gather.icd.mapping.pojo.vo;
import com.njcn.gather.icd.mapping.pojo.enums.GenerateStatus;
import java.util.ArrayList;
import java.util.List;
// 手动写getter/setter避免Lombok依赖问题
public class IcdToXmlResponse {
private GenerateStatus status;
private String message;
private String iedName;
private String ldInst;
private String savedPath;
private MappingDocumentResponse mappingDocument;
private List<IndexCandidateResponse> indexCandidates = new ArrayList<IndexCandidateResponse>();
private List<String> problems = new ArrayList<String>();
public GenerateStatus getStatus() { return status; }
public void setStatus(GenerateStatus status) { this.status = status; }
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
public String getIedName() { return iedName; }
public void setIedName(String iedName) { this.iedName = iedName; }
public String getLdInst() { return ldInst; }
public void setLdInst(String ldInst) { this.ldInst = ldInst; }
public String getSavedPath() { return savedPath; }
public void setSavedPath(String savedPath) { this.savedPath = savedPath; }
public MappingDocumentResponse getMappingDocument() { return mappingDocument; }
public void setMappingDocument(MappingDocumentResponse mappingDocument) { this.mappingDocument = mappingDocument; }
public List<IndexCandidateResponse> getIndexCandidates() { return indexCandidates; }
public void setIndexCandidates(List<IndexCandidateResponse> indexCandidates) { this.indexCandidates = indexCandidates; }
public List<String> getProblems() { return problems; }
public void setProblems(List<String> problems) { this.problems = problems; }
}

View File

@@ -0,0 +1,182 @@
package com.njcn.gather.icd.mapping.service.impl;
import com.njcn.gather.icd.mapping.component.*;
import com.njcn.gather.icd.mapping.pojo.bo.IcdToXmlGenerateResult;
import com.njcn.gather.icd.mapping.pojo.bo.analysis.IndexAnalysis;
import com.njcn.gather.icd.mapping.pojo.bo.analysis.ValidationResult;
import com.njcn.gather.icd.mapping.pojo.bo.icd.IcdDocument;
import com.njcn.gather.icd.mapping.pojo.bo.mapping.MappingDocument;
import com.njcn.gather.icd.mapping.pojo.bo.template.DefaultTemplate;
import com.njcn.gather.icd.mapping.pojo.dto.IcdToXmlGenerateCommand;
import com.njcn.gather.icd.mapping.pojo.enums.GenerateStatus;
import org.springframework.stereotype.Service;
import java.io.InputStream;
import java.util.List;
@Service
public class IcdToXmlTaskAppService {
private final IcdParserService icdParserService;
private final DefaultTemplateLoader defaultTemplateLoader;
private final IndexAnalysisService indexAnalysisService;
private final IndexValidationService indexValidationService;
private final MappingGenerationService mappingGenerationService;
private final RuleBasedXmlMappingService ruleBasedXmlMappingService;
private final MappingDocumentSerializer mappingDocumentSerializer;
private final FileStorageService fileStorageService;
private final IcdToXmlMappingService icdToXmlMappingService;
private final JsonToXmlConversionService jsonToXmlConversionService;
public IcdToXmlTaskAppService(IcdParserService icdParserService,
DefaultTemplateLoader defaultTemplateLoader,
IndexAnalysisService indexAnalysisService,
IndexValidationService indexValidationService,
MappingGenerationService mappingGenerationService,
RuleBasedXmlMappingService ruleBasedXmlMappingService,
MappingDocumentSerializer mappingDocumentSerializer,
FileStorageService fileStorageService,
IcdToXmlMappingService icdToXmlMappingService,
JsonToXmlConversionService jsonToXmlConversionService) {
this.icdParserService = icdParserService;
this.defaultTemplateLoader = defaultTemplateLoader;
this.indexAnalysisService = indexAnalysisService;
this.indexValidationService = indexValidationService;
this.mappingGenerationService = mappingGenerationService;
this.ruleBasedXmlMappingService = ruleBasedXmlMappingService;
this.mappingDocumentSerializer = mappingDocumentSerializer;
this.fileStorageService = fileStorageService;
this.icdToXmlMappingService = icdToXmlMappingService;
this.jsonToXmlConversionService = jsonToXmlConversionService;
}
public IcdToXmlGenerateResult generateFromIcd(IcdToXmlGenerateCommand command) {
IcdToXmlGenerateResult result = new IcdToXmlGenerateResult();
try {
// ========== 第一步ICD → JSON ==========
// 1. 解析 ICD
IcdDocument icdDocument = icdParserService.parse(command.getFileBytes(), command.getFileName());
result.setIedName(icdDocument.getIedName());
result.setLdInst(icdDocument.getLdInst());
// 2. 加载 DefaultCfg.txt和默认配置
//DefaultTemplate template = defaultTemplateLoader.loadDefaultTemplateToXml();
DefaultTemplate template = defaultTemplateLoader.load();
result.getProblems().addAll(template.verify());
InputStream templateStream = ruleBasedXmlMappingService.loadDefaultXmlFile();
if(templateStream==null){result.getProblems().add("缺少默认xml配置文件");}
List<InputStream> ruleStreams = ruleBasedXmlMappingService.loadDefaultRuleFile();
if(ruleStreams==null){result.getProblems().add("缺少默认规则配置文件");}
// 3. 分析索引候选
IndexAnalysis indexAnalysis = indexAnalysisService.analyze(icdDocument, template);
result.setIndexAnalysis(indexAnalysis);
result.getProblems().addAll(indexAnalysis.getProblems());
// 4. 如果没有提交任何绑定关系,则直接返回待匹配项
if (command.getIndexSelection() == null || command.getIndexSelection().isEmpty()) {
result.setStatus(GenerateStatus.NEED_INDEX_SELECTION);
result.setMessage("索引配置缺失或不合法,请根据候选信息完成标签与数字索引的绑定后重新提交");
return result;
}
// 5. 校验用户提交的绑定关系
ValidationResult validationResult = indexValidationService.validate(indexAnalysis, command.getIndexSelection());
if (!validationResult.isValid()) {
result.setStatus(GenerateStatus.NEED_INDEX_SELECTION);
result.setMessage("索引配置缺失或不合法,请根据候选信息完成标签与数字索引的绑定后重新提交");
result.getProblems().addAll(validationResult.getProblems());
return result;
}
// 6. 生成正式映射结构JSON中间态
MappingDocument mappingDocument = mappingGenerationService.generate(
icdDocument,
template,
indexAnalysis,
command.getIndexSelection(),
command.getVersion(),
command.getAuthor()
);
result.setMappingDocument(mappingDocument);
// 7. 序列化为JSON字符串中间产物
String mappingJson = mappingDocumentSerializer.toPrettyJson(mappingDocument);
// ========== 第二步JSON → XML ==========
// 8. 重新绑定正确索引
IcdToXmlMappingService.IndexMappingConfig mappingConfig = icdToXmlMappingService.buildIndexMappingFromSelection(command.getIndexSelection());
icdToXmlMappingService.setIndexMapping(mappingConfig);
// 9. 从JSON转换为XML
String xmlPath = jsonToXmlConversionService.convertFromJson(
mappingJson,
templateStream,
ruleStreams,
icdToXmlMappingService.getIndexMapping()
);
result.setSavedPath(xmlPath);
result.setStatus(GenerateStatus.SUCCESS);
result.setMessage("映射生成成功");
return result;
} catch (Exception ex) {
result.setStatus(GenerateStatus.FAILED);
result.setMessage("映射生成失败:" + ex.getMessage());
result.getProblems().add(ex.getMessage());
return result;
}
}
/**
* 直接从 JSON 字符串生成 XML 文件。
*
* @param mappingJson MMS 映射 JSON 字符串(由 getIcdMmsJson 接口返回)
* @return XML 生成结果
*/
public IcdToXmlGenerateResult generateXmlFromJson(String mappingJson){
IcdToXmlGenerateResult result = new IcdToXmlGenerateResult();
try {
// 1. 加载模板和规则文件
InputStream templateStream = ruleBasedXmlMappingService.loadDefaultXmlFile();
if (templateStream == null) {
result.setStatus(GenerateStatus.FAILED);
result.getProblems().add("缺少默认xml配置文件");
result.setMessage("XML 生成失败缺少默认xml配置文件");
return result;
}
List<InputStream> ruleStreams = ruleBasedXmlMappingService.loadDefaultRuleFile();
if (ruleStreams == null) {
result.setStatus(GenerateStatus.FAILED);
result.getProblems().add("缺少默认规则配置文件");
result.setMessage("XML 生成失败:缺少默认规则配置文件");
return result;
}
// 3. 从 JSON 转换为 XML
String xmlPath = jsonToXmlConversionService.convertFromJson(
mappingJson,
templateStream,
ruleStreams,
icdToXmlMappingService.getIndexMapping()
);
result.setSavedPath(xmlPath);
result.setStatus(GenerateStatus.SUCCESS);
result.setMessage("XML 生成成功");
return result;
} catch (Exception ex) {
result.setStatus(GenerateStatus.FAILED);
result.setMessage("XML 生成失败:" + ex.getMessage());
result.getProblems().add(ex.getMessage());
return result;
}
}
}

View File

@@ -0,0 +1,42 @@
package com.njcn.gather.icd.mapping.utils;
import org.springframework.stereotype.Component;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
@Component
public class XmlTemplateParser {
/**
* 读取XML模板为原始字符串100%保留所有换行、空格、注释、空属性)
*/
public ParseResult parse(InputStream inputStream) throws Exception {
StringBuilder template = new StringBuilder();
try (BufferedReader br = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
String line;
while ((line = br.readLine()) != null) {
template.append(line).append("\n"); // 保留原始换行
}
}
Map<String, String> baseParams = new HashMap<>();
return new ParseResult(template.toString(), baseParams);
}
public static class ParseResult {
private final String templateContent;
private final Map<String, String> baseParams;
public ParseResult(String templateContent, Map<String, String> baseParams) {
this.templateContent = templateContent;
this.baseParams = baseParams;
}
public String getTemplateContent() { return templateContent; }
public Map<String, String> getBaseParams() { return baseParams; }
}
}

View File

@@ -0,0 +1,723 @@
{
"ReportList": [
{
"desc": "统计数据",
"inst": "01",
"TrgOps": "96",
"Select": "DataStatFileMap",
"DataSetList": [
"dsStatisticData",
"dsStHarm",
"dsStIHarm",
"dsStMMXU",
"dsStMSQI",
],
"LnInstList": [
"最大值",
"最小值",
"平均值",
"95值",
"间谐波最大值",
"间谐波最小值",
"间谐波平均值",
"间谐波95值",
]
},
{
"desc": "波动闪变",
"inst": "01",
"TrgOps": "96",
"Select": "FlickerFileMap",
"DataSetList": [
"dsFlickerData",
"dsPST"
],
"LnInstList": [
"波动闪变值"
]
},
{
"desc": "暂态事件",
"inst": "01",
"TrgOps": "96",
"Select": "QVVR",
"DataSetList": [
"dsEveQVVR"
],
"LnInstList": [
"电压变动A",
"电压变动B",
"电压变动C"
]
}
],
"LnClassList": [
{
"desc": "基本数据",
"nameList": [
"MMXU"
]
},
{
"desc": "序分量值",
"nameList": [
"MSQI"
]
},
{
"desc": "谐波/间谐波数据",
"nameList": [
"MHAI"
]
},
{
"desc": "波动闪变",
"nameList": [
"MFLK"
]
},
{
"desc": "电压变动",
"nameList": [
"QVVR"
]
}
],
"PhaseList": [
{
"desc": "无相别",
"nameList": [
"null"
]
},
{
"desc": "正序",
"nameList": [
"c1"
]
},
{
"desc": "负序",
"nameList": [
"c2"
]
},
{
"desc": "零序",
"nameList": [
"c3"
]
},
{
"desc": "A相",
"nameList": [
"phsA",
"phsAHar"
]
},
{
"desc": "B相",
"nameList": [
"phsB",
"phsBHar"
]
},
{
"desc": "C相",
"nameList": [
"phsC",
"phsCHar"
]
},
{
"desc": "AB线",
"nameList": [
"phsAB",
"phsABHar"
]
},
{
"desc": "BC线",
"nameList": [
"phsBC",
"phsBCHar"
]
},
{
"desc": "CA线",
"nameList": [
"phsCA",
"phsCAHar"
]
}
],
"MultiplierList": [
{
"multiplier": 1,
"nameList": [
"null"
]
},
{
"multiplier": 1000,
"nameList": [
"k"
]
}
],
"UnitList": [
{
"desc": "other",
"nameList": [
"null"
]
},
{
"desc": "v",
"nameList": [
"V"
]
},
{
"desc": "a",
"nameList": [
"A"
]
},
{
"desc": "w",
"nameList": [
"W",
"VAr",
"VA"
]
}
],
"TypeList": [
{
"desc": "值",
"nameList": [
"mag"
]
},
{
"desc": "角度",
"nameList": [
"ang"
]
}
],
"DataObjectsList": [
{
"desc": "非间谐波数据",
"LnInstList": [
"最大值",
"最小值",
"平均值",
"95值"
],
"ObjectList": [
{
"desc": "频率",
"nameList": [
"Hz"
]
},
{
"desc": "线电压总有效值",
"nameList": [
"PPV"
]
},
{
"desc": "相电压总有效值",
"nameList": [
"PhV"
]
},
{
"desc": "电流总有效值",
"nameList": [
"A"
]
},
{
"desc": "有功功率",
"nameList": [
"W"
]
},
{
"desc": "无功功率",
"nameList": [
"VAr"
]
},
{
"desc": "视在功率",
"nameList": [
"VA"
]
},
{
"desc": "功率因数",
"nameList": [
"PF"
]
},
{
"desc": "位移功率因数",
"nameList": [
"DF"
]
},
{
"desc": "三相总有功功率",
"nameList": [
"TotW"
]
},
{
"desc": "三相总无功功率",
"nameList": [
"TotVAr"
]
},
{
"desc": "三相总视在功率",
"nameList": [
"TotVA"
]
},
{
"desc": "三相功率因数",
"nameList": [
"TotPF"
]
},
{
"desc": "三相位移功率因数",
"nameList": [
"TotDF"
]
},
{
"desc": "频率偏差",
"nameList": [
"HzDev"
]
},
{
"desc": "相电压偏差",
"nameList": [
"PhVDev"
]
},
{
"desc": "线电压偏差",
"nameList": [
"PPVDev"
]
},
{
"desc": "正序负序和零序电压",
"nameList": [
"SeqV"
]
},
{
"desc": "正序负序和零序电流",
"nameList": [
"SeqA"
]
},
{
"desc": "电压负序不平衡度",
"nameList": [
"ImbNgV"
]
},
{
"desc": "电流负序不平衡度",
"nameList": [
"ImbNgA"
]
},
{
"desc": "电压零序不平衡度",
"nameList": [
"ImbZroV"
]
},
{
"desc": "电流零序不平衡度",
"nameList": [
"ImbZroA"
]
},
{
"desc": "相电压谐波总畸变率",
"nameList": [
"ThdPhV"
]
},
{
"desc": "相电压总偶次谐波畸变率",
"nameList": [
"ThdEvnPhV"
]
},
{
"desc": "相电压总奇次谐波畸变率",
"nameList": [
"ThdOddPhV"
]
},
{
"desc": "线电压谐波总畸变率",
"nameList": [
"ThdPPV"
]
},
{
"desc": "线电压总偶次谐波畸变率",
"nameList": [
"ThdEvnPPV"
]
},
{
"desc": "线电压总奇次谐波畸变率",
"nameList": [
"ThdOddPPV"
]
},
{
"desc": "相电压谐波含有率序列",
"baseflag": 1,
"basecount": 49,
"nameList": [
"HRPhV",
"HPhVMag"
]
},
{
"desc": "线电压谐波含有率序列",
"baseflag": 1,
"basecount": 49,
"nameList": [
"HRPPV"
]
},
{
"desc": "电流总谐波畸变率",
"nameList": [
"ThdA"
]
},
{
"desc": "电流总偶次谐波畸变率",
"nameList": [
"ThdEvnA"
]
},
{
"desc": "电流总奇次谐波畸变率",
"nameList": [
"ThdOddA"
]
},
{
"desc": "谐波电流有效值序列",
"baseflag": 1,
"basecount": 49,
"nameList": [
"HA",
"HAMag"
]
},
{
"desc": "谐波电压有效值序列",
"baseflag": 1,
"basecount": 49,
"nameList": [
"HPhV"
]
},
{
"desc": "2~50次谐波有功功率序列",
"baseflag": 1,
"basecount": 49,
"nameList": [
"HW"
]
},
{
"desc": "2~50次谐波无功功率序列",
"baseflag": 1,
"basecount": 49,
"nameList": [
"HVAr"
]
},
{
"desc": "2~50次谐波视在功率序列",
"baseflag": 1,
"basecount": 49,
"nameList": [
"HVA"
]
},
{
"desc": "三相总谐波视在功率",
"nameList": [
"TotHVA"
]
},
{
"desc": "三相总谐波无功功率",
"nameList": [
"TotHVAr"
]
},
{
"desc": "三相总谐波有功功率",
"nameList": [
"TotHW"
]
},
{
"desc": "相电压基波有效值",
"baseflag": 2,
"queuecount": 49,
"nameList": [
"HFundPhV",
"FundPhV"
],
"queueList":[
"HPhV"
]
},
{
"desc": "线电压基波有效值",
"baseflag": 2,
"queuecount": 49,
"nameList": [
"HFundPPV"
],
"queueList":[
"HPPV"
]
},
{
"desc": "电流基波有效值",
"baseflag": 2,
"queuecount": 49,
"nameList": [
],
"queueList":[
"HA"
]
},
{
"desc": "基波有功功率",
"baseflag": 2,
"queuecount": 49,
"nameList": [
],
"queueList":[
"HW"
]
},
{
"desc": "基波无功功率",
"baseflag": 2,
"queuecount": 49,
"nameList": [
],
"queueList":[
"HVAr"
]
},
{
"desc": "基波视在功率",
"baseflag": 2,
"queuecount": 49,
"nameList": [
],
"queueList":[
"HVA"
]
}
]
},
{
"desc": "间谐波数据",
"LnInstList": [
"间谐波最大值",
"间谐波最小值",
"间谐波平均值",
"间谐波95值",
"间谐波实时数据"
],
"ObjectList": [
{
"desc": "相电压间谐波含有率序列",
"baseflag": 1,
"basecount": 50,
"nameList": [
"HPhV"
]
},
{
"desc": "线电压间谐波含有率序列",
"baseflag": 1,
"basecount": 50,
"nameList": [
"HRPPV"
]
},
{
"desc": "间谐波电流有效值序列",
"baseflag": 1,
"basecount": 50,
"nameList": [
"HA"
]
},
{
"desc": "间谐波电压有效值序列",
"baseflag": 1,
"basecount": 50,
"nameList": [
"HRPhV"
]
}
]
},
{
"desc": "电压变动",
"LnInstList": [
"电压变动A",
"电压变动B",
"电压变动C",
],
"ObjectList": [
{
"desc": "电压扰动事件启动",
"nameList": [
"VarStr"
]
},
{
"desc": "电压暂降事件启动",
"nameList": [
"DipStr"
]
},
{
"desc": "电压暂升事件启动",
"nameList": [
"SwlStr"
]
},
{
"desc": "电压中断事件启动",
"nameList": [
"IntrStr"
]
},
{
"desc": "电压扰动事件特征幅值",
"nameList": [
"VVa"
]
},
{
"desc": "电压扰动事件持续时间",
"nameList": [
"VVaTm"
]
},
{
"desc": "电压暂降启动定值",
"nameList": [
"DipStrVal"
]
},
{
"desc": "电压暂升启动定值",
"nameList": [
"SwlStrVal"
]
},
{
"desc": "电压中断启动定值",
"nameList": [
"IntrStrVal"
]
}
]
},
{
"desc": "其余数据",
"LnInstList": [
"波动闪变值",
"录波文件"
],
"ObjectList": [
{
"desc": "线电压短时闪变值",
"nameList": [
"PPPst"
]
},
{
"desc": "相电压短时闪变值",
"nameList": [
"PhPst"
]
},
{
"desc": "线电压长时闪变值",
"nameList": [
"PPPlt"
]
},
{
"desc": "相电压长时闪变值",
"nameList": [
"PhPlt"
]
},
{
"desc": "线电压电压变动幅值",
"nameList": [
"PPFluc"
]
},
{
"desc": "相电压电压变动幅值",
"nameList": [
"PhFluc"
]
},
{
"desc": "线电压电压变动频度",
"nameList": [
"PPFlucf"
]
},
{
"desc": "相电压电压变动频度",
"nameList": [
"PhFlucf"
]
}
]
}
]
}

View File

@@ -0,0 +1,248 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--注:#7#代表通配符,用于通配数据类型 采用8421码8-CP95 4-最小值 2-最大值 1-平均值-->
<!-- %2,50%代表通配符,用于通配谐波数据,采用范围编码,第一个数字表示谐波起始号,第二个数字谐波结束号-->
<!-- SEQ=后面的值采用8421码 8-T 4-C 2-B 1-A-->
<!--type类型0-DataType 1-监测点 2-剔除标记 3-发生时刻,毫秒 4-数据链表 5-相位 6-值索引 9-实时SOE事件-->
<JSConfigTemplate version="2023-10-31" author="ww" SelectStat="JiangSu" SelectReal="Kafka Producer" desc="默认">
<!--注:暂态事件解析规则配置 Flag0-不分相 1-分相 如果Flag=0 ABC配置成一样如果Flag=1ABC根据实际配置-->
<WavePhasic Flag="1" A="QVVR0" B="QVVR1" C="QVVR2" />
<!--暂态事件持续事件单位0-毫秒 1--->
<UnitOfTime Unit="0" />
<!--上送值的时间UTC-UTC时间 beijing-北京时间-->
<ValueOfTime Unit="UTC" />
<!--录波文件的时间UTC-UTC时间 beijing-北京时间-->
<ComtradeFile WaveTimeFlag="beijing" />
<IED name="PQMonitor" desc="电能质量监测装置" />
<LDevice Prefix="PQM" desc="监测点" />
<ReportMap>
<!--用于映射需要触发那些报告-->
<!--ReportControl:ID,RCBName,intgPd,dchg,qchg,dupd,period,gi,issuffixed,seqNum,timeStamp,reasonCode,dataSet,dataRef,bufOvfl,entryID,configRef,segmentation,FlickerFlag-->
<!--600/60秒即10分钟/1分钟-->
<!--0,0,0,1,0为周期触发报告-->
<!--1,0,0,0,0为变位触发报告-->
<!--0,0,1,0,0为更新触发报告-->
<ReportStat>
<Report ReportControl="LLN0$BR$brcbFlickerData,600,0,0,1,0,0,yes,1,1,1,1,1,0,1,1,1,3,1" />
<Report ReportControl="LLN0$BR$brcbStatisticData,60,0,0,1,0,0,yes,1,1,1,1,1,0,1,1,1,3,0" />
</ReportStat>
<ReportReal>
<Report ReportControl="LLN0$RP$brcbFlickerData,600,0,0,0,1,0,yes,1,1,1,1,1,0,1,1,1,3,1" />
<Report ReportControl="LLN0$RP$urcbRealData,3,0,0,0,1,0,yes,1,1,1,1,1,0,1,1,1,3,0" />
</ReportReal>
<ReportEvent>
<Report ReportControl="LLN0$BR$brcbQVVR,60,1,0,0,0,0,yes,1,1,1,1,1,0,1,1,1,8,1,0"/>
<Report ReportControl="LLN0$BR$brcbRDRE,60,1,0,0,0,0,yes,1,1,1,1,1,0,1,1,1,8,1,0"/>
<Report ReportControl="LLN0$BR$brcbGGIO,60,1,0,0,0,0,yes,1,1,1,1,1,0,1,1,1,8,1,0"/>
</ReportEvent>
</ReportMap>
<Topic name="HISDATA" desc="历史稳态数据">
<DataType name="DATA_TYPE" value="02" desc="历史短时闪变数据" type="0">
<Monitor name="MONITOR" desc="监测点" type="1">
<Item name="TIME" desc="发生时刻" type="3" />
<Item name="F_S" desc="短时闪变和波动" type="4" >
<Sequence name="SEQ" value="7" desc="相别" type="5" >
<Value name="PST" desc="短时闪变" type="6" DO="" DA="" />
<Value name="FLUC" desc="电压波动幅度" type="6" DO="" DA="" />
<Value name="FLUCF" desc="电压波动频度" type="6" DO="" DA="" />
</Sequence>
</Item>
</Monitor>
</DataType>
<DataType name="DATA_TYPE" value="04" desc="历史长时闪变数据" type="0">
<Monitor name="MONITOR" desc="监测点" type="1">
<Item name="TIME" desc="发生时刻" type="3" />
<Item name="F_L" desc="闪变" type="4" >
<Sequence name="SEQ" value="7" desc="相别" type="5" >
<Value name="PLT" desc="长时闪变" type="6" DO="" DA="" />
</Sequence>
</Item>
</Monitor>
</DataType>
<DataType name="DATA_TYPE" value="03" desc="历史暂态数据" type="0" >
<Monitor name="MONITOR" desc="监测点" type="1" >
<Item name="VOLTAGE" desc="暂态指标" type="4" >
<Sequence name="SEQ" value="7" desc="相别" type="5">
<Value name="MAG" desc="残余电压" type="6" DO="" DA="" />
<Value name="DUR" desc="持续时间" type="6" DO="" DA="" />
<Value name="SEQ" desc="相别" type="6" DO="" DA="" />
<Value name="STARTTIME" desc="开始时间" type="6" DO="" DA="" />
<Value name="ENDTIME" desc="结束时间" type="6" DO="" DA="" />
<Value name="DISKIND" desc="暂降类型" type="6" DO="" DA="" />
<Value name="WAVEFILE" desc="波形文件名称" type="6" DO="" DA="" />
</Sequence>
</Item>
</Monitor>
</DataType>
<DataType name="DATA_TYPE" value="01" desc="历史稳态数据" type="0">
<Monitor name="MONITOR" desc="监测点" type="1">
<Item name="FLAG" value="0" desc="剔除标记" type="2" />
<Item name="TIME" desc="发生时刻" type="3" />
<Item name="V" desc="电压" type="4" >
<!--电压V部分(A-C相)-->
<Sequence name="SEQ" value="7" desc="相别(A-C相)" type="5">
<!--Coefficient 值系数转成kafka的时候乘以系数BaseFlag 基础数据标志 0或没有-非基础数据 1-基础数据LimitUp 数据合理上限,如果包含*%UN字符表示需要乘以电压等级电压如果包含*%U字符表示需要乘以电压等级/1.732电压例如220kV则相电压上限为1.5*220LimitDown 数据合理下限 如果包含*%U字符表示需要乘以电压等级电压-->
<Value name="G_DELTA_V" desc="电压偏差95值" type="6" DO="" DA="" BaseFlag="1" LimitUp="20" LimitDown="-20" Coefficient="1"/>
<Value name="DELTA_V" desc="电压偏差平均值" type="6" DO="" DA="" BaseFlag="1" LimitUp="20" LimitDown="-20" Coefficient="1"/>
<Value name="MAX_DELTA_V" desc="电压偏差最大值" type="6" DO="" DA="" BaseFlag="1" LimitUp="20" LimitDown="-20" Coefficient="1"/>
<Value name="MIN_DELTA_V" desc="电压偏差最小值" type="6" DO="" DA="" BaseFlag="1" LimitUp="20" LimitDown="-20" Coefficient="1"/>
<Value name="G_VRMS" desc="电压有效值95值" type="6" DO="" DA="" BaseFlag="1" LimitUp="0*%U" LimitDown="150*%U" />
<Value name="VRMS" desc="电压有效值平均值" type="6" DO="" DA="" BaseFlag="1" LimitUp="0*%U" LimitDown="150*%U" />
<Value name="MAX_VRMS" desc="电压有效值最大值" type="6" DO="" DA="" BaseFlag="1" LimitUp="0*%U" LimitDown="150*%U" />
<Value name="MIN_VRMS" desc="电压有效值最小值" type="6" DO="" DA="" BaseFlag="1" LimitUp="0*%U" LimitDown="150*%U" />
<Value name="VTHD" desc="电压总谐波畸变率平均值" type="6" DO="" DA="" />
<Value name="MAX_VTHD" desc="电压总谐波畸变率最大值" type="6" DO="" DA="" />
<Value name="MIN_VTHD" desc="电压总谐波畸变率最小值" type="6" DO="" DA="" />
<Value name="MIN_VFUND_ANGLE" desc="基波电压相角最小值" type="6" DO="" DA="" />
<Value name="V1" desc="基波电压有效值平均值" type="6" DO="" DA="" />
<Value name="MAX_V1" desc="基波电压有效值最大值" type="6" DO="" DA="" />
<Value name="MIN_V1" desc="基波电压有效值最小值" type="6" DO="" DA="" />
<Value name="SV_%0,49%" desc="间谐波电压含有率(f25-2475)平均值" type="6" DO="" DA="" />
<Value name="MAX_SV_%0,49%" desc="间谐波电压含有率(f25-2475)最大值" type="6" DO="" DA="" />
<Value name="MIN_SV_%0,49%" desc="间谐波电压含有率(f25-2475)最小值" type="6" DO="" DA="" />
<Value name="V%2,50%" desc="谐波电压含有率(2-50)平均值" type="6" DO="" DA="" />
<Value name="MAX_V%2,50%" desc="谐波电压含有率(2-50)最大值" type="6" DO="" DA="" />
<Value name="MIN_V%2,50%" desc="谐波电压含有率(2-50)最小值" type="6" DO="" DA="" />
<Value name="VA%2,50%" desc="谐波电压相角(2-50)平均值" type="6" DO="" DA="" />
</Sequence>
<Sequence name="SEQ" value="8" desc="相别(T相)" type="5">
<!--电压V部分(T相)-->
<Value name="VNSEQ" desc="负序电压平均值" type="6" DO="" DA="" />
<Value name="MAX_VNSEQ" desc="负序电压最大值" type="6" DO="" DA="" />
<Value name="MIN_VNSEQ" desc="负序电压最小值" type="6" DO="" DA="" />
<Value name="V_UNBAN" desc="负序电压不平衡平均值" type="6" DO="" DA="" />
<Value name="MAX_V_UNBAN" desc="负序电压不平衡最大值" type="6" DO="" DA="" />
<Value name="MIN_V_UNBAN" desc="负序电压不平衡最小值" type="6" DO="" DA="" />
<Value name="VZSEQ" desc="零序电压平均值" type="6" DO="" DA="" />
<Value name="MAX_VZSEQ" desc="零序电压最大值" type="6" DO="" DA="" />
<Value name="MIN_VZSEQ" desc="零序电压最小值" type="6" DO="" DA="" />
<Value name="VZSEQ_UNBAN" desc="零序电压不平衡平均值" type="6" DO="" DA="" />
<Value name="MAX_VZSEQ_UNBAN" desc="零序电压不平衡最大值" type="6" DO="" DA="" />
<Value name="MIN_VZSEQ_UNBAN" desc="零序电压不平衡最小值" type="6" DO="" DA="" />
<Value name="G_FREQ" desc="频率95值" type="6" DO="" DA="" />
<Value name="FREQ" desc="频率平均值" type="6" DO="" DA="" />
<Value name="MAX_FREQ" desc="频率最大值" type="6" DO="" DA="" />
<Value name="MIN_FREQ" desc="频率最小值" type="6" DO="" DA="" />
<Value name="G_DELTA_FREQ" desc="频率偏差95值" type="6" DO="" DA="" />
<Value name="DELTA_FREQ" desc="频率偏差平均值" type="6" DO="" DA="" />
<Value name="MAX_DELTA_FREQ" desc="频率偏差最大值" type="6" DO="" DA="" />
<Value name="MIN_DELTA_FREQ" desc="频率偏差最小值" type="6" DO="" DA="" />
<Value name="VPSEQ" desc="正序电压平均值" type="6" DO="" DA="" />
<Value name="MAX_VPSEQ" desc="正序电压最大值" type="6" DO="" DA="" />
<Value name="MIN_VPSEQ" desc="正序电压最小值" type="6" DO="" DA="" />
</Sequence>
</Item>
<Item name="I" desc="电流" type="4" >
<!--电流I部分(A-C相)-->
<Sequence name="SEQ" value="7" desc="相别(A-C相)" type="5">
<Value name="G_IRMS" desc="电流有效值95值" type="6" DO="" DA="" />
<Value name="IRMS" desc="电流有效值平均值" type="6" DO="" DA="" />
<Value name="MAX_IRMS" desc="电流有效值最大值" type="6" DO="" DA="" />
<Value name="MIN_IRMS" desc="电流有效值最小值" type="6" DO="" DA="" />
<Value name="I1" desc="基波电流有效值平均值" type="6" DO="" DA="" />
<Value name="MAX_I1" desc="基波电流有效值最大值" type="6" DO="" DA="" />
<Value name="MIN_I1" desc="基波电流有效值最小值" type="6" DO="" DA="" />
<Value name="SI_%0,49%" desc="间谐波电流幅值(f25-2475)平均值" type="6" DO="" DA="" />
<Value name="MAX_SI_%0,49%" desc="间谐波电流幅值(f25-2475)最大值" type="6" DO="" DA="" />
<Value name="MIN_SI_%0,49%" desc="间谐波电流幅值(f25-2475)最小值" type="6" DO="" DA="" />
<Value name="I%2,50%" desc="谐波电流幅值(2-50)平均值" type="6" DO="" DA="" />
<Value name="MAX_I%2,50%" desc="谐波电流幅值(2-50)最大值" type="6" DO="" DA="" />
<Value name="MIN_I%2,50%" desc="谐波电流幅值(2-50)最小值" type="6" DO="" DA="" />
</Sequence>
<!--电流I部分(T相)-->
<Sequence name="SEQ" value="8" desc="相别(T相)" type="5">
<Value name="INSEQ" desc="负序电流平均值" type="6" DO="" DA="" />
<Value name="MAX_INSEQ" desc="负序电流最大值" type="6" DO="" DA="" />
<Value name="MIN_INSEQ" desc="负序电流最小值" type="6" DO="" DA="" />
<Value name="I_UNBAN" desc="负序电流不平衡平均值" type="6" DO="" DA="" />
<Value name="MAX_I_UNBAN" desc="负序电流不平衡最大值" type="6" DO="" DA="" />
<Value name="MIN_I_UNBAN" desc="负序电流不平衡最小值" type="6" DO="" DA="" />
<Value name="IZSEQ" desc="零序电流平均值" type="6" DO="" DA="" />
<Value name="MAX_IZSEQ" desc="零序电流最大值" type="6" DO="" DA="" />
<Value name="MIN_IZSEQ" desc="零序电流最小值" type="6" DO="" DA="" />
<Value name="IZSEQ_UNBAN" desc="零序电流不平衡平均值" type="6" DO="" DA="" />
<Value name="MAX_IZSEQ_UNBAN" desc="零序电流不平衡最大值" type="6" DO="" DA="" />
<Value name="MIN_IZSEQ_UNBAN" desc="零序电流不平衡最小值" type="6" DO="" DA="" />
<Value name="IPSEQ" desc="正序电流平均值" type="6" DO="" DA="" />
<Value name="MAX_IPSEQ" desc="正序电流最大值" type="6" DO="" DA="" />
<Value name="MIN_IPSEQ" desc="正序电流最小值" type="6" DO="" DA="" />
</Sequence>
</Item>
<Item name="PQ" desc="功率" type="4" >
<!--功率PQ部分(A-C相)-->
<Sequence name="SEQ" value="7" desc="相别(A-C相)" type="5">
<Value name="PF" desc="功率因数平均值" type="6" DO="" DA="" />
<Value name="MAX_PF" desc="功率因数最大值" type="6" DO="" DA="" />
<Value name="MIN_PF" desc="功率因数最小值" type="6" DO="" DA="" />
<Value name="DF" desc="基波功率因数平均值" type="6" DO="" DA="" />
<Value name="MAX_DF" desc="基波功率因数最大值" type="6" DO="" DA="" />
<Value name="P_FUND" desc="基波有功功率平均值" type="6" DO="" DA="" />
<Value name="MAX_P_FUND" desc="基波有功功率最大值" type="6" DO="" DA="" />
<Value name="MIN_P_FUND" desc="基波有功功率最小值" type="6" DO="" DA="" />
<Value name="G_S" desc="视在功率95值" type="6" DO="" DA="" />
<Value name="S" desc="视在功率平均值" type="6" DO="" DA="" />
<Value name="MAX_S" desc="视在功率最大值" type="6" DO="" DA="" />
<Value name="MIN_S" desc="视在功率最小值" type="6" DO="" DA="" />
<Value name="G_Q" desc="无功功率95值" type="6" DO="" DA="" />
<Value name="Q" desc="无功功率平均值" type="6" DO="" DA="" />
<Value name="MAX_Q" desc="无功功率最大值" type="6" DO="" DA="" />
<Value name="MIN_Q" desc="无功功率最小值" type="6" DO="" DA="" />
<Value name="P%2,50%" desc="谐波有功功率(2-50)平均值" type="6" DO="" DA="" />
<Value name="MAX_P%2,50%" desc="谐波有功功率(2-50)最大值" type="6" DO="" DA="" />
<Value name="MIN_P%2,50%" desc="谐波有功功率(2-50)最小值" type="6" DO="" DA="" />
<Value name="G_P" desc="有功功率95值" type="6" DO="" DA="" />
<Value name="P" desc="有功功率平均值" type="6" DO="" DA="" />
<Value name="MAX_P" desc="有功功率最大值" type="6" DO="" DA="" />
<Value name="MIN_P" desc="有功功率最小值" type="6" DO="" DA="" />
</Sequence>
<!--功率PQ部分(T相)-->
<Sequence name="SEQ" value="8" desc="相别(T相)" type="5">
<Value name="PF" desc="总功率因数平均值" type="6" DO="" DA="" />
<Value name="MAX_PF" desc="总功率因数最大值" type="6" DO="" DA="" />
<Value name="MIN_PF" desc="总功率因数最小值" type="6" DO="" DA="" />
<Value name="DF" desc="总基波功率因数平均值" type="6" DO="" DA="" />
<Value name="MAX_DF" desc="总基波功率因数最大值" type="6" DO="" DA="" />
<Value name="MIN_DF" desc="总基波功率因数最小值" type="6" DO="" DA="" />
<Value name="S" desc="总视在功率平均值" type="6" DO="" DA="" />
<Value name="MAX_S" desc="总视在功率最大值" type="6" DO="" DA="" />
<Value name="MIN_S" desc="总视在功率最小值" type="6" DO="" DA="" />
<Value name="Q" desc="总无功功率平均值" type="6" DO="" DA="" />
<Value name="MAX_Q" desc="总无功功率最大值" type="6" DO="" DA="" />
<Value name="MIN_Q" desc="总无功功率最小值" type="6" DO="" DA="" />
<Value name="P" desc="总有功功率平均值" type="6" DO="" DA="" />
<Value name="MAX_P" desc="总有功功率最大值" type="6" DO="" DA="" />
<Value name="MIN_P" desc="总有功功率最小值" type="6" DO="" DA="" />
</Sequence>
</Item>
</Monitor>
</DataType>
</Topic>
<Topic name="SOEDATA" desc="告警SOE">
<SOE name="ThdVVal" desc="电压总畸变率越限告警" type="9" DO="" DA="" />
<SOE name="ThdAVal" desc="电流总畸变率越限告警" type="9" DO="" DA="" />
<SOE name="HToddVVal" desc="奇次谐波电压含有率越限告警" type="9" DO="" DA="" />
<SOE name="HTeddVVal" desc="偶次谐波电压含有率越限告警" type="9" DO="" DA="" />
<SOE name="H2AVal" desc="2次谐波电流越限告警" type="9" DO="" DA="" />
<SOE name="H3AVal" desc="3次谐波电流越限告警" type="9" DO="" DA="" />
<SOE name="H4AVal" desc="4次谐波电流越限告警" type="9" DO="" DA="" />
<SOE name="H5AVal" desc="5次谐波电流越限告警" type="9" DO="" DA="" />
<SOE name="H7AVal" desc="7次谐波电流越限告警" type="9" DO="" DA="" />
<SOE name="H9AVal" desc="9次谐波电流越限告警" type="9" DO="" DA="" />
<SOE name="H11AVal" desc="11次谐波电流越限告警" type="9" DO="" DA="" />
<SOE name="H13AVal" desc="13次谐波电流越限告警" type="9" DO="" DA="" />
<SOE name="ImbNgVFVal" desc="电压负序不平衡度越限告警" type="9" DO="" DA="" />
<SOE name="ImbNgAFVal" desc="电流负序不平衡度越限告警" type="9" DO="" DA="" />
<SOE name="OvHzStrVal" desc="频率高越限告警" type="9" DO="" DA="" />
<SOE name="UnHzStrVal" desc="频率低越限告警" type="9" DO="" DA="" />
<SOE name="LRVInterrup" desc="长期电压中断告警" type="9" DO="" DA="" />
<SOE name="LRVSwell" desc="电压上偏差越限告警" type="9" DO="" DA="" />
<SOE name="LRVSag" desc="电压下偏差越限告警" type="9" DO="" DA="" />
<SOE name="PhPstVal" desc="短时闪变越限告警" type="9" DO="" DA="" />
<SOE name="PhPltVal" desc="长时闪变越限告警" type="9" DO="" DA="" />
<SOE name="PhyStateFault" desc="终端运行状态:故障" type="9" DO="" DA="" />
<SOE name="PhyStateRun" desc="终端运行状态:运行" type="9" DO="" DA="" />
<SOE name="PwrUp" desc="终端上电" type="9" DO="" DA="" />
<SOE name="PwrDn" desc="终端掉电" type="9" DO="" DA="" />
<SOE name="CommInterrupt" desc="终端通信中断" type="9" DO="" DA="" />
<SOE name="CommResume" desc="终端通信恢复" type="9" DO="" DA="" />
</Topic>
</JSConfigTemplate>

View File

@@ -0,0 +1,127 @@
<Value name="相电压短时闪变值波动闪变值值" desc="短时闪变" type="6" DO="MFLK1$MX$PhPst" DA="phs*$cVal$mag$f" />
<Value name="相电压电压变动幅值波动闪变值值" desc="电压波动幅值" type="6" DO="MMXU2$MX$FLUC" DA="phs*$cVal$mag$f" />
<Value name="相电压电压变动频度波动闪变值值" desc="电压波动频度" type="6" DO="MMXU2$MX$FLUCCF" DA="phs*$cVal$mag$f" />
<Value name="相电压长时闪变值波动闪变值值" desc="长时闪变" type="6" DO="MFLK2$MX$PhPlt" DA="phs*$cVal$mag$f" />
<Value name="电压扰动事件特征幅值值" desc="残余电压" type="6" DO="QVVR0$MX$VVa" DA="mag$f" />
<Value name="电压扰动事件持续时间" desc="持续时间" type="6" DO="QVVR0$MX$VVaTm" DA="mag$f" />
<Value name="SEQ" desc="相别" type="6" DO="" DA="" />
<Value name="STARTTIME" desc="开始时间" type="6" DO="" DA="" />
<Value name="ENDTIME" desc="结束时间" type="6" DO="" DA="" />
<Value name="DISKIND" desc="暂降类型" type="6" DO="" DA="" />
<Value name="WAVEFILE" desc="波形文件名称" type="6" DO="" DA="" />
<Value name="相电压偏差95值值" desc="电压偏差95值" type="6" DO="MMXU5$MX$VolDev" DA="phs*$cVal$mag$f" BaseFlag="1" LimitUp="20" LimitDown="-20" Coefficient="1"/>
<Value name="相电压偏差平均值值" desc="电压偏差平均值" type="6" DO="MMXU2$MX$VolDev" DA="phs*$cVal$mag$f" BaseFlag="1" LimitUp="20" LimitDown="-20" Coefficient="1"/>
<Value name="相电压偏差最大值值" desc="电压偏差最大值" type="6" DO="MMXU3$MX$VolDev" DA="phs*$cVal$mag$f" BaseFlag="1" LimitUp="20" LimitDown="-20" Coefficient="1"/>
<Value name="相电压偏差最小值值" desc="电压偏差最小值" type="6" DO="MMXU4$MX$VolDev" DA="phs*$cVal$mag$f" BaseFlag="1" LimitUp="20" LimitDown="-20" Coefficient="1"/>
<Value name="相电压总有效值95值值" desc="电压有效值95值" type="6" DO="MMXU5$MX$PhV" DA="phs*$cVal$mag$f" BaseFlag="1" LimitUp="0*%U" LimitDown="150*%U" />
<Value name="相电压总有效值平均值值" desc="电压有效值平均值" type="6" DO="MMXU2$MX$PhV" DA="phs*$cVal$mag$f" BaseFlag="1" LimitUp="0*%U" LimitDown="150*%U" />
<Value name="相电压总有效值最大值值" desc="电压有效值最大值" type="6" DO="MMXU3$MX$PhV" DA="phs*$cVal$mag$f" BaseFlag="1" LimitUp="0*%U" LimitDown="150*%U" />
<Value name="相电压总有效值最小值值" desc="电压有效值最小值" type="6" DO="MMXU4$MX$PhV" DA="phs*$cVal$mag$f" BaseFlag="1" LimitUp="0*%U" LimitDown="150*%U" />
<Value name="相电压谐波总畸变率平均值值" desc="电压总谐波畸变率平均值" type="6" DO="MHAI2$MX$ThdPhV" DA="phs*$cVal$mag$f" />
<Value name="相电压谐波总畸变率最大值值" desc="电压总谐波畸变率最大值" type="6" DO="MHAI3$MX$ThdPhV" DA="phs*$cVal$mag$f" />
<Value name="相电压谐波总畸变率最小值值" desc="电压总谐波畸变率最小值" type="6" DO="MHAI4$MX$ThdPhV" DA="phs*$cVal$mag$f" />
<Value name="MIN_VFUND_ANGLE" desc="基波电压相角最小值" type="6" DO="MHAI4$MX$FundPhVAng" DA="phs*$cVal$ang$f" />
<Value name="相电压基波有效值平均值值" desc="基波电压有效值平均值" type="6" DO="MHAI2$MX$FundPhV" DA="phs*$cVal$mag$f" />
<Value name="相电压基波有效值最大值值" desc="基波电压有效值最大值" type="6" DO="MHAI3$MX$FundPhV" DA="phs*$cVal$mag$f" />
<Value name="相电压基波有效值最小值值" desc="基波电压有效值最小值" type="6" DO="MHAI4$MX$FundPhV" DA="phs*$cVal$mag$f" />
<Value name="相电压间谐波含有率序列间谐波平均值值" desc="间谐波电压含有率(f25-2475)平均值" type="6" DO="MHAI8$MX$HPhV" DA="phs*Har[%-0]$mag$f" />
<Value name="相电压间谐波含有率序列间谐波最大值值" desc="间谐波电压含有率(f25-2475)最大值" type="6" DO="MHAI9$MX$HPhV" DA="phs*Har[%-0]$mag$f" />
<Value name="相电压间谐波含有率序列间谐波最小值值" desc="间谐波电压含有率(f25-2475)最小值" type="6" DO="MHAI10$MX$HPhV" DA="phs*Har[%-0]$mag$f" />
<Value name="间谐波电压有效值序列间谐波平均值值" desc="间谐波电压含有率(f25-2475)平均值" type="6" DO="MHAI8$MX$HPhV" DA="phs*Har[%-0]$mag$f" />
<Value name="间谐波电压有效值序列间谐波最大值值" desc="间谐波电压含有率(f25-2475)最大值" type="6" DO="MHAI9$MX$HPhV" DA="phs*Har[%-0]$mag$f" />
<Value name="间谐波电压有效值序列间谐波最小值值" desc="间谐波电压含有率(f25-2475)最小值" type="6" DO="MHAI10$MX$HPhV" DA="phs*Har[%-0]$mag$f" />
<Value name="相电压谐波含有率序列平均值值" desc="谐波电压含有率(2-50)平均值" type="6" DO="MHAI2$MX$HPhV" DA="phs*Har[%-2]$mag$f" />
<Value name="相电压谐波含有率序列最大值值" desc="谐波电压含有率(2-50)最大值" type="6" DO="MHAI3$MX$HPhV" DA="phs*Har[%-2]$mag$f" />
<Value name="相电压谐波含有率序列最小值值" desc="谐波电压含有率(2-50)最小值" type="6" DO="MHAI4$MX$HPhV" DA="phs*Har[%-2]$mag$f" />
<Value name="谐波电压有效值序列平均值角度" desc="谐波电压相角(2-50)平均值" type="6" DO="MHAI2$MX$HPhV" DA="phs*Har[%-2]$ang$f" />
<Value name="正序负序和零序电压平均值负序值" desc="负序电压平均值" type="6" DO="MSQI2$MX$SeqV" DA="c2$cVal$mag$f" />
<Value name="正序负序和零序电压最大值负序值" desc="负序电压最大值" type="6" DO="MSQI3$MX$SeqV" DA="c2$cVal$mag$f" />
<Value name="正序负序和零序电压最小值负序值" desc="负序电压最小值" type="6" DO="MSQI4$MX$SeqV" DA="c2$cVal$mag$f" />
<Value name="电压负序不平衡度平均值值" desc="负序电压不平衡平均值" type="6" DO="MSQI2$MX$ImbNgV" DA="mag$f" />
<Value name="电压负序不平衡度最大值值" desc="负序电压不平衡最大值" type="6" DO="MSQI3$MX$ImbNgV" DA="mag$f" />
<Value name="电压负序不平衡度最小值值" desc="负序电压不平衡最小值" type="6" DO="MSQI4$MX$ImbNgV" DA="mag$f" />
<Value name="正序负序和零序电压平均值零序值" desc="零序电压平均值" type="6" DO="MSQI2$MX$SeqV" DA="c3$cVal$mag$f" />
<Value name="正序负序和零序电压最大值零序值" desc="零序电压最大值" type="6" DO="MSQI3$MX$SeqV" DA="c3$cVal$mag$f" />
<Value name="正序负序和零序电压最小值零序值" desc="零序电压最小值" type="6" DO="MSQI4$MX$SeqV" DA="c3$cVal$mag$f" />
<Value name="电压零序不平衡度平均值值" desc="零序电压不平衡平均值" type="6" DO="MSQI2$MX$ImbZroV" DA="mag$f" />
<Value name="电压零序不平衡度最大值值" desc="零序电压不平衡最大值" type="6" DO="MSQI3$MX$ImbZroV" DA="mag$f" />
<Value name="电压零序不平衡度最小值值" desc="零序电压不平衡最小值" type="6" DO="MSQI4$MX$ImbZroV" DA="mag$f" />
<Value name="频率95值值" desc="频率95值" type="6" DO="MMXU5$MX$Hz" DA="mag$f" />
<Value name="频率平均值值" desc="频率平均值" type="6" DO="MMXU2$MX$Hz" DA="mag$f" />
<Value name="频率最大值值" desc="频率最大值" type="6" DO="MMXU3$MX$Hz" DA="mag$f" />
<Value name="频率最小值值" desc="频率最小值" type="6" DO="MMXU4$MX$Hz" DA="mag$f" />
<Value name="频率偏差95值值" desc="频率偏差95值" type="6" DO="MMXU5$MX$FreqDev" DA="mag$f" />
<Value name="频率偏差平均值值" desc="频率偏差平均值" type="6" DO="MMXU2$MX$FreqDev" DA="mag$f" />
<Value name="频率偏差最大值值" desc="频率偏差最大值" type="6" DO="MMXU3$MX$FreqDev" DA="mag$f" />
<Value name="频率偏差最小值值" desc="频率偏差最小值" type="6" DO="MMXU4$MX$FreqDev" DA="mag$f" />
<Value name="正序负序和零序电压平均值正序值" desc="正序电压平均值" type="6" DO="MSQI2$MX$SeqV" DA="c1$cVal$mag$f" />
<Value name="正序负序和零序电压最大值正序值" desc="正序电压最大值" type="6" DO="MSQI3$MX$SeqV" DA="c1$cVal$mag$f" />
<Value name="正序负序和零序电压最小值正序值" desc="正序电压最小值" type="6" DO="MSQI4$MX$SeqV" DA="c1$cVal$mag$f" />
<Value name="电流总有效值95值值" desc="电流有效值95值" type="6" DO="MMXU5$MX$A" DA="phs*$cVal$mag$f" />
<Value name="电流总有效值平均值值" desc="电流有效值平均值" type="6" DO="MMXU2$MX$A" DA="phs*$cVal$mag$f" />
<Value name="电流总有效值最大值值" desc="电流有效值最大值" type="6" DO="MMXU3$MX$A" DA="phs*$cVal$mag$f" />
<Value name="电流总有效值最小值值" desc="电流有效值最小值" type="6" DO="MMXU4$MX$A" DA="phs*$cVal$mag$f" />
<Value name="电流基波有效值平均值值" desc="基波电流有效值平均值" type="6" DO="MHAI2$MX$HA" DA="phs*Har[1]$mag$f" />
<Value name="电流基波有效值最大值值" desc="基波电流有效值最大值" type="6" DO="MHAI3$MX$HA" DA="phs*Har[1]$mag$f" />
<Value name="电流基波有效值最小值值" desc="基波电流有效值最小值" type="6" DO="MHAI4$MX$HA" DA="phs*Har[1]$mag$f" />
<Value name="间谐波电流有效值序列间谐波平均值值" desc="间谐波电流幅值(f25-2475)平均值" type="6" DO="MHAI8$MX$HA" DA="phs*Har[%-0]$mag$f" />
<Value name="间谐波电流有效值序列间谐波最大值值" desc="间谐波电流幅值(f25-2475)最大值" type="6" DO="MHAI9$MX$HA" DA="phs*Har[%-0]$mag$f" />
<Value name="间谐波电流有效值序列间谐波最小值值" desc="间谐波电流幅值(f25-2475)最小值" type="6" DO="MHAI10$MX$HA" DA="phs*Har[%-0]$mag$f" />
<Value name="谐波电流有效值序列平均值值" desc="谐波电流幅值(2-50)平均值" type="6" DO="MHAI2$MX$HA" DA="phs*Har[%-0]$mag$f" />
<Value name="谐波电流有效值序列最大值值" desc="谐波电流幅值(2-50)最大值" type="6" DO="MHAI3$MX$HA" DA="phs*Har[%-0]$mag$f" />
<Value name="谐波电流有效值序列最小值值" desc="谐波电流幅值(2-50)最小值" type="6" DO="MHAI4$MX$HA" DA="phs*Har[%-0]$mag$f" />
<Value name="正序负序和零序电流平均值负序值" desc="负序电流平均值" type="6" DO="MSQI2$MX$SeqA" DA="c2$cVal$mag$f" />
<Value name="正序负序和零序电流最大值负序值" desc="负序电流最大值" type="6" DO="MSQI3$MX$SeqA" DA="c2$cVal$mag$f" />
<Value name="正序负序和零序电流最小值负序值" desc="负序电流最小值" type="6" DO="MSQI4$MX$SeqA" DA="c2$cVal$mag$f" />
<Value name="电流负序不平衡度平均值值" desc="负序电流不平衡平均值" type="6" DO="MSQI2$MX$ImbNgA" DA="mag$f" />
<Value name="电流负序不平衡度最大值值" desc="负序电流不平衡最大值" type="6" DO="MSQI3$MX$ImbNgA" DA="mag$f" />
<Value name="电流负序不平衡度最小值值" desc="负序电流不平衡最小值" type="6" DO="MSQI4$MX$ImbNgA" DA="mag$f" />
<Value name="正序负序和零序电流平均值零序值" desc="零序电流平均值" type="6" DO="MSQI2$MX$SeqA" DA="c3$cVal$mag$f" />
<Value name="正序负序和零序电流最大值零序值" desc="零序电流最大值" type="6" DO="MSQI3$MX$SeqA" DA="c3$cVal$mag$f" />
<Value name="正序负序和零序电流最小值零序值" desc="零序电流最小值" type="6" DO="MSQI4$MX$SeqA" DA="c3$cVal$mag$f" />
<Value name="电流零序不平衡度平均值值" desc="零序电流不平衡平均值" type="6" DO="MSQI2$MX$ImbZroA" DA="mag$f" />
<Value name="电流零序不平衡度最大值值" desc="零序电流不平衡最大值" type="6" DO="MSQI3$MX$ImbZroA" DA="mag$f" />
<Value name="电流零序不平衡度最小值值" desc="零序电流不平衡最小值" type="6" DO="MSQI4$MX$ImbZroA" DA="mag$f" />
<Value name="正序负序和零序电流平均值正序值" desc="正序电流平均值" type="6" DO="MSQI2$MX$SeqA" DA="c1$cVal$mag$f" />
<Value name="正序负序和零序电流最大值正序值" desc="正序电流最大值" type="6" DO="MSQI3$MX$SeqA" DA="c1$cVal$mag$f" />
<Value name="正序负序和零序电流最小值正序值" desc="正序电流最小值" type="6" DO="MSQI4$MX$SeqA" DA="c1$cVal$mag$f" />
<Value name="功率因数平均值值" desc="功率因数平均值" type="6" DO="MMXU2$MX$PF" DA="phs*$cVal$mag$f" />
<Value name="功率因数最大值值" desc="功率因数最大值" type="6" DO="MMXU3$MX$PF" DA="phs*$cVal$mag$f" />
<Value name="功率因数最小值值" desc="功率因数最小值" type="6" DO="MMXU4$MX$PF" DA="phs*$cVal$mag$f" />
<Value name="位移功率因数平均值值" desc="基波功率因数平均值" type="6" DO="MHAI2$MX$HSigPF" DA="phs*Har[1]$mag$f" />
<Value name="位移功率因数最大值值" desc="基波功率因数最大值" type="6" DO="MHAI3$MX$HSigPF" DA="phs*Har[1]$mag$f" />
<Value name="基波有功功率平均值值" desc="基波有功功率平均值" type="6" DO="MHAI2$MX$HW" DA="phs*Har[1]$mag$f" />
<Value name="基波有功功率最大值值" desc="基波有功功率最大值" type="6" DO="MHAI3$MX$HW" DA="phs*Har[1]$mag$f" />
<Value name="基波有功功率最小值值" desc="基波有功功率最小值" type="6" DO="MHAI4$MX$HW" DA="phs*Har[1]$mag$f" />
<Value name="视在功率95值值" desc="视在功率95值" type="6" DO="MMXU5$MX$VA" DA="phs*$cVal$mag$f" />
<Value name="视在功率平均值值" desc="视在功率平均值" type="6" DO="MMXU2$MX$VA" DA="phs*$cVal$mag$f" />
<Value name="视在功率最大值值" desc="视在功率最大值" type="6" DO="MMXU3$MX$VA" DA="phs*$cVal$mag$f" />
<Value name="视在功率最小值值" desc="视在功率最小值" type="6" DO="MMXU4$MX$VA" DA="phs*$cVal$mag$f" />
<Value name="无功功率95值值" desc="无功功率95值" type="6" DO="MMXU5$MX$VAr" DA="phs*$cVal$mag$f" />
<Value name="无功功率平均值值" desc="无功功率平均值" type="6" DO="MMXU2$MX$VAr" DA="phs*$cVal$mag$f" />
<Value name="无功功率最大值值" desc="无功功率最大值" type="6" DO="MMXU3$MX$VAr" DA="phs*$cVal$mag$f" />
<Value name="无功功率最小值值" desc="无功功率最小值" type="6" DO="MMXU4$MX$VAr" DA="phs*$cVal$mag$f" />
<Value name="2~50次谐波有功功率序列平均值值" desc="谐波有功功率(2-50)平均值" type="6" DO="MHAI2$MX$HW" DA="phs*Har[%-0]$mag$f" />
<Value name="2~50次谐波有功功率序列最大值值" desc="谐波有功功率(2-50)最大值" type="6" DO="MHAI3$MX$HW" DA="phs*Har[%-0]$mag$f" />
<Value name="2~50次谐波有功功率序列最小值值" desc="谐波有功功率(2-50)最小值" type="6" DO="MHAI4$MX$HW" DA="phs*Har[%-0]$mag$f" />
<Value name="有功功率95值值" desc="有功功率95值" type="6" DO="MMXU5$MX$W" DA="phs*$cVal$mag$f" />
<Value name="有功功率平均值值" desc="有功功率平均值" type="6" DO="MMXU2$MX$W" DA="phs*$cVal$mag$f" />
<Value name="有功功率最大值值" desc="有功功率最大值" type="6" DO="MMXU3$MX$W" DA="phs*$cVal$mag$f" />
<Value name="有功功率最小值值" desc="有功功率最小值" type="6" DO="MMXU4$MX$W" DA="phs*$cVal$mag$f" />
<Value name="三相功率因数平均值值" desc="总功率因数平均值" type="6" DO="MMXU2$MX$TotPF" DA="mag$f" />
<Value name="三相功率因数最大值值" desc="总功率因数最大值" type="6" DO="MMXU3$MX$TotPF" DA="mag$f" />
<Value name="三相功率因数最小值值" desc="总功率因数最小值" type="6" DO="MMXU4$MX$TotPF" DA="mag$f" />
<Value name="三相位移功率因数平均值值" desc="总基波功率因数平均值" type="6" DO="MHAI2$MX$HTotPF" DA="har[1]$mag$f" />
<Value name="三相位移功率因数最大值值" desc="总基波功率因数最大值" type="6" DO="MHAI3$MX$HTotPF" DA="har[1]$mag$f" />
<Value name="三相位移功率因数最小值值" desc="总基波功率因数最小值" type="6" DO="MHAI4$MX$HTotPF" DA="har[1]$mag$f" />
<Value name="三相总视在功率平均值值" desc="总视在功率平均值" type="6" DO="MMXU2$MX$TotVA" DA="mag$f" />
<Value name="三相总视在功率最大值值" desc="总视在功率最大值" type="6" DO="MMXU3$MX$TotVA" DA="mag$f" />
<Value name="三相总视在功率最小值值" desc="总视在功率最小值" type="6" DO="MMXU4$MX$TotVA" DA="mag$f" />
<Value name="三相总无功功率平均值值" desc="总无功功率平均值" type="6" DO="MMXU2$MX$TotVAr" DA="mag$f" />
<Value name="三相总无功功率最大值值" desc="总无功功率最大值" type="6" DO="MMXU3$MX$TotVAr" DA="mag$f" />
<Value name="三相总无功功率最小值值" desc="总无功功率最小值" type="6" DO="MMXU4$MX$TotVAr" DA="mag$f" />
<Value name="三相总有功功率平均值值" desc="总有功功率平均值" type="6" DO="MMXU2$MX$TotW" DA="mag$f" />
<Value name="三相总有功功率最大值值" desc="总有功功率最大值" type="6" DO="MMXU3$MX$TotW" DA="mag$f" />
<Value name="三相总有功功率最小值值" desc="总有功功率最小值" type="6" DO="MMXU4$MX$TotW" DA="mag$f" />