/**
 * @file:    $RCSfile: rdb_ext_utils.c,v $
 * @brief:   $ rdb 利用usr_ext扩充的结构的一些工具函数
 *
 * @version: $Revision: 1.11 $
 * @date:    $Date: 2018/12/23 12:39:52 $
 * @author:  $Author: lizhongming $
 * @state:   $State: Exp $
 *
 * @latest:  $Id: rdb_ext_utils.c,v 1.11 2018/12/23 12:39:52 lizhongming Exp $
 */
#include <string.h>
#include "rdb_client.h"
#include "xmltools.h"
#include "db_interface.h"
#include "../cfg_parse/custom_printf.h"//lnk20250225
#include "../log4cplus/log4.h"//lnk添加log4
//extern rdb_t*			g_rdb  ;
extern node_t*			g_node ;
extern char 			g_my_conf_fname[256];
extern apr_pool_t*		g_init_pool;
extern apr_pool_t*		g_run_pool;
extern pt61850app_t*	g_pt61850app;
//extern int				g_sysfile_appid;
//extern char			    *g_sysfile_filedir;
//int                     my_index =0;
//extern ied_info_t       *my_info;
//extern byte_t			g_file_name_len;
//extern byte_t			g_file_time_from;
////////////////////////str 辅助函数//////////////////////////

char* str_trim_both(char* temp,const char* pattern)  
{  // pattern  such as " \t"  or " \t\'" 
	unsigned int   i,j;  
	for(i=0;i<strlen(temp);i++)  {
		if  ( strchr(pattern,temp[i]) == NULL )
			break; 
	}
	temp +=i;

	for(j=(strlen(temp)-1);j>=0;j--)  {
		if  ( strchr(pattern,temp[j])  )
			temp[j] = 0;
		else
			break;           
	}
	return(temp); 
}  


////////////////////////rdb 61850扩充结构 辅助函数//////////////////////////

ied_usr_t* GET_IEDEXT_ADDR(ied_t *ied)
{
	assert(ied);
	return (ied_usr_t* )ied->usr_ext;
}

element_usr_t* GET_DOTEXT_ADDR(element_t *elem)
{
	element_usr_t *elem_usr;
	assert(elem);
	if (elem->usr_ext == NULL) {
		elem_usr = apr_pcalloc(g_init_pool,sizeof(element_usr_t));
		elem_usr->last_soe_status = 99;
		elem->usr_ext = elem_usr;
	}
	return (element_usr_t* )elem->usr_ext;
}

LD_info_t* find_LD_from_cpuCount_and_LEDRs(ied_t* ied, byte_t cpuCount)
{
	LD_info_t	*LD_info = NULL;
	element_t	*elem = NULL;		
	
	LD_info = & (GET_IEDEXT_ADDR(ied)->LD_info[cpuCount]);
	
	elem = apr_hash_get(LD_info->ht_fcd, "LLN0$CO$LEDRs", APR_HASH_KEY_STRING);
	if (elem) 
		return LD_info;
	else 
		return NULL;
}

LD_info_t* find_LD_from_cpuId(ied_t* ied,byte_t cpuId)
{
	int				cpuno;
	LD_info_t*		LD_info = NULL;
	assert(ied);
	for(cpuno=0 ; cpuno<ied->cpucount; cpuno++) {
		if ( ied->cpuinfo[cpuno].addr != cpuId )
			continue;
		LD_info = & (GET_IEDEXT_ADDR(ied)->LD_info[cpuno]) ;
	}
	return LD_info;
}

LD_info_t* find_LD_from_IEDLDName(ied_t* ied, char *IEDLDName)
{
	int				cpuno;
	LD_info_t*		LD_info = NULL;
	assert(ied);
	for(cpuno=0 ; cpuno<ied->cpucount; cpuno++) {
		LD_info = & (GET_IEDEXT_ADDR(ied)->LD_info[cpuno]) ;
		if(LD_info->LD_name){
			if (strcmp(LD_info->LD_name, IEDLDName)==0)
				return LD_info;
		}
	}
	return NULL;
}

LD_info_t* find_LD_from_IEDLDName_in_report(ied_t* ied, char *IEDLDName) 
{
	ST_CHAR *		DomName;
	LD_info_t*		LD_info = NULL;

	DomName = IEDLDName;		//strtok (DataReference, "/");/* extract domain name    */
	LD_info = find_LD_from_IEDLDName(ied, DomName);
	assert(LD_info);
	return LD_info;
}


LD_info_t* find_LD_from_element(element_t* elem)
{
	return find_LD_from_cpuId(elem->ied, (elem->grp->id>>8)&0xff ) ;
}



