
using namespace std;

#include <QFile>
#include <QXmlStreamWriter>
#include <QtXml/QDomDocument>
#include <QtXml/QDomElement>
#include <QtXml/QDomNode>
#include <QtXml/QDomNodeList>
#include <QtCore/qglobal.h>
#include <QStringList>
#include <QSettings>
#include <QDateTime>
#include <QDir>
#include <QFileInfo>
#include <QDebug>
#include <QMutex>
#include <QMap>
#include <ctime>
#define OTL_ODBC_ODBC
#define OTL_ODBC_UNIX
#include <unistd.h>

//#include "otlv4.h"
#include <stdio.h>

#include <sstream> //lnk 2024-10-16
#include <set>	//写去重的设备类型
#include <fstream>//上传文件

#include "../mms/db_interface.h"
#include "../json/save2json.h"
#include "../json/mms_json_inter.h"

#include "../mms/rdb_client.h"
#include "../mms/interface.h"
#include "../json/cjson.h"//WW 2023-08-27新增json解析函数
#include "../include/curl/curl.h" 

#include "../log4cplus/log4.h"//lnk添加log4
#include <cstring>

//同步arm，移除otlv头文件
class otl_datetime {
public:
  int year;
  int month;
  int day;
  int hour;
  int minute;
  int second;
};

//用于测试时防止数据激增
#ifndef apr_time_from_msec
#define apr_time_from_msec(ms) ((apr_time_t)(ms) * 1000)
#endif

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */

	extern pt61850app_t* 	g_pt61850app;
	extern node_t* 			g_node;
	extern apr_pool_t* 		g_cfg_pool;
	extern apr_pool_t* 		g_init_pool;
	extern apr_pool_t* 		g_temp_dev_pool;
	extern char				subdir[128];
	extern int 				g_front_seg_index;

	extern unsigned int		g_no_auth;
	extern char				g_onlyIP[255];			//直连某个IP，仅仅为方便测试

	int						g_DevFlag = 0;

#ifdef __cplusplus
}
#endif
///////////////////////////////lnk20250118台账变更使用的内存池：动态内存池的方式，每个内存池放一个ied的所有相关内容，这个方案暂不使用，只保留相关实现函数
// 用于存储终端 ID 和对应的子池
std::list<std::pair<std::string, apr_pool_t*> > pool_list;
///////////////////////////////////////////////////

//ZW 2023-10-10 单条补招结构
class RecallInfo
{
public:
	long long starttime;
	long long endtime;
};

//CZY 2023-09-17 控制命令解析
class ProgramParam
{
public:
	QList<QString> terminal_list;//终端id列表
	QString file_name;//程序下装可用，下装程序在oss中的位置
};

//CZY 2023-09-17 控制命令解析
class RecallParam
{
public:
	QString mp_id;//监测点id
	QString start;//补招数据起始时间
	QString end;//补招数据结束时间
	int voltage;//暂态数据标志
	int stat;//稳态数据标志
};

//CZY 2023-10-12 装置识别码与密钥
class terminal_ext //
{
public:
	char terminal_identify_code[100];//终端识别码
	char terminal_key[100];//终端密钥
};

class CJournalRecall //日志补招结构类
{
public:
	QString MonitorID; //线路监测点号
	QString StartTime; //数据补招起始时间
	QString EndTime;   //数据补招结束时间
	QString STEADY;    //补招历史统计数据标识 0-不补招；1-补招     
	QString VOLTAGE;   //补招暂态事件标识 0-不补招；1-补招
};

/*lnk 2024-10-15 */
class ledger_monitor //监测点台账
{
public:
	char monitor_id[64];
    char terminal_code[64];
    char monitor_name[64];
    char logical_device_seq[64];
    char voltage_level[64];
    char terminal_connect[64];
    char timestamp[64];
	char status[255];
	char count_cfg[64];	//不是台账的一部分，用来记录数据库或业务中台的台账数量

	int log_level;	//0 ERROR 1 WARN 2 NORMAL 3 DEBUG
};

class terminal_dev //终端台账
{
public:
	char terminal_id[64];
    char terminal_code[64];
    char org_name[64];
    char maint_name[64];
    char station_name[64];
    char tmnl_factory[64];
    char tmnl_status[64];
    char dev_type[64];
    char dev_key[255];
    char dev_series[255];
    char com_type[32];
    char addr_str[64];
    char port[64];
    char timestamp[64];

	//lnk20250210添加进程号
	char processNo[64];
	char maxProcessNum[64];

    ledger_monitor line[10];

	char count_cfg[64];	//不是台账的一部分，用来记录数据库或业务中台的台账数量

	int log_level;	//0 ERROR 1 WARN 2 NORMAL 3 DEBUG
};

class icd_model //icd模型
{
public:
	char model_id[64];
    char tmnl_type[64];
	char tmnl_type_id[64];	//不使用
    char tmnl_factory[64];	//不使用
    char file_name[128];
	char file_path[128];
    char timestamp[64];
};

/*lnk 2024-10-15 */

list<CJournalRecall> g_StatisticLackList; //日志补招结构类链表

QMutex g_StatisticLackList_list_mutex; //recall队列数据锁

QString DEVIE_CONFIG_FN = QString("Device_Config.xml");//不用
QString LINE_CONFIG_FN = QString("Line_Config.xml");//不用
QString JSON_CONFIG_FN_old = QString("JiangSu_Config.xml");//默认映射文件
QString THREE_SECS_CONFIG_FN = QString("Trigger3S.xml");//实时数据用
QString RECALL_CONFIG_FN = QString("Recall.xml");//不用

//lnk20241220创建一个用来台账更新的文件，保证数据完整的情况下更新台账
std::string LEDGER_UPDATE_FN = "LedgerUpdate.log";

const int MAX_CPUNO = 10;

//lnk20250121终端台账数量配置
int IED_COUNT = 300;	//默认300

extern int INITFLAG;

extern uint32_t	g_ontime_blocked_times;

//lnk2024-8-14添加角型接线标志,0不存在角形接线，1存在角形接线
int isdelta_flag = 0;

//////CZY 2023-09-06 config
//多前置flag:1为开启,0为关闭
int MULTIPLE_NODE_FLAG = 1;
extern const char* PROGRAM_VERSION;

extern int FRONT_MP_NUM;

int ACCOUNT_UPDATE_INTERVAL;
char* ACCOUNT_UPDATE_LAST_TIME;

int MULIT_NODE_INTERVAL;

int COMMUNICATION_LOG_STATUS_TIME;
int COMMUNICATION_LOG_ABNORMAL_TIME;

char* POSTGRES_DATABASE;//数据库库名
char* POSTGRES_USERNAME;//数据库用户名
char* POSTGRES_PASSWORD;//数据库密码
char* POSTGRES_SCHEMA;//数据库模式名
char* POSTGRES_DNSNAME;//取postgres/guass库
char* POSTGRES_TABLEPREFIX;//表名前缀

char* CLIENT_ID;//中台CLIENT_ID
char* CLIENT_SECRET;//中台CLIENT_SECRET
char* TOKEN_URL;//中台取token接口
char* DEVICE_URL;//中台取终端接口
char* GRANT_TYPE;//中台GRANT_TYPE

char* UDS_UPLOAD_URL;
char* UDS_DOWNLOAD_URL;
char* UDS_DELETE_URL;

int  FILE_FLAG;
int  SEND_FLAG;
std::string FRONT_INST;//lnk20250512改为string
char* FRONT_IP;
int CITY_FLAG;

//lnk20260309添加一个全局变量，标志是否只运行补招程序
int RECALL_ONLY_FLAG;

int recall_len;
int recall_sta;
int recall_daily;

char* BROKER_LIST;
char* TOPIC_STAT;
char* TOPIC_PST;
char* TOPIC_PLT;
char* TOPIC_EVENT;
char* TOPIC_ALARM;
char* TOPIC_SNG;

//lnk20241220
char* TOPIC_RTDATA;

char* PROTOCOL;
char* MECHANISMS;
char* KEYTAB_FILE;
char* SERVICE_NAME;
char* PRINCIPAL;
char* DOMAIN_NAME;

extern int     g_front_seg_index;
extern int     g_front_seg_num;

/*移植配置变量lnk10-9*/
//生产者
std::string G_ROCKETMQ_PRODUCER = "";//rocketmq producer
std::string G_ROCKETMQ_IPPORT = "";//rocketmq ip+port
std::string G_ROCKETMQ_TOPIC_TEST = "";//topie
std::string G_ROCKETMQ_TAG_TEST = "";//tag
std::string G_ROCKETMQ_KEY_TEST = "";//key
int QUEUENUM = 0;
std::string BROKERNAME = "";
//消费者
std::string G_ROCKETMQ_CONSUMER = "";//rocketmq consumer
std::string G_MQCONSUMER_IPPORT = "";//consumer ip+port
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_ACCESSKEY = "";
std::string G_MQCONSUMER_SECRETKEY = "";
std::string G_MQCONSUMER_CHANNEL = "";
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_LOG_TOPIC = "";//topie
std::string G_LOG_TAG = "";//tag
std::string G_LOG_KEY = "";//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_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 DATA_TRACE_TOPIC = "";
std::string DATA_TRACE_TAG = "";
std::string DATA_TRACE_KEY = "";

//lnk20260310添加文件管理的topic和tag
std::string G_MQCONSUMER_TOPIC_FILE = "";//consumer topie
std::string G_MQCONSUMER_TAG_FILE = "";//consumer tag
std::string G_MQCONSUMER_KEY_FILE = "";//consumer key

std::string G_REPLY_TOPIC_FILE = "";//consumer topie
std::string G_REPLY_TAG_FILE = "";//consumer tag
std::string G_REPLY_KEY_FILE = "";//consumer key

std::string G_MQCONSUMER_TOPIC_TEST = "";

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;//解析的列表数组

//终端和监测点的状态筛选
std::string TERMINAL_STATUS = "";
std::string MONITOR_STATUS = "";
std::string ICD_FLAG = "";

//保留socket连接设置开关
int SOCKET_PORT = 13000;
int SOCKETENABLE = 0;

//添加http配置和台账配置lnk20241031
int HTTPENABLE = 0;
std::string HTTP_IP = "";
int HTTP_PORT = 12000;

/*添加web接口lnk202411-6*/
std::string WEB_DEVICE = "";
std::string WEB_ICD = "";
std::string WEB_INTEGRITY = "";	//暂不使用
std::string WEB_COMFLAG = "";
std::string WEB_EVENT = "";
std::string WEB_FILEUPLOAD = "";
std::string WEB_FILEDOWNLOAD = "";

// 日志限流配置
int G_LOG_RATE_RESET_SEC = 3600;          // 1小时重置
int G_LOG_RATE_LIMIT_SEC = 60;            // 进入限流后：60秒1条
int G_LOG_RATE_KEEP_ALL_MS = 60000;       // 间隔 >= 60000ms，全部保留
int G_LOG_RATE_KEEP_BURST_MS = 1000;      // 间隔 >= 1000ms，按二级策略
int G_LOG_RATE_KEEP_BURST_COUNT = 60;     // 二级保留前60条
int G_LOG_RATE_KEEP_HIGHFREQ_COUNT = 10;  // 高频保留前10条

//lnk20250115添加台账锁
extern pthread_mutex_t mtx;

/*lnk 2024-10-21 */
std::string intToString(int number);

//lnk20250512
void send_heartbeat_to_kafka(const std::string& status);
int get_max_stat_data_index(const char* filepath);
extern void execute_bash(string fun,int process_num,string type);
extern void close_listening_socket();
void save_ledger_json(const char* ptr);
void read_latest_ledger_file(char** out);
//////////////////////////////////////////////////////////////////////////

extern int server_socket; //Web Socket服务端实例
extern unsigned int	g_node_id; //前置程序类型(100-500)

extern QMutex kafka_data_list_mutex; //Kafka发送数据锁
extern QList<Ckafka_data_t> kafka_data_list; //kafka发送数据链表
//WW 2023-08-20 end
////////////////////////////////////////////////////////////////////////////////////////////////////

void parse_log_switch_ini(unsigned int* error, unsigned int* warn, unsigned int* info)
{
	QString pt61850netd_pqfe_IniFilename = QString("../etc/pt61850netd_pqfe.ini");
	QSettings settings(pt61850netd_pqfe_IniFilename, QSettings::IniFormat);

	settings.beginGroup("Log");
	*error = settings.value("error", 0).toUInt();
	*warn = settings.value("warn", 0).toUInt();
	*info = settings.value("info", 0).toUInt();

	g_no_auth = settings.value("no_auth", 0).toUInt();

	g_DevFlag = settings.value("DevFlag", 0).toUInt();

	settings.endGroup();
}


//lnk20250328添加测试列表用的函数
void parseTestList(const std::string& input) {
    TESTARRAY.clear(); // 清空旧数据

    size_t start = 0;
    size_t end = 0;

    while ((end = input.find(',', start)) != std::string::npos) {
        std::string id = input.substr(start, end - start);
        if (!id.empty()) {
            TESTARRAY.push_back(id);
			std::cout << "use realdata device id:" << id << std::endl;
        }
        start = end + 1;
    }

    // 添加最后一个 ID（如果没有逗号结尾）
    if (start < input.length()) {
        std::string lastId = input.substr(start);
        if (!lastId.empty()) {
            TESTARRAY.push_back(lastId);
			std::cout << "use realdata device id:" << lastId << std::endl;
        }
    }
}

//CZY 2023-09-06 config
void init_config() {
	QByteArray ba;
	QString MyKafkaIniFilename = QString("../etc/config/") + QString("mykafka.ini"); //+QString::fromAscii(subdir)
	QSettings settings(MyKafkaIniFilename, QSettings::IniFormat);

	ACCOUNT_UPDATE_INTERVAL = settings.value("AccountUpdate/Interval", 0).toInt();
	qDebug() << "Read ACCOUNT_UPDATE_INTERVAL:" << ACCOUNT_UPDATE_INTERVAL << endl;
	ba = settings.value("AccountUpdate/LastUpdateTime", "").toString().toLatin1();
	ACCOUNT_UPDATE_LAST_TIME = strdup(ba.data());
	qDebug() << "Read ACCOUNT_UPDATE_LAST_TIME:" << ACCOUNT_UPDATE_LAST_TIME << endl;

	MULIT_NODE_INTERVAL = settings.value("MultiNode/Interval", 0).toInt();
	qDebug() << "Read MULIT_NODE_INTERVAL:" << MULIT_NODE_INTERVAL << endl;

	SEND_FLAG = settings.value("Flag/SendFlag", 0).toInt();
	qDebug() << "Read SEND_FLAG:" << SEND_FLAG << endl;
	FILE_FLAG = settings.value("Flag/FileFlag", 0).toInt();
	qDebug() << "Read FILE_FLAG:" << FILE_FLAG << endl;

	//FRONT_INST = settings.value("Flag/FrontInst", 0).toInt();
	//qDebug() << "Read FRONT_INST:" << FRONT_INST << endl;
	ba = settings.value("Flag/FrontInst", "").toString().toLatin1();
	FRONT_INST = strdup(ba.data());
	std::cout << "Read FRONT_INST:" << FRONT_INST << endl;

	ba = settings.value("Flag/FrontIP", "").toString().toLatin1();
	FRONT_IP = strdup(ba.data());
	qDebug() << "Read FRONT_IP:" << FRONT_IP << endl;
	CITY_FLAG = settings.value("Flag/CityFlag", 0).toInt();
	qDebug() << "Read CITY_FLAG:" << CITY_FLAG << endl;

	//lnk20260309添加一个全局变量，标志是否只运行补招程序
	RECALL_ONLY_FLAG = settings.value("Flag/RecallOnlyFlag", 0).toInt();
	qDebug() << "Read RECALL_ONLY_FLAG:" << RECALL_ONLY_FLAG << endl;

//台账配置lnk20241031//////////////////////////////////////////////////////////////
	TERMINAL_STATUS = settings.value("Ledger/TerminalStatus", 0).toString().toStdString();
	std::cout << "Read TERMINAL_STATUS:" << TERMINAL_STATUS << std::endl;
	MONITOR_STATUS = settings.value("Ledger/MonitorStatus", 0).toString().toStdString();
	std::cout << "Read MONITOR_STATUS:" << MONITOR_STATUS << std::endl;
	ICD_FLAG = settings.value("Ledger/IcdFlag", 0).toString().toStdString();
	std::cout << "Read ICD_FLAG:" << ICD_FLAG << std::endl;
	IED_COUNT = settings.value("Ledger/IedCount", 0).toInt();

//////////////////////////////////////////////////添加socket开关///////////////////
	SOCKETENABLE = settings.value("Socket/SocketEnable", 0).toInt();
	SOCKET_PORT = settings.value("Socket/SocketPort", 0).toInt();
//////添加http配置//////////////////////////////////////////////////////////////////
	HTTPENABLE = settings.value("Http/HttpEnable", 0).toInt();
	ba = settings.value("Http/HttpIp", "").toString().toLatin1();
	HTTP_IP = strdup(ba.data());
	std::cout << "Read HTTP_IP:" << HTTP_IP << std::endl;
	HTTP_PORT = settings.value("Http/HttpPort", 0).toInt();
	std::cout << "Read HTTP_PORT:" << HTTP_PORT << std::endl;

	ba = settings.value("Http/WebDevice", "").toString().toLatin1();
	WEB_DEVICE = strdup(ba.data());
	std::cout << "Read WEB_DEVICE:" << WEB_DEVICE << std::endl;
	ba = settings.value("Http/WebIcd", "").toString().toLatin1();
	WEB_ICD = strdup(ba.data());
	std::cout << "Read WEB_ICD:" << WEB_ICD << std::endl;
	ba = settings.value("Http/WebIntegrity", "").toString().toLatin1();
	WEB_INTEGRITY = strdup(ba.data());
	std::cout << "Read WEB_INTEGRITY:" << WEB_INTEGRITY << std::endl;
	ba = settings.value("Http/WebComflag", "").toString().toLatin1();
	WEB_COMFLAG = strdup(ba.data());
	std::cout << "Read WEB_COMFLAG:" << WEB_COMFLAG << std::endl;
	ba = settings.value("Http/WebEvent", "").toString().toLatin1();
	WEB_EVENT = strdup(ba.data());
	std::cout << "Read WEB_EVENT:" << WEB_EVENT << std::endl;
	ba = settings.value("Http/WebFileupload", "").toString().toLatin1();
	WEB_FILEUPLOAD = strdup(ba.data());
	std::cout << "Read WEB_FILEUPLOAD:" << WEB_FILEUPLOAD << std::endl;
	ba = settings.value("Http/WebFiledownload", "").toString().toLatin1();
	WEB_FILEDOWNLOAD = strdup(ba.data());
	std::cout << "Read WEB_FILEDOWNLOAD:" << WEB_FILEDOWNLOAD << std::endl;

///////////////////////////////////////////////////////////////////////////////////////////////////////
	recall_len = settings.value("Recall/recall_lenth", 0).toInt();
	qDebug() << "Read recall_lenth:" << recall_len << endl;
	recall_sta = settings.value("Recall/recall_start", 0).toInt();
	qDebug() << "Read recall_start:" << recall_sta << endl;
	recall_daily = settings.value("Recall/recall_dailytime", 0).toInt();
	qDebug() << "Read recall_dailytime:" << recall_daily << endl;

	COMMUNICATION_LOG_STATUS_TIME = settings.value("CommunicationLog/StatusRecordDuration", 0).toInt();
	qDebug() << "Read COMMUNICATION_LOG_STATUS_TIME:" << COMMUNICATION_LOG_STATUS_TIME << endl;
	COMMUNICATION_LOG_ABNORMAL_TIME = settings.value("CommunicationLog/AbnormalRecordDuration", 0).toInt();
	qDebug() << "Read COMMUNICATION_LOG_ABNORMAL_TIME:" << COMMUNICATION_LOG_ABNORMAL_TIME << endl;


	ba = settings.value("Postgres/Database", "").toString().toLatin1();
	POSTGRES_DATABASE = strdup(ba.data());
	ba = settings.value("Postgres/Username", "").toString().toLatin1();
	POSTGRES_USERNAME = strdup(ba.data());

	ba = settings.value("Postgres/Password", "").toString().toLatin1();
	POSTGRES_PASSWORD = strdup(ba.data());

	ba = settings.value("Postgres/Schema", "").toString().toLatin1();
	POSTGRES_SCHEMA = strdup(ba.data());
	ba = settings.value("Postgres/Dnsname", "").toString().toLatin1();
	POSTGRES_DNSNAME = strdup(ba.data());
	ba = settings.value("Postgres/TablePrefix", "").toString().toLatin1();
	POSTGRES_TABLEPREFIX = strdup(ba.data());


	qDebug() << "Read POSTGRES_DATABASE:" << POSTGRES_DATABASE << endl;
	qDebug() << "Read POSTGRES_USERNAME:" << POSTGRES_USERNAME << endl;
	qDebug() << "Read POSTGRES_PASSWORD:" << POSTGRES_PASSWORD << endl;
	qDebug() << "Read POSTGRES_SCHEMA:" << POSTGRES_SCHEMA << endl;
	qDebug() << "Read POSTGRES_DNSNAME:" << POSTGRES_DNSNAME << endl;
	qDebug() << "Read POSTGRES_TABLEPREFIX:" << POSTGRES_TABLEPREFIX << endl;

/*
	ba = settings.value("Oss/OssEndpoint", "").toString().toLatin1();
	OSS_ENDPOINT = strdup(ba.data());
	ba = settings.value("Oss/AccessKeyID", "").toString().toLatin1();
	ACCESS_KEY_ID = strdup(ba.data());
	ba = settings.value("Oss/AccessKeySecret", "").toString().toLatin1();
	ACCESS_KEY_SECRET = strdup(ba.data());
	ba = settings.value("Oss/BucketName", "").toString().toLatin1();
	BUCKET_NAME = strdup(ba.data());
	qDebug() << "Read OSS_ENDPOINT:" << OSS_ENDPOINT << endl;
	qDebug() << "Read ACCESS_KEY_ID:" << ACCESS_KEY_ID << endl;
	qDebug() << "Read ACCESS_KEY_SECRET:" << ACCESS_KEY_SECRET << endl;
	qDebug() << "Read BUCKET_NAME:" << BUCKET_NAME << endl;
*/

	ba = settings.value("Kafka/brokerlist", "").toString().toLatin1();
	BROKER_LIST = strdup(ba.data());
	ba = settings.value("Kafka/HisTopic", "").toString().toLatin1();
	TOPIC_STAT = strdup(ba.data());
	ba = settings.value("Kafka/PSTTopic", "").toString().toLatin1();
	TOPIC_PST = strdup(ba.data());
	ba = settings.value("Kafka/PLTTopic", "").toString().toLatin1();
	TOPIC_PLT = strdup(ba.data());

	ba = settings.value("Kafka/EventTopic", "").toString().toLatin1();
	TOPIC_EVENT = strdup(ba.data());

	ba = settings.value("Kafka/AlmTopic", "").toString().toLatin1();
	TOPIC_ALARM = strdup(ba.data());
	ba = settings.value("Kafka/SngTopic", "").toString().toLatin1();
	TOPIC_SNG = strdup(ba.data());

	ba = settings.value("Kafka/RTDataTopic", "").toString().toLatin1();
	TOPIC_RTDATA = strdup(ba.data());

	qDebug() << "Read BROKER_LIST:" << BROKER_LIST << endl;
	qDebug() << "Read TOPIC_STAT:" << TOPIC_STAT << endl;
	qDebug() << "Read TOPIC_PST:" << TOPIC_PST << endl;
	qDebug() << "Read TOPIC_PLT:" << TOPIC_PLT << endl;
	qDebug() << "Read TOPIC_EVENT:" << TOPIC_EVENT << endl;
	qDebug() << "Read TOPIC_ALARM:" << TOPIC_ALARM << endl;
	qDebug() << "Read TOPIC_SNG:" << TOPIC_SNG << endl;
	qDebug() << "Read TOPIC_RTDATA:" << TOPIC_RTDATA << endl;


	ba = settings.value("Kafka/Protocol", "").toString().toLatin1();
	PROTOCOL = strdup(ba.data());
	ba = settings.value("Kafka/Mechanisms", "").toString().toLatin1();
	MECHANISMS = strdup(ba.data());
	ba = settings.value("Kafka/KeytabFile", "").toString().toLatin1();
	KEYTAB_FILE = strdup(ba.data());
	ba = settings.value("Kafka/ServiceName", "").toString().toLatin1();
	SERVICE_NAME = strdup(ba.data());
	ba = settings.value("Kafka/Principal", "").toString().toLatin1();
	PRINCIPAL = strdup(ba.data());
	ba = settings.value("Kafka/DomainName", "").toString().toLatin1();
	DOMAIN_NAME = strdup(ba.data());

	qDebug() << "Read PROTOCOL:" << PROTOCOL << endl;
	qDebug() << "Read MECHANISMS:" << MECHANISMS << endl;
	qDebug() << "Read KEYTAB_FILE:" << KEYTAB_FILE << endl;
	qDebug() << "Read SERVICE_NAME:" << SERVICE_NAME << endl;
	qDebug() << "Read PRINCIPAL:" << PRINCIPAL << endl;
	qDebug() << "Read DOMAIN_NAME:" << DOMAIN_NAME << endl;

	ba = settings.value("Web/ClientId", "").toString().toLatin1();
	CLIENT_ID = strdup(ba.data());
	ba = settings.value("Web/ClientSecret", "").toString().toLatin1();
	CLIENT_SECRET = strdup(ba.data());
	ba = settings.value("Web/TokenUrl", "").toString().toLatin1();
	TOKEN_URL = strdup(ba.data());
	ba = settings.value("Web/DeviceUrl", "").toString().toLatin1();
	DEVICE_URL = strdup(ba.data());
	ba = settings.value("Web/GrantType", "").toString().toLatin1();
	GRANT_TYPE = strdup(ba.data());

	qDebug() << "Read CLIENT_ID:" << CLIENT_ID << endl;
	qDebug() << "Read CLIENT_SECRET:" << CLIENT_SECRET << endl;
	qDebug() << "Read TOKEN_URL:" << TOKEN_URL << endl;
	qDebug() << "Read DEVICE_URL:" << DEVICE_URL << endl;
	qDebug() << "Read GRANT_TYPE:" << GRANT_TYPE << endl;

	ba = settings.value("Uds/UdsUploadUrl", "").toString().toLatin1();
	UDS_UPLOAD_URL = strdup(ba.data());
	ba = settings.value("Uds/UdsDownloadUrl", "").toString().toLatin1();
	UDS_DOWNLOAD_URL = strdup(ba.data());

	qDebug() << "Read UDS_UPLOAD_URL:" << UDS_UPLOAD_URL << endl;
	qDebug() << "Read UDS_DOWNLOAD_URL:" << UDS_DOWNLOAD_URL << endl;

/*添加rocketmq的解析10-9 */////////////////////////////////////////////////////////////////
//生产者
	ba = settings.value("RocketMq/producer", "").toString().toLatin1();
	G_ROCKETMQ_PRODUCER = strdup(ba.data());
	ba = settings.value("RocketMq/Ipport", "").toString().toLatin1();
	G_ROCKETMQ_IPPORT = strdup(ba.data());
	ba = settings.value("RocketMq/TESTTopic", "").toString().toLatin1();
	G_ROCKETMQ_TOPIC_TEST = strdup(ba.data());
	ba = settings.value("RocketMq/TESTTag", "").toString().toLatin1();
	G_ROCKETMQ_TAG_TEST = strdup(ba.data());
	ba = settings.value("RocketMq/TESTKey", "").toString().toLatin1();
	G_ROCKETMQ_KEY_TEST = strdup(ba.data());
	QUEUENUM = settings.value("RocketMq/Queuenum", 0).toInt();

	//心跳
	ba = settings.value("RocketMq/Heart_Beat_Topic", "").toString().toLatin1();
	Heart_Beat_Topic = strdup(ba.data());
	ba = settings.value("RocketMq/Heart_Beat_Tag", "").toString().toLatin1();
	Heart_Beat_Tag = strdup(ba.data());
	ba = settings.value("RocketMq/Heart_Beat_Key", "").toString().toLatin1();
	Heart_Beat_Key = strdup(ba.data());
	//消息响应
	ba = settings.value("RocketMq/Topic_Reply_Topic", "").toString().toLatin1();
	Topic_Reply_Topic = strdup(ba.data());
	ba = settings.value("RocketMq/Topic_Reply_Tag", "").toString().toLatin1();
	Topic_Reply_Tag = strdup(ba.data());
	ba = settings.value("RocketMq/Topic_Reply_Key", "").toString().toLatin1();
	Topic_Reply_Key = strdup(ba.data());

	//数据追踪
	ba = settings.value("RocketMq/TraceTopic", "").toString().toLatin1();
	DATA_TRACE_TOPIC = strdup(ba.data());
	ba = settings.value("RocketMq/TraceTag", "").toString().toLatin1();
	DATA_TRACE_TAG = strdup(ba.data());
	ba = settings.value("RocketMq/TraceKey", "").toString().toLatin1();
	DATA_TRACE_KEY = strdup(ba.data());

//消费者
	ba = settings.value("RocketMq/consumer", "").toString().toLatin1();
	G_ROCKETMQ_CONSUMER = strdup(ba.data());
	ba = settings.value("RocketMq/ConsumerIpport", "").toString().toLatin1();
	G_MQCONSUMER_IPPORT = strdup(ba.data());
	ba = settings.value("RocketMq/ConsumerTopicRT", "").toString().toLatin1();
	G_MQCONSUMER_TOPIC_RT = strdup(ba.data());
	ba = settings.value("RocketMq/ConsumerTagRT", "").toString().toLatin1();
	G_MQCONSUMER_TAG_RT = strdup(ba.data());
	ba = settings.value("RocketMq/ConsumerKeyRT", "").toString().toLatin1();
	G_MQCONSUMER_KEY_RT = strdup(ba.data());
	ba = settings.value("RocketMq/ConsumerAccessKey", "").toString().toLatin1();
	G_MQCONSUMER_ACCESSKEY = strdup(ba.data());
	ba = settings.value("RocketMq/ConsumerSecretKey", "").toString().toLatin1();
	G_MQCONSUMER_SECRETKEY = strdup(ba.data());
	ba = settings.value("RocketMq/ConsumerChannel", "").toString().toLatin1();
	G_MQCONSUMER_CHANNEL = strdup(ba.data());
	ba = settings.value("RocketMq/ConsumerTopicUD", "").toString().toLatin1();
	G_MQCONSUMER_TOPIC_UD = strdup(ba.data());
	ba = settings.value("RocketMq/ConsumerTagUD", "").toString().toLatin1();
	G_MQCONSUMER_TAG_UD = strdup(ba.data());
	ba = settings.value("RocketMq/ConsumerKeyUD", "").toString().toLatin1();
	G_MQCONSUMER_KEY_UD = strdup(ba.data());
	ba = settings.value("RocketMq/ConsumerTopicRC", "").toString().toLatin1();
	G_MQCONSUMER_TOPIC_RC = strdup(ba.data());
	ba = settings.value("RocketMq/ConsumerTagRC", "").toString().toLatin1();
	G_MQCONSUMER_TAG_RC = strdup(ba.data());
	ba = settings.value("RocketMq/ConsumerKeyRC", "").toString().toLatin1();
	G_MQCONSUMER_KEY_RC = strdup(ba.data());
	ba = settings.value("RocketMq/ConsumerTopicSET", "").toString().toLatin1();
	G_MQCONSUMER_TOPIC_SET = strdup(ba.data());
	ba = settings.value("RocketMq/ConsumerTagSET", "").toString().toLatin1();
	G_MQCONSUMER_TAG_SET = strdup(ba.data());
	ba = settings.value("RocketMq/ConsumerKeySET", "").toString().toLatin1();
	G_MQCONSUMER_KEY_SET = strdup(ba.data());

	ba = settings.value("RocketMq/ConsumerTopicLOG", "").toString().toLatin1();
	G_MQCONSUMER_TOPIC_LOG = strdup(ba.data());
	ba = settings.value("RocketMq/ConsumerTagLOG", "").toString().toLatin1();
	G_MQCONSUMER_TAG_LOG = strdup(ba.data());
	ba = settings.value("RocketMq/ConsumerKeyLOG", "").toString().toLatin1();
	G_MQCONSUMER_KEY_LOG = strdup(ba.data());
	ba = settings.value("RocketMq/LOGTopic", "").toString().toLatin1();
	G_LOG_TOPIC = strdup(ba.data());
	ba = settings.value("RocketMq/LOGTag", "").toString().toLatin1();
	G_LOG_TAG = strdup(ba.data());
	ba = settings.value("RocketMq/LOGKey", "").toString().toLatin1();
	G_LOG_KEY = strdup(ba.data());
	ba = settings.value("RocketMq/CONNECTTopic", "").toString().toLatin1();
	G_CONNECT_TOPIC = strdup(ba.data());
	ba = settings.value("RocketMq/CONNECTTag", "").toString().toLatin1();
	G_CONNECT_TAG = strdup(ba.data());
	ba = settings.value("RocketMq/CONNECTKey", "").toString().toLatin1();
	G_CONNECT_KEY = strdup(ba.data());

	//lnk20260310添加文件管理的topic和tag
	ba = settings.value("RocketMq/ConsumerTopicFILE", "").toString().toLatin1();
	G_MQCONSUMER_TOPIC_FILE = strdup(ba.data());
	ba = settings.value("RocketMq/ConsumerTagFILE", "").toString().toLatin1();
	G_MQCONSUMER_TAG_FILE = strdup(ba.data());
	ba = settings.value("RocketMq/ConsumerKeyFILE", "").toString().toLatin1();
	G_MQCONSUMER_KEY_FILE = strdup(ba.data());
	ba = settings.value("RocketMq/ReplyTopicFILE", "").toString().toLatin1();
	G_REPLY_TOPIC_FILE = strdup(ba.data());
	ba = settings.value("RocketMq/ReplyTagFILE", "").toString().toLatin1();
	G_REPLY_TAG_FILE = strdup(ba.data());
	ba = settings.value("RocketMq/ReplyKeyFILE", "").toString().toLatin1();
	G_REPLY_KEY_FILE = strdup(ba.data());

	ba = settings.value("RocketMq/ConsumerTopicTEST", "").toString().toLatin1();
	G_MQCONSUMER_TOPIC_TEST = strdup(ba.data());


//MQ测试
	G_TEST_FLAG = settings.value("RocketMq/Testflag", 0).toInt();
	G_TEST_NUM = settings.value("RocketMq/Testnum", 0).toInt();
	G_TEST_TYPE = settings.value("RocketMq/Testtype", 0).toInt();

	ba = settings.value("RocketMq/TestList", 0).toString().toLatin1();
	G_TEST_LIST = strdup(ba.data());
	parseTestList(G_TEST_LIST);//解析测试用的终端列表

//测试shell
	TEST_PORT = settings.value("RocketMq/TestPort", 0).toInt();
	
//生产者相关打印
	std::cout << "Read G_ROCKETMQ_PRODUCER:" << G_ROCKETMQ_PRODUCER << std::endl;
	std::cout << "Read G_ROCKETMQ_IPPORT:" << G_ROCKETMQ_IPPORT << std::endl;
	std::cout << "Read G_ROCKETMQ_TOPIC_TEST:" << G_ROCKETMQ_TOPIC_TEST << std::endl;
	std::cout << "Read G_ROCKETMQ_TAG_TEST:" << G_ROCKETMQ_TAG_TEST << std::endl;
	std::cout << "Read G_ROCKETMQ_KEY_TEST:" << G_ROCKETMQ_KEY_TEST << std::endl;
	std::cout << "Read QUEUENUM:" << QUEUENUM << std::endl;
	std::cout << "Read G_LOG_TOPIC:" << G_LOG_TOPIC << std::endl;
	std::cout << "Read G_LOG_TAG:" << G_LOG_TAG << std::endl;
	std::cout << "Read G_LOG_KEY:" << G_LOG_KEY << std::endl;
	std::cout << "Read G_CONNECT_TOPIC:" << G_CONNECT_TOPIC << std::endl;
	std::cout << "Read G_CONNECT_TAG:" << G_CONNECT_TAG << std::endl;
	std::cout << "Read G_CONNECT_KEY:" << G_CONNECT_KEY << std::endl;
	std::cout << "Read Heart_Beat_Topic:" << Heart_Beat_Topic << std::endl;
	std::cout << "Read Heart_Beat_Tag:" << Heart_Beat_Tag << std::endl;
	std::cout << "Read Heart_Beat_Key:" << Heart_Beat_Key << std::endl;
	std::cout << "Read Topic_Reply_Topic:" << Topic_Reply_Topic << std::endl;
	std::cout << "Read Topic_Reply_Tag:" << Topic_Reply_Tag << std::endl;
	std::cout << "Read Topic_Reply_Key:" << Topic_Reply_Key << std::endl;
//消费者相关打印
	std::cout << "Read G_ROCKETMQ_CONSUMER:" << G_ROCKETMQ_CONSUMER << std::endl;
	std::cout << "Read G_MQCONSUMER_IPPORT:" << G_MQCONSUMER_IPPORT << std::endl;
	std::cout << "Read G_MQCONSUMER_TOPIC_RT:" << G_MQCONSUMER_TOPIC_RT << std::endl;
	std::cout << "Read G_MQCONSUMER_TAG_RT:" << G_MQCONSUMER_TAG_RT << std::endl;
	std::cout << "Read G_MQCONSUMER_KEY_RT:" << G_MQCONSUMER_KEY_RT << std::endl;
	std::cout << "Read G_MQCONSUMER_ACCESSKEY:" << G_MQCONSUMER_ACCESSKEY << std::endl;
	std::cout << "Read G_MQCONSUMER_SECRETKEY:" << G_MQCONSUMER_SECRETKEY << std::endl;
	std::cout << "Read G_MQCONSUMER_CHANNEL:" << G_MQCONSUMER_CHANNEL << std::endl;
	std::cout << "Read G_MQCONSUMER_TOPIC_UD:" << G_MQCONSUMER_TOPIC_UD << std::endl;
	std::cout << "Read G_MQCONSUMER_TAG_UD:" << G_MQCONSUMER_TAG_UD << std::endl;
	std::cout << "Read G_MQCONSUMER_KEY_UD:" << G_MQCONSUMER_KEY_UD << std::endl;
	std::cout << "Read G_MQCONSUMER_TOPIC_RC:" << G_MQCONSUMER_TOPIC_RC << std::endl;
	std::cout << "Read G_MQCONSUMER_TAG_RC:" << G_MQCONSUMER_TAG_RC << std::endl;
	std::cout << "Read G_MQCONSUMER_KEY_RC:" << G_MQCONSUMER_KEY_RC << std::endl;
	std::cout << "Read G_MQCONSUMER_TOPIC_SET:" << G_MQCONSUMER_TOPIC_SET << std::endl;
	std::cout << "Read G_MQCONSUMER_TAG_SET:" << G_MQCONSUMER_TAG_SET << std::endl;
	std::cout << "Read G_MQCONSUMER_KEY_SET:" << G_MQCONSUMER_KEY_SET << std::endl;
	std::cout << "Read G_MQCONSUMER_TOPIC_LOG:" << G_MQCONSUMER_TOPIC_LOG << std::endl;
	std::cout << "Read G_MQCONSUMER_TAG_LOG:" << G_MQCONSUMER_TAG_LOG << std::endl;
	std::cout << "Read G_MQCONSUMER_KEY_LOG:" << G_MQCONSUMER_KEY_LOG << std::endl;
//Mq测试相关打印
	std::cout << "Read G_TEST_FLAG:" << G_TEST_FLAG << std::endl;
	std::cout << "Read G_TEST_NUM:" << G_TEST_NUM << std::endl;
	std::cout << "Read G_TEST_TYPE:" << G_TEST_TYPE << std::endl;


	// 日志限流配置
	G_LOG_RATE_RESET_SEC = settings.value("LogRate/ResetSec", 3600).toInt();
	G_LOG_RATE_LIMIT_SEC = settings.value("LogRate/LimitSec", 60).toInt();
	G_LOG_RATE_KEEP_ALL_MS = settings.value("LogRate/KeepAllMs", 60000).toInt();
	G_LOG_RATE_KEEP_BURST_MS = settings.value("LogRate/KeepBurstMs", 1000).toInt();
	G_LOG_RATE_KEEP_BURST_COUNT = settings.value("LogRate/KeepBurstCount", 60).toInt();
	G_LOG_RATE_KEEP_HIGHFREQ_COUNT = settings.value("LogRate/KeepHighFreqCount", 10).toInt();

	std::cout << "Read G_LOG_RATE_RESET_SEC:" << G_LOG_RATE_RESET_SEC << std::endl;
	std::cout << "Read G_LOG_RATE_LIMIT_SEC:" << G_LOG_RATE_LIMIT_SEC << std::endl;
	std::cout << "Read G_LOG_RATE_KEEP_ALL_MS:" << G_LOG_RATE_KEEP_ALL_MS << std::endl;
	std::cout << "Read G_LOG_RATE_KEEP_BURST_MS:" << G_LOG_RATE_KEEP_BURST_MS << std::endl;
	std::cout << "Read G_LOG_RATE_KEEP_BURST_COUNT:" << G_LOG_RATE_KEEP_BURST_COUNT << std::endl;
	std::cout << "Read G_LOG_RATE_KEEP_HIGHFREQ_COUNT:" << G_LOG_RATE_KEEP_HIGHFREQ_COUNT << std::endl;

	if (G_LOG_RATE_RESET_SEC <= 0) G_LOG_RATE_RESET_SEC = 3600;
	if (G_LOG_RATE_LIMIT_SEC <= 0) G_LOG_RATE_LIMIT_SEC = 60;
	if (G_LOG_RATE_KEEP_ALL_MS <= 0) G_LOG_RATE_KEEP_ALL_MS = 60000;
	if (G_LOG_RATE_KEEP_BURST_MS <= 0) G_LOG_RATE_KEEP_BURST_MS = 1000;
	if (G_LOG_RATE_KEEP_BURST_COUNT < 0) G_LOG_RATE_KEEP_BURST_COUNT = 60;
	if (G_LOG_RATE_KEEP_HIGHFREQ_COUNT < 0) G_LOG_RATE_KEEP_HIGHFREQ_COUNT = 10;

//20241212lnk添加多前置
	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;
	}
	else{
		MULTIPLE_NODE_FLAG = 0;
		std::cout << "this is single process" << std::endl;
	}
	
//20250109lnk添加进程测试打印端口
	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;
	}
	else if (g_node_id == PQDIF_DATA_BASE_NODE_ID) {
		TEST_PORT = TEST_PORT + PQDIF_DATA_BASE_NODE_ID +  g_front_seg_index;
	}
	
}

// CZY 测试 ping IP
// 执行命令并获取输出结果
std::string executeCommand(const std::string& command) {
	std::string result = "";
	FILE* pipe = popen(command.c_str(), "r");
	if (!pipe) {
		std::cerr << "Error executing command." << std::endl;
		return result;
	}

	char buffer[128];
	while (!feof(pipe)) {
		if (fgets(buffer, 128, pipe) != NULL)
			result += buffer;
	}

	pclose(pipe);
	return result;
}

bool telnet_port_socket(const char* ip, int port) {
	struct sockaddr_in server_addr;
	memset(&server_addr, 0, sizeof(server_addr));
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(port);
	inet_pton(AF_INET, ip, &server_addr.sin_addr);

	int sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (sockfd < 0) {
		perror("socket");
		return false;
	}

	// 设置连接超时时间为5秒
	struct timeval timeout;
	timeout.tv_sec = 5;
	timeout.tv_usec = 0;
	if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout)) < 0) {
		perror("setsockopt");
		close(sockfd);
		return false;
	}
	if (setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout, sizeof(timeout)) < 0) {
		perror("setsockopt");
		close(sockfd);
		return false;
	}

	if (connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
		if (errno == EINPROGRESS) {
			fd_set fds;
			FD_ZERO(&fds);
			FD_SET(sockfd, &fds);

			int ret = select(sockfd + 1, NULL, &fds, NULL, &timeout);
			if (ret == -1) {
				perror("select");
				close(sockfd);
				return false;
			}
			else if (ret == 0) {
				// 连接超时
				close(sockfd);
				return false;
			}
			else {
				int error;
				socklen_t len = sizeof(error);
				getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len);
				if (error == 0) {
					// 连接成功
					close(sockfd);
					return true;
				}
				else {
					// 连接失败
					close(sockfd);
					return false;
				}
			}
		}
		else {
			// 连接失败
			close(sockfd);
			return false;
		}
	}

	// 连接成功
	close(sockfd);
	return true;
}

// ping IP
bool ping_ip(const std::string& ip) {
	std::string command = "ping -c 1 -w 2.5 " + ip;
	std::string result = executeCommand(command);

	// 判断输出结果是否包含 "1 packets transmitted, 1 received" 字符串
	if (result.find("1 packets transmitted, 1 received") != std::string::npos) {
		return true;
	}
	else {
		return false;
	}
}
void pingPrint(QTcpSocket* clientSocket, const std::string &msg) {
    // 输出到标准输出
    std::cout << msg << std::endl;
    // 记录到日志文件
    add_comm_log(const_cast<char*>(msg.c_str()));
    // 如果 clientSocket 不为空，则发送到 shell
    if (clientSocket != nullptr) {
        clientSocket->write((msg + "\r\n").c_str());
        clientSocket->flush();
    }
}
int Worker::init_ping_telnet(QTcpSocket* clientSocket, int& ip_count, int& telnet_count) {
    pingPrint(clientSocket, "start test ping telnet");
    ied_t* ied = NULL;
    int iedno;
    // 遍历所有设备
    for (iedno = 0; iedno < g_node->n_clients && !g_stopTelnetTest; iedno++) {
        // **1. 监听输入，用户输入 ``` 退出**
        if (clientSocket->waitForReadyRead(100)) {  // ? 监听输入
            QByteArray input = clientSocket->readAll().trimmed();
            if (input == "`") {  // ? 用户输入 ```，退出日志模式
                std::cout << "Received '`' from shell socket! Exiting viewlog...\n";
				g_stopTelnetTest = true;
                break;
            }
        }

        ied = g_node->clients[iedno];
        if (ied) {
            if (g_onlyIP[0] != 0 && (strcmp(g_onlyIP, ied->channel[0].addr_str) != 0)) {
                continue;
            }
            bool pingResult = ping_ip(ied->channel[0].addr_str);
            bool telnetResult = telnet_port_socket(ied->channel[0].addr_str, ied->channel[0].port);

            std::string logMsg;
            logMsg.append("Ping to IP ");
            logMsg.append(ied->channel[0].addr_str);
            if (pingResult) {
                ip_count++;
                logMsg.append(" is successful.");
            } else {
                logMsg.append(" is unsuccessful.");
            }
            pingPrint(clientSocket, logMsg);
            add_comm_log(const_cast<char*>(logMsg.c_str()));

            logMsg = "";
            logMsg.append("Telnet port ");
            logMsg.append(QString::number(ied->channel[0].port).toStdString());
            if (telnetResult) {
                telnet_count++;
                logMsg.append(" is open on IP ");
            } else {
                logMsg.append(" is closed on IP ");
            }
            logMsg.append(ied->channel[0].addr_str);
            add_comm_log(const_cast<char*>(logMsg.c_str()));
            pingPrint(clientSocket, logMsg);

            std::string countMsg = "iedno:" + intToString(iedno) +
                                   " ip_count:" + intToString(ip_count) +
                                   " telnet_count:" + intToString(telnet_count);
            pingPrint(clientSocket, countMsg);

            // 更新配置文件
            QString MyKafkaIniFilename = QString("../etc/") + QString("testping.ini");
            QSettings settings(MyKafkaIniFilename, QSettings::IniFormat);
            settings.setValue("test/IpCount", ip_count);
            settings.setValue("test/TelnetCount", telnet_count);
            settings.setValue("test/IedCount", iedno);
        }
    }
    pingPrint(clientSocket, "end test ping telnet");
    return 1;
}

//CZY 2023-08-30 chat* null or emptry
bool isCharPtrEmpty(const char* str) {
	return str == nullptr || str[0] == '\0' || str == "";
}

// CZY 2024-07-24 函数：检查字符串是否全为数字  
int isAllDigits(const char* str) {
	while (*str) {
		if (!isdigit((unsigned char)*str)) {
			return 0; // 发现非数字字符  
		}
		str++;
	}
	return 1; // 所有字符都是数字  
}

// CZY 2024-07-24函数：将字符串转换为int（如果可能）  
int stringToInt(const char* str, int* result) {
	if (isAllDigits(str)) {
		*result = atoi(str); // 使用atoi进行转换，注意atoi不会检查溢出  
		return 1; // 转换成功  
	}
	return 0; // 转换失败  
}


int GetServerIndexFromDB() //获取前置服务器序号
{

	register int fd, interface;
	const int MAXINTERFACES = 100;
	struct ifreq buf[MAXINTERFACES];
	struct arpreq arp;
	struct ifconf ifc;
	char mac[32] = "";

	if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) >= 0) {
		ifc.ifc_len = sizeof buf;
		ifc.ifc_buf = (caddr_t)buf;

		if (!ioctl(fd, SIOCGIFCONF, (char*)&ifc)) {
			
			interface = ifc.ifc_len / sizeof(struct ifreq);
			printf("\ninterface num is interface= %d\n", interface);

			while (interface-- > 0) {
				printf("net device %s\n", buf[interface].ifr_name);

				/*确保网卡是否支持混杂模式 Jugde whether the net card status is promisc */
				if (!(ioctl(fd, SIOCGIFFLAGS, (char*)&buf[interface]))) {
					if (buf[interface].ifr_flags & IFF_PROMISC) {
						printf("the interface is PROMISC \n");
					}
				}
				else {
					char str[256] = "";
					sprintf(str, "cpm: ioctl device %s", buf[interface].ifr_name);
					perror(str);
				}

				/*判断网卡状态是否打开 Judge whether the net card status is up */
				if (buf[interface].ifr_flags & IFF_UP) {
					printf("the interface status is UP\n");
				}
				else {
					printf("the interface status is DOWN\n");
				}

				/*获取网卡IP地址 Get IP of the net card */
				if (!(ioctl(fd, SIOCGIFADDR, (char*)&buf[interface]))) {
					printf("IP address is: %s\n", inet_ntoa(((struct sockaddr_in*)(&buf[interface].ifr_addr))->sin_addr));

				}
				else {
					char str[256] = "";
					sprintf(str, "cpm: ioctl device %s", buf[interface].ifr_name);
					perror(str);
				}

				/*获取网卡的HW地址 Get HW ADDRESS of the net card */
				if (!(ioctl(fd, SIOCGIFHWADDR, (char*)&buf[interface]))) {
					printf("HW address is: ");
					sprintf(mac, "%02x:%02x:%02x:%02x:%02x:%02x",
						(unsigned char)buf[interface].ifr_hwaddr.sa_data[0],
						(unsigned char)buf[interface].ifr_hwaddr.sa_data[1],
						(unsigned char)buf[interface].ifr_hwaddr.sa_data[2],
						(unsigned char)buf[interface].ifr_hwaddr.sa_data[3],
						(unsigned char)buf[interface].ifr_hwaddr.sa_data[4],
						(unsigned char)buf[interface].ifr_hwaddr.sa_data[5]);
					printf("%s\n\n", mac);
				}
				else {
					char str[256];
					sprintf(str, "cpm: ioctl device %s", buf[interface].ifr_name);
					perror(str);
				}
			}
		}
		else
			perror("cpm: ioctl");
	}
	else
		perror("cpm: socket");

	close(fd);
	return 0;
}
/////////////////////////////////////////////////////////CZY

void parse_one_rpt_log_ini(int idx, QStringList* rpt_cfg_strlist, QStringList* log_cfg_strlist, char* type)
{
	char* tmp;
	tmp = Get_xmlpath(type);//获取模型id号
	if (tmp == NULL) {//找不到模型编号使用默认的型号和配置文件
		//zw修改 2023 - 8 - 15 将触发报告的解析移至XML 原配置RptLogCfg.ini取消
		if (strcmp(subdir, "cfg_stat_data") == 0)
		{
			QString devtype;
			devtype.append(type);
			QString devtype2;
			devtype2.append("HL-6810");//稳态默认型号HL-6810

			qDebug() << "cfg_stat_data";
			QString xml_dir = QString("../") + QString("etc/"); //Linux下调试路径

			QDomDocument doc;   //新建QDomDocument类对象，它代表一个XML文档
			QFile file(xml_dir + QString("JiangSu_Config.xml"));//默认配置文件JiangSu_Config.xml
			if (!file.open(QIODevice::ReadOnly | QFile::Text)) //以只读方式打开xml
			{
				qDebug() << "Read RPT Error1";
				return;
			}
			if (!doc.setContent(&file)) //将文件内容读到doc中
			{
				qDebug() << "Read RPT Error2";
				file.close();
				return;
			}
			file.close();
			QDomNode firstNode = doc.firstChild(); //根节点"JSConfigTemplate"
			QDomElement docElem = doc.documentElement();  //返回根节点元素
			QDomNode n = docElem.firstChild(); //获得doc的第一个节点，即"Topic"
			while (!n.isNull()) //如果Topic节点不为空 
			{
				if (n.isElement())
				{
					QDomElement e = n.toElement(); //将其转换为元素
					QString strTag = e.tagName(); //
					if ("ReportMap" == strTag)//zw修改 2023 - 8 - 15 增加判断 将触发报告的解析移至XML 原配置RptLogCfg.ini取消
					{
						qDebug() << "ReportStat";
						QDomNodeList list = e.childNodes(); //获得元素的所有子节点的列表
						for (int i = 0; i < list.count(); i++) //遍历 DataType列表
						{
							QDomNode node = list.at(i); //node1 <DataType>
							if (node.isElement())
							{
								QString strTag2 = node.toElement().tagName(); //DataType节点
								if ("ReportStat" == strTag2)
								{
									QDomNodeList list2 = node.childNodes(); //获得元素DataType的所有子节点的列表 
									for (int i2 = 0; i2 < list2.count(); i2++) //遍历 Monitor列表
									{
										QDomNode node2 = list2.at(i2); //node2 <Monitor>
										if (node2.isElement())
										{
											rpt_cfg_strlist->append(node2.toElement().attribute("ReportControl"));
											qDebug() << "devtype:" << devtype << node2.toElement().attribute("ReportControl").toAscii().data();
										}
									}
								}
							}

						}
					}
				}
				n = n.nextSibling();
			}

			QString log_cfg_str = "LLN0$LG$lcStatisticData,dsStatisticData,PQM1,0,600000,1,0,0,0";
			log_cfg_strlist->append(log_cfg_str);
		}
		if (strcmp(subdir, "cfg_soe_comtrade") == 0)
		{
			qDebug() << "cfg_soe_comtrade";
			QString xml_dir = QString("../") + QString("etc/"); //Linux下调试路径

			QDomDocument doc;   //新建QDomDocument类对象，它代表一个XML文档
			QFile file(xml_dir + QString("JiangSu_Config.xml"));
			if (!file.open(QIODevice::ReadOnly | QFile::Text)) //以只读方式打开xml
			{
				qDebug() << "Read RPT Error1";
				return;
			}
			if (!doc.setContent(&file)) //将文件内容读到doc中
			{
				qDebug() << "Read RPT Error2";
				file.close();
				return;
			}
			file.close();
			QDomNode firstNode = doc.firstChild(); //根节点"JSConfigTemplate"
			QDomElement docElem = doc.documentElement();  //返回根节点元素
			QDomNode n = docElem.firstChild(); //获得doc的第一个节点，即"Topic"
			while (!n.isNull()) //如果Topic节点不为空 
			{
				if (n.isElement())
				{
					QDomElement e = n.toElement(); //将其转换为元素
					QString strTag = e.tagName(); //
					if ("ReportMap" == strTag)//zw修改 2023 - 8 - 15 增加判断 将触发报告的解析移至XML 原配置RptLogCfg.ini取消
					{
						qDebug() << "ReportEvent";
						QDomNodeList list = e.childNodes(); //获得元素的所有子节点的列表
						for (int i = 0; i < list.count(); i++) //遍历 DataType列表
						{
							QDomNode node = list.at(i); //node1 <DataType>
							if (node.isElement())
							{
								QString strTag2 = node.toElement().tagName(); //DataType节点
								if ("ReportEvent" == strTag2)
								{
									QDomNodeList list2 = node.childNodes(); //获得元素DataType的所有子节点的列表 
									for (int i2 = 0; i2 < list2.count(); i2++) //遍历 Monitor列表
									{
										QDomNode node2 = list2.at(i2); //node2 <Monitor>
										if (node2.isElement())
										{
											rpt_cfg_strlist->append(node2.toElement().attribute("ReportControl"));
											qDebug() << node2.toElement().attribute("ReportControl").toAscii().data();
										}
									}
								}
							}

						}
					}
				}
				n = n.nextSibling();
			}

			QString log_cfg_str = "LLN0$LG$lcStatisticData,dsStatisticData,PQM1,0,600000,1,0,0,0";
			log_cfg_strlist->append(log_cfg_str);
		}
		if (strcmp(subdir, "cfg_his_data") == 0 || strcmp(subdir, "cfg_newhis_data") == 0 || strcmp(subdir, "cfg_recallhis_data") == 0 || strcmp(subdir, "cfg_recallall_data") == 0)
		{
			QString log_cfg_str = "LLN0$LG$lcStatisticData,dsStatisticData,PQM1,0,600000,1,0,0,0";
			log_cfg_strlist->append(log_cfg_str);
		}
	}
	else//型号不为空读取指定的配置文件
	{
		QString tmppath;
		tmppath.append("/FeProject/dat/").append(tmp).append(".xml");
		qDebug() << tmppath;

		//lnk20241126调试用
		std::cout << "rptcfgfile:" << tmppath.toStdString() << std::endl;

		//zw修改 2023 - 8 - 15 将触发报告的解析移至XML 原配置RptLogCfg.ini取消
		if (strcmp(subdir, "cfg_stat_data") == 0)
		{
			qDebug() << "cfg_stat_data";
			QDomDocument doc;   //新建QDomDocument类对象，它代表一个XML文档
			QFile file(tmppath);
			if (!file.open(QIODevice::ReadOnly | QFile::Text)) //以只读方式打开xml
			{
				qDebug() << "Read RPT Error1";		

				//lnk20241126调试用
				std::cout << "Read RPT Error1" << std::endl;

				return;
			}
			if (!doc.setContent(&file)) //将文件内容读到doc中
			{
				qDebug() << "Read RPT Error2";

				//lnk20241126调试用
				std::cout << "Read RPT Error2" << std::endl;

				file.close();
				return;
			}
			file.close();
			QDomNode firstNode = doc.firstChild(); //根节点"JSConfigTemplate"
			QDomElement docElem = doc.documentElement();  //返回根节点元素
			QDomNode n = docElem.firstChild(); //获得doc的第一个节点，即"Topic"
			while (!n.isNull()) //如果Topic节点不为空 
			{
				if (n.isElement())
				{
					QDomElement e = n.toElement(); //将其转换为元素
					QString strTag = e.tagName(); //
					if ("ReportMap" == strTag)//zw修改 2023 - 8 - 15 增加判断 将触发报告的解析移至XML 原配置RptLogCfg.ini取消
					{
						qDebug() << "ReportStat";
						QDomNodeList list = e.childNodes(); //获得元素的所有子节点的列表
						for (int i = 0; i < list.count(); i++) //遍历 DataType列表
						{
							QDomNode node = list.at(i); //node1 <DataType>
							if (node.isElement())
							{
								QString strTag2 = node.toElement().tagName(); //DataType节点
								if ("ReportStat" == strTag2)
								{
									QDomNodeList list2 = node.childNodes(); //获得元素DataType的所有子节点的列表 
									for (int i2 = 0; i2 < list2.count(); i2++) //遍历 Monitor列表
									{
										QDomNode node2 = list2.at(i2); //node2 <Monitor>
										if (node2.isElement())
										{
											rpt_cfg_strlist->append(node2.toElement().attribute("ReportControl"));
											qDebug() << node2.toElement().attribute("ReportControl").toAscii().data();//读取各个报告配置
										}
									}
								}
							}

						}
					}
				}
				n = n.nextSibling();
			}

			QString log_cfg_str = "LLN0$LG$lcStatisticData,dsStatisticData,PQM1,0,600000,1,0,0,0";//日志的配置是写死的
			log_cfg_strlist->append(log_cfg_str);
		}
		
		//lnk添加实时数据20241125
		if (strcmp(subdir, "cfg_3s_data") == 0)
		{
			qDebug() << "cfg_3s_data";
			QDomDocument doc;   //新建QDomDocument类对象，它代表一个XML文档
			QFile file(tmppath);
			if (!file.open(QIODevice::ReadOnly | QFile::Text)) //以只读方式打开xml
			{
				qDebug() << "Read RPT Error1";
				return;
			}
			if (!doc.setContent(&file)) //将文件内容读到doc中
			{
				qDebug() << "Read RPT Error2";
				file.close();
				return;
			}
			file.close();
			QDomNode firstNode = doc.firstChild(); //根节点"JSConfigTemplate"
			QDomElement docElem = doc.documentElement();  //返回根节点元素
			QDomNode n = docElem.firstChild(); //获得doc的第一个节点，即"Topic"
			while (!n.isNull()) //如果Topic节点不为空 
			{
				if (n.isElement())
				{
					QDomElement e = n.toElement(); //将其转换为元素
					QString strTag = e.tagName(); //
					if ("ReportMap" == strTag)//zw修改 2023 - 8 - 15 增加判断 将触发报告的解析移至XML 原配置RptLogCfg.ini取消
					{
						qDebug() << "ReportReal";
						QDomNodeList list = e.childNodes(); //获得元素的所有子节点的列表
						for (int i = 0; i < list.count(); i++) //遍历 DataType列表
						{
							QDomNode node = list.at(i); //node1 <DataType>
							if (node.isElement())
							{
								QString strTag2 = node.toElement().tagName(); //DataType节点
								if ("ReportReal" == strTag2)
								{
									QDomNodeList list2 = node.childNodes(); //获得元素DataType的所有子节点的列表 
									for (int i2 = 0; i2 < list2.count(); i2++) //遍历 Monitor列表
									{
										QDomNode node2 = list2.at(i2); //node2 <Monitor>
										if (node2.isElement())
										{
											rpt_cfg_strlist->append(node2.toElement().attribute("ReportControl"));
											qDebug() << node2.toElement().attribute("ReportControl").toAscii().data();
										}
									}
								}
							}

						}
					}
				}
				n = n.nextSibling();
			}

			//实时数据没有日志
			//QString log_cfg_str = "LLN0$LG$lcStatisticData,dsStatisticData,PQM1,0,600000,1,0,0,0";
			//log_cfg_strlist->append(log_cfg_str);
		}
		
		if (strcmp(subdir, "cfg_soe_comtrade") == 0)
		{
			qDebug() << "cfg_soe_comtrade";
			QDomDocument doc;   //新建QDomDocument类对象，它代表一个XML文档
			QFile file(tmppath);
			if (!file.open(QIODevice::ReadOnly | QFile::Text)) //以只读方式打开xml
			{
				qDebug() << "Read RPT Error1";
				return;
			}
			if (!doc.setContent(&file)) //将文件内容读到doc中
			{
				qDebug() << "Read RPT Error2";
				file.close();
				return;
			}
			file.close();
			QDomNode firstNode = doc.firstChild(); //根节点"JSConfigTemplate"
			QDomElement docElem = doc.documentElement();  //返回根节点元素
			QDomNode n = docElem.firstChild(); //获得doc的第一个节点，即"Topic"
			while (!n.isNull()) //如果Topic节点不为空 
			{
				if (n.isElement())
				{
					QDomElement e = n.toElement(); //将其转换为元素
					QString strTag = e.tagName(); //
					if ("ReportMap" == strTag)//zw修改 2023 - 8 - 15 增加判断 将触发报告的解析移至XML 原配置RptLogCfg.ini取消
					{
						qDebug() << "ReportEvent";
						QDomNodeList list = e.childNodes(); //获得元素的所有子节点的列表
						for (int i = 0; i < list.count(); i++) //遍历 DataType列表
						{
							QDomNode node = list.at(i); //node1 <DataType>
							if (node.isElement())
							{
								QString strTag2 = node.toElement().tagName(); //DataType节点
								if ("ReportEvent" == strTag2)//区分点
								{
									QDomNodeList list2 = node.childNodes(); //获得元素DataType的所有子节点的列表 
									for (int i2 = 0; i2 < list2.count(); i2++) //遍历 Monitor列表
									{
										QDomNode node2 = list2.at(i2); //node2 <Monitor>
										if (node2.isElement())
										{
											rpt_cfg_strlist->append(node2.toElement().attribute("ReportControl"));
											qDebug() << node2.toElement().attribute("ReportControl").toAscii().data();
										}
									}
								}
							}

						}
					}
				}
				n = n.nextSibling();
			}

			QString log_cfg_str = "LLN0$LG$lcStatisticData,dsStatisticData,PQM1,0,600000,1,0,0,0";
			log_cfg_strlist->append(log_cfg_str);
		}
		if (strcmp(subdir, "cfg_his_data") == 0 || strcmp(subdir, "cfg_newhis_data") == 0 || strcmp(subdir, "cfg_recallhis_data") == 0 || strcmp(subdir, "cfg_recallall_data") == 0)
		{
			QString log_cfg_str = "LLN0$LG$lcStatisticData,dsStatisticData,PQM1,0,600000,1,0,0,0";
			log_cfg_strlist->append(log_cfg_str);
		}
		delete[] tmp;
	}
}

int parse_rpt_log_ini()
{
	const int MAX_DEV_FLAG = 10;
	bool not_loaded[MAX_DEV_FLAG];

	QMap<QString, QStringList*> rpt_cfg_strlists;
	QMap<QString, QStringList*> log_cfg_strlists;

	int			iedno, cpuno;
	ied_t* ied;
	ied_usr_t* ied_usr;
	LD_info_t* LD_info;
	char		buf[256];


	for (iedno = 0; iedno < g_node->n_clients; iedno++) {
		ied = g_node->clients[iedno];
		ied_usr = GET_IEDEXT_ADDR(ied);
		QString type;
		type.append(ied_usr->dev_type);
		if (!rpt_cfg_strlists.contains(type))
		{
			QStringList* rpt_temp = new QStringList();
			QStringList* log_temp = new QStringList();
			rpt_cfg_strlists.insert(type, rpt_temp);
			log_cfg_strlists.insert(type, log_temp);
			//g_DevFlag没有使用
			parse_one_rpt_log_ini(g_DevFlag, rpt_cfg_strlists[type], log_cfg_strlists[type], ied_usr->dev_type);

		}

		for (cpuno = 0; cpuno < ied->cpucount; cpuno++) {//遍历所有测点，但是某些测点可能为空，台账初始化时ied->cpucount取的是测点号
			LD_info = &(ied_usr->LD_info[cpuno]);//测点号从1开始，cpuno从0开始，所以ied_usr->LD_info[cpuno]对应测点号为cpuno+1，注意台账初始化时ied_usr->LD_info数组的大小是按照测点总数分配的，所以有些测点号可能没有对应的LD_info，添加判断防止崩溃
			//char str[256];					//256大小
			char* tmp = Get_IED(ied_usr->dev_type);
			if(tmp == NULL){std::cerr << "front read ied config error!" << std::endl;continue;}
			qDebug() << tmp << endl;

			//apr_snprintf(str, sizeof(str), tmp, cpuno + 1);

			//ied_usr->LD_info[cpuno].LD_name = apr_pstrdup(g_init_pool, str);

			//已经分配过内存，直接复制到里面，在台账初始化有一句ied_usr->LD_info[cpuno - 1].LD_name = (char *)apr_palloc(g_init_pool, 256);
			//调试用
			printf("%s使用内存地址 LD_name[%d]: %p\n", ied_usr->terminal_id, cpuno, (void*)ied_usr->LD_info[cpuno].LD_name);

			//添加判断，比如有的监测点没有cpuno为2，直接申请了LD_info[1]，没申请LD_info[0]
			if(ied_usr->LD_info[cpuno].LD_name == NULL){	//空说明台账初始化没申请空间
				printf("this ld_info didn't palloc space ,maybe this ledger has problem!");
				DIY_ERRORLOG_CODE("process",0,LOG_CODE_RPTINIT,"【ERROR】终端%s的监测点序号为%d的监测点无法初始化报告,这个装置的台账存在缺失,请检查装置台账的监测点总数和各监测点的序号",ied_usr->terminal_id,cpuno + 1);

				//添加默认值，防止崩溃20260601
				LD_info->rptcount = 0;
				LD_info->logcount = 0;
				LD_info->cpuno = 0;
				LD_info->LD_name = NULL;
				LD_info->rptinfo = NULL;
				LD_info->loginfo = NULL;

				delete[] tmp;//释放内存
				
				continue;//跳过防止崩溃
			}
			
			apr_snprintf(ied_usr->LD_info[cpuno].LD_name, 256, tmp, cpuno + 1);//注意拷贝大小，容易段错误，

			delete[] tmp;

			init_rptctrl_by_count(LD_info, rpt_cfg_strlists[type]->size());

			for (int i = 0; i < rpt_cfg_strlists[type]->size(); ++i) {

				apr_snprintf(buf, sizeof(buf), "%s", rpt_cfg_strlists[type]->at(i).toAscii().constData());

				//fill_rptctrl_by_cfg(LD_info, i, buf);
				int ret = fill_rptctrl_by_cfg(LD_info, i, buf);
				if (ret != 0) {
				    printf("[RPT_INIT][ERROR] fill_rptctrl_by_cfg failed dev=%s cpu=%d rpt=%d cfg=%s\n",
				           ied_usr->terminal_id, cpuno, i, buf);

					DIY_ERRORLOG_CODE(ied_usr->terminal_id,1,LOG_CODE_RPTINIT,"【ERROR】终端%s的监测点序号为%d的监测点的第%d个报告配置解析失败，请检查报告配置格式是否正确",ied_usr->terminal_id, cpuno + 1, i + 1);
					
				    if (LD_info->rptinfo && LD_info->rptinfo[i]) {
				        LD_info->rptinfo[i]->rptID = NULL;
				        LD_info->rptinfo[i]->LD_info = NULL;
				    }
				}

			}

			init_logctrl_by_count(LD_info, log_cfg_strlists[type]->size());
			for (int i = 0; i < log_cfg_strlists[type]->size(); ++i) {
				apr_snprintf(buf, sizeof(buf), "%s", log_cfg_strlists[type]->at(i).toAscii().constData());
				char* tmp = Get_LDevice(ied_usr->dev_type);
				if(tmp == NULL){std::cerr << "front read monitor config error!" << std::endl;continue;}
				fill_logctrl_by_cfg(LD_info, i, buf, tmp);
				delete[] tmp;
			}

		}
	}

	//报告控制块和日志控制块处理结束后清理容器
	for (QMap<QString, QStringList*>::iterator it1 = log_cfg_strlists.begin(); it1 != log_cfg_strlists.end(); ++it1)
	{
		delete it1.value();
	}
	for (QMap<QString, QStringList*>::iterator it2 = rpt_cfg_strlists.begin(); it2 != rpt_cfg_strlists.end(); ++it2)
	{
		delete it2.value();
	}
	rpt_cfg_strlists.clear();
	log_cfg_strlists.clear();

	return APR_SUCCESS;
}

//台账更新部分///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
std::string LEDGER_UPDATE_DIR = "../etc/ledgerupdate/";

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格式化为字符串

    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;  // 返回所有找到的文件名
}

// 提取标签中的值的通用函数
std::string extract_value(const std::string& data, const std::string& tag) {
    size_t start_pos = data.find("<" + tag + ">");
    if (start_pos == std::string::npos) return "";
    size_t end_pos = data.find("</" + tag + ">", start_pos);
    if (end_pos == std::string::npos) return "";

    return data.substr(start_pos + tag.length() + 2, end_pos - start_pos - tag.length() - 2);
}

// 根据 str_tag 将 terminal 添加到对应的数组
void add_terminal_to_trigger_update(trigger_update_xml_t* trigger_update_xml, const std::string& str_tag, const terminal& work_terminal) {
    if (str_tag == "add") {
		std::cout << "new ledger!!!!"<<std::endl;
        if (trigger_update_xml->new_update_num < MAX_UPDATEA_NUM) {
            trigger_update_xml->new_updates[trigger_update_xml->new_update_num] = work_terminal;
            trigger_update_xml->new_update_num+= 1;  // 更新新终端的数量
        } else {
            std::cerr << "Exceeded MAX_UPDATEA_NUM limit for new updates!" << std::endl;
        }
    } 
    else if (str_tag == "modify") {
		std::cout << "modify ledger!!!"<<std::endl;
        if (trigger_update_xml->modify_update_num < MAX_UPDATEA_NUM) {
            trigger_update_xml->modify_updates[trigger_update_xml->modify_update_num] = work_terminal;
            trigger_update_xml->modify_update_num+= 1;  // 更新修改终端的数量
        } else {
            std::cerr << "Exceeded MAX_UPDATEA_NUM limit for modify updates!" << std::endl;
        }
    }
    else {
        std::cerr << "Unknown tag: " << str_tag << std::endl;
    }
}

static bool terminal_update_should_bind_to_current_process(const terminal* dev);

// 解析 XML 数据并提取 terminal 信息
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 work_terminal = {};  // 创建新的 terminal 对象

    // 提取各个字段
    strcpy(work_terminal.terminal_id, extract_value(data, "id").c_str());
    strcpy(work_terminal.terminal_code, extract_value(data, "terminalCode").c_str());
    strcpy(work_terminal.org_name, extract_value(data, "orgName").c_str());
    strcpy(work_terminal.maint_name, extract_value(data, "maintName").c_str());
    strcpy(work_terminal.station_name, extract_value(data, "stationName").c_str());
    strcpy(work_terminal.tmnl_factory, extract_value(data, "manufacturer").c_str());
    strcpy(work_terminal.tmnl_status, extract_value(data, "status").c_str());
    strcpy(work_terminal.dev_type, extract_value(data, "devType").c_str());
    strcpy(work_terminal.dev_key, extract_value(data, "devKey").c_str());
    strcpy(work_terminal.dev_series, extract_value(data, "series").c_str());
	std::string com_type = extract_value(data, "comType");
	if (com_type.empty())
		com_type = extract_value(data, "com_type");
	if (com_type.empty())
		com_type = "MMS";
	strncpy(work_terminal.com_type, com_type.c_str(), sizeof(work_terminal.com_type) - 1);
	work_terminal.com_type[sizeof(work_terminal.com_type) - 1] = '\0';
	strcpy(work_terminal.processNo, extract_value(data, "processNo").c_str());
    strcpy(work_terminal.addr_str, extract_value(data, "ip").c_str());
    strcpy(work_terminal.port, extract_value(data, "port").c_str());
    strcpy(work_terminal.timestamp, extract_value(data, "updateTime").c_str());

	//添加日志等级
	std::string level = extract_value(data, "loglevel");
	int tmp_level = -1;
	if (!level.empty()) {
	    try {
	        tmp_level = std::stoi(level);
	    } catch (...) {
	        tmp_level = -1;
	    }
	}
	if (tmp_level >= 0 && tmp_level <= 3) {
	    work_terminal.log_level = tmp_level;
	} else {
	    work_terminal.log_level = 1;  // 默认 warn
	}

	//添加guid20250506
	strncpy(work_terminal.guid, guid_value.c_str(), sizeof(work_terminal.guid) - 1);
	work_terminal.guid[sizeof(work_terminal.guid) - 1] = '\0';

    size_t monitor_pos = 0;
	size_t monitor_count = 0;

	// 查找每个 monitorData，最多处理 10 个
	while ((monitor_pos = data.find("<monitorData", monitor_pos)) != std::string::npos && monitor_count < 10) {
	    monitor work_monitor = {};  // 创建新的 monitor 对象

	    // 找到当前 monitorData 的结束位置
	    size_t monitor_end_pos = data.find("</monitorData", monitor_pos);
	    std::string monitor_data = data.substr(monitor_pos, monitor_end_pos - monitor_pos);

	    // 提取各个字段并填充到 work_monitor 中
	    std::string monitor_id = extract_value(monitor_data, "id");
	    std::string monitor_name = extract_value(monitor_data, "name");
	    std::string voltage_level = extract_value(monitor_data, "voltageLevel");
	    std::string terminal_connect = extract_value(monitor_data, "ptType");
	    std::string line_no = extract_value(monitor_data, "lineNo");
	    std::string timestamp = extract_value(monitor_data, "timestamp");
	    std::string terminal_code = extract_value(monitor_data, "terminal_code");
	    std::string status = extract_value(monitor_data, "status");

	    // 将提取的数据复制到 work_monitor 中，若没有数据则复制 "N/A"
	    strcpy(work_monitor.monitor_id, monitor_id.empty() ? "N/A" : monitor_id.c_str());
	    strcpy(work_monitor.monitor_name, monitor_name.empty() ? "N/A" : monitor_name.c_str());
	    strcpy(work_monitor.voltage_level, voltage_level.empty() ? "N/A" : voltage_level.c_str());
	    strcpy(work_monitor.terminal_connect, terminal_connect.empty() ? "N/A" : terminal_connect.c_str());
	    strcpy(work_monitor.logical_device_seq, line_no.empty() ? "N/A" : line_no.c_str());
	    strcpy(work_monitor.timestamp, timestamp.empty() ? "N/A" : timestamp.c_str());
	    strcpy(work_monitor.terminal_code, terminal_code.empty() ? "N/A" : terminal_code.c_str());
	    strcpy(work_monitor.status, status.empty() ? "N/A" : status.c_str());
		
	    std::string log_level = extract_value(data, "loglevel");
		int tmp_level = -1;
		// 尝试解析 monitor loglevel
		if (!log_level.empty()) {
		    try {
		        tmp_level = std::stoi(log_level);
		    } catch (...) {
		        tmp_level = -1;
		    }
		}
		// 判断是否合法
		if (tmp_level >= 0 && tmp_level <= 3) {
		    work_monitor.log_level = tmp_level;
		} 
		else if (work_terminal.log_level >= 0 && work_terminal.log_level <= 3) {
		    work_monitor.log_level = work_terminal.log_level;  // 继承 terminal
		} 
		else {
		    work_monitor.log_level = 1;  // 默认 warn
		}

	    // 将提取的 monitor 数据存入 work_terminal.line[monitor_count]
	    work_terminal.line[monitor_count] = work_monitor;

	    monitor_count++;  // 增加 monitor 的计数
	    monitor_pos = monitor_end_pos;  // 移动 monitor_pos 到下一个 <monitorData> 的位置
	}


    // 根据 str_tag 将 terminal 添加到相应的数组
	if (!terminal_update_should_bind_to_current_process(&work_terminal)) {
		std::cout << "skip ledger update xml by comType:" << work_terminal.com_type
				  << " terminal:" << work_terminal.terminal_id
				  << " process:" << subdir << g_front_seg_index << std::endl;
		return;
	}

    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;
    // 根据 strTag 做不同处理
    if (strTag == "add" || strTag == "modify") {

        // 填充终端信息
        parse_terminal_from_data(trigger_update_xml, strTag, data,guid_value);

    }else if (strTag == "delete") {

        terminal delete_terminal;
        memset(&delete_terminal, 0, sizeof(delete_terminal));
        
        if (strTag == "delete") {
			// 填充终端信息
			strcpy(delete_terminal.terminal_id, extract_value(data, "id").c_str());
			std::string com_type = extract_value(data, "comType");
			if (com_type.empty())
				com_type = extract_value(data, "com_type");
			if (com_type.empty())
				com_type = "MMS";
			strncpy(delete_terminal.com_type, com_type.c_str(), sizeof(delete_terminal.com_type) - 1);
			delete_terminal.com_type[sizeof(delete_terminal.com_type) - 1] = '\0';

			//添加guid20250506
			strncpy(delete_terminal.guid, guid_value.c_str(), sizeof(delete_terminal.guid) - 1);
			delete_terminal.guid[sizeof(delete_terminal.guid) - 1] = '\0';

			if (!terminal_update_should_bind_to_current_process(&delete_terminal)) {
				std::cout << "skip delete ledger update xml by comType:" << delete_terminal.com_type
						  << " terminal:" << delete_terminal.terminal_id
						  << " process:" << subdir << g_front_seg_index << std::endl;
				return;
			}

            // 将删除信息写入 delete_updates
			std::cout << "delete ledger!!!!!"<<std::endl;
            if (trigger_update_xml->delete_update_num < MAX_UPDATEA_NUM) {
                trigger_update_xml->delete_updates[trigger_update_xml->delete_update_num] = delete_terminal;
                trigger_update_xml->delete_update_num += 1;  // 增加计数
            }
		}

    } 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.c_str());  // 打开XML文件
    if (!file.is_open()) {
        std::cerr << "Failed to open file: " << xml_fn << std::endl;
        return -1;
    }

    std::string line;
    std::stringstream file_content;
    
    // 读取文件内容
    while (std::getline(file, line)) {
        file_content << line << "\n";
    }
    file.close();

    std::string content = file_content.str();

    // 查找 <ledger_update> 标签
    size_t ledger_pos = content.find("<ledger_update>");
    if (ledger_pos == std::string::npos) {
        std::cerr << "ledger_update tag not found!" << std::endl;
        return -1;
    }

	// === 新增：查找 <guid> 标签 ===
	std::string guid_value;
    size_t guid_start = content.find("<guid>", ledger_pos);
	size_t guid_end = content.find("</guid>", ledger_pos);
	if (guid_start != std::string::npos && guid_end != std::string::npos && guid_end > guid_start) {
	    size_t guid_value_start = guid_start + std::string("<guid>").length();
	    guid_value = content.substr(guid_value_start, guid_end - guid_value_start);

	    std::cout << "Found guid: " << guid_value << std::endl;
	} else {
	    std::cout << "No guid found in xml." << std::endl;
	}


    // 查找 <add>, <delete>, <modify> 标签
    size_t add_pos = content.find("<add>", ledger_pos);
    size_t delete_pos = content.find("<delete>", ledger_pos);
    size_t modify_pos = content.find("<modify>", ledger_pos);

    // 确定哪个标签存在，并获取相应的位置
    size_t target_pos = std::string::npos;
    std::string target_tag;

    if (add_pos != std::string::npos) {
        target_pos = add_pos;
        target_tag = "add";
    } else if (delete_pos != std::string::npos) {
        target_pos = delete_pos;
        target_tag = "delete";
    } else if (modify_pos != std::string::npos) {
        target_pos = modify_pos;
        target_tag = "modify";
    }

	//没找到正确的标签退出处理
    if (target_pos == std::string::npos) {
        std::cerr << "No <add>, <delete>, or <modify> tag found!" << std::endl;
        return -1;
    }

    // 查找目标标签的结束位置
    size_t end_pos = content.find("</" + target_tag + ">", target_pos);
    if (end_pos == std::string::npos) {
        std::cerr << "Closing </" + target_tag + "> tag not found!" << std::endl;
        return -1;
    }

    // 提取目标标签的内容
    std::string target_content = content.substr(target_pos + target_tag.length() + 2, end_pos - (target_pos + target_tag.length() + 2));

    // 解析 <data> 和其中的内容
    size_t data_pos = 0;
    while ((data_pos = target_content.find("<terminalData>", data_pos)) != std::string::npos) {
        size_t data_end_pos = target_content.find("</terminalData>", data_pos);
        if (data_end_pos == std::string::npos) {
            std::cerr << "Closing </terminalData> tag not found!" << std::endl;
            return -1;
        }

        std::string data_content = target_content.substr(data_pos + 14, data_end_pos - (data_pos + 14));
        
		std::cout << "ledger data_content is " << data_content <<std::endl;

        // 调用parse_ledger_update处理台账的内容
        parse_ledger_update(trigger_update_xml, target_tag, data_content,guid_value);

        // 移动到下一个<terminalData>标签，目前每个文件只有一个台账
        data_pos = data_end_pos + 15;
    }

	std::cout << "load one xml finish"<<std::endl;

    return 0;  // 成功返回
}

int parse_ledger_update_xml(trigger_update_xml_t* trigger_update_xml)
{
	//printf("begin ledger update xml...\n");

	std::list<std::string> result = find_xml_belong_to_this_process();

    if (!result.empty()) {
        std::cout << "Found XML files:" << std::endl;
        for (std::list<std::string>::iterator it = result.begin(); it != result.end(); ++it) {
		    const std::string& filename = *it;
		    std::cout << filename << std::endl;
		    //解析每个文件，提取终端，判断终端是否满足更新条件
		    apr_sleep(apr_time_from_sec(1) / 10);
		    //加载一个文件的内容到数据结构
		    if (!load_ledger_update_from_xml(trigger_update_xml, filename)) {
		        std::cout << "read /etc/ledgerupdate/" << filename << " success..." << std::endl;
				DIY_WARNLOG_CODE("process",0,LOG_CODE_LEDGER_UPDATE,"【WARN】前置的%s%d号进程 读取台账更新文件成功，开始更新台账", get_front_msg_from_subdir(), g_front_seg_index);
		    }
		    //处理过的文件删除掉
		    
		    if (std::remove(filename.c_str()) != 0) {
		        std::cerr << "Failed to remove file: " << filename << " Error: " << strerror(errno) << std::endl;
				DIY_ERRORLOG_CODE("process",0,LOG_CODE_LEDGER_UPDATE,"【ERROR】前置的%s%d号进程 删除已读取的台账更新文件失败！请检查台账更新文件是否残留在/FeProject/etc/ledgerupdate", get_front_msg_from_subdir(), g_front_seg_index);
		        return APR_EGENERAL;
		    }
			else{
				std::cout << "remove file: " << filename << " success..." << std::endl;
				DIY_INFOLOG_CODE("process",0,LOG_CODE_LEDGER_UPDATE,"【NORMAL】前置的%s%d号进程 删除已读取的台账更新文件成功", get_front_msg_from_subdir(), g_front_seg_index);
			}
		}
    } 
	else {
        //std::cout << "No matching XML files found." << std::endl;//减少不必要的打印
		return APR_EGENERAL;
    }

	//有数据返回成功，后续进行处理
	//printf("Modify Update Count: %d\n", trigger_update_xml->modify_update_num);
	//printf("Delete Update Count: %d\n", trigger_update_xml->delete_update_num);
	//printf("New Update Count: %d\n", trigger_update_xml->new_update_num);
	if(trigger_update_xml->modify_update_num || trigger_update_xml->delete_update_num || trigger_update_xml->new_update_num){
		printf("ledger update xml have data...\n");
		return APR_SUCCESS;
	}
	else{//无数据返回失败，后续不处理
		printf("ledger update xml no data...\n");
		return APR_EGENERAL;
	}
		
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//实时触发部分/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
QString THREE_SECS_WEBSERVICE_DIR = QString("../etc/trigger3s/");

QString get_3s_trig_fn()
{
	QDir directory(THREE_SECS_WEBSERVICE_DIR);
	QStringList filters = QStringList() << "*.xml";
	QStringList fileNames = directory.entryList(filters, QDir::Files, QDir::Time/*|QDir::Reversed*/);
	if (fileNames.size() > 0)
		return fileNames.at(0);
	else
		return "";
}
///////////////////////////////////////////////////////////////
int getValueFromElemAttrStr(QString str)
{
	if (str == "true")
		return 1;
	else if (str == "false")
		return 0;
	else
		return -1;
}

//lnk20241125添加打印调试用/////////////////////////////////////////////////////////////////////////////////////////////////
// 打印 trigger_t 结构体的函数
void print_trigger(const trigger_t& trigger) {
    printf("  dev_idx: %d, line_id: %d, real_data: %d, soe_data: %d, limit: %d, count: %d\n",
           trigger.dev_idx, trigger.line_id, trigger.real_data, trigger.soe_data, trigger.limit, trigger.count);
}
// 打印 trigger_3s_xml_t 结构体的函数
void print_trigger_3s_xml(const trigger_3s_xml_t& trigger_3s_xml) {
    printf("Work Trigger Count: %d\n", trigger_3s_xml.work_trigger_num);
    for (int i = 0; i < trigger_3s_xml.work_trigger_num; ++i) {
        printf("  Work Trigger [%d]:\n", i + 1);
        print_trigger(trigger_3s_xml.work_triggers[i]);
    }

    printf("New Trigger Count: %d\n", trigger_3s_xml.new_trigger_num);
    for (int i = 0; i < trigger_3s_xml.new_trigger_num; ++i) {
        printf("  New Trigger [%d]:\n", i + 1);
        print_trigger(trigger_3s_xml.new_triggers[i]);
    }

    printf("Delete Trigger Count: %d\n", trigger_3s_xml.delete_trigger_num);
    for (int i = 0; i < trigger_3s_xml.delete_trigger_num; ++i) {
        printf("  Delete Trigger [%d]:\n", i + 1);
        print_trigger(trigger_3s_xml.delete_triggers[i]);
    }

    printf("Modify Trigger Count: %d\n", trigger_3s_xml.modify_trigger_num);
    for (int i = 0; i < trigger_3s_xml.modify_trigger_num; ++i) {
        printf("  Modify Trigger [%d]:\n", i + 1);
        print_trigger(trigger_3s_xml.modify_triggers[i]);
    }
}

//实时触发文件处理部分//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void parse_3s_trigger(trigger_3s_xml_t* trigger_3s_xml, QString parentTag, QDomElement& trigger_e)
{
	QString e_atr("");
	trigger_t trigger;

	trigger.dev_idx = trigger_e.attribute("DevSeries").toInt();
	trigger.line_id = trigger_e.attribute("Line").toInt();

	e_atr = trigger_e.attribute("RealData").toLower();
	trigger.real_data = getValueFromElemAttrStr(e_atr);

	e_atr = trigger_e.attribute("SOEData").toLower();
	trigger.soe_data = getValueFromElemAttrStr(e_atr);

	trigger.limit = trigger_e.attribute("Limit").toInt();
	trigger.count = trigger_e.attribute("Count").toInt();
	//qDebug()<<parentTag << "-> " << dev_idx<<" "<<line_id<<" "<<real_data<<" "<<soe_data <<" " <<  limit<<" " << count;

	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;
	}
	//调试用lnk20241125
	print_trigger_3s_xml(*trigger_3s_xml);

}

int load_3s_data_from_xml(trigger_3s_xml_t* trigger_3s_xml, QString xml_fn)
{
	QDomDocument doc;   //新建QDomDocument类对象，它代表一个XML文档
	QFile file(xml_fn);
	if (!file.open(QIODevice::ReadOnly))
		return APR_EBADPATH;  //以只读方式打开
	bool ret = doc.setContent(&file);
	file.close();
	if (!ret)
		return APR_EBADF;

	//将文件内容读到doc中
	QDomElement docElem = doc.documentElement();  //返回根元素  
	QDomNode n = docElem.firstChild();   //返回根节点的第一个子节点 
	while (!n.isNull()) {   //如果节点不为空  
		if (n.isElement()) {   //如果节点是元素  	
			QDomElement e = n.toElement();  //将其转换为元素  
			QString strTag = e.tagName();
			if (strTag == "Work" || strTag == "New" || strTag == "Delete" || strTag == "Modify") {
				QDomNodeList list = e.childNodes(); //获得元素e的所有子节点的列表  
				for (int i = 0; i < list.count(); i++) {  //遍历该列表  				
					QDomNode node = list.at(i);
					if (node.isElement()) {
						QDomElement trigger_e = node.toElement();
						QString strTag2 = trigger_e.tagName();
						if (strTag2 == "Trigger") {
							parse_3s_trigger(trigger_3s_xml, strTag, trigger_e);
						} //else if ( strTag == "Trigger" )
					}
				}
			}
		}
		n = n.nextSibling();  //下一个兄弟节点
	}

	return APR_SUCCESS;
}

QString BAK_WEBSERVICE_3S_TRIG_COMMAND_XML_FN = THREE_SECS_WEBSERVICE_DIR + "bak_3s_trig_command.txt";
int parse_3s_xml(trigger_3s_xml_t* trigger_3s_xml)
{
	//调试用
	//printf("begin 3s xml...\n");
	memset(trigger_3s_xml, 0, sizeof(trigger_3s_xml_t));

	//这个文件是用来记录正在进行中的实时触发
	QString cfg_dir = QString("../")/*+QString::fromAscii(subdir)*/ + QString("etc/");
	//load_3s_data_from_xml(trigger_3s_xml, (cfg_dir + THREE_SECS_CONFIG_FN));	//加载/Feproject/etc/Trigger3S.xml

	QString the_webservice_xml_fn = get_3s_trig_fn();//  ../etc/trigger3s/目录下的最新的xml文件，这个文件是用来打开实时触发的开关

	//调试用
	//printf("the_webservice_xml_fn.size():%d\n",the_webservice_xml_fn.size());

	if (the_webservice_xml_fn.size() > 4) {//文件名大于4说明找到文件
		apr_sleep(apr_time_from_sec(1) / 10);
		the_webservice_xml_fn = THREE_SECS_WEBSERVICE_DIR + the_webservice_xml_fn;
		load_3s_data_from_xml(trigger_3s_xml, the_webservice_xml_fn);
		QFile::remove(BAK_WEBSERVICE_3S_TRIG_COMMAND_XML_FN);
		QFile::rename(the_webservice_xml_fn, BAK_WEBSERVICE_3S_TRIG_COMMAND_XML_FN);

		printf("/etc/trigger3s/*.xml success...\n");

		DIY_INFOLOG_CODE("process",0,LOG_CODE_RT_DATA,"【WARN】前置读取实时数据触发文件成功,即将注册实时数据报告");

		return APR_SUCCESS;
	}

	//调试用
	//printf("3s xml fail...\n");
	return APR_EGENERAL;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//3s触发记录写入文件
void append_triggers(QDomDocument& doc, QDomElement& root, QString parentTag, trigger_t* trigger, int trigger_num)
{
	QString str;
	QDomElement funcItem = doc.createElement(parentTag);
	root.appendChild(funcItem);
	for (int i = 0; i < trigger_num; i++) {
		if (trigger[i].dev_idx == INVALID_DEV_IDX)
			continue;
		if (trigger[i].real_data == 0 && trigger[i].soe_data == 0)
			continue;
		QDomElement triggerItem = doc.createElement("Trigger");
		triggerItem.setAttribute("DevSeries", trigger[i].dev_idx);
		triggerItem.setAttribute("Line", trigger[i].line_id);
		str = trigger[i].real_data ? "true" : "false";
		triggerItem.setAttribute("RealData", str);
		str = trigger[i].soe_data ? "true" : "false";
		triggerItem.setAttribute("SOEData", str);
		triggerItem.setAttribute("Limit", trigger[i].limit);
		triggerItem.setAttribute("Count", trigger[i].count);
		funcItem.appendChild(triggerItem);
	}
}

int create_3s_xml(trigger_3s_xml_t* trigger_3s_xml)
{

	QDomDocument doc;
	doc.appendChild(doc.createProcessingInstruction("xml", "version=\"1.0\" encoding=\"gb2312\""));

	QDomElement root = doc.createElement("Trigger3S");
	doc.appendChild(root);

	append_triggers(doc, root, "Work", trigger_3s_xml->work_triggers, trigger_3s_xml->work_trigger_num);
	append_triggers(doc, root, "New", trigger_3s_xml->new_triggers, 0);
	append_triggers(doc, root, "Delete", trigger_3s_xml->delete_triggers, 0);
	append_triggers(doc, root, "Modify", trigger_3s_xml->modify_triggers, 0);

	QString cfg_dir = QString("../")/*+QString::fromAscii(subdir)*/ + QString("etc/");
	QFile file(cfg_dir + THREE_SECS_CONFIG_FN /*+".bak.xml"*/);
	if (!file.open(QIODevice::ReadWrite | QIODevice::Truncate | QIODevice::Text)) {
		return -1;
	}
	QTextStream out(&file);
	out.setCodec("gb2312");
	doc.save(out, 4);
	file.close();

	return APR_SUCCESS;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 函数功能：从指定的 .cfg 配置文件中提取起始时间和触发时间，并将其转化为毫秒级时间戳。
// 输入参数：
//    - comtrade_fn：输入的文件名，作为参考用于定位对应的 .cfg 配置文件。
// 输出参数：
//    - start_tm：返回的起始时间戳（单位：毫秒）。
//    - trig_tm：返回的触发时间戳（单位：毫秒）。
// 返回值：
//    - 返回 APR_SUCCESS 表示成功，返回 APR_EBADF 表示文件错误。
int extract_timestamp_from_cfg_file(char* comtrade_fn, long long* start_tm, long long* trig_tm)
{
    // 获取文件名信息，路径和文件名提取
    QFileInfo fi(QString::fromAscii(comtrade_fn));
    QString fn = fi.fileName();
    QString cfgFileName_temp = QString("../comtrade/") + fn;  // 拼接.cfg文件的路径

    // 检查文件是否以 ".cfg" 或 ".CFG" 结尾
    if (!cfgFileName_temp.endsWith(".cfg", Qt::CaseInsensitive) && !cfgFileName_temp.endsWith(".CFG", Qt::CaseInsensitive))
        return APR_EBADF;  // 如果文件名不符合要求，返回文件错误

    // 打开配置文件
    QFile cfgFile_temp(cfgFileName_temp);
    if (!cfgFile_temp.exists()) {  // 如果文件不存在
        qDebug() << QString("Cannot find corresponding .cfg file: %1").arg(cfgFileName_temp);
        cfgFile_temp.close();
        return APR_EBADF;
    }
    else if (!cfgFile_temp.open(QFile::ReadOnly | QFile::Text)) {  // 如果文件无法打开
        qDebug() << QString("Cannot open file %1:\n%2.").arg(cfgFileName_temp).arg(cfgFile_temp.errorString());
        return APR_EBADF;
    }
    else {
        QStringList datContentList_temp;
        QTextStream in_temp(&cfgFile_temp);  // 创建文本流读取文件
        // 忽略 GBK 编码相关的代码（被注释掉了）

        QString start_time_str("");  // 用于存储起始时间字符串
        QString trigger_time_str("");  // 用于存储触发时间字符串

        // 按行读取配置文件内容
        while (!in_temp.atEnd()) {
            QString line_temp = in_temp.readLine().trimmed();  // 读取并去除空白字符
            QString upper_line_temp = line_temp.toUpper();  // 转换为大写字符

            // 如果读取到 "ASCII" 或 "BINARY" 字符串，则停止处理
            if ((upper_line_temp == QString("ASCII")) || (upper_line_temp == QString("BINARY")))
                break;
            else {
                // 如果没有，更新起始时间和触发时间
                start_time_str = trigger_time_str;
                trigger_time_str = line_temp;
            }
        }

        // 如果提取到的 start_time_str 长度大于3，则去掉最后3个字符（通常是毫秒部分）
        if (start_time_str.size() > 3)
            start_time_str = start_time_str.left(start_time_str.size() - 3);

        // 将 start_time_str 转换为 QDateTime 对象，并转换为自纪元以来的毫秒数
        QDateTime start_time_dt = QDateTime::fromString(start_time_str, "dd/MM/yyyy,hh:mm:ss.zzz");
        *start_tm = start_time_dt.toMSecsSinceEpoch();  // 转换为毫秒时间戳并赋值给 start_tm

        // 同样处理触发时间（去掉最后3个字符并转换为时间戳）
        if (trigger_time_str.size() > 3)
            trigger_time_str = trigger_time_str.left(trigger_time_str.size() - 3);

        QDateTime trigger_time_dt = QDateTime::fromString(trigger_time_str, "dd/MM/yyyy,hh:mm:ss.zzz");
        *trig_tm = trigger_time_dt.toMSecsSinceEpoch();  // 转换为毫秒时间戳并赋值给 trig_tm

        cfgFile_temp.close();  // 关闭文件
    }
    
    return APR_SUCCESS;  // 成功返回
}

//录波部分///////////////////////////////////////////////////////////////////////////////
//WW 2023-11-01 增加录波段号采用int类型匹配
int parse_file_names_by_fltnum(int fltnum, char* domname, char** filenames, int filenum, int* cfg_idx, int* dat_idx, char* file_base_name, char* file_yyyymm)
{
	int j;
	printf(">>>>>>>>>>>>>>>>>>pares_file_names file list match fltnum=%d", fltnum);

	*cfg_idx = -1;
	*dat_idx = -1;
	for (j = 0; j < filenum; ++j) {

		char fileNameTemp[64];
		strcpy(fileNameTemp, filenames[j]); //临时存放 装置录波文件名
		printf(" %s ", fileNameTemp);
		if (strstr(fileNameTemp, domname) == NULL) //例：域名:"PQMonitor_PQM1" 不是 装置录波文件名:"PQMonitor_PQM1_000001_20191121_154534_689.CFG/.DAT/.HDR"的子串
			continue;
		char* p = strtok(fileNameTemp, "_"); //PQMonitor
		p = strtok(NULL, "_"); //PQM1
		p = strtok(NULL, "_"); //000001
		int nFltNum = atoi(p); //将字符转换成整型
		printf(">>>>>>>>>>>>>>>>>>get nFltNum from file list is %d", nFltNum);

		if (nFltNum == fltnum) {

			QString fn_str = QString::fromAscii(filenames[j]);
			if (fn_str.endsWith(".cfg", Qt::CaseInsensitive) || fn_str.endsWith(".CFG", Qt::CaseInsensitive)) {
				//PQMonitor_PQM4_000349_20180531_113701_618.CFG
				*cfg_idx = j;

				QFileInfo fi(fn_str);
				QString fn = fi.baseName();
				strcpy(file_base_name, fn.toAscii().data());

				QString remain_str = fn_str.split(QString::fromAscii(p)).at(1);
				QString yyyymm_str = remain_str.mid(1, 6);
				strcpy(file_yyyymm, yyyymm_str.toAscii().data());
			}
			else if (fn_str.endsWith(".dat", Qt::CaseInsensitive) || fn_str.endsWith(".DAT", Qt::CaseInsensitive))
				*dat_idx = j;

		}
		if (*cfg_idx != -1 && *dat_idx != -1)//两个文件都已经找到
			break;
	}

	printf("\n<<<<<<<<<<<<<<<<<<<<<pares_file_names file list match end cfg_inx=%d dat_idx=%d  \n", *cfg_idx, *dat_idx);
	if ((*cfg_idx) >= 0 && (*dat_idx) >= 0)
		return APR_SUCCESS;
	else
		return APR_EBADF;
}
//WW2023-11-01 end

//补招部分////////////////////////////////////////////////////////////////////////////////

////////////////////////////20250801
/* 最小块，每次至少扩容这么多；也可以调大一点 */
#define RECALL_CHUNK 64

/* 初始化 */
void recall_xml_init(recall_xml_t *rx)         // ★
{
    rx->work_cnt = rx->work_cap = 0;
    rx->new_cnt  = rx->new_cap  = 0;
    rx->work_recalls = rx->new_recalls = nullptr;
}

/* 若需要，扩容 `*arr` 直到能放下 needed 条记录 */
static int ensure_cap(recall_t **arr, int *cap, int need)
{
    if (need <= *cap) return 0;

    int new_cap = (*cap) ? (*cap) : 16;
    while (new_cap < need) new_cap <<= 1;

    /* C++ 里要显式转换 */
    void *mem = realloc(*arr, new_cap * sizeof(recall_t));
    if (!mem) return -1;

    *arr = static_cast<recall_t *>(mem);       // ★ 显式 static_cast
    *cap = new_cap;
    return 0;
}

/* push 到 work / new 数组 */
static int push_work(recall_xml_t *rx, const recall_t *rec)
{
    if (ensure_cap(&rx->work_recalls, &rx->work_cap, rx->work_cnt + 1))
        return -1;
    rx->work_recalls[rx->work_cnt++] = *rec;
    return 0;
}
static int push_new(recall_xml_t *rx, const recall_t *rec)
{
    if (ensure_cap(&rx->new_recalls, &rx->new_cap, rx->new_cnt + 1))
        return -1;
    rx->new_recalls[rx->new_cnt++] = *rec;
    return 0;
}

/* 释放内存（栈退出前或不用时调用） */
void recall_xml_free(recall_xml_t *rx)         // ★
{
    for (int i = 0; i < rx->work_cnt; ++i)
        free(rx->work_recalls[i].line_id);
    for (int i = 0; i < rx->new_cnt;  ++i)
        free(rx->new_recalls[i].line_id);

    free(rx->work_recalls);
    free(rx->new_recalls);

    recall_xml_init(rx);   /* 复位为全 0，防止悬挂指针 */
}
////////////////////////////20250801

void parse_recall(recall_xml_t* recall_xml, QString parentTag, QDomElement& recall_e, char* id)
{
	//recall_t recall;//lnk20250801

	//recall.line_id = id;

	recall_t recall = {0};

    recall.line_id = strdup(id);                     /* malloc 字符串 */

	QDateTime start_dt = QDateTime::fromString(recall_e.attribute("StartTime"), "yyyy-MM-dd hh:mm:ss");
	recall.start_time = start_dt.toMSecsSinceEpoch() / 1000;
	QDateTime end_dt = QDateTime::fromString(recall_e.attribute("EndTime"), "yyyy-MM-dd hh:mm:ss");
	recall.end_time = end_dt.toMSecsSinceEpoch() / 1000;

	recall.need_steady = recall_e.attribute("STEADY").toInt();
	recall.need_voltage = recall_e.attribute("VOLTAGE").toInt();

	qDebug() << parentTag << "-> " << " " << recall.line_id << " " << recall.need_steady << " " << recall.need_voltage
		<< " " << recall.start_time << " " << recall.end_time;

	if (parentTag == "Work") {
		//recall_xml->work_recalls[recall_xml->work_recall_num++] = recall;
		push_work(recall_xml, &recall);
	}
	else if (parentTag == "New") {
		//recall_xml->new_recalls[recall_xml->new_recall_num++] = recall;
		push_new(recall_xml,  &recall);
	}
}

int delete_recall_xml(char* id)
{
	QString cfg_dir = QString("../")/*+QString::fromAscii(subdir)*/ + QString("etc/recall");
	QString file_name = QString(subdir) + QString("_") + QString(QString::number(g_front_seg_index, 10)) + QString("_") + QString(id) + QString("_") + QString("*") + QString("_Recall.xml");

	//指定文件夹名
	QDir dir(cfg_dir);
	if (!dir.exists()) {
		qDebug() << "folder does not exist!";
		return false;
	}
	//指定文件后缀名，可指定多种类型
	QStringList filter(file_name);
	//指定查找类型和排序，按最新的修改时间获取
	QStringList files = dir.entryList(filter, QDir::Files | QDir::Readable | QDir::NoDotAndDotDot, QDir::Name | QDir::Time);
	for (int i = 0; i < files.size(); i++) {//清空读取文件
		QString qstrRecallPath = cfg_dir + QString("/") + files[i];
		QFile::remove(qstrRecallPath);
	}
	return APR_SUCCESS;
}

int parse_recall_xml(recall_xml_t* recall_xml, char* id)
{

	QString cfg_dir = QString("../")/*+QString::fromAscii(subdir)*/ + QString("etc/recall");
	QString file_name = QString(subdir) + QString("_") + QString(QString::number(g_front_seg_index, 10)) + QString("_") + QString(id) + QString("_") + QString("*") + QString("_Recall.xml");
	//lnk20241225这里文件名
	//指定文件夹名
	QDir dir(cfg_dir);
	if (!dir.exists()) {
		qDebug() << "folder does not exist!";
		DIY_ERRORLOG_CODE("process",0,LOG_CODE_RECALL,"【ERROR】前置的%s%d号进程 无法解析补招文件，补招文件路径/FeProject/etc/recall/不存在", get_front_msg_from_subdir(), g_front_seg_index);
		return false;
	}
	//指定文件后缀名，可指定多种类型
	QStringList filter(file_name);
	//指定查找类型和排序，按最新的修改时间获取
	QStringList files = dir.entryList(filter, QDir::Files | QDir::Readable | QDir::NoDotAndDotDot, QDir::Name | QDir::Time);
	for (int i = 0; i < files.size(); i++) {
		QString qstrRecallPath = cfg_dir + QString("/") + files[i];
		qDebug() << qstrRecallPath;

		QDomDocument doc;   //新建QDomDocument类对象，它代表一个XML文档
		QFile file(qstrRecallPath);
		if (!file.open(QIODevice::ReadOnly))
		{
			qDebug() << "file.open error";
			DIY_ERRORLOG_CODE("process",0,LOG_CODE_RECALL,"【ERROR】前置的%s%d号进程 无法打开补招文件%s", get_front_msg_from_subdir(), g_front_seg_index,qstrRecallPath.toStdString().c_str());
			continue;  //以只读方式打开
		}
		bool ret = doc.setContent(&file);
		file.close();
		if (!ret)
		{
			qDebug() << "doc.setContent error";
			DIY_ERRORLOG_CODE("process",0,LOG_CODE_RECALL,"【ERROR】前置的%s%d号进程 无法解析补招文件%s,补招内容无效", get_front_msg_from_subdir(), g_front_seg_index,qstrRecallPath.toStdString().c_str());
			continue;
		}
		//将文件内容读到doc中
		QDomElement docElem = doc.documentElement();  //返回根元素  
		QDomNode n = docElem.firstChild();   //返回根节点的第一个子节点 
		while (!n.isNull()) {   //如果节点不为空  
			if (n.isElement()) {   //如果节点是元素  	
				QDomElement e = n.toElement();  //将其转换为元素  
				QString strTag = e.tagName();
				if (strTag == "Work" || strTag == "New") {
					QDomNodeList list = e.childNodes(); //获得元素e的所有子节点的列表  
					for (int i = 0; i < list.count(); i++) {  //遍历该列表  				
						QDomNode node = list.at(i);
						if (node.isElement()) {
							QDomElement  recall_e = node.toElement();
							QString strTag2 = recall_e.tagName();
							if (strTag2 == "Recall") {
								parse_recall(recall_xml, strTag, recall_e, id);
							} //else if ( strTag == "Trigger" )
						}
					}
				}
			}
			n = n.nextSibling();  //下一个兄弟节点
		}
	}

	return APR_SUCCESS;
}

/*void process_recall_config(recall_xml_t* recall_xml)
{
	int			i, j;
	recall_t* recall;
	recall_t* recall_work;
	int			recall_num;
	int			need_write_file;
	QList<RecallInfo> recallinfo_list_hour;               //待补招队列-以小时为最大间隔

	need_write_file = FALSE;
	recall = recall_xml->new_recalls;
	recall_num = recall_xml->new_recall_num;
	for (i = 0; i < recall_num; i++) {
		recall_xml->work_recalls[recall_xml->work_recall_num++] = recall[i];
		need_write_file = TRUE;
	}

	recall = recall_xml->work_recalls;
	recall_num = recall_xml->work_recall_num;

	if (recall_num > 0) {
		LD_info_t* LD_info = find_LD_info_only_from_mp_id(recall[0].line_id);
		printf("\n recall[0].line_id == %s \n", recall[0].line_id);
		printf("\n recall[0].start_time == %d \n", recall[0].start_time);
		if (LD_info == NULL || LD_info->read_flag == 0) {
			printf("\n recall[0].line_id == NULL \n");
			printf("\n Find LD_info == null \n");
		}
		else {
			printf("\n Find LD_info != null \n");
			if (LD_info->autorecallflag != 0 || LD_info->autorecallcount == 0) {	//
				if (LD_info->autorecallcount != 0) {
					for (int j = 0; j < LD_info->autorecallcount; j++) {
						delete[] LD_info->autorecall[j];//避免重复释放lnk20250801
					}
					delete LD_info->autorecall;
					LD_info->autorecallcount = 0;
				}
				LD_info->autorecallcount = recall_num;
				LD_info->autorecall = new autorecall_t * [recall_num];

				for (int k = 0; k < recall_num; ++k) {
    				LD_info->autorecall[k] = nullptr;	//避免野指针lnk20250801
				}

				for (int j = 0; j < recall_num; j++) {
					printf("\n %d ===== %d\n", recall[j].start_time, recall[j].end_time);
					LD_info->autorecall[j] = new autorecall_t[1];
					LD_info->autorecall[j]->start = recall[j].start_time;

					LD_info->autorecall[j]->end = recall[j].end_time;

					//lnk20241030补充补招稳态暂态标志，每个文件都有很多条时间记录
					LD_info->autorecall[j]->need_steady = recall[j].need_steady;
					LD_info->autorecall[j]->need_voltage = recall[j].need_voltage;

				}
				LD_info->autorecallflag = 0;//这个监测点刚读取到补招文件，重置正在补招标志


			}
		}

		//调试
		printf("== autorecall count = %d ==\n", LD_info->autorecallcount);
		for (int j = 0; j < LD_info->autorecallcount; ++j) {
		    if (LD_info->autorecall[j] == nullptr) {
		        printf("autorecall[%d] = nullptr !!!\n", j);
		    } else {
		        printf("autorecall[%d] = %p | start = %d, end = %d\n", 
		            j, LD_info->autorecall[j], 
		            LD_info->autorecall[j]->start, LD_info->autorecall[j]->end);
		    }
		}


	}

	

}*/
void process_recall_config(recall_xml_t *rx)
{
    if (rx->new_cnt) {                                       
        /* 预留空间 */
        if (ensure_cap(&rx->work_recalls, &rx->work_cap,
                        rx->work_cnt + rx->new_cnt))         
            return;  /* 内存不足，直接返回 */

        memcpy(&rx->work_recalls[rx->work_cnt],              
               rx->new_recalls,
               rx->new_cnt * sizeof(recall_t));
        rx->work_cnt += rx->new_cnt;                         
        rx->new_cnt  = 0;                                    
    }

    /* work 列表为空，直接结束 */
    if (rx->work_cnt == 0)                                   
        return;


    recall_t *recall   = rx->work_recalls;                   
    int       recall_num = rx->work_cnt;                     

    LD_info_t *LD_info = find_LD_info_only_from_mp_id(recall[0].line_id);
    printf("\n recall[0].line_id == %s \n", recall[0].line_id);
    printf("\n recall[0].start_time == %lld \n", recall[0].start_time);

    if (!LD_info || LD_info->read_flag == 0) {
        printf("\n Find LD_info == null \n");
        return;
    }

    printf("\n Find LD_info != null \n");

    /* 若正在补招中则跳过；否则装载新补招队列 */
    if (LD_info->autorecallflag != 0 || LD_info->autorecallcount == 0) {

        /* 先释放旧队列 */
        if (LD_info->autorecallcount != 0) {
            for (int j = 0; j < LD_info->autorecallcount; ++j)
                delete[] LD_info->autorecall[j];             /* 原逻辑 */
            delete LD_info->autorecall;
            LD_info->autorecallcount = 0;
        }

        /* 重新分配新队列，大小 = recall_num */
        LD_info->autorecallcount = recall_num;
        LD_info->autorecall      = new autorecall_t*[recall_num];

        for (int k = 0; k < recall_num; ++k)
            LD_info->autorecall[k] = nullptr;

        for (int j = 0; j < recall_num; ++j) {
            LD_info->autorecall[j]        = new autorecall_t[1];
            LD_info->autorecall[j]->start = recall[j].start_time;
            LD_info->autorecall[j]->end   = recall[j].end_time;
            LD_info->autorecall[j]->need_steady  = recall[j].need_steady;
            LD_info->autorecall[j]->need_voltage = recall[j].need_voltage;

            printf("\n %lld ===== %lld\n",
                   recall[j].start_time, recall[j].end_time);
        }
        LD_info->autorecallflag = 0;      /* 重置“正在补招”标志 */
    }

    /* 调试输出 */
	if(DEBUGOPEN){
    	printf("== autorecall count = %d ==\n", LD_info->autorecallcount);
    	for (int j = 0; j < LD_info->autorecallcount; ++j) {
    	    if (!LD_info->autorecall[j])
    	        printf("autorecall[%d] = nullptr !!!\n", j);
    	    else
    	        printf("autorecall[%d] = %p | start = %lld, end = %lld\n",
    	               j, LD_info->autorecall[j],
    	               LD_info->autorecall[j]->start,
    	               LD_info->autorecall[j]->end);
    	}
	}
}
//////////////////////////////////////////////////////////////////////////////////

void WebSocketThread::run()
{
	//if (g_node_id != THREE_SECS_DATA_BASE_NODE_ID)
	//	return;

	printf("WebSocketThread::run() is called ...... \n");
	struct sockaddr_in client_sockaddr;
	memset(&client_sockaddr, 0, sizeof(client_sockaddr));
	socklen_t address_len = 0;
	while (1) {
		address_len = sizeof(struct sockaddr_in);
		int client_socket = accept(server_socket, (struct sockaddr*)&client_sockaddr, &address_len);
		if (client_socket == -1)
		{
			printf("accept client %s:%d failed,error msg=%s\n\a", inet_ntoa(client_sockaddr.sin_addr), htons(client_sockaddr.sin_port), strerror(errno));
			continue;
		}
		printf("\naccept client %s:%d succes，address_len= %d，client_socket= %d\n", inet_ntoa(client_sockaddr.sin_addr), htons(client_sockaddr.sin_port), address_len, client_socket);

		char buffer[256];
		int iRet = 0;
		while (1) {
			memset(buffer, 0, sizeof(buffer));
			int recvLength = recv(client_socket, buffer, sizeof(buffer), 0);
			if (recvLength > 0)
			{
				iRet = HandleReceiveMessage(client_socket, buffer);

				//if (0 != iRet)
				//	iRet = SendMessageToWeb(client_socket, iRet);
				break;
			}
			else
			{
				if (errno == EINTR)
					continue;
				printf("end socket client %d(%s:%d) msg,disconnetc Socket client\n", client_socket, inet_ntoa(client_sockaddr.sin_addr), htons(client_sockaddr.sin_port));
				break;
			}
		}

		close(client_socket);
		printf("close socket client %d(%s:%d) \n", client_socket, inet_ntoa(client_sockaddr.sin_addr), htons(client_sockaddr.sin_port));
		msleep(1);
	}

	close(server_socket);
	return;
}

string MatchErrorMessage(int errorCode) //根据错误码匹配错误消息
{
	string strErrorMessage = "";
	switch (errorCode)
	{
	case 0:
		strErrorMessage = "success"; //"成功"
		break;
	default:
		strErrorMessage = "error"; //"错误";
		break;
		//case 10002:
		//	strErrorMessage = "Incorrect message length structure"; //"报文长度结构错误";
		//	break;
		//case 10003:
		//	strErrorMessage = "JSON string parsing error"; //"JSON字符串解析出错";
		//	break;
		//case 10004:
		//	strErrorMessage = "Socket handling error"; //"socket处理出现异常";
		//	break;
		//case 10005:
		//	strErrorMessage = "The device corresponding to ID was not found"; //"ID对应的装置未找到";
		//	break;
		//case 10006:
		//	strErrorMessage = "The monitoring point corresponding to ID has not been found"; //"ID对应的监测点未找到";
		//	break;
		//case 10007:
		//	strErrorMessage = "Wrong parameter passed in"; //"传入的参数异常";
		//	break;
		//case 10008:
		//	strErrorMessage = "The same type of operation is being performed"; //"有同一类型的操作正在执行";
		//	break;
		//case 10009:
		//	strErrorMessage = "The device turned off the corresponding function"; //"装置关闭了对应功能";
		//	break;
		//case 10010:
		//	strErrorMessage = "The TYPE parameter passed in is exceptional"; //"传入的TYPE参数异常";
		//	break;
		//default:
		//	strErrorMessage = "An unknown error"; //"未知错误";
		//	break;
	}

	return strErrorMessage;
}

int SendMessageToWeb(int socketClient, int iErrorCode) //向Web Socket客户端发送消息
{
	string strSendJson = "";
	char cTemp[8];
	string strErrorCode = "";
	sprintf(cTemp, "%06d", iErrorCode);
	strErrorCode = cTemp;
	char sendBuffer[256];
	int sendLength = 0;

	string strErrorMessage = MatchErrorMessage(iErrorCode);
	//if (2 == Log_Enable)
	//	printf("错误码：%d，匹配到的消息：%s\n", iErrorCode, strErrorMessage.c_str());

	strSendJson = "{\"errors\":\"" + strErrorMessage + "\",\"status\":\"" + strErrorCode + "\"}";
	strcpy(sendBuffer, strSendJson.c_str());
	sendLength = send(socketClient, strSendJson.c_str(), strSendJson.length() + 1, 0);
	if (-1 == sendLength)
	{
		printf("server to client[%d] send[%d] message error,error message:%s\n", socketClient, iErrorCode, strSendJson.c_str());
		return -1;
	}

	//if (1 == Log_Enable)
	//	printf("服务端向客户端%d发送[%d]消息：%s\n", socketClient, iErrorCode, sendBuffer);

	return sendLength;
}

int ExecuteWebCommand(LD_info_t* LD_info, int iType) //执行Web Socket命令消息
{
	try
	{
		//if (1 == Log_Enable)
		//	printf(">>>线路%d，\"%s\"，iType= %d，real_data= %s，soe_data= %s，limit= %d，count= %d\n", LD_info->line_id, LD_info->name,
		//		iType, 0 == LD_info->real_data ? "False" : "True", 0 == LD_info->soe_data ? "False" : "True", 20, LD_info->count);

		if (0 == iType || 1 == iType)
		{
			LD_info->count = 0;
			if (0 == iType)
			{
				LD_info->real_data = 1;
				LD_info->soe_data = 1;
				//if (1 == Log_Enable)
				//	printf(">>>触发%d，\"%s\"，iType= %d，real_data= %s，soe_data= %s，limit= %d，count= %d\n", LD_info->line_id, LD_info->name,
				//		iType, 0 == LD_info->real_data ? "False" : "True", 0 == LD_info->soe_data ? "False" : "True", 20, LD_info->count);
			}
			else
			{
				//LD_info->heart_beat = 1;
				//if (1 == Log_Enable)
				//	printf(">>>持续%d，\"%s\"，iType= %d，real_data= %s，soe_data= %s，limit= %d，count= %d\n", LD_info->line_id, LD_info->name,
				//		iType, 0 == LD_info->real_data ? "False" : "True", 0 == LD_info->soe_data ? "False" : "True", 20, LD_info->count);
			}
		}

		if (2 == iType)
		{
			LD_info->real_data = 0;
			LD_info->soe_data = 0;
			//if (1 == Log_Enable)
			//	printf(">>>停止%d，\"%s\"，iType= %d，real_data= %s，soe_data= %s，limit= %d，count= %d\n", LD_info->line_id, LD_info->name,
			//		iType, 0 == LD_info->real_data ? "False" : "True", 0 == LD_info->soe_data ? "False" : "True", 20, LD_info->count);
		}
	}
	catch (exception& e)
	{
		printf("触发/停止线路%d，\"%s\"失败，原因：%s\n", LD_info->line_id, LD_info->name, e.what());
		return 10004;
	}

	return 10000;
}

void Get_Recall_Time_Char(char* start_time, char* end_time, QList<RecallInfo>& recallinfo_list_hour) //数据完整性补招判断
{

	QDateTime start_dt = QDateTime::fromString(start_time, "yyyy-MM-dd HH:mm:ss");
	QDateTime end_dt = QDateTime::fromString(end_time, "yyyy-MM-dd HH:mm:ss");

	long long starttime = start_dt.toMSecsSinceEpoch() / 1000;  //补招起始时间
	long long endtime = end_dt.toMSecsSinceEpoch() / 1000;                //补招结束时间
	QList<long long> timestamp_list;                      //数据库完整性记录时间点
	QList<RecallInfo> recallinfo_list;                    //待补招队列

	RecallInfo info;
	info.starttime = starttime;
	info.endtime = endtime;
	recallinfo_list.append(info);

	for (int i = 0; i < recallinfo_list.size(); i++)
	{
		//printf("\n %lld ----- %11d\n", recallinfo_list[i].starttime, recallinfo_list[i].endtime);
		long long duration = recallinfo_list[i].endtime - recallinfo_list[i].starttime;
		long long max_interval = 3600;
		for (long long j = 0; j <= duration; j += max_interval)
		{
			if (j + max_interval > duration) {
				long long start = recallinfo_list[i].starttime + j;
				long long end = recallinfo_list[i].endtime;

				RecallInfo info;
				info.starttime = start;
				info.endtime = end;
				recallinfo_list_hour.append(info);
			}
			else {
				long long start = recallinfo_list[i].starttime + j;
				long long end = recallinfo_list[i].starttime + j + max_interval;

				RecallInfo info;
				info.starttime = start;
				info.endtime = end - 1;
				recallinfo_list_hour.append(info);
			}
		}
	}

}

int HandleReceiveMessage(int socketClient, char buffer[256]) //接收并处理Web Socket客户端发来的信息
{
	int iErrorCode = 000000;

	try
	{
		QString qstrBuffer = QString(QLatin1String(buffer));
		printf("来自ffe客户端%d消息：qstrBur= %s\n", socketClient, qstrBuffer.toAscii().data());
		cJSON* json_root = cJSON_Parse(qstrBuffer.toUtf8().constData());   //json格式序列化
		if (json_root == NULL) {
			return 10000;
		}
		QString qstrCode = NULL;//记录指令
		cJSON* json_code = NULL;
		cJSON* json_param = NULL;
		cJSON* json_program_param = NULL;
		cJSON* json_recall_param = NULL;
		cJSON* json_wave_param = NULL;
		cJSON* json_update_time = NULL;
		cJSON* json_node = NULL;

		QString update_time;
		QList<QString> wave_param;
		ProgramParam program_param;

		json_code = cJSON_GetObjectItem(json_root, "code");    //获取code
		if (json_code != NULL) {
			qstrCode = json_code->valuestring;
		}
		else {
			return 10000;
		}
		json_param = cJSON_GetObjectItem(json_root, "param");    //获取param
		if (json_param == NULL) {
			return 10000;
		}
		if (qstrCode == "dev_update" || qstrCode == "model_update" || qstrCode == "program_update") {
			//设备更新记录更新时间
			json_update_time = cJSON_GetObjectItem(json_param, "update_time");    //获取update_time
			if (json_update_time != NULL) {
				update_time = json_update_time->valuestring;
			}
		}
		else if (qstrCode == "manual_wave") {
			//手动录波：功能未完成
			json_wave_param = cJSON_GetObjectItem(json_param, "wave_param");    //获取wave_param
			int array_size = cJSON_GetArraySize(json_wave_param);    //获取数组大小
			int i;

			for (i = 0; i < array_size; i++)
			{
				json_node = cJSON_GetArrayItem(json_wave_param, i);//array
				if (json_node != NULL) {
					wave_param.append(json_node->valuestring);
				}
			}

		}
		else if (qstrCode == "model_update") {
			//程序参数：终端列表和程序文件
			json_program_param = cJSON_GetObjectItem(json_param, "program_param");    //获取program_param
			cJSON* json_temp = NULL;
			json_temp = cJSON_GetObjectItem(json_program_param, "terminal_list");    //获取
			int array_size = cJSON_GetArraySize(json_temp);    //获取数组大小
			int i;

			for (i = 0; i < array_size; i++)
			{
				json_node = cJSON_GetArrayItem(json_temp, i);//array
				if (json_node != NULL) {
					program_param.terminal_list.append(json_node->valuestring);
				}
			}
			json_node = cJSON_GetObjectItem(json_program_param, "file_name");    //获取file_name
			if (json_node != NULL) {
				program_param.file_name = json_node->valuestring;
			}
		}
		else if (qstrCode == "manual_recall") {//手动补招的指令
			//补招参数
			json_recall_param = cJSON_GetObjectItem(json_param, "recall_param");    //获取recall_param
			cJSON* json_temp = NULL;
			int array_size = cJSON_GetArraySize(json_recall_param);    //获取数组大小
			int i;
			QList<RecallInfo> recallinfo_list_hour;
			char start_time[64];
			char end_time[64];
			QString mp_id;
			for (i = 0; i < array_size; i++)
			{
				json_temp = cJSON_GetArrayItem(json_recall_param, i);//array
				if (json_temp != NULL) {
					json_node = cJSON_GetObjectItem(json_temp, "mp_id");    //获取mp_id
					if (json_node != NULL) {
						mp_id = QString::fromUtf8(json_node->valuestring);
					}
					if (i == 0) {
						json_node = cJSON_GetObjectItem(json_temp, "start");    //获取start
						if (json_node != NULL) {
							apr_snprintf(start_time, sizeof(start_time), "%s", json_node->valuestring);//start_time
						}
						json_node = cJSON_GetObjectItem(json_temp, "end");    //获取end
						if (json_node != NULL) {
							apr_snprintf(end_time, sizeof(end_time), "%s", json_node->valuestring);//end_time
						}
						Get_Recall_Time_Char(start_time, end_time, recallinfo_list_hour);
					}
					for (int j = 0; j < recallinfo_list_hour.size(); j++) {
						CJournalRecall jr;
						jr.MonitorID = mp_id;
						jr.StartTime = QDateTime::fromTime_t(recallinfo_list_hour[j].starttime).toString("yyyy-MM-dd hh:mm:ss");
						jr.EndTime = QDateTime::fromTime_t(recallinfo_list_hour[j].endtime).toString("yyyy-MM-dd hh:mm:ss");
						jr.STEADY = QString::number(1, 10);//默认都是1，1的十进制转化
						jr.VOLTAGE = QString::number(1, 10);
						g_StatisticLackList_list_mutex.lock();
						g_StatisticLackList.push_back(jr);
						g_StatisticLackList_list_mutex.unlock();
					}
				}
			}
		}
		cJSON_Delete(json_root);
		SendMessageToWeb(socketClient, 000000);//响应web
	}
	catch (exception& e)
	{
		printf("处理客户端：%d发送的消息错误，原因：%s\n", socketClient, e.what());
		return 10004;
	}
	return 000000;
}

///////////////////////////////////////////////////////////////////////////////////////
void Cout_account_information() {
	ied_t* ied = NULL;
	int			iedno;
	LD_info_t* LD_info = NULL;

	for (iedno = 0; iedno < g_node->n_clients; iedno++) {
		ied = g_node->clients[iedno];
		if (ied) {
			LD_info_t* LD_info = NULL;
			ied_usr_t* ied_usr = GET_IEDEXT_ADDR(ied);
			int			cpuno;
			QString text;//待组装的pgsql语句
			text.append(QString("terminal_code: \"%1\"  ,ip:\"%2\" ,port:\"%3\" ,cpucount:\"%4\" ").arg(ied_usr->terminal_code).arg(ied->channel[0].addr_str).arg(ied->channel[0].port).arg(ied->cpucount));
			add_comm_log(const_cast<char*>(text.toLocal8Bit().constData()));
			for (cpuno = 0; cpuno < ied->cpucount; cpuno++) {
				LD_info = &(ied_usr->LD_info[cpuno]);

				if (LD_info->cpuno == 0 || LD_info->LD_name == NULL)
    				continue;

				QString text2;//待组装的pgsql语句
				text2.append(QString("mp_id: \"%1\" terminal_code:\"%2\" ").arg(LD_info->mp_id).arg(LD_info->terminal_code));
				add_comm_log(const_cast<char*>(text2.toLocal8Bit().constData()));
			}
		}
	}
}

/// <summary>
/// 删除过期的xmllnk:多个进程并发删除导致的失败不会影响进程
/// </summary>
void DeletcRecallXml() {
	QString cfg_dir = QString("../")/*+QString::fromAscii(subdir)*/ + QString("etc/recall");
	QString file_name = QString(subdir) + QString("_") + QString("*") + QString("_Recall.xml");

	//指定文件夹名
	QDir dir(cfg_dir);
	if (!dir.exists()) {
		qDebug() << "folder does not exist!";
		DIY_ERRORLOG_CODE("process",0,LOG_CODE_RECALL,"【ERROR】前置的%s%d号进程 删除旧的补招文件失败,补招文件路径/FeProject/etc/recall/不存在", get_front_msg_from_subdir(), g_front_seg_index);
		return;
	}
	QStringList filter(file_name);
	//指定查找类型和排序，按最新的修改时间获取
	QStringList files = dir.entryList(filter, QDir::Files | QDir::Readable | QDir::NoDotAndDotDot, QDir::Name | QDir::Time);

	// 设定过滤的日期和时间  
	QDateTime saveDaysAgo = QDateTime::currentDateTime().addDays(-2);

	for (int i = 0; i < files.size(); i++) {//清空读取文件
		QFileInfo fileInfo(dir.filePath(files[i]));
		if (fileInfo.lastModified() < saveDaysAgo) {
			QFile::remove(fileInfo.absoluteFilePath());

			DIY_INFOLOG_CODE("process",0,LOG_CODE_RECALL,"【NORMAL】前置的%s%d号进程 删除超过两天的补招文件", get_front_msg_from_subdir(), g_front_seg_index);
		}
	}

}

void CreateRecallXml()
{
	apr_time_t	previousTime = apr_time_now();
	long long stamp = static_cast<long long>(previousTime) / 1000000;
	QDateTime deltime_Qtime = QDateTime::fromTime_t(stamp);

	g_StatisticLackList_list_mutex.lock();
	if (g_StatisticLackList.size() > 0)
	{
		printf("insert ID_CJournalRecall_Map！\n");

		DIY_INFOLOG_CODE("process",0,LOG_CODE_RECALL,"【NORMAL】前置的%s%d号进程 开始写入补招文件", get_front_msg_from_subdir(), g_front_seg_index);

		QMap<QString, QList<CJournalRecall> > ID_CJournalRecall_Map;

		list<CJournalRecall>::iterator sl = g_StatisticLackList.begin();
		while (sl != g_StatisticLackList.end())
		{
			CJournalRecall jr = *sl++;
			if (ID_CJournalRecall_Map.contains(jr.MonitorID))
			{
				ID_CJournalRecall_Map[jr.MonitorID].append(jr);
			}
			else
			{
				QList<CJournalRecall> TempList;
				TempList.append(jr);
				ID_CJournalRecall_Map.insert(jr.MonitorID, TempList);
			}
		}

		for (QMap<QString, QList<CJournalRecall> >::iterator it2 = ID_CJournalRecall_Map.begin(); it2 != ID_CJournalRecall_Map.end(); ++it2)
		{
			QString key2 = it2.key();
			QList<CJournalRecall> value2 = it2.value();

			QString cfg_dir = QString("../")/*+QString::fromAscii(subdir)*/ + QString("etc/recall/");
			QString qstrRecallPath = cfg_dir + QString(subdir) + QString("_") + QString(QString::number(g_front_seg_index, 10)) + QString("_") + key2 + QString("_") + QString(deltime_Qtime.toString("yyyyMMddhhmmss")) + QString("_Recall.xml");

			std::string strRecallPath = qstrRecallPath.toStdString();
			QFile file(strRecallPath.c_str());
			if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
				printf("补招查询完成,打开%s失败,无法写入线路补招配置！\n", qstrRecallPath.toAscii().data());

				DIY_ERRORLOG_CODE("process",0,LOG_CODE_RECALL,"【ERROR】前置的%s%d号进程 无法将补招文件写入补招文件路径/FeProject/etc/recall/", get_front_msg_from_subdir(), g_front_seg_index);

				QMap<QString, QList<CJournalRecall> >().swap(ID_CJournalRecall_Map);
				return;
			}

			QXmlStreamWriter writer(&file);
			writer.setAutoFormatting(true);
			writer.writeStartDocument();
			writer.writeStartElement("RecallList");
			writer.writeStartElement("Work");
			writer.writeEndElement();

			writer.writeStartElement("New");

			while (!value2.isEmpty())
			{
				CJournalRecall jr = value2.takeFirst();
				writer.writeStartElement("Recall");
				writer.writeAttribute("MonitorID", jr.MonitorID);
				writer.writeAttribute("StartTime", jr.StartTime);
				writer.writeAttribute("EndTime", jr.EndTime);
				writer.writeAttribute("STEADY", jr.STEADY);
				writer.writeAttribute("VOLTAGE", jr.VOLTAGE);
				writer.writeEndElement();
			}
			writer.writeEndElement();
			writer.writeEndElement();
			writer.writeEndDocument();
			file.close();
		}

		QMap<QString, QList<CJournalRecall> >().swap(ID_CJournalRecall_Map);
	}
	g_StatisticLackList.clear();
	g_StatisticLackList_list_mutex.unlock();
}
///////zw修改 2023-8-30 end

/*/////////////////////////////////////////////////////////lnk2024-10-11移除sql的测试代码/////////////////////////////////////////////////////////////*/
std::string intToString(int number) {
    if (number == 0) return "0";
    std::string str;
    bool isNegative = number < 0;
    if (isNegative) number = -number;

    while (number > 0) {
        str.insert(str.begin(), '0' + (number % 10));
        number /= 10;
    }

    if (isNegative) str.insert(str.begin(), '-');
    return str;
}

otl_datetime parseTimestamp(const std::string timestampStr) {
    otl_datetime timestamp;
	
	// 定义年、月、日、时、分、秒变量
    int year, month, day, hour, minute, second;

    // 使用字符串流进行解析
    std::istringstream ss(timestampStr);
    char discard; // 用于丢弃分隔符

    // 解析字符串
    ss >> year >> discard >> month >> discard >> day >> hour >> discard >> minute >> discard >> second;

    // 将解析的值赋给 otl_datetime
    timestamp.year = year;
    timestamp.month = month;
    timestamp.day = day;
    timestamp.hour = hour;
    timestamp.minute = minute;
    timestamp.second = second;

	return timestamp;
}

size_t req_reply_web(void* ptr, size_t size, size_t nmemb, void* stream)
{
	string* str = (string*)stream;
	(*str).append((char*)ptr, size * nmemb);
	return size * nmemb;
}

void SendWebAPI_web(const string strUrl, const char* code, char** ptr)
{
	// curl初始化  
	CURL* curl = curl_easy_init();
	// curl返回值 
	CURLcode res;
	if (curl)
	{
		char url[100];
		sprintf(url, "%s?%s", strUrl.c_str(), code);
		//printf(">>>json %s\n", url);//减少不必要的打印
		// 设置URL
		curl_easy_setopt(curl, CURLOPT_URL, url);

		//设置数据接收和写入函数
		curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, req_reply_web);

		//数据接收
		string resPost0;
		curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)&resPost0);

		//设置超时时间
		curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10);
		curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10);

		//设置header
		curl_slist* headers = NULL;
		headers = curl_slist_append(headers, "Content-Type: application/json");
		curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
		
		// 开启post请求
		res = curl_easy_perform(curl);

		// 检查请求是否成功
		if (res != CURLE_OK) {
			printf("web failed res code: ");
		}
		else {
			printf(">>> web return str:%s \n", resPost0.c_str());

			*ptr = (char*)malloc(strlen(resPost0.c_str()) + 1); // 分配足够的内存空间
			if (*ptr != NULL) {
				strcpy(*ptr, resPost0.c_str());
			}
			else {
				printf("Memory allocation failed!\n");
			}
		}
	}
	else
	{
		printf(">>> web curl init failed");
	}
	curl_easy_cleanup(curl);
}

/*/////////////////////////////////////////////////////////lnk10-24根据web接口修改/////////////////////////////////////////////////////////////*/
//添加了json字符串的入参
// 回调函数，将数据直接追加到 *ptr 中
size_t req_reply_http(void* contents, size_t size, size_t nmemb, void* userp) {
    size_t realsize = size * nmemb;
    char** responsePtr = (char**)userp;

    size_t oldLen = strlen(*responsePtr);  // 当前已有长度
    char* temp = (char*)realloc(*responsePtr, oldLen + realsize + 1); // +1 留空间给 '\0'
    if (temp == NULL) {
        printf("Memory reallocation failed!\n");
        return 0;
    }

    *responsePtr = temp;
    memcpy(*responsePtr + oldLen, contents, realsize);  // 直接拷贝原始数据
    (*responsePtr)[oldLen + realsize] = '\0';           // 手动添加字符串结束符

    return realsize;
}

void SendJsonAPI_web(const std::string& strUrl, const char* code, const std::string& json, char** ptr) {
	//【修改点1】先校验 ptr 本身，避免空指针解引用
    if (ptr == NULL) {
        printf("SendJsonAPI_web error: ptr is NULL\n");
        return;
    }

    //【修改点2】如果上层传进来的 *ptr 非空，先释放并置空，避免旧内存残留
    if (*ptr != NULL) {
        free(*ptr);
        *ptr = NULL;
    }

    CURL* curl = curl_easy_init();
    CURLcode res;
	long http_code = 0;   //【修改点3】增加 HTTP 状态码

    // 初始化 *ptr 并分配空字符串
    *ptr = (char*)malloc(1);
    if (*ptr == NULL) {
        printf("Memory allocation failed!\n");
        return;
    }
    (*ptr)[0] = '\0';  // 为空字符串以便 strncat 操作

    if (curl) {
        char url[256];
        //【修改点5】避免 code 为 NULL 时 snprintf 异常
        snprintf(url, sizeof(url), "%s?%s", strUrl.c_str(), (code != NULL ? code : ""));

        curl_easy_setopt(curl, CURLOPT_URL, url);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, req_reply_http);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, ptr);
        curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10);
        curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10);

		//【修改点6】建议禁用 signal，防止多线程里超时信号影响别的线程
        curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);

        if (!json.empty()) {
            curl_easy_setopt(curl, CURLOPT_POST, 1L);
            curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json.c_str());
        }

        struct curl_slist* headers = NULL;
        headers = curl_slist_append(headers, "Content-Type: application/json");
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);

        res = curl_easy_perform(curl);

		//【修改点7】先取 HTTP 状态码
        curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);

        if (res != CURLE_OK) {
            printf("web failed res code: %s\n", curl_easy_strerror(res));
        }
		//【修改点9】即使 curl 成功，HTTP 非 200 也按失败处理
        /*else if (http_code != 200) {
            printf("web http failed, http_code: %ld\n", http_code);

            if (*ptr != NULL) {
                free(*ptr);
                *ptr = NULL;
            }
        }*/
        //【修改点10】即使 HTTP 200，但返回内容为空串，也按失败处理
        else if (*ptr == NULL || (*ptr)[0] == '\0') {
            printf("web response is empty\n");

            if (*ptr != NULL) {
                free(*ptr);
                *ptr = NULL;
            }
        } 
		else {
            //printf(">>> web return str: %s \n", *ptr);
        }

        curl_slist_free_all(headers);
        curl_easy_cleanup(curl);
    } else {
        printf(">>> web curl init failed\n");

		//【修改点11】curl 初始化失败时，也要释放之前分配的空串并置 NULL
        if (*ptr != NULL) {
            free(*ptr);
            *ptr = NULL;
        }
    }
}

// 打印 terminal_dev_map 中所有内容的函数
static bool is_cfg_pqdif_process()
{
	return std::strstr(subdir, "cfg_pqdif_data") != NULL;
}

static bool is_pqdif_com_type(const char* com_type)
{
	QString type = com_type ? QString::fromLocal8Bit(com_type).trimmed() : QString();
	return type.compare("PQDIF", Qt::CaseInsensitive) == 0;
}

static const char* PQDIF_CONFIG_DIR = "/FeProject/etc";
static const char* PQDIF_CONFIG_FILE = "/FeProject/etc/pqdif-config.xml";
static const char* PQDIF_CONFIG_TMP_FILE = "/FeProject/etc/pqdif-config.xml.tmp";

static bool pqdif_config_value_valid(const char* value)
{
	if (value == NULL)
		return false;

	QString text = QString::fromLocal8Bit(value).trimmed();
	return !text.isEmpty() && text.compare("N/A", Qt::CaseInsensitive) != 0;
}

static int pqdif_config_write_begin(QFile& file, QXmlStreamWriter& writer)
{
	if (!QDir().mkpath(PQDIF_CONFIG_DIR)) {
		printf("[PQDIF][CONFIG] mkdir failed: %s\n", PQDIF_CONFIG_DIR);
		return APR_EGENERAL;
	}

	QFile::remove(PQDIF_CONFIG_TMP_FILE);
	file.setFileName(PQDIF_CONFIG_TMP_FILE);
	if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) {
		printf("[PQDIF][CONFIG] open tmp failed: %s, err=%s\n",
		       PQDIF_CONFIG_TMP_FILE,
		       file.errorString().toLocal8Bit().constData());
		return APR_EGENERAL;
	}

	// Always write a complete temporary XML first. Java only reads the
	// final file after the tmp file has been closed and atomically renamed.
	writer.setDevice(&file);
	writer.setAutoFormatting(true);
	writer.setCodec("UTF-8");
	writer.writeStartDocument("1.0");
	writer.writeStartElement("PqdifConfig");
	return APR_SUCCESS;
}

static int pqdif_config_write_commit(QFile& file, QXmlStreamWriter& writer)
{
	writer.writeEndElement();
	writer.writeEndDocument();
	file.flush();
	file.close();

	if (::rename(PQDIF_CONFIG_TMP_FILE, PQDIF_CONFIG_FILE) != 0) {
		printf("[PQDIF][CONFIG] rename %s to %s failed\n",
		       PQDIF_CONFIG_TMP_FILE,
		       PQDIF_CONFIG_FILE);
		QFile::remove(PQDIF_CONFIG_TMP_FILE);
		return APR_EGENERAL;
	}

	return APR_SUCCESS;
}

static void pqdif_config_write_abort(QFile& file)
{
	if (file.isOpen())
		file.close();
	QFile::remove(PQDIF_CONFIG_TMP_FILE);
}

static void pqdif_config_write_point(QXmlStreamWriter& writer, const LD_info_t* LD_info)
{
	writer.writeEmptyElement("Point");
	writer.writeAttribute("pointNo", QString::number((int)LD_info->cpuno));
	writer.writeAttribute("monitorId", QString::fromLocal8Bit(LD_info->mp_id).trimmed());
}

static int pqdif_config_write_device(QXmlStreamWriter& writer,
                                     ied_t* ied,
                                     ied_usr_t* ied_usr)
{
	writer.writeStartElement("Device");
	writer.writeAttribute("ip", QString::fromLocal8Bit(ied->channel[0].addr_str).trimmed());
	writer.writeAttribute("pqdifType", QString::fromLocal8Bit(ied_usr->dev_type).trimmed());

	int point_count = 0;
	if (ied_usr->LD_info != NULL) {
		for (int cpuno = 0; cpuno < ied->cpucount; ++cpuno) {
			LD_info_t* LD_info = &(ied_usr->LD_info[cpuno]);
			if (LD_info->read_flag != ENABLE)
				continue;
			if (LD_info->cpuno == 0 || !pqdif_config_value_valid(LD_info->mp_id))
				continue;

			pqdif_config_write_point(writer, LD_info);
			++point_count;
		}
	}

	writer.writeEndElement();
	return point_count;
}

int pqdif_rebuild_config_from_current_ledger()
{
	if (!is_cfg_pqdif_process())
		return APR_SUCCESS;

	QFile file;
	QXmlStreamWriter writer;
	int write_count = 0;
	int point_count = 0;

	if (pqdif_config_write_begin(file, writer) != APR_SUCCESS)
		return APR_EGENERAL;

	if (g_node != NULL && g_node->clients != NULL) {
		for (int i = 0; i < g_node->n_clients; ++i) {
			ied_t* ied = g_node->clients[i];
			if (ied == NULL || ied->usr_ext == NULL || ied->chncount <= 0 || ied->channel == NULL)
				continue;

			ied_usr_t* ied_usr = (ied_usr_t*)ied->usr_ext;
			if (ied_usr->dev_flag != ENABLE)
				continue;
			if (!is_pqdif_com_type(ied_usr->com_type))
				continue;
			if (!pqdif_config_value_valid(ied->channel[0].addr_str) ||
			    !pqdif_config_value_valid(ied_usr->dev_type)) {
				printf("[PQDIF][CONFIG] skip invalid ledger, terminal=%s ip=%s type=%s\n",
				       ied_usr->terminal_id,
				       ied->channel[0].addr_str,
				       ied_usr->dev_type);
				continue;
			}

			point_count += pqdif_config_write_device(writer, ied, ied_usr);
			++write_count;
		}
	}

	if (pqdif_config_write_commit(file, writer) != APR_SUCCESS) {
		pqdif_config_write_abort(file);
		return APR_EGENERAL;
	}

	printf("[PQDIF][CONFIG] rebuild success, file=%s, devices=%d, points=%d\n",
	       PQDIF_CONFIG_FILE,
	       write_count,
	       point_count);
	return APR_SUCCESS;
}

static bool terminal_should_bind_to_current_process(const terminal_dev* dev)
{
	if (dev == NULL)
		return false;

	if (is_pqdif_com_type(dev->com_type))
		return is_cfg_pqdif_process();

	if (is_cfg_pqdif_process())
		return false;

	return (atoi(dev->processNo) == g_front_seg_index || g_front_seg_index == 0);
}

static bool terminal_update_should_bind_to_current_process(const terminal* dev)
{
	if (dev == NULL)
		return false;

	if (is_pqdif_com_type(dev->com_type))
		return is_cfg_pqdif_process();

	if (is_cfg_pqdif_process())
		return false;

	return true;
}

void printTerminalDevMap(const QMap<QString, terminal_dev*>& terminal_dev_map) {
	QMap<QString, terminal_dev*>::const_iterator it;
    for (it = terminal_dev_map.constBegin(); it != terminal_dev_map.constEnd(); ++it) {
        QString key = it.key();
        terminal_dev* dev = it.value();
        
        if (dev) {
            qDebug() << "Key:" << key
                     << ", Terminal ID:" << QString(dev->terminal_id)
                     << ", Terminal Code:" << QString(dev->terminal_code)
                     << ", Organization Name:" << QString(dev->org_name)
                     << ", Maintenance Name:" << QString(dev->maint_name)
                     << ", Station Name:" << QString(dev->station_name)
                     << ", Factory:" << QString(dev->tmnl_factory)
                     << ", Status:" << QString(dev->tmnl_status)
                     << ", Device Type:" << QString(dev->dev_type)
                     << ", Device Key:" << QString(dev->dev_key)
                     << ", Device Series:" << QString(dev->dev_series)
					 << ", Device comType:" << QString(dev->com_type)
					 << ", Device processNo:" << QString(dev->processNo)
					 << ", Device maxProcessNum:" << QString(dev->maxProcessNum)
                     << ", Address:" << QString(dev->addr_str)
                     << ", Port:" << QString(dev->port)
					 << ", log_level:" << QString(dev->log_level)
                     << ", Timestamp:" << QString(dev->timestamp);

            // 打印监测点信息
            for (int i = 0; i < 10; ++i) {
                qDebug() << " Monitor ID:" << QString(dev->line[i].monitor_id)
                         << ", Terminal Code:" << QString(dev->line[i].terminal_code)
                         << ", Monitor Name:" << QString(dev->line[i].monitor_name)
                         << ", Logical Device Seq:" << QString(dev->line[i].logical_device_seq)
                         << ", Voltage Level:" << QString(dev->line[i].voltage_level)
                         << ", Terminal Connect:" << QString(dev->line[i].terminal_connect)
                         << ", Timestamp:" << QString(dev->line[i].timestamp)
						 << ", log_level:" << QString(dev->line[i].log_level)
                         << ", Status:" << QString(dev->line[i].status);
            }
        } else {
            qDebug() << "Key:" << key << ", Value is nullptr";
        }
    }
}

//打印当前进程的台账//////////////////////////////////////////////////////////////////////////
// 打印结构体信息的递归函数
void printLedger(const ied_usr_t& ied_usr) {
    std::cout << "------------------------------------" << std::endl;
    std::cout << "|-- terminal_id: " << ied_usr.terminal_id << std::endl;
	std::cout << "|-- dev_index: " << ied_usr.dev_idx << std::endl;
    std::cout << "|-- dev_type: " << ied_usr.dev_type << std::endl;
    std::cout << "|-- dev_key: " << ied_usr.dev_key << std::endl;
    std::cout << "|-- dev_series: " << ied_usr.dev_series << std::endl;
    std::cout << "|-- com_type: " << ied_usr.com_type << std::endl;
    std::cout << "|-- dev_flag: " << ied_usr.dev_flag << std::endl;

    std::cout << "|-- last_call_wavelist_time: " << ied_usr.last_call_wavelist_time << std::endl;

    std::cout << "|-- org_name: " << ied_usr.org_name << std::endl;
    std::cout << "|-- maint_name: " << ied_usr.maint_name << std::endl;
    std::cout << "|-- station_name: " << ied_usr.station_name << std::endl;
    std::cout << "|-- tmnl_factory: " << ied_usr.tmnl_factory << std::endl;
    std::cout << "|-- time: " << ied_usr.time << std::endl;
    std::cout << "|-- tmnl_status: " << ied_usr.tmnl_status << std::endl;
    std::cout << "|-- terminal_code: " << ied_usr.terminal_code << std::endl;
    std::cout << "|-- update_flag: " << ied_usr.update_flag << std::endl;
    std::cout << "|-- log_level: " << ied_usr.log_level << std::endl;

    // 打印每个LD_info的内容
    for (int i = 0; i < 10; ++i) {
        if (strcmp(ied_usr.LD_info[i].mp_id, "") != 0) {
            std::cout << "|-- LD_info[" << i << "]:" << std::endl;
//name
            std::cout << "    |-- name: " << ied_usr.LD_info[i].name << std::endl;
			std::cout << "    |-- LD_name: " 
                      << (strlen(ied_usr.LD_info[i].LD_name) == 0 ? "NA" : ied_usr.LD_info[i].LD_name) 
                      << std::endl;
			std::cout << "    |-- read_flag: " << ied_usr.LD_info[i].read_flag << std::endl;
			std::cout << "    |-- log_level: " << ied_usr.LD_info[i].log_level << std::endl;
//index
			std::cout << "    |-- line_id: " << ied_usr.LD_info[i].line_id << std::endl;
//monitorledger
            std::cout << "    |-- mp_id: " << ied_usr.LD_info[i].mp_id << std::endl;
            std::cout << "    |-- terminal_code: " << ied_usr.LD_info[i].terminal_code << std::endl;
            std::cout << "    |-- voltage_level: " << ied_usr.LD_info[i].voltage_level << std::endl;
            std::cout << "    |-- v_wiring_type: " << ied_usr.LD_info[i].v_wiring_type << std::endl;
            std::cout << "    |-- time: " << ied_usr.LD_info[i].time << std::endl;
            std::cout << "    |-- update_flag: " << ied_usr.LD_info[i].update_flag << std::endl;
            std::cout << "    |-- monitor_status: " << ied_usr.LD_info[i].monitor_status << std::endl;
//count暂不打印数组
			std::cout << "    |-- rptcount: " << ied_usr.LD_info[i].rptcount << std::endl;
			std::cout << "    |-- logcount: " << ied_usr.LD_info[i].logcount << std::endl;
//rpt
			std::cout << "    |-- rptRecvFlag: " << ied_usr.LD_info[i].rptRecvFlag << std::endl;
			std::cout << "    |-- rptRecvCheckFlag: " << ied_usr.LD_info[i].rptRecvCheckFlag << std::endl;
			std::cout << "    |-- rptPstRecvFlag: " << ied_usr.LD_info[i].rptPstRecvFlag << std::endl;
			std::cout << "    |-- rptPstRecvCheckFlag: " << ied_usr.LD_info[i].rptPstRecvCheckFlag << std::endl;
//rtdata
			std::cout << "    |-- real_data: " << ied_usr.LD_info[i].real_data << std::endl;
			std::cout << "    |-- soe_data: " << ied_usr.LD_info[i].soe_data << std::endl;
			std::cout << "    |-- limit: " << ied_usr.LD_info[i].limit << std::endl;
			std::cout << "    |-- count: " << ied_usr.LD_info[i].count << std::endl;
//RDRE
			std::cout << "    |-- RDRE_FltNum: " << ied_usr.LD_info[i].RDRE_FltNum << std::endl;
			for (int j = 0; j < 256; ++j) { 
                if (ied_usr.LD_info[i].FltNum[j] != 0) {
					std::cout << "        |-- FltNum[" << j << "]:" << ied_usr.LD_info[i].FltNum[j] << std::endl;
				}
			}
//QVVR
			std::cout << "    |-- qvvr_idx: " << ied_usr.LD_info[i].qvvr_idx << std::endl;
            std::cout << "    |-- QVVRs:" << std::endl;
            for (int j = 0; j < 256; ++j) {  
                if (ied_usr.LD_info[i].qvvr[j].used_status != 0) {
                    std::cout << "        |-- QVVR[" << j << "]:" << std::endl;
                    std::cout << "		      |-- used_status: " << ied_usr.LD_info[i].qvvr[j].used_status << std::endl;
                    std::cout << "		      |-- QVVR_start: " << ied_usr.LD_info[i].qvvr[j].QVVR_start << std::endl;
                    std::cout << "		      |-- QVVR_type: " << ied_usr.LD_info[i].qvvr[j].QVVR_type << std::endl;
                    std::cout << "		      |-- QVVR_time: " << ied_usr.LD_info[i].qvvr[j].QVVR_time << std::endl;
                    std::cout << "		      |-- QVVR_PerTime: " << ied_usr.LD_info[i].qvvr[j].QVVR_PerTime << std::endl;
                    std::cout << "		      |-- QVVR_Amg: " << ied_usr.LD_info[i].qvvr[j].QVVR_Amg << std::endl;
                    std::cout << "		      |-- QVVR_Rptname: " << ied_usr.LD_info[i].qvvr[j].QVVR_Rptname << std::endl;
                    std::cout << "		      |-- timestamp: " << ied_usr.LD_info[i].qvvr[j].timestamp << std::endl;
                }
            }
        }
    }
    std::cout << "------------------------------------" << std::endl;
}
void printLedgerinshell(const ied_usr_t& ied_usr, QIODevice* outputDevice) {
	ied_t* ied;
	ied = find_ied_from_dev_idx(ied_usr.dev_idx);

    outputDevice->write("\r\x1B[K");outputDevice->write("------------------------------------\n");
    outputDevice->write("\r\x1B[K");outputDevice->write("|-- terminal_id: " + QByteArray(ied_usr.terminal_id) + "\n");
    outputDevice->write("\r\x1B[K");outputDevice->write("|-- dev_index: " + QByteArray::number(ied_usr.dev_idx) + "\n");
	outputDevice->write("\r\x1B[K");outputDevice->write("|-- dev_cpucount: " + QByteArray::number(ied->cpucount) + "\n");
	outputDevice->write("\r\x1B[K");outputDevice->write("|-- dev_ip: " + QByteArray(ied->channel[0].addr_str) + "\n");
	char portStr[20];  // 用于存放端口号的字符串
	sprintf(portStr, "%u", ied->channel[0].port);  // 将端口号转为字符串
	outputDevice->write("\r\x1B[K");outputDevice->write("|-- dev_port: " + QByteArray(portStr) + "\n");

	char statusStr[20];  // 用于存放状态的字符串
	sprintf(statusStr, "%u", ied->channel[0].status);  // 将连接状态转为字符串
	outputDevice->write("\r\x1B[K");outputDevice->write("|-- dev_connect_status: " + QByteArray(statusStr) + "\n");
    outputDevice->write("\r\x1B[K");outputDevice->write("|-- dev_type: " + QByteArray(ied_usr.dev_type) + "\n");
    outputDevice->write("\r\x1B[K");outputDevice->write("|-- dev_key: " + QByteArray(ied_usr.dev_key) + "\n");
    outputDevice->write("\r\x1B[K");outputDevice->write("|-- dev_series: " + QByteArray(ied_usr.dev_series) + "\n");
	outputDevice->write("\r\x1B[K");outputDevice->write("|-- com_type: " + QByteArray(ied_usr.com_type) + "\n");
	outputDevice->write("\r\x1B[K");outputDevice->write("|-- dev_processNo: " + QByteArray(ied_usr.processNo) + "\n");
    outputDevice->write("\r\x1B[K");outputDevice->write("|-- dev_flag: " + QByteArray::number(ied_usr.dev_flag) + "\n");
    outputDevice->write("\r\x1B[K");outputDevice->write("|-- last_call_wavelist_time: " + QByteArray::number(ied_usr.last_call_wavelist_time) + "\n");
    outputDevice->write("\r\x1B[K");outputDevice->write("|-- org_name: " + QByteArray(ied_usr.org_name) + "\n");
    outputDevice->write("\r\x1B[K");outputDevice->write("|-- maint_name: " + QByteArray(ied_usr.maint_name) + "\n");
    outputDevice->write("\r\x1B[K");outputDevice->write("|-- station_name: " + QByteArray(ied_usr.station_name) + "\n");
    outputDevice->write("\r\x1B[K");outputDevice->write("|-- tmnl_factory: " + QByteArray(ied_usr.tmnl_factory) + "\n");
    outputDevice->write("\r\x1B[K");outputDevice->write("|-- time: " + QByteArray::number(ied_usr.time) + "\n");
    outputDevice->write("\r\x1B[K");outputDevice->write("|-- tmnl_status: " + QByteArray(ied_usr.tmnl_status) + "\n");
    outputDevice->write("\r\x1B[K");outputDevice->write("|-- terminal_code: " + QByteArray(ied_usr.terminal_code) + "\n");
    outputDevice->write("\r\x1B[K");outputDevice->write("|-- update_flag: " + QByteArray::number(ied_usr.update_flag) + "\n");
	outputDevice->write("\r\x1B[K");outputDevice->write("|-- log_level: " + QByteArray::number(ied_usr.log_level) + "\n");

    // 打印每个LD_info的内容
    for (int i = 0; i < 10; ++i) {
        if (strcmp(ied_usr.LD_info[i].mp_id, "") != 0) {
            outputDevice->write("\r\x1B[K");outputDevice->write("|-- LD_info[" + QByteArray::number(i) + "]:\n");
            
            // name
            outputDevice->write("\r\x1B[K");outputDevice->write("    |-- name: " + QByteArray(ied_usr.LD_info[i].name) + "\n");
            outputDevice->write("\r\x1B[K");outputDevice->write("    |-- LD_name: " 
                + (strlen(ied_usr.LD_info[i].LD_name) == 0 ? QByteArray("NA") : QByteArray(ied_usr.LD_info[i].LD_name))
                + "\n");
			outputDevice->write("\r\x1B[K");outputDevice->write("    |-- read_flag: " + QByteArray::number(ied_usr.LD_info[i].read_flag) + "\n");

			outputDevice->write("\r\x1B[K");outputDevice->write("    |-- log_level: " + QByteArray::number(ied_usr.LD_info[i].log_level) + "\n");

            // index
            outputDevice->write("\r\x1B[K");outputDevice->write("    |-- line_id: " + QByteArray::number(ied_usr.LD_info[i].line_id) + "\n");

            // monitorledger
            outputDevice->write("\r\x1B[K");outputDevice->write("    |-- mp_id: " + QByteArray(ied_usr.LD_info[i].mp_id) + "\n");
            outputDevice->write("\r\x1B[K");outputDevice->write("    |-- terminal_code: " + QByteArray(ied_usr.LD_info[i].terminal_code) + "\n");
            outputDevice->write("\r\x1B[K");outputDevice->write("    |-- voltage_level: " + QByteArray(ied_usr.LD_info[i].voltage_level) + "\n");
            outputDevice->write("\r\x1B[K");outputDevice->write("    |-- v_wiring_type: " + QByteArray(ied_usr.LD_info[i].v_wiring_type) + "\n");
            outputDevice->write("\r\x1B[K");outputDevice->write("    |-- time: " + QByteArray::number(ied_usr.LD_info[i].time) + "\n");
            outputDevice->write("\r\x1B[K");outputDevice->write("    |-- update_flag: " + QByteArray::number(ied_usr.LD_info[i].update_flag) + "\n");
            outputDevice->write("\r\x1B[K");outputDevice->write("    |-- monitor_status: " + QByteArray(ied_usr.LD_info[i].monitor_status) + "\n");

            // count暂不打印数组
            outputDevice->write("\r\x1B[K");outputDevice->write("    |-- rptcount: " + QByteArray::number(ied_usr.LD_info[i].rptcount) + "\n");
            outputDevice->write("\r\x1B[K");outputDevice->write("    |-- logcount: " + QByteArray::number(ied_usr.LD_info[i].logcount) + "\n");

			//recall
			outputDevice->write("\r\x1B[K");outputDevice->write("    |-- autorecallflag: " + QByteArray::number(ied_usr.LD_info[i].autorecallflag) + "\n");
            outputDevice->write("\r\x1B[K");outputDevice->write("    |-- autorecallcount: " + QByteArray::number(ied_usr.LD_info[i].autorecallcount) + "\n");
			for (int j = 0; 
				j < ied_usr.LD_info[i].autorecallcount && 
				ied_usr.LD_info[i].autorecall != NULL &&  // 保护 LD_info->autorecall 本身
				ied_usr.LD_info[i].autorecall[j] != NULL; 
				j++) 
		   {
			   outputDevice->write("\r\x1B[K");
			   outputDevice->write("        |-- autorecall_t[" + QByteArray::number(j) + "]:\n");
		   
			   if (ied_usr.LD_info[i].autorecall[j] == NULL) {
				   outputDevice->write("            |-- [Error] autorecall[j] is NULL\n");
				   continue;  // 避免访问 NULL 指针
			   }
		   
			   outputDevice->write("\r\x1B[K");
			   outputDevice->write("            |-- start_time: " + QByteArray::number(ied_usr.LD_info[i].autorecall[j]->start) + "\n");		   
			   outputDevice->write("\r\x1B[K");
			   outputDevice->write("            |-- end_time: " + QByteArray::number(ied_usr.LD_info[i].autorecall[j]->end) + "\n");	   
			   outputDevice->write("\r\x1B[K");
			   outputDevice->write("            |-- need_steady: " + QByteArray::number(ied_usr.LD_info[i].autorecall[j]->need_steady) + "\n");	   
			   outputDevice->write("\r\x1B[K");
			   outputDevice->write("            |-- need_voltage: " + QByteArray::number(ied_usr.LD_info[i].autorecall[j]->need_voltage) + "\n");
		   }
            // rpt
            outputDevice->write("\r\x1B[K");outputDevice->write("    |-- rptRecvFlag: " + QByteArray::number(ied_usr.LD_info[i].rptRecvFlag) + "\n");
            outputDevice->write("\r\x1B[K");outputDevice->write("    |-- rptRecvCheckFlag: " + QByteArray::number(ied_usr.LD_info[i].rptRecvCheckFlag) + "\n");
            outputDevice->write("\r\x1B[K");outputDevice->write("    |-- rptPstRecvFlag: " + QByteArray::number(ied_usr.LD_info[i].rptPstRecvFlag) + "\n");
            outputDevice->write("\r\x1B[K");outputDevice->write("    |-- rptPstRecvCheckFlag: " + QByteArray::number(ied_usr.LD_info[i].rptPstRecvCheckFlag) + "\n");

            // rtdata
            outputDevice->write("\r\x1B[K");outputDevice->write("    |-- real_data: " + QByteArray::number(ied_usr.LD_info[i].real_data) + "\n");
            outputDevice->write("\r\x1B[K");outputDevice->write("    |-- soe_data: " + QByteArray::number(ied_usr.LD_info[i].soe_data) + "\n");
            outputDevice->write("\r\x1B[K");outputDevice->write("    |-- limit: " + QByteArray::number(ied_usr.LD_info[i].limit) + "\n");
            outputDevice->write("\r\x1B[K");outputDevice->write("    |-- count: " + QByteArray::number(ied_usr.LD_info[i].count) + "\n");

            // RDRE
            outputDevice->write("\r\x1B[K");outputDevice->write("    |-- RDRE_FltNum: " + QByteArray::number(ied_usr.LD_info[i].RDRE_FltNum) + "\n");
            for (int j = 0; j < 256; ++j) {
                if (ied_usr.LD_info[i].FltNum[j] != 0) {
                    outputDevice->write("\r\x1B[K");outputDevice->write("        |-- FltNum[" + QByteArray::number(j) + "]: " 
                        + QByteArray::number(ied_usr.LD_info[i].FltNum[j]) + "\n");
                }
            }

            // QVVR
            outputDevice->write("\r\x1B[K");outputDevice->write("    |-- qvvr_idx: " + QByteArray::number(ied_usr.LD_info[i].qvvr_idx) + "\n");
            outputDevice->write("\r\x1B[K");outputDevice->write("    |-- QVVRs:\n");
            for (int j = 0; j < 256; ++j) {
                if (ied_usr.LD_info[i].qvvr[j].used_status != QVVR_DATA_NOT_USED) {
                    outputDevice->write("\r\x1B[K");outputDevice->write("        |-- QVVR[" + QByteArray::number(j) + "]:\n");
                    outputDevice->write("\r\x1B[K");outputDevice->write("            |-- used_status: " + QByteArray::number(ied_usr.LD_info[i].qvvr[j].used_status) + "\n");
                    outputDevice->write("\r\x1B[K");outputDevice->write("            |-- QVVR_start: " + QByteArray::number(ied_usr.LD_info[i].qvvr[j].QVVR_start) + "\n");
                    outputDevice->write("\r\x1B[K");outputDevice->write("            |-- QVVR_type: " + QByteArray::number(ied_usr.LD_info[i].qvvr[j].QVVR_type) + "\n");
                    outputDevice->write("\r\x1B[K");outputDevice->write("            |-- QVVR_time: " + QByteArray::number(ied_usr.LD_info[i].qvvr[j].QVVR_time) + "\n");
                    outputDevice->write("\r\x1B[K");outputDevice->write("            |-- QVVR_PerTime: " + QByteArray::number(ied_usr.LD_info[i].qvvr[j].QVVR_PerTime) + "\n");
                    outputDevice->write("\r\x1B[K");outputDevice->write("            |-- QVVR_Amg: " + QByteArray::number(ied_usr.LD_info[i].qvvr[j].QVVR_Amg) + "\n");
                    outputDevice->write("\r\x1B[K");outputDevice->write("            |-- QVVR_Rptname: " + QByteArray(ied_usr.LD_info[i].qvvr[j].QVVR_Rptname) + "\n");
                    outputDevice->write("\r\x1B[K");outputDevice->write("            |-- timestamp: " + QByteArray::number(ied_usr.LD_info[i].qvvr[j].timestamp) + "\n");
                }
            }
        }
    }
    outputDevice->write("\r\x1B[K");outputDevice->write("------------------------------------\n");
}

// 打印所有设备信息或特定终端信息
void ledger(const char* terminal_id, QIODevice* outputDevice) {
	outputDevice->write("\r\x1B[K");
	outputDevice->write("print ledger in shell");
	pthread_mutex_lock(&mtx);   std::cout << "ledger()hold lock !!!!!!!!!!!" << std::endl;
    bool found = false;
    ied_t* ied;
    ied_usr_t* ied_usr;
    for (int t = 0; t < g_node->n_clients; t++) {
        ied = (ied_t*)g_node->clients[t];
		if(ied != NULL){
			ied_usr = (ied_usr_t*)ied->usr_ext;
        	if (ied_usr != NULL && (terminal_id == NULL || strcmp(ied_usr->terminal_id, terminal_id) == 0)) {
            	printLedgerinshell(*ied_usr, outputDevice);  // 使用 QIODevice 输出
				//std::cout << "!!! print to log !!!"<< std::endl;
				//printLedger(*ied_usr);
				if(terminal_id != NULL && strcmp(ied_usr->terminal_id, terminal_id) == 0){
					found = true;
				}
            	
        	}
		}
        
    }
	pthread_mutex_unlock(&mtx); std::cout << "ledger()free lock !!!!!!!!!!!" << std::endl;
    if (terminal_id != NULL && !found) {
		std::cout << "terminal not exsist: " << terminal_id << std::endl;
        QByteArray msg = "terminal not exsist: " + QByteArray(terminal_id) + "\n";
		outputDevice->write("\r\x1B[K");
        outputDevice->write(msg);  // 输出到 QIODevice
    }
	
}

//lnk20250210打印指定的变量名
void value_print(const char *variableName, QTcpSocket *clientSocket) {
    char buffer[256];  // 用于存储变量值的缓冲区

	pthread_mutex_lock(&mtx); std::cout << "value_print hold lock !!!!!!!!!!!" << std::endl;
    // 打印变量值
	
    if (strcmp(variableName, "frontindex") == 0) {
        sprintf(buffer, "frontindex = %d", g_front_seg_index);  // 将 int 转换为字符串
		clientSocket->write("\r\x1B[K");
        clientSocket->write(buffer);  // 发送字符串到客户端
    }
	else if (strcmp(variableName, "remtable") == 0) {
        sprintf(buffer, "remtable = %d",g_pt61850app->chnl_counts);
		clientSocket->write("\r\x1B[K");
        clientSocket->write(buffer);
    } 
	else if (strcmp(variableName, "iedcount") == 0) {
        sprintf(buffer, "g_node->n_clients = %d, ied config count = %d",g_node->n_clients ,IED_COUNT);
		clientSocket->write("\r\x1B[K");
        clientSocket->write(buffer);
    } else if (strcmp(variableName, "frontfun") == 0) {
        sprintf(buffer, "frontfun = %s", subdir);
		clientSocket->write("\r\x1B[K");
        clientSocket->write(buffer); 
	} else if (strcmp(variableName, "log") == 0) {
		sprintf(buffer, "showinshellflag = %d,debugOutputEnabled = %d,normalOutputEnabled = %d,warnOutputEnabled = %d,errorOutputEnabled = %d", showinshellflag,debugOutputEnabled,normalOutputEnabled,warnOutputEnabled,errorOutputEnabled);
		clientSocket->write("\r\x1B[K");
		clientSocket->write(buffer);
	}else if (strcmp(variableName, "init") == 0) {
		sprintf(buffer, "INITFLAG = %d",INITFLAG);
		clientSocket->write("\r\x1B[K");
		clientSocket->write(buffer);
	}else {
		clientSocket->write("\r\x1B[K");
        clientSocket->write("Unknown variable name\n> ");
    }
	pthread_mutex_unlock(&mtx); std::cout << "value_print free lock !!!!!!!!!!!" << std::endl;
    clientSocket->flush();
}

std::list<std::string>* getLogList(const QString& level) {
	if (level == "ERROR") return &errorList;
	if (level == "WARN") return &warnList;
	if (level == "NORMAL") return &normalList;
	if (level == "DEBUG") return &debugList;
	return NULL;
}

pthread_mutex_t* getLogMutex(const QString& level) {
	if (level == "ERROR") return &errorListMutex;
	if (level == "WARN") return &warnListMutex;
	if (level == "NORMAL") return &normalListMutex;
	if (level == "DEBUG") return &debugListMutex;
	return NULL;
}

void Worker::handleViewLogCommand(const QString& command, QTcpSocket* clientSocket) {
    QStringList parts = command.split(" ");
    if (parts.size() != 2) {
		clientSocket->write("\r\x1B[K");
        clientSocket->write("Usage: viewlog [ERROR|WARN|NORMAL|DEBUG]\n> ");
        clientSocket->flush();
        return;
    }

    QString logLevel = parts[1].toUpper();
    std::list<std::string>* logList = getLogList(logLevel);
    pthread_mutex_t* logMutex = getLogMutex(logLevel);

    if (!logList || !logMutex) {
		clientSocket->write("\r\x1B[K");
        clientSocket->write("Invalid log level! Use ERROR, WARN, NORMAL, or DEBUG.\n> ");
        clientSocket->flush();
        return;
    }

    stopViewLog = false;
    activeClient = clientSocket;  // 记录当前 shell socket

	clientSocket->write("\r\x1B[K");
    clientSocket->write(QString("Viewing logs for level: %1 (Press '`' to exit)\n> ").arg(logLevel).toUtf8());
    clientSocket->flush();

	// --- 新增 begin ---
    // 创建 /FeProject/dat/log 目录（若不存在）
    QString logDir = "/FeProject/dat/log";
    QDir().mkpath(logDir);

    // 生成唯一的日志文件名，如 temp.log、temp_1.log、temp_2.log 等
    QString baseName = "temp.log";
    QString filePath = logDir + "/" + baseName;
    int index = 1;
    while (QFile::exists(filePath)) {
        filePath = QString("%1/temp_%2.log").arg(logDir).arg(index++);
    }

    QFile logFile(filePath);
    QTextStream logStream(&logFile);
    if (!logFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
        clientSocket->write("\r\x1B[K");
        clientSocket->write("Failed to open log file for writing.\n> ");
        clientSocket->flush();
        return;
    }
    // --- 新增 end ---

    while (!stopViewLog) {
	    // 1. 监听退出标志（收到反引号 `）
	    if (clientSocket->waitForReadyRead(500)) {
	        QByteArray input = clientSocket->readAll().trimmed();
	        if (input == "`") {
	            std::cout << "Received '`' from shell socket! Exiting viewlog...\n";
	            stopViewLog = true;
	            showinshellflag = false;
	            break;
	        }
	    }

	    // 2. 批量获取日志并发送
	    std::list<std::string> tempLogs;

	    pthread_mutex_lock(logMutex);
	    if (!logList->empty()) {
	        tempLogs.swap(*logList);  // 把 logList 中的内容全取出
	    }
	    pthread_mutex_unlock(logMutex);

	    if (!tempLogs.empty()) {
	        for (const auto& logEntry : tempLogs) {
	            if (!logEntry.empty()) {
	                clientSocket->write("\r\x1B[K");  // 清除当前行
	                clientSocket->write((logEntry + "\n").c_str());

					// --- 新增 begin ---
	                logStream << QString::fromStdString(logEntry) << "\n";
	                // --- 新增 end ---
	            }
	        }
	        clientSocket->flush();

			// --- 新增 begin ---
	        logStream.flush();  // 确保及时写入文件
	        // --- 新增 end ---
	    } else {
	        usleep(500000);  // 空闲等待，降低 CPU 占用
	    }
	}

    // **3. 退出 `viewlog`，返回 Shell**
	clientSocket->write("\r\x1B[K");
    clientSocket->write("\nLog view stopped. Returning to shell.\n> ");
    clientSocket->flush();

	// --- 新增 begin ---
    logFile.close();  // 关闭文件
    // --- 新增 end ---
}

//////////////////////////////////////////////////////////////////////////////////////////////////
// 解析 JSON 的函数  多前置动态均分
int terminal_ledger_web(QMap<QString, terminal_dev*>* terminal_dev_map,
                        const std::vector<std::string>& codes,
                        int index,
                        int num)
{
	//后续修改为根据index来获取对应的台账，num就没有作用了，index用来均分的也没有作用了
	if(1 == MULTIPLE_NODE_FLAG){
		// 参数验证
    	if (num <= 0) {
    	    std::cerr << "Error: 'num' must be greater than 0." << std::endl;
			DIY_ERRORLOG_CODE("process",0,LOG_CODE_LEDGER,"【ERROR】前置的多进程最大进程号为:%d,应该为大于0的整数",num);
    	    return 1; // 返回适当的错误码
    	}

		index = index - 1;
    	if (index < 0 || index >= num) {
    	    std::cerr << "Error: 'index' must be in the range [0, num]." << std::endl;
			DIY_ERRORLOG_CODE("process",0,LOG_CODE_LEDGER,"【ERROR】前置当前进程的进程号为:%d,应该为0到最大进程号范围内的整数",index);
    	    return 1; // 返回适当的错误码
    	}
	}    

    // 获取参数
    if (codes.empty()) {
        std::cerr << "Error: 'codes' vector is empty." << std::endl;
		DIY_ERRORLOG_CODE("process",0,LOG_CODE_LEDGER,"【ERROR】前置的%s%d号进程调用web台账接口的入参为空",get_front_msg_from_subdir(), g_front_seg_index);
        return 1;
    }

    std::string parm = codes[0];

    char* ptr = NULL;

	int retry = 0;

	cJSON* root = NULL;

	while (1) {

		//防止泄露lnk20260312
		if (root != NULL) {
    	    cJSON_Delete(root);
    	    root = NULL;
    	}

    	if (ptr != NULL) {
    	    free(ptr);
    	    ptr = NULL;
    	}

    	// 请求接口
    	SendJsonAPI_web(WEB_DEVICE, "", parm.c_str(), &ptr);		

    	if (ptr != NULL && ptr[0] != '\0') {

			// 调试用
    		printf("ptr:%s\n", ptr);

    	    root = cJSON_Parse(ptr);   //json格式序列化
    	    if (root != NULL) {
				//添加解析
    	        cJSON* codeItem = cJSON_GetObjectItem(root, "code");
            	cJSON* msgItem = cJSON_GetObjectItem(root, "msg");
            	std::string code = (codeItem && codeItem->type == cJSON_String) ? codeItem->valuestring : "not found";
            	std::string msg = (msgItem && msgItem->type == cJSON_String) ? msgItem->valuestring : "not found";

            	std::cout << "code: " << code << std::endl;
            	std::cout << "msg: " << msg << std::endl;

            	cJSON* data = cJSON_GetObjectItem(root, "data");
            	if (data && data->type == cJSON_Array) {
            	    int data_size = cJSON_GetArraySize(data);
            	    std::cout << "data_size " << data_size << std::endl;
            	    if (data_size > 0) {
            	        break; // 成功解析并非空数组
            	    }
            	}
				std::cerr << "data 无效或为空数组，重试" << std::endl;
				DIY_ERRORLOG_CODE("process",0,LOG_CODE_LEDGER,"【ERROR】前置从web接口中获取的台账信息为空或者无效信息无法解析,请核对前置使用的入参信息:%s",parm.c_str());
    	    }
    	}

    	// 解析失败，尝试重试
    	//printf("web error %s\n", cJSON_GetErrorPtr());
    	retry++;

    	if (retry > 3) {
    	    printf("web error after 3 retry\n");

    	    // 释放之前的 ptr
    	    if (ptr) {
    	        free(ptr);
    	        ptr = NULL;
    	    }

    	    // 读取本地台账文件
			if (root != NULL) {
			    cJSON_Delete(root);
			    root = NULL;
			}

    	    char* ledger = NULL;
			read_latest_ledger_file(&ledger);
    	    if (ledger != NULL) {
    	        root = cJSON_Parse(ledger);
    	        if (root != NULL) {

					//解析台账
					cJSON* codeItem = cJSON_GetObjectItem(root, "code");
                	cJSON* msgItem = cJSON_GetObjectItem(root, "msg");
                	std::string code = (codeItem && codeItem->type == cJSON_String) ? codeItem->valuestring : "not found";
                	std::string msg = (msgItem && msgItem->type == cJSON_String) ? msgItem->valuestring : "not found";

                	std::cout << "code: " << code << std::endl;
                	std::cout << "msg: " << msg << std::endl;

                	cJSON* data = cJSON_GetObjectItem(root, "data");
                	if (data && data->type == cJSON_Array) {
                	    int data_size = cJSON_GetArraySize(data);
                	    std::cout << "data_size " << data_size << std::endl;
                	    if (data_size > 0) {
                	        free(ledger);
                	        break; // 本地台账解析成功且数组非空
                	    }
                	}
					DIY_ERRORLOG_CODE("process",0,LOG_CODE_LEDGER,"【ERROR】前置从本地台账中获取的台账信息为空或者无效信息无法解析,请核对前置使用的入参信息:%s",parm.c_str());
    	        }

    	        free(ledger); // root==null释放内容
    	    }

    	    // 本地文件解析仍失败，等待 5 分钟后再重试
    	    printf("still failed after sleep retry, wait 5 min and retry...\n");
    	    apr_sleep(apr_time_from_sec(300));  // 5 分钟

    	    retry = 0;  // 重置 retry 重新开始循环
    	    continue;
    	}

		//每次重试等2秒
		apr_sleep(apr_time_from_sec(2));
	}

    // 获取 "code" 和 "msg"
    cJSON* codeItem = cJSON_GetObjectItem(root, "code");
    cJSON* msgItem = cJSON_GetObjectItem(root, "msg");

    // 使用 std::string 获取值
    std::string code = (codeItem != NULL && codeItem->type == cJSON_String) ? codeItem->valuestring : "not found";
    std::string msg = (msgItem != NULL && msgItem->type == cJSON_String) ? msgItem->valuestring : "not found";

    // 打印结果
    std::cout << "code: " << code << std::endl;
    std::cout << "msg: " << msg << std::endl;

    // 获取 "data" 数组
    cJSON* data = cJSON_GetObjectItem(root, "data");
    if (!data || data->type != cJSON_Array) {//不可能
        std::cerr << "Error: 'data' is not an array." << std::endl;
        cJSON_Delete(root);
        free(ptr);
        return 1;
    }

    int data_size = cJSON_GetArraySize(data);

	std::cout << "data_size " << data_size << std::endl;

    if (data_size == 0) {//不可能
        std::cerr << "Error: 'data' array is empty." << std::endl;
        cJSON_Delete(root);
        free(ptr);
        return 1;
    }

	int start_index = 0;
	int end_index = data_size;

    // 遍历指定范围内的元素
    for (int i = start_index; i < end_index; ++i) {
        cJSON* item = cJSON_GetArrayItem(data, i);
        if (!item || item->type != cJSON_Object) {
            std::cerr << "Warning: Invalid item at index " << i << "." << std::endl;
            continue; // 跳过无效的项
        }

        terminal_dev* dev = new terminal_dev();
        memset(dev, 0, sizeof(terminal_dev)); // 初始化结构体

        // 解析各个字段
        cJSON* id = cJSON_GetObjectItem(item, "id"); // terminal_id
        if (id && id->type == cJSON_String) strncpy(dev->terminal_id, id->valuestring, sizeof(dev->terminal_id) - 1);
        else strncpy(dev->terminal_id, "N/A", sizeof(dev->terminal_id) - 1);
		dev->terminal_id[sizeof(dev->terminal_id) - 1] = '\0';

        cJSON* ip = cJSON_GetObjectItem(item, "ip"); // addr_str
        if (ip && ip->type == cJSON_String) strncpy(dev->addr_str, ip->valuestring, sizeof(dev->addr_str) - 1);
        else strncpy(dev->addr_str, "N/A", sizeof(dev->addr_str) - 1);
		dev->addr_str[sizeof(dev->addr_str) - 1] = '\0';

        cJSON* terminalCode = cJSON_GetObjectItem(item, "name"); // terminal_code
        if (terminalCode && terminalCode->type == cJSON_String) strncpy(dev->terminal_code, terminalCode->valuestring, sizeof(dev->terminal_code) - 1);
        else strncpy(dev->terminal_code, "N/A", sizeof(dev->terminal_code) - 1);
		dev->terminal_code[sizeof(dev->terminal_code) - 1] = '\0';

        cJSON* orgName = cJSON_GetObjectItem(item, "org_name"); // org_name
        if (orgName && orgName->type == cJSON_String) strncpy(dev->org_name, orgName->valuestring, sizeof(dev->org_name) - 1);
        else strncpy(dev->org_name, "N/A", sizeof(dev->org_name) - 1);
		dev->org_name[sizeof(dev->org_name) - 1] = '\0';

        cJSON* maintName = cJSON_GetObjectItem(item, "maint_name"); // maint_name
        if (maintName && maintName->type == cJSON_String) strncpy(dev->maint_name, maintName->valuestring, sizeof(dev->maint_name) - 1);
        else strncpy(dev->maint_name, "N/A", sizeof(dev->maint_name) - 1);
		dev->maint_name[sizeof(dev->maint_name) - 1] = '\0';

        cJSON* stationName = cJSON_GetObjectItem(item, "stationName"); // station_name
        if (stationName && stationName->type == cJSON_String) strncpy(dev->station_name, stationName->valuestring, sizeof(dev->station_name) - 1);
        else strncpy(dev->station_name, "N/A", sizeof(dev->station_name) - 1);
		dev->station_name[sizeof(dev->station_name) - 1] = '\0';

        cJSON* manufacturer = cJSON_GetObjectItem(item, "manufacturer"); // tmnl_factory
        if (manufacturer && manufacturer->type == cJSON_String) strncpy(dev->tmnl_factory, manufacturer->valuestring, sizeof(dev->tmnl_factory) - 1);
        else strncpy(dev->tmnl_factory, "N/A", sizeof(dev->tmnl_factory) - 1);
		dev->tmnl_factory[sizeof(dev->tmnl_factory) - 1] = '\0';

        cJSON* status = cJSON_GetObjectItem(item, "status"); // tmnl_status
        if (status && status->type == cJSON_String) strncpy(dev->tmnl_status, status->valuestring, sizeof(dev->tmnl_status) - 1);
        else strncpy(dev->tmnl_status, "N/A", sizeof(dev->tmnl_status) - 1);
		dev->tmnl_status[sizeof(dev->tmnl_status) - 1] = '\0';

        cJSON* devType = cJSON_GetObjectItem(item, "devType"); // dev_type
        if (devType && devType->type == cJSON_String) strncpy(dev->dev_type, devType->valuestring, sizeof(dev->dev_type) - 1);
        else strncpy(dev->dev_type, "N/A", sizeof(dev->dev_type) - 1);
		dev->dev_type[sizeof(dev->dev_type) - 1] = '\0';

        cJSON* devKey = cJSON_GetObjectItem(item, "devKey"); // dev_key
        if (devKey && devKey->type == cJSON_String) strncpy(dev->dev_key, devKey->valuestring, sizeof(dev->dev_key) - 1);
        else strncpy(dev->dev_key, "N/A", sizeof(dev->dev_key) - 1);
		dev->dev_key[sizeof(dev->dev_key) - 1] = '\0';

        cJSON* series = cJSON_GetObjectItem(item, "series"); // dev_series
        if (series && series->type == cJSON_String) strncpy(dev->dev_series, series->valuestring, sizeof(dev->dev_series) - 1);
        else strncpy(dev->dev_series, "N/A", sizeof(dev->dev_series) - 1);
		dev->dev_series[sizeof(dev->dev_series) - 1] = '\0';

		cJSON* comType = cJSON_GetObjectItem(item, "comType");
		if (comType == NULL)
			comType = cJSON_GetObjectItem(item, "com_type");
		if (comType && comType->type == cJSON_String) strncpy(dev->com_type, comType->valuestring, sizeof(dev->com_type) - 1);
		else strncpy(dev->com_type, "MMS", sizeof(dev->com_type) - 1);
		dev->com_type[sizeof(dev->com_type) - 1] = '\0';

		//lnk20250210台账进程号
		cJSON* processNo = cJSON_GetObjectItem(item, "processNo"); // processNo转为字符串
        if (processNo && processNo->type == cJSON_Number) snprintf(dev->processNo, sizeof(dev->processNo), "%d", processNo->valueint);
        else strncpy(dev->processNo, "N/A", sizeof(dev->processNo) - 1);
		dev->processNo[sizeof(dev->processNo) - 1] = '\0';

		//20250513进程数量
		cJSON* maxProcessNum = cJSON_GetObjectItem(item, "maxProcessNum"); // maxProcessNum转为字符串
        if (maxProcessNum && maxProcessNum->type == cJSON_Number) snprintf(dev->maxProcessNum, sizeof(dev->maxProcessNum), "%d", maxProcessNum->valueint);
        else strncpy(dev->maxProcessNum, "N/A", sizeof(dev->maxProcessNum) - 1);
		dev->maxProcessNum[sizeof(dev->maxProcessNum) - 1] = '\0';

        cJSON* port = cJSON_GetObjectItem(item, "port"); // port
        if (port && port->type == cJSON_String) strncpy(dev->port, port->valuestring, sizeof(dev->port) - 1);
        else strncpy(dev->port, "N/A", sizeof(dev->port) - 1);
		dev->port[sizeof(dev->port) - 1] = '\0';

        cJSON* updateTime = cJSON_GetObjectItem(item, "updateTime"); // timestamp
        if (updateTime && updateTime->type == cJSON_String) strncpy(dev->timestamp, updateTime->valuestring, sizeof(dev->timestamp) - 1);
        else strncpy(dev->timestamp, "N/A", sizeof(dev->timestamp) - 1);
		dev->timestamp[sizeof(dev->timestamp) - 1] = '\0';

		cJSON* logLevel = cJSON_GetObjectItem(item, "log_level"); // log_level
		int tmp_level = -1;
		if (logLevel && logLevel->type == cJSON_Number) {
		    tmp_level = logLevel->valueint;
		}
		else if (logLevel && logLevel->type == cJSON_String && logLevel->valuestring) {
		    tmp_level = atoi(logLevel->valuestring);
		}
		// 判断是否合法 0~3
		if (tmp_level >= 0 && tmp_level <= 3) {
		    dev->log_level = tmp_level;
		} else {
		    dev->log_level = 1; // 默认 WARN
		}
		printf("dev->log_level: %d\n", dev->log_level);

        // 解析 monitorData 数组
        cJSON* monitorData = cJSON_GetObjectItem(item, "monitorData");
        if (monitorData && monitorData->type == cJSON_Array) {
            int j = 0;
            cJSON* monitorItem;
            cJSON_ArrayForEach(monitorItem, monitorData) {
                if (j >= 10){
                    std::cout << "一个终端最多只能有十个监测点" << std::endl;
                    break;  // 限制为最多10个
                }

                cJSON* monitor_id = cJSON_GetObjectItem(monitorItem, "id"); // monitor_id
                if (monitor_id && monitor_id->type == cJSON_String) strncpy(dev->line[j].monitor_id, monitor_id->valuestring, sizeof(dev->line[j].monitor_id) - 1);
                else strncpy(dev->line[j].monitor_id, "N/A", sizeof(dev->line[j].monitor_id) - 1);
                dev->line[j].monitor_id[sizeof(dev->line[j].monitor_id) - 1] = '\0';

                cJSON* monitor_name = cJSON_GetObjectItem(monitorItem, "name"); // monitor_name
                if (monitor_name && monitor_name->type == cJSON_String) strncpy(dev->line[j].monitor_name, monitor_name->valuestring, sizeof(dev->line[j].monitor_name) - 1);
                else strncpy(dev->line[j].monitor_name, "N/A", sizeof(dev->line[j].monitor_name) - 1);
                dev->line[j].monitor_name[sizeof(dev->line[j].monitor_name) - 1] = '\0';

                cJSON* lineNo = cJSON_GetObjectItem(monitorItem, "lineNo"); // logical_device_seq
                if (lineNo && lineNo->type == cJSON_String) strncpy(dev->line[j].logical_device_seq, lineNo->valuestring, sizeof(dev->line[j].logical_device_seq) - 1);
                else strncpy(dev->line[j].logical_device_seq, "N/A", sizeof(dev->line[j].logical_device_seq) - 1);
                dev->line[j].logical_device_seq[sizeof(dev->line[j].logical_device_seq) - 1] = '\0';

                cJSON* voltageLevel = cJSON_GetObjectItem(monitorItem, "voltageLevel"); // voltage_level
                if (voltageLevel && voltageLevel->type == cJSON_String) strncpy(dev->line[j].voltage_level, voltageLevel->valuestring, sizeof(dev->line[j].voltage_level) - 1);
                else strncpy(dev->line[j].voltage_level, "N/A", sizeof(dev->line[j].voltage_level) - 1);
                dev->line[j].voltage_level[sizeof(dev->line[j].voltage_level) - 1] = '\0';

                cJSON* ptType = cJSON_GetObjectItem(monitorItem, "ptType"); // terminal_connect
                if (ptType && ptType->type == cJSON_String) strncpy(dev->line[j].terminal_connect, ptType->valuestring, sizeof(dev->line[j].terminal_connect) - 1);
                else strncpy(dev->line[j].terminal_connect, "N/A", sizeof(dev->line[j].terminal_connect) - 1);
                dev->line[j].terminal_connect[sizeof(dev->line[j].terminal_connect) - 1] = '\0';

                // 添加监测点状态
                cJSON* monitorstatus = cJSON_GetObjectItem(monitorItem, "status"); // status
                if (monitorstatus && monitorstatus->type == cJSON_String) strncpy(dev->line[j].status, monitorstatus->valuestring, sizeof(dev->line[j].status) - 1);
                else strncpy(dev->line[j].status, "N/A", sizeof(dev->line[j].status) - 1);
                dev->line[j].status[sizeof(dev->line[j].status) - 1] = '\0';

				cJSON* logLevel_m = cJSON_GetObjectItem(monitorItem, "log_level"); // log_level
				int tmp_level = -1;							
				if (logLevel_m && logLevel_m->type == cJSON_Number) {
				    tmp_level = logLevel_m->valueint;
				}
				else if (logLevel_m && logLevel_m->type == cJSON_String && logLevel_m->valuestring) {
				    tmp_level = atoi(logLevel_m->valuestring);
				}
				// 判断是否合法 (0~3)
				if (tmp_level >= 0 && tmp_level <= 3) {
				    dev->line[j].log_level = tmp_level;
				} else {
				    dev->line[j].log_level = dev->log_level; // 默认 WARN
				}
				printf("line[%d].log_level: %d\n", j, dev->line[j].log_level);

                j++;
            }
        }

        // 准备键
    	QString key = QString(dev->terminal_id);//用id而不是code区分：有的code存在乱码

		//lnk20260312防止内存泄漏
		bool inserted = false;
		bool should_bind = terminal_should_bind_to_current_process(dev);

    	// 检查是否存在重复键
    	if (terminal_dev_map->contains(key)) {
        	std::cerr << "Duplicate terminal_code found: " << key.toStdString() << std::endl;

        	// 删除旧的 terminal_dev 对象以避免内存泄漏
			if (should_bind) delete terminal_dev_map->value(key);

        	// 移除旧的键值对
			if (should_bind) terminal_dev_map->remove(key);

        	// 插入新的 terminal_dev 对象
			if(should_bind){//lnk20250210匹配进程号
				//调试用
				std::cout<< "process num match" << std::endl;
        		terminal_dev_map->insert(key, dev);
				inserted = true;
			}//后续修改为只有进程号匹配上index才录入当前进程
    	} else {
        	// 插入新的 terminal_dev 对象
			if(should_bind){//lnk20250210匹配进程号
				//调试用
				std::cout<< "process num match" << std::endl;
        		terminal_dev_map->insert(key, dev);
				inserted = true;
			}//后续修改为只有进程号匹配上index才录入当前进程
			//调试用
			//std::cout << "i =  " << i << std::endl;
			//std::cout << "terminal_dev_map.size:" << terminal_dev_map->size() << std::endl;
    	}//如果出现重复项，日志要有体现方便排查

		if (!inserted) {
		    delete dev;
		    dev = NULL;
		}

    }

	//读取台账有效，保存或更新到台账文件20250513lnk
	if(g_node_id == STAT_DATA_BASE_NODE_ID && g_front_seg_index == 1  && ptr != NULL && ptr[0] != '\0'){
		save_ledger_json(ptr);
		//普通日志，更新本地台账
	}
	

    // 释放资源
    cJSON_Delete(root);
    free(ptr);

    return 0; // 确保函数有返回值
}

static void init_oper_type_cache(ied_usr_t *ied_usr)
{
    if (ied_usr == NULL)
        return;

    ied_usr->oper_type_cache.inited = SD_FALSE;

    ied_usr->oper_type_cache.ledrs_oper_type_id = -1;
    ied_usr->oper_type_cache.reboot_oper_type_id = -1;
	ied_usr->oper_type_cache.reset_oper_type_id = -1;
}

int parse_device_cfg_web()
{
	std::cout << "parse_device_cfg_web" << endl;

	std::vector<std::string> codes;	//入参集合

	ied_t* ied;
	ied_usr_t* ied_usr;
	chnl_usr_t* chnl_usr;
	int count_cfg = 0;	//终端台账总数
	int count_real = 0;	//遍历终端台账的计数器

	//接口合并，只用一个web接口获取终端台账和监测点台账
	QMap<QString, terminal_dev*> terminal_dev_map;	

	//构造入参json
	std::string input_jstr = "{";
	input_jstr += "\"ip\":\"" + std::string(FRONT_IP) + "\",";
	input_jstr += "\"runFlag\":" + TERMINAL_STATUS + "";
	input_jstr += "}";

	std::cout << "input_jstr: " << input_jstr << std::endl; // 输出结果

	DIY_DEBUGLOG_CODE("process",0,LOG_CODE_LEDGER,"【DEBUG】前置的%s%d号进程调用web接口获取台账使用的请求输入为:%s",get_front_msg_from_subdir(), g_front_seg_index,input_jstr);

	codes.push_back(input_jstr);	//是否需要筛选状态直接在配置文件控制

	if(terminal_ledger_web(&terminal_dev_map,codes,g_front_seg_index,g_front_seg_num)){
		return APR_EBADF;
	}

	codes.clear();

	//调试用
	//printTerminalDevMap(terminal_dev_map);
	//稳态进程1添加功能：判断看门狗配置是否正确
	if(g_node_id == STAT_DATA_BASE_NODE_ID && g_front_seg_index == 1)
	{
		int max_index;
		int max_process_num;

		//读取配置文件的最大进程号
		max_index = get_max_stat_data_index("/FeProject/etc/runtime.cf");
		printf("max_index = %d\n", max_index);

		//读取第一条数据的最大进程号
		QMap<QString, terminal_dev*>::iterator it = terminal_dev_map.begin();
		if (it != terminal_dev_map.end() && it.value()) {
		    terminal_dev* dev = it.value();
		    max_process_num = atoi(dev->maxProcessNum);  // 转为 int
		    printf("maxProcessNum = %d\n", max_process_num);
		} else {
		    printf("terminal_dev_map is empty or contains null entry.\n");
		}

		//判断是否相等
		if(max_process_num != max_index){
			if(max_process_num > 0 && max_process_num < 10){
				DIY_WARNLOG_CODE("process",0,LOG_CODE_LEDGER,"【WARN】前置比对台账获取的进程数:%d和本地配置的进程数:%d,不匹配,按照台账进程数重置前置的进程数量",max_process_num,max_index);
				// 调用执行脚本函数
				close_listening_socket();
        		execute_bash("reset", max_process_num, "all");
			}
			else{
				DIY_ERRORLOG_CODE("process",0,LOG_CODE_LEDGER,"【ERROR】前置从台账获取的进程数:%d不符合范围1~9,按照本地配置进程数启动进程",max_process_num);
			}
		}
	}

	count_cfg = terminal_dev_map.size();//容器的数量就是台账的数量

	std::cout << "terminal_ledger_num:" << count_cfg << std::endl;

	DIY_DEBUGLOG_CODE("process",0,LOG_CODE_LEDGER,"【DEBUG】前置的%s%d号进程调用获取到的台账的数量为:%d",get_front_msg_from_subdir(), g_front_seg_index,count_cfg);

	//如果当前进程获取的台账为0，按照配置数量申请空间，台账内容为空
	g_node->n_clients = count_cfg;
	
	//这里开辟的ied的空间由配置文件中的终端台账数量决定lnk20250121
	if(IED_COUNT < count_cfg){		//申请数至少是初始化能读取到的台账数，防止设置失误导致的崩溃。
		//如果是多进程，IED_COUNT应该设置为大于平均数，
		//如果是单进程则应该设置为大于终端总数，
		//单进程多进程同时存在则单进程应该大于终端总数，否则添加台账时单进程部分就无法同步添加。

		g_node->clients = (ied_t**)apr_pcalloc(g_cfg_pool, count_cfg * sizeof(ied_t*));
		//添加提示
		std::cout << "!!!!!!!!!!single process can not add any ledger unless reboot!!!!!!!"<< std::endl;

		DIY_WARNLOG_CODE("process",0,LOG_CODE_LEDGER,"【WARN】前置的%s%d号进程获取到的台账的数量大于配置文件中给单个进程配置的台账数量:%d,这个进程将按照获取到的台账的数量来创建台账空间，这个进程不能直接通过台账添加来新增台账，只能通过重启进程或者先删除已有台账再添加台账的方式来添加新台账",get_front_msg_from_subdir(), g_front_seg_index,IED_COUNT);
	}
	else{
		g_node->clients = (ied_t**)apr_pcalloc(g_cfg_pool, IED_COUNT * sizeof(ied_t*));//g_node->clients 这块大内存空间存储了 count_cfg 个 ied_t* 类型的指针（即一个指针数组）这是（指向内存块的指针）的指针数组
		DIY_INFOLOG_CODE("process",0,LOG_CODE_LEDGER,"【NORMAL】前置的%s%d号进程根据配置文件中给单个进程配置的台账数量:%d来创建台账空间",get_front_msg_from_subdir(), g_front_seg_index,IED_COUNT);
	}
	
	//把ied放入数组
	
	for (int k = 0; k < count_cfg; k++){

		//调试用
		std::cout << "!!!!!!!!!!gnodeindex:" << k  << std::endl;

		g_node->clients[k] = (ied_t*)apr_pcalloc(g_cfg_pool, sizeof(ied_t));
	}//每个 g_node->clients[k] 指向的内存块是独立的（每个 ied_t 结构体占用的内存块）这是指向内存块的指针

	//读数据
	try {
		char terminal_id[64];
		char terminal_code[64];
		char org_name[64];
		char maint_name[64];
		char station_name[64];
		char tmnl_factory[64];
		char tmnl_status[64];
		char dev_type[64];
		char dev_key[255];
		char dev_series[255];
		char com_type[32];
		char addr_str[64];
		char port_char[64];

		//lnk20250210添加进程号
		char processNo[64];

		//lnk20260304添加日志等级
		int log_level;

		otl_datetime timestamp;

		// 遍历终端台账容器
		QMap<QString, terminal_dev*>::iterator it;
    	for (it = terminal_dev_map.begin(); it != terminal_dev_map.end(); ++it) {
    		terminal_dev* value = it.value();

			// 确保 value 不为空
    		if (value != nullptr) {
        		// 找到容器中对应名称并取值
        		strncpy(terminal_id, value->terminal_id, sizeof(terminal_id) - 1);
        		strncpy(terminal_code, value->terminal_code, sizeof(terminal_code) - 1);
        		strncpy(org_name, value->org_name, sizeof(org_name) - 1);
        		strncpy(maint_name, value->maint_name, sizeof(maint_name) - 1);
        		strncpy(station_name, value->station_name, sizeof(station_name) - 1);
        		strncpy(tmnl_factory, value->tmnl_factory, sizeof(tmnl_factory) - 1);
        		strncpy(tmnl_status, value->tmnl_status, sizeof(tmnl_status) - 1);
        		strncpy(dev_type, value->dev_type, sizeof(dev_type) - 1);
        		strncpy(dev_key, value->dev_key, sizeof(dev_key) - 1);
        		strncpy(dev_series, value->dev_series, sizeof(dev_series) - 1);
				strncpy(com_type, value->com_type, sizeof(com_type) - 1);
				com_type[sizeof(com_type) - 1] = '\0';
        		strncpy(addr_str, value->addr_str, sizeof(addr_str) - 1);
        		strncpy(port_char, value->port, sizeof(port_char) - 1);
				strncpy(processNo, value->processNo, sizeof(processNo) - 1);//进程号
				timestamp = parseTimestamp(value->timestamp);

				log_level = value->log_level;//日志等级
			} else {
				std::cerr << "Warning: terminal_dev pointer is null for key: " << it.key().toStdString() << std::endl;
				continue; // 跳过空指针
			}

			//处理终端台账
			ied = g_node->clients[count_real++];
			//这里申请的空间基于ied的数量，挂载到ied上
			ied_usr = (ied_usr_t*)apr_pcalloc(g_init_pool, sizeof(ied_usr_t));//终端台账在initpool中申请空间
			ied->usr_ext = ied_usr;
			if (ied_usr == NULL)
				return APR_ENOMEM;
			ied_usr->last_call_wavelist_time = sGetMsTime() + g_pt61850app->giTime * 1000;
			//这里申请的空间基于ied的数量，挂载到ied上
			ied_usr->LD_info = (LD_info_t*)apr_pcalloc(g_init_pool, MAX_CPUNO * sizeof(LD_info_t));//监测点台账在initpool中申请空间
			if (ied_usr->LD_info == NULL)
				return APR_ENOMEM;
			ied_usr->dev_flag = ENABLE;//终端有效
			ied->chncount = 1;//设备通信端口总数
			//这里申请的空间基于ied的数量，挂载到ied上
			ied->channel = (channel_t*)apr_pcalloc(g_cfg_pool, sizeof(channel_t) * ied->chncount);//通信结构在g_cfg_pool中申请空间：终端的ip端口等
			ied->channel[0].ied = ied;
			ied->channel[0].status = STATUS_BREAKOFF;
			ied->cpucount = 0;
			if (strlen(terminal_id) != 0) {
				apr_snprintf(ied_usr->terminal_id, sizeof(ied_usr->terminal_id), "%s", terminal_id);//terminal_id
				cout << "ied_usr->terminal_id:" << ied_usr->terminal_id << endl;
			}
			if (terminal_code != NULL) {
				apr_snprintf(ied_usr->terminal_code, sizeof(ied_usr->terminal_code), "%s", terminal_code);//terminal_code
				cout << "ied_usr->terminal_code:" << ied_usr->terminal_code << endl;
			}
			/*不需要这三个信息			
			if (org_name != NULL) {
				apr_snprintf(ied_usr->org_name, sizeof(ied_usr->org_name), "%s", org_name);//org_name
				cout << "ied_usr->org_name:" << ied_usr->org_name << endl;
			}
			if (maint_name != NULL) {
				apr_snprintf(ied_usr->maint_name, sizeof(ied_usr->maint_name), "%s", maint_name);//maint_name
				cout << "ied_usr->maint_name:" << ied_usr->maint_name << endl;
			}
			if (station_name != NULL) {
				apr_snprintf(ied_usr->station_name, sizeof(ied_usr->station_name), "%s", station_name);//station_name
				cout << "ied_usr->station_name:" << ied_usr->station_name << endl;
			}
			*/
			if (tmnl_factory != NULL) {
				apr_snprintf(ied_usr->tmnl_factory, sizeof(ied_usr->tmnl_factory), "%s", tmnl_factory);//tmnl_factory
				cout << "ied_usr->tmnl_factory:" << ied_usr->tmnl_factory << endl;
			}
			if (tmnl_status != NULL) {
				apr_snprintf(ied_usr->tmnl_status, sizeof(ied_usr->tmnl_status), "%s", tmnl_status);//tmnl_status
				cout << "ied_usr->tmnl_status:" << ied_usr->tmnl_status << endl;
			}
			if (dev_type != NULL) {
				apr_snprintf(ied_usr->dev_type, sizeof(ied_usr->dev_type), "%s", dev_type);//dev_type
				cout << "ied_usr->dev_type:" << ied_usr->dev_type << endl;
			}
			//lnk20250210台账进程号
			if (com_type != NULL && com_type[0] != '\0') {
				apr_snprintf(ied_usr->com_type, sizeof(ied_usr->com_type), "%s", com_type);//comType
				cout << "ied_usr->com_type:" << ied_usr->com_type << endl;
			}
			else {
				apr_snprintf(ied_usr->com_type, sizeof(ied_usr->com_type), "%s", "MMS");
				cout << "ied_usr->com_type(default):" << ied_usr->com_type << endl;
			}
			if (processNo != NULL) {
				apr_snprintf(ied_usr->processNo, sizeof(ied_usr->processNo), "%s", processNo);//processNo
				cout << "ied_usr->processNo:" << ied_usr->processNo << endl;
			}
			if (dev_series != NULL) {
				apr_snprintf(ied_usr->dev_series, sizeof(ied_usr->dev_series), "%s", dev_series);//DEV_Series
				cout << "defalut dev_series:" << ied_usr->dev_series << endl;
			}
			else
			{
				apr_snprintf(ied_usr->dev_series, sizeof(ied_usr->dev_series), "%s", "");//DEV_Series
				cout << "defalut dev_series:" << ied_usr->dev_series << endl;
			}
			if (dev_key != NULL) {
				apr_snprintf(ied_usr->dev_key, sizeof(ied_usr->dev_key), "%s", dev_key);//DEV_Key
				cout << "defalut dev_key:" << ied_usr->dev_key << endl;
			}
			else
			{
				apr_snprintf(ied_usr->dev_key, sizeof(ied_usr->dev_key), "%s", "");//DEV_Key
				cout << "defalut dev_key:" << ied_usr->dev_key << endl;
			}

			//lnk20260512
			init_oper_type_cache(ied_usr);

			//lnk20260304
			ied_usr->log_level = log_level;//日志等级
			cout << "ied_usr->log_level:" << ied_usr->log_level << endl;
			//lnk20241125实时数据用
			ied_usr->dev_idx = count_real;
			cout << "dev_idx:" << ied_usr->dev_idx << endl;
			ied->channel[0].channel_type = CHANNEL_TYPE_IPV4;//channel
			ied->channel[0].addr_str[LONGNAME - 1] = 0;//DEV_IP
			if (addr_str != NULL) {
				ied->channel[0].addr = ntohl(inet_addr(addr_str));//DEV_IP
				strncpy(ied->channel[0].addr_str, addr_str, LONGNAME - 1);//DEV_IP
				cout << "ied_usr->addr_str:" << ied->channel[0].addr_str << endl;
			}
			else
			{
				ied->channel[0].addr = ntohl(inet_addr("0.0.0.0"));//DEV_IP
				strncpy(ied->channel[0].addr_str, addr_str, LONGNAME - 1);//DEV_IP
				cout << "ied_usr->addr_str:" << ied->channel[0].addr_str << endl;
			}
			if (port_char != NULL) {
				int port = 102;
				if (stringToInt(port_char, &port)) {
					// 转换成功，portStr全为数字，并且已经转换为int类型的port  
					ied->channel[0].port = port;//DEV_PortID
					cout << "ied_usr->port:" << ied->channel[0].port << endl;//DEV_PortID
				}
				else {
					ied->channel[0].port = 102;//DEV_PortID
					cout << "ied_usr->port:" << port_char << ",非合法端口.使用默认端口:" << ied->channel[0].port << endl;//DEV_PortID
				}
			}
			if (timestamp.year != 0) {
				//// 构造struct tm对象
				struct tm timeinfo;
				timeinfo.tm_year = timestamp.year - 1900; // 年份需要减去1900
				timeinfo.tm_mon = timestamp.month - 1; // 月份需要减去1
				timeinfo.tm_mday = timestamp.day;
				timeinfo.tm_hour = timestamp.hour;
				timeinfo.tm_min = timestamp.minute;
				timeinfo.tm_sec = timestamp.second;
				time_t time = std::mktime(&timeinfo);
				ied_usr->time = static_cast<long long>(time);
				cout << "ied_usr->time:" << ied_usr->time << endl;
			}
			//这里申请的空间基于ied的数量，挂载到ied上
			chnl_usr = (chnl_usr_t*)apr_pcalloc(g_init_pool, sizeof(chnl_usr_t));//拓展定义的通信结构在g_init_pool中申请空间：拓展记录一些开关时间信息
			ied->channel[0].connect = chnl_usr;
			chnl_usr->chnl = &(ied->channel[0]);
			chnl_usr->chnl_id = 0;
			chnl_usr->m_state = CHANNEL_DISCONNECTED;
			chnl_usr->m_ClosedMsTime = NEXT_CONNECT_TIME * (-1);
			g_pt61850app->chnl_counts++;
			//调试用
			//std::cout << "value:" << terminal_id <<"  "<<terminal_code <<"  "<<tmnl_factory <<"  "<<dev_series <<"  "<<dev_key <<"  "<< tmnl_status <<"  "<<dev_type <<"  "<<addr_str <<"  "<<port_char <<"  "<< timestamp.year << std::endl;
			// 遍历监测点信息
			LD_info_t line_info;
			int count_real_monitor = 0;	//遍历监测点台账的计数器
			//调试用
			//std::cout << "n_clients:" << g_node->n_clients << std::endl;
			if (g_node->n_clients <= 0) {
				std::cout << "no terminal exist " << std::endl;
				return APR_EBADF;
			}
			char monitor_id[64];
			//char terminal_code[64];
			char monitor_name[64];
			char logical_device_seq[64];
			char voltage_level[64];
			char terminal_connect[64];
			char monitor_status[64];
			//otl_datetime timestamp;
			int monitor_log_level = 1;//监测点日志等级
            //for (int j = 0; j < 10; ++j) { // 假设最多有10个监测点
			for (int j = 0; value->line[j].monitor_id[0] != '\0'; ++j){//只看有测点号的监测点，避免访问无效数据
            	ledger_monitor& monitor = value->line[j];
            	// 检查监测点 ID 是否为空以避免访问无效数据
            	/*if (monitor.monitor_id[0] != '\0') { 
            	    std::cout << "  Monitor ID: " << monitor.monitor_id << std::endl;
            	    std::cout << "  Terminal Code: " << monitor.terminal_code << std::endl;		//应该为空，json不带
            	    std::cout << "  Monitor Name: " << monitor.monitor_name << std::endl;
            	    std::cout << "  Logical Device Seq: " << monitor.logical_device_seq << std::endl;
            	    std::cout << "  Voltage Level: " << monitor.voltage_level << std::endl;
            	    std::cout << "  Terminal Connect: " << monitor.terminal_connect << std::endl;
            	    std::cout << "  Timestamp: " << monitor.timestamp << std::endl;				//应该为空json不带
					std::cout << "  monitor_status: " << monitor.status << std::endl;*/
				strncpy(monitor_id, monitor.monitor_id, sizeof(monitor_id) - 1);
        		//strncpy(terminal_code, monitor.terminal_code, sizeof(terminal_code) - 1);	//从上级获取
        		strncpy(monitor_name, monitor.monitor_name, sizeof(monitor_name) - 1);
        		strncpy(logical_device_seq, monitor.logical_device_seq, sizeof(logical_device_seq) - 1);
        		strncpy(voltage_level, monitor.voltage_level, sizeof(voltage_level) - 1);
        		strncpy(terminal_connect, monitor.terminal_connect, sizeof(terminal_connect) - 1);
				//timestamp = parseTimestamp(monitor.timestamp);								//从上级获取
				strncpy(monitor_status, monitor.status, sizeof(monitor_status) - 1);//添加监测点状态
				monitor_log_level = monitor.log_level;//监测点日志等级
				//监测点台账处理
				count_real_monitor++;//初始为0，进入一次+1，有效监测点排号从1开始
				memset(&line_info, 0, sizeof(line_info));//有效测点的初始化

				line_info.line_id = count_real_monitor;			//监测点排号，前置本地使用
				cout << "line_id:" << line_info.line_id << endl;

				strcpy(line_info.mp_id, monitor_id);//测点id
				cout << "mp_id:" << line_info.mp_id << endl;

				strcpy(line_info.terminal_code, terminal_code);					//从上级获取的终端号
				cout << "terminal_code:" << line_info.terminal_code << endl;

				if (isCharPtrEmpty(logical_device_seq)) {
					line_info.cpuno = 1;										//默认监测点实例号1
					cout << "logical_device_seq:is null,set cpuno:"<< line_info.cpuno << endl;
				}
				else {
					line_info.cpuno = std::atoi(logical_device_seq);	//台账实例号，即台账的测点号
					cout << "logical_device_seq:"<< line_info.cpuno << endl;
				}
				//cout << "cpuno:" << line_info.cpuno << endl;
				strcpy(line_info.voltage_level, voltage_level);//电压等级
				cout << "voltage_level:" << line_info.voltage_level << endl;

				strcpy(line_info.v_wiring_type, terminal_connect);//接线方式
				cout << "v_wiring_type:" << line_info.v_wiring_type << endl;

				//lnk2024-8-14记录接线标志
				if (strcmp(line_info.v_wiring_type, "0") != 0)
				{
					isdelta_flag = 1;	//存在一个监测点为角型接线则这个前置就要启动第二个配置列表
					cout << "monitor_id" << monitor_id << "v_wiring_type:" << line_info.v_wiring_type << "is delta wiring:" << isdelta_flag << endl;
					DIY_WARNLOG_CODE("process",0,LOG_CODE_LEDGER,"【WARN】前置连接的监测点 %s 是角形接线，对应终端为%s 终端类型是%s",line_info.mp_id,ied_usr->terminal_id,ied_usr->dev_type);
				}
				strcpy(line_info.monitor_status, monitor_status);//测点状态
				cout << "monitor_status:" << line_info.monitor_status << endl;
				//// 构造struct tm对象
				struct tm timeinfo;
				timeinfo.tm_year = timestamp.year - 1900; // 年份需要减去1900		//从上级获取的timestamp
				timeinfo.tm_mon = timestamp.month - 1; // 月份需要减去1
				timeinfo.tm_mday = timestamp.day;
				timeinfo.tm_hour = timestamp.hour;
				timeinfo.tm_min = timestamp.minute;
				timeinfo.tm_sec = timestamp.second;
				time_t time = std::mktime(&timeinfo);
				line_info.time = static_cast<long long>(time);//测点更新时间
				cout << "time:" << line_info.time << endl;

				strcpy(line_info.name, monitor_name);//测点名
				cout << "name:" << line_info.name << endl;

				line_info.read_flag = ENABLE;				//监测点有效

				line_info.log_level = monitor_log_level;		//监测点日志等级
				cout << "log_level_monitor:" << line_info.log_level << endl;

				//ied = find_ied_from_dev_code(line_info.terminal_code); //不需要再找上级终端了，已经在终端里了
				if (ied && ied->usr_ext && line_info.cpuno && (static_cast<int>(line_info.cpuno) < 10)) {//台账测点号有效
					char str[256];			//256大小
					byte_t cpuno = line_info.cpuno;	//台账的测点号
					cout << "cpuno:" << (int)line_info.cpuno << endl;
					cout << "index cpuno:" << cpuno-1 << endl;

					ied_usr = (ied_usr_t*)ied->usr_ext;
					ied_usr->LD_info[cpuno - 1] = line_info;   //cpuno默认是1
					ied_usr->LD_info[cpuno - 1].ied = ied;		//父级指针绑定
					apr_snprintf(str, sizeof(str), "PQMonitorPQM%d", cpuno);//将监测点逻辑号转为PQMonitorPQM+逻辑号
					//lnk20250208不使用apr_pstrdup，后续直接复用
					//ied_usr->LD_info[cpuno - 1].LD_name = apr_pstrdup(g_init_pool, str);//将 str 中的格式化字符串复制到内存池 g_init_pool 中。ied_usr->LD_info[cpuno - 1].LD_name 存储了这个字符串的副本，LD_name 现在是 PQMonitorPQM{cpuno} 的形式。
					// 从 g_init_pool 内存池中分配固定 256 字节的内存
					ied_usr->LD_info[cpuno - 1].LD_name = (char *)apr_palloc(g_init_pool, 256);//根据台账中的测点号申请内存
					//调试用，申请的地址
					printf("%s分配内存地址 LD_name[%d]: %p\n", ied_usr->terminal_id, cpuno - 1, (void*)ied_usr->LD_info[cpuno - 1].LD_name);
					// 清空内存，防止残留数据
					memset(ied_usr->LD_info[cpuno - 1].LD_name, 0, 256);
					// 将 str 中的内容复制到预先分配的内存中，最多复制 256 字节（包含结束符）
					apr_cpystrn(ied_usr->LD_info[cpuno - 1].LD_name, str, 256);
					
					//这里申请的空间基于ied的数量，挂载到ied上
					ied_usr->LD_info[cpuno - 1].ht_fcd = apr_hash_make(g_init_pool);	//这两行代码分别为 ied_usr->LD_info[cpuno - 1] 的两个成员（ht_fcd 和 ht_full_fcda）创建了空的哈希表。apr_hash_make(g_init_pool) 会在 g_init_pool 内存池中为这两个哈希表分配内存空间
					ied_usr->LD_info[cpuno - 1].ht_full_fcda = apr_hash_make(g_init_pool);//它们的 key 值和 value 在后续的代码中可能会被填充
					ied_usr->LD_info[cpuno - 1].rptcount = 0;//有效测点报告数为初始为0
					cout << "rptcount:" << ied_usr->LD_info[cpuno - 1].rptcount << endl;
					if (cpuno > ied->cpucount) {//测点号大于测点数
						ied->cpucount = cpuno;//取测点号，后续遍历测点时需要跳过无效测点
					}
				}						
            }
		}
//////////////////////////////////////////////////////////////////////////////////////////////////		

		if (count_real < count_cfg){//读取计数器和台账数量对比，如果读取的数量小于台账数量，说明有些台账读取失败了，按照读取的数量来设置进程的台账数量，避免后续访问读取失败的台账导致崩溃
			g_node->n_clients = count_real;
		}
			
		if (count_cfg != count_real){//应该相等，如果不相等说明有些台账读取失败了，按照读取的数量来设置进程的台账数量，避免后续访问读取失败的台账导致崩溃
			return APR_EBADF;
		}
			
		cout << "dev init create count:" << count_real;

		if (pqdif_rebuild_config_from_current_ledger() != APR_SUCCESS)
			return APR_EGENERAL;

		return APR_SUCCESS;
	}
	catch (...)
	{
		printf("\n device error \n");
		return APR_EBADF;
	}
}

int parse_model_web(QMap<QString, icd_model*>* icd_model_map,const std::vector<std::string>& codes)
{
	std::string parm = codes[0];	//装置型号列表，格式["型号1","型号2"...]

	char* ptr=NULL;

	SendJsonAPI_web(WEB_ICD,"",parm.c_str(),&ptr);

	// 检查 ptr 是否为 NULL，避免 std::string 初始化失败
	if (ptr == NULL || ptr[0] == '\0') {
		std::cout << "Error: Received NULL or empty response" << std::endl;

		//【修改点2】ptr 非空时要释放，避免泄漏
		if (ptr != NULL) {
			free(ptr);
			ptr = NULL;
		}
		return 1;
	}

	//调试用
	printf("ptr:%s\n",ptr);

	cJSON* root = cJSON_Parse(ptr);   //json格式序列化

	int retry = 0;

	if (root == NULL) {
		printf("web error %s\n", cJSON_GetErrorPtr());

		//重发多次
		while(root == NULL){
			// 在重试前释放之前的 ptr 以避免内存泄漏
            if (ptr != NULL) {
                free(ptr);
                ptr = NULL;
            }

			sleep(2);

			SendJsonAPI_web(WEB_ICD,"",parm.c_str(),&ptr);
			if(ptr == NULL || ptr[0] == '\0'){
				retry++;if(retry>3)break;
				continue;
			}
			root = cJSON_Parse(ptr);
			retry++;
			if(retry>3)break;
		}
		if (root == NULL) {
			if (ptr != NULL) {
				free(ptr);
				ptr = NULL;
			}
        	printf("web error %s\n", cJSON_GetErrorPtr());
        	return 1;
    	}
	}

	cJSON* codeItem = cJSON_GetObjectItem(root, "code");
	cJSON* msgItem = cJSON_GetObjectItem(root, "msg");

	// 使用 std::string 获取值
    std::string code = (codeItem != NULL && codeItem->valuestring != NULL) ? codeItem->valuestring : "not found";
    std::string msg = (msgItem != NULL && msgItem->valuestring != NULL) ? msgItem->valuestring : "not found";

    // 打印结果
    std::cout << "code: " << code << std::endl;
    std::cout << "msg: " << msg << std::endl;

    cJSON* data = cJSON_GetObjectItem(root, "data");
    if (data && data->type == cJSON_Array) {
        cJSON* item;
        cJSON_ArrayForEach(item, data) {
            icd_model* model = new icd_model();

            cJSON* id = cJSON_GetObjectItem(item, "id");//model_id
            if (id && id->type == cJSON_String) {
				strncpy(model->model_id, id->valuestring, sizeof(model->model_id) - 1);
				model->model_id[sizeof(model->model_id) - 1] = '\0';
			}
			
            cJSON* fileName = cJSON_GetObjectItem(item, "fileName");//file_name
            if (fileName && fileName->type == cJSON_String) {
                strncpy(model->file_name, fileName->valuestring, sizeof(model->file_name) - 1);
                model->file_name[sizeof(model->file_name) - 1] = '\0';
            }

            cJSON* filePath = cJSON_GetObjectItem(item, "filePath");//新增
            if (filePath && filePath->type == cJSON_String) {
                strncpy(model->file_path, filePath->valuestring, sizeof(model->file_path) - 1);
                model->file_path[sizeof(model->file_path) - 1] = '\0';
            }

            cJSON* devType = cJSON_GetObjectItem(item, "devType");//tmnl_type
            if (devType && devType->type == cJSON_String) {
                strncpy(model->tmnl_type, devType->valuestring, sizeof(model->tmnl_type) - 1);
                model->tmnl_type[sizeof(model->tmnl_type) - 1] = '\0';
            }

            cJSON* updateTime = cJSON_GetObjectItem(item, "updateTime");//timestamp
            if (updateTime && updateTime->type == cJSON_String) {
                strncpy(model->timestamp, updateTime->valuestring, sizeof(model->timestamp) - 1);
                model->timestamp[sizeof(model->timestamp) - 1] = '\0';
            }

            // 添加到 QMap
            if (model->model_id[0] != '\0') {
        	    icd_model_map->insert(model->model_id, model);
        	} else {
        	    delete model;
        	    model = NULL;
        	}
        }
    }
	else{
		std::cout << "Error: 'data' is not an array or is missing" << std::endl;
		cJSON_Delete(root);
        if (ptr != NULL) {
            free(ptr);
            ptr = NULL;
        }
        return 1;
	}

	if (cJSON_GetArraySize(data) == 0) {
	    std::cout << "Error: 'data' array is empty" << std::endl;
	    cJSON_Delete(root);
	    if (ptr != NULL) {
	        free(ptr);
	        ptr = NULL;
	    }
	    return 1;
	}

    cJSON_Delete(root);
	if (ptr != NULL) {
		free(ptr);
		ptr = NULL;
	}

	return 0; // 确保函数有返回值
}

void delete_icd_model_map(QMap<QString, icd_model*>& map) {	//lnk20250701防止内存泄漏
    QMap<QString, icd_model*>::iterator it;
    for (it = map.begin(); it != map.end(); ++it) {
        delete it.value();
    }
    map.clear();
}

int parse_model_cfg_web()
{
	std::vector<std::string> codes;//入参集合
	QMap<QString, icd_model*>  icd_model_map;
/////////////////////////////////////////////////////////////////////////
	//测试用
	//codes.push_back("code1=model1");	//填入终端型号列表

	//使用中端型号列表构建入参json字符串
	// 遍历前置所有监测点
	ied_t* ied;
	ied_usr_t* ied_usr;
	std::cout <<  "g_node->n_clients" <<  g_node->n_clients << std::endl;
    std::set<std::string> devTypes; // 用于去重的集合set

    for (int t = 0; t < g_node->n_clients; t++) {
        ied = (ied_t*)g_node->clients[t];
        ied_usr = (ied_usr_t*)ied->usr_ext;

        // 假设 dev_type 是一个字符串类型，加入集合 ,这里会去重
        if (strlen(ied_usr->dev_type) > 0) {
            devTypes.insert(std::string(ied_usr->dev_type));
        }
    }

    // 手动构建 JSON 字符串
    std::string input_jstr = "[";
    bool first = true; // 用于处理逗号

    for (std::set<std::string>::iterator it = devTypes.begin(); it != devTypes.end(); ++it) {
        if (!first) {
            input_jstr += ","; // 添加逗号
        }
        first = false; // 第一次之后设置为 false
        input_jstr += "\"" + *it + "\""; // 添加字符串
    }

    input_jstr += "]"; // 结束 JSON 数组
    std::cout << "input_jstr: " << input_jstr << std::endl; // 输出结果

	if(ICD_FLAG == "1"){
		codes.push_back(input_jstr);	//填入终端型号列表-获取指定的icd配置文件
	}
	else{
		codes.push_back("[]");	//不填-获取所有的icd配置文件
	}

/////////////////////////////////////////////////////////////////////////
	if(parse_model_web(&icd_model_map,codes)){
		DIY_ERRORLOG_CODE("process",0,LOG_CODE_ICD_AND_DOWNLOAD,"【ERROR】前置的%s%d号进程 icd模型接口异常,将使用默认的icd模型,请检查接口配置", get_front_msg_from_subdir(), g_front_seg_index);
		return APR_SUCCESS;	//可以使用默认的映射文件所以返回正常
	}

	codes.clear();

	try {
		char model_id[64];
		char tmnl_type[64];
		char file_name[128];
		char file_path[128];
		otl_datetime timestamp  = {};

		// 遍历终端台账容器
		QMap<QString, icd_model*>::iterator it;
    	for (it = icd_model_map.begin(); it != icd_model_map.end(); ++it) {
    		icd_model* value = it.value();

			// 确保 value 不为空
    		if (value != nullptr) {
        		// 找到容器中对应名称并取值
        		strncpy(model_id, value->model_id, sizeof(model_id) - 1);
        		strncpy(tmnl_type, value->tmnl_type, sizeof(tmnl_type) - 1);
        		strncpy(file_path, value->file_path, sizeof(file_path) - 1);
        		strncpy(file_name, value->file_name, sizeof(file_name) - 1);

				//lnk20260311
				model_id[sizeof(model_id) - 1] = '\0';
				tmnl_type[sizeof(tmnl_type) - 1] = '\0';
				file_path[sizeof(file_path) - 1] = '\0';
				file_name[sizeof(file_name) - 1] = '\0';

				std::cout << "model_id" << model_id << std::endl;
				std::cout << "tmnl_type" << tmnl_type << std::endl;
				std::cout << "filepath" << file_path << std::endl;
				std::cout << "filename" << file_name << std::endl;

				Set_xml_databaseinfo(model_id, tmnl_type, file_path, file_name, timestamp.year, timestamp.month, timestamp.day, timestamp.hour, timestamp.minute, timestamp.second);
    		}
		}
		delete_icd_model_map(icd_model_map);
		return APR_SUCCESS;
	}
	catch (...)
	{
		printf("\n icd model error\n");
		return APR_EBADF;
	}
}
////////////////////////////////////////////////////////////icd模型重构函数lnk20250116
char* parse_model_cfg_web_one(ied_t* ied, char* out_model)
{
	std::vector<std::string> codes;//入参集合
	QMap<QString, icd_model*>  icd_model_map;
	ied_usr_t* ied_usr;

    ied_usr = (ied_usr_t*)ied->usr_ext;

	if (strlen(ied_usr->dev_type) == 0) {
    	std::cerr << "parse_model_cfg_web_one warning: dev_type is empty" << std::endl;
    	return nullptr;
	}	

    // 手动构建 JSON 字符串
    std::string input_jstr = "[";
    input_jstr += "\"" + std::string(ied_usr->dev_type) + "\""; 
    input_jstr += "]"; // 结束 JSON 数组
    std::cout << "input_jstr: " << input_jstr << std::endl; // 输出结果

	codes.push_back(input_jstr);	//填入终端型号列表-获取指定的icd配置文件

	parse_model_web(&icd_model_map,codes);

	codes.clear();

	try {
		char model_id[64];
		char tmnl_type[64];
		char file_name[128];
		char file_path[128];
		otl_datetime timestamp;//不使用

		// 遍历终端台账容器
		QMap<QString, icd_model*>::iterator it;
    	for (it = icd_model_map.begin(); it != icd_model_map.end(); ++it) {
    		icd_model* value = it.value();

			// 确保 value 不为空
    		if (value != nullptr) {
        		// 找到容器中对应名称并取值
        		strncpy(model_id, value->model_id, sizeof(model_id) - 1);
				model_id[sizeof(model_id) - 1] = '\0';

        		strncpy(tmnl_type, value->tmnl_type, sizeof(tmnl_type) - 1);
        		strncpy(file_path, value->file_path, sizeof(file_path) - 1);
        		strncpy(file_name, value->file_name, sizeof(file_name) - 1);

				std::cout << "model_id:" << model_id << std::endl;
				std::cout << "tmnl_type:" << tmnl_type << std::endl;
				std::cout << "filepath:" << file_path << std::endl;
				std::cout << "filename:" << file_name << std::endl;

				Set_xml_databaseinfo(model_id, tmnl_type, file_path, file_name, timestamp.year, timestamp.month, timestamp.day, timestamp.hour, timestamp.minute, timestamp.second);
    		
				if (out_model != NULL) {
					strncpy(out_model, model_id, 64);
					out_model[63] = '\0';
				}
				break;//lnk20250701
			}
		}
		delete_icd_model_map(icd_model_map);//lnk20250701
		return NULL;
	}
	catch (...)
	{
		printf("\n icd model error\n");
		return NULL;
	}
}
////////////////////////////////////////////////////////////
//定时任务不需要定时日志清理和其他一些功能，统一放在这里修改
void OnTimerThread::run()
{
	msleep(10000);
	printf("OnTimerThread::run() is called ...... \n");

	bool account_update = true;
	bool asd = true;

	//当前时间获取
	apr_time_t	previousTime = apr_time_now();//
	apr_time_exp_t localTime;
	apr_time_exp_gmt(&localTime, previousTime);
	cout << "Local Time: "
		<< localTime.tm_year + 1900 << "-" << localTime.tm_mon + 1 << "-" << localTime.tm_mday << " "
		<< localTime.tm_hour << ":" << localTime.tm_min << ":" << localTime.tm_sec
		<< endl;

	//单联模式
	if (g_onlyIP[0] != 0)
	{
		printf("g_onlyIP[0]=!0 ontimer--%s--\n", g_onlyIP);
		add_comm_log(const_cast<char*>("g_onlyIP[0]=!0,g_onlyIP is --%s--", g_onlyIP));
	}
	else 
	{
		printf("g_onlyIP[0] == 0!");
	}

	int pgflag = 0;
	int pgmin = 0;
	int mp_num_hour = 0;

	//时间计数用
	int counter = 0;

	//初始化发送一次心跳
	send_heartbeat_to_kafka("1");

	while (1)
	{
		//进入线程时间
		previousTime = apr_time_now();
		apr_time_exp_gmt(&localTime, previousTime);

		//记录连接终端数
		if (strcmp(subdir, "cfg_stat_data") == 0 || strcmp(subdir, "cfg_newhis_data") == 0) {//台账更新,多节点,通讯
			
			//日志记录
			if (mp_num_hour != localTime.tm_hour) {
				pthread_mutex_lock(&mtx);     std::cout << "ontime hold lock !!!!!!!!!!!" << std::endl;
				std::string mp_num_str = "";
				mp_num_str.append("connected device count:");
				mp_num_str.append(QString::number(FRONT_MP_NUM).toStdString());//记录连接的检测点数
				mp_num_str.append(",g_node clients:");
				mp_num_str.append(QString::number(g_node->n_clients).toStdString());

				add_comm_log(const_cast<char*>(mp_num_str.c_str()));
				mp_num_hour = localTime.tm_hour;
				pthread_mutex_unlock(&mtx);   std::cout << "ontime free lock !!!!!!!!!!!" << std::endl;
			}

		}

		//添加日志开关控制lnk20250508
		update_log_entries_countdown();

		//添加进程心跳
		if (counter >= 30) {
        	send_heartbeat_to_kafka("1");  // 每30秒发送一次心跳
        	counter = 0;
    	}
		counter++;

		//清空计数器
		g_ontime_blocked_times =0;

		msleep(1000);
	}
	printf(">>>OnTimerThread::run() is end!!!\n");
}

//补招web处理函数
int recall_json_handle(const char* jstr)
{
	//不指定稳态/暂态则全部补招
	int stat = 0;	//都不补
	int voltage= 0;
	try{

		std::vector<RecallParam> recallParams;
		cJSON* json_root = cJSON_Parse(jstr);   //json格式序列化  //第一个参数
		if (json_root == NULL) {
			std::cout <<  "json root解析错误"<< std::endl;
			return 10000;
		}

		// 遍历数组的每个对象
    	if (json_root->type == cJSON_Array) {
        	for (cJSON* item = json_root->child; item != nullptr; item = item->next) {
        	    // 获取 monitorId 数组
        	    cJSON* monitorIdArray = cJSON_GetObjectItem(item, "monitorId");
        	    cJSON* timeIntervalArray = cJSON_GetObjectItem(item, "timeInterval");
				//判断dataType是否为空
				cJSON* datatype = cJSON_GetObjectItem(item, "dataType");

				//添加异常判断防止崩溃
				if(monitorIdArray == NULL || timeIntervalArray == NULL  || datatype == NULL ){
					std::cout <<  "json内容解析错误 "<< std::endl;
					return 10000;
				}

				if(strcmp(datatype->valuestring, "") != 0)//非空
				{
					stat = (strcmp(datatype->valuestring, "0") == 0) ? 1 : 0;//稳态
					voltage= (strcmp(datatype->valuestring, "1") == 0) ? 1 : 0;//暂态
				}
				else //空或其他
				{
					stat = 1;
					voltage= 1;
				}

				//调试用
				if(monitorIdArray != nullptr && monitorIdArray->child != nullptr)std::cout <<  "monitorIdArray的成员为"<< monitorIdArray->child->valuestring << std::endl;
				if(monitorIdArray != nullptr && monitorIdArray->child == nullptr)std::cout <<  "monitorIdArray没有成员"<< std::endl;

				//monitorIdArray有数据的情况
        	    if (monitorIdArray != nullptr && monitorIdArray->type == cJSON_Array && timeIntervalArray->type == cJSON_Array && monitorIdArray->child != nullptr) {
        	        // 遍历 monitorId 数组				
        	        for (cJSON* idItem = monitorIdArray->child; idItem != nullptr; idItem = idItem->next) {
        	            QString monitorId = QString(idItem->valuestring);

						//添加判断数组中当前监测点是否属于本进程20241230，防止出现非本进程的监测点触发补招
						int mppair = 0;
						ied_t* ied;
						ied_usr_t* ied_usr;
						for (int t = 0; t < g_node->n_clients; t++){
							ied = (ied_t*)g_node->clients[t];
							ied_usr = (ied_usr_t*)ied->usr_ext;
							for (int m = 0; m<10; m++){//最多10个

								if(strcmp(ied_usr->LD_info[m].mp_id,"") == 0)continue;//跳过空的

								if(strcmp(ied_usr->LD_info[m].mp_id,monitorId.toStdString().c_str()) == 0){//匹配上了

									DIY_INFOLOG_CODE("process",0,LOG_CODE_RECALL,"【NORMAL】前置的%s%d号进程处理监测点%s -id:%s 的数据补招",get_front_msg_from_subdir(), g_front_seg_index,ied_usr->LD_info[m].name,ied_usr->LD_info[m].mp_id);
									mppair = 1;
									break;//找到就退出监测点循环
								}
							
							}
							if(mppair == 1)break;//找到就退出终端循环
						}
						if(mppair == 0)continue;//当前进程的监测点都没有找到这个检测点号，说明不是本进程的监测点，处理数组的下一个监测点
						//调试用
						std::cout << "find mpid:" << monitorId.toStdString() << "in this process,mppair=" << mppair <<std::endl;

        	            // 遍历 timeInterval 数组
        	            for (cJSON* timeItem = timeIntervalArray->child; timeItem != nullptr; timeItem = timeItem->next) {
        	                QString timeInterval = QString(timeItem->valuestring);
        	                QString start = timeInterval.left(timeInterval.indexOf("~"));
        	                QString end = timeInterval.mid(timeInterval.indexOf("~") + 1);

        	                // 创建 RecallParam 对象并添加到列表中

        	                RecallParam param;
        	                param.mp_id = monitorId;
        	                param.start = start;
        	                param.end = end;
        	                param.stat = stat; 
							param.voltage = voltage;
        	                recallParams.push_back(param);
        	            }
        	        }
        	    }
				//monitorIdArray为空的情况
				else if (monitorIdArray != nullptr && monitorIdArray->type == cJSON_Array && monitorIdArray->child == nullptr) {
    				// monitorIdArray 为空
    				std::cout << "monitorIdArray is null" << std::endl;
					//所有监测点补招
					// 遍历前置所有监测点
					ied_t* ied;
					ied_usr_t* ied_usr;
					std::cout <<  "g_node->n_clients" <<  g_node->n_clients << std::endl;
        	        for (int t = 0; t < g_node->n_clients; t++){
						ied = (ied_t*)g_node->clients[t];
						ied_usr = (ied_usr_t*)ied->usr_ext;
						for (int m = 0; m<10; m++){

							if(strcmp(ied_usr->LD_info[m].mp_id,"") == 0)continue;
							std::cout <<  "ied_usr->LD_info[m].mp_id" <<  m  << "   "<<  ied_usr->LD_info[m].mp_id << std::endl;
        	            	QString monitorId = QString(ied_usr->LD_info[m].mp_id);

        	            	// 遍历 timeInterval 数组
        	            	for (cJSON* timeItem = timeIntervalArray->child; timeItem != nullptr; timeItem = timeItem->next) {
        	            	    QString timeInterval = QString(timeItem->valuestring);
        	            	    QString start = timeInterval.left(timeInterval.indexOf("~"));
        	            	    QString end = timeInterval.mid(timeInterval.indexOf("~") + 1);

        	            	    // 创建 RecallParam 对象并添加到列表中
        	            	    RecallParam param;
        	            	    param.mp_id = monitorId;
        	            	    param.start = start;
								param.end = end;
        	                	param.stat = stat; 
								param.voltage = voltage;
        	            	    recallParams.push_back(param);
        	            	}
						}
        	        }
				}
				else{
					std::cout << "monitorIdArray 不存在或类型不正确" << std::endl;
				}
        	}
		}
		cJSON_Delete(json_root);	//web入参处理结束
		//遍历容器取出所有补招
		for (std::vector<RecallParam>::iterator it = recallParams.begin(); it != recallParams.end(); ++it) {

			QList<RecallInfo> recallinfo_list_hour;
			char start_time[64];
			char end_time[64];
			QString mp_id;

			mp_id = it->mp_id;
			apr_snprintf(start_time, sizeof(start_time), "%s", it->start.toStdString().c_str());//start_time
			apr_snprintf(end_time, sizeof(end_time), "%s", it->end.toStdString().c_str());//end_time

			qDebug() << "mp_id" << mp_id << "  " <<  "start_time" << start_time << "  " << "end_time" << "  " << "stat" << it->stat << "  " << "voltage" << it->voltage<< end_time;
				
			Get_Recall_Time_Char(start_time, end_time, recallinfo_list_hour);
			
			for (int j = 0; j < recallinfo_list_hour.size(); j++) {
				CJournalRecall jr;
				jr.MonitorID = mp_id;
				jr.StartTime = QDateTime::fromTime_t(recallinfo_list_hour[j].starttime).toString("yyyy-MM-dd hh:mm:ss");
				jr.EndTime = QDateTime::fromTime_t(recallinfo_list_hour[j].endtime).toString("yyyy-MM-dd hh:mm:ss");

				//现在暂态稳态根据web获取
				//jr.STEADY = QString::number(1, 10); // 将整数 1 转换为字符串并赋值
				//jr.VOLTAGE = QString::number(1, 10);// 将整数 1 转换为字符串并赋值
				//调试用
				//std::cout << "stat" << it->stat << "voltage" << it->voltage << std::endl;
				jr.STEADY = QString::number(it->stat);
				jr.VOLTAGE = QString::number(it->voltage);

				g_StatisticLackList_list_mutex.lock();
				g_StatisticLackList.push_back(jr);
				g_StatisticLackList_list_mutex.unlock();
			}				
		}		
	}
	catch (exception& e)
	{
		printf("处理客户端发送的消息错误，原因：%s\n", e.what());
		return 10004;
	}

	return 000000;
}

int rtdata_http(const char* jstr)
{

}

// 声明外部函数，http功能用库链接，单独编译
#ifdef __cplusplus
extern "C" {
#endif

void httprun();
const char* getReceivedData(int fun);
void threadmsgweb(int fun);
bool threadmsghttp(int fun);

#ifdef __cplusplus
}
#endif


void WebhttpThread::run()
{
	int ret = 1;
	printf("WebhttpThread::run() is called ...... \n");

	while(1){

		//补招进程
		if (!threadmsghttp(1) && g_node_id == RECALL_HIS_DATA_BASE_NODE_ID) {//http处理一条消息,状态变为false，不再处理消息（http此时可能有消息来但是不处理）后这个线程开始读取数据

			const char* data = getReceivedData(1);//从http中取数据,指针

			std::cout << "recall data cfg:" << data <<std::endl;

			pthread_mutex_lock(&mtx);    std::cout << "recall http hold lock !!!!!!!!!!!" << std::endl;
			ret = recall_json_handle(data);//处理补招指令
			pthread_mutex_unlock(&mtx);  std::cout << "recall http free lock !!!!!!!!!!!" << std::endl;

			
			threadmsgweb(1);//读取数据后开启http消息处理，此时有消息来才会处理，处理完状态变更等下一次循环
    	}
		//3s数据进程
		if (!threadmsghttp(2) && g_node_id == THREE_SECS_DATA_BASE_NODE_ID) {//http处理一条消息,状态变为false，不再处理消息（http此时可能有消息来但是不处理）后这个线程开始读取数据

			const char* data = getReceivedData(2);//从http中取数据,指针

			std::cout << "rt data cfg:" << data <<std::endl;

			ret = rtdata_http(data);//处理补招指令

			threadmsgweb(2);//读取数据后开启http消息处理，此时有消息来才会处理，处理完状态变更等下一次循环
    	}
	}
	printf("WebhttpThread::run() is end ...... \n");
}

void httpThread::run()
{
	printf("httpThread::run() is called ...... \n");
	std::cout << "Server started at" << HTTP_IP << ":" << HTTP_PORT << std::endl;
	httprun();	//开始运行http监听所有ip，端口10004

	printf("httpThread::run() is end ...... \n");
}

// Base64 编码函数，将输入的字符串编码为 Base64 格式 //实现json中的文件流
std::string base64_encode(const std::string& in) {
    // Base64 字符集，由 64 个字符组成
    static const std::string base64_chars = 
        "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
        "abcdefghijklmnopqrstuvwxyz"
        "0123456789+/";

    std::string out;  // 输出字符串
    int val = 0;      // 当前值
    int valb = -6;    // 值的位偏移量，初始为 -6

    // 遍历输入字符串的每一个字符
    for (std::string::const_iterator it = in.begin(); it != in.end(); ++it) {
        // 将字符的 ASCII 值左移 8 位并加到 val 中
        val = (val << 8) + *it;
        valb += 8;  // 更新位偏移量，每个字符占 8 位

        // 当 valb 大于等于 0 时，说明有足够的位数可以进行编码
        while (valb >= 0) {
            // 将 val 的高 6 位转化为 Base64 字符，并添加到输出中
            out.push_back(base64_chars[(val >> valb) & 0x3F]);
            valb -= 6;  // 每次编码后，位偏移量减少 6 位
        }
    }

    // 如果还有剩余的位数，补充 '=' 字符
    while (valb > -6) {
        out.push_back('=');
        valb -= 6;  // 每次添加一个 '='，位偏移量减少 6 位
    }
    
    return out;  // 返回编码后的字符串
}

void handleUploadResponse(const std::string& response,const std::string& localPath, char* wavepath, int type) {
    
	// 解析 JSON 响应
    cJSON* json_data = cJSON_Parse(response.c_str());
    if (json_data == nullptr) {
        std::cerr << "Error parsing response: " << cJSON_GetErrorPtr() << std::endl;
		DIY_ERRORLOG_CODE("process",0,LOG_CODE_TRANSIENT_COMM,"【ERROR】前置上传文件失败,web返回的消息错误，无法解析");
        return;
    }

    // 提取字段
    cJSON* codeItem = cJSON_GetObjectItem(json_data, "code");
    cJSON* msgItem = cJSON_GetObjectItem(json_data, "msg");
    cJSON* dataItem = cJSON_GetObjectItem(json_data, "data");

    if (codeItem && dataItem) {
        std::string code = codeItem->valuestring;
		std::cout << "Response Code: " << code << std::endl;
        std::string msg = (msgItem != NULL) ? msgItem->valuestring : "not found";
		std::cout << "Message: " << msg << std::endl;

        cJSON* nameItem = cJSON_GetObjectItem(dataItem, "name");
        cJSON* fileNameItem = cJSON_GetObjectItem(dataItem, "fileName");
        cJSON* urlItem = cJSON_GetObjectItem(dataItem, "url");

        if (nameItem && fileNameItem && urlItem) {
            std::string name = nameItem->valuestring;
            std::string fileName = fileNameItem->valuestring;
            std::string url = urlItem->valuestring;

            // 输出信息
            std::cout << "File Path: " << name << std::endl;
			std::cout << "Uploaded File Name: " << fileName << std::endl;
            std::cout << "File URL: " << url << std::endl;

			// 找到最后一个 '.'
			size_t pos = fileName.find_last_of('.');
			std::string nameWithoutExt;
		
			if (pos != std::string::npos) {
				// 截取去掉后缀的部分
				nameWithoutExt = fileName.substr(0, pos);
			} else {
				// 如果没有后缀，直接使用原文件名
				nameWithoutExt = fileName;
			}
		
			// 拷贝到 wavepath
			if (type == 1) {
			    strcpy(wavepath, nameWithoutExt.c_str());
			}
			else {
			    strcpy(wavepath, name.c_str());
			}

			std::cout << "wavepath: " << wavepath << std::endl;

			DIY_INFOLOG_CODE("process",0,LOG_CODE_TRANSIENT_COMM,"【NORMAL】前置上传文件成功,远端文件名:%s",wavepath);

			// =========================
            // 上传成功后删除本地文件
            // =========================
            if (remove(localPath.c_str()) == 0)
            {
                std::cout << "Delete local file success: "
                          << localPath << std::endl;
                DIY_INFOLOG_CODE("process",0,
                    LOG_CODE_TRANSIENT_COMM,
                    "【NORMAL】删除本地文件成功:%s",
                    localPath.c_str());
            }
            else
            {
                std::cout << "Delete local file failed: "
                          << localPath
                          << " errno=" << errno
                          << " err=" << strerror(errno)
                          << std::endl;
                DIY_ERRORLOG_CODE("process",0,
                    LOG_CODE_TRANSIENT_COMM,
                    "【ERROR】删除本地文件失败:%s errno=%d err=%s",
                    localPath.c_str(),
                    errno,
                    strerror(errno));
            }
        }
    } else {
        std::cerr << "Error: Missing expected fields in JSON response." << std::endl;
		DIY_ERRORLOG_CODE("process",0,LOG_CODE_TRANSIENT_COMM,"【ERROR】前置上传文件失败,web返回的消息没有远端文件名");
    }

    // 释放 JSON 对象
    cJSON_Delete(json_data);
}
//这是json结构发送的方式
/*void SendFileWeb(const std::string& strUrl, const char* localpath, const char* cloudpath, char* wavepath) {
    // 从本地路径读取文件内容
    std::ifstream file(localpath, std::ios::binary);
    if (!file) {
        std::cerr << "Failed to open file: " << localpath << std::endl;
        return;
    }
    
    std::ostringstream ss;
    ss << file.rdbuf();	
    std::string fileContent = ss.str();//文件流
    std::string encodedFile = base64_encode(fileContent);//文件流编码

    // 创建 JSON 对象 实现入参要求

    cJSON* json_root = cJSON_CreateObject();
    cJSON_AddItemToObject(json_root, "multipartFile", cJSON_CreateString(encodedFile.c_str()));
    cJSON_AddItemToObject(json_root, "path", cJSON_CreateString(cloudpath));//远端路径
    cJSON_AddItemToObject(json_root, "isReserveName", cJSON_CreateBool(true)); // 布尔值

    char* szjson = cJSON_Print(json_root);
    std::cout << ">>> json: " << szjson << std::endl;

    // curl 初始化
    CURL* curl = curl_easy_init();
    if (curl) {
        // 设置请求为 POST 请求
        curl_easy_setopt(curl, CURLOPT_POST, 1);
        curl_easy_setopt(curl, CURLOPT_URL, strUrl.c_str());
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, szjson);
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, curl_slist_append(NULL, "Content-Type: application/json"));

        // 设置超时时间
        curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10);
        curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10);

        std::string resPost0;
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)&resPost0);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, req_reply_web); // 设置数据接收和写入函数

        // 执行请求
        CURLcode res = curl_easy_perform(curl);
        if (res != CURLE_OK) {
            std::cerr << "lnk web failed: " << curl_easy_strerror(res) << std::endl;
        } else {
            std::cout << "lnk web success, response: " << resPost0 << std::endl;
			 handleUploadResponse(resPost0,wavepath); // 处理响应
        }

        // 清理
        free(szjson);
    } else {
        std::cerr << ">>> curl init failed" << std::endl;
    }
    curl_easy_cleanup(curl);
    cJSON_Delete(json_root);
}*/

//这是dataform发送方式
void SendFileWeb(const std::string& strUrl, const char* localpath, const char* cloudpath, char* wavepath,int type) {
    // 初始化 curl
    CURL* curl = curl_easy_init();
    if (curl) {
        // 设置请求 URL 和 POST 请求
        curl_easy_setopt(curl, CURLOPT_URL, strUrl.c_str());
        curl_easy_setopt(curl, CURLOPT_POST, 1);

        // 创建表单数据
        curl_httppost* formpost = nullptr;
        curl_httppost* lastptr = nullptr;

        // 添加文件字段，直接从本地路径读取文件内容
        curl_formadd(&formpost, &lastptr,
                     CURLFORM_COPYNAME, "file",
                     CURLFORM_FILE, localpath,
                     CURLFORM_END);

        // 添加 `path` 字段
        curl_formadd(&formpost, &lastptr,
                     CURLFORM_COPYNAME, "path",
                     CURLFORM_COPYCONTENTS, cloudpath,
                     CURLFORM_END);

        // 添加 `isReserveName` 字段
        curl_formadd(&formpost, &lastptr,
                     CURLFORM_COPYNAME, "isReserveName",
                     CURLFORM_COPYCONTENTS, "true",
                     CURLFORM_END);

        // 设置表单数据到请求
        curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);

        // 设置头信息
        struct curl_slist* header_list = nullptr;
        header_list = curl_slist_append(header_list, "Content-Type: multipart/form-data");
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header_list);

        // 设置超时时间
        curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10);
        curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10);

        // 设置写入响应数据的函数
        std::string resPost0;
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)&resPost0);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, req_reply_web);

        // 执行请求
        CURLcode res = curl_easy_perform(curl);
        if (res != CURLE_OK) {
            std::cerr << "http web failed: " << curl_easy_strerror(res) << std::endl;
			DIY_ERRORLOG_CODE("process",0,LOG_CODE_TRANSIENT_COMM,"【ERROR】前置上传暂态录波文件 %s 失败，请检查文件上传接口配置",localpath);
        } else {
            std::cout << "http web success, response: " << resPost0 << std::endl;
            handleUploadResponse(resPost0, localpath, wavepath, type);  // 处理响应
        }

        // 清理
        curl_formfree(formpost);  // 释放表单数据
        curl_slist_free_all(header_list);  // 释放头部列表
        curl_easy_cleanup(curl);
    } else {
        std::cerr << ">>> curl init failed" << std::endl;
    }
}

void SOEFileWeb(char* localpath,char* cloudpath, char* wavepath)
{
	//示例ip，更换为实际ip即可
	SendFileWeb(WEB_FILEUPLOAD,localpath,cloudpath,wavepath,1);
}

void SOEFileWeb_test()
{
	char localpath[128] = {"/FeProject/comtrade/his/PQMonitor_PQM1_000420_20250310_151030_923.cfg"};
	char cloudpath[128] = {"/comtrade/192.168.1.105/"};
	char wavepath[128] = {""};
	SOEFileWeb(localpath,cloudpath,wavepath);
	std::cout << "wavepath:" << wavepath << std::endl;
}
////////////////////////////////////////////////////////////////////////////////////////////////////lnk20260310
static size_t write_file_callback(void* ptr, size_t size, size_t nmemb, void* stream)
{
    FILE* fp = (FILE*)stream;
    return fwrite(ptr, size, nmemb, fp);
}
/* 
 * 从 WEB_FILEDOWNLOAD 下载 path 对应的文件到 localpath
 * 成功返回 0，失败返回 -1
 */
int DownloadFileWeb(const std::string& strUrl,
                    const char* remotePath,
                    const char* localpath)
{
    if (remotePath == NULL || localpath == NULL)
        return -1;

    CURL* curl = curl_easy_init();
    if (!curl)
    {
        std::cerr << "curl init failed" << std::endl;
        return -1;
    }

    FILE* fp = fopen(localpath, "wb");
    if (fp == NULL)
    {
        std::cerr << "open local file failed: " << localpath << std::endl;
        curl_easy_cleanup(curl);
        return -1;
    }

    char* encodedPath = curl_easy_escape(curl, remotePath, 0);
    if (encodedPath == NULL)
    {
        fclose(fp);
        curl_easy_cleanup(curl);
        return -1;
    }

    std::string fullUrl = strUrl;
    if (fullUrl.find('?') == std::string::npos)
        fullUrl += "?filePath=";
    else
        fullUrl += "&filePath=";
    fullUrl += encodedPath;

    curl_easy_setopt(curl, CURLOPT_URL, fullUrl.c_str());
    curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L);
    curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10L);
    curl_easy_setopt(curl, CURLOPT_TIMEOUT, 60L);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_file_callback);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);

    CURLcode res = curl_easy_perform(curl);

    long http_code = 0;
    curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);

    curl_free(encodedPath);
    fclose(fp);
    curl_easy_cleanup(curl);

    if (res != CURLE_OK)
    {
        std::cerr << "DownloadFileWeb failed: " << curl_easy_strerror(res) << std::endl;
		remove(localpath);
        return -1;
    }

    if (http_code != 200)
    {
        std::cerr << "DownloadFileWeb http code: " << http_code << std::endl;
		remove(localpath);
        return -1;
    }

	if (chmod(localpath, 0777) != 0)
	{
	    std::cerr << "chmod 777 failed: " << localpath << std::endl;
	}
	else
	{
	    std::cout << "chmod 777 success: " << localpath << std::endl;
	}

    return 0;
}

/*/////////////////////////////////////////////////////////lnk10-24根据web接口修改/////////////////////////////////////////////////////////////*/
/*封装C可调用的台账更新函数 */////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

int update_one_terminal_ledger(terminal* update, int i,ied_t* ied,int terminal_index,int ied_take)			
{
	chnl_usr_t* chnl_usr = NULL;
	ied_usr_t* ied_usr = NULL;
	ied_usr = (ied_usr_t*)ied->usr_ext;

	// 将 update[i] 中的数据写入到 ied_usr 中
	if (strlen(update[i].terminal_id) != 0) {
	    apr_snprintf(ied_usr->terminal_id, sizeof(ied_usr->terminal_id), "%s", update[i].terminal_id);
	    printf("ied_usr->terminal_id: %s\n", ied_usr->terminal_id);
	}
	if (update[i].terminal_code != NULL) {
	    apr_snprintf(ied_usr->terminal_code, sizeof(ied_usr->terminal_code), "%s", update[i].terminal_code);
	    printf("ied_usr->terminal_code: %s\n", ied_usr->terminal_code);
	}
	if (update[i].tmnl_factory != NULL) {
	    apr_snprintf(ied_usr->tmnl_factory, sizeof(ied_usr->tmnl_factory), "%s", update[i].tmnl_factory);
	    printf("ied_usr->tmnl_factory: %s\n", ied_usr->tmnl_factory);
	}
	if (update[i].tmnl_status != NULL) {
	    apr_snprintf(ied_usr->tmnl_status, sizeof(ied_usr->tmnl_status), "%s", update[i].tmnl_status);
	    printf("ied_usr->tmnl_status: %s\n", ied_usr->tmnl_status);
	}
	if (update[i].dev_type != NULL) {
	    apr_snprintf(ied_usr->dev_type, sizeof(ied_usr->dev_type), "%s", update[i].dev_type);
	    printf("ied_usr->dev_type: %s\n", ied_usr->dev_type);
	}
	if (update[i].com_type != NULL && update[i].com_type[0] != '\0') {
	    apr_snprintf(ied_usr->com_type, sizeof(ied_usr->com_type), "%s", update[i].com_type);
	    printf("ied_usr->com_type: %s\n", ied_usr->com_type);
	} else {
	    apr_snprintf(ied_usr->com_type, sizeof(ied_usr->com_type), "%s", "MMS");
	    printf("ied_usr->com_type (default): %s\n", ied_usr->com_type);
	}
	if (update[i].processNo != NULL) {
	    apr_snprintf(ied_usr->processNo, sizeof(ied_usr->processNo), "%s", update[i].processNo);
	    printf("ied_usr->processNo: %s\n", ied_usr->processNo);
	}

	//log_level拷贝到ied_usr中
	if (update[i].log_level >= 0 && update[i].log_level <= 3) {
	    ied_usr->log_level = update[i].log_level;
	    printf("ied_usr->log_level: %d\n", ied_usr->log_level);
	} else {
	    ied_usr->log_level = 1;  // 默认为1，表示warn级别
	    printf("ied_usr->log_level (default): %d\n", ied_usr->log_level);
	}

	if (update[i].dev_series != NULL) {
	    apr_snprintf(ied_usr->dev_series, sizeof(ied_usr->dev_series), "%s", update[i].dev_series);
	    printf("ied_usr->dev_series: %s\n", ied_usr->dev_series);
	} else {
	    apr_snprintf(ied_usr->dev_series, sizeof(ied_usr->dev_series), "%s", "");  // 默认为空字符串
	    printf("ied_usr->dev_series (default): %s\n", ied_usr->dev_series);
	}
	if (update[i].dev_key != NULL) {
	    apr_snprintf(ied_usr->dev_key, sizeof(ied_usr->dev_key), "%s", update[i].dev_key);
	    printf("ied_usr->dev_key: %s\n", ied_usr->dev_key);
	} else {
	    apr_snprintf(ied_usr->dev_key, sizeof(ied_usr->dev_key), "%s", "");  // 默认为空字符串
	    printf("ied_usr->dev_key (default): %s\n", ied_usr->dev_key);
	}

	ied_usr->dev_idx = terminal_index;        //终端排号，g_node下标
	printf("dev_idx: %d\n", ied_usr->dev_idx);

	ied->channel[0].channel_type = CHANNEL_TYPE_IPV4;  // channel
	ied->channel[0].addr_str[LONGNAME - 1] = 0;  // DEV_IP
	if (update[i].addr_str != NULL) {
	    ied->channel[0].addr = ntohl(inet_addr(update[i].addr_str));  // DEV_IP
	    strncpy(ied->channel[0].addr_str, update[i].addr_str, LONGNAME - 1);  // DEV_IP
		ied->channel[0].addr_str[LONGNAME-1] = '\0';
	    printf("ied_usr->addr_str: %s\n", ied->channel[0].addr_str);
	} else {
	    ied->channel[0].addr = ntohl(inet_addr("0.0.0.0"));  // DEV_IP
	    strncpy(ied->channel[0].addr_str, "0.0.0.0", LONGNAME - 1);  // DEV_IP
		ied->channel[0].addr_str[LONGNAME-1] = '\0';
	    printf("ied_usr->addr_str: %s\n", ied->channel[0].addr_str);
	}
	if (update[i].port != NULL) {
	    int port = 102;
	    if (stringToInt(update[i].port, &port)) {
	        // 转换成功，portStr全为数字，并且已经转换为int类型的port  
	        ied->channel[0].port = port;  // DEV_PortID
	        printf("ied_usr->port: %d\n", ied->channel[0].port);  // DEV_PortID
	    } else {
	        ied->channel[0].port = 102;  // DEV_PortID
	        printf("ied_usr->port: %s, 非合法端口. 使用默认端口: %d\n", update[i].port, ied->channel[0].port);  // DEV_PortID
	    }
	}

	if (update[i].timestamp != NULL && strlen(update[i].timestamp) > 0) {
	    // 构造struct tm对象
	    struct tm timeinfo = {0};  // 初始化为0
	    // 假设时间字符串格式为 "YYYY-MM-DD HH:MM:SS"
	    // 使用sscanf从字符串中提取各个时间字段
	    if (sscanf(update[i].timestamp, "%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) {
	        // 将年份从1900开始计算
	        timeinfo.tm_year -= 1900;
	        // 月份从0开始计算，减去1
	        timeinfo.tm_mon -= 1;
	        // 对tm_isdst进行初始化，通常可以设置为-1，由mktime自动判断
	        timeinfo.tm_isdst = -1;
	        // 使用mktime将struct tm转换为time_t
	        time_t raw_time = mktime(&timeinfo);
	        // 判断mktime是否成功
	        if (raw_time != -1) {
	            ied_usr->time = (long long)raw_time;
	            printf("ied_usr->time: %lld\n", ied_usr->time);
	        } else {
	            printf("Error: mktime failed.\n");
	        }
	    } else {
	        printf("Error: sscanf failed. Invalid timestamp format.\n");
	    }
	}

	//如果是使用已有空间则不需要再申请
	if(!ied_take){
		chnl_usr = (chnl_usr_t*)apr_pcalloc(g_init_pool, sizeof(chnl_usr_t));
		ied->channel[0].connect = chnl_usr;
		g_pt61850app->chnl_counts++;	//新增的需要添加,假设一直添加失败，这里会一直递增到最大终端数，后续不再添加
	}
	else{
		chnl_usr = (chnl_usr_t*)ied->channel[0].connect;
	}

	chnl_usr->chnl = &(ied->channel[0]);//如果是已有的ied，这个值是原本就存在的，再赋值一次
	chnl_usr->chnl_id = 0;
	chnl_usr->m_state = CHANNEL_DISCONNECTED;
	chnl_usr->m_ClosedMsTime = NEXT_CONNECT_TIME * (-1);

	//lnk20260512
	init_oper_type_cache(ied_usr);

	// 将 monitorData 中的数据写入到 LD_info 中
	int count_real_monitor = 0;	//遍历监测点台账的计数器
	int j;
	for (j = 0; j < 10 && update[i].line[j].monitor_id[0] != '\0'; ++j) {
	    monitor monitor_data = update[i].line[j];
		//监测点计数
		count_real_monitor++;
	    // 初始化用于值拷贝的 LD_info，里面涉及指针的部分都为空
	    LD_info_t line_info;
	    memset(&line_info, 0, sizeof(line_info));

		char logical_device_seq[64];
	    // 填充监测点信息
	    strncpy(line_info.mp_id, monitor_data.monitor_id, sizeof(line_info.mp_id) - 1);
	    strncpy(line_info.name, monitor_data.monitor_name, sizeof(line_info.name) - 1);	    
	    strncpy(line_info.voltage_level, monitor_data.voltage_level, sizeof(line_info.voltage_level) - 1);
	    strncpy(line_info.v_wiring_type, monitor_data.terminal_connect, sizeof(line_info.v_wiring_type) - 1);
	    strncpy(line_info.monitor_status, monitor_data.status, sizeof(line_info.monitor_status) - 1);
		strncpy(line_info.terminal_code, monitor_data.terminal_code, sizeof(line_info.terminal_code) - 1);
		strncpy(logical_device_seq, monitor_data.logical_device_seq, sizeof(logical_device_seq) - 1);

		//log_level拷贝到line_info中
		if (monitor_data.log_level >= 0 && monitor_data.log_level <= 3) {	//优先使用监测点的log_level
		    line_info.log_level = monitor_data.log_level;
		    printf("line_info.log_level (monitor): %d\n", line_info.log_level);
		} else if (ied_usr->log_level >= 0 && ied_usr->log_level <= 3) {	//继承终端的log_level
		    line_info.log_level = ied_usr->log_level;
		    printf("line_info.log_level (inherit terminal): %d\n", line_info.log_level);
		} else {
		    line_info.log_level = 1;
		    printf("line_info.log_level (default): %d\n", line_info.log_level);
		}

		if (isCharPtrEmpty(logical_device_seq)) {
		    line_info.cpuno = 1;  // 默认监测点实例号1
		    printf("logical_device_seq: is null, set cpuno: %d\n", (int)line_info.cpuno);
		} else {
		    line_info.cpuno = atoi(logical_device_seq);
		    printf("logical_device_seq: %d\n", (int)line_info.cpuno);
		}
		line_info.line_id = count_real_monitor;  // 记录终端排号

		printf("line_id: %d\n", line_info.line_id);
		printf("mp_id: %s\n", line_info.mp_id);
		printf("terminal_code: %s\n", line_info.terminal_code);
		printf("voltage_level: %s\n", line_info.voltage_level);
		printf("v_wiring_type: %s\n", line_info.v_wiring_type);
		printf("monitor_status: %s\n", line_info.monitor_status);
		printf("name: %s\n", line_info.name);
		printf("log_level: %d\n", line_info.log_level);

		//lnk20250214角形
		if (strcmp(line_info.v_wiring_type, "0") != 0)
		{
			isdelta_flag = 1;	//存在一个监测点为角型接线则这个前置就要启动第二个配置列表
			cout << "monitor_id" << line_info.mp_id << "v_wiring_type:" << line_info.v_wiring_type << "is delta wiring:" << isdelta_flag << endl;
		}

	    // 填充时间戳
	    if (update[i].timestamp[0] != '\0') {
		    struct tm timeinfo;
		    char timestamp[64];
		    // 假设update[i].timestamp格式为 "YYYY-MM-DD HH:MM:SS"
		    // 例如："2023-01-14 12:34:56"
		    sscanf(update[i].timestamp, "%4d-%2d-%2d %2d:%2d:%2d",
		           &timeinfo.tm_year, &timeinfo.tm_mon, &timeinfo.tm_mday,
		           &timeinfo.tm_hour, &timeinfo.tm_min, &timeinfo.tm_sec);
		    // 将年份从1900年开始计算
		    timeinfo.tm_year -= 1900;
		    // 月份从0开始，减1
		    timeinfo.tm_mon -= 1;
		    // 对tm_isdst进行初始化，通常可以设置为-1，由mktime自动判断
		    timeinfo.tm_isdst = -1;
		    // 使用mktime将struct tm转换为time_t
		    time_t raw_time = mktime(&timeinfo);
		    // 判断mktime是否成功
		    if (raw_time != -1) {
		        line_info.time = (long long)raw_time;
		        printf("time: %lld\n", line_info.time);
		    } else {
		        printf("Error: mktime failed.\n");
				return -1;
		    }
		}				
		line_info.read_flag = 1;				//监测点有效
	    // 填充 LD_info
		if (ied && ied->usr_ext && line_info.cpuno && ((int)line_info.cpuno < 10)) {
		    char str[256];
		    byte_t cpuno = line_info.cpuno;	//使用新台账的逻辑序列号，在使用原有ied的情况下，如果序列号数量小于原有数量，那么新的部分会被覆盖，如果删除时没有清理，原有的其他部分会仍存在内存中，但不会被使用
		    printf("cpuno: %d\n", (int)line_info.cpuno);
		    printf("index cpuno: %d\n", cpuno - 1);

		    ied_usr = (ied_usr_t*)ied->usr_ext;
		    //ied_usr->LD_info[cpuno - 1] = line_info;//这个放在后面，因为需要先判断原有的指针
		    //ied_usr->LD_info[cpuno - 1].ied = ied;  //这个放在后面，因为需要先判断原有的指针

			//用来记录可能的已有的ldname
			char *ldname = NULL;

			//用来记录可能已有的报告、日志数组
			loginfo_t			**loginfo = NULL;
			rptinfo_t			**rptinfo = NULL;

			//调试
			//printf("check error111 !!!!!!!!!!!!!!\n");

			

/////////			//清空已有的补招数组占用空间
			if (ied_usr->LD_info[cpuno - 1].autorecallcount != 0) {
				for (int j = 0; j < ied_usr->LD_info[cpuno - 1].autorecallcount; j++) {
					if(NULL != ied_usr->LD_info[cpuno - 1].autorecall[j]){
						delete ied_usr->LD_info[cpuno - 1].autorecall[j];
					}					
				}
				if(NULL != ied_usr->LD_info[cpuno - 1].autorecall){
					delete ied_usr->LD_info[cpuno - 1].autorecall;
				}
				
				ied_usr->LD_info[cpuno - 1].autorecallcount = 0;
			}

			//调试
			//printf("check error112 !!!!!!!!!!!!!!\n");

////////			//记录原有的报告和日志数组
			loginfo = ied_usr->LD_info[cpuno - 1].loginfo?ied_usr->LD_info[cpuno - 1].loginfo:NULL;
			rptinfo = ied_usr->LD_info[cpuno - 1].rptinfo?ied_usr->LD_info[cpuno - 1].rptinfo:NULL;

			//调试
			//printf("check error113 !!!!!!!!!!!!!!\n");

    		//避免重复分配内存，如果使用已存在的ied，那么它之前初始化就分配了哈希表，如果在删除台账时没有清除，则需要将原来的清除
    		if (ied_usr->LD_info[cpuno - 1].ht_fcd != NULL) {
    		    apr_hash_clear(ied_usr->LD_info[cpuno - 1].ht_fcd);
    		}
    		if (ied_usr->LD_info[cpuno - 1].ht_full_fcda != NULL) {
    		    apr_hash_clear(ied_usr->LD_info[cpuno - 1].ht_full_fcda);
    		}

			//调试
			//printf("check error116 !!!!!!!!!!!!!!\n");

			//这些可能是已有的内存，如果存在则需要清除，（在台账删除时就应该清除）
			apr_snprintf(str, sizeof(str), "PQMonitorPQM%d", cpuno);

			// 仅在没有值时更新 LD_name，避免重复分配内存
			if (ied_usr->LD_info[cpuno - 1].LD_name == NULL) {

				//lnk20250208调试用
				std::cout << "new space for LD_name" << std::endl;
			  
				//lnk20250208不使用apr_pstrdup，使用固定大小，后续都在这块内存上直接复用
				//ied_usr->LD_info[cpuno - 1].LD_name = apr_pstrdup(g_init_pool, str);
				// 从 g_init_pool 内存池中分配固定 256 字节的内存
				ied_usr->LD_info[cpuno - 1].LD_name = (char *)apr_palloc(g_init_pool, 256);

				// 清空内存，防止残留数据
				memset(ied_usr->LD_info[cpuno - 1].LD_name, 0, 256);

				// 将 str 中的内容复制到预先分配的内存中，最多复制 256 字节（包含结束符）
				apr_cpystrn(ied_usr->LD_info[cpuno - 1].LD_name, str, 256);

			} else {//已有则替换，在原有空间上覆盖

				//lnk20250208调试用
				std::cout << "old space for LD_name:" << ied_usr->LD_info[cpuno - 1].LD_name <<std::endl;

				// 清空内存，防止残留数据
				memset(ied_usr->LD_info[cpuno - 1].LD_name, 0, 256);

				//printf("check error333 !!!!!!!!!!!!!!\n");

				apr_cpystrn(ied_usr->LD_info[cpuno - 1].LD_name, str, 256);	//原有空间覆盖

				//printf("check error222 !!!!!!!!!!!!!!\n");
			}

			ldname = ied_usr->LD_info[cpuno - 1].LD_name;

			//调试
			//printf("check error114 !!!!!!!!!!!!!!\n");

			ied_usr->LD_info[cpuno - 1] = line_info;//这个放在后面，因为需要先判断原有的指针
		    ied_usr->LD_info[cpuno - 1].ied = ied;  //这个放在后面，因为需要先判断原有的指针

			//调试
			//printf("check error115 !!!!!!!!!!!!!!\n");



			ied_usr->LD_info[cpuno - 1].LD_name = ldname;//记录原有的或者新的ldname

			//调试
			printf("ledger ied_usr->LD_info[cpuno - 1].LD_name: %s\n", ied_usr->LD_info[cpuno - 1].LD_name);

			ied_usr->LD_info[cpuno - 1].ht_fcd = apr_hash_make(g_init_pool);	//重新创建哈希表
			ied_usr->LD_info[cpuno - 1].ht_full_fcda = apr_hash_make(g_init_pool);

		    ied_usr->LD_info[cpuno - 1].rptcount = 0;	//报告数清零，这个值在ied_usr->LD_info[cpuno - 1] = line_info;时应该就已清0，报告的空间会在报告块初始化时分配
		    printf("rptcount: %d\n", ied_usr->LD_info[cpuno - 1].rptcount);

			//使用原有的报告日志空间
			ied_usr->LD_info[cpuno - 1].loginfo = loginfo;	//不管是新的还是旧的空间，后续初始化报告都会将它覆盖
			ied_usr->LD_info[cpuno - 1].rptinfo = rptinfo;

		    if (cpuno > ied->cpucount) {//新台账的逻辑号大于ied原有的cpu数（初始化是0），则更新cpu数，用来记录有多少个监测点
		        ied->cpucount = cpuno;
		    }
		}
		printf("Monitor Info [ID: %s, Name: %s] saved in LD_info\n", line_info.mp_id, line_info.name);
	}
	return 0;
}

////////////////////////////////////////////////////////////////////////台账更新记录日志
// 获取当前时间并格式化为 "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 = "../etc/" + std::string(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;      // 用于存储new的terminal_id和时间
	std::vector<std::pair<std::string, std::string> > modify_entries;   // 用于存储modify的terminal_id和时间
	std::vector<std::pair<std::string, std::string> > delete_entries;   // 用于存储delete的terminal_id和时间

    std::string current_time = get_current_time(); // 获取当前时间

    // 处理 new_updates
    for (int i = 0; i < ledger_update_xml->new_update_num; ++i) {
        std::string terminal_id = ledger_update_xml->new_updates[i].terminal_id;
        new_entries.push_back(std::make_pair(terminal_id, current_time));
    }

    // 处理 modify_updates
    for (int i = 0; i < ledger_update_xml->modify_update_num; ++i) {
        std::string terminal_id = ledger_update_xml->modify_updates[i].terminal_id;
        modify_entries.push_back(std::make_pair(terminal_id, current_time));
    }

    // 处理 delete_updates
    for (int i = 0; i < ledger_update_xml->delete_update_num; ++i) {
        std::string terminal_id = ledger_update_xml->delete_updates[i].terminal_id;
        delete_entries.push_back(std::make_pair(terminal_id, current_time));
    }

    // 写入日志文件
    if (!new_entries.empty()) {
        log_file << "<new>\n";
        for (size_t i = 0; i < new_entries.size(); ++i) {
            write_log_entry(log_file, "add", new_entries[i].first, new_entries[i].second);
        }
        log_file << "</new>\n";
    }

    if (!modify_entries.empty()) {
        log_file << "<modify>\n";
        for (size_t i = 0; i < modify_entries.size(); ++i) {
            write_log_entry(log_file, "modify", modify_entries[i].first, modify_entries[i].second);
        }
        log_file << "</modify>\n";
    }

    if (!delete_entries.empty()) {
        log_file << "<delete>\n";
        for (size_t i = 0; i < delete_entries.size(); ++i) {
            write_log_entry(log_file, "delete", delete_entries[i].first, delete_entries[i].second);
        }
        log_file << "</delete>\n";
    }

    log_file.close();
    std::cout << "Ledger log has been updated." << std::endl;
}	
///////////////////////////////////////////////////////////////////////////台账内存分配部分功能代码：未使用
// 删除指定的子池
void delete_sub_pool(const char* terminal_id) {
    std::list<std::pair<std::string, apr_pool_t*> >::iterator it;
    
    for (it = pool_list.begin(); it != pool_list.end(); ++it) {
        if (it->first == terminal_id) {
            apr_pool_destroy(it->second);  // 销毁子池
            pool_list.erase(it);  // 从容器中移除
            break;
        }
    }
}
//找对应的子池
apr_pool_t* find_sub_pool(const char* terminal_id) {
    std::list<std::pair<std::string, apr_pool_t*> >::iterator it;
    
    for (it = pool_list.begin(); it != pool_list.end(); ++it) {
        if (it->first == terminal_id) {
            return it->second;  // 找到对应的子池
        }
    }
    
    return NULL;  // 没有找到对应的子池
}
// 创建子池并添加到容器
apr_pool_t* create_sub_pool(apr_pool_t* parent_pool, const char* terminal_id) {
    apr_pool_t* new_sub_pool = NULL;

    // 在父池中创建一个子池
    apr_pool_create(&new_sub_pool, parent_pool);
    
    if (new_sub_pool == NULL) {
        return NULL;  // 创建失败
    }

    // 将子池和终端 ID 存储在容器中
    pool_list.push_back(std::make_pair(std::string(terminal_id), new_sub_pool));

    return new_sub_pool;
}
//////////////////////////////////////////////////////////////////////////////////////////////////
//台账变更的报告块初始化
int parse_rpt_log_ini_one(ied_t* ied)
{
	QMap<QString, QStringList*> rpt_cfg_strlists;
	QMap<QString, QStringList*> log_cfg_strlists;

	int	iedno, cpuno;
	ied_usr_t* ied_usr;
	LD_info_t* LD_info;
	char		buf[256];

	ied_usr = GET_IEDEXT_ADDR(ied);
	QString type;
	type.append(ied_usr->dev_type);
	if (!rpt_cfg_strlists.contains(type))
	{
		QStringList* rpt_temp = new QStringList();
		QStringList* log_temp = new QStringList();
		rpt_cfg_strlists.insert(type, rpt_temp);//报告控制列表
		log_cfg_strlists.insert(type, log_temp);//日志控制列表
		//g_DevFlag没有使用
		parse_one_rpt_log_ini(g_DevFlag, rpt_cfg_strlists[type], log_cfg_strlists[type], ied_usr->dev_type);
	}

	//lnk20250208调试用
	std::cout << "ied->cpucount:" << (int)(ied->cpucount) <<std::endl;

	for (cpuno = 0; cpuno < ied->cpucount; cpuno++) {//根据ied实际的检测点数遍历

		//lnk20250208调试用
		std::cout << "cpuno:" << cpuno << "log init !!!!!!" <<std::endl;

		LD_info = &(ied_usr->LD_info[cpuno]);

		//这些可能是已有的内存，经过判断才能从g_init_pool中分配内存，lnk20250122
		//char str[256];
		char* tmp = Get_IED(ied_usr->dev_type);
		if(tmp == NULL){std::cerr << "front read ied config error!" << std::endl;continue;}
		qDebug() << tmp << endl;
		//apr_snprintf(str, sizeof(str), tmp, cpuno + 1);

		//调试
		printf("%s使用内存地址 LD_name[%d]: %p\n", ied_usr->terminal_id, cpuno, (void*)ied_usr->LD_info[cpuno].LD_name);

		//添加判断，有的监测点没有cpuno为2，直接申请了LD_info[2-1]，没申请LD_info[0]
		if(ied_usr->LD_info[cpuno].LD_name == NULL){
			printf("this ld_info didn't palloc space ,maybe this ledger has problem!");
			DIY_ERRORLOG_CODE("process",0,LOG_CODE_RPTINIT,"【ERROR】终端%s的监测点序号为%d的监测点无法初始化报告,这个装置的台账存在缺失,请检查装置台账的监测点总数和各监测点的序号",ied_usr->terminal_id,cpuno + 1);
			
			//添加默认值，防止崩溃20260601
				LD_info->rptcount = 0;
				LD_info->logcount = 0;
				LD_info->cpuno = 0;
				LD_info->LD_name = NULL;
				LD_info->rptinfo = NULL;
				LD_info->loginfo = NULL;

			delete[] tmp;//Get_IED中分配了内存，使用后删除

			continue;//跳过防止崩溃
		}

		printf("old logini ied_usr->LD_info[cpuno - 1].LD_name: %s\n", ied_usr->LD_info[cpuno].LD_name);

		apr_snprintf(ied_usr->LD_info[cpuno].LD_name, 256, tmp, cpuno + 1);//注意拷贝大小，容易段错误，lnk20250702

		printf("new logini ied_usr->LD_info[cpuno - 1].LD_name: %s\n", ied_usr->LD_info[cpuno].LD_name);

		delete[] tmp;//Get_IED中分配了内存，使用后删除

		//lnk20250208调试用
		std::cout << "cpuno:" << cpuno << " fill report control" <<std::endl;

		//初始化监测点的报告控制
		init_rptctrl_by_count(LD_info, rpt_cfg_strlists[type]->size());
		for (int i = 0; i < rpt_cfg_strlists[type]->size(); ++i) {
			apr_snprintf(buf, sizeof(buf), "%s", rpt_cfg_strlists[type]->at(i).toAscii().constData());
			//fill_rptctrl_by_cfg(LD_info, i, buf);
			int ret = fill_rptctrl_by_cfg(LD_info, i, buf);
			if (ret != 0) {
			    printf("[RPT_INIT][ERROR] fill_rptctrl_by_cfg failed dev=%s cpu=%d rpt=%d cfg=%s\n",
			           ied_usr->terminal_id, cpuno, i, buf);
				DIY_ERRORLOG_CODE(ied_usr->terminal_id,1,LOG_CODE_RPTINIT,"【ERROR】终端%s的监测点序号为%d的监测点的第%d个报告配置解析失败，请检查报告配置格式是否正确",ied_usr->terminal_id, cpuno + 1, i + 1);
				
			    if (LD_info->rptinfo && LD_info->rptinfo[i]) {
			        LD_info->rptinfo[i]->rptID = NULL;
			        LD_info->rptinfo[i]->LD_info = NULL;
			    }
			}
		}
		//初始化监测点的日志控制
		init_logctrl_by_count(LD_info, log_cfg_strlists[type]->size());
		for (int i = 0; i < log_cfg_strlists[type]->size(); ++i) {
			apr_snprintf(buf, sizeof(buf), "%s", log_cfg_strlists[type]->at(i).toAscii().constData());
			char* tmp = Get_LDevice(ied_usr->dev_type);
			if(tmp == NULL){std::cerr << "front read monitor config error!" << std::endl;continue;}
			fill_logctrl_by_cfg(LD_info, i, buf, tmp);
			delete[] tmp;
		}
	}

	//报告控制块和日志控制块处理结束后清理容器
	for (QMap<QString, QStringList*>::iterator it1 = log_cfg_strlists.begin(); it1 != log_cfg_strlists.end(); ++it1)
	{
		delete it1.value();
	}
	for (QMap<QString, QStringList*>::iterator it2 = rpt_cfg_strlists.begin(); it2 != rpt_cfg_strlists.end(); ++it2)
	{
		delete it2.value();
	}
	rpt_cfg_strlists.clear();
	log_cfg_strlists.clear();

	return APR_SUCCESS;
}
////////////////////////////////////////////////////////////remdibtable
apr_status_t init_rem_dib_table_one(ied_t *ied)
{
	int pos = 0;
	int			iedno,chnl_no;
	struct in_addr	ip;
	chnl_usr_t	*chnl_usr;

	ied_usr_t* ied_usr;

	//插入的位置和g_node一样
	ied_usr = (ied_usr_t*)ied->usr_ext;
	pos = ied_usr->dev_idx - 1;

	std::cout << "!!!!!!!!!rem_dib_table pos is " << pos << std::endl;

	for(chnl_no=0 ; chnl_no<ied->chncount; chnl_no++) {	
		chnl_usr = (chnl_usr_t*)ied->channel[chnl_no].connect;//初始化时已根据最大数量申请空间
		g_pt61850app->chnl_usr[pos] = chnl_usr;

		std::cout << "!!!!!!!!!g_pt61850app pos " << pos << "is " << ied_usr->terminal_id << "is" << (chnl_usr == NULL?"NULL":"NOTNULL") << std::endl;

		ip.s_addr = htonl(ied->channel[chnl_no].addr);
		strcpy(chnl_usr->ip_str,inet_ntoa(ip));
		printf( " add_rem_dib_table  %s:%d \n",chnl_usr->ip_str ,ied->channel[chnl_no].port );
		add_rem_dib_table (pos++,chnl_usr->ip_str,ied->channel[chnl_no].port );//当前终端的IP端口记录到表中，这里要注意添加的下标，如果删除终端，这个表内的内容仍存在，再次使用时会被替换
		{
			char comm_str[256]; 
			memset(comm_str,0,256);
			apr_snprintf(comm_str,sizeof(comm_str),"%16s:%d\t\tinited",chnl_usr->ip_str,ied->channel[chnl_no].port);
			add_comm_log(comm_str);
		}
	}
	return APR_SUCCESS;
}
///////////////////////////////////////////////////////////////////////////////////////////////////清理ied
void clearLogInfo(loginfo_t *loginfo) {
    if (loginfo == nullptr) {
        return; // 如果传入的指针为空，直接返回
    }

    // 清空字符数组
    memset(loginfo->logName, 0, sizeof(loginfo->logName));

    // 清空其他非指针成员
    loginfo->IntgPd = 0;
    loginfo->reasonCode = 0;
    loginfo->TrgOpt = 0;

    loginfo->start_time = 0;
    loginfo->end_time = 0;
    loginfo->need_steady = 0;
    loginfo->need_voltage = 0;

    // 对指针成员进行清理，但不释放内存
    if (loginfo->lcbName != nullptr) {
        memset(loginfo->lcbName, 0, strlen(loginfo->lcbName)); // 清空字符串内容
    }
    if (loginfo->datasetName != nullptr) {
        memset(loginfo->datasetName, 0, strlen(loginfo->datasetName)); // 清空字符串内容
    }

    // 由于 LD_info 是指针，它不清理,仍指向上级
}

void clearRptInfo(rptinfo_t *rptinfo) {
    if (rptinfo == nullptr) {
        return; // 如果传入的指针为空，直接返回
    }

    // 清空字符数组和非指针成员
	rptinfo->instanceNeedSuffix = 0;
	rptinfo->TrgOpt = 0;

    memset(rptinfo->OptFlds, 0, sizeof(rptinfo->OptFlds));
    rptinfo->IntgPd = 0;
    rptinfo->report_PQ_type = 0;

    rptinfo->rpt_registered = 0;
    rptinfo->chnl_id = 0;

    rptinfo->m_LastDataTime = 0;
    rptinfo->m_LastGITime = 0;

    rptinfo->m_LastRegisterFailedTime = 0;
    rptinfo->m_LastUnRegisterFailedTime = 0;

    memset(rptinfo->m_EntryID, 0, sizeof(rptinfo->m_EntryID));
    rptinfo->m_curRptSuffix = 0;

    rptinfo->count = 0;
    rptinfo->rptNo = 0;
    rptinfo->flickerflag = 0;

    rptinfo->pstflag = 0;

    // 对指针成员进行清理，但不释放内存
    if (rptinfo->rptID != nullptr) {
        memset(rptinfo->rptID, 0, strlen(rptinfo->rptID)); // 清空字符串内容
    }

    // 由于 LD_info 是指针，它不需要清理，仍指向父级
    //rptinfo->LD_info = nullptr;

    // 对 RCBC_INFO 结构体指针进行处理
    if (rptinfo->m_rcb_info != nullptr) {
        // 如果需要，可以选择清理结构体内部内容，但不释放内存
        rptinfo->m_rcb_info = nullptr;//这里可以清空，因为这个指针指向的区域在连接通道关闭时会被清理
    }
}

void clearLDInfo(LD_info_t *ld_info) {
    if (ld_info == nullptr) {
		std::cout << "ldinfo is null" << std::endl;
        return;
    }

	//保留原来的ied指向

	//清空报告
	if (ld_info->rptinfo != nullptr) {
        for (int i = 0; i < ld_info->rptcount; ++i) {
            if (ld_info->rptinfo[i] != nullptr) {
                clearRptInfo(ld_info->rptinfo[i]); //清空报告控制块
            }
        }
    }
	std::cout << "clean RptInfo!!!" << std::endl;

	//清空日志
    if (ld_info->loginfo != nullptr) {
        for (int i = 0; i < ld_info->logcount; ++i) {
            if (ld_info->loginfo[i] != nullptr) {
                clearLogInfo(ld_info->loginfo[i]); //清空日志控制块
            }
        }
    }
	std::cout << "clean loginfo_t!!!" << std::endl;

	//清空补招
    if (ld_info->autorecall != nullptr) {
        for (int i = 0; i < ld_info->autorecallcount; ++i) {
            if (ld_info->autorecall[i] != nullptr) {
                memset(ld_info->autorecall[i], 0, sizeof(autorecall_t));
				delete[] ld_info->autorecall[i];	//删除数组元素空间，如果有正在补招的内容，则已经申请空间，需要释放lnk20250801
            }
        }
		delete ld_info->autorecall;	//删除数组空间
    }
	std::cout << "clean autorecall_t!!!" << std::endl;

    if (ld_info->ht_fcd != nullptr) {
        apr_hash_clear(ld_info->ht_fcd);
    }
    if (ld_info->ht_full_fcda != nullptr) {
        apr_hash_clear(ld_info->ht_full_fcda);
    }

    // 清空其他指针成员
	if (ld_info->name != nullptr) {
        memset(ld_info->name, 0, sizeof(char) * 256);
    }
    if (ld_info->LD_name != nullptr) {
        memset(ld_info->LD_name, 0, sizeof(char) * 256);
    }
    if (ld_info->mp_id != nullptr) {
        memset(ld_info->mp_id, 0, sizeof(char) * 256);
    }
    if (ld_info->voltage_level != nullptr) {
        memset(ld_info->voltage_level, 0, sizeof(char) * 256);
    }
    if (ld_info->v_wiring_type != nullptr) {
        memset(ld_info->v_wiring_type, 0, sizeof(char) * 256);
    }
    if (ld_info->monitor_status != nullptr) {
        memset(ld_info->monitor_status, 0, sizeof(char) * 64);
    }
    if (ld_info->terminal_code != nullptr) {
        memset(ld_info->terminal_code, 0, sizeof(char) * 256);
    }

	ld_info->cpuno = 0;	//清空逻辑序列号

	ld_info->time = 0; //台账更新时间清0
	ld_info->update_flag = 0; //监测点更新标志，暂不使用

	ld_info->rptRecvFlag = 0;//重置报告标志
	ld_info->rptRecvCheckFlag = 0;
	ld_info->rptPstRecvFlag = 0;
	ld_info->rptPstRecvCheckFlag = 0;

	ld_info->registcount = 0;
	ld_info->has_logged_regist = 0;

	ld_info->read_flag = 0;//监测点无效

    ld_info->rptcount = 0;
    ld_info->logcount = 0;
    ld_info->autorecallflag = 0;
    ld_info->autorecallcount = 0;

	//ld_info->group = 0; //不使用

	//清空实时数据部分
    ld_info->line_id = 0;
    ld_info->real_data = 0;
    ld_info->soe_data = 0;
    ld_info->limit = 0;
    ld_info->count = 0;

    //ld_info->SubV_Index = 0;	//不使用
    //ld_info->Dev_Index = 0;	//不使用
    //ld_info->Sub_Index = 0;	//不使用
    //ld_info->GD_Index = 0;	//不使用

	//清空暂态结构
	for (int i = 0; i < QVVR_NUM; ++i) {
        ld_info->qvvr[i].used_status = 0;
        ld_info->qvvr[i].QVVR_start = 0;
        ld_info->qvvr[i].QVVR_type = 0;
        ld_info->qvvr[i].QVVR_time = 0;
        ld_info->qvvr[i].QVVR_PerTime = 0.0f;
        ld_info->qvvr[i].QVVR_Amg = 0.0f;
        memset(ld_info->qvvr[i].QVVR_Rptname, 0, sizeof(ld_info->qvvr[i].QVVR_Rptname));  // 清空字符数组
        ld_info->qvvr[i].timestamp = 0;
    }
	ld_info->qvvr_idx = 0;

	memset(ld_info->FltNum, 0, sizeof(ld_info->FltNum));

    ld_info->RDRE_FltNum = 0;	//录波号清零

	ld_info->log_level = 1;	//log_level设为1，默认日志级别为1，后续根据需要调整
    

}

void clearIedUsr(ied_usr_t *ied_usr) {
    if (ied_usr == nullptr) {
		std::cout << "ied_usr is null" << std::endl;
        return;
    }

    // 清空 ied_usr_t 中的非指针部分
    //ied_usr->dev_idx = 0;//保留index
    ied_usr->dev_flag = UNUSED;
    ied_usr->last_call_wavelist_time = 0;
    ied_usr->time = 0;			//台账更新时间
    ied_usr->update_flag = 0;	//台账更新标志暂不使用

	ied_usr->lastconnectstat = 0;
	ied_usr->has_logged_disconnect = 0;

    // 清空指针部分，但不清理非空指针
    if (ied_usr->LD_info != nullptr) {

        // 如果 LD_info 不为空，清理它内部的内容
        for (int i = 0; i < MAX_CPUNO; ++i) {
			LD_info_t * ld =NULL;
			ld = (LD_info_t *)&ied_usr->LD_info[i];
            if (ld != NULL) {
                clearLDInfo(ld);
            }
        }
    }

    if (ied_usr->cookie != nullptr) {	//不使用
		std::cout << "cookie not null" << std::endl;
    }

    // 清空其他非指针部分，也可以不清0，后续都会覆盖
    memset(ied_usr->dev_type, 0, sizeof(ied_usr->dev_type));
    memset(ied_usr->dev_key, 0, sizeof(ied_usr->dev_key));
    memset(ied_usr->dev_series, 0, sizeof(ied_usr->dev_series));
    memset(ied_usr->com_type, 0, sizeof(ied_usr->com_type));
    memset(ied_usr->terminal_id, 0, sizeof(ied_usr->terminal_id));
    memset(ied_usr->org_name, 0, sizeof(ied_usr->org_name));
    memset(ied_usr->maint_name, 0, sizeof(ied_usr->maint_name));
    memset(ied_usr->station_name, 0, sizeof(ied_usr->station_name));
    memset(ied_usr->tmnl_factory, 0, sizeof(ied_usr->tmnl_factory));
    memset(ied_usr->tmnl_status, 0, sizeof(ied_usr->tmnl_status));
    memset(ied_usr->terminal_code, 0, sizeof(ied_usr->terminal_code));
	ied_usr->log_level = 1;	//log_level设为1，默认日志级别为1，后续根据需要调整
}

// 清空 channel 和 cpuinfo 的非指针部分
void clear_channel_and_cpuinfo(byte_t chncount, channel_t *channel, byte_t cpucount, cpuinfo_t *cpuinfo) {
    // 清空 channel 数组中的每个元素的非指针字段
    for (byte_t i = 0; i < chncount && (&channel[i] != NULL); ++i) {
        channel[i].master = 0;
        channel[i].channel_type = 0;
        memset(channel[i].addr_str, 0, LONGNAME);
        channel[i].addr = 0;
        channel[i].port = 0;
        channel[i].status = CHANNEL_DISCONNECTED;
        channel[i].last_ticks = 0;
        channel[i].last_send_ticks = 0;
        channel[i].ied_id = 0;

		// 清理 channel 里的 chnl_usr_t 部分
		// 关闭连接时已清理，保留指针本身
    }

    // 清空 cpuinfo 数组中的每个元素的非指针字段
    for (byte_t i = 0; i < cpucount  && (&cpuinfo[i] != NULL); ++i) {
        cpuinfo[i].addr = 0;
        cpuinfo[i].status = 0;
        cpuinfo[i].templ = 0;
        memset(cpuinfo[i].name, 0, LONGNAME);
        cpuinfo[i].last_gi = 0;
        cpuinfo[i].last_gi_send = 0;
        cpuinfo[i].next_gi_send = 0;
        cpuinfo[i].last_ticks = 0;
        cpuinfo[i].last_send_ticks = 0;
    }
}

void clearIed(ied_t *ied) {
    if (ied == nullptr) {
        return;
    }

    // 清空 ied_t 的id
    ied->id = 0;		
    //ied->flags = 0;	//未使用
    //ied->type = 0;	//未使用

	//清理channel	//清理cpuinfo
	clear_channel_and_cpuinfo(ied->chncount,ied->channel,ied->cpucount,ied->cpuinfo);
    ied->chncount = 0;	//清空通道数
    ied->cpucount = 0;	//清空检测点数

	//名称清理
	memset(ied->name, 0, LONGNAME);

    //ied->station = 0;		//未使用
    //ied->node = 0;		//未使用
    //ied->frequency = 0;	//未使用
    //ied->delay = 0;		//未使用
    //ied->count = 0;		//未使用
	
	//数据组定义部分未使用
	//ied->groups = NULL;
	//ied->htgroups = NULL;

	//系统令牌未使用
    //ied->systoken_st = 0;

    //ied->index = 0;			//未使用
    ied->status = STATUS_NOINIT;		//设备状态设为未初始化
    //ied->last_ticks = 0;	//未使用
    //ied->last_gi = 0;		//未使用

    // 清空指针部分
    if (ied->usr_ext != nullptr) {
        ied_usr_t *ied_usr = (ied_usr_t*)ied->usr_ext;
        clearIedUsr(ied_usr);
    }

	if (ied->sys_ext != nullptr) {
		//不使用
    }

	if (ied->app_ext != nullptr) {
		//不使用
    }

}
/*封装C可调用的台账更新函数 */////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//测试函数
std::string my_to_string(long long value) {
    std::stringstream ss;
    ss << value;
    return ss.str();
}

//lnk20250328用来跳过匹配到的终端
bool shouldSkipTerminal(const char* terminal_id) {
    for (size_t i = 0; i < TESTARRAY.size(); ++i) {
        if (TESTARRAY[i] == terminal_id) {
            return true;
        }
    }
    return false;
}

void rocketmq_test_300(int mpnum,int front_index,int type) {
    Ckafka_data_t data;
    data.strTopic = QString::fromStdString(G_ROCKETMQ_TOPIC_TEST);
    data.mp_id = "0";

    // 读取文件内容
    //std::ifstream file("long_string.txt");  // 文件中存储长字符串
	std::vector<std::string> filenames;
	filenames.push_back("long_string.txt");
	filenames.push_back("PLT_string.txt");
	filenames.push_back("fluc_string.txt");
	filenames.push_back("qvvr_string.txt");

	for (std::vector<std::string>::iterator it = filenames.begin(); it != filenames.end(); ++it) {
	    const std::string& filename = *it;
	    std::ifstream file(filename.c_str());
	    if (!file.is_open()) {
	        std::cerr << "跳过无法打开的文件: " << filename << std::endl;
	        continue;
	    }

    	std::stringstream buffer;
    	buffer << file.rdbuf();
    	std::string file_contents = buffer.str();  // 获取文件内容
    	std::string base_strText = file_contents;

    	// 获取当前时间作为开始时间
    	std::time_t t = std::time(NULL);//获取当前的系统时间（自 1970 年 1 月 1 日以来的秒数，通常称为 UNIX 时间戳）
    	std::tm* time_info = std::localtime(&t);//将 std::time_t（表示当前的 UNIX 时间戳）转换为本地时间（std::tm 结构）
    	time_info->tm_sec = 0;  // 清零秒位
    	//time_info->tm_msec = 0; // 清零毫秒位（如果需要更精确，使用高精度时间）

    	// 获取当前的时间戳（秒）
    	std::time_t base_time_t = std::mktime(time_info);//将 std::tm 结构（本地时间）转换回 std::time_t（时间戳）

    	// 计算每条消息的时间戳，精确到分钟，毫秒和秒清零
    	long long current_time_ms = static_cast<long long>(base_time_t) * 1000;  // 每分钟递增，单位毫秒

    	// 设定总的消息数量
    	int total_messages = mpnum;

    	ied_t* ied;
    	ied_usr_t* ied_usr;

    	// 循环发送 300 条消息
		if(type == 0){
			std::cout << " use ledger send msg " << std::endl;

			pthread_mutex_lock(&mtx);

    		for (int i = 0; (total_messages > 0 && g_node_id == 100) && i < g_node->n_clients; ++i) {//台账模拟不限制进程号

    		    ied = (ied_t*)g_node->clients[i];
				if(ied != NULL){
					ied_usr = (ied_usr_t*)ied->usr_ext;

					//跳过正常的终端
					if (shouldSkipTerminal(ied_usr->terminal_id)) {
						std::cout << ied_usr->terminal_id << " use true message " << std::endl;
						continue;
					}

    		        for (int j = 0; j < 10 && ied_usr->LD_info[j].mp_id[0] != '\0'; j++){
    		            // 修改 Monitor 值
    		            char monitor_id[256] = {};
    		            strncpy(monitor_id, ied_usr->LD_info[j].mp_id, sizeof(monitor_id) - 1);
    		            monitor_id[sizeof(monitor_id) - 1] = '\0';

    		            data.mp_id = QString(monitor_id);

    		            data.monitor_id = i + j;
					
    		            std::string modified_time = my_to_string(current_time_ms);  // 时间转换为整数类型（Unix时间戳）

    		            // 替换消息中的 Monitor 和 TIME 字段（只匹配字段名，不匹配具体数值）
    		            std::string modified_strText = base_strText;

    		            // 替换 Monitor 字段
    		            size_t monitor_pos = modified_strText.find("\"Monitor\"");
    		            if (monitor_pos != std::string::npos) {
    		                size_t colon_pos = modified_strText.find(":", monitor_pos);
    		                size_t quote_pos = modified_strText.find("\"", colon_pos);
    		                size_t end_quote_pos = modified_strText.find("\"", quote_pos + 1);
    		                if (colon_pos != std::string::npos && quote_pos != std::string::npos && end_quote_pos != std::string::npos) {
    		                    modified_strText.replace(quote_pos + 1, end_quote_pos - quote_pos - 1, data.mp_id.toStdString());
    		                }
    		            }

    		            // 替换 TIME 字段
    		            size_t time_pos = modified_strText.find("\"TIME\"");
    		            if (time_pos != std::string::npos) {
    		                size_t colon_pos = modified_strText.find(":", time_pos);
    		                size_t quote_pos = colon_pos;
    		                size_t end_quote_pos = modified_strText.find(",", quote_pos + 1);
    		                if (colon_pos != std::string::npos && quote_pos != std::string::npos && end_quote_pos != std::string::npos) {
    		                    modified_strText.replace(quote_pos + 1, end_quote_pos - quote_pos - 1, modified_time);
    		                }
    		            }

    		            // 更新数据
    		            data.strText = QString::fromStdString(modified_strText);

    		            // 发送数据
    		            my_rocketmq_send(data);

    		            // 输出调试信息
    		            std::cout << "Sent message " << (i + 1) << " with Monitor " << data.monitor_id << " and TIME " << modified_time << std::endl;

						/*int sleeptime = 6000/total_messages;//终端为单位
						if(sleeptime > 50){
							apr_sleep(apr_time_from_msec(50));  // 添加毫秒延时
						}
						else{
							apr_sleep(apr_time_from_msec(6000/total_messages));  // 添加毫秒延时
						}*/
						
    		        }
    		    }
    		}

			

			std::cout << "Finished sending " << g_node->n_clients << " messages." << std::endl;

			pthread_mutex_unlock(&mtx);

		}
		else{
			std::cout << " use monitor + number send msg " << std::endl;

			for (int i = 0; (total_messages > 0 && g_front_seg_index == 1 && g_node_id == 100) && i < total_messages; ++i) {

    		    // 修改 Monitor 值
    		    char monitor_id[256] = {};
    		    snprintf(monitor_id, sizeof(monitor_id), "testmonitor%05d", i);

    		    monitor_id[sizeof(monitor_id) - 1] = '\0';
    		    data.mp_id = QString(monitor_id);
    		    data.monitor_id = i;
			
    		    std::string modified_time = my_to_string(current_time_ms);  // 时间转换为整数类型（Unix时间戳）
    		    // 替换消息中的 Monitor 和 TIME 字段（只匹配字段名，不匹配具体数值）
    		    std::string modified_strText = base_strText;
    		    // 替换 Monitor 字段
    		    size_t monitor_pos = modified_strText.find("\"Monitor\"");
    		    if (monitor_pos != std::string::npos) {
    		        size_t colon_pos = modified_strText.find(":", monitor_pos);
    		        size_t quote_pos = modified_strText.find("\"", colon_pos);
    		        size_t end_quote_pos = modified_strText.find("\"", quote_pos + 1);
    		        if (colon_pos != std::string::npos && quote_pos != std::string::npos && end_quote_pos != std::string::npos) {
    		            modified_strText.replace(quote_pos + 1, end_quote_pos - quote_pos - 1, data.mp_id.toStdString());
    		        }
    		    }
    		    // 替换 TIME 字段
    		    size_t time_pos = modified_strText.find("\"TIME\"");
    		    if (time_pos != std::string::npos) {
    		        size_t colon_pos = modified_strText.find(":", time_pos);
    		        size_t quote_pos = colon_pos;
    		        size_t end_quote_pos = modified_strText.find(",", quote_pos + 1);
    		        if (colon_pos != std::string::npos && quote_pos != std::string::npos && end_quote_pos != std::string::npos) {
    		            modified_strText.replace(quote_pos + 1, end_quote_pos - quote_pos - 1, modified_time);
    		        }
    		    }
    		    // 更新数据
    		    data.strText = QString::fromStdString(modified_strText);
    		    // 发送数据
    		    my_rocketmq_send(data);
    		    // 输出调试信息
    		    std::cout << "Sent message " << (i + 1) << " with Monitor " << data.monitor_id << " and TIME " << modified_time << std::endl;

				/*int sleeptime = 60000/total_messages;//监测点为单位
				if(sleeptime > 50){
					apr_sleep(apr_time_from_msec(50));  // 添加毫秒延时
				}
				else{
					apr_sleep(apr_time_from_msec(60000/total_messages));  // 添加毫秒延时
				}*/
    		}

			std::cout << "Finished sending " << total_messages << " messages." << std::endl;
		}
	
	}

}

///////////////////////////////////////////////////////////////////////////////lnk实时日志部分20250205
// ------------------ 全局日志列表和锁 ------------------
std::list<std::string> errorList;
std::list<std::string> warnList;
std::list<std::string> normalList;
std::list<std::string> debugList;   // 新增 debugList

pthread_mutex_t errorListMutex  = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t warnListMutex   = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t normalListMutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t debugListMutex  = PTHREAD_MUTEX_INITIALIZER; // 新增 debugList 互斥锁

// ------------------ 输出开关 ------------------
bool errorOutputEnabled  = false;  // 是否将 error 级别写入 errorList
bool warnOutputEnabled   = false;  // 是否将 warn 级别写入 warnList
bool normalOutputEnabled = false;  // 是否将 normal 级别写入 normalList
bool debugOutputEnabled  = false;   // 新增 debug 开关

// ------------------ 用于恢复原始缓冲区 ------------------
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,
	LOGDEBUG       // 新增 debug 级别
};

// ------------------------------------------------------------------
//  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();
            }
        }

		// ✅ 如果当前级别未启用，不记录
    	bool shouldLog = false;
    	switch (m_level) {
    	    case LOGERROR:  shouldLog = errorOutputEnabled; break;
    	    case LOGWARN:   shouldLog = warnOutputEnabled; break;
    	    case LOGNORMAL: shouldLog = normalOutputEnabled; break;
    	    case LOGDEBUG:  shouldLog = debugOutputEnabled; break;
    	}

    	if (!shouldLog) return ch;  // 🚫 日志开关未启用，直接跳过

        // 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 (debugOutputEnabled) {
                    pthread_mutex_lock(&debugListMutex);
                    debugList.push_back(m_buffer);
                    pthread_mutex_unlock(&debugListMutex);
                }
                else 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 (debugOutputEnabled) {
                    pthread_mutex_lock(&debugListMutex);
                    debugList.push_back(m_buffer);
                    pthread_mutex_unlock(&debugListMutex);
                }
                else 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 (debugOutputEnabled) {
                    pthread_mutex_lock(&debugListMutex);
                    debugList.push_back(m_buffer);
                    pthread_mutex_unlock(&debugListMutex);
                }
                else 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;

// ------------------ 我们另外提供一个全局的 debug 输出流 ------------------
// 其原始buf默认为 NULL（不输出到终端），只存到 debugList(若开关开)
//static std::ostream g_debug(&g_debugTeeBuf);

// 让 qDebug() 映射到这个全局流
//#define qDebug() g_debug

// ------------------ 重定向函数 ------------------
// 只在第一次启用时，用 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 redirectDebugOutput(bool enable)
{
    debugOutputEnabled = enable;
    // 不去改 clog.rdbuf()
    // 不去 install 任何 msg handler
}

// ------------------ Qt4 消息处理函数 ------------------
void myQtMsgHandler(QtMsgType type, const char *msg)
{
    // 不论消息类型如何，都写入 debugList
    if (debugOutputEnabled) {
        pthread_mutex_lock(&debugListMutex);
        debugList.push_back(msg);
        pthread_mutex_unlock(&debugListMutex);
    }

    // 根据消息类型选择输出流
    FILE* output = nullptr;
    const char* typeStr = "";
    switch (type) {
    case QtDebugMsg:
        typeStr = "Debug";
        output = stdout;   // Debug 走标准输出
        break;
    case QtWarningMsg:
        typeStr = "Warning";
        output = stderr;   // Warning 走标准错误
        break;
    case QtCriticalMsg:
        typeStr = "Critical";
        output = stderr;   // Critical 走标准错误
        break;
    case QtFatalMsg:
        typeStr = "Fatal";
        output = stderr;   // Fatal 走标准错误
        break;
    }

    fprintf(output, "[%s] %s\n", typeStr, msg);
    fflush(output);

    // Fatal 时进程退出
    if (type == QtFatalMsg) {
        abort();
    }
}

// ------------------ 自定义 printf 输出 ------------------
// 这里示例：把它当成 normal 级别 => 最终会进入normalList(若开关开)
// 定义一个全局静态的互斥锁，名称为 printfmtx
static pthread_mutex_t printfmtx = PTHREAD_MUTEX_INITIALIZER;

// RAII风格的锁包装类
class LockGuard {
public:
    explicit LockGuard(pthread_mutex_t& mutex) : mtx(mutex) {
        pthread_mutex_lock(&mtx);
    }
    ~LockGuard() {
        pthread_mutex_unlock(&mtx);
    }
private:
    pthread_mutex_t& mtx;
};

int customPrintf(const char* format, ...)
{
    // 在进入函数时自动加锁，退出时自动解锁
    LockGuard lock(printfmtx);

    va_list args;
    va_start(args, format);

    char buffer[1024];
    int written = vsnprintf(buffer, sizeof(buffer), format, args);

    va_end(args);

    if (written < 0) {
        return -1;
    }

    // 将格式化后的字符串输出到 std::cout
    std::cout << buffer << std::endl;

    return written;
}

///////////////////////////////////////////////////////////////////////////////
void echo_msg_errexy(const char *file_name, int line_no, int rv, const char *fmt, ...) {
    char __buf[1024];
    va_list __ap;
    va_start(__ap, fmt);
    vsnprintf(__buf, sizeof(__buf), fmt, __ap);
    va_end(__ap);

    // 终端输出
    printf("[ERROR] rv=%d %s:%d => %s", rv, file_name, line_no, __buf);
    fflush(stdout);

    // 写入 errorList
    if (errorOutputEnabled) {
        pthread_mutex_lock(&errorListMutex);
        errorList.push_back(std::string(__buf));
        pthread_mutex_unlock(&errorListMutex);
    }
}

void echo_msg_warnexy(const char *file_name, int line_no, const char *fmt, ...) {
    char __buf[1024];
    va_list __ap;
    va_start(__ap, fmt);
    vsnprintf(__buf, sizeof(__buf), fmt, __ap);
    va_end(__ap);

    // 终端输出
    printf("[WARN ] %s:%d => %s", file_name, line_no, __buf);
    fflush(stdout);

    // 写入 warnList
    if (warnOutputEnabled) {
        pthread_mutex_lock(&warnListMutex);
        warnList.push_back(std::string(__buf));
        pthread_mutex_unlock(&warnListMutex);
    }
}

void echo_msg_debugexy(const char *file_name, int line_no, const char *fmt, ...) {
    char __buf[1024];
    va_list __ap;
    va_start(__ap, fmt);
    vsnprintf(__buf, sizeof(__buf), fmt, __ap);
    va_end(__ap);

    // 终端输出
    printf("[DEBUG] %s:%d => %s", file_name, line_no, __buf);
    fflush(stdout);

    // 写入 normalList
    if (normalOutputEnabled) {
        pthread_mutex_lock(&normalListMutex);
        normalList.push_back(std::string(__buf));
        pthread_mutex_unlock(&normalListMutex);
    }
}
///////////////////////////////////////////////////////////////////////////////
void Worker::telnetetst(QTcpSocket* clientSocket) {
	int ip_count = 0;
	int telnet_count = 0;
	//在测试连接时给台账加锁lnk20250114
	pthread_mutex_lock(&mtx);   std::cout << "testping hold lock !!!!!!!!!!!" << std::endl;
	Worker::init_ping_telnet(clientSocket,ip_count, telnet_count);
	Cout_account_information();
	pthread_mutex_unlock(&mtx); std::cout << "testping free lock !!!!!!!!!!!" << std::endl; 
}

////////////////////////////////////////////////////////////////////////////////
const char* get_front_msg_from_subdir() {
    if (std::strstr(subdir, "cfg_3s_data") != NULL)
        return "实时数据进程";
    else if (std::strstr(subdir, "cfg_soe_comtrade") != NULL)
        return "暂态和告警进程";
    else if (std::strstr(subdir, "cfg_pqdif_data") != NULL)
        return "pqdif处理进程";
    else if (std::strstr(subdir, "cfg_recallhis_data") != NULL)
        return "稳态补招进程";
    else if (std::strstr(subdir, "cfg_stat_data") != NULL)
        return "稳态统计进程";
    else
        return "unknown";
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void send_reply_to_kafka(const std::string& guid, const std::string& step, const std::string& result) {
    // 构造 JSON 字符串
    std::ostringstream oss;
    oss << "{"
        << "\"guid\":\"" << guid << "\","
        << "\"step\":\"" << step << "\","
        << "\"result\":\"" << result << "\","
		<< "\"processNo\":\"" << g_front_seg_index << "\","
		<< "\"frontType\":\"" << get_front_type_from_subdir() << "\","
		<< "\"nodeId\":\"" << FRONT_INST << "\""
        << "}";

    std::string jsonString = oss.str();

    // 封装 Kafka 消息
    Ckafka_data_t connect_info;
    connect_info.strTopic = QString::fromStdString(Topic_Reply_Topic);
	connect_info.mp_id = QString::fromStdString(guid);//guid作为key
    connect_info.strText = QString::fromStdString(jsonString);

    // 加入发送队列（带互斥锁保护）
    kafka_data_list_mutex.lock();
    kafka_data_list.append(connect_info);
    kafka_data_list_mutex.unlock();
}

void send_reply_to_kafka_recall(const std::string& guid, const std::string& step, const std::string& result,const std::string& lineIndex,const std::string& recallStartDate,const std::string& recallEndDate){
	// 构造 JSON 字符串
    std::ostringstream oss;
    oss << "{"
        << "\"guid\":\"" << guid << "\","
        << "\"step\":\"" << step << "\","
        << "\"result\":\"" << result << "\","
		<< "\"lineIndex\":\"" << lineIndex << "\","
        << "\"recallStartDate\":\"" << recallStartDate << "\","
        << "\"recallEndDate\":\"" << recallEndDate << "\","
		<< "\"processNo\":\"" << g_front_seg_index << "\","
		<< "\"frontType\":\"" << get_front_type_from_subdir() << "\","
		<< "\"nodeId\":\"" << FRONT_INST << "\""
        << "}";

    std::string jsonString = oss.str();

    // 封装 Kafka 消息
    Ckafka_data_t connect_info;
    connect_info.strTopic = QString::fromStdString(Topic_Reply_Topic);
	connect_info.mp_id = QString::fromStdString(guid);//guid作为key
    connect_info.strText = QString::fromStdString(jsonString);

    // 加入发送队列（带互斥锁保护）
    kafka_data_list_mutex.lock();
    kafka_data_list.append(connect_info);
    kafka_data_list_mutex.unlock();
}

void send_heartbeat_to_kafka(const std::string& status) {

	std::string front_type = get_front_type_from_subdir();
    // 构造 JSON 字符串
    std::ostringstream oss;
    oss << "{"
        << "\"nodeId\":\"" << FRONT_INST << "\","
        << "\"frontType\":\"" << front_type << "\","
        << "\"processNo\":\"" << g_front_seg_index << "\","
		<< "\"status\":\"" << status << "\""
        << "}";

    std::string jsonString = oss.str();

	std::string mpid_str = std::to_string(g_node_id) + "_" + std::to_string(g_front_seg_index);

    // 封装 Kafka 消息
    Ckafka_data_t connect_info;
    connect_info.strTopic = QString::fromStdString(Heart_Beat_Topic);
	connect_info.mp_id = QString::fromStdString(mpid_str);
    connect_info.strText = QString::fromStdString(jsonString);

    // 加入发送队列（带互斥锁保护）
    kafka_data_list_mutex.lock();
    kafka_data_list.append(connect_info);
    kafka_data_list_mutex.unlock();
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//找看门狗中
int get_max_stat_data_index(const char* filepath) {
    std::ifstream file(filepath);
    if (!file.is_open()) {
        std::cerr << "Failed to open file: " << filepath << std::endl;
        return -1;
    }

    std::string line;
    int max_value = -1;

    while (std::getline(file, line)) {
        // 查找符合要求的行
        if (line.find("pt61850netd_pqfe -d cfg_stat_data -s") != std::string::npos) {
            // 找到 -s 参数位置
            std::size_t pos = line.find("-s");
            if (pos != std::string::npos) {
                std::istringstream iss(line.substr(pos + 2));
                std::string token;
                iss >> token;

                // 格式应该是 1_2 或类似格式
                std::size_t under_pos = token.find('_');
                if (under_pos != std::string::npos) {
                    std::string first = token.substr(0, under_pos);
                    std::string second = token.substr(under_pos + 1);

                    int val1 = std::atoi(first.c_str());
                    int val2 = std::atoi(second.c_str());

                    if (val1 > max_value) max_value = val1;
                    if (val2 > max_value) max_value = val2;
                }
            }
        }
    }

    file.close();
    return max_value;
}
////////////////////////////////////////////////////////////////////////////////////////////
//保存/更新台账
void save_ledger_json(const char* ptr) {
    if (!ptr) return;

    const char* dir = "/FeProject/dat/ledger";

    // 确保目录存在
    struct stat st;
    if (stat(dir, &st) != 0) {
        mkdir("/FeProject", 0755);          // 可选，确保上级目录存在
        mkdir("/FeProject/dat", 0755);
        mkdir(dir, 0755);
    }

    // 删除已有 *_ledger.txt 文件
    DIR* dp = opendir(dir);
    if (dp) {
        struct dirent* entry;
        while ((entry = readdir(dp)) != NULL) {
            if (strstr(entry->d_name, "_ledger.txt")) {
                char old_file_path[256];
                snprintf(old_file_path, sizeof(old_file_path), "%s/%s", dir, entry->d_name);
                remove(old_file_path); // 删除旧文件
            }
        }
        closedir(dp);
    }

    // 当前时间格式化为 yyyyMMddHHmmss
    char time_buf[32];
    time_t now = time(NULL);
    struct tm* tm_info = localtime(&now);
    strftime(time_buf, sizeof(time_buf), "%Y%m%d%H%M%S", tm_info);

    // 构造文件路径
    char filepath[256];
    snprintf(filepath, sizeof(filepath), "%s/%s_ledger.txt", dir, time_buf);

    // 写入文件
    FILE* fp = fopen(filepath, "w");
    if (fp) {
        fputs(ptr, fp);
        fclose(fp);
    } else {
        perror("fopen ledger file failed");
    }
}

void read_latest_ledger_file(char** out) {
    *out = NULL;
    const char* dir = "/FeProject/dat/ledger";
    DIR* dp = opendir(dir);
    if (!dp) return;

    struct dirent* entry;
    char latest_file[256] = {0};
    time_t latest_time = 0;

    while ((entry = readdir(dp)) != NULL) {
        if (strstr(entry->d_name, "_ledger.txt")) {
            char filepath[256];
            snprintf(filepath, sizeof(filepath), "%s/%s", dir, entry->d_name);
            struct stat st;
            if (stat(filepath, &st) == 0) {
                if (st.st_mtime > latest_time) {
                    latest_time = st.st_mtime;
                    strncpy(latest_file, filepath, sizeof(latest_file) - 1);
                }
            }
        }
    }
    closedir(dp);

    if (latest_file[0] != '\0') {
        FILE* fp = fopen(latest_file, "r");
        if (fp) {
            fseek(fp, 0, SEEK_END);
            long size = ftell(fp);
            fseek(fp, 0, SEEK_SET);
            *out = (char*)malloc(size + 1);
            if (*out) {
                fread(*out, 1, size, fp);
                (*out)[size] = '\0';
            }
            fclose(fp);
        }
    }
}
