/************************************************************************/
/* SISCO SOFTWARE MODULE HEADER *****************************************/
/************************************************************************/
/*	(c) Copyright Systems Integration Specialists Company, Inc.,	*/
/*	2006-2006 All Rights Reserved					*/
/*									*/
/* MODULE NAME : mvl61850_rpt.c						*/
/* PRODUCT(S)  : MMSEASE-LITE						*/
/*									*/
/* MODULE DESCRIPTION :							*/
/*	Code to support IEC 61850 Reporting as a Server.		*/
/*									*/
/* BUFFERING OF REPORTS:						*/
/* For Buffered Report Controls, buffering is automatic (no user	*/
/* intervention). For each BRCB, all reports are stored in a linked	*/
/* list "rpt_list". Also, "rpt_list_next" points to the next report to	*/
/* send. If all reports have been sent, rpt_list_next==NULL.		*/
/*									*/
/* The maximum number of bytes to store in the buffer is a		*/
/* user-configurable parameter. When the maximum number of bytes is	*/
/* exceeded, the oldest report in the buffer is discarded.		*/
/*									*/
/* The standard SISCO linked list functions are used for buffering.	*/
/*									*/
/* GLOBAL FUNCTIONS DEFINED IN THIS MODULE :				*/
/*	mvl61850_brcb_rpt_save						*/
/*	mvl61850_brcb_rpt_send						*/
/*	mvl61850_brcb_rpt_lists_clean					*/
/*	mvl61850_brcb_client_service					*/
/*	mvl61850_urcb_client_service					*/
/*									*/
/* MODIFICATION LOG :							*/
/*  Date     Who   Rev     Comments					*/
/* --------  ---  ------   -------------------------------------------  */
/* 06/30/08  JRB     10    For Ed2, save lastSentTimeOfEntry for BRCB.	*/
/*			   Resync to oldest if all 0 written to EntryID.*/
/* 06/04/08  JRB     09    Start buffering on power-up if DatSet attr	*/
/*			   references existing DataSet (for 61850-7-2 Ed2)*/
/* 05/15/08  JRB     08    Add mvl61850_integrity_timeout for 61850 only*/
/*			   (does NOT scan data, just sends current data)*/
/* 05/06/08  JRB     07    Fix memory leak after "PDU size" error.	*/
/* 02/29/08  JRB     06    Create correct AA_SPEC or VMD_SPEC DatSet.	*/
/*			   Reset cur_va_index, SubSeqNum when all_seg_done*/
/*			   so retransmission starts with first va.	*/
/*			   If dsNvl==NULL, do not service RCB.		*/
/* 02/13/07  JRB     05    mvl61850_get_rcb: del unused net_info arg.	*/
/*			   Use scan_rate to control scan timing.	*/
/*			   Save TimeofEntry in "basrcb".		*/
/* 11/30/06  JRB     04    Fix *_brcb_entryid_init so user can call it.	*/
/* 10/10/06  JRB     03    Add mvl61850_rpt_ctrl_destroy_all.		*/
/* 08/18/06  JRB     02    Disable debug printing. Clean up comments.	*/
/* 08/09/06  JRB     01    New. Moved most 61850 Rpt code to here	*/
/*			   with following changes:			*/
/*			   New RPT functions just for 61850 (not UCA):	*/
/*			    mvl61850_create_rpt_ctrl, mvl61850_free_rpt_ctrl,*/
/*			    mvl61850_rpt_service.			*/
/*			   Chg BRCB code to save "raw" data in buffer	*/
/*			    and encode rpt only when sending.		*/
/*			   Chg to one rpt list with ptr to next to send.*/
/*			   ..urcb_client_service: First chk if connected.*/
/*			   Put start time in first 4 bytes of EntryID.	*/
/*			   When GI rpt buffered, discard old GI rpt.	*/
/************************************************************************/
#include "glbtypes.h"
#include "sysincs.h"
#include "mvl_acse.h"	/* need mvl_cfg_info	*/
#include "mvl_uca.h"
#include "mvl_log.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

/* DEBUG: IEC 61850 is not clear about exactly how to handle "BufOvfl",
 * so the code in this module implements the following:
 * 1. If client writes EntryID & the value can't be found in the buffer,
 *    BufOvfl is set, an error is returned, and the oldest buffer entry will
 *    be sent next (see mvl61850_brcb_rpt_set_entryid).
 * 2. If entry found but discarded before RptEna,
 *    BufOvfl is set, and the oldest buffer entry will
 *    be sent next (see buflist_discard_old).
 * 3. If entries lost while connected, set BufOvfl (see buflist_discard_old).
 * 4. After one report sent with BufOvfl, clear it
 *    (see mvl61850_brcb_client_service).
 */

/************************************************************************/
/*			print macros for debugging			*/
/************************************************************************/
#if 0	/* DEBUG: enable to see queue activity.	*/
/* Use function here (too complex for macro).	*/
ST_VOID print_queues(MVL61850_BRCB_CTRL *brcbCtrl)
  {
  BUFLIST_ENTRY *entry;
  printf ("REPORT BUFFER ('.'=rpt, '+'=next_to_send)");
  for (entry = brcbCtrl->rpt_list;
      entry != NULL;
      entry = (BUFLIST_ENTRY *) list_get_next (brcbCtrl->rpt_list, entry))
   {
   if (entry == brcbCtrl->rpt_list_next)
     putchar('+');
   else
     putchar('.');
   }
  putchar('\n');
  }
#define print_msg0(fmt)		printf (fmt)
#define print_msg1(fmt,a)	printf (fmt,a)
#else
#define print_queues(brcbCtrl)
#define print_msg0(fmt)
#define print_msg1(fmt,a)
#endif

/************************************************************************/
/* 			mvl61850_mk_rptid				*/
/* Construct RptID from info in rptCtrl.				*/
/* RETURNS:	SD_SUCCESS or SD_FAILURE				*/
/*		Also on error, put a NULL string in RptID & log error.	*/
/************************************************************************/
ST_RET mvl61850_mk_rptid (MVLU_RPT_CTRL *rptCtrl, ST_CHAR *RptID, size_t max_len)
  {
ST_RET retcode;

  /* If domname + '/' + basrcb_name will fit in RptID, then write it.	*/
  if (strlen (rptCtrl->dsNvl->nvl_scope.dom->name) + strlen (rptCtrl->basrcb_name) + 1 <= max_len)
    {
    sprintf (RptID, "%s/%s", rptCtrl->dsNvl->nvl_scope.dom->name,
             rptCtrl->basrcb_name);
    retcode = SD_SUCCESS;
    }
  else
    {
    MVL_LOG_ERR0 ("Cannot construct RptID (too long)");	/* should NEVER happen*/
    RptID [0] = '\0';		/* return empty RptID	*/
    retcode = SD_FAILURE;
    }
  return (retcode);
  }

/************************************************************************/
/*			mvl61850_mk_dataref				*/
/************************************************************************/
ST_VOID mvl61850_mk_dataref (MVL_VAR_ASSOC *va, MVL_SCOPE *va_scope,
	ST_CHAR *databuf, ST_INT maxlen)
  {
  if (va_scope->scope == DOM_SPEC)
    {
    strcpy (databuf, va_scope->dom->name);
    strcat (databuf, "/");
    }
  else if (va_scope->scope == VMD_SPEC)
    strcpy (databuf, "/");
  else			/* must be AA_SPEC	*/
    strcpy (databuf, "@");
  strncat (databuf, va->name, maxlen - strlen (databuf));
  }

/************************************************************************/
/*			mvl61850_rcb_cleanup				*/
/* Cleanup RPT info to prepare for next report.				*/
/************************************************************************/
ST_VOID mvl61850_rcb_cleanup (MVLU_RPT_CLIENT *rptClient)
  {
MVL_NVLIST_CTRL *dsNvl;
ST_INT incSize;		/* num bytes for inclusion bitstring	*/
  dsNvl = rptClient->rpt_ctrl->dsNvl;
  incSize = BSTR_NUMBITS_TO_NUMBYTES(dsNvl->num_of_entries);
  /* Reset reasons, etc. to prepare for new report triggers.	*/
  memset (rptClient->reasons_data, 0, dsNvl->num_of_entries);
  memset (rptClient->changed_flags, 0, incSize);
  rptClient->numTrgs = 0;
  }