RCB_INFO* FindRcbInfo(MVL_NET_INFO *net_info,ST_CHAR *dom_name,	ST_CHAR *rcb_name)
{
	ied_t		*ied;
	ied_usr_t  *ied_usr;
	chnl_usr_t *chnl_usr;
	LD_info_t	*LD_info;
	rptinfo_t	*rptinfo = NULL;
	int			cpuno,rpt_no;
	char		rpt_inst_name[65];

	chnl_usr = net_info->user_ext;
	ied = chnl_usr->chnl->ied;
	ied_usr = GET_IEDEXT_ADDR(ied);

	for(cpuno=0 ; cpuno<ied->cpucount; cpuno++) {
		LD_info = &(ied_usr->LD_info[cpuno]);
		//if (!LD_info->LD_name)
		//	continue;
		//添加保护lnk20260602
		if (LD_info->cpuno == 0 ||
		    LD_info->LD_name == NULL ||
		    LD_info->rptcount <= 0 ||
		    LD_info->rptinfo == NULL)
		{
		    continue;
		}

		if ( strcmp(LD_info->LD_name,dom_name)!=0 )
			continue;
		for(rpt_no=0 ; rpt_no<LD_info->rptcount; rpt_no++) {
			rptinfo = LD_info->rptinfo[rpt_no];

			//添加保护lnk20260602
			if (rptinfo == NULL || rptinfo->rptID == NULL)
        		continue;

			get_rpt_inst_name(rptinfo,rpt_inst_name);
			if ( strcmp(rpt_inst_name,rcb_name)==0 )	{
				if (rptinfo->rpt_registered)
					return rptinfo->m_rcb_info;
				else
					return NULL;
			}
		}
	}

	return NULL;
}
////////////////////////////////////////
//WW 2023-08-29 由于rcb_info和dev_type绑定不是和监测点绑定，这里修改规则，保留报告名称匹配
rptinfo_t* find_rptinfo_from_net_rpt_info_name(MVL_NET_INFO *net_info, RCB_INFO *rcb_info)
{
	ied_t		*ied;
	ied_usr_t  *ied_usr;
	chnl_usr_t *chnl_usr;
	LD_info_t	*LD_info;
	rptinfo_t	*rptinfo = NULL;
	int			cpuno,rpt_no;

	chnl_usr = net_info->user_ext;
	ied = chnl_usr->chnl->ied;
	ied_usr = GET_IEDEXT_ADDR(ied);

	for(cpuno=0 ; cpuno<ied->cpucount; cpuno++) {
		LD_info = &(ied_usr->LD_info[cpuno]);

		//添加保护lnk20260602
		if (LD_info->cpuno == 0 ||
		    LD_info->LD_name == NULL ||
		    LD_info->rptcount <= 0 ||
		    LD_info->rptinfo == NULL)
		{
		    continue;
		}

		for(rpt_no=0 ; rpt_no<LD_info->rptcount; rpt_no++) {
			rptinfo = LD_info->rptinfo[rpt_no];

			//添加保护lnk20260602
			if (rptinfo == NULL || rptinfo->rptID == NULL || rcb_info == NULL || rcb_info->RptID == NULL)
        		continue;

			printf("%d rptinfo %s,rcbinfo %s  ", rpt_no, rptinfo->rptID, rcb_info->RptID);
			if (strcmp(rcb_info->RptID,rptinfo->rptID)==0)//WW 修改为匹配字符串
					return rptinfo;
		}
	}
	return NULL;
}
//WW 2023-08-29 注释
////////////////////////////////////////

