/**
* @file:    $RCSfile: mmscli_rpt.c,v $
* @brief:   $IEC 61850 Protocol
*
* @version: $Revision: 1.12 $
* @date:    $Date: 2022/11/28 07:13:13 $
* @author:  $Author: lizhongming $
* @state:   $State: Exp $
*
* @latest:  $Id: mmscli_rpt.c,v 1.12 2022/11/28 07:13:13 lizhongming Exp $
*
*/
//$Header: /JoyProject/jspqfe/src/pt61850netd_pqfe/source/mms/mmscli_rpt.c,v 1.12 2022/11/28 07:13:13 lizhongming Exp $
/************************************************************************/
/* SISCO SOFTWARE MODULE HEADER *****************************************/
/************************************************************************/
/*	(c) Copyright Systems Integration Specialists Company, Inc.,	*/
/*	2003 - 2005, All Rights Reserved				*/
/*									*/
/* MODULE NAME : cli_rpt.c						*/
/* PRODUCT(S)  : MMSEASE-LITE						*/
/*									*/
/* MODULE DESCRIPTION :							*/
/*	Functions to perform "client" processing of IEC-61850 Reports	*/
/*	and UCA Reports received from "servers".			*/
/*									*/
/* GLOBAL FUNCTIONS DEFINED IN THIS MODULE :				*/
/*	rpt_typeids_find						*/
/*	rcb_info_create							*/
/*	rcb_info_destroy						*/
/*	u_iec_rpt_ind							*/
/*	u_iec_rpt_ind_data (user modify or replace)			*/
/*									*/
/* MODIFICATION LOG :							*/
/*  Date     Who   Rev			Comments			*/
/* --------  ---  ------   -------------------------------------------	*/
/* 07/22/05  JRB     08    Allow one conn to control multiple RCBs	*/
/*		 	   (user_info must point to ALL_RCB_INFO).	*/
/*			   Save varNameArray in rcb_info to use later.	*/
/*			   Chg u_iec_rpt_ind_data 4th arg to (RCB_INFO *).*/
/* 12/03/04  JRB     07    Extract domain name of NVL from dataSetName.	*/
/*			   Increase RptID length to vstring65.		*/
/* 08/04/04  EJV     06    rcb_info_create: added typecast for Inclusion*/
/* 06/29/04  JRB     05    Del mvl_tdl_to_type_id & instead use new	*/
/*			   mvl_type_id_create_from_tdl.			*/
/* 05/13/04  JRB     04    Chg SqNum to INT16U to match 61850-7-2.	*/
/* 01/23/04  JRB     03    Use ms_local_to_text.			*/
/* 12/17/03  JRB     02    61850-8-1 FDIS changes:			*/
/*			   Decode ConfRev in rpt if enabled by OptFlds.	*/
/*			   Move SubSeqNum, MoreSegmentsFollow to just	*/
/*			   before inclusion bitstring.			*/
/*			   Chg OptFlds from bvstring9 to bvstring10.	*/
/* 10/24/03  JRB     01    New						*/
/************************************************************************/

#include "rdb_client.h"
#include "db_interface.h"
#include "../json/mms_json_inter.h"
#include "../cfg_parse/custom_printf.h"//lnk20250225
#include "../log4cplus/log4.h"//lnk添加log4 20250526
/************************************************************************/
/* For debug version, use a static pointer to avoid duplication of 	*/
/* __FILE__ strings.							*/
/************************************************************************/

#ifdef DEBUG_SISCO
SD_CONST static ST_CHAR* SD_CONST thisFileName = __FILE__;
#endif

extern pt61850app_t* g_pt61850app;
extern RPT_TYPEIDS g_rpt_typeids;
//lnk20250115
extern pthread_mutex_t mtx;

/************************************************************************/
/* Global variables.							*/
/************************************************************************/
/* NONE	*/

/************************************************************************/
/* Static function prototypes.						*/
/************************************************************************/
static OBJECT_NAME** nvl_var_name_array_create(MVL_NET_INFO* net_info, ST_CHAR* domName,
	ST_CHAR* nvlName, ST_INT* numVarOut, ST_INT timeOut);
static ST_VOID nvl_var_name_array_destroy(OBJECT_NAME** varNameArray, ST_INT numVar);
static ST_INT var_type_create(MVL_NET_INFO* net_info, OBJECT_NAME* varObj,
	ST_INT timeOut);
static ST_RET rcb_info_var_create(RCB_INFO* rcb_info, RPT_TYPEIDS* rpt_typeids);
static ST_VOID rcb_info_var_destroy(RCB_INFO* rcb_info);
static ST_VOID log_var_data(MVL_VAR_ASSOC* var, MMS_DECODE_DATA* data);

/* DEBUG: move these functions to library modules?	*/
OBJECT_NAME* object_name_clone_create(OBJECT_NAME* srcobj);
ST_VOID object_name_clone_destroy(OBJECT_NAME* obj);

static ST_ULONG a_get_rem_ip_addr_inline(ST_LONG acse_conn_id, int* nPort);
static char* _convert_ip_2_char(unsigned long dwIP);

#if 1  //测试  获取服务器IP  WW 2023-08-29
static char* _convert_ip_2_char(unsigned long dwIP)
{
	static char buf[64] = { 0 };
	sprintf(buf, "%d.%d.%d.%d", (dwIP >> 24) & 0xff, (dwIP >> 16) & 0xff, (dwIP >> 8) & 0xff, (dwIP) & 0xff);
	return  buf;
}

#if defined (TP0_ENABLED)
/************************************************************************/
/*			a_get_rem_ip_addr				*/
/* This function returns the remote IP Address as an unsigned long in	*/
/* "network byte order", just like the "standard sockets" function	*/
/* "inet_addr" does. If there is an error, it returns			*/
/* "htonl (INADDR_NONE)", just like "inet_addr" does.			*/
/*   The return value can be stored in the appropriate union member	*/
/* in the structure "in_addr", which can be passed to "inet_ntoa".	*/
/************************************************************************/
#include "tp4.h"			/* Need internal TP4 defines	*/
#include "tp0_sock.h"			/* Need "sockets" defines	*/
#include "acse2.h"			/* Need "sockets" defines	*/
#define MAX_PATH 256
static ST_ULONG get_rem_ip_addr_inline(ST_LONG acse_conn_id, int* nPort)
{
	ACSE_CONN *acse_conn;
	ST_LONG tp_conn_id;
	TP0_CONN *tp0_conn;
	ST_INT ret;
	SOCKADDR_IN sockaddr_in;
	SOCK_ADDRLEN addr_len;
	char buf[MAX_PATH] = { 0 };
	acse_conn = (ACSE_CONN *)acse_conn_id;
	tp_conn_id = acse_conn->tp4_conn_id;
	if (tp_conn_id >= MIN_TP0_CONN_ID &&
		tp_conn_id < tp0_cfg.max_num_conns + MIN_TP0_CONN_ID)
	{				/* Conn id is a legal TP0 conn id	*/
		tp0_conn = &tp0_conn_arr[tp_conn_id - MIN_TP0_CONN_ID];

		addr_len = sizeof(SOCKADDR_IN);	/* CRITICAL: set to expected len.*/
		ret = getpeername(tp0_conn->sock_info->genSock->hSock, (SOCKADDR *)&sockaddr_in, &addr_len);
		*nPort = htons(sockaddr_in.sin_port);
		if (ret == 0)
			return (sockaddr_in.sin_addr.s_addr);
	}

	return (htonl(INADDR_NONE));		/* something failed	*/
}
#endif	/* defined (TP0_ENABLED)	*/
#endif

/************************************************************************/
/*			rpt_typeids_find				*/
/* Find ALL types needed for controlling & decoding IEC/UCA reports.	*/
/* These types must be defined in the ODF file and created by Foundry.	*/
/* RETURN: SD_SUCCESS or SD_FAILURE (if ANY type is NOT found)		*/
/* NOTE: this function based on "mvlu_rpt_find_typeids" in "mvlu_rpt.c".*/
/************************************************************************/
ST_RET rpt_typeids_find(RPT_TYPEIDS* rpt_typeids)
{
	ST_RET retCode = SD_FAILURE;	/* assume FAILURE for now	*/
	ST_CHAR* type_name;		/* name of type to be found	*/

	/* stop on any error	*/
	do
	{	/* "one-time" loop: just to have something to break out of	*/
		if ((rpt_typeids->mmsbool = mvl_typename_to_typeid(type_name = "RTYP_BOOL")) < 0)
			break;
		if ((rpt_typeids->bstr6 = mvl_typename_to_typeid(type_name = "RTYP_BSTR6")) < 0)
			break;
		if ((rpt_typeids->bstr8 = mvl_typename_to_typeid(type_name = "RTYP_BSTR8")) < 0)
			break;
		if ((rpt_typeids->bstr9 = mvl_typename_to_typeid(type_name = "RTYP_BSTR9")) < 0)
			break;
		if ((rpt_typeids->bvstring6 = mvl_typename_to_typeid(type_name = "RTYP_BVSTR6")) < 0)
			break;
		if ((rpt_typeids->bvstring8 = mvl_typename_to_typeid(type_name = "RTYP_BVSTR8")) < 0)
			break;
		if ((rpt_typeids->bvstring10 = mvl_typename_to_typeid(type_name = "RTYP_BVSTR10")) < 0)
			break;
		if ((rpt_typeids->btime6 = mvl_typename_to_typeid(type_name = "RTYP_BTIME6")) < 0)
			break;
		if ((rpt_typeids->int8u = mvl_typename_to_typeid(type_name = "RTYP_INT8U")) < 0)
			break;
		if ((rpt_typeids->int16u = mvl_typename_to_typeid(type_name = "RTYP_INT16U")) < 0)
			break;
		if ((rpt_typeids->int32u = mvl_typename_to_typeid(type_name = "RTYP_INT32U")) < 0)
			break;
		if ((rpt_typeids->ostring8 = mvl_typename_to_typeid(type_name = "RTYP_OSTR8")) < 0)
			break;
		if ((rpt_typeids->vstring32 = mvl_typename_to_typeid(type_name = "RTYP_VSTR32")) < 0)
			break;
		if ((rpt_typeids->vstring65 = mvl_typename_to_typeid(type_name = "RTYP_VSTR65")) < 0)
			break;

		retCode = SD_SUCCESS;  /* If we get here, all were successful	*/
	} while (0);	/* end of "one-time" loop	*/

	if (retCode)
		MVL_LOG_ERR1("Can't find type '%s'", type_name);
	return (retCode);	/* If ANY find failed, SD_FAILURE is returned	*/
}

/************************************************************************/
/*			rcb_info_create					*/
/************************************************************************/
RCB_INFO* rcb_info_create(MVL_NET_INFO* net_info, ST_CHAR* domName,
	ST_CHAR* rcbName, RPT_TYPEIDS* rpt_typeids, ST_INT timeOut)
{
	RCB_INFO* rcb_info;
	ST_CHAR varName[MAX_IDENT_LEN + 1];
	ST_CHAR datSetName[65 + 1];	/* data set name (Vstring65)	*/
	ST_CHAR datdomName[65 + 1];	/* data set name (Vstring65)	*/
	ST_CHAR RptID[65 + 1];		/* RptID (Vstring65)	*/
	ST_CHAR* nvlName;
	ST_INT numDsVar;		/* num variables in NVL		*/
	OBJECT_NAME** varNameArray;	/* array of variable names in NVL	*/
	ST_INT j;
	size_t extended_size;
	ST_CHAR* extended_ptr;
	ST_CHAR InclusionTdl[30];	/* place to create TDL for inclusion bstr*/
	ST_RET status = SD_SUCCESS;	/* set to SD_FAILURE if anything fails.	*/
	ST_CHAR* nvlDomName;
	ST_INT rcb_type;

	assert(rpt_typeids->mmsbool || rpt_typeids->int8u);	/* make sure global struct initialized*/

	/* Read "RptID" from the server & save in "RptID" local variable.	*/
	/* Copy to "rcb_info" struct later.					*/
	strcpy(varName, rcbName);
	strcat(varName, "$RptID");
	if (mms_named_var_read(net_info, varName, DOM_SPEC, domName, rpt_typeids->vstring65, RptID, timeOut))
	{
		echo_warn2("Error reading RptID '%s' in domain '%s'\n", varName, domName);
		return (NULL);
	}
	/* Figure out RCB type from 'rcbName'.	*/
	if (strstr(rcbName, "$BR$") != NULL)
		rcb_type = RCB_TYPE_IEC_BRCB;
	else if (strstr(rcbName, "$RP$") != NULL)
	{
		/* This could be IEC URCB or UCA URCB. Only IEC contains "Resv" attr.*/
		/* Try to read "Resv". If successful, RCB is IEC_URCB, else it is UCA.*/
		/* NOTE: "RptID" read was successful, so we know this RCB exists.	*/
		ST_BOOLEAN tmpResv;
		strcpy(varName, rcbName);
		strcat(varName, "$Resv");
		if (mms_named_var_read(net_info, varName, DOM_SPEC, domName, rpt_typeids->mmsbool, &tmpResv, timeOut) == SD_SUCCESS)
			rcb_type = RCB_TYPE_IEC_URCB;
		else
			rcb_type = RCB_TYPE_UCA;
	}
	else
	{
		echo_warn1("RCB name '%s' invalid. Must contain 'BR' or 'RP'.\n", rcbName);
		return (NULL);
	}
	/* Read "DatSet" from the server.	*/
	strcpy(varName, rcbName);
	strcat(varName, "$DatSet");
	if (mms_named_var_read(net_info, varName, DOM_SPEC, domName, rpt_typeids->vstring65, datSetName, timeOut))
	{	/* can't read data set name	*/
		echo_warn2("Error reading data set name ('%s' in domain '%s')\n", varName, domName);
		return (NULL);
	}
	/* datSetName should now be set	*/
	strcpy(datdomName, datSetName);//WW 2023-10-23
	nvlDomName = strtok(datdomName, "/");/* extract domain name of NVL  */
	nvlName = strtok(NULL, "");		/* extract NVL name  */
	if (nvlName == NULL)	/* "/" not found	*/
	{
		echo_warn1("datSetName '%s' invalid. Does not contain '/' character.", datSetName);
		return (NULL);
	}
	/* Get NVL Attributes from the server	*/
	/* NOTE: Resources allocated by this funct are freed by calling 	*/
	/*   nvl_var_name_array_destroy (called from rcb_info_destroy).	*/
	varNameArray = nvl_var_name_array_create(net_info, nvlDomName, nvlName, &numDsVar, timeOut);
	if (varNameArray == NULL)
	{
		echo_warn2("GetNamedVariableListAttributes request failed for '%s' in domain '%s'",
			nvlName, domName);
		return (NULL);
	}
	/* Allocate RCB_INFO structure plus extra room for additional data
	* that depends on "numDsVar". This effectively combines 8 allocations into one.
	* Set pointers in RCB_INFO structure to point into the extra space.
	* CRITICAL: use chk_calloc to start with clean structure
	*/
	extended_size = sizeof(RCB_INFO)
		+ numDsVar * sizeof(ST_INT)		/* rcb_info->typeIdArr		*/
		+ numDsVar * sizeof(MVL_VAR_ASSOC*)	/* rcb_info->rcb_var.dataRefName*/
		+ numDsVar * sizeof(MVL_VAR_ASSOC*)	/* rcb_info->rcb_var.dataValue	*/
		+ numDsVar * sizeof(MVL_VAR_ASSOC*)	/* rcb_info->rcb_var.Reason	*/
		+ numDsVar * 66				/* rcb_info->rcb_data.dataRefName*/
		+ numDsVar * sizeof(MMS_BVSTRING)	/* rcb_info->rcb_data.Reason	*/
		+ BSTR_NUMBITS_TO_NUMBYTES(numDsVar);	/* rcb_info->rcb_data.Inclusion	*/

	rcb_info = (RCB_INFO*)chk_calloc(1, extended_size);
	strcpy(rcb_info->dom_Name, domName); //WW 2023-08-29 逻辑设备名称
	strcpy(rcb_info->rcb_name, rcbName); //WW 2023-08-29 报告控制块名称
	//////////////////////////////////////////////////////////////////////////
	strcpy(rcb_info->ds_Nam, datSetName); //WW 2023-08-29 数据集名称
	//////////////////////////////////////////////////////////////////////////

	echo_msg4("Client RCB info 0x%X created for '%s' in domain '%s'.datasetnam '%s' \n", rcb_info, rcbName, domName, datSetName);
	extended_ptr = (ST_CHAR*)(rcb_info + 1);	/* point after RCB_INFO struct	*/

	rcb_info->typeIdArr = (ST_INT*)extended_ptr;
	extended_ptr += numDsVar * sizeof(ST_INT);	/* point after rcb_info->typeIdArr*/

	rcb_info->rcb_var.dataRefName = (MVL_VAR_ASSOC**)extended_ptr;
	extended_ptr += numDsVar * sizeof(MVL_VAR_ASSOC*);	/* rcb_info->rcb_var.dataRefName*/

	rcb_info->rcb_var.dataValue = (MVL_VAR_ASSOC**)extended_ptr;
	extended_ptr += numDsVar * sizeof(MVL_VAR_ASSOC*);	/* rcb_info->rcb_var.dataValue	*/

	rcb_info->rcb_var.Reason = (MVL_VAR_ASSOC**)extended_ptr;
	extended_ptr += numDsVar * sizeof(MVL_VAR_ASSOC*);	/* rcb_info->rcb_var.Reason	*/

	rcb_info->rcb_data.dataRefName = extended_ptr;
	extended_ptr += numDsVar * 66;			/* rcb_info->rcb_data.dataRefName*/

	rcb_info->rcb_data.Reason = (MMS_BVSTRING*)extended_ptr;
	extended_ptr += numDsVar * sizeof(MMS_BVSTRING);	/* rcb_info->rcb_data.Reason	*/

	rcb_info->rcb_data.Inclusion = (ST_UINT8*)extended_ptr;
	extended_ptr += BSTR_NUMBITS_TO_NUMBYTES(numDsVar);	/* rcb_info->rcb_data.Inclusion	*/

	/* Make sure we computed sizes correctly.	*/
	assert(extended_ptr == (ST_CHAR*)rcb_info + extended_size);

	/* Fill in other rcb_info structure members.	*/
	rcb_info->rcb_type = rcb_type;
	strcpy(rcb_info->RptID, RptID);	/* save RptID to compare when RPTs received*/
	rcb_info->varNameArray = varNameArray;
	rcb_info->numDsVar = numDsVar;

	/* Create all necessary types.					*/
	/* Try to create all types even if one fails.				*/
	/* This way, we can later destroy all without remembering which	*/
	/* were successfully created.						*/

	/* Create a Type for the Inclusion bitstring. Save it in "rcb_info->InclusionTypeid" */
	apr_snprintf(InclusionTdl, sizeof(InclusionTdl), "Bstring%d", rcb_info->numDsVar);
	rcb_info->InclusionTypeid = mvl_type_id_create_from_tdl(NULL, InclusionTdl);
	if (rcb_info->InclusionTypeid < 0)
	{
		echo_warn("Error - could not add type for 'Inclusion'.");
		status = SD_FAILURE;
	}

	/* Create types and fill in rcb_info->typeIdArr	*/
	/* Start with all set to invalid, so if some fail, cleanup is easier.	*/
	for (j = 0; j < numDsVar; j++)
		rcb_info->typeIdArr[j] = -1;	/* invalid type_id	*/
	for (j = 0; j < numDsVar; j++)
	{
		if ((rcb_info->typeIdArr[j] = var_type_create(net_info, varNameArray[j], timeOut))
			< 0)
		{	/* couldn't create this type, so stop	*/
			echo_warn1("Error creating type for variable '%s'",
				varNameArray[j]->obj_name.vmd_spec);
			status = SD_FAILURE;
			break;
		}
	}

	if (status == SD_SUCCESS)
	{
		/* Types created OK, so create variables	*/
		status = rcb_info_var_create(rcb_info, rpt_typeids);	/* returns SD_SUCCESS or SD_FAILURE*/
	}

	if (status != SD_SUCCESS)
	{	/* something failed. Destroy anything created.	*/
		rcb_info_destroy(rcb_info);
		rcb_info = NULL;
	}

	return (rcb_info);
}