/************************************************************************/
/*			mvl61850_brcb_rpt_set_entryid			*/
/************************************************************************/
ST_RET mvl61850_brcb_rpt_set_entryid (MVLU_RPT_CLIENT *rptClient, ST_UCHAR *EntryID)
  {
BUFLIST_ENTRY *entry;
BUFLIST_ENTRY *foundEntry = NULL;	/* matching entry, assume NULL (not found)*/
MVL61850_BRCB_CTRL *brcbCtrl;
ST_RET retcode = SD_FAILURE;	/* assume failure	*/
ST_UCHAR all_zero[8]={0,0,0,0,0,0,0,0};

  /* Assume this is a BRCB. Should never get here otherwise.	*/
  assert (rptClient->rpt_ctrl->rcb_type == RCB_TYPE_IEC_BRCB);
  brcbCtrl = &rptClient->rpt_ctrl->brcbCtrl;

  /* Is client writing all 0?	*/
  if (memcmp (all_zero, EntryID, 8) == 0)
    {	/* special case. Just resync to oldest rpt in buffer	*/
    brcbCtrl->rpt_list_next = brcbCtrl->rpt_list;	/* oldest	*/
    return (SD_SUCCESS);	/* this can never fail*/
    }

  /* Search "sent" list for this ID.	*/
  /* If "rpt_list_next != NULL" start search there. 			*/
  /* If "rpt_list_next == NULL" start search at end of list.		*/
  if (brcbCtrl->rpt_list_next)
    entry = brcbCtrl->rpt_list_next;
  else
    entry = (BUFLIST_ENTRY *) list_find_last ((DBL_LNK *) brcbCtrl->rpt_list);
  for ( ;
       entry != NULL;
       entry = (BUFLIST_ENTRY *) list_find_prev ((DBL_LNK *) brcbCtrl->rpt_list, (DBL_LNK *) entry))
    {
		
    if (memcmp (entry->EntryID, EntryID, 8) == 0)
      {
      foundEntry = entry;
      break;
      }
    }
  /* If matching entry found, just point to next one.	*/
  if (foundEntry)
    {
    brcbCtrl->rpt_list_next = list_get_next (brcbCtrl->rpt_list, entry);
    retcode = SD_SUCCESS;
    }
  else
    {	/* not found, point to "oldest" rpt	*/
		if((entry = brcbCtrl->rpt_list_next))
			printf("next_entry =%02x%02x%02x%02x%02x%02x%02x%02x\n\r",entry->EntryID[0],entry->EntryID[1],entry->EntryID[2],
			entry->EntryID[3],entry->EntryID[4],entry->EntryID[5],entry->EntryID[6],entry->EntryID[7]);
		if((entry = (BUFLIST_ENTRY *) list_find_last ((DBL_LNK *) brcbCtrl->rpt_list)))
			printf("last_entry=%02x%02x%02x%02x%02x%02x%02x%02x\n\r",entry->EntryID[0],entry->EntryID[1],entry->EntryID[2],
			entry->EntryID[3],entry->EntryID[4],entry->EntryID[5],entry->EntryID[6],entry->EntryID[7]);
		printf("find_entry=%02x%02x%02x%02x%02x%02x%02x%02x\n\r",EntryID[0],EntryID[1],EntryID[2],EntryID[3],
			EntryID[4],EntryID[5],EntryID[6],EntryID[7]);

		brcbCtrl->rpt_list_next = brcbCtrl->rpt_list;	/* oldest	            */
		brcbCtrl->BufOvfl = SD_TRUE;		            /* overflow occurred	*/
    }

  return (retcode);
  }

/************************************************************************/
/*			fill_asn1_len_array				*/
/* Compute the ASN.1 encoded len for each var in a rpt dataset, and	*/
/* fill in "bufEntry->asn1_len_array".					*/
/************************************************************************/
static ST_RET fill_asn1_len_array (
	BUFLIST_ENTRY *bufEntry,
	MVL_NVLIST_CTRL *dsNvl)	/* rpt dataset				*/
  {
ST_UCHAR *tmp_asn1_buf;	/* temporary ASN.1 buffer (allocated)	*/
ST_INT tmp_asn1_buf_size = mvl_cfg_info->max_msg_size;	/* reasonable size*/
ST_INT j;
ST_RET retCode=SD_SUCCESS;	/* assume success	*/

  tmp_asn1_buf = chk_malloc (tmp_asn1_buf_size);
  for (j = 0; j < dsNvl->num_of_entries; ++j)
    {
    if (bufEntry->var_data[j].reason_for_incl != 0)
      {		/* this var should be included in rpt	*/
      ST_UCHAR *asn1_ptr;
      MVL_TYPE_CTRL *type_ctrl = dsNvl->entries[j]->type_ctrl;

      retCode = ms_local_to_asn1_2 (type_ctrl->rt, type_ctrl->num_rt,
                bufEntry->var_data[j].data_ptr,	/* CRITICAL: use data from buffer entry*/
                tmp_asn1_buf,
                tmp_asn1_buf_size,
                &asn1_ptr,	/* function sets asn1_ptr (not needed here)*/
                &bufEntry->asn1_len_array[j]);	/* function sets len at this ptr*/
      if (retCode)
        {
	/* Should NEVER fail, but if so, don't continue with other vars.*/
        break;
        }
      }
    }	/* end loop	*/
  chk_free (tmp_asn1_buf);
  return (retCode);
  }

/************************************************************************/
/*			buflist_entry_create				*/
/* Allocate and initialize BUFLIST_ENTRY and add it to list.		*/
/* Also update brcbCtrl->cur_bufsize & brcbCtrl->rpt_count.		*/
/************************************************************************/
BUFLIST_ENTRY *buflist_entry_create (MVLU_RPT_CLIENT *rptClient)
  {
BUFLIST_ENTRY *bufEntry;
MVL_NVLIST_CTRL *dsNvl = rptClient->rpt_ctrl->dsNvl;
ST_INT j;
int    num;
MVL61850_BRCB_CTRL *brcbCtrl = &rptClient->rpt_ctrl->brcbCtrl;

  bufEntry = (BUFLIST_ENTRY *) chk_calloc (1, sizeof (BUFLIST_ENTRY));
  bufEntry->num_var = dsNvl->num_of_entries;
  /* Use calloc so start out with all data_ptr=NULL & all reason_for_incl=0.*/
  bufEntry->var_data = (VAR_DATA *) chk_calloc (bufEntry->num_var,
                    sizeof (VAR_DATA));
  bufEntry->asn1_len_array = chk_calloc (dsNvl->num_of_entries, sizeof(ST_INT));

  for (j = 0; j < bufEntry->num_var; j++)
    {
    /* Check "reason for inclusion".	*/
    if (rptClient->reasons_data[j] != 0)
      {				/* this var must be in the report	*/
      ST_INT data_len;
      MVL_VAR_ASSOC *var_assoc = dsNvl->entries[j];
      MVL_TYPE_CTRL *type_ctrl = dsNvl->entries[j]->type_ctrl;
      VAR_DATA *var_data = &bufEntry->var_data[j];

      /* Find data_len	*/
      data_len = type_ctrl->data_size;

      brcbCtrl->cur_bufsize += data_len;	/* update buffer size*/
      var_data->data_len = data_len;
      var_data->data_ptr = chk_malloc (data_len);
      memcpy (var_data->data_ptr, var_assoc->data, data_len);
      /* Save "reason for inclusion".	*/
      var_data->reason_for_incl = rptClient->reasons_data[j];
      }
    }

  /* Fill in other entry data.	*/
  /* Point to last 4 bytes of Ostring, cast to INT32 & increment.*/
#ifdef WIN32
  num = ((int)rptClient->basrcb.EntryID[7]<<0 )+
		((int)rptClient->basrcb.EntryID[6]<<8 )+
		((int)rptClient->basrcb.EntryID[5]<<16)+
		((int)rptClient->basrcb.EntryID[4]<<24);
  num++;
  rptClient->basrcb.EntryID[7] = (ST_UCHAR)(num>>0);
  rptClient->basrcb.EntryID[6] = (ST_UCHAR)(num>>8);
  rptClient->basrcb.EntryID[5] = (ST_UCHAR)(num>>16);
  rptClient->basrcb.EntryID[4] = (ST_UCHAR)(num>>24);
#else
  ++(*(ST_INT32 *)(&rptClient->basrcb.EntryID[4]));
#endif  
  /* Then copy to bufEntry.	*/
  memcpy (bufEntry->EntryID, rptClient->basrcb.EntryID, 8);
  /* Call user function to get report time.	*/
  u_mvlu_rpt_time_get (&bufEntry->TimeOfEntry);
  /* Copy report time to RCB.	*/
  memcpy (&rptClient->basrcb.TimeofEntry, &bufEntry->TimeOfEntry, sizeof (MMS_BTIME6));

  /* Fill in "bufEntry->asn1_len_array", needed later by "chk_seg_needed".	*/
  if (fill_asn1_len_array (bufEntry, dsNvl)!=SD_SUCCESS)
    {
    /* NOTE: This should NEVER fail, but if it does, just log it.	*/
    /*       Real encode will almost certainly fail later.		*/
    MVL_LOG_ERR0 ("Cannot calculate ASN.1 length for Buffered Report.");
    }

  /* Add entry to list.	*/
  list_add_last (&brcbCtrl->rpt_list, bufEntry);
  brcbCtrl->rpt_count++;	/* Update list count	*/

  /* If next ptr is NULL (all old rpts sent), point to this new entry.*/
  if (brcbCtrl->rpt_list_next == NULL)
    brcbCtrl->rpt_list_next = bufEntry;

  return (bufEntry);
  }