////////////////////////////////////////
//WW 2023-08-29 注释
rptinfo_t* find_rptinfo_from_net_rcb_info(MVL_NET_INFO *net_info,RCB_INFO *rcb_info)
{
	ied_t		*ied;
	ied_usr_t  *ied_usr;
	chnl_usr_t *chnl_usr;
	LD_info_t	*LD_info;
	rptinfo_t	*rptinfo = NULL;
	int			cpuno,rpt_no;

	chnl_usr = net_info->user_ext;
	ied = chnl_usr->chnl->ied;
	ied_usr = GET_IEDEXT_ADDR(ied);

	for(cpuno=0 ; cpuno<ied->cpucount; cpuno++) {
		LD_info = &(ied_usr->LD_info[cpuno]);

		//添加保护lnk20260602
		if (LD_info->cpuno == 0 ||
		    LD_info->LD_name == NULL ||
		    LD_info->rptcount <= 0 ||
		    LD_info->rptinfo == NULL)
		{
		    continue;
		}

		for(rpt_no=0 ; rpt_no<LD_info->rptcount; rpt_no++) {
			rptinfo = LD_info->rptinfo[rpt_no];

			if (rptinfo == NULL)
        		continue;

			if (rcb_info==rptinfo->m_rcb_info)
					return rptinfo;
		}
	}
	return NULL;
}
////////////////////////////////////////
//WW 2023-08-29 注释 end
/*void get_rpt_inst_name(rptinfo_t	*rptinfo, char	* rpt_inst_name )
{
	strcpy(rpt_inst_name,rptinfo->rptID);
	if (rptinfo->instanceNeedSuffix)	{
		char rpt_suffix_str[8];
		apr_snprintf(rpt_suffix_str,sizeof(rpt_suffix_str),"%02d",rptinfo->m_curRptSuffix);
		strcat(rpt_inst_name,rpt_suffix_str);
	}
} */
void get_rpt_inst_name(rptinfo_t *rptinfo, char *rpt_inst_name)
{
    if (rpt_inst_name == NULL)
    {
        printf("get_rpt_inst_name: rpt_inst_name is NULL\n");
        return;
    }

    rpt_inst_name[0] = '\0';

    if (rptinfo == NULL)
    {
        printf("get_rpt_inst_name: rptinfo is NULL\n");
        strcpy(rpt_inst_name, "NULL_RPT");
        return;
    }

    if (rptinfo->rptID == NULL)
    {
        printf("get_rpt_inst_name: rptID is NULL\n");
        strcpy(rpt_inst_name, "NULL_RPT");
        return;
    }

    printf("[RPT] rptinfo=%p rptID=%s suffix=%d needSuffix=%d\n",
           rptinfo,
           rptinfo->rptID,
           rptinfo->m_curRptSuffix,
           rptinfo->instanceNeedSuffix);

    // 原代码
    strncpy(rpt_inst_name, rptinfo->rptID, 64);
    rpt_inst_name[64] = '\0';

    if (rptinfo->instanceNeedSuffix)
    {
        char rpt_suffix_str[8] = {0};

        apr_snprintf(rpt_suffix_str,
                     sizeof(rpt_suffix_str),
                     "%02d",
                     rptinfo->m_curRptSuffix);

        // 防止越界
        strncat(rpt_inst_name,
                rpt_suffix_str,
                64 - strlen(rpt_inst_name));
    }
}
  

void strip_file_name_tail_to_ms(char *fileName)
{
	char	*p = NULL;

	assert(fileName);
	assert(strlen(fileName)<128);
	assert(strlen(fileName)>0);
	p = fileName;
	while(*p != '.') {
		p++;
	}
	while((p>fileName)&&(*p<0x30 || *p>0x39)) {
		p--;
	}
	*++p = '\0';
}
 
static void _cut_file_name(char *fileName, byte_t len)
{
	size_t	full_len = strlen(fileName);	

	if (full_len<=len) 
		return;
	else {
		memmove(fileName, fileName+(full_len-len), len);
		*(fileName+len) = '\0';
	}
}
 
////////////////////////////////////////////////////////////////////////////////////////////////////

ied_t* find_ied_from_dev_idx(int dev_idx)
{
	ied_t		*ied = NULL; 
	int			iedno;
	ied_usr_t	*ied_usr = NULL; 

	for(iedno=0; iedno<g_node->n_clients; iedno++) {
		ied = g_node->clients[iedno];	
		ied_usr = (ied_usr_t*)ied->usr_ext; 
		if (ied_usr && ied_usr->dev_idx == dev_idx )
			return ied;
	}
	return NULL;
}

ied_t* find_ied_from_dev_code(char dev_idx[])
{
	ied_t* ied = NULL;
	int			iedno;
	ied_usr_t* ied_usr = NULL;
	for (iedno = 0; iedno < g_node->n_clients; iedno++) {
		ied = g_node->clients[iedno];
		ied_usr = (ied_usr_t*)ied->usr_ext;
		if (ied_usr && strcmp(ied_usr->terminal_code, dev_idx) == 0) {
			return ied;
		}

	}
	return NULL;
}

//lnk20250114新增通过终端id查找终端
ied_t* find_ied_from_terminal_id(char terminal_id[])
{
	ied_t* ied = NULL;
	int			iedno;
	ied_usr_t* ied_usr = NULL;
	for (iedno = 0; iedno < g_node->n_clients; iedno++) {
		ied = g_node->clients[iedno];
		ied_usr = (ied_usr_t*)ied->usr_ext;
		if (ied_usr && strcmp(ied_usr->terminal_id, terminal_id) == 0) {
			return ied;
		}

	}
	return NULL;
}
//lnk20250121找不使用的ied空间
ied_t* find_ied_unused()
{
	ied_t* ied_find_unused = NULL;
	int			iedno;
	ied_usr_t* ied_usr_find_unused = NULL;
	for (iedno = 0; iedno < g_node->n_clients; iedno++) {
		ied_find_unused = g_node->clients[iedno];
		ied_usr_find_unused = (ied_usr_t*)ied_find_unused->usr_ext;
		if (ied_usr_find_unused && ied_usr_find_unused->dev_flag == UNUSED) {
			return ied_find_unused;//找到第一个就返回
		}
	}
	return NULL;
}