/************************************************************************/
/*			rcb_info_destroy				*/
/************************************************************************/
ST_VOID rcb_info_destroy(RCB_INFO* rcb_info)
{
	ST_INT j;

	/* Free up array of variable names saved in rcb_info_create	*/
	nvl_var_name_array_destroy(rcb_info->varNameArray, rcb_info->numDsVar);

	/* Destroy all variables.	*/
	rcb_info_var_destroy(rcb_info);


	/* Destroy "Inclusion" Type (this should be AFTER destroying "Inclusion" Variable).*/
	if (rcb_info->InclusionTypeid > 0)	/* 0=never created. -1=error	*/
		mvl_type_id_destroy(rcb_info->InclusionTypeid);

	/* Destroy "dataValue" Types (this should be AFTER destroying "dataValue" variables).	*/
	for (j = 0; j < rcb_info->numDsVar; j++)
	{
		/* All types were initialized to -1. They are also set to -1 if create failed.*/
		/* Only destroy types that are NOT (-1).				*/
		if (rcb_info->typeIdArr[j] >= 0)
			mvl_type_id_destroy(rcb_info->typeIdArr[j]);
	}

	chk_free(rcb_info);
	echo_warn1("Client RCB info 0x%X destroyed", rcb_info);
}
/************************************************************************/
/*			nvl_var_name_array_create			*/
/* Send a GetNamedVariableListAttributes request to get a list of	*/
/* variable names in a NamedVariableList (NVL).				*/
/* Assume the NVL is domain-specific					*/
/* RETURN: ptr to array of ptrs to variable names (OBJECT_NAME structs).*/
/*	   Also the variable pointed to by numVarOut contains the number*/
/*         of variables in the NVL.					*/
/* NOTE: this function allocates memory for the (OBJECT_NAME *) array.	*/
/*	Call "nvl_var_name_array_destroy" to free the memory.		*/
/************************************************************************/
static OBJECT_NAME** nvl_var_name_array_create(MVL_NET_INFO* net_info, ST_CHAR* domName, ST_CHAR* nvlName,
	ST_INT* numVarOut,
	ST_INT timeOut)
{
	MVL_REQ_PEND* reqCtrl;
	GETVLIST_REQ_INFO getvlist_req;
	GETVLIST_RESP_INFO* getvlist_resp;	/* set to reqCtrl->u.getvlist_resp_info*/
	VARIABLE_LIST* variable_list;
	OBJECT_NAME** varNameArray;	/* Ptr to array of ptrs to var names	*/
	ST_RET ret;			/* general purpose return code	*/
	ST_INT j;

	getvlist_req.vl_name.object_tag = DOM_SPEC;
	getvlist_req.vl_name.domain_id = domName;
	getvlist_req.vl_name.obj_name.vmd_spec = nvlName;

	ret = mvla_getvlist(net_info, &getvlist_req, &reqCtrl);
	if (ret == SD_SUCCESS)
		ret = waitReqDone(reqCtrl, timeOut);
	if (ret != SD_SUCCESS)
	{
		echo_warn1("mvla_getvlist Error = 0x%X.", ret);
		varNameArray = NULL;
	}
	else
	{
		getvlist_resp = reqCtrl->u.getvlist.resp_info;
		variable_list = (VARIABLE_LIST*)(getvlist_resp + 1);
		/* Allocate array of variable names.	*/
		*numVarOut = getvlist_resp->num_of_variables;
		varNameArray = (OBJECT_NAME**)chk_malloc(getvlist_resp->num_of_variables * sizeof(OBJECT_NAME*));
		/* Copy info from response to allocated array	*/
		for (j = 0; j < getvlist_resp->num_of_variables; j++, variable_list++)
		{
			/* Assume all variables are named.	*/
			assert(variable_list->var_spec.var_spec_tag == VA_SPEC_NAMED);
			varNameArray[j] = object_name_clone_create(&variable_list->var_spec.vs.name);
		}
	}

	mvl_free_req_ctrl(reqCtrl);	/* CRITICAL:		*/
	return (varNameArray);
}
/************************************************************************/
/*			nvl_var_name_array_destroy			*/
/* Free up all resources allocated by "nvl_var_name_array_create".	*/
/************************************************************************/
static ST_VOID nvl_var_name_array_destroy(OBJECT_NAME** varNameArray, ST_INT numVar)
{
	ST_INT j;
	for (j = 0; j < numVar; j++)
		object_name_clone_destroy(varNameArray[j]);
	chk_free(varNameArray);
}

/************************************************************************/
/*			var_type_create					*/
/* Send GetVariableAccessAttributes request. Wait for response. When	*/
/* response received, create new type. Pass NULL as type_name arg to	*/
/* "mvl_type_id_create" (this avoids the problem of duplicate names).	*/
/* RETURN: Type ID							*/
/* NOTE: Call "mvl_type_id_destroy" to destroy the type created here.	*/
/************************************************************************/
static ST_INT var_type_create(MVL_NET_INFO* net_info, OBJECT_NAME* varObj,
	ST_INT timeOut)
{
	MVL_REQ_PEND* reqCtrl;
	GETVAR_REQ_INFO getvar_req;
	VAR_ACC_TSPEC* type_spec;
	ST_INT type_id = -1;	/* invalid. If anything fails, this value returned*/
	ST_RET ret;

	getvar_req.req_tag = GETVAR_NAME;
	getvar_req.name = *varObj;

	/* Send GetVariableAccessAttributes request & wait for response.	*/
	ret = mvla_getvar(net_info, &getvar_req, &reqCtrl);
	if (ret == SD_SUCCESS)
		ret = waitReqDone(reqCtrl, timeOut);
	if (ret != SD_SUCCESS)
		echo_warn1("GetVariableAccessAttributes error = 0x%x.", ret);
	else
	{
		type_spec = &reqCtrl->u.getvar.resp_info->type_spec;
		/* First arg (type_name) is NULL to avoid duplicate names.	*/
		type_id = mvl_type_id_create(NULL, type_spec->data, type_spec->len);
	}
	mvl_free_req_ctrl(reqCtrl);
	return (type_id);
}

/************************************************************************/
/* 			rcb_info_var_create				*/
/* Create dummy variables to be used later when a report is received.	*/
/* These variables are needed to decode the data received in a report.	*/
/* This function fills in "rcb_info->rcb_data" and "rcb_info->rcb_var".	*/
/************************************************************************/
static ST_RET rcb_info_var_create(RCB_INFO* rcb_info, RPT_TYPEIDS* rpt_typeids)
{
	OBJECT_NAME dummyvar_objname;	/* dummy variable object name	*/
	ST_INT j;
	ST_INT incSize;		/* num bytes for inclusion bitstring	*/

	/* CRITICAL: "mvl_var_create" is used (NOT "mvl_var_add") to create
	* local variables to store data received later in IEC/UCA Reports.
	* These are NOT real variables, so remote MMS nodes cannot access them.
	* All variables use the same name (dummyvar): that's OK because
	* "mvl_var_create" does NOT add them to the database and nothing needs
	* to use the name.
	*/

	/* NOTE: last arg to mvl_var_create is always SD_FALSE, so the name is
	* NOT copied. This is OK because all names here are fixed strings.
	*/

	/* Set up one OBJECT_NAME to use for all variables.	*/
	dummyvar_objname.object_tag = VMD_SPEC;
	dummyvar_objname.obj_name.vmd_spec = "dummyvar";	/* all vars use same name	*/

	if ((rcb_info->rcb_var.RptID = mvl_var_create(&dummyvar_objname, rpt_typeids->vstring65,
		&rcb_info->rcb_data.RptID, NULL, SD_FALSE)) == NULL)
		return (SD_FAILURE);

	if ((rcb_info->rcb_var.OptFlds = mvl_var_create(&dummyvar_objname, rpt_typeids->bvstring10,
		&rcb_info->rcb_data.OptFlds, NULL, SD_FALSE)) == NULL)
		return (SD_FAILURE);

	if ((rcb_info->rcb_var.SqNum = mvl_var_create(&dummyvar_objname, rpt_typeids->int16u,
		&rcb_info->rcb_data.SqNum, NULL, SD_FALSE)) == NULL)
		return (SD_FAILURE);

	if ((rcb_info->rcb_var.TimeOfEntry = mvl_var_create(&dummyvar_objname, rpt_typeids->btime6,
		&rcb_info->rcb_data.TimeOfEntry, NULL, SD_FALSE)) == NULL)
		return (SD_FAILURE);

	if ((rcb_info->rcb_var.DatSetNa = mvl_var_create(&dummyvar_objname, rpt_typeids->vstring65,
		&rcb_info->rcb_data.DatSetNa, NULL, SD_FALSE)) == NULL)
		return (SD_FAILURE);

	if ((rcb_info->rcb_var.BufOvfl = mvl_var_create(&dummyvar_objname, rpt_typeids->mmsbool,
		&rcb_info->rcb_data.BufOvfl, NULL, SD_FALSE)) == NULL)
		return (SD_FAILURE);

	if ((rcb_info->rcb_var.SubSeqNum = mvl_var_create(&dummyvar_objname, rpt_typeids->int16u,
		&rcb_info->rcb_data.SubSeqNum, NULL, SD_FALSE)) == NULL)
		return (SD_FAILURE);

	if ((rcb_info->rcb_var.MoreSegmentsFollow = mvl_var_create(&dummyvar_objname, rpt_typeids->mmsbool,
		&rcb_info->rcb_data.MoreSegmentsFollow, NULL, SD_FALSE)) == NULL)
		return (SD_FAILURE);

	if ((rcb_info->rcb_var.EntryID = mvl_var_create(&dummyvar_objname, rpt_typeids->ostring8,
		&rcb_info->rcb_data.EntryID, NULL, SD_FALSE)) == NULL)
		return (SD_FAILURE);

	if ((rcb_info->rcb_var.ConfRev = mvl_var_create(&dummyvar_objname, rpt_typeids->int32u,
		&rcb_info->rcb_data.ConfRev, NULL, SD_FALSE)) == NULL)
		return (SD_FAILURE);

	incSize = BSTR_NUMBITS_TO_NUMBYTES(rcb_info->numDsVar);
	/* Note: rcb_info->rcb_data.Inclusion is a "ptr" to data, so don't add "&".	*/
	if ((rcb_info->rcb_var.Inclusion = mvl_var_create(&dummyvar_objname, rcb_info->InclusionTypeid,
		rcb_info->rcb_data.Inclusion, NULL, SD_FALSE)) == NULL)
		return (SD_FAILURE);

	/* Allocate array of DataRefName (vstring65)	*/
	/* & array of (MVL_VAR_ASSOC *).	*/
	/* Can't alloc array of strings, so we just allocate one big buffer	*/
	/* and use [j*66] to find postion for each string in the buffer.	*/
	/* Create separate variable for each array entry.	*/
	for (j = 0; j < rcb_info->numDsVar; j++)
	{
		if ((rcb_info->rcb_var.dataRefName[j] = mvl_var_create(&dummyvar_objname, rpt_typeids->vstring65,
			&rcb_info->rcb_data.dataRefName[j * 66], NULL, SD_FALSE)) == NULL)
			return (SD_FAILURE);
	}

	/* Create variable for each DataSet variable.	*/
	for (j = 0; j < rcb_info->numDsVar; j++)
	{
		ST_VOID* remote_data;	/* buffer to store remote data	*/
		MVL_TYPE_CTRL* type_ctrl;

		type_ctrl = mvl_type_ctrl_find(rcb_info->typeIdArr[j]);
		assert(type_ctrl);	/* find should never fail	*/
		/* Allocate buffer to store remote data.		*/
		remote_data = chk_malloc(type_ctrl->data_size);

		if ((rcb_info->rcb_var.dataValue[j] = mvl_var_create(&dummyvar_objname,
			rcb_info->typeIdArr[j],	/* Use type just created	*/
			remote_data,	/* buffer for data	*/
			NULL,		/* proc funs	*/
			SD_FALSE))	/* DON'T copy name	*/
			== NULL)
		{	/* couldn't create this variable, so stop	*/
			return (SD_FAILURE);
		}
	}

	/* Allocate array of Reason (bstr6)(one byte for each Reason)	*/
	/* & array of (MVL_VAR_ASSOC *).	*/
	/* Create separate variable for each array entry.	*/
	for (j = 0; j < rcb_info->numDsVar; j++)
	{
		/* Use bvstring8 because IEC sends bstr6 and UCA sends bstr8.	*/
		if ((rcb_info->rcb_var.Reason[j] = mvl_var_create(&dummyvar_objname, rpt_typeids->bvstring8,
			&rcb_info->rcb_data.Reason[j], NULL, SD_FALSE)) == NULL)
			return (SD_FAILURE);
	}

	/* If we get this far, everything was successful.	*/
	return (SD_SUCCESS);
}

/************************************************************************/
/* 			rcb_info_var_destroy				*/
/* Destroy everything created in "rcb_info_var_create".			*/
/* Check that each was successfully creating before destroying.	Any one	*/
/* of the "mvl_var_create" calls in "rcb_info_var_create" may have failed.*/
/************************************************************************/
static ST_VOID rcb_info_var_destroy(RCB_INFO* rcb_info)
{
	ST_INT j;

	if (rcb_info->rcb_var.RptID)
		mvl_var_destroy(rcb_info->rcb_var.RptID);

	if (rcb_info->rcb_var.OptFlds)
		mvl_var_destroy(rcb_info->rcb_var.OptFlds);

	if (rcb_info->rcb_var.SqNum)
		mvl_var_destroy(rcb_info->rcb_var.SqNum);

	if (rcb_info->rcb_var.TimeOfEntry)
		mvl_var_destroy(rcb_info->rcb_var.TimeOfEntry);

	if (rcb_info->rcb_var.DatSetNa)
		mvl_var_destroy(rcb_info->rcb_var.DatSetNa);

	if (rcb_info->rcb_var.BufOvfl)
		mvl_var_destroy(rcb_info->rcb_var.BufOvfl);

	if (rcb_info->rcb_var.SubSeqNum)
		mvl_var_destroy(rcb_info->rcb_var.SubSeqNum);

	if (rcb_info->rcb_var.MoreSegmentsFollow)
		mvl_var_destroy(rcb_info->rcb_var.MoreSegmentsFollow);

	if (rcb_info->rcb_var.EntryID)
		mvl_var_destroy(rcb_info->rcb_var.EntryID);

	if (rcb_info->rcb_var.ConfRev)
		mvl_var_destroy(rcb_info->rcb_var.ConfRev);

	if (rcb_info->rcb_var.Inclusion)
		mvl_var_destroy(rcb_info->rcb_var.Inclusion);

	/* Destroy "dataRefName" variables.	*/
	for (j = 0; j < rcb_info->numDsVar; j++)
	{
		if (rcb_info->rcb_var.dataRefName[j])
			mvl_var_destroy(rcb_info->rcb_var.dataRefName[j]);
	}

	/* Destroy "dataValue" variables.	*/
	for (j = 0; j < rcb_info->numDsVar; j++)
	{
		if (rcb_info->rcb_var.dataValue[j] != NULL)
		{
			chk_free(rcb_info->rcb_var.dataValue[j]->data);
			mvl_var_destroy(rcb_info->rcb_var.dataValue[j]);
		}
	}

	/* Destroy "Reason" variables.	*/
	for (j = 0; j < rcb_info->numDsVar; j++)
	{
		if (rcb_info->rcb_var.Reason[j])
			mvl_var_destroy(rcb_info->rcb_var.Reason[j]);
	}
}