/************************************************************************/
/*			buflist_entry_destroy				*/
/* Remove BUFLIST_ENTRY from list and free it.				*/
/* Also update brcbCtrl->cur_bufsize & brcbCtrl->rpt_count.		*/
/************************************************************************/
ST_VOID buflist_entry_destroy (MVL61850_BRCB_CTRL *brcbCtrl, BUFLIST_ENTRY *entry)
  {
ST_INT j;
  if (entry == brcbCtrl->rpt_list_next)
    {
    /* Deleting next rpt to send.			*/
    /* CRITICAL: must fix up next to send.		*/
    brcbCtrl->rpt_list_next = list_get_next (brcbCtrl->rpt_list, entry);
    }

  list_unlink (&brcbCtrl->rpt_list, entry);	/* remove rpt from list	*/
  brcbCtrl->rpt_count--;			/* update list count	*/
  /* Free all in reverse order.	*/
  for (j = 0; j < entry->num_var; j++)
    {
    VAR_DATA *var_data = &entry->var_data[j];
    if (var_data->data_ptr)
      {
      chk_free (var_data->data_ptr);
      brcbCtrl->cur_bufsize -= var_data->data_len;	/* update buffer size*/
      }
    }
  chk_free (entry->asn1_len_array);
  chk_free (entry->var_data);
  chk_free (entry);
  }

/************************************************************************/
/*			buflist_discard_old_gi				*/
/* Search for old GI report in buffer, and delete it.			*/
/************************************************************************/
ST_VOID buflist_discard_old_gi (MVL61850_BRCB_CTRL *brcbCtrl)
  {
BUFLIST_ENTRY *entry;
  for (entry = brcbCtrl->rpt_list;
       entry != NULL;
       entry = list_get_next (brcbCtrl->rpt_list, entry))
    {
    /* For GI rpt, "reason_for_incl" for all vars is MVLU_TRGOPS_GI,	*/
    /* so just check reason for first var.				*/
    if (entry->var_data[0].reason_for_incl == MVLU_TRGOPS_GI)
      {
      /* Remove entry from list and free it.	*/
      buflist_entry_destroy (brcbCtrl, entry);
      break;	/* never more than one GI, so stop looping now	*/
      }
    }
  }

/************************************************************************/
/*			buflist_discard_old				*/
/* If memory usage exceeds buffer size, remove oldest reports from list	*/
/* until memory usage is reduced.					*/
/************************************************************************/
ST_VOID buflist_discard_old (MVL61850_BRCB_CTRL *brcbCtrl)
  {
BUFLIST_ENTRY *entry;
  while (brcbCtrl->cur_bufsize > brcbCtrl->brcb_bufsize)
    {
    entry = brcbCtrl->rpt_list;	/* find first on list	*/
    if (entry != NULL)
      {
      if (entry == brcbCtrl->rpt_list_next)
        {
        /* Deleting next rpt to send (i.e. rpts are being lost).	*/
        /* Set BufOvfl flag.	*/
        brcbCtrl->BufOvfl = SD_TRUE;
        brcbCtrl->rpts_lost_count++;	/* keep a count of lost reports	*/
        }
      /* Remove entry from list and free it.	*/
      buflist_entry_destroy (brcbCtrl, entry);
      }
    else
      {		/* This error should never happen. Buffer is much too small.*/
      MVL_LOG_ERR0 ("Report buffer size too small to store any reports.");
      }
    }	/* end loop	*/
  }

/************************************************************************/
/*			mvl61850_brcb_rpt_lists_clean			*/
/* Delete and destroy all entries on the buffered report linked list	*/
/* for this BRCB.							*/
/************************************************************************/
ST_VOID mvl61850_brcb_rpt_lists_clean (MVL61850_BRCB_CTRL *brcbCtrl)
  {
BUFLIST_ENTRY *entry;
  /* Loop removing first entry from list.	*/
  while ((entry = brcbCtrl->rpt_list) != NULL)
    buflist_entry_destroy (brcbCtrl, entry);	/* removes it from list*/

  assert (brcbCtrl->rpt_list_next == NULL);	/* if list empty, this must be NULL*/

  if (brcbCtrl->rpt_count != 0 || brcbCtrl->cur_bufsize != 0)
    {
    MVL_LOG_ERR2 ("ERROR removing buffered reports from list (count=%d, bufsize=%d). Possible memory leak.",
                brcbCtrl->rpt_count, brcbCtrl->cur_bufsize);
    brcbCtrl->rpt_count = 0;		/* reset count		*/
    brcbCtrl->cur_bufsize = 0;		/* reset buffer size	*/
    }
  brcbCtrl->BufOvfl = SD_FALSE;		/* Clear BufOvfl flag	*/
  brcbCtrl->rpt_list_next = NULL;	/* CRITICAL: must not point to deleted entry*/
  }

/************************************************************************/
/*			mvl61850_brcb_rpt_save				*/
/* For BRCB, just buffer the raw data in a linked list to be encoded	*/
/* and sent later (see mvl61850_brcb_rpt_send).				*/
/************************************************************************/
ST_RET mvl61850_brcb_rpt_save (MVLU_RPT_CLIENT *rptClient)
  {
  /* If this is GI rpt, discard previous GI rpt from buffer before	*/
  /* saving new one in buffer.						*/
  if (rptClient->reasons_data[0] == MVLU_TRGOPS_GI)
    buflist_discard_old_gi (&rptClient->rpt_ctrl->brcbCtrl);

  buflist_entry_create (rptClient);	/* add rpt to buffer		*/

  /* If buffer is full, discard oldest reports until not full.	*/
  buflist_discard_old (&rptClient->rpt_ctrl->brcbCtrl);
  mvl61850_rcb_cleanup (rptClient);	/* prepare for next RPT.	*/
  return (SD_SUCCESS);
  }

