Files
datatrace/source/pq_app.h
2026-07-14 17:02:23 +08:00

301 lines
13 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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";
std::string terminalStatus = "[0]";
std::string monitorStatus = "[1,2]";
std::string icdFlag = "0";
std::string webDevice = "http://192.168.1.68:10202/nodeDevice/nodeDeviceList";
std::string webIcd = "http://192.168.1.68:10202/icd/icdPathList";
std::string webFiledownload = "http://192.168.1.68:10207/file/download";
std::string xmlSaveDir = "runtime\\downloaded_xml";
std::string producer = "TRACE_PRODUCER";
std::string ipport = "192.168.1.68:9876";
std::string producerAccessKey = "rmqroot";
std::string producerSecretKey = "001@#njcnmq";
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";
std::string consumerAccessKey = "rmqroot";
std::string consumerSecretKey = "001@#njcnmq";
std::string dataStatTopic = "MQ_INST_1697676490574298_Bm3DLamM%LN_Topic";
std::string dataRealtimeTopic = "MQ_INST_1697676490574298_Bm3DLamM%Real_Time_Data_Topic";
std::string dataTopic = "MQ_INST_1697676490574298_Bm3DLamM%LN_Topic";
std::string dataStatTag = "6c2adb324940e19e208d717f4970f2ee";
std::string dataStatKey = "Test_Keys";
std::string dataRealtimeTag = "6c2adb324940e19e208d717f4970f2ee";
std::string dataRealtimeKey = "Test_Keys";
std::string dataTag = "6c2adb324940e19e208d717f4970f2ee";
std::string dataKey = "Test_Keys";
std::string traceTopic = "Trace_Topic";
std::string traceTag = "6c2adb324940e19e208d717f4970f2ee";
std::string traceKey = "Test_Key";
std::string xmlPath;
std::string jsonDataPath = "runtime\\json\\jsondata.txt";
std::vector<std::string> jsonDataPaths;
std::vector<std::string> originPaths;
std::string outputPath = "runtime\\final_sorted.xls";
std::string traceRootDir = "runtime\\trace_data";
};
/*
* 台账中的单个测点。
*
* 输入pqFetchLedgerAndIcd() 从 WebDevice 接口的 monitorData 数组解析。
* 输出GUI 左侧树显示;数据追踪时 monitor.id 会写入 MQ 追踪消息。
*/
struct PqLedgerMonitor {
std::string id;
std::string name;
std::string lineNo;
std::string voltageLevel;
std::string connectType;
std::string status;
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;
std::string ip;
std::string status;
std::string devType;
std::string devKey;
std::string orgName;
std::string stationName;
std::string subName;
std::string manufacturer;
std::string port;
std::string series;
std::string updateTime;
std::string processNo;
std::string maxProcessNum;
std::string xmlPath;
std::vector<std::pair<std::string, std::string>> fields;
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;
std::string body;
std::string path;
std::string monitorId;
std::string traceDir;
};
using PqMqEventFn = std::function<void(const PqMqEvent&)>;
/**
* @brief 创建一份完整的默认运行配置。
* @details mq.cpp 会把相对路径修正到 exe 所在目录旁的 runtime 目录GUI 随后再用
* 编辑框内容覆盖相应字段。返回值是独立副本,调用方可以安全修改。
* @return 包含 HTTP、RocketMQ、Topic/Tag/Key 和落盘路径默认值的 PqToolConfig。
*/
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 = {});
/**
* @brief 清空指定装置/测点本次追踪可消费的历史文本记录。
* @details 路径由 pqTraceDirectoryFor() 统一计算;只移除 txt、json、jsonl、log 等
* 追踪记录并保留目录,以保证点击“数据追踪”后只读取新到达的数据。
* @param cfg 包含 traceRootDir 的运行配置。
* @param device 当前台账装置,参与进程和装置目录名计算。
* @param monitor 当前测点,参与最终目录名计算。
*/
void pqClearTraceRecords(const PqToolConfig& cfg,
const PqLedgerDevice& device,
const PqLedgerMonitor& monitor);
/**
* @brief 计算某个装置和测点的稳定追踪目录。
* @details 目录布局为 traceRootDir/process_x/device_x/monitor_x实现会清理不适合
* Windows 文件名的字符,所有发送、消费、自动解析流程都应调用本函数,避免
* 各模块自行拼路径后指向不同目录。
* @return UTF-8 编码的目录字符串;本函数只计算路径,不创建目录。
*/
std::string pqTraceDirectoryFor(const PqToolConfig& cfg,
const PqLedgerDevice& device,
const PqLedgerMonitor& monitor);
/**
* @brief 为当前装置选择最合适的 ICD/XML 映射模板。
* @details 查找顺序为 device.xmlPath、icd_response.json 中的下载缓存、cfg.xmlPath
* 只有实际存在的普通文件才会返回,防止解析阶段拿到失效路径。
* @param cfg 提供 XML 缓存目录和用户配置的兜底路径。
* @param device 提供装置自身的 xmlPath、devType 等识别信息。
* @return 找到时返回 UTF-8 路径,未找到时返回空字符串。
*/
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);
/**
* @brief 启动 Win32 图形界面并运行消息循环。
* @details main.cpp 在无命令行参数时调用;函数注册窗口类、创建 AppState 和各页面,
* 然后通过 GetMessage/DispatchMessage 把用户操作交给 interface.cpp::wndProc。
* @return 正常退出时返回消息循环的退出码,初始化失败时返回非零值。
*/
int runPqGuiApp();