/************************************************************************/
/* This sample function sets some options and enables the RCB.		*/
/* If any write fails, stop.						*/
/* If more options needed, add more arguments to this function.		*/
/************************************************************************/
ST_RET rcb_enable(MVL_NET_INFO* netInfo, ST_CHAR* domName,
	ST_CHAR* rcbName, ST_UCHAR* OptFlds, ST_UCHAR* TrgOps,
	ST_UINT32 IntgPd, RPT_TYPEIDS* rpt_typeids, ST_INT timeOut)
{
	ST_BOOLEAN RptEna = 1;		/* 1 = enable the report	*/
	ST_RET ret = SD_SUCCESS;
	ST_CHAR varName[MAX_IDENT_LEN + 1];

	//if (ret == SD_SUCCESS)
	//{
	//	apr_snprintf (varName,sizeof(varName),"%s$IntgPd", rcbName);
	//	ret = mms_named_var_write (netInfo, varName, DOM_SPEC, domName, rpt_typeids->int32u, (ST_CHAR *) &IntgPd, timeOut);
	//}

	if (ret == SD_SUCCESS)
	{
		/* NOTE: only write 9 bits. 10th bit (segmentation) is read-only.	*/
		apr_snprintf(varName, sizeof(varName), "%s$OptFlds", rcbName);
		ret = mms_named_var_write(netInfo, varName, DOM_SPEC, domName, rpt_typeids->bstr9, OptFlds, timeOut);
	}

	if (ret == SD_SUCCESS)
	{
		apr_snprintf(varName, sizeof(varName), "%s$TrgOps", rcbName);
		ret = mms_named_var_write(netInfo, varName, DOM_SPEC, domName, rpt_typeids->bstr6, TrgOps, timeOut);
		if (ret != SD_SUCCESS) {
			printf("%s Write Error!!!", varName);
			echo_warn1("Report注册 %s Write Error!!!", varName);
		}
	}

#if 0	/* Add something like this if other options needed.	???????? BufTm 等??*/
	if (ret == SD_SUCCESS)
	{
		apr_snprintf(varName, sizeof(varName), "%s$Trgs", rcbName);
		ret = mms_named_var_write(netInfo, varName, DOM_SPEC, domName, rpt_typeids->int16u, (ST_CHAR*)&Trgs, timeOut);
	}
#endif

	if (ret == SD_SUCCESS)
	{
		apr_snprintf(varName, sizeof(varName), "%s$RptEna", rcbName);
		ret = mms_named_var_write(netInfo, varName, DOM_SPEC, domName, rpt_typeids->mmsbool, (ST_CHAR*)&RptEna, timeOut);
	}

	return (ret);
}


ST_RET rcb_disable(MVL_NET_INFO* netInfo, ST_CHAR* domName,
	ST_CHAR* rcbName, RPT_TYPEIDS* rpt_typeids, ST_INT timeOut)
{
	ST_BOOLEAN RptEna = 0;		/* 0 = disable the report	*/
	ST_UINT32 IntgPd = 0;
	ST_RET ret = SD_SUCCESS;
	ST_CHAR varName[MAX_IDENT_LEN + 1];

	if (ret == SD_SUCCESS)
	{
		apr_snprintf(varName, sizeof(varName), "%s$RptEna", rcbName);
		ret = mms_named_var_write(netInfo, varName, DOM_SPEC, domName, rpt_typeids->mmsbool, (ST_CHAR*)&RptEna, timeOut);
	}

	if (ret == SD_SUCCESS)
	{
		apr_snprintf(varName, sizeof(varName), "%s$IntgPd", rcbName);
		ret = mms_named_var_write(netInfo, varName, DOM_SPEC, domName, rpt_typeids->int32u, (ST_CHAR*)&IntgPd, timeOut);
	}
	return (ret);
}

/************************************************************************/
/************************************************************************/
static ST_VOID log_var_data(MVL_VAR_ASSOC* var, MMS_DECODE_DATA* data)
{
	ST_CHAR tdl_buf[500];	/* increase size if complex TDL expected*/
	MVL_TYPE_CTRL* type_ctrl;

	type_ctrl = mvl_type_ctrl_find(var->type_id);
	if (type_ctrl)
	{
		/* If the TDL produced is longer than max_tdl_len, this function	*/
		/* "gracefully" fails (i.e. returns 0).					*/
		if (ms_runtime_to_tdl(type_ctrl->rt, type_ctrl->num_rt, tdl_buf, sizeof(tdl_buf)) > 0)
			;//SLOGCALWAYS1 ("  TYPE: %s", tdl_buf);
		else
			echo_warn("  TYPE: unknown");

		//printf ("\nTYPE: %s \n", tdl_buf);

		//printf("type_ctrl->num_rt %d %x %x \n", type_ctrl->num_rt , &data, &(var->data));
		my_local_to_data((ST_CHAR*)var->data, type_ctrl->rt, type_ctrl->num_rt, data);
		if (data->item_num == 0) {
			echo_warn("!!!!!!!!!!!!!!!!!!!data num is 0 !!!!!!!!!!!!!!!!\n");
		}

	}
	else
		echo_warn("  ERR: type_id is invalid");
}



/************************************************************************/
/*			u_iec_rpt_ind_data				*/
/* User function to process the received report data. This example	*/
/* function simply writes the data to the log file.			*/
/*									*/
/* It should be easy to modify or rewrite this function to do whatever	*/
/* is appropriate for your application.					*/
/************************************************************************/
ST_VOID u_iec_rpt_ind_data_by_devtype(MVL_VAR_ASSOC** info_va,
	ST_UINT8* OptFldsData,	/* ptr to data part of OptFlds bvstring	*/
	ST_UINT8* InclusionData,	/* ptr to Inclusion bstring	*/
	RCB_INFO* rcb_info,
	ST_INT va_total,
	MVL_NET_INFO* net_info)
{
	ST_INT va_num = 0;
	ST_INT j;
	MMS_DECODE_DATA mms_dec_data;
	//uint32_t add_mms_dec_data = &mms_dec_data;
	ied_t* ied;
	rptinfo_t* rptinfo;
	//	element_t* pDataOMSObject;
	LD_info_t* LD_info;
	chnl_usr_t* chnl_usr;
	char** DataObjectFullName;
	//	LD_info_t* LD_info_of_Data; 
	ST_INT  not_set_rpt_TimeID_this;
	ST_INT  not_set_rpt_q_this;

	//printf("Var# %02d:    RptId", va_num);
	log_var_data(info_va[va_num], &mms_dec_data);
	//assert(add_mms_dec_data== (uint32_t)&mms_dec_data);
	rptinfo = find_rptinfo_from_net_rpt_info_name(net_info, rcb_info);
	//rptinfo->m_LastDataTime = sGetMsTime();//WW 2023-08-29 去除
	LD_info = rptinfo->LD_info;
	chnl_usr = net_info->user_ext;
	ied = chnl_usr->chnl->ied;

	not_set_rpt_TimeID_this = TRUE;
	not_set_rpt_q_this = TRUE;
	//g_db_inf_clear_data(RPT_IDX);
	rptinfo->count++;
	printf("[BEGIND Process] Received Report From %s:%d %s %s ,va_total =  %i ,【count = %i】 \n",
		chnl_usr->ip_str, chnl_usr->chnl->port, LD_info->LD_name, rcb_info->RptID, va_total, rptinfo->count);

	//mq日志
	DIY_INFOLOG_CODE(LD_info->mp_id,2,LOG_CODE_REPORT,"【NORMAL】前置收到监测点:%s - id:%s的报告,报告名称:%s,监测点对应终端的ip和端口是: %s:%d,报告总数:%i,当前报告数:%i", LD_info->name,LD_info->mp_id,rcb_info->RptID,chnl_usr->ip_str,chnl_usr->chnl->port,va_total, rptinfo->count);

	//apr_time_t	previousTime = apr_time_now();//
	//apr_time_exp_t localTime;
	//apr_time_exp_gmt(&localTime, previousTime);
	//printf("\n ------>>>>>>> begind time %d %d:%d:%d.%d \n",
	//	localTime.tm_mday, localTime.tm_hour, localTime.tm_min, localTime.tm_sec, localTime.tm_usec);

	va_num++;
	//  SLOGCALWAYS1("Var# %02d:    OptFlds", va_num);
	log_var_data(info_va[va_num], &mms_dec_data);
	//assert(add_mms_dec_data== (uint32_t)&mms_dec_data);
	va_num++;

	/* NOTE: Don't change initial entries in "info_va". Add these right after "OptFlds".*/
	if (BSTR_BIT_GET(OptFldsData, OPTFLD_BITNUM_SQNUM))
	{
		//    SLOGCALWAYS1("Var# %02d:    SqNum", va_num);
		log_var_data(info_va[va_num], &mms_dec_data);
		//assert(add_mms_dec_data== (uint32_t)&mms_dec_data);
		printf("\n ------>>>>>>> SqNum received in Report From %s %s %s ,SqNum =  %d \n",
			chnl_usr->ip_str, LD_info->LD_name, rcb_info->RptID, mms_dec_data.data_item[0].u.data_uint);
		va_num++;
	}

	if (BSTR_BIT_GET(OptFldsData, OPTFLD_BITNUM_TIMESTAMP))
	{
		//    SLOGCALWAYS1("Var# %02d:    TimeOfEntry", va_num);
		log_var_data(info_va[va_num], &mms_dec_data);
		//assert(add_mms_dec_data== (uint32_t)&mms_dec_data);
		va_num++;
		//set_rpt_TimeID(convert_btime6_to_apr_time(&(mms_dec_data.data_item[0].u.data_bTime6)) );
	}

	if (BSTR_BIT_GET(OptFldsData, OPTFLD_BITNUM_DATSETNAME))
	{
		//    SLOGCALWAYS1("Var# %02d:    DatSetNa", va_num);
		log_var_data(info_va[va_num], &mms_dec_data);
		//assert(add_mms_dec_data== (uint32_t)&mms_dec_data);
		va_num++;
	}

	if (BSTR_BIT_GET(OptFldsData, OPTFLD_BITNUM_BUFOVFL))
	{
		//    SLOGCALWAYS1("Var# %02d:    BufOvfl", va_num);
		log_var_data(info_va[va_num], &mms_dec_data);
		//assert(add_mms_dec_data== (uint32_t)&mms_dec_data);
		va_num++;
	}

	if (BSTR_BIT_GET(OptFldsData, OPTFLD_BITNUM_ENTRYID))
	{
		char* EntryIDStr;
		int i;
		log_var_data(info_va[va_num], &mms_dec_data);
		//assert(add_mms_dec_data== (uint32_t)&mms_dec_data);
		EntryIDStr = mms_dec_data.data_item[0].u.data_str;
		printf("\n ------>>>>>>> EntryID received in Report From %s %s %s ,EntryID =  %s\n\n\n",
			chnl_usr->ip_str, LD_info->LD_name, rcb_info->RptID, EntryIDStr);
		for (i = 0; i < 8; i++) {
			EntryIDStr[2] = '\0';
			rptinfo->m_EntryID[i] = (byte_t)strtol(EntryIDStr, NULL, 16);
			EntryIDStr += 3; //skip a blank
		}
		va_num++;
	}
	printf("\n -------------rptinfo->m_EntryID %02x %02x %02x %02x %02x %02x %02x %02x -------- EntryID\n\n\n",
		*rptinfo->m_EntryID, *(rptinfo->m_EntryID + 1), *(rptinfo->m_EntryID + 2),
		*(rptinfo->m_EntryID + 3), *(rptinfo->m_EntryID + 4), *(rptinfo->m_EntryID + 5),
		*(rptinfo->m_EntryID + 6), *(rptinfo->m_EntryID + 7));
	if (BSTR_BIT_GET(OptFldsData, OPTFLD_BITNUM_CONFREV))
	{
		//    SLOGCALWAYS1("Var# %02d:    ConfRev", va_num);
		log_var_data(info_va[va_num], &mms_dec_data);
		//assert(add_mms_dec_data== (uint32_t)&mms_dec_data);
		va_num++;
	}
	//printf("\n ----1--va_num %d -\n", va_num);	
	if (BSTR_BIT_GET(OptFldsData, OPTFLD_BITNUM_SUBSEQNUM))
	{
		//    SLOGCALWAYS1("Var# %02d:    SubSeqNum", va_num);
		log_var_data(info_va[va_num], &mms_dec_data);
		//assert(add_mms_dec_data== (uint32_t)&mms_dec_data);
		va_num++;
		//    SLOGCALWAYS1("Var# %02d:    MoreSegmentsFollow", va_num);
		log_var_data(info_va[va_num], &mms_dec_data);
		//assert(add_mms_dec_data== (uint32_t)&mms_dec_data);
		va_num++;
	}
	//printf("\n ----2---va_num %d \n", va_num);	
	//  SLOGCALWAYS1("Var# %02d:    Inclusion", va_num);
	log_var_data(info_va[va_num], &mms_dec_data);
	//assert(add_mms_dec_data== (uint32_t)&mms_dec_data);
	va_num++;

	/* If data-Ref enabled, check "Inclusion" to figure out what is being received.*/
	if (BSTR_BIT_GET(OptFldsData, OPTFLD_BITNUM_DATAREF))
	{
		for (j = 0; j < rcb_info->numDsVar; ++j)
		{
			if (BSTR_BIT_GET(InclusionData, j))
			{
				//        SLOGCALWAYS2("Var# %02d:    DataRefName# %d", va_num, j);
				log_var_data(info_va[va_num], &mms_dec_data);
				//assert(add_mms_dec_data== (uint32_t)&mms_dec_data);
				va_num++;
			}
		}
	}
	//printf("\n ----3---%d \n", va_num);	
	//rcb_info->numDsVar+1 just to avoid rcb_info->numDsVar==0 ,waste a little memory
	DataObjectFullName = apr_pcalloc(g_pt61850app->tmp_pool, (rcb_info->numDsVar + 1) * sizeof(char*));
	//RWCStringVec DataName(rcb_info->numDsVar+1);
	//RWCStringVec AttrName(rcb_info->numDsVar+1);  
	//RWTValVector<CDataOMSObject*>  pDataOMSObjects(rcb_info->numDsVar+1);
	//printf("\n ----4--%d rcb_info->numDsVar %d -\n", va_num, rcb_info->numDsVar);	
	for (j = 0; j < rcb_info->numDsVar; ++j)
	{
		if (BSTR_BIT_GET(InclusionData, j))
		{
			DataObjectFullName[j] = apr_pstrdup(g_pt61850app->tmp_pool, rcb_info->varNameArray[j]->obj_name.vmd_spec);
		}
	}
	//printf("\n ----5--%d-\n", va_num);	
	//	if  (g_pt61850app->IsCallEvtDes )
	//		cout<<"pEquipment->m_OMSObject_FULL_FCDA_Map.entries() = "<<pEquipment->m_OMSObject_FULL_FCDA_Map.entries()<<endl

	//assert(0);
	if (LD_info->line_id > 0) {
		if (strstr(rcb_info->RptID, "QVVR"))//CZY 2023-08-17 WW 2022-11-14 修改判断LLN0$BR$brcbQVVR
			processQVVR_start(LD_info);
		else if (strstr(rcb_info->RptID, "RDRE"))//CZY 2023-08-17 WW 2022-11-14 修改判断LLN0$BR$brcbRDRE
			processRDRE_start(LD_info);
		else {
			ied_t* ied;
			ied = find_ied_from_dev_code(LD_info->terminal_code);

			ied_usr_t* ied_usr = (ied_usr_t*)ied->usr_ext;
			json_trace_block_create_start(LD_info->voltage_level, LD_info->mp_id, rptinfo->flickerflag, ied_usr->dev_type, LD_info->line_id, LD_info->v_wiring_type, rcb_info->RptID, rptinfo->rptNo);

			if (rptinfo->flickerflag==1)//CZY 2023-08-17 WW 2022-11-14 只有闪变和第一次进入才会初始化 json_block_create_start(LD_info->line_id);
				json_block_create_start( LD_info->voltage_level, LD_info->mp_id, rptinfo->flickerflag, ied_usr->dev_type, LD_info->line_id);
			else if(rptinfo->flickerflag == 0) {
				if (LD_info->rptRecvCheckFlag == 0)
					json_block_create_start( LD_info->voltage_level, LD_info->mp_id, rptinfo->flickerflag, ied_usr->dev_type, LD_info->line_id);
				LD_info->rptRecvCheckFlag |= 0x01 << rptinfo->rptNo;
			}
			else if (rptinfo->flickerflag == 2) {
				if (LD_info->rptPstRecvCheckFlag == 0)
					json_block_create_start(LD_info->voltage_level, LD_info->mp_id, rptinfo->flickerflag, ied_usr->dev_type, LD_info->line_id);
				LD_info->rptPstRecvCheckFlag |= 0x01 << rptinfo->rptNo;
			}
		}
	}
	/* HERE'S THE DATA. Check "Inclusion" to figure out what is being received.*/
	for (j = 0; j < rcb_info->numDsVar; ++j)
	{
		/*if (rcb_info->numDsVar==13) {
		printf("bbbbbbbbbbbbbbbbbbbbbbbbb\n");
		}*/
		if (BSTR_BIT_GET(InclusionData, j))
		{
			int ii;
			//	  SLOGCALWAYS3("Var# %02d:    DataSet Var# %d: Name=%s", va_num, j,
			//	               rcb_info->varNameArray[j]->obj_name.vmd_spec);
			//printf("\n ----5.0--%d-\n", va_num);	
			log_var_data(info_va[va_num], &mms_dec_data);
			//assert(add_mms_dec_data== (uint32_t)&mms_dec_data);
			va_num++;
			//printf("\n ----5.1--%d-%d\n", va_num,mms_dec_data.item_num);	
			// 	  if ( !pDataOMSObjects[j] ) continue;

			//cout<<endl<<j<<"	$  "<<AttrName[j]<<"	$ "<<DataName[j]<<"	= ";
			//cout<<endl<<j<<" "<<DataObjectFullName[j]<<"	"<<endl;	  	
			//	  cout<<"  mms_dec_data.item_num ="<<(int)mms_dec_data.item_num<<endl;
			for (ii = 0; ii < mms_dec_data.item_num; ++ii)
			{
				char* FULL_FCDA_Name;
				int length_FCDA;

				//printf("\n ----5.11--%d-%s\n", va_num, mms_dec_data.data_item[ii].comp_name);	
				if (strcmp(mms_dec_data.data_item[ii].comp_name, "") == 0)
					FULL_FCDA_Name = apr_pstrdup(g_pt61850app->tmp_pool, DataObjectFullName[j]);
				else
					FULL_FCDA_Name = apr_pstrcat(g_pt61850app->tmp_pool, DataObjectFullName[j], "$", mms_dec_data.data_item[ii].comp_name, NULL);
				//		cout<<"		FULL_FCDA_Name "<<  FULL_FCDA_Name <<endl;
				//printf("\n ----5.12--%d- %s \n", va_num, FULL_FCDA_Name);
				length_FCDA = strlen(FULL_FCDA_Name);
				//11 08 beijing 许继装置不处理q
				if (('$' == FULL_FCDA_Name[length_FCDA - 2]) && ('q' == FULL_FCDA_Name[length_FCDA - 1])) {
					if (not_set_rpt_q_this) {
						int flag = Set_q_from_61850rpt(mms_dec_data.data_item[ii].u.data_str);

						if (strstr(rcb_info->RptID, "QVVR")) {//CZY 2023-08-17 WW 2022-11-14 修改判断LLN0$BR$brcbQVVR
							//need do nothing!
							not_set_rpt_q_this = FALSE;
						}
						else if (strstr(rcb_info->RptID, "RDRE")) {//CZY 2023-08-17 WW 2022-11-14修改判断 LLN0$BR$brcbRDRE
							//need do nothing!
							not_set_rpt_q_this = FALSE;
						}
						else {
							json_block_create_flag(LD_info->mp_id, flag, rptinfo->flickerflag);
							json_trace_block_create_flag(LD_info->mp_id, flag, rptinfo->flickerflag, rcb_info->RptID, rptinfo->rptNo);
							not_set_rpt_q_this = FALSE;
						}
					}
				}
				else if (('$' == FULL_FCDA_Name[length_FCDA - 2]) && ('t' == FULL_FCDA_Name[length_FCDA - 1])) {
					if (not_set_rpt_TimeID_this) {
						apr_time_t t = convert_btime6_to_apr_time(&(mms_dec_data.data_item[ii].u.data_bTime6));//微秒
						if (strstr(rcb_info->RptID, "QVVR")) {//CZY 2023-08-17 WW 2022-11-14 修改判断LLN0$BR$brcbQVVR
							if (strstr(FULL_FCDA_Name, "VarStr$t")) {
								processQVVR_time(LD_info, t / 1000);//毫秒
								not_set_rpt_TimeID_this = FALSE;
							}
						}
						else if (strstr(rcb_info->RptID, "RDRE")) {//CZY 2023-08-17 WW 2022-11-14修改判断 LLN0$BR$brcbRDRE
							//need do nothing!
							not_set_rpt_TimeID_this = FALSE;
						}
						else {
							json_block_create_time(LD_info->mp_id, t / 1000, rptinfo->flickerflag);
							json_trace_block_create_time(LD_info->mp_id, t / 1000, rptinfo->flickerflag, rcb_info->RptID, rptinfo->rptNo);
							printf("rcb_info->RptID=%s ,LineId=%d , Timestamp= %lld \n", rcb_info->RptID, LD_info->line_id, t / 1000);
							not_set_rpt_TimeID_this = FALSE;
							/*if (strstr(rcb_info->RptID, "LLN0$RP$urcbRealData")) {//lnk 20250624
								//20241223lnk添加终端号参数
								ied_usr_t	*ied_usr = GET_IEDEXT_ADDR(ied);
								if (urcbRealDataHasReceived(ied_usr->dev_idx,LD_info, t / 1000))
									return;
							}*/
						}
					}
				}
				else {
					double v = 0.0;
					if (mms_dec_data.data_item[ii].type == DATA_INT_TYPE)
						v = mms_dec_data.data_item[ii].u.data_int;
					else if (mms_dec_data.data_item[ii].type == DATA_UINT_TYPE)
						v = mms_dec_data.data_item[ii].u.data_uint;
					else if (mms_dec_data.data_item[ii].type == DATA_INT64_TYPE)
						v = (double)mms_dec_data.data_item[ii].u.data_int64;
					else if (mms_dec_data.data_item[ii].type == DATA_UINT64_TYPE)
						v = (double)mms_dec_data.data_item[ii].u.data_uint64;
					else if (mms_dec_data.data_item[ii].type == DATA_DOUBLE_TYPE)
						v = mms_dec_data.data_item[ii].u.data_double;
					else if (mms_dec_data.data_item[ii].type == DATA_STR_TYPE)
						v = strtol(mms_dec_data.data_item[ii].u.data_str, NULL, 2);

					if (strstr(rcb_info->RptID, "QVVR")) {//CZY 2023-08-17 WW 2022-11-14 修改判断LLN0$BR$brcbQVVR
						processQVVR_data(LD_info, FULL_FCDA_Name, v);
					}
					else if (strstr(rcb_info->RptID, "RDRE")) {//CZY 2023-08-17 WW 2022-11-14 修改判断LLN0$BR$brcbRDRE
						processRDRE_data(LD_info, FULL_FCDA_Name, v);
					}
					else {
						json_block_create_data(LD_info->mp_id, FULL_FCDA_Name, v, rptinfo->flickerflag);
						json_trace_block_create_data(LD_info->mp_id, FULL_FCDA_Name, v, rptinfo->flickerflag, rcb_info->RptID, rptinfo->rptNo);
					}
				}//else			   	
			}
		}
	}
	if (LD_info->line_id > 0) {
		//set_rpt_LineID(LD_info->line_id);
		//set_line_info(RPT_IDX,LD_info->line_id,LD_info->SubV_Index,LD_info->Dev_Index,LD_info->Sub_Index,LD_info->GD_Index);
		//write_updatetime_to_db(chnl_usr->chnl->addr);
		if (strstr(rcb_info->RptID, "QVVR")) {//CZY 2023-08-17 WW 2022-11-14 修改判断LLN0$BR$brcbQVVR
			processQVVR_end(LD_info);
		}
		else if (strstr(rcb_info->RptID, "RDRE")) {//CZY 2023-08-17 WW 2022-11-14 修改判断LLN0$BR$brcbRDRE
			processRDRE_end(LD_info);
		}
		else {
			printf("%d : %d", LD_info->rptRecvFlag, LD_info->rptRecvCheckFlag);
			json_trace_block_create_end(LD_info->v_wiring_type, LD_info->mp_id, rptinfo->flickerflag, rcb_info->RptID, rptinfo->rptNo);
			//append_db_records(RPT_IDX);
			if (rptinfo->flickerflag==1)//CZY 2023-08-17 WW 2022-11-14 增加闪变标志
			{
				//ied_usr_t* ied_usr = ied->usr_ext;//CZY 2023-08-17 WW 2022年12月6日14:09:08 增加多个ICD支持
				//int devkind = ied_usr->dev_flag;
				json_block_create_end(LD_info->v_wiring_type, LD_info->mp_id, rptinfo->flickerflag);	//lnk2024-8-16增加接线类型参数
			}
			else if(rptinfo->flickerflag == 0){//CZY 2023-08-17 WW 2022-11-14 增加多个报告判断
				if (LD_info->rptRecvFlag == LD_info->rptRecvCheckFlag) {
					//ied_usr_t* ied_usr = ied->usr_ext;//CZY 2023-08-17 WW 2022年12月6日14:09:08 增加多个ICD支持
					//int devkind = ied_usr->dev_flag;
					json_block_create_end(LD_info->v_wiring_type, LD_info->mp_id, rptinfo->flickerflag);	//lnk2024-8-16增加接线类型参数
					LD_info->rptRecvCheckFlag = 0;
				}
			}
			else if (rptinfo->flickerflag == 2) {//CZY 2023-08-17 WW 2022-11-14 增加多个报告判断
				if (LD_info->rptPstRecvFlag == LD_info->rptPstRecvCheckFlag) {
					//ied_usr_t* ied_usr = ied->usr_ext;//CZY 2023-08-17 WW 2022年12月6日14:09:08 增加多个ICD支持
					//int devkind = ied_usr->dev_flag;
					json_block_create_end(LD_info->v_wiring_type, LD_info->mp_id, rptinfo->flickerflag);	//lnk2024-8-16增加接线类型参数
					LD_info->rptPstRecvCheckFlag = 0;
				}
			}
		}
	}
	else {
		echo_err3("Ignore this report due to line_id invalid , Report From %s %s %s  !!!",
			APR_EGENERAL, chnl_usr->ip_str, LD_info->LD_name, rcb_info->RptID);
		//mq日志
		DIY_ERRORLOG_CODE(LD_info->mp_id,2,LOG_CODE_REPORT,"【ERROR】前置不处理这个监测点:%s - id:%s的报告,报告名称:%s,原因是监测点的序号非法", LD_info->name,LD_info->mp_id,rcb_info->RptID);
		
	}
	printf("[END Process] Report From %s:%d %s %s ,va_total =  %i ,【count = %i】 \n",
		chnl_usr->ip_str, chnl_usr->chnl->port, LD_info->LD_name, rcb_info->RptID, va_total, rptinfo->count);

	//mq日志
	DIY_INFOLOG_CODE(LD_info->mp_id,2,LOG_CODE_REPORT,"【NORMAL】前置处理监测点:%s - id:%s的报告结束,报告名称:%s,监测点对应终端的ip和端口是: %s:%d,报告总数:%i,当前报告数:%i", LD_info->name,LD_info->mp_id,rcb_info->RptID,chnl_usr->ip_str,chnl_usr->chnl->port,va_total, rptinfo->count);

	//apr_time_t	previousTimeend = apr_time_now();//
	//apr_time_exp_t localTimeend;
	//apr_time_exp_gmt(&localTimeend, previousTimeend);
	//printf("\n ------>>>>>>> end time %d %d:%d:%d.%d \n",
	//	localTimeend.tm_mday, localTimeend.tm_hour, localTimeend.tm_min, localTimeend.tm_sec, localTimeend.tm_usec);
	//printf("\n ----6-%d--\n", va_num);	
	/* If "reason" enabled, check "Inclusion" to figure out what is being received.*/
	if (BSTR_BIT_GET(OptFldsData, OPTFLD_BITNUM_REASON)) {
		for (j = 0; j < rcb_info->numDsVar; ++j) {
			if (BSTR_BIT_GET(InclusionData, j)) {
				//   SLOGCALWAYS2("Var# %02d:    Reason# %d", va_num, j);
				//log_var_data (info_va[va_num],&mms_dec_data);       
				va_num++;
			}
		}
	}

	//设置响应时间，避免频繁召唤装置名称，导致丢包 add on Aug 3 for testing of guangdong CRITICAL
	chnl_usr->m_LastPosRespTime = sGetMsTime();

	//assert (va_num==va_total);	/* Did we count right?	*/
	if (va_num != va_total)
		echo_err5("va_num!=va_total! Report From %s %s %s , %d!=%d !!!",
			APR_EGENERAL, chnl_usr->ip_str, LD_info->LD_name, rcb_info->RptID, va_num, va_total);
}