/************************************************************************/
/*			mvl61850_brcb_rpt_send				*/
/* Encode and send a Buffered Report (possibly in multiple segments).	*/
/* NOTE: This is similar to mvl61850_urcb_rpt_send. The main difference	*/
/*       is that it gets most data from the BUFLIST_ENTRY struct.	*/
/* NOTE: DOES NOT use rptClient->reasons_data, rptClient->changed_flags,*/
/*       rptClient->numTrgs. Use "reason_for_incl" from BUFLIST_ENTRY	*/
/*       to construct inclusion bitstring.				*/
/************************************************************************/
ST_RET mvl61850_brcb_rpt_send (MVLU_RPT_CTRL *rptCtrl,
	MVLU_RPT_CLIENT *rptClient,
	BUFLIST_ENTRY *bufEntry,
	ST_BOOLEAN *all_seg_done)
  {
MVL_NVLIST_CTRL *dsNvl;
MVL_NVLIST_CTRL *rptNvl;
MVL_VAR_ASSOC *va;
ST_INT j;		/* loop counter	*/
ST_RET retCode;
ST_INT sendIndex;
ST_INT sendIndexSave;	/* save index before data, compare after data	*/
ST_UINT8 *optFlds;
MVLU_BASRCB *basrcb;
ST_INT incSize;		/* num bytes for inclusion bitstring	*/
MVL_VAR_ASSOC *tmp_va_arr;	/* alloc array of structs	*/
ST_INT tmp_va_arr_size;		/* num of entries in tmp_va_arr	*/
MVL_VAR_ASSOC *tmp_va;		/* current entry in tmp_va_arr	*/
/* For Segmented reports, all segments are generated by this function,
 * so these parameters can be local variables.
 */
ST_INT next_va_index;		/* index into dsNvl va array	*/
ST_BOOLEAN segNeeded;		/* If TRUE, segmenting needed	*/
ST_BOOLEAN MoreSegmentsFollow;
ST_CHAR *tmp_dataref_buf;
ST_CHAR tmpRptID [MVL61850_MAX_RPTID_LEN+1];


  retCode = SD_SUCCESS;		/* assume success. Change if error detected.*/
  *all_seg_done = SD_FALSE;	/* initialize flag			*/

  basrcb = &rptClient->basrcb;

  optFlds = basrcb->OptFlds.data_1;
  dsNvl = rptCtrl->dsNvl;
  rptNvl = &rptCtrl->rptNvl;

  incSize = BSTR_NUMBITS_TO_NUMBYTES(dsNvl->num_of_entries);

  MVLU_LOG_FLOW1 ("Building IEC-61850 BRCB Report, MVL_NET_INFO %08lx", rptClient->netInfo);

  /* Need tmp va's for options, array of data_refs, array of dataset vars,*/
  /* array of reason_for_incl.						*/
  tmp_va_arr_size = MVLU_MAX_RPT_OPTS + (dsNvl->num_of_entries * 3);
  tmp_va_arr = M_CALLOC (MSMEM_GEN, sizeof (MVL_VAR_ASSOC), tmp_va_arr_size);

  /* Need tmp buffer for datarefs. One buffer for all.		*/
  /* Allow max len plus NULL for each dataref.			*/
  tmp_dataref_buf = M_CALLOC (MSMEM_GEN, (MVL61850_MAX_OBJREF_LEN+1), dsNvl->num_of_entries);

  assert (rptCtrl->dsNvl->num_of_entries);	/* must be >0 entries	*/

  /* MAIN LOOP	*/
  /* Start with "bufEntry->cur_va_index" and "bufEntry->SubSeqNum".*/
  /* Usually they are both 0, but if segmenting occurs and buffers were	*/
  /* not available to send all segments, they indicate what is next.	*/
  for (next_va_index = bufEntry->cur_va_index;
       next_va_index < rptCtrl->dsNvl->num_of_entries;
       bufEntry->cur_va_index = next_va_index, bufEntry->SubSeqNum++)
    {	/* BEGIN MAIN LOOP	*/
    if (retCode)
      break;	/* something failed in this loop or earlier loop, so stop now.*/

    /* If buffers not available, break out of loop.			*/
    /* Use cur_va_index, SubSeqNum in bufEntry as starting point	*/
    /* next time this function is called.				*/
    if (mvl_ureq_bufs_avail (rptClient->netInfo) <= 0)	/* stack bufs avail to send rpt	*/
      break;

    tmp_va = tmp_va_arr;	/* start loop pointing to first entry	*/
    sendIndex = 0;		/* start with first rptNvl->entries.	*/

    /* Prepare a Report NVL to be sent, based on the			*/
    /* options, data, and inclusion bitstring in the rptCtrl.		*/

    /* CRITICAL: asn1_len_array must be filled in before call to chk_seg_needed.*/
    segNeeded = chk_seg_needed (rptClient, bufEntry->asn1_len_array, bufEntry->cur_va_index, &next_va_index);

    if (next_va_index - bufEntry->cur_va_index == 0)
      {
      MVL_LOG_ERR0 ("MMS PDU size too small to fit ANY variables in IEC-61850 Report. Report not sent.");
      retCode = SD_FAILURE;
      break;	/* break out of loop so normal cleanup occurs.	*/
      }

    /* We will create a NVL to send, using the dsNvl as a model ...	*/

    /* RptID and OptFlds are always the first 2 entries in the Rpt NVL.	*/

    /* Get RptID from RCB (OK because changing it causes PurgeBuf).	*/
    tmp_va->type_id = rptCtrl->rpt_typeids.vstring65;
    if (basrcb->RptID [0] == '\0')
      {		/* RptID not set in RCB, so construct it.	*/
      mvl61850_mk_rptid (rptCtrl, tmpRptID, MVL61850_MAX_RPTID_LEN);
      tmp_va->data = tmpRptID;
      }
    else
      tmp_va->data = basrcb->RptID;
    MVLU_LOG_CFLOW1 ("  RptID='%s", tmp_va->data);	/* log before tmp_va++*/
    rptNvl->entries[sendIndex++] = tmp_va++;	/* set entry & point to next*/

    /* Get OptFlds from RCB (OK because changing it causes PurgeBuf).	*/
    tmp_va->type_id = rptCtrl->rpt_typeids.bvstring10;
    tmp_va->data = &basrcb->OptFlds;
    rptNvl->entries[sendIndex++] = tmp_va++;	/* set entry & point to next*/
    MVLU_LOG_CFLOW2 ("  OptFld = 0x%02x 0x%02x", basrcb->OptFlds.data_1[0],
                     basrcb->OptFlds.data_1[1]);	/* 10 bit bstr (2 bytes)	*/

    /* Add optional RCB vars to NVL, depending on OptFlds.	*/

    /* Get SqNum from RCB.	*/
    if (BSTR_BIT_GET(optFlds, OPTFLD_BITNUM_SQNUM))
      {
      assert (rptCtrl->rcb_type == RCB_TYPE_IEC_BRCB);
      /* SqNum in BRCB is INT16U	*/
      tmp_va->type_id = rptCtrl->rpt_typeids.int16u;
      tmp_va->data = &basrcb->SqNumInt16u;
      rptNvl->entries[sendIndex++] = tmp_va++;	/* set entry & point to next*/
      MVLU_LOG_CFLOW1 ("    SqNum : %u", (unsigned) basrcb->SqNumInt16u);
      }

    /* Get TimeofEntry from BUFLIST_ENTRY.	*/
    if (BSTR_BIT_GET(optFlds, OPTFLD_BITNUM_TIMESTAMP))
      {
      MVLU_LOG_CFLOW2 ("    RptTim : %lums, %lu days",
              bufEntry->TimeOfEntry.ms, bufEntry->TimeOfEntry.day);

      tmp_va->type_id = rptCtrl->rpt_typeids.btime6;
      tmp_va->data = &bufEntry->TimeOfEntry;	/* use time saved in buffer*/
      rptNvl->entries[sendIndex++] = tmp_va++;	/* set entry & point to next*/
      }

    /* Get DatSet from RCB (OK because changing it causes PurgeBuf).	*/
    if (BSTR_BIT_GET(optFlds, OPTFLD_BITNUM_DATSETNAME))
      {
      /* UCA called it OutDat instead of DatSet. Value is same.	*/
      MVLU_LOG_CFLOW1 ("    DatSet : %s", basrcb->DatSetNa);
      tmp_va->type_id = rptCtrl->rpt_typeids.vstring129; /*ObjectReference*/
      tmp_va->data = basrcb->DatSetNa;
      rptNvl->entries[sendIndex++] = tmp_va++;	/* set entry & point to next*/
      }

    /* Get BufOvfl.	*/
    if (BSTR_BIT_GET(optFlds, OPTFLD_BITNUM_BUFOVFL))
      {
      tmp_va->type_id = rptCtrl->rpt_typeids.mmsbool;
      tmp_va->data = &rptCtrl->brcbCtrl.BufOvfl;
      rptNvl->entries[sendIndex++] = tmp_va++;	/* set entry & point to next*/
      }

    /* Get EntryID from BUFLIST_ENTRY.	*/
    if (BSTR_BIT_GET(optFlds, OPTFLD_BITNUM_ENTRYID))
      {
      /* NOTE: EntryID only incremented when RPT buffered.		*/
      /*       Copied to bufEntry->EntryID after incrementing.	*/
      tmp_va->type_id = rptCtrl->rpt_typeids.ostring8;
      tmp_va->data = bufEntry->EntryID;
      rptNvl->entries[sendIndex++] = tmp_va++;	/* set entry & point to next*/
      }

    /* Add "ConfRev" if enabled by "OptFlds".	*/
    if (BSTR_BIT_GET(optFlds, OPTFLD_BITNUM_CONFREV))
      {
      tmp_va->type_id = rptCtrl->rpt_typeids.int32u;
      tmp_va->data = &basrcb->ConfRev;
      rptNvl->entries[sendIndex++] = tmp_va++;	/* set entry & point to next*/
      }

	 if(segNeeded)
	 printf("brcb:segNeeded=%d num=%d va_index = %d->%d\r\n",
		 segNeeded,rptCtrl->dsNvl->num_of_entries,bufEntry->cur_va_index, next_va_index);

    if (segNeeded)
      {
      /* Set appropriate bit in OptFlds.			*/
      /* NOTE: this bit only for output. Never used as input.	*/
      BSTR_BIT_SET_ON (optFlds,OPTFLD_BITNUM_SUBSEQNUM);

      tmp_va->type_id = rptCtrl->rpt_typeids.int16u;
      tmp_va->data = &bufEntry->SubSeqNum;
      rptNvl->entries[sendIndex++] = tmp_va++;	/* set entry & point to next*/

      if (next_va_index < rptCtrl->dsNvl->num_of_entries)
        MoreSegmentsFollow = SD_TRUE;
      else
        MoreSegmentsFollow = SD_FALSE;

      tmp_va->type_id = rptCtrl->rpt_typeids.mmsbool;
      tmp_va->data = &MoreSegmentsFollow;
      rptNvl->entries[sendIndex++] = tmp_va++;	/* set entry & point to next*/
      }
    else
      {
      BSTR_BIT_SET_OFF (optFlds,OPTFLD_BITNUM_SUBSEQNUM);
      }

    /* Construct inclusion bitstring using "reason_for_incl" in BUFLIST_ENTRY.	*/
    tmp_va->type_id = rptCtrl->inclusion_typeid;
    /* just set the bits included in this segment.	*/
    memset (rptClient->segmented_inclusion, 0, incSize);	/* start clean	*/
    for (j = bufEntry->cur_va_index; j < next_va_index; ++j)
      {
      if (bufEntry->var_data[j].reason_for_incl)
        BSTR_BIT_SET_ON (rptClient->segmented_inclusion, j);
      }
    tmp_va->data = rptClient->segmented_inclusion;
    rptNvl->entries[sendIndex++] = tmp_va++;	/* set entry & point to next*/

    /* If data-Ref enabled, go through inclusion_data to decide what to send*/
    if (BSTR_BIT_GET(optFlds, OPTFLD_BITNUM_DATAREF))
      {
      for (j = bufEntry->cur_va_index; j < next_va_index; ++j)
        {
        if (bufEntry->var_data[j].reason_for_incl)
          {	/* include dataRef for this variable	*/
          tmp_va->type_id = rptCtrl->rpt_typeids.vstring129; /*ObjectReference*/
          /* point to right part of tmp buffer & construct dataref	*/
          tmp_va->data = &tmp_dataref_buf [j * (MVL61850_MAX_OBJREF_LEN+1)];
          mvl61850_mk_dataref (dsNvl->entries[j], &dsNvl->va_scope[j],
                               tmp_va->data, MVL61850_MAX_OBJREF_LEN);
          rptNvl->entries[sendIndex++] = tmp_va++;	/* set entry & point to next*/
          }
        }
      }

    /* HERE'S DATA: Go through inclusion_data to decide what to send	*/
    sendIndexSave = sendIndex;	/* save index before data included	*/
    for (j = bufEntry->cur_va_index; j < next_va_index; ++j)
      {
      if (bufEntry->var_data[j].reason_for_incl)
        {	/* include this variable	*/
        va = dsNvl->entries[j];
        tmp_va->data = bufEntry->var_data[j].data_ptr;	/* buffered data*/
        tmp_va->type_id = va->type_id;
        MVLU_LOG_CFLOW3 ("  Including variable %d ('%s'), reason_for_incl=0x%02X",
                        j, va->name, bufEntry->var_data[j].reason_for_incl);
        rptNvl->entries[sendIndex++] = tmp_va++;
        }
      }
    assert (sendIndex>sendIndexSave);	/* make sure SOME data included	*/

    /* If "reason" enabled, go through inclusion_data to decide what to send*/
    if (BSTR_BIT_GET(optFlds, OPTFLD_BITNUM_REASON))
      {
      for (j = bufEntry->cur_va_index; j < next_va_index; ++j)
        {
        if (bufEntry->var_data[j].reason_for_incl)
          {	/* include reason for this variable	*/
          /* Don't need BVSTR here, because size is fixed.	*/
          tmp_va->type_id = rptCtrl->rpt_typeids.bstr6;
          tmp_va->data = &bufEntry->var_data[j].reason_for_incl;
          rptNvl->entries[sendIndex++] = tmp_va++;	/* set entry & point to next*/
          }
        }
      }
    rptNvl->num_of_entries = sendIndex;
    assert (rptNvl->num_of_entries <= rptCtrl->maxNumRptVars);	/* past end of array?*/
    assert ((tmp_va-tmp_va_arr) <= tmp_va_arr_size);	/* past end of array?*/

    /* Encode the InformationReport and send it.	*/
    retCode = mvl_info_variables (rptClient->netInfo,
                        rptNvl,
                        SD_FALSE);	/* FALSE means "send as NVL"	*/
    }	/* END MAIN LOOP	*/

  /* Did we send all segments of this report?	*/
  if (retCode == SD_SUCCESS && bufEntry->cur_va_index == rptCtrl->dsNvl->num_of_entries)
    {
    *all_seg_done = SD_TRUE;	/* YES: Set flag	*/
    /* CRITICAL: Reset these to be ready for retransmit later.	*/
    bufEntry->cur_va_index = 0;
    bufEntry->SubSeqNum = 0;
    }

  M_FREE (MSMEM_GEN, tmp_va_arr);
  M_FREE (MSMEM_GEN, tmp_dataref_buf);
  return (retCode);
  }

