优化了xml修改和表格逆生成功能

This commit is contained in:
lnk
2026-07-14 15:14:54 +08:00
parent 70c1534d96
commit 6bfaa32db4
74 changed files with 15935 additions and 1804129 deletions

Binary file not shown.

Binary file not shown.

View File

@@ -1,10 +1,35 @@
@echo off
setlocal EnableExtensions
rem ============================================================================
rem Build script for the PQ mapping/parser/tracing tool.
rem
rem Purpose:
rem 1. Compile sources from the source directory.
rem 2. Write bin\pq_tool.exe by default, or write the output path passed as %1.
rem 3. Require a 32-bit i686 MinGW compiler because the executable and
rem RocketMQ runtime DLLs must use the same 32-bit architecture.
rem
rem Inputs:
rem %1 Optional output exe path.
rem PQ_BUILD_TEMP Optional temp directory, useful when the system temp drive
rem has too little free space.
rem
rem Outputs:
rem Success: generated exe and optional DLL warnings.
rem Failure: non-zero errorlevel and a printed error message.
rem
rem Keep this file ASCII-only. cmd.exe parses batch files with the active code
rem page, and UTF-8 Chinese comments can break command parsing on some systems.
rem ============================================================================
rem ROOT is the directory containing this script. All relative paths are based on
rem the project root after pushd.
set "ROOT=%~dp0"
pushd "%ROOT%" >nul
set "SRC_DIR=%ROOT%source"
rem Prefer the known 32-bit MinGW g++; otherwise fall back to g++ from PATH.
set "DEFAULT_CXX=C:\Users\lnk\Downloads\mingw32\bin\g++.exe"
if exist "%DEFAULT_CXX%" (
set "CXX=%DEFAULT_CXX%"
@@ -12,6 +37,8 @@ if exist "%DEFAULT_CXX%" (
set "CXX=g++"
)
rem The first argument controls the output path, so a test build can coexist with
rem the normal bin\pq_tool.exe.
set "OUT=%~1"
if "%OUT%"=="" set "OUT=bin\pq_tool.exe"
if not exist "bin" mkdir "bin"
@@ -21,6 +48,7 @@ if not exist "%SRC_DIR%\main.cpp" (
exit /b 1
)
rem Redirect TMP/TEMP when the caller provides a valid build temp directory.
if not "%PQ_BUILD_TEMP%"=="" (
if exist "%PQ_BUILD_TEMP%" (
set "TMP=%PQ_BUILD_TEMP%"
@@ -31,6 +59,7 @@ if not "%PQ_BUILD_TEMP%"=="" (
)
)
rem -dumpmachine returns values such as i686-w64-mingw32. Reject x86_64 here.
echo [INFO] Compiler: %CXX%
for /f "delims=" %%M in ('"%CXX%" -dumpmachine 2^>nul') do set "TARGET=%%M"
echo [INFO] Target: %TARGET%
@@ -43,6 +72,8 @@ if errorlevel 1 (
)
echo [INFO] Output: %OUT%
rem Delete the old output before compiling. If deletion fails, the program is
rem usually still running or the file is locked by another process.
if exist "%OUT%" (
del /f "%OUT%" >nul 2>nul
if exist "%OUT%" (
@@ -52,6 +83,12 @@ if exist "%OUT%" (
)
)
rem Compile units:
rem main.cpp CLI entry, JSON/XML mapping parser, Excel XML output.
rem interface.cpp Win32 GUI, page interaction, preview, reverse XML output.
rem mq.cpp HTTP ledger/ICD fetch, RocketMQ loader, trace data storage.
rem icd_mapper.cpp Imports ICD descriptions into unmatched-origin workbook rows.
rem tinyxml2.cpp XML parsing/printing library.
"%CXX%" ^
-std=gnu++17 ^
-O2 ^
@@ -69,12 +106,15 @@ if errorlevel 1 (
exit /b 1
)
rem Confirm that the compiler really produced the output file.
if not exist "%OUT%" (
echo [ERR] Build command finished but output file was not created: %OUT%
popd >nul
exit /b 1
)
rem These DLLs are loaded dynamically at runtime. The program can still start
rem without them, but MQ tracing will fall back to runtime\json\mq_outbox_*.jsonl.
if not exist "bin\rocketmq-client-cpp.dll" (
echo [WARN] bin\rocketmq-client-cpp.dll is missing. MQ runtime will not load.
)

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,7 @@
{
"key": "b699a184-9a96-4ab2-81f7-b60dc84c1483",
"messageBody": "{\"devSeries\":\"ca5420a552a727558c3b27ffaaf22a59\",\"limit\":20,\"line\":\"73e0a6553c2d24ee17f2623e14f9776b\",\"realData\":true,\"soeData\":true}",
"retryTimes": 0,
"sendTime": "2026-07-14T15:09:20.405",
"source": "WEB"
}

View File

@@ -1,7 +1,7 @@
{
"key": "5948178e-f193-4c1c-bb77-f164d8c7f17f",
"messageBody": "{\"code\":\"set_log\",\"frontType\":\"cfg_stat_data\",\"grade\":\"TRACE\",\"guid\":\"5948178e-f193-4c1c-bb77-f164d8c7f17f\",\"id\":\"73e0a6553c2d24ee17f2623e14f9776b\",\"level\":\"measurepoint\",\"logtype\":1,\"nodeId\":\"6c2adb324940e19e208d717f4970f2ee\",\"processNo\":1}",
"key": "ede0f3ef-93ea-43e5-8023-61867603162e",
"messageBody": "{\"code\":\"set_log\",\"frontType\":\"cfg_3s_data\",\"grade\":\"TRACE\",\"guid\":\"ede0f3ef-93ea-43e5-8023-61867603162e\",\"id\":\"73e0a6553c2d24ee17f2623e14f9776b\",\"level\":\"measurepoint\",\"logtype\":1,\"nodeId\":\"6c2adb324940e19e208d717f4970f2ee\",\"processNo\":0}",
"retryTimes": 0,
"sendTime": "2026-07-13T14:57:45.340",
"sendTime": "2026-07-14T15:09:20.368",
"source": "WEB"
}

View File

@@ -1 +1 @@
{"code":"A0000","message":"根据前置ip获取对应的终端台账信息,成功","data":[{"id":"08faa2065851a339c717c3ed740a4101","ip":"10.98.2.163","devType":"NRJB","org_name":null,"port":"102","stationName":null,"name":"CN-PQ-10101","subName":"110kV龙楼站","updateTime":"2026-04-15 09:13:13","manufacturer":"南瑞继保","status":"0","series":"3EE892A1B700DC65","devKey":"3EE892A1B700DC65","processNo":1,"maxProcessNum":2,"monitorData":[{"id":"046237ae3fdb2c4140d23e6dfb0f84e1","name":"110kV龙商线","lineNo":"1","voltageLevel":"110kV","ptType":"0","status":"0"},{"id":"4ce4ec51ccd9ac5fcead64cdd1ca18b2","name":"测时间点","lineNo":"4","voltageLevel":"110kV","ptType":"0","status":"0"}]},{"id":"2846527052bb9676b1e4cb4aa12eb955","ip":"192.168.2.136","devType":"QHMS_MMS","org_name":null,"port":"102","stationName":null,"name":"580数据校验测试1","subName":"测试变电站","updateTime":"2026-06-22 22:01:00","manufacturer":"南京灿能","status":"0","series":"2DijPPYO62hj8xoILU8wjA==","devKey":"2DijPPYO62hj8xoILU8wjA==","processNo":1,"maxProcessNum":2,"monitorData":[{"id":"0c9df16db618355080964b4b21d65110","name":"监测点1_星型","lineNo":"1","voltageLevel":"10kV","ptType":"0","status":"0"},{"id":"ce79a007a2bde0d58ff11540a9f50df7","name":"监测点2_角形","lineNo":"2","voltageLevel":"10kV","ptType":"1","status":"0"}]},{"id":"2e096de089538622968ceb790ef6e42e","ip":"192.168.1.103","devType":"NPQS_882_QH","org_name":null,"port":"102","stationName":null,"name":"海口测试装置","subName":"海口测试变电站","updateTime":"2026-04-15 09:13:14","manufacturer":"南京灿能","status":"0","series":"","devKey":"","processNo":1,"maxProcessNum":2,"monitorData":[{"id":"1c67c3eb11b8f532701e767d83890d86","name":"海口测试监测点","lineNo":"1","voltageLevel":"0.1kV","ptType":"0","status":"0"}]},{"id":"ae459b86e680f3532a686813f4b5f7f1","ip":"10.96.71.89","devType":"PQS882_B4(4U4I)","org_name":null,"port":"102","stationName":null,"name":"CN-PQ-C-LL882B4","subName":"110kV龙楼站","updateTime":"2026-04-15 09:13:14","manufacturer":"南京灿能","status":"0","series":"3EE892A1B700DC65","devKey":"3EE892A1B700DC65","processNo":1,"maxProcessNum":2,"monitorData":[{"id":"b4a7ecede01717b2f9487ac5ef72e851","name":"110kV龙发线","lineNo":"1","voltageLevel":"110kV","ptType":"0","status":"0"}]},{"id":"be2a30de595edcccd0275c9ec5cf3f4e","ip":"192.168.2.227","devType":"QHMS_MMS","org_name":null,"port":"102","stationName":null,"name":"国产装置测试辽宁","subName":"测试变电站","updateTime":"2011-12-26 05:21:00","manufacturer":"南京灿能","status":"0","series":"","devKey":"","processNo":1,"maxProcessNum":2,"monitorData":[{"id":"10ecb57a79aaa1b4804d45a71cf868c7","name":"辽宁国产装置测点1","lineNo":"1","voltageLevel":"0.1kV","ptType":"0","status":"0"}]},{"id":"ca5420a552a727558c3b27ffaaf22a59","ip":"192.168.3.126","devType":"QH_ms","org_name":null,"port":"102","stationName":null,"name":"自主协议测试终端1","subName":"测试变电站","updateTime":"2019-04-04 07:57:00","manufacturer":"南京灿能","status":"1","series":"","devKey":"","processNo":1,"maxProcessNum":2,"monitorData":[{"id":"73e0a6553c2d24ee17f2623e14f9776b","name":"自主协议监测点4","lineNo":"4","voltageLevel":"0.1kV","ptType":"0","status":"0"},{"id":"bd45e7c4df770ae1af803e8a05465e54","name":"自主协议测试监测点1","lineNo":"1","voltageLevel":"0.1kV","ptType":"1","status":"0"},{"id":"c7dbb4ebdb4a3aee920b5f0c9cb58d3d","name":"自主协议测试检测点2","lineNo":"2","voltageLevel":"0.1kV","ptType":"0","status":"0"},{"id":"e57ecbe0e81e29b7b0d6f1c902af655a","name":"自主协议监测点3","lineNo":"3","voltageLevel":"0.1kV","ptType":"0","status":"0"}]}]}
{"code":"A0000","message":"根据前置ip获取对应的终端台账信息,成功","data":[{"id":"08faa2065851a339c717c3ed740a4101","ip":"10.98.2.163","devType":"NRJB","org_name":null,"port":"102","stationName":null,"name":"CN-PQ-10101","subName":"110kV龙楼站","updateTime":"2026-04-15 09:13:13","manufacturer":"南瑞继保","status":"0","series":"3EE892A1B700DC65","devKey":"3EE892A1B700DC65","processNo":1,"maxProcessNum":2,"monitorData":[{"id":"046237ae3fdb2c4140d23e6dfb0f84e1","name":"110kV龙商线","lineNo":"1","voltageLevel":"110kV","ptType":"0","status":"0"},{"id":"4ce4ec51ccd9ac5fcead64cdd1ca18b2","name":"测时间点","lineNo":"4","voltageLevel":"110kV","ptType":"0","status":"0"}]},{"id":"2846527052bb9676b1e4cb4aa12eb955","ip":"192.168.2.136","devType":"QHMS_MMS","org_name":null,"port":"102","stationName":null,"name":"580数据校验测试1","subName":"测试变电站","updateTime":"2026-06-22 22:01:00","manufacturer":"南京灿能","status":"0","series":"2DijPPYO62hj8xoILU8wjA==","devKey":"2DijPPYO62hj8xoILU8wjA==","processNo":1,"maxProcessNum":2,"monitorData":[{"id":"0c9df16db618355080964b4b21d65110","name":"监测点1_星型","lineNo":"1","voltageLevel":"10kV","ptType":"0","status":"0"},{"id":"ce79a007a2bde0d58ff11540a9f50df7","name":"监测点2_角形","lineNo":"2","voltageLevel":"10kV","ptType":"1","status":"0"}]},{"id":"2e096de089538622968ceb790ef6e42e","ip":"192.168.1.103","devType":"NPQS_882_QH","org_name":null,"port":"102","stationName":null,"name":"海口测试装置","subName":"海口测试变电站","updateTime":"2026-04-15 09:13:14","manufacturer":"南京灿能","status":"0","series":"","devKey":"","processNo":1,"maxProcessNum":2,"monitorData":[{"id":"1c67c3eb11b8f532701e767d83890d86","name":"海口测试监测点","lineNo":"1","voltageLevel":"0.1kV","ptType":"0","status":"0"}]},{"id":"ae459b86e680f3532a686813f4b5f7f1","ip":"10.96.71.89","devType":"PQS882_B4(4U4I)","org_name":null,"port":"102","stationName":null,"name":"CN-PQ-C-LL882B4","subName":"110kV龙楼站","updateTime":"2026-04-15 09:13:14","manufacturer":"南京灿能","status":"0","series":"3EE892A1B700DC65","devKey":"3EE892A1B700DC65","processNo":1,"maxProcessNum":2,"monitorData":[{"id":"b4a7ecede01717b2f9487ac5ef72e851","name":"110kV龙发线","lineNo":"1","voltageLevel":"110kV","ptType":"0","status":"0"}]},{"id":"be2a30de595edcccd0275c9ec5cf3f4e","ip":"192.168.2.227","devType":"QHMS_MMS","org_name":null,"port":"102","stationName":null,"name":"国产装置测试辽宁","subName":"测试变电站","updateTime":"2011-12-26 05:21:00","manufacturer":"南京灿能","status":"0","series":"","devKey":"","processNo":1,"maxProcessNum":2,"monitorData":[{"id":"10ecb57a79aaa1b4804d45a71cf868c7","name":"辽宁国产装置测点1","lineNo":"1","voltageLevel":"0.1kV","ptType":"0","status":"0"}]},{"id":"ca5420a552a727558c3b27ffaaf22a59","ip":"192.168.3.126","devType":"QH_ms","org_name":null,"port":"102","stationName":null,"name":"自主协议测试终端1","subName":"测试变电站","updateTime":"2019-04-05 08:10:00","manufacturer":"南京灿能","status":"1","series":"","devKey":"","processNo":1,"maxProcessNum":2,"monitorData":[{"id":"73e0a6553c2d24ee17f2623e14f9776b","name":"自主协议监测点4","lineNo":"4","voltageLevel":"0.1kV","ptType":"0","status":"0"},{"id":"bd45e7c4df770ae1af803e8a05465e54","name":"自主协议测试监测点1","lineNo":"1","voltageLevel":"0.1kV","ptType":"1","status":"0"},{"id":"c7dbb4ebdb4a3aee920b5f0c9cb58d3d","name":"自主协议测试检测点2","lineNo":"2","voltageLevel":"0.1kV","ptType":"0","status":"0"},{"id":"e57ecbe0e81e29b7b0d6f1c902af655a","name":"自主协议监测点3","lineNo":"3","voltageLevel":"0.1kV","ptType":"0","status":"0"}]}]}

View File

@@ -1,5 +1,5 @@
{
"activeDataTopic": "MQ_INST_1697676490574298_Bm3DLamM%LN_Topic",
"activeDataTopic": "MQ_INST_1697676490574298_Bm3DLamM%Real_Time_Data_Topic",
"consumer": "TRACE_CONSUMER",
"consumerAccessKey": "rmqroot",
"consumerIpport": "192.168.1.68:9876",
@@ -13,11 +13,14 @@
"dataStatTopic": "MQ_INST_1697676490574298_Bm3DLamM%LN_Topic",
"dataTag": "6c2adb324940e19e208d717f4970f2ee",
"ipport": "192.168.1.68:9876",
"keyAsk": "Test_Keys",
"keyLOG": "Test_Keys",
"producer": "TRACE_PRODUCER",
"producerAccessKey": "rmqroot",
"producerSecretKey": "001@#njcnmq",
"tagAsk": "6c2adb324940e19e208d717f4970f2ee",
"tagLOG": "6c2adb324940e19e208d717f4970f2ee",
"topicAsk": "MQ_INST_1697676490574298_Bm3DLamM%ask_real_data_topic",
"topicLOG": "ask_log_Topic",
"traceKey": "Test_Key",
"traceTag": "6c2adb324940e19e208d717f4970f2ee",

File diff suppressed because it is too large Load Diff

View File

@@ -1 +0,0 @@
{"DATA_TYPE":"02" , "Monitor":"e57ecbe0e81e29b7b0d6f1c902af655a" , "Value":{"FLAG":0, "TIME":"1554079200000", "F_S":{"A":{ "PST":"0.000000","FLUC":"0.000000","FLUCF":null }, "B":{ "PST":"0.000000","FLUC":"0.000000","FLUCF":null }, "C":{ "PST":"0.000000","FLUC":"0.000000","FLUCF":null }}}}

View File

@@ -1 +0,0 @@
{"mp_id":"e57ecbe0e81e29b7b0d6f1c902af655a","v_wiring_type":"0","rpt_id":"rptStMSQI","rpt_no":5,"func_type":100,"data_time":1553991480000,"voltage_level":"0.000000","dev_type":"QH_ms","mms_json":{"MSQI2$MX$ImbNgA$mag$f":0,"MSQI2$MX$ImbNgV$mag$f":0,"MSQI2$MX$ImbZroA$mag$f":0,"MSQI2$MX$ImbZroV$mag$f":0,"MSQI2$MX$SeqA$c1$cVal$ang$f":0,"MSQI2$MX$SeqA$c1$cVal$mag$f":1.46783713717014e-05,"MSQI2$MX$SeqA$c2$cVal$ang$f":0,"MSQI2$MX$SeqA$c2$cVal$mag$f":1.52986631292151e-05,"MSQI2$MX$SeqA$c3$cVal$ang$f":0,"MSQI2$MX$SeqA$c3$cVal$mag$f":0.000197322646272369,"MSQI2$MX$SeqA$seqT":0,"MSQI2$MX$SeqV$c1$cVal$ang$f":0,"MSQI2$MX$SeqV$c1$cVal$mag$f":1.20933464131667e-07,"MSQI2$MX$SeqV$c2$cVal$ang$f":0,"MSQI2$MX$SeqV$c2$cVal$mag$f":0.000122754703625105,"MSQI2$MX$SeqV$c3$cVal$ang$f":0,"MSQI2$MX$SeqV$c3$cVal$mag$f":0.000665643834508955,"MSQI2$MX$SeqV$seqT":0,"MSQI3$MX$ImbNgA$mag$f":0,"MSQI3$MX$ImbNgV$mag$f":0,"MSQI3$MX$ImbZroA$mag$f":0,"MSQI3$MX$ImbZroV$mag$f":0,"MSQI3$MX$SeqA$c1$cVal$ang$f":0,"MSQI3$MX$SeqA$c1$cVal$mag$f":4.12217123084702e-05,"MSQI3$MX$SeqA$c2$cVal$ang$f":0,"MSQI3$MX$SeqA$c2$cVal$mag$f":3.68594082829077e-05,"MSQI3$MX$SeqA$c3$cVal$ang$f":0,"MSQI3$MX$SeqA$c3$cVal$mag$f":0.000222743459744379,"MSQI3$MX$SeqA$seqT":0,"MSQI3$MX$SeqV$c1$cVal$ang$f":0,"MSQI3$MX$SeqV$c1$cVal$mag$f":2.96029725177505e-07,"MSQI3$MX$SeqV$c2$cVal$ang$f":0,"MSQI3$MX$SeqV$c2$cVal$mag$f":0.000350504764355719,"MSQI3$MX$SeqV$c3$cVal$ang$f":0,"MSQI3$MX$SeqV$c3$cVal$mag$f":0.000915939279366285,"MSQI3$MX$SeqV$seqT":0,"MSQI4$MX$ImbNgA$mag$f":0,"MSQI4$MX$ImbNgV$mag$f":0,"MSQI4$MX$ImbZroA$mag$f":0,"MSQI4$MX$ImbZroV$mag$f":0,"MSQI4$MX$SeqA$c1$cVal$ang$f":0,"MSQI4$MX$SeqA$c1$cVal$mag$f":6.54809866773576e-07,"MSQI4$MX$SeqA$c2$cVal$ang$f":0,"MSQI4$MX$SeqA$c2$cVal$mag$f":5.72666181142267e-07,"MSQI4$MX$SeqA$c3$cVal$ang$f":0,"MSQI4$MX$SeqA$c3$cVal$mag$f":0.000159146627993323,"MSQI4$MX$SeqA$seqT":0,"MSQI4$MX$SeqV$c1$cVal$ang$f":0,"MSQI4$MX$SeqV$c1$cVal$mag$f":4.01115096693161e-09,"MSQI4$MX$SeqV$c2$cVal$ang$f":0,"MSQI4$MX$SeqV$c2$cVal$mag$f":4.55929057352478e-06,"MSQI4$MX$SeqV$c3$cVal$ang$f":0,"MSQI4$MX$SeqV$c3$cVal$mag$f":0.000429329433245584,"MSQI4$MX$SeqV$seqT":0,"MSQI5$MX$ImbNgA$mag$f":0,"MSQI5$MX$ImbNgV$mag$f":0,"MSQI5$MX$ImbZroA$mag$f":0,"MSQI5$MX$ImbZroV$mag$f":0,"MSQI5$MX$SeqA$c1$cVal$ang$f":0,"MSQI5$MX$SeqA$c1$cVal$mag$f":2.53820508078206e-05,"MSQI5$MX$SeqA$c2$cVal$ang$f":0,"MSQI5$MX$SeqA$c2$cVal$mag$f":2.63220572378486e-05,"MSQI5$MX$SeqA$c3$cVal$ang$f":0,"MSQI5$MX$SeqA$c3$cVal$mag$f":0.000212616010685451,"MSQI5$MX$SeqA$seqT":0,"MSQI5$MX$SeqV$c1$cVal$ang$f":0,"MSQI5$MX$SeqV$c1$cVal$mag$f":2.09265721196061e-07,"MSQI5$MX$SeqV$c2$cVal$ang$f":0,"MSQI5$MX$SeqV$c2$cVal$mag$f":0.000211027974728495,"MSQI5$MX$SeqV$c3$cVal$ang$f":0,"MSQI5$MX$SeqV$c3$cVal$mag$f":0.000799190544057637,"MSQI5$MX$SeqV$seqT":0,"MSQI6$MX$ImbNgA$mag$f":0,"MSQI6$MX$ImbNgV$mag$f":0,"MSQI6$MX$ImbZroA$mag$f":0,"MSQI6$MX$ImbZroV$mag$f":0,"MSQI6$MX$SeqA$c1$cVal$ang$f":0,"MSQI6$MX$SeqA$c1$cVal$mag$f":1.46783713717014e-05,"MSQI6$MX$SeqA$c2$cVal$ang$f":0,"MSQI6$MX$SeqA$c2$cVal$mag$f":1.52986631292151e-05,"MSQI6$MX$SeqA$c3$cVal$ang$f":0,"MSQI6$MX$SeqA$c3$cVal$mag$f":0.000197322646272369,"MSQI6$MX$SeqA$seqT":0,"MSQI6$MX$SeqV$c1$cVal$ang$f":0,"MSQI6$MX$SeqV$c1$cVal$mag$f":1.20933464131667e-07,"MSQI6$MX$SeqV$c2$cVal$ang$f":0,"MSQI6$MX$SeqV$c2$cVal$mag$f":0.000122754703625105,"MSQI6$MX$SeqV$c3$cVal$ang$f":0,"MSQI6$MX$SeqV$c3$cVal$mag$f":0.000665643834508955,"MSQI6$MX$SeqV$seqT":0}}

View File

@@ -1 +0,0 @@
{"mp_id":"c7dbb4ebdb4a3aee920b5f0c9cb58d3d","v_wiring_type":"0","rpt_id":"rptFluc","rpt_no":2,"func_type":100,"data_time":1553991600000,"voltage_level":"0.000000","dev_type":"QH_ms","mms_json":{"MMXU2$MX$FLUC$phsA$cVal$mag$f":0,"MMXU2$MX$FLUC$phsB$cVal$mag$f":0,"MMXU2$MX$FLUC$phsC$cVal$mag$f":0}}

View File

@@ -1 +0,0 @@
{"mp_id":"c7dbb4ebdb4a3aee920b5f0c9cb58d3d","v_wiring_type":"0","rpt_id":"rptPST","rpt_no":1,"func_type":100,"data_time":1553991600000,"voltage_level":"0.000000","dev_type":"QH_ms","mms_json":{"MFLK1$MX$PhPst$phsA$cVal$mag$f":0,"MFLK1$MX$PhPst$phsB$cVal$mag$f":0,"MFLK1$MX$PhPst$phsC$cVal$mag$f":0}}

View File

@@ -1 +0,0 @@
{"mp_id":"e57ecbe0e81e29b7b0d6f1c902af655a","v_wiring_type":"0","rpt_id":"rptFluc","rpt_no":2,"func_type":100,"data_time":1553991600000,"voltage_level":"0.000000","dev_type":"QH_ms","mms_json":{"MMXU2$MX$FLUC$phsA$cVal$mag$f":0,"MMXU2$MX$FLUC$phsB$cVal$mag$f":0,"MMXU2$MX$FLUC$phsC$cVal$mag$f":0}}

View File

@@ -1 +0,0 @@
{"mp_id":"e57ecbe0e81e29b7b0d6f1c902af655a","v_wiring_type":"0","rpt_id":"rptPST","rpt_no":1,"func_type":100,"data_time":1553991600000,"voltage_level":"0.000000","dev_type":"QH_ms","mms_json":{"MFLK1$MX$PhPst$phsA$cVal$mag$f":0,"MFLK1$MX$PhPst$phsB$cVal$mag$f":0,"MFLK1$MX$PhPst$phsC$cVal$mag$f":0}}

View File

@@ -1 +1 @@
{"mp_id":"e57ecbe0e81e29b7b0d6f1c902af655a","v_wiring_type":"0","rpt_id":"rptFluc","rpt_no":2,"func_type":100,"data_time":1554079200000,"voltage_level":"0.000000","dev_type":"QH_ms","mms_json":{"MMXU2$MX$FLUC$phsA$cVal$mag$f":0,"MMXU2$MX$FLUC$phsB$cVal$mag$f":0,"MMXU2$MX$FLUC$phsC$cVal$mag$f":0}}
{"mp_id":"e57ecbe0e81e29b7b0d6f1c902af655a","v_wiring_type":"0","rpt_id":"rptFluc","rpt_no":2,"func_type":200,"data_time":1554324000000,"voltage_level":"0.000000","dev_type":"QH_ms","mms_json":{"MMXU2$MX$FLUC$phsA$cVal$mag$f":0,"MMXU2$MX$FLUC$phsB$cVal$mag$f":0,"MMXU2$MX$FLUC$phsC$cVal$mag$f":0}}

View File

@@ -0,0 +1 @@
{"mp_id":"e57ecbe0e81e29b7b0d6f1c902af655a","v_wiring_type":"0","rpt_id":"rptPLT","rpt_no":0,"func_type":200,"data_time":1782957600000,"voltage_level":"0.000000","dev_type":"QH_ms","mms_json":{"MFLK2$MX$PhPlt$phsA$cVal$mag$f":0,"MFLK2$MX$PhPlt$phsB$cVal$mag$f":0,"MFLK2$MX$PhPlt$phsC$cVal$mag$f":0}}

View File

@@ -1 +1 @@
{"mp_id":"e57ecbe0e81e29b7b0d6f1c902af655a","v_wiring_type":"0","rpt_id":"rptPST","rpt_no":1,"func_type":100,"data_time":1554079200000,"voltage_level":"0.000000","dev_type":"QH_ms","mms_json":{"MFLK1$MX$PhPst$phsA$cVal$mag$f":0,"MFLK1$MX$PhPst$phsB$cVal$mag$f":0,"MFLK1$MX$PhPst$phsC$cVal$mag$f":0}}
{"mp_id":"e57ecbe0e81e29b7b0d6f1c902af655a","v_wiring_type":"0","rpt_id":"rptPST","rpt_no":1,"func_type":200,"data_time":1554316200000,"voltage_level":"0.000000","dev_type":"QH_ms","mms_json":{"MFLK1$MX$PhPst$phsA$cVal$mag$f":0,"MFLK1$MX$PhPst$phsB$cVal$mag$f":0,"MFLK1$MX$PhPst$phsC$cVal$mag$f":0}}

View File

@@ -0,0 +1 @@
{"mp_id":"e57ecbe0e81e29b7b0d6f1c902af655a","v_wiring_type":"0","rpt_id":"rptRtMMXU","rpt_no":6,"func_type":200,"data_time":1782982587000,"voltage_level":"0.000000","dev_type":"QH_ms","mms_json":{"MMXU1$MX$A$phsA$cVal$ang$f":0,"MMXU1$MX$A$phsA$cVal$mag$f":0.000887073925696313,"MMXU1$MX$A$phsB$cVal$ang$f":0,"MMXU1$MX$A$phsB$cVal$mag$f":0.00207136641256511,"MMXU1$MX$A$phsC$cVal$ang$f":0,"MMXU1$MX$A$phsC$cVal$mag$f":0.00880554690957069,"MMXU1$MX$FreqDev$mag$f":0,"MMXU1$MX$Hz$mag$f":50,"MMXU1$MX$PF$phsA$cVal$mag$f":1,"MMXU1$MX$PF$phsB$cVal$mag$f":1,"MMXU1$MX$PF$phsC$cVal$mag$f":1,"MMXU1$MX$PPV$phsAB$cVal$ang$f":0,"MMXU1$MX$PPV$phsAB$cVal$mag$f":2.33369773923187e-05,"MMXU1$MX$PPV$phsBC$cVal$ang$f":0,"MMXU1$MX$PPV$phsBC$cVal$mag$f":2.43472222791752e-05,"MMXU1$MX$PPV$phsCA$cVal$ang$f":0,"MMXU1$MX$PPV$phsCA$cVal$mag$f":7.77665627538227e-06,"MMXU1$MX$PhV$phsA$cVal$ang$f":0,"MMXU1$MX$PhV$phsA$cVal$mag$f":4.83914254800766e-06,"MMXU1$MX$PhV$phsB$cVal$ang$f":0,"MMXU1$MX$PhV$phsB$cVal$mag$f":2.72020020020136e-06,"MMXU1$MX$PhV$phsC$cVal$ang$f":0,"MMXU1$MX$PhV$phsC$cVal$mag$f":4.92830440634862e-06,"MMXU1$MX$TotPF$mag$f":1,"MMXU1$MX$TotVA$mag$f":3.37255290361327e-09,"MMXU1$MX$TotVAr$mag$f":0,"MMXU1$MX$TotW$mag$f":3.37255290361327e-09,"MMXU1$MX$VA$phsA$cVal$mag$f":1.13420950409449e-09,"MMXU1$MX$VA$phsB$cVal$mag$f":1.05850306297839e-09,"MMXU1$MX$VA$phsC$cVal$mag$f":1.17984011449579e-09,"MMXU1$MX$VAr$phsA$cVal$mag$f":0,"MMXU1$MX$VAr$phsB$cVal$mag$f":0,"MMXU1$MX$VAr$phsC$cVal$mag$f":0,"MMXU1$MX$VolDev$phsA$cVal$mag$f":0,"MMXU1$MX$VolDev$phsB$cVal$mag$f":0,"MMXU1$MX$VolDev$phsC$cVal$mag$f":0,"MMXU1$MX$W$phsA$cVal$mag$f":1.13420950409449e-09,"MMXU1$MX$W$phsB$cVal$mag$f":1.05850306297839e-09,"MMXU1$MX$W$phsC$cVal$mag$f":1.17984011449579e-09}}

View File

@@ -0,0 +1 @@
{"mp_id":"e57ecbe0e81e29b7b0d6f1c902af655a","v_wiring_type":"0","rpt_id":"rptRtMSQI","rpt_no":5,"func_type":200,"data_time":1782982587000,"voltage_level":"0.000000","dev_type":"QH_ms","mms_json":{"MSQI1$MX$ImbNgA$mag$f":0,"MSQI1$MX$ImbNgV$mag$f":0,"MSQI1$MX$ImbZroA$mag$f":0,"MSQI1$MX$ImbZroV$mag$f":0,"MSQI1$MX$SeqA$c1$cVal$ang$f":0,"MSQI1$MX$SeqA$c1$cVal$mag$f":1.60180952661904e-05,"MSQI1$MX$SeqA$c2$cVal$ang$f":0,"MSQI1$MX$SeqA$c2$cVal$mag$f":1.38958503157482e-05,"MSQI1$MX$SeqA$c3$cVal$ang$f":0,"MSQI1$MX$SeqA$c3$cVal$mag$f":0.000193346117157489,"MSQI1$MX$SeqA$seqT":0,"MSQI1$MX$SeqV$c1$cVal$ang$f":0,"MSQI1$MX$SeqV$c1$cVal$mag$f":1.32824013121535e-07,"MSQI1$MX$SeqV$c2$cVal$ang$f":0,"MSQI1$MX$SeqV$c2$cVal$mag$f":0.000130813350551762,"MSQI1$MX$SeqV$c3$cVal$ang$f":0,"MSQI1$MX$SeqV$c3$cVal$mag$f":0.000654586998280138,"MSQI1$MX$SeqV$seqT":0}}

View File

@@ -1 +0,0 @@
{"mp_id":"e57ecbe0e81e29b7b0d6f1c902af655a","v_wiring_type":"0","rpt_id":"rptStMSQI","rpt_no":5,"func_type":100,"data_time":1554078960000,"voltage_level":"0.000000","dev_type":"QH_ms","mms_json":{"MSQI2$MX$ImbNgA$mag$f":0,"MSQI2$MX$ImbNgV$mag$f":0,"MSQI2$MX$ImbZroA$mag$f":0,"MSQI2$MX$ImbZroV$mag$f":0,"MSQI2$MX$SeqA$c1$cVal$ang$f":0,"MSQI2$MX$SeqA$c1$cVal$mag$f":1.50571331687388e-05,"MSQI2$MX$SeqA$c2$cVal$ang$f":0,"MSQI2$MX$SeqA$c2$cVal$mag$f":1.49890156535548e-05,"MSQI2$MX$SeqA$c3$cVal$ang$f":0,"MSQI2$MX$SeqA$c3$cVal$mag$f":0.000195257423911244,"MSQI2$MX$SeqA$seqT":0,"MSQI2$MX$SeqV$c1$cVal$ang$f":0,"MSQI2$MX$SeqV$c1$cVal$mag$f":1.2523780412721e-07,"MSQI2$MX$SeqV$c2$cVal$ang$f":0,"MSQI2$MX$SeqV$c2$cVal$mag$f":0.00012145827349741,"MSQI2$MX$SeqV$c3$cVal$ang$f":0,"MSQI2$MX$SeqV$c3$cVal$mag$f":0.000668085063807666,"MSQI2$MX$SeqV$seqT":0,"MSQI3$MX$ImbNgA$mag$f":0,"MSQI3$MX$ImbNgV$mag$f":0,"MSQI3$MX$ImbZroA$mag$f":0,"MSQI3$MX$ImbZroV$mag$f":0,"MSQI3$MX$SeqA$c1$cVal$ang$f":0,"MSQI3$MX$SeqA$c1$cVal$mag$f":3.73459333786741e-05,"MSQI3$MX$SeqA$c2$cVal$ang$f":0,"MSQI3$MX$SeqA$c2$cVal$mag$f":3.83881342713721e-05,"MSQI3$MX$SeqA$c3$cVal$ang$f":0,"MSQI3$MX$SeqA$c3$cVal$mag$f":0.000222166156163439,"MSQI3$MX$SeqA$seqT":0,"MSQI3$MX$SeqV$c1$cVal$ang$f":0,"MSQI3$MX$SeqV$c1$cVal$mag$f":3.94805823589195e-07,"MSQI3$MX$SeqV$c2$cVal$ang$f":0,"MSQI3$MX$SeqV$c2$cVal$mag$f":0.000296594691462815,"MSQI3$MX$SeqV$c3$cVal$ang$f":0,"MSQI3$MX$SeqV$c3$cVal$mag$f":0.000961637939326465,"MSQI3$MX$SeqV$seqT":0,"MSQI4$MX$ImbNgA$mag$f":0,"MSQI4$MX$ImbNgV$mag$f":0,"MSQI4$MX$ImbZroA$mag$f":0,"MSQI4$MX$ImbZroV$mag$f":0,"MSQI4$MX$SeqA$c1$cVal$ang$f":0,"MSQI4$MX$SeqA$c1$cVal$mag$f":3.57456201527384e-07,"MSQI4$MX$SeqA$c2$cVal$ang$f":0,"MSQI4$MX$SeqA$c2$cVal$mag$f":4.64877643935324e-07,"MSQI4$MX$SeqA$c3$cVal$ang$f":0,"MSQI4$MX$SeqA$c3$cVal$mag$f":0.000164380791829899,"MSQI4$MX$SeqA$seqT":0,"MSQI4$MX$SeqV$c1$cVal$ang$f":0,"MSQI4$MX$SeqV$c1$cVal$mag$f":4.37183489410131e-09,"MSQI4$MX$SeqV$c2$cVal$ang$f":0,"MSQI4$MX$SeqV$c2$cVal$mag$f":2.20051947508182e-06,"MSQI4$MX$SeqV$c3$cVal$ang$f":0,"MSQI4$MX$SeqV$c3$cVal$mag$f":0.000395290233427659,"MSQI4$MX$SeqV$seqT":0,"MSQI5$MX$ImbNgA$mag$f":0,"MSQI5$MX$ImbNgV$mag$f":0,"MSQI5$MX$ImbZroA$mag$f":0,"MSQI5$MX$ImbZroV$mag$f":0,"MSQI5$MX$SeqA$c1$cVal$ang$f":0,"MSQI5$MX$SeqA$c1$cVal$mag$f":2.57019964919891e-05,"MSQI5$MX$SeqA$c2$cVal$ang$f":0,"MSQI5$MX$SeqA$c2$cVal$mag$f":2.60492779489141e-05,"MSQI5$MX$SeqA$c3$cVal$ang$f":0,"MSQI5$MX$SeqA$c3$cVal$mag$f":0.000209666875889525,"MSQI5$MX$SeqA$seqT":0,"MSQI5$MX$SeqV$c1$cVal$ang$f":0,"MSQI5$MX$SeqV$c1$cVal$mag$f":2.22368853997068e-07,"MSQI5$MX$SeqV$c2$cVal$ang$f":0,"MSQI5$MX$SeqV$c2$cVal$mag$f":0.000208474855753593,"MSQI5$MX$SeqV$c3$cVal$ang$f":0,"MSQI5$MX$SeqV$c3$cVal$mag$f":0.000809346733149141,"MSQI5$MX$SeqV$seqT":0,"MSQI6$MX$ImbNgA$mag$f":0,"MSQI6$MX$ImbNgV$mag$f":0,"MSQI6$MX$ImbZroA$mag$f":0,"MSQI6$MX$ImbZroV$mag$f":0,"MSQI6$MX$SeqA$c1$cVal$ang$f":0,"MSQI6$MX$SeqA$c1$cVal$mag$f":1.50571331687388e-05,"MSQI6$MX$SeqA$c2$cVal$ang$f":0,"MSQI6$MX$SeqA$c2$cVal$mag$f":1.49890156535548e-05,"MSQI6$MX$SeqA$c3$cVal$ang$f":0,"MSQI6$MX$SeqA$c3$cVal$mag$f":0.000195257423911244,"MSQI6$MX$SeqA$seqT":0,"MSQI6$MX$SeqV$c1$cVal$ang$f":0,"MSQI6$MX$SeqV$c1$cVal$mag$f":1.2523780412721e-07,"MSQI6$MX$SeqV$c2$cVal$ang$f":0,"MSQI6$MX$SeqV$c2$cVal$mag$f":0.00012145827349741,"MSQI6$MX$SeqV$c3$cVal$ang$f":0,"MSQI6$MX$SeqV$c3$cVal$mag$f":0.000668085063807666,"MSQI6$MX$SeqV$seqT":0}}

View File

@@ -0,0 +1,7 @@
{
"key": "b7d99cfb-fa35-4f46-8642-2bfa14b41330",
"messageBody": "{\"devSeries\":\"ca5420a552a727558c3b27ffaaf22a59\",\"limit\":20,\"line\":\"e57ecbe0e81e29b7b0d6f1c902af655a\",\"realData\":true,\"soeData\":true}",
"retryTimes": 0,
"sendTime": "2026-07-14T13:45:05.683",
"source": "WEB"
}

View File

@@ -1,7 +1,7 @@
{
"key": "1e205ae3-1052-49d4-9dfa-bb5114fe48b4",
"messageBody": "{\"code\":\"set_log\",\"frontType\":\"cfg_stat_data\",\"grade\":\"TRACE\",\"guid\":\"1e205ae3-1052-49d4-9dfa-bb5114fe48b4\",\"id\":\"e57ecbe0e81e29b7b0d6f1c902af655a\",\"level\":\"measurepoint\",\"logtype\":1,\"nodeId\":\"6c2adb324940e19e208d717f4970f2ee\",\"processNo\":1}",
"key": "88e8770b-b4af-4962-bc6d-1c6aec8223be",
"messageBody": "{\"code\":\"set_log\",\"frontType\":\"cfg_3s_data\",\"grade\":\"TRACE\",\"guid\":\"88e8770b-b4af-4962-bc6d-1c6aec8223be\",\"id\":\"e57ecbe0e81e29b7b0d6f1c902af655a\",\"level\":\"measurepoint\",\"logtype\":1,\"nodeId\":\"6c2adb324940e19e208d717f4970f2ee\",\"processNo\":0}",
"retryTimes": 0,
"sendTime": "2026-07-10T15:32:39.681",
"sendTime": "2026-07-14T13:45:05.671",
"source": "WEB"
}

View File

@@ -2,17 +2,19 @@
"devType": "QH_ms",
"deviceId": "ca5420a552a727558c3b27ffaaf22a59",
"deviceName": "自主协议测试终端1",
"frontType": "cfg_stat_data",
"guid": "1e205ae3-1052-49d4-9dfa-bb5114fe48b4",
"jsonDataPath": "C:\\Users\\lnk\\Desktop\\发送jaon映射xml工具\\runtime\\trace_data\\process_1\\device_自主协议测试终端1_ca5420a552a727558c3b27ffaaf22a59\\monitor_自主协议监测点3_e57ecbe0e81e29b7b0d6f1c902af655a\\jsondata_01.txt",
"frontType": "cfg_3s_data",
"guid": "88e8770b-b4af-4962-bc6d-1c6aec8223be",
"jsonDataPath": "D:\\canneng\\发送jaon映射xml工具\\runtime\\trace_data\\process_1\\device_自主协议测试终端1_ca5420a552a727558c3b27ffaaf22a59\\monitor_自主协议监测点3_e57ecbe0e81e29b7b0d6f1c902af655a\\jsondata_01.txt",
"jsonDataPaths": [
"C:\\Users\\lnk\\Desktop\\发送jaon映射xml工具\\runtime\\trace_data\\process_1\\device_自主协议测试终端1_ca5420a552a727558c3b27ffaaf22a59\\monitor_自主协议监测点3_e57ecbe0e81e29b7b0d6f1c902af655a\\jsondata_01.txt",
"C:\\Users\\lnk\\Desktop\\发送jaon映射xml工具\\runtime\\trace_data\\process_1\\device_自主协议测试终端1_ca5420a552a727558c3b27ffaaf22a59\\monitor_自主协议监测点3_e57ecbe0e81e29b7b0d6f1c902af655a\\jsondata_02.txt",
"C:\\Users\\lnk\\Desktop\\发送jaon映射xml工具\\runtime\\trace_data\\process_1\\device_自主协议测试终端1_ca5420a552a727558c3b27ffaaf22a59\\monitor_自主协议监测点3_e57ecbe0e81e29b7b0d6f1c902af655a\\jsondata_03.txt"
"D:\\canneng\\发送jaon映射xml工具\\runtime\\trace_data\\process_1\\device_自主协议测试终端1_ca5420a552a727558c3b27ffaaf22a59\\monitor_自主协议监测点3_e57ecbe0e81e29b7b0d6f1c902af655a\\jsondata_01.txt",
"D:\\canneng\\发送jaon映射xml工具\\runtime\\trace_data\\process_1\\device_自主协议测试终端1_ca5420a552a727558c3b27ffaaf22a59\\monitor_自主协议监测点3_e57ecbe0e81e29b7b0d6f1c902af655a\\jsondata_02.txt",
"D:\\canneng\\发送jaon映射xml工具\\runtime\\trace_data\\process_1\\device_自主协议测试终端1_ca5420a552a727558c3b27ffaaf22a59\\monitor_自主协议监测点3_e57ecbe0e81e29b7b0d6f1c902af655a\\jsondata_03.txt"
],
"monitorId": "e57ecbe0e81e29b7b0d6f1c902af655a",
"monitorName": "自主协议监测点3",
"outputPath": "C:\\Users\\lnk\\Desktop\\发送jaon映射xml工具\\runtime\\trace_data\\process_1\\device_自主协议测试终端1_ca5420a552a727558c3b27ffaaf22a59\\monitor_自主协议监测点3_e57ecbe0e81e29b7b0d6f1c902af655a\\final_sorted.xls",
"outputPath": "D:\\canneng\\发送jaon映射xml工具\\runtime\\trace_data\\process_1\\device_自主协议测试终端1_ca5420a552a727558c3b27ffaaf22a59\\monitor_自主协议监测点3_e57ecbe0e81e29b7b0d6f1c902af655a\\final_sorted.xls",
"processNo": "1",
"xmlPath": "C:\\Users\\lnk\\Desktop\\发送jaon映射xml工具\\runtime\\downloaded_xml\\be3e57c36220ed847a4acfa62c59dc38.xml"
"startedAt": "2026-07-14T13:45:05.673",
"startedAtMs": 1784007905671,
"xmlPath": "D:\\canneng\\发送jaon映射xml工具\\runtime\\downloaded_xml\\be3e57c36220ed847a4acfa62c59dc38.xml"
}

View File

@@ -1 +0,0 @@
{"DATA_TYPE":"02" , "Monitor":"73e0a6553c2d24ee17f2623e14f9776b" , "Value":{"FLAG":0, "TIME":"1554336000000", "F_S":{"A":{ "PST":"0.000000","FLUC":"0.000000","FLUCF":"0.000000" }, "B":{ "PST":"0.000000","FLUC":"0.000000","FLUCF":"0.000000" }, "C":{ "PST":"0.000000","FLUC":"0.000000","FLUCF":"0.000000" }}}}

View File

@@ -1 +0,0 @@
{"DATA_TYPE":"03" , "Monitor":"73e0a6553c2d24ee17f2623e14f9776b" , "Value":{"FLAG":0, "TIME":"1554336000000", "F_L":{"A":{ "PLT":"0.000000" }, "B":{ "PLT":"0.000000" }, "C":{ "PLT":"0.000000" }}}}

View File

@@ -1 +0,0 @@
{"mp_id":"73e0a6553c2d24ee17f2623e14f9776b","v_wiring_type":"0","rpt_id":"rptFluc","rpt_no":2,"func_type":100,"data_time":1554336000000,"voltage_level":"0.000000","dev_type":"QH_ms","mms_json":{"MMXU2$MX$FLUC$phsA$cVal$mag$f":0,"MMXU2$MX$FLUC$phsB$cVal$mag$f":0,"MMXU2$MX$FLUC$phsC$cVal$mag$f":0,"MMXU2$MX$FLUCCF$phsA$cVal$mag$f":0,"MMXU2$MX$FLUCCF$phsB$cVal$mag$f":0,"MMXU2$MX$FLUCCF$phsC$cVal$mag$f":0}}

View File

@@ -1 +0,0 @@
{"mp_id":"73e0a6553c2d24ee17f2623e14f9776b","v_wiring_type":"0","rpt_id":"rptPLT","rpt_no":0,"func_type":100,"data_time":1554336000000,"voltage_level":"0.000000","dev_type":"QH_ms","mms_json":{"MFLK2$MX$PhPlt$phsA$cVal$mag$f":0,"MFLK2$MX$PhPlt$phsB$cVal$mag$f":0,"MFLK2$MX$PhPlt$phsC$cVal$mag$f":0}}

View File

@@ -1 +0,0 @@
{"mp_id":"73e0a6553c2d24ee17f2623e14f9776b","v_wiring_type":"0","rpt_id":"rptPST","rpt_no":1,"func_type":100,"data_time":1554336000000,"voltage_level":"0.000000","dev_type":"QH_ms","mms_json":{"MFLK1$MX$PhPst$phsA$cVal$mag$f":0,"MFLK1$MX$PhPst$phsB$cVal$mag$f":0,"MFLK1$MX$PhPst$phsC$cVal$mag$f":0}}

View File

@@ -0,0 +1 @@
{"mp_id":"73e0a6553c2d24ee17f2623e14f9776b","v_wiring_type":"0","rpt_id":"rptRtMMXU","rpt_no":6,"func_type":200,"data_time":1554423033000,"voltage_level":"0.000000","dev_type":"QH_ms","mms_json":{"MMXU1$MX$A$phsA$cVal$ang$f":0,"MMXU1$MX$A$phsA$cVal$mag$f":0.00534555548802018,"MMXU1$MX$A$phsB$cVal$ang$f":0,"MMXU1$MX$A$phsB$cVal$mag$f":0.00142798584420234,"MMXU1$MX$A$phsC$cVal$ang$f":0,"MMXU1$MX$A$phsC$cVal$mag$f":0.00183050846680999,"MMXU1$MX$PF$phsA$cVal$mag$f":1,"MMXU1$MX$PF$phsB$cVal$mag$f":1,"MMXU1$MX$PF$phsC$cVal$mag$f":1,"MMXU1$MX$PPV$phsAB$cVal$ang$f":0,"MMXU1$MX$PPV$phsAB$cVal$mag$f":1.10044593384373e-05,"MMXU1$MX$PPV$phsBC$cVal$ang$f":0,"MMXU1$MX$PPV$phsBC$cVal$mag$f":4.90294878545683e-05,"MMXU1$MX$PPV$phsCA$cVal$ang$f":0,"MMXU1$MX$PPV$phsCA$cVal$mag$f":4.32097003795207e-05,"MMXU1$MX$PhV$phsA$cVal$ang$f":0,"MMXU1$MX$PhV$phsA$cVal$mag$f":4.74101216241252e-06,"MMXU1$MX$PhV$phsB$cVal$ang$f":0,"MMXU1$MX$PhV$phsB$cVal$mag$f":5.34178616362624e-06,"MMXU1$MX$PhV$phsC$cVal$ang$f":0,"MMXU1$MX$PhV$phsC$cVal$mag$f":8.99274766652525e-07,"MMXU1$MX$TotPF$mag$f":1,"MMXU1$MX$TotVA$mag$f":2.6581070677878e-09,"MMXU1$MX$TotVAr$mag$f":0,"MMXU1$MX$TotW$mag$f":2.6581070677878e-09,"MMXU1$MX$VA$phsA$cVal$mag$f":9.83165215906467e-10,"MMXU1$MX$VA$phsB$cVal$mag$f":8.93840002014201e-10,"MMXU1$MX$VA$phsC$cVal$mag$f":7.8110196088943e-10,"MMXU1$MX$VAr$phsA$cVal$mag$f":0,"MMXU1$MX$VAr$phsB$cVal$mag$f":0,"MMXU1$MX$VAr$phsC$cVal$mag$f":0,"MMXU1$MX$VolDev$phsA$cVal$mag$f":0,"MMXU1$MX$VolDev$phsB$cVal$mag$f":0,"MMXU1$MX$VolDev$phsC$cVal$mag$f":0,"MMXU1$MX$W$phsA$cVal$mag$f":9.83165215906467e-10,"MMXU1$MX$W$phsB$cVal$mag$f":8.93840002014201e-10,"MMXU1$MX$W$phsC$cVal$mag$f":7.8110196088943e-10}}

View File

@@ -0,0 +1 @@
{"mp_id":"73e0a6553c2d24ee17f2623e14f9776b","v_wiring_type":"0","rpt_id":"rptRtMSQI","rpt_no":5,"func_type":200,"data_time":1554423033000,"voltage_level":"0.000000","dev_type":"QH_ms","mms_json":{"MSQI1$MX$ImbNgA$mag$f":0,"MSQI1$MX$ImbNgV$mag$f":0,"MSQI1$MX$ImbZroA$mag$f":0,"MSQI1$MX$ImbZroV$mag$f":0,"MSQI1$MX$SeqA$c1$cVal$ang$f":0,"MSQI1$MX$SeqA$c1$cVal$mag$f":3.79269768018275e-05,"MSQI1$MX$SeqA$c2$cVal$ang$f":0,"MSQI1$MX$SeqA$c2$cVal$mag$f":4.16279581259005e-05,"MSQI1$MX$SeqA$c3$cVal$ang$f":0,"MSQI1$MX$SeqA$c3$cVal$mag$f":0.000162676893523894,"MSQI1$MX$SeqA$seqT":0,"MSQI1$MX$SeqV$c1$cVal$ang$f":0,"MSQI1$MX$SeqV$c1$cVal$mag$f":1.39747072580576e-07,"MSQI1$MX$SeqV$c2$cVal$ang$f":0,"MSQI1$MX$SeqV$c2$cVal$mag$f":0.000123805395560339,"MSQI1$MX$SeqV$c3$cVal$ang$f":0,"MSQI1$MX$SeqV$c3$cVal$mag$f":0.000381623744033277,"MSQI1$MX$SeqV$seqT":0}}

View File

@@ -1 +0,0 @@
{"mp_id":"73e0a6553c2d24ee17f2623e14f9776b","v_wiring_type":"0","rpt_id":"rptStMSQI","rpt_no":5,"func_type":100,"data_time":1554336000000,"voltage_level":"0.000000","dev_type":"QH_ms","mms_json":{"MSQI2$MX$ImbNgA$mag$f":0,"MSQI2$MX$ImbNgV$mag$f":0,"MSQI2$MX$ImbZroA$mag$f":0,"MSQI2$MX$ImbZroV$mag$f":0,"MSQI2$MX$SeqA$c1$cVal$ang$f":0,"MSQI2$MX$SeqA$c1$cVal$mag$f":3.68404398614075e-05,"MSQI2$MX$SeqA$c2$cVal$ang$f":0,"MSQI2$MX$SeqA$c2$cVal$mag$f":3.74274641217198e-05,"MSQI2$MX$SeqA$c3$cVal$ang$f":0,"MSQI2$MX$SeqA$c3$cVal$mag$f":0.000163955686730333,"MSQI2$MX$SeqA$seqT":0,"MSQI2$MX$SeqV$c1$cVal$ang$f":0,"MSQI2$MX$SeqV$c1$cVal$mag$f":1.47475219591797e-07,"MSQI2$MX$SeqV$c2$cVal$ang$f":0,"MSQI2$MX$SeqV$c2$cVal$mag$f":0.000120074160804506,"MSQI2$MX$SeqV$c3$cVal$ang$f":0,"MSQI2$MX$SeqV$c3$cVal$mag$f":0.000399634998757392,"MSQI2$MX$SeqV$seqT":0,"MSQI3$MX$ImbNgA$mag$f":0,"MSQI3$MX$ImbNgV$mag$f":0,"MSQI3$MX$ImbZroA$mag$f":0,"MSQI3$MX$ImbZroV$mag$f":0,"MSQI3$MX$SeqA$c1$cVal$ang$f":0,"MSQI3$MX$SeqA$c1$cVal$mag$f":6.18439007666893e-05,"MSQI3$MX$SeqA$c2$cVal$ang$f":0,"MSQI3$MX$SeqA$c2$cVal$mag$f":6.43364692223258e-05,"MSQI3$MX$SeqA$c3$cVal$ang$f":0,"MSQI3$MX$SeqA$c3$cVal$mag$f":0.000193939675227739,"MSQI3$MX$SeqA$seqT":0,"MSQI3$MX$SeqV$c1$cVal$ang$f":0,"MSQI3$MX$SeqV$c1$cVal$mag$f":3.55142219632398e-07,"MSQI3$MX$SeqV$c2$cVal$ang$f":0,"MSQI3$MX$SeqV$c2$cVal$mag$f":0.000320550752803683,"MSQI3$MX$SeqV$c3$cVal$ang$f":0,"MSQI3$MX$SeqV$c3$cVal$mag$f":0.000669123779516667,"MSQI3$MX$SeqV$seqT":0,"MSQI4$MX$ImbNgA$mag$f":0,"MSQI4$MX$ImbNgV$mag$f":0,"MSQI4$MX$ImbZroA$mag$f":0,"MSQI4$MX$ImbZroV$mag$f":0,"MSQI4$MX$SeqA$c1$cVal$ang$f":0,"MSQI4$MX$SeqA$c1$cVal$mag$f":1.05252083812957e-05,"MSQI4$MX$SeqA$c2$cVal$ang$f":0,"MSQI4$MX$SeqA$c2$cVal$mag$f":6.66677306071506e-06,"MSQI4$MX$SeqA$c3$cVal$ang$f":0,"MSQI4$MX$SeqA$c3$cVal$mag$f":0.000135470312670805,"MSQI4$MX$SeqA$seqT":0,"MSQI4$MX$SeqV$c1$cVal$ang$f":0,"MSQI4$MX$SeqV$c1$cVal$mag$f":2.32098762431576e-09,"MSQI4$MX$SeqV$c2$cVal$ang$f":0,"MSQI4$MX$SeqV$c2$cVal$mag$f":3.16639261654927e-06,"MSQI4$MX$SeqV$c3$cVal$ang$f":0,"MSQI4$MX$SeqV$c3$cVal$mag$f":0.000132512417621911,"MSQI4$MX$SeqV$seqT":0,"MSQI5$MX$ImbNgA$mag$f":0,"MSQI5$MX$ImbNgV$mag$f":0,"MSQI5$MX$ImbZroA$mag$f":0,"MSQI5$MX$ImbZroV$mag$f":0,"MSQI5$MX$SeqA$c1$cVal$ang$f":0,"MSQI5$MX$SeqA$c1$cVal$mag$f":5.04406889376696e-05,"MSQI5$MX$SeqA$c2$cVal$ang$f":0,"MSQI5$MX$SeqA$c2$cVal$mag$f":5.09150995640084e-05,"MSQI5$MX$SeqA$c3$cVal$ang$f":0,"MSQI5$MX$SeqA$c3$cVal$mag$f":0.000178682268597186,"MSQI5$MX$SeqA$seqT":0,"MSQI5$MX$SeqV$c1$cVal$ang$f":0,"MSQI5$MX$SeqV$c1$cVal$mag$f":2.56418076105547e-07,"MSQI5$MX$SeqV$c2$cVal$ang$f":0,"MSQI5$MX$SeqV$c2$cVal$mag$f":0.000204816984478384,"MSQI5$MX$SeqV$c3$cVal$ang$f":0,"MSQI5$MX$SeqV$c3$cVal$mag$f":0.000519392138812691,"MSQI5$MX$SeqV$seqT":0,"MSQI6$MX$ImbNgA$mag$f":0,"MSQI6$MX$ImbNgV$mag$f":0,"MSQI6$MX$ImbZroA$mag$f":0,"MSQI6$MX$ImbZroV$mag$f":0,"MSQI6$MX$SeqA$c1$cVal$ang$f":0,"MSQI6$MX$SeqA$c1$cVal$mag$f":3.68404398614075e-05,"MSQI6$MX$SeqA$c2$cVal$ang$f":0,"MSQI6$MX$SeqA$c2$cVal$mag$f":3.74274641217198e-05,"MSQI6$MX$SeqA$c3$cVal$ang$f":0,"MSQI6$MX$SeqA$c3$cVal$mag$f":0.000163955686730333,"MSQI6$MX$SeqA$seqT":0,"MSQI6$MX$SeqV$c1$cVal$ang$f":0,"MSQI6$MX$SeqV$c1$cVal$mag$f":1.47475219591797e-07,"MSQI6$MX$SeqV$c2$cVal$ang$f":0,"MSQI6$MX$SeqV$c2$cVal$mag$f":0.000120074160804506,"MSQI6$MX$SeqV$c3$cVal$ang$f":0,"MSQI6$MX$SeqV$c3$cVal$mag$f":0.000399634998757392,"MSQI6$MX$SeqV$seqT":0}}

View File

@@ -0,0 +1,7 @@
{
"key": "b699a184-9a96-4ab2-81f7-b60dc84c1483",
"messageBody": "{\"devSeries\":\"ca5420a552a727558c3b27ffaaf22a59\",\"limit\":20,\"line\":\"73e0a6553c2d24ee17f2623e14f9776b\",\"realData\":true,\"soeData\":true}",
"retryTimes": 0,
"sendTime": "2026-07-14T15:09:20.405",
"source": "WEB"
}

View File

@@ -1,7 +1,7 @@
{
"key": "5948178e-f193-4c1c-bb77-f164d8c7f17f",
"messageBody": "{\"code\":\"set_log\",\"frontType\":\"cfg_stat_data\",\"grade\":\"TRACE\",\"guid\":\"5948178e-f193-4c1c-bb77-f164d8c7f17f\",\"id\":\"73e0a6553c2d24ee17f2623e14f9776b\",\"level\":\"measurepoint\",\"logtype\":1,\"nodeId\":\"6c2adb324940e19e208d717f4970f2ee\",\"processNo\":1}",
"key": "ede0f3ef-93ea-43e5-8023-61867603162e",
"messageBody": "{\"code\":\"set_log\",\"frontType\":\"cfg_3s_data\",\"grade\":\"TRACE\",\"guid\":\"ede0f3ef-93ea-43e5-8023-61867603162e\",\"id\":\"73e0a6553c2d24ee17f2623e14f9776b\",\"level\":\"measurepoint\",\"logtype\":1,\"nodeId\":\"6c2adb324940e19e208d717f4970f2ee\",\"processNo\":0}",
"retryTimes": 0,
"sendTime": "2026-07-13T14:57:45.340",
"sendTime": "2026-07-14T15:09:20.368",
"source": "WEB"
}

View File

@@ -2,17 +2,19 @@
"devType": "QH_ms",
"deviceId": "ca5420a552a727558c3b27ffaaf22a59",
"deviceName": "自主协议测试终端1",
"frontType": "cfg_stat_data",
"guid": "5948178e-f193-4c1c-bb77-f164d8c7f17f",
"jsonDataPath": "C:\\Users\\lnk\\Desktop\\发送jaon映射xml工具\\runtime\\trace_data\\process_1\\device_自主协议测试终端1_ca5420a552a727558c3b27ffaaf22a59\\monitor_自主协议监测点4_73e0a6553c2d24ee17f2623e14f9776b\\jsondata_01.txt",
"frontType": "cfg_3s_data",
"guid": "ede0f3ef-93ea-43e5-8023-61867603162e",
"jsonDataPath": "D:\\canneng\\发送jaon映射xml工具\\runtime\\trace_data\\process_1\\device_自主协议测试终端1_ca5420a552a727558c3b27ffaaf22a59\\monitor_自主协议监测点4_73e0a6553c2d24ee17f2623e14f9776b\\jsondata_01.txt",
"jsonDataPaths": [
"C:\\Users\\lnk\\Desktop\\发送jaon映射xml工具\\runtime\\trace_data\\process_1\\device_自主协议测试终端1_ca5420a552a727558c3b27ffaaf22a59\\monitor_自主协议监测点4_73e0a6553c2d24ee17f2623e14f9776b\\jsondata_01.txt",
"C:\\Users\\lnk\\Desktop\\发送jaon映射xml工具\\runtime\\trace_data\\process_1\\device_自主协议测试终端1_ca5420a552a727558c3b27ffaaf22a59\\monitor_自主协议监测点4_73e0a6553c2d24ee17f2623e14f9776b\\jsondata_02.txt",
"C:\\Users\\lnk\\Desktop\\发送jaon映射xml工具\\runtime\\trace_data\\process_1\\device_自主协议测试终端1_ca5420a552a727558c3b27ffaaf22a59\\monitor_自主协议监测点4_73e0a6553c2d24ee17f2623e14f9776b\\jsondata_03.txt"
"D:\\canneng\\发送jaon映射xml工具\\runtime\\trace_data\\process_1\\device_自主协议测试终端1_ca5420a552a727558c3b27ffaaf22a59\\monitor_自主协议监测点4_73e0a6553c2d24ee17f2623e14f9776b\\jsondata_01.txt",
"D:\\canneng\\发送jaon映射xml工具\\runtime\\trace_data\\process_1\\device_自主协议测试终端1_ca5420a552a727558c3b27ffaaf22a59\\monitor_自主协议监测点4_73e0a6553c2d24ee17f2623e14f9776b\\jsondata_02.txt",
"D:\\canneng\\发送jaon映射xml工具\\runtime\\trace_data\\process_1\\device_自主协议测试终端1_ca5420a552a727558c3b27ffaaf22a59\\monitor_自主协议监测点4_73e0a6553c2d24ee17f2623e14f9776b\\jsondata_03.txt"
],
"monitorId": "73e0a6553c2d24ee17f2623e14f9776b",
"monitorName": "自主协议监测点4",
"outputPath": "C:\\Users\\lnk\\Desktop\\发送jaon映射xml工具\\runtime\\trace_data\\process_1\\device_自主协议测试终端1_ca5420a552a727558c3b27ffaaf22a59\\monitor_自主协议监测点4_73e0a6553c2d24ee17f2623e14f9776b\\final_sorted.xls",
"outputPath": "D:\\canneng\\发送jaon映射xml工具\\runtime\\trace_data\\process_1\\device_自主协议测试终端1_ca5420a552a727558c3b27ffaaf22a59\\monitor_自主协议监测点4_73e0a6553c2d24ee17f2623e14f9776b\\final_sorted.xls",
"processNo": "1",
"xmlPath": "C:\\Users\\lnk\\Desktop\\发送jaon映射xml工具\\runtime\\downloaded_xml\\be3e57c36220ed847a4acfa62c59dc38.xml"
"startedAt": "2026-07-14T15:09:20.372",
"startedAtMs": 1784012960369,
"xmlPath": "D:\\canneng\\发送jaon映射xml工具\\runtime\\downloaded_xml\\be3e57c36220ed847a4acfa62c59dc38.xml"
}

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
{"mp_id":"73e0a6553c2d24ee17f2623e14f9776b","v_wiring_type":"0","rpt_id":"rptFluc","rpt_no":2,"func_type":100,"data_time":1554315000000,"voltage_level":"0.000000","dev_type":"QH_ms","mms_json":{"MMXU2$MX$FLUC$phsA$cVal$mag$f":0,"MMXU2$MX$FLUC$phsB$cVal$mag$f":0,"MMXU2$MX$FLUC$phsC$cVal$mag$f":0}}

View File

@@ -1 +0,0 @@
{"mp_id":"73e0a6553c2d24ee17f2623e14f9776b","v_wiring_type":"0","rpt_id":"rptFluc","rpt_no":2,"func_type":100,"data_time":1554315600000,"voltage_level":"0.000000","dev_type":"QH_ms","mms_json":{"MMXU2$MX$FLUC$phsA$cVal$mag$f":0,"MMXU2$MX$FLUC$phsB$cVal$mag$f":0,"MMXU2$MX$FLUC$phsC$cVal$mag$f":0}}

View File

@@ -1 +0,0 @@
{"mp_id":"73e0a6553c2d24ee17f2623e14f9776b","v_wiring_type":"0","rpt_id":"rptPST","rpt_no":1,"func_type":100,"data_time":1554315000000,"voltage_level":"0.000000","dev_type":"QH_ms","mms_json":{"MFLK1$MX$PhPst$phsA$cVal$mag$f":0,"MFLK1$MX$PhPst$phsB$cVal$mag$f":0,"MFLK1$MX$PhPst$phsC$cVal$mag$f":0}}

View File

@@ -1 +0,0 @@
{"mp_id":"73e0a6553c2d24ee17f2623e14f9776b","v_wiring_type":"0","rpt_id":"rptPST","rpt_no":1,"func_type":100,"data_time":1554315600000,"voltage_level":"0.000000","dev_type":"QH_ms","mms_json":{"MFLK1$MX$PhPst$phsA$cVal$mag$f":0,"MFLK1$MX$PhPst$phsB$cVal$mag$f":0,"MFLK1$MX$PhPst$phsC$cVal$mag$f":0}}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
{"mp_id":"73e0a6553c2d24ee17f2623e14f9776b","v_wiring_type":"0","rpt_id":"rptStMSQI","rpt_no":5,"func_type":100,"data_time":1554314940000,"voltage_level":"0.000000","dev_type":"QH_ms","mms_json":{"MSQI2$MX$ImbNgA$mag$f":0,"MSQI2$MX$ImbNgV$mag$f":0,"MSQI2$MX$ImbZroA$mag$f":0,"MSQI2$MX$ImbZroV$mag$f":0,"MSQI2$MX$SeqA$c1$cVal$ang$f":0,"MSQI2$MX$SeqA$c1$cVal$mag$f":3.81384634238202e-05,"MSQI2$MX$SeqA$c2$cVal$ang$f":0,"MSQI2$MX$SeqA$c2$cVal$mag$f":3.8304668123601e-05,"MSQI2$MX$SeqA$c3$cVal$ang$f":0,"MSQI2$MX$SeqA$c3$cVal$mag$f":0.000161453004693612,"MSQI2$MX$SeqA$seqT":0,"MSQI2$MX$SeqV$c1$cVal$ang$f":0,"MSQI2$MX$SeqV$c1$cVal$mag$f":1.43297441468349e-07,"MSQI2$MX$SeqV$c2$cVal$ang$f":0,"MSQI2$MX$SeqV$c2$cVal$mag$f":0.000124581449199468,"MSQI2$MX$SeqV$c3$cVal$ang$f":0,"MSQI2$MX$SeqV$c3$cVal$mag$f":0.000413570844102651,"MSQI2$MX$SeqV$seqT":0,"MSQI3$MX$ImbNgA$mag$f":0,"MSQI3$MX$ImbNgV$mag$f":0,"MSQI3$MX$ImbZroA$mag$f":0,"MSQI3$MX$ImbZroV$mag$f":0,"MSQI3$MX$SeqA$c1$cVal$ang$f":0,"MSQI3$MX$SeqA$c1$cVal$mag$f":6.48184504825622e-05,"MSQI3$MX$SeqA$c2$cVal$ang$f":0,"MSQI3$MX$SeqA$c2$cVal$mag$f":6.23395317234099e-05,"MSQI3$MX$SeqA$c3$cVal$ang$f":0,"MSQI3$MX$SeqA$c3$cVal$mag$f":0.000188535108463839,"MSQI3$MX$SeqA$seqT":0,"MSQI3$MX$SeqV$c1$cVal$ang$f":0,"MSQI3$MX$SeqV$c1$cVal$mag$f":3.47228279906631e-07,"MSQI3$MX$SeqV$c2$cVal$ang$f":0,"MSQI3$MX$SeqV$c2$cVal$mag$f":0.000331826304318383,"MSQI3$MX$SeqV$c3$cVal$ang$f":0,"MSQI3$MX$SeqV$c3$cVal$mag$f":0.000654798583127558,"MSQI3$MX$SeqV$seqT":0,"MSQI4$MX$ImbNgA$mag$f":0,"MSQI4$MX$ImbNgV$mag$f":0,"MSQI4$MX$ImbZroA$mag$f":0,"MSQI4$MX$ImbZroV$mag$f":0,"MSQI4$MX$SeqA$c1$cVal$ang$f":0,"MSQI4$MX$SeqA$c1$cVal$mag$f":1.09114616861916e-05,"MSQI4$MX$SeqA$c2$cVal$ang$f":0,"MSQI4$MX$SeqA$c2$cVal$mag$f":1.10286291601369e-05,"MSQI4$MX$SeqA$c3$cVal$ang$f":0,"MSQI4$MX$SeqA$c3$cVal$mag$f":0.000130246393382549,"MSQI4$MX$SeqA$seqT":0,"MSQI4$MX$SeqV$c1$cVal$ang$f":0,"MSQI4$MX$SeqV$c1$cVal$mag$f":7.38720085102784e-10,"MSQI4$MX$SeqV$c2$cVal$ang$f":0,"MSQI4$MX$SeqV$c2$cVal$mag$f":7.02868248936284e-07,"MSQI4$MX$SeqV$c3$cVal$ang$f":0,"MSQI4$MX$SeqV$c3$cVal$mag$f":0.000135239184601232,"MSQI4$MX$SeqV$seqT":0,"MSQI5$MX$ImbNgA$mag$f":0,"MSQI5$MX$ImbNgV$mag$f":0,"MSQI5$MX$ImbZroA$mag$f":0,"MSQI5$MX$ImbZroV$mag$f":0,"MSQI5$MX$SeqA$c1$cVal$ang$f":0,"MSQI5$MX$SeqA$c1$cVal$mag$f":5.08828888996504e-05,"MSQI5$MX$SeqA$c2$cVal$ang$f":0,"MSQI5$MX$SeqA$c2$cVal$mag$f":5.18556116730906e-05,"MSQI5$MX$SeqA$c3$cVal$ang$f":0,"MSQI5$MX$SeqA$c3$cVal$mag$f":0.000175979032064788,"MSQI5$MX$SeqA$seqT":0,"MSQI5$MX$SeqV$c1$cVal$ang$f":0,"MSQI5$MX$SeqV$c1$cVal$mag$f":2.46728177444311e-07,"MSQI5$MX$SeqV$c2$cVal$ang$f":0,"MSQI5$MX$SeqV$c2$cVal$mag$f":0.000215921842027456,"MSQI5$MX$SeqV$c3$cVal$ang$f":0,"MSQI5$MX$SeqV$c3$cVal$mag$f":0.000542122579645365,"MSQI5$MX$SeqV$seqT":0,"MSQI6$MX$ImbNgA$mag$f":0,"MSQI6$MX$ImbNgV$mag$f":0,"MSQI6$MX$ImbZroA$mag$f":0,"MSQI6$MX$ImbZroV$mag$f":0,"MSQI6$MX$SeqA$c1$cVal$ang$f":0,"MSQI6$MX$SeqA$c1$cVal$mag$f":3.81384634238202e-05,"MSQI6$MX$SeqA$c2$cVal$ang$f":0,"MSQI6$MX$SeqA$c2$cVal$mag$f":3.8304668123601e-05,"MSQI6$MX$SeqA$c3$cVal$ang$f":0,"MSQI6$MX$SeqA$c3$cVal$mag$f":0.000161453004693612,"MSQI6$MX$SeqA$seqT":0,"MSQI6$MX$SeqV$c1$cVal$ang$f":0,"MSQI6$MX$SeqV$c1$cVal$mag$f":1.43297441468349e-07,"MSQI6$MX$SeqV$c2$cVal$ang$f":0,"MSQI6$MX$SeqV$c2$cVal$mag$f":0.000124581449199468,"MSQI6$MX$SeqV$c3$cVal$ang$f":0,"MSQI6$MX$SeqV$c3$cVal$mag$f":0.000413570844102651,"MSQI6$MX$SeqV$seqT":0}}

View File

@@ -1 +0,0 @@
{"mp_id":"73e0a6553c2d24ee17f2623e14f9776b","v_wiring_type":"0","rpt_id":"rptStMSQI","rpt_no":5,"func_type":100,"data_time":1554315120000,"voltage_level":"0.000000","dev_type":"QH_ms","mms_json":{"MSQI2$MX$ImbNgA$mag$f":0,"MSQI2$MX$ImbNgV$mag$f":0,"MSQI2$MX$ImbZroA$mag$f":0,"MSQI2$MX$ImbZroV$mag$f":0,"MSQI2$MX$SeqA$c1$cVal$ang$f":0,"MSQI2$MX$SeqA$c1$cVal$mag$f":3.77992801077198e-05,"MSQI2$MX$SeqA$c2$cVal$ang$f":0,"MSQI2$MX$SeqA$c2$cVal$mag$f":3.71770620404277e-05,"MSQI2$MX$SeqA$c3$cVal$ang$f":0,"MSQI2$MX$SeqA$c3$cVal$mag$f":0.000162459997227415,"MSQI2$MX$SeqA$seqT":0,"MSQI2$MX$SeqV$c1$cVal$ang$f":0,"MSQI2$MX$SeqV$c1$cVal$mag$f":1.46863996519642e-07,"MSQI2$MX$SeqV$c2$cVal$ang$f":0,"MSQI2$MX$SeqV$c2$cVal$mag$f":0.000123170510050841,"MSQI2$MX$SeqV$c3$cVal$ang$f":0,"MSQI2$MX$SeqV$c3$cVal$mag$f":0.000407903105951846,"MSQI2$MX$SeqV$seqT":0,"MSQI3$MX$ImbNgA$mag$f":0,"MSQI3$MX$ImbNgV$mag$f":0,"MSQI3$MX$ImbZroA$mag$f":0,"MSQI3$MX$ImbZroV$mag$f":0,"MSQI3$MX$SeqA$c1$cVal$ang$f":0,"MSQI3$MX$SeqA$c1$cVal$mag$f":6.66953565087169e-05,"MSQI3$MX$SeqA$c2$cVal$ang$f":0,"MSQI3$MX$SeqA$c2$cVal$mag$f":5.95236160734203e-05,"MSQI3$MX$SeqA$c3$cVal$ang$f":0,"MSQI3$MX$SeqA$c3$cVal$mag$f":0.000194157313671894,"MSQI3$MX$SeqA$seqT":0,"MSQI3$MX$SeqV$c1$cVal$ang$f":0,"MSQI3$MX$SeqV$c1$cVal$mag$f":3.83766177947109e-07,"MSQI3$MX$SeqV$c2$cVal$ang$f":0,"MSQI3$MX$SeqV$c2$cVal$mag$f":0.000363907107384875,"MSQI3$MX$SeqV$c3$cVal$ang$f":0,"MSQI3$MX$SeqV$c3$cVal$mag$f":0.000638670870102942,"MSQI3$MX$SeqV$seqT":0,"MSQI4$MX$ImbNgA$mag$f":0,"MSQI4$MX$ImbNgV$mag$f":0,"MSQI4$MX$ImbZroA$mag$f":0,"MSQI4$MX$ImbZroV$mag$f":0,"MSQI4$MX$SeqA$c1$cVal$ang$f":0,"MSQI4$MX$SeqA$c1$cVal$mag$f":1.19578453450231e-05,"MSQI4$MX$SeqA$c2$cVal$ang$f":0,"MSQI4$MX$SeqA$c2$cVal$mag$f":9.52819573285524e-06,"MSQI4$MX$SeqA$c3$cVal$ang$f":0,"MSQI4$MX$SeqA$c3$cVal$mag$f":0.000127479725051671,"MSQI4$MX$SeqA$seqT":0,"MSQI4$MX$SeqV$c1$cVal$ang$f":0,"MSQI4$MX$SeqV$c1$cVal$mag$f":6.87537715649e-09,"MSQI4$MX$SeqV$c2$cVal$ang$f":0,"MSQI4$MX$SeqV$c2$cVal$mag$f":1.64034304361849e-06,"MSQI4$MX$SeqV$c3$cVal$ang$f":0,"MSQI4$MX$SeqV$c3$cVal$mag$f":0.000147920189192519,"MSQI4$MX$SeqV$seqT":0,"MSQI5$MX$ImbNgA$mag$f":0,"MSQI5$MX$ImbNgV$mag$f":0,"MSQI5$MX$ImbZroA$mag$f":0,"MSQI5$MX$ImbZroV$mag$f":0,"MSQI5$MX$SeqA$c1$cVal$ang$f":0,"MSQI5$MX$SeqA$c1$cVal$mag$f":5.11015205120202e-05,"MSQI5$MX$SeqA$c2$cVal$ang$f":0,"MSQI5$MX$SeqA$c2$cVal$mag$f":5.05749376316089e-05,"MSQI5$MX$SeqA$c3$cVal$ang$f":0,"MSQI5$MX$SeqA$c3$cVal$mag$f":0.000177015652297996,"MSQI5$MX$SeqA$seqT":0,"MSQI5$MX$SeqV$c1$cVal$ang$f":0,"MSQI5$MX$SeqV$c1$cVal$mag$f":2.514420884836e-07,"MSQI5$MX$SeqV$c2$cVal$ang$f":0,"MSQI5$MX$SeqV$c2$cVal$mag$f":0.000213714985875413,"MSQI5$MX$SeqV$c3$cVal$ang$f":0,"MSQI5$MX$SeqV$c3$cVal$mag$f":0.000528822594787925,"MSQI5$MX$SeqV$seqT":0,"MSQI6$MX$ImbNgA$mag$f":0,"MSQI6$MX$ImbNgV$mag$f":0,"MSQI6$MX$ImbZroA$mag$f":0,"MSQI6$MX$ImbZroV$mag$f":0,"MSQI6$MX$SeqA$c1$cVal$ang$f":0,"MSQI6$MX$SeqA$c1$cVal$mag$f":3.77992801077198e-05,"MSQI6$MX$SeqA$c2$cVal$ang$f":0,"MSQI6$MX$SeqA$c2$cVal$mag$f":3.71770620404277e-05,"MSQI6$MX$SeqA$c3$cVal$ang$f":0,"MSQI6$MX$SeqA$c3$cVal$mag$f":0.000162459997227415,"MSQI6$MX$SeqA$seqT":0,"MSQI6$MX$SeqV$c1$cVal$ang$f":0,"MSQI6$MX$SeqV$c1$cVal$mag$f":1.46863996519642e-07,"MSQI6$MX$SeqV$c2$cVal$ang$f":0,"MSQI6$MX$SeqV$c2$cVal$mag$f":0.000123170510050841,"MSQI6$MX$SeqV$c3$cVal$ang$f":0,"MSQI6$MX$SeqV$c3$cVal$mag$f":0.000407903105951846,"MSQI6$MX$SeqV$seqT":0}}

View File

@@ -1,509 +0,0 @@
<?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="江西灿能PQS-882B4">
<!--注:暂态事件解析规则配置 Flag0-不分相 1-分相 如果Flag=0 ABC配置成一样如果Flag=1ABC根据实际配置-->
<WavePhasic Flag="1" A="QVVR1" B="QVVR2" C="QVVR3" />
<!--暂态事件持续事件单位0-毫秒 1--->
<UnitOfTime Unit="0" />
<!--上送值的时间UTC-UTC时间 beijing-北京时间-->
<ValueOfTime Unit="beijing" />
<!--录波文件的时间UTC-UTC时间 beijing-北京时间-->
<ComtradeFile WaveTimeFlag="beijing" />
<IED name="PQ" desc="电能质量监测装置" />
<LDevice Prefix="PQLD" 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为变位触发报告-->
<ReportStat>
<Report ReportControl="LLN0$BR$brcbPLT,600,1,0,0,0,0,yes,1,1,1,1,1,0,1,1,1,3,1" />
<Report ReportControl="LLN0$BR$brcbPST,600,1,0,0,0,0,yes,1,1,1,1,1,0,1,1,1,3,2" />
<Report ReportControl="LLN0$BR$brcbFluc,600,1,0,0,0,0,yes,1,1,1,1,1,0,1,1,1,3,2" />
<Report ReportControl="LLN0$BR$brcbStIHarm,60,1,0,0,0,0,yes,1,1,1,1,1,0,1,1,1,1,0" />
<Report ReportControl="LLN0$BR$brcbStHarm,60,1,0,0,0,0,yes,1,1,1,1,1,0,1,1,1,1,0" />
<Report ReportControl="LLN0$BR$brcbStMSQI,60,1,0,0,0,0,yes,1,1,1,1,1,0,1,1,1,1,0" />
<Report ReportControl="LLN0$BR$brcbStMMXU,60,1,0,0,0,0,yes,1,1,1,1,1,0,1,1,1,1,0" />
</ReportStat>
<ReportReal>
<Report ReportControl="LLN0$BR$brcbPLT,600,1,0,0,0,0,yes,1,1,1,1,1,0,1,1,1,3,1" />
<Report ReportControl="LLN0$BR$brcbPST,600,1,0,0,0,0,yes,1,1,1,1,1,0,1,1,1,3,1" />
<Report ReportControl="LLN0$BR$brcbFluc,600,1,0,0,0,0,yes,1,1,1,1,1,0,1,1,1,3,1" />
<Report ReportControl="LLN0$RP$urcbRtIHarm,3,1,0,0,0,0,yes,1,1,1,1,1,0,1,1,1,3,0" />
<Report ReportControl="LLN0$RP$urcbRtHarm,3,1,0,0,0,0,yes,1,1,1,1,1,0,1,1,1,3,0" />
<Report ReportControl="LLN0$RP$urcbRtMSQI,3,1,0,0,0,0,yes,1,1,1,1,1,0,1,1,1,3,0" />
<Report ReportControl="LLN0$RP$urcbRtMMXU,3,1,0,0,0,0,yes,1,1,1,1,1,0,1,1,1,3,0" />
</ReportReal>
<ReportEvent>
<Report ReportControl="LLN0$BR$brcbEveQVVR,60,1,0,0,0,0,yes,1,1,1,1,1,0,1,1,1,8,1,0"/>
<Report ReportControl="LLN0$BR$brcbEveRDRE,60,1,0,0,0,0,yes,1,1,1,1,1,0,1,1,1,8,1,0"/>
<Report ReportControl="LLN0$BR$brcbAlarm,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="FLAG" value="0" desc="剔除标记" type="2" />
<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="MFLK1$MX$PhPst" DA="phs*$cVal$mag$f" />
<Value name="FLUC" desc="电压波动幅值" type="6" DO="MMXU2$MX$FLUC" DA="phs*$cVal$mag$f" />
<Value name="FLUCF" desc="电压波动频度" type="6" DO="MMXU2$MX$FLUCCF" DA="phs*$cVal$mag$f" />
</Sequence>
<Sequence name="SEQ" value="112" desc="相别" type="5" >
<Value name="PST" desc="短时闪变" type="6" DO="MFLK1$MX$PPPst" DA="phs*$cVal$mag$f" />
<Value name="FLUC" desc="电压波动幅度" type="6" DO="MMXU2$MX$PPFluc" DA="phs*$cVal$mag$f" />
<Value name="FLUCF" desc="电压波动频度" type="6" DO="MMXU2$MX$PPFlucf" DA="phs*$cVal$mag$f" />
</Sequence>
</Item>
</Monitor>
</DataType>
<DataType name="DATA_TYPE" value="03" 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="F_L" desc="闪变" type="4" >
<Sequence name="SEQ" value="7" desc="相别" type="5" >
<Value name="PLT" desc="长时闪变" type="6" DO="MFLK2$MX$PhPlt" DA="phs*$cVal$mag$f" />
</Sequence>
<Sequence name="SEQ" value="112" desc="相别" type="5" >
<Value name="PLT" desc="长时闪变" type="6" DO="MFLK2$MX$PPPlt" DA="phs*$cVal$mag$f" />
</Sequence>
</Item>
</Monitor>
</DataType>
<DataType name="DATA_TYPE" value="04" 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="QVVR0$MX$VVa" DA="mag$f" />
<Value name="DUR" 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="" />
</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="MMXU5$MX$VolDev" DA="phs*$cVal$mag$f" BaseFlag="1" LimitUp="20" LimitDown="-20" Coefficient="1"/>
<Value name="DELTA_V" desc="电压偏差平均值" type="6" DO="MMXU2$MX$VolDev" DA="phs*$cVal$mag$f" BaseFlag="1" LimitUp="20" LimitDown="-20" Coefficient="1"/>
<Value name="MAX_DELTA_V" desc="电压偏差最大值" type="6" DO="MMXU3$MX$VolDev" DA="phs*$cVal$mag$f" BaseFlag="1" LimitUp="20" LimitDown="-20" Coefficient="1"/>
<Value name="MIN_DELTA_V" desc="电压偏差最小值" type="6" DO="MMXU4$MX$VolDev" DA="phs*$cVal$mag$f" BaseFlag="1" LimitUp="20" LimitDown="-20" Coefficient="1"/>
<Value name="G_VRMS" desc="电压有效值95值" type="6" DO="MMXU5$MX$PhV" DA="phs*$cVal$mag$f" BaseFlag="1" LimitUp="0*%U" LimitDown="150*%U" />
<Value name="VRMS" desc="电压有效值平均值" type="6" DO="MMXU2$MX$PhV" DA="phs*$cVal$mag$f" BaseFlag="1" LimitUp="0*%U" LimitDown="150*%U" />
<Value name="MAX_VRMS" desc="电压有效值最大值" type="6" DO="MMXU3$MX$PhV" DA="phs*$cVal$mag$f" BaseFlag="1" LimitUp="0*%U" LimitDown="150*%U" />
<Value name="MIN_VRMS" desc="电压有效值最小值" type="6" DO="MMXU4$MX$PhV" DA="phs*$cVal$mag$f" BaseFlag="1" LimitUp="0*%U" LimitDown="150*%U" />
<Value name="G_VRMS_LVR" desc="线电压有效值95值" type="6" DO="MMXU5$MX$PPV" DA="l_phs*$cVal$mag$f" BaseFlag="1" LimitUp="0*%U" LimitDown="150*%U" />
<Value name="VRMS_LVR" desc="线电压有效值平均值" type="6" DO="MMXU2$MX$PPV" DA="l_phs*$cVal$mag$f" BaseFlag="1" LimitUp="0*%U" LimitDown="150*%U" />
<Value name="MAX_VRMS_LVR" desc="线电压有效值最大值" type="6" DO="MMXU3$MX$PPV" DA="l_phs*$cVal$mag$f" BaseFlag="1" LimitUp="0*%U" LimitDown="150*%U" />
<Value name="MIN_VRMS_LVR" desc="线电压有效值最小值" type="6" DO="MMXU4$MX$PPV" DA="l_phs*$cVal$mag$f" BaseFlag="1" LimitUp="0*%U" LimitDown="150*%U" />
<Value name="G_VTHD" desc="电压总谐波畸变率95值" type="6" DO="MHAI5$MX$ThdPhV" DA="phs*$cVal$mag$f" />
<Value name="VTHD" desc="电压总谐波畸变率平均值" type="6" DO="MHAI2$MX$ThdPhV" DA="phs*$cVal$mag$f" />
<Value name="MAX_VTHD" desc="电压总谐波畸变率最大值" type="6" DO="MHAI3$MX$ThdPhV" DA="phs*$cVal$mag$f" />
<Value name="MIN_VTHD" desc="电压总谐波畸变率最小值" type="6" DO="MHAI4$MX$ThdPhV" DA="phs*$cVal$mag$f" />
<Value name="G_VFUND_ANGLE" desc="基波电压相角95值" type="6" DO="MHAI5$MX$FundPhVAng" DA="phs*$cVal$ang$f" />
<Value name="VFUND_ANGLE" desc="基波电压相角平均值" type="6" DO="MHAI2$MX$FundPhVAng" DA="phs*$cVal$ang$f" />
<Value name="MAX_VFUND_ANGLE" desc="基波电压相角最大值" type="6" DO="MHAI3$MX$FundPhVAng" DA="phs*$cVal$ang$f" />
<Value name="MIN_VFUND_ANGLE" desc="基波电压相角最小值" type="6" DO="MHAI4$MX$FundPhVAng" DA="phs*$cVal$ang$f" />
<Value name="G_V1" desc="基波电压有效值95值" type="6" DO="MHAI5$MX$FundPhV" DA="phs*$cVal$mag$f" />
<Value name="V1" desc="基波电压有效值平均值" type="6" DO="MHAI2$MX$FundPhV" DA="phs*$cVal$mag$f" />
<Value name="MAX_V1" desc="基波电压有效值最大值" type="6" DO="MHAI3$MX$FundPhV" DA="phs*$cVal$mag$f" />
<Value name="MIN_V1" desc="基波电压有效值最小值" type="6" DO="MHAI4$MX$FundPhV" DA="phs*$cVal$mag$f" />
<Value name="G_SV_%0,49%" desc="间谐波电压含有率(f25-2475)95值" type="6" DO="MHAI11$MX$HPhV" DA="phs*Har[%-0]$mag$f" />
<Value name="SV_%0,49%" desc="间谐波电压含有率(f25-2475)平均值" type="6" DO="MHAI8$MX$HPhV" DA="phs*Har[%-0]$mag$f" />
<Value name="MAX_SV_%0,49%" desc="间谐波电压含有率(f25-2475)最大值" type="6" DO="MHAI9$MX$HPhV" DA="phs*Har[%-0]$mag$f" />
<Value name="MIN_SV_%0,49%" desc="间谐波电压含有率(f25-2475)最小值" type="6" DO="MHAI10$MX$HPhV" DA="phs*Har[%-0]$mag$f" />
<Value name="G_V%2,50%" desc="谐波电压含有率(2-50)95值" type="6" DO="MHAI5$MX$HPhV" DA="phs*Har[%-2]$mag$f" />
<Value name="V%2,50%" desc="谐波电压含有率(2-50)平均值" type="6" DO="MHAI2$MX$HPhV" DA="phs*Har[%-2]$mag$f" />
<Value name="MAX_V%2,50%" desc="谐波电压含有率(2-50)最大值" type="6" DO="MHAI3$MX$HPhV" DA="phs*Har[%-2]$mag$f" />
<Value name="MIN_V%2,50%" desc="谐波电压含有率(2-50)最小值" type="6" DO="MHAI4$MX$HPhV" DA="phs*Har[%-2]$mag$f" />
<Value name="G_VA%2,50%" desc="谐波电压相角(2-50)95值" type="6" DO="MHAI5$MX$HPhV" DA="phs*Har[%-2]$ang$f" />
<Value name="VA%2,50%" desc="谐波电压相角(2-50)平均值" type="6" DO="MHAI2$MX$HPhV" DA="phs*Har[%-2]$ang$f" />
<Value name="MAX_VA%2,50%" desc="谐波电压相角(2-50)最大值" type="6" DO="MHAI3$MX$HPhV" DA="phs*Har[%-2]$ang$f" />
<Value name="MIN_VA%2,50%" desc="谐波电压相角(2-50)最小值" type="6" DO="MHAI4$MX$HPhV" DA="phs*Har[%-2]$ang$f" />
</Sequence>
<!--电压V部分(A-C相)角型接线-->
<Sequence name="SEQ" value="112" desc="相别(A-C相)" type="5">
<!--Coefficient 值系数转成kafka的时候乘以系数BaseFlag 基础数据标志 0或没有-非基础数据 1-基础数据LimitUp 数据合理上限,如果包含*%UN字符表示需要乘以电压等级电压如果包含*%U字符表示需要乘以电压等级/1.732电压例如220kV则相电压上限为1.5*220LimitDown 数据合理下限 如果包含*%U字符表示需要乘以电压等级电压-->
<Value name="G_VRMS_LVR" desc="电压有效值95值" type="6" DO="MMXU5$MX$PPV" DA="phs*$cVal$mag$f" BaseFlag="1" LimitUp="0*%U" LimitDown="150*%U" />
<Value name="VRMS_LVR" desc="电压有效值平均值" type="6" DO="MMXU2$MX$PPV" DA="phs*$cVal$mag$f" BaseFlag="1" LimitUp="0*%U" LimitDown="150*%U" />
<Value name="MAX_VRMS_LVR" desc="电压有效值最大值" type="6" DO="MMXU3$MX$PPV" DA="phs*$cVal$mag$f" BaseFlag="1" LimitUp="0*%U" LimitDown="150*%U" />
<Value name="MIN_VRMS_LVR" desc="电压有效值最小值" type="6" DO="MMXU4$MX$PPV" DA="phs*$cVal$mag$f" BaseFlag="1" LimitUp="0*%U" LimitDown="150*%U" />
<Value name="G_VFUND_ANGLE" desc="基波电压相角95值" type="6" DO="MHAI5$MX$FundPPVAng" DA="phs*$cVal$ang$f" />
<Value name="VFUND_ANGLE" desc="基波电压相角平均值" type="6" DO="MHAI2$MX$FundPPVAng" DA="phs*$cVal$ang$f" />
<Value name="MAX_VFUND_ANGLE" desc="基波电压相角最大值" type="6" DO="MHAI3$MX$FundPPVAng" DA="phs*$cVal$ang$f" />
<Value name="MIN_VFUND_ANGLE" desc="基波电压相角最小值" type="6" DO="MHAI4$MX$FundPPVAng" DA="phs*$cVal$ang$f" />
<Value name="G_V1" desc="基波电压有效值95值" type="6" DO="MHAI5$MX$FundPPV" DA="phs*$cVal$mag$f" />
<Value name="V1" desc="基波电压有效值平均值" type="6" DO="MHAI2$MX$FundPPV" DA="phs*$cVal$mag$f" />
<Value name="MAX_V1" desc="基波电压有效值最大值" type="6" DO="MHAI3$MX$FundPPV" DA="phs*$cVal$mag$f" />
<Value name="MIN_V1" desc="基波电压有效值最小值" type="6" DO="MHAI4$MX$FundPPV" DA="phs*$cVal$mag$f" />
<Value name="G_DELTA_V" desc="电压偏差95值" type="6" DO="MMXU5$MX$PPVDev" DA="phs*$cVal$mag$f" BaseFlag="1" LimitUp="20" LimitDown="-20" Coefficient="1"/>
<Value name="DELTA_V" desc="电压偏差平均值" type="6" DO="MMXU2$MX$PPVDev" DA="phs*$cVal$mag$f" BaseFlag="1" LimitUp="20" LimitDown="-20" Coefficient="1"/>
<Value name="MAX_DELTA_V" desc="电压偏差最大值" type="6" DO="MMXU3$MX$PPVDev" DA="phs*$cVal$mag$f" BaseFlag="1" LimitUp="20" LimitDown="-20" Coefficient="1"/>
<Value name="MIN_DELTA_V" desc="电压偏差最小值" type="6" DO="MMXU4$MX$PPVDev" DA="phs*$cVal$mag$f" BaseFlag="1" LimitUp="20" LimitDown="-20" Coefficient="1"/>
<Value name="G_VTHD" desc="电压总谐波畸变率95值" type="6" DO="MHAI5$MX$ThdPPV" DA="phs*$cVal$mag$f" />
<Value name="VTHD" desc="电压总谐波畸变率平均值" type="6" DO="MHAI2$MX$ThdPPV" DA="phs*$cVal$mag$f" />
<Value name="MAX_VTHD" desc="电压总谐波畸变率最大值" type="6" DO="MHAI3$MX$ThdPPV" DA="phs*$cVal$mag$f" />
<Value name="MIN_VTHD" desc="电压总谐波畸变率最小值" type="6" DO="MHAI4$MX$ThdPPV" DA="phs*$cVal$mag$f" />
<Value name="G_SV_%0,49%" desc="间谐波电压含有率(f25-2475)95值" type="6" DO="MHAI11$MX$HRPPV" DA="phs*Har[%-0]$mag$f" />
<Value name="SV_%0,49%" desc="间谐波电压含有率(f25-2475)平均值" type="6" DO="MHAI8$MX$HRPPV" DA="phs*Har[%-0]$mag$f" />
<Value name="MAX_SV_%0,49%" desc="间谐波电压含有率(f25-2475)最大值" type="6" DO="MHAI9$MX$HRPPV" DA="phs*Har[%-0]$mag$f" />
<Value name="MIN_SV_%0,49%" desc="间谐波电压含有率(f25-2475)最小值" type="6" DO="MHAI10$MX$HRPPV" DA="phs*Har[%-0]$mag$f" />
<Value name="G_V%2,50%" desc="谐波电压含有率(2-50)95值" type="6" DO="MHAI5$MX$HRPPV" DA="phs*Har[%-2]$mag$f" />
<Value name="V%2,50%" desc="谐波电压含有率(2-50)平均值" type="6" DO="MHAI2$MX$HRPPV" DA="phs*Har[%-2]$mag$f" />
<Value name="MAX_V%2,50%" desc="谐波电压含有率(2-50)最大值" type="6" DO="MHAI3$MX$HRPPV" DA="phs*Har[%-2]$mag$f" />
<Value name="MIN_V%2,50%" desc="谐波电压含有率(2-50)最小值" type="6" DO="MHAI4$MX$HRPPV" DA="phs*Har[%-2]$mag$f" />
<Value name="G_VA%2,50%" desc="谐波电压相角(2-50)95值" type="6" DO="MHAI5$MX$HRPPV" DA="phs*Har[%-2]$ang$f" />
<Value name="VA%2,50%" desc="谐波电压相角(2-50)平均值" type="6" DO="MHAI2$MX$HRPPV" DA="phs*Har[%-2]$ang$f" />
<Value name="MAX_VA%2,50%" desc="谐波电压相角(2-50)最大值" type="6" DO="MHAI3$MX$HRPPV" DA="phs*Har[%-2]$ang$f" />
<Value name="MIN_VA%2,50%" desc="谐波电压相角(2-50)最小值" type="6" DO="MHAI4$MX$HRPPV" DA="phs*Har[%-2]$ang$f" />
</Sequence>
<Sequence name="SEQ" value="8" desc="相别(T相)" type="5">
<!--电压V部分(T相)-->
<Value name="G_VNSEQ" desc="负序电压95值" type="6" DO="MSQI5$MX$SeqV" DA="c2$cVal$mag$f" />
<Value name="VNSEQ" desc="负序电压平均值" type="6" DO="MSQI2$MX$SeqV" DA="c2$cVal$mag$f" />
<Value name="MAX_VNSEQ" desc="负序电压最大值" type="6" DO="MSQI3$MX$SeqV" DA="c2$cVal$mag$f" />
<Value name="MIN_VNSEQ" desc="负序电压最小值" type="6" DO="MSQI4$MX$SeqV" DA="c2$cVal$mag$f" />
<Value name="G_V_UNBAN" desc="负序电压不平衡95值" type="6" DO="MSQI5$MX$ImbNgV" DA="mag$f" />
<Value name="V_UNBAN" desc="负序电压不平衡平均值" type="6" DO="MSQI2$MX$ImbNgV" DA="mag$f" />
<Value name="MAX_V_UNBAN" desc="负序电压不平衡最大值" type="6" DO="MSQI3$MX$ImbNgV" DA="mag$f" />
<Value name="MIN_V_UNBAN" desc="负序电压不平衡最小值" type="6" DO="MSQI4$MX$ImbNgV" DA="mag$f" />
<Value name="G_VZSEQ" desc="零序电压95值" type="6" DO="MSQI5$MX$SeqV" DA="c3$cVal$mag$f" />
<Value name="VZSEQ" desc="零序电压平均值" type="6" DO="MSQI2$MX$SeqV" DA="c3$cVal$mag$f" />
<Value name="MAX_VZSEQ" desc="零序电压最大值" type="6" DO="MSQI3$MX$SeqV" DA="c3$cVal$mag$f" />
<Value name="MIN_VZSEQ" desc="零序电压最小值" type="6" DO="MSQI4$MX$SeqV" DA="c3$cVal$mag$f" />
<Value name="G_VZSEQ_UNBAN" desc="零序电压不平衡95值" type="6" DO="MSQI5$MX$ImbZroV" DA="mag$f" />
<Value name="VZSEQ_UNBAN" desc="零序电压不平衡平均值" type="6" DO="MSQI2$MX$ImbZroV" DA="mag$f" />
<Value name="MAX_VZSEQ_UNBAN" desc="零序电压不平衡最大值" type="6" DO="MSQI3$MX$ImbZroV" DA="mag$f" />
<Value name="MIN_VZSEQ_UNBAN" desc="零序电压不平衡最小值" type="6" DO="MSQI4$MX$ImbZroV" DA="mag$f" />
<Value name="G_FREQ" desc="频率95值" type="6" DO="MMXU5$MX$Hz" DA="mag$f" />
<Value name="FREQ" desc="频率平均值" type="6" DO="MMXU2$MX$Hz" DA="mag$f" />
<Value name="MAX_FREQ" desc="频率最大值" type="6" DO="MMXU3$MX$Hz" DA="mag$f" />
<Value name="MIN_FREQ" desc="频率最小值" type="6" DO="MMXU4$MX$Hz" DA="mag$f" />
<Value name="G_DELTA_FREQ" desc="频率偏差95值" type="6" DO="MMXU5$MX$FreqDev" DA="mag$f" />
<Value name="DELTA_FREQ" desc="频率偏差平均值" type="6" DO="MMXU2$MX$FreqDev" DA="mag$f" />
<Value name="MAX_DELTA_FREQ" desc="频率偏差最大值" type="6" DO="MMXU3$MX$FreqDev" DA="mag$f" />
<Value name="MIN_DELTA_FREQ" desc="频率偏差最小值" type="6" DO="MMXU4$MX$FreqDev" DA="mag$f" />
<Value name="G_VPSEQ" desc="正序电压95值" type="6" DO="MSQI5$MX$SeqV" DA="c1$cVal$mag$f" />
<Value name="VPSEQ" desc="正序电压平均值" type="6" DO="MSQI2$MX$SeqV" DA="c1$cVal$mag$f" />
<Value name="MAX_VPSEQ" desc="正序电压最大值" type="6" DO="MSQI3$MX$SeqV" DA="c1$cVal$mag$f" />
<Value name="MIN_VPSEQ" desc="正序电压最小值" type="6" DO="MSQI4$MX$SeqV" DA="c1$cVal$mag$f" />
</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="MMXU5$MX$A" DA="phs*$cVal$mag$f" />
<Value name="IRMS" desc="电流有效值平均值" type="6" DO="MMXU2$MX$A" DA="phs*$cVal$mag$f" />
<Value name="MAX_IRMS" desc="电流有效值最大值" type="6" DO="MMXU3$MX$A" DA="phs*$cVal$mag$f" />
<Value name="MIN_IRMS" desc="电流有效值最小值" type="6" DO="MMXU4$MX$A" DA="phs*$cVal$mag$f" />
<Value name="G_I1" desc="基波电流有效值95值" type="6" DO="MHAI5$MX$HA" DA="phs*Har[1]$mag$f" />
<Value name="I1" desc="基波电流有效值平均值" type="6" DO="MHAI2$MX$HA" DA="phs*Har[1]$mag$f" />
<Value name="MAX_I1" desc="基波电流有效值最大值" type="6" DO="MHAI3$MX$HA" DA="phs*Har[1]$mag$f" />
<Value name="MIN_I1" desc="基波电流有效值最小值" type="6" DO="MHAI4$MX$HA" DA="phs*Har[1]$mag$f" />
<Value name="G_IFUND_ANGLE" desc="基波电流相角95值" type="6" DO="MHAI5$MX$HA" DA="phs*Har[1]$ang$f" />
<Value name="IFUND_ANGLE" desc="基波电流相角平均值" type="6" DO="MHAI2$MX$HA" DA="phs*Har[1]$ang$f" />
<Value name="MAX_IFUND_ANGLE" desc="基波电流相角最大值" type="6" DO="MHAI3$MX$HA" DA="phs*Har[1]$ang$f" />
<Value name="MIN_IFUND_ANGLE" desc="基波电流相角最小值" type="6" DO="MHAI4$MX$HA" DA="phs*Har[1]$ang$f" />
<Value name="G_SI_%2,51%" desc="间谐波电流幅值(f25-2475)95值" type="6" DO="MHAI11$MX$HA" DA="phs*Har[%-0]$mag$f" Offset="-2" />
<Value name="SI_%2,51%" desc="间谐波电流幅值(f25-2475)平均值" type="6" DO="MHAI8$MX$HA" DA="phs*Har[%-0]$mag$f" Offset="-2" />
<Value name="MAX_SI_%2,51%" desc="间谐波电流幅值(f25-2475)最大值" type="6" DO="MHAI9$MX$HA" DA="phs*Har[%-0]$mag$f" Offset="-2" />
<Value name="MIN_SI_%2,51%" desc="间谐波电流幅值(f25-2475)最小值" type="6" DO="MHAI10$MX$HA" DA="phs*Har[%-0]$mag$f" Offset="-2" />
<Value name="G_I%2,50%" desc="谐波电流幅值(2-50)95值" type="6" DO="MHAI5$MX$HA" DA="phs*Har[%-0]$mag$f" />
<Value name="I%2,50%" desc="谐波电流幅值(2-50)平均值" type="6" DO="MHAI2$MX$HA" DA="phs*Har[%-0]$mag$f" />
<Value name="MAX_I%2,50%" desc="谐波电流幅值(2-50)最大值" type="6" DO="MHAI3$MX$HA" DA="phs*Har[%-0]$mag$f" />
<Value name="MIN_I%2,50%" desc="谐波电流幅值(2-50)最小值" type="6" DO="MHAI4$MX$HA" DA="phs*Har[%-0]$mag$f" />
<Value name="G_IA%2,50%" desc="谐波电流相角(2-50)95值" type="6" DO="MHAI5$MX$HA" DA="phs*Har[%-0]$ang$f" />
<Value name="IA%2,50%" desc="谐波电流相角(2-50)平均值" type="6" DO="MHAI2$MX$HA" DA="phs*Har[%-0]$ang$f" />
<Value name="MAX_IA%2,50%" desc="谐波电流相角(2-50)最大值" type="6" DO="MHAI3$MX$HA" DA="phs*Har[%-0]$ang$f" />
<Value name="MIN_IA%2,50%" desc="谐波电流相角(2-50)最小值" type="6" DO="MHAI4$MX$HA" DA="phs*Har[%-0]$ang$f" />
<Value name="ITHD" desc="电流总谐波畸变率平均" type="6" DO="MHAI2$MX$ThdA" DA="phs*$cVal$mag$f" Coefficient="1" />
<Value name="MAX_ITHD" desc="电流总谐波畸变率最大" type="6" DO="MHAI3$MX$ThdA" DA="phs*$cVal$mag$f" Coefficient="1" />
<Value name="MIN_ITHD" desc="电流总谐波畸变率最小" type="6" DO="MHAI4$MX$ThdA" DA="phs*$cVal$mag$f" Coefficient="1" />
<Value name="G_ITHD" desc="电流总谐波畸变率95" type="6" DO="MHAI5$MX$ThdA" DA="phs*$cVal$mag$f" Coefficient="1" />
</Sequence>
<!--电流I部分(T相)-->
<Sequence name="SEQ" value="8" desc="相别(T相)" type="5">
<Value name="G_IPSEQ" desc="正序电流95值" type="6" DO="MSQI5$MX$SeqA" DA="c1$cVal$mag$f" />
<Value name="IPSEQ" desc="正序电流平均值" type="6" DO="MSQI2$MX$SeqA" DA="c1$cVal$mag$f" />
<Value name="MAX_IPSEQ" desc="正序电流最大值" type="6" DO="MSQI3$MX$SeqA" DA="c1$cVal$mag$f" />
<Value name="MIN_IPSEQ" desc="正序电流最小值" type="6" DO="MSQI4$MX$SeqA" DA="c1$cVal$mag$f" />
<Value name="G_INSEQ" desc="负序电流95值" type="6" DO="MSQI5$MX$SeqA" DA="c2$cVal$mag$f" />
<Value name="INSEQ" desc="负序电流平均值" type="6" DO="MSQI2$MX$SeqA" DA="c2$cVal$mag$f" />
<Value name="MAX_INSEQ" desc="负序电流最大值" type="6" DO="MSQI3$MX$SeqA" DA="c2$cVal$mag$f" />
<Value name="MIN_INSEQ" desc="负序电流最小值" type="6" DO="MSQI4$MX$SeqA" DA="c2$cVal$mag$f" />
<Value name="G_IZSEQ" desc="零序电流平95值" type="6" DO="MSQI5$MX$SeqA" DA="c3$cVal$mag$f" />
<Value name="IZSEQ" desc="零序电流平均值" type="6" DO="MSQI2$MX$SeqA" DA="c3$cVal$mag$f" />
<Value name="MAX_IZSEQ" desc="零序电流最大值" type="6" DO="MSQI3$MX$SeqA" DA="c3$cVal$mag$f" />
<Value name="MIN_IZSEQ" desc="零序电流最小值" type="6" DO="MSQI4$MX$SeqA" DA="c3$cVal$mag$f" />
<Value name="G_I_UNBAN" desc="负序电流不平衡95值" type="6" DO="MSQI5$MX$ImbNgA" DA="mag$f" />
<Value name="I_UNBAN" desc="负序电流不平衡平均值" type="6" DO="MSQI2$MX$ImbNgA" DA="mag$f" />
<Value name="MAX_I_UNBAN" desc="负序电流不平衡最大值" type="6" DO="MSQI3$MX$ImbNgA" DA="mag$f" />
<Value name="MIN_I_UNBAN" desc="负序电流不平衡最小值" type="6" DO="MSQI4$MX$ImbNgA" DA="mag$f" />
<Value name="G_IZSEQ_UNBAN" desc="零序电流不平衡95值" type="6" DO="MSQI5$MX$ImbZroA" DA="mag$f" />
<Value name="IZSEQ_UNBAN" desc="零序电流不平衡平均值" type="6" DO="MSQI2$MX$ImbZroA" DA="mag$f" />
<Value name="MAX_IZSEQ_UNBAN" desc="零序电流不平衡最大值" type="6" DO="MSQI3$MX$ImbZroA" DA="mag$f" />
<Value name="MIN_IZSEQ_UNBAN" desc="零序电流不平衡最小值" type="6" DO="MSQI4$MX$ImbZroA" DA="mag$f" />
</Sequence>
</Item>
<Item name="PQ" desc="功率" type="4" >
<!--功率PQ部分(A-C相)-->
<Sequence name="SEQ" value="7" desc="相别(A-C相)" type="5">
<Value name="G_DF" desc="位移功率因数95值" type="6" DO="MMXU5$MX$DF" DA="phs*$cVal$mag$f" />
<Value name="DF" desc="位移功率因数平均值" type="6" DO="MMXU2$MX$DF" DA="phs*$cVal$mag$f" />
<Value name="MAX_DF" desc="位移功率因数最大值" type="6" DO="MMXU3$MX$DF" DA="phs*$cVal$mag$f" />
<Value name="MIN_DF" desc="位移功率因数最小值" type="6" DO="MMXU4$MX$DF" DA="phs*$cVal$mag$f" />
<Value name="G_PF" desc="功率因数95值" type="6" DO="MMXU5$MX$PF" DA="phs*$cVal$mag$f" />
<Value name="PF" desc="功率因数平均值" type="6" DO="MMXU2$MX$PF" DA="phs*$cVal$mag$f" />
<Value name="MAX_PF" desc="功率因数最大值" type="6" DO="MMXU3$MX$PF" DA="phs*$cVal$mag$f" />
<Value name="MIN_PF" desc="功率因数最小值" type="6" DO="MMXU4$MX$PF" DA="phs*$cVal$mag$f" />
<Value name="G_P_FUND" desc="基波有功功率95值" type="6" DO="MHAI5$MX$HW" DA="phs*Har[1]$mag$f" />
<Value name="P_FUND" desc="基波有功功率平均值" type="6" DO="MHAI2$MX$HW" DA="phs*Har[1]$mag$f" />
<Value name="MAX_P_FUND" desc="基波有功功率最大值" type="6" DO="MHAI3$MX$HW" DA="phs*Har[1]$mag$f" />
<Value name="MIN_P_FUND" desc="基波有功功率最小值" type="6" DO="MHAI4$MX$HW" DA="phs*Har[1]$mag$f" />
<Value name="G_S" desc="视在功率95值" type="6" DO="MMXU5$MX$VA" DA="phs*$cVal$mag$f" />
<Value name="S" desc="视在功率平均值" type="6" DO="MMXU2$MX$VA" DA="phs*$cVal$mag$f" />
<Value name="MAX_S" desc="视在功率最大值" type="6" DO="MMXU3$MX$VA" DA="phs*$cVal$mag$f" />
<Value name="MIN_S" desc="视在功率最小值" type="6" DO="MMXU4$MX$VA" DA="phs*$cVal$mag$f" />
<Value name="G_Q" desc="无功功率95值" type="6" DO="MMXU5$MX$VAr" DA="phs*$cVal$mag$f" />
<Value name="Q" desc="无功功率平均值" type="6" DO="MMXU2$MX$VAr" DA="phs*$cVal$mag$f" />
<Value name="MAX_Q" desc="无功功率最大值" type="6" DO="MMXU3$MX$VAr" DA="phs*$cVal$mag$f" />
<Value name="MIN_Q" desc="无功功率最小值" type="6" DO="MMXU4$MX$VAr" DA="phs*$cVal$mag$f" />
<Value name="G_P%2,50%" desc="谐波有功功率(2-50)95值" type="6" DO="MHAI5$MX$HW" DA="phs*Har[%-0]$mag$f" />
<Value name="P%2,50%" desc="谐波有功功率(2-50)平均值" type="6" DO="MHAI2$MX$HW" DA="phs*Har[%-0]$mag$f" />
<Value name="MAX_P%2,50%" desc="谐波有功功率(2-50)最大值" type="6" DO="MHAI3$MX$HW" DA="phs*Har[%-0]$mag$f" />
<Value name="MIN_P%2,50%" desc="谐波有功功率(2-50)最小值" type="6" DO="MHAI4$MX$HW" DA="phs*Har[%-0]$mag$f" />
<Value name="G_Q%1,50%" desc="谐波无功功率(1-50)95值" type="6" DO="MHAI5$MX$HVAr" DA="phs*Har[%-0]$mag$f" />
<Value name="Q%1,50%" desc="谐波无功功率(1-50)平均值" type="6" DO="MHAI2$MX$HVAr" DA="phs*Har[%-0]$mag$f" />
<Value name="MAX_Q%1,50%" desc="谐波无功功率(1-50)最大值" type="6" DO="MHAI3$MX$HVAr" DA="phs*Har[%-0]$mag$f" />
<Value name="MIN_Q%1,50%" desc="谐波无功功率(1-50)最小值" type="6" DO="MHAI4$MX$HVAr" DA="phs*Har[%-0]$mag$f" />
<Value name="G_S%1,50%" desc="谐波视在功率(1-50)95值" type="6" DO="MHAI5$MX$HVA" DA="phs*Har[%-0]$mag$f" />
<Value name="S%1,50%" desc="谐波视在功率(1-50)平均值" type="6" DO="MHAI2$MX$HVA" DA="phs*Har[%-0]$mag$f" />
<Value name="MAX_S%1,50%" desc="谐波视在功率(1-50)最大值" type="6" DO="MHAI3$MX$HVA" DA="phs*Har[%-0]$mag$f" />
<Value name="MIN_S%1,50%" desc="谐波视在功率(1-50)最小值" type="6" DO="MHAI4$MX$HVA" DA="phs*Har[%-0]$mag$f" />
<Value name="G_P" desc="有功功率95值" type="6" DO="MMXU5$MX$W" DA="phs*$cVal$mag$f" />
<Value name="P" desc="有功功率平均值" type="6" DO="MMXU2$MX$W" DA="phs*$cVal$mag$f" />
<Value name="MAX_P" desc="有功功率最大值" type="6" DO="MMXU3$MX$W" DA="phs*$cVal$mag$f" />
<Value name="MIN_P" desc="有功功率最小值" type="6" DO="MMXU4$MX$W" DA="phs*$cVal$mag$f" />
</Sequence>
<!--功率PQ部分(T相)-->
<Sequence name="SEQ" value="8" desc="相别(T相)" type="5">
<Value name="G_DF" desc="位移功率因数95值" type="6" DO="MMXU5$MX$TotDF" DA="mag$f" />
<Value name="DF" desc="位移功率因数平均值" type="6" DO="MMXU2$MX$TotDF" DA="mag$f" />
<Value name="MAX_DF" desc="位移功率因数最大值" type="6" DO="MMXU3$MX$TotDF" DA="mag$f" />
<Value name="MIN_DF" desc="位移功率因数最小值" type="6" DO="MMXU4$MX$TotDF" DA="mag$f" />
<Value name="G_PF" desc="功率因数95值" type="6" DO="MMXU5$MX$TotPF" DA="mag$f" />
<Value name="PF" desc="功率因数平均值" type="6" DO="MMXU2$MX$TotPF" DA="mag$f" />
<Value name="MAX_PF" desc="功率因数最大值" type="6" DO="MMXU3$MX$TotPF" DA="mag$f" />
<Value name="MIN_PF" desc="功率因数最小值" type="6" DO="MMXU4$MX$TotPF" DA="mag$f" />
<Value name="G_S" desc="视在功率95值" type="6" DO="MMXU5$MX$TotVA" DA="mag$f" />
<Value name="S" desc="视在功率平均值" type="6" DO="MMXU2$MX$TotVA" DA="mag$f" />
<Value name="MAX_S" desc="视在功率最大值" type="6" DO="MMXU3$MX$TotVA" DA="mag$f" />
<Value name="MIN_S" desc="视在功率最小值" type="6" DO="MMXU4$MX$TotVA" DA="mag$f" />
<Value name="G_Q" desc="无功功率95值" type="6" DO="MMXU5$MX$TotVAr" DA="mag$f" />
<Value name="Q" desc="无功功率平均值" type="6" DO="MMXU2$MX$TotVAr" DA="mag$f" />
<Value name="MAX_Q" desc="无功功率最大值" type="6" DO="MMXU3$MX$TotVAr" DA="mag$f" />
<Value name="MIN_Q" desc="无功功率最小值" type="6" DO="MMXU4$MX$TotVAr" DA="mag$f" />
<Value name="G_P" desc="有功功率95值" type="6" DO="MMXU5$MX$TotW" DA="mag$f" />
<Value name="P" desc="有功功率平均值" type="6" DO="MMXU2$MX$TotW" DA="mag$f" />
<Value name="MAX_P" desc="有功功率最大值" type="6" DO="MMXU3$MX$TotW" DA="mag$f" />
<Value name="MIN_P" desc="有功功率最小值" type="6" DO="MMXU4$MX$TotW" DA="mag$f" />
</Sequence>
</Item>
</Monitor>
</DataType>
</Topic>
<Topic name="RTDATA" desc="实时数据">
<DataType name="DATA_TYPE" value="01" desc="实时稳态数据" send="1" type="0">
<Monitor name="-" desc="监测点" type="1">
<Item name="FLAG" value="0" desc="剔除标记" type="3" />
<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">
<Value name="VRMS" desc="电压有效值" type="6" DO="MMXU1$MX$PhV" DA="phs*$cVal$mag$f" Coefficient="1" />
<Value name="DELTA_V" desc="电压偏差" type="6" DO="MMXU1$MX$VolDev" DA="phs*$cVal$mag$f" Coefficient="1" />
<Value name="VTHD" desc="电压总谐波畸变率" type="6" DO="MHAI1$MX$ThdPhV" DA="phs*$cVal$mag$f" Coefficient="1" />
<Value name="VFUND_ANGLE" desc="基波电压相角" type="6" DO="MHAI1$MX$FundPhVAng" DA="phs*$cVal$ang$f" Coefficient="1" />
<Value name="VRMS_LVR" desc="线电压有效值" type="6" DO="MMXU1$MX$PPV" DA="l_phs*$cVal$mag$f" BaseFlag="1" LimitUp="0*%U" LimitDown="150*%U" />
<Value name="V1" desc="基波电压有效值" type="6" DO="MHAI1$MX$FundPhV" DA="phs*$cVal$mag$f" Coefficient="1" />
<Value name="V%2,50%" desc="2-50次谐波电压含有率" type="6" DO="MHAI1$MX$HPhVMag" DA="phs*Har[%-2]$mag$f" Coefficient="1" Offset="0" />
<Value name="SV_%0,49%" desc="1-50次间谐波电压含有率" type="6" DO="MHAI7$MX$HPhV" DA="phs*Har[%-0]$mag$f" Coefficient="1" Offset="0" />
<Value name="VA%2,50%" desc="谐波电压相角(2-50)" type="6" DO="MHAI1$MX$HPhV" DA="phs*Har[%-2]$ang$f" />
</Sequence>
<!--电压V部分(A-C相)角型接线-->
<Sequence name="SEQ" value="112" desc="相别(A-C相)" type="5">
<!--Coefficient 值系数转成kafka的时候乘以系数BaseFlag 基础数据标志 0或没有-非基础数据 1-基础数据LimitUp 数据合理上限,如果包含*%UN字符表示需要乘以电压等级电压如果包含*%U字符表示需要乘以电压等级/1.732电压例如220kV则相电压上限为1.5*220LimitDown 数据合理下限 如果包含*%U字符表示需要乘以电压等级电压-->
<Value name="DELTA_V" desc="电压偏差" type="6" DO="MMXU1$MX$VolDev" DA="phs*$cVal$mag$f" Coefficient="1"/>
<Value name="VRMS_LVR" desc="电压有效值" type="6" DO="MMXU1$MX$PPV" DA="phs*$cVal$mag$f" BaseFlag="1" LimitUp="0*%U" LimitDown="150*%U" Coefficient="1" />
<Value name="VTHD" desc="电压总谐波畸变率" type="6" DO="MHAI1$MX$ThdPPV" DA="phs*$cVal$mag$f" Coefficient="1"/>
<Value name="VFUND_ANGLE" desc="基波电压相角" type="6" DO="MHAI1$MX$FundPPVAng" DA="phs*$cVal$ang$f" Coefficient="1" />
<Value name="V1" desc="基波电压有效值" type="6" DO="MHAI1$MX$FundPPV" DA="phs*$cVal$mag$f" Coefficient="1"/>
<Value name="SV_%0,49%" desc="间谐波电压含有率(f25-2475)" type="6" DO="MHAI7$MX$HPPV" DA="phs*Har[%-0]$mag$f" Coefficient="1" Offset="0" />
<Value name="V%2,50%" desc="谐波电压含有率(2-50)" type="6" DO="MHAI1$MX$HPPV" DA="phs*Har[%-2]$mag$f" Coefficient="1" Offset="0" />
<Value name="VA%2,50%" desc="谐波电压相角(2-50)" type="6" DO="MHAI1$MX$HPPV" DA="phs*Har[%-2]$ang$f" />
</Sequence>
<Sequence name="SEQ" value="8" desc="相别(T相)" type="5">
<!--电压V部分(T相)-->
<Value name="VNSEQ" desc="负序电压" type="6" DO="MSQI1$MX$SeqV" DA="c2$cVal$mag$f" Coefficient="1" />
<Value name="V_UNBAN" desc="负序电压不平衡" type="6" DO="MSQI1$MX$ImbNgV" DA="mag$f" Coefficient="1" />
<Value name="VZSEQ" desc="零序电压" type="6" DO="MSQI1$MX$SeqV" DA="c3$cVal$mag$f" Coefficient="1" />
<Value name="VZSEQ_UNBAN" desc="零序电压不平衡" type="6" DO="MSQI1$MX$ImbZroV" DA="mag$f" Coefficient="1" />
<Value name="FREQ" desc="频率" type="6" DO="MMXU1$MX$Hz" DA="mag$f" Coefficient="1" />
<Value name="DELTA_FREQ" desc="频率偏差" type="6" DO="MMXU1$MX$FreqDev" DA="mag$f" Coefficient="1" />
<Value name="VPSEQ" desc="正序电压" type="6" DO="MSQI1$MX$SeqV" DA="c1$cVal$mag$f" Coefficient="1" />
</Sequence>
</Item>
<Item name="I" desc="电流" type="4" >
<!--电流I部分(A-C相)-->
<Sequence name="SEQ" value="7" desc="相别(A-C相)" type="5">
<Value name="IRMS" desc="电流有效值" type="6" DO="MMXU1$MX$A" DA="phs*$cVal$mag$f" Coefficient="1" />
<Value name="I1" desc="基波电流有效值" type="6" DO="MHAI1$MX$FundA" DA="phs*$cVal$mag$f" Coefficient="1" />
<Value name="ITHD" desc="电流总谐波畸变率" type="6" DO="MHAI1$MX$ThdA" DA="phs*$cVal$mag$f" Coefficient="1" />
<Value name="I_ANGLE" desc="基波电流相角" type="6" DO="MHAI1$MX$FundAAng" DA="phs*$cVal$ang$f" Coefficient="1" />
<Value name="I%2,50%" desc="2-50次谐波电流幅值" type="6" DO="MHAI1$MX$HA" DA="phs*Har[%-0]$mag$f" Coefficient="1" Offset="0" />
<Value name="SI_%0,49%" desc="1-50次间谐波电流含有率" type="6" DO="MHAI7$MX$HA" DA="phs*Har[%-0]$mag$f" Coefficient="1" Offset="0" />
</Sequence>
<!--电流I部分(T相)-->
<Sequence name="SEQ" value="8" desc="相别(T相)" type="5">
<Value name="INSEQ" desc="负序电流" type="6" DO="MSQI1$MX$SeqA" DA="c2$cVal$mag$f" Coefficient="1" />
<Value name="I_UNBAN" desc="负序电流不平衡" type="6" DO="MSQI1$MX$ImbNgA" DA="mag$f" Coefficient="1" />
<Value name="IZSEQ" desc="零序电流" type="6" DO="MSQI1$MX$SeqA" DA="c3$cVal$mag$f" Coefficient="1" />
<Value name="IZSEQ_UNBAN" desc="零序电流不平衡" type="6" DO="MSQI1$MX$ImbZroA" DA="mag$f" Coefficient="1" />
<Value name="IPSEQ" desc="正序电流" type="6" DO="MSQI1$MX$SeqA" DA="c1$cVal$mag$f" Coefficient="1" />
</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="MMXU1$MX$PF" DA="phs*$cVal$mag$f" Coefficient="1" />
<Value name="DF" desc="基波功率因数" type="6" DO="MMXU1$MX$DF" DA="phs*$cVal$mag$f" Coefficient="1" />
<Value name="P_FUND" desc="基波有功功率" type="6" DO="MHAI1$MX$HW" DA="phs*Har[0]$mag$f" Coefficient="1" />
<Value name="S" desc="视在功率" type="6" DO="MMXU1$MX$VA" DA="phs*$cVal$mag$f" Coefficient="1" />
<Value name="Q" desc="无功功率" type="6" DO="MMXU1$MX$VAr" DA="phs*$cVal$mag$f" Coefficient="1" />
<Value name="P" desc="有功功率" type="6" DO="MMXU1$MX$W" DA="phs*$cVal$mag$f" Coefficient="1" />
</Sequence>
<!--功率PQ部分(T相)-->
<Sequence name="SEQ" value="8" desc="相别(T相)" type="5">
<Value name="PF" desc="功率因数" type="6" DO="MMXU1$MX$TotPF" DA="mag$f" Coefficient="1" />
<Value name="DF" desc="基波功率因数" type="6" DO="MMXU1$MX$TotDF" DA="mag$f" Coefficient="1" />
<Value name="S" desc="视在功率" type="6" DO="MMXU1$MX$TotVA" DA="mag$f" Coefficient="1" />
<Value name="Q" desc="无功功率" type="6" DO="MMXU1$MX$TotVAr" DA="mag$f" Coefficient="1" />
<Value name="P" desc="有功功率" type="6" DO="MMXU1$MX$TotW" DA="mag$f" Coefficient="1" />
</Sequence>
</Item>
</Monitor>
</DataType>
<DataType name="DATA_TYPE" value="02" desc="实时闪变数据" send="0" type="0">
<Monitor name="MONITOR" desc="监测点" type="1">
<Item name="TIME" desc="发生时刻" type="3" />
<Item name="F" desc="闪变" type="4" >
<Sequence name="SEQ" value="7" desc="相别" type="5" >
<Value name="SF" desc="短时闪变" type="6" DO="MFLK0$MX$PhPst" DA="phs*$cVal$mag$f" Coefficient="1" PltFlag="False" />
<Value name="LF" desc="长时闪变" type="6" DO="MFLK0$MX$PhPlt" DA="phs*$cVal$mag$f" Coefficient="1" PltFlag="True" />
</Sequence>
<Sequence name="SEQ" value="112" desc="相别" type="5" >
<Value name="SF" desc="短时闪变" type="6" DO="MFLK0$MX$PPPst" DA="phs*$cVal$mag$f" Coefficient="1" PltFlag="False" />
<Value name="LF" desc="长时闪变" type="6" DO="MFLK0$MX$PPPlt" DA="phs*$cVal$mag$f" Coefficient="1" PltFlag="True" />
</Sequence>
</Item>
</Monitor>
</DataType>
<DataType name="DATA_TYPE" value="03" desc="实时暂态事件" send="0" type="0" >
<Monitor name="MONITOR" desc="监测点" type="1" >
<Item name="VOLTAGE" desc="暂态指标" type="4" >
<Sequence name="SEQ" value="-" desc="相别" type="5">
<Value name="MAG" desc="残余电压" type="6" DO="QVVR0$MX$VVa" DA="mag$f" Coefficient="1" />
<Value name="DUR" desc="持续时间" type="6" DO="QVVR0$MX$VVaTm" DA="mag$f" Coefficient="1" />
</Sequence>
</Item>
</Monitor>
</DataType>
</Topic>
<Topic name="SOEDATA" desc="告警SOE">
<SOE name="ThdVVal" desc="电压总畸变率越限告警" type="9" DO="GGIO1$ST$Alm1" DA="stVal" />
<SOE name="ThdAVal" desc="电流总畸变率越限告警" type="9" DO="GGIO1$ST$Alm2" DA="stVal" />
<SOE name="HToddVVal" desc="奇次谐波电压含有率越限告警" type="9" DO="GGIO1$ST$Alm3" DA="stVal" />
<SOE name="HTeddVVal" desc="偶次谐波电压含有率越限告警" type="9" DO="GGIO1$ST$Alm4" DA="stVal" />
<SOE name="H2AVal" desc="2次谐波电流越限告警" type="9" DO="GGIO1$ST$Alm5" DA="stVal" />
<SOE name="H3AVal" desc="3次谐波电流越限告警" type="9" DO="GGIO1$ST$Alm6" DA="stVal" />
<SOE name="H4AVal" desc="4次谐波电流越限告警" type="9" DO="GGIO1$ST$Alm7" DA="stVal" />
<SOE name="H5AVal" desc="5次谐波电流越限告警" type="9" DO="GGIO1$ST$Alm8" DA="stVal" />
<SOE name="H7AVal" desc="7次谐波电流越限告警" type="9" DO="GGIO1$ST$Alm9" DA="stVal" />
<SOE name="H9AVal" desc="9次谐波电流越限告警" type="9" DO="GGIO1$ST$Alm10" DA="stVal" />
<SOE name="H11AVal" desc="11次谐波电流越限告警" type="9" DO="GGIO1$ST$Alm11" DA="stVal" />
<SOE name="H13AVal" desc="13次谐波电流越限告警" type="9" DO="GGIO1$ST$Alm12" DA="stVal" />
<SOE name="ImbNgVFVal" desc="电压负序不平衡度越限告警" type="9" DO="GGIO1$ST$Alm13" DA="stVal" />
<SOE name="ImbNgAFVal" desc="电流负序不平衡度越限告警" type="9" DO="GGIO1$ST$Alm14" DA="stVal" />
<SOE name="OvHzStrVal" desc="频率高越限告警" type="9" DO="GGIO1$ST$Alm15" DA="stVal" />
<SOE name="UnHzStrVal" desc="频率低越限告警" type="9" DO="GGIO1$ST$Alm16" DA="stVal" />
<SOE name="LRVInterrup" desc="长期电压中断告警" type="9" DO="GGIO1$ST$Alm17" DA="stVal" />
<SOE name="LRVSwell" desc="电压上偏差越限告警" type="9" DO="GGIO1$ST$Alm18" DA="stVal" />
<SOE name="LRVSag" desc="电压下偏差越限告警" type="9" DO="GGIO1$ST$Alm19" DA="stVal" />
<SOE name="PhPstVal" desc="短时闪变越限告警" type="9" DO="GGIO1$ST$Alm20" DA="stVal" />
<SOE name="PhPltVal" desc="长时闪变越限告警" type="9" DO="GGIO1$ST$Alm21" DA="stVal" />
<SOE name="PhyStateFault" desc="终端运行状态:故障" type="9" DO="GGIO1$ST$Alm22" DA="stVal" />
<SOE name="PhyStateRun" desc="终端运行状态:运行" type="9" DO="GGIO1$ST$Alm23" DA="stVal" />
<SOE name="PwrUp" desc="终端上电" type="9" DO="LPHD0$ST$PwrUp" DA="stVal" />
<SOE name="PwrDn" desc="终端掉电" type="9" DO="LPHD0$ST$PwrDn" DA="stVal" />
<SOE name="CommInterrupt" desc="终端通信中断" type="9" DO="" DA="" />
<SOE name="CommResume" desc="终端通信恢复" type="9" DO="" DA="" />
</Topic>
</JSConfigTemplate>

View File

@@ -14,6 +14,26 @@
#include <unordered_map>
#include <vector>
/*
* icd_mapper.cpp
*
* 功能:把外部 ICD/XML 中 LN/DOI 的 desc 描述补写到工作簿
* “未匹配的原始数据”工作表的“icd中代表的字段”列。
*
* 输入:
* icdPath 外部 ICD/XML 文件,可能是 UTF-8也可能声明/实际使用 GBK/GB2312。
* workbookPath main.cpp 生成的 Excel 2003 XML 格式 .xls。
*
* 输出:
* 直接修改 workbookPath。message 返回解析结果、匹配数量或错误原因。
*
* 核心匹配规则:
* 1. 从 ICD 的每个 LN 节点读取 lnClass + inst并遍历其 DOI 子节点;
* 2. 组成 DO keylnClass + inst + "$MX$" + DOI/@name
* 3. 工作簿未匹配页的“原始路径”如果等于该 key或以 key + "$" 开头,
* 就认为该原始 MMS 路径属于这个 DOI把 DOI/@desc 写入目标列。
*/
namespace fs = std::filesystem;
namespace {
@@ -46,6 +66,7 @@ bool declaresGbEncoding(const std::string& text) {
}
bool isLikelyUtf8(const std::string& text) {
// 只做 UTF-8 字节合法性判断,不判断语义;合法就优先按 UTF-8 给 tinyxml2。
int remaining = 0;
for (unsigned char c : text) {
if (remaining == 0) {
@@ -63,6 +84,7 @@ bool isLikelyUtf8(const std::string& text) {
}
std::string ansiToUtf8(const std::string& text) {
// 936 是 Windows 简体中文代码页,兼容常见 GBK/GB2312 ICD 文件。
if (text.empty()) return "";
int wideLen = MultiByteToWideChar(936, 0, text.data(), (int)text.size(), nullptr, 0);
if (wideLen <= 0) return text;
@@ -76,6 +98,7 @@ std::string ansiToUtf8(const std::string& text) {
std::string loadXmlText(const fs::path& path) {
std::string raw = readAllBinary(path);
// ICD 声明 GB 编码或字节本身不像 UTF-8 时,先转成 UTF-8 再交给 tinyxml2。
if (declaresGbEncoding(raw) || !isLikelyUtf8(raw)) return ansiToUtf8(raw);
return raw;
}
@@ -106,6 +129,7 @@ void collectIcdDescriptions(const tinyxml2::XMLElement* element,
std::string doiName = attr(child, "name");
std::string desc = attr(child, "desc");
if (doiName.empty() || desc.empty()) continue;
// 这里构造的是 origin 原始路径前缀。后面路径可继续带 $mag$f 等 DA 后缀。
std::string key = lnClass + inst + "$MX$" + doiName;
descByDo[key] = desc;
}
@@ -155,6 +179,7 @@ tinyxml2::XMLElement* cellAt(tinyxml2::XMLElement* row, int col) {
tinyxml2::XMLElement* ensureCell(tinyxml2::XMLDocument& doc, tinyxml2::XMLElement* row, int col) {
if (tinyxml2::XMLElement* existing = cellAt(row, col)) return existing;
// Excel XML 允许通过 ss:Index 跳列;补列时直接设置 ss:Index避免改动前面已有单元格。
tinyxml2::XMLElement* cell = doc.NewElement("Cell");
cell->SetAttribute("ss:Index", col);
row->InsertEndChild(cell);
@@ -175,6 +200,7 @@ std::vector<std::string> sortedKeysByLength(const std::unordered_map<std::string
std::vector<std::string> keys;
keys.reserve(values.size());
for (const auto& kv : values) keys.push_back(kv.first);
// 长路径优先,避免短 key 先命中长 key 的前缀。
std::sort(keys.begin(), keys.end(), [](const std::string& a, const std::string& b) {
if (a.size() != b.size()) return a.size() > b.size();
return a < b;
@@ -183,6 +209,7 @@ std::vector<std::string> sortedKeysByLength(const std::unordered_map<std::string
}
bool pathMatchesDo(const std::string& rawPath, const std::string& doKey) {
// rawPath 可以正好是 DO也可以是 DO$DA...;必须在 "$" 边界匹配,避免误命中相似前缀。
return rawPath == doKey ||
(rawPath.size() > doKey.size() &&
rawPath.compare(0, doKey.size(), doKey) == 0 &&
@@ -230,6 +257,7 @@ bool pqApplyIcdDescriptionsToWorkbook(const std::string& icdPath,
const std::string& workbookPath,
std::string* message) {
try {
// 第一步:解析 ICD得到 “DO 路径前缀 -> 中文描述”。
auto descByDo = parseIcdDescriptions(fs::u8path(icdPath));
if (descByDo.empty()) {
if (message) *message = "ICD 中没有解析到 LN/DOI desc";
@@ -237,6 +265,7 @@ bool pqApplyIcdDescriptionsToWorkbook(const std::string& icdPath,
}
std::vector<std::string> orderedKeys = sortedKeysByLength(descByDo);
// 第二步:读取 main.cpp 输出的工作簿。该 .xls 实际是 XML可直接用 tinyxml2 修改。
tinyxml2::XMLDocument doc;
std::string workbookXml = readAllBinary(fs::u8path(workbookPath));
tinyxml2::XMLError rc = doc.Parse(workbookXml.c_str(), workbookXml.size());
@@ -245,6 +274,7 @@ bool pqApplyIcdDescriptionsToWorkbook(const std::string& icdPath,
return false;
}
// 第三步:只处理“未匹配的原始数据”页,因为已匹配页已经有明确映射关系。
tinyxml2::XMLElement* sheet = findWorksheet(doc, "未匹配的原始数据");
if (!sheet) {
if (message) *message = "没有找到 sheet: 未匹配的原始数据";
@@ -259,6 +289,7 @@ bool pqApplyIcdDescriptionsToWorkbook(const std::string& icdPath,
return false;
}
// 第四步:逐行读取原始路径,按 DO 前缀回填 DOI 描述。
int matched = 0;
for (tinyxml2::XMLElement* row = header ? header->NextSiblingElement("Row") : nullptr;
row;

View File

@@ -29,11 +29,31 @@
#include <unordered_map>
#include <unordered_set>
/*
* interface.cpp
*
* 本文件实现 Win32 桌面界面和页面交互,是用户操作到业务函数的“调度层”。
*
* 页面结构:
* 1. 配置项页填写前置、接口、MQ Topic/Tag/Key 等配置。
* 2. 台账列表页:获取台账/ICD选择装置和测点执行初始化 MQ、数据追踪、一键解析。
* 3. JSON 详情页展示已发送追踪请求、DATATopic jsondata、TraceTopic origin。
* 4. 表格预览页:读取 main.cpp 输出的 Excel XML按状态/相位/搜索过滤,可编辑配置列。
* 5. XML 预览页:根据预览页编辑结果反向写回 XML并支持导出。
*
* 关键交互流程:
* runGetLedger() -> pqFetchLedgerAndIcd() -> populateTree()/updatePathsForSelection()
* runTrace() -> pqSendTraceRequest() -> MQ 回调 addJsonEvent() -> autoParseTraceDir()
* runParse() -> pqRunParser() -> refreshPreviewFromWorkbook()
* runReverseXml()-> generateReverseXml() -> XML 预览页
*/
namespace {
using json = nlohmann::json;
namespace fs = std::filesystem;
// 所有控件 ID 集中在这里WM_COMMAND/WM_NOTIFY 根据这些 ID 路由事件。
enum ControlId {
IDC_TAB = 900,
IDC_PAGE_CONFIG,
@@ -59,6 +79,9 @@ enum ControlId {
IDC_TOPIC_LOG,
IDC_TAG_LOG,
IDC_KEY_LOG,
IDC_TOPIC_ASK,
IDC_TAG_ASK,
IDC_KEY_ASK,
IDC_CONSUMER,
IDC_CONSUMER_IPPORT,
IDC_DATA_STAT_TOPIC,
@@ -111,6 +134,7 @@ struct NodeRef {
int monitor = -1;
};
// JSON 树中的一条记录,可能来自 sent/data/trace 三种来源。
struct JsonItem {
std::string kind;
std::string title;
@@ -133,11 +157,13 @@ struct JsonMonitorChoice {
};
struct PreviewSheetRow {
std::vector<std::string> cells;
std::vector<std::string> originalCells;
std::vector<std::string> cellStyles;
std::string phase;
int status = 0;
std::vector<std::string> cells; // 当前可见/可编辑的单元格文本。
std::vector<std::string> originalCells; // 从工作簿读出的原始单元格,用于判断逆向生成时哪些行被改过。
std::vector<std::string> cellStyles; // Excel XML 中的 ss:StyleID用于 GUI 着色。
std::string phase; // A/B/C/T用于相位过滤和同步编辑。
int status = 0; // 0 空白1 正常2 异常3 缺失4 表头5 分段6 子分组。
bool finalRangeChanged = false; // 字段数组范围因表格参数修改而变化,字段格显示深红色。
int modifiedOriginPickColumn = -1; // 修改后的原始值取值起点列GUI 用紫色标记。
};
struct PreviewSheet {
@@ -186,15 +212,15 @@ struct AppState {
HFONT previewFont = nullptr;
HFONT xmlFont = nullptr;
bool ownsFont = false;
std::vector<PqLedgerDevice> devices;
std::vector<PqLedgerDevice> devices; // 当前台账数据,左侧树和追踪请求都从这里取装置/测点。
std::vector<std::unique_ptr<NodeRef>> refs;
std::vector<JsonItem> jsonItems;
std::vector<std::unique_ptr<JsonNodeRef>> jsonRefs;
std::vector<JsonMonitorChoice> jsonMonitorChoices;
std::vector<PreviewWorkbookChoice> previewWorkbooks;
std::vector<PreviewSheet> previewSheets;
std::string activeTraceDir;
std::string activePreviewWorkbook;
std::string activeTraceDir; // 当前测点追踪目录,用于过滤 JSON 记录和自动解析。
std::string activePreviewWorkbook; // 当前表格预览使用的 .xls。
std::string pendingJsonMonitorTraceDir;
std::string generatedXmlText;
std::string generatedXmlSourcePath;
@@ -205,8 +231,10 @@ struct AppState {
bool autoPreviewEnabled = false;
bool jsonMonitorRefreshing = false;
bool jsonMonitorRefreshPending = false;
bool reverseXmlDirty = true;
bool autoParseBusy = false;
bool reverseXmlDirty = true; // 预览表编辑后置 trueXML 预览/搜索前会重新生成。
bool updatingXmlPreview = false; // SetWindowText 会触发 EN_CHANGE用它区分自动刷新和人工编辑。
bool xmlPreviewEdited = false;
bool autoParseBusy = false; // MQ 数据到达后自动解析时显示忙碌提示。
int activePreviewSheet = 0;
int previewZoomPercent = 100;
int previewStatusFilter = -1;
@@ -361,28 +389,21 @@ void appendLog(AppState& app, const std::string& line) {
appendLogW(app, u8w(line));
}
std::string paddedLineNumber(int line, int width) {
std::string text = std::to_string(line);
if ((int)text.size() < width) text.insert(text.begin(), width - text.size(), ' ');
return text;
}
std::string xmlPreviewDisplayText(const std::string& xml) {
if (xml.empty()) return "";
int lines = 1;
for (char c : xml) {
if (c == '\n') ++lines;
// 可编辑预览必须展示原始 XML不能再混入行号否则导出后不是合法 XML。
std::string out;
out.reserve(xml.size() + xml.size() / 20);
for (size_t i = 0; i < xml.size(); ++i) {
if (xml[i] == '\r') {
if (i + 1 < xml.size() && xml[i + 1] == '\n') ++i;
out += "\r\n";
} else if (xml[i] == '\n') {
out += "\r\n";
} else {
out.push_back(xml[i]);
}
}
int width = std::max(3, (int)std::to_string(lines).size());
std::ostringstream out;
std::stringstream ss(xml);
std::string line;
int lineNo = 1;
while (std::getline(ss, line)) {
if (!line.empty() && line.back() == '\r') line.pop_back();
out << paddedLineNumber(lineNo++, width) << " " << line << "\r\n";
}
return out.str();
return out;
}
bool highlightXmlSearch(AppState& app, bool focusPreview = false, bool findNext = false) {
@@ -425,10 +446,20 @@ bool highlightXmlSearch(AppState& app, bool focusPreview = false, bool findNext
void updateXmlPreviewText(AppState& app) {
if (!app.xmlPreview) return;
app.updatingXmlPreview = true;
SetWindowTextW(app.xmlPreview, u8w(xmlPreviewDisplayText(app.generatedXmlText)).c_str());
app.updatingXmlPreview = false;
app.xmlPreviewEdited = false;
highlightXmlSearch(app, false, false);
}
void syncXmlPreviewFromEditor(AppState& app) {
if (!app.xmlPreview || app.updatingXmlPreview) return;
app.generatedXmlText = getText(app.hwnd, IDC_XML_PREVIEW_EDIT);
app.reverseXmlDirty = false;
app.xmlPreviewEdited = true;
}
void showBusyPopup(AppState& app, const wchar_t* message) {
if (!app.hwnd) return;
if (app.busyPopup && IsWindow(app.busyPopup)) {
@@ -545,6 +576,7 @@ std::string selectedFrontTypeFromWindow(HWND hwnd) {
PqToolConfig readConfig(HWND hwnd) {
PqToolConfig cfg = pqDefaultConfig();
// GUI 编辑框是配置最终来源;先用默认值兜底,再逐项用界面内容覆盖。
cfg.frontInst = getText(hwnd, IDC_FRONT_INST);
cfg.frontIp = getText(hwnd, IDC_FRONT_IP);
cfg.terminalStatus = getText(hwnd, IDC_TERMINAL_STATUS);
@@ -561,6 +593,9 @@ PqToolConfig readConfig(HWND hwnd) {
cfg.topicLOG = getText(hwnd, IDC_TOPIC_LOG);
cfg.tagLOG = getText(hwnd, IDC_TAG_LOG);
cfg.keyLOG = getText(hwnd, IDC_KEY_LOG);
cfg.topicAsk = getText(hwnd, IDC_TOPIC_ASK);
cfg.tagAsk = getText(hwnd, IDC_TAG_ASK);
cfg.keyAsk = getText(hwnd, IDC_KEY_ASK);
cfg.consumer = getText(hwnd, IDC_CONSUMER);
cfg.consumerIpport = getText(hwnd, IDC_CONSUMER_IPPORT);
cfg.consumerAccessKey = getText(hwnd, IDC_CONSUMER_ACCESS_KEY);
@@ -572,6 +607,7 @@ PqToolConfig readConfig(HWND hwnd) {
cfg.dataRealtimeTag = getText(hwnd, IDC_DATA_REALTIME_TAG);
cfg.dataRealtimeKey = getText(hwnd, IDC_DATA_REALTIME_KEY);
std::string frontType = selectedFrontTypeFromWindow(hwnd);
// frontType 是统计/实时模式开关:决定本次追踪和解析关注哪个 DATATopic。
if (frontType == "cfg_3s_data") {
cfg.dataTopic = cfg.dataRealtimeTopic;
cfg.dataTag = cfg.dataRealtimeTag;
@@ -674,6 +710,7 @@ void populateTree(AppState& app) {
app.selectedDevice = -1;
app.selectedMonitor = -1;
// 台账树按进程号分组maxProcessNum 可让没有设备的进程也显示出来。
int maxProcess = 0;
for (const auto& d : app.devices) {
maxProcess = std::max(maxProcess, toInt(d.processNo));
@@ -999,6 +1036,7 @@ void refreshPreviewWorkbookChoices(AppState& app, const std::string& selectedPat
void resetJsonSession(AppState& app, const std::string& traceDir) {
app.activeTraceDir = traceDir;
// 切换测点追踪时只清掉当前 traceDir 对应的 JSON 记录,其他测点历史还能通过下拉框找回。
app.jsonItems.erase(std::remove_if(app.jsonItems.begin(), app.jsonItems.end(),
[&](const JsonItem& item) {
if (!item.traceDir.empty() && sameTraceDir(item.traceDir, traceDir)) return true;
@@ -1084,6 +1122,7 @@ void addJsonEvent(AppState& app, const PqMqEvent& ev) {
if (item.kind == "data") {
item.dataType = dataTypeFromJsonText(item.body);
if (item.dataType.empty()) item.dataType = "unknown";
// 同一追踪目录下同一个 DATA_TYPE 保留最新一条,避免列表里堆积重复 MQ 消息。
for (size_t i = 0; i < app.jsonItems.size(); ++i) {
auto& existing = app.jsonItems[i];
if (existing.kind == "data" &&
@@ -1142,6 +1181,7 @@ std::string compactCells(const std::vector<std::string>& cells, size_t start) {
}
int previewStatusFromStyle(const std::string& style) {
// 这里把 main.cpp 写出的 Excel 样式名转换成 GUI 过滤/着色用的状态码。
if (style == "MatchGreen") return 1;
if (style == "MatchOrange") return 2;
if (style == "MatchRed") return 3;
@@ -1160,6 +1200,7 @@ std::vector<std::string> rowCells(const tinyxml2::XMLElement* row,
for (const tinyxml2::XMLElement* cell = row ? row->FirstChildElement("Cell") : nullptr;
cell;
cell = cell->NextSiblingElement("Cell")) {
// Excel XML 允许跳列,例如 <Cell ss:Index="8">;读取时必须补齐前面的空列。
const char* indexAttr = cell->Attribute("ss:Index");
if (indexAttr && *indexAttr) col = std::max(1, std::atoi(indexAttr));
if ((int)cells.size() < col) {
@@ -1205,7 +1246,7 @@ void resetPreviewColumns(HWND list, size_t colCount) {
LVCOLUMNW col{};
col.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM;
col.pszText = title.data();
int baseWidth = i == 0 ? 210 : (i == 4 || i == 6 ? 300 : 110);
int baseWidth = i == 0 ? 210 : (i == 4 || i == 6 || i == 8 ? 300 : 110);
col.cx = std::max(45, baseWidth * zoom / 100);
col.iSubItem = static_cast<int>(i);
ListView_InsertColumn(list, static_cast<int>(i), &col);
@@ -1327,6 +1368,7 @@ void populatePreviewList(AppState& app, int forcedTopModelRow = -1, int forcedHo
resetPreviewColumns(app.previewList, sheet.maxCols);
int statusFilter = app.previewStatusFilter;
int visibleRows = 0;
// ListView 只显示过滤后的行,但 lParam 仍保存原始模型行号,编辑时用它回写 previewSheets。
for (size_t i = 0; i < sheet.rows.size(); ++i) {
const PreviewSheetRow& row = sheet.rows[i];
if (!previewRowMatchesFilter(row, statusFilter)) continue;
@@ -1359,6 +1401,7 @@ bool isEditablePreviewCell(const AppState& app, int rowIndex, int colIndex) {
const auto& rows = app.previewSheets[app.activePreviewSheet].rows;
if (rowIndex < 0 || rowIndex >= (int)rows.size()) return false;
const PreviewSheetRow& row = rows[rowIndex];
// 只允许改“数组基准/取值偏移量/最终值偏移/配置项”这几列,避免误改最终值或 origin 值。
if (colIndex < 1 || colIndex > 4) return false;
if (!(row.status == 1 || row.status == 2 || row.status == 3)) return false;
return !row.cells.empty() && !row.cells[0].empty();
@@ -1379,6 +1422,7 @@ void syncPreviewPhaseGroup(AppState& app, int rowIndex, int colIndex, const std:
const PreviewSheetRow& row = sheet.rows[rowIndex];
if (!isAbcPhase(row.phase) || row.cells.empty()) return;
std::string baseLabel = labelWithoutPhase(row.cells[0]);
// A/B/C 三相同一个指标通常应该保持同一配置;编辑一相时同步另外两相。
for (auto& other : sheet.rows) {
if (!isAbcPhase(other.phase) || other.cells.empty() || other.cells[0].empty()) continue;
if (labelWithoutPhase(other.cells[0]) != baseLabel) continue;
@@ -1387,6 +1431,108 @@ void syncPreviewPhaseGroup(AppState& app, int rowIndex, int colIndex, const std:
}
}
bool parsePreviewRange(const std::string& text, int& start, int& end) {
std::smatch m;
static const std::regex re(R"(^\s*(-?\d+)\s*-->\s*(-?\d+)\s*$)");
if (!std::regex_match(text, m, re)) return false;
try {
start = std::stoi(m[1].str());
end = std::stoi(m[2].str());
return start <= end;
} catch (...) {
return false;
}
}
bool parsePreviewFieldRange(const std::string& field, int& start, int& end) {
std::smatch m;
static const std::regex re(R"(\[(-?\d+)-(-?\d+)\])");
if (!std::regex_search(field, m, re)) return false;
try {
start = std::stoi(m[1].str());
end = std::stoi(m[2].str());
return start <= end;
} catch (...) {
return false;
}
}
int parsePreviewOffset(const std::string& text) {
try {
std::string value = trimAscii(text);
return value.empty() ? 0 : std::stoi(value);
} catch (...) {
return 0;
}
}
std::string replacePreviewFieldRange(const std::string& field, int start, int end) {
static const std::regex re(R"(\[(-?\d+)-(-?\d+)\])");
return std::regex_replace(field, re,
"[" + std::to_string(start) + "-" + std::to_string(end) + "]",
std::regex_constants::format_first_only);
}
std::string effectivePreviewField(const std::string& field) {
size_t arrow = field.rfind(" >> ");
return arrow == std::string::npos ? field : field.substr(arrow + 4);
}
void recalculatePreviewEditEffects(PreviewSheet& sheet) {
for (auto& row : sheet.rows) {
row.finalRangeChanged = false;
row.modifiedOriginPickColumn = -1;
// 字段列不可直接编辑,因此每次从工作簿原值重新计算,恢复参数时可无损恢复。
if (!row.cells.empty() && !row.cells[0].empty() && row.originalCells.size() > 8) {
if (row.cells.size() <= 8) row.cells.resize(9);
row.cells[8] = row.originalCells[8];
}
}
for (size_t i = 0; i < sheet.rows.size(); ++i) {
PreviewSheetRow& row = sheet.rows[i];
if (!(row.status == 1 || row.status == 2 || row.status == 3)) continue;
if (row.cells.empty() || row.cells[0].empty() || row.cells.size() <= 8 || row.originalCells.size() <= 8) continue;
int oldBaseStart = 0, oldBaseEnd = 0, newBaseStart = 0, newBaseEnd = 0;
bool oldBaseOk = row.originalCells.size() > 1 && parsePreviewRange(row.originalCells[1], oldBaseStart, oldBaseEnd);
bool newBaseOk = row.cells.size() > 1 && parsePreviewRange(row.cells[1], newBaseStart, newBaseEnd);
int oldValueOffset = row.originalCells.size() > 2 ? parsePreviewOffset(row.originalCells[2]) : 0;
int newValueOffset = row.cells.size() > 2 ? parsePreviewOffset(row.cells[2]) : 0;
int oldFinalOffset = row.originalCells.size() > 3 ? parsePreviewOffset(row.originalCells[3]) : 0;
int newFinalOffset = row.cells.size() > 3 ? parsePreviewOffset(row.cells[3]) : 0;
int oldFieldStart = 0, oldFieldEnd = 0;
if (oldBaseOk && newBaseOk && parsePreviewFieldRange(row.originalCells[8], oldFieldStart, oldFieldEnd)) {
int newFieldStart = oldFieldStart + (newBaseStart - oldBaseStart) + (newFinalOffset - oldFinalOffset);
int newFieldEnd = oldFieldEnd + (newBaseEnd - oldBaseEnd) + (newFinalOffset - oldFinalOffset);
std::string newField = replacePreviewFieldRange(row.originalCells[8], newFieldStart, newFieldEnd);
if (newField != row.originalCells[8]) {
row.cells[8] = row.originalCells[8] + " >> " + newField;
row.finalRangeChanged = true;
}
}
if (!oldBaseOk || !newBaseOk || i + 1 >= sheet.rows.size()) continue;
PreviewSheetRow& originRow = sheet.rows[i + 1];
if (originRow.cells.size() <= 8 || originRow.cells[7] != "原始值") continue;
int originalBlueColumn = -1;
for (size_t col = 9; col < originRow.cellStyles.size(); ++col) {
if (originRow.cellStyles[col] == "OriginPickBlue") {
originalBlueColumn = static_cast<int>(col);
break;
}
}
if (originalBlueColumn < 0) continue;
int oldPickStart = oldBaseStart + oldValueOffset;
int newPickStart = newBaseStart + newValueOffset;
int purpleColumn = originalBlueColumn + (newPickStart - oldPickStart);
if (newPickStart != oldPickStart && purpleColumn >= 9 && purpleColumn < (int)originRow.cells.size()) {
originRow.modifiedOriginPickColumn = purpleColumn;
}
}
}
void commitPreviewEdit(AppState& app, bool save) {
HWND edit = app.previewEdit;
if (!edit) return;
@@ -1411,6 +1557,7 @@ void commitPreviewEdit(AppState& app, bool save) {
if ((int)row.cells.size() <= colIndex) row.cells.resize(colIndex + 1);
row.cells[colIndex] = value;
syncPreviewPhaseGroup(app, rowIndex, colIndex, value);
recalculatePreviewEditEffects(app.previewSheets[app.activePreviewSheet]);
app.reverseXmlDirty = true;
}
@@ -1473,6 +1620,7 @@ void refreshPreviewFromWorkbook(AppState& app, const std::string& workbookPath)
return;
}
// 工作簿是 Excel 2003 XML 格式,不需要 COM/Excel直接用 tinyxml2 读 Worksheet/Row/Cell。
tinyxml2::XMLDocument doc;
std::string xml = readAllText(fs::u8path(workbookPath));
if (doc.Parse(xml.c_str(), xml.size()) != tinyxml2::XML_SUCCESS) {
@@ -1803,8 +1951,11 @@ void showPage(AppState& app, int index) {
refreshPreviewFromWorkbook(app, selected);
}
} else if (index == 4) {
if (app.reverseXmlDirty || app.generatedXmlText.empty()) generateReverseXml(app, false);
updateXmlPreviewText(app);
// XML 有人工修改时不自动逆生成,避免仅切换页面就静默覆盖用户内容。
if (!app.xmlPreviewEdited) {
if (app.reverseXmlDirty || app.generatedXmlText.empty()) generateReverseXml(app, false);
else updateXmlPreviewText(app);
}
}
}
@@ -1818,6 +1969,7 @@ void runGetLedger(AppState& app) {
PqToolConfig cfg = readConfig(app.hwnd);
appendLogW(app, L"开始获取台账和 ICD/XML...");
std::string firstXml;
// 输出 devices 会刷新左侧树firstXml 会自动填到 XML 路径,减少手动选择。
pqFetchLedgerAndIcd(cfg, app.devices, firstXml, [&](const std::string& s) { appendLog(app, s); });
populateTree(app);
updateDetails(app);
@@ -1851,16 +2003,17 @@ void runTrace(AppState& app) {
const auto& d = app.devices[app.selectedDevice];
const auto& m = d.monitors[app.selectedMonitor];
std::string traceDir = pqTraceDirectoryFor(cfg, d, m);
// pqSendTraceRequest 会在 MQ 互斥锁内建立唯一活动会话并清空旧记录,
// 从这一刻开始只接收 Broker 存储时间不早于本次点击的消息。
resetJsonSession(app, traceDir);
pqClearTraceRecords(cfg, d, m);
appendLog(app, "已清空当前测点旧文本记录: " + traceDir);
pqSendTraceRequest(cfg, d, m, selectedFrontType(app),
[&](const std::string& s) { appendLog(app, s); },
makeMqEventCallback(app));
appendLog(app, "已建立本次追踪消息时间边界并清空旧记录: " + traceDir);
prepareTraceWorkspace(app, cfg, d, m, true);
app.autoPreviewEnabled = true;
selectPage(app, 3);
autoParseTraceDir(app, traceDir, "trace-start");
pqSendTraceRequest(cfg, d, m, selectedFrontType(app),
[&](const std::string& s) { appendLog(app, s); },
makeMqEventCallback(app));
} catch (const std::exception& e) {
appendLog(app, std::string("数据追踪失败: ") + e.what());
}
@@ -1871,6 +2024,7 @@ void runParse(AppState& app) {
std::string err;
appendLogW(app, L"开始解析生成表格...");
std::vector<std::string> jsonPaths = cfg.jsonDataPaths.empty() ? splitPathList(cfg.jsonDataPath) : cfg.jsonDataPaths;
// 一键解析是手动路径模式:用界面上的 XML/jsondata/origin/output 调用核心解析器。
bool ok = pqRunParser(cfg.xmlPath, jsonPaths, cfg.originPaths, cfg.outputPath, &err, selectedFrontType(app));
if (ok) {
refreshPreviewWorkbookChoices(app, cfg.outputPath);
@@ -1971,6 +2125,7 @@ std::string reverseNameFromField(const std::string& field, const std::string& ar
std::smatch m;
static const std::regex rangeRe(R"(^(.*)\[(\d+)-(\d+)\](.*)$)");
if (std::regex_match(name, m, rangeRe)) {
// 表格里序列显示为 [start-end],写回 XML 时恢复为 %start,end%。
std::string start = m[2].str();
std::string end = m[3].str();
static const std::regex baseRe(R"(^\s*(-?\d+)\s*-->\s*(-?\d+)\s*$)");
@@ -2014,6 +2169,7 @@ std::string placeholderForValueOffset(const std::string& offsetText) {
std::string reverseDaForXml(std::string daPath, const std::string& valueOffset) {
if (valueOffset.empty()) return daPath;
static const std::regex rangeRe(R"(\[\d+-\d+\])");
// 表格展示的 [start-end] 在 XML 中是 [%offset] 占位符,按“取值偏移量”恢复。
return std::regex_replace(daPath, rangeRe, "[" + placeholderForValueOffset(valueOffset) + "]",
std::regex_constants::format_first_only);
}
@@ -2035,6 +2191,7 @@ std::vector<ReverseRowEdit> collectReverseRows(const AppState& app) {
std::vector<ReverseRowEdit> rows;
if (app.activePreviewSheet < 0 || app.activePreviewSheet >= (int)app.previewSheets.size()) return rows;
const PreviewSheet& sheet = app.previewSheets[app.activePreviewSheet];
// 只采集正常/异常/缺失这些数据行,表头和分组行不会参与反向生成。
for (const auto& row : sheet.rows) {
if (!(row.status == 1 || row.status == 2 || row.status == 3)) continue;
if (row.cells.size() <= 8 || row.cells[0].empty()) continue;
@@ -2049,7 +2206,7 @@ std::vector<ReverseRowEdit> collectReverseRows(const AppState& app) {
parseConfigText(originalConfig, edit.originalDoPath, edit.originalDaPath);
edit.modified = row.cells != row.originalCells;
edit.daPath = reverseDaForXml(edit.daPath, edit.valueOffset);
edit.name = reverseNameFromField(row.cells[8], edit.arrayBase);
edit.name = reverseNameFromField(effectivePreviewField(row.cells[8]), edit.arrayBase);
if (!edit.name.empty() || !edit.label.empty()) rows.push_back(std::move(edit));
}
std::stable_sort(rows.begin(), rows.end(), [](const ReverseRowEdit& a, const ReverseRowEdit& b) {
@@ -2092,6 +2249,7 @@ bool reverseNodeMatches(const ValueNodeRef& node, const ReverseSheetSpec& spec,
if (node.topic != spec.topic || node.dataType != spec.dataType || !reverseWiringAllowed(node.wiring, spec.wiring)) return false;
std::string nodeDo = xmlAttr(node.element, "DO");
std::string nodeDa = xmlAttr(node.element, "DA");
// 优先用原始 DO/DA 精确定位;如果用户把 DO/DA 改了,也能找到原 XML 节点。
if (!edit.originalDoPath.empty() && !edit.originalDaPath.empty() &&
nodeDo == edit.originalDoPath && nodeDa == edit.originalDaPath) {
return true;
@@ -2121,6 +2279,7 @@ std::string generateReverseXml(AppState& app, bool showMessage) {
return "";
}
// 当前 Sheet 名决定要修改 XML 中哪个 Topic/DataType/接线分支。
ReverseSheetSpec spec = reverseSpecForSheet(app.previewSheets[app.activePreviewSheet].name);
std::vector<ReverseRowEdit> edits = collectReverseRows(app);
std::vector<ValueNodeRef> nodes;
@@ -2156,12 +2315,28 @@ std::string generateReverseXml(AppState& app, bool showMessage) {
}
void runReverseXml(AppState& app) {
if (app.xmlPreviewEdited) {
int choice = MessageBoxW(app.hwnd,
L"XML预览此前有过手动修改。继续逆生成会覆盖这些人工修改是否继续",
L"确认覆盖 XML 人工修改",
MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2);
if (choice != IDYES) return;
}
if (!generateReverseXml(app, true).empty()) selectPage(app, 4);
}
void runExportXml(AppState& app) {
if (app.xmlPreviewEdited) syncXmlPreviewFromEditor(app);
if (app.generatedXmlText.empty() || app.reverseXmlDirty) generateReverseXml(app, false);
if (app.generatedXmlText.empty()) return;
tinyxml2::XMLDocument check;
if (check.Parse(app.generatedXmlText.c_str(), app.generatedXmlText.size()) != tinyxml2::XML_SUCCESS) {
MessageBoxW(app.hwnd,
u8w(std::string("当前编辑内容不是合法 XML") + check.ErrorStr()).c_str(),
L"导出 XML 失败", MB_ICONERROR);
return;
}
std::string out;
if (!saveFileDialog(app.hwnd, L"\x5BFC\x51FAXML", L"XML Files\0*.xml;*.icd\0All Files\0*.*\0", L"xml", out)) return;
std::ofstream ofs(fs::u8path(out), std::ios::binary);
@@ -2181,6 +2356,7 @@ void autoParseTraceDir(AppState& app, const std::string& traceDir, const char* r
std::string outputPath = (dir / "final_sorted.xls").u8string();
std::string frontType = selectedFrontType(app);
// trace_context.json 是 pqSendTraceRequest() 写下的上下文,自动解析优先用它定位 XML 和输出路径。
fs::path contextPath = dir / "trace_context.json";
if (fs::exists(contextPath)) {
try {
@@ -2279,21 +2455,25 @@ void createConfigPage(AppState& app) {
addLabeledEdit(app, p, L"TopicLOG", IDC_TOPIC_LOG, cfg.topicLOG, 120, 398 + rowGap * 2, 90, 360);
addLabeledEdit(app, p, L"TagLOG", IDC_TAG_LOG, cfg.tagLOG, 575, 398 + rowGap * 2, 75, 270);
addLabeledEdit(app, p, L"KeyLOG", IDC_KEY_LOG, cfg.keyLOG, 940, 398 + rowGap * 2, 70, 120);
addLabeledEdit(app, p, L"TopicAsk", IDC_TOPIC_ASK, cfg.topicAsk, 120, 398 + rowGap * 3, 90, 360);
addLabeledEdit(app, p, L"TagAsk", IDC_TAG_ASK, cfg.tagAsk, 575, 398 + rowGap * 3, 75, 270);
addLabeledEdit(app, p, L"KeyAsk", IDC_KEY_ASK, cfg.keyAsk, 940, 398 + rowGap * 3, 70, 120);
addLabel(app, p, L"消费者", x1, 512, 80, 22);
addLabeledEdit(app, p, L"consumer", IDC_CONSUMER, cfg.consumer, 120, 512, 90, 300);
addLabeledEdit(app, p, L"Ipport", IDC_CONSUMER_IPPORT, cfg.consumerIpport, 600, 512, 80, 260);
addLabeledEdit(app, p, L"AccessKey", IDC_CONSUMER_ACCESS_KEY, cfg.consumerAccessKey, 120, 512 + rowGap, 90, 300);
addLabeledEdit(app, p, L"SecretKey", IDC_CONSUMER_SECRET_KEY, cfg.consumerSecretKey, 600, 512 + rowGap, 80, 260);
addLabeledEdit(app, p, L"\x7EDF\x8BA1Topic", IDC_DATA_STAT_TOPIC, cfg.dataStatTopic, 120, 512 + rowGap * 2, 90, 360);
addLabeledEdit(app, p, L"DATATag", IDC_DATA_STAT_TAG, cfg.dataStatTag, 575, 512 + rowGap * 2, 75, 270);
addLabeledEdit(app, p, L"DATAKey", IDC_DATA_STAT_KEY, cfg.dataStatKey, 940, 512 + rowGap * 2, 70, 120);
addLabeledEdit(app, p, L"\x5B9E\x65F6Topic", IDC_DATA_REALTIME_TOPIC, cfg.dataRealtimeTopic, 120, 512 + rowGap * 3, 90, 360);
addLabeledEdit(app, p, L"DATATag", IDC_DATA_REALTIME_TAG, cfg.dataRealtimeTag, 575, 512 + rowGap * 3, 75, 270);
addLabeledEdit(app, p, L"DATAKey", IDC_DATA_REALTIME_KEY, cfg.dataRealtimeKey, 940, 512 + rowGap * 3, 70, 120);
addLabeledEdit(app, p, L"TraceTopic", IDC_TRACE_TOPIC, cfg.traceTopic, 120, 512 + rowGap * 4, 90, 360);
addLabeledEdit(app, p, L"TraceTag", IDC_TRACE_TAG, cfg.traceTag, 575, 512 + rowGap * 4, 75, 270);
addLabeledEdit(app, p, L"TraceKey", IDC_TRACE_KEY, cfg.traceKey, 940, 512 + rowGap * 4, 70, 120);
const int consumerY = 526;
addLabel(app, p, L"消费者", x1, consumerY, 80, 22);
addLabeledEdit(app, p, L"consumer", IDC_CONSUMER, cfg.consumer, 120, consumerY, 90, 300);
addLabeledEdit(app, p, L"Ipport", IDC_CONSUMER_IPPORT, cfg.consumerIpport, 600, consumerY, 80, 260);
addLabeledEdit(app, p, L"AccessKey", IDC_CONSUMER_ACCESS_KEY, cfg.consumerAccessKey, 120, consumerY + rowGap, 90, 300);
addLabeledEdit(app, p, L"SecretKey", IDC_CONSUMER_SECRET_KEY, cfg.consumerSecretKey, 600, consumerY + rowGap, 80, 260);
addLabeledEdit(app, p, L"\x7EDF\x8BA1Topic", IDC_DATA_STAT_TOPIC, cfg.dataStatTopic, 120, consumerY + rowGap * 2, 90, 360);
addLabeledEdit(app, p, L"DATATag", IDC_DATA_STAT_TAG, cfg.dataStatTag, 575, consumerY + rowGap * 2, 75, 270);
addLabeledEdit(app, p, L"DATAKey", IDC_DATA_STAT_KEY, cfg.dataStatKey, 940, consumerY + rowGap * 2, 70, 120);
addLabeledEdit(app, p, L"\x5B9E\x65F6Topic", IDC_DATA_REALTIME_TOPIC, cfg.dataRealtimeTopic, 120, consumerY + rowGap * 3, 90, 360);
addLabeledEdit(app, p, L"DATATag", IDC_DATA_REALTIME_TAG, cfg.dataRealtimeTag, 575, consumerY + rowGap * 3, 75, 270);
addLabeledEdit(app, p, L"DATAKey", IDC_DATA_REALTIME_KEY, cfg.dataRealtimeKey, 940, consumerY + rowGap * 3, 70, 120);
addLabeledEdit(app, p, L"TraceTopic", IDC_TRACE_TOPIC, cfg.traceTopic, 120, consumerY + rowGap * 4, 90, 360);
addLabeledEdit(app, p, L"TraceTag", IDC_TRACE_TAG, cfg.traceTag, 575, consumerY + rowGap * 4, 75, 270);
addLabeledEdit(app, p, L"TraceKey", IDC_TRACE_KEY, cfg.traceKey, 940, consumerY + rowGap * 4, 70, 120);
}
void createLedgerPage(AppState& app) {
@@ -2429,7 +2609,7 @@ void createXmlPreviewPage(AppState& app) {
app.xmlSearch = addEdit(app, p, IDC_XML_SEARCH, "", 230, 12, 360, 28);
app.xmlPreview = CreateWindowExW(WS_EX_CLIENTEDGE, L"EDIT", L"",
WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_MULTILINE |
ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_NOHIDESEL |
ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_NOHIDESEL | ES_WANTRETURN |
WS_VSCROLL | WS_HSCROLL,
18, 50, 1115, 585, p,
(HMENU)(INT_PTR)IDC_XML_PREVIEW_EDIT, app.inst, nullptr);
@@ -2437,7 +2617,7 @@ void createXmlPreviewPage(AppState& app) {
DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
CLEARTYPE_QUALITY, FIXED_PITCH | FF_MODERN, L"Consolas");
SendMessageW(app.xmlPreview, WM_SETFONT, (WPARAM)(app.xmlFont ? app.xmlFont : app.font), TRUE);
SendMessageW(app.xmlPreview, EM_SETREADONLY, TRUE, 0);
SendMessageW(app.xmlPreview, EM_SETLIMITTEXT, 0x7FFFFFFE, 0);
}
void createConfigPageClean(AppState& app) {
@@ -2472,21 +2652,25 @@ void createConfigPageClean(AppState& app) {
addLabeledEdit(app, p, L"TopicLOG", IDC_TOPIC_LOG, cfg.topicLOG, mqFieldX, 398 + rowGap * 2, 90, mqTopicW);
addLabeledEdit(app, p, L"TagLOG", IDC_TAG_LOG, cfg.tagLOG, mqTagX, 398 + rowGap * 2, 75, mqTagW);
addLabeledEdit(app, p, L"KeyLOG", IDC_KEY_LOG, cfg.keyLOG, mqKeyX, 398 + rowGap * 2, 70, mqKeyW);
addLabeledEdit(app, p, L"TopicAsk", IDC_TOPIC_ASK, cfg.topicAsk, mqFieldX, 398 + rowGap * 3, 90, mqTopicW);
addLabeledEdit(app, p, L"TagAsk", IDC_TAG_ASK, cfg.tagAsk, mqTagX, 398 + rowGap * 3, 75, mqTagW);
addLabeledEdit(app, p, L"KeyAsk", IDC_KEY_ASK, cfg.keyAsk, mqKeyX, 398 + rowGap * 3, 70, mqKeyW);
addLabel(app, p, L"\x6D88\x8D39\x8005", mqNameX, 512, 90, 22);
addLabeledEdit(app, p, L"consumer", IDC_CONSUMER, cfg.consumer, mqFieldX, 512, 90, 300);
addLabeledEdit(app, p, L"Ipport", IDC_CONSUMER_IPPORT, cfg.consumerIpport, mqTagX, 512, 80, 260);
addLabeledEdit(app, p, L"AccessKey", IDC_CONSUMER_ACCESS_KEY, cfg.consumerAccessKey, mqFieldX, 512 + rowGap, 90, 300);
addLabeledEdit(app, p, L"SecretKey", IDC_CONSUMER_SECRET_KEY, cfg.consumerSecretKey, mqTagX, 512 + rowGap, 80, 260);
addLabeledEdit(app, p, L"\x7EDF\x8BA1Topic", IDC_DATA_STAT_TOPIC, cfg.dataStatTopic, mqFieldX, 512 + rowGap * 2, 90, mqTopicW);
addLabeledEdit(app, p, L"DATATag", IDC_DATA_STAT_TAG, cfg.dataStatTag, mqTagX, 512 + rowGap * 2, 75, mqTagW);
addLabeledEdit(app, p, L"DATAKey", IDC_DATA_STAT_KEY, cfg.dataStatKey, mqKeyX, 512 + rowGap * 2, 70, mqKeyW);
addLabeledEdit(app, p, L"\x5B9E\x65F6Topic", IDC_DATA_REALTIME_TOPIC, cfg.dataRealtimeTopic, mqFieldX, 512 + rowGap * 3, 90, mqTopicW);
addLabeledEdit(app, p, L"DATATag", IDC_DATA_REALTIME_TAG, cfg.dataRealtimeTag, mqTagX, 512 + rowGap * 3, 75, mqTagW);
addLabeledEdit(app, p, L"DATAKey", IDC_DATA_REALTIME_KEY, cfg.dataRealtimeKey, mqKeyX, 512 + rowGap * 3, 70, mqKeyW);
addLabeledEdit(app, p, L"TraceTopic", IDC_TRACE_TOPIC, cfg.traceTopic, mqFieldX, 512 + rowGap * 4, 90, mqTopicW);
addLabeledEdit(app, p, L"TraceTag", IDC_TRACE_TAG, cfg.traceTag, mqTagX, 512 + rowGap * 4, 75, mqTagW);
addLabeledEdit(app, p, L"TraceKey", IDC_TRACE_KEY, cfg.traceKey, mqKeyX, 512 + rowGap * 4, 70, mqKeyW);
const int consumerY = 526;
addLabel(app, p, L"\x6D88\x8D39\x8005", mqNameX, consumerY, 90, 22);
addLabeledEdit(app, p, L"consumer", IDC_CONSUMER, cfg.consumer, mqFieldX, consumerY, 90, 300);
addLabeledEdit(app, p, L"Ipport", IDC_CONSUMER_IPPORT, cfg.consumerIpport, mqTagX, consumerY, 80, 260);
addLabeledEdit(app, p, L"AccessKey", IDC_CONSUMER_ACCESS_KEY, cfg.consumerAccessKey, mqFieldX, consumerY + rowGap, 90, 300);
addLabeledEdit(app, p, L"SecretKey", IDC_CONSUMER_SECRET_KEY, cfg.consumerSecretKey, mqTagX, consumerY + rowGap, 80, 260);
addLabeledEdit(app, p, L"\x7EDF\x8BA1Topic", IDC_DATA_STAT_TOPIC, cfg.dataStatTopic, mqFieldX, consumerY + rowGap * 2, 90, mqTopicW);
addLabeledEdit(app, p, L"DATATag", IDC_DATA_STAT_TAG, cfg.dataStatTag, mqTagX, consumerY + rowGap * 2, 75, mqTagW);
addLabeledEdit(app, p, L"DATAKey", IDC_DATA_STAT_KEY, cfg.dataStatKey, mqKeyX, consumerY + rowGap * 2, 70, mqKeyW);
addLabeledEdit(app, p, L"\x5B9E\x65F6Topic", IDC_DATA_REALTIME_TOPIC, cfg.dataRealtimeTopic, mqFieldX, consumerY + rowGap * 3, 90, mqTopicW);
addLabeledEdit(app, p, L"DATATag", IDC_DATA_REALTIME_TAG, cfg.dataRealtimeTag, mqTagX, consumerY + rowGap * 3, 75, mqTagW);
addLabeledEdit(app, p, L"DATAKey", IDC_DATA_REALTIME_KEY, cfg.dataRealtimeKey, mqKeyX, consumerY + rowGap * 3, 70, mqKeyW);
addLabeledEdit(app, p, L"TraceTopic", IDC_TRACE_TOPIC, cfg.traceTopic, mqFieldX, consumerY + rowGap * 4, 90, mqTopicW);
addLabeledEdit(app, p, L"TraceTag", IDC_TRACE_TAG, cfg.traceTag, mqTagX, consumerY + rowGap * 4, 75, mqTagW);
addLabeledEdit(app, p, L"TraceKey", IDC_TRACE_KEY, cfg.traceKey, mqKeyX, consumerY + rowGap * 4, 70, mqKeyW);
}
void createLedgerPageClean(AppState& app) {
@@ -2534,6 +2718,7 @@ void createLedgerPageClean(AppState& app) {
}
void createUi(AppState& app) {
// 这里手写 Win32 控件,没有资源文件;所有页面尺寸和控件布局都在 create*Page 函数中生成。
app.font = CreateFontW(-16, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE,
DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
CLEARTYPE_QUALITY, DEFAULT_PITCH | FF_SWISS, L"Microsoft YaHei UI");
@@ -2583,6 +2768,7 @@ LRESULT CALLBACK wndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
AppState& app = *g_app;
switch (msg) {
case WM_PQ_MQ_EVENT: {
// RocketMQ 回调线程不能直接操作窗口mq.cpp 通过 PostMessage 把事件切回 GUI 线程。
std::unique_ptr<PqMqEvent> ev(reinterpret_cast<PqMqEvent*>(lp));
if (ev) addJsonEvent(app, *ev);
return 0;
@@ -2595,6 +2781,7 @@ LRESULT CALLBACK wndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
createUi(app);
return 0;
case WM_COMMAND:
// WM_COMMAND 处理按钮、编辑框、下拉框;每个控件 ID 对应上面的 ControlId。
if (LOWORD(wp) == IDC_PREVIEW_FILTER && HIWORD(wp) == CBN_SELCHANGE) {
int idx = (int)SendMessageW(app.previewFilter, CB_GETCURSEL, 0, 0);
int filters[] = {-1, 1, 2, 3, 0};
@@ -2621,9 +2808,17 @@ LRESULT CALLBACK wndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
highlightXmlSearch(app, false, false);
return 0;
}
if (LOWORD(wp) == IDC_XML_PREVIEW_EDIT && HIWORD(wp) == EN_CHANGE) {
if (!app.updatingXmlPreview) {
syncXmlPreviewFromEditor(app);
app.xmlSearchLastText.clear();
app.xmlSearchNextPos = 0;
}
return 0;
}
if (LOWORD(wp) == IDC_XML_SEARCH_BUTTON && HIWORD(wp) == BN_CLICKED) {
app.xmlSearchText = getText(hwnd, IDC_XML_SEARCH);
if (app.reverseXmlDirty || app.generatedXmlText.empty()) {
if (!app.xmlPreviewEdited && (app.reverseXmlDirty || app.generatedXmlText.empty())) {
generateReverseXml(app, false);
app.xmlSearchLastText.clear();
app.xmlSearchNextPos = 0;
@@ -2726,6 +2921,7 @@ LRESULT CALLBACK wndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
}
break;
case WM_NOTIFY:
// WM_NOTIFY 处理 TreeView/ListView/TabControl 的选择、双击、自绘等通知。
if (((LPNMHDR)lp)->idFrom == IDC_PREVIEW_LIST && ((LPNMHDR)lp)->code == NM_DBLCLK) {
auto* item = reinterpret_cast<NMITEMACTIVATE*>(lp);
if (item->iItem >= 0 && item->iSubItem >= 0) {
@@ -2743,17 +2939,26 @@ LRESULT CALLBACK wndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
size_t idx = static_cast<size_t>(cd->nmcd.lItemlParam);
int status = 0;
std::string cellStyle;
bool finalRangeChanged = false;
int modifiedOriginPickColumn = -1;
if (!app.previewSheets.empty() &&
app.activePreviewSheet >= 0 &&
app.activePreviewSheet < (int)app.previewSheets.size() &&
idx < app.previewSheets[app.activePreviewSheet].rows.size()) {
const PreviewSheetRow& row = app.previewSheets[app.activePreviewSheet].rows[idx];
status = row.status;
finalRangeChanged = row.finalRangeChanged;
modifiedOriginPickColumn = row.modifiedOriginPickColumn;
if (cd->iSubItem >= 0 && cd->iSubItem < (int)row.cellStyles.size()) {
cellStyle = row.cellStyles[cd->iSubItem];
}
}
if (cellStyle == "OriginPickBlue") cd->clrTextBk = RGB(207, 226, 243);
if (finalRangeChanged && cd->iSubItem == 8) {
cd->clrTextBk = RGB(153, 0, 0);
cd->clrText = RGB(255, 255, 255);
}
else if (modifiedOriginPickColumn == cd->iSubItem) cd->clrTextBk = RGB(180, 167, 214);
else if (cellStyle == "OriginPickBlue") cd->clrTextBk = RGB(207, 226, 243);
else if (status == 1) cd->clrTextBk = RGB(217, 234, 211);
else if (status == 2) cd->clrTextBk = RGB(252, 228, 214);
else if (status == 3) cd->clrTextBk = RGB(244, 204, 204);
@@ -2802,6 +3007,7 @@ int runPqGuiApp() {
AppState app;
g_app = &app;
app.inst = GetModuleHandleW(nullptr);
// COM 初始化主要服务于系统组件/对话框;主流程仍是普通 Win32 消息循环。
CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
INITCOMMONCONTROLSEX icc{};
@@ -2836,6 +3042,7 @@ int runPqGuiApp() {
ShowWindow(hwnd, SW_SHOW);
UpdateWindow(hwnd);
// 标准消息循环:用户操作 -> wndProc -> run* 业务函数 -> 刷新 AppState/控件。
MSG message;
while (GetMessageW(&message, nullptr, 0, 0) > 0) {
TranslateMessage(&message);

View File

@@ -22,26 +22,57 @@
#include "pq_app.h"
#include "tinyxml2.h" // tinyxml2
/*
* main.cpp
*
* 本文件是“映射解析 + 报表生成”的核心,也保留了命令行入口。
*
* 主流程:
* 1. loadValueMetas() 读取 ICD/XML 模板,把每个 <Value> 的 name、desc、DO、DA、
* Topic、DataType、Item、Sequence 等上下文整理成 ValueMeta。
* 2. readJsonDataFile() 读取 DATATopic 的 jsondataflattenJson() 压平成
* Value_... -> value 的键值对mergeSequences() 把连续编号合并成数组。
* 3. buildItemsForSheet() 按 SheetSpec 生成每张工作表的 OutItem 行。
* 4. addOriginFileToContext() 读取 TraceTopic 的 origin/MMS 数据matchOrigin()
* 用 DO + DA + 相位 + 序列范围匹配最终值和原始值。
* 5. writeExcelWorkbook() 输出 Excel 2003 XML 格式的 .xls供 Excel/GUI 预览打开。
*
* 标志/转换要点:
* - Sequence/@value == 7 代表星形接线 star。
* - Sequence/@value == 112 代表角形接线 delta。
* - DATA_TYPE "1" 会规范成 "01",用于匹配统计/闪变/实时 Sheet。
* - 指标名前缀 G_/MAX_/MIN_ 分别转换成 95值/最大值/最小值;无前缀默认平均值。
* - DA 中 phs* 是相位占位符,星形转换为 phsA/phsB/phsC角形转换为
* phsAB/phsBC/phsCA。
* - Value 名称中的 %start,end% 是 XML 序列模板;运行时显示成 [start-end]。
*/
using json = nlohmann::json;
namespace fs = std::filesystem;
/*
* XML <Value> 节点的完整业务元信息。
*
* 输入loadValueMetas() 在递归遍历 XML 时填充。
* 输出:后续用于把 jsondata 的 key 映射成 DO/DA、中文描述、接线方式、Topic/DataType。
*/
struct ValueMeta {
std::string name;
std::string desc;
std::string doPath;
std::string daPath;
std::string wiring = "common"; // star/delta/common
std::string topic;
std::string dataType;
std::string itemName;
std::string seqValue;
bool hasOffset = false;
int offset = 0;
std::string name; // Value/@name可能是普通指标 V1也可能是序列模板 MAX_V_%2,50%。
std::string desc; // Value/@desc最终表格“名称”列主要来自这里。
std::string doPath; // Value/@DOIEC 61850 MMS 对象路径前半段。
std::string daPath; // Value/@DAIEC 61850 MMS 数据属性路径后半段。
std::string wiring = "common"; // star/delta/common:由 Sequence/@value 推断。
std::string topic; // 父级 Topic/@name区分 HISDATA/RTDATA。
std::string dataType; // 父级 DataType/@value区分 01/02/03。
std::string itemName; // 父级 Item/@name如 V/I/PQ/F_S/F_L。
std::string seqValue; // 父级 Sequence/@value后续判断 T 相或接线方式会用到。
bool hasOffset = false; // Value/@Offset 是否存在。
int offset = 0; // 最终值偏移量,用于把最终值数组范围挪到 origin 对齐范围。
bool isSequence = false;
int seqStart = 0; // 例如 %2,50% 里的 2
int seqEnd = 0; // 例如 %2,50% 里的 50
std::string seqPrefixName; // 例如 MAX_V
bool isSequence = false; // name 是否匹配 %start,end% 序列模板。
int seqStart = 0; // 例如 %2,50% 里的 2
int seqEnd = 0; // 例如 %2,50% 里的 50
std::string seqPrefixName; // 例如 MAX_V_%2,50% 的前缀 MAX_V_。
};
static std::string readAll(const fs::path& p) {
@@ -58,6 +89,7 @@ static std::string ltrimBOM(std::string s) {
return s;
}
// DO/DA 中有时会出现 A||A||B 这种重复候选;这里保持顺序并去重,减少表格噪声。
static std::string collapseRepeatedAlternatives(const std::string& s) {
std::vector<std::string> parts;
std::unordered_set<std::string> seen;
@@ -100,6 +132,14 @@ static std::string toStr(const json& v) {
return v.dump();
}
/*
* 把嵌套 JSON 压平成下划线连接的 key。
*
* 输入示例:{"Value":{"V":{"A":{"MAX_V1":12}}}}
* 输出示例Value_V_A_MAX_V1 -> 12
*
* 数组会直接转成逗号分隔值,这样后续单元格可以拆成多列展示。
*/
static void flattenJson(const json& j, const std::string& prefix,
std::vector<std::pair<std::string, std::string>>& out) {
if (j.is_object()) {
@@ -121,7 +161,20 @@ static void flattenJson(const json& j, const std::string& prefix,
out.push_back({prefix, toStr(j)});
}
// 读取 XMLValue/@name -> 完整元信息desc/DO/DA
/*
* 读取 XMLValue/@name -> 完整元信息列表。
*
* 输入xmlPath通常是下载的 ICD/XML 映射文件。
* 输出unordered_map<name, vector<ValueMeta>>。同一个 name 可能在不同 Topic、
* DataType 或接线方式下出现,所以值是 vector。
*
* 递归 walk 的上下文参数含义:
* wiring 当前 Sequence 推断出的接线方式,默认 common
* topic 最近一个父级 Topic/@name
* dataType 最近一个父级 DataType/@value
* itemName 最近一个父级 Item/@name
* seqValue 最近一个父级 Sequence/@value。
*/
static std::unordered_map<std::string, std::vector<ValueMeta>> loadValueMetas(const fs::path& xmlPath) {
std::unordered_map<std::string, std::vector<ValueMeta>> mp;
tinyxml2::XMLDocument doc;
@@ -148,6 +201,7 @@ static std::unordered_map<std::string, std::vector<ValueMeta>> loadValueMetas(co
std::string nextDataType = dataType;
std::string nextItemName = itemName;
std::string nextSeqValue = seqValue;
// Topic/DataType/Item/Sequence 不是最终数据项,而是给子孙 Value 提供上下文。
if (nodeName == "Topic") {
const char* name = e->Attribute("name");
nextTopic = name ? name : "";
@@ -161,6 +215,7 @@ static std::unordered_map<std::string, std::vector<ValueMeta>> loadValueMetas(co
if (nodeName == "Sequence") {
const char* seqValue = e->Attribute("value");
nextSeqValue = seqValue ? seqValue : "";
// 项目约定7=星形接线112=角形接线;其他 Sequence 保持 common。
if (seqValue && std::string(seqValue) == "7") nextWiring = "star";
else if (seqValue && std::string(seqValue) == "112") nextWiring = "delta";
}
@@ -189,6 +244,7 @@ static std::unordered_map<std::string, std::vector<ValueMeta>> loadValueMetas(co
}
std::smatch m;
// name 中的 %start,end% 表示一个序列模板,后面要和 jsondata 的 [start-end] 对齐。
if (std::regex_match(vm.name, m, seqRe)) {
vm.isSequence = true;
vm.seqPrefixName = m[1].str(); // 例如 MAX_V
@@ -270,7 +326,19 @@ static bool parseSequenceKey(const std::string& key, std::string& groupId, int&
return false;
}
// ✅ 合并序列:但如果 metricName 在 xmlNames 里存在(比如 V1/MAX_V1/G_V1则禁止合并
/*
* 合并 jsondata 中的序列项。
*
* 输入flattenJson() 得到的 key/value 列表,例如:
* Value_V_A_MAX_V_2=...
* Value_V_A_MAX_V_3=...
*
* 输出:合并成 Value_V_A_MAX_V[2-3]=v2,v3。
*
* 保护规则:
* 如果相位后的 metricName 已经是 XML 中定义的真实指标名,例如 V1/MAX_V1/G_V1
* 就不能把它误当成“序列编号 1”合并否则会把普通指标压成数组。
*/
static std::vector<std::pair<std::string, std::string>>
mergeSequences(const std::vector<std::pair<std::string, std::string>>& flat,
const std::unordered_set<std::string>& xmlNames) {
@@ -341,6 +409,15 @@ struct ParsedKey {
std::string baseName; // 去掉 G_/MAX_/MIN_ 后的 name
};
/*
* 从 jsondata key 中提取排序和展示维度。
*
* 输入Value_V_A_MAX_V1、Value_I_T_G_THD 等。
* 输出:
* phase A/B/C/T
* stat 由指标名前缀转换G_=95值MAX_=最大值MIN_=最小值,默认平均值;
* baseName 去掉统计前缀后的基础指标名,用于匹配 XML Value/@name。
*/
static ParsedKey parseForSort(const std::string& key) {
std::string base = key;
auto bpos = base.find('[');
@@ -495,6 +572,7 @@ static bool metaFilterAllowed(const ValueMeta& vm, const MetaFilter& filter) {
return true;
}
// 多个 ValueMeta 都可能同名时用相位、统计类型、Offset、Tot 等特征打分选最像的那一个。
static int scoreMetaForKey(const ValueMeta& vm, const ParsedKey& pk) {
int score = 0;
if (daMatchesPhase(vm.daPath, pk.phase)) score += 20;
@@ -581,6 +659,7 @@ static const ValueMeta* findMetaForKey(
std::string preferred = preferredMetaName(rawMetric, pk);
std::string secondary = secondaryMetaName(rawMetric, pk);
// 第一优先级key 已经是 [start-end] 数组时,匹配 XML 的 %start,end% 序列模板。
int keyStart = 0, keyEnd = 0;
if (parseKeyRange(key, keyStart, keyEnd)) {
std::string seqPreferred = rawMetric.empty() ? preferred : rawMetric;
@@ -590,6 +669,7 @@ static const ValueMeta* findMetaForKey(
if (!seqCandidates.empty()) return bestMeta(seqCandidates, pk);
}
// 第二优先级按指标名精确匹配。G_ 前缀会优先转成 baseName因为 95 值常复用同一 XML 描述。
std::vector<const ValueMeta*> exactCandidates;
collectByName(metas, preferred, wiring, filter, exactCandidates);
if (exactCandidates.empty()) collectByName(metas, secondary, wiring, filter, exactCandidates);
@@ -693,6 +773,13 @@ struct OutItem {
int offset = 0;
};
/*
* 一张输出工作表的筛选条件。
*
* topic/dataType/wiring 决定从 XML ValueMeta 中取哪些项;
* showStatBlocks 决定统计类 Sheet 是否按 95值/平均值/最大值/最小值分段显示;
* frontType 用于 GUI 选择统计(cfg_stat_data)或实时(cfg_3s_data)时裁剪 Sheet。
*/
struct SheetSpec {
std::string name;
std::string topic;
@@ -727,7 +814,7 @@ struct OriginData {
std::vector<OriginRecord> records;
std::vector<OriginHeader> headers;
std::unordered_map<std::string, std::vector<size_t>> pathToRecords;
std::unordered_set<size_t> used;
std::unordered_set<size_t> used; // matchOrigin 命中后标记,最后可输出“已匹配/未匹配”清单。
};
struct OriginMatch {
@@ -754,8 +841,8 @@ struct JsonDataBucket {
struct WorkbookContext {
fs::path xmlPath;
std::unordered_map<std::string, std::vector<ValueMeta>> valueMetas;
std::unordered_set<std::string> xmlNames;
std::unordered_map<std::string, JsonDataBucket> dataByType;
std::unordered_set<std::string> xmlNames; // XML 中存在的原始 name用于避免序列误合并。
std::unordered_map<std::string, JsonDataBucket> dataByType; // DATA_TYPE -> jsondata 桶。
OriginData originData;
bool hasOrigin = false;
std::string frontType;
@@ -831,6 +918,7 @@ static std::vector<json> parseJsonRoots(const std::string& text) {
throw std::runtime_error("origin file contains unexpected content outside JSON object");
}
// origin 文件可能是多个 JSON 对象直接拼接;这里用括号深度扫描出每个完整根对象。
bool inString = false;
bool escape = false;
int depth = 0;
@@ -867,6 +955,7 @@ static void appendOriginRoot(OriginData& data, const fs::path& originPath, int s
std::string reportName = reportNameFromRoot(root, originPath, sourceOrder);
appendOriginHeader(data, originPath, sourceOrder, root, reportName);
// 常见 origin 结构是 { "mms_json": { "path": value } };如果没有 mms_json则直接把根对象当路径表。
const json* mms = &root;
if (root.is_object() && root.contains("mms_json") && root["mms_json"].is_object()) {
mms = &root["mms_json"];
@@ -932,6 +1021,12 @@ static std::string statInstanceSuffix(const std::string& stat) {
return "";
}
/*
* 把统计类型转换到 DO 实例号。
*
* 规则来自原始路径约定:平均/最大/最小/95值分别用 1/2/3/4 替换 DO 头部末尾的 0。
* 例如 xxx0$MX$... + 最大值 -> xxx2$MX$...
*/
static std::string applyStatInstance(const std::string& doPath, const std::string& stat) {
std::string suffix = statInstanceSuffix(stat);
if (suffix.empty()) return doPath;
@@ -947,6 +1042,7 @@ static std::string applyStatInstance(const std::string& doPath, const std::strin
}
static std::string phaseTokenForDa(const std::string& phase, const std::string& wiring) {
// 角形接线的 A/B/C 在 DA 中对应线电压 AB/BC/CA星形对应普通相 A/B/C。
if (wiring == "delta") {
if (phase == "A") return "phsAB";
if (phase == "B") return "phsBC";
@@ -975,6 +1071,7 @@ static std::string makeOriginPath(const std::string& doPath,
const std::string& phase,
const std::string& wiring) {
if (doPath.empty() || daPath.empty()) return "";
// origin 的 MMS 路径由 DO 与具体相位后的 DA 拼接stat 预留给 DO 实例号转换。
return doPath + "$" + concreteDaPath(daPath, phase, wiring);
}
@@ -1039,6 +1136,13 @@ static OriginMatch matchOrigin(OriginData* origin,
OriginMatch match;
if (!origin) return match;
/*
* 匹配步骤:
* 1. 根据 DO/DA/stat/phase/wiring 生成候选 origin 路径;
* 2. 若候选路径是 [start-end] 序列,按 sourceOrder 分组,选覆盖目标区间最多的报告;
* 3. 将选中报告中的连续索引拼成逗号值,并记录 valueIndexes 用于最终值对齐;
* 4. 普通路径则取第一个未使用记录,避免同一路径在多个输出行重复占用。
*/
for (const std::string& path : makeOriginPathCandidates(doPath, daPath, stat, phase, wiring)) {
if (path.empty()) continue;
@@ -1220,6 +1324,7 @@ static bool makeOutItemForWiring(const std::unordered_map<std::string, std::vect
const ValueMeta* vm = findMetaForKey(valueMetas, key, pk, wiring, filter);
if (requireMeta && !vm) return false;
// label 用“去掉统计后缀的 desc + stat”组成例如“电压最大值”。
std::string desc = vm ? vm->desc : "";
std::string descClean = cleanDescSuffix(desc);
std::string attrSort = !descClean.empty() ? descClean : pk.baseName;
@@ -1230,6 +1335,7 @@ static bool makeOutItemForWiring(const std::unordered_map<std::string, std::vect
int keyStart = 0, keyEnd = 0;
if (vm && vm->isSequence && parseKeyRange(key, keyStart, keyEnd)) {
// XML 中 DA 写的是 [%offset] 占位符,表格展示时改成最终可对齐的 [start-end] 范围。
daDisp = rewriteDaRangeForDisplay(daDisp, *vm, keyStart, keyEnd);
}
@@ -1352,6 +1458,7 @@ static OutItem makeOutItemFromMeta(const ValueMeta& vm,
const std::string& sheetWiring,
const std::unordered_map<std::string, std::string>& valueByKey) {
std::string dataKey = dataKeyForMeta(vm, phase);
// lookupDataKeyForMeta 会把 XML Offset 应用到取数 key 上,使最终值和 XML 定义对齐。
auto it = valueByKey.find(lookupDataKeyForMeta(vm, phase));
std::string val = it == valueByKey.end() ? "" : it->second;
@@ -1423,6 +1530,7 @@ static std::vector<OutItem> buildItemsForSheet(const std::unordered_map<std::str
const std::unordered_map<std::string, std::string>& valueByKey) {
std::vector<OutItem> out;
std::unordered_set<std::string> seen;
// 以 XML 定义为主生成行,即使 jsondata 缺值也保留表格行,后续用红色标出缺失。
for (const auto& kv : valueMetas) {
for (const ValueMeta& vm : kv.second) {
if (vm.topic != spec.topic || !specAllowsDataType(spec, vm.dataType)) continue;
@@ -1515,6 +1623,7 @@ static std::vector<std::string> roundOriginValuesForDisplay(const std::vector<st
static std::string compareValueKey(const std::string& value) {
std::string trimmed = trimValue(value);
long double number = 0;
// 数字统一为 6 位小数比较,避免 "1" 与 "1.000000" 被误判不同;非数字则小写比较。
if (parseNumberValue(trimmed, number)) return fixedSixNumber(number);
return lowerValue(trimmed);
}
@@ -1587,6 +1696,7 @@ static ComparePlan comparePlanForItem(const std::string& finalKey, const OutItem
int baseEnd = 0;
if (!parseArrayBaseRange(item.arrayBase, baseStart, baseEnd)) return plan;
int valueOffset = parseOffsetText(item.valueOffset, 0);
// originPickStart/End 表示原始值中真正要拿来和最终值第 1 列比较的数组下标区间。
plan.originPickStart = baseStart + valueOffset;
plan.originPickEnd = baseEnd + valueOffset;
return plan;
@@ -1715,6 +1825,16 @@ static void writeDataWorksheet(std::ofstream& ofs,
std::vector<OriginHeader> visibleOriginHeaders = visibleOriginHeadersForSheet(origin, items, wiring);
const int totalCols = std::max(9 + maxValues, 3 + static_cast<int>(visibleOriginHeaders.size()));
/*
* 每个数据项写两行:
* 第一行“最终值”:来自 jsondata按逗号拆到 值1..值64
* 第二行“原始值”:来自 origin按同一比较区间对齐展示。
* 行颜色:
* MatchGreen 最终值与原始值一致;
* MatchOrange 找到原始值但内容不一致;
* MatchRed 最终值为 null 或没有匹配到原始值;
* OriginPickBlue 标出序列比较时真正对齐起点。
*/
ofs << "<Worksheet ss:Name=\"" << htmlEscape(sheetName) << "\"><Table>\n";
ofs << "<Column ss:Width=\"210\"/><Column ss:Width=\"75\"/><Column ss:Width=\"85\"/><Column ss:Width=\"85\"/>";
ofs << "<Column ss:Width=\"310\"/><Column ss:Width=\"120\"/><Column ss:Width=\"280\"/>";
@@ -2014,6 +2134,7 @@ static void writeExcelWorkbook(const fs::path& outPath,
ofs << "<Style ss:ID=\"OriginPickBlue\" ss:Parent=\"Default\"><Interior ss:Color=\"#CFE2F3\" ss:Pattern=\"Solid\"/></Style>\n";
ofs << "</Styles>\n";
// SheetSpec 是报表目录统计数据和实时数据各自按接线方式、DATA_TYPE 分 Sheet。
std::vector<SheetSpec> sheets = {
{"星形接线", "HISDATA", "01", "star", true, "cfg_stat_data"},
{"角型接线", "HISDATA", "01", "delta", true, "cfg_stat_data"},
@@ -2060,6 +2181,7 @@ static const std::vector<std::string>& statBlocks() {
}
static void writeWorkbookStage(WorkbookContext& ctx, const fs::path& outPath) {
// 每次重写工作簿前清空 used让匹配过程从头计算避免多次刷新后状态累积。
if (ctx.hasOrigin) ctx.originData.used.clear();
OriginData* origin = ctx.hasOrigin ? &ctx.originData : nullptr;
writeExcelWorkbook(outPath, statBlocks(), ctx.valueMetas, ctx.dataByType, origin, ctx.frontType);
@@ -2071,6 +2193,7 @@ static WorkbookContext createWorkbookTemplateFromXml(const fs::path& xmlPath, co
ctx.frontType = frontType;
ctx.valueMetas = loadValueMetas(xmlPath);
ctx.xmlNames.reserve(ctx.valueMetas.size() * 2);
// xmlNames 只保存 XML 原名,用于 mergeSequences 防止 V1/G_V1 被误合并。
for (const auto& kv : ctx.valueMetas) ctx.xmlNames.insert(kv.first);
writeWorkbookStage(ctx, outPath);
return ctx;
@@ -2117,6 +2240,7 @@ static void applyJsonDataToWorkbook(WorkbookContext& ctx, const json& data, cons
auto merged = mergeSequences(flat, ctx.xmlNames);
std::string dataType = dataTypeFromMerged(merged);
JsonDataBucket& bucket = ctx.dataByType[dataType];
// 同一个 DATA_TYPE 多次出现时保留第一份,避免 MQ 重复消息覆盖已生成结果。
if (bucket.hasData) continue;
bucket.headers.clear();
bucket.valueByKey.clear();
@@ -2200,6 +2324,7 @@ bool pqRunParser(const std::string& xmlPath,
fs::path xmlFile = fs::u8path(xmlPath);
fs::path output = outPath.empty() ? fs::path("final_sorted.xls") : fs::u8path(outPath);
// 解析顺序固定:先 XML 建模板,再填 jsondata最后追加 origin 并重写带对比结果的工作簿。
WorkbookContext ctx = createWorkbookTemplateFromXml(xmlFile, output, frontType);
for (const auto& jsonPath : jsonPaths) {
if (!jsonPath.empty()) applyJsonDataToWorkbook(ctx, readJsonDataFile(fs::u8path(jsonPath)), output);
@@ -2218,6 +2343,7 @@ bool pqRunParser(const std::string& xmlPath,
int main(int argc, char** argv) {
try {
if (argc == 1) {
// 无参数启动 GUI带参数则走下面的旧命令行解析模式。
return runPqGuiApp();
}

View File

@@ -23,6 +23,26 @@
#include <unordered_map>
#include <unordered_set>
/*
* mq.cpp
*
* 本文件负责“外部数据输入/输出”:
* - HTTP 请求台账、ICD 列表和 XML 文件下载;
* - 动态加载 RocketMQ DLL初始化生产者/消费者;
* - 发送数据追踪请求;
* - 消费 DATATopic/TraceTopic把 jsondata/origin 落盘到 trace_data
* - RocketMQ 不可用时把待发消息写到 mq_outbox_*.jsonl便于离线排查。
*
* 数据追踪落盘结构:
* runtime\trace_data\process_N\device_x\monitor_y\
* trace_context.json 本次追踪上下文GUI 自动解析依赖它;
* sent_trace_request.json 已发送/待发送追踪请求;
* jsondata_01.txt DATA_TYPE=01 的 DATATopic 数据;
* jsondata_02.txt/03.txt 闪变等其它 DATA_TYPE
* origin_*.txt TraceTopic 原始 MMS/报告数据;
* final_sorted.xls 自动解析生成的工作簿。
*/
using json = nlohmann::json;
namespace fs = std::filesystem;
@@ -39,6 +59,7 @@ fs::path appRootDir() {
fs::path dir = executableDir();
std::wstring name = dir.filename().wstring();
std::transform(name.begin(), name.end(), name.begin(), [](wchar_t ch) { return (wchar_t)towlower(ch); });
// exe 通常在 bin 下运行runtime/source 与 bin 同级,所以从 bin 回到项目根。
if (name == L"bin" && dir.has_parent_path()) return dir.parent_path();
return dir;
}
@@ -97,11 +118,13 @@ bool setContains(const std::unordered_set<std::string>& allowed, const std::stri
std::unordered_set<std::string> parseStatusSet(const std::string& text) {
std::unordered_set<std::string> out;
try {
// 支持 GUI 默认的 JSON 数组写法,例如 "[0]"、"[1,2]"。
json j = json::parse(text);
if (j.is_array()) {
for (const auto& v : j) out.insert(jsonString(json{{"v", v}}, "v"));
}
} catch (...) {
// 也兼容用户手写 "0,1,2" 或带方括号/引号的宽松格式。
std::stringstream ss(text);
std::string part;
while (std::getline(ss, part, ',')) {
@@ -177,6 +200,7 @@ std::string httpRequest(const std::string& url,
if (!connect) throw std::runtime_error("WinHttpConnect 失败");
DWORD flags = (uc.nScheme == INTERNET_SCHEME_HTTPS) ? WINHTTP_FLAG_SECURE : 0;
// 这个项目约定body 为空走 GET不为空走 POST JSON。
const wchar_t* method = body.empty() ? L"GET" : L"POST";
request = WinHttpOpenRequest(connect, method, object.c_str(),
nullptr, WINHTTP_NO_REFERER,
@@ -300,6 +324,12 @@ std::string nowIsoMillis() {
return oss.str();
}
long long nowUnixMillis() {
return std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch())
.count();
}
void appendTextFile(const fs::path& path, const std::string& text) {
fs::create_directories(path.parent_path().empty() ? fs::path(".") : path.parent_path());
std::ofstream ofs(path, std::ios::binary | std::ios::app);
@@ -346,6 +376,7 @@ bool tagMatches(const std::string& expectedExpression, const std::string& actual
while (start <= expr.size()) {
size_t pos = expr.find("||", start);
std::string part = trimCopy(expr.substr(start, pos == std::string::npos ? std::string::npos : pos - start));
// RocketMQ C 接口订阅表达式可能是 "tagA || tagB",消费时也按同样规则过滤。
if (part == "*" || part == actual) return true;
if (pos == std::string::npos) break;
start = pos + 2;
@@ -369,6 +400,7 @@ long long findTimestampValue(const json& value) {
"Value_TIME", "valueTime", "timestamp", "timeStamp", "storeTimestamp", "time", "Time"
};
if (value.is_object()) {
// 不同消息体时间字段命名不统一,因此递归查找一组常见 key。
for (const auto& key : keys) {
auto it = value.find(key);
if (it != value.end()) {
@@ -507,6 +539,7 @@ bool findMonitorIdValue(const json& value, std::string& out) {
}
for (auto it = value.begin(); it != value.end(); ++it) {
if ((it.key() == "messageBody" || it.key() == "body") && it.value().is_string()) {
// RocketMQ 外层 JSON 的 messageBody 常是字符串化 JSON需要再 parse 一次。
std::string embedded = it.value().get<std::string>();
if (looksLikeJsonText(embedded)) {
try {
@@ -663,6 +696,14 @@ struct CSendResult {
long long offset;
};
/*
* RocketMqRuntime 是进程内唯一的 RocketMQ 适配器。
*
* 设计原因:
* 1. RocketMQ C DLL 通过 LoadLibrary 动态加载,运行时可能不存在;
* 2. GUI 会多次点击“初始化 MQ”需要避免重复启动/热重启导致 DLL 崩溃;
* 3. MQ 回调在 SDK 线程触发,必须用互斥锁保护会话状态,再通过 event_ 通知 GUI。
*/
class RocketMqRuntime {
struct DetachedHandles {
CProducer* producer = nullptr;
@@ -681,7 +722,9 @@ class RocketMqRuntime {
std::string xmlPath;
std::string frontType;
std::string guid;
std::unordered_set<std::string> receivedDataTypes;
long long startedAtMs = 0; // 点击“数据追踪”的时间;更早的 MQ 存量消息不得进入本次会话。
std::unordered_set<std::string> receivedDataTypes; // 同一次追踪中每个 DATA_TYPE 只保留一次。
std::unordered_set<std::string> receivedTraceReports; // 同一次追踪中每个 rpt_id 只保留第一条。
long long dataTimestamp = 0;
int originCounter = 0;
};
@@ -700,6 +743,7 @@ public:
DetachedHandles old;
{
std::lock_guard<std::mutex> lock(mu_);
// 如果生产/消费连接参数未变,只刷新配置和回调;避免重复 StartProducer/StartPushConsumer。
bool sameRuntimeConfig = cfg.producer == cfg_.producer &&
cfg.ipport == cfg_.ipport &&
cfg.producerAccessKey == cfg_.producerAccessKey &&
@@ -721,6 +765,7 @@ public:
return true;
}
if (active_ && producer_ && consumer_) {
// 连接参数变了也不在进程内重启底层 SDK提示用户重启工具后生效。
cfg_ = cfg;
event_ = std::move(event);
if (log) {
@@ -785,6 +830,7 @@ public:
SetPushConsumerLogPath(consumer_, mqLogDir.c_str());
SetPushConsumerThreadCount(consumer_, 2);
// 同一个 Topic 可能同时配置统计/实时 Tag这里合并成 RocketMQ 支持的 "A || B" 表达式。
std::map<std::string, std::string> subscriptions;
auto addSubscription = [&](const std::string& topic, const std::string& tag) {
if (topic.empty()) return;
@@ -830,8 +876,10 @@ public:
const PqLedgerMonitor& monitor,
const std::string& frontType,
const std::string& guid) {
const long long startedAtMs = nowUnixMillis();
std::lock_guard<std::mutex> lock(mu_);
cfg_ = cfg;
// 记录当前追踪目标。后续 MQ 回调会用 monitorId 或 guid 把消息路由到对应 TraceSession。
currentTraceDir_ = pqTraceDirectoryFor(cfg, device, monitor);
currentXmlPath_ = pqFindDeviceXmlPath(cfg, device);
currentMonitorId_ = monitor.id;
@@ -842,10 +890,9 @@ public:
dataTimestamp_ = 0;
originCounter_ = 0;
std::string monitorKey = normalizeMonitorId(monitor.id);
auto old = sessions_.find(monitorKey);
if (old != sessions_.end() && !old->second.guid.empty()) {
sessionsByGuid_.erase(normalizeMonitorId(old->second.guid));
}
// 一次按钮点击只允许一个活动会话。否则旧测点后续到达的消息仍会写入旧会话目录。
sessions_.clear();
sessionsByGuid_.clear();
TraceSession& session = sessions_[monitorKey];
session.monitorId = monitor.id;
session.monitorName = monitor.name;
@@ -853,11 +900,14 @@ public:
session.xmlPath = currentXmlPath_;
session.frontType = frontType;
session.guid = guid;
session.startedAtMs = startedAtMs;
session.receivedDataTypes.clear();
session.receivedTraceReports.clear();
session.dataTimestamp = 0;
session.originCounter = 0;
if (!guid.empty()) sessionsByGuid_[normalizeMonitorId(guid)] = monitorKey;
// GUI 自动解析优先读取这个上下文,避免用户手动维护 json/origin/output 路径。
json context = {
{"deviceId", device.id},
{"deviceName", device.name},
@@ -867,6 +917,8 @@ public:
{"monitorName", monitor.name},
{"guid", guid},
{"frontType", frontType},
{"startedAt", nowIsoMillis()},
{"startedAtMs", startedAtMs},
{"xmlPath", currentXmlPath_},
{"jsonDataPath", (dir / "jsondata_01.txt").u8string()},
{"jsonDataPaths", json::array({
@@ -879,10 +931,13 @@ public:
writeTextFile(dir / "trace_context.json", context.dump(2));
}
bool sendTrace(const PqToolConfig& cfg,
const std::string& payload,
std::string& err,
PqLogFn log) {
bool sendPayload(const std::string& topic,
const std::string& tag,
const std::string& messageKey,
const std::string& payload,
const std::string& label,
std::string& err,
PqLogFn log) {
std::lock_guard<std::mutex> lock(mu_);
err.clear();
if (!active_ || !producer_) {
@@ -890,14 +945,14 @@ public:
return false;
}
CMessage* msg = CreateMessage(nonEmptyCStr(cfg.topicLOG, ""));
CMessage* msg = CreateMessage(nonEmptyCStr(topic, ""));
if (!msg) {
err = "CreateMessage 返回空指针";
return false;
}
SetMessageTags(msg, nonEmptyCStr(cfg.tagLOG, ""));
SetMessageKeys(msg, nonEmptyCStr(cfg.keyLOG, ""));
SetMessageTags(msg, nonEmptyCStr(tag, ""));
SetMessageKeys(msg, nonEmptyCStr(messageKey, ""));
SetMessageBody(msg, payload.c_str());
CSendResult result{};
@@ -909,7 +964,7 @@ public:
}
if (log) {
log("RocketMQ 数据追踪消息已发送TopicLOG=" + cfg.topicLOG
log("RocketMQ " + label + "消息已发送Topic=" + topic
+ "MsgId=" + std::string(result.msgId));
}
return true;
@@ -929,6 +984,7 @@ private:
actualMonitor.clear();
reason.clear();
// 优先用消息体中的测点 ID 路由;没有测点 ID 时再用 guid/key 路由。
actualMonitor = monitorIdFromJsonText(body);
std::string normalized = normalizeMonitorId(actualMonitor);
if (!normalized.empty()) {
@@ -967,6 +1023,7 @@ private:
+ " tag=" + tag
+ " key=" + key
+ " bodyLen=" + std::to_string(body.size()) + "\n");
// 先按 Topic/Tag 判断消息类型,再按 monitorId/guid 过滤到当前追踪会话。
std::string dataFrontType = dataFrontTypeForMessageLocked(topic, tag);
bool isData = !dataFrontType.empty();
bool isTrace = topic == cfg_.traceTopic && tagMatches(cfg_.traceTag, tag);
@@ -990,10 +1047,22 @@ private:
+ " topic=" + topic + " tag=" + tag + "\n");
return 0;
}
// PushConsumer 可能在点击后继续投递积压消息。必须在任何去重、落盘和 GUI 事件之前
// 使用 Broker 存储时间做会话边界判断,保证 jsondata 与 origin 都来自本次追踪。
if (storeTs <= 0 || storeTs < session->startedAtMs) {
appendTextFile(runtimeLogPath("mq_messages.log"), nowIsoMillis()
+ " skipped " + (isData ? "DATATopic" : "TraceTopic")
+ " before trace session: storeTs=" + std::to_string(storeTs)
+ ", startedAtMs=" + std::to_string(session->startedAtMs)
+ ", monitor=" + session->monitorId
+ " topic=" + topic + " tag=" + tag + "\n");
return 0;
}
}
if (isData) {
std::string dataType = dataTypeFromJsonText(body);
std::string normalizedType = normalizeDataType(dataType);
// 同一 DATA_TYPE 重复到达时跳过,避免后来的旧数据覆盖已解析文件。
if (!session->receivedDataTypes.insert(normalizedType).second) {
appendTextFile(runtimeLogPath("mq_messages.log"), nowIsoMillis()
+ " skipped DATATopic duplicate DATA_TYPE=" + normalizedType
@@ -1012,10 +1081,22 @@ private:
if (event_) event_({"data", eventTitle("DATATopic", out.u8string()), body, out.u8string(), session->monitorId, session->traceDir});
} else if (isTrace) {
long long bodyTs = timestampFromJsonText(body);
// 如果 TraceTopic 的时间早于已经收到的 DATATopic认为是旧追踪结果避免串入本次追踪。
if (session->dataTimestamp > 0 && bodyTs > 0 && bodyTs < session->dataTimestamp) {
appendTextFile(runtimeLogPath("mq_messages.log"), nowIsoMillis() + " TraceTopic tag=" + tag + " skipped by timestamp\n");
} else {
fs::path out = nextOriginOutputPathLocked(*session, reportNameFromJsonText(body));
std::string reportName = reportNameFromJsonText(body);
std::string reportKey = lowerAscii(trimCopy(reportName));
if (reportKey.empty()) reportKey = "__unknown_report__";
// TopicAsk 的 limit=20 可能返回同一 rpt_id 的多份历史采样;本工具每次点击只取第一份。
if (!session->receivedTraceReports.insert(reportKey).second) {
appendTextFile(runtimeLogPath("mq_messages.log"), nowIsoMillis()
+ " skipped TraceTopic duplicate report=" + reportKey
+ " monitor=" + session->monitorId
+ " topic=" + topic + " tag=" + tag + "\n");
return 0;
}
fs::path out = nextOriginOutputPathLocked(*session, reportName);
writeTextFile(out, body);
appendTextFile(runtimeLogPath("mq_messages.log"), nowIsoMillis() + " TraceTopic tag=" + tag + " -> " + out.u8string() + "\n");
if (event_) event_({"trace", eventTitle("TraceTopic", out.u8string()), body, out.u8string(), session->monitorId, session->traceDir});
@@ -1104,6 +1185,7 @@ private:
bool loadLibraryLocked() {
if (module_) return true;
// 兼容不同部署目录和 DLL 命名LoadLibraryEx 用于带路径 DLL 时顺带搜索其依赖。
std::vector<std::string> candidates = {
"rocketmq-client-cpp.dll",
"rocketmq.dll",
@@ -1328,6 +1410,7 @@ RocketMqRuntime& rocketRuntime() {
PqToolConfig pqDefaultConfig() {
PqToolConfig cfg;
// 这里只覆盖需要根据 exe 位置动态计算的路径,以及 MQ 常用默认参数。
cfg.producer = "TRACE_PRODUCER";
cfg.consumer = "TRACE_CONSUMER";
cfg.dataStatTopic = "MQ_INST_1697676490574298_Bm3DLamM%LN_Topic";
@@ -1350,6 +1433,7 @@ std::string pqTraceDirectoryFor(const PqToolConfig& cfg,
const PqLedgerDevice& device,
const PqLedgerMonitor& monitor) {
fs::path root = fs::u8path(cfg.traceRootDir.empty() ? "trace_data" : cfg.traceRootDir);
// 路径中同时放名称和 id既方便人读也保证同名测点不互相覆盖。
std::string proc = safePathPart(firstNonEmpty(device.processNo, "1"), "1");
std::string dev = safePathPart(firstNonEmpty(device.name, device.id), "device");
std::string mon = safePathPart(firstNonEmpty(monitor.name, monitor.id), "monitor");
@@ -1372,6 +1456,7 @@ void pqClearTraceRecords(const PqToolConfig& cfg,
std::string pqFindDeviceXmlPath(const PqToolConfig& cfg, const PqLedgerDevice& device) {
if (!device.xmlPath.empty() && fs::exists(fs::u8path(device.xmlPath))) return device.xmlPath;
// 下载 ICD 后会把接口响应缓存到 runtime\json\icd_response.json这里用 devType 反查本地 XML。
fs::path saveDir = fs::u8path(cfg.xmlSaveDir.empty() ? "downloaded_xml" : cfg.xmlSaveDir);
auto existing = [&](const std::string& name) -> std::string {
if (name.empty()) return "";
@@ -1423,6 +1508,7 @@ bool pqFetchLedgerAndIcd(const PqToolConfig& cfg,
devices.clear();
firstXmlPath.clear();
// 1. 请求台账frontIp + terminalStatus/runFlag。
json ledgerReq;
ledgerReq["ip"] = cfg.frontIp;
try {
@@ -1438,6 +1524,7 @@ bool pqFetchLedgerAndIcd(const PqToolConfig& cfg,
std::unordered_set<std::string> terminalAllowed = parseStatusSet(cfg.terminalStatus);
std::set<std::string> devTypes;
// 2. 解析装置和 monitorData 测点,并按 terminalStatus 过滤装置。
json data = parseResponseDataArray(ledgerRaw, "台账");
for (const auto& item : data) {
PqLedgerDevice dev;
@@ -1479,6 +1566,7 @@ bool pqFetchLedgerAndIcd(const PqToolConfig& cfg,
if (log) log("台账解析完成: 装置 " + std::to_string(devices.size()) + "");
// 3. 请求 ICD 列表。icdFlag=1 时只请求本次台账中出现的 devType。
json icdReq = json::array();
if (cfg.icdFlag == "1") {
for (const auto& t : devTypes) icdReq.push_back(t);
@@ -1492,6 +1580,7 @@ bool pqFetchLedgerAndIcd(const PqToolConfig& cfg,
json icdData = parseResponseDataArray(icdRaw, "ICD");
int downloaded = 0;
std::unordered_map<std::string, std::string> devTypeToXml;
// 4. 按 filePath 下载 XML保存到 xmlSaveDir并建立 devType -> XML 路径映射。
for (const auto& model : icdData) {
std::string modelId = jsonString(model, "id");
std::string devType = jsonString(model, "devType");
@@ -1524,6 +1613,7 @@ bool pqFetchLedgerAndIcd(const PqToolConfig& cfg,
}
bool pqInitializeMq(const PqToolConfig& cfg, PqLogFn log, PqMqEventFn event) {
// 初始化时先把界面配置落盘,便于用户核对,也便于离线排查 MQ 参数。
json saved = {
{"producer", cfg.producer},
{"ipport", cfg.ipport},
@@ -1532,6 +1622,9 @@ bool pqInitializeMq(const PqToolConfig& cfg, PqLogFn log, PqMqEventFn event) {
{"topicLOG", cfg.topicLOG},
{"tagLOG", cfg.tagLOG},
{"keyLOG", cfg.keyLOG},
{"topicAsk", cfg.topicAsk},
{"tagAsk", cfg.tagAsk},
{"keyAsk", cfg.keyAsk},
{"consumer", cfg.consumer},
{"consumerIpport", cfg.consumerIpport},
{"consumerAccessKey", cfg.consumerAccessKey},
@@ -1552,6 +1645,7 @@ bool pqInitializeMq(const PqToolConfig& cfg, PqLogFn log, PqMqEventFn event) {
writeTextFile(runtimeJsonPath("mq_runtime_config.json"), saved.dump(2));
if (log) log("MQ config saved to " + runtimeJsonPath("mq_runtime_config.json").u8string());
if (!rocketRuntime().initialize(cfg, log, event) && log) {
// 这里仍返回 true让 GUI 可以继续构造追踪请求;发送失败时会进入 outbox 兜底。
log("RocketMQ runtime is not active; trace messages will be written to mq_outbox_*.jsonl.");
log("RocketMQ source is present, but a 32-bit rocketmq-client-cpp.dll or rocketmq.dll is still required.");
}
@@ -1571,6 +1665,7 @@ std::string pqBuildTracePayload(const PqToolConfig& cfg,
std::string guid = makeGuid();
int processNo = 0;
if (frontType == "cfg_3s_data") {
// 实时数据进程约定为 0统计数据使用台账中的 processNo。
processNo = 0;
} else {
try {
@@ -1580,6 +1675,7 @@ std::string pqBuildTracePayload(const PqToolConfig& cfg,
}
}
// body 是前置识别的 set_log 指令outer 是 RocketMQ 业务包装。
json body = {
{"code", "set_log"},
{"nodeId", cfg.frontInst},
@@ -1602,6 +1698,26 @@ std::string pqBuildTracePayload(const PqToolConfig& cfg,
return outer.dump();
}
std::string buildRealtimeAskPayload(const PqLedgerDevice& device,
const PqLedgerMonitor& monitor) {
const std::string guid = makeGuid();
json body = {
{"devSeries", device.id},
{"limit", 20},
{"line", monitor.id},
{"realData", true},
{"soeData", true}
};
json outer = {
{"key", guid},
{"source", "WEB"},
{"sendTime", nowIsoMillis()},
{"retryTimes", 0},
{"messageBody", body.dump()}
};
return outer.dump();
}
bool pqSendTraceRequest(const PqToolConfig& cfg,
const PqLedgerDevice& device,
const PqLedgerMonitor& monitor,
@@ -1610,6 +1726,7 @@ bool pqSendTraceRequest(const PqToolConfig& cfg,
PqMqEventFn event) {
std::string payload = pqBuildTracePayload(cfg, device, monitor, frontType);
std::string guid = guidFromJsonText(payload);
// 先设置追踪目标,确保追踪请求发出后立即到达的 MQ 回调也能正确路由。
rocketRuntime().setTraceTarget(cfg, device, monitor, frontType, guid);
fs::path traceDir = fs::u8path(pqTraceDirectoryFor(cfg, device, monitor));
fs::create_directories(traceDir);
@@ -1617,24 +1734,36 @@ bool pqSendTraceRequest(const PqToolConfig& cfg,
writeTextFile(sentPath, prettyJsonOrRaw(payload));
writeTextFile(runtimeJsonPath("last_trace_request.json"), json::parse(payload).dump(2));
if (event) event({"sent", eventTitle("Send", sentPath.u8string()), payload, sentPath.u8string(), monitor.id, traceDir.u8string()});
std::string mqError;
if (rocketRuntime().sendTrace(cfg, payload, mqError, log)) {
if (log) log("Trace target monitor=" + monitor.name + "(" + monitor.id + "), frontType=" + frontType);
return true;
auto sendOrSave = [&](const std::string& topic,
const std::string& tag,
const std::string& messageKey,
const std::string& body,
const std::string& label) {
std::string mqError;
if (rocketRuntime().sendPayload(topic, tag, messageKey, body, label, mqError, log)) return;
// RocketMQ 未初始化或发送失败时不丢请求,按目标 Topic 追加到对应 outbox。
std::string topicPart = sanitizeFilePart(topic.empty() ? label : topic);
fs::path outbox = runtimeJsonPath("mq_outbox_" + topicPart + ".jsonl");
std::ofstream ofs(outbox, std::ios::binary | std::ios::app);
if (!ofs) throw std::runtime_error("无法写入 MQ outbox: " + outbox.string());
ofs << body << "\n";
if (log) log(label + "消息发送失败(" + mqError + "),已写入 " + outbox.string());
};
// 第一条始终先发到 TopicLOG请求前置建立本次追踪日志。
sendOrSave(cfg.topicLOG, cfg.tagLOG, cfg.keyLOG, payload, "set_log");
// cfg_3s_data 紧接着向 TopicAsk 请求该装置、测点的实时/SOE 数据。
if (frontType == "cfg_3s_data") {
std::string askPayload = buildRealtimeAskPayload(device, monitor);
fs::path askPath = traceDir / "sent_realtime_request.json";
writeTextFile(askPath, prettyJsonOrRaw(askPayload));
writeTextFile(runtimeJsonPath("last_realtime_request.json"), json::parse(askPayload).dump(2));
if (event) event({"sent", eventTitle("Send Ask", askPath.u8string()), askPayload, askPath.u8string(), monitor.id, traceDir.u8string()});
sendOrSave(cfg.topicAsk, cfg.tagAsk, cfg.keyAsk, askPayload, "realtime ask");
}
std::string topicPart = sanitizeFilePart(cfg.topicLOG.empty() ? "TopicLOG" : cfg.topicLOG);
fs::path outbox = runtimeJsonPath("mq_outbox_" + topicPart + ".jsonl");
std::ofstream ofs(outbox, std::ios::binary | std::ios::app);
if (!ofs) throw std::runtime_error("无法写入 MQ outbox: " + outbox.string());
ofs << payload << "\n";
writeTextFile(runtimeJsonPath("last_trace_request.json"), json::parse(payload).dump(2));
if (log) {
log("已构造数据追踪消息TopicLOG=" + cfg.topicLOG + ", TagLOG=" + cfg.tagLOG + ", KeyLOG=" + cfg.keyLOG);
log("测点=" + monitor.name + "(" + monitor.id + "), frontType=" + frontType);
log("当前未直连 RocketMQ消息已写入 " + outbox.string());
}
if (log) log("Trace target monitor=" + monitor.name + "(" + monitor.id + "), frontType=" + frontType);
return true;
}

View File

@@ -1,10 +1,44 @@
#pragma once
/*
* pq_app.h
*
* 这是整个工具的公共边界文件:
* - interface.cpp 负责 Win32 GUI通过这些结构体读取/显示配置和台账。
* - mq.cpp 负责 HTTP 台账/ICD 获取、RocketMQ 初始化和数据追踪落盘。
* - main.cpp 负责 JSON/XML 映射解析,生成 Excel 2003 XML 格式的 .xls。
* - icd_mapper.cpp 负责把外部 ICD 中的 DOI 描述补写到工作簿。
*
* 头文件中只放跨模块共享的数据结构和函数声明,不放 Win32/RocketMQ 的实现细节。
*/
#include <functional>
#include <string>
#include <utility>
#include <vector>
/*
* 工具运行配置。
*
* 输入来源:
* 1. pqDefaultConfig() 给出默认值;
* 2. GUI 中 readConfig() 会用用户界面上的编辑框覆盖默认值;
* 3. 命令行模式直接使用 main.cpp 中传入的 XML/JSON/origin 路径。
*
* 关键字段:
* frontInst/frontIp 前置节点标识和 IP参与台账请求和追踪消息。
* terminalStatus 终端状态过滤条件,支持 JSON 数组字符串,如 "[0]"。
* monitorStatus 预留的测点状态过滤字段。
* icdFlag 为 "1" 时按 devType 请求 ICD 列表,为其他值时请求全量/默认列表。
* dataStatTopic 统计数据 Topic对应 frontType=cfg_stat_data。
* dataRealtimeTopic 实时数据 Topic对应 frontType=cfg_3s_data。
* topicAsk/tagAsk/keyAsk cfg_3s_data 发送 set_log 后的实时数据请求目标。
* dataTopic/dataTag/dataKey GUI 读取 frontType 后会设置成当前活跃数据 Topic/Tag/Key。
* traceRootDir 每次“数据追踪”按进程/装置/测点保存 JSON、origin 和报表的根目录。
*
* 输出影响:
* xmlSaveDir、jsonDataPath、originPaths、outputPath、traceRootDir 控制程序落盘位置。
*/
struct PqToolConfig {
std::string frontInst = "6c2adb324940e19e208d717f4970f2ee";
std::string frontIp = "192.168.1.68";
@@ -24,6 +58,9 @@ struct PqToolConfig {
std::string topicLOG = "ask_log_Topic";
std::string tagLOG = "6c2adb324940e19e208d717f4970f2ee";
std::string keyLOG = "Test_Keys";
std::string topicAsk = "ask_data_Topic";
std::string tagAsk = "6c2adb324940e19e208d717f4970f2ee";
std::string keyAsk = "Test_Keys";
std::string consumer = "TRACE_CONSUMER";
std::string consumerIpport = "192.168.1.68:9876";
@@ -50,6 +87,12 @@ struct PqToolConfig {
std::string traceRootDir = "runtime\\trace_data";
};
/*
* 台账中的单个测点。
*
* 输入pqFetchLedgerAndIcd() 从 WebDevice 接口的 monitorData 数组解析。
* 输出GUI 左侧树显示;数据追踪时 monitor.id 会写入 MQ 追踪消息。
*/
struct PqLedgerMonitor {
std::string id;
std::string name;
@@ -60,6 +103,15 @@ struct PqLedgerMonitor {
std::vector<std::pair<std::string, std::string>> fields;
};
/*
* 台账中的单个装置。
*
* 输入pqFetchLedgerAndIcd() 从 WebDevice 接口的 data 数组解析。
* 输出:
* - GUI 按 processNo -> device -> monitor 组织成树;
* - devType 用于匹配并下载对应 ICD/XML
* - xmlPath 记录当前装置优先使用的 XML 模板。
*/
struct PqLedgerDevice {
std::string id;
std::string name;
@@ -81,8 +133,19 @@ struct PqLedgerDevice {
std::vector<PqLedgerMonitor> monitors;
};
// 日志回调业务函数只发字符串GUI/命令行决定显示到日志框还是控制台。
using PqLogFn = std::function<void(const std::string&)>;
/*
* MQ/追踪过程中抛给 GUI 的事件。
*
* kind:
* sent 已构造并发送/落盘的数据追踪请求;
* data DATATopic 收到的 jsondata 数据;
* trace TraceTopic 收到的 origin/原始 MMS 数据。
*
* path/traceDir 让 GUI 能把事件定位到具体追踪目录并自动刷新解析预览。
*/
struct PqMqEvent {
std::string kind;
std::string title;
@@ -94,44 +157,114 @@ struct PqMqEvent {
using PqMqEventFn = std::function<void(const PqMqEvent&)>;
// 默认配置。mq.cpp 会把运行期路径修正到 exe 所在目录旁的 runtime 目录。
PqToolConfig pqDefaultConfig();
/*
* 获取台账并下载 ICD/XML。
*
* 输入cfg 中的 WebDevice/WebIcd/WebFiledownload、状态过滤和保存目录。
* 输出:
* devices 装置和测点列表;
* firstXmlPath 第一个成功下载的 XML用于自动填充 GUI 的 XML 路径;
* return true 表示接口请求和解析流程完成,异常会抛出到调用方。
*/
bool pqFetchLedgerAndIcd(const PqToolConfig& cfg,
std::vector<PqLedgerDevice>& devices,
std::string& firstXmlPath,
PqLogFn log);
/*
* 初始化 RocketMQ 运行时。
*
* 输入生产者、消费者、Topic、Tag、Key、NameServer 和鉴权配置。
* 输出:运行配置会保存到 runtime\json\mq_runtime_config.json
* 如果 DLL 不可用,函数仍返回 true但后续发送会落到 mq_outbox_*.jsonl。
*/
bool pqInitializeMq(const PqToolConfig& cfg, PqLogFn log, PqMqEventFn event = {});
/*
* 构造数据追踪消息体。
*
* 输入:
* device.processNo 统计数据追踪时参与 processNo实时数据固定 processNo=0。
* monitor.id 写入 messageBody.id代表追踪的测点。
* frontType cfg_stat_data 或 cfg_3s_data。
* 输出:外层 RocketMQ JSON 字符串key 为 guidmessageBody 是内部 JSON 字符串。
*/
std::string pqBuildTracePayload(const PqToolConfig& cfg,
const PqLedgerDevice& device,
const PqLedgerMonitor& monitor,
const std::string& frontType);
/*
* 发送一次数据追踪请求。
*
* 执行步骤:
* 1. 构造 payload 并在 trace_context.json 中记录本次追踪上下文;
* 2. 写 sent_trace_request.json 和 last_trace_request.json便于复盘
* 3. RocketMQ 可用时先同步发送到 topicLOG/tagLOG/keyLOG
* 4. cfg_3s_data 再发送实时数据请求到 topicAsk/tagAsk/keyAsk
* 5. RocketMQ 不可用时追加到对应 Topic 的 mq_outbox_*.jsonl 作为兜底。
*/
bool pqSendTraceRequest(const PqToolConfig& cfg,
const PqLedgerDevice& device,
const PqLedgerMonitor& monitor,
const std::string& frontType,
PqLogFn log,
PqMqEventFn event = {});
// 清空某个追踪目录下的 txt/json/jsonl/log 文本记录,保留目录本身。
void pqClearTraceRecords(const PqToolConfig& cfg,
const PqLedgerDevice& device,
const PqLedgerMonitor& monitor);
// 按 traceRootDir/process_x/device_x/monitor_x 生成稳定追踪目录。
std::string pqTraceDirectoryFor(const PqToolConfig& cfg,
const PqLedgerDevice& device,
const PqLedgerMonitor& monitor);
// 为装置寻找 XML优先 device.xmlPath其次 icd_response.json 下载缓存,最后 cfg.xmlPath。
std::string pqFindDeviceXmlPath(const PqToolConfig& cfg, const PqLedgerDevice& device);
/*
* 解析 XML + 单个 jsondata + 多个 origin生成 Excel XML 工作簿。
* 这是旧调用形式,内部会转成 vector 版本。
*/
bool pqRunParser(const std::string& xmlPath,
const std::string& jsonPath,
const std::vector<std::string>& originPaths,
const std::string& outPath,
std::string* err,
const std::string& frontType = "");
/*
* 解析 XML + 多个 jsondata + 多个 origin生成 Excel XML 工作簿。
*
* 输入:
* xmlPath ICD/XML 映射模板;
* jsonPaths DATATopic 统计/实时数据,可按 DATA_TYPE 分桶;
* originPaths TraceTopic 原始 MMS 数据,用于值对比和未匹配列表;
* frontType 为空表示生成所有工作表cfg_stat_data/cfg_3s_data 表示只生成对应流程。
* 输出:
* outPath Excel 2003 XML 格式的 .xls
* err 失败时写入异常信息;
* return true 成功false 失败。
*/
bool pqRunParser(const std::string& xmlPath,
const std::vector<std::string>& jsonPaths,
const std::vector<std::string>& originPaths,
const std::string& outPath,
std::string* err,
const std::string& frontType = "");
/*
* 把外部 ICD 中 LN/DOI 的 desc 导入到工作簿“未匹配的原始数据”页。
* 通过原始路径前缀匹配 DO 路径,帮助解释没有在当前映射中匹配到的 origin 数据。
*/
bool pqApplyIcdDescriptionsToWorkbook(const std::string& icdPath,
const std::string& workbookPath,
std::string* message = nullptr);
// GUI 入口。main.cpp 无命令行参数时调用,内部创建 Win32 主窗口和消息循环。
int runPqGuiApp();