//lnk 20250624匹配不同的实时控制块
const char* extractAfterUrCb(const char* rptID) {
    if (!rptID) return "N/A";

    // 查找 "urcb"（区分大小写）
    const char* pos = strstr(rptID, "urcb");
    if (!pos) return "N/A";  // 如果没有 urcb

    return pos + 4;  // 返回 "urcb" 后面的部分指针
}
//lnk

ST_VOID u_iec_rpt_ind_data(MVL_VAR_ASSOC** info_va,
	ST_UINT8* OptFldsData,	/* ptr to data part of OptFlds bvstring	*/
	ST_UINT8* InclusionData,	/* ptr to Inclusion bstring	*/
	RCB_INFO* rcb_info,
	ST_INT va_total,
	MVL_NET_INFO* net_info)
{
	ST_INT va_num = 0;
	ST_INT j;
	MMS_DECODE_DATA mms_dec_data;
	//uint32_t add_mms_dec_data = &mms_dec_data;
	ied_t* ied;
	rptinfo_t* rptinfo;
	//	element_t* pDataOMSObject;
	LD_info_t* LD_info;
	chnl_usr_t* chnl_usr;
	char** DataObjectFullName;
	//	LD_info_t* LD_info_of_Data; 
	ST_INT  not_set_rpt_TimeID_this;
	ST_INT  not_set_rpt_q_this;

	//printf("Var# %02d:    RptId", va_num);
	log_var_data(info_va[va_num], &mms_dec_data);
	//assert(add_mms_dec_data== (uint32_t)&mms_dec_data);
	rptinfo = find_rptinfo_from_net_rcb_info(net_info, rcb_info);
	//rptinfo->m_LastDataTime = sGetMsTime();//WW 2023-08-29 去除
	LD_info = rptinfo->LD_info;
	chnl_usr = net_info->user_ext;
	ied = chnl_usr->chnl->ied;

	not_set_rpt_TimeID_this = TRUE;
	not_set_rpt_q_this = TRUE;
	//g_db_inf_clear_data(RPT_IDX);
	rptinfo->count++;
	printf("[BEGIND Process] Received Report From %s:%d %s %s ,va_total =  %i ,【count = %i】 mp_id=%s\n",
		chnl_usr->ip_str, chnl_usr->chnl->port, LD_info->LD_name, rcb_info->RptID, va_total, rptinfo->count, LD_info->mp_id);

	//apr_time_t	previousTime = apr_time_now();//
	//apr_time_exp_t localTime;
	//apr_time_exp_gmt(&localTime, previousTime);
	//printf("\n ------>>>>>>> begind time %d %d:%d:%d.%d \n",
	//	localTime.tm_mday, localTime.tm_hour, localTime.tm_min, localTime.tm_sec, localTime.tm_usec);

	va_num++;
	//  SLOGCALWAYS1("Var# %02d:    OptFlds", va_num);
	log_var_data(info_va[va_num], &mms_dec_data);
	//assert(add_mms_dec_data== (uint32_t)&mms_dec_data);
	va_num++;

	/* NOTE: Don't change initial entries in "info_va". Add these right after "OptFlds".*/
	if (BSTR_BIT_GET(OptFldsData, OPTFLD_BITNUM_SQNUM))
	{
		//    SLOGCALWAYS1("Var# %02d:    SqNum", va_num);
		log_var_data(info_va[va_num], &mms_dec_data);
		//assert(add_mms_dec_data== (uint32_t)&mms_dec_data);
		printf("\n ------>>>>>>> SqNum received in Report From %s %s %s ,SqNum =  %d \n",
			chnl_usr->ip_str, LD_info->LD_name, rcb_info->RptID, mms_dec_data.data_item[0].u.data_uint);
		va_num++;
	}

	if (BSTR_BIT_GET(OptFldsData, OPTFLD_BITNUM_TIMESTAMP))
	{
		//    SLOGCALWAYS1("Var# %02d:    TimeOfEntry", va_num);
		log_var_data(info_va[va_num], &mms_dec_data);
		//assert(add_mms_dec_data== (uint32_t)&mms_dec_data);
		va_num++;
		//set_rpt_TimeID(convert_btime6_to_apr_time(&(mms_dec_data.data_item[0].u.data_bTime6)) );
	}

	if (BSTR_BIT_GET(OptFldsData, OPTFLD_BITNUM_DATSETNAME))
	{
		//    SLOGCALWAYS1("Var# %02d:    DatSetNa", va_num);
		log_var_data(info_va[va_num], &mms_dec_data);
		//assert(add_mms_dec_data== (uint32_t)&mms_dec_data);
		va_num++;
	}

	if (BSTR_BIT_GET(OptFldsData, OPTFLD_BITNUM_BUFOVFL))
	{
		//    SLOGCALWAYS1("Var# %02d:    BufOvfl", va_num);
		log_var_data(info_va[va_num], &mms_dec_data);
		//assert(add_mms_dec_data== (uint32_t)&mms_dec_data);
		va_num++;
	}

	if (BSTR_BIT_GET(OptFldsData, OPTFLD_BITNUM_ENTRYID))
	{
		char* EntryIDStr;
		int i;
		log_var_data(info_va[va_num], &mms_dec_data);
		//assert(add_mms_dec_data== (uint32_t)&mms_dec_data);
		EntryIDStr = mms_dec_data.data_item[0].u.data_str;
		printf("\n ------>>>>>>> EntryID received in Report From %s %s %s ,EntryID =  %s\n\n\n",
			chnl_usr->ip_str, LD_info->LD_name, rcb_info->RptID, EntryIDStr);
		for (i = 0; i < 8; i++) {
			EntryIDStr[2] = '\0';
			rptinfo->m_EntryID[i] = (byte_t)strtol(EntryIDStr, NULL, 16);
			EntryIDStr += 3; //skip a blank
		}
		va_num++;
	}
	printf("\n -------------rptinfo->m_EntryID %02x %02x %02x %02x %02x %02x %02x %02x -------- EntryID\n\n\n",
		*rptinfo->m_EntryID, *(rptinfo->m_EntryID + 1), *(rptinfo->m_EntryID + 2),
		*(rptinfo->m_EntryID + 3), *(rptinfo->m_EntryID + 4), *(rptinfo->m_EntryID + 5),
		*(rptinfo->m_EntryID + 6), *(rptinfo->m_EntryID + 7));
	if (BSTR_BIT_GET(OptFldsData, OPTFLD_BITNUM_CONFREV))
	{
		//    SLOGCALWAYS1("Var# %02d:    ConfRev", va_num);
		log_var_data(info_va[va_num], &mms_dec_data);
		//assert(add_mms_dec_data== (uint32_t)&mms_dec_data);
		va_num++;
	}
	//printf("\n ----1--va_num %d -\n", va_num);	
	if (BSTR_BIT_GET(OptFldsData, OPTFLD_BITNUM_SUBSEQNUM))
	{
		//    SLOGCALWAYS1("Var# %02d:    SubSeqNum", va_num);
		log_var_data(info_va[va_num], &mms_dec_data);
		//assert(add_mms_dec_data== (uint32_t)&mms_dec_data);
		va_num++;
		//    SLOGCALWAYS1("Var# %02d:    MoreSegmentsFollow", va_num);
		log_var_data(info_va[va_num], &mms_dec_data);
		//assert(add_mms_dec_data== (uint32_t)&mms_dec_data);
		va_num++;
	}
	//printf("\n ----2---va_num %d \n", va_num);	
	//  SLOGCALWAYS1("Var# %02d:    Inclusion", va_num);
	log_var_data(info_va[va_num], &mms_dec_data);
	//assert(add_mms_dec_data== (uint32_t)&mms_dec_data);
	va_num++;

	/* If data-Ref enabled, check "Inclusion" to figure out what is being received.*/
	if (BSTR_BIT_GET(OptFldsData, OPTFLD_BITNUM_DATAREF))
	{
		for (j = 0; j < rcb_info->numDsVar; ++j)
		{
			if (BSTR_BIT_GET(InclusionData, j))
			{
				//        SLOGCALWAYS2("Var# %02d:    DataRefName# %d", va_num, j);
				log_var_data(info_va[va_num], &mms_dec_data);
				//assert(add_mms_dec_data== (uint32_t)&mms_dec_data);
				va_num++;
			}
		}
	}
	//printf("\n ----3---%d \n", va_num);	
	//rcb_info->numDsVar+1 just to avoid rcb_info->numDsVar==0 ,waste a little memory
	DataObjectFullName = apr_pcalloc(g_pt61850app->tmp_pool, (rcb_info->numDsVar + 1) * sizeof(char*));
	//RWCStringVec DataName(rcb_info->numDsVar+1);
	//RWCStringVec AttrName(rcb_info->numDsVar+1);  
	//RWTValVector<CDataOMSObject*>  pDataOMSObjects(rcb_info->numDsVar+1);
	//printf("\n ----4--%d rcb_info->numDsVar %d -\n", va_num, rcb_info->numDsVar);	
	for (j = 0; j < rcb_info->numDsVar; ++j)
	{
		if (BSTR_BIT_GET(InclusionData, j))
		{
			DataObjectFullName[j] = apr_pstrdup(g_pt61850app->tmp_pool, rcb_info->varNameArray[j]->obj_name.vmd_spec);
		}
	}
	//printf("\n ----5--%d-\n", va_num);	
	//	if  (g_pt61850app->IsCallEvtDes )
	//		cout<<"pEquipment->m_OMSObject_FULL_FCDA_Map.entries() = "<<pEquipment->m_OMSObject_FULL_FCDA_Map.entries()<<endl
	//assert(0);
	if (LD_info->line_id > 0) {
		if (strstr(rcb_info->RptID, "QVVR"))//CZY 2023-08-17 WW 2022-11-14 修改判断LLN0$BR$brcbQVVR
			processQVVR_start(LD_info);//上送的报告里面的监测点号
		else if (strstr(rcb_info->RptID, "RDRE"))//CZY 2023-08-17 WW 2022-11-14 修改判断LLN0$BR$brcbRDRE
			processRDRE_start(LD_info);
		else {
			ied_t* ied;

			ied = find_ied_from_dev_code(LD_info->terminal_code);

			ied_usr_t* ied_usr = (ied_usr_t*)ied->usr_ext;
			json_trace_block_create_start(LD_info->voltage_level, LD_info->mp_id, rptinfo->flickerflag, ied_usr->dev_type, LD_info->line_id, LD_info->v_wiring_type, rcb_info->RptID, rptinfo->rptNo);
			if (rptinfo->flickerflag == 1)//CZY 2023-08-17 WW 2022-11-14 只有闪变和第一次进入才会初始化 json_block_create_start(LD_info->line_id);
				json_block_create_start(LD_info->voltage_level, LD_info->mp_id, rptinfo->flickerflag, ied_usr->dev_type, LD_info->line_id);
			else if (rptinfo->flickerflag == 0) {
				if (LD_info->rptRecvCheckFlag == 0)
					json_block_create_start(LD_info->voltage_level, LD_info->mp_id, rptinfo->flickerflag, ied_usr->dev_type, LD_info->line_id);
				LD_info->rptRecvCheckFlag |= 0x01 << rptinfo->rptNo;
			}
			else if (rptinfo->flickerflag == 2) {
				if (LD_info->rptPstRecvCheckFlag == 0)
					json_block_create_start(LD_info->voltage_level, LD_info->mp_id, rptinfo->flickerflag, ied_usr->dev_type, LD_info->line_id);
				LD_info->rptPstRecvCheckFlag |= 0x01 << rptinfo->rptNo;
				printf("LD_info->rptPstRecvCheckFlag=%d",LD_info->rptPstRecvCheckFlag);
			}
		}
	}
	/* HERE'S THE DATA. Check "Inclusion" to figure out what is being received.*/
	for (j = 0; j < rcb_info->numDsVar; ++j)
	{
		/*if (rcb_info->numDsVar==13) {
		printf("bbbbbbbbbbbbbbbbbbbbbbbbb\n");
		}*/
		if (BSTR_BIT_GET(InclusionData, j))
		{
			int ii;
			//	  SLOGCALWAYS3("Var# %02d:    DataSet Var# %d: Name=%s", va_num, j,
			//	               rcb_info->varNameArray[j]->obj_name.vmd_spec);
			//printf("\n ----5.0--%d-\n", va_num);	
			log_var_data(info_va[va_num], &mms_dec_data);
			//assert(add_mms_dec_data== (uint32_t)&mms_dec_data);
			va_num++;
			//printf("\n ----5.1--%d-%d\n", va_num,mms_dec_data.item_num);	
			// 	  if ( !pDataOMSObjects[j] ) continue;

			//cout<<endl<<j<<"	$  "<<AttrName[j]<<"	$ "<<DataName[j]<<"	= ";
			//cout<<endl<<j<<" "<<DataObjectFullName[j]<<"	"<<endl;	  	
			//	  cout<<"  mms_dec_data.item_num ="<<(int)mms_dec_data.item_num<<endl;
			long long time;
			for (ii = 0; ii < mms_dec_data.item_num; ++ii) 
			{
				char* FULL_FCDA_Name;
				int length_FCDA;
				if (strcmp(mms_dec_data.data_item[ii].comp_name, "") == 0)
					FULL_FCDA_Name = apr_pstrdup(g_pt61850app->tmp_pool, DataObjectFullName[j]);
				else
					FULL_FCDA_Name = apr_pstrcat(g_pt61850app->tmp_pool, DataObjectFullName[j], "$", mms_dec_data.data_item[ii].comp_name, NULL);
				length_FCDA = strlen(FULL_FCDA_Name);
				if (('$' == FULL_FCDA_Name[length_FCDA - 2]) && ('t' == FULL_FCDA_Name[length_FCDA - 1]))
				{
					apr_time_t t = convert_btime6_to_apr_time(&(mms_dec_data.data_item[ii].u.data_bTime6));
					time = t / 1000;
					break;
				}
			}
			for (ii = 0; ii < mms_dec_data.item_num; ++ii)//遍历FCDA
			{
				char* FULL_FCDA_Name;
				int length_FCDA;

				//printf("\n ----5.11--%d-%s\n", va_num, mms_dec_data.data_item[ii].comp_name);	
				if (strcmp(mms_dec_data.data_item[ii].comp_name, "") == 0)
					FULL_FCDA_Name = apr_pstrdup(g_pt61850app->tmp_pool, DataObjectFullName[j]);
				else
					FULL_FCDA_Name = apr_pstrcat(g_pt61850app->tmp_pool, DataObjectFullName[j], "$", mms_dec_data.data_item[ii].comp_name, NULL);
				//		cout<<"		FULL_FCDA_Name "<<  FULL_FCDA_Name <<endl;
				//printf("\n ----5.12--%d- %s \n", va_num, FULL_FCDA_Name);
				length_FCDA = strlen(FULL_FCDA_Name);
				//11 08 beijing 许继装置不处理q
				if (('$' == FULL_FCDA_Name[length_FCDA - 2]) && ('q' == FULL_FCDA_Name[length_FCDA - 1])) {
					if (not_set_rpt_q_this) {
						int flag = Set_q_from_61850rpt(mms_dec_data.data_item[ii].u.data_str);

						if (strstr(rcb_info->RptID, "QVVR")) {//CZY 2023-08-17 WW 2022-11-14 修改判断LLN0$BR$brcbQVVR
							//need do nothing!
							not_set_rpt_q_this = FALSE;
						}
						else if (strstr(rcb_info->RptID, "RDRE")) {//CZY 2023-08-17 WW 2022-11-14修改判断 LLN0$BR$brcbRDRE
							//need do nothing!
							not_set_rpt_q_this = FALSE;
						}
						else if (strstr(FULL_FCDA_Name, "GGIO") || strstr(FULL_FCDA_Name, "LPHD"))
						{
							not_set_rpt_q_this = FALSE;
						}
						else {
							json_block_create_flag(LD_info->mp_id, flag, rptinfo->flickerflag);
							json_trace_block_create_flag(LD_info->mp_id, flag, rptinfo->flickerflag, rcb_info->RptID, rptinfo->rptNo);
							not_set_rpt_q_this = FALSE;
						}
					}
				}
				else if (('$' == FULL_FCDA_Name[length_FCDA - 2]) && ('t' == FULL_FCDA_Name[length_FCDA - 1])) {
					if (not_set_rpt_TimeID_this) {
						apr_time_t t = convert_btime6_to_apr_time(&(mms_dec_data.data_item[ii].u.data_bTime6));
						if (strstr(rcb_info->RptID, "QVVR")) {//CZY 2023-08-17 WW 2022-11-14 修改判断LLN0$BR$brcbQVVR
							if (strstr(FULL_FCDA_Name, "VarStr$t")) {
								processQVVR_time(LD_info, t / 1000);
								not_set_rpt_TimeID_this = FALSE;
							}
						}
						else if (strstr(rcb_info->RptID, "RDRE")) {//CZY 2023-08-17 WW 2022-11-14修改判断 LLN0$BR$brcbRDRE
							//need do nothing!
							not_set_rpt_TimeID_this = FALSE;
						}
						else if (strstr(FULL_FCDA_Name, "GGIO") || strstr(FULL_FCDA_Name, "LPHD")) {//CZY 2023-08-17 WW 2022-11-14修改判断 LLN0$BR$brcbRDRE
							//need do nothing!
							not_set_rpt_TimeID_this = FALSE;
						}
						else {
							json_block_create_time(LD_info->mp_id, t / 1000, rptinfo->flickerflag);
							json_trace_block_create_time(LD_info->mp_id, t / 1000, rptinfo->flickerflag, rcb_info->RptID, rptinfo->rptNo);
							printf("rcb_info->RptID=%s ,LineId=%d , Timestamp= %lld \n", rcb_info->RptID, LD_info->line_id, t / 1000);
							not_set_rpt_TimeID_this = FALSE;
							//if (strstr(rcb_info->RptID, "LLN0$RP$urcbRealData")) {
							int rpt_no;
							//20241223lnk添加终端号参数
							ied_usr_t	*ied_usr = GET_IEDEXT_ADDR(ied);
							for(rpt_no=0 ; rpt_no<LD_info->rptcount; rpt_no++) {//实时数据的每份报告都进行时间判断lnk20250624
								printf("LD_info->rptinfo[%d]->rptID=%s ",rpt_no,LD_info->rptinfo[rpt_no]->rptID);

								if(strstr(rcb_info->RptID,extractAfterUrCb(LD_info->rptinfo[rpt_no]->rptID ))){
									printf("rtdata RptID match");
									if (urcbRealDataHasReceived(ied_usr->dev_idx,rpt_no,LD_info, t / 1000)){//判断时间重复
										printf("this rt report Time repeats");
										//json_trace_block_create_end(LD_info->v_wiring_type, LD_info->mp_id, rptinfo->flickerflag, rcb_info->RptID, rptinfo->rptNo);
										return;
									}
								}								
							}
						}
					}
				}
				else {
					double v = 0.0;
					if (mms_dec_data.data_item[ii].type == DATA_INT_TYPE)
						v = mms_dec_data.data_item[ii].u.data_int;
					else if (mms_dec_data.data_item[ii].type == DATA_UINT_TYPE)
						v = mms_dec_data.data_item[ii].u.data_uint;
					else if (mms_dec_data.data_item[ii].type == DATA_INT64_TYPE)
						v = (double)mms_dec_data.data_item[ii].u.data_int64;
					else if (mms_dec_data.data_item[ii].type == DATA_UINT64_TYPE)
						v = (double)mms_dec_data.data_item[ii].u.data_uint64;
					else if (mms_dec_data.data_item[ii].type == DATA_DOUBLE_TYPE)
						v = mms_dec_data.data_item[ii].u.data_double;
					else if (mms_dec_data.data_item[ii].type == DATA_STR_TYPE)
						v = strtol(mms_dec_data.data_item[ii].u.data_str, NULL, 2);

					if (strstr(rcb_info->RptID, "QVVR")) {//CZY 2023-08-17 WW 2022-11-14 修改判断LLN0$BR$brcbQVVR
						processQVVR_data(LD_info, FULL_FCDA_Name, v);
					}
					else if (strstr(rcb_info->RptID, "RDRE")) {//CZY 2023-08-17 WW 2022-11-14 修改判断LLN0$BR$brcbRDRE
						processRDRE_data(LD_info, FULL_FCDA_Name, v);
					}
					else if (strstr(FULL_FCDA_Name, "GGIO") || strstr(FULL_FCDA_Name, "LPHD"))
					{
						ied_t* ied = LD_info->ied;
						ied_usr_t* ied_usr = GET_IEDEXT_ADDR(ied);
						processGGIO_start_data_end(LD_info->mp_id, FULL_FCDA_Name, v, time, ied_usr->dev_type, LD_info->line_id);//GGIO数据全套处理流程
					}
					else {
						json_block_create_data(LD_info->mp_id, FULL_FCDA_Name, v, rptinfo->flickerflag);
						json_trace_block_create_data(LD_info->mp_id, FULL_FCDA_Name, v, rptinfo->flickerflag, rcb_info->RptID, rptinfo->rptNo);
					}
				}//else			   	
			}

		}
	}
	if (LD_info->line_id > 0) {
		//set_rpt_LineID(LD_info->line_id);
		//set_line_info(RPT_IDX,LD_info->line_id,LD_info->SubV_Index,LD_info->Dev_Index,LD_info->Sub_Index,LD_info->GD_Index);
		//write_updatetime_to_db(chnl_usr->chnl->addr);
		if (strstr(rcb_info->RptID, "QVVR")) {//CZY 2023-08-17 WW 2022-11-14 修改判断LLN0$BR$brcbQVVR
			processQVVR_end(LD_info);
		}
		else if (strstr(rcb_info->RptID, "RDRE")) {//CZY 2023-08-17 WW 2022-11-14 修改判断LLN0$BR$brcbRDRE
			processRDRE_end(LD_info);
		}
		else {
			printf("%d : %d", LD_info->rptRecvFlag, LD_info->rptRecvCheckFlag);
			json_trace_block_create_end(LD_info->v_wiring_type, LD_info->mp_id, rptinfo->flickerflag, rcb_info->RptID, rptinfo->rptNo);
			//append_db_records(RPT_IDX);
			if (rptinfo->flickerflag == 1)//CZY 2023-08-17 WW 2022-11-14 增加闪变标志
			{
				//ied_usr_t* ied_usr = ied->usr_ext;//CZY 2023-08-17 WW 2022年12月6日14:09:08 增加多个ICD支持
				//int devkind = ied_usr->dev_flag;
				json_block_create_end(LD_info->v_wiring_type, LD_info->mp_id, rptinfo->flickerflag);//lnk角形处理
			}
			else if (rptinfo->flickerflag == 0) {//CZY 2023-08-17 WW 2022-11-14 增加多个报告判断
				if (LD_info->rptRecvFlag == LD_info->rptRecvCheckFlag) {
					//LD_info->rptRecvFlag是报告初始化时报告序号左移之和，固定于报告初始化
					//ied_usr_t* ied_usr = ied->usr_ext;//CZY 2023-08-17 WW 2022年12月6日14:09:08 增加多个ICD支持
					//int devkind = ied_usr->dev_flag;
					json_block_create_end(LD_info->v_wiring_type, LD_info->mp_id, rptinfo->flickerflag);//lnk角形处理
					LD_info->rptRecvCheckFlag = 0;
				}
			}
			else if (rptinfo->flickerflag == 2) {//CZY 2023-08-17 WW 2022-11-14 增加多个报告判断
				if (LD_info->rptPstRecvFlag == LD_info->rptPstRecvCheckFlag) {
					//ied_usr_t* ied_usr = ied->usr_ext;//CZY 2023-08-17 WW 2022年12月6日14:09:08 增加多个ICD支持
					//int devkind = ied_usr->dev_flag;
					json_block_create_end(LD_info->v_wiring_type, LD_info->mp_id, rptinfo->flickerflag);//lnk角形处理
					LD_info->rptPstRecvCheckFlag = 0;
				}
			}
		}
	}
	else {
		echo_err3("Ignore this report due to line_id invalid , Report From %s %s %s  !!!",
			APR_EGENERAL, chnl_usr->ip_str, LD_info->LD_name, rcb_info->RptID);
	}
	printf("[END Process] Report From %s:%d %s %s ,va_total =  %i ,【count = %i】 \n",
		chnl_usr->ip_str, chnl_usr->chnl->port, LD_info->LD_name, rcb_info->RptID, va_total, rptinfo->count);

	//apr_time_t	previousTimeend = apr_time_now();//
	//apr_time_exp_t localTimeend;
	//apr_time_exp_gmt(&localTimeend, previousTimeend);
	//printf("\n ------>>>>>>> end time %d %d:%d:%d.%d \n",
	//	localTimeend.tm_mday, localTimeend.tm_hour, localTimeend.tm_min, localTimeend.tm_sec, localTimeend.tm_usec);

	//printf("\n ----6-%d--\n", va_num);	
	/* If "reason" enabled, check "Inclusion" to figure out what is being received.*/
	if (BSTR_BIT_GET(OptFldsData, OPTFLD_BITNUM_REASON)) {
		for (j = 0; j < rcb_info->numDsVar; ++j) {
			if (BSTR_BIT_GET(InclusionData, j)) {
				//   SLOGCALWAYS2("Var# %02d:    Reason# %d", va_num, j);
				//log_var_data (info_va[va_num],&mms_dec_data);       
				va_num++;
			}
		}
	}
	//设置响应时间，避免频繁召唤装置名称，导致丢包 add on Aug 3 for testing of guangdong CRITICAL
	chnl_usr->m_LastPosRespTime = sGetMsTime();
	//assert (va_num==va_total);	/* Did we count right?	*/
	if (va_num != va_total)
		echo_err5("va_num!=va_total! Report From %s %s %s , %d!=%d !!!",
			APR_EGENERAL, chnl_usr->ip_str, LD_info->LD_name, rcb_info->RptID, va_num, va_total);
}
/************************************************************************/
/*			object_name_clone_create			*/
/* Create clone of OBJECT_NAME struct. Can't just copy struct because	*/
/* struct contains pointers to strings.					*/
/* The function "object_name_clone_destroy" should be called to destroy	*/
/* the clone.								*/
/************************************************************************/
OBJECT_NAME* object_name_clone_create(OBJECT_NAME* srcobj)
{
	OBJECT_NAME* dstobj;
	size_t extended_size;
	ST_CHAR* extended_ptr;

	/* Allocate OBJECT_NAME structure plus extra room for additional data.
	* This effectively combines 3 allocations into one.
	* Set pointers in OBJECT_NAME structure to point into the extra space.
	*/
	extended_size = sizeof(OBJECT_NAME)
		+ MAX_IDENT_LEN + 1			/* dstobj->obj_name.vmd_spec	*/
		+ MAX_IDENT_LEN + 1;			/* dstobj->domain_id		*/

	dstobj = (OBJECT_NAME*)chk_malloc(extended_size);

	/* Copy old struct to new struct (before setting ptrs in new struct).	*/
	memcpy(dstobj, srcobj, sizeof(OBJECT_NAME));

	/* Fix ptrs to strings in new struct	*/
	extended_ptr = (ST_CHAR*)(dstobj + 1);	/* point after dstobj struct	*/

	dstobj->obj_name.vmd_spec = extended_ptr;
	extended_ptr += (MAX_IDENT_LEN + 1);		/* point after dstobj->obj_name.vmd_spec*/

	dstobj->domain_id = extended_ptr;

	/* Copy strings to the new struct.	*/
	strcpy(dstobj->obj_name.vmd_spec, srcobj->obj_name.vmd_spec);
	if (dstobj->object_tag == DOM_SPEC)
		strcpy(dstobj->domain_id, srcobj->domain_id);
	return (dstobj);
}