LD_info_t* find_LD_info_from_line_id(ied_t* ied, int line_id)
{ 
	LD_info_t		*LD_info = NULL;
	ied_usr_t	*ied_usr = GET_IEDEXT_ADDR(ied);
	int			cpuno;

	for(cpuno=0 ; cpuno<ied->cpucount; cpuno++) {
		LD_info = &(ied_usr->LD_info[cpuno]); 
		if (LD_info && LD_info->line_id==line_id)
			return LD_info;
	} 
	return NULL;
}

LD_info_t* find_LD_info_only_from_line_id(int line_id)
{
	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 = find_LD_info_from_line_id(ied,line_id);
			if (LD_info)
				return LD_info;
		}
	}
	return NULL;
}

LD_info_t* find_LD_info_from_mp_id(ied_t* ied, char* mp_id)
{

	if (mp_id == NULL)
    	return NULL;

	LD_info_t* LD_info = NULL;
	ied_usr_t* ied_usr = GET_IEDEXT_ADDR(ied);
	int			cpuno;

	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;

		if (LD_info && strcmp(LD_info->mp_id, mp_id) == 0)
			return LD_info;
	}
	return NULL;
}

LD_info_t* find_LD_info_only_from_mp_id(char* mp_id)
{
	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 = find_LD_info_from_mp_id(ied, mp_id);
			if (LD_info)
				return LD_info;
		}
	}
	return NULL;
}
 
void clear_all_LD_real_soe_report_shoud_register()
{
	ied_t		*ied = NULL; 
	int			iedno;
	ied_usr_t	*ied_usr = NULL; 
	LD_info_t	*LD_info = NULL;
	int			cpuno;

	for(iedno=0; iedno<g_node->n_clients; iedno++) {
		ied = g_node->clients[iedno];	
		ied_usr = (ied_usr_t*)ied->usr_ext; 
		for(cpuno=0 ; cpuno<ied->cpucount; cpuno++) {
			LD_info = &(ied_usr->LD_info[cpuno]); 
			if (LD_info )  {
				LD_info->real_data = 0;
				LD_info->soe_data = 0;
			}
		} 
	}
}

void clear_one_LD_real_soe_report_shoud_register(int dev_index,int line_index)
{
	ied_t		*ied;
	LD_info_t	*LD_info;

	ied = find_ied_from_dev_idx(dev_index);
	if (!ied)
		return; 
	LD_info = find_LD_info_from_line_id(ied,line_index);
	if (!LD_info)
		return;
	if (LD_info )  {
		LD_info->real_data = 0;
		LD_info->soe_data = 0;
	}
}

void clear_rpt_counter_by_trigger(trigger_t *trigger)
{
	ied_t		*ied;
	LD_info_t	*LD_info;
	int			rpt_no;
	rptinfo_t	*rptinfo = NULL;

	trigger->count = 0;
	ied = find_ied_from_dev_idx(trigger->dev_idx);
	if (!ied)
		return; 
	LD_info = find_LD_info_from_line_id(ied,trigger->line_id);
	if (!LD_info)
		return;
	LD_info->count = 0;
	for(rpt_no=0 ; rpt_no<LD_info->rptcount; rpt_no++) {
			rptinfo = LD_info->rptinfo[rpt_no];
			rptinfo->count = 0;
	}
}

int get_real_report_count(LD_info_t	*LD_info)
{ 
	int			rpt_no;
	rptinfo_t	*rptinfo = NULL; 

	for(rpt_no=0 ; rpt_no<LD_info->rptcount; rpt_no++) {
		rptinfo = LD_info->rptinfo[rpt_no];
		//if (rptinfo->report_PQ_type & REPORT_TYPE_REAL)//遍历所有报告判断报告类型
		if (rptinfo->report_PQ_type)//上层已判断实时数据，不需要再判断
			return rptinfo->count;
	}
	return 0;
}

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