/************************************************************************/
/*			mvl61850_brcb_enable_all			*/
/* Start buffering all buffered reports.				*/
/************************************************************************/
ST_VOID mvl61850_brcb_enable_all ()
  {
MVLU_RPT_CTRL *rptCtrl;

  for (rptCtrl = mvl61850_rpt_ctrl_list;
       rptCtrl != NULL;
       rptCtrl = (MVLU_RPT_CTRL *) list_get_next (mvl61850_rpt_ctrl_list, rptCtrl))
    {
    if (rptCtrl->rcb_type == RCB_TYPE_IEC_BRCB)
      rptCtrl->brcbCtrl.enabled_once = SD_TRUE;
    }
  }

/************************************************************************/
/*			mvl61850_brcb_entryid_init			*/
/* Set initial value of EntryID for this BRCB. EntryID must point to	*/
/* an 8 byte octet string.						*/
/************************************************************************/
ST_VOID mvl61850_brcb_entryid_init (MVLU_RPT_CTRL *rptCtrl, ST_UINT8 *EntryID)
  {
  memcpy (rptCtrl->only_client.basrcb.EntryID, EntryID, 8);
  }

/************************************************************************/
/*			mvl61850_rpt_change_scan_va_done			*/
/* Used by code that scans for dchg, qchg, dupd changes.		*/
/* This function is called by 'mvlu_rd_prim_done' when all "leaf"	*/
/* functions for a VA have been completed (i.e. data ready for this VA).*/
/************************************************************************/
void    mvlu_log_va_change (MVL_VAR_ASSOC *va, ST_UCHAR reason, ST_VOID *new_data);
ST_VOID mvl61850_rpt_change_scan_va_done (MVL_IND_PEND *indCtrl,
					MVL_VAR_ASSOC *va)
  {
  /* If "rpt_reason" set by ANY leaf function, call mvlu_rpt_va_change.	*/
  if (va->rpt_reason)
  {
    mvlu_rpt_va_change (va, va->rpt_reason, NULL);
	/*renxiaobao 日志 
	//mvlu_log_va_change (va, va->rpt_reason, NULL);*/
  }
  }

/************************************************************************/
/*			mvl61850_rpt_change_scan_done			*/
/* Used by code that scans for dchg, qchg, dupd changes.		*/
/* This function is called when the scan is complete.			*/
/* (i.e. 'mvlu_rd_prim_done' has been called for all "leafs" of all	*/
/* variables in the report). Everything is ready to build a report,	*/
/* so build it now.							*/
/* CRITICAL: when all done, this function must free the temporary	*/
/*   MVL_IND_PEND struct by calling "mvlu_integrity_scan_destroy".	*/
/************************************************************************/
ST_RET mvl61850_rpt_change_scan_done (MVL_IND_PEND *indCtrl)
  {
ST_RET retCode = SD_SUCCESS;	/* nothing to fail here	*/

  /* NOTE: not much to do here. DO NOT send reports.			*/
  /*       mvlu_rpt_service chks numTrgs & BufTim & sends when needed.	*/

  /* CRITICAL: free the temporary MVL_IND_PEND struct.	*/
  mvlu_integrity_scan_destroy (indCtrl);
  return (retCode);
  }