/************************************************************************/
/*			object_name_clone_destroy			*/
/* Destroy OBJECT_NAME clone created by "object_name_clone_create".	*/
/************************************************************************/
ST_VOID object_name_clone_destroy(OBJECT_NAME* obj)
{
	/* allocated by object_name_clone_create using chk_malloc, so use chk_free.*/
	chk_free(obj);
}

ST_RET check_va_count_too_big(ST_INT va_current_count, ST_INT va_total)
{
	if (va_current_count < va_total) {
		return SD_SUCCESS;
	}
	else {
		echo_warn1("report va total %d is so small,may be error\n", va_total);
		return SD_FAILURE;
	}
}

/************************************************************************/
/*			u_iec_rpt_ind					*/
/* This function processes ONLY IEC-61850 and UCA Reports. If any	*/
/* other InformationReport is received, it logs an error message and	*/
/* ignores it.								*/
/* CRITICAL: this function assumes that a pointer to an ALL_RCB_INFO	*/
/*	structure has been saved in the					*/
/*	user_info member of the MVL_NET_INFO structure for this conn.	*/
/************************************************************************/
ST_RET u_iec_rpt_ind_by_devtype(MVL_COMM_EVENT* event)
{
	INFO_REQ_INFO* info_ptr;
	ST_INT j, va_num, va_total;
	VAR_ACC_SPEC* va_spec;
	MVL_VAR_ASSOC** info_va;
	RCB_INFO* rcb_info;
	ST_UINT8* OptFldsData;		/* ptr to data part of OptFlds bvstring	*/
	ST_UINT8* InclusionData;	/* ptr to Inclusion bstring		*/
	ST_CHAR saveRptID[66];
	ALL_RCB_INFO* all_rcb_info;
	ST_RET retcode = SD_SUCCESS;

	info_ptr = (INFO_REQ_INFO*)event->u.mms.dec_rslt.data_ptr;
	va_spec = &info_ptr->va_spec;
	va_total = info_ptr->num_of_acc_result;

	/* An IEC-61850 or UCA report must be a NamedVariableList named "RPT".
	* Ignore any other InformationReport.
	*/
	if (va_spec->var_acc_tag != VAR_ACC_NAMEDLIST
		|| strcmp(va_spec->vl_name.obj_name.vmd_spec, "RPT") != 0)
	{
		echo_warn("Received InformationReport is not a IEC-61850 Report or UCA Report. Ignored.");
		return (SD_FAILURE);
	}
	//////////////////
	//WW 2023-08-29 增加终端类型与报告rcb_info绑定代码
	int port; //端口

	RCB_INFO *rcb_info1;
	ST_CHAR rptID[MVL61850_MAX_RPTID_LEN + 1] = { 0 }; //报告ID 66
	ST_ULONG a_ip = get_rem_ip_addr_inline(event->net_info->acse_conn_id, &port); //WW 2023-08-29 获取装置IP、端口号
	ST_CHAR *p_ip = _convert_ip_2_char(htonl(a_ip)); //WW 2023-08-29 将IP转为字符串指针  例：192.168.1.238
	if (gDev_rcb_list.dev_rcb_info_Head == NULL) //全局报告控制块指针 || gDev_rcb_list->rcb_info_list == NULL
	{
		//SLOGALWAYS0 ("Received InformationReport. No IEC-61850 or UCA RCB enabled. Ignored.");
		//LOG_INFO("收到报告，未启用IEC-61850或UCA RCB使能。忽略！"); //仅调试使用！ zl 2019-1-23 09:10:39
		return (SD_FAILURE);
	}
	//逻辑：根据装置IP、端口号，先找到装置类型，再匹配报告信息，再解析报告！2019-12-24 17:27:29
//装置类型查找判断------------------------------------------------------------------------------------------------------------------------------
	Dev_RCB_INFO *dev_rcb = NULL;//装置报告结构指针
	Dev_IP_Port_INFO *dev_info = NULL; //装置IP、端口号结构指针
	ST_BOOLEAN bFindIpPort = 0; //是否在链表中匹配到装置IP、端口号
	for (dev_rcb = gDev_rcb_list.dev_rcb_info_Head; dev_rcb != NULL; dev_rcb = (Dev_RCB_INFO *)list_get_next(gDev_rcb_list.dev_rcb_info_Head, dev_rcb)) //①遍历 全局装置报告控制块链表
	{
		//LOG_INFO("(报告解析)遍历gDev_rcb_list链表, dev_type_name= %s", dev_rcb->dev_type_name); //仅调试使用！ zl 2019-12-24 00:16:31
		//装置IP、端口号查找判断-------------------------------------------------------------------------------------------------------------------------------------------------------------------
		for (dev_info = dev_rcb->dev_ip_port_list; dev_info != NULL; dev_info = (Dev_IP_Port_INFO *)list_get_next(dev_rcb->dev_ip_port_list, dev_info)) //②遍历 全局装置IP、端口链表
		{
			//LOG_INFO("(报告解析)遍历dev_ip_port_list链表, dev_type_name= %s, ip= %s, port= %d", dev_rcb->dev_type_name, dev_info->IP, dev_info->Port); //仅调试使用！ zl 2019-12-24 00:16:31
			if ((strcmp(dev_info->IP, p_ip) == 0) && (dev_info->Port == port)) //匹配 装置IP && 端口号
			{
				bFindIpPort = 1; //查找到装置IP && 端口号
				//LOG_INFO("(报告解析)匹配到装置dev_type_name= %s, IP= %s，Port= %d", dev_rcb->dev_type_name, dev_info->IP, dev_info->Port); //仅调试使用！ zl 2019-12-24 00:23:55
				//strcpy(dev_rcb->dev_type_name, dev_rcb->dev_type_name); //匹配 装置类型
				break;
			}
		} //遍历 装置IP、端口链表 结束
		if (bFindIpPort) //在链表中匹配到装置IP、端口号
			break;
	} //遍历 全局装置报告控制块链表 结束

	if (!bFindIpPort) //未查找到装置IP、端口
	{
		//LOG_INFO("(报告解析)dev_type_name= %s未匹配到IP= %s，Port= %d的装置类型，goto CLEANUP跳出！", dev_rcb->dev_type_name, p_ip, port); //仅调试使用！ zl 2019-12-24 00:23:55
		retcode = SD_FAILURE;
		goto END; //跳转至 CLEANUP，即：返回retcode结束
	}

	rcb_info = dev_rcb->rcb_info_list; //根据装置类型，匹配报告控制块信息
	//WW 2023-08-29 end
	//////////////////////////////


	/* Get "all_rcb_info" from "user_info". User must set "user_info" when conn established.*/
	//////////////////////
	//WW 2023-08-29注释
	//all_rcb_info = (ALL_RCB_INFO*)event->net_info->user_info;

	///* Check "all_rcb_info" to see if any RCB has been enabled.	*/
	//if (all_rcb_info == NULL || all_rcb_info->rcb_info_list == NULL)
	//{
	//	echo_warn("Received InformationReport. No IEC-61850 or UCA RCB enabled. Ignored.");
	//	return (SD_FAILURE);
	//}
	//WW 2023-08-29 end
	////////////////////////
	/* Create array of (MVL_VAR_ASSOC *) needed for converting the received
	* data to local format.
	* Use variables created earlier to fill in the array. These variables will
	* hold the decoded data.
	* NOTE: any return after this must free info_va (see CLEANUP label).
	*/
	info_va = (MVL_VAR_ASSOC**)chk_calloc(va_total,
		sizeof(MVL_VAR_ASSOC*));

	//////////////////
//WW 2023-08-29 增加终端类型与报告rcb_info绑定代码
	rcb_info = dev_rcb->rcb_info_list; //根据装置类型，匹配报告控制块信息
	va_num = 0;
	info_va[va_num++] = rcb_info->rcb_var.RptID;
	//LOG_INFO("va_num = %d", va_num); //仅调试使用！ zl 2019-1-23 09:10:39

	all_rcb_info = (ALL_RCB_INFO*)chk_calloc(1, sizeof(ALL_RCB_INFO));
	all_rcb_info->rpt_typeids = &g_rpt_typeids;
	all_rcb_info->rcb_info_list = dev_rcb->rcb_info_list;
	event->net_info->user_info = all_rcb_info;
	rcb_info = all_rcb_info->rcb_info_list;
	rcb_info->rcb_data.RptID[0] = '\0';	/* start with empty string	*/
	mvl_info_data_to_local(event, va_num, info_va); //将报告数据转换为本地格式
	/* NOTE: decoded RptID is now in "rcb_info->rcb_data.RptID". Save it.*/
	strcpy(saveRptID, rcb_info->rcb_data.RptID); //装置上送报告ID  例：PQM2/LLN0$BR$brcbStatisticData01
	//LOG_INFO("装置上送报告saveRptID：%s", saveRptID); //仅调试使用！ zl 2019-1-23 09:10:39
	chk_free(all_rcb_info);
	event->net_info->user_info = NULL;
	/* Search list of "rcb_info" to find one with matching RptID. If not found, rcb_info will be == NULL. */
	//装置报告控制块查找判断-----------------------------------------------------------------------------------------------------------------------------------------------------
	for (rcb_info = dev_rcb->rcb_info_list; rcb_info != NULL; rcb_info = (RCB_INFO *)list_get_next(dev_rcb->rcb_info_list, rcb_info)) //遍历 全局报告控制块链表
	{
		//LOG_INFO("遍历报告控制块链表RptID：%s，saveRptID：%s", rcb_info->RptID, saveRptID); //仅调试使用！ zl 2019-1-23 09:10:39
		if (strcmp(rcb_info->RptID, saveRptID) == 0)
			break; /* rcb_info now points to right structure */
	}

	if (!rcb_info) //报告控制块指针为空！
	{
		//SLOGALWAYS1 ("RptID '%s' not recognized on this connection. Received report ignored.", saveRptID);
		//LOG_INFO("(报告解析)报告控制块链表查找不到RptID= '%s'，goto CLEANUP跳出！", saveRptID); //仅调试使用！ zl 2019-12-23 15:09:36
		retcode = SD_FAILURE;
		goto CLEANUP; //跳转至 CLEANUP，即：返回retcode结束
	}
//WW 2023-08-29 end
//////////////////////////////
	//////////////////////
	//WW 2023-08-29注释
	///* Must decode the RptID first to find the correct RCB_INFO. Could decode into
	//* any "vstring65" variable. We just use the RptID variable from the first
	//* rcb_info on the list.
	//*/
	//rcb_info = all_rcb_info->rcb_info_list;	/* use first rcb_info on list*/
	//va_num = 0;
	//info_va[va_num++] = rcb_info->rcb_var.RptID;

	//rcb_info->rcb_data.RptID[0] = '\0';	/* start with empty string	*/
	//mvl_info_data_to_local(event, va_num, info_va);
	///* NOTE: decoded RptID is now in "rcb_info->rcb_data.RptID". Save it.*/
	//strcpy(saveRptID, rcb_info->rcb_data.RptID);

	///* Search list of "rcb_info" to find one with matching RptID.	*/
	///* If not found, rcb_info will be == NULL.			*/
	//for (rcb_info = all_rcb_info->rcb_info_list;
	//	rcb_info != NULL;
	//	rcb_info = (RCB_INFO*)list_get_next(all_rcb_info->rcb_info_list, rcb_info))
	//{
	//	if (strcmp(rcb_info->RptID, saveRptID) == 0)
	//		break;	/* rcb_info now points to right structure	*/
	//}

	//if (!rcb_info)
	//{
	//	echo_warn1("RptID '%s' not recognized on this connection. Received report ignored.", saveRptID);
	//	retcode = SD_FAILURE;
	//	goto CLEANUP;
	//}

	//WW 2023-08-29 end
	////////////////////////
	va_num = 0;
	info_va[va_num++] = rcb_info->rcb_var.RptID;
	if ((retcode = check_va_count_too_big(va_num, va_total)) == SD_FAILURE) goto CLEANUP;
	info_va[va_num++] = rcb_info->rcb_var.OptFlds;
	if ((retcode = check_va_count_too_big(va_num, va_total)) == SD_FAILURE) goto CLEANUP;
	/* Perform 1st decode (up through "OptFlds"). Need "OptFlds" to figure
	* out what comes next.
	*/
	mvl_info_data_to_local(event, va_num, info_va);

	/* Examine "OptFlds", and set up 2nd decode to decode all options	*/
	OptFldsData = rcb_info->rcb_data.OptFlds.data_1;	/* use local var	*/
	//  printf ("OptFlds = 0x%02x 0x%02x\n", OptFldsData[0],OptFldsData[1]);	/* 9 bit bstr (2 bytes)	*/

	/* NOTE: Don't change initial entries in "info_va". Add these right after "OptFlds".*/
	if (BSTR_BIT_GET(OptFldsData, OPTFLD_BITNUM_SQNUM))
		info_va[va_num++] = rcb_info->rcb_var.SqNum;
	if ((retcode = check_va_count_too_big(va_num, va_total)) == SD_FAILURE) goto CLEANUP;
	if (BSTR_BIT_GET(OptFldsData, OPTFLD_BITNUM_TIMESTAMP))
		info_va[va_num++] = rcb_info->rcb_var.TimeOfEntry;
	if ((retcode = check_va_count_too_big(va_num, va_total)) == SD_FAILURE) goto CLEANUP;
	if (BSTR_BIT_GET(OptFldsData, OPTFLD_BITNUM_DATSETNAME))
	{
		info_va[va_num++] = rcb_info->rcb_var.DatSetNa;
		///////////////////
		//WW 2023-08-29 

		log_var_to_data(rcb_info->rcb_var.DatSetNa, rptID);
		//for (rcb_info1 = all_rcb_info->rcb_info_list;
		//    rcb_info1 != NULL;
		//	rcb_info1 = (RCB_INFO *) list_get_next (all_rcb_info->rcb_info_list, rcb_info1)) //遍历 装置报告控制块链表
		for (rcb_info1 = dev_rcb->rcb_info_list;
			rcb_info1 != NULL;
			rcb_info1 = (RCB_INFO *)list_get_next(dev_rcb->rcb_info_list, rcb_info1)) //遍历 报告控制块链表
		{
			if (strcmp(rcb_info1->ds_Nam, rptID) == 0) //报告ID，作为不同报告间的唯一标识符
			{
				//SLOGALWAYS3 ("Num of var received in RPT (%d) does not match expected (%d)dateSet=%s ", va_total, va_num,rptID);
				//LOG_INFO("(报告解析)接收报告RptID= '%s'数据集rptID= %s，大小(%d)与预期大小(%d)不匹配，brak跳出！", saveRptID, rptID, va_total, va_num); //仅调试使用！ zl 2019-12-23 15:09:30
				rcb_info = rcb_info1;
				break;
			}
		}
		//WW 2023-08-29 en
		///////////////////////////
	}
	if ((retcode = check_va_count_too_big(va_num, va_total)) == SD_FAILURE) goto CLEANUP;
	/* The following optional vars are supported by IEC-61850 but they are
	* NOT supported by UCA. This client must NOT set these OptFlds bits
	* when connected to a UCA server, and these variables will NOT be
	* included in a report received from a UCA server.
	*/

	if (BSTR_BIT_GET(OptFldsData, OPTFLD_BITNUM_BUFOVFL))
		info_va[va_num++] = rcb_info->rcb_var.BufOvfl;
	if ((retcode = check_va_count_too_big(va_num, va_total)) == SD_FAILURE) goto CLEANUP;
	if (BSTR_BIT_GET(OptFldsData, OPTFLD_BITNUM_ENTRYID))
		info_va[va_num++] = rcb_info->rcb_var.EntryID;
	if ((retcode = check_va_count_too_big(va_num, va_total)) == SD_FAILURE) goto CLEANUP;
	if (BSTR_BIT_GET(OptFldsData, OPTFLD_BITNUM_CONFREV))
		info_va[va_num++] = rcb_info->rcb_var.ConfRev;
	if ((retcode = check_va_count_too_big(va_num, va_total)) == SD_FAILURE) goto CLEANUP;
	if (BSTR_BIT_GET(OptFldsData, OPTFLD_BITNUM_SUBSEQNUM))
	{
		info_va[va_num++] = rcb_info->rcb_var.SubSeqNum;
		if ((retcode = check_va_count_too_big(va_num, va_total)) == SD_FAILURE) goto CLEANUP;
		info_va[va_num++] = rcb_info->rcb_var.MoreSegmentsFollow;
		if ((retcode = check_va_count_too_big(va_num, va_total)) == SD_FAILURE) goto CLEANUP;
	}
	info_va[va_num++] = rcb_info->rcb_var.Inclusion;
	if ((retcode = check_va_count_too_big(va_num, va_total)) == SD_FAILURE) goto CLEANUP;
	assert(va_num < va_total);

	/* Perform 2nd decode (up through "Inclusion").	*/
	mvl_info_data_to_local(event, va_num, info_va);

	/* Examine "Inclusion", and set up 3rd decode to decode all data.	*/
	InclusionData = rcb_info->rcb_data.Inclusion;	/* use local var	*/
	//  printf ("\nThe 1st byte Inclusion = 0x%02X\n", InclusionData[0]);	/* Just print 1st byte	*/

	/* NOTE: Don't change initial entries in "info_va". Add these right after "Inclusion".*/

	/* If "data-Ref" enabled, check "Inclusion" to figure out what is being received.*/
	if (BSTR_BIT_GET(OptFldsData, OPTFLD_BITNUM_DATAREF))
	{
		for (j = 0; j < rcb_info->numDsVar; ++j)
		{
			if (BSTR_BIT_GET(InclusionData, j)) {
				info_va[va_num++] = rcb_info->rcb_var.dataRefName[j];
				if ((retcode = check_va_count_too_big(va_num, va_total)) == SD_FAILURE) goto CLEANUP;
			}
		}
	}

	/* HERE'S THE DATA. Check "Inclusion" to figure out what is being received.*/
	for (j = 0; j < rcb_info->numDsVar; ++j)
	{
		if (BSTR_BIT_GET(InclusionData, j)) {
			info_va[va_num++] = rcb_info->rcb_var.dataValue[j];
			if ((retcode = check_va_count_too_big(va_num, va_total)) == SD_FAILURE) goto CLEANUP;
		}
	}

	/* If "reason" enabled, check "Inclusion" to figure out what is being received.*/
	if (BSTR_BIT_GET(OptFldsData, OPTFLD_BITNUM_REASON))
	{
		for (j = 0; j < rcb_info->numDsVar; ++j)
		{
			if (BSTR_BIT_GET(InclusionData, j)) {
				info_va[va_num++] = rcb_info->rcb_var.Reason[j];
			}
		}
	}

	/* Does num of variables received match expected num.*/
	if (va_num != va_total)
	{
		echo_warn2("Num of var received in RPT (%d) does not match expected (%d). Possibly incorrect OptFlds or inclusion bitstring", va_total, va_num);
		//		printf      ("Num of var received in RPT (%d) does not match expected (%d). Possibly incorrect OptFlds or inclusion bitstring\n", va_total, va_num);
	}
	else
	{
		/* Perform 3rd decode (everything).	*/
		mvl_info_data_to_local(event, va_num, info_va);

		u_iec_rpt_ind_data_by_devtype(info_va, OptFldsData, InclusionData, rcb_info, va_total, event->net_info);
	}

CLEANUP:
	chk_free(info_va);
END:
	return (retcode);
}

