/////////////////////////////////////////////////////////////////////////////////////////////////

#include <ctime>
#include <unistd.h>
#include <string>
#include <iostream>
#include <list>
#include <sstream> 	//流解析
#include <set>		//写去重的设备类型
#include <fstream>	//打开文件
#include <algorithm>
#include <cctype>
#include <dirent.h> 
#include <vector>
#include <array>
#include <map>
#include <mutex>
#include <thread>
#include <atomic>
#include <cstdio>
#include <sys/stat.h>
#include <fnmatch.h>
#include <memory>

/////////////////////////////////////////////////////////////////////////////////////////////////

#include "nlohmann/json.hpp"
#include "curl/curl.h" 
#include "log4cplus/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_t> 	queue_data_list; 		//queue发送数据链表

extern int three_secs_enabled;

extern std::map<std::string, Xmldata*> xmlinfo_list;//保存所有型号对应的icd映射文件解析数据
extern XmlConfig xmlcfg;//星形接线xml节点解析的数据-默认映射文件解析数据
extern std::list<CTopic *> topicList; //队列发送主题链表

extern XmlConfig xmlcfg2;//角型接线xml节点解析的数据-默认映射文件解析数据
extern std::list<CTopic*> topicList2; //角型接线发送主题链表
extern std::map<std::string, Xmldata*> xmlinfo_list2;//保存所有型号角形接线对应的icd映射文件解析数据

////////////////////////////////////////////////////////////////////////////////////////////////////

//补招
std::list<JournalRecall> 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<std::string> 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<std::string, std::string*> strMap;
    std::unordered_map<std::string, int*>        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;
	}*/	
	TEST_PORT = TEST_PORT  +  g_front_seg_index;
}

////////////////////////////////////////////////////////////////////////////////////////////获取当前时间

// 用于获取当前时间，单位毫秒
double sGetMsTime() {
    auto now = std::chrono::system_clock::now();
    auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
        now.time_since_epoch()
    ).count();
    return static_cast<double>(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<std::pair<std::string, time_t>> 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<std::string, time_t>& a, const std::pair<std::string, time_t>& 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<std::string, std::string>& 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】前置的%d号进程 无法解析补招文件，补招文件路径FRONT_PATH + /etc/recall/不存在", 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】前置的%d号进程 无法解析补招文件%s,补招内容无效",  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<std::string, std::string> 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】前置的%d号进程 删除旧的补招文件失败,补招文件路径FRONT_PATH + /etc/recall/不存在", 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】前置的%d号进程 删除超过两天的补招文件", 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】前置的%d号进程 开始写入补招文件",  g_front_seg_index);

        std::map<std::string, std::list<JournalRecall>> 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<JournalRecall>& 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】前置的%d号进程 无法将补招文件写入路径: %s", 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<long long>(std::mktime(&tm));
}