////////////////////电压波动处理/////////////////////////////
void processQVVR_start(LD_info_t* LD_info)
{
	int			i; 

	for (i=0;i<QVVR_NUM;i++) {
		if (LD_info->qvvr[i].used_status==QVVR_DATA_NOT_USED) {
			LD_info->qvvr_idx = i;	//这个点没使用，使用这个点
			break;
		}
	}
	if (i>=QVVR_NUM) {	//已经使用到了最后一个点
		uint32_t timestamp = LD_info->qvvr[0].timestamp;//第一个点的时间
		LD_info->qvvr_idx = 0;//从第一个点开始记录
		echo_warn1("QVVR data memory is full,to replace the oldest,line_id=%d \n",LD_info->line_id);
		for (i=1;i<QVVR_NUM;i++) {
			if (LD_info->qvvr[i].timestamp<timestamp) {//找到时间最小的点，使用它的位置
				LD_info->qvvr_idx = i;
				timestamp = LD_info->qvvr[i].timestamp;
			}
		}
	}
	
	LD_info->qvvr[LD_info->qvvr_idx].used_status = QVVR_DATA_RECEIVED;//这个点标记为收到暂态数据
	LD_info->qvvr[LD_info->qvvr_idx].QVVR_type = 0;	//类型未定
	LD_info->qvvr[LD_info->qvvr_idx].timestamp = apr_time_sec(apr_time_now());//记录当前时间
	printf("\n~~~~~~~~~~~~~~~~~ processQVVR_start: line_id=%d \n",LD_info->line_id);
}

void processQVVR_time(LD_info_t* LD_info, long long Time)
{
	LD_info->qvvr[LD_info->qvvr_idx].QVVR_time = Time;
	printf("\n~~~~~~~~~~~~~~~~~ processQVVR_time: line_id=%d ,Time=%lld \n",LD_info->line_id,Time);//这里是报告上送的时间
}


void processQVVR_data(LD_info_t* LD_info,char* FULL_FCDA_Name,double v)
{ 
	if ( strstr(FULL_FCDA_Name,"VarStr$stVal")  ) {
		LD_info->qvvr[LD_info->qvvr_idx].QVVR_start = (v>0.99) ? 1:0;
	}
	else if ( strstr(FULL_FCDA_Name,"VVaTm$mag$f")  )
		LD_info->qvvr[LD_info->qvvr_idx].QVVR_PerTime = (float)v;

	else if ( strstr(FULL_FCDA_Name,"VVa$mag$f") )
		LD_info->qvvr[LD_info->qvvr_idx].QVVR_Amg = (float)v;
	else  {
		if ( strstr(FULL_FCDA_Name,"DipStr$stVal") && (v>0.99) )  //电压暂降
			LD_info->qvvr[LD_info->qvvr_idx].QVVR_type = 1210002;//按照接口修改
		else if ( strstr(FULL_FCDA_Name,"SwlStr$stVal") && (v>0.99) ) //电压暂升
			LD_info->qvvr[LD_info->qvvr_idx].QVVR_type = 1210001;
		else if ( strstr(FULL_FCDA_Name,"IntrStr$stVal") && (v>0.99) )  //电压中断
			LD_info->qvvr[LD_info->qvvr_idx].QVVR_type = 1210004;

	}

	if (strstr(FULL_FCDA_Name, "QVVR")) {
		apr_snprintf(LD_info->qvvr[LD_info->qvvr_idx].QVVR_Rptname, sizeof(LD_info->qvvr[LD_info->qvvr_idx].QVVR_Rptname), "%s", FULL_FCDA_Name);//terminal_code
	}
	 
	printf("\n~~~~~~~~~~~~~~~~~ processQVVR_data: line_id=%d ,mms_str=%s,v=%f \n",LD_info->line_id,FULL_FCDA_Name,v);

}


