/************************************************************************/
/* SISCO SOFTWARE MODULE HEADER *****************************************/
/************************************************************************/
/*	(c) Copyright Systems Integration Specialists Company, Inc.,	*/
/*	2002-2006 All Rights Reserved					*/
/*									*/
/* MODULE NAME : mvl61850.c						*/
/* PRODUCT(S)  : MMSEASE-LITE						*/
/*									*/
/* MODULE DESCRIPTION :							*/
/*	Code to support IEC-61850 Reporting as a Server.		*/
/*									*/
/* GLOBAL FUNCTIONS DEFINED IN THIS MODULE :				*/
/*	mvl61850_rcb_chk_state						*/
/*	mvl61850_urcb_rpt_send						*/
/*									*/
/* MODIFICATION LOG :							*/
/*  Date     Who   Rev     Comments					*/
/* --------  ---  ------   -------------------------------------------  */
/* 08/05/08  JRB     31    Increment ConfRev if DatSet is written.	*/
/* 07/22/08  JRB     30    Add mvl61850_objref_create.			*/
/* 06/30/08  JRB     29    For Ed2, return different EntryID/TimeOfEntry*/
/*			   values if RptEna=FALSE or TRUE.		*/
/* 05/15/08  JRB     28    Use NEW mvl61850_integrity_timeout.		*/
/* 05/06/08  JRB     27    Fix memory leak after "PDU size" error.	*/
/* 04/15/08  JRB     26	   Don't call mvlu_rpt_va_change for GI		*/
/*			   (save "reason" later when scan completes).	*/
/* 02/29/08  JRB     25    Allow VMD_SPEC DatSet for URCB or BRCB.	*/
/*			   Allow AA_SPEC DatSet for URCB, but ONLY if	*/
/*			   RCB is reserved (Resv=TRUE).			*/
/*			   Set changed_flags for Integrity BEFORE loop.	*/
/*			   chk_seg_needed: allow longer (129) RptID, etc*/
/* 03/16/07  JRB     24    mvlu_gi_wr_ind: Chk that this client reserved URCB.*/
/* 03/07/07  JRB     23    Use mvlu_get_leaf_val_int_any for Beh$stVal.	*/
/* 02/28/07  JRB     22    mvlu_gi_wr_ind: allow write only if RCB is	*/
/*			   enabled; if TrgOps.GI=FALSE, allow write but	*/
/*			   DO NOT generate a report.			*/
/* 02/13/07  JRB     21    mvl61850_get_rcb: del unused net_info arg.	*/
/*			   Use new _rcb_writable.			*/
/* 12/20/06  JRB     20    PurgeBuf only if DatSet val changed (tissue 322)*/
/*			   Allow Beh$stVal to be INT8 (Enum) or INT32	*/
/*			   (for Tissues 120, 146, 171, 234).		*/
/* 10/30/06  JRB     19    Use new mvl_vmd_* object handling functions.	*/
/* 08/09/06  JRB     18    Moved most 61850 Rpt code to mvl61850_rpt.c	*/
/*			   Chg mvl61850_rcb_build to mvl61850_urcb_rpt_send*/
/*			   & use only for URCB (new functs handle BRCB).*/
/*			   Use new mvlu_rpt_ready, mvl61850_mk_dataref,	*/
/*			    mvl61850_rcb_cleanup.			*/
/*			   Use new mvl61850_mk_rptid to generate RptID	*/
/*			    in RPT if RptID is NULL in RCB.		*/
/*			   Allow GI write only if TrgOp GI bit=1.	*/
/*			   Del mvl_enc_va_data, use new ms_local_to_asn1_2.*/
/*			   Add mvl61850_datset_wr_ind to allow DatSet write.*/
/*			   Store EntryID everywhere as Ostring8.	*/
/*			   Fix type for all ObjectReference (Vstring129).*/
/*			   Use mvl61850_get_rcb to find the RCB.	*/
/* 07/11/05  JRB     17    BUG FIX: Set cur_bufsize=0 after PurgeBuf write.*/
/*			   Set BufOvfl=SD_FALSE after PurgeBuf write	*/
/*			   (not clear in 61850, but passes KEMA test).	*/
/* 07/08/05  JRB     16    For URCB, ignore BUFOVFL, ENTRYID bits in	*/
/*			   OptFlds to conform to 61850-8-1.		*/
/* 06/27/05  JRB     15    mvlu_entryid_rd_ind: return lastSentEntryID.	*/
/*			   Old code returned "last queued" EntryID.	*/
/* 06/27/05  JRB     14    _mvlu_get_rd_rcb: add (MVLU_RPT_CLIENT **) arg*/
/* 05/16/05  JRB     13    Set failure=ARE_TEMP_UNAVAIL when trying to	*/
/*			   write RCB elements while RptEna=TRUE.	*/
/*			   Del mvlu_sqnum_int16u_wr.. (not writable).	*/
/* 05/04/05  JRB     12    Call u_mvlu_rpt_time_get just once BEFORE	*/
/*			   main loop in ..rcb_build so that each	*/
/*			   segmented report has the same time stamp.	*/
/* 01/19/05  JRB     11    Add mvl61850_beh_stval_rd_ind.		*/
/* 08/20/04  JRB     10    Increment EntryID before encoding rpt,	*/
/*			   so EntryID encoded in rpt matches EntryID	*/
/*			   stored in BUFLIST_ENTRY struct.		*/
/* 08/06/04  EJV     09    mvlu_entryid_wr_ind: add typecast.		*/
/* 06/30/04  JRB     08    Add mvlu_sqnum_int16u_rd(wr)_ind_fun & use	*/
/*			   SqNumInt16u instead of SqNum for BRCB.	*/
/*			   Chg RptID type to vstring65.			*/
/*			   Del SCL parsing functions:			*/
/*			    mvl61850_ln_create_start, mvl61850_do_create,*/
/*			    mvl61850_da_create, mvl61850_ln_create_finish,*/
/*			    & mvl61850_ln_destroy. New SCL standard	*/
/*			    makes these obsolete.			*/
/* 03/18/04  JRB     07    Fix dataref in reports by adding scope.	*/
/* 12/17/03  JRB     06    61850-8-1 FDIS changes:			*/
/*			   Add ConfRev to rpt if enabled by OptFlds.	*/
/*			   Move SubSeqNum, MoreSegmentsFollow to just	*/
/*			   before inclusion bitstring.			*/
/*			   Save TimeofEntry in "basrcb".		*/
/*			   Add mvlu_timeofentry_rd_ind.			*/
/* 07/09/03  JRB     05    Del assert in mvlu_gi_scan_done so GI works	*/
/*			   for URCB too.				*/
/* 04/14/03  JRB     04    Eliminate compiler warnings.			*/
/* 04/04/03  JRB     03    Fix integrity/GI scan code so multiple	*/
/*			   concurrent scans don't corrupt one another.	*/
/* 03/13/03  JRB     02    Add URCB support: rename/rearrange some things*/
/*			   Add code for 61850 dynamic type creation.	*/
/*			   Trigger Intg Rpt ONLY by setting IntgPd!=0.	*/
/*			   Trigger GI ONLY by writing GI!=0.		*/
/*			   Move bstrcpy, bvstrcpy to "asn1r.c".		*/
/*			   Fix report segmenting (see chk_seg_needed).	*/
/* 12/15/02  JRB     01    Created					*/
/************************************************************************/
#include "glbtypes.h"
#include "sysincs.h"
#include "mvl_uca.h"
#include "mvl_log.h"
#include "mvl_acse.h"	/* need mvl_cfg_info	*/