// 数据完整性补招判断（划分为1小时）
void Get_Recall_Time_Char(const std::string& start_time_str,
                          const std::string& end_time_str,
                          std::vector<RecallInfo>& 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> 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<JournalRecall> 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<std::string>();
            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<std::string>();

                    // 判断此监测点是否归属当前进程
                    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<std::string>();
                        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<std::string>();
                            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> 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<time_t>(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<time_t>(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<std::mutex> 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<std::string, terminal_dev>& 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_name
                  << ", 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

                  << ", mac: "              << dev.mac

                  << 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_id
                      << ", Name: "  << m.monitor_name
                      << ", Seq: "   << m.logical_device_seq
                      << ", Voltage: "<< m.voltage_level
                      << ", Connect: "<< m.terminal_connect
                      << ", Timestamp:"<< m.timestamp
                      << ", Status: " << m.status

                      << ", CT1: " << m.CT1
                      << ", CT2: " << m.CT2
                      << ", PT1: " << m.PT1
                      << ", PT2: " << m.PT2

                      << std::endl;
        }
    }
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////更新台账

//在台账更新目录查找自己进程要处理的文件
std::list<std::string> 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<std::string> 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_name  = 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");

    work_terminal.mac      = get_value("mac");

    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_id    = monitor->FirstChildElement("terminal_id") ? monitor->FirstChildElement("terminal_name")->GetText() : "N/A";
        mon.status           = monitor->FirstChildElement("status") ? monitor->FirstChildElement("status")->GetText() : "N/A";

        mon.CT1 = monitor->FirstChildElement("CT1") && monitor->FirstChildElement("CT1")->GetText()
                    ? atof(monitor->FirstChildElement("CT1")->GetText()) : 0.0;
        mon.CT2 = monitor->FirstChildElement("CT2") && monitor->FirstChildElement("CT2")->GetText()
                    ? atof(monitor->FirstChildElement("CT2")->GetText()) : 0.0;
        mon.PT1 = monitor->FirstChildElement("PT1") && monitor->FirstChildElement("PT1")->GetText()
                    ? atof(monitor->FirstChildElement("PT1")->GetText()) : 0.0;
        mon.PT2 = monitor->FirstChildElement("PT2") && monitor->FirstChildElement("PT2")->GetText()
                    ? atof(monitor->FirstChildElement("PT2")->GetText()) : 0.0;

        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 <ledger_update> 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 <add>, <modify>, or <delete> 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<std::string> 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_name.empty()) {
        target_dev.terminal_name = update.terminal_name;
        std::cout << "terminal_name: " << target_dev.terminal_name << 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.mac.empty()) {
        target_dev.mac = update.mac;
        std::cout << "mac: " << target_dev.mac << 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<long long>(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_id      = mon.terminal_id;
        m.timestamp          = mon.timestamp;

        m.CT1          = mon.CT1;
        m.CT2          = mon.CT2;
        m.PT1          = mon.PT1;
        m.PT2          = mon.PT2;

        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<long long>(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<size_t>(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_update_xml_t> 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<std::mutex> 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 &current_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<std::pair<std::string, std::string>> new_entries;
    std::vector<std::pair<std::string, std::string>> modify_entries;
    std::vector<std::pair<std::string, std::string>> 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 << "<new>\n";
        for (const auto& entry : new_entries) {
            write_log_entry(log_file, "add", entry.first, entry.second);
        }
        log_file << "</new>\n";
    }

    if (!modify_entries.empty()) {
        log_file << "<modify>\n";
        for (const auto& entry : modify_entries) {
            write_log_entry(log_file, "modify", entry.first, entry.second);
        }
        log_file << "</modify>\n";
    }

    if (!delete_entries.empty()) {
        log_file << "<delete>\n";
        for (const auto& entry : delete_entries) {
            write_log_entry(log_file, "delete", entry.first, entry.second);
        }
        log_file << "</delete>\n";
    }

    log_file.close();
    std::cout << "Ledger log has been updated." << std::endl;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////shell打印日志

// ------------------ 全局日志列表和锁 ------------------
std::list<std::string> errorList;
std::list<std::string> warnList;
std::list<std::string> 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<char>(ch)) == traits_type::eof()) {
                return traits_type::eof();
            }
        }
        // 2) 存到我们的临时缓存，注意加锁保护
        pthread_mutex_lock(&m_mutex);	//防止多线程推入崩溃lnk20250305
        m_buffer.push_back(static_cast<char>(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 <<std::endl;
    
    // 执行命令
    system(command);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////打印台账更新

void print_monitor(const ledger_monitor& mon) {
    auto safe = [](const std::string& s) { return s.empty() ? "N/A" : s; };

    std::cout << "Monitor ID: "               << safe(mon.monitor_id)         << "\n";
    std::cout << "Terminal ID: "            << safe(mon.terminal_id)      << "\n";
    std::cout << "Monitor Name: "             << safe(mon.monitor_name)       << "\n";
    std::cout << "Logical Device Sequence: "  << safe(mon.logical_device_seq) << "\n";
    std::cout << "Voltage Level: "            << safe(mon.voltage_level)      << "\n";
    std::cout << "Terminal Connect: "         << safe(mon.terminal_connect)   << "\n";
    std::cout << "Timestamp: "                << safe(mon.timestamp)          << "\n";
    std::cout << "Status: "                   << safe(mon.status)             << "\n";

    std::cout << "CT1: "                   << mon.CT1            << "\n";
    std::cout << "CT2: "                   << mon.CT2            << "\n";
    std::cout << "PT1: "                   << mon.PT1            << "\n";
    std::cout << "PT2: "                   << mon.PT2            << "\n";
}

void print_terminal(const terminal_dev& tmnl) {
    auto safe = [](const std::string& s) { return s.empty() ? "N/A" : s; };

    std::cout << "GUID: "             << safe(tmnl.guid)         << "\n";
    std::cout << "Terminal ID: "      << safe(tmnl.terminal_id)  << "\n";
    std::cout << "Terminal Code: "    << safe(tmnl.terminal_name)<< "\n";
    std::cout << "Organization Name: "<< safe(tmnl.org_name)     << "\n";
    std::cout << "Maintenance Name: " << safe(tmnl.maint_name)   << "\n";
    std::cout << "Station Name: "     << safe(tmnl.station_name) << "\n";
    std::cout << "Factory Name: "     << safe(tmnl.tmnl_factory) << "\n";
    std::cout << "Terminal Status: "  << safe(tmnl.tmnl_status)  << "\n";
    std::cout << "Device Type: "      << safe(tmnl.dev_type)     << "\n";
    std::cout << "Device Key: "       << safe(tmnl.dev_key)      << "\n";
    std::cout << "Device Series: "    << safe(tmnl.dev_series)   << "\n";
    std::cout << "Address: "          << safe(tmnl.addr_str)     << "\n";
    std::cout << "Port: "             << safe(tmnl.port)         << "\n";
    std::cout << "Timestamp: "        << safe(tmnl.timestamp)    << "\n";

    std::cout << "mac: "        << safe(tmnl.mac)    << "\n";

    for (size_t i = 0; i < 10 && !tmnl.line[i].monitor_id.empty(); ++i) {
        std::cout << "  Monitor " << (i + 1) << ":\n";
        print_monitor(tmnl.line[i]);
    }
}

void print_trigger_update_xml(const trigger_update_xml_t& trigger_update) {
    std::cout << "Work Updates Count: "   << trigger_update.work_updates.size()   << "\n";
    std::cout << "New Updates Count: "    << trigger_update.new_updates.size()    << "\n";
    std::cout << "Delete Updates Count: " << trigger_update.delete_updates.size() << "\n";
    std::cout << "Modify Updates Count: " << trigger_update.modify_updates.size() << "\n\n";

    std::cout << "Work Updates:\n";
    for (size_t i = 0; i < trigger_update.work_updates.size(); ++i) {
        std::cout << "Work Update " << (i + 1) << ":\n";
        print_terminal(trigger_update.work_updates[i]);
    }

    std::cout << "\nNew Updates:\n";
    for (size_t i = 0; i < trigger_update.new_updates.size(); ++i) {
        std::cout << "New Update " << (i + 1) << ":\n";
        print_terminal(trigger_update.new_updates[i]);
    }

    std::cout << "\nDelete Updates:\n";
    for (size_t i = 0; i < trigger_update.delete_updates.size(); ++i) {
        std::cout << "Delete Update " << (i + 1) << ":\n";
        print_terminal(trigger_update.delete_updates[i]);
    }

    std::cout << "\nModify Updates:\n";
    for (size_t i = 0; i < trigger_update.modify_updates.size(); ++i) {
        std::cout << "Modify Update " << (i + 1) << ":\n";
        print_terminal(trigger_update.modify_updates[i]);
    }
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////解析模板文件

//解析映射文件
bool ParseXMLConfig2(int xml_flag, XmlConfig *cfg, std::list<CTopic*> *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<CTopic*>::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<std::string, Xmldata*>
    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;
    }
}


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////数据转换函数
// DataArrayItem to_json
void to_json(nlohmann::json& j, const DataArrayItem& d) {
    j = nlohmann::json{
        {"DataAttr", d.DataAttr},
        {"DataTimeSec", d.DataTimeSec},
        {"DataTimeUSec", d.DataTimeUSec},
        {"DataTag", d.DataTag},
        {"Data", d.Data}
    };
}

// MsgObj to_json
void to_json(nlohmann::json& j, const MsgObj& m) {
    j = nlohmann::json{
        {"Cldid", m.Cldid},
        {"DataType", m.DataType},
        {"DataAttr", m.DataAttr},
        {"DsNameIdx", m.DsNameIdx},
        {"DataArray", m.DataArray}
    };
}

// FullObj to_json
void to_json(nlohmann::json& j, const FullObj& f) {
    j = nlohmann::json{
        {"Mid", f.Mid},
        {"Did", f.Did},
        {"Pri", f.Pri},
        {"Type", f.Type},
        {"Msg", f.Msg}
    };
}
std::string generate_json(
    int Mid,    //需应答的报文订阅者收到后需以此ID应答，无需应答填入“-1”
    int Did,    //设备唯一标识Ldid，填入0代表Ndid。
    int Pri,    //报文处理的优先级
    int Type,   //消息类型
    int Cldid,  //逻辑子设备ID，0-逻辑设备本身，无填-1
    int DataType,   //数据类型，0-表示以数据集方式上送
    int DataAttr,   //数据属性：无“0”、实时“1”、统计“2”等。
    int DsNameIdx,  //数据集序号（以数据集方式上送），无填-1
    const std::vector<DataArrayItem>& dataArray     //数据数组。
) {
    FullObj fobj;
    fobj.Mid = Mid;
    fobj.Did = Did;
    fobj.Pri = Pri;
    fobj.Type = Type;
    fobj.Msg.Cldid = Cldid;
    fobj.Msg.DataType = DataType;
    fobj.Msg.DataAttr = DataAttr;
    fobj.Msg.DsNameIdx = DsNameIdx;
    fobj.Msg.DataArray = dataArray;
    nlohmann::json j = fobj;
    return j.dump(); // 输出标准 json 字符串
}

void upload_data_test(){
    std::vector<DataArrayItem> arr;
    arr.push_back({1, 1725477660, 0, 1, "xxxx"});   //数据属性  -1-无， 0-“Rt”,1-“Max”,2-“Min”,3-“Avg”,4-“Cp95”
                                                    //数据时标，相对1970年的秒，无效填入“-1”
                                                    //数据时标，微秒钟，无效填入“-1”
                                                    //数据标识，1-标识数据异常
                                                    //数据序列（数据集上送时将二进制数据流转换成Base64字符串，其他数据为object）
    arr.push_back({2, 1691741340, 0, 1, "yyyy"});

    std::string js = generate_json(
        -1, 2, 1, 4866, 1, 0, 2, 1, arr
    );
    std::cout << js << std::endl;
    
    queue_data_t data;
    data.monitor_no = 1;
    data.strTopic = TOPIC_ALARM;
    data.strText = js;
    data.mp_id = "test";
    std::lock_guard<std::mutex> lock(queue_data_list_mutex);
    queue_data_list.push_back(data);
}

//////////////////////////////////////////////////////////////////////////////////////////////////台账赋值给通信

std::vector<DeviceInfo> GenerateDeviceInfoFromLedger(const std::vector<terminal_dev>& terminal_devlist) {
    std::vector<DeviceInfo> devices;

    for (const auto& terminal : terminal_devlist) {
        DeviceInfo device;
        device.device_id = terminal.terminal_id;
        device.name = terminal.terminal_name;
        device.model = terminal.dev_series;
        device.mac = terminal.mac;

        for (const auto& monitor : terminal.line) {
            PointInfo point;
            point.point_id = monitor.monitor_id;
            point.name = monitor.monitor_name;
            point.device_id = terminal.terminal_id;
            point.PT1 = monitor.PT1;
            point.PT2 = monitor.PT2;
            point.CT1 = monitor.CT1;
            point.CT2 = monitor.CT2;

            device.points.push_back(point);
        }

        devices.push_back(device);
    }

    return devices;
}