/************************************************************************/   
/* 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;   
//支持快速报告控制块信息获取而添加的变量 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);   
    }   
   
  //通过获取的数据集名在当前vmd中查找其控制结构体指针 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;//获取数据集元素的数目   
  ///////////////////////////////////////////////////////////////////////////////////   
  /* 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;   
   
  //生成数据集元素变量名列表 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;  
      }  
    }*/   
  //生成报告各元素类型 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 调用报告控制回调函数   
        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);   
  }   

 