/************************************************************************/
/*			mvl61850_rpt_change_scan			*/
/* Scan all IEC 61850 Report Datasets for dchg, qchg, dupd.		*/
/************************************************************************/
/*renxiaobao 报告*/
ST_VOID mvl61850_rpt_change_scan (char *dom,char CHG)
/*ST_VOID mvl61850_rpt_change_scan (ST_VOID)*/
  {
MVLU_RPT_CTRL   *rptCtrl;
MVL_NVLIST_CTRL *dsNvl;
ST_INT			 j;
ST_DOUBLE        timeNow;
short            rpt_num=0;

  /* Reset "already_scanned=SD_FALSE" for each NVL.				*/
  /* Reset it for every NVL, even if not enabled (faster).			*/
	for(rptCtrl	= mvl61850_rpt_ctrl_list;
		rptCtrl!= NULL;
		rptCtrl	= (MVLU_RPT_CTRL *) list_get_next (mvl61850_rpt_ctrl_list, rptCtrl))
    {
		if(dom&&strcmp(rptCtrl->dsNvl->va_scope->dom->name,dom))	continue;
		dsNvl = rptCtrl->dsNvl;
		if (dsNvl)				/* dsNvl might be NULL	*/
		{
			dsNvl->already_scanned = SD_FALSE;
		}
		rpt_num++;
    }
	if(!rpt_num)	return;
	timeNow = sGetMsTime ();	/* get time just once. Use it in loop.							*/
								/* Loop through all Report NVLs to scan for data changes.		*/
								/* Set already_scanned=SD_TRUE for each NVL. If same NVL used in another	*/
								/* Report, do NOT rescan it.									*/
	for(rptCtrl = mvl61850_rpt_ctrl_list;
		rptCtrl!= NULL;
		rptCtrl = (MVLU_RPT_CTRL *) list_get_next (mvl61850_rpt_ctrl_list, rptCtrl))
    {
		if(dom&&strcmp(rptCtrl->dsNvl->va_scope->dom->name,dom))	continue;
		/* If scan_rate > 0, check timer. If not, go ahead and scan now.	*/
		/* NOTE: if scan_rate is too small, there may be extra delay between scans.*/
        if(CHG)
		{
			if (timeNow >= rptCtrl->next_scan_start)
				rptCtrl->next_scan_start = timeNow + rptCtrl->scan_rate;
		}
		else if (rptCtrl->scan_rate > 0)
		{	/* check timer to see if it's time to scan	*/
			if (timeNow < rptCtrl->next_scan_start)
				continue;	/* DO NOT scan now. Skip to next rptCtrl	*/
			else
				rptCtrl->next_scan_start = timeNow + rptCtrl->scan_rate;
		}
		dsNvl = rptCtrl->dsNvl;
		if (dsNvl!=NULL && dsNvl->already_scanned==SD_FALSE)	/* dsNvl might be NULL*/
		{	/* This NVL not yet scanned.	*/
			/* If URCB enabled or BRCB enabled ONCE, start scan.*/
			if( rptCtrl->only_client.basrcb.RptEna||(rptCtrl->rcb_type == RCB_TYPE_IEC_BRCB&& 
				rptCtrl->brcbCtrl.enabled_once))
			{
				/* Initialize "rpt_reason=0" for all variables. May be changed		*/
				/* in leaf functions. Checked in mvl61850_rpt_change_scan_va_done.	*/
				for (j = 0; j < dsNvl->num_of_entries; j++)
					dsNvl->entries[j]->rpt_reason = 0;

				/* This is NOT an Integrity scan, but mvlu_integrity_scan_read		*/
				/* does what we need if the right callback functions passed to it.	*/
				/* Scan for dchg, qchg, dupd.										*/
				/* This function "begins" the scan. When the scan is				*/
				/* complete, "mvl61850_rpt_change_scan_done" is called to build the	*/
				/* the report. If ALL "leaf" functions are synchronous,				*/
				/* "mvl61850_rpt_change_scan_done" is called BEFORE this function returns.*/
				/* CRITICAL: this function allocates a temporary MVL_IND_PEND		*/
				/*   which must be freed by mvl61850_rpt_change_scan_done.			*/
				mvlu_integrity_scan_read (&rptCtrl->only_client,
						mvl61850_rpt_change_scan_va_done,
						mvl61850_rpt_change_scan_done);
				dsNvl->already_scanned = SD_TRUE;	/* this NVL scanned. Don't scan again for another RCB*/
			}
		}
    }
 }
/************************************************************************/
/*			mvl61850_get_rcb				*/
/* Find IEC 61850 RCB (URCB or BRCB).					*/
/* NOTE: Caller can access MVLU_RPT_CTRL, if needed, by using "rpt_ctrl"*/
/* member of MVLU_RPT_CLIENT.						*/
/************************************************************************/
MVLU_BASRCB *mvl61850_get_rcb (MVL_VAR_ASSOC *baseVa,
	RUNTIME_TYPE *rt,
	MVLU_RPT_CLIENT **rptClientOut)
  {
MVLU_RPT_CTRL *rptCtrl;
MVLU_BASRCB *rcb = NULL;	/* ptr to be returned	*/

   /* Find the associated Report Control, using this RT as a key */
  for (rptCtrl = mvl61850_rpt_ctrl_list;
       rptCtrl != NULL;
       rptCtrl = (MVLU_RPT_CTRL *) list_get_next (mvl61850_rpt_ctrl_list, rptCtrl))
    {
    if (rptCtrl->base_va == baseVa &&
        (rt > rptCtrl->rcbRtHead) &&
        (rt <= rptCtrl->rcbRtHead + rptCtrl->rcbRtHead->u.str.num_rt_blks))
      {
      if (rptCtrl->rcb_type != RCB_TYPE_UCA)
        {
        rcb = &rptCtrl->only_client.basrcb;
        if (rptClientOut != NULL)		/* set optional ptr	*/
          *rptClientOut = &rptCtrl->only_client;	/* always EXACTLY one client*/
        }
      break;	/* rptCtrl is set. Just need to return it.	*/
      }
    }
  /* rcb will be NULL if rptCtrl NOT found or NOT 61850	*/
  return (rcb);
  }

/************************************************************************/
/*			mvl61850_rpt_dataset_create			*/
/* Create IEC 61850 Report Control Block resources for a dataset.	*/
/* Assumes EXACTLY one client per rptCtrl (see rptCtrl->only_client).	*/
/************************************************************************/
ST_RET mvl61850_rpt_dataset_create (MVLU_RPT_CTRL *rptCtrl,
			MVL_NVLIST_CTRL *dsNvl)
  {
RUNTIME_TYPE *incRt;
ST_RET rc;
ST_INT numDsVars;
ST_INT changed_flags_size;
ST_INT allocSize;

  if (rptCtrl->dsNvl)
    {	/* dsNvl already set. Must call mvl61850_rpt_dataset_destroy first*/
    MVL_LOG_ERR0 ("Report Dataset already exists. Must destroy it before creating new one.");
    return (SD_FAILURE);
    }

  if (dsNvl == NULL)
    return (SD_SUCCESS);	/* this is allowed, but no need to do anything*/

  numDsVars = dsNvl->num_of_entries;

  /* NOTE: RptID (in MVLU_BASRCB struct) might be empty (0 length string).*/
  /*   If so, a string will be constructed each time a report is built	*/
  /*   (see "mvlu_mk_rptid" function).				*/

/* Alloc the entries table; allow for RptID, OptFlds, SqNum, RptTim, 	*/
/* DatSet, BufOvfl, EntryID, ConfRev,					*/
/* SubSeqNum, MoreSegmentsFollow, & InclusionBitstring plus ARRAYS of	*/
/* dataRefs, values, and Reasons.					*/
/* "assert" later if "maxNumRptVars" exceeded.				*/
/* Allow (3 * numDsVars) to handle arrays of dataRef, value, and reason.*/
  rptCtrl->maxNumRptVars = MVLU_MAX_RPT_OPTS + (3 * numDsVars);

  rptCtrl->rptNvl.entries = (MVL_VAR_ASSOC **) M_CALLOC (MSMEM_GEN, rptCtrl->maxNumRptVars,
					sizeof (MVL_VAR_ASSOC *));

  /* NOTE: The "rptNvl->entries" array is not filled in and
   *       and "rptNvl->num_of_entries" is not set
   *       until a Report is being built.
   */

  /* We need to create a Runtime Type for the inclusion bitstring */
  incRt = &rptCtrl->incRt;
  incRt->el_tag = RT_BIT_STRING;
  incRt->el_size = BSTR_NUMBITS_TO_NUMBYTES(numDsVars);
  incRt->offset_to_last = incRt->el_size;
  /*renxiaobao 5.3版本修改*/
  /*incRt->u.p.el_len = (ST_RTINT) numDsVars;*/    
  incRt->u.p.el_len = numDsVars;
  rc = mvlu_add_rt_type (&rptCtrl->incRt, 1, &rptCtrl->inclusion_typeid);
  if (rc != SD_SUCCESS)
    {
    MVL_LOG_NERR0 ("Error - could not add temp RT type");
    return (SD_FAILURE);
    }

/* That's all we can do ahead of time for the RPT variable associations	*/

/* Set a bit of the data for the BASRCB */
  if (dsNvl->nvl_scope.scope == AA_SPEC)
    sprintf (rptCtrl->only_client.basrcb.DatSetNa, "@%s", dsNvl->name);
  else if (dsNvl->nvl_scope.scope == VMD_SPEC)
    sprintf (rptCtrl->only_client.basrcb.DatSetNa, "/%s", dsNvl->name);
  else
    sprintf (rptCtrl->only_client.basrcb.DatSetNa, "%s/%s",
			dsNvl->nvl_scope.dom->name, dsNvl->name);

  /* Alloc one buf for reasons_data, changed_flags, segmented_inclusion.*/
  /* Free this buf in mvl61850_rpt_dataset_destroy.				*/
  changed_flags_size = BSTR_NUMBITS_TO_NUMBYTES(numDsVars);
  allocSize = (numDsVars * sizeof (ST_UINT8)) +	  /* reasons_data     	*/
	(2 * changed_flags_size);	/* changed_flags & segmented_inclusion*/
  rptCtrl->only_client.reasons_data = chk_calloc (1, allocSize);
  rptCtrl->only_client.changed_flags = rptCtrl->only_client.reasons_data + numDsVars;
  rptCtrl->only_client.segmented_inclusion = rptCtrl->only_client.changed_flags + changed_flags_size;

  rptCtrl->dsNvl = dsNvl;	/* now save new dataset	*/

  return (SD_SUCCESS);
  }

