diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..7f561a7 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,65 @@ +{ + "C_Cpp_Runner.cCompilerPath": "gcc", + "C_Cpp_Runner.cppCompilerPath": "g++", + "C_Cpp_Runner.debuggerPath": "gdb", + "C_Cpp_Runner.cStandard": "", + "C_Cpp_Runner.cppStandard": "", + "C_Cpp_Runner.msvcBatchPath": "C:/Program Files/Microsoft Visual Studio/VR_NR/Community/VC/Auxiliary/Build/vcvarsall.bat", + "C_Cpp_Runner.useMsvc": false, + "C_Cpp_Runner.warnings": [ + "-Wall", + "-Wextra", + "-Wpedantic", + "-Wshadow", + "-Wformat=2", + "-Wcast-align", + "-Wconversion", + "-Wsign-conversion", + "-Wnull-dereference" + ], + "C_Cpp_Runner.msvcWarnings": [ + "/W4", + "/permissive-", + "/w14242", + "/w14287", + "/w14296", + "/w14311", + "/w14826", + "/w44062", + "/w44242", + "/w14905", + "/w14906", + "/w14263", + "/w44265", + "/w14928" + ], + "C_Cpp_Runner.enableWarnings": true, + "C_Cpp_Runner.warningsAsError": false, + "C_Cpp_Runner.compilerArgs": [], + "C_Cpp_Runner.linkerArgs": [], + "C_Cpp_Runner.includePaths": [], + "C_Cpp_Runner.includeSearch": [ + "*", + "**/*" + ], + "C_Cpp_Runner.excludeSearch": [ + "**/build", + "**/build/**", + "**/.*", + "**/.*/**", + "**/.vscode", + "**/.vscode/**" + ], + "C_Cpp_Runner.useAddressSanitizer": false, + "C_Cpp_Runner.useUndefinedSanitizer": false, + "C_Cpp_Runner.useLeakSanitizer": false, + "C_Cpp_Runner.showCompilationTime": false, + "C_Cpp_Runner.useLinkTimeOptimization": false, + "C_Cpp_Runner.msvcSecureNoWarnings": false, + "files.associations": { + "new": "cpp", + "mutex": "cpp", + "ostream": "cpp", + "thread": "cpp" + } +} \ No newline at end of file diff --git a/LFtid1056/build.sh b/LFtid1056/build.sh new file mode 100644 index 0000000..e7341c7 --- /dev/null +++ b/LFtid1056/build.sh @@ -0,0 +1,70 @@ +#!/bin/bash + +TARGET="cloud-front-test" +SRC_DIR="./cloudfront/code" +LIB_DIR="./cloudfront/lib" +OUT_DIR="./bin" + +# 创建输出目录 +mkdir -p "$OUT_DIR" + +# 源文件,所有主 cpp 文件 +SRC="$SRC_DIR/main.cpp \ +$SRC_DIR/worker.cpp \ +$SRC_DIR/log4.cpp \ +$SRC_DIR/cfg_parser.cpp \ +$SRC_DIR/interface.cpp \ +$SRC_DIR/rocketmq.cpp \ +$SRC_DIR/tinyxml2.cpp \ +./client2.cpp \ +./dealMsg.cpp \ +./main_thread.cpp \ +./PQSMsg.cpp " + +INCLUDE_DIRS="-I$SRC_DIR \ +-I$SRC_DIR/nlohmann \ +-I$SRC_DIR/curl \ +-I$SRC_DIR/log4cplus \ +-I$SRC_DIR/rocketmq \ +-I$SRC_DIR \ +-I./lib/libuv-v1.51.0/include \ +-I. " + +LIB_DIRS="-L$LIB_DIR -L/usr/lib64 -L/usr/local/lib" + +LIBS="./cloudfront/lib/libcurl.so \ +./cloudfront/lib/libssl.so \ +./cloudfront/lib/libcrypto.so \ +./cloudfront/lib/liblog4cplus.so \ +-lpthread -ldl -lrt \ +-lstdc++fs \ +-lz \ +./libuv.a \ +-pthread" + +# 如果有静态 rocketmq 库就加上 +if [ -f "$LIB_DIR/librocketmq.a" ]; then + LIBS="$LIB_DIR/librocketmq.a $LIBS" +fi + +# 判断是否为 debug 版本 +if [[ "$1" == "debug" ]]; then + CXXFLAGS="-std=c++11 -g -O0" + TARGET="${TARGET}d" + echo "🟢 编译调试版本 (-g -O0)" +else + CXXFLAGS="-std=c++11 -O2 -static-libstdc++ -static-libgcc" + echo "🔵 编译正式版本 (-O2 -static)" +fi + +# 编译命令 +g++ $CXXFLAGS $SRC $INCLUDE_DIRS $LIB_DIRS $LIBS -Wl,-rpath='$ORIGIN/../cloudfront/lib' -o "$OUT_DIR/$TARGET" + +# 编译结果检查 +if [ $? -eq 0 ]; then + echo "✅ 编译成功,生成可执行文件: $OUT_DIR/$TARGET" + echo "🔍 依赖库检查:" + ldd "$OUT_DIR/$TARGET" || echo "是静态编译程序 ✔" +else + echo "❌ 编译失败" +fi \ No newline at end of file diff --git a/LFtid1056/cloudfront/code/cfg_parser.cpp b/LFtid1056/cloudfront/code/cfg_parser.cpp new file mode 100644 index 0000000..b32a813 --- /dev/null +++ b/LFtid1056/cloudfront/code/cfg_parser.cpp @@ -0,0 +1,2617 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include //流解析 +#include //写去重的设备类型 +#include //打开文件 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "nlohmann/json.hpp" +#include "curl/curl.h" +#include "log4.h" //关键上送日志 +#include "interface.h" //台账结构 +#include "tinyxml2.h" +#include "rocketmq.h" + +///////////////////////////////////////////////////////////////////////////////////////////////// + +using namespace std; + +///////////////////////////////////////////////////////////////////////////////////////////////// + +//进程标识 +extern std::string subdir; +extern int g_front_seg_index; +extern int g_front_seg_num; +extern unsigned int g_node_id; //前置程序类型(100-600) + +//初始化完成标识 +extern int INITFLAG; + +//线程阻塞计数 +extern uint32_t g_ontime_blocked_times; + +//台账锁 +extern std::mutex ledgermtx; + +//队列 +extern std::mutex queue_data_list_mutex; //queue发送数据锁 +extern std::list queue_data_list; //queue发送数据链表 + +extern int three_secs_enabled; + +extern std::vector terminal_devlist; + +extern std::map xmlinfo_list;//保存所有型号对应的icd映射文件解析数据 +extern XmlConfig xmlcfg;//星形接线xml节点解析的数据-默认映射文件解析数据 +extern std::list topicList; //队列发送主题链表 + +extern XmlConfig xmlcfg2;//角型接线xml节点解析的数据-默认映射文件解析数据 +extern std::list topicList2; //角型接线发送主题链表 +extern std::map xmlinfo_list2;//保存所有型号角形接线对应的icd映射文件解析数据 + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +//补招 +std::list g_StatisticLackList; //日志补招结构类链表 +std::mutex g_StatisticLackList_list_mutex; //补招队列数据锁 + +std::string DEFAULT_CONFIG_FN = "Default_Config.xml"; //默认映射文件 +std::string LEDGER_UPDATE_FN = "LedgerUpdate.log"; //台账更新本地记录日志 + +//一个终端最大检测点数 +const int MAX_CPUNO = 10; + +//角型接线标志,0不存在角形接线,1存在角形接线 +int isdelta_flag = 0; + +//多前置flag:1为开启,0为关闭 +int MULTIPLE_NODE_FLAG = 1; + +//终端台账数量配置 +int IED_COUNT = 300; //默认300 + +//前置标志 +std::string FRONT_INST = ""; +std::string FRONT_IP = ""; + +//终端和监测点的状态筛选 +std::string TERMINAL_STATUS = ""; +std::string MONITOR_STATUS = ""; +std::string ICD_FLAG = ""; + +//web接口 +std::string WEB_DEVICE = ""; +std::string WEB_ICD = ""; +std::string WEB_EVENT = ""; +std::string WEB_FILEUPLOAD = ""; +std::string WEB_FILEDOWNLOAD = ""; + +////////////////////////////////////////////////////////////////////////////mq配置 + +//备用 +std::string BROKER_LIST = ""; + +//通用主题 +std::string TOPIC_STAT = ""; +std::string TOPIC_PST = ""; +std::string TOPIC_PLT = ""; +std::string TOPIC_EVENT = ""; +std::string TOPIC_ALARM = ""; +std::string TOPIC_SNG = ""; +std::string TOPIC_RTDATA = ""; + +//通用tagkey +std::string G_ROCKETMQ_TAG = "";//tag +std::string G_ROCKETMQ_KEY = "";//key + +//生产者 +std::string G_ROCKETMQ_PRODUCER = ""; //rocketmq producer +std::string G_MQPRODUCER_IPPORT = ""; //rocketmq ip+port +std::string G_MQPRODUCER_ACCESSKEY = ""; //rocketmq 认证 +std::string G_MQPRODUCER_SECRETKEY = ""; //rocketmq 秘钥 + +//日志 +std::string G_LOG_TOPIC = "";//topie +std::string G_LOG_TAG = "";//tag +std::string G_LOG_KEY = "";//key +//终端连接 +std::string G_CONNECT_TOPIC = "";//consumer topie +std::string G_CONNECT_TAG = "";//consumer tag +std::string G_CONNECT_KEY = "";//consumer key +//心跳 +std::string Heart_Beat_Topic = ""; +std::string Heart_Beat_Tag = ""; +std::string Heart_Beat_Key = ""; +//消息响应 +std::string Topic_Reply_Topic = ""; +std::string Topic_Reply_Tag = ""; +std::string Topic_Reply_Key = ""; + +//消费者 +std::string G_ROCKETMQ_CONSUMER = "";//rocketmq consumer +std::string G_MQCONSUMER_IPPORT = "";//consumer ip+port +std::string G_MQCONSUMER_ACCESSKEY = ""; +std::string G_MQCONSUMER_SECRETKEY = ""; +std::string G_MQCONSUMER_CHANNEL = ""; +//实时数据请求 +std::string G_MQCONSUMER_TOPIC_RT = "";//consumer topie +std::string G_MQCONSUMER_TAG_RT = "";//consumer tag +std::string G_MQCONSUMER_KEY_RT = "";//consumer key +//台账更新请求 +std::string G_MQCONSUMER_TOPIC_UD = "";//consumer topie +std::string G_MQCONSUMER_TAG_UD = "";//consumer tag +std::string G_MQCONSUMER_KEY_UD = "";//consumer key +//补招数据请求 +std::string G_MQCONSUMER_TOPIC_RC = "";//consumer topie +std::string G_MQCONSUMER_TAG_RC = "";//consumer tag +std::string G_MQCONSUMER_KEY_RC = "";//consumer key +//进程控制请求 +std::string G_MQCONSUMER_TOPIC_SET = "";//consumer topie +std::string G_MQCONSUMER_TAG_SET = "";//consumer tag +std::string G_MQCONSUMER_KEY_SET = "";//consumer key +//日志数据请求 +std::string G_MQCONSUMER_TOPIC_LOG = "";//consumer topie +std::string G_MQCONSUMER_TAG_LOG = "";//consumer tag +std::string G_MQCONSUMER_KEY_LOG = "";//consumer key + +//测试用的主题 +std::string G_ROCKETMQ_TOPIC_TEST = "";//topie +std::string G_ROCKETMQ_TAG_TEST = "";//tag +std::string G_ROCKETMQ_KEY_TEST = "";//key + +//测试相关配置 +int G_TEST_FLAG = 0; +int G_TEST_NUM = 0; +int G_TEST_TYPE = 0; +int TEST_PORT = 11000; //用于当前进程登录测试shell的端口 +std::string G_TEST_LIST = ""; //测试用的发送实际数据的终端列表 +std::vector TESTARRAY; //解析的列表数组 + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////其他文件定义的函数引用声明 + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////当前文件函数声明 + +void create_ledger_log(trigger_update_xml_t* ledger_update_xml); +void print_trigger_update_xml(const trigger_update_xml_t& trigger_update); + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////辅助函数 + +//去除字符串前后空格 +static void trim(std::string& s) { + auto not_space = [](int ch) { return !std::isspace(ch); }; + s.erase(s.begin(), std::find_if(s.begin(), s.end(), not_space)); + s.erase(std::find_if(s.rbegin(), s.rend(), not_space).base(), s.end()); +} + +//判断空格 +bool is_blank(const std::string& str) +{ + for (std::string::const_iterator it = str.begin(); it != str.end(); ++it) + { + if (!std::isspace(*it)) { + return false; + } + } + return true; +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////配置文件读取 + +//将 G_TEST_LIST 按逗号分割,填充 TESTARRAY +static void parseTestList() { + TESTARRAY.clear(); + std::istringstream iss(G_TEST_LIST); + std::string token; + while (std::getline(iss, token, ',')) { + trim(token); + if (!token.empty()) { + TESTARRAY.push_back(token); + } + } +} + +//简易映射方式解析配置文件文件 +void loadConfig(const std::string& filename) { + // 1. 构造“节.键 → 变量地址”映射表 + std::unordered_map strMap; + std::unordered_map intMap; + + // [Flag] + strMap["Flag.FrontInst"] = &FRONT_INST; + strMap["Flag.FrontIP"] = &FRONT_IP; + + // [Ledger] + intMap["Ledger.IedCount"] = &IED_COUNT; + strMap["Ledger.TerminalStatus"]= &TERMINAL_STATUS; + strMap["Ledger.MonitorStatus"] = &MONITOR_STATUS; + strMap["Ledger.IcdFlag"] = &ICD_FLAG; + + // [Http] + strMap["Http.WebDevice"] = &WEB_DEVICE; + strMap["Http.WebIcd"] = &WEB_ICD; + strMap["Http.WebEvent"] = &WEB_EVENT; + strMap["Http.WebFileupload"] = &WEB_FILEUPLOAD; + strMap["Http.WebFiledownload"]= &WEB_FILEDOWNLOAD; + + // [Queue] + strMap["Queue.BrokerList"] = &BROKER_LIST; + strMap["Queue.RTDataTopic"] = &TOPIC_RTDATA; + strMap["Queue.HisTopic"] = &TOPIC_STAT; + strMap["Queue.PSTTopic"] = &TOPIC_PST; + strMap["Queue.PLTTopic"] = &TOPIC_PLT; + strMap["Queue.EventTopic"] = &TOPIC_EVENT; + strMap["Queue.AlmTopic"] = &TOPIC_ALARM; + strMap["Queue.SngTopic"] = &TOPIC_SNG; + strMap["Queue.QUEUE_TAG"] = &G_ROCKETMQ_TAG; + strMap["Queue.QUEUE_KEY"] = &G_ROCKETMQ_KEY; + + // [RocketMq] —— 生产者 + strMap["RocketMq.producer"] = &G_ROCKETMQ_PRODUCER; + strMap["RocketMq.Ipport"] = &G_MQPRODUCER_IPPORT; + strMap["RocketMq.AccessKey"] = &G_MQPRODUCER_ACCESSKEY; + strMap["RocketMq.SecretKey"] = &G_MQPRODUCER_SECRETKEY; + + strMap["RocketMq.LOGTopic"] = &G_LOG_TOPIC; + strMap["RocketMq.LOGTag"] = &G_LOG_TAG; + strMap["RocketMq.LOGKey"] = &G_LOG_KEY; + + strMap["RocketMq.CONNECTTopic"] = &G_CONNECT_TOPIC; + strMap["RocketMq.CONNECTTag"] = &G_CONNECT_TAG; + strMap["RocketMq.CONNECTKey"] = &G_CONNECT_KEY; + + strMap["RocketMq.Heart_Beat_Topic"] = &Heart_Beat_Topic; + strMap["RocketMq.Heart_Beat_Tag"] = &Heart_Beat_Tag; + strMap["RocketMq.Heart_Beat_Key"] = &Heart_Beat_Key; + + strMap["RocketMq.Topic_Reply_Topic"] = &Topic_Reply_Topic; + strMap["RocketMq.Topic_Reply_Tag"] = &Topic_Reply_Tag; + strMap["RocketMq.Topic_Reply_Key"] = &Topic_Reply_Key; + + // [RocketMq] —— 消费者 + strMap["RocketMq.consumer"] = &G_ROCKETMQ_CONSUMER; + strMap["RocketMq.ConsumerIpport"] = &G_MQCONSUMER_IPPORT; + strMap["RocketMq.ConsumerAccessKey"] = &G_MQCONSUMER_ACCESSKEY; + strMap["RocketMq.ConsumerSecretKey"] = &G_MQCONSUMER_SECRETKEY; + strMap["RocketMq.ConsumerChannel"] = &G_MQCONSUMER_CHANNEL; + + strMap["RocketMq.ConsumerTopicRT"] = &G_MQCONSUMER_TOPIC_RT; + strMap["RocketMq.ConsumerTagRT"] = &G_MQCONSUMER_TAG_RT; + strMap["RocketMq.ConsumerKeyRT"] = &G_MQCONSUMER_KEY_RT; + + strMap["RocketMq.ConsumerTopicUD"] = &G_MQCONSUMER_TOPIC_UD; + strMap["RocketMq.ConsumerTagUD"] = &G_MQCONSUMER_TAG_UD; + strMap["RocketMq.ConsumerKeyUD"] = &G_MQCONSUMER_KEY_UD; + + strMap["RocketMq.ConsumerTopicRC"] = &G_MQCONSUMER_TOPIC_RC; + strMap["RocketMq.ConsumerTagRC"] = &G_MQCONSUMER_TAG_RC; + strMap["RocketMq.ConsumerKeyRC"] = &G_MQCONSUMER_KEY_RC; + + strMap["RocketMq.ConsumerTopicSET"] = &G_MQCONSUMER_TOPIC_SET; + strMap["RocketMq.ConsumerTagSET"] = &G_MQCONSUMER_TAG_SET; + strMap["RocketMq.ConsumerKeySET"] = &G_MQCONSUMER_KEY_SET; + + strMap["RocketMq.ConsumerTopicLOG"] = &G_MQCONSUMER_TOPIC_LOG; + strMap["RocketMq.ConsumerTagLOG"] = &G_MQCONSUMER_TAG_LOG; + strMap["RocketMq.ConsumerKeyLOG"] = &G_MQCONSUMER_KEY_LOG; + + strMap["RocketMq.Topic_Test"] = &G_ROCKETMQ_TOPIC_TEST; + strMap["RocketMq.Tag_Test"] = &G_ROCKETMQ_TAG_TEST; + strMap["RocketMq.Key_Test"] = &G_ROCKETMQ_KEY_TEST; + + intMap["RocketMq.Testflag"] = &G_TEST_FLAG; + intMap["RocketMq.Testnum"] = &G_TEST_NUM; + intMap["RocketMq.Testtype"] = &G_TEST_TYPE; + intMap["RocketMq.TestPort"] = &TEST_PORT; + strMap["RocketMq.TestList"] = &G_TEST_LIST; + + // 2. 打开并逐行解析 INI 文件 + std::ifstream fin(filename); + if (!fin.is_open()) { + std::cerr << "无法打开配置文件: " << filename << "\n"; + return; + } + + std::string line; + std::string currentSection; + while (std::getline(fin, line)) { + trim(line); + if (line.empty() || line[0] == ';' || line[0] == '#') { + continue; // 跳过空白或注释 + } + if (line.front() == '[' && line.back() == ']') { + currentSection = line.substr(1, line.size() - 2); + trim(currentSection); + continue; + } + auto pos = line.find('='); + if (pos == std::string::npos) { + continue; + } + std::string key = line.substr(0, pos); + std::string value = line.substr(pos + 1); + trim(key); + trim(value); + + // 去掉值两端双引号 + if (value.size() >= 2 && value.front() == '"' && value.back() == '"') { + value = value.substr(1, value.size() - 2); + } + + // 拼出 "节.键" + std::string fullKey = currentSection + "." + key; + + // 如果在字符串映射表里,就写入对应的全局 std::string + auto sit = strMap.find(fullKey); + if (sit != strMap.end()) { + *(sit->second) = value; + continue; + } + // 如果在整型映射表里,就 stoi 后写入对应的全局 int + auto iit = intMap.find(fullKey); + if (iit != intMap.end()) { + try { + *(iit->second) = std::stoi(value); + } catch (...) { + *(iit->second) = 0; + } + } + } + fin.close(); + + // 3. 将 G_TEST_LIST 拆分到 TESTARRAY + parseTestList(); +} + +//打印所有全局变量,名称对齐 +void printConfig() { + const int nameWidth = 30; // 变量名区域宽度 + std::cout << "------- Loaded Configuration -------\n"; + + // 辅助 lambda 方便打印 + auto printStr = [&](const std::string& name, const std::string& val) { + std::cout << std::left << std::setw(nameWidth) << name + << " = " << val << "\n"; + }; + auto printInt = [&](const std::string& name, int val) { + std::cout << std::left << std::setw(nameWidth) << name + << " = " << val << "\n"; + }; + + std::cout << "\n// 前置区分 —— 通用\n"; + printInt("IED_COUNT", IED_COUNT); + printStr("FRONT_INST", FRONT_INST); + printStr("FRONT_IP", FRONT_IP); + + std::cout << "\n// 消息队列 —— 通用\n"; + printStr("BROKER_LIST", BROKER_LIST); + printStr("TOPIC_STAT", TOPIC_STAT); + printStr("TOPIC_PST", TOPIC_PST); + printStr("TOPIC_PLT", TOPIC_PLT); + printStr("TOPIC_EVENT", TOPIC_EVENT); + printStr("TOPIC_ALARM", TOPIC_ALARM); + printStr("TOPIC_SNG", TOPIC_SNG); + printStr("TOPIC_RTDATA", TOPIC_RTDATA); + + std::cout << "\n// MQ —— 生产者\n"; + printStr("G_ROCKETMQ_PRODUCER", G_ROCKETMQ_PRODUCER); + printStr("G_MQPRODUCER_IPPORT", G_MQPRODUCER_IPPORT); + printStr("G_MQPRODUCER_ACCESSKEY", G_MQPRODUCER_ACCESSKEY); + printStr("G_MQPRODUCER_SECRETKEY", G_MQPRODUCER_SECRETKEY); + + printStr("G_LOG_TOPIC", G_LOG_TOPIC); + printStr("G_LOG_TAG", G_LOG_TAG); + printStr("G_LOG_KEY", G_LOG_KEY); + + printStr("G_CONNECT_TOPIC", G_CONNECT_TOPIC); + printStr("G_CONNECT_TAG", G_CONNECT_TAG); + printStr("G_CONNECT_KEY", G_CONNECT_KEY); + + std::cout << "\n// MQ —— 心跳 & 响应\n"; + printStr("Heart_Beat_Topic", Heart_Beat_Topic); + printStr("Heart_Beat_Tag", Heart_Beat_Tag); + printStr("Heart_Beat_Key", Heart_Beat_Key); + + printStr("Topic_Reply_Topic", Topic_Reply_Topic); + printStr("Topic_Reply_Tag", Topic_Reply_Tag); + printStr("Topic_Reply_Key", Topic_Reply_Key); + + std::cout << "\n// MQ —— 消费者\n"; + printStr("G_ROCKETMQ_CONSUMER", G_ROCKETMQ_CONSUMER); + printStr("G_MQCONSUMER_IPPORT", G_MQCONSUMER_IPPORT); + printStr("G_MQCONSUMER_ACCESSKEY", G_MQCONSUMER_ACCESSKEY); + printStr("G_MQCONSUMER_SECRETKEY", G_MQCONSUMER_SECRETKEY); + printStr("G_MQCONSUMER_CHANNEL", G_MQCONSUMER_CHANNEL); + + std::cout << "\n// MQ —— 主题细分类\n"; + printStr("G_MQCONSUMER_TOPIC_RT", G_MQCONSUMER_TOPIC_RT); + printStr("G_MQCONSUMER_TAG_RT", G_MQCONSUMER_TAG_RT); + printStr("G_MQCONSUMER_KEY_RT", G_MQCONSUMER_KEY_RT); + + printStr("G_MQCONSUMER_TOPIC_UD", G_MQCONSUMER_TOPIC_UD); + printStr("G_MQCONSUMER_TAG_UD", G_MQCONSUMER_TAG_UD); + printStr("G_MQCONSUMER_KEY_UD", G_MQCONSUMER_KEY_UD); + + printStr("G_MQCONSUMER_TOPIC_RC", G_MQCONSUMER_TOPIC_RC); + printStr("G_MQCONSUMER_TAG_RC", G_MQCONSUMER_TAG_RC); + printStr("G_MQCONSUMER_KEY_RC", G_MQCONSUMER_KEY_RC); + + printStr("G_MQCONSUMER_TOPIC_SET", G_MQCONSUMER_TOPIC_SET); + printStr("G_MQCONSUMER_TAG_SET", G_MQCONSUMER_TAG_SET); + printStr("G_MQCONSUMER_KEY_SET", G_MQCONSUMER_KEY_SET); + + printStr("G_MQCONSUMER_TOPIC_LOG", G_MQCONSUMER_TOPIC_LOG); + printStr("G_MQCONSUMER_TAG_LOG", G_MQCONSUMER_TAG_LOG); + printStr("G_MQCONSUMER_KEY_LOG", G_MQCONSUMER_KEY_LOG); + + std::cout << "\n// MQ —— 测试用主题 & 参数\n"; + printStr("G_ROCKETMQ_TOPIC_TEST", G_ROCKETMQ_TOPIC_TEST); + printStr("G_ROCKETMQ_TAG_TEST", G_ROCKETMQ_TAG_TEST); + printStr("G_ROCKETMQ_KEY_TEST", G_ROCKETMQ_KEY_TEST); + + printInt("G_TEST_FLAG", G_TEST_FLAG); + printInt("G_TEST_NUM", G_TEST_NUM); + printInt("G_TEST_TYPE", G_TEST_TYPE); + printInt("TEST_PORT", TEST_PORT); + printStr("G_TEST_LIST", G_TEST_LIST); + + // 打印解析后的 TESTARRAY + std::cout << std::left << std::setw(nameWidth) << "TESTARRAY" << " = ["; + for (size_t i = 0; i < TESTARRAY.size(); ++i) { + std::cout << TESTARRAY[i]; + if (i + 1 < TESTARRAY.size()) { + std::cout << ", "; + } + } + std::cout << "]\n"; + + std::cout << "\n// 终端 & 监测点状态筛选\n"; + printStr("TERMINAL_STATUS", TERMINAL_STATUS); + printStr("MONITOR_STATUS", MONITOR_STATUS); + printStr("ICD_FLAG", ICD_FLAG); + + std::cout << "\n// Web 接口\n"; + printStr("WEB_DEVICE", WEB_DEVICE); + printStr("WEB_ICD", WEB_ICD); + printStr("WEB_EVENT", WEB_EVENT); + printStr("WEB_FILEUPLOAD", WEB_FILEUPLOAD); + printStr("WEB_FILEDOWNLOAD", WEB_FILEDOWNLOAD); + + std::cout << "-------------------------------------\n"; +} + +//初始化配置 +void init_config() { + + loadConfig(FRONT_PATH + "/config/front.cfg"); + printConfig(); + + //多前置处理 + if (g_front_seg_index > 0 && g_front_seg_num > 0) { + MULTIPLE_NODE_FLAG = 1; + std::cout << "this is multiple process of index:" << g_front_seg_index << std::endl; + + if(g_front_seg_index > g_front_seg_num){ + DIY_ERRORLOG("process","【ERROR】前置当前进程的进程号为:%d,前置的多进程最大进程号为:%d,当前进程的进程号应该为1到最大进程号范围内的整数,退出该进程",g_front_seg_index,g_front_seg_num); + exit(-1039); + } + + } + else if(g_front_seg_num == 0 && g_front_seg_index == 0){ + MULTIPLE_NODE_FLAG = 0; + std::cout << "this is single process" << std::endl; + } + else{ + DIY_ERRORLOG("process","【ERROR】前置当前进程的进程号为:%d,前置的多进程最大进程号为:%d,应该为大于0的整数,退出该进程",g_front_seg_index,g_front_seg_num); + exit(-1039); + } + + //测试进程端口 + if (g_node_id == STAT_DATA_BASE_NODE_ID)//统计采集 + TEST_PORT = TEST_PORT + STAT_DATA_BASE_NODE_ID + g_front_seg_index; + else if (g_node_id == RECALL_HIS_DATA_BASE_NODE_ID) {//补召 + TEST_PORT = TEST_PORT + RECALL_HIS_DATA_BASE_NODE_ID + g_front_seg_index; + } + else if (g_node_id == THREE_SECS_DATA_BASE_NODE_ID) {//3秒采集 + TEST_PORT = TEST_PORT + THREE_SECS_DATA_BASE_NODE_ID + g_front_seg_index; + } + else if (g_node_id == SOE_COMTRADE_BASE_NODE_ID) {//暂态录波 + TEST_PORT = TEST_PORT + SOE_COMTRADE_BASE_NODE_ID + g_front_seg_index; + } + +} + +////////////////////////////////////////////////////////////////////////////////////////////获取当前时间 + +// 用于获取当前时间,单位毫秒 +double sGetMsTime() { + auto now = std::chrono::system_clock::now(); + auto ms = std::chrono::duration_cast( + now.time_since_epoch() + ).count(); + return static_cast(ms); +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////实时触发部分 + +//获取实时触发文件 +std::string get_3s_trig_fn() { + const std::string dirPath = FRONT_PATH + "/etc/trigger3s"; + DIR* dp = opendir(dirPath.c_str()); + if (!dp) return ""; + + struct dirent* entry; + std::vector> xmlFiles; + + while ((entry = readdir(dp)) != nullptr) { + if (strstr(entry->d_name, ".xml")) { + std::string fullPath = dirPath + "/" + entry->d_name; + struct stat st; + if (stat(fullPath.c_str(), &st) == 0 && S_ISREG(st.st_mode)) { + xmlFiles.emplace_back(entry->d_name, st.st_mtime); + } + } + } + closedir(dp); + + if (xmlFiles.empty()) return ""; + + std::sort(xmlFiles.begin(), xmlFiles.end(), + [](const std::pair& a, const std::pair& b) { + return a.second > b.second; // 最近时间在前 + }); + + return xmlFiles.front().first; +} + +// 打印 trigger_t 结构体的函数 +void print_trigger(const trigger_t& trigger) { + std::cout << " dev_idx: " << trigger.dev_idx + << ", line_id: " << trigger.line_id + << ", real_data: " << trigger.real_data + << ", soe_data: " << trigger.soe_data + << ", limit: " << trigger.limit + << ", count: " << trigger.count + << std::endl; +} + +// 打印 trigger_3s_xml_t 结构体的函数 +void print_trigger_3s_xml(const trigger_3s_xml_t& trigger_3s_xml) { + std::cout << "Work Trigger Count: " << trigger_3s_xml.work_trigger_num << std::endl; + for (int i = 0; i < trigger_3s_xml.work_trigger_num; ++i) { + std::cout << " Work Trigger [" << (i + 1) << "]:" << std::endl; + print_trigger(trigger_3s_xml.work_triggers[i]); + } + + std::cout << "New Trigger Count: " << trigger_3s_xml.new_trigger_num << std::endl; + for (int i = 0; i < trigger_3s_xml.new_trigger_num; ++i) { + std::cout << " New Trigger [" << (i + 1) << "]:" << std::endl; + print_trigger(trigger_3s_xml.new_triggers[i]); + } + + std::cout << "Delete Trigger Count: " << trigger_3s_xml.delete_trigger_num << std::endl; + for (int i = 0; i < trigger_3s_xml.delete_trigger_num; ++i) { + std::cout << " Delete Trigger [" << (i + 1) << "]:" << std::endl; + print_trigger(trigger_3s_xml.delete_triggers[i]); + } + + std::cout << "Modify Trigger Count: " << trigger_3s_xml.modify_trigger_num << std::endl; + for (int i = 0; i < trigger_3s_xml.modify_trigger_num; ++i) { + std::cout << " Modify Trigger [" << (i + 1) << "]:" << std::endl; + print_trigger(trigger_3s_xml.modify_triggers[i]); + } +} + +//处理触发标志 +int getValueFromElemAttrStr(std::string str) +{ + if (str == "true") + return 1; + else if (str == "false") + return 0; + else + return -1; +} + +//将文件内容读取到结构中 +void parse_3s_trigger(trigger_3s_xml_t* trigger_3s_xml, const std::string& parentTag, tinyxml2::XMLElement* trigger_e) +{ + if (!trigger_3s_xml || !trigger_e) return; + + trigger_t trigger; + + const char* attr = nullptr; + + // DevSeries + attr = trigger_e->Attribute("DevSeries"); + trigger.dev_idx = attr ? std::atoi(attr) : 0; + + // Line + attr = trigger_e->Attribute("Line"); + trigger.line_id = attr ? std::atoi(attr) : 0; + + // RealData + attr = trigger_e->Attribute("RealData"); + std::string realDataStr = attr ? attr : ""; + std::transform(realDataStr.begin(), realDataStr.end(), realDataStr.begin(), ::tolower); + trigger.real_data = getValueFromElemAttrStr(realDataStr); + + // SOEData + attr = trigger_e->Attribute("SOEData"); + std::string soeDataStr = attr ? attr : ""; + std::transform(soeDataStr.begin(), soeDataStr.end(), soeDataStr.begin(), ::tolower); + trigger.soe_data = getValueFromElemAttrStr(soeDataStr); + + // Limit + attr = trigger_e->Attribute("Limit"); + trigger.limit = attr ? std::atoi(attr) : 0; + + // Count + attr = trigger_e->Attribute("Count"); + trigger.count = attr ? std::atoi(attr) : 0; + + // 分类插入 + if (parentTag == "Work") { + trigger_3s_xml->work_triggers[trigger_3s_xml->work_trigger_num++] = trigger; + } else if (parentTag == "New") { + trigger_3s_xml->new_triggers[trigger_3s_xml->new_trigger_num++] = trigger; + } else if (parentTag == "Delete") { + trigger_3s_xml->delete_triggers[trigger_3s_xml->delete_trigger_num++] = trigger; + } else if (parentTag == "Modify") { + trigger_3s_xml->modify_triggers[trigger_3s_xml->modify_trigger_num++] = trigger; + } + + // 调试用 + print_trigger_3s_xml(*trigger_3s_xml); +} + +//实时触发文件处理 +int load_3s_data_from_xml(trigger_3s_xml_t* trigger_3s_xml, const std::string& xml_fn) { + if (!trigger_3s_xml) return 1; + + tinyxml2::XMLDocument doc; + tinyxml2::XMLError result = doc.LoadFile(xml_fn.c_str()); + if (result == tinyxml2::XML_ERROR_FILE_NOT_FOUND) { + return 1; + } else if (result != tinyxml2::XML_SUCCESS) { + return 1; + } + + tinyxml2::XMLElement* root = doc.RootElement(); + if (!root) return 1; + + for (tinyxml2::XMLElement* groupElem = root->FirstChildElement(); groupElem != nullptr; groupElem = groupElem->NextSiblingElement()) { + std::string strTag = groupElem->Name(); + if (strTag == "Work" || strTag == "New" || strTag == "Delete" || strTag == "Modify") { + for (tinyxml2::XMLElement* triggerElem = groupElem->FirstChildElement("Trigger"); triggerElem != nullptr; triggerElem = triggerElem->NextSiblingElement("Trigger")) { + parse_3s_trigger(trigger_3s_xml, strTag, triggerElem); + } + } + } + + return 0; +} + +//将内容写入协议 +void process_3s_config(trigger_3s_xml_t *trigger_3s_xml) +{ + //根据协议补充内容 + + //根据协议补充内容 +} + +//实时触发处理 +int parse_3s_xml(trigger_3s_xml_t* trigger_3s_xml) { + std::cout << "begin 3s xml..." << std::endl; + std::memset(trigger_3s_xml, 0, sizeof(trigger_3s_xml_t)); + + std::string THREE_SECS_WEBSERVICE_DIR = FRONT_PATH + "/etc/trigger3s/"; //实时数据读取目录 + std::string BAK_WEBSERVICE_3S_TRIG_COMMAND_XML_FN = THREE_SECS_WEBSERVICE_DIR + "bak_3s_trig_command.txt"; //实时触发文件备份文件名 + + std::string the_webservice_xml_fn = get_3s_trig_fn(); // 获取目录下最新 XML 文件名 + + if (the_webservice_xml_fn.length() > 4) { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); // 等待 0.1 秒 + + std::string full_path = std::string(THREE_SECS_WEBSERVICE_DIR) + the_webservice_xml_fn; + + if (load_3s_data_from_xml(trigger_3s_xml, full_path) != 0) { + std::cerr << "Failed to load 3s data from XML: " << full_path << std::endl; + return 1; + } + + std::remove(BAK_WEBSERVICE_3S_TRIG_COMMAND_XML_FN.c_str()); // 删除旧备份 + if (std::rename(full_path.c_str(), BAK_WEBSERVICE_3S_TRIG_COMMAND_XML_FN.c_str()) != 0) { + std::perror("Rename failed"); + } + + std::cout << "/etc/trigger3s/*.xml success..." << std::endl; + DIY_WARNLOG("process", "【WARN】前置读取实时数据触发文件成功,即将注册实时数据报告"); + + return 0; + } + + std::cout << "3s xml fail..." << std::endl; + return 1; +} + +void check_3s_config() { + double now; + static double last_check_3s_config_time = 0.0; // 初始化时间 + trigger_3s_xml_t trigger_3s_xml; // 3s触发文件 + + if (!three_secs_enabled) // 只有cfg_3s_data进程才会开启 + return; + + now = sGetMsTime(); // 当前时间 + if (std::fabs(now - last_check_3s_config_time) < 3 * 1000) // wait 3secs + return; // 当前进程任务执行时查看当前时间和上次执行时间间隔,小于3秒不执行,大于等于3秒往下执行 + + last_check_3s_config_time = now; // 记录本次运行时间 + while (0 == parse_3s_xml(&trigger_3s_xml)) { // 处理3秒文件,一次处理一个 + process_3s_config(&trigger_3s_xml); // 根据文件处理数据 + } +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////补招部分 + +//将文件内容读取到结构中 +void parse_recall(recall_xml_t* recall_xml, const std::string& parentTag, const std::map& attributes, const std::string& id) { + recall_t recall; + std::memset(&recall, 0, sizeof(recall_t)); + + // 设置监测点 ID + recall.line_id = id; + + // 解析时间字符串 + auto parse_time = [](const std::string& time_str) -> std::time_t { + std::tm tm = {}; + std::istringstream ss(time_str); + ss >> std::get_time(&tm, "%Y-%m-%d %H:%M:%S"); + if (ss.fail()) return 0; + return std::mktime(&tm); + }; + + recall.start_time = parse_time(attributes.at("StartTime")); + recall.end_time = parse_time(attributes.at("EndTime")); + recall.need_steady = std::stoi(attributes.at("STEADY")); + recall.need_voltage = std::stoi(attributes.at("VOLTAGE")); + + std::cout << parentTag << " -> " << recall.line_id + << " " << recall.need_steady + << " " << recall.need_voltage + << " " << recall.start_time + << " " << recall.end_time << std::endl; + + if (parentTag == "Work" && recall_xml->work_recall_num < MAX_RECALL_NUM) { + recall_xml->work_recalls[recall_xml->work_recall_num++] = recall; + } else if (parentTag == "New" && recall_xml->new_recall_num < MAX_RECALL_NUM) { + recall_xml->new_recalls[recall_xml->new_recall_num++] = recall; + } +} + +//读取补招文件 +int parse_recall_xml(recall_xml_t* recall_xml, const std::string& id) { + std::string cfg_dir = FRONT_PATH + "/etc/recall"; + std::string pattern = subdir + "_" + std::to_string(g_front_seg_index) + "_" + id + "_*_Recall.xml"; + + DIR* dir = opendir(cfg_dir.c_str()); + if (!dir) { + DIY_ERRORLOG("process", "【ERROR】前置的%s%d号进程 无法解析补招文件,补招文件路径FRONT_PATH + /etc/recall/不存在", get_front_msg_from_subdir(), g_front_seg_index); + return false; + } + + struct dirent* entry; + while ((entry = readdir(dir)) != NULL) { + std::string filename = entry->d_name; + if (fnmatch(pattern.c_str(), filename.c_str(), 0) != 0) continue; + + std::string filepath = cfg_dir + "/" + filename; + tinyxml2::XMLDocument doc; + if (doc.LoadFile(filepath.c_str()) != tinyxml2::XML_SUCCESS) { + DIY_ERRORLOG("process", "【ERROR】前置的%s%d号进程 无法解析补招文件%s,补招内容无效", get_front_msg_from_subdir(), g_front_seg_index, filepath.c_str()); + continue; + } + + tinyxml2::XMLElement* root = doc.RootElement(); + if (!root) continue; + + for (tinyxml2::XMLElement* elem = root->FirstChildElement(); elem != nullptr; elem = elem->NextSiblingElement()) { + std::string tag = elem->Name(); + if (tag == "Work" || tag == "New") { + for (tinyxml2::XMLElement* recallElem = elem->FirstChildElement("Recall"); recallElem != nullptr; recallElem = recallElem->NextSiblingElement("Recall")) { + std::map attrs; + const char* start = recallElem->Attribute("StartTime"); + const char* end = recallElem->Attribute("EndTime"); + const char* steady = recallElem->Attribute("STEADY"); + const char* voltage = recallElem->Attribute("VOLTAGE"); + + if (start && end && steady && voltage) { + attrs["StartTime"] = start; + attrs["EndTime"] = end; + attrs["STEADY"] = steady; + attrs["VOLTAGE"] = voltage; + + parse_recall(recall_xml, tag, attrs, id); + } + } + } + } + } + + closedir(dir); + return 0; +} + +//将读取到的补招文件写入到监测点的运行结构中,后续根据实际补充 +void process_recall_config(recall_xml_t* recall_xml) +{ + +} + +//根据监测点id来获取补招数据,补招时调用这个 +void Check_Recall_Config(const std::string& id) { + if (g_node_id == HIS_DATA_BASE_NODE_ID || + g_node_id == NEW_HIS_DATA_BASE_NODE_ID || + g_node_id == RECALL_HIS_DATA_BASE_NODE_ID || + g_node_id == RECALL_ALL_DATA_BASE_NODE_ID) { + + recall_xml_t recall_xml; + std::memset(&recall_xml, 0, sizeof(recall_xml_t)); + + // 解析补招文件 + parse_recall_xml(&recall_xml, id); + + // 将补招数据赋值到全局变量 + process_recall_config(&recall_xml); + } +} + +//补招成功后删除补招文件,补招后调用这个 +int delete_recall_xml(const std::string& id) { + std::string cfg_dir = FRONT_PATH + "/etc/recall"; + std::string pattern = std::string(subdir) + "_" + + std::to_string(g_front_seg_index) + "_" + + id + "_*_Recall.xml"; + + DIR* dir = opendir(cfg_dir.c_str()); + if (!dir) { + std::cerr << "Folder does not exist or cannot be opened: " << cfg_dir << std::endl; + return false; + } + + struct dirent* entry; + while ((entry = readdir(dir)) != NULL) { + std::string filename = entry->d_name; + if (fnmatch(pattern.c_str(), filename.c_str(), 0) == 0) { + std::string fullpath = cfg_dir + "/" + filename; + if (remove(fullpath.c_str()) == 0) { + std::cout << "Deleted: " << fullpath << std::endl; + } else { + std::cerr << "Failed to delete: " << fullpath << std::endl; + } + } + } + + closedir(dir); + return 0; +} + +//删除过期的xmllnk:多个进程并发删除导致的失败不会影响进程 +void DeletcRecallXml() { + std::string cfg_dir = FRONT_PATH + "/etc/recall"; + std::string pattern = std::string(subdir) + "_*_Recall.xml"; + + DIR* dir = opendir(cfg_dir.c_str()); + if (!dir) { + std::cerr << "folder does not exist!" << std::endl; + DIY_ERRORLOG("process", "【ERROR】前置的%s%d号进程 删除旧的补招文件失败,补招文件路径FRONT_PATH + /etc/recall/不存在",get_front_msg_from_subdir(), g_front_seg_index); + return; + } + + // 获取当前时间,计算 2 天前的时间戳 + std::time_t now = std::time(nullptr); + std::time_t cutoff = now - 2 * 24 * 60 * 60; // 两天前 + + struct dirent* entry; + while ((entry = readdir(dir)) != NULL) { + std::string filename = entry->d_name; + + if (fnmatch(pattern.c_str(), filename.c_str(), 0) == 0) { + std::string fullpath = cfg_dir + "/" + filename; + + struct stat file_stat; + if (stat(fullpath.c_str(), &file_stat) == 0) { + if (file_stat.st_mtime < cutoff) { + if (remove(fullpath.c_str()) == 0) { + DIY_INFOLOG("process", "【NORMAL】前置的%s%d号进程 删除超过两天的补招文件",get_front_msg_from_subdir(), g_front_seg_index); + } else { + std::cerr << "Failed to remove file: " << fullpath << std::endl; + } + } + } + } + } + + closedir(dir); +} + +//根据补招列表创建补招文件 +void CreateRecallXml() { + std::time_t now = std::time(nullptr); + std::tm* tm_now = std::localtime(&now); + char timestamp[32] = {0}; + std::strftime(timestamp, sizeof(timestamp), "%Y%m%d%H%M%S", tm_now); + + g_StatisticLackList_list_mutex.lock(); + + if (!g_StatisticLackList.empty()) { + DIY_INFOLOG("process", "【NORMAL】前置的%s%d号进程 开始写入补招文件", get_front_msg_from_subdir(), g_front_seg_index); + + std::map> id_map; + for (const auto& jr : g_StatisticLackList) { + id_map[jr.MonitorID].push_back(jr); + } + + for (const auto& pair : id_map) { + const std::string& monitor_id = pair.first; + const std::list& recalls = pair.second; + + std::ostringstream path; + path << FRONT_PATH << "/etc/recall/" << subdir << "_" << g_front_seg_index << "_" << monitor_id << "_" << timestamp << "_Recall.xml"; + + tinyxml2::XMLDocument doc; + tinyxml2::XMLDeclaration* decl = doc.NewDeclaration(); + doc.InsertFirstChild(decl); + + tinyxml2::XMLElement* root = doc.NewElement("RecallList"); + doc.InsertEndChild(root); + + // 空 Work 段 + tinyxml2::XMLElement* work = doc.NewElement("Work"); + root->InsertEndChild(work); + + // New 段 + tinyxml2::XMLElement* new_elem = doc.NewElement("New"); + for (const auto& jr : recalls) { + tinyxml2::XMLElement* recall = doc.NewElement("Recall"); + recall->SetAttribute("MonitorID", jr.MonitorID.c_str()); + recall->SetAttribute("StartTime", jr.StartTime.c_str()); + recall->SetAttribute("EndTime", jr.EndTime.c_str()); + recall->SetAttribute("STEADY", jr.STEADY.c_str()); + recall->SetAttribute("VOLTAGE", jr.VOLTAGE.c_str()); + new_elem->InsertEndChild(recall); + } + root->InsertEndChild(new_elem); + + tinyxml2::XMLError save_result = doc.SaveFile(path.str().c_str()); + if (save_result != tinyxml2::XML_SUCCESS) { + DIY_ERRORLOG("process", "【ERROR】前置的%s%d号进程 无法将补招文件写入路径: %s",get_front_msg_from_subdir(), g_front_seg_index, path.str().c_str()); + continue; + } + } + } + + g_StatisticLackList.clear(); + g_StatisticLackList_list_mutex.unlock(); +} + +//生成待补招xml文件 +void create_recall_xml() +{ + if (g_node_id == HIS_DATA_BASE_NODE_ID || g_node_id == NEW_HIS_DATA_BASE_NODE_ID || g_node_id == RECALL_HIS_DATA_BASE_NODE_ID || (g_node_id == RECALL_ALL_DATA_BASE_NODE_ID)) { + DeletcRecallXml(); + CreateRecallXml(); + } +} + +// 工具函数:将时间字符串转为 time_t(秒级) +static long long parse_time_to_epoch(const std::string& time_str) { + std::tm tm = {}; + std::istringstream ss(time_str); + ss >> std::get_time(&tm, "%Y-%m-%d %H:%M:%S"); + if (ss.fail()) { + return 0; + } + return static_cast(std::mktime(&tm)); +} + +// 数据完整性补招判断(划分为1小时) +void Get_Recall_Time_Char(const std::string& start_time_str, + const std::string& end_time_str, + std::vector& recallinfo_list_hour) { + long long starttime = parse_time_to_epoch(start_time_str); + long long endtime = parse_time_to_epoch(end_time_str); + + if (starttime == 0 || endtime == 0 || starttime >= endtime) { + return; + } + + // 初始区间加入 + RecallInfo initial; + initial.starttime = starttime; + initial.endtime = endtime; + + std::vector recallinfo_list; + recallinfo_list.push_back(initial); + + const long long max_interval = 3600; // 1小时 + for (const auto& interval : recallinfo_list) { + long long duration = interval.endtime - interval.starttime; + + for (long long j = 0; j <= duration; j += max_interval) { + RecallInfo info; + info.starttime = interval.starttime + j; + if (j + max_interval > duration) { + info.endtime = interval.endtime; + } else { + info.endtime = interval.starttime + j + max_interval - 1; + } + recallinfo_list_hour.push_back(info); + } + } +} + +//mq调用将补招信息写入补招列表 +int recall_json_handle(const std::string& jstr) { + // 不指定稳态/暂态则全部补招 + int stat = 0; + int voltage = 0; + try { + std::vector recallParams; + + // 1. 解析 JSON 数组 + auto json_root = nlohmann::json::parse(jstr); + if (!json_root.is_array()) { + std::cout << "json root解析错误" << std::endl; + return 10000; + } + + // 2. 遍历每个补招项 + for (auto& item : json_root) { + // 获取必需字段 + if (!item.contains("monitorId") || + !item.contains("timeInterval") || + !item.contains("dataType")) + { + std::cout << "json内容解析错误" << std::endl; + return 10000; + } + + // 2.1 解析 dataType + std::string datatype = item["dataType"].get(); + if (!datatype.empty()) { + stat = (datatype == "0") ? 1 : 0; // 稳态 + voltage = (datatype == "1") ? 1 : 0; // 暂态 + } else { + stat = voltage = 1; // 全补 + } + + // 2.2 处理 monitorId 数组 + auto& midArr = item["monitorId"]; + auto& tiArr = item["timeInterval"]; + if (midArr.is_array() && tiArr.is_array() && !midArr.empty()) { + for (auto& idItem : midArr) { + std::string monitorId = idItem.get(); + + // 判断此监测点是否归属当前进程 + bool mppair = false; + for (const auto& dev : terminal_devlist) { + // 只处理本进程对应的终端 + if (std::stoi(dev.processNo) != g_front_seg_index && + g_front_seg_index != 0) { + continue; + } + for (const auto& mon : dev.line) { + if (mon.monitor_id.empty()) continue; + if (mon.monitor_id == monitorId) { + mppair = true; + std::cout << "Matched monitorId " << monitorId + << " in terminal " << dev.terminal_id + << std::endl; + break; + } + } + if (mppair) break; + } + if (!mppair) continue; + + // 遍历 timeInterval 数组 + for (auto& timeItem : tiArr) { + std::string ti = timeItem.get(); + auto pos = ti.find('~'); + std::string start = ti.substr(0, pos); + std::string end = ti.substr(pos + 1); + + JournalRecall param; + param.MonitorID = monitorId; + param.StartTime = start; + param.EndTime = end; + param.STEADY = std::to_string(stat); + param.VOLTAGE = std::to_string(voltage); + recallParams.push_back(param); + } + } + } + // 2.3 monitorId 数组存在但为空 -> 补招所有监测点 + else if (midArr.is_array() && midArr.empty()) { + std::cout << "monitorIdArray is null,补招所有监测点" << std::endl; + for (const auto& dev : terminal_devlist) { + if (std::stoi(dev.processNo) != g_front_seg_index && + g_front_seg_index != 0) { + continue; + } + for (const auto& mon : dev.line) { + if (mon.monitor_id.empty()) continue; + for (auto& timeItem : tiArr) { + std::string ti = timeItem.get(); + auto pos = ti.find('~'); + std::string start = ti.substr(0, pos); + std::string end = ti.substr(pos + 1); + + JournalRecall param; + param.MonitorID = mon.monitor_id; + param.StartTime = start; + param.EndTime = end; + param.STEADY = std::to_string(stat); + param.VOLTAGE = std::to_string(voltage); + recallParams.push_back(param); + } + } + } + } else { + std::cout << "monitorIdArray 不存在或类型不正确" << std::endl; + } + } + + // 3. 生成具体补招记录 + for (auto& rp : recallParams) { + std::string start_time = rp.StartTime; + std::string end_time = rp.EndTime; + + std::cout << "mp_id " << rp.MonitorID + << " start_time " << start_time + << " end_time " << end_time + << " stat " << rp.STEADY + << " voltage " << rp.VOLTAGE + << std::endl; + + std::vector recallinfo_list_hour; + Get_Recall_Time_Char(start_time, end_time, recallinfo_list_hour); + + for (auto& info : recallinfo_list_hour) { + JournalRecall jr; + jr.MonitorID = rp.MonitorID; + + // 转换 starttime + char buf[20]; + std::tm tm{}; + time_t st = static_cast(info.starttime); + localtime_r(&st, &tm); + std::strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &tm); + jr.StartTime = buf; + + // 转换 endtime + std::tm tm2{}; + time_t et = static_cast(info.endtime); + localtime_r(&et, &tm2); + std::strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &tm2); + jr.EndTime = buf; + + jr.STEADY = rp.STEADY; + jr.VOLTAGE = rp.VOLTAGE; + + std::lock_guard lk(g_StatisticLackList_list_mutex); + g_StatisticLackList.push_back(jr); + } + } + } + catch (const std::exception& e) { + std::cout << "处理客户端发送的消息错误,原因:" << e.what() << std::endl; + return 10004; + } + + return 0; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////台账接口打印 + +// 打印 terminal_dev_map 中所有内容的函数 +void printTerminalDevMap(const std::map& terminal_dev_map) { + for (const auto& kv : terminal_dev_map) { + const std::string& key = kv.first; + const terminal_dev& dev = kv.second; // 引用对象 + + std::cout << "Key: " << key + << ", Terminal ID: " << dev.terminal_id + << ", Terminal Code: " << dev.terminal_code + << ", Organization Name: "<< dev.org_name + << ", Maintenance Name: " << dev.maint_name + << ", Station Name: " << dev.station_name + << ", Factory: " << dev.tmnl_factory + << ", Status: " << dev.tmnl_status + << ", Device Type: " << dev.dev_type + << ", Device Key: " << dev.dev_key + << ", Device Series: " << dev.dev_series + << ", ProcessNo: " << dev.processNo + << ", MaxProcessNum: " << dev.maxProcessNum + << ", Address: " << dev.addr_str + << ", Port: " << dev.port + << ", Timestamp: " << dev.timestamp + << std::endl; + + // 打印监测点信息 + for (size_t i = 0; i < dev.line.size(); ++i) { + const auto& m = dev.line[i]; + std::cout << " Monitor [" << i << "] " + << "ID: " << m.monitor_id + << ", Code: " << m.terminal_code + << ", Name: " << m.monitor_name + << ", Seq: " << m.logical_device_seq + << ", Voltage: "<< m.voltage_level + << ", Connect: "<< m.terminal_connect + << ", Timestamp:"<< m.timestamp + << ", Status: " << m.status + << std::endl; + } + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////更新台账 + +//在台账更新目录查找自己进程要处理的文件 +std::list find_xml_belong_to_this_process() +{ + char prefix[20]; // 假设最多需要20个字符(根据实际需要调整) + sprintf(prefix, "%d_%d", g_node_id, g_front_seg_index); // 将g_node_id和g_front_seg_index格式化为字符串 + + std::string LEDGER_UPDATE_DIR = FRONT_PATH + "/etc/ledgerupdate/"; //台账更新读取目录 + + DIR *dir = opendir(LEDGER_UPDATE_DIR.c_str()); // 打开目录 + struct dirent *entry; + std::list found_files; // 用于存储找到的所有匹配文件名 + + if (dir == NULL) { + std::cout << "Failed to open directory: " << LEDGER_UPDATE_DIR << std::endl; + return found_files; // 返回空的list + } + + // 遍历目录中的所有文件 + while ((entry = readdir(dir)) != NULL) { + std::string filename = entry->d_name; + + // 排除 "." 和 ".." 目录 + if (filename == "." || filename == "..") { + continue; + } + + std::cout << "find" << filename << "in" << LEDGER_UPDATE_DIR << std::endl; + + + // 判断文件名是否以 prefix 开头且扩展名是 .xml + if (filename.find(prefix) == 0 && filename.substr(filename.find_last_of('.') + 1) == "xml") { + std::string full_path = LEDGER_UPDATE_DIR + filename; + found_files.push_back(full_path); // 将完整路径加入容器 + } + } + + closedir(dir); // 关闭目录 + + return found_files; // 返回所有找到的文件名 +} + +// 根据 str_tag 将 terminal 添加到对应的数组 +void add_terminal_to_trigger_update(trigger_update_xml_t& trigger_update_xml, + const std::string& str_tag, + const terminal_dev& work_terminal) { + if (str_tag == "add") { + std::cout << "new ledger!!!!" << std::endl; + trigger_update_xml.new_updates.push_back(work_terminal); + } else if (str_tag == "modify") { + std::cout << "modify ledger!!!" << std::endl; + trigger_update_xml.modify_updates.push_back(work_terminal); + } else { + std::cerr << "Unknown tag: " << str_tag << std::endl; + } +} + +// 将添加和修改的文件内容写入结构 +void parse_terminal_from_data(trigger_update_xml_t& trigger_update_xml, + const std::string& str_tag, + const std::string& data, + const std::string& guid_value) { + terminal_dev work_terminal; + work_terminal.guid = guid_value; + + tinyxml2::XMLDocument doc; + if (doc.Parse(data.c_str()) != tinyxml2::XML_SUCCESS) { + return; + } + + auto root = doc.FirstChildElement("terminal"); + if (!root) return; + + auto get_value = [&](const char* tag) -> std::string { + auto elem = root->FirstChildElement(tag); + return elem && elem->GetText() ? elem->GetText() : ""; + }; + + work_terminal.terminal_id = get_value("id"); + work_terminal.terminal_code = get_value("terminalCode"); + work_terminal.org_name = get_value("orgName"); + work_terminal.maint_name = get_value("maintName"); + work_terminal.station_name = get_value("stationName"); + work_terminal.tmnl_factory = get_value("manufacturer"); + work_terminal.tmnl_status = get_value("status"); + work_terminal.dev_type = get_value("devType"); + work_terminal.dev_key = get_value("devKey"); + work_terminal.dev_series = get_value("series"); + work_terminal.processNo = get_value("processNo"); + work_terminal.addr_str = get_value("ip"); + work_terminal.port = get_value("port"); + work_terminal.timestamp = get_value("updateTime"); + + for (tinyxml2::XMLElement* monitor = root->FirstChildElement("monitorData"); + monitor; + monitor = monitor->NextSiblingElement("monitorData")) { + ledger_monitor mon; + mon.monitor_id = monitor->FirstChildElement("id") ? monitor->FirstChildElement("id")->GetText() : "N/A"; + mon.monitor_name = monitor->FirstChildElement("name") ? monitor->FirstChildElement("name")->GetText() : "N/A"; + mon.voltage_level = monitor->FirstChildElement("voltageLevel") ? monitor->FirstChildElement("voltageLevel")->GetText() : "N/A"; + mon.terminal_connect = monitor->FirstChildElement("ptType") ? monitor->FirstChildElement("ptType")->GetText() : "N/A"; + mon.logical_device_seq = monitor->FirstChildElement("lineNo") ? monitor->FirstChildElement("lineNo")->GetText() : "N/A"; + mon.timestamp = monitor->FirstChildElement("timestamp") ? monitor->FirstChildElement("timestamp")->GetText() : "N/A"; + mon.terminal_code = monitor->FirstChildElement("terminal_code") ? monitor->FirstChildElement("terminal_code")->GetText() : "N/A"; + mon.status = monitor->FirstChildElement("status") ? monitor->FirstChildElement("status")->GetText() : "N/A"; + + work_terminal.line.push_back(mon); + } + + add_terminal_to_trigger_update(trigger_update_xml, str_tag, work_terminal); +} + +// 统一处理文件内容和结构 +void parse_ledger_update(trigger_update_xml_t& trigger_update_xml, + const std::string& strTag, + const std::string& data, + const std::string& guid_value) { + std::cout << "record one xml.." << std::endl; + + if (strTag == "add" || strTag == "modify") { + parse_terminal_from_data(trigger_update_xml, strTag, data, guid_value); + } else if (strTag == "delete") { + terminal_dev delete_terminal; + tinyxml2::XMLDocument doc; + + if (doc.Parse(data.c_str()) != tinyxml2::XML_SUCCESS) { + std::cerr << "Failed to parse XML for delete tag." << std::endl; + return; + } + + tinyxml2::XMLElement* root = doc.FirstChildElement("terminalData"); + if (!root) { + std::cerr << "Missing terminalData element in delete tag." << std::endl; + return; + } + + tinyxml2::XMLElement* idElem = root->FirstChildElement("id"); + if (idElem && idElem->GetText()) { + delete_terminal.terminal_id = idElem->GetText(); + } else { + std::cerr << "Missing id element in delete tag." << std::endl; + return; + } + + delete_terminal.guid = guid_value; + trigger_update_xml.delete_updates.push_back(delete_terminal); + } else { + std::cerr << "Unsupported strTag: " << strTag << std::endl; + } +} + +//读取台账更新文件 +int load_ledger_update_from_xml(trigger_update_xml_t& trigger_update_xml, const std::string& xml_fn) { + std::cout << "start to load one xml.." << std::endl; + + std::ifstream file(xml_fn); + if (!file.is_open()) { + std::cerr << "Failed to open file: " << xml_fn << std::endl; + return -1; + } + + std::stringstream buffer; + buffer << file.rdbuf(); + std::string content = buffer.str(); + file.close(); + + tinyxml2::XMLDocument doc; + if (doc.Parse(content.c_str()) != tinyxml2::XML_SUCCESS) { + std::cerr << "Failed to parse XML content." << std::endl; + return -1; + } + + auto* root = doc.FirstChildElement("ledger_update"); + if (!root) { + std::cerr << "Missing root tag." << std::endl; + return -1; + } + + std::string guid_value; + auto* guidElem = root->FirstChildElement("guid"); + if (guidElem && guidElem->GetText()) { + guid_value = guidElem->GetText(); + std::cout << "Found guid: " << guid_value << std::endl; + } + + const char* tag_names[] = {"add", "modify", "delete"}; + tinyxml2::XMLElement* tagElem = nullptr; + std::string target_tag; + + for (const auto& tag : tag_names) { + tagElem = root->FirstChildElement(tag); + if (tagElem) { + target_tag = tag; + break; + } + } + + if (!tagElem) { + std::cerr << "No , , or tag found!" << std::endl; + return -1; + } + + for (auto* termElem = tagElem->FirstChildElement("terminalData"); + termElem; + termElem = termElem->NextSiblingElement("terminalData")) { + tinyxml2::XMLPrinter printer; + termElem->Accept(&printer); + std::string data_content = printer.CStr(); + + std::cout << "ledger data_content is " << data_content << std::endl; + parse_ledger_update(trigger_update_xml, target_tag, data_content, guid_value); + } + + std::cout << "load one xml finish" << std::endl; + return 0; +} + +//台账更新处理文件 +int parse_ledger_update_xml(trigger_update_xml_t& trigger_update_xml) +{ + std::list result = find_xml_belong_to_this_process(); + + if (result.empty()) return 1; + + for (const auto& filename : result) { + std::cout << "Found XML: " << filename << std::endl; + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + if (!load_ledger_update_from_xml(trigger_update_xml, filename)) { + DIY_WARNLOG("process", "【WARN】成功读取台账更新文件: %s", filename.c_str()); + } + + if (std::remove(filename.c_str()) != 0) { + DIY_ERRORLOG("process", "【ERROR】删除台账更新文件失败: %s", filename.c_str()); + return 1; + } else { + DIY_INFOLOG("process", "【NORMAL】成功删除台账更新文件: %s", filename.c_str()); + } + } + + if (!trigger_update_xml.new_updates.empty() || + !trigger_update_xml.modify_updates.empty() || + !trigger_update_xml.delete_updates.empty()) { + std::cout << "ledger update xml have data..." << std::endl; + return 0; + } + + std::cout << "ledger update xml no data..." << std::endl; + return 1; +} + +//更新单个台账 +int update_one_terminal_ledger(const terminal_dev& update,terminal_dev& target_dev) { + // 更新基本信息 + if (!update.terminal_id.empty()) { + target_dev.terminal_id = update.terminal_id; + std::cout << "terminal_id: " << target_dev.terminal_id << std::endl; + } + if (!update.terminal_code.empty()) { + target_dev.terminal_code = update.terminal_code; + std::cout << "terminal_code: " << target_dev.terminal_code << std::endl; + } + if (!update.tmnl_factory.empty()) { + target_dev.tmnl_factory = update.tmnl_factory; + std::cout << "tmnl_factory: " << target_dev.tmnl_factory << std::endl; + } + if (!update.tmnl_status.empty()) { + target_dev.tmnl_status = update.tmnl_status; + std::cout << "tmnl_status: " << target_dev.tmnl_status << std::endl; + } + if (!update.dev_type.empty()) { + target_dev.dev_type = update.dev_type; + std::cout << "dev_type: " << target_dev.dev_type << std::endl; + } + if (!update.processNo.empty()) { + target_dev.processNo = update.processNo; + std::cout << "processNo: " << target_dev.processNo << std::endl; + } + if (!update.dev_series.empty()) { + target_dev.dev_series = update.dev_series; + std::cout << "dev_series: " << target_dev.dev_series << std::endl; + } + if (!update.dev_key.empty()) { + target_dev.dev_key = update.dev_key; + std::cout << "dev_key: " << target_dev.dev_key << std::endl; + } + + if (!update.addr_str.empty()) { + target_dev.addr_str = update.addr_str; + std::cout << "addr_str: " << target_dev.addr_str << std::endl; + } + if (!update.port.empty()) { + target_dev.port = update.port; + std::cout << "port: " << target_dev.port << std::endl; + } + + if (!update.timestamp.empty()) { + struct tm timeinfo = {}; + if (sscanf(update.timestamp.c_str(), "%4d-%2d-%2d %2d:%2d:%2d", + &timeinfo.tm_year, &timeinfo.tm_mon, &timeinfo.tm_mday, + &timeinfo.tm_hour, &timeinfo.tm_min, &timeinfo.tm_sec) == 6) { + timeinfo.tm_year -= 1900; + timeinfo.tm_mon -= 1; + timeinfo.tm_isdst = -1; + time_t raw_time = mktime(&timeinfo); + if (raw_time != -1) { + target_dev.timestamp = static_cast(raw_time); + std::cout << "timestamp (unix): " << target_dev.timestamp << std::endl; + } else { + std::cerr << "Error: mktime failed." << std::endl; + return -1; + } + } else { + std::cerr << "Error: invalid timestamp format." << std::endl; + return -1; + } + } + + // 清空旧监测点并重新填充 + target_dev.line.clear(); + + for (const auto& mon : update.line) { + if (mon.monitor_id.empty()) break; + + ledger_monitor m; + m.monitor_id = mon.monitor_id; + m.monitor_name = mon.monitor_name; + m.logical_device_seq = mon.logical_device_seq; + m.voltage_level = mon.voltage_level; + m.terminal_connect = mon.terminal_connect; + m.status = mon.status; + m.terminal_code = mon.terminal_code; + m.timestamp = mon.timestamp; + + if (m.terminal_connect != "0") { + isdelta_flag = 1; + std::cout << "monitor_id " << m.monitor_id << " uses delta wiring." << std::endl; + } + + if (!m.timestamp.empty()) { + struct tm timeinfo = {}; + if (sscanf(m.timestamp.c_str(), "%4d-%2d-%2d %2d:%2d:%2d", + &timeinfo.tm_year, &timeinfo.tm_mon, &timeinfo.tm_mday, + &timeinfo.tm_hour, &timeinfo.tm_min, &timeinfo.tm_sec) == 6) { + timeinfo.tm_year -= 1900; + timeinfo.tm_mon -= 1; + timeinfo.tm_isdst = -1; + time_t raw_time = mktime(&timeinfo); + if (raw_time != -1) { + m.timestamp = static_cast(raw_time); + std::cout << "monitor time (unix): " << m.timestamp << std::endl; + } + } + } + + target_dev.line.push_back(m); + } + + return 0; +} + +//台账更新到台账列表 +void process_ledger_update(trigger_update_xml_t& ledger_update_xml) +{ + // --- 1. 新增处理 --- + std::cout << "add ledger num: " << ledger_update_xml.new_updates.size() << std::endl; + + for (auto it = ledger_update_xml.new_updates.begin(); it != ledger_update_xml.new_updates.end(); ) { + terminal_dev& new_dev = *it; + + auto found = std::find_if(terminal_devlist.begin(), terminal_devlist.end(), + [&](const terminal_dev& d) { return d.terminal_id == new_dev.terminal_id; }); + + if (found != terminal_devlist.end()) { + if (ledger_update_xml.modify_updates.size() < MAX_UPDATEA_NUM) { + ledger_update_xml.modify_updates.push_back(new_dev); + } else { + std::cerr << "Exceeded MAX_UPDATEA_NUM limit for modify_updates!" << std::endl; + } + it = ledger_update_xml.new_updates.erase(it); // 删除已处理项 + continue; + } + + if (terminal_devlist.size() >= static_cast(IED_COUNT)) { + send_reply_to_queue(new_dev.guid, "2", + "终端 id: " + new_dev.terminal_id + " 台账更新失败,配置台账数量已满"); + ++it; + continue; + } + + terminal_dev target_dev; + if (update_one_terminal_ledger(new_dev, target_dev) != 0) { + send_reply_to_queue(new_dev.guid, "2", + "终端 id: " + new_dev.terminal_id + " 台账更新失败,无法写入台账"); + ++it; + continue; + } + + if (parse_model_cfg_web_one(target_dev.dev_type).empty()) { + send_reply_to_queue(new_dev.guid, "2", + "终端 id: " + new_dev.terminal_id + " 台账更新失败,未找到装置型号"); + ++it; + continue; + } + + Set_xml_nodeinfo_one(target_dev.dev_type); + init_loggers_bydevid(target_dev.terminal_id); + terminal_devlist.push_back(target_dev); + + send_reply_to_queue(new_dev.guid, "2", + "终端 id: " + new_dev.terminal_id + " 台账添加成功"); + + it = ledger_update_xml.new_updates.erase(it); + } + + // --- 2. 修改处理 --- + std::cout << "modify ledger num: " << ledger_update_xml.modify_updates.size() << std::endl; + + for (auto& mod_dev : ledger_update_xml.modify_updates) { + auto it = std::find_if(terminal_devlist.begin(), terminal_devlist.end(), + [&](const terminal_dev& d) { return d.terminal_id == mod_dev.terminal_id; }); + + if (it != terminal_devlist.end()) { + remove_loggers_by_terminal_id(mod_dev.terminal_id); + if (update_one_terminal_ledger(mod_dev, *it) != 0) { + send_reply_to_queue(mod_dev.guid, "2", + "终端 id: " + mod_dev.terminal_id + " 台账更新失败,写入失败"); + continue; + } + + if (parse_model_cfg_web_one(it->dev_type).empty()) { + send_reply_to_queue(mod_dev.guid, "2", + "终端 id: " + mod_dev.terminal_id + " 台账更新失败,未找到装置型号"); + continue; + } + + Set_xml_nodeinfo_one(it->dev_type); + init_loggers_bydevid(mod_dev.terminal_id); + send_reply_to_queue(mod_dev.guid, "2", + "终端 id: " + mod_dev.terminal_id + " 台账修改成功"); + } else { + send_reply_to_queue(mod_dev.guid, "2", + "终端 id: " + mod_dev.terminal_id + " 台账修改失败,未找到终端"); + } + } + + // --- 3. 删除处理 --- + std::cout << "delete ledger num: " << ledger_update_xml.delete_updates.size() << std::endl; + + for (auto& del_dev : ledger_update_xml.delete_updates) { + auto it = std::find_if(terminal_devlist.begin(), terminal_devlist.end(), + [&](const terminal_dev& d) { return d.terminal_id == del_dev.terminal_id; }); + + if (it != terminal_devlist.end()) { + remove_loggers_by_terminal_id(del_dev.terminal_id); + terminal_devlist.erase(it); + send_reply_to_queue(del_dev.guid, "2", + "终端 id: " + del_dev.terminal_id + " 台账删除成功"); + } else { + send_reply_to_queue(del_dev.guid, "2", + "终端 id: " + del_dev.terminal_id + " 台账删除失败,未找到终端"); + } + } + + // --- 4. 日志记录 --- + if (!ledger_update_xml.modify_updates.empty() || + !ledger_update_xml.new_updates.empty() || + !ledger_update_xml.delete_updates.empty()) { + create_ledger_log(&ledger_update_xml); + } +} + +//台账更新处理函数 +void check_ledger_update() +{ + static double last_check_time = 0.0; + double now = sGetMsTime(); + + if (now - last_check_time < 3000.0) + return; + + last_check_time = now; + + std::unique_ptr trigger_ledger_update_xml(new trigger_update_xml_t()); + + if (0 == parse_ledger_update_xml(*trigger_ledger_update_xml)) { + print_trigger_update_xml(*trigger_ledger_update_xml); + std::lock_guard lock(ledgermtx); + process_ledger_update(*trigger_ledger_update_xml); + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////台账更新记录日志 + +// 获取当前时间并格式化为 "YYYY-MM-DD HH:MM:SS" +std::string get_current_time() { + std::time_t t = std::time(NULL); + struct std::tm tm = *std::localtime(&t); + + char buffer[80]; + strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", &tm); + return std::string(buffer); +} + +// 写入日志条目 +void write_log_entry(std::ofstream &log_file, const std::string &action, const std::string &terminal_id, const std::string ¤t_time) { + log_file << terminal_id << "\t" << action << "time:" << current_time << "\n"; +} + +// 创建台账更新日志 +void create_ledger_log(trigger_update_xml_t* ledger_update_xml) { + std::cout << "create_ledger_log." << std::endl; + + std::string log_filename = FRONT_PATH + "/etc/" + LEDGER_UPDATE_FN; + std::ofstream log_file(log_filename.c_str(), std::ios::app); // 以追加模式打开文件 + + if (!log_file.is_open()) { + std::cerr << "Failed to open log file: " << log_filename << std::endl; + return; + } + + std::vector> new_entries; + std::vector> modify_entries; + std::vector> delete_entries; + + std::string current_time = get_current_time(); // 获取当前时间 + + // new_updates + for (const auto& dev : ledger_update_xml->new_updates) { + new_entries.emplace_back(dev.terminal_id, current_time); + } + + // modify_updates + for (const auto& dev : ledger_update_xml->modify_updates) { + modify_entries.emplace_back(dev.terminal_id, current_time); + } + + // delete_updates + for (const auto& dev : ledger_update_xml->delete_updates) { + delete_entries.emplace_back(dev.terminal_id, current_time); + } + + // 写入日志 + if (!new_entries.empty()) { + log_file << "\n"; + for (const auto& entry : new_entries) { + write_log_entry(log_file, "add", entry.first, entry.second); + } + log_file << "\n"; + } + + if (!modify_entries.empty()) { + log_file << "\n"; + for (const auto& entry : modify_entries) { + write_log_entry(log_file, "modify", entry.first, entry.second); + } + log_file << "\n"; + } + + if (!delete_entries.empty()) { + log_file << "\n"; + for (const auto& entry : delete_entries) { + write_log_entry(log_file, "delete", entry.first, entry.second); + } + log_file << "\n"; + } + + log_file.close(); + std::cout << "Ledger log has been updated." << std::endl; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////shell打印日志 + +// ------------------ 全局日志列表和锁 ------------------ +std::list errorList; +std::list warnList; +std::list normalList; + +pthread_mutex_t errorListMutex = PTHREAD_MUTEX_INITIALIZER; +pthread_mutex_t warnListMutex = PTHREAD_MUTEX_INITIALIZER; +pthread_mutex_t normalListMutex = PTHREAD_MUTEX_INITIALIZER; + +// ------------------ 输出开关 ------------------ +bool errorOutputEnabled = false; // 是否将 error 级别写入 errorList +bool warnOutputEnabled = false; // 是否将 warn 级别写入 warnList +bool normalOutputEnabled = false; // 是否将 normal 级别写入 normalList + +// ------------------ 用于恢复原始缓冲区 ------------------ +static std::streambuf* g_originalCoutBuf = NULL; +static std::streambuf* g_originalClogBuf = NULL; +static std::streambuf* g_originalCerrBuf = NULL; + +// ------------------ 日志级别枚举(C++98) ------------------ +enum LogLevel { + LOGERROR, + LOGWARN, + LOGNORMAL +}; + +// ------------------------------------------------------------------ +// TeeStreamBuf: 先写到原始buf(保持终端输出),再拷贝到list里 +// ------------------------------------------------------------------ +class TeeStreamBuf : public std::streambuf +{ +public: + // 默认构造:先把指针设为NULL + TeeStreamBuf() + : m_originalBuf(NULL), m_level(LOGNORMAL) + { + pthread_mutex_init(&m_mutex, NULL); + } + + // 带参构造:直接初始化 + TeeStreamBuf(std::streambuf* originalBuf, LogLevel level) + : m_originalBuf(originalBuf), m_level(level) + { + pthread_mutex_init(&m_mutex, NULL); + } + + // 析构函数:销毁互斥锁 + virtual ~TeeStreamBuf() + { + pthread_mutex_destroy(&m_mutex); + } + + // 自定义 init(...) 函数:在同一个对象上重新设置 + void init(std::streambuf* originalBuf, LogLevel level) + { + m_originalBuf = originalBuf; + m_level = level; + pthread_mutex_lock(&m_mutex); + m_buffer.clear(); + pthread_mutex_unlock(&m_mutex); + } + +protected: + // 当 flush 或 std::endl 时会调用 sync() + virtual int sync() + { + // 先让原始缓冲区执行同步 + if (m_originalBuf) { + m_originalBuf->pubsync(); + } + // 再将自身的缓存 flush + flushBuffer(); + return 0; // 成功 + } + + // 当写入一个新字符时,overflow() 被调用 + virtual int_type overflow(int_type ch) + { + if (ch == traits_type::eof()) { + return ch; + } + // 1) 写到原始缓冲区 → 保留终端输出 + if (m_originalBuf) { + if (m_originalBuf->sputc(static_cast(ch)) == traits_type::eof()) { + return traits_type::eof(); + } + } + // 2) 存到我们的临时缓存,注意加锁保护 + pthread_mutex_lock(&m_mutex); //防止多线程推入崩溃lnk20250305 + m_buffer.push_back(static_cast(ch)); + // 3) 遇到换行就 flushBuffer() + if (ch == '\n') { + flushBuffer_locked(); + } + pthread_mutex_unlock(&m_mutex); + return ch; + } + +private: + // 内部版本:假定互斥锁已经被加锁 + void flushBuffer_locked() + { + if (m_buffer.empty()) { + return; + } + // 根据等级和对应开关,将 m_buffer 写入相应的 list + switch (m_level) { + case LOGERROR: + if (normalOutputEnabled) { + pthread_mutex_lock(&normalListMutex); + normalList.push_back(m_buffer); + pthread_mutex_unlock(&normalListMutex); + } + else if (warnOutputEnabled) { + pthread_mutex_lock(&warnListMutex); + warnList.push_back(m_buffer); + pthread_mutex_unlock(&warnListMutex); + } + else if (errorOutputEnabled) { + pthread_mutex_lock(&errorListMutex); + errorList.push_back(m_buffer); + pthread_mutex_unlock(&errorListMutex); + } + break; + + case LOGWARN: + if (normalOutputEnabled) { + pthread_mutex_lock(&normalListMutex); + normalList.push_back(m_buffer); + pthread_mutex_unlock(&normalListMutex); + } + else if (warnOutputEnabled) { + pthread_mutex_lock(&warnListMutex); + warnList.push_back(m_buffer); + pthread_mutex_unlock(&warnListMutex); + } + break; + + case LOGNORMAL: + if (normalOutputEnabled) { + pthread_mutex_lock(&normalListMutex); + normalList.push_back(m_buffer); + pthread_mutex_unlock(&normalListMutex); + } + break; + } + m_buffer.clear(); + } + + // 对外接口,内部对 m_buffer 加锁 + void flushBuffer() + { + pthread_mutex_lock(&m_mutex); + flushBuffer_locked(); + pthread_mutex_unlock(&m_mutex); + } + +private: + // 禁止自动生成的赋值函数 + TeeStreamBuf& operator=(const TeeStreamBuf&); + +private: + std::streambuf* m_originalBuf; + LogLevel m_level; + std::string m_buffer; + pthread_mutex_t m_mutex; +}; + +// ------------------ 全局Tee对象(避免重复赋值) ------------------ +static TeeStreamBuf g_errorTeeBuf; +static TeeStreamBuf g_warnTeeBuf; +static TeeStreamBuf g_normalTeeBuf; + +// ------------------ 重定向函数 ------------------ +// 只在第一次启用时,用 init(...) 初始化 TeeStreamBuf; +// 之后再启用时,不再 new 或 赋值,而是直接用之前构造好的对象。 +void redirectErrorOutput(bool enabled) +{ + errorOutputEnabled = enabled; + if (enabled) { + if (g_originalCerrBuf == NULL) { + g_originalCerrBuf = std::cerr.rdbuf(); + g_errorTeeBuf.init(g_originalCerrBuf, LOGERROR); + } + std::cerr.rdbuf(&g_errorTeeBuf); + } else { + if (g_originalCerrBuf) { + std::cerr.rdbuf(g_originalCerrBuf); + } + } +} + +void redirectWarnOutput(bool enabled) +{ + warnOutputEnabled = enabled; + if (enabled) { + if (g_originalClogBuf == NULL) { + g_originalClogBuf = std::clog.rdbuf(); + g_warnTeeBuf.init(g_originalClogBuf, LOGWARN); + } + std::clog.rdbuf(&g_warnTeeBuf); + } else { + if (g_originalClogBuf) { + std::clog.rdbuf(g_originalClogBuf); + } + } +} + +void redirectNormalOutput(bool enabled) +{ + normalOutputEnabled = enabled; + if (enabled) { + if (g_originalCoutBuf == NULL) { + g_originalCoutBuf = std::cout.rdbuf(); + g_normalTeeBuf.init(g_originalCoutBuf, LOGNORMAL); + } + std::cout.rdbuf(&g_normalTeeBuf); + } else { + if (g_originalCoutBuf) { + std::cout.rdbuf(g_originalCoutBuf); + } + } +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////脚本控制 + +void execute_bash(string fun,int process_num,string type) +{ + // 为 char 数组分配足够的空间 + char p_num_str[20]; + // 使用 sprintf 转换 + std::sprintf(p_num_str, "%d", process_num); + const char* script = std::string(FRONT_PATH + "/bin/set_process.sh").c_str();//使用setsid防止端口占用 + const char* param1 = fun.c_str(); + const char* param2 = p_num_str; + const char* param3 = type.c_str(); + + // 构造完整的命令 + char command[256]; + snprintf(command, sizeof(command), "%s %s %s %s &", script, param1, param2, param3); + + std::cout << "command:" << command < *ctopiclist, const std::string& path) +{ + // 注释同原代码 + const std::string strPhasic[4] = { "A", "B", "C", "T" }; + const std::string strLine[4] = { "AB", "BC", "CA", "T" }; + int nStart = 1, nEnd = 1; + std::string strValueTemp; + + tinyxml2::XMLDocument doc; + std::string xmlFile; + if (path == "not define") { + xmlFile = FRONT_PATH + "/etc/" + DEFAULT_CONFIG_FN; //默认映射文件 + std::cout << "[调试] 加载XML路径: " << xmlFile << std::endl; + } else { + xmlFile = FRONT_PATH + "/dat/" + path + ".xml"; + std::cout << "[调试] 加载XML路径: " << xmlFile << std::endl; + } + auto result = doc.LoadFile(xmlFile.c_str()); + if (result != tinyxml2::XML_SUCCESS) { + std::cout << "[错误] 无法打开XML文件: " << xmlFile << std::endl; + return false; + } + + tinyxml2::XMLElement* root = doc.RootElement(); + if (!root) { + std::cout << "[错误] XML无根节点!" << std::endl; + return false; + } + for (tinyxml2::XMLElement* topicElem = root->FirstChildElement(); topicElem; topicElem = topicElem->NextSiblingElement()) { + std::string strTag = topicElem->Name(); + if (strTag == "Topic") { + CTopic* topic = new CTopic(); + topic->strTopic = topicElem->Attribute("name") ? topicElem->Attribute("name") : ""; + ctopiclist->push_back(topic); + std::cout << "[调试] 解析Topic节点: " << topic->strTopic << std::endl; + + // HISDATA、RTDATA + if (topic->strTopic == "HISDATA" || topic->strTopic == "RTDATA") { + for (tinyxml2::XMLElement* dataTypeElem = topicElem->FirstChildElement(); dataTypeElem; dataTypeElem = dataTypeElem->NextSiblingElement()) { + if (std::string(dataTypeElem->Name()) == "DataType") { + CDataType* dt = new CDataType(); + dt->iDataType = dataTypeElem->IntAttribute("value", 0); + dt->type = dataTypeElem->Attribute("type") ? dataTypeElem->Attribute("type") : ""; + dt->BaseFlag0 = 0; + dt->BaseFlag1 = 0; + topic->DataTypeList.push_back(dt); + + // 监测点 + for (tinyxml2::XMLElement* monitorElem = dataTypeElem->FirstChildElement(); monitorElem; monitorElem = monitorElem->NextSiblingElement()) { + if (std::string(monitorElem->Name()) == "Monitor") { + CMonitor* mt = new CMonitor(); + mt->strMonitor = monitorElem->Attribute("name") ? monitorElem->Attribute("name") : ""; + mt->type = monitorElem->Attribute("type") ? monitorElem->Attribute("type") : ""; + dt->MonitorList.push_back(mt); + + // 数据项 + for (tinyxml2::XMLElement* itemElem = monitorElem->FirstChildElement(); itemElem; itemElem = itemElem->NextSiblingElement()) { + if (std::string(itemElem->Name()) == "Item") { + CItem* it = new CItem(); + it->strItemName = itemElem->Attribute("name") ? itemElem->Attribute("name") : ""; + it->type = itemElem->Attribute("type") ? itemElem->Attribute("type") : ""; + if (it->strItemName == "FLAG") + it->strItemValue = itemElem->Attribute("value") ? itemElem->Attribute("value") : ""; + mt->ItemList.push_back(it); + + // Sequence + for (tinyxml2::XMLElement* seqElem = itemElem->FirstChildElement(); seqElem; seqElem = seqElem->NextSiblingElement()) { + if (std::string(seqElem->Name()) == "Sequence") { + std::string strPhase = seqElem->Attribute("value") ? seqElem->Attribute("value") : ""; + + // 读取ABC三相 + if ((!xml_flag || it->strItemName != "V") && strPhase == "7") { + for (int n = 0; n < 3; ++n) { + CSequence* sq = new CSequence(); + sq->strSValue = strPhase; + sq->type = seqElem->Attribute("type") ? seqElem->Attribute("type") : ""; + sq->strSeq = strPhasic[n]; + it->SequenceList.push_back(sq); + + for (tinyxml2::XMLElement* valueElem = seqElem->FirstChildElement(); valueElem; valueElem = valueElem->NextSiblingElement()) { + if (std::string(valueElem->Name()) == "Value") { + std::string strDVName = valueElem->Attribute("name") ? valueElem->Attribute("name") : ""; + std::string strDAName = valueElem->Attribute("DA") ? valueElem->Attribute("DA") : ""; + + // l_phs* → phsAB + if (strDAName.find("l_phs*") != std::string::npos) { + strDAName.replace(strDAName.find("l_phs"), 5, "phs"); + size_t starPos = strDAName.find("*"); + if (starPos != std::string::npos) + strDAName.replace(starPos, 1, strLine[n]); + } else if (strDAName.find("phs*") != std::string::npos) { + size_t starPos = strDAName.find("*"); + if (starPos != std::string::npos) + strDAName.replace(starPos, 1, sq->strSeq); + } + + // 谐波数据 + if (strDVName.find("%") != std::string::npos && strDAName.find("%-") != std::string::npos) { + size_t firstP = strDVName.find('%'), lastP = strDVName.rfind('%'); + if (firstP != std::string::npos && lastP != std::string::npos && firstP != lastP) { + std::string harmRange = strDVName.substr(firstP + 1, lastP - firstP - 1); // 0,49 + size_t comma = harmRange.find(','); + if (comma != std::string::npos) { + nStart = std::stoi(harmRange.substr(0, comma)); + nEnd = std::stoi(harmRange.substr(comma + 1)); + strValueTemp = "%" + harmRange + "%"; + } + } + // 取DA的范围 + size_t lb = strDAName.find('['), rb = strDAName.find(']'); + int strDAoffset = 0; + if (lb != std::string::npos && rb != std::string::npos && lb < rb) { + std::string sub = strDAName.substr(lb + 1, rb - lb - 1); + size_t dash = sub.find('-'); + if (dash != std::string::npos) + strDAoffset = std::stoi(sub.substr(dash + 1)); + } + int offset = valueElem->IntAttribute("Offset", 0); + for (int i = nStart; i <= nEnd; ++i) { + std::string strDVNameTemp = strDVName; + std::string strDANameTemp = strDAName; + CDataValue* dv1 = new CDataValue(); + dv1->type = valueElem->Attribute("type") ? valueElem->Attribute("type") : ""; + if (valueElem->Attribute("Coefficient")) { + dv1->strCoefficient = valueElem->Attribute("Coefficient"); + dv1->fCoefficient = valueElem->FloatAttribute("Coefficient", 1.0f); + } + dv1->strOffset = valueElem->Attribute("Offset") ? valueElem->Attribute("Offset") : ""; + dv1->iOffset = offset; + dv1->DO = valueElem->Attribute("DO") ? valueElem->Attribute("DO") : ""; + + size_t tmpPos = strDVNameTemp.find(strValueTemp); + if (tmpPos != std::string::npos) + strDVNameTemp.replace(tmpPos, strValueTemp.size(), std::to_string(i + offset)); + size_t subPos = strDANameTemp.find('['); + size_t subPos2 = strDANameTemp.find(']'); + if (subPos != std::string::npos && subPos2 != std::string::npos) + strDANameTemp.replace(subPos + 1, subPos2 - subPos - 1, std::to_string(i - strDAoffset)); + dv1->strName = strDVNameTemp; + dv1->DA = strDANameTemp; + + // BaseFlag, LimitUp, LimitDown + if (valueElem->Attribute("BaseFlag")) { + dv1->BaseFlag = valueElem->Attribute("BaseFlag"); + if (dv1->BaseFlag == "1") dt->BaseFlag1++; else dt->BaseFlag0++; + } else { + dt->BaseFlag0++; + dv1->BaseFlag = "0"; + } + dv1->LimitUp = valueElem->Attribute("LimitUp") ? valueElem->Attribute("LimitUp") : "not define"; + dv1->LimitDown = valueElem->Attribute("LimitDown") ? valueElem->Attribute("LimitDown") : "not define"; + + if (!dv1->DO.empty() && !dv1->DA.empty()) + dv1->strFullName = dv1->DO + "$" + dv1->DA; + else + dv1->strFullName = "not define"; + sq->DataValueList.push_back(dv1); + + // 调试打印谐波展开 + std::cout << "[调试] 谐波展开DataValue: " << dv1->strName << " DA=" << dv1->DA << std::endl; + } + } else { + // 非谐波数据 + CDataValue* dv2 = new CDataValue(); + dv2->strName = strDVName; + dv2->type = valueElem->Attribute("type") ? valueElem->Attribute("type") : ""; + if (valueElem->Attribute("Coefficient")) { + dv2->strCoefficient = valueElem->Attribute("Coefficient"); + dv2->fCoefficient = valueElem->FloatAttribute("Coefficient", 1.0f); + } + dv2->DO = valueElem->Attribute("DO") ? valueElem->Attribute("DO") : ""; + dv2->DA = strDAName; + + if (valueElem->Attribute("BaseFlag")) { + dv2->BaseFlag = valueElem->Attribute("BaseFlag"); + if (dv2->BaseFlag == "1") dt->BaseFlag1++; else dt->BaseFlag0++; + } else { + dt->BaseFlag0++; + dv2->BaseFlag = "0"; + } + dv2->LimitUp = valueElem->Attribute("LimitUp") ? valueElem->Attribute("LimitUp") : "not define"; + dv2->LimitDown = valueElem->Attribute("LimitDown") ? valueElem->Attribute("LimitDown") : "not define"; + + if (valueElem->Attribute("PltFlag") && std::string(valueElem->Attribute("PltFlag")) == "True") + dv2->bPlt = true; + else + dv2->bPlt = false; + if (!dv2->DO.empty() && !dv2->DA.empty()) + dv2->strFullName = dv2->DO + "$" + dv2->DA; + else + dv2->strFullName = "not define"; + sq->DataValueList.push_back(dv2); + + std::cout << "[调试] 普通DataValue: " << dv2->strName << " DA=" << dv2->DA << std::endl; + } + } + } + } + } + + // T相 + if (strPhase == "8") { + CSequence* sq = new CSequence(); + sq->strSValue = strPhase; + sq->type = seqElem->Attribute("type") ? seqElem->Attribute("type") : ""; + sq->strSeq = strPhasic[3]; + it->SequenceList.push_back(sq); + for (tinyxml2::XMLElement* valueElem = seqElem->FirstChildElement(); valueElem; valueElem = valueElem->NextSiblingElement()) { + if (std::string(valueElem->Name()) == "Value") { + CDataValue* dv2 = new CDataValue(); + dv2->strName = valueElem->Attribute("name") ? valueElem->Attribute("name") : ""; + dv2->type = valueElem->Attribute("type") ? valueElem->Attribute("type") : ""; + if (valueElem->Attribute("Coefficient")) { + dv2->strCoefficient = valueElem->Attribute("Coefficient"); + dv2->fCoefficient = valueElem->FloatAttribute("Coefficient", 1.0f); + } + dv2->DO = valueElem->Attribute("DO") ? valueElem->Attribute("DO") : ""; + dv2->DA = valueElem->Attribute("DA") ? valueElem->Attribute("DA") : ""; + + if (valueElem->Attribute("BaseFlag")) { + dv2->BaseFlag = valueElem->Attribute("BaseFlag"); + if (dv2->BaseFlag == "1") dt->BaseFlag1++; else dt->BaseFlag0++; + } else { + dt->BaseFlag0++; + dv2->BaseFlag = "0"; + } + dv2->LimitUp = valueElem->Attribute("LimitUp") ? valueElem->Attribute("LimitUp") : "not define"; + dv2->LimitDown = valueElem->Attribute("LimitDown") ? valueElem->Attribute("LimitDown") : "not define"; + if (!dv2->DO.empty() && !dv2->DA.empty()) + dv2->strFullName = dv2->DO + "$" + dv2->DA; + else + dv2->strFullName = "not define"; + sq->DataValueList.push_back(dv2); + + std::cout << "[调试] T相DataValue: " << dv2->strName << std::endl; + } + } + } + } + } + } + } + } + } + } + } + } + // RTDATASOE + else if (topic->strTopic == "RTDATASOE") { + for (tinyxml2::XMLElement* dataTypeElem = topicElem->FirstChildElement(); dataTypeElem; dataTypeElem = dataTypeElem->NextSiblingElement()) { + if (std::string(dataTypeElem->Name()) == "DataType") { + CDataType* dt = new CDataType(); + dt->iDataType = dataTypeElem->IntAttribute("value", 0); + dt->type = dataTypeElem->Attribute("type") ? dataTypeElem->Attribute("type") : ""; + topic->DataTypeList.push_back(dt); + + for (tinyxml2::XMLElement* soeElem = dataTypeElem->FirstChildElement(); soeElem; soeElem = soeElem->NextSiblingElement()) { + if (std::string(soeElem->Name()) == "SOE") { + CEventData* ed = new CEventData(); + ed->triggerFlag = soeElem->Attribute("TriggerFlag") ? soeElem->Attribute("TriggerFlag") : ""; + ed->DO = soeElem->Attribute("DO") ? soeElem->Attribute("DO") : ""; + ed->DA = soeElem->Attribute("DA") ? soeElem->Attribute("DA") : ""; + ed->type = soeElem->Attribute("type") ? soeElem->Attribute("type") : ""; + if (!ed->DO.empty() && !ed->DA.empty()) + ed->strFullName = ed->DO + "$" + ed->DA; + else + ed->strFullName = "not define"; + dt->SOEList.push_back(ed); + std::cout << "[调试] SOE事件: " << ed->strFullName << std::endl; + } + } + } + } + } + // SOEDATA + else if (topic->strTopic == "SOEDATA") { + for (tinyxml2::XMLElement* dataTypeElem = topicElem->FirstChildElement(); dataTypeElem; dataTypeElem = dataTypeElem->NextSiblingElement()) { + if (std::string(dataTypeElem->Name()) == "DataType") { + CEventData* ed = new CEventData(); + ed->triggerFlag = dataTypeElem->Attribute("name") ? dataTypeElem->Attribute("name") : ""; + ed->DO = dataTypeElem->Attribute("DO") ? dataTypeElem->Attribute("DO") : ""; + ed->DA = dataTypeElem->Attribute("DA") ? dataTypeElem->Attribute("DA") : ""; + ed->type = dataTypeElem->Attribute("type") ? dataTypeElem->Attribute("type") : ""; + if (!ed->DO.empty() && !ed->DA.empty()) + ed->strFullName = ed->DO + "$" + ed->DA; + else + ed->strFullName = "not define"; + cfg->SOEList.push_back(ed); + std::cout << "[调试] SOEDATA事件: " << ed->strFullName << std::endl; + } + } + } + } + // 其他节点 + else if (strTag == "WavePhasic") { + const char* flagAttr = topicElem->Attribute("Flag"); + if (flagAttr) + cfg->WavePhasicFlag = flagAttr; + if (!cfg->WavePhasicFlag.empty() && cfg->WavePhasicFlag == "1") { + const char* aAttr = topicElem->Attribute("A"); + if (aAttr) cfg->WavePhasicA = aAttr; + const char* bAttr = topicElem->Attribute("B"); + if (bAttr) cfg->WavePhasicB = bAttr; + const char* cAttr = topicElem->Attribute("C"); + if (cAttr) cfg->WavePhasicC = cAttr; + } + std::cout << "[调试] WavePhasic解析" << std::endl; + } else if (strTag == "UnitOfTime") { + const char* unitAttr = topicElem->Attribute("Unit"); + if (unitAttr) + cfg->UnitOfTimeUnit = unitAttr; + std::cout << "[调试] UnitOfTime解析" << std::endl; + } else if (strTag == "ValueOfTime") { + const char* unitAttr = topicElem->Attribute("Unit"); + if (unitAttr) + cfg->ValueOfTimeUnit = unitAttr; + std::cout << "[调试] ValueOfTime解析" << std::endl; + } else if (strTag == "ComtradeFile") { + const char* waveTimeFlag = topicElem->Attribute("WaveTimeFlag"); + if (waveTimeFlag) + cfg->WaveTimeFlag = waveTimeFlag; + std::cout << "[调试] ComtradeFile解析" << std::endl; + } else if (strTag == "IED") { + const char* nameAttr = topicElem->Attribute("name"); + if (nameAttr) + cfg->IEDname = nameAttr; + std::cout << "[调试] IED解析" << std::endl; + } else if (strTag == "LDevice") { + const char* prefixAttr = topicElem->Attribute("Prefix"); + if (prefixAttr) + cfg->LDevicePrefix = prefixAttr; + std::cout << "[调试] LDevice解析" << std::endl; + } + + } + return true; +} + +//清理旧的映射配置 +void clearXmlConfigAndTopicList(Xmldata* data) { + // 清空 XmlConfig + data->xmlcfg = XmlConfig(); // 通过重新赋值重置 xmlcfg + + // 清空 topicList + std::list::iterator it; + for (it = data->topicList.begin(); it != data->topicList.end(); ++it) { + delete *it; // 释放内存 + } + data->topicList.clear(); // 清空链表 +} + +//配置映射文件单个 +void Set_xml_nodeinfo_one(const std::string& dev_type) +{ + bool ret = false; + + // 假定 xmlinfo_list 是 std::map + if (xmlinfo_list.count(dev_type) && xmlinfo_list[dev_type] != nullptr) { // 已存在这个类型的节点 + if (xmlinfo_list[dev_type]->updataflag == true) { // 需要更新 + + // 删除这个点的 xmlcfg 和 topicList + clearXmlConfigAndTopicList(xmlinfo_list[dev_type]); + + ret = ParseXMLConfig2( + 0, + &(xmlinfo_list[dev_type]->xmlcfg), + &(xmlinfo_list[dev_type]->topicList), + xmlinfo_list[dev_type]->xmlbase.MODEL_ID + ); + if (!ret) { + std::cout << "!!!! this ledger xml config fail!!!!" << std::endl; + } + } + } + else { + std::cout << "xmlinfo_list not contain this devtype" << std::endl; + } + + // 处理角形 + if (isdelta_flag) { + if (xmlinfo_list2.count(dev_type) && xmlinfo_list2[dev_type] != nullptr) { // 已存在 + if (xmlinfo_list2[dev_type]->updataflag == true) { // 需要更新 + + // 删除这个点的 xmlcfg 和 topicList + clearXmlConfigAndTopicList(xmlinfo_list2[dev_type]); + + ret = ParseXMLConfig2( + 1, + &(xmlinfo_list2[dev_type]->xmlcfg), + &(xmlinfo_list2[dev_type]->topicList), + xmlinfo_list2[dev_type]->xmlbase.MODEL_ID + ); + if (!ret) { + std::cout << "!!!! this ledger xml config fail!!!!" << std::endl; + } + } + } + else { + std::cout << "xmlinfo_list2 not contain this devtype" << std::endl; + } + } +} + +//配置映射文件全部 +void Set_xml_nodeinfo() +{ + // 配置无对应 xml 文件时的默认解析配置 + if (!DEFAULT_CONFIG_FN.empty()) { //可改为在配置中读取默认映射文件名 + std::string path = "not define"; + ParseXMLConfig2(0, &xmlcfg, &topicList, path); // 调用 ParseXMLConfig() 解析 JiangSu_Config.xml 配置文件 + if (isdelta_flag) { + ParseXMLConfig2(1, &xmlcfg2, &topicList2, path); // 角型接线 + } + } + + if (!xmlinfo_list.empty()) { + std::cout << "!!!!!!!!!! xmlinfo_list.size() != 0 !!!!!!!!!!!" << std::endl; + + for (auto& pair : xmlinfo_list) { + const std::string& key = pair.first; + Xmldata* value = pair.second; + + if (value && value->updataflag) { + ParseXMLConfig2(0, &(value->xmlcfg), &(value->topicList), value->xmlbase.MODEL_ID); + } + } + + if (isdelta_flag) { + for (auto& pair : xmlinfo_list2) { + const std::string& key = pair.first; + Xmldata* value = pair.second; + + if (value && value->updataflag) { + ParseXMLConfig2(1, &(value->xmlcfg), &(value->topicList), value->xmlbase.MODEL_ID); + } + } + } + + } else { + std::cout << "!!!!!!!!!! xmlinfo_list.size() == 0 !!!!!!!!!!!" << std::endl; + } +} + + diff --git a/LFtid1056/cloudfront/code/interface.cpp b/LFtid1056/cloudfront/code/interface.cpp new file mode 100644 index 0000000..d994001 --- /dev/null +++ b/LFtid1056/cloudfront/code/interface.cpp @@ -0,0 +1,1291 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include +#include // for std::move +#include // for std::free +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "curl/curl.h" +#include "interface.h" +#include "rocketmq.h" +#include "nlohmann/json.hpp" +#include "log4.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +using json = nlohmann::json; + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +std::map xmlinfo_list;//保存所有型号对应的icd映射文件解析数据 +XmlConfig xmlcfg;//星形接线xml节点解析的数据-默认映射文件解析数据 +std::list topicList; //队列发送主题链表 + +XmlConfig xmlcfg2;//角型接线xml节点解析的数据-默认映射文件解析数据 +std::list topicList2; //角型接线发送主题链表 +std::map xmlinfo_list2;//保存所有型号角形接线对应的icd映射文件解析数据 + +//台账list +std::vector terminal_devlist; + +//台账锁 +std::mutex ledgermtx; + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////// +extern int g_front_seg_index; +extern int g_front_seg_num; +extern std::string FRONT_IP; +extern uint32_t g_node_id; + +//筛选的终端状态:数组【0,1】筛选运行和在运 +extern std::string TERMINAL_STATUS; + +//筛选的icd范围:1根据台账获取0获取全部 +extern std::string ICD_FLAG; + +//五个接口 +extern std::string WEB_FILEUPLOAD; +extern std::string WEB_FILEDOWNLOAD; +extern std::string WEB_ICD; +extern std::string WEB_DEVICE; +extern std::string WEB_EVENT; + +//角型接线标志 +extern int isdelta_flag; + +//单个进程的台账数量 +extern int IED_COUNT; + +/////////////////////////////////////////////////////////////////////////////////////////////////////// + +void handleCommentResponse(const std::string& response); + +//////////////////////////////////////////////////////////////////////////////////////////////////////// + +extern void execute_bash(std::string fun,int process_num,std::string type); + +////////////////////////////////////////////////////////////////////////////////////////////////////////通用curl请求接口 + +size_t req_reply_web(void* ptr, size_t size, size_t nmemb, void* stream) +{ + std::string* str = (std::string*)stream; + (*str).append((char*)ptr, size * nmemb); + return size * nmemb; +} + +void SendJsonAPI_web(const std::string& strUrl, //接口路径 + const std::string& code, //上传文件用 + const std::string& json, //请求json + std::string& responseStr) //响应 +{ + CURL* curl = curl_easy_init(); + CURLcode res; + + responseStr.clear(); + + if (curl) { + // 使用 std::string 拼接完整 URL + std::string fullUrl = strUrl + "?" + code; + std::cout << ">>>json " << fullUrl << std::endl; + + curl_easy_setopt(curl, CURLOPT_URL, fullUrl.c_str()); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, req_reply_web); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &responseStr); + curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10); + curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10); + + if (!json.empty()) { + curl_easy_setopt(curl, CURLOPT_POST, 1L); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json.c_str()); + } + + struct curl_slist* headers = nullptr; + headers = curl_slist_append(headers, "Content-Type: application/json"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + + res = curl_easy_perform(curl); + if (res != CURLE_OK) { + std::cerr << "web failed, res code: " << curl_easy_strerror(res) << std::endl; + } else { + std::cout << ">>> web return str: " << responseStr << std::endl; + } + + curl_slist_free_all(headers); + curl_easy_cleanup(curl); + } else { + std::cerr << ">>> web curl init failed" << std::endl; + } +} + + +////////////////////////////////////////////////////////////////////////////////////////////////////////上传文件接口 + +//处理文件上传响应 +void handleUploadResponse(const std::string& response, std::string& wavepath) { + + using nlohmann::json; //把 nlohmann::json 这个名字带到当前作用域 + + // 解析 JSON 响应 + json json_data; + try { + json_data = json::parse(response); + } + catch (const json::parse_error& e) { + std::cerr << "Error parsing response: " << e.what() << std::endl; + DIY_ERRORLOG("process", "【ERROR】前置上传暂态录波文件失败,web返回的消息不是json格式"); + return; + } + + // 提取字段 + if (!json_data.contains("code") || !json_data.contains("data")) { + std::cerr << "Error: Missing expected fields in JSON response." << std::endl; + DIY_ERRORLOG("process", "【ERROR】前置上传暂态录波文件失败,web返回的消息没有远端文件名"); + return; + } + + std::string code = json_data["code"].get(); + std::cout << "Response Code: " << code << std::endl; + + std::string msg = json_data.value("msg", std::string{"not found"}); + std::cout << "Message: " << msg << std::endl; + + auto& data = json_data["data"]; + if (!data.contains("name") || !data.contains("fileName") || !data.contains("url")) { + std::cerr << "Error: Missing expected fields in JSON data object." << std::endl; + DIY_ERRORLOG("process", "【ERROR】前置上传暂态录波文件失败,web返回的消息没有远端文件名"); + return; + } + + std::string name = data["name"].get(); + std::string fileName = data["fileName"].get(); + std::string url = data["url"].get(); + + // 输出信息 + std::cout << "File Path: " << name << std::endl; + std::cout << "Uploaded File Name: " << fileName << std::endl; + std::cout << "File URL: " << url << std::endl; + + // 找到最后一个 '.' + size_t pos = fileName.find_last_of('.'); + std::string nameWithoutExt; + if (pos != std::string::npos) { + // 截取去掉后缀的部分 + nameWithoutExt = fileName.substr(0, pos); + } else { + // 如果没有后缀,直接使用原文件名 + nameWithoutExt = fileName; + } + + // 拷贝到 wavepath + wavepath = nameWithoutExt; + + std::cout << "wavepath: " << wavepath << std::endl; + DIY_INFOLOG("process", "【NORMAL】前置上传暂态录波文件成功,远端文件名:%s", wavepath.c_str()); +} + +//上传文件 +void SendFileWeb(const std::string& strUrl, const std::string& localpath, const std::string& cloudpath, std::string& wavepath) { + // 初始化 curl + CURL* curl = curl_easy_init(); + if (curl) { + // 设置请求 URL 和 POST 请求 + curl_easy_setopt(curl, CURLOPT_URL, strUrl.c_str()); + curl_easy_setopt(curl, CURLOPT_POST, 1); + + // 创建表单数据 + curl_httppost* formpost = nullptr; + curl_httppost* lastptr = nullptr; + + if (access(localpath.c_str(), F_OK) != 0) { + std::cerr << "Local file does not exist: " << localpath << std::endl; + return; + } + + // 添加文件字段,直接从本地路径读取文件内容 + curl_formadd(&formpost, &lastptr, + CURLFORM_COPYNAME, "file", + CURLFORM_FILE, localpath, + CURLFORM_END); + + // 添加 `path` 字段 + curl_formadd(&formpost, &lastptr, + CURLFORM_COPYNAME, "path", + CURLFORM_COPYCONTENTS, cloudpath, + CURLFORM_END); + + // 添加 `isReserveName` 字段 + curl_formadd(&formpost, &lastptr, + CURLFORM_COPYNAME, "isReserveName", + CURLFORM_COPYCONTENTS, "true", + CURLFORM_END); + + // 设置表单数据到请求 + curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost); + + // 设置头信息 + struct curl_slist* header_list = nullptr; + header_list = curl_slist_append(header_list, "Content-Type: multipart/form-data"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header_list); + + // 设置超时时间 + curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10); + curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10); + + // 设置写入响应数据的函数 + std::string resPost0; + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)&resPost0); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, req_reply_web); + + // 执行请求 + CURLcode res = curl_easy_perform(curl); + if (res != CURLE_OK) { + std::cerr << "http web failed: " << curl_easy_strerror(res) << std::endl; + DIY_ERRORLOG("process","【ERROR】前置上传暂态录波文件 %s 失败,请检查文件上传接口配置",localpath); + } else { + std::cout << "http web success, response: " << resPost0 << std::endl; + handleUploadResponse(resPost0, wavepath); // 处理响应 + } + + // 清理 + curl_formfree(formpost); // 释放表单数据 + curl_slist_free_all(header_list); // 释放头部列表 + curl_easy_cleanup(curl); + } else { + std::cerr << ">>> curl init failed" << std::endl; + } +} + +//上传暂态文件 +void SOEFileWeb(std::string& localpath,std::string& cloudpath, std::string& wavepath) +{ + SendFileWeb(WEB_FILEUPLOAD,localpath,cloudpath,wavepath); +} + +//上传文件测试函数 +void Fileupload_test() +{ + std::string localpath = FRONT_PATH + "/bin/file_test.txt"; + std::string cloudpath = "/fileuploadtest/"; + std::string wavepath; + SOEFileWeb(localpath,cloudpath,wavepath); + std::cout << "wavepath:" << wavepath << std::endl; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////映射文件下载接口 +void download_xml_for_icd(const std::string& MODEL_ID, + const std::string& TMNL_TYPE, + const std::string& FILE_PATH, + const std::string& FILE_NAME, + const std::string& time) +{ + if (MODEL_ID.empty() || TMNL_TYPE.empty() || FILE_PATH.empty() || FILE_NAME.empty()) + { + std::cerr << "Function download_xml_for_icd Error, param empty!" << std::endl; + return; + } + std::cout << "Function download_xml_for_icd Start!" << std::endl; + + std::string id(MODEL_ID); + std::string type(TMNL_TYPE); + std::string filepath(FILE_PATH); + std::string name(FILE_NAME); + std::string currentTime(time); + + // 调试用 + std::cout << "terminal type:" << TMNL_TYPE << std::endl; + + auto it = xmlinfo_list.find(type); + if (it == xmlinfo_list.end()) // 在终端类型列表中没查到 + { + Xmldata* config = new Xmldata(); // 没找到就插个新的终端类型到列表中 + xmlinfo_list[type] = config; //在这里将 config 转成一个Xmldata 的右值引用,触发 unique_ptr 的移动赋值 + + // 调试 + std::cout << "xmlinfo_list insert type:" << type << std::endl; + } + else // 查到就更新覆盖 + { + // 调试 + std::cout << "xmlinfo_list type contain:" << type << std::endl; + + if (xmlinfo_list[type]->xmlbase.updatetime == currentTime) { // 终端型号更新标志,如果新增的型号错误,导致实际用的映射文件不一样,或者覆盖了原来的映射文件这里可能出问题。数据库在录入型号和映射文件时要注意 + xmlinfo_list[type]->updataflag = false; // 时间值一样说明是没有更新,当前业务中不包含时间值,所以每次都会更新 + } + else { + xmlinfo_list[type]->updataflag = true; + } + } + + xmlinfo_list[type]->xmlbase.MODEL_ID = id; + xmlinfo_list[type]->xmlbase.TMNL_TYPE = type; + xmlinfo_list[type]->xmlbase.FILE_PATH = filepath; + xmlinfo_list[type]->xmlbase.FILE_NAME = name; + + xmlinfo_list[type]->xmlbase.updatetime = currentTime; + + //调试 + std::cout << "##################################isdelta_flag is " << isdelta_flag << std::endl; + + if (isdelta_flag) { + std::cout << "xmllist2 create" << std::endl; + + auto it2 = xmlinfo_list2.find(type); + if (it2 == xmlinfo_list2.end()) + { + Xmldata* config = new Xmldata(); + xmlinfo_list2[type] = config; + } + else + { + if (xmlinfo_list2[type]->xmlbase.updatetime == currentTime) { + xmlinfo_list2[type]->updataflag = false; + } + else { + xmlinfo_list2[type]->updataflag = true; + } + } + xmlinfo_list2[type]->xmlbase.MODEL_ID = id; + xmlinfo_list2[type]->xmlbase.TMNL_TYPE = type; + xmlinfo_list2[type]->xmlbase.FILE_PATH = filepath; + xmlinfo_list2[type]->xmlbase.FILE_NAME = name; + xmlinfo_list2[type]->xmlbase.updatetime = currentTime; + } + + //下载文件 + std::string remote_file_name = name; + std::string save_name = FRONT_PATH + "/dat/" + id + ".xml"; // 本地保存路径 + + std::cout << "remote file name:" << remote_file_name << "local save name:" << save_name << std::endl; + + // mq日志 + DIY_WARNLOG("process","【WARN】前置获取到终端类型%s,该终端类型对应的映射文件为%s,映射文件将下载并保存在本地为%s",TMNL_TYPE,FILE_PATH,save_name); + + std::string fileContent; + std::string fullPath = std::string("filePath=") + filepath; //填写远端路径作为入参 + + // 调试 + std::cout << "input fullpath" << fullPath << std::endl; + + SendJsonAPI_web(WEB_FILEDOWNLOAD, fullPath.c_str(), "", fileContent); + + if (!fileContent.empty()) { + // 创建并打开文件 + std::ofstream outFile(save_name, std::ios::out); // 文本模式打开 + if (outFile.is_open()) { + + // 将文件流写入文件 + outFile.write(fileContent.c_str(), fileContent.size()); + outFile.close(); + + std::cout << "File saved successfully!" << std::endl; + DIY_WARNLOG("process","【WARN】前置下载映射文件%s成功",save_name); + } else { + std::cerr << "Error: Unable to open file for writing." << std::endl; + DIY_ERRORLOG("process","【ERROR】前置写入本地映射文件%s失败",save_name); + } + } else { + std::cerr << "Error: Unable to download file." << std::endl; + DIY_ERRORLOG("process","【ERROR】前置调用文件下载接口下载远端文件文件%s失败",FILE_PATH); + } +} + +////////////////////////////////////////////////////////////////////////////////////////////////////台账接口 + +//读最新本地台账 +std::string read_latest_ledger_file() { + const char* dir = std::string(FRONT_PATH + "/dat/ledger").c_str(); + DIR* dp = opendir(dir); + if (!dp) return ""; + + struct dirent* entry; + std::string latest_file; + time_t latest_time = 0; + + while ((entry = readdir(dp)) != nullptr) { + if (strstr(entry->d_name, "_ledger.txt")) { + std::string filepath = std::string(dir) + "/" + entry->d_name; + std::cout << "localledger filepath" << filepath << std::endl; + struct stat st; + if (stat(filepath.c_str(), &st) == 0 && st.st_mtime > latest_time) { + latest_time = st.st_mtime; + latest_file = filepath; + } + } + } + closedir(dp); + + if (!latest_file.empty()) { + std::ifstream inFile(latest_file); + if (inFile) { + std::string content((std::istreambuf_iterator(inFile)), + std::istreambuf_iterator()); + return content; + } + } + + return ""; +} + +//保存台账到本地 +void save_ledger_json(const std::string& content) { + if (content.empty()) return; + + const std::string dir = FRONT_PATH + "/dat/ledger"; + + // 确保目录存在 + struct stat st; + if (stat(dir.c_str(), &st) != 0) { + mkdir(FRONT_PATH.c_str(), 0755); // 确保上级目录存在(可选) + mkdir(std::string(FRONT_PATH + "/dat").c_str(), 0755); + mkdir(dir.c_str(), 0755); + } + + // 删除已有 *_ledger.txt 文件 + DIR* dp = opendir(dir.c_str()); + if (dp) { + struct dirent* entry; + while ((entry = readdir(dp)) != nullptr) { + if (strstr(entry->d_name, "_ledger.txt")) { + std::string old_file_path = dir + "/" + entry->d_name; + std::remove(old_file_path.c_str()); + } + } + closedir(dp); + } + + // 当前时间格式化为 yyyyMMddHHmmss + char time_buf[32]; + time_t now = time(nullptr); + struct tm* tm_info = localtime(&now); + strftime(time_buf, sizeof(time_buf), "%Y%m%d%H%M%S", tm_info); + + // 构造文件路径 + std::string filepath = dir + "/" + time_buf + "_ledger.txt"; + + // 写入文件 + std::ofstream outFile(filepath); + if (outFile) { + outFile << content; + outFile.close(); + } else { + std::cerr << "Error: Unable to open ledger file for writing: " << filepath << std::endl; + } +} + +//找看门狗中最大的进程号 +int get_max_stat_data_index(const std::string& filepath) { + std::ifstream file(filepath.c_str()); + if (!file.is_open()) { + std::cerr << "Failed to open file: " << filepath << std::endl; + return -1; + } + + std::string line; + int max_value = -1; + + while (std::getline(file, line)) { + // 查找符合要求的行 + if (line.find("pt61850netd_pqfe -d cfg_stat_data -s") != std::string::npos) { + // 找到 -s 参数位置 + std::size_t pos = line.find("-s"); + if (pos != std::string::npos) { + std::istringstream iss(line.substr(pos + 2)); + std::string token; + iss >> token; + + // 格式应该是 1_2 或类似格式 + std::size_t under_pos = token.find('_'); + if (under_pos != std::string::npos) { + std::string first = token.substr(0, under_pos); + std::string second = token.substr(under_pos + 1); + + int val1 = std::atoi(first.c_str()); + int val2 = std::atoi(second.c_str()); + + if (val1 > max_value) max_value = val1; + if (val2 > max_value) max_value = val2; + } + } + } + } + + file.close(); + return max_value; +} + +//台账信息获取 +int terminal_ledger_web(std::map& terminal_dev_map, + const std::string& inputstring) +{ + if (inputstring.empty()) { + std::cerr << "Error: inputstring is empty\n"; + DIY_ERRORLOG("process","【ERROR】前置的%s%d号进程调用web台账接口的入参为空", get_front_msg_from_subdir(), g_front_seg_index); + return 1; + } + + std::string inputparm = inputstring; + std::string responseStr; + int retry = 0; + nlohmann::json json_data; + + // 1. 拉取并解析 JSON,最多 3 次 + while (true) { + SendJsonAPI_web(WEB_DEVICE, "", inputparm, responseStr); + if (!responseStr.empty()) { + try { + json_data = nlohmann::json::parse(responseStr); + if (json_data.contains("data") && json_data["data"].is_array() && !json_data["data"].empty()) { + break; + } + std::cerr << "data 无效或为空数组,重试\n"; + DIY_ERRORLOG("process","【ERROR】前置从web接口中获取的台账信息为空或者无效信息无法解析,请核对前置使用的入参信息:%s",inputparm.c_str()); + } catch (const nlohmann::json::parse_error& e) { + std::cerr << "parse error: " << e.what() << ", retrying...\n"; + } + } else { + std::cerr << "HTTP response NULL, retrying...\n"; + } + + if (++retry > 3) { + std::cerr << "web error after 3 retry, fallback to local file\n"; + std::string ledger = read_latest_ledger_file(); + if (!ledger.empty()) { + try { + json_data = nlohmann::json::parse(ledger); + if (json_data.contains("data") && json_data["data"].is_array() && !json_data["data"].empty()) { + break; + } + DIY_ERRORLOG("process", "【ERROR】前置从本地台账中获取的台账信息为空或者无效信息无法解析,请核对前置使用的入参信息:%s",inputparm.c_str()); + } catch (const nlohmann::json::parse_error& e) { + std::cerr << "local parse error: " << e.what() << "\n"; + } + } + std::cerr << "still failed, sleep 5 min then retry...\n"; + std::this_thread::sleep_for(std::chrono::minutes(5)); + retry = 0; + continue; + } + } + + // 2. 安全读取 code/msg + std::string code = json_data.value("code", "not found"); + std::string msg = json_data.value("msg", "not found"); + std::cout << "code: " << code << "\n"; + std::cout << "msg : " << msg << "\n"; + + // 3. 逐条解析 data + const auto& data = json_data["data"]; + for (size_t i = 0; i < data.size(); ++i) { + const auto& item = data[i]; + if (!item.is_object()) { + std::cerr << "Warning: Invalid item at index " << i << "\n"; + continue; + } + terminal_dev dev; // 不用 new + + auto safe_str = [](const nlohmann::json& j, const char* key) -> std::string { + if (!j.contains(key) || j[key].is_null()) return "N/A"; + if (j[key].is_string()) return j[key].get(); + if (j[key].is_number_integer()) return std::to_string(j[key].get()); + if (j[key].is_number_unsigned()) return std::to_string(j[key].get()); + if (j[key].is_number_float()) return std::to_string(j[key].get()); + return "N/A"; + }; + dev.terminal_id = safe_str(item, "id"); + dev.addr_str = safe_str(item, "ip"); + dev.terminal_code = safe_str(item, "name"); + dev.org_name = safe_str(item, "org_name"); + dev.maint_name = safe_str(item, "maint_name"); + dev.station_name = safe_str(item, "stationName"); + dev.tmnl_factory = safe_str(item, "manufacturer"); + dev.tmnl_status = safe_str(item, "status"); + dev.dev_type = safe_str(item, "devType"); + dev.dev_key = safe_str(item, "devKey"); + dev.dev_series = safe_str(item, "series"); + dev.port = safe_str(item, "port"); + dev.timestamp = safe_str(item, "updateTime"); + dev.processNo = safe_str(item, "processNo"); + dev.maxProcessNum = safe_str(item, "maxProcessNum"); + + if (item.contains("monitorData") && item["monitorData"].is_array()) { + for (auto& mon : item["monitorData"]) { + if (dev.line.size() >= 10) break; + ledger_monitor m; + m.monitor_id = safe_str(mon, "id"); + m.terminal_code = safe_str(mon, "terminal_code"); + m.monitor_name = safe_str(mon, "name"); + m.logical_device_seq = safe_str(mon, "lineNo"); + m.voltage_level = safe_str(mon, "voltageLevel"); + m.terminal_connect = safe_str(mon, "ptType"); + m.timestamp = safe_str(mon, "updateTime"); + m.status = safe_str(mon, "status"); + dev.line.push_back(m); + } + } + + // 插入 map(去重 + 仅本进程号匹配时插入) + // dev 已经是对象类型 + std::string key = dev.terminal_id; + auto it = terminal_dev_map.find(key); + bool match = false; + try { + match = (std::stoi(dev.processNo) == g_front_seg_index || g_front_seg_index == 0); + } catch (...) { + std::cerr << "processNo parse error for terminal: " << dev.terminal_id << "\n"; + } + if (it != terminal_dev_map.end()) { + std::cerr << "Duplicate terminal_id: " << key << std::endl; + terminal_dev_map.erase(it); // 覆盖前先提示并擦除 + if (match) { + std::cout << "remove duplicate terminal ledger and insert lastest terminal ledger id:" << key << std::endl; + terminal_dev_map[key] = dev; + } + } else { + if (match) { + std::cout << "process num match, terminal ledger insert id:" << key << std::endl; + terminal_dev_map[key] = dev; + } + } + } + + // 5. 主进程保存台账 + if (g_node_id == STAT_DATA_BASE_NODE_ID && g_front_seg_index == 1) { + save_ledger_json(responseStr); + } + + return 0; +} + +//台账信息写入全局 +int parse_device_cfg_web() +{ + std::cout << "parse_device_cfg_web" << std::endl; + + // 1. 构造入参 JSON + std::string input_jstr = "{"; + input_jstr += "\"ip\":\"" + FRONT_IP + "\","; + input_jstr += "\"runFlag\":" + TERMINAL_STATUS; + input_jstr += "}"; + + std::cout << "input_jstr: " << input_jstr << std::endl; + DIY_DEBUGLOG("process","【DEBUG】前置的%s%d号进程调用web接口获取台账使用的请求输入为:%s",get_front_msg_from_subdir(), g_front_seg_index, input_jstr.c_str()); + + // 2. 调用接口 + std::map terminal_dev_map; + if (terminal_ledger_web(terminal_dev_map, input_jstr)) { + return 1; // 入参为空或接口失败 + } + + // 3. 调试打印 + printTerminalDevMap(terminal_dev_map); + + // 4. 看门狗配置校验(仅主进程稳态) + if (g_node_id == STAT_DATA_BASE_NODE_ID && g_front_seg_index == 1) { + int max_index = get_max_stat_data_index(FRONT_PATH + "/etc/runtime.cf"); + std::cout << "max_index = " << max_index << std::endl; + + int max_process_num = 0; + auto it = terminal_dev_map.begin(); + if (it != terminal_dev_map.end()) { + const terminal_dev& dev = it->second; + max_process_num = std::atoi(dev.maxProcessNum.c_str()); + std::cout << "maxProcessNum = " << max_process_num << std::endl; + } else { + std::cout << "terminal_dev_map is empty." << std::endl; + } + + if (max_process_num != max_index) { + if (max_process_num >= 1 && max_process_num <= 9) { + DIY_WARNLOG("process", "【WARN】前置比对台账获取的进程数:%d和本地配置的进程数:%d,不匹配,按照台账进程数重置前置的进程数量",max_process_num, max_index); + execute_bash("reset", max_process_num, "all"); + } else { + DIY_ERRORLOG("process","【ERROR】前置从台账获取的进程数:%d不符合范围1~9,按照本地配置进程数启动进程",max_process_num); + } + } + } + + // 5. 台账数量与配置比对 + int count_cfg = static_cast(terminal_dev_map.size()); + std::cout << "terminal_ledger_num: " << count_cfg << std::endl; + DIY_DEBUGLOG("process", "【DEBUG】前置的%s%d号进程调用获取到的台账的数量为:%d",get_front_msg_from_subdir(), g_front_seg_index, count_cfg); + + if (IED_COUNT < count_cfg) { + std::cout << "!!!!!!!!!!single process can not add any ledger unless reboot!!!!!!!" << std::endl; + DIY_WARNLOG("process","【WARN】前置的%s%d号进程获取到的台账的数量大于配置文件中给单个进程配置的台账数量:%d,这个进程将按照获取到的台账的数量来创建台账空间,这个进程不能直接通过台账添加来新增台账,只能通过重启进程或者先删除已有台账再添加台账的方式来添加新台账",get_front_msg_from_subdir(), g_front_seg_index, IED_COUNT); + } else { + DIY_INFOLOG("process","【NORMAL】前置的%s%d号进程根据配置文件中给单个进程配置的台账数量:%d来创建台账空间",get_front_msg_from_subdir(), g_front_seg_index, IED_COUNT); + } + + ///////////////////////////////////////////////////////////////////////////////用例这里将局部的map拷贝到全局map,后续根据协议台账修改 + // 先清空全局 container,再逐个拷贝 map 中的 terminal_dev + terminal_devlist.clear(); + for (const auto& kv : terminal_dev_map) { + terminal_devlist.push_back(kv.second); // kv.second 是对象,不用判断指针 + } + + // 判断监测点接线类型 + for (auto& dev : terminal_devlist) { + for (auto& mon : dev.line) { + if (!mon.terminal_connect.empty() && mon.terminal_connect != "0") { + isdelta_flag = 1; + std::cout << "monitor_id " << mon.monitor_id<< " v_wiring_type: " << mon.terminal_connect << " is delta wiring: " << isdelta_flag << std::endl; + DIY_WARNLOG("process","【WARN】前置连接的监测点%s是角形接线,对应终端为%s 终端类型是%s",mon.monitor_id.c_str(),dev.terminal_id.c_str(),dev.dev_type.c_str()); + } + } + } + + terminal_dev_map.clear(); + + return 0; +} + +////////////////////////////////////////////////////////////////////////////////////////////////////icd接口 + +int parse_model_web(std::map* icd_model_map, + const std::string& inputstr) +{ + // 1. 清理调用方 map 中的旧数据 + for (auto& kv : *icd_model_map) { + delete kv.second; + } + icd_model_map->clear(); + + // 2. 准备请求参数 + std::string inputparm = inputstr; // 装置型号列表,格式 ["型号1","型号2",...] 或 [] + + // 3. 调用接口并重试解析 JSON(最多 3 次) + std::string responseStr; + nlohmann::json root; + bool parsed = false; + const int maxRetry = 3; + for (int attempt = 1; attempt <= maxRetry; ++attempt) { + + // 发起 HTTP 请求 + SendJsonAPI_web(WEB_ICD, "", inputparm, responseStr); + + if (responseStr.empty()) { + std::cerr << "Attempt " << attempt << ": received NULL response, retrying...\n"; + continue; + } + + std::cout << "Attempt " << attempt << " icd responseStr: " << responseStr << "\n"; + + // 尝试解析 + try { + root = nlohmann::json::parse(responseStr); + parsed = true; + break; + } + catch (const nlohmann::json::parse_error& e) { + std::cerr << "Attempt " << attempt << " parse error: " << e.what() << ", retrying...\n"; + } + } + + // 4. 如果解析仍失败,返回错误 + if (!parsed) { + std::cerr << "Error: failed to retrieve or parse JSON after " << maxRetry << " attempts\n"; + return 1; + } + + // 5. 打印 code / msg + std::string code = root.value("code", "not found"); + std::string msg = root.value("msg", "not found"); + std::cout << "code: " << code << "\n"; + std::cout << "msg : " << msg << "\n"; + + // 6. 处理 data 数组,将每个条目填充到调用方的 map + if (root.contains("data") && root["data"].is_array()) { + for (auto& item : root["data"]) { + auto model = new icd_model(); + + if (item.contains("id") && item["id"].is_string()) + model->model_id = item["id"].get(); + if (item.contains("devType") && item["devType"].is_string()) + model->tmnl_type = item["devType"].get(); + if (item.contains("devTypeId") && item["devTypeId"].is_string()) + model->tmnl_type_id = item["devTypeId"].get(); + if (item.contains("devFactory") && item["devFactory"].is_string()) + model->tmnl_factory = item["devFactory"].get(); + if (item.contains("fileName") && item["fileName"].is_string()) + model->file_name = item["fileName"].get(); + if (item.contains("filePath") && item["filePath"].is_string()) + model->file_path = item["filePath"].get(); + if (item.contains("updateTime") && item["updateTime"].is_string()) + model->updatetime = item["updateTime"].get(); + + // 只有当 model_id 不为空时才插入 + if (!model->model_id.empty()) { + (*icd_model_map)[model->model_id] = model; + } else { + delete model; + } + } + } + + return 0; +} + +int parse_model_cfg_web() +{ + // 1. 根据全局终端列表去重 + std::set devTypes; + for (auto& dev : terminal_devlist) { + if (!dev.dev_type.empty()) { + devTypes.insert(dev.dev_type); + } + } + + std::cout << "终端总数: " << terminal_devlist.size() << "\n"; + std::cout << "不同的 dev_type 个数: " << devTypes.size() << "\n"; + for (auto& t : devTypes) { + std::cout << " - " << t << "\n"; + } + + // 2. 构造 JSON 参数 + std::string input_jstr; + if (ICD_FLAG == "1") { + input_jstr = "["; + bool first = true; + for (auto& t : devTypes) { + if (!first) input_jstr += ","; + first = false; + input_jstr += "\"" + t + "\""; + } + input_jstr += "]"; + } else { + input_jstr = "[]"; + } + std::cout << "input_jstr: " << input_jstr << "\n"; + + // 3. 调用接口 + std::map icd_model_map; + if (parse_model_web(&icd_model_map, input_jstr)) { + DIY_ERRORLOG("process", "【ERROR】前置的%s%d号进程 icd模型接口异常,将使用默认的icd模型,请检查接口配置",get_front_msg_from_subdir(), g_front_seg_index); + // 确保释放 map + for (auto& kv : icd_model_map) delete kv.second; + return 0; + } + + // 4. 遍历并下载 + try { + for (auto& kv : icd_model_map) { + icd_model* mdl = kv.second; + if (!mdl) continue; + + std::cout << "model_id : " << mdl->model_id << "\n"; + std::cout << "tmnl_type : " << mdl->tmnl_type << "\n"; + std::cout << "file_path : " << mdl->file_path << "\n"; + std::cout << "file_name : " << mdl->file_name << "\n"; + std::cout << "timestamp : " << mdl->updatetime << "\n"; + + download_xml_for_icd( + mdl->model_id, + mdl->tmnl_type, + mdl->file_path, + mdl->file_name, + mdl->updatetime + ); + } + } + catch (const std::exception& e) { + std::cout << "icd model error, ERROR code=" << e.what() << std::endl; + // 释放 map + for (auto& kv : icd_model_map) delete kv.second; + return 1; + } + + // 5. 释放所有 icd_model 对象 + for (auto& kv : icd_model_map) { + delete kv.second; + } + return 0; +} + +////////////////////////////////////////////////////////////////////////////////////////////////////icd接口台账更新 +std::string parse_model_cfg_web_one(const std::string& terminal_type) +{ + // 本地 map,用完后我们会把里面的指针 delete + std::map icd_model_map; + + // 1. 构造 JSON 数组:["terminal_type"] + std::string input_jstr = "[\"" + terminal_type + "\"]"; + std::cout << "input_jstr: " << input_jstr << std::endl; + + // 2. 拉取并解析 + if (parse_model_web(&icd_model_map, input_jstr) != 0) { + std::cerr << "parse_model_web failed for type: " << terminal_type << std::endl; + DIY_ERRORLOG("process","【ERROR】前置的%s%d号进程 icd模型接口异常,将使用默认的icd模型,请检查接口配置",get_front_msg_from_subdir(), g_front_seg_index); + // 清理(即使 map 为空,也安全) + for (auto& kv : icd_model_map) delete kv.second; + return ""; + } + + std::string ret_model_id; + + // 3. 找到第一个模型,打印并下载 + if (icd_model_map.empty()) { + std::cerr << "Warning: no ICD model returned for type: " << terminal_type << std::endl; + } + else { + auto& kv = *icd_model_map.begin(); + icd_model* mdl = kv.second; + if (mdl) { + ret_model_id = mdl->model_id; + std::cout << "model_id : " << mdl->model_id << std::endl; + std::cout << "tmnl_type : " << mdl->tmnl_type << std::endl; + std::cout << "file_path : " << mdl->file_path << std::endl; + std::cout << "file_name : " << mdl->file_name << std::endl; + std::cout << "updatetime : " << mdl->updatetime << std::endl; + + try { + download_xml_for_icd( + mdl->model_id, + mdl->tmnl_type, + mdl->file_path, + mdl->file_name, + mdl->updatetime + ); + } catch (const std::exception& e) { + std::cerr << "download_xml_for_icd exception: " << e.what() << std::endl; + } + } + } + + // 4. 释放所有 new 出来的 icd_model + for (auto& kv : icd_model_map) { + delete kv.second; + } + icd_model_map.clear(); + + return ret_model_id; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////暂态接口 +std::string FormatTimeForFilename(const std::string& timeStr) { + std::string result; + for (char c : timeStr) { + if (isdigit(c)) { + result += c; + } + } + return result; +} + +// 将 JSON 字符串写入指定文件(C++11 版本,使用 std::string) +static void writeJsonToFile(const std::string& filePath, const std::string& jsonString) +{ + FILE* fp = fopen(filePath.c_str(), "w"); + if (!fp) { + DIY_ERRORLOG("process", "【ERROR】无法将暂态事件写入本地缓存"); + std::cerr << "Failed to write in file : " << filePath << std::endl; + return; + } + fprintf(fp, "%s", jsonString.c_str()); + fclose(fp); +} + +// 获取指定目录下所有文件的信息(文件名、修改时间、大小),以便后续做删除或判断文件总大小 +struct FileInfo { + std::string fileName; + time_t modTime; // 上次修改时间 + long long fileSize; // 文件大小 +}; + +// 扫描目录,获取该目录下所有普通文件的信息 +static void getDirectoryFilesInfo(const std::string& dirPath, std::vector& fileList) +{ + DIR* dp = opendir(dirPath.c_str()); + if (!dp) { + std::cerr << "Failed to open directory: " << dirPath << " - " << strerror(errno) << std::endl; + return; + } + + struct dirent* entry = nullptr; + while ((entry = readdir(dp)) != nullptr) { + // 跳过 . 和 .. + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { + continue; + } + + // 拼接完整路径 + std::string fullPath = dirPath; + if (fullPath.back() != '/' && fullPath.back() != '\\') { + fullPath += '/'; + } + fullPath += entry->d_name; + + // 获取文件信息 + struct stat st; + if (stat(fullPath.c_str(), &st) == 0) { + if (S_ISREG(st.st_mode)) { + FileInfo fi; + fi.fileName = fullPath; + fi.modTime = st.st_mtime; + fi.fileSize = static_cast(st.st_size); + fileList.push_back(fi); + } + } else { + std::cerr << "Failed to stat file: " << fullPath << " - " << strerror(errno) << std::endl; + } + } + + closedir(dp); +} + +// 检查 qvvr 目录下文件总大小,若超过 10M 则删除最老的一个文件 +// 注意:该逻辑严格按照你需求“只删一个最老的文件”来实现,而不是循环删到小于10M +static void checkAndRemoveOldestIfNeeded(const std::string& dirPath, long long maxBytes) +{ + // 1) 判断目录是否存在,不存在则尝试创建 + struct stat st; + if (stat(dirPath.c_str(), &st) == -1) { + if (errno == ENOENT) { + // 目录不存在,尝试创建(只创建最后一级,不递归) + if (mkdir(dirPath.c_str(), 0777) != 0) { + std::cerr << "Failed to create directory: " << dirPath << std::endl; + return; + } + } else { + std::cerr << "stat error: " << strerror(errno) << std::endl; + return; + } + } else if (!S_ISDIR(st.st_mode)) { + std::cerr << dirPath << " exists but is not a directory." << std::endl; + return; + } + + // 2) 获取目录下所有文件信息 + std::vector fileList; + getDirectoryFilesInfo(dirPath, fileList); + + // 3) 计算总大小 + long long totalSize = 0; + for (const auto& file : fileList) { + totalSize += file.fileSize; + } + + // 4) 如果超过阈值,则删除最老的文件 + if (totalSize > maxBytes && !fileList.empty()) { + std::sort(fileList.begin(), fileList.end(), [](const FileInfo& a, const FileInfo& b) { + return a.modTime < b.modTime; + }); + + std::remove(fileList[0].fileName.c_str()); + } +} + +// 扫描目录下的离线文件,依次读取并发送;若发送成功则删除该文件,发送不成功则保留 +static void scanAndResendOfflineFiles(const std::string& dirPath) +{ + // 获取目录下所有文件信息 + std::vector fileList; + std::cout << "getDirectoryFilesInfo" << std::endl; + getDirectoryFilesInfo(dirPath, fileList); + + std::cout << "send every file" << std::endl; + + for (const auto& file : fileList) { + // 读取 JSON 文件内容 + std::ifstream inFile(file.fileName.c_str()); + if (!inFile) { + DIY_ERRORLOG("process", "【ERROR】无法打开本地缓存的暂态事件"); + std::cerr << "fail to open existing file: " << file.fileName << std::endl; + continue; + } + + std::string jsonContent((std::istreambuf_iterator(inFile)), + std::istreambuf_iterator()); + inFile.close(); + + std::cout << "send jsonContent: " << jsonContent << std::endl; + + // 尝试发送 + std::string response; + SendJsonAPI_web(WEB_EVENT, "", jsonContent, response); + + if (!response.empty()) { + try { + json j_r = json::parse(response); + + DIY_WARNLOG("process", "【WARN】前置重发暂态事件成功"); + std::cout << "old file send success, remove it" << std::endl; + + std::remove(file.fileName.c_str()); + } catch (...) { + std::cout << "old file send fail (response parse failed)" << std::endl; + + DIY_WARNLOG("process", "【WARN】前置重发暂态事件失败"); + handleCommentResponse(response); // 仍然处理文本响应 + } + } else { + std::cout << "old file send fail (no response)" << std::endl; + // 不删除文件,等待下次重发 + } + } +} + +int transfer_json_qvvr_data(unsigned int func_type, int monitor_id, + double mag, double dur, long long start_tm, long long end_tm, int dis_kind, + const std::string& uuid_cfg, const std::string& uuid_dat, + const std::string& mp_id, const std::string& Qvvr_rptname, const std::string& devtype) { + + // 监测点日志的 key, lnk20250526 + std::string full_key_m_c = "monitor." + mp_id + ".COM"; + std::string full_key_m_d = "monitor." + mp_id + ".DATA"; + // 监测点日志的 key, lnk20250526 + + // 获取装置类型的映射配置 + XmlConfig c_xmlcfg; + if (xmlinfo_list.count(devtype)) { + c_xmlcfg = xmlinfo_list[devtype]->xmlcfg; + } else { + c_xmlcfg = xmlcfg; + } + + if (mp_id.empty()) { + std::cout << "mp_id is null" << std::endl; + return 0; + } + + // 构造 JSON 对象 + json root; + root["monitorId"] = mp_id; + root["amplitude"] = mag; + root["duration"] = dur; + root["eventType"] = dis_kind; + + // 时间处理 + time_t start_sec = start_tm / 1000; //毫秒级取秒 + struct tm* time_info = localtime(&start_sec); + char time_buf[32]; + strftime(time_buf, sizeof(time_buf), "%Y-%m-%d %H:%M:%S", time_info); + std::ostringstream start_time_stream; + start_time_stream << time_buf << "." << std::setfill('0') << std::setw(3) << (start_tm % 1000);//构造成年月日时分秒.毫秒 + std::string start_time_str = start_time_stream.str(); + + root["startTime"] = start_time_str; + root["wavePath"] = uuid_dat; //接口提供了两个文件名入参,实际上名字一样只用一个,可优化 + + if (c_xmlcfg.WavePhasicFlag == "1") { //映射配置分相 + if (Qvvr_rptname.find(c_xmlcfg.WavePhasicA) != std::string::npos) { + root["phase"] = "A"; + } else if (Qvvr_rptname.find(c_xmlcfg.WavePhasicB) != std::string::npos) { + root["phase"] = "B"; + } else if (Qvvr_rptname.find(c_xmlcfg.WavePhasicC) != std::string::npos) { + root["phase"] = "C"; + } else { + root["phase"] = "unknow"; + } + } else { + root["phase"] = "unknow"; //不分相 + } + + std::string json_string = root.dump(4); + std::cout << json_string << std::endl; + + // 发送到暂态接口 + std::string response; + SendJsonAPI_web(WEB_EVENT, "", json_string, response); + + // ================ 插入新功能 ========================= + if (!response.empty()) { + try { + json j_r = json::parse(response); + // 有效响应,略过 + } catch (...) { + // 响应异常,保存 json + DIY_ERRORLOG(full_key_m_d.c_str(), "【ERROR】暂态接口响应异常,无法上送监测点%s的暂态事件", mp_id.c_str()); + + std::cout << "qvvr send fail ,store in local" << std::endl; + std::string qvvrDir = FRONT_PATH + "/dat/qvvr/"; + std::string fileName = qvvrDir + mp_id + "-" + FormatTimeForFilename(start_time_str) + "-" + std::to_string(dis_kind) + ".txt"; + writeJsonToFile(fileName, json_string); + checkAndRemoveOldestIfNeeded(qvvrDir, 10LL * 1024 * 1024); + } + } else { + // 无响应,保存 json + DIY_ERRORLOG(full_key_m_d.c_str(), "【ERROR】暂态接口无响应,无法上送监测点%s的暂态事件", mp_id.c_str()); + + std::cout << "qvvr send fail ,store in local" << std::endl; + std::string qvvrDir = FRONT_PATH + "/dat/qvvr/"; + std::string fileName = qvvrDir + mp_id + "-" + FormatTimeForFilename(start_time_str) + "-" + std::to_string(dis_kind) + ".txt"; + writeJsonToFile(fileName, json_string); + checkAndRemoveOldestIfNeeded(qvvrDir, 10LL * 1024 * 1024); + return 1; + } + + // 离线重发机制 + { + std::string qvvrDir = FRONT_PATH + "/dat/qvvr/"; + scanAndResendOfflineFiles(qvvrDir); + } + + // 响应处理 + std::cout << "current qvvr handle response" << std::endl; + handleCommentResponse(response); + + return 0; +} + +void qvvr_test() +{ + char uuid_cfg[] = {"/comtrade/"}; + char uuid_dat[] = {"/comtrade/"}; + char mp_id[] = {"qvvrtest123"}; + char Qvvr_rptname[] = {"unknow"}; + char devtype[] = {"01"}; + + transfer_json_qvvr_data(1, 123456789, 220, 180, 1730894400.123, 1730894580, 1210001,uuid_cfg,uuid_dat,mp_id,Qvvr_rptname,devtype); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////通用接口响应 + +void handleCommentResponse(const std::string& response) { + try { + auto json_data = json::parse(response); // 或 json::parse(response); + + std::string code = "not found"; + std::string msg = "not found"; + if (json_data.contains("code")) { + if (json_data["code"].is_string()) { + code = json_data["code"]; + } else if (json_data["code"].is_number_integer() || json_data["code"].is_number()) { + code = std::to_string(json_data["code"].get()); + } + } + + if (json_data.contains("msg")) { + if (json_data["msg"].is_string()) { + msg = json_data["msg"]; + } + } + + if (code != "not found") { + std::cout << "Response Code: " << code << std::endl; + std::cout << "Message: " << msg << std::endl; + } else { + std::cerr << "Error: Missing expected fields in JSON response." << std::endl; + } + } catch (const json::parse_error& e) { + std::cerr << "Error parsing response: " << e.what() << std::endl; + } catch (const std::exception& e) { + std::cerr << "Unexpected exception: " << e.what() << std::endl; + } +} + diff --git a/LFtid1056/cloudfront/code/log4.cpp b/LFtid1056/cloudfront/code/log4.cpp new file mode 100644 index 0000000..2a39de5 --- /dev/null +++ b/LFtid1056/cloudfront/code/log4.cpp @@ -0,0 +1,534 @@ +////////////////////////////////////////////////////////////////////////////////////////////////////////// +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +////////////////////////////////////////////////////////////////////////////////////////////////////////// +#include "log4cplus/logger.h" +#include "log4cplus/configurator.h" +#include "log4cplus/fileappender.h" +#include "log4cplus/layout.h" +#include "log4cplus/ndc.h" +#include "log4.h" +#include "log4cplus/spi/loggingevent.h" + +#include "rocketmq.h" +#include "interface.h" +#include "log4.h" +///////////////////////////////////////////////////////////////////////////////////////////////////////////// + +//log4命名空间 +using namespace log4cplus; +using namespace log4cplus::helpers; + +///////////////////////////////////////////////////////////////////////////////////////////////////////////// + +//queue结构定义 +extern std::mutex queue_data_list_mutex; //queue发送数据锁 +extern std::list queue_data_list; //queue发送数据链表 + +extern unsigned int g_node_id; +extern int g_front_seg_index; +extern std::string FRONT_INST; +extern std::string subdir; + +//mq +extern std::mutex queue_data_list_mutex; //queue发送数据锁 +extern std::list queue_data_list; //queue发送数据链表 + +//日志主题 +extern std::string G_LOG_TOPIC; + +extern std::vector terminal_devlist; + +////////////////////////////////////////////////////////辅助函数 +std::string get_front_type_from_subdir() { + if (subdir == "cfg_3s_data") + return "realTime"; + else if (subdir == "cfg_soe_comtrade") + return "comtrade"; + else if (subdir == "cfg_recallhis_data") + return "recall"; + else if (subdir == "cfg_stat_data") + return "stat"; + else + return "unknown"; +} + +// 递归创建目录 +bool create_directory_recursive(const std::string& path) { + size_t pos = 0; + std::string current; + while (pos != std::string::npos) { + pos = path.find('/', pos + 1); + current = path.substr(0, pos); + if (!current.empty() && access(current.c_str(), F_OK) != 0) { + if (mkdir(current.c_str(), 0755) != 0) { + perror(("mkdir failed: " + current).c_str()); + return false; + } + } + } + return true; +} +////////////////////////////////////////////////////////////////////// +std::string extract_logger_id(const std::string& logger_name) { + size_t pos = logger_name.find('.'); + if (pos != std::string::npos && pos + 1 < logger_name.size()) { + return logger_name.substr(pos + 1); + } + return ""; // 没有找到 '.' 或 '.' 后为空 +} + +std::string get_level_str(int level) { + switch (level) { + case 10000: return "DEBUG"; + case 20000: return "NORMAL"; // 或 "INFO" 根据你业务定义 + case 30000: return "WARN"; + case 40000: return "ERROR"; + default: return "UNKNOWN"; + } +} +////////////////////////////////////////////////////////////////////// +TypedLogger::TypedLogger() {} +TypedLogger::TypedLogger(const Logger& l, int t) : logger(l), logtype(t) {} + +DebugSwitch::DebugSwitch() : debug_open(false), min_level(WARN_LOG_LEVEL) {} +void DebugSwitch::open() { debug_open = true; } +void DebugSwitch::close() { + debug_open = false; + targets.clear(); + type_enable.clear(); +} +void DebugSwitch::set_target(const std::string& name) { targets.insert(name); } +void DebugSwitch::set_level(int level) { min_level = level; } +void DebugSwitch::enable_type(int type) { type_enable[type] = true; } +void DebugSwitch::disable_type(int type) { type_enable[type] = false; } + +bool DebugSwitch::match(const std::string& logger_name, int level, int logtype) { + if (!debug_open) return false; + if (level < min_level) return false; + if (type_enable.count(logtype) && !type_enable[logtype]) return false; + std::set::iterator it; + for (it = targets.begin(); it != targets.end(); ++it) { + if (logger_name.find(*it) != std::string::npos) + return true; + } + return false; +} + +std::map logger_map; +DebugSwitch g_debug_switch; + +class SendAppender : public Appender { +protected: + void append(const spi::InternalLoggingEvent& event) { + std::string logger_name = event.getLoggerName(); + int level = event.getLogLevel(); + std::string msg = event.getMessage(); + + int logtype = (logger_name.find(".COM") != std::string::npos) ? LOGTYPE_COM : LOGTYPE_DATA; + std::string level_str; + if (logger_name.find("process") == 0) + level_str = "process"; + else if (logger_name.find("monitor") != std::string::npos) + level_str = "measurepoint"; + else + level_str = "terminal"; + + if (level == ERROR_LOG_LEVEL || level == WARN_LOG_LEVEL || g_debug_switch.match(logger_name, level, logtype)) { + std::ostringstream oss; + oss << "{\"processNo\":\"" << std::to_string(g_front_seg_index) + << "\",\"nodeId\":\"" << FRONT_INST + << "\",\"businessId\":\"" << extract_logger_id(logger_name) + << "\",\"level\":\"" << level_str + << "\",\"grade\":\"" << get_level_str(level) + << "\",\"logtype\":\"" << (logtype == LOGTYPE_COM ? "com" : "data") + << "\",\"frontType\":\"" << get_front_type_from_subdir() + << "\",\"log\":\"" << escape_json(msg) << "\"}"; + + std::string jsonString = oss.str(); + + queue_data_t connect_info; + connect_info.strTopic = G_LOG_TOPIC; + connect_info.strText = jsonString; + + std::lock_guard lock(queue_data_list_mutex); + queue_data_list.push_back(connect_info); + } + } + + std::string escape_json(const std::string& input) { + std::ostringstream ss; + for (unsigned int i = 0; i < input.size(); ++i) { + switch (input[i]) { + case '\\': ss << "\\\\"; break; + case '"': ss << "\\\""; break; + case '\n': ss << "\\n"; break; + case '\r': ss << "\\r"; break; + case '\t': ss << "\\t"; break; + default: ss << input[i]; break; + } + } + return ss.str(); + } + + virtual void close() { + // 可空实现 + } + +public: + SendAppender() {} + virtual ~SendAppender() { + destructorImpl(); // 重要!释放 log4cplus 基类资源 + } +}; + +//用来控制日志上送的结构 +struct LOGEntry { + std::string id; + std::string level; // terminal / measurepoint + int logtype; // com / data + int min_grade; + int countdown; +}; + +//日志上送map管理 +std::map g_log_entries; +pthread_mutex_t g_log_mutex = PTHREAD_MUTEX_INITIALIZER; + +// 生成唯一 key +std::string build_debug_key(const std::string& id, const std::string& level, int logtype) { + return id + "|" + level + "|" + (logtype == 1 ? "COM" : "DATA"); +} + +// 外部线程中调用:每秒更新所有倒计时,0 则删除 +void update_log_entries_countdown() { + pthread_mutex_lock(&g_log_mutex); + std::map::iterator it = g_log_entries.begin(); + while (it != g_log_entries.end()) { + if (it->second.countdown > 0) { + it->second.countdown--; + if (it->second.countdown == 0) { + std::cout << "[LOG] debug日志上送自动关闭: " << it->first << std::endl; + it = g_log_entries.erase(it); + continue; + } + } + ++it; + } + pthread_mutex_unlock(&g_log_mutex); +} + +void process_log_command(const std::string& id, const std::string& level, const std::string& grade, const std::string& logtype_str) { + if (level != "terminal" && level != "measurepoint") return; + + int type = (logtype_str == "com") ? LOGTYPE_COM : LOGTYPE_DATA; + int grade_level = (grade == "DEBUG") ? DEBUG_LOG_LEVEL : INFO_LOG_LEVEL; + + std::string key = build_debug_key(id, level, type); + + pthread_mutex_lock(&g_log_mutex); + + LOGEntry& entry = g_log_entries[key]; // 会自动 insert 或取已有 + entry.id = id; + entry.level = level; + entry.logtype = type; + entry.min_grade = grade_level; + entry.countdown = 60; // 重置倒计时 + + pthread_mutex_unlock(&g_log_mutex); + +} + +Logger init_logger(const std::string& full_name, const std::string& file_dir, const std::string& base_file, SharedAppenderPtr fileAppender) { + create_directory_recursive(file_dir); + + Logger logger = Logger::getInstance(full_name); + if (!fileAppender) { + std::string file_path = file_dir + "/" + base_file + ".log"; + fileAppender = SharedAppenderPtr(new RollingFileAppender(file_path, 1 * 1024 * 1024, 2)); + fileAppender->setLayout(std::unique_ptr( + new PatternLayout("%D{%Y-%m-%d %H:%M:%S} [%p] [%c] %m%n"))); + } + + SharedAppenderPtr sendAppender(new SendAppender()); + logger.addAppender(fileAppender); + logger.addAppender(sendAppender); + logger.setLogLevel(DEBUG_LOG_LEVEL); + return logger; +} + +// 重载版本:无 Appender 传入时调用上面的实现 +log4cplus::Logger init_logger(const std::string& full_name, + const std::string& file_dir, + const std::string& base_file) { + return init_logger(full_name, file_dir, base_file, + log4cplus::SharedAppenderPtr()); // 空指针 +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////应用函数 + +//进程的日志 +void init_logger_process() { + std::string base_dir = FRONT_PATH + "/" + subdir + "/processNo" + std::to_string(g_front_seg_index) + "/log"; + logger_map["process"] = TypedLogger(init_logger(std::string("process"), base_dir, std::string("process")), LOGTYPE_DATA); + std::cout << "process log init ok" << std::endl; +} + +//单个终端的日志初始化 +void init_loggers_bydevid(const std::string& dev_id) +{ + if (dev_id.empty()) return; + + std::string terminal_id = dev_id; + std::string base_dir = FRONT_PATH + "/" + subdir + "/processNo" + std::to_string(g_front_seg_index) + "/log"; + + for (size_t i = 0; i < terminal_devlist.size(); ++i) { + terminal_dev& term = terminal_devlist[i]; + + // 跳过终端台账信息为空的节点 + if (term.terminal_id.empty()) { + std::cout << "terminal_dev No." << i << " is null" << std::endl; + continue; + } + + // 跳过不匹配的终端 + if (term.terminal_id != dev_id) continue; + + std::string ip_str = term.addr_str.empty() ? "unknown" : term.addr_str; + std::string device_dir = base_dir + "/" + ip_str; + std::string device_key_c = std::string("terminal.") + dev_id + ".COM"; + std::string device_key_d = std::string("terminal.") + dev_id + ".DATA"; + + // 添加判断:终端日志 logger 是否已存在 + if (logger_map.find(device_key_c) == logger_map.end() && + logger_map.find(device_key_d) == logger_map.end()) { + + // 所有终端日志(com 和 data)写到同一个 device 日志文件中 + std::string file_path_t = device_dir + "/" + dev_id + ".log"; + + // 共用一个 appender 实例 + SharedAppenderPtr device_appender(new RollingFileAppender(file_path_t, 1 * 1024 * 1024, 2)); + device_appender->setLayout(std::unique_ptr(new PatternLayout("%D{%Y-%m-%d %H:%M:%S} [%p] [%c] %m%n"))); + + Logger device_logger_c = init_logger(device_key_c, device_dir, dev_id, device_appender); + Logger device_logger_d = init_logger(device_key_d, device_dir, dev_id, device_appender); + logger_map[device_key_c] = TypedLogger(device_logger_c, LOGTYPE_COM); + logger_map[device_key_d] = TypedLogger(device_logger_d, LOGTYPE_DATA); + + DIY_WARNLOG(device_key_d.c_str(), "【WARN】终端id:%s终端级日志初始化完毕", term.terminal_id.c_str()); + } + + // 初始化监测点日志,monitor..COM / .DATA + for (size_t j = 0; j < term.line.size(); ++j) { + const ledger_monitor& monitor = term.line[j]; + if (!monitor.monitor_id.empty()) { + std::ostringstream mon_key_c, mon_key_d, mon_path, mon_name; + mon_key_c << "monitor." << monitor.monitor_id << ".COM"; + mon_key_d << "monitor." << monitor.monitor_id << ".DATA"; + mon_path << device_dir << "/monitor" << j; + mon_name << monitor.monitor_id; + + // 判断监测点 logger 是否已存在 + if (logger_map.find(mon_key_c.str()) == logger_map.end() && + logger_map.find(mon_key_d.str()) == logger_map.end()) { + + // 所有监测点日志(com 和 data)写到同一个 monitor 日志文件中 + std::string file_path_m = mon_path.str() + "/" + mon_name.str() + ".log"; + + // 共用一个 appender 实例 + SharedAppenderPtr monitor_appender(new RollingFileAppender(file_path_m, 1 * 1024 * 1024, 2)); + monitor_appender->setLayout(std::unique_ptr(new PatternLayout("%D{%Y-%m-%d %H:%M:%S} [%p] [%c] %m%n"))); + + Logger mon_logger_c = init_logger(mon_key_c.str(), mon_path.str(), mon_name.str(), monitor_appender); + Logger mon_logger_d = init_logger(mon_key_d.str(), mon_path.str(), mon_name.str(), monitor_appender); + logger_map[mon_key_c.str()] = TypedLogger(mon_logger_c, LOGTYPE_COM); + logger_map[mon_key_d.str()] = TypedLogger(mon_logger_d, LOGTYPE_DATA); + + DIY_WARNLOG(mon_key_d.str().c_str(), "【WARN】监测点:%s - id:%s监测点级日志初始化完毕", monitor.monitor_name.c_str(), monitor.monitor_id.c_str()); + } + } + } + + break; // 只匹配一个 terminal_id + } +} + +//初始化台账日志 +void init_loggers() +{ + std::string base_dir = FRONT_PATH + "/" + subdir + "/processNo" + std::to_string(g_front_seg_index) + "/log"; + + // 遍历所有终端 + for (size_t t = 0; t < terminal_devlist.size(); ++t) { + terminal_dev& term = terminal_devlist[t]; + + // 跳过无效终端 + if (term.terminal_id.empty()) { + std::cout << "terminal_dev No." << t << " is null" << std::endl; + continue; + } + + std::string ip_str = term.addr_str.empty() ? "unknown" : term.addr_str; + std::string device_dir = base_dir + "/" + ip_str; + + std::string device_key_c = std::string("terminal.") + term.terminal_id + ".COM"; + std::string device_key_d = std::string("terminal.") + term.terminal_id + ".DATA"; + + // 所有终端日志(com 和 data)写到同一个 device 日志文件中 + std::string file_path_t = device_dir + "/" + term.terminal_id + ".log"; + + // 共用一个 appender 实例 + SharedAppenderPtr device_appender(new RollingFileAppender(file_path_t, 1 * 1024 * 1024, 2)); + device_appender->setLayout(std::unique_ptr(new PatternLayout("%D{%Y-%m-%d %H:%M:%S} [%p] [%c] %m%n"))); + + Logger device_logger_c = init_logger(device_key_c, device_dir, term.terminal_id, device_appender); + Logger device_logger_d = init_logger(device_key_d, device_dir, term.terminal_id, device_appender); + + logger_map[device_key_c] = TypedLogger(device_logger_c, LOGTYPE_COM); + logger_map[device_key_d] = TypedLogger(device_logger_d, LOGTYPE_DATA); + + DIY_WARNLOG(device_key_d.c_str(), "【WARN】终端id:%s终端级日志初始化完毕", term.terminal_id.c_str()); + + // 初始化监测点日志 + for (size_t i = 0; i < term.line.size(); ++i) { + const ledger_monitor& monitor = term.line[i]; + + if (!monitor.monitor_id.empty()) { + std::ostringstream mon_key_c, mon_key_d, mon_path, mon_name; + mon_key_c << "monitor." << monitor.monitor_id << ".COM"; + mon_key_d << "monitor." << monitor.monitor_id << ".DATA"; + + mon_path << device_dir << "/monitor" << i; // 用monitor+序号作为目录 + mon_name << monitor.monitor_id; + + std::string file_path_m = mon_path.str() + "/" + mon_name.str() + ".log"; + + // 共用一个 appender 实例 + SharedAppenderPtr monitor_appender(new RollingFileAppender(file_path_m, 1 * 1024 * 1024, 2)); + monitor_appender->setLayout(std::unique_ptr(new PatternLayout("%D{%Y-%m-%d %H:%M:%S} [%p] [%c] %m%n"))); + + Logger mon_logger_c = init_logger(mon_key_c.str(), mon_path.str(), mon_name.str(), monitor_appender); + Logger mon_logger_d = init_logger(mon_key_d.str(), mon_path.str(), mon_name.str(), monitor_appender); + + logger_map[mon_key_c.str()] = TypedLogger(mon_logger_c, LOGTYPE_COM); + logger_map[mon_key_d.str()] = TypedLogger(mon_logger_d, LOGTYPE_DATA); + + DIY_WARNLOG(mon_key_d.str().c_str(), "【WARN】监测点:%s - id:%s监测点级日志初始化完毕", + monitor.monitor_name.c_str(), monitor.monitor_id.c_str()); + } + } + } +} + +//单个终端的日志删除 +void remove_loggers_by_terminal_id(const std::string& terminal_id) { + // 遍历所有终端 + for (size_t t = 0; t < terminal_devlist.size(); ++t) { + terminal_dev& term = terminal_devlist[t]; + if (term.terminal_id != terminal_id) continue; + + // 删除终端日志 logger + std::string com_key = "terminal." + terminal_id + ".COM"; + std::string data_key = "terminal." + terminal_id + ".DATA"; + + if (logger_map.count(com_key)) { + logger_map[com_key].logger.removeAllAppenders(); + logger_map.erase(com_key); + } + + if (logger_map.count(data_key)) { + logger_map[data_key].logger.removeAllAppenders(); + logger_map.erase(data_key); + } + + // 删除监测点日志 logger + for (size_t i = 0; i < term.line.size(); ++i) { + const ledger_monitor& monitor = term.line[i]; + if (!monitor.monitor_id.empty()) { + std::string mon_prefix = "monitor." + monitor.monitor_id; + std::string mon_com_key = mon_prefix + ".COM"; + std::string mon_data_key = mon_prefix + ".DATA"; + + if (logger_map.count(mon_com_key)) { + logger_map[mon_com_key].logger.removeAllAppenders(); + logger_map.erase(mon_com_key); + } + + if (logger_map.count(mon_data_key)) { + logger_map[mon_data_key].logger.removeAllAppenders(); + logger_map.erase(mon_data_key); + } + } + } + + std::cout << "[LOG] Logger for terminal_id=" << terminal_id << " removed." << std::endl; + break; // 找到匹配终端后退出 + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////封装函数,C/C++通用 + +#ifdef __cplusplus +extern "C" { +#endif + + // 公共函数 + void log4_log_with_level(const char* key, const char* msg, int level) { + std::map::iterator it = logger_map.find(key); + if (it == logger_map.end()) return; + + Logger logger = it->second.logger; + switch (level) { + case 0: LOG4CPLUS_DEBUG(logger, msg); break; + case 1: LOG4CPLUS_INFO(logger, msg); break; + case 2: LOG4CPLUS_WARN(logger, msg); break; + case 3: LOG4CPLUS_ERROR(logger, msg); break; + default: break; + } + } + + // 四个包装函数 + void log_debug(const char* key, const char* msg) { log4_log_with_level(key, msg, 0); } + void log_info (const char* key, const char* msg) { log4_log_with_level(key, msg, 1); } + void log_warn (const char* key, const char* msg) { log4_log_with_level(key, msg, 2); } + void log_error(const char* key, const char* msg) { log4_log_with_level(key, msg, 3); } + + void send_reply_to_queue_c(const char* guid, const char* step, const char* result) { + send_reply_to_queue(std::string(guid), std::string(step), std::string(result)); + } + + //标准化日志接口 + void format_log_msg(char* buf, size_t buf_size, const char* fmt, ...) { + // 写入时间 + time_t now = time(NULL); + struct tm tm_info; + localtime_r(&now, &tm_info); + strftime(buf, buf_size, "%Y-%m-%d %H:%M:%S ", &tm_info); // 时间+空格 + + // 处理可变参数并写入剩余内容 + va_list args; + va_start(args, fmt); + vsnprintf(buf + strlen(buf), buf_size - strlen(buf), fmt, args); + va_end(args); + } + +#ifdef __cplusplus +} +#endif diff --git a/LFtid1056/cloudfront/code/log4.h b/LFtid1056/cloudfront/code/log4.h new file mode 100644 index 0000000..89b4d6a --- /dev/null +++ b/LFtid1056/cloudfront/code/log4.h @@ -0,0 +1,111 @@ +#ifndef LOG4_H +#define LOG4_H + +#ifdef __cplusplus + +#include +#include + +//防止#include 里的冲突 +#ifdef min +#undef min +#endif + +#ifdef max +#undef max +#endif +//防止#include 里的冲突 + +#include "logger.h" +#include +#include "loggingmacros.h" + +#include "appender.h" + +#define LOGTYPE_COM 1 +#define LOGTYPE_DATA 2 + +struct TypedLogger { + log4cplus::Logger logger; + int logtype; + TypedLogger(); + TypedLogger(const log4cplus::Logger& l, int t); +}; + +struct DebugSwitch { + bool debug_open; + std::set targets; + int min_level; + std::map type_enable; + + DebugSwitch(); + void open(); + void close(); + void set_target(const std::string& name); + void set_level(int level); + void enable_type(int type); + void disable_type(int type); + bool match(const std::string& logger_name, int level, int logtype); +}; + +extern std::map logger_map; +extern DebugSwitch g_debug_switch; + +extern void send_reply_to_queue(const std::string& guid, const std::string& step, const std::string& result); + + +std::string get_front_type_from_subdir(); + + +// 不带 Appender 的版本 +log4cplus::Logger init_logger(const std::string& full_name, + const std::string& file_dir, + const std::string& base_file); + +// 带 Appender 的版本 +log4cplus::Logger init_logger(const std::string& full_name, + const std::string& file_dir, + const std::string& base_file, + log4cplus::SharedAppenderPtr fileAppender); + +void process_log_command(const std::string& id, const std::string& level, const std::string& grade, const std::string& logtype_str); + + +void update_log_entries_countdown(); + +extern "C" { +#endif +void remove_loggers_by_terminal_id(const std::string& terminal_id_cstr); +void init_logger_process(); +void init_loggers(); +void init_loggers_bydevid(const std::string& dev_id); + +void log_debug(const char* key, const char* msg); +void log_info(const char* key, const char* msg); +void log_warn(const char* key, const char* msg); +void log_error(const char* key, const char* msg); + +void send_reply_to_queue_c(const char* guid, const char* step, const char* result); +void format_log_msg(char* buf, size_t buf_size, const char* fmt, ...); + +//宏定义 +#define DIY_LOG(LEVEL_FUNC, KEY, ...) \ + do { \ + char buf[256]; \ + format_log_msg(buf, sizeof(buf), __VA_ARGS__); \ + LEVEL_FUNC(KEY, buf); \ + } while (0) + +#define DIY_ERRORLOG(KEY, ...) DIY_LOG(log_error, KEY, __VA_ARGS__) +#define DIY_WARNLOG(KEY, ...) DIY_LOG(log_warn, KEY, __VA_ARGS__) +#define DIY_INFOLOG(KEY, ...) DIY_LOG(log_info, KEY, __VA_ARGS__) +#define DIY_DEBUGLOG(KEY, ...) DIY_LOG(log_debug, KEY, __VA_ARGS__) + + +#ifdef __cplusplus +} +#endif + + + +#endif // LOG4_H diff --git a/LFtid1056/cloudfront/code/log4cplus/config.h b/LFtid1056/cloudfront/code/log4cplus/config.h new file mode 100644 index 0000000..acf99f4 --- /dev/null +++ b/LFtid1056/cloudfront/code/log4cplus/config.h @@ -0,0 +1,472 @@ +/* include/log4cplus/config.h. Generated from config.h.in by configure. */ +/* include/log4cplus/config.h.in. Generated from configure.ac by autoheader. */ + +#ifndef LOG4CPLUS_CONFIG_H + +#define LOG4CPLUS_CONFIG_H + +/* define if the compiler supports basic C++11 syntax */ +/* #undef HAVE_CXX11 */ + +/* Define to 1 if you have the header file. */ +#define HAVE_DLFCN_H 1 + +/* Define to 1 if you have the `fcntl' function. */ +#define HAVE_FCNTL 1 + +/* Define to 1 if you have the `flock' function. */ +#define HAVE_FLOCK 1 + +/* Define to 1 if you have the `ftime' function. */ +#define HAVE_FTIME 1 + +/* Define to 1 if the system has the `constructor' function attribute */ +#define HAVE_FUNC_ATTRIBUTE_CONSTRUCTOR 1 + +/* Define to 1 if the system has the `constructor_priority' function attribute + */ +#define HAVE_FUNC_ATTRIBUTE_CONSTRUCTOR_PRIORITY 1 + +/* */ +#define HAVE_GETADDRINFO 1 + +/* */ +#define HAVE_GETHOSTBYNAME_R 1 + +/* Define to 1 if you have the `getpid' function. */ +#define HAVE_GETPID 1 + +/* Define to 1 if you have the `gmtime_r' function. */ +#define HAVE_GMTIME_R 1 + +/* Define to 1 if you have the `htonl' function. */ +#define HAVE_HTONL 1 + +/* Define to 1 if you have the `htons' function. */ +#define HAVE_HTONS 1 + +/* Define to 1 if you have the `iconv' function. */ +/* #undef HAVE_ICONV */ + +/* Define to 1 if you have the `iconv_close' function. */ +/* #undef HAVE_ICONV_CLOSE */ + +/* Define to 1 if you have the `iconv_open' function. */ +/* #undef HAVE_ICONV_OPEN */ + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the `advapi32' library (-ladvapi32). */ +/* #undef HAVE_LIBADVAPI32 */ + +/* Define to 1 if you have the `libiconv' function. */ +/* #undef HAVE_LIBICONV */ + +/* Define to 1 if you have the `libiconv_close' function. */ +/* #undef HAVE_LIBICONV_CLOSE */ + +/* Define to 1 if you have the `libiconv_open' function. */ +/* #undef HAVE_LIBICONV_OPEN */ + +/* Define to 1 if you have the `kernel32' library (-lkernel32). */ +/* #undef HAVE_LIBKERNEL32 */ + +/* Define to 1 if you have the `oleaut32' library (-loleaut32). */ +/* #undef HAVE_LIBOLEAUT32 */ + +/* Define to 1 if you have the `ws2_32' library (-lws2_32). */ +/* #undef HAVE_LIBWS2_32 */ + +/* Define to 1 if you have the `localtime_r' function. */ +#define HAVE_LOCALTIME_R 1 + +/* Define to 1 if you have the `lockf' function. */ +#define HAVE_LOCKF 1 + +/* Define to 1 if you have the `lstat' function. */ +#define HAVE_LSTAT 1 + +/* Define to 1 if you have the `mbstowcs' function. */ +#define HAVE_MBSTOWCS 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_MEMORY_H */ + +/* Define to 1 if you have the `ntohl' function. */ +#define HAVE_NTOHL 1 + +/* Define to 1 if you have the `ntohs' function. */ +#define HAVE_NTOHS 1 + +/* Define to 1 if you have the `OutputDebugStringW' function. */ +/* #undef HAVE_OUTPUTDEBUGSTRINGW */ + +/* Define to 1 if you have the `pipe' function. */ +#define HAVE_PIPE 1 + +/* Define to 1 if you have the `pipe2' function. */ +#define HAVE_PIPE2 1 + +/* Define to 1 if you have the `poll' function. */ +#define HAVE_POLL 1 + +/* Define if you have POSIX threads libraries and header files. */ +#define HAVE_PTHREAD 1 + +/* Have PTHREAD_PRIO_INHERIT. */ +#define HAVE_PTHREAD_PRIO_INHERIT 1 + +/* If available, contains the Python version number currently in use. */ +/* #undef HAVE_PYTHON */ + +/* Define to 1 if you have the `shutdown' function. */ +#define HAVE_SHUTDOWN 1 + +/* Define to 1 if you have the `stat' function. */ +#define HAVE_STAT 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Defined if the compiler understands __thread or __declspec(thread) + construct. */ +#define HAVE_TLS_SUPPORT 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if the system has the `init_priority' variable attribute */ +#define HAVE_VAR_ATTRIBUTE_INIT_PRIORITY 1 + +/* Define to 1 if you have the `vfprintf_s' function. */ +/* #undef HAVE_VFPRINTF_S */ + +/* Define to 1 if you have the `vfwprintf_s' function. */ +/* #undef HAVE_VFWPRINTF_S */ + +/* Define to 1 if you have the `vsnprintf' function. */ +#define HAVE_VSNPRINTF 1 + +/* Define to 1 if you have the `vsnwprintf' function. */ +/* #undef HAVE_VSNWPRINTF */ + +/* Define to 1 if you have the `vsprintf_s' function. */ +/* #undef HAVE_VSPRINTF_S */ + +/* Define to 1 if you have the `vswprintf_s' function. */ +/* #undef HAVE_VSWPRINTF_S */ + +/* Define to 1 if you have the `wcstombs' function. */ +#define HAVE_WCSTOMBS 1 + +/* Define to 1 if you have the `_vsnprintf' function. */ +/* #undef HAVE__VSNPRINTF */ + +/* Define to 1 if you have the `_vsnprintf_s' function. */ +/* #undef HAVE__VSNPRINTF_S */ + +/* Define to 1 if you have the `_vsnwprintf' function. */ +/* #undef HAVE__VSNWPRINTF */ + +/* Define to 1 if you have the `_vsnwprintf_s' function. */ +/* #undef HAVE__VSNWPRINTF_S */ + +/* Defined if the compiler supports __FUNCTION__ macro. */ +/* #undef HAVE___FUNCTION___MACRO */ + +/* Defined if the compiler supports __func__ symbol. */ +/* #undef HAVE___FUNC___SYMBOL */ + +/* Defined if the compiler supports __PRETTY_FUNCTION__ macro. */ +/* #undef HAVE___PRETTY_FUNCTION___MACRO */ + +/* Defined for --enable-debugging builds. */ +/* #undef LOG4CPLUS_DEBUGGING */ + +/* Defined if the compiler understands __declspec(dllimport) or + __attribute__((visibility("default"))) or __global construct. */ +#define LOG4CPLUS_DECLSPEC_EXPORT __attribute__ ((visibility("default"))) + +/* Defined if the compiler understands __declspec(dllimport) or + __attribute__((visibility("default"))) or __global construct. */ +#define LOG4CPLUS_DECLSPEC_IMPORT __attribute__ ((visibility("default"))) + +/* Defined if the compiler understands __attribute__((visibility("hidden"))) + or __hidden construct. */ +#define LOG4CPLUS_DECLSPEC_PRIVATE __attribute__ ((visibility("hidden"))) + +/* */ +#define LOG4CPLUS_HAVE_ARPA_INET_H 1 + +/* */ +#define LOG4CPLUS_HAVE_ENAMETOOLONG 1 + +/* */ +#define LOG4CPLUS_HAVE_ERRNO_H 1 + +/* */ +#define LOG4CPLUS_HAVE_FCNTL 1 + +/* */ +#define LOG4CPLUS_HAVE_FCNTL_H 1 + +/* */ +#define LOG4CPLUS_HAVE_FLOCK 1 + +/* */ +#define LOG4CPLUS_HAVE_FTIME 1 + +/* */ +#define LOG4CPLUS_HAVE_FUNCTION_MACRO 1 + +/* */ +#define LOG4CPLUS_HAVE_FUNC_ATTRIBUTE_CONSTRUCTOR 1 + +/* */ +#define LOG4CPLUS_HAVE_FUNC_ATTRIBUTE_CONSTRUCTOR_PRIORITY 1 + +/* */ +#define LOG4CPLUS_HAVE_FUNC_SYMBOL 1 + +/* */ +#define LOG4CPLUS_HAVE_GETADDRINFO 1 + +/* */ +#define LOG4CPLUS_HAVE_GETHOSTBYNAME_R 1 + +/* */ +#define LOG4CPLUS_HAVE_GETPID 1 + +/* */ +#define LOG4CPLUS_HAVE_GETTID 1 + +/* */ +#define LOG4CPLUS_HAVE_GMTIME_R 1 + +/* */ +#define LOG4CPLUS_HAVE_HTONL 1 + +/* */ +#define LOG4CPLUS_HAVE_HTONS 1 + +/* */ +/* #undef LOG4CPLUS_HAVE_ICONV */ + +/* */ +/* #undef LOG4CPLUS_HAVE_ICONV_CLOSE */ + +/* */ +/* #undef LOG4CPLUS_HAVE_ICONV_H */ + +/* */ +/* #undef LOG4CPLUS_HAVE_ICONV_OPEN */ + +/* */ +#define LOG4CPLUS_HAVE_LIMITS_H 1 + +/* */ +#define LOG4CPLUS_HAVE_LOCALTIME_R 1 + +/* */ +#define LOG4CPLUS_HAVE_LOCKF 1 + +/* */ +#define LOG4CPLUS_HAVE_LSTAT 1 + +/* */ +#define LOG4CPLUS_HAVE_MBSTOWCS 1 + +/* */ +#define LOG4CPLUS_HAVE_NETDB_H 1 + +/* */ +#define LOG4CPLUS_HAVE_NETINET_IN_H 1 + +/* */ +#define LOG4CPLUS_HAVE_NETINET_TCP_H 1 + +/* */ +#define LOG4CPLUS_HAVE_NTOHL 1 + +/* */ +#define LOG4CPLUS_HAVE_NTOHS 1 + +/* */ +/* #undef LOG4CPLUS_HAVE_OUTPUTDEBUGSTRING */ + +/* */ +#define LOG4CPLUS_HAVE_PIPE 1 + +/* */ +#define LOG4CPLUS_HAVE_PIPE2 1 + +/* */ +#define LOG4CPLUS_HAVE_POLL 1 + +/* */ +#define LOG4CPLUS_HAVE_POLL_H 1 + +/* */ +#define LOG4CPLUS_HAVE_PRETTY_FUNCTION_MACRO 1 + +/* */ +#define LOG4CPLUS_HAVE_SHUTDOWN 1 + +/* */ +#define LOG4CPLUS_HAVE_STAT 1 + +/* */ +#define LOG4CPLUS_HAVE_STDARG_H 1 + +/* */ +#define LOG4CPLUS_HAVE_STDIO_H 1 + +/* */ +#define LOG4CPLUS_HAVE_STDLIB_H 1 + +/* */ +#define LOG4CPLUS_HAVE_SYSLOG_H 1 + +/* */ +#define LOG4CPLUS_HAVE_SYS_FILE_H 1 + +/* */ +#define LOG4CPLUS_HAVE_SYS_SOCKET_H 1 + +/* */ +#define LOG4CPLUS_HAVE_SYS_STAT_H 1 + +/* */ +#define LOG4CPLUS_HAVE_SYS_SYSCALL_H 1 + +/* */ +#define LOG4CPLUS_HAVE_SYS_TIMEB_H 1 + +/* */ +#define LOG4CPLUS_HAVE_SYS_TIME_H 1 + +/* */ +#define LOG4CPLUS_HAVE_SYS_TYPES_H 1 + +/* */ +#define LOG4CPLUS_HAVE_TIME_H 1 + +/* */ +#define LOG4CPLUS_HAVE_TLS_SUPPORT 1 + +/* */ +#define LOG4CPLUS_HAVE_UNISTD_H 1 + +/* */ +#define LOG4CPLUS_HAVE_VAR_ATTRIBUTE_INIT_PRIORITY 1 + +/* */ +/* #undef LOG4CPLUS_HAVE_VFPRINTF_S */ + +/* */ +/* #undef LOG4CPLUS_HAVE_VFWPRINTF_S */ + +/* */ +#define LOG4CPLUS_HAVE_VSNPRINTF 1 + +/* */ +/* #undef LOG4CPLUS_HAVE_VSNWPRINTF */ + +/* */ +/* #undef LOG4CPLUS_HAVE_VSPRINTF_S */ + +/* */ +/* #undef LOG4CPLUS_HAVE_VSWPRINTF_S */ + +/* */ +#define LOG4CPLUS_HAVE_WCHAR_H 1 + +/* */ +#define LOG4CPLUS_HAVE_WCSTOMBS 1 + +/* */ +/* #undef LOG4CPLUS_HAVE__VSNPRINTF */ + +/* */ +/* #undef LOG4CPLUS_HAVE__VSNPRINTF_S */ + +/* */ +/* #undef LOG4CPLUS_HAVE__VSNWPRINTF */ + +/* */ +/* #undef LOG4CPLUS_HAVE__VSNWPRINTF_S */ + +/* Define if this is a single-threaded library. */ +/* #undef LOG4CPLUS_SINGLE_THREADED */ + +/* */ +#define LOG4CPLUS_THREAD_LOCAL_VAR thread_local + +/* */ +/* #undef LOG4CPLUS_USE_PTHREADS */ + +/* Define when iconv() is available. */ +/* #undef LOG4CPLUS_WITH_ICONV */ + +/* Defined to enable unit tests. */ +/* #undef LOG4CPLUS_WITH_UNIT_TESTS */ + +/* Define for C99 compilers/standard libraries that support more than just the + "C" locale. */ +/* #undef LOG4CPLUS_WORKING_C_LOCALE */ + +/* Define for compilers/standard libraries that support more than just the "C" + locale. */ +/* #undef LOG4CPLUS_WORKING_LOCALE */ + +/* Define to the sub-directory where libtool stores uninstalled libraries. */ +#define LT_OBJDIR ".libs/" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "log4cplus" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "log4cplus 2.1.2" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "log4cplus" + +/* Define to the home page for this package. */ +#define PACKAGE_URL "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "2.1.2" + +/* Define to necessary symbol if this constant uses a non-standard name on + your system. */ +/* #undef PTHREAD_CREATE_JOINABLE */ + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Defined to the actual TLS support construct. */ +#define TLS_SUPPORT_CONSTRUCT thread_local + +/* Substitute for socklen_t */ +/* #undef socklen_t */ + +#endif // LOG4CPLUS_CONFIG_H diff --git a/LFtid1056/cloudfront/code/log4cplus/config/defines.hxx b/LFtid1056/cloudfront/code/log4cplus/config/defines.hxx new file mode 100644 index 0000000..166396d --- /dev/null +++ b/LFtid1056/cloudfront/code/log4cplus/config/defines.hxx @@ -0,0 +1,248 @@ +/* include/log4cplus/config/defines.hxx. Generated from defines.hxx.in by configure. */ +#ifndef LOG4CPLUS_CONFIG_DEFINES_HXX +#define LOG4CPLUS_CONFIG_DEFINES_HXX + +/* */ +#define LOG4CPLUS_HAVE_SYSLOG_H 1 + +/* */ +#define LOG4CPLUS_HAVE_ARPA_INET_H 1 + +/* */ +#define LOG4CPLUS_HAVE_NETINET_IN_H 1 + +/* */ +#define LOG4CPLUS_HAVE_NETINET_TCP_H 1 + +/* */ +#define LOG4CPLUS_HAVE_SYS_TIMEB_H 1 + +/* */ +#define LOG4CPLUS_HAVE_SYS_TIME_H 1 + +/* */ +#define LOG4CPLUS_HAVE_SYS_TYPES_H 1 + +/* */ +#define LOG4CPLUS_HAVE_SYS_STAT_H 1 + +/* */ +#define LOG4CPLUS_HAVE_SYS_SYSCALL_H 1 + +/* */ +#define LOG4CPLUS_HAVE_SYS_FILE_H 1 + +/* */ +#define LOG4CPLUS_HAVE_TIME_H 1 + +/* */ +#define LOG4CPLUS_HAVE_SYS_SOCKET_H 1 + +/* */ +#define LOG4CPLUS_HAVE_NETDB_H 1 + +/* */ +#define LOG4CPLUS_HAVE_UNISTD_H 1 + +/* */ +#define LOG4CPLUS_HAVE_FCNTL_H 1 + +/* */ +#define LOG4CPLUS_HAVE_STDARG_H 1 + +/* */ +#define LOG4CPLUS_HAVE_STDIO_H 1 + +/* */ +#define LOG4CPLUS_HAVE_STDLIB_H 1 + +/* */ +#define LOG4CPLUS_HAVE_ERRNO_H 1 + +/* */ +#define LOG4CPLUS_HAVE_WCHAR_H 1 + +/* */ +/* #undef LOG4CPLUS_HAVE_ICONV_H */ + +/* */ +#define LOG4CPLUS_HAVE_LIMITS_H 1 + +/* */ +#define LOG4CPLUS_HAVE_FTIME 1 + +/* */ +#define LOG4CPLUS_HAVE_GETADDRINFO 1 + +/* */ +#define LOG4CPLUS_HAVE_GETHOSTBYNAME_R 1 + +/* */ +#define LOG4CPLUS_HAVE_GETPID 1 + +/* */ +#define LOG4CPLUS_HAVE_GMTIME_R 1 + +/* */ +#define LOG4CPLUS_HAVE_HTONL 1 + +/* */ +#define LOG4CPLUS_HAVE_HTONS 1 + +/* */ +#define LOG4CPLUS_HAVE_LOCALTIME_R 1 + +/* */ +#define LOG4CPLUS_HAVE_LSTAT 1 + +/* */ +#define LOG4CPLUS_HAVE_FCNTL 1 + +/* */ +#define LOG4CPLUS_HAVE_LOCKF 1 + +/* */ +#define LOG4CPLUS_HAVE_FLOCK 1 + +/* */ +#define LOG4CPLUS_HAVE_NTOHL 1 + +/* */ +#define LOG4CPLUS_HAVE_NTOHS 1 + +/* Define to 1 if you have the `shutdown' function. */ +#define LOG4CPLUS_HAVE_SHUTDOWN 1 + +/* */ +#define LOG4CPLUS_HAVE_PIPE 1 + +/* */ +#define LOG4CPLUS_HAVE_PIPE2 1 + +/* */ +#define LOG4CPLUS_HAVE_POLL 1 + +/* */ +#define LOG4CPLUS_HAVE_POLL_H 1 + +/* */ +#define LOG4CPLUS_HAVE_STAT 1 + +/* Define if this is a single-threaded library. */ +/* #undef LOG4CPLUS_SINGLE_THREADED */ + +/* */ +/* #undef LOG4CPLUS_USE_PTHREADS */ + +/* Define for compilers/standard libraries that support more than just the "C" + locale. */ +/* #undef LOG4CPLUS_WORKING_LOCALE */ + +/* Define for C99 compilers/standard libraries that support more than just the + "C" locale. */ +/* #undef LOG4CPLUS_WORKING_C_LOCALE */ + +/* Define to int if undefined. */ +/* #undef socklen_t */ + +/* Defined for --enable-debugging builds. */ +/* #undef LOG4CPLUS_DEBUGGING */ + +/* Defined if the compiler understands __declspec(dllexport) or + __attribute__((visibility("default"))) construct. */ +#define LOG4CPLUS_DECLSPEC_EXPORT __attribute__ ((visibility("default"))) + +/* Defined if the compiler understands __declspec(dllimport) or + __attribute__((visibility("default"))) construct. */ +#define LOG4CPLUS_DECLSPEC_IMPORT __attribute__ ((visibility("default"))) + +/* Defined if the compiler understands + __attribute__((visibility("hidden"))) construct. */ +#define LOG4CPLUS_DECLSPEC_PRIVATE __attribute__ ((visibility("hidden"))) + +/* */ +#define LOG4CPLUS_HAVE_TLS_SUPPORT 1 + +/* */ +#define LOG4CPLUS_THREAD_LOCAL_VAR thread_local + +/* Defined if the host OS provides ENAMETOOLONG errno value. */ +#define LOG4CPLUS_HAVE_ENAMETOOLONG 1 + +/* */ +#define LOG4CPLUS_HAVE_VSNPRINTF 1 + +/* Define to 1 if you have the `vsnwprintf' function. */ +/* #undef LOG4CPLUS_HAVE_VSNWPRINTF */ + +/* Define to 1 if you have the `_vsnwprintf' function. */ +/* #undef LOG4CPLUS_HAVE__VSNWPRINTF */ + +/* */ +/* #undef LOG4CPLUS_HAVE__VSNPRINTF */ + +/* Define to 1 if you have the `vfprintf_s' function. */ +/* #undef LOG4CPLUS_HAVE_VFPRINTF_S */ + +/* Define to 1 if you have the `vfwprintf_s' function. */ +/* #undef LOG4CPLUS_HAVE_VFWPRINTF_S */ + +/* Define to 1 if you have the `vsprintf_s' function. */ +/* #undef LOG4CPLUS_HAVE_VSPRINTF_S */ + +/* Define to 1 if you have the `vswprintf_s' function. */ +/* #undef LOG4CPLUS_HAVE_VSWPRINTF_S */ + +/* Define to 1 if you have the `_vsnprintf_s' function. */ +/* #undef LOG4CPLUS_HAVE__VSNPRINTF_S */ + +/* Define to 1 if you have the `_vsnwprintf_s' function. */ +/* #undef LOG4CPLUS_HAVE__VSNWPRINTF_S */ + +/* Defined if the compiler supports __FUNCTION__ macro. */ +#define LOG4CPLUS_HAVE_FUNCTION_MACRO 1 + +/* Defined if the compiler supports __PRETTY_FUNCTION__ macro. */ +#define LOG4CPLUS_HAVE_PRETTY_FUNCTION_MACRO 1 + +/* Defined if the compiler supports __func__ symbol. */ +#define LOG4CPLUS_HAVE_FUNC_SYMBOL 1 + +/* Define to 1 if you have the `mbstowcs' function. */ +#define LOG4CPLUS_HAVE_MBSTOWCS 1 + +/* Define to 1 if you have the `wcstombs' function. */ +#define LOG4CPLUS_HAVE_WCSTOMBS 1 + +/* Define to 1 if you have Linux style syscall(SYS_gettid). */ +#define LOG4CPLUS_HAVE_GETTID 1 + +/* Define when iconv() is available. */ +/* #undef LOG4CPLUS_WITH_ICONV */ + +/* Define to 1 if you have the `iconv' function. */ +/* #undef LOG4CPLUS_HAVE_ICONV */ + +/* Define to 1 if you have the `iconv_close' function. */ +/* #undef LOG4CPLUS_HAVE_ICONV_CLOSE */ + +/* Define to 1 if you have the `iconv_open' function. */ +/* #undef LOG4CPLUS_HAVE_ICONV_OPEN */ + +/* Define to 1 if you have the `OutputDebugString' function. */ +/* #undef LOG4CPLUS_HAVE_OUTPUTDEBUGSTRING */ + +/* Define to 1 if the system has the `constructor' function attribute + with priority */ +#define LOG4CPLUS_HAVE_FUNC_ATTRIBUTE_CONSTRUCTOR_PRIORITY 1 + +/* Define to 1 if the system has the `constructor' function attribute */ +#define LOG4CPLUS_HAVE_FUNC_ATTRIBUTE_CONSTRUCTOR 1 + +/* Define to 1 if the system has the `init_priority' variable attribute */ +#define LOG4CPLUS_HAVE_VAR_ATTRIBUTE_INIT_PRIORITY 1 + +/* Defined to enable unit tests. */ +/* #undef LOG4CPLUS_WITH_UNIT_TESTS */ + +#endif // LOG4CPLUS_CONFIG_DEFINES_HXX diff --git a/LFtid1056/cloudfront/code/log4cplus/config/stamp-h2 b/LFtid1056/cloudfront/code/log4cplus/config/stamp-h2 new file mode 100644 index 0000000..109f0fe --- /dev/null +++ b/LFtid1056/cloudfront/code/log4cplus/config/stamp-h2 @@ -0,0 +1 @@ +timestamp for include/log4cplus/config/defines.hxx diff --git a/LFtid1056/cloudfront/code/log4cplus/stamp-h1 b/LFtid1056/cloudfront/code/log4cplus/stamp-h1 new file mode 100644 index 0000000..6218c71 --- /dev/null +++ b/LFtid1056/cloudfront/code/log4cplus/stamp-h1 @@ -0,0 +1 @@ +timestamp for include/log4cplus/config.h diff --git a/LFtid1056/cloudfront/code/main.cpp b/LFtid1056/cloudfront/code/main.cpp new file mode 100644 index 0000000..df103d8 --- /dev/null +++ b/LFtid1056/cloudfront/code/main.cpp @@ -0,0 +1,597 @@ +///////////////////////////////////////////////////////////////////////////////////////////////////// + +#include +#include //任务队列 +#include //信号处理 +#include //标准输入输出 +#include //字符串 +#include //数组型容器 +#include //映射 +#include //锁 +#include //线程 +#include //线程控制 +#include //原子操作 +#include //时间 +#include //时间 +#include //流处理 +#include //字符处理 +#include //格式控制 +#include //类消费者绑定 +#include //获取目录 +#include +#include +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "interface.h" //用于访问接口 +#include "log4.h" //用于日志 +#include "curl/curl.h" //用于访问接口 +#include "nlohmann/json.hpp" //用于构造json +#include "worker.h" //shell接口 +#include "rocketmq.h" +#include "rocketmq/MQClientException.h" +#include "front.h" + +////////////////////////////////////////////////////////////////////////////////////////////////////// + +using json = nlohmann::json; + +//////////////////////////////////////////////////////////////////////////////////////////////////////全局变量 + +//前置程序路径 +std::string FRONT_PATH; + + + +//初始化标志 +int INITFLAG = 0; + +//前置标置 +std::string subdir = "cfg_stat_data"; //默认稳态 +uint32_t g_node_id = 0; +int g_front_seg_index = 0; //默认单进程 +int g_front_seg_num = 0; //默认单进程 + +//实时进程标志 +int three_secs_enabled = 0; + +//稳态进程自动注册报告标志 +int auto_register_report_enabled = 0; + +//mq生产线程和定时线程都加上死锁计数器 +uint32_t g_mqproducer_blocked_times = 0; +uint32_t g_ontime_blocked_times = 0; + +//进程控制 +std::atomic running{true}; +void onSignal(int){ running = false; } + +///////////////////////////////////////////////////////////////////////////////////////////////////// + +extern int G_TEST_FLAG; //测试线程开启开关 +extern int TEST_PORT; //测试端口号 + +extern std::string FRONT_INST; + +extern std::mutex queue_data_list_mutex; +extern std::list queue_data_list; + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 功能函数 + +template +std::unique_ptr make_unique(Args&&... args) { + return std::unique_ptr(new T(std::forward(args)...)); +} + +//处理参数 +bool parse_param(int argc, char* argv[]) { + for (int i = 1; i < argc; ++i) { + std::string arg = argv[i]; + + // 处理 -s 参数 + if (arg == "-s" && i + 1 < argc) { + std::string val = argv[++i]; + auto pos = val.find('_'); + if (pos != std::string::npos) { + try { + g_front_seg_index = std::stoi(val.substr(0, pos)); + g_front_seg_num = std::stoi(val.substr(pos + 1)); + } catch (...) { + std::cerr << "Invalid -s format." << std::endl; + } + } + continue; + } + // 处理 -s1_5 这种紧凑写法 + if (arg.rfind("-s", 0) == 0 && arg.length() > 2) { + std::string val = arg.substr(2); + auto pos = val.find('_'); + if (pos != std::string::npos) { + try { + g_front_seg_index = std::stoi(val.substr(0, pos)); + g_front_seg_num = std::stoi(val.substr(pos + 1)); + } catch (...) { + std::cerr << "Invalid -s format." << std::endl; + } + } + continue; + } + // 处理 -d 或 -D 参数 + if ((arg == "-d" || arg == "-D") && i + 1 < argc) { + subdir = argv[++i]; + continue; + } + if ((arg.rfind("-d", 0) == 0 || arg.rfind("-D", 0) == 0) && arg.length() > 2) { + subdir = arg.substr(2); + continue; + } + // 这里可以继续添加其它参数解析,例如 -x, -y + // if (arg == "-x" ... ) {...} + } + + // 输出结果 + std::cout << "g_front_seg_index: " << g_front_seg_index << "\n"; + std::cout << "g_front_seg_num : " << g_front_seg_num << "\n"; + std::cout << "subdir : " << subdir << "\n"; + return true; +} + +//获取前置类型 +void init_global_function_enable() { + if (subdir == "cfg_stat_data") { // 历史稳态 + g_node_id = STAT_DATA_BASE_NODE_ID; + auto_register_report_enabled = 1; + } else if (subdir == "cfg_3s_data") { // 实时 + g_node_id = THREE_SECS_DATA_BASE_NODE_ID; + three_secs_enabled = 1; + } else if (subdir == "cfg_soe_comtrade") { // 告警、录波、暂态 + g_node_id = SOE_COMTRADE_BASE_NODE_ID; + } else if (subdir == "cfg_recallhis_data") { // 补招 + g_node_id = RECALL_HIS_DATA_BASE_NODE_ID; + } +} + +//获取功能名称 +std::string get_front_msg_from_subdir() { + if (subdir.find("cfg_3s_data") != std::string::npos) + return "实时数据进程"; + else if (subdir.find("cfg_soe_comtrade") != std::string::npos) + return "暂态和告警进程"; + else if (subdir.find("cfg_recallhis_data") != std::string::npos) + return "稳态补招进程"; + else if (subdir.find("cfg_stat_data") != std::string::npos) + return "稳态统计进程"; + else + return "unknown"; +} + +//获取前置路径 +std::string get_parent_directory() { + // 获取当前工作目录 + char cwd[PATH_MAX]; + if (!getcwd(cwd, sizeof(cwd))) { + // 获取失败 + return ""; + } + // dirname 可能会修改传入的字符串,需要副本 + std::string current_dir(cwd); + std::unique_ptr temp(new char[current_dir.size() + 1]); + std::strcpy(temp.get(), current_dir.c_str()); + + // 获取父目录 + char* parent = dirname(temp.get()); + if (!parent) return ""; + + // 返回绝对路径 + return std::string(parent); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////主要类结构 + +//------------------- Front 类(C++) ------------------- + + //构造函数 + Front::Front(): + m_worker(this), + m_threadPool(std::thread::hardware_concurrency()) // 用系统核数初始化线程池 + { + + //初始化g_node_id + init_global_function_enable(); + + //配置初始化 + init_config(); + + //启动进程日志 + init_logger_process(); + DIY_WARNLOG("process","【WARN】前置的%s%d号进程 进程级日志初始化完毕", get_front_msg_from_subdir(), g_front_seg_index); + + //读取台账 + parse_device_cfg_web(); + + //初始化日志 + init_loggers(); + + //读取模型,下载文件 + parse_model_cfg_web(); + + //解析文件 + Set_xml_nodeinfo(); + + StartFrontThread(); //开启主线程 + + StartMQConsumerThread(); //开启消费者线程 + + StartMQProducerThread(); //开启生产者线程 + + StartTimerThread(); //开启定时线程 + + //启动worker 根据启动标志启动 + if(G_TEST_FLAG){ + if(!m_worker.startServer(TEST_PORT)) { + std::cerr << "[testshell] startServer failed.\n"; + } + } + + //初始化标志 + std::this_thread::sleep_for(std::chrono::seconds(3)); + INITFLAG = 1; + + } + + Front::~Front() { + FormClosing(); + } + + // ============ 关闭所有运行中的线程============ + void Front::FormClosing() { + //确保testshell关闭 + m_worker.stopServer(); + + // **确保前置线程被关闭** + if(m_FrontThread.joinable()) { + m_bIsFrontThreadCancle = true; + m_FrontThread.join(); // **等待前置线程结束** + } + + // 定时线程 + if (m_TimerThread.joinable()) { + m_IsTimerCancel = true; + m_TimerThread.join(); + } + + // 生产者线程 + if (m_MQProducerThread.joinable()) { + m_IsMQProducerCancel = true; + m_MQProducerThread.join(); + } + + // 消费者线程 + m_IsMQConsumerCancel = true; + + if (m_mqConsumer) { + try { + m_mqConsumer->shutdown(); + } catch (...) { + std::cerr << "mq consumer shutdown error" << std::endl; + } + m_mqConsumer.reset(); + } + + m_listener.reset(); + + if (m_MQConsumerThread.joinable()) { + m_MQConsumerThread.join(); + } + + } + + //============ 线程函数 ============ + + void Front::StartFrontThread() { + m_bIsFrontThreadCancle = false; + m_FrontThread = std::thread(&Front::FrontThread, this); + } + + void Front::StartMQConsumerThread() { + m_IsMQConsumerCancel = false; + m_MQConsumerThread = std::thread(&Front::mqconsumerThread, this); + } + + void Front::StartMQProducerThread() { + m_IsMQProducerCancel = false; + m_MQProducerThread = std::thread(&Front::mqproducerThread, this); + } + + void Front::StartTimerThread() { + m_IsTimerCancel = false; + m_TimerThread = std::thread(&Front::OnTimerThread, this); + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////主功能线程 + +void Front::FrontThread() { + std::cout << "FrontThread::run() is called ...... \n"; + + try { + while (!m_bIsFrontThreadCancle) { + check_3s_config(); // 实时数据触发 + create_recall_xml(); // 生成待补招xml文件 + check_ledger_update(); // 触发台账更新 + } + } catch (const std::exception& e) { + std::cerr << "[FrontThread] Caught exception: " << e.what() << std::endl; + } catch (...) { + std::cerr << "[FrontThread] Caught unknown exception" << std::endl; + } + + // 设置重启标志 + { + std::lock_guard lock(m_threadCheckMutex); + m_needRestartFrontThread = true; + } + + std::cout << "[FrontThread] exited, will be restarted by monitor\n"; +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////定时任务 + +void Front::OnTimerThread() +{ + try { + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + std::cout << "OnTimerThread::run() is called ...... \n"; + + int counter = 0; + send_heartbeat_to_queue("1"); + + while (!m_IsTimerCancel) + { + update_log_entries_countdown(); + + if (counter >= 30) { + send_heartbeat_to_queue("1"); + counter = 0; + } + counter++; + + g_ontime_blocked_times = 0; + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + } + } catch (const std::exception& e) { + std::cerr << "[OnTimerThread] Caught exception: " << e.what() << std::endl; + } catch (...) { + std::cerr << "[OnTimerThread] Caught unknown exception" << std::endl; + } + + // 设置重启标志 + { + std::lock_guard lock(m_threadCheckMutex); + m_needRestartTimerThread = true; + } + + std::cout << "[OnTimerThread] exited, will be restarted by monitor\n"; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////消费者线程 + +void Front::mqconsumerThread() +{ + try { + std::string consumerGroup = subdir + std::to_string(g_front_seg_index); + std::string nameServer = G_MQCONSUMER_IPPORT; + std::vector subscriptions; + + if (g_node_id == THREE_SECS_DATA_BASE_NODE_ID) { + subscriptions.emplace_back(FRONT_INST + "_" + G_MQCONSUMER_TOPIC_RT, G_MQCONSUMER_TAG_RT, myMessageCallbackrtdata); + } + subscriptions.emplace_back(FRONT_INST + "_" + G_MQCONSUMER_TOPIC_UD, G_MQCONSUMER_TAG_UD, myMessageCallbackupdate); + if (g_node_id == RECALL_HIS_DATA_BASE_NODE_ID) { + subscriptions.emplace_back(FRONT_INST + "_" + G_MQCONSUMER_TOPIC_RC, G_MQCONSUMER_TAG_RC, myMessageCallbackrecall); + } + subscriptions.emplace_back(FRONT_INST + "_" + G_MQCONSUMER_TOPIC_SET, G_MQCONSUMER_TAG_SET, myMessageCallbackset); + subscriptions.emplace_back(FRONT_INST + "_" + G_MQCONSUMER_TOPIC_LOG, G_MQCONSUMER_TAG_LOG, myMessageCallbacklog); + + m_mqConsumer = make_unique(consumerGroup); + m_mqConsumer->setNamesrvAddr(nameServer); + m_mqConsumer->setSessionCredentials(G_MQCONSUMER_ACCESSKEY, G_MQCONSUMER_SECRETKEY, G_MQCONSUMER_CHANNEL); + m_mqConsumer->setInstanceName("inst_" + std::to_string(sGetMsTime())); + m_mqConsumer->setConsumeFromWhere(rocketmq::CONSUME_FROM_LAST_OFFSET); + + std::map callbackMap; + for (const auto& sub : subscriptions) { + std::string key = sub.topic + ":" + sub.tag; + callbackMap.emplace(key, sub.callback); + m_mqConsumer->subscribe(sub.topic, sub.tag); + std::cout << "[mqconsumerThread] 已订阅 Topic=\"" << sub.topic << "\", Tag=\"" << sub.tag << "\"" << std::endl; + } + + m_listener = std::make_shared(callbackMap); + m_mqConsumer->registerMessageListener(m_listener.get()); + + m_mqConsumer->start(); + std::cout << "[mqconsumerThread] Consumer 已启动,等待消息..." << std::endl; + + // ✳️ 保持线程不主动退出,由 RocketMQ 内部驱动执行回调 + // 如果 RocketMQ 内部机制失败或意外退出线程,就走 catch + } + catch (const rocketmq::MQClientException& e) { + std::cerr << "[mqconsumerThread] MQClientException: " << e.what() << std::endl; + std::lock_guard lock(m_threadCheckMutex); + m_needRestartConsumerThread = true; + return; + } catch (const std::exception& e) { + std::cerr << "[mqconsumerThread] std::exception: " << e.what() << std::endl; + std::lock_guard lock(m_threadCheckMutex); + m_needRestartConsumerThread = true; + return; + } catch (...) { + std::cerr << "[mqconsumerThread] Unknown exception" << std::endl; + std::lock_guard lock(m_threadCheckMutex); + m_needRestartConsumerThread = true; + return; + } + + // 程序运行中,消费者会通过回调处理消息,线程保持存活即可 + std::cout << "[mqconsumerThread] Consumer 线程正在运行,等待消息到达..." << std::endl; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////生产者线程 + +void Front::mqproducerThread() +{ + try { + // 1. 初始化生产者 + InitializeProducer(m_producer); + std::cout << "\n[mqproducerThread] is running ...... \n\n"; + + uint32_t count = 0; + + while (!m_IsMQProducerCancel) { + queue_data_t data; + bool data_gotten = false; + + if(INITFLAG) + { + std::lock_guard lock(queue_data_list_mutex); + if (!queue_data_list.empty()) { + data = queue_data_list.front(); + queue_data_list.pop_front(); + data_gotten = true; + } + } + + if (data_gotten) { + auto now = std::chrono::system_clock::now(); + auto ms_part = std::chrono::duration_cast( + now.time_since_epoch()) % 1000; + auto time_t_part = std::chrono::system_clock::to_time_t(now); + std::tm tm_buf; + localtime_r(&time_t_part, &tm_buf); + char timeStr[32]; + std::strftime(timeStr, sizeof(timeStr), "%Y-%m-%d %H:%M:%S", &tm_buf); + + std::cout << "BEGIN my_queue_send no." << count + << " >>>> " << timeStr + << "." << std::setw(3) << std::setfill('0') << ms_part.count() + << std::endl; + + // 调用实际发送 + my_rocketmq_send(data, m_producer); + + now = std::chrono::system_clock::now(); + ms_part = std::chrono::duration_cast( + now.time_since_epoch()) % 1000; + time_t_part = std::chrono::system_clock::to_time_t(now); + localtime_r(&time_t_part, &tm_buf); + std::strftime(timeStr, sizeof(timeStr), "%Y-%m-%d %H:%M:%S", &tm_buf); + + std::cout << "END my_queue_send no." << count++ + << " >>>> " << timeStr + << "." << std::setw(3) << std::setfill('0') << ms_part.count() + << "\n\n"; + } + + g_mqproducer_blocked_times = 0; + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + + std::cout << "[mqproducerThread] 正常退出\n"; + } + catch (const std::exception& e) { + std::cerr << "[mqproducerThread] std::exception: " << e.what() << std::endl; + std::lock_guard lock(m_threadCheckMutex); + m_needRestartProducerThread = true; + } + catch (...) { + std::cerr << "[mqproducerThread] unknown exception\n"; + std::lock_guard lock(m_threadCheckMutex); + m_needRestartProducerThread = true; + } +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////用例,除通讯外其他功能都可实现 + +//int main(int argc char** argv) //变为线程 + +extern thread_info_t thread_info[THREAD_CONNECTIONS]; + +void* cloudfrontthread(void* arg) { +/////////////////////////////////////// + ThreadArgs* args = static_cast(arg); + int argc = args->argc; + char **argv = args->argv; + + printf("argc = %d, argv[0] = %s\n", argc, argv[0]); + + //添加线程处理 + int index = *(int*)argv[0]; + + // 更新线程状态为运行中 + pthread_mutex_lock(&thread_info[index].lock); + printf("cloudfrontthread %d started\n", index); + thread_info[index].state = THREAD_RUNNING; + pthread_mutex_unlock(&thread_info[index].lock); + +/////////////////////////////////////// + // 解析命令行参数 + if(!parse_param(argc,argv)){ + std::cerr << "process param error,exit" << std::endl; + return nullptr; + } + + // 线程使用完后清理参数 + delete args; + + //路径获取 + FRONT_PATH = get_parent_directory(); + std::cout << "FRONT_PATH:" << FRONT_PATH << std::endl; + + //声明前置 + std::unique_ptr FrontProcess; + FrontProcess = make_unique(); + + std::cout << "[Main] Program running in background.\n"; + + // 5) 主线程保持后台运行 + while(running) { + { + std::lock_guard lock(FrontProcess->m_threadCheckMutex); + + if (FrontProcess->m_needRestartFrontThread) { + std::cout << "[Monitor] Restarting FrontThread..." << std::endl; + FrontProcess->StartFrontThread(); + FrontProcess->m_needRestartFrontThread = false; + } + + if (FrontProcess->m_needRestartConsumerThread) { + std::cout << "[Monitor] Restarting MQConsumerThread..." << std::endl; + FrontProcess->StartMQConsumerThread(); + FrontProcess->m_needRestartConsumerThread = false; + } + + if (FrontProcess->m_needRestartProducerThread) { + std::cout << "[Monitor] Restarting MQProducerThread..." << std::endl; + FrontProcess->StartMQProducerThread(); + FrontProcess->m_needRestartProducerThread = false; + } + + if (FrontProcess->m_needRestartTimerThread) { + std::cout << "[Monitor] Restarting TimerThread..." << std::endl; + FrontProcess->StartTimerThread(); + FrontProcess->m_needRestartTimerThread = false; + } + } + + std::this_thread::sleep_for(std::chrono::seconds(60));//每分钟检测一次 + } + + return nullptr; +} + diff --git a/LFtid1056/cloudfront/code/rocketmq.cpp b/LFtid1056/cloudfront/code/rocketmq.cpp new file mode 100644 index 0000000..dd47b5c --- /dev/null +++ b/LFtid1056/cloudfront/code/rocketmq.cpp @@ -0,0 +1,1618 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +////////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "rocketmq/DefaultMQProducer.h" +#include "rocketmq/DefaultMQPushConsumer.h" +#include "rocketmq/MQMessageListener.h" +#include "rocketmq/MQMessageExt.h" +#include "rocketmq/MQMessageQueue.h" +#include "rocketmq/MQSelector.h" +#include "rocketmq/SendResult.h" +#include "rocketmq/SessionCredentials.h" + +#include "rocketmq.h" +#include "nlohmann/json.hpp" +#include "log4.h" +#include "interface.h" +#include "front.h" + +////////////////////////////////////////////////////////////////////////////////////////////////////////// + +using namespace std; + +using nlohmann::json; + +////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef nullptr +#define nullptr NULL +#endif + +/////////////////////////////////////////////////////////////////////////////////////////////////////////// + +std::mutex queue_data_list_mutex; +std::list queue_data_list; + +static rocketmq::RocketMQProducer* g_producer = nullptr; //生产者 + +/////////////////////////////////////////////////////////////////////////////////////////////////////////// + +//台账 +extern std::mutex ledgermtx; +extern std::vector terminal_devlist; + +//前置进程 +extern unsigned int g_node_id; +extern int g_front_seg_index; +extern std::string subdir; +extern std::string FRONT_INST; + +//生产者线程控制 +extern uint32_t g_mqproducer_blocked_times; + +//初始化标志 +extern int INITFLAG; + +//测试用的终端数组 +extern std::vector TESTARRAY; + +////////////////////////////////////////////////////////////////////////////////////////////////////////外部文件函数声明 + +extern void execute_bash(std::string fun,int process_num,std::string type); +extern int recall_json_handle(const std::string& jstr); + +//////////////////////////////////////////////////////////////////////////////////////////////////////本文件函数向前声明 + +bool createXmlFile(int devindex, int mpindex, bool realData, bool soeData, int limit,std::string type); +std::string prepare_update(const std::string& code_str, const terminal_dev& json_data,const std::string& guid); +bool writeToFile(const std::string& filePath, const std::string& xmlContent); + +////////////////////////////////////////////////////////////////////////////////////////////////////// + +namespace rocketmq { + +//----------------------------------------------------------------------------- +// RocketMQConsumer 方法实现 +//----------------------------------------------------------------------------- + +RocketMQConsumer::RocketMQConsumer(const std::string& consumerGroup, + const std::string& nameServer) + : consumer_(consumerGroup) + , listener_(nullptr) { + // 设置 NameServer 地址 + consumer_.setNamesrvAddr(nameServer); + + // 设置默认的会话凭证 + consumer_.setSessionCredentials(G_MQCONSUMER_ACCESSKEY, G_MQCONSUMER_SECRETKEY, G_MQCONSUMER_CHANNEL); + + // 初始化内部监听器 + listener_ = new InternalListener(this); +} + +void RocketMQConsumer::subscribe(const std::string& topic, + const std::string& tag, + MessageCallback callback) { + // 调用 C++ 接口的订阅方法,subExpression 参数支持 Tag 过滤,如 "TagA || TagB" + consumer_.subscribe(topic, tag); + std::cout << "[RocketMQConsumer] 已订阅 Topic: " << topic << ", Tag 表达式: " << tag << std::endl; + + // 将 topic:tag 作为键,保存回调函数 + std::string key = topic + ":" + tag; + std::lock_guard lock(callbackMutex_); + callbackMap_[key] = callback; +} + +void RocketMQConsumer::start() { + // 注册消息监听器 + consumer_.registerMessageListener(listener_); + + // 启动消费者 + try { + consumer_.start(); + std::cout << "[RocketMQConsumer] Consumer 已启动,等待消息..." << std::endl; + } catch (const MQClientException& e) { + std::cerr << "[RocketMQConsumer] 启动失败: " << e.what() << std::endl; + throw; + } +} + +void RocketMQConsumer::shutdown() { + try { + consumer_.shutdown(); + std::cout << "[RocketMQConsumer] Consumer 已关闭。" << std::endl; + } catch (const MQClientException& e) { + std::cerr << "[RocketMQConsumer] 关闭失败: " << e.what() << std::endl; + } +} + +RocketMQConsumer::~RocketMQConsumer() { + // 先关闭消费者 + try { + consumer_.shutdown(); + } catch (...) { + // 忽略异常 + } + // 清理监听器 + delete listener_; + listener_ = nullptr; + std::cout << "[RocketMQConsumer] Consumer 销毁完毕。" << std::endl; +} + +//----------------------------------------------------------------------------- +// RocketMQProducer 方法实现 +//----------------------------------------------------------------------------- + +RocketMQProducer::RocketMQProducer(const std::string& groupName, + const std::string& nameServer) + : producer_(groupName) { + // 设置 NameServer 地址 + producer_.setNamesrvAddr(nameServer); + + // 设置默认的会话凭证 + producer_.setSessionCredentials(G_MQPRODUCER_ACCESSKEY, G_MQPRODUCER_SECRETKEY, ""); + + // 启动生产者 + try { + producer_.start(); + std::cout << "[RocketMQProducer] Producer 已启动。" << std::endl; + } catch (const MQClientException& e) { + std::cerr << "[RocketMQProducer] 启动失败: " << e.what() << std::endl; + throw; + } +} + +void RocketMQProducer::sendMessage(const std::string& body, + const std::string& topic, + const std::string& tags, + const std::string& keys) { + try { + // 创建消息对象 + MQMessage msg(topic, tags, keys, body); + + // 同步发送 + SendResult result = producer_.send(msg); + std::cout << "[RocketMQProducer] 消息发送成功. " + << "Topic=" << topic + << ", MsgID=" << result.getMsgId() + << ", Status=" << result.getSendStatus() + << std::endl; + } catch (const MQClientException& e) { + std::cerr << "[RocketMQProducer] 发送失败: " << e.what() << std::endl; + // 根据需要进行重试或日志记录 + } catch (const std::exception& e) { + std::cerr << "[RocketMQProducer] 异常: " << e.what() << std::endl; + } catch (...) { + std::cerr << "[RocketMQProducer] 未知错误,消息发送失败。" << std::endl; + } +} + +void RocketMQProducer::sendMessageOrderly(const std::string& body, + const std::string& topic, + const std::string& tags, + const std::string& keys) { + try { + // 创建消息对象 + MQMessage msg(topic, tags, keys, body); + + // 使用轮询队列选择器进行顺序发送 + SendResult result = producer_.send(msg, &selector_, nullptr); + std::cout << "[RocketMQProducer] 顺序消息发送成功. " + << "Topic=" << topic + << ", MsgID=" << result.getMsgId() + << ", Status=" << result.getSendStatus() + << std::endl; + } catch (const MQClientException& e) { + std::cerr << "[RocketMQProducer] 顺序发送失败: " << e.what() << std::endl; + } catch (const std::exception& e) { + std::cerr << "[RocketMQProducer] 异常: " << e.what() << std::endl; + } catch (...) { + std::cerr << "[RocketMQProducer] 未知错误,顺序消息发送失败。" << std::endl; + } +} + +RocketMQProducer::~RocketMQProducer() { + try { + producer_.shutdown(); + std::cout << "[RocketMQProducer] Producer 已关闭。" << std::endl; + } catch (const MQClientException& e) { + std::cerr << "[RocketMQProducer] 关闭失败: " << e.what() << std::endl; + } +} + + + +} // namespace rocketmq + +///////////////////////////////////////////////////////////////////////////////////////////////////生产者接口 +// 初始化生产者 +void InitializeProducer(rocketmq::RocketMQProducer*& producer) { + if (producer == nullptr) { + try { + producer = new rocketmq::RocketMQProducer(G_ROCKETMQ_PRODUCER, G_MQPRODUCER_IPPORT); + } catch (const std::exception& e) { + std::cerr << "[InitializeProducer] Failed to initialize producer: " << e.what() << std::endl; + throw; + } + } +} + +// 关闭并销毁生产者(在程序结束时调用一次) +void ShutdownAndDestroyProducer() +{ + if (g_producer != NULL) { + delete g_producer; + g_producer = NULL; + } +} + +// 使用 C++ 接口封装的 RocketMQProducer 类 +void rocketmq_producer_send(rocketmq::RocketMQProducer* producer, + const std::string& body, + const std::string& topic) { + if (!producer) { + std::cerr << "[rocketmq_producer_send] producer 不可用,未初始化\n"; + return; + } + + const std::string& tags = G_ROCKETMQ_TAG; + const std::string& keys = G_ROCKETMQ_KEY; + + try { + producer->sendMessage(body, topic, tags, keys); + } catch (const std::exception& e) { + std::cerr << "[rocketmq_producer_send] 发送失败: " << e.what() << std::endl; + DIY_ERRORLOG("process", "【ERROR】前置的%s%d号进程 MQ发送失败", get_front_msg_from_subdir(), g_front_seg_index); + } +} + +//mq发送接口 +void my_rocketmq_send(queue_data_t& data,rocketmq::RocketMQProducer* producer) +{ + static std::string topic; + static std::string cfg_His_tp; + static std::string cfg_PLT_tp; + static std::string cfg_PST_tp; + static std::string cfg_Evt_tp; + static std::string cfg_Alm_tp; + static std::string cfg_Rt_tp; + static bool init = false; + if (!init) { + + cfg_His_tp = TOPIC_STAT; + cfg_PLT_tp = TOPIC_PLT; + cfg_PST_tp = TOPIC_PST; + cfg_Evt_tp = TOPIC_EVENT; + cfg_Alm_tp = TOPIC_ALARM; + cfg_Rt_tp = TOPIC_RTDATA; + + init = true; + } + + std::string key = data.mp_id; + std::string senddata = data.strText; + if (data.strTopic == "HISDATA") + { + topic = cfg_His_tp; + } + else if (data.strTopic == "PLT") + { + topic = cfg_PLT_tp; + } + else if (data.strTopic == "PST") + { + topic = cfg_PST_tp; + } + else if (data.strTopic == "Alm") + { + topic = cfg_Alm_tp; + } + else if (data.strTopic == "RTDATA") + { + topic = cfg_Rt_tp; + } + else + { + topic = data.strTopic; + + } + + rocketmq_producer_send(producer,senddata,topic); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////查找台账下标 +// 根据终端 ID 查找 terminal_devlist 中的索引,找不到返回 -1 +int find_dev_index_from_dev_id(const std::string& dev_id) { + for (size_t i = 0; i < terminal_devlist.size(); ++i) { + if (terminal_devlist[i].terminal_id == dev_id) { + return static_cast(i); + } + } + return -1; // 未找到 +} + +int find_mp_index_from_mp_id(const std::string& mp_id) { + for (const auto& dev : terminal_devlist) { + for (size_t j = 0; j < dev.line.size(); ++j) { + if (dev.line[j].monitor_id == mp_id) { + return static_cast(j); // 返回 line[] 的下标 + } + } + } + return -1; // 未找到 +} + +/////////////////////////////////////////////////////////////////////////////////////////////////回调函数的json处理 + +std::string parseJsonMessageRC(const std::string& inputJson) { + // 解析输入 JSON 字符串 + json root; + try { + root = json::parse(inputJson); + } catch (const std::exception& e) { + std::cerr << "Error parsing JSON: " << e.what() << std::endl; + return ""; + } + + // 提取 "messageBody" 部分(它是一个字符串) + if (!root.contains("messageBody") || !root["messageBody"].is_string()) { + std::cerr << "'messageBody' is missing or is not a string" << std::endl; + return ""; + } + + std::string messageBodyStr = root["messageBody"].get(); + if (messageBodyStr.empty()) { + std::cerr << "'messageBody' is empty" << std::endl; + return ""; + } + + // 解析 messageBody 中的 JSON 字符串 + json messageBody; + try { + messageBody = json::parse(messageBodyStr); + } catch (const std::exception& e) { + std::cerr << "Failed to parse 'messageBody' JSON: " << e.what() << std::endl; + return ""; + } + + // 提取 "guid" 部分 + if (!messageBody.contains("guid") || !messageBody["guid"].is_string()) { + std::cerr << "'guid' is missing or is not a string" << std::endl; + return ""; + } + std::string guid = messageBody["guid"].get(); + + // 发送 guid 回复 + send_reply_to_queue(guid, "1", "收到补招指令"); + + // 提取 "data" 部分 + if (!messageBody.contains("data") || !messageBody["data"].is_array()) { + std::cerr << "'data' is missing or is not an array" << std::endl; + return ""; + } + + // 返回 "data" 数组的字符串形式 + try { + return messageBody["data"].dump(); // 默认带缩进;如需去除缩进:dump(-1) + } catch (const std::exception& e) { + std::cerr << "Error converting 'data' to string: " << e.what() << std::endl; + return ""; + } +} + +bool parseJsonMessageRT(const std::string& body,std::string& devSeries,std::string& line,bool& realData,bool& soeData,int& limit){ + json root; + try { + root = json::parse(body); + } catch (const std::exception& e) { + std::cerr << "Failed to parse JSON message: " << e.what() << std::endl; + return false; + } + + // 提取 "messageBody" 字符串 + if (!root.contains("messageBody") || !root["messageBody"].is_string()) { + std::cerr << "'messageBody' is missing or not a string." << std::endl; + return false; + } + + std::string messageBodyStr = root["messageBody"]; + if (messageBodyStr.empty()) { + std::cerr << "'messageBody' is empty." << std::endl; + return false; + } + + // 解析 "messageBody" 中的 JSON 字符串 + json messageBody; + try { + messageBody = json::parse(messageBodyStr); + } catch (const std::exception& e) { + std::cerr << "Failed to parse 'messageBody': " << e.what() << std::endl; + return false; + } + + // 检查并提取字段 + if (!messageBody.contains("devSeries") || + !messageBody.contains("line") || + !messageBody.contains("realData") || + !messageBody.contains("soeData") || + !messageBody.contains("limit")) + { + std::cerr << "Missing expected fields in 'messageBody'." << std::endl; + return false; + } + + try { + devSeries = messageBody["devSeries"].get(); + line = messageBody["line"].get(); + realData = messageBody["realData"].get(); + soeData = messageBody["soeData"].get(); + limit = messageBody["limit"].get(); + } catch (const std::exception& e) { + std::cerr << "Type error while extracting fields: " << e.what() << std::endl; + return false; + } + + // 提取 guid + std::string guid; + if (messageBody.contains("guid") && messageBody["guid"].is_string()) { + guid = messageBody["guid"].get(); + } + + // 回复:执行结果直接看实时数据,不需要再回复,1是收到消息 + if (!guid.empty()) { + send_reply_to_queue(guid, "1", "收到三秒数据指令"); + } + + return true; +} + +bool parseJsonMessageSET(const std::string& json_str) { + json root; + try { + root = json::parse(json_str); + } catch (const std::exception& e) { + std::cout << "Error parsing JSON: " << e.what() << std::endl; + return false; + } + + if (!root.contains("messageBody") || !root["messageBody"].is_string()) { + std::cerr << "'messageBody' is missing or is not a string" << std::endl; + return false; + } + + std::string messageBodyStr = root["messageBody"]; + if (messageBodyStr.empty()) { + std::cerr << "'messageBody' is empty" << std::endl; + return false; + } + + json messageBody; + try { + messageBody = json::parse(messageBodyStr); + } catch (const std::exception& e) { + std::cerr << "Failed to parse 'messageBody': " << e.what() << std::endl; + return false; + } + + // 获取字段 + if (!messageBody.contains("guid") || !messageBody.contains("code") || + !messageBody.contains("processNo") || !messageBody.contains("fun") || + !messageBody.contains("frontType")) { + std::cout << "Missing one or more required fields in messageBody." << std::endl; + return false; + } + + std::string guid, code_str, fun, frontType; + int index_value = 0; + + try { + guid = messageBody["guid"].get(); + code_str = messageBody["code"].get(); + index_value = messageBody["processNo"].get(); + fun = messageBody["fun"].get(); + frontType = messageBody["frontType"].get(); + } catch (const std::exception& e) { + std::cerr << "Field parsing error: " << e.what() << std::endl; + return false; + } + + // 判断进程号是否匹配 + if (index_value != g_front_seg_index && g_front_seg_index != 0) { + std::cout << "msg index: " << index_value << " doesn't match self index: " << g_front_seg_index << std::endl; + return true; + } + + std::cout << "msg index: " << index_value << " self index: " << g_front_seg_index << std::endl; + + DIY_INFOLOG("process", "【NORMAL】前置的%s%d号进程处理topic:%s_%s的进程控制消息",get_front_msg_from_subdir(), g_front_seg_index,FRONT_INST.c_str(), G_MQCONSUMER_TOPIC_SET.c_str()); + + if (code_str == "set_process") { + if (!messageBody.contains("processNum")) { + std::cout << "Missing 'processNum' in JSON." << std::endl; + return false; + } + + int processNum = 0; + try { + processNum = messageBody["processNum"].get(); + } catch (...) { + std::cout << "'processNum' parsing failed." << std::endl; + return false; + } + + // 校验参数并执行 + if ((fun == "reset" || fun == "add") && + (processNum >= 1 && processNum < 10) && + (frontType == "stat" || frontType == "recall" || frontType == "all")) { + + if (g_node_id == STAT_DATA_BASE_NODE_ID && g_front_seg_index == 1) { + + execute_bash(fun, processNum, frontType); + + DIY_WARNLOG("process", "【WARN】前置的%s%d号进程执行指令:%s,reset表示重启所有进程,add表示添加进程",get_front_msg_from_subdir(), g_front_seg_index, fun.c_str()); + + send_reply_to_queue(guid, "1", "收到重置进程指令,重启所有进程!"); + std::cout << "this msg should only execute once" << std::endl; + } else { + std::cout << "only cfg_stat_data index 1 can control process, this process not handle this msg" << std::endl; + } + } + else if (fun == "delete") { + + send_reply_to_queue(guid, "1", "收到删除进程指令,这个进程将会重启 "); + + DIY_WARNLOG("process", "【WARN】前置的%s%d号进程执行指令:%s,即将重启",get_front_msg_from_subdir(), g_front_seg_index, fun.c_str()); + + std::this_thread::sleep_for(std::chrono::seconds(10)); + ::_exit(-1039); // 进程退出 + } + else { + std::cout << "param is not executable" << std::endl; + } + } else { + std::cout << "set process code str error" << std::endl; + } + + return true; +} + +bool parseJsonMessageLOG(const std::string& json_str) { + json root; + try { + root = json::parse(json_str); + } catch (const std::exception& e) { + std::cout << "Error parsing JSON: " << e.what() << std::endl; + return false; + } + + // 提取 messageBody(JSON 字符串) + if (!root.contains("messageBody") || !root["messageBody"].is_string()) { + std::cerr << "'messageBody' is missing or not a string" << std::endl; + return false; + } + + std::string messageBodyStr = root["messageBody"]; + if (messageBodyStr.empty()) { + std::cerr << "'messageBody' is empty." << std::endl; + return false; + } + + json messageBody; + try { + messageBody = json::parse(messageBodyStr); + } catch (const std::exception& e) { + std::cerr << "Failed to parse 'messageBody': " << e.what() << std::endl; + return false; + } + + // 校验字段是否存在 + static const std::array required_fields = { + "guid", "code", "processNo", "id", "level", "grade", "logtype", "frontType" + }; + + for (const auto& field : required_fields) { + if (!messageBody.contains(field)) { + std::cout << "Missing '" << field << "' in messageBody." << std::endl; + return false; + } + } + + // 提取字段 + std::string guid, code_str, id, level, grade, logtype, frontType; + int processNo = 0; + + try { + guid = messageBody["guid"].get(); + code_str = messageBody["code"].get(); + processNo = messageBody["processNo"].get(); + id = messageBody["id"].get(); + level = messageBody["level"].get(); + grade = messageBody["grade"].get(); + logtype = messageBody["logtype"].get(); + frontType = messageBody["frontType"].get(); + } catch (const std::exception& e) { + std::cerr << "Error extracting fields: " << e.what() << std::endl; + return false; + } + + // 判断进程号是否匹配 + if (processNo != g_front_seg_index) { + std::cout << "msg index: " << processNo << " doesn't match self index: " << g_front_seg_index << std::endl; + return true; + } + + // 判断 frontType 是否匹配 + if (frontType != subdir) { + std::cout << "msg frontType: " << frontType << " doesn't match self frontType: " << subdir << std::endl; + return true; + } + + DIY_INFOLOG("process", "【NORMAL】前置的%s%d号进程处理日志上送消息", get_front_msg_from_subdir(), g_front_seg_index); + + std::cout << "msg index: " << processNo << " self index: " << g_front_seg_index << std::endl; + std::cout << "msg frontType: " << frontType << " self frontType: " << subdir << std::endl; + + // 回复消息 + send_reply_to_queue(guid, "1", "收到实时日志指令"); + + if (code_str == "set_log") { + // 校验数据合法性 + bool valid = + (level == "terminal" || level == "measurepoint") && + (grade == "NORMAL" || grade == "DEBUG") && + (logtype == "com" || logtype == "data") && + (!id.empty() && !is_blank(id)); + + if (valid) { + process_log_command(id, level, grade, logtype); + } else { + std::cout << "type doesn't match" << std::endl; + DIY_WARNLOG("process", "【WARN】前置的%s%d号进程处理日志上送消息,格式不正确", get_front_msg_from_subdir(), g_front_seg_index); + } + + std::cout << "this msg should only execute once" << std::endl; + } + + return true; +} + +bool parseJsonMessageUD(const std::string& json_str, const std::string& output_dir) { + json root; + try { + root = json::parse(json_str); + } catch (...) { + std::cout << "Error parsing JSON." << std::endl; + return false; + } + + if (!root.contains("messageBody") || !root["messageBody"].is_string()) { + std::cerr << "'messageBody' is missing or is not a string" << std::endl; + return false; + } + + std::string messageBodyStr = root["messageBody"]; + if (messageBodyStr.empty()) { + std::cerr << "'messageBody' is empty." << std::endl; + return false; + } + + json messageBody; + try { + messageBody = json::parse(messageBodyStr); + } catch (...) { + std::cerr << "Failed to parse 'messageBody' JSON." << std::endl; + return false; + } + + // 提取字段 + std::string code_str = messageBody.value("code", ""); + int process_No = messageBody.value("processNo", -1); + std::string guid = messageBody.value("guid", ""); + + if (guid.empty() || code_str.empty() || process_No == -1) { + std::cout << "Missing required fields: guid/code/processNo" << std::endl; + return false; + } + + if (process_No != g_front_seg_index && g_front_seg_index != 0) { + std::cout << "msg index: " << process_No << " doesn't match self index: " << g_front_seg_index << std::endl; + return true; + } + + std::cout << "msg index: " << process_No << " self index: " << g_front_seg_index << std::endl; + + DIY_INFOLOG("process", "【NORMAL】前置的%s%d号进程处理topic:%s_%s的台账更新消息", + get_front_msg_from_subdir(), g_front_seg_index, FRONT_INST.c_str(), G_MQCONSUMER_TOPIC_UD.c_str()); + + send_reply_to_queue(guid, "1", "收到台账更新指令"); + + if (code_str == "add_terminal" || code_str == "ledger_modify") { + std::cout << "add or update ledger" << std::endl; + + if (messageBody.contains("data") && messageBody["data"].is_array()) { + for (const auto& item : messageBody["data"]) { + terminal_dev json_data; + + json_data.terminal_id = item.value("id", ""); + json_data.terminal_code = item.value("name", ""); + json_data.org_name = item.value("org_name", ""); + json_data.maint_name = item.value("maint_name", ""); + json_data.station_name = item.value("stationName", ""); + json_data.tmnl_factory = item.value("manufacturer", ""); + json_data.tmnl_status = item.value("status", ""); + json_data.dev_type = item.value("devType", ""); + json_data.dev_key = item.value("devKey", ""); + json_data.dev_series = item.value("series", ""); + + int procNo = item.value("processNo", -1); + json_data.processNo = std::to_string(procNo); + + json_data.addr_str = item.value("ip", ""); + json_data.port = item.value("port", ""); + json_data.timestamp = item.value("updateTime", ""); + + if (item.contains("monitorData") && item["monitorData"].is_array()) { + int j = 0; + for (const auto& monitor_item : item["monitorData"]) { + if (j >= 10) break; + auto& m = json_data.line[j++]; + m.monitor_id = monitor_item.value("id", ""); + m.monitor_name = monitor_item.value("name", ""); + m.voltage_level = monitor_item.value("voltageLevel", ""); + m.status = monitor_item.value("status", ""); + m.logical_device_seq = monitor_item.value("lineNo", ""); + m.terminal_connect = monitor_item.value("ptType", ""); + m.timestamp = json_data.timestamp; + m.terminal_code = json_data.terminal_code; + } + } + + print_terminal(json_data); + + std::string xmlContent = prepare_update(code_str, json_data, guid); + if (!xmlContent.empty()) { + char nodeid[20]; + std::sprintf(nodeid, "%u", g_node_id); + std::string file_name = output_dir + "/" + nodeid + "_" + std::to_string(g_front_seg_index) + "_" + json_data.terminal_id + "_" + code_str + ".xml"; + writeToFile(file_name, xmlContent); + } + } + } + } else if (code_str == "delete_terminal") { + std::cout << "delete ledger" << std::endl; + if (messageBody.contains("data") && messageBody["data"].is_array()) { + for (const auto& item : messageBody["data"]) { + terminal_dev json_data{}; + auto id = item.value("id", ""); + json_data.terminal_id = id; + + std::string xmlContent = prepare_update(code_str, json_data, guid); + if (!xmlContent.empty()) { + char nodeid[20]; + std::sprintf(nodeid, "%u", g_node_id); + std::string file_name = output_dir + "/" + nodeid + "_" + std::to_string(g_front_seg_index) + "_" + json_data.terminal_id + "_delete_terminal.xml"; + writeToFile(file_name, xmlContent); + } + } + } + } else { + std::cout << "code_str error" << std::endl; + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////回调函数 + +rocketmq::ConsumeStatus myMessageCallbackrtdata(const rocketmq::MQMessageExt& msg) { + //未初始化不处理消费 + if (INITFLAG != 1) { + return rocketmq::RECONSUME_LATER; + } + + std::string body = msg.getBody(); + std::string key = msg.getKeys(); + + if (body.empty()) { + std::cerr << "Message body is NULL or empty." << std::endl; + return rocketmq::RECONSUME_LATER; + } + + // 日志记录 + DIY_INFOLOG("process", "【NORMAL】前置消费topic:%s_%s的实时触发消息",FRONT_INST.c_str(), G_MQCONSUMER_TOPIC_RT.c_str()); + + std::cout << "rtdata Callback received message: " << body << std::endl; + if (!key.empty()) { + std::cout << "Message Key: " << key << std::endl; + } else { + std::cout << "Message Key: N/A" << std::endl; + } + + // 消息解析 + std::string devid, line; + bool realData = false, soeData = false; + int limit = 0; + + if (!parseJsonMessageRT(body, devid, line, realData, soeData, limit)) { + std::cerr << "Failed to parse the JSON message." << std::endl; + DIY_ERRORLOG("process", "【ERROR】前置消费topic:%s_%s的实时触发消息失败,消息的json格式不正确", FRONT_INST.c_str(), G_MQCONSUMER_TOPIC_RT.c_str()); + return rocketmq::RECONSUME_LATER; + } + + // 加锁访问台账 + int dev_index; + int mp_index; + if( !devid.empty() && !line.empty()){ + std::lock_guard lock(ledgermtx); + dev_index = find_dev_index_from_dev_id(devid); + mp_index = find_mp_index_from_mp_id(line); + } + else{ + std::cerr << "rtdata is NULL." << std::endl; + DIY_ERRORLOG("process","【ERROR】前置的%s%d号进程处理topic:%s_%s的补招触发消息失败,消息的json结构不正确",get_front_msg_from_subdir(), g_front_seg_index,FRONT_INST.c_str(), G_MQCONSUMER_TOPIC_RT.c_str()); + } + + + if (dev_index == -1 || mp_index == -1) { + std::cerr << "dev index or mp index is not found" << std::endl; + return rocketmq::RECONSUME_LATER; + } + + // 写入 XML + if (!createXmlFile(dev_index, mp_index, realData, soeData, limit, "new")) { + DIY_ERRORLOG("process", "【ERROR】前置无法创建实时数据触发文件"); + std::cerr << "Failed to create the XML file." << std::endl; + return rocketmq::RECONSUME_LATER; + } + + return rocketmq::CONSUME_SUCCESS; +} + +rocketmq::ConsumeStatus myMessageCallbackupdate(const rocketmq::MQMessageExt& msg) { + //未初始化不处理消费 + if (INITFLAG != 1) { + return rocketmq::RECONSUME_LATER; + } + + std::string body = msg.getBody(); + std::string key = msg.getKeys(); + + if (body.empty()) { + std::cerr << "Message body is NULL or empty." << std::endl; + return rocketmq::RECONSUME_LATER; + } + + // 打印日志 + std::cout << "ledger update Callback received message: " << body << std::endl; + if (!key.empty()) { + std::cout << "Message Key: " << key << std::endl; + } else { + std::cout << "Message Key: N/A" << std::endl; + } + + // 调用业务逻辑处理函数 + std::string updatefilepath = FRONT_PATH + "/etc/ledgerupdate"; + if (!parseJsonMessageUD(body, updatefilepath)) { + DIY_ERRORLOG("process","【ERROR】前置的%s%d号进程处理topic:%s_%s的台账更新消息失败,消息的json结构不正确",get_front_msg_from_subdir(), g_front_seg_index,FRONT_INST.c_str(), G_MQCONSUMER_TOPIC_UD.c_str()); + } + + return rocketmq::CONSUME_SUCCESS; +} + +rocketmq::ConsumeStatus myMessageCallbackset(const rocketmq::MQMessageExt& msg) { + //未初始化不处理消费 + if (INITFLAG != 1) { + return rocketmq::RECONSUME_LATER; + } + + std::string body = msg.getBody(); + std::string key = msg.getKeys(); + + if (body.empty()) { + std::cerr << "Message body is NULL or empty." << std::endl; + return rocketmq::RECONSUME_LATER; + } + + // 打印消息内容和 key + std::cout << "process set Callback received message: " << body << std::endl; + if (!key.empty()) { + std::cout << "Message Key: " << key << std::endl; + } else { + std::cout << "Message Key: N/A" << std::endl; + } + + // 调用业务处理逻辑 + if (!parseJsonMessageSET(body)) { + DIY_ERRORLOG("process","【ERROR】前置的%s%d号进程处理topic:%s_%s的进程控制消息失败,消息的json结构不正确",get_front_msg_from_subdir(), g_front_seg_index,FRONT_INST.c_str(), G_MQCONSUMER_TOPIC_SET.c_str()); + } + + return rocketmq::CONSUME_SUCCESS; +} + +rocketmq::ConsumeStatus myMessageCallbacklog(const rocketmq::MQMessageExt& msg) { + //未初始化不处理消费 + if (INITFLAG != 1) { + return rocketmq::RECONSUME_LATER; + } + + std::string body = msg.getBody(); + std::string key = msg.getKeys(); + + if (body.empty()) { + std::cerr << "Message body is NULL or empty." << std::endl; + return rocketmq::RECONSUME_LATER; + } + + // 打印日志信息 + std::cout << "log Callback received message: " << body << std::endl; + if (!key.empty()) { + std::cout << "Message Key: " << key << std::endl; + } else { + std::cout << "Message Key: N/A" << std::endl; + } + + // 执行日志上送处理 + if (!parseJsonMessageLOG(body)) { + DIY_ERRORLOG("process", "【ERROR】前置的%s%d号进程处理topic:%s_%s的日志上送消息失败,消息的json结构不正确",get_front_msg_from_subdir(), g_front_seg_index,FRONT_INST.c_str(), G_MQCONSUMER_TOPIC_LOG.c_str()); + } + + return rocketmq::CONSUME_SUCCESS; +} + +rocketmq::ConsumeStatus myMessageCallbackrecall(const rocketmq::MQMessageExt& msg) { + + //未初始化不处理消费 + if (INITFLAG != 1) { + return rocketmq::RECONSUME_LATER; + } + + // 调试输出 + std::cout << "myMessageCallbackrecall" << std::endl; + + std::string body = msg.getBody(); + std::string key = msg.getKeys(); + + if (body.empty()) { + std::cerr << "Message body is NULL or empty." << std::endl; + return rocketmq::RECONSUME_LATER; + } + + // 打印消息内容 + std::cout << "recall Callback received message: " << body << std::endl; + if (!key.empty()) { + std::cout << "Message Key: " << key << std::endl; + } else { + std::cout << "Message Key: N/A" << std::endl; + } + + // 解析 JSON 字符串 + std::string result = parseJsonMessageRC(body); // 使用 std::string 接收解析结果 + std::cout << "parseJsonMessageRC: " << result << std::endl; + + if (!result.empty()) { + + std::lock_guard lock(ledgermtx); + recall_json_handle(result); + + } else { + std::cerr << "recall data is NULL." << std::endl; + DIY_ERRORLOG("process","【ERROR】前置的%s%d号进程处理topic:%s_%s的补招触发消息失败,消息的json结构不正确",get_front_msg_from_subdir(), g_front_seg_index,FRONT_INST.c_str(), G_MQCONSUMER_TOPIC_RC.c_str()); + } + + return rocketmq::CONSUME_SUCCESS; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////生成实时触发和停止文件 + +//根据监测点序号和终端序号来生成触发文件,后续可修改 +std::string createnewXmlContent(int devindex, int mpindex, bool realData, bool soeData, int limit) +{ + std::ostringstream xmlContent; + xmlContent << "\n" + << "\n" + << " \n" + << " \n" + << " \n" + << "\n"; + return xmlContent.str(); +} + +//根据监测点序号和终端序号来生成触发文件,后续可修改 +std::string createdeleteXmlContent(int devindex, int mpindex) +{ + std::ostringstream xmlContent; + xmlContent << "\n" + << "\n" + << " \n" + << " \n" + << " \n" + << "\n"; + return xmlContent.str(); +} + +// 写入 XML 内容到文件的函数 +bool writeToFile(const std::string& filePath, const std::string& xmlContent) +{ + // 打开文件流以写入 XML 内容 + std::ofstream outFile(filePath.c_str()); // 使用 c_str() 转换为 const char* + if (outFile.is_open()) { + outFile << xmlContent; // 写入内容 + outFile.close(); + std::cout << "XML file created at: " << filePath << std::endl; + return true; + } else { + std::cerr << "Failed to open file for writing: " << filePath << std::endl; + return false; + } +} + +// 创建并写入新的 XML 文件的主函数 +bool createXmlFile(int devindex, int mpindex, bool realData, bool soeData, int limit,std::string type) +{ + std::string xmlContent = ""; + std::string directory = ""; + std::string filePath = ""; + + if(type == "new"){ + // 构造 XML 内容 + xmlContent = createnewXmlContent(devindex, mpindex, realData, soeData, limit); + + // 设置文件路径 + directory = FRONT_PATH + "/etc/trigger3s/"; + filePath = directory + "newtrigger.xml"; + } + else if(type == "delete"){ + // 构造 XML 内容 + xmlContent = createdeleteXmlContent(devindex, mpindex); + + // 设置文件路径 + directory = FRONT_PATH + "/etc/trigger3s/"; + filePath = directory + "deletetrigger.xml"; + } + else{ + std::cerr << "Failed to create xmlfile,type error: " << std::endl; + return false; + } + + + // 创建目录(如果不存在) + if (system(("mkdir -p " + directory).c_str()) != 0) { + std::cerr << "Failed to create directory: " << directory << std::endl; + return false; + } + + // 将 XML 内容写入文件 + return writeToFile(filePath, xmlContent); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////生成台账更新文件 + +void add_indent(std::stringstream& stream, int level) { + for (int i = 0; i < level; ++i) { + stream << " "; // 每一级缩进 2 个空格 + } +} + +std::string prepare_update(const std::string& code_str, const terminal_dev& json_data,const std::string& guid) +{ + std::cout << "prepare update" << std::endl; + + std::stringstream xmlStream; + int indentLevel = 0; // 缩进级别 + + // 根节点 + add_indent(xmlStream, indentLevel); + xmlStream << "" << std::endl; + indentLevel++; + + // 添加 guid 节点 + add_indent(xmlStream, indentLevel); + xmlStream << "" << guid << "" << std::endl; + + if (code_str == "ledger_modify" || code_str == "add_terminal") { + + // 如果是 modify 类型 + if (code_str == "ledger_modify") { + add_indent(xmlStream, indentLevel); + xmlStream << "" << std::endl; + indentLevel++; + } + else { + add_indent(xmlStream, indentLevel); + xmlStream << "" << std::endl; + indentLevel++; + } + + // 添加数据部分 + add_indent(xmlStream, indentLevel); + xmlStream << "" << std::endl; + indentLevel++; + + add_indent(xmlStream, indentLevel); + xmlStream << "" << json_data.terminal_id << "" << std::endl; + + add_indent(xmlStream, indentLevel); + xmlStream << "" << json_data.addr_str << "" << std::endl; // Assuming `addr_str` for IP + + add_indent(xmlStream, indentLevel); + xmlStream << "" << json_data.dev_type << "" << std::endl; + + add_indent(xmlStream, indentLevel); + xmlStream << "" << json_data.maint_name << "" << std::endl; + + add_indent(xmlStream, indentLevel); + xmlStream << "" << json_data.org_name << "" << std::endl; + + add_indent(xmlStream, indentLevel); + xmlStream << "" << json_data.port << "" << std::endl; + + add_indent(xmlStream, indentLevel); + xmlStream << "" << json_data.station_name << "" << std::endl; + + add_indent(xmlStream, indentLevel); + xmlStream << "" << json_data.terminal_code << "" << std::endl; + + add_indent(xmlStream, indentLevel); + xmlStream << "" << json_data.timestamp << "" << std::endl; // Assuming `timestamp` + + add_indent(xmlStream, indentLevel); + xmlStream << "" << json_data.tmnl_factory << "" << std::endl; + + add_indent(xmlStream, indentLevel); + xmlStream << "" << json_data.tmnl_status << "" << std::endl; + + add_indent(xmlStream, indentLevel); + xmlStream << "" << json_data.dev_series << "" << std::endl; + + //lnk20250210 + add_indent(xmlStream, indentLevel); + xmlStream << "" << json_data.processNo << "" << std::endl; + + add_indent(xmlStream, indentLevel); + xmlStream << "" << json_data.dev_key << "" << std::endl; + + // monitorData 部分 + for (int i = 0; json_data.line[i].monitor_id[0] != '\0'; i++) { + const ledger_monitor& monitor = json_data.line[i]; + + add_indent(xmlStream, indentLevel); + xmlStream << "" << std::endl; + indentLevel++; + + add_indent(xmlStream, indentLevel); + xmlStream << "" << monitor.monitor_id << "" << std::endl; + + add_indent(xmlStream, indentLevel); + xmlStream << "" << monitor.monitor_name << "" << std::endl; + + add_indent(xmlStream, indentLevel); + xmlStream << "" << monitor.logical_device_seq << "" << std::endl; + + add_indent(xmlStream, indentLevel); + xmlStream << "" << monitor.voltage_level << "" << std::endl; + + add_indent(xmlStream, indentLevel); + xmlStream << "" << monitor.terminal_connect << "" << std::endl; + + add_indent(xmlStream, indentLevel); + xmlStream << "" << monitor.timestamp << "" << std::endl; + + add_indent(xmlStream, indentLevel); + xmlStream << "" << monitor.terminal_code << "" << std::endl; + + add_indent(xmlStream, indentLevel); + xmlStream << "" << monitor.status << "" << std::endl; + + indentLevel--; + add_indent(xmlStream, indentLevel); + xmlStream << "" << std::endl; + } + + indentLevel--; + add_indent(xmlStream, indentLevel); + xmlStream << "" << std::endl; + + // 结束 modify 或 add 标签 + if (code_str == "ledger_modify") { + indentLevel--; + add_indent(xmlStream, indentLevel); + xmlStream << "" << std::endl; + + } + else { + indentLevel--; + add_indent(xmlStream, indentLevel); + xmlStream << "" << std::endl; + + } + + } else if (code_str == "delete_terminal") { + // 如果是 delete 类型 + add_indent(xmlStream, indentLevel); + xmlStream << "" << std::endl; + indentLevel++; + + add_indent(xmlStream, indentLevel); + xmlStream << "" << std::endl; + indentLevel++; + + add_indent(xmlStream, indentLevel); + xmlStream << "" << json_data.terminal_id << "" << std::endl; + + indentLevel--; + add_indent(xmlStream, indentLevel); + xmlStream << "" << std::endl; + + indentLevel--; + add_indent(xmlStream, indentLevel); + xmlStream << "" << std::endl; + } + else { + std::cerr << "code_str error" << std::endl; + return ""; + } + + // 结束根节点 + indentLevel--; + add_indent(xmlStream, indentLevel); + xmlStream << "" << std::endl; + + return xmlStream.str(); // 返回构造的 XML 字符串 +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////终端连接消息 + +void connect_status_to_queue(const std::string& id, const std::string& datetime, int status) +{ + try { + // 构造 JSON + nlohmann::json jsonObject; + jsonObject["id"] = id; + jsonObject["date"] = datetime; + jsonObject["status"] = status; + + // 构造发送结构 + queue_data_t data; + data.strTopic = G_CONNECT_TOPIC; + data.strText = jsonObject.dump(); // 转换为字符串 + + if (g_node_id == STAT_DATA_BASE_NODE_ID) { + std::lock_guard lock(queue_data_list_mutex); + queue_data_list.push_back(data); + } + } + catch (const std::exception& e) { + std::cerr << "connect_status_to_queue exception: " << e.what() << std::endl; + } +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////响应消息 + +void send_reply_to_queue(const std::string& guid, const std::string& step, const std::string& result) { + try { + // 构造 JSON 对象 + nlohmann::json obj; + obj["guid"] = guid; + obj["step"] = step; + obj["result"] = result; + obj["processNo"] = g_front_seg_index; + obj["frontType"] = get_front_type_from_subdir(); + obj["nodeId"] = FRONT_INST; + + // 构造 queue 消息 + queue_data_t connect_info; + connect_info.strTopic = Topic_Reply_Topic; + connect_info.strText = obj.dump(); // 序列化为 JSON 字符串 + + // 加入发送队列(线程安全) + std::lock_guard lock(queue_data_list_mutex); + queue_data_list.push_back(connect_info); + } + catch (const std::exception& e) { + std::cerr << "send_reply_to_queue exception: " << e.what() << std::endl; + } +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////心跳消息 + +void send_heartbeat_to_queue(const std::string& status) { + try{ + nlohmann::json obj; + obj["nodeId"] = FRONT_INST; + obj["frontType"] = get_front_type_from_subdir(); + obj["processNo"] = g_front_seg_index; + obj["status"] = status; + + queue_data_t connect_info; + connect_info.strTopic = Heart_Beat_Topic; + connect_info.strText = obj.dump(); // 紧凑格式 JSON + + std::lock_guard lock(queue_data_list_mutex); + queue_data_list.push_back(connect_info); + } + catch (const std::exception& e) { + std::cerr << "send_heartbeat_to_queue exception: " << e.what() << std::endl; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////稳态测试函数 + +bool shouldSkipTerminal(const std::string& terminal_id) { + for (size_t i = 0; i < TESTARRAY.size(); ++i) { + if (TESTARRAY[i] == terminal_id) { + return true; + } + } + return false; +} + +void rocketmq_test_300(int mpnum, int front_index, int type,Front* front) { + + if(!INITFLAG){ + std::cout << "前置未初始化完成\n"; + return; + } + + if (!front || !front->m_producer) { + std::cerr << "front 或 producer 无效\n"; + return; + } + + rocketmq::RocketMQProducer* producer = front->m_producer; + + queue_data_t data; + data.strTopic = G_ROCKETMQ_TOPIC_TEST; + data.mp_id = "0"; + + std::vector filenames = { + "long_string.txt", + "PLT_string.txt", + "fluc_string.txt", + "qvvr_string.txt" + }; + + for (const auto& filename : filenames) { + std::ifstream file(filename); + if (!file.is_open()) { + std::cerr << "跳过无法打开的文件: " << filename << std::endl; + continue; + } + + std::stringstream buffer; + buffer << file.rdbuf(); + std::string base_strText = buffer.str(); + + std::time_t t = std::time(nullptr); + std::tm* time_info = std::localtime(&t); + time_info->tm_sec = 0; + std::time_t base_time_t = std::mktime(time_info); + long long current_time_ms = static_cast(base_time_t) * 1000; + + int total_messages = mpnum; + + if (type == 0) { + std::cout << "use ledger send msg" << std::endl; + + for (size_t i = 0; (total_messages > 0 && g_front_seg_index == 1 && g_node_id == 100) && i < terminal_devlist.size(); ++i) { + const auto& dev = terminal_devlist[i]; + + if (shouldSkipTerminal(dev.terminal_id)) { + std::cout << dev.terminal_id << " use true message" << std::endl; + continue; + } + + for (size_t j = 0; j < dev.line.size(); ++j) { + if (dev.line[j].monitor_id.empty()) break; + + data.mp_id = dev.line[j].monitor_id; + data.monitor_no = static_cast(i + j); + std::string modified_time = std::to_string(current_time_ms); + + std::string modified_strText = base_strText; + + // 替换 Monitor + size_t monitor_pos = modified_strText.find("\"Monitor\""); + if (monitor_pos != std::string::npos) { + size_t colon_pos = modified_strText.find(":", monitor_pos); + size_t quote_pos = modified_strText.find("\"", colon_pos); + size_t end_quote_pos = modified_strText.find("\"", quote_pos + 1); + if (colon_pos != std::string::npos && quote_pos != std::string::npos && end_quote_pos != std::string::npos) { + modified_strText.replace(quote_pos + 1, end_quote_pos - quote_pos - 1, data.mp_id); + } + } + + // 替换 TIME + size_t time_pos = modified_strText.find("\"TIME\""); + if (time_pos != std::string::npos) { + size_t colon_pos = modified_strText.find(":", time_pos); + size_t quote_pos = colon_pos; + size_t end_quote_pos = modified_strText.find(",", quote_pos + 1); + if (colon_pos != std::string::npos && quote_pos != std::string::npos && end_quote_pos != std::string::npos) { + modified_strText.replace(quote_pos + 1, end_quote_pos - quote_pos - 1, modified_time); + } + } + + data.strText = modified_strText; + //my_rocketmq_send(data,front->m_producer); + std::lock_guard lock(queue_data_list_mutex); + queue_data_list.push_back(data); + + std::cout << "Sent message " << (i + 1) + << " with Monitor " << data.monitor_no + << " and TIME " << modified_time << std::endl; + + } + } + } else { + std::cout << "use monitor + number send msg" << std::endl; + + for (int i = 0; (total_messages > 0 && g_front_seg_index == 1 && g_node_id == 100) && i < total_messages; ++i) { + std::string monitor_id = "testmonitor" + std::to_string(i); + + data.mp_id = monitor_id; + data.monitor_no = i; + std::string modified_time = std::to_string(current_time_ms); + std::string modified_strText = base_strText; + + // 替换 Monitor + size_t monitor_pos = modified_strText.find("\"Monitor\""); + if (monitor_pos != std::string::npos) { + size_t colon_pos = modified_strText.find(":", monitor_pos); + size_t quote_pos = modified_strText.find("\"", colon_pos); + size_t end_quote_pos = modified_strText.find("\"", quote_pos + 1); + if (colon_pos != std::string::npos && quote_pos != std::string::npos && end_quote_pos != std::string::npos) { + modified_strText.replace(quote_pos + 1, end_quote_pos - quote_pos - 1, data.mp_id); + } + } + + // 替换 TIME + size_t time_pos = modified_strText.find("\"TIME\""); + if (time_pos != std::string::npos) { + size_t colon_pos = modified_strText.find(":", time_pos); + size_t quote_pos = colon_pos; + size_t end_quote_pos = modified_strText.find(",", quote_pos + 1); + if (colon_pos != std::string::npos && quote_pos != std::string::npos && end_quote_pos != std::string::npos) { + modified_strText.replace(quote_pos + 1, end_quote_pos - quote_pos - 1, modified_time); + } + } + + data.strText = modified_strText; + //my_rocketmq_send(data,front->m_producer); + std::lock_guard lock(queue_data_list_mutex); + queue_data_list.push_back(data); + + std::cout << "Sent message " << (i + 1) + << " with Monitor " << data.monitor_no + << " and TIME " << modified_time << std::endl; + + } + } + + std::cout << "Finished sending " << total_messages << " messages." << std::endl; + } +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////其他测试函数 + +void rocketmq_test_rt(Front* front)//用来测试实时数据 +{ + if (!front || !front->m_producer) { + std::cerr << "front 或 producer 无效\n"; + return; + } + + rocketmq::RocketMQProducer* producer = front->m_producer; + + queue_data_t data; + data.monitor_no = 123; + data.strTopic = std::string(FRONT_INST) + "_" + G_MQCONSUMER_TOPIC_RT; + std::ifstream file("rt.txt"); // 文件中存储长字符串 + std::stringstream buffer; + buffer << file.rdbuf(); // 读取整个文件内容 + + data.strText = std::string(buffer.str()); + data.mp_id = "123123"; + //my_rocketmq_send(data,front->m_producer); + std::lock_guard lock(queue_data_list_mutex); + queue_data_list.push_back(data); +} + +void rocketmq_test_ud(Front* front)//用来测试台账更新 +{ + if (!front || !front->m_producer) { + std::cerr << "front 或 producer 无效\n"; + return; + } + + rocketmq::RocketMQProducer* producer = front->m_producer; + + queue_data_t data; + data.monitor_no = 123; + data.strTopic = std::string(FRONT_INST) + "_" + G_MQCONSUMER_TOPIC_UD; + std::ifstream file("ud.txt"); // 文件中存储长字符串 + std::stringstream buffer; + buffer << file.rdbuf(); // 读取整个文件内容 + + data.strText = std::string(buffer.str()); + data.mp_id = "123123"; + //my_rocketmq_send(data,front->m_producer); + std::lock_guard lock(queue_data_list_mutex); + queue_data_list.push_back(data); +} + +void rocketmq_test_set(Front* front)//用来测试进程控制脚本 +{ + if (!front || !front->m_producer) { + std::cerr << "front 或 producer 无效\n"; + return; + } + + rocketmq::RocketMQProducer* producer = front->m_producer; + + queue_data_t data; + data.monitor_no = 123; + data.strTopic = std::string(FRONT_INST) + "_" + G_MQCONSUMER_TOPIC_SET; + std::ifstream file("set.txt"); // 文件中存储长字符串 + std::stringstream buffer; + buffer << file.rdbuf(); // 读取整个文件内容 + + data.strText = std::string(buffer.str()); + data.mp_id = "123123"; + //my_rocketmq_send(data,front->m_producer); + std::lock_guard lock(queue_data_list_mutex); + queue_data_list.push_back(data); +} + +void rocketmq_test_rc(Front* front)//用来测试补招 +{ + if (!front || !front->m_producer) { + std::cerr << "front 或 producer 无效\n"; + return; + } + + rocketmq::RocketMQProducer* producer = front->m_producer; + + queue_data_t data; + data.monitor_no = 123; + data.strTopic = std::string(FRONT_INST) + "_" + G_MQCONSUMER_TOPIC_RC; + std::ifstream file("rc.txt"); // 文件中存储长字符串 + std::stringstream buffer; + buffer << file.rdbuf(); // 读取整个文件内容 + + data.strText = std::string(buffer.str()); + data.mp_id = "123123"; + //my_rocketmq_send(data,front->m_producer); + std::lock_guard lock(queue_data_list_mutex); + queue_data_list.push_back(data); +} + diff --git a/LFtid1056/cloudfront/code/worker.cpp b/LFtid1056/cloudfront/code/worker.cpp new file mode 100644 index 0000000..d426691 --- /dev/null +++ b/LFtid1056/cloudfront/code/worker.cpp @@ -0,0 +1,590 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// socket 网络编程 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "worker.h" +#include "interface.h" +#include "rocketmq.h" +#include "log4.h" +#include "front.h" + +///////////////////////////////////////////////////////////////////////////////////////////////////// + +//shell日志打印开关 +bool showinshellflag =false; + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +extern std::list errorList, warnList, normalList; +extern std::mutex errorListMutex, warnListMutex, normalListMutex; + +extern std::vector terminal_devlist; +extern std::mutex ledgermtx; + +extern std::list queue_data_list; + +extern int IED_COUNT; +extern int INITFLAG; +extern int g_front_seg_index; +extern std::string subdir; + +extern int G_TEST_NUM; +extern int G_TEST_TYPE; + +extern bool errorOutputEnabled; +extern bool warnOutputEnabled; +extern bool normalOutputEnabled; + +////////////////////////////////////////////////////////////////////////////////////////////////////测试登录类 + + Worker::Worker(Front* front) + : m_front(front), listenFD(-1), running(false), historyIndex(-1), stopViewLog(true), g_stopTelnetTest(true) {} + + Worker::~Worker() { + stopServer(); + } + + // 启动 Telnet 服务(监听端口) + bool Worker::startServer(int port) { + if (running) return false; + + listenFD = socket(AF_INET, SOCK_STREAM, 0); + if (listenFD < 0) return false; + + int opt = 1; + setsockopt(listenFD, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); + + sockaddr_in addr{}; + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = INADDR_ANY; + addr.sin_port = htons(port); + + if (bind(listenFD, (sockaddr*)&addr, sizeof(addr)) < 0) return false; + if (listen(listenFD, 5) < 0) return false; + + running = true; + serverThread = std::thread(&Worker::acceptLoop, this); + + std::thread periodicThread(&Worker::doPeriodicTask, this); + periodicThread.detach(); + + return true; + } + + // 停止 Telnet 服务 + void Worker::stopServer() { + if (!running) return; + running = false; + shutdown(listenFD, SHUT_RDWR); + close(listenFD); + if (serverThread.joinable()) serverThread.join(); + } + + // 发送字符串 + void Worker::sendStr(int fd, const std::string& s) { + send(fd, s.c_str(), s.size(), 0); + } + + // 接收客户端连接主循环 + void Worker::acceptLoop() { + while (running) { + sockaddr_in caddr; + socklen_t clen = sizeof(caddr); + int cfd = accept(listenFD, (sockaddr*)&caddr, &clen); + if (cfd >= 0) std::thread(&Worker::sessionThread, this, cfd).detach(); + } + } + + //打印提示符 + void Worker::printPrompt(int clientFD) + { + sendStr(clientFD, "\r\n>"); + } + + // 客户端会话线程处理函数 + void Worker::sessionThread(int clientFD) { + sendTelnetNegotiation(clientFD); + sendStr(clientFD, "\r\nWelcome to the test shell. Type 'help'.\r\n> "); + + while (true) { + char buf[512]; + int n = recv(clientFD, buf, sizeof(buf), 0); + if (n <= 0) break; + + for (int i = 0; i < n; ++i) { + unsigned char c = (unsigned char)buf[i]; + + // 如果不是可打印字符,且也不是 上/下箭头、退格、回车等,就忽略 + bool isPrintable = (c >= 32 && c <= 126); //可见字符范围 + bool isArrowOrEsc = (c == 0x1b); //ESC + bool isEnter = (c == '\r' || c == '\n'); //换行 + bool isBackspace = (c == 0x7f || c == 0x08);//退格 + bool isTab = (c == '\t'); //tab + if( !isPrintable && !isArrowOrEsc && !isEnter && !isBackspace && !isTab ) + { + // 跳过不认识的控制字符:如 '\0' + continue; + } + + // Telnet 协议协商处理 + if (c == IAC) { + if (i + 1 < n && (buf[i+1] == TELDO || buf[i+1] == DONT || buf[i+1] == WILL || buf[i+1] == WONT)) i += 2; + else i++; + continue; + } + + // 退出 viewlog / telnet 流程 + if (c == '`') { + stopViewLog = true; // [MOD] 支持停止日志查看 + g_stopTelnetTest = true; // [MOD] 停止 telnet 测试 + sendStr(clientFD, "\r\n[Log view stopped]\r\n> "); + continue; + } + + // 2) 回车/换行 => 执行命令 + if (c == '\r' || c == '\n') + { + // 如果当前是 '\r' 并且下一个是 '\n',就跳过 '\n' + if (c == '\r') { + if (i + 1 < n && buf[i+1] == '\n') { + i++; + } + // 这里再加一步,如果紧跟着是 '\0',也跳过 + if (i + 1 < n && buf[i+1] == '\0') { + i++; + } + } + // 如果当前是 '\n',也可以检查一下下一个是不是 '\0'(有些客户端可能发 '\n\0') + else { + if (i + 1 < n && buf[i+1] == '\0') { + i++; + } + } + + // 现在把 当前输入的指令 前后空白去掉 + std::string cmdtrim = trim(currentCommand); + if (!cmdtrim.empty()) { //输入指令非空则记录到历史 + + if (commandHistory.empty() || commandHistory.back() != cmdtrim) { //防止连续重复的历史记录 + commandHistory.push_back(cmdtrim); + } + + historyIndex = commandHistory.size(); //更新历史指令下标 + + processCommand(cmdtrim, clientFD); //处理当前指令 + } + + // 处理后清空并打印新的提示符 + currentCommand.clear(); + printPrompt(clientFD); + + // 回车后处理新的命令行 + continue; + } + + // 上/下箭头处理 + if (c == 0x1b && i + 2 < n && buf[i+1] == '[') { + char arrow = buf[i+2]; + if (arrow == 'A') handleUpArrow(clientFD); + else if (arrow == 'B') handleDownArrow(clientFD); + i += 2; + continue; + } + + // 退格处理 + if (c == 0x7f || c == 0x08) { + if (!currentCommand.empty()) { + currentCommand.pop_back(); + sendStr(clientFD, "\b \b"); + } + continue; + } + + // 普通字符输入 + currentCommand.push_back((char)c); + sendBytes(clientFD, (const char*)&c, 1); + } + } + + close(clientFD); + } + + // 发送 Telnet 协商指令 + void Worker::sendTelnetNegotiation(int clientFD) { + unsigned char will_echo[3] = { IAC, WILL, TELOPT_ECHO }; + unsigned char will_sga[3] = { IAC, WILL, TELOPT_SUPPRESS_GO_AHEAD }; + unsigned char dont_lin[3] = { IAC, DONT, TELOPT_LINEMODE }; + sendBytes(clientFD, (const char*)will_echo, 3); + sendBytes(clientFD, (const char*)will_sga, 3); + sendBytes(clientFD, (const char*)dont_lin, 3); + } + + // 发送字节数组 + void Worker::sendBytes(int fd, const char* buf, int len) { + send(fd, buf, len, 0); + } + + void Worker::setTestNum(int num) { + std::lock_guard locker(testMutex); + G_TEST_NUM = num; + } + + void Worker::setTestType(int type) { + std::lock_guard locker(testMutex); + G_TEST_TYPE = type; + } + + // 日志控制 + void Worker::setTestlog(bool flag) { + redirectErrorOutput(flag); + redirectWarnOutput(flag); + redirectNormalOutput(flag); + } + + void Worker::doPeriodicTask() { + while (running) { + { + std::lock_guard locker(testMutex); + std::cout << "[PeriodicTask] G_TEST_NUM = " << G_TEST_NUM + << ", G_TEST_TYPE = " << G_TEST_TYPE << std::endl; + + if (G_TEST_NUM != 0) { + std::cout << "[PeriodicTask] Executing rocketmq_test_300()\n"; + rocketmq_test_300(G_TEST_NUM, g_front_seg_index, G_TEST_TYPE,m_front); + } + } + + std::this_thread::sleep_for(std::chrono::seconds(60)); // 每 60 秒执行一次 + } + } + + // 命令处理逻辑扩展 + void Worker::processCommand(const std::string &cmd, int clientFD) { + std::cout << "Received command: " << cmd << std::endl; + + if (cmd == "help") { + std::string helpText = + "Available commands:\r\n" + "G_TEST_NUM= - Set the G_TEST_NUM\r\n" + "G_TEST_TYPE= - Set the G_TEST_TYPE 0:use ledger,1:use number\r\n" + "LOG= - Set the LOG\r\n" + "rc - Execute rocketmq_test_rc\r\n" + "rt - Execute rocketmq_test_rt\r\n" + "ud - Execute rocketmq_test_ud\r\n" + "set - Execute rocketmq_test_set\r\n" + "log - Execute rocketmq_test_log\r\n" + "upload - Execute upload file\r\n" + "qvvr - Execute http_test_qvvr\r\n" + "ledger - Execute ledger with optional terminal_id\r\n" + "viewlog - View logs (ERROR, WARN, NORMAL, DEBUG)\r\n" + "value - Execute value print with valuename\r\n" + "exit - Exit the shell\r\n" + "help - Show this help message\r\n"; + sendStr(clientFD, "\r\x1B[K" + helpText); + } else if (cmd.find("viewlog") == 0) { + showinshellflag = true; + handleViewLogCommand(cmd, clientFD); + } else if (cmd.find("G_TEST_NUM=") == 0) { + int num = std::atoi(cmd.substr(9).c_str()); + setTestNum(num); + sendStr(clientFD, "\r\x1B[KTEST_NUM updated\r\n"); + } else if (cmd.find("G_TEST_TYPE=") == 0) { + int type = std::atoi(cmd.substr(10).c_str()); + setTestType(type); + sendStr(clientFD, "\r\x1B[KTEST_TYPE updated\r\n"); + } else if (cmd.find("LOG=") == 0) { + int flag = std::atoi(cmd.substr(4).c_str()); + setTestlog(flag); + sendStr(clientFD, "\r\x1B[KLOG updated\r\n"); + } else if (cmd == "rc") { + rocketmq_test_rc(m_front); + sendStr(clientFD, "\r\x1B[KExecuted rocketmq_test_rc\r\n"); + } else if (cmd == "rt") { + rocketmq_test_rt(m_front); + sendStr(clientFD, "\r\x1B[KExecuted rocketmq_test_rt\r\n"); + } else if (cmd == "ud") { + rocketmq_test_ud(m_front); + sendStr(clientFD, "\r\x1B[KExecuted rocketmq_test_ud\r\n"); + } else if (cmd == "set") { + rocketmq_test_set(m_front); + sendStr(clientFD, "\r\x1B[KExecuted rocketmq_test_set\r\n"); + } else if (cmd == "upload") { + Fileupload_test(); + sendStr(clientFD, "\r\x1B[KExecuted upload file\r\n"); + } else if (cmd == "qvvr") { + qvvr_test(); + sendStr(clientFD, "\r\x1B[KExecuted http_test_qvvr\r\n"); + } else if (cmd.find("ledger") == 0) { + size_t pos = cmd.find(' '); + if (pos != std::string::npos) { + std::string terminalId = cmd.substr(pos + 1); + ledger(terminalId.c_str(), clientFD); + sendStr(clientFD, "\r\x1B[KExecuted ledger with terminal_id\r\n"); + } else { + ledger("", clientFD); + sendStr(clientFD, "\r\x1B[KExecuted ledger without parameters\r\n"); + } + } else if (cmd.find("value") == 0) { + size_t pos = cmd.find(' '); + if (pos != std::string::npos) { + std::string var = cmd.substr(pos + 1); + sendStr(clientFD, "\r\x1B[KExecuted value with variable name: " + var + "\r\n"); + value_print(var, clientFD); + } else { + sendStr(clientFD, "\r\x1B[KPlease provide a variable name\r\n"); + } + } else if (cmd == "exit") { + sendStr(clientFD, "\r\x1B[KGoodbye! Exiting shell...\r\n"); + shutdown(clientFD, SHUT_RDWR); + close(clientFD); + return; + } else { + sendStr(clientFD, "\r\x1B[KUnknown command\r\n"); + } + + // 打印提示符 + sendStr(clientFD, "> "); + } + + // 上箭头历史回溯 + void Worker::handleUpArrow(int fd) { + if (!commandHistory.empty() && historyIndex > 0) { + historyIndex--; + currentCommand = commandHistory[historyIndex]; + sendStr(fd, "\r\x1B[K> " + currentCommand); + } + } + + // 下箭头历史前进 + void Worker::handleDownArrow(int fd) { + if (!commandHistory.empty() && historyIndex < (int)commandHistory.size() - 1) { + historyIndex++; + currentCommand = commandHistory[historyIndex]; + sendStr(fd, "\r\x1B[K> " + currentCommand); + } else { + currentCommand.clear(); + sendStr(fd, "\r\x1B[K> "); + } + } + + // 字符串 trim + std::string Worker::trim(const std::string& s) { + auto start = s.begin(); + while (start != s.end() && std::isspace(*start)) ++start; + auto end = s.end(); + do { --end; } while (std::distance(start, end) > 0 && std::isspace(*end)); + return (start <= end) ? std::string(start, end + 1) : ""; + } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////测试shell用的函数 + +void Worker::printLedgerinshell(const terminal_dev& dev, int fd) { + std::ostringstream os; + os << "\r\x1B[K------------------------------------\n"; + os << "\r\x1B[K|-- terminal_id: " << dev.terminal_id << "\n"; + os << "\r\x1B[K|-- terminal_code: " << dev.terminal_code << "\n"; + os << "\r\x1B[K|-- dev_ip: " << dev.addr_str << "\n"; + os << "\r\x1B[K|-- dev_port: " << dev.port << "\n"; + os << "\r\x1B[K|-- dev_type: " << dev.dev_type << "\n"; + os << "\r\x1B[K|-- dev_key: " << dev.dev_key << "\n"; + os << "\r\x1B[K|-- dev_series: " << dev.dev_series << "\n"; + os << "\r\x1B[K|-- dev_processNo: " << dev.processNo << "\n"; + os << "\r\x1B[K|-- maxProcessNum: " << dev.maxProcessNum << "\n"; + os << "\r\x1B[K|-- org_name: " << dev.org_name << "\n"; + os << "\r\x1B[K|-- maint_name: " << dev.maint_name << "\n"; + os << "\r\x1B[K|-- station_name: " << dev.station_name << "\n"; + os << "\r\x1B[K|-- tmnl_factory: " << dev.tmnl_factory << "\n"; + os << "\r\x1B[K|-- tmnl_status: " << dev.tmnl_status << "\n"; + os << "\r\x1B[K|-- timestamp: " << dev.timestamp << "\n"; + + for (size_t i = 0; i < dev.line.size(); ++i) { + const auto& ld = dev.line[i]; + if (ld.monitor_id.empty()) continue; + os << "\r\x1B[K|-- line[" << i << "]:\n"; + os << "\r\x1B[K |-- monitor_id: " << ld.monitor_id << "\n"; + os << "\r\x1B[K |-- monitor_name: " << ld.monitor_name << "\n"; + os << "\r\x1B[K |-- logical_device_seq: " << ld.logical_device_seq << "\n"; + os << "\r\x1B[K |-- terminal_code: " << ld.terminal_code << "\n"; + os << "\r\x1B[K |-- voltage_level: " << ld.voltage_level << "\n"; + os << "\r\x1B[K |-- terminal_connect: " << ld.terminal_connect << "\n"; + os << "\r\x1B[K |-- status: " << ld.status << "\n"; + os << "\r\x1B[K |-- timestamp: " << ld.timestamp << "\n"; + } + + os << "\r\x1B[K------------------------------------\n"; + sendStr(fd, os.str()); +} + +void Worker::ledger(const std::string& terminal_id, int fd) { + sendStr(fd, "\r\x1B[Kprint ledger in shell\n"); + std::lock_guard lock(ledgermtx); + bool found = false; + + if (terminal_id.empty()) { + for (const auto& dev : terminal_devlist) { + printLedgerinshell(dev, fd); + } + } else { + for (const auto& dev : terminal_devlist) { + if (dev.terminal_id == terminal_id) { + printLedgerinshell(dev, fd); + found = true; + break; + } + } + + if (!found) { + std::ostringstream msg; + msg << "\r\x1B[Kterminal not exist: " << terminal_id << "\n"; + sendStr(fd, msg.str()); + } + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////打印指定的变量名 + +void Worker::value_print(const std::string& variableName, int clientFD) { + std::string output; + + { + std::lock_guard lock(ledgermtx); + std::cout << "value_print hold lock !!!!!!!!!!!" << std::endl; + + if (variableName == "frontindex") { + output = "frontindex = " + std::to_string(g_front_seg_index); + } else if (variableName == "iedcount") { + output = "ledger list = " + std::to_string(terminal_devlist.size()) + + ", ied config count = " + std::to_string(IED_COUNT); + } else if (variableName == "frontfun") { + output = "frontfun = " + subdir; + } else if (variableName == "log") { + output = "showinshellflag = " + std::to_string(showinshellflag) + + ", normalOutputEnabled = " + std::to_string(normalOutputEnabled) + + ", warnOutputEnabled = " + std::to_string(warnOutputEnabled) + + ", errorOutputEnabled = " + std::to_string(errorOutputEnabled); + } else if (variableName == "init") { + output = "INITFLAG = " + std::to_string(INITFLAG); + } else { + output = "Unknown variable name: " + variableName; + } + + std::cout << "value_print free lock !!!!!!!!!!!" << std::endl; + } + + sendStr(clientFD, "\r\x1B[K" + output + "\r\n"); +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////日志开关控制 + +//日志消息列表 +std::list* getLogList(const std::string& level) { + if (level == "ERROR") return &errorList; + if (level == "WARN") return &warnList; + if (level == "NORMAL") return &normalList; + return nullptr; +} + +//日志锁 +std::mutex* getLogMutex(const std::string& level) { + if (level == "ERROR") return &errorListMutex; + if (level == "WARN") return &warnListMutex; + if (level == "NORMAL") return &normalListMutex; + return nullptr; +} + +void Worker::handleViewLogCommand(const std::string& command, int clientFD) { + std::istringstream iss(command); + std::string cmd, level; + iss >> cmd >> level; + std::transform(level.begin(), level.end(), level.begin(), ::toupper); + + if (level.empty()) { + sendStr(clientFD, "\r\x1B[KUsage: viewlog [ERROR|WARN|NORMAL]\r\n> "); + return; + } + + std::list* logList = getLogList(level); + std::mutex* logMutex = getLogMutex(level); + + if (!logList || !logMutex) { + sendStr(clientFD, "\r\x1B[KInvalid log level! Use ERROR, WARN, NORMAL.\r\n> "); + return; + } + + stopViewLog = false; + showinshellflag = true; + sendStr(clientFD, "\r\x1B[KViewing logs for level: " + level + " (Press '`' to exit)\r\n> "); + + char inputBuf[16]; + + while (!stopViewLog) { + // 1. 监听 shell 输入退出符号 ` + fd_set read_fds; + FD_ZERO(&read_fds); + FD_SET(clientFD, &read_fds); + + timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 500000; // 500ms + + int activity = select(clientFD + 1, &read_fds, nullptr, nullptr, &timeout); + if (activity > 0 && FD_ISSET(clientFD, &read_fds)) { + int n = recv(clientFD, inputBuf, sizeof(inputBuf), 0); + if (n > 0 && strchr(inputBuf, '`')) { + stopViewLog = true; + showinshellflag = false; + break; + } + } + + // 2. 输出日志 + std::string logEntry; + { + std::lock_guard lock(*logMutex); + if (!logList->empty()) { + logEntry = logList->front(); + logList->pop_front(); + } + } + + if (!logEntry.empty()) { + sendStr(clientFD, "\r\x1B[K" + logEntry + "\r\n"); + } else { + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + } + } + + // 3. 打印退出提示 + sendStr(clientFD, "\r\x1B[K\nLog view stopped. Returning to shell.\r\n> "); +} + diff --git a/LFtid1056/cloudfront/lib/libcrypto.so b/LFtid1056/cloudfront/lib/libcrypto.so new file mode 100644 index 0000000..a29308b Binary files /dev/null and b/LFtid1056/cloudfront/lib/libcrypto.so differ diff --git a/LFtid1056/cloudfront/lib/libcrypto.so.1.0.0 b/LFtid1056/cloudfront/lib/libcrypto.so.1.0.0 new file mode 100644 index 0000000..9ef3011 Binary files /dev/null and b/LFtid1056/cloudfront/lib/libcrypto.so.1.0.0 differ diff --git a/LFtid1056/cloudfront/lib/libcrypto.so.1.1 b/LFtid1056/cloudfront/lib/libcrypto.so.1.1 new file mode 100644 index 0000000..a29308b Binary files /dev/null and b/LFtid1056/cloudfront/lib/libcrypto.so.1.1 differ diff --git a/LFtid1056/cloudfront/lib/libcurl.a b/LFtid1056/cloudfront/lib/libcurl.a new file mode 100644 index 0000000..d5e29ea Binary files /dev/null and b/LFtid1056/cloudfront/lib/libcurl.a differ diff --git a/LFtid1056/cloudfront/lib/libcurl.la b/LFtid1056/cloudfront/lib/libcurl.la new file mode 100644 index 0000000..7e29bea --- /dev/null +++ b/LFtid1056/cloudfront/lib/libcurl.la @@ -0,0 +1,41 @@ +# libcurl.la - a libtool library file +# Generated by libtool (GNU libtool) 2.4.2 Debian-2.4.2-1.3 +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# The name that we can dlopen(3). +dlname='libcurl.so.4' + +# Names of this library. +library_names='libcurl.so.4.3.0 libcurl.so.4 libcurl.so' + +# The name of the static archive. +old_library='libcurl.a' + +# Linker flags that can not go in dependency_libs. +inherited_linker_flags='' + +# Libraries that this one depends upon. +dependency_libs='' + +# Names of additional weak libraries provided by this library +weak_library_names='' + +# Version information for libcurl. +current=7 +age=3 +revision=0 + +# Is this an already installed library? +installed=yes + +# Should we warn about portability when linking against -modules? +shouldnotlink=no + +# Files to dlopen/dlpreopen +dlopen='' +dlpreopen='' + +# Directory that this library needs to be installed in: +libdir='/home/pq/curl/lib' diff --git a/LFtid1056/cloudfront/lib/libcurl.so b/LFtid1056/cloudfront/lib/libcurl.so new file mode 100644 index 0000000..9a73018 Binary files /dev/null and b/LFtid1056/cloudfront/lib/libcurl.so differ diff --git a/LFtid1056/cloudfront/lib/libcurl.so.4 b/LFtid1056/cloudfront/lib/libcurl.so.4 new file mode 100644 index 0000000..9a73018 Binary files /dev/null and b/LFtid1056/cloudfront/lib/libcurl.so.4 differ diff --git a/LFtid1056/cloudfront/lib/libcurl.so.4.3.0 b/LFtid1056/cloudfront/lib/libcurl.so.4.3.0 new file mode 100644 index 0000000..9409fe2 Binary files /dev/null and b/LFtid1056/cloudfront/lib/libcurl.so.4.3.0 differ diff --git a/LFtid1056/cloudfront/lib/libcurl.so.4.7.0 b/LFtid1056/cloudfront/lib/libcurl.so.4.7.0 new file mode 100644 index 0000000..9a73018 Binary files /dev/null and b/LFtid1056/cloudfront/lib/libcurl.so.4.7.0 differ diff --git a/LFtid1056/cloudfront/lib/libssl.so b/LFtid1056/cloudfront/lib/libssl.so new file mode 100644 index 0000000..24b8b13 Binary files /dev/null and b/LFtid1056/cloudfront/lib/libssl.so differ diff --git a/LFtid1056/cloudfront/lib/libssl.so.1.0.0 b/LFtid1056/cloudfront/lib/libssl.so.1.0.0 new file mode 100644 index 0000000..bbb42c6 Binary files /dev/null and b/LFtid1056/cloudfront/lib/libssl.so.1.0.0 differ diff --git a/LFtid1056/cloudfront/lib/libssl.so.1.1 b/LFtid1056/cloudfront/lib/libssl.so.1.1 new file mode 100644 index 0000000..24b8b13 Binary files /dev/null and b/LFtid1056/cloudfront/lib/libssl.so.1.1 differ