/************************************************************************/
/* 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

static ST_RET fill_asn1_len_array (MVLU_RPT_CLIENT *rptClient,
	MVL_NVLIST_CTRL *dsNvl,
	ST_INT *asn1_len_array);

/******
 *			mvl61850_rcb_chk_state
 * Check RCB state and build/send report if necessary. This function works for
 * BRCB or URCB.
 */
ST_VOID mvl61850_rcb_chk_state (MVLU_RPT_CTRL *rptCtrl,
			MVLU_RPT_CLIENT *rptClient,
			ST_DOUBLE timeNow)
  {
  /* If URCB enabled or BRCB enabled ONCE, chk Integrity period & triggers.*/
  if (rptClient->basrcb.RptEna
      || (rptClient->rpt_ctrl->rcb_type == RCB_TYPE_IEC_BRCB
          && rptClient->rpt_ctrl->brcbCtrl.enabled_once))
    {
    /* Chk for Integrity Period timeout.	*/
    /* NOTE: use mvl61850_integrity_timeout (mvlu_integrity_timeout is	*/
    /*       only for UCA).						*/
    mvl61850_integrity_timeout (rptClient, timeNow);

    /* Chk for other triggers.	*/
    /* NOTE: numTrgs ONLY incremented if changes matched the TrgOps.	*/
    if (rptClient->numTrgs > 0)
      {
      /* If BufTim disabled (=0) OR BufTim expired, send report now.	*/
      if (rptClient->basrcb.BufTim == 0 || timeNow > rptClient->buf_time_done)
        {
        MVLU_LOG_FLOW1 ("Non-Integrity report for client %08lx", rptClient);
        mvlu_rpt_ready (rptClient, MVLU_RPT_TYPE_RBE);	/* send or queue rpt*/
        }
      }
    }	/* end if rpt enabled at least once	*/
  return;
  }

/************************************************************************/
/*			mvlu_gi_scan_va_done				*/
/* 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).*/
/************************************************************************/

ST_VOID mvlu_gi_scan_va_done (MVL_IND_PEND *indCtrl,
					MVL_VAR_ASSOC *va)
  {
  ST_INT data_size;
  /* CRITICAL: DO NOT call mvlu_rpt_va_change. rptClient->reasons_data	*/
  /*     will be set in mvlu_gi_scan_done when scan completes.		*/
  data_size = mvl_type_ctrl[va->type_id].data_size;
  memcpy (va->last_data, va->data, data_size);
  }

/************************************************************************/
/*			mvlu_gi_scan_done				*/
/* Same as "_mvlu_rpt_scan_done" except arg is (MVL_IND_PEND *)		*/
/* instead of (MVLU_RPT_SCAN_CTRL *).					*/
/*									*/
/* This function is called when the gi 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.							*/
/************************************************************************/

ST_RET mvlu_gi_scan_done (MVL_IND_PEND *indCtrl)
  {
ST_RET retCode;
MVLU_RPT_CLIENT *rptClient;
ST_INT j;

  /* Get "rptClient", saved in "indCtrl" when scan initialized.	*/
  rptClient = (MVLU_RPT_CLIENT *) indCtrl->usr_ind_ctrl;

  /* 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_GI;

  /* Assume this is a BRCB or URCB. Should never get here otherwise.	*/

  /* Build the report.	*/
  retCode = mvlu_rpt_ready (rptClient, MVLU_RPT_TYPE_INTEGRITY_OR_GI);

  if (retCode != SD_SUCCESS)
    MVLU_LOG_FLOW1 ("GI report send failed: err=0x%X", retCode);

  mvlu_integrity_scan_destroy (indCtrl);	/* destroy temporary struct*/
  return (retCode);
  }