void processQVVR_end(LD_info_t* LD_info)
{
	//lnk20241227lnk添加暂态事件直接上报，等录波事件上来后，那边会再上报一次更新文件路径，这里不更新文件路径
	ied_t		*ied = LD_info->ied;
	ied_usr_t  *ied_usr = GET_IEDEXT_ADDR(ied);
	int ret;

	////////////////////////////////////////////////////////////////////////////////////////////////////////////
	int			find_paired = FALSE;
	int			i; 

	if (LD_info->qvvr[LD_info->qvvr_idx].QVVR_start)//波动数据处理时FCDA包含VarStr$stVal，QVVR_start为1，退出处理
		return;

	//获取时间类型lnk20250520
	printf("~~~~~~~this dev type is %s~~~~~~~",ied_usr->dev_type);
	XmlConfigC cfg;
	if (get_xml_config_by_dev_type(ied_usr->dev_type, &cfg)) {
	    printf("UnitOfTimeUnit = %s\n", cfg.UnitOfTimeUnit);
	    printf("ValueOfTimeUnit = %s\n", cfg.ValueOfTimeUnit);
	} else {
	    printf("读取失败，未找到 dev_type\n");
	}
	double s_or_ms;
	long long utc_or_beijing;
	if(strcmp(cfg.UnitOfTimeUnit, "1") == 0){//持续时间上送的是秒1还是毫秒0
		s_or_ms = 0.001;
		//DIY_WARNLOG_CODE(LD_info->mp_id,2,LOG_CODE_TRANSIENT,"【WARN】监测点:%s - id:%s上送的暂态持续时间单位是秒,监测点对应装置型号:%s",LD_info->name,LD_info->mp_id,ied_usr->dev_type);
	}
	else{
		s_or_ms = 1.0;
		//DIY_WARNLOG_CODE(LD_info->mp_id,2,LOG_CODE_TRANSIENT,"【WARN】监测点:%s - id:%s上送的暂态持续时间单位是毫秒,监测点对应装置型号:%s",LD_info->name,LD_info->mp_id,ied_usr->dev_type);
	}

	if(strcmp(cfg.ValueOfTimeUnit, "utc") == 0){//上送的是utc还是北京
		utc_or_beijing = 0;//61850库已经转换过了，八小时毫秒数28800000;
	}
	else{
		utc_or_beijing = 0;
	}


	for (i=0;i<QVVR_NUM;i++) {		//暂降/暂升/中断等进入处理，遍历所有波动位置
		if (i==LD_info->qvvr_idx)	//跳过监测点当前波动位置,不会和自己做匹配。第一次记录波形后，第一次的第一个事件会让0号点为QVVR_DATA_RECEIVED，
									//第二次qvvr_idx=1，1号中为QVVR_DATA_RECEIVED，QVVR_start=0，会和0点做匹配
		{
			printf("skip this index\n");
			continue;
		}
			
		if (LD_info->qvvr[i].used_status != QVVR_DATA_RECEIVED)//跳过没有收到波动的位置
		{
			printf("qvvr %d is not received\n",i);
			continue;

		}
			
		//添加时间判断lnk20250311，当前的时间减去找到的时间应该等于持续时间
		if (fabs((LD_info->qvvr[LD_info->qvvr_idx].QVVR_time - LD_info->qvvr[i].QVVR_time)/1.0 //判断秒还是毫秒
         - LD_info->qvvr[LD_info->qvvr_idx].QVVR_PerTime/s_or_ms) > 1.0){
			printf("~~~~~~~fail in pair qvvr node %f~~~~~~~~~~ \n",fabs((LD_info->qvvr[LD_info->qvvr_idx].QVVR_time - LD_info->qvvr[i].QVVR_time)/1.0 - LD_info->qvvr[LD_info->qvvr_idx].QVVR_PerTime/s_or_ms));
			continue;
		 }
			

		
		//其他某位置的波动也没有定义类型/这个位置的波动类型和当前位置的波动类型一致，暂降事件一次会发三个报告,启动和上值和结束
		if ( (LD_info->qvvr[i].QVVR_type==0)||(LD_info->qvvr[i].QVVR_type==LD_info->qvvr[LD_info->qvvr_idx].QVVR_type) ) {

			//long long end_tm = (long long)(LD_info->qvvr[LD_info->qvvr_idx].QVVR_PerTime*1000) + LD_info->qvvr[i].QVVR_time;//结束时间是持续时间加最初的触发时间，毫秒
			long long end_tm = (long long)(LD_info->qvvr[LD_info->qvvr_idx].QVVR_PerTime/s_or_ms) + LD_info->qvvr[i].QVVR_time;//结束时间是持续时间加最初的触发时间，毫秒

			//调试用
			printf("\n~~~~~~~now qvvr node type before record is %d~~~~~~~~~~ \n",LD_info->qvvr[LD_info->qvvr_idx].QVVR_type);
			printf("~~~~~~~now qvvr node QVVR_PerTime before record is %f~~~~~~~~~~ \n",LD_info->qvvr[LD_info->qvvr_idx].QVVR_PerTime);
			printf("~~~~~~~now qvvr node QVVR_Amg before record is %f~~~~~~~~~~ \n",LD_info->qvvr[LD_info->qvvr_idx].QVVR_Amg);
			printf("~~~~~~~now qvvr node QVVR_time before record is %lld~~~~~~~~~~ \n",LD_info->qvvr[LD_info->qvvr_idx].QVVR_time);
			printf("~~~~~~~now qvvr node used_status before record is %d~~~~~~~~~~ \n",LD_info->qvvr[LD_info->qvvr_idx].used_status);
			printf("~~~~~~~now qvvr node QVVR_start before record is %d~~~~~~~~~~ \n",LD_info->qvvr[LD_info->qvvr_idx].QVVR_start);
			printf("~~~~~~~now qvvr node timestamp before record is %d~~~~~~~~~~ \n",LD_info->qvvr[LD_info->qvvr_idx].timestamp);

			LD_info->qvvr[i].used_status = QVVR_DATA_PAIRED;	//匹配上了
			LD_info->qvvr[i].QVVR_type = LD_info->qvvr[LD_info->qvvr_idx].QVVR_type; 
			LD_info->qvvr[i].QVVR_PerTime = LD_info->qvvr[LD_info->qvvr_idx].QVVR_PerTime;//存在内存的还是原始读出的数据
			LD_info->qvvr[i].QVVR_Amg = LD_info->qvvr[LD_info->qvvr_idx].QVVR_Amg;			//记录当前这个点的波动数据到找到的点位置上，确保记录的是最新的相同类型的事件

			LD_info->qvvr[LD_info->qvvr_idx].used_status = QVVR_DATA_NOT_USED; //当前这个点位置释放
			find_paired = TRUE;

			//调试用
			printf("~~~~~~~this qvvr node QVVR_PerTime after record is %f~~~~~~~~~~ \n",LD_info->qvvr[i].QVVR_PerTime);
			printf("~~~~~~~this qvvr node QVVR_Amg after record is %f~~~~~~~~~~ \n",LD_info->qvvr[i].QVVR_Amg);
			printf("~~~~~~~this qvvr node used_status after record is %d~~~~~~~~~~ \n",LD_info->qvvr[i].used_status);
			printf("~~~~~~~this qvvr node QVVR_type after record is %d~~~~~~~~~~ \n",LD_info->qvvr[i].QVVR_type);
			printf("~~~~~~~this qvvr node QVVR_time after record is %lld~~~~~~~~~~ \n",LD_info->qvvr[i].QVVR_time);
			printf("~~~~~~~this qvvr node timestamp before record is %d~~~~~~~~~~ \n",LD_info->qvvr[i].timestamp);
			printf("~~~~~~~this qvvr node QVVR_start before record is %d~~~~~~~~~~ \n",LD_info->qvvr[i].QVVR_start);

			//匹配后再发qvvr，起始时间要填暂态触发的时间，就是第一次事件上送时只有时间没有值的那个时间
			//mq日志
			DIY_WARNLOG_CODE(LD_info->mp_id,2,LOG_CODE_TRANSIENT,"【WARN】监测点%s - id:%s 发生暂态事件，暂态时间：%lld，暂态持续时间：%f，暂态幅值：%f，暂态类型：%d",LD_info->name,LD_info->mp_id,
				LD_info->qvvr[LD_info->qvvr_idx].QVVR_time,
				LD_info->qvvr[LD_info->qvvr_idx].QVVR_PerTime,
				LD_info->qvvr[LD_info->qvvr_idx].QVVR_Amg,
				LD_info->qvvr[LD_info->qvvr_idx].QVVR_type);

			ret = transfer_json_qvvr_data(g_node_id, 	//这个参数没有使用
				LD_info->line_id, 							//监测点序号
				(double)LD_info->qvvr[LD_info->qvvr_idx].QVVR_Amg,
				(double)LD_info->qvvr[LD_info->qvvr_idx].QVVR_PerTime/s_or_ms, //上传转换为ms
				LD_info->qvvr[i].QVVR_time + utc_or_beijing, //上传北京时间
				end_tm + utc_or_beijing, //上传北京时间
				LD_info->qvvr[LD_info->qvvr_idx].QVVR_type, //伏值、持续时间、开始时间、结束时间、暂态类型
				"", 
				"", //两个文件路径为空
				LD_info->mp_id, 
				LD_info->qvvr[LD_info->qvvr_idx].QVVR_Rptname, 
				ied_usr->dev_type);//监测点号，文件和监测点暂态事件匹配上的暂态报告名，终端类型
			if(!ret)//失败
			{
				printf("\n~~~~~~~~~~~~~~~~~ QVVR_json_data send error: line_id=%d \n",LD_info->line_id);
			}

			break;
		}
	}

	if (find_paired == FALSE)  {
		LD_info->qvvr[LD_info->qvvr_idx].used_status = QVVR_DATA_NOT_USED; //全都没有匹配上直接释放这个点，下次事件可以直接在这个点上使用
		printf("\nERROR:~~~~~~~~~~~~~ processQVVR qvvr returned to 0,but found no data to pair!, line_id=%d,QVVR_type=%d  \n",
			LD_info->line_id, LD_info->qvvr[LD_info->qvvr_idx].QVVR_type);

		//mq日志
		if(strcmp(cfg.UnitOfTimeUnit, "1") == 0){//持续时间上送的是秒1还是毫秒0
			DIY_WARNLOG_CODE(LD_info->mp_id,2,LOG_CODE_TRANSIENT,"【WARN】监测点:%s - id:%s发生了暂态事件但是没有记录,可能是配置的暂态持续时间不匹配，当前装置型号:%s ,当前使用的配置的暂态持续时间单位是s",LD_info->name,LD_info->mp_id,ied_usr->dev_type);
		}
		else{
			DIY_WARNLOG_CODE(LD_info->mp_id,2,LOG_CODE_TRANSIENT,"【WARN】监测点:%s - id:%s发生了暂态事件但是没有记录,可能是配置的暂态持续时间不匹配，当前装置型号:%s ,当前使用的配置的暂态持续时间单位是ms",LD_info->name,LD_info->mp_id,ied_usr->dev_type);
		}

	}

	printf("\n~~~~~~~~~~~~~~~~~ processQVVR_end: line_id=%d \n",LD_info->line_id);
}

