#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 #include #include #include /* * 工具运行配置。 * * 输入来源: * 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 jsonDataPaths; std::vector 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> 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> fields; std::vector monitors; }; // 日志回调:业务函数只发字符串,GUI/命令行决定显示到日志框还是控制台。 using PqLogFn = std::function; /* * 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; // 默认配置。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& 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 为 guid,messageBody 是内部 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& 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& jsonPaths, const std::vector& 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();