ST_RET u_iec_rpt_ind(MVL_COMM_EVENT* event)
{
	INFO_REQ_INFO* info_ptr;
	ST_INT j, va_num, va_total;
	VAR_ACC_SPEC* va_spec;
	MVL_VAR_ASSOC** info_va;
	RCB_INFO* rcb_info, * rcb_info1;
	ST_UINT8* OptFldsData;		/* ptr to data part of OptFlds bvstring	*/
	ST_UINT8* InclusionData;	/* ptr to Inclusion bstring		*/
	ST_CHAR saveRptID[66];
	ALL_RCB_INFO* all_rcb_info;
	ST_RET retcode = SD_SUCCESS;

	info_ptr = (INFO_REQ_INFO*)event->u.mms.dec_rslt.data_ptr;
	va_spec = &info_ptr->va_spec;
	va_total = info_ptr->num_of_acc_result;

	/* An IEC-61850 or UCA report must be a NamedVariableList named "RPT".
	* Ignore any other InformationReport.
	*/
	if (va_spec->var_acc_tag != VAR_ACC_NAMEDLIST
		|| strcmp(va_spec->vl_name.obj_name.vmd_spec, "RPT") != 0)
	{
		echo_warn("Received InformationReport is not a IEC-61850 Report or UCA Report. Ignored.");
		return (SD_FAILURE);
	}

	/* Get "all_rcb_info" from "user_info". User must set "user_info" when conn established.*/
	all_rcb_info = (ALL_RCB_INFO*)event->net_info->user_info;

	/* Check "all_rcb_info" to see if any RCB has been enabled.	*/
	if (all_rcb_info == NULL || all_rcb_info->rcb_info_list == NULL)
	{
		echo_warn("Received InformationReport. No IEC-61850 or UCA RCB enabled. Ignored.");
		return (SD_FAILURE);
	}

	/* Create array of (MVL_VAR_ASSOC *) needed for converting the received
	* data to local format.
	* Use variables created earlier to fill in the array. These variables will
	* hold the decoded data.
	* NOTE: any return after this must free info_va (see CLEANUP label).
	*/
	info_va = (MVL_VAR_ASSOC**)chk_calloc(va_total,
		sizeof(MVL_VAR_ASSOC*));

	/* Must decode the RptID first to find the correct RCB_INFO. Could decode into
	* any "vstring65" variable. We just use the RptID variable from the first
	* rcb_info on the list.
	*/
	rcb_info = all_rcb_info->rcb_info_list;	/* use first rcb_info on list*/
	va_num = 0;
	info_va[va_num++] = rcb_info->rcb_var.RptID;

	rcb_info->rcb_data.RptID[0] = '\0';	/* start with empty string	*/
	mvl_info_data_to_local(event, va_num, info_va);
	/* NOTE: decoded RptID is now in "rcb_info->rcb_data.RptID". Save it.*/
	strcpy(saveRptID, rcb_info->rcb_data.RptID);

	/* Search list of "rcb_info" to find one with matching RptID.	*/
	/* If not found, rcb_info will be == NULL.			*/
	for (rcb_info = all_rcb_info->rcb_info_list;
		rcb_info != NULL;
		rcb_info = (RCB_INFO*)list_get_next(all_rcb_info->rcb_info_list, rcb_info))
	{
		if (strcmp(rcb_info->RptID, saveRptID) == 0)
			break;	/* rcb_info now points to right structure	*/
	}

	if (!rcb_info)
	{
		echo_warn1("RptID '%s' not recognized on this connection. Received report ignored.", saveRptID);
		retcode = SD_FAILURE;
		goto CLEANUP;
	}

	va_num = 0;
	info_va[va_num++] = rcb_info->rcb_var.RptID;
	if ((retcode = check_va_count_too_big(va_num, va_total)) == SD_FAILURE) goto CLEANUP;
	info_va[va_num++] = rcb_info->rcb_var.OptFlds;
	if ((retcode = check_va_count_too_big(va_num, va_total)) == SD_FAILURE) goto CLEANUP;
	/* Perform 1st decode (up through "OptFlds"). Need "OptFlds" to figure
	* out what comes next.
	*/
	mvl_info_data_to_local(event, va_num, info_va);

	/* Examine "OptFlds", and set up 2nd decode to decode all options	*/
	OptFldsData = rcb_info->rcb_data.OptFlds.data_1;	/* use local var	*/
	//  printf ("OptFlds = 0x%02x 0x%02x\n", OptFldsData[0],OptFldsData[1]);	/* 9 bit bstr (2 bytes)	*/

	/* NOTE: Don't change initial entries in "info_va". Add these right after "OptFlds".*/
	if (BSTR_BIT_GET(OptFldsData, OPTFLD_BITNUM_SQNUM))
		info_va[va_num++] = rcb_info->rcb_var.SqNum;
	if ((retcode = check_va_count_too_big(va_num, va_total)) == SD_FAILURE) goto CLEANUP;
	if (BSTR_BIT_GET(OptFldsData, OPTFLD_BITNUM_TIMESTAMP))
		info_va[va_num++] = rcb_info->rcb_var.TimeOfEntry;
	if ((retcode = check_va_count_too_big(va_num, va_total)) == SD_FAILURE) goto CLEANUP;

	//WW 2024-09-02 数据集名称确认
	if (BSTR_BIT_GET(OptFldsData, OPTFLD_BITNUM_DATSETNAME))
		info_va[va_num++] = rcb_info->rcb_var.DatSetNa;

	mvl_info_data_to_local(event, va_num, info_va);
	//WW 2024-09-02 endss

	if (BSTR_BIT_GET(OptFldsData, OPTFLD_BITNUM_DATSETNAME))
	{
		//info_va[va_num++] = rcb_info->rcb_var.DatSetNa; //WW 2024-09-02  注释
		///////////////////
		//WW 2023-08-29 
		log_var_to_data(rcb_info->rcb_var.DatSetNa, saveRptID);
		for (rcb_info1 = all_rcb_info->rcb_info_list;
			rcb_info1 != NULL;
			rcb_info1 = (RCB_INFO*)list_get_next(all_rcb_info->rcb_info_list, rcb_info1)) //遍历 报告控制块链表
		{
			if (strcmp(rcb_info1->ds_Nam, saveRptID) == 0) //报告ID，作为不同报告间的唯一标识符
			{
				printf("ds_name '%s' ! Recive name %s WWTest \n", rcb_info1->ds_Nam, saveRptID);
				//SLOGALWAYS3 ("Num of var received in RPT (%d) does not match expected (%d)dateSet=%s ", va_total, va_num,rptID);
				//LOG_INFO("(报告解析)接收报告RptID= '%s'数据集rptID= %s，大小(%d)与预期大小(%d)不匹配，brak跳出！", saveRptID, rptID, va_total, va_num); //仅调试使用！ zl 2019-12-23 15:09:30
				rcb_info = rcb_info1;
				break;
			}
		}
		//WW 2023-08-29 en
		///////////////////////////
	}
	if ((retcode = check_va_count_too_big(va_num, va_total)) == SD_FAILURE) goto CLEANUP;
	/* The following optional vars are supported by IEC-61850 but they are
	* NOT supported by UCA. This client must NOT set these OptFlds bits
	* when connected to a UCA server, and these variables will NOT be
	* included in a report received from a UCA server.
	*/

	if (BSTR_BIT_GET(OptFldsData, OPTFLD_BITNUM_BUFOVFL))
		info_va[va_num++] = rcb_info->rcb_var.BufOvfl;
	if ((retcode = check_va_count_too_big(va_num, va_total)) == SD_FAILURE) goto CLEANUP;
	if (BSTR_BIT_GET(OptFldsData, OPTFLD_BITNUM_ENTRYID))
		info_va[va_num++] = rcb_info->rcb_var.EntryID;
	if ((retcode = check_va_count_too_big(va_num, va_total)) == SD_FAILURE) goto CLEANUP;
	if (BSTR_BIT_GET(OptFldsData, OPTFLD_BITNUM_CONFREV))
		info_va[va_num++] = rcb_info->rcb_var.ConfRev;
	if ((retcode = check_va_count_too_big(va_num, va_total)) == SD_FAILURE) goto CLEANUP;
	if (BSTR_BIT_GET(OptFldsData, OPTFLD_BITNUM_SUBSEQNUM))
	{
		info_va[va_num++] = rcb_info->rcb_var.SubSeqNum;
		if ((retcode = check_va_count_too_big(va_num, va_total)) == SD_FAILURE) goto CLEANUP;
		info_va[va_num++] = rcb_info->rcb_var.MoreSegmentsFollow;
		if ((retcode = check_va_count_too_big(va_num, va_total)) == SD_FAILURE) goto CLEANUP;
	}
	info_va[va_num++] = rcb_info->rcb_var.Inclusion;
	if ((retcode = check_va_count_too_big(va_num, va_total)) == SD_FAILURE) goto CLEANUP;
	assert(va_num < va_total);

	/* Perform 2nd decode (up through "Inclusion").	*/
	mvl_info_data_to_local(event, va_num, info_va);

	/* Examine "Inclusion", and set up 3rd decode to decode all data.	*/
	InclusionData = rcb_info->rcb_data.Inclusion;	/* use local var	*/
	//  printf ("\nThe 1st byte Inclusion = 0x%02X\n", InclusionData[0]);	/* Just print 1st byte	*/

	/* NOTE: Don't change initial entries in "info_va". Add these right after "Inclusion".*/

	/* If "data-Ref" enabled, check "Inclusion" to figure out what is being received.*/
	if (BSTR_BIT_GET(OptFldsData, OPTFLD_BITNUM_DATAREF))
	{
		for (j = 0; j < rcb_info->numDsVar; ++j)
		{
			if (BSTR_BIT_GET(InclusionData, j)) {
				info_va[va_num++] = rcb_info->rcb_var.dataRefName[j];
				if ((retcode = check_va_count_too_big(va_num, va_total)) == SD_FAILURE) goto CLEANUP;
			}
		}
	}

	/* HERE'S THE DATA. Check "Inclusion" to figure out what is being received.*/
	for (j = 0; j < rcb_info->numDsVar; ++j)
	{
		if (BSTR_BIT_GET(InclusionData, j)) {
			info_va[va_num++] = rcb_info->rcb_var.dataValue[j];
			if ((retcode = check_va_count_too_big(va_num, va_total)) == SD_FAILURE) goto CLEANUP;
		}
	}

	/* If "reason" enabled, check "Inclusion" to figure out what is being received.*/
	if (BSTR_BIT_GET(OptFldsData, OPTFLD_BITNUM_REASON))
	{
		for (j = 0; j < rcb_info->numDsVar; ++j)
		{
			if (BSTR_BIT_GET(InclusionData, j)) {
				info_va[va_num++] = rcb_info->rcb_var.Reason[j];
			}
		}
	}

	/* Does num of variables received match expected num.*/
	if (va_num != va_total)
	{
		echo_warn2("Num of var received in RPT (%d) does not match expected (%d). Possibly incorrect OptFlds or inclusion bitstring", va_total, va_num);
		//		printf      ("Num of var received in RPT (%d) does not match expected (%d). Possibly incorrect OptFlds or inclusion bitstring\n", va_total, va_num);
	}
	else
	{
		/* Perform 3rd decode (everything).	*/
		mvl_info_data_to_local(event, va_num, info_va);

		//报告处理加锁lnk20250114
		//pthread_mutex_lock(&mtx);	printf("rpt hold lock !!!!!!!!!!!");
		u_iec_rpt_ind_data(info_va, OptFldsData, InclusionData, rcb_info, va_total, event->net_info);
		//pthread_mutex_unlock(&mtx); printf("rpt free lock !!!!!!!!!!!");

	}

CLEANUP:
	chk_free(info_va);
	return (retcode);
}
/************************************************************************/
//61850报告解析为本地数据
/************************************************************************/
void log_var_to_data(MVL_VAR_ASSOC *var, char *pdata)
{
	static char		text_buf[30000];	/* increase size if ms_local_to_text fails	*/
	//	ST_CHAR			tdl_buf [500];		/* increase size if complex TDL expected*/
	MVL_TYPE_CTRL	*type_ctrl;
	ST_CHAR			*data_text;			/* var data converted to text	*/
	text_buf[0] = 0;
	type_ctrl = mvl_type_ctrl_find(var->type_id);
	if (type_ctrl)
	{
		//if (ms_runtime_to_tdl(type_ctrl->rt,type_ctrl->num_rt,tdl_buf,sizeof(tdl_buf))>0)
		//	 RTP_PRI ("  TYPE: %s\r\n", tdl_buf);
		//else RTP_PRI ("  TYPE: unknown\r\n");
		data_text = ms_local_to_text((char *)var->data, type_ctrl->rt, type_ctrl->num_rt, text_buf, sizeof(text_buf));
		if (data_text) { strcpy(pdata, data_text); }
		else			*pdata = '0';
	}
	else		*pdata = '0';
}
/************************************************************************/
/*			LocToTextBs					*/
/************************************************************************/
#define TEMP_DATA_BUF_SIZE 1024