// 函数功能：根据触发时间戳（trig_tm）查找匹配的 QVVR 数据。
// 输入参数：
//    - LD_info：指向 LD_info_t 类型结构体的指针，包含多个 QVVR 数据。
//    - trig_tm：触发时间戳，用于与 QVVR 数据中的时间进行匹配。
// 输出参数：
//    - 返回匹配的 QVVR 数据的指针。如果没有找到匹配的 QVVR 数据，则返回 NULL。
// 返回值：
//    - 如果找到符合条件的 QVVR 数据，返回指向该数据的指针。
//    - 如果没有找到符合条件的 QVVR 数据，返回 NULL。
QVVR_t* find_qvvr_by_trig_tm(LD_info_t* LD_info, long long trig_tm)
{
    const long long EIGHT_HOURS_MS = 8LL * 60 * 60 * 1000;

    long long candidates[3] = {
        trig_tm,
        trig_tm - EIGHT_HOURS_MS,
        trig_tm + EIGHT_HOURS_MS
    };

    for (int c = 0; c < 3; ++c) {

        long long cmp_tm = candidates[c];

        printf("===== try trig_tm:%lld offset:%lld =====\n",
               cmp_tm,
               cmp_tm - trig_tm);

        for (int i = 0; i < QVVR_NUM; i++) {

            long long diff =
                llabs(LD_info->qvvr[i].QVVR_time - cmp_tm);

            printf("QVVRTIME:%lld >>>>> COMTRADE trig_tm:%lld >>>>> diff:%lld\n",
                   LD_info->qvvr[i].QVVR_time,
                   cmp_tm,
                   diff);

            if ((LD_info->qvvr[i].used_status == QVVR_DATA_PAIRED)
                && (diff <= 1))
            {
                printf(">>>>> pair QVVR success>>>>>> offset=%lld\n",
                       cmp_tm - trig_tm);

                return &(LD_info->qvvr[i]);
            }
        }
    }

    printf(">>>>> pair QVVR fail>>>>>>\n");

    return NULL;
}
#if 0
QVVR_t*  find_qvvr_by_trig_tm(LD_info_t* LD_info,long long trig_tm)
{
	long long 	diff;
	int			i;  

	for (i=0;i<QVVR_NUM;i++) { 
		diff = abs(LD_info->qvvr[i].QVVR_time-trig_tm);
		if ( (LD_info->qvvr[i].used_status==QVVR_DATA_PAIRED) && (diff<=1) ) {
			return &(LD_info->qvvr[i]); 
		}
	}
	return NULL;
}
#endif
////////////////////录波文件处理/////////////////////////////

void processRDRE_start(LD_info_t* LD_info)
{
	LD_info->RDRE_FltNum = -1;
	printf("\n~~~~~~RDRE~~~~~~  processRDRE_start: line_id=%d \n",LD_info->line_id);
} 

void processRDRE_data(LD_info_t* LD_info,char* FULL_FCDA_Name,double v)
{
	if ( strstr(FULL_FCDA_Name,"FltNum$stVal") )
		LD_info->RDRE_FltNum = (int)(v+0.4);	  

	printf("\n~~~~~~RDRE~~~~~~ processRDRE_data: line_id=%d ,mms_str=%s,v=%f \n",LD_info->line_id,FULL_FCDA_Name,v);
} 

void processRDRE_end(LD_info_t* LD_info)
{
	if (LD_info->RDRE_FltNum<=0)
		return;

	prepare_call_cn_wavelist(LD_info,LD_info->RDRE_FltNum);
	printf("\n~~~~~~RDRE~~~~~~ processRDRE_end: line_id=%d \n",LD_info->line_id);
}

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