You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1145 lines
48 KiB
C

1 year ago
/************************************************************************/
/* 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 "glbtypes.h"
#include "sysincs.h"
#include "mvl_acse.h"
#include "mvl_log.h"
#include "mvl_uca.h" /* only need OPTFLD_* defines */
#include "client.h"
/************************************************************************/
/* 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
/************************************************************************/
/* 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);
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);
/* 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);
/************************************************************************/
/* 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, MVL_VMD_CTRL *vmd_ctrl,
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 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;
//֧<>ֿ<EFBFBD><D6BF>ٱ<EFBFBD><D9B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ƿ<EFBFBD><C6BF><EFBFBD>Ϣ<EFBFBD><CFA2>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD><EFBFBD>ӵı<D3B5><C4B1><EFBFBD> liwei 2012.11.24
OBJECT_NAME nvl_oname;
MVL_NVLIST_CTRL *nvl;
OBJECT_NAME *dstobj;
int i;
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 (var_read (net_info, varName, DOM_SPEC, domName, rpt_typeids->vstring65, RptID, timeOut))
{
SLOGALWAYS2 ("Error reading RptID '%s' in domain '%s'", 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 (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
{
SLOGALWAYS1 ("RCB name '%s' invalid. Must contain 'BR' or 'RP'.", rcbName);
return (NULL);
}
/* Read "DatSet" from the server. */
strcpy (varName, rcbName);
strcat (varName, "$DatSet");
if (var_read (net_info, varName, DOM_SPEC, domName, rpt_typeids->vstring65, datSetName, timeOut))
{ /* can't read data set name */
SLOGALWAYS2 ("Error reading data set name ('%s' in domain '%s')", varName, domName);
return (NULL);
}
/* datSetName should now be set */
nvlDomName = strtok (datSetName, "/");/* extract domain name of NVL */
nvlName = strtok (NULL, ""); /* extract NVL name */
if (nvlName == NULL) /* "/" not found */
{
SLOGALWAYS1 ("datSetName '%s' invalid. Does not contain '/' character.", datSetName);
return (NULL);
}
//ͨ<><CDA8><EFBFBD><EFBFBD>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD><EFBFBD>ݼ<EFBFBD><DDBC><EFBFBD><EFBFBD>ڵ<EFBFBD>ǰvmd<6D>в<EFBFBD><D0B2><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ƽ<C6BD><E1B9B9>ָ<EFBFBD><D6B8> liwei 2012.11.24//////////////
nvl_oname.object_tag = DOM_SPEC;
nvl_oname.domain_id = domName;
nvl_oname.obj_name.item_id = nvlName;
nvl = mvl_vmd_find_nvl (vmd_ctrl, &nvl_oname, NULL);
if (nvl == NULL)
{
SLOGALWAYS1 ("datSetName '%s' no find.liwei", datSetName);
return (NULL);
}
numDsVar = nvl->num_of_entries;//<2F><>ȡ<EFBFBD><C8A1><EFBFBD>ݼ<EFBFBD>Ԫ<EFBFBD>ص<EFBFBD><D8B5><EFBFBD>Ŀ
///////////////////////////////////////////////////////////////////////////////////
/* 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)
{
SLOGALWAYS2 ("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, (unsigned int)extended_size);
SLOGALWAYS3 ("Client RCB info 0x%X created for '%s' in domain '%s'", rcb_info, rcbName, domName);
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;
//<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ݼ<EFBFBD>Ԫ<EFBFBD>ر<EFBFBD><D8B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD>б<EFBFBD> liwei 2012.11.24////////////////////////////
varNameArray = chk_malloc (numDsVar * sizeof (OBJECT_NAME *));
for (i = 0; i < numDsVar; i++)
{
extended_size = sizeof (OBJECT_NAME)
+ MAX_IDENT_LEN + 1
+ MAX_IDENT_LEN + 1;
dstobj =(OBJECT_NAME *) chk_malloc ((unsigned int)extended_size);
extended_ptr = (ST_CHAR *)(dstobj + 1);
dstobj->obj_name.vmd_spec = extended_ptr;
extended_ptr += (MAX_IDENT_LEN + 1);
dstobj->domain_id = extended_ptr;
strcpy(dstobj->domain_id, domName);
strcpy(dstobj->obj_name.vmd_spec, nvl->entries[i]->name);
dstobj->object_tag = DOM_SPEC;
varNameArray[i] = dstobj;
}
rcb_info->varNameArray = varNameArray;
/////////////////////////////////////////////////////////////////////////
/* 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" */
sprintf (InclusionTdl, "Bstring%d", rcb_info->numDsVar);
rcb_info->InclusionTypeid = mvl_type_id_create_from_tdl (NULL, InclusionTdl);
if (rcb_info->InclusionTypeid < 0)
{
SLOGALWAYS0 ("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)
{
SLOGALWAYS1 ("Error creating type for variable '%s'",
varNameArray[j]->obj_name.vmd_spec);
status = SD_FAILURE;
break;
}
}*/
//<2F><><EFBFBD>ɱ<EFBFBD><C9B1><EFBFBD><EFBFBD><EFBFBD>Ԫ<EFBFBD><D4AA><EFBFBD><EFBFBD><EFBFBD><EFBFBD> liwei 2012.11.24/////////////////////////////////////////////////
for (i=0; i<numDsVar; i++)
rcb_info->typeIdArr [i] = nvl->entries[i]->type_id;
//////////////////////////////////////////////////////////////////////////////////////
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);
//liwei 2012.07.13
if (rcb_info->rcb_create_type == 0)
{
for (j=0; j<rcb_info->numDsVar; j++)
{
if (rcb_info->typeIdArr [j] >= 0)
mvl_type_id_destroy (rcb_info->typeIdArr [j]);
}
}
chk_free (rcb_info);
SLOGALWAYS1 ("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)
{
SLOGALWAYS1 ("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 = 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)
SLOGALWAYS1 ("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". */
/************************************************************************/
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 0 /* Add something like this if other options needed. */
if (ret == SD_SUCCESS)
{
sprintf (varName, "%s$IntgPd", rcbName);
ret = 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. */
sprintf (varName, "%s$OptFlds", rcbName);
ret = var_write (netInfo, varName, DOM_SPEC, domName, rpt_typeids->bstr9, OptFlds, timeOut);
}
if (ret == SD_SUCCESS)
{
sprintf (varName, "%s$TrgOps", rcbName);
ret = var_write (netInfo, varName, DOM_SPEC, domName, rpt_typeids->bstr8, TrgOps, timeOut);
}
if (ret == SD_SUCCESS)
{
sprintf (varName, "%s$Trgs", rcbName);
ret = named_var_write (netInfo, varName, DOM_SPEC, domName, rpt_typeids->int16u, (ST_CHAR *) &Trgs, timeOut);
}
#endif
if (ret == SD_SUCCESS)
{
sprintf (varName, "%s$RptEna", rcbName);
ret = var_write (netInfo, varName, DOM_SPEC, domName, rpt_typeids->mmsbool, (ST_CHAR *) &RptEna, timeOut);
}
return (ret);
}
/************************************************************************/
/************************************************************************/
static ST_VOID log_var_data (MVL_VAR_ASSOC *var)
{
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 */
ST_CHAR text_buf [30000]; /* increase size if ms_local_to_text fails */
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
SLOGCALWAYS0 (" TYPE: unknown");
data_text = ms_local_to_text (var->data,
type_ctrl->rt,
type_ctrl->num_rt, text_buf, sizeof (text_buf));
if (data_text)
SLOGCALWAYS1 (" DATA: %s", data_text);
else
SLOGCALWAYS0 (" DATA: cannot be converted to text");
}
else
SLOGCALWAYS0 (" 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 (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,
RPT_INFO *rpt_info)
{
ST_INT va_num=0;
ST_INT j;
rpt_info->RptID = (char *) info_va[va_num]->data;
va_num++;
rpt_info->OptFlds = info_va[va_num]->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))
{
rpt_info->SqNum = (ST_UINT16 *) info_va[va_num]->data;
va_num++;
}
else
rpt_info->SqNum = NULL;
if (BSTR_BIT_GET(OptFldsData, OPTFLD_BITNUM_TIMESTAMP))
{
rpt_info->TimeOfEntry = (MMS_BTIME6 *) info_va[va_num]->data;
va_num++;
}
else
rpt_info->TimeOfEntry = NULL;
if (BSTR_BIT_GET(OptFldsData, OPTFLD_BITNUM_DATSETNAME))
{
rpt_info->DatSetNa = (char *) info_va[va_num]->data;
va_num++;
}
else
rpt_info->DatSetNa = NULL;
if (BSTR_BIT_GET (OptFldsData, OPTFLD_BITNUM_BUFOVFL))
{
rpt_info->BufOvfl = (ST_BOOLEAN *) info_va[va_num]->data;
va_num++;
}
else
rpt_info->BufOvfl = NULL;
if (BSTR_BIT_GET(OptFldsData, OPTFLD_BITNUM_ENTRYID))
{
rpt_info->EntryID = (ST_UINT8 *) info_va[va_num]->data;
va_num++;
}
else
rpt_info->EntryID = NULL;
if (BSTR_BIT_GET(OptFldsData, OPTFLD_BITNUM_CONFREV))
{
rpt_info->ConfRev = (ST_UINT32 *) info_va[va_num]->data;
va_num++;
}
else
rpt_info->ConfRev = NULL;
if (BSTR_BIT_GET (OptFldsData,OPTFLD_BITNUM_SUBSEQNUM))
{
rpt_info->SubSeqNum = (ST_UINT16 *) info_va[va_num]->data;
va_num++;
rpt_info->MoreSegmentsFollow = (ST_BOOLEAN *) info_va[va_num]->data;
va_num++;
}
else
{
rpt_info->SubSeqNum = NULL;
rpt_info->MoreSegmentsFollow = NULL;
}
rpt_info->Inclusion = (ST_UINT8 *) info_va[va_num]->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))
{
rpt_info->dataRefName[j] = (char *) info_va[va_num]->data;
va_num++;
}
else
rpt_info->dataRefName[j] = NULL;
}
}
/* 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))
{
rpt_info->Value[j] = info_va[va_num]->data;
va_num++;
}
else
rpt_info->Value[j] = NULL;
}
/* 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))
{
rpt_info->Reason[j] = (MMS_BVSTRING *)info_va[va_num]->data;
va_num++;
}
else
rpt_info->Reason[j] = NULL;
}
}
rpt_info->num_data = rcb_info->numDsVar;
assert (va_num == va_total); /* Did we count right? */
}
/************************************************************************/
/* 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 ((unsigned int)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);
}
/************************************************************************/
/* 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 (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;
RPT_INFO rpt_info;
CLIENT_CTRL *client_ctrl;//
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)
{
SLOGALWAYS0 ("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 = 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)
{
SLOGALWAYS0 ("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)
{
SLOGALWAYS1 ("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;
info_va[va_num++] = rcb_info->rcb_var.OptFlds;
/* 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 */
/* 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 (BSTR_BIT_GET(OptFldsData, OPTFLD_BITNUM_TIMESTAMP))
info_va[va_num++] = rcb_info->rcb_var.TimeOfEntry;
if (BSTR_BIT_GET(OptFldsData, OPTFLD_BITNUM_DATSETNAME))
info_va[va_num++] = rcb_info->rcb_var.DatSetNa;
/* 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 (BSTR_BIT_GET(OptFldsData, OPTFLD_BITNUM_ENTRYID))
info_va[va_num++] = rcb_info->rcb_var.EntryID;
if (BSTR_BIT_GET(OptFldsData, OPTFLD_BITNUM_CONFREV))
info_va[va_num++] = rcb_info->rcb_var.ConfRev;
if (BSTR_BIT_GET (OptFldsData,OPTFLD_BITNUM_SUBSEQNUM))
{
info_va[va_num++] = rcb_info->rcb_var.SubSeqNum;
info_va[va_num++] = rcb_info->rcb_var.MoreSegmentsFollow;
}
info_va[va_num++] = rcb_info->rcb_var.Inclusion;
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 */
/* 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];
}
}
/* 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 "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)
SLOGALWAYS2 ("Num of var received in RPT (%d) does not match expected (%d). Possibly incorrect OptFlds or inclusion bitstring", va_total, va_num);
else
{
/* Perform 3rd decode (everything). */
rpt_info.dataRefName = (ST_CHAR **) chk_calloc(rcb_info->numDsVar, sizeof(ST_CHAR *));
rpt_info.Value = (void **)chk_calloc(rcb_info->numDsVar, sizeof(void *));
rpt_info.Reason = (MMS_BVSTRING **)chk_calloc(rcb_info->numDsVar, sizeof(MMS_BVSTRING *));
mvl_info_data_to_local (event, va_num, info_va);
u_iec_rpt_ind_data (info_va, OptFldsData, InclusionData, rcb_info, va_total, &rpt_info);
//liwei <20><><EFBFBD>ñ<EFBFBD><C3B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ƻص<C6BB><D8B5><EFBFBD><EFBFBD><EFBFBD>
client_ctrl = FindConnectServer(event->net_info);
if (client_ctrl != NULL)
{
if (client_ctrl->rpt_callback_fun != NULL)
client_ctrl->rpt_callback_fun(client_ctrl->sever_name, &rpt_info);
}
chk_free(rpt_info.Value);
chk_free(rpt_info.Reason);
chk_free(rpt_info.dataRefName);
}
CLEANUP:
chk_free (info_va);
return (retcode);
}