static ST_RET myLocToTextBs(ST_UCHAR* pSrc, RUNTIME_TYPE* rt, ST_CHAR* text)
{
	int i;
	int j;
	int k;
	int numBits;
	ST_INT16* sp;
	ST_CHAR* destBuf;
	ST_UCHAR mask;

	//调试用
	//printf("pSrc[0]: 0x%X, pSrc[1]: 0x%X, text: %s\n", pSrc[0], pSrc[1], text);

	//printf("%s  text_len %d  %d  pSrc %d %d  \n", text, strlen(text), rt->u.p.el_len, pSrc[0],  pSrc[1]);

	text[0] = 0;
	numBits = rt->u.p.el_len;

	/* We take 1 dest byte per bit, make sure it fits				*/
	if (numBits > TEMP_DATA_BUF_SIZE - 1)
	{
		MLOG_NERR1("Bit String (%d bits) too long to encode", numBits);
		return (SD_FAILURE);
	}

	if (numBits < 0)		/* a variable length bit string	*/
	{
		sp = (ST_INT16*)pSrc;
		numBits = *sp;
		//printf("numBits %d \n", numBits);
		if ((numBits > TEMP_DATA_BUF_SIZE - 1) || (numBits <= 0)) {
			if (numBits != 0) //add by lzm @2018.11.1,to avoid too many print message!!! 
				echo_warn1("Bit String (%d bits) too long to encode\n", numBits);
			return (SD_FAILURE);
		}
		k = 2;
	}
	else
		k = 0;

	destBuf = text;
	for (i = 0; i < numBits; ++k)	/* for each byte, while bits remain	*/
	{
		mask = 0x80;
		for (j = 0; j < 8 && i < numBits; ++i, ++j)
		{
			if (pSrc[k] & mask)
				destBuf[i] = '1';
			else
				destBuf[i] = '0';
			mask >>= 1;
		}
	}
	destBuf[i] = 0;

	//调试用
	//printf("pSrc[0]: 0x%X, pSrc[1]: 0x%X, text: %s\n", pSrc[0], pSrc[1], text);

	return (SD_SUCCESS);
}
/************************************************************************/
/*			LocToTextOct				*/
/************************************************************************/
static ST_RET myLocToTextOct(ST_UCHAR* pSrc, RUNTIME_TYPE* rt, ST_CHAR* text)
{
	int i, k;
	int numBytes;
	ST_INT16* sp;
	ST_CHAR* destBuf;

	text[0] = 0;
	numBytes = rt->u.p.el_len;

	if (numBytes < 0)		/* a variable length octet string	*/
	{
		sp = (ST_INT16*)pSrc;
		numBytes = *sp;
		k = 2;
	}
	else
		k = 0;

	/* We take 3 bytes per octet, make sure it fits			*/
	if (numBytes > TEMP_DATA_BUF_SIZE / 3)
	{
		MLOG_NERR1("Octet String (%d bytes) too long to encode", numBytes);
		return SD_FAILURE;
	}

	destBuf = text;
	for (i = 0; i < numBytes; ++i, ++k)
	{
		apr_snprintf(destBuf, sizeof(destBuf), "%02x ", (unsigned int)pSrc[k]);
		destBuf += 3;
	}

	/* Eliminate the trailing space					*/
	*(destBuf - 1) = 0;
	return (SD_SUCCESS);
}