/************************************************************************/
/*			mvl61850_urcb_rpt_send				*/
/* Set up all info needed to build an Information Report for a URCB,	*/
/* then call "mvl_info_variables" to encode/send it.			*/
/************************************************************************/
ST_RET mvl61850_urcb_rpt_send (MVLU_RPT_CTRL *rptCtrl,
			MVLU_RPT_CLIENT *rptClient,
			ST_INT rpt_type)
  {
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 cur_va_index, next_va_index;	/* indices into dsNvl va array	*/
ST_BOOLEAN segNeeded;		/* If TRUE, segmenting needed	*/
ST_UINT16 SubSeqNum = 0;
ST_BOOLEAN MoreSegmentsFollow;
ST_CHAR *tmp_dataref_buf;
ST_CHAR tmpRptID [MVL61850_MAX_RPTID_LEN+1];
ST_INT *asn1_len_array;	/* array of ASN.1 lengths, one for each var (allocated)*/

  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 URCB Report, MVL_NET_INFO %08lx", rptClient->netInfo);

  /* Need tmp va's for options, array of data_refs, array of reasons_data.*/
  tmp_va_arr_size = MVLU_MAX_RPT_OPTS + (dsNvl->num_of_entries * 2);
  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);

  asn1_len_array = M_CALLOC (MSMEM_GEN, rptCtrl->dsNvl->num_of_entries, sizeof(ST_INT));

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

  /* Call user function to get report time. Called here before loop	*/
  /* so that all segmented reports get the same time stamp.		*/
  u_mvlu_rpt_time_get (&basrcb->TimeofEntry);

  /* Fill in "asn1_len_array", needed later by "chk_seg_needed".	*/
  if (fill_asn1_len_array (rptClient, rptCtrl->dsNvl, asn1_len_array)!=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 Unbuffered Report.");
    }

  /* If this is Integrity (or GI) report, just set all inclusion bits.*/
  if (rpt_type == MVLU_RPT_TYPE_INTEGRITY_OR_GI)
    memset (rptClient->changed_flags, 0xff, incSize);

  for (cur_va_index = 0, next_va_index = 0;
       next_va_index < rptCtrl->dsNvl->num_of_entries;
       cur_va_index = next_va_index, SubSeqNum++)
    {	/* BEGIN MAIN LOOP	*/
    tmp_va = tmp_va_arr;	/* start loop pointing to first entry	*/
    sendIndex = 0;		/* start with first rptNvl->entries.	*/

    /* Prepares a UCA Report NVL to be sent, based on the		*/
    /* options, data, and inclusion bitstring in the rptCtrl. After this*/
    /* function completes, the rptCtrl->rptNvl is ready to be sent.	*/

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


/*		
		printf("urcb:segNeeded=%d num=%d va_index = %d->%d\r\n",
		 segNeeded,rptCtrl->dsNvl->num_of_entries,cur_va_index, next_va_index);
*/

    if (next_va_index - 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.	*/

    /* Add RptID to Report.						*/
    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*/

    /* Add OptFlds to Report.						*/
    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.	*/

    /* The following optional vars are controlled by the same OptFlds bits,
     * are of the same type, and are in the same order as in UCA, so that
     * UCA clients can decode reports if only these options are used.
     */

    if (BSTR_BIT_GET(optFlds, OPTFLD_BITNUM_SQNUM))
      {
      /* NOTE: 61850-7-2 says SqNum in Report is INT16U, but we encode
       *       INT8U from URCB. ASN.1 encodes same either way, so client
       *       may decode as INT16U if needed.
       */
      tmp_va->type_id = rptCtrl->rpt_typeids.int8u;
      tmp_va->data = &basrcb->SqNum;
      rptNvl->entries[sendIndex++] = tmp_va++;	/* set entry & point to next*/
      MVLU_LOG_CFLOW1 ("    SqNum : %u", (unsigned) basrcb->SqNum);
      }
    if (BSTR_BIT_GET(optFlds, OPTFLD_BITNUM_TIMESTAMP))
      {
      MVLU_LOG_CFLOW2 ("    RptTim : %lums, %lu days",
              basrcb->TimeofEntry.ms, basrcb->TimeofEntry.day);

      tmp_va->type_id = rptCtrl->rpt_typeids.btime6;
      tmp_va->data = &basrcb->TimeofEntry;
      rptNvl->entries[sendIndex++] = tmp_va++;	/* set entry & point to next*/
      }
    if (BSTR_BIT_GET(optFlds, OPTFLD_BITNUM_DATSETNAME))
      {
      /* UCA called it OutDat instead of DataSetName. Value is same.	*/
      MVLU_LOG_CFLOW1 ("    DataSetName : %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*/
      }

    /* The following optional vars are NOT supported by UCA. The OptFlds bits
     * must never be set by UCA clients, and the corresponding data
     * must NOT be sent to UCA clients.
     */

    /* For URCB, ignore BUFOVFL bit in OptFlds.	*/
    /* For URCB, ignore ENTRYID bit in OptFlds.	*/

    /* 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)
      {
      /* 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 = &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);
      }

    /* Add inclusion bitstring.	*/
    tmp_va->type_id = rptCtrl->inclusion_typeid;
    if (segNeeded)
      {
      /* just set the bits included in this segment.	*/
      memset (rptClient->segmented_inclusion, 0, incSize);	/* start clean	*/
      for (j = cur_va_index; j < next_va_index; ++j)
        {
        if (BSTR_BIT_GET (rptClient->changed_flags, j))
          BSTR_BIT_SET_ON (rptClient->segmented_inclusion, j);
        }
      tmp_va->data = rptClient->segmented_inclusion;
      }
    else
      tmp_va->data = rptClient->changed_flags;
    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 = cur_va_index; j < next_va_index; ++j)
        {
        if (BSTR_BIT_GET (rptClient->changed_flags, j))
          {
          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 = cur_va_index; j < next_va_index; ++j)
      {
      if (BSTR_BIT_GET (rptClient->changed_flags, j))
        {
        va = dsNvl->entries[j];
        MVLU_LOG_CFLOW3 ("  Including variable %d ('%s'), reason=0x%02X",
                        j, va->name, rptClient->reasons_data[j]);
        rptNvl->entries[sendIndex++] = va;
        /* va->data already points to latest data. No need to copy data.*/
        }
      }
    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 = cur_va_index; j < next_va_index; ++j)
        {
        if (BSTR_BIT_GET (rptClient->changed_flags, j))
          {
          /* Don't need BVSTR here, because size is fixed.	*/
          tmp_va->type_id = rptCtrl->rpt_typeids.bstr6;
          tmp_va->data = &rptClient->reasons_data[j];
          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,	/* URCB: encode & send*/
                        &rptClient->rpt_ctrl->rptNvl,
                        SD_FALSE);	/* FALSE means "send as NVL"	*/
    /* If send fails, just log it. Try to send other segments anyway.*/
    if (retCode)
      MVL_LOG_ERR1 ("Sending IEC-61850 Unbuffered Report failed: err=0x%X", retCode);
    }	/* END MAIN LOOP	*/

  /* Increment seq number for next rpt.	*/
  basrcb->SqNum++;		/* used only for URCB	*/

  /* Reset reasons, etc. to prepare for new report triggers.	*/
  mvl61850_rcb_cleanup (rptClient);	/* prepare for next RPT.	*/
  if (retCode != SD_SUCCESS)
    MVLU_LOG_FLOW1 ("Report Build failed: err=0x%X", retCode);
  M_FREE (MSMEM_GEN, tmp_va_arr);
  M_FREE (MSMEM_GEN, tmp_dataref_buf);
  M_FREE (MSMEM_GEN, asn1_len_array);
  return (retCode);
  }

/******
 *			chk_seg_needed
 * Check if segmenting is needed.
 * Set (*next_va_index) = first va to send in "next" report.
 * RETURN: SD_FALSE if segmenting NOT needed.
 *         SD_TRUE if segmenting IS needed.
 */
ST_BOOLEAN chk_seg_needed (MVLU_RPT_CLIENT *rptClient,
			ST_INT *asn1_len_array,
			ST_INT cur_va_index,	/* input arg	*/
			ST_INT *next_va_index)	/* output arg	*/
  {
ST_INT negotiatedPduSize;
ST_INT unusedSize;
ST_INT num_entries;
ST_INT loopIndex;
ST_BOOLEAN segNeeded;

  negotiatedPduSize = rptClient->netInfo->max_pdu_size;

/*negotiatedPduSize = 2*1024;*/

  num_entries = rptClient->rpt_ctrl->dsNvl->num_of_entries;

  /* Subtract overhead from max "negotiated" PDU size.	*/
  /* Very verbose, but most data are constants so compiler should simplify.*/
  unusedSize = negotiatedPduSize
		- 32	/* InfoRpt overhead (ROUGH estimate)	*/
		- 131	/* RptID	(Vstring129)	*/
		- 5	/* OptFlds	(Bstring10)	*/
		- 4	/* SqNum	(Int16)		*/
		- 131	/* DataSetNam	(Vstring129)	*/
		- 3	/* BufOvfl	(Boolean)	*/
		- 8	/* TimeOfEntry	(Btime6)	*/
		- 4	/* SubSeqNum	(Int16)		*/
		- 3	/* MoreSegFoll	(Boolean)	*/
		-10	/* EntryID	(Ostring8)	*/
		-(3+num_entries/8)	/* Inclusion Bitstring	*/
		;

  /* In a loop, check if each var fits in the Report.	*/
  for (loopIndex = cur_va_index; loopIndex < num_entries; ++loopIndex)
    {
    /* Subtract space needed for this var.	*/
    /* If len==0, this var must not be included in the report, so skip it.*/
    if (asn1_len_array[loopIndex])
      {	/* this var should be included in rpt. Check for space.	*/
      unusedSize -= 131;		/* DataRef	(Vstring129)	*/
      unusedSize -= 4;		/* ReasonForInclusion	(Bstring4?)	*/
      unusedSize -= asn1_len_array[loopIndex];	/* Data		*/
      }
    if (unusedSize < 0)
      break;	/* cannot fit this va. Break out of loop.	*/
    }
  /* When loop ends, "loopIndex" is index to first variable of "next" report.*/

  *next_va_index = loopIndex;
  if (cur_va_index == 0 && (*next_va_index) == num_entries)
    segNeeded = SD_FALSE;	/* All fit in one PDU (no segmenting)	*/
  else
    segNeeded = SD_TRUE;
  return (segNeeded);
  }

/************************************************************************/
/*			mvlu_confrev_rd_ind				*/
/* NOTE: confrev write funct should be something like u_no_write_allowed*/
/************************************************************************/
ST_VOID mvlu_confrev_rd_ind (MVLU_RD_VA_CTRL *mvluRdVaCtrl)
  {
ST_UINT32 *dest;
MVLU_BASRCB *rcb;
ST_RET rc;

  rc = SD_FAILURE;
  /* Only for 61850, so use mvl61850_get_rcb to find the RCB.	*/
  rcb = mvl61850_get_rcb (mvluRdVaCtrl->rdVaCtrl->va->base_va,
	    mvluRdVaCtrl->rt,
            NULL);
  if (rcb != NULL)
    {
    dest = (ST_UINT32 *) mvluRdVaCtrl->primData;
    *dest = rcb->ConfRev;
    rc = SD_SUCCESS;
    }
  mvlu_rd_prim_done (mvluRdVaCtrl, rc);
  }

/************************************************************************/
/*			mvlu_purgebuf_rd_ind				*/
/************************************************************************/
ST_VOID mvlu_purgebuf_rd_ind (MVLU_RD_VA_CTRL *mvluRdVaCtrl)
  {
ST_BOOLEAN *dest;
MVLU_BASRCB *rcb;
ST_RET rc;

  rc = SD_FAILURE;
  /* Only for 61850, so use mvl61850_get_rcb to find the RCB.	*/
  rcb = mvl61850_get_rcb (mvluRdVaCtrl->rdVaCtrl->va->base_va,
	    mvluRdVaCtrl->rt,
            NULL);
  if (rcb != NULL)
    {
    dest = (ST_BOOLEAN *) mvluRdVaCtrl->primData;
    *dest = 0;		/* no data stored. ALWAYS return 0	*/
    rc = SD_SUCCESS;
    }
  mvlu_rd_prim_done (mvluRdVaCtrl, rc);
  }

/************************************************************************/
/*			mvlu_gi_rd_ind					*/
/************************************************************************/
ST_VOID mvlu_gi_rd_ind (MVLU_RD_VA_CTRL *mvluRdVaCtrl)
  {
ST_BOOLEAN *dest;
MVLU_BASRCB *rcb;
ST_RET rc;

  rc = SD_FAILURE;
  /* Only for 61850, so use mvl61850_get_rcb to find the RCB.	*/
  rcb = mvl61850_get_rcb (mvluRdVaCtrl->rdVaCtrl->va->base_va,
	    mvluRdVaCtrl->rt,
            NULL);
  if (rcb != NULL)
    {
    dest = (ST_BOOLEAN *) mvluRdVaCtrl->primData;
    *dest = 0;		/* no data stored. ALWAYS return 0	*/
    rc = SD_SUCCESS;
    }
  mvlu_rd_prim_done (mvluRdVaCtrl, rc);
  }


/************************************************************************/
/*			mvlu_entryid_rd_ind				*/
/************************************************************************/
ST_VOID mvlu_entryid_rd_ind (MVLU_RD_VA_CTRL *mvluRdVaCtrl)
  {
MVLU_BASRCB *rcb;
ST_RET rc;
MVLU_RPT_CLIENT *rptClient;	/* can access all rpt info from this struct.*/
BUFLIST_ENTRY *entry;

  rc = SD_FAILURE;

  /* Only for 61850, so use mvl61850_get_rcb to find the RCB.	*/
  rcb = mvl61850_get_rcb (mvluRdVaCtrl->rdVaCtrl->va->base_va,
	    mvluRdVaCtrl->rt,
            &rptClient);
  if (rcb != NULL)
    {
#if 1	/* NOTE: Ed2 says to return different value if RptEna=FALSE or TRUE.*/
    if (rptClient->basrcb.RptEna==0)
      {
      entry = (BUFLIST_ENTRY *) list_find_last ((DBL_LNK *) rptClient->rpt_ctrl->brcbCtrl.rpt_list);
      if (entry == NULL)	/* empty list, return all 0	*/
        memset (mvluRdVaCtrl->primData, 0, 8);
      else
        memcpy (mvluRdVaCtrl->primData, entry->EntryID, 8);
      }
    else
      memcpy (mvluRdVaCtrl->primData, rptClient->rpt_ctrl->brcbCtrl.lastSentEntryID, 8);
#else	/* old way (before Ed2)	*/
    memcpy (mvluRdVaCtrl->primData, rptClient->rpt_ctrl->brcbCtrl.lastSentEntryID, 8);
#endif
    rc = SD_SUCCESS;
    }
  mvlu_rd_prim_done (mvluRdVaCtrl, rc);
  }

/************************************************************************/
/*			mvlu_timeofentry_rd_ind				*/
/************************************************************************/

ST_VOID mvlu_timeofentry_rd_ind (MVLU_RD_VA_CTRL *mvluRdVaCtrl)
  {
MVLU_BASRCB *rcb;
ST_RET rc;
MVLU_RPT_CLIENT *rptClient;	/* can access all rpt info from this struct.*/
BUFLIST_ENTRY *entry;

  rc = SD_FAILURE;
  /* Only for 61850, so use mvl61850_get_rcb to find the RCB.	*/
  rcb = mvl61850_get_rcb (mvluRdVaCtrl->rdVaCtrl->va->base_va,
	    mvluRdVaCtrl->rt,
            &rptClient);
  if (rcb != NULL)
    {
#if 1	/* NOTE: Ed2 says to return different value if RptEna=FALSE or TRUE.*/
    if (rptClient->basrcb.RptEna==0)
      {
      entry = (BUFLIST_ENTRY *) list_find_last ((DBL_LNK *) rptClient->rpt_ctrl->brcbCtrl.rpt_list);
      if (entry == NULL)	/* empty list	*/
        {	/* Empty buffer. Return all 0.	*/
        MMS_BTIME6 *btime = (MMS_BTIME6 *)mvluRdVaCtrl->primData;
        btime->ms = 0;
        btime->day = 0;
        }
      else
        memcpy (mvluRdVaCtrl->primData, &entry->TimeOfEntry, sizeof (MMS_BTIME6));
      }
    else
      memcpy (mvluRdVaCtrl->primData, &rptClient->rpt_ctrl->brcbCtrl.lastSentTimeOfEntry, sizeof (MMS_BTIME6));
#else	/* old way (before Ed2)	*/
    memcpy (mvluRdVaCtrl->primData, &rcb->TimeofEntry, sizeof (MMS_BTIME6));
#endif
    rc = SD_SUCCESS;
    }
  mvlu_rd_prim_done (mvluRdVaCtrl, rc);
  }

/************************************************************************/
/*                       mvlu_purgebuf_wr_ind				*/
/************************************************************************/
ST_VOID mvlu_purgebuf_wr_ind (MVLU_WR_VA_CTRL *mvluWrVaCtrl)
  {
ST_BOOLEAN newval;	/* val to write to RptEna	*/
MVLU_BASRCB *rcb;
MVLU_RPT_CLIENT *rptClient;
ST_RET rc;

  rc = SD_FAILURE;
  /* Only for 61850, so use mvl61850_get_rcb to find the RCB.	*/
  rcb = mvl61850_get_rcb (mvluWrVaCtrl->wrVaCtrl->va->base_va,
	    mvluWrVaCtrl->rt,
            &rptClient);
  if (_rcb_writable (rcb, rptClient, mvluWrVaCtrl))
    {					/* rcb found and NOT enabled	*/
    rc = SD_SUCCESS;			/* allow write		*/
    newval = *(ST_BOOLEAN *) mvluWrVaCtrl->primData;

    /* If the client is enabling the Purge, dump rpts_sent & rpts_queued */
    if (newval != 0)	/* any non-zero val means "enable"	*/
      {
      /* this function does just what we want.		*/
      mvl61850_brcb_rpt_lists_clean (&rptClient->rpt_ctrl->brcbCtrl);
      /*renxiaobao 61850 第二版修改*/
      memset(rcb->EntryID,0,8);
      }

    /* NOTE: don't actually change any value because PurgeBuf is	*/
    /* automatically set back to 0 when Purge is complete.		*/
    }
  else
    mvluWrVaCtrl->wrVaCtrl->failure = ARE_TEMP_UNAVAIL;
  mvlu_wr_prim_done (mvluWrVaCtrl, rc);
  }

/************************************************************************/
/*                       mvlu_gi_wr_ind					*/
/************************************************************************/
ST_VOID mvlu_gi_wr_ind (MVLU_WR_VA_CTRL *mvluWrVaCtrl)
  {
ST_BOOLEAN newval;	/* val to write to RptEna	*/
MVLU_BASRCB *rcb;
MVLU_RPT_CLIENT *rptClient;
ST_RET rc;

  rc = SD_FAILURE;
  /* Only for 61850, so use mvl61850_get_rcb to find the RCB.	*/
  rcb = mvl61850_get_rcb (mvluWrVaCtrl->wrVaCtrl->va->base_va,
	    mvluWrVaCtrl->rt,
            &rptClient);

  /* Check all condtions. If all conditions right, set rc=SD_SUCCESS to allow write.*/
  if (rcb != NULL)
    {
    /* RCB must be enabled.	*/
    /* NOTE: this is only RCB attribute that is writable when RCB enabled.*/
    if (rptClient->basrcb.RptEna)
      {
      if (rptClient->rpt_ctrl->rcb_type == RCB_TYPE_IEC_URCB)
        {
        /* URCB must be reserved by this clint. RCB is enabled, so it	*/
        /* must already be reserved. Just make sure reserved by this client.*/
        if (rptClient->netInfo == mvluWrVaCtrl->indCtrl->event->net_info)
          rc = SD_SUCCESS;			/* allow write		*/
        else
          MVLU_LOG_FLOW1 ("Write of GI for RptID='%s' FAILED because RCB reserved by another client",
              rcb->RptID);
        }
      else					/* RCB_TYPE_IEC_BRCB	*/
        rc = SD_SUCCESS;			/* allow write		*/
      }
    else
      MVLU_LOG_FLOW1 ("Write of GI for RptID='%s' FAILED because RCB not enabled",
           rcb->RptID);
    }

  if (rc == SD_SUCCESS)
    {
    newval = *(ST_BOOLEAN *) mvluWrVaCtrl->primData;

    /* If client is enabling the GI & TrgOps.GI=TRUE, start GI scan.*/
    if (newval != 0	/* any non-zero val means "enable"	*/
        && BSTR_BIT_GET(rcb->TrgOps.data, TRGOPS_BITNUM_GENERAL_INTERROGATION) != 0)
      mvlu_integrity_scan_read (rptClient,
                mvlu_gi_scan_va_done,
                mvlu_gi_scan_done); /* begin GI scan*/
	else if(newval != 0)  rc = SD_FAILURE;     /*renxiaobao add 20180731*/

    /* NOTE: don't actually change any value because GI is automatically*/
    /* set back to 0 when GI is complete.				*/
    }
  else		/* for any error, return TEMP_UNAVAIL	*/
    mvluWrVaCtrl->wrVaCtrl->failure = ARE_TEMP_UNAVAIL;
  mvlu_wr_prim_done (mvluWrVaCtrl, rc);
  }

/************************************************************************/
/*                      mvlu_entryid_wr_ind				*/
/************************************************************************/
ST_VOID mvlu_entryid_wr_ind (MVLU_WR_VA_CTRL *mvluWrVaCtrl)
  {
MVLU_BASRCB *rcb;
MVLU_RPT_CLIENT *rptClient;
ST_RET rc;

  rc = SD_FAILURE;
  /* Only for 61850, so use mvl61850_get_rcb to find the RCB.	*/
  rcb = mvl61850_get_rcb (mvluWrVaCtrl->wrVaCtrl->va->base_va,
	    mvluWrVaCtrl->rt,
            &rptClient);
  if (_rcb_writable (rcb, rptClient, mvluWrVaCtrl))
    {
    /* NOTE: primData points to EntryID as Ostring8.	*/
    rc = mvl61850_brcb_rpt_set_entryid (rptClient, (ST_UCHAR *) mvluWrVaCtrl->primData);
    }
  else
    mvluWrVaCtrl->wrVaCtrl->failure = ARE_TEMP_UNAVAIL;
  mvlu_wr_prim_done (mvluWrVaCtrl, rc);
  }

/************************************************************************/
/*			mvlu_sqnum_int16u_rd_ind_fun			*/
/* Same as mvlu_sqnum_rd_ind_fun except uses SqNumInt16u		*/
/* (UINT16 instead of UINT8).						*/
/************************************************************************/

ST_VOID mvlu_sqnum_int16u_rd_ind_fun (MVLU_RD_VA_CTRL *mvluRdVaCtrl)
  {
ST_UINT16 *dest;
MVLU_BASRCB *rcb;
ST_RET rc;

  rc = SD_FAILURE;
  /* Only for 61850, so use mvl61850_get_rcb to find the RCB.	*/
  rcb = mvl61850_get_rcb (mvluRdVaCtrl->rdVaCtrl->va->base_va,
	    mvluRdVaCtrl->rt,
            NULL);
  if (rcb != NULL)
    {
    dest = (ST_UINT16 *) mvluRdVaCtrl->primData;
    *dest = rcb->SqNumInt16u;
    rc = SD_SUCCESS;
    }
  mvlu_rd_prim_done (mvluRdVaCtrl, rc);
  }

/************************************************************************/
/*			mvl61850_beh_stval_rd_ind			*/
/************************************************************************/
ST_VOID mvl61850_beh_stval_rd_ind (MVLU_RD_VA_CTRL *mvluRdVaCtrl)
  {
MVL_VAR_ASSOC *base_var;
MVL_VAR_ASSOC *lln0_var;
ST_RET retcode = SD_SUCCESS;
ST_INT32 mod_stval;	/* ST$Mod$stVal from this LN	*/
ST_INT32 lln0mod_stval;	/* ST$Mod$stVal from LLN0	*/
ST_INT32 beh_stval;	/* ST$Beh$stVal from this LN (computed)	*/
MVL_DOM_CTRL *dom;
ST_INT j;

  base_var = mvluRdVaCtrl->rdVaCtrl->va->base_va;

  /* Get value of "ST$Mod$stVal" in this LN.	*/
  retcode = mvlu_get_leaf_val_int_any (base_var, "ST$Mod$stVal", &mod_stval);

  if (retcode == SD_SUCCESS)
    {
    /* Get value of "ST$Mod$stVal" in LLN0 of the same domain.	*/
    lln0_var = NULL;	/* assume it's not found	*/
    assert (mvluRdVaCtrl->rdVaCtrl->va_scope.scope == DOM_SPEC);
    dom = mvluRdVaCtrl->rdVaCtrl->va_scope.dom;
    for (j = 0; j < dom->num_var_assoc; j++)
      {
      /* Use "strstr" because name may have prefix and suffix*/
      if (strstr (dom->var_assoc_tbl[j]->name, "LLN0") != NULL)
        {
        lln0_var = dom->var_assoc_tbl[j];
        break;	/* found it. stop looping	*/
        }
      }
    if (lln0_var)
      retcode = mvlu_get_leaf_val_int_any (lln0_var, "ST$Mod$stVal", &lln0mod_stval);
    else
      retcode = SD_FAILURE;
    }

  if (retcode == SD_SUCCESS)
    {	/* both successful. combine results.	*/
    switch (lln0mod_stval)
      {
      case 1:
        if (mod_stval > 0 && mod_stval <= 5)
          beh_stval = mod_stval;
        else
          beh_stval = 0;	/* Mod is illegal, so set Beh to illegal value too*/
        break;

      case 2:
        switch (mod_stval)
          {
          case 1:
          case 2:
            beh_stval = 2; break;
          case 3:
          case 4:
            beh_stval = 4; break;
          case 5:
            beh_stval = 5; break;
          default:
            beh_stval = 0; break;	/* Mod is illegal, so set Beh to illegal value too*/
          }
        break;

      case 3:
        switch (mod_stval)
          {
          case 1:
            beh_stval = 3; break;
          case 2:
            beh_stval = 4; break;
          case 3:
            beh_stval = 3; break;
          case 4:
            beh_stval = 4; break;
          case 5:
            beh_stval = 5; break;
          default:
            beh_stval = 0; break;	/* Mod is illegal, so set Beh to illegal value too*/
          }
        break;

      case 4:
        switch (mod_stval)
          {
          case 1:
          case 2:
          case 3:
          case 4:
            beh_stval = 4; break;
          case 5:
            beh_stval = 5; break;
          default:
            beh_stval = 0; break;	/* Mod is illegal, so set Beh to illegal value too*/
          }
        break;

      case 5:
        if (mod_stval > 0 && mod_stval <= 5)
          beh_stval = 5;
        else
          beh_stval = 0;	/* Mod is illegal, so set Beh to illegal value too*/
        break;

      default:
        beh_stval = 0;	/* LLNO Mod is illegal, so set Beh to illegal value too*/
        break;
      }	/* end outer switch	*/
    }

  if (retcode == SD_SUCCESS)
    {
    /* Convert data to configured type (INT8, INT16, or INT32).		*/
    if (mvluRdVaCtrl->rt->u.p.el_len == 1)
      *(ST_INT8 *) mvluRdVaCtrl->primData = (ST_INT8) beh_stval;	/* copy data	*/
    else if (mvluRdVaCtrl->rt->u.p.el_len == 2)
      *(ST_INT16 *) mvluRdVaCtrl->primData = (ST_INT16) beh_stval;	/* copy data	*/
    else if (mvluRdVaCtrl->rt->u.p.el_len == 4)
      *(ST_INT32 *) mvluRdVaCtrl->primData = beh_stval;	/* copy data	*/
    else
      retcode = SD_FAILURE;	/* unsupported len. Should never happen.*/
    }
  mvlu_rd_prim_done (mvluRdVaCtrl, retcode);
  }

/************************************************************************/
/*			mvl61850_datset_wr_ind				*/
/* Try to change IEC 61850 Report Dataset.				*/
/* NOTE: for IEC 61850 only (not for UCA).				*/
/************************************************************************/
ST_VOID mvl61850_datset_wr_ind (MVLU_WR_VA_CTRL *mvluWrVaCtrl)
  {
MVLU_BASRCB *rcb;
ST_RET retCode;
ST_CHAR tmpbuf [MVL61850_MAX_OBJREF_LEN+1];
OBJECT_NAME nvl_oname;
MVLU_RPT_CLIENT *rptClient;
MVL_NVLIST_CTRL *nvl;
ST_BOOLEAN do_purge = SD_FALSE;	/* Chg to SD_TRUE if Purge Buffer required*/
ST_INT16 failure = ARE_TEMP_UNAVAIL;	/* use on err if no other value set*/

  retCode = SD_FAILURE;
  /* Only for 61850, so use mvl61850_get_rcb to find the RCB.	*/
  rcb = mvl61850_get_rcb (mvluWrVaCtrl->wrVaCtrl->va->base_va,
	    mvluWrVaCtrl->rt,
            &rptClient);
  if (_rcb_writable (rcb, rptClient, mvluWrVaCtrl))
    {
    if (strcmp (rcb->DatSetNa, mvluWrVaCtrl->primData) == 0)
      {
      retCode = SD_SUCCESS;	/* Same val written. Nothing to do but allow it*/
      }
    else if (mvluWrVaCtrl->primData[0] == '\0')
      {	/* client wrote empty string, and it was NOT already empty	*/
      mvl61850_rpt_dataset_destroy (rptClient->rpt_ctrl);
      rcb->ConfRev++;	/* DatSet changed. Must increment ConfRev.	*/
      retCode = SD_SUCCESS;
      }
    else if (strlen ((ST_CHAR *) mvluWrVaCtrl->primData) > MVL61850_MAX_OBJREF_LEN)
      {
      failure = ARE_OBJ_VALUE_INVALID;
      }
    else
      {
      /* strtok modifies input buffer, so copy to local buffer first.	*/
      strcpy (tmpbuf, (ST_CHAR *) mvluWrVaCtrl->primData);
      if (tmpbuf[0] == '@')
        {	/* AA-Specific Dataset	*/
        nvl_oname.obj_name.vmd_spec = &tmpbuf[1];	/*extract NVL name*/
        nvl_oname.object_tag = AA_SPEC;
        }
      else if (tmpbuf[0] == '/')
        {	/* VMD-Specific Dataset	*/
        nvl_oname.obj_name.vmd_spec = &tmpbuf[1];	/*extract NVL name*/
        nvl_oname.object_tag = VMD_SPEC;
        }
      else
        {	/* must be Domain-Specific Dataset	*/
        nvl_oname.domain_id = strtok (tmpbuf, "/");	/*extract dom name*/
        nvl_oname.obj_name.vmd_spec = strtok (NULL, "");	/*extract NVL name*/
        nvl_oname.object_tag = DOM_SPEC;
        }

      /* If DatSet name is illegal, fail.*/
      if (nvl_oname.obj_name.vmd_spec == NULL)	/* strtok failed*/
        {
        MVL_LOG_ERR1 ("DatSet name '%s' is invalid. Cannot write it to RCB.",
            (ST_CHAR *) mvluWrVaCtrl->primData);
        failure = ARE_OBJ_VALUE_INVALID;
        }
      else if (nvl_oname.object_tag == AA_SPEC &&
               rptClient->rpt_ctrl->rcb_type == RCB_TYPE_IEC_BRCB)
        {
        MVL_LOG_ERR1 ("Writing AA-Specific DatSet '%s' to BRCB is NEVER allowed.",
            (ST_CHAR *) mvluWrVaCtrl->primData);
        failure = ARE_OBJ_VALUE_INVALID;
        }
      else if (nvl_oname.object_tag == AA_SPEC &&
               rptClient->rpt_ctrl->rcb_type == RCB_TYPE_IEC_URCB &&
               rptClient->basrcb.Resv == 0)
        {
        MVL_LOG_ERR1 ("Writing AA-Specific DatSet '%s' to URCB not allowed because URCB is not reserved.",
            (ST_CHAR *) mvluWrVaCtrl->primData);
        failure = ARE_OBJ_VALUE_INVALID;
        }
      else
        {
        nvl = mvl_vmd_find_nvl (&mvl_vmd, &nvl_oname,
                  mvluWrVaCtrl->indCtrl->event->net_info);	/* for AA_SPEC*/
        if (nvl)
          {
          if (rptClient->rpt_ctrl->rcb_type == RCB_TYPE_IEC_BRCB)
            do_purge = SD_TRUE;	/* BRCB & val changed, must purge buffer*/
          /* Replace "dataset".	*/
          /* NOTE: If dataset was empty, destroy function does nothing.	*/
          mvl61850_rpt_dataset_destroy (rptClient->rpt_ctrl);
          retCode = mvl61850_rpt_dataset_create (rptClient->rpt_ctrl, nvl);
          /* WARNING: if create fails, old dataset is lost.*/
          if (retCode)
            {
            MVL_LOG_ERR1 ("DatSet change failed. RCB DatSet '%s' is no longer valid.", rcb->DatSetNa);
            /* Old DatSet is no longer valid, so clear it.*/
            rcb->DatSetNa[0] = '\0';
            }
          /* Even if create failed, DatSet changed (to NULL) so inc ConfRev.*/
          rcb->ConfRev++;	/* DatSet changed. Must increment ConfRev.*/
          /*renxiaobao 61850 第二版修改*/
          memset(rcb->EntryID,0,8);
          }
        }
      }
    }

  if (do_purge)
    mvl61850_brcb_rpt_lists_clean (&rptClient->rpt_ctrl->brcbCtrl);

  if (retCode != SD_SUCCESS)
    mvluWrVaCtrl->wrVaCtrl->failure = failure;

  mvlu_wr_prim_done (mvluWrVaCtrl, retCode);
  }

/************************************************************************/
/*			fill_asn1_len_array				*/
/* Compute the ASN.1 encoded len for each var in a rpt dataset.		*/
/************************************************************************/
static ST_RET fill_asn1_len_array (MVLU_RPT_CLIENT *rptClient,
	MVL_NVLIST_CTRL *dsNvl,	/* rpt dataset				*/
	ST_INT *asn1_len_array)	/* array of ASN.1 lengths, one for each var*/
				/* array filled in by this function	*/
  {
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	*/

  /* Alloc tmp ASN.1 buffer. Use it for each encode in this loop.	*/
  tmp_asn1_buf = chk_malloc (tmp_asn1_buf_size);
  for (j = 0; j < dsNvl->num_of_entries; ++j)
    {
    if (rptClient->reasons_data[j])	/* 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,
              dsNvl->entries[j]->data,	/* CRITICAL: use data from var*/
              tmp_asn1_buf,
              tmp_asn1_buf_size,
              &asn1_ptr,	/* function sets asn1_ptr (not needed here)*/
              &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);
  }

/************************************************************************/
/*			mvl61850_objref_create				*/
/* Create the ObjectReference for this Object.				*/
/* objRef must point to buffer of at least [MVL61850_MAX_OBJREF_LEN+1] char*/
/************************************************************************/
ST_RET mvl61850_objref_create (ST_CHAR *objName,
	MVL_SCOPE *objScope, 
	ST_CHAR *objRef)	/* ptr to ObjectReference		*/
  {
  switch (objScope->scope)
    {
    case VMD_SPEC: 
      strcpy (objRef, "/");
    break;

    case DOM_SPEC: 
      strcpy (objRef, objScope->dom->name);
      strcat (objRef, "/");
    break;

    case AA_SPEC:  
      strcpy (objRef,"@/");
    break;
    }

  /* Scope in place, now append the name of the Object.		*/
  if (strlen(objRef) + strlen(objName) > MVL61850_MAX_OBJREF_LEN)
    {	/* should NEVER happen	*/
    MVL_LOG_ERR0 ("Can't generate ObjectReference (too long)");
    objRef [0] = '\0';	/* return empty string	*/
    return (SD_FAILURE);
    }
  strcat (objRef, objName);
  return (SD_SUCCESS);
  }