/************************************************************************/
/*			mvl61850_rpt_dataset_destroy			*/
/* Destroy resources created by mvl61850_rpt_dataset_create.		*/
/************************************************************************/
ST_VOID mvl61850_rpt_dataset_destroy (MVLU_RPT_CTRL *rptCtrl)
  {
  if (rptCtrl->dsNvl != NULL)
    {		/* if dsNvl is already NULL, do nothing	*/
    chk_free (rptCtrl->only_client.reasons_data);
    rptCtrl->only_client.basrcb.DatSetNa[0] = '\0';	/* clear DatSet	*/

    rptCtrl->maxNumRptVars = 0;

    M_FREE (MSMEM_GEN, rptCtrl->rptNvl.entries);

    /* Free the Runtime Type for the inclusion bitstring */
    mvlu_free_rt_type (rptCtrl->inclusion_typeid);
    rptCtrl->dsNvl = NULL;
    }
  return;
  }

/************************************************************************/
/*                       mvl61850_create_rpt_ctrl			*/
/* Create IEC 61850 Report Control Block.				*/
/* Similar to UCA function "mvlu_create_rpt_ctrl", but just for 61850.	*/
/* Only one client per RCB (see "only_client" param).			*/
/* The "dsNvl" arg may be NULL. If so, the RCB may not be enabled until	*/
/* it is set.								*/
/* NOTE: Does not support UCA Reports (see assert checking "rcb_type").	*/
/************************************************************************/
MVLU_RPT_CTRL *mvl61850_create_rpt_ctrl (ST_CHAR *basrcbName,
			MVL_NVLIST_CTRL *dsNvl,	/* may be NULL		*/
			MVL_VAR_ASSOC *base_va,
			ST_INT rcb_type,
			ST_INT buftim_action,	/* MVLU_RPT_BUFTIM_*	*/
			ST_INT brcb_bufsize,	/* for BRCB only	*/
			ST_UINT32 ConfRev)	/* for BRCB/URCB only	*/

  {
MVLU_RPT_CTRL *rptCtrl;
ST_INT rptCtrlSize;
ST_INT numRt;
ST_RET status;	/* SD_SUCCESS if everything worked	*/

  assert (rcb_type != RCB_TYPE_UCA);	/* DO NOT use this funct for UCA rpts*/

  /* If any rpt_ctrl created, disconnect function ptr must be set.	*/
  _mvlu_rpt_disconnect_rcvd_fun = _mvlu_rpt_disconnect_rcvd;

  /* Allocate MVLU_RPT_CTRL struct. Allow extra space after struct to	*/
  /* store basrcbName.							*/
  rptCtrlSize = sizeof (MVLU_RPT_CTRL) + strlen (basrcbName) + 1;
  rptCtrl = (MVLU_RPT_CTRL *) M_CALLOC (MSMEM_GEN, 1, rptCtrlSize);

  rptCtrl->basrcb_name = (ST_CHAR *) (rptCtrl + 1);	/*point after struct*/
  strcpy (rptCtrl->basrcb_name, basrcbName);

  /* Initialize structure members.	*/
  rptCtrl->base_va = base_va;
  rptCtrl->rcb_type = rcb_type;
  rptCtrl->buftim_action = buftim_action;

  rptCtrl->brcbCtrl.brcb_bufsize = brcb_bufsize;	/* ONLY for IEC BRCB*/

  rptCtrl->rptNvl.name = "RPT";	/* Report NVL name ALWAYS = "RPT"*/

  rptCtrl->only_client.basrcb.ConfRev = ConfRev;
  rptCtrl->only_client.basrcb.OptFlds.len_1 = 10;	/* bitstring len*/
  rptCtrl->only_client.basrcb.TrgOps.len = 6;		/* bitstring len*/

  rptCtrl->only_client.rpt_ctrl = rptCtrl;	/* client struct needs ptr back to rptCtrl*/

  /* For BRCB, set the initial "EntryID" value.	*/
  /* Put current time in first 4 bytes, 0 in last 4 bytes.	*/
  if (rcb_type == RCB_TYPE_IEC_BRCB)
    {
    ST_INT32 tmp[2];
	tmp [0] = (ST_INT32) time (NULL);
	/*renxiaobao 报告*/
    tmp [0] = 0;
    tmp [1] = 0;
    mvl61850_brcb_entryid_init (rptCtrl, (ST_UINT8 *) tmp);
    }

  /* The following initializations could fail, so we set & check "status".*/

  /* Find the runtime type of the RCB and save it in rptCtrl->rcbRtHead.*/
  /* Used by leaf functions to find the correct rptCtrl.		*/
  /* If successful, this function sets "rptCtrl->rcbRtHead" & "numRt".	*/
  status = mvlu_rpt_rcb_type_find (base_va->type_id, basrcbName, &rptCtrl->rcbRtHead, &numRt);

  if (status == SD_SUCCESS)
    {
    if (mvlu_rpt_find_typeids (&rptCtrl->rpt_typeids))
      {
      MVL_LOG_ERR0 ("Cannot find Report Control Block types");
      status = SD_FAILURE;
      }
    }

  /* mvl61850_rpt_dataset_create initializes everything related to dsNvl.	*/
  if (status == SD_SUCCESS)
    status = mvl61850_rpt_dataset_create (rptCtrl, dsNvl);

  if (status == SD_SUCCESS)
    {
#if 1	/* IEC 61850-7-2 Ed2 says to start buffering on power-up if	*/
	/* DatSet attribute references an existing DataSet.		*/
    if (dsNvl != NULL && rptCtrl->rcb_type == RCB_TYPE_IEC_BRCB)
      rptCtrl->brcbCtrl.enabled_once = SD_TRUE;	/* starts buffering	*/
#endif
    list_add_last((ST_VOID **) &mvl61850_rpt_ctrl_list, (ST_VOID *) rptCtrl);
    }
  else
    {
    M_FREE (MSMEM_GEN, rptCtrl);	/* clean up	*/
    rptCtrl = NULL;			/* set error return value	*/
    }
  return (rptCtrl);
  }

/************************************************************************/
/*                       mvl61850_free_rpt_ctrl				*/
/* Free a MVLU_RPT_CTRL allocated by 'mvl61850_create_rpt_ctrl'.	*/
/************************************************************************/

ST_VOID mvl61850_free_rpt_ctrl (MVLU_RPT_CTRL *rptCtrl)
  {
  assert (rptCtrl->rcb_type != RCB_TYPE_UCA);

/* Take it off the report control list */
  list_unlink ((ST_VOID **) &mvl61850_rpt_ctrl_list, (ST_VOID *) rptCtrl);

  mvl61850_rpt_dataset_destroy (rptCtrl);

/* BRCB may contain large lists of reports sent or queued.	*/
  if (rptCtrl->rcb_type == RCB_TYPE_IEC_BRCB)
    mvl61850_brcb_rpt_lists_clean (&rptCtrl->brcbCtrl);

  M_FREE (MSMEM_GEN, rptCtrl);
  }