/************************************************************************/
/*			asn1_convert_utc_to_btod			*/
/* This function converts MMS_UTC_TIME (time relative to 1/1/1970) to	*/
/* the MMS_BTOD (time relative to 1/1/1984).				*/
/* The form field in the MMS_BTOD is set to MMS_BTOD6 by this function.	*/
/* Parameters:								*/
/*	utc	pointer to MMS_UTC_TIME struct that should be converted	*/
/*		to the MMS_BTOD						*/
/*	btod	pointer to MMS_BTOD struct where the result of the	*/
/*		conversion will be placed				*/
/* Return:								*/
/*	SD_SUCCESS	if function successful				*/
/*	SD_FAILURE	otherwise					*/
/************************************************************************/
ST_RET my_asn1_convert_utc_to_btod(MMS_UTC_TIME* utc, MMS_BTOD* btod)
{
	time_t tJan84 = TIME_T_1984_JAN_1;

	/* Now compute the MMS_BTOD time	*/
	btod->day = (long)(utc->secs - tJan84) / SECONDS_PER_DAY;	  /* num of days since 1/1/1984		*/
	btod->ms = (long)((utc->secs - tJan84) % SECONDS_PER_DAY) * 1000; /* num milliseconds since midnight	*/
	/* NOTE: use 0x01000000 (2**24) in fraction computations.	*/
	// 要四舍五入到毫秒数，比如 28.9999应该是29ms
	btod->ms += (ST_INT32)(0.5 + (ST_DOUBLE)utc->fraction * 1000.0 / (ST_DOUBLE)0x01000000);
	/* add the milliseconds left in a sec	*/
	btod->form = MMS_BTOD6;

	return (SD_SUCCESS);
}

/************************************************************************/
/*			ms_local_to_text				*/
/* NOTE: tmpBuf is passed to most "LocToText.." static functions. These	*/
/*	functions must NOT write past end of tmpBuf.			*/
/************************************************************************/


void my_local_to_data(ST_CHAR* datptr, SD_CONST RUNTIME_TYPE* rt_head,
	ST_INT rt_num, MMS_DECODE_DATA* data)
{
	RUNTIME_TYPE* rt_ptr;
	RUNTIME_TYPE* rt_end;
	ST_RET uDataRet;
	ST_INT arr_loop_level = 0;
	ST_INT arr_loops[ASN1_MAX_LEVEL];
	//ST_CHAR *ret_ptr;
	ST_CHAR tmpBuf[TEMP_DATA_BUF_SIZE];
	//ST_UINT  nStrLen = 0;	/* Current text total length 	*/
	ST_UINT	valid_item_num = 0;
	SD_CONST ST_CHAR* comp_name;	/* component name	*///add by lzm
	//ST_INT			comp_name_len;	/* len of component name*/
	ST_UINT		deep = 0;   // deep of structure 
	unsigned int jjj;
	char 	attr_str[256];
	char 	comp_str[32][256];
	int		cur_array_deep = -1;
	ST_UINT	 cur_array_idx;

	if (data == NULL) {
		echo_warn("error ~~~~~~~ my_local_to_data MMS_DECODE_DATA data is NULL return \n");
		return;
	}
	if (rt_num <= 0) {
		echo_warn1("error !!!!!!! my_local_to_data rt_num %d = return \n", rt_num);
		return;
	}
	if (rt_head->el_tag > RT_ARR_END) {
		echo_warn2("error @@@@@@@ my_local_to_data rt_head->el_tag %d size %d = return \n", rt_head->el_tag, rt_head->el_size);
		return;
	}
	data->item_num = 0;
	rt_ptr = (RUNTIME_TYPE*)rt_head;  	/* point to head rt_block	*/
	rt_end = rt_ptr + rt_num; 	/* done when pointer is here		*/
	if (rt_end == NULL) {
		echo_warn("error XXXXXX my_local_to_data rt_end \n");
		return;
	}
	//  SLOGCALWAYS1("	rt_num = %i",rt_num);
	uDataRet = SD_SUCCESS;
	while (rt_ptr < rt_end && uDataRet == SD_SUCCESS)
	{
		if (rt_ptr == NULL) {
			echo_warn("error !!!!!!! my_local_to_data rt_ptr is NULL error,return \n");
			return;
		}
		if (valid_item_num >= MAX_DATA_ITEMS_IN_ONE_DATA) {
			echo_warn2("my_local_to_data RUNTIME_TYPE valid_item_num % >= %d \n", valid_item_num, MAX_DATA_ITEMS_IN_ONE_DATA);
			return;
		}
		if (rt_ptr->el_tag == RT_ARR_END)	/* treat case of array ending	*/
		{
			if (--arr_loops[arr_loop_level] > 0) /* if need to do next ar elmnt	*/
				rt_ptr -= rt_ptr->u.arr.num_rt_blks; /* mv rt_ptr to start of arr	*/
			else
				--arr_loop_level;
		}
		if (rt_ptr->el_tag == RT_ARR_START)	/* treat case of array starting	*/
		{
			/* initialize the loop counter for the array			*/
			++arr_loop_level;
			arr_loops[arr_loop_level] = rt_ptr->u.arr.num_elmnts;
		}

		comp_name = ms_comp_name_find(rt_ptr);//add by lzm
		//printf ("\n %i, comp_name = %s  cur_array_deep/deep: %d/%d \n",rt_ptr->el_tag,comp_name,cur_array_deep,deep);
		//    if (comp_name != NULL  &&  comp_name[0] != 0)
		//      	SLOGCALWAYS2 ("	comp_name= %s, rt_ptr->el_tag =%i ",comp_name,rt_ptr->el_tag); // add by lzm
		memset(attr_str, 0, sizeof(attr_str));
		if (deep == (cur_array_deep + 1))
			cur_array_idx++;
		for (jjj = 0; jjj < deep; jjj++)
		{
			if (strcmp(comp_str[jjj], "")) {
				strcat(attr_str, comp_str[jjj]);
			}
			if (jjj == cur_array_deep) {
				char 	tmp_str[8];
				apr_snprintf(tmp_str, sizeof(tmp_str), "[%d]", cur_array_idx);
				strcat(attr_str, tmp_str);
			}
			if (strcmp(comp_str[jjj], "")) {
				strcat(attr_str, "$");
			}
			//if (strcmp (comp_str[jjj], ""))	{				
			//}
		}
		strcat(attr_str, comp_name);

		switch (rt_ptr->el_tag)
		{
		case RT_ARR_START:
			strcpy(comp_str[deep], comp_name);
			cur_array_deep = deep;
			cur_array_idx = -1;
			assert(++deep < 32);
			break;			/* do nothing	*/

		case RT_ARR_END:			/* array done			*/
			assert(--deep >= 0);
			memset(comp_str[deep], 0, sizeof(comp_str[deep]));
			cur_array_deep = -1;
			cur_array_idx = -1;
			break;			/* do nothing	*/

		case RT_STR_START:
			strcpy(comp_str[deep], comp_name);
			assert(++deep < 32);
			//uDataRet = AddString ("{", textBuf, textBufSize, &nStrLen);
			break;

		case RT_STR_END:			/* structure done		*/
			assert(--deep >= 0);
			memset(comp_str[deep], 0, sizeof(comp_str[deep]));
			//uDataRet = AddString ("}", textBuf, textBufSize, &nStrLen);
			break;

		case RT_BOOL:
			strcpy(data->data_item[valid_item_num].comp_name, (const char*)attr_str);
			data->data_item[valid_item_num].size = 1;
			data->data_item[valid_item_num].type = DATA_INT_TYPE;
			data->data_item[valid_item_num].u.data_int = *((ST_BOOLEAN*)datptr);
			valid_item_num++;
			//        SLOGCALWAYS2("valid_item_num = %i , value=%i",valid_item_num,  *((ST_BOOLEAN *) datptr) );
			data->item_num = valid_item_num;
			break;

		case RT_BIT_STRING:
			myLocToTextBs((ST_UCHAR*)datptr, rt_ptr, tmpBuf);//将存储在内存中的比特串转换为对应的文本字符串（由字符 '1' 和 '0' 组成）
			strcpy(data->data_item[valid_item_num].comp_name, (const char*)attr_str);
			data->data_item[valid_item_num].size = abs(rt_ptr->u.p.el_len);
			data->data_item[valid_item_num].type = DATA_STR_TYPE;
			strcpy(data->data_item[valid_item_num].u.data_str, tmpBuf);
			valid_item_num++;
			//        SLOGCALWAYS2("valid_item_num = %i , value=%s",valid_item_num, tmpBuf );
			data->item_num = valid_item_num;
			break;

		case RT_INTEGER:
		case RT_BCD:
			switch (rt_ptr->u.p.el_len)	/* determine length		*/
			{
			case 1:			/* one byte int 		*/
			case 2:			/* two byte int 		*/
			case 4:			/* four byte integer		*/
				strcpy(data->data_item[valid_item_num].comp_name, (const char*)attr_str);
				data->data_item[valid_item_num].size = (unsigned char)rt_ptr->u.p.el_len;
				data->data_item[valid_item_num].type = DATA_INT_TYPE;
				if (rt_ptr->u.p.el_len == 1)
					data->data_item[valid_item_num].u.data_int = *((ST_INT8*)datptr);
				else if (rt_ptr->u.p.el_len == 2)
					data->data_item[valid_item_num].u.data_int = *((ST_INT16*)datptr);
				else if (rt_ptr->u.p.el_len == 4)
					data->data_item[valid_item_num].u.data_int = *((ST_INT32*)datptr);
				//	        SLOGCALWAYS1("value=%i",data->data_item[valid_item_num].u.data_int );
				valid_item_num++;
				//	        SLOGCALWAYS1("valid_item_num = %i ",valid_item_num );
				data->item_num = valid_item_num;
				break;

#ifdef INT64_SUPPORT
			case 8:			/* eight byte integer		*/
				strcpy(data->data_item[valid_item_num].comp_name, (const char*)attr_str);
				data->data_item[valid_item_num].size = (unsigned char)rt_ptr->u.p.el_len;
				data->data_item[valid_item_num].type = DATA_INT64_TYPE;
				data->data_item[valid_item_num].u.data_int64 = *((ST_INT64*)datptr);
				//	        SLOGCALWAYS1(" value=%lld ",data->data_item[valid_item_num].u.data_int64 );
				valid_item_num++;
				//	        SLOGCALWAYS1("valid_item_num = %i ",valid_item_num );
				data->item_num = valid_item_num;
				break;
#endif
			default:
				echo_warn1("未处理的RT_INTEGER或RT_BCD 整数 size = %i \n", rt_ptr->u.p.el_len);
				break;
			}
			break;

		case RT_UNSIGNED:
			switch (rt_ptr->u.p.el_len)	/* determine length		*/
			{
			case 1:			/* one byte int 		*/
			case 2:			/* two byte int 		*/
			case 4:			/* four byte integer		*/
				strcpy(data->data_item[valid_item_num].comp_name, (const char*)attr_str);
				data->data_item[valid_item_num].size = (unsigned char)rt_ptr->u.p.el_len;
				data->data_item[valid_item_num].type = DATA_UINT_TYPE;
				if (rt_ptr->u.p.el_len == 1)
					data->data_item[valid_item_num].u.data_uint = *((ST_UCHAR*)datptr);
				else if (rt_ptr->u.p.el_len == 2)
					data->data_item[valid_item_num].u.data_uint = *((ST_UINT16*)datptr);
				else if (rt_ptr->u.p.el_len == 4)
					data->data_item[valid_item_num].u.data_uint = *((ST_UINT32*)datptr);
				//	        SLOGCALWAYS1("value=%u",data->data_item[valid_item_num].u.data_uint );
				valid_item_num++;
				//	        SLOGCALWAYS1("valid_item_num = %i ",valid_item_num );
				data->item_num = valid_item_num;
				break;

#ifdef INT64_SUPPORT
			case 8:			/* eight byte integer		*/
				strcpy(data->data_item[valid_item_num].comp_name, (const char*)attr_str);
				data->data_item[valid_item_num].size = (unsigned char)rt_ptr->u.p.el_len;
				data->data_item[valid_item_num].type = DATA_UINT64_TYPE;
				data->data_item[valid_item_num].u.data_int64 = *((ST_UINT64*)datptr);
				//	        SLOGCALWAYS1(" value=%llu ",data->data_item[valid_item_num].u.data_uint64 );
				valid_item_num++;
				//	        SLOGCALWAYS1("valid_item_num = %i ",valid_item_num );
				data->item_num = valid_item_num;
				break;
#endif 	/* INT64_SUPPORT	*/
			default:
				echo_warn1("未处理的 无符号整数 size = %i \n", rt_ptr->u.p.el_len);
				break;
			}
			break;



#ifdef FLOAT_DATA_SUPPORT
		case RT_FLOATING_POINT:
			strcpy(data->data_item[valid_item_num].comp_name, (const char*)attr_str);
			data->data_item[valid_item_num].size = (unsigned char)rt_ptr->u.p.el_len;
			data->data_item[valid_item_num].type = DATA_DOUBLE_TYPE;
			if (rt_ptr->u.p.el_len != sizeof(ST_FLOAT))
				data->data_item[valid_item_num].u.data_double = *((ST_DOUBLE*)datptr);
			else
				data->data_item[valid_item_num].u.data_double = *((ST_FLOAT*)datptr);
			//        SLOGCALWAYS1(" value=%.16g ",data->data_item[valid_item_num].u.data_double );
			valid_item_num++;
			//         SLOGCALWAYS1("valid_item_num = %i ",valid_item_num );
			data->item_num = valid_item_num;
			break;
#endif

		case RT_OCTET_STRING:
			myLocToTextOct((ST_UCHAR*)datptr, rt_ptr, tmpBuf);
			strcpy(data->data_item[valid_item_num].comp_name, (const char*)attr_str);
			data->data_item[valid_item_num].size = abs(rt_ptr->u.p.el_len);
			data->data_item[valid_item_num].type = DATA_STR_TYPE;
			strcpy(data->data_item[valid_item_num].u.data_str, tmpBuf);
			valid_item_num++;
			//SLOGCALWAYS2("valid_item_num = %i , value=%s",valid_item_num, tmpBuf );
			data->item_num = valid_item_num;
			break;

		case RT_VISIBLE_STRING: 	/* No conversion needed. Just add to working text*/
			strcpy(data->data_item[valid_item_num].comp_name, (const char*)attr_str);
			data->data_item[valid_item_num].size = abs(rt_ptr->u.p.el_len);
			data->data_item[valid_item_num].type = DATA_STR_TYPE;
			strcpy(data->data_item[valid_item_num].u.data_str, datptr);
			valid_item_num++;
			//         SLOGCALWAYS2("valid_item_num = %i , value=%s",valid_item_num, datptr );
			data->item_num = valid_item_num;
			break;

#ifdef TIME_DATA_SUPPORT
		case RT_GENERAL_TIME:
			strcpy(data->data_item[valid_item_num].comp_name, (const char*)attr_str);
			data->data_item[valid_item_num].size = abs(rt_ptr->u.p.el_len);
			data->data_item[valid_item_num].type = DATA_TIME_TYPE;
			asn1_convert_timet_to_btime6(*((time_t*)datptr),
				&(data->data_item[valid_item_num].u.data_bTime6));
			valid_item_num++;
			data->item_num = valid_item_num;
			break;
#endif

#ifdef BTOD_DATA_SUPPORT
		case RT_BINARY_TIME:
			switch (rt_ptr->u.p.el_len)	/* determine length		*/
			{
				MMS_BTIME6 btime6;
			case 4:
				asn1_convert_timet_to_btime6(time(NULL), &btime6);
				btime6.ms = *((ST_INT32*)datptr);
				strcpy(data->data_item[valid_item_num].comp_name, (const char*)attr_str);
				data->data_item[valid_item_num].size = 4;
				data->data_item[valid_item_num].type = DATA_TIME_TYPE;
				data->data_item[valid_item_num].u.data_bTime6 = btime6;
				valid_item_num++;
				data->item_num = valid_item_num;
				break;
			case 6:
				strcpy(data->data_item[valid_item_num].comp_name, (const char*)attr_str);
				data->data_item[valid_item_num].size = 6;
				data->data_item[valid_item_num].type = DATA_TIME_TYPE;
				data->data_item[valid_item_num].u.data_bTime6 = *((MMS_BTIME6*)datptr);
				valid_item_num++;
				data->item_num = valid_item_num;
				break;
			}
			break;
#endif

		case RT_UTC_TIME:
		{
			MMS_BTOD btod;
			//cout<<"((MMS_UTC_TIME *) datptr)->fraction = "<<((MMS_UTC_TIME *) datptr)->fraction;
			//cout<<"((MMS_UTC_TIME *)datptr)->qflags = "<<((MMS_UTC_TIME *)datptr)->qflags<<endl;
			my_asn1_convert_utc_to_btod((MMS_UTC_TIME*)datptr, &btod);
			strcpy(data->data_item[valid_item_num].comp_name, (const char*)attr_str);
			data->data_item[valid_item_num].size = 1;
			data->data_item[valid_item_num].type = DATA_TIME_TYPE;
			data->data_item[valid_item_num].u.data_bTime6.day = btod.day;
			data->data_item[valid_item_num].u.data_bTime6.ms = btod.ms;
			valid_item_num++;
			//         SLOGALWAYS3("valid_item_num = %i , UTC TIME seconds=%lu, fraction=%lu",
			//        	valid_item_num,  ((MMS_UTC_TIME *) datptr)->fraction,
			//        		((MMS_UTC_TIME *) datptr)->secs );
			data->item_num = valid_item_num;
		}
		break;

		case RT_UTF8_STRING:
			strcpy(data->data_item[valid_item_num].comp_name, (const char*)attr_str);
			data->data_item[valid_item_num].size = abs(rt_ptr->u.p.el_len);
			data->data_item[valid_item_num].type = DATA_STR_TYPE;
			strcpy(data->data_item[valid_item_num].u.data_str, datptr);
			valid_item_num++;
			//         SLOGCALWAYS2("valid_item_num = %i , value=%s",valid_item_num, datptr );
			data->item_num = valid_item_num;
			break;

		default: 			/* should not be any other tag	*/
			MLOG_ERR1("Invalid tag: %d", (int)rt_ptr->el_tag);
			uDataRet = SD_FAILURE;
			break;
		}

		assert(strlen(tmpBuf) < sizeof(tmpBuf));	/* Must not exceed buffer*/

		datptr += rt_ptr->el_size; 	/* Adjust data pointer		*/
		rt_ptr++;  			/* point to next rt element	*/
	}
}