/************************************************************************/
/*			mvl61850_brcb_client_service			*/
/* Check the BRCB state and perform appropriate actions.		*/
/************************************************************************/
ST_VOID mvl61850_brcb_client_service (MVLU_RPT_CTRL *rptCtrl,
			MVLU_RPT_CLIENT *rptClient,
			ST_DOUBLE timeNow)
  {
BUFLIST_ENTRY *entry;
ST_BOOLEAN all_seg_done;	/* SD_TRUE if done sending all segments	*/
ST_RET retCode;

  /* Check BRBC state and, if necessary, encode AND queue up report.	*/
  mvl61850_rcb_chk_state (rptCtrl, rptClient, timeNow);

  /* Now, examine BRCB state and send report if necessary.
   * If connected, reports are enabled, reports are queued, AND stack buffers
   * are available, send a report.
   */
  if (rptClient->netInfo  &&			/* connected	*/
      rptClient->basrcb.RptEna  &&		/* rpts enabled	*/
      rptCtrl->brcbCtrl.rpt_list_next)		/* rpts queued & not all sent*/
    {
    if (mvl_ureq_bufs_avail (rptClient->netInfo) > 0)	/* stack bufs avail to send rpt	*/
      {
      /* Encode and send next buffered report.	*/
      entry = rptCtrl->brcbCtrl.rpt_list_next;	/* this is next one to send*/
      retCode = mvl61850_brcb_rpt_send (rptCtrl, rptClient, entry, &all_seg_done);
      if (retCode != SD_SUCCESS)
        MVL_LOG_ERR1 ("Sending IEC-61850 Buffered Report failed: err=0x%X", retCode);

      /* If error OR done sending all segments of this rpt,		*/
      /* point to next rpt on list (NULL if all sent) & update SqNum	*/
      if (retCode != SD_SUCCESS ||
          all_seg_done)	/* done sending all segments of this report.	*/
        {	/* point to next rpt on the list (NULL if all sent)	*/
        rptCtrl->brcbCtrl.rpt_list_next = list_get_next (rptCtrl->brcbCtrl.rpt_list, entry);
        memcpy (rptCtrl->brcbCtrl.lastSentEntryID, entry->EntryID, 8);	/* save last sent EntryID*/
        memcpy (&rptCtrl->brcbCtrl.lastSentTimeOfEntry, &entry->TimeOfEntry, sizeof (MMS_BTIME6));
        print_queues (&rptCtrl->brcbCtrl);		/* just for debug	*/
        /* Increment seq number for next rpt.	*/
        rptClient->basrcb.SqNumInt16u++;	/* used only for BRCB	*/
        /* Only one report should be sent with BufOvfl TRUE; clear it now.*/
        rptCtrl->brcbCtrl.BufOvfl = SD_FALSE;
        }
      }
    else
      print_msg0 ("STACK BUFFERS NOT AVAILABLE TO SEND REPORT\n");
    }
  return;
  }

/************************************************************************/
/*			mvl61850_urcb_client_service			*/
/* Check the URCB state and perform appropriate actions.		*/
/************************************************************************/
int urcb_netInfo_pri=0;
ST_VOID mvl61850_urcb_client_service (MVLU_RPT_CTRL *rptCtrl,
			MVLU_RPT_CLIENT *rptClient,
			ST_DOUBLE timeNow)
  {
  /* Must be connected to send Unbuffered reports.	*/
  if (rptClient->netInfo!=NULL)
    {
	 /* if(urcb_netInfo_pri)
	  printf("rptClient->netInfo=%d\r\n ",rptClient->netInfo);*/
    /* If no buffers are available to send unbuffered reports, don't waste time
     * checking timers, scanning, etc.
     */
    if (mvl_ureq_bufs_avail (rptClient->netInfo) <= 0)	/* stack bufs avail to send rpt	*/
      print_msg0 ("STACK BUFFERS NOT AVAILABLE TO SEND REPORT\n");
    else
      /* Check URBC state and, if necessary, encode AND send report.	*/
      mvl61850_rcb_chk_state (rptCtrl, rptClient, timeNow);
    }
  return;
  }

/************************************************************************/
/*			mvl61850_rpt_service 				*/
/* Perform servicing for all IEC 61850 Report Control Blocks.		*/
/* Scanning for changes (dchg, qchg, etc) is automatic		  	*/
/* (see mvl61850_rpt_change_scan).					*/
/************************************************************************/

ST_VOID mvl61850_rpt_service (char *dom,char CHG)
  {
MVLU_RPT_CTRL *rptCtrl;
ST_DOUBLE timeNow;

/* Scan all report data for changes (dchg, qchg, dupd).	*/
  mvl61850_rpt_change_scan (dom,CHG);
/* See if it is time to send reports	*/
  timeNow = sGetMsTime ();
  for (rptCtrl = mvl61850_rpt_ctrl_list;
       rptCtrl != NULL;
       rptCtrl = (MVLU_RPT_CTRL *) list_get_next (mvl61850_rpt_ctrl_list, rptCtrl))
    {
		if(dom&&strcmp(rptCtrl->dsNvl->va_scope->dom->name,dom))	continue;
			/* NOTE: if DatSet is NULL, don't need to do anything for this rptCtrl.*/
		if (rptCtrl->dsNvl==NULL)
			continue;
		/* Only one client (rptCtrl->only_client).	*/
		if (rptCtrl->rcb_type == RCB_TYPE_IEC_BRCB)
			mvl61850_brcb_client_service (rptCtrl, &rptCtrl->only_client, timeNow);
		else
		{
		  /*assert (rptCtrl->rcb_type == RCB_TYPE_IEC_URCB);*/	/* must be IEC_URCB*/
			mvl61850_urcb_client_service (rptCtrl, &rptCtrl->only_client, timeNow);
      }
    }
 }
/************************************************************************/
/*			mvl61850_rpt_ctrl_destroy_all			*/
/* Destroy all 61850 report controls.					*/
/************************************************************************/
ST_VOID mvl61850_rpt_ctrl_destroy_all ()
  {
MVLU_RPT_CTRL *rptCtrl;
  /* Use list_find_last to avoid calling list_unlink twice.	*/
  while ((rptCtrl = (MVLU_RPT_CTRL *) list_find_last ((DBL_LNK *)mvl61850_rpt_ctrl_list)) != NULL)
    {
    mvl61850_free_rpt_ctrl (rptCtrl);	/* this also unlinks it from list*/
    }
  }

/************************************************************************/
/*			mvl61850_integrity_timeout			*/
/* NOTE: For IEC 61850 only. For UCA, use mvlu_integrity_timeout.	*/
/* If Integrity period timeout occurred, send Integrity Report.		*/
/* NOTE: data scan already done at start of mvl61850_rpt_service.	*/
/*       No need to rescan data. Just send the data from the data scan.	*/
/* RETURNS: SD_TRUE if IntgPd is set and timeout occurred		*/
/*          SD_FALSE otherwise						*/
/************************************************************************/
ST_RET mvl61850_integrity_timeout (MVLU_RPT_CLIENT *rptClient, ST_DOUBLE timeNow)
  {
ST_RET retVal;
ST_INT j;
ST_RET readyRet;	/* return code from mvlu_rpt_ready	*/

  if (rptClient->basrcb.IntgPd != 0
      && BSTR_BIT_GET (rptClient->basrcb.TrgOps.data, TRGOPS_BITNUM_INTEGRITY)	/* enabled*/
      && timeNow > rptClient->next_integ_rpt_time)	/* timeout occurred*/
    {
		MVLU_LOG_FLOW1 ("IEC 61850 Report Integrity Period timeout for client %08lx",rptClient);
		/* Send last buffered entry (due to BufTm buffering) BEFORE this integrity report.*/
		if (rptClient->numTrgs > 0)
		{		
			/* send or queue report	*/
			mvlu_rpt_ready (rptClient, MVLU_RPT_TYPE_RBE);
		}

    /* Set "reason" for each variable of Dataset now.	*/
    for (j = 0; j < rptClient->rpt_ctrl->dsNvl->num_of_entries; j++)
      rptClient->reasons_data[j] = MVLU_TRGOPS_INTEGRITY;

    /* send or queue rpt	*/
    readyRet = mvlu_rpt_ready (rptClient, MVLU_RPT_TYPE_INTEGRITY_OR_GI);

    if (readyRet != SD_SUCCESS)
      MVLU_LOG_FLOW1 ("IEC 61850 Integrity report could not be sent or queued: err=0x%X", readyRet);

    /* set time for next integrity report	*/
    rptClient->next_integ_rpt_time = sGetMsTime ()
                     + (ST_DOUBLE) rptClient->basrcb.IntgPd;
    retVal = SD_TRUE;
    }
  else
    retVal = SD_FALSE;
  return (retVal);
  }


