/************************************************************************/   
/* SISCO SOFTWARE MODULE HEADER *****************************************/   
/************************************************************************/   
/*   (c) Copyright Systems Integration Specialists Company, Inc.,   */   
/*                 1998-2005, All Rights Reserved.              */   
/*                                  */   
/*          PROPRIETARY AND CONFIDENTIAL            */   
/*                                  */   
/* MODULE NAME : mvl_uca.c                      */   
/* PRODUCT(S)  : MMSEASE                        */   
/*                                  */   
/* MODULE DESCRIPTION :                         */   
/*  Special read/write processing functions for UCA and     */   
/*  IEC 61850 objects.                      */   
/*                                  */   
/* NOTE: define MVL61850_CTL_DISABLE to avoid calling user functions    */   
/*  (u_mvl61850_ctl_oper_*) if 61850 Controls not needed.       */   
/*                                  */   
/* GLOBAL FUNCTIONS DEFINED IN THIS MODULE :                */   
/*                                  */   
/* MODIFICATION LOG :                           */   
/*  Date     Who   Rev          Comments            */   
/* --------  ---  ------   -------------------------------------------  */   
/* 08/05/29  LW      58    加入支持3层目录模型的服务器代码（加入代码后注释包括//lw）  
/* 03/07/07  JRB     57    Add mvlu_get_leaf_val_int_any.       */   
/* 11/21/06  JRB     56    Send LastApplError if write of Oper, etc fails.*/   
/* 10/30/06  JRB     55    Use new mvl_vmd_* object handling functions. */   
/*             mvlu_find_base_va: add args.         */   
/*             u_mvl_get_va_aa: add args.           */   
/*             u_gnl_ind_*: add net_info arg to     */   
/*             elim use of global var "_mvl_curr_net_info". */   
/*             u_mvl_get_va_aa: do not set va->usr_ind_ctrl  */   
/*              (done by calling functions).        */   
/* 10/26/06  JRB     54    Del unused local vars.           */   
/* 09/27/06  MDE     53    Added MMSOP_RDWR_USR_HANDLED for IOS     */   
/* 09/13/06  JRB     52    mvlu_find_comp_type: allow non-dynamic types.*/   
/* 08/09/06  JRB     51    Del u_mvl_get_nvl, u_mvl_free_nvl functions  */   
/*             (not needed with new improved Foundry).  */   
/*             Use "mvl_var_create/destroy" so all variables*/   
/*             created/destroyed in one place.      */   
/* 07/24/06  JRB     50    Chg some common ERR logs to FLOW.        */   
/* 03/27/06  JRB     49    Add more mvlu_get_leaf_* functions.      */   
/* 07/29/05  MDE     48    Fixed static data use for write handling */   
/* 07/11/05  JRB     47    Call user fcts (u_mvl61850_ctl_oper_*) if    */   
/*             !defined (MVL61850_CTL_DISABLE).     */   
/* 01/19/05  JRB     46    u_gnl_ind_* return (-1) on error.        */   
/* 12/09/04  JRB     45    init_prim_info_recursive: fix ARR_END handling.*/   
/*             init_prim_info_arr: simplify & ret ST_VOID.  */   
/*             Add mvlu_find_comp_type, mvlu_get_leaf_val*. */   
/*             Chg trim_branch_name to global mvlu_trim_..  */   
/*             & simplify it using strrchr.         */   
/* 09/20/04  JRB     44    startElWrites: if writing "Oper" or "Cancel" */   
/*             struct, check SBO state.         */   
/*             mvlu_wr_prim_done call mvlu_sbo_ctrl_free.   */   
/* 06/29/04  JRB     43    Del global var mvluUseStaticData, instead use*/   
/*             use_static_data flag in MVL_VAR_ASSOC for Read.*/   
/*             NEVER use static data for Write.     */   
/*             startElReads, etc: add prim_info arg.    */   
/*             Del elmntOffset, use prim_info->prim_offset.  */   
/* 11/24/03  JRB     42    getGnlVarNames: fix prefix len by using  */   
/*             MAX_IDENT_LEN, chk overflow BEFORE writing,  */   
/*             & add logging & asserts.         */   
/* 09/18/03  JRB     41    Allow alt acc on array of "nested" structures*/   
/*             Add some debug logging & extra comments. */   
/* 05/02/03  JRB     40    switch(rt->el_tag): Use default for most cases*/   
/* 04/04/03  JRB     39    Fix integrity/GI scan code so multiple   */   
/*             concurrent scans don't corrupt one another.  */   
/* 12/20/02  JRB     38    Moved mvlu_set_leaf_param to mvluleaf.c  */   
/* 12/12/02  JRB     37    Use usr_resp_fun ptr to call scan done funcs.*/   
/* 12/09/02  MDE     36    Made mvlu_find_uca_var global        */   
/* 11/27/02  MDE     35    Addded leaf indication handlers      */   
/*             Addded mvlu_find_rt_leaf         */   
/*             Addded mvlu_set_leaf_param           */   
/* 11/29/01  MDE     34    Added GOOSE function pointer         */   
/* 11/14/01  EJV     33    Added support for new MMS type UtcTime:      */   
/* 11/13/01  MDE     32    Added GOOSE scan support         */   
/* 05/21/01  MDE     31    Cleaned up memory allocation for SMEM    */   
/* 10/25/00  JRB     30    Del u_mvl & u_gnl funct ptrs. Call directly. */   
/*             Del mvlu_install (no longer needed).     */   
/* 08/18/00  JRB     29    Don't clear va_to_free. Need value later.    */   
/*             mvlu_free_nvl free va->va_to_free only if */   
/*             it was allocated by mvlu_get_nvl.        */   
/* 08/18/00  RKR     28    Added rt fields to MVLU_ typedefs        */   
/* 07/13/00  JRB     27    Cleanup ms_comp_na.. chg for MVL_XNAME.  */   
/* 07/13/00  JRB     26    Use new ms_comp_name_find to get comp names. */   
/* 07/13/00  JRB     25    Move these functs to mvl_type.c:     */   
/*               mvlu_add_rt_type, mvlu_free_rt_type.   */   
/* 06/21/00  MDE     24    Now copy base VA user_info to new VA     */   
/* 05/15/00  MDE     23    Now filder out too-long variable names   */   
/* 04/14/00  JRB     22    Lint cleanup.                */   
/* 04/05/00  RKR     21    Made MVL_XNAME a compile time option     */   
/* 04/03/00  RKR     20    Added the xName to UCA Rd and Wr Ind funs    */   
/* 03/30/00  RKR     19    Passed the expanded UCA var name to ind fun  */   
/* 01/21/00  MDE     18    Now use MEM_SMEM for dynamic memory      */   
/* 12/20/99  MDE     17    Fix getArrAARtType to return SUCCESS/FAIL    */   
/* 11/03/99  JRB     16    Fix GetNameList if CA name = base var name.  */    
/* 09/30/99  EJV     15    Added slog macro to mvlu_rd_prim_done    */   
/* 09/13/99  MDE     14    Added SD_CONST modifiers         */   
/* 09/07/99  MDE     13    Revised and enhanced the UCA report system   */   
/* 06/04/99  MDE     12    Now allow arrays as base VA type, other  */   
/*             minor changes to VA processing       */   
/* 06/04/99  MDE     11    Fixed memory leak for nested array alt acc   */   
/* 04/07/99  MDE     10    Logging improvements (fixed wrong AA log too)*/   
/* 03/09/99  JRB     12    Fix illegal free of gnlNameBuf.      */   
/* 02/22/99  JRB     11    BUG FIX: Always start with clean "arrCtrl".  */   
/* 01/08/99  JRB     10    Use new "bsearch" object model. Don't use    */   
/*             "_UCA_" prefix on va and nvl names.      */   
/* 12/11/98  MDE     09    Removed scope references from VA     */   
/* 11/17/98  MDE     08    Made mvlu_get_va_aa alloc space for name */   
/* 11/16/98  MDE     07    Renamed internal functions (prefix '_')  */   
/* 09/21/98  MDE     06    Uninitialized ptr fix, Minor lint cleanup    */   
/* 08/11/98  MDE     05    Added UCA variable array support     */   
/* 07/13/98  MDE     04    Mixed scope NVL fixes, data alignment fix    */   
/* 06/29/98  MDE     03    Added report function pointers       */   
/* 06/15/98  MDE     02    Changes to allow compile under C++       */   
/* 01/02/98  MDE     01    New                      */   
/************************************************************************/   
   
#include "glbtypes.h"   
#include "sysincs.h"   
#include "glbsem.h"   
#include "mmsdefs.h"   
#include "mms_pvar.h"   
#include "mms_vvar.h"   
#include "mvl_uca.h"   
#include "mvl_log.h"   
   
#if defined(MVL_UCA)    /* This entire module is only valid for UCA.    */   
   
   
/************************************************************************/   
/* 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   
   
/* PRIM_INFO struct:  extra info about primitive.   */   
typedef struct   
  {   
  ST_UINT prim_num;     /* index to data            */   
  ST_UINT prim_offset;      /* mem offset from start of var     */   
  ST_UINT prim_offset_base; /* mem offset from start of "base" var  */   
  } PRIM_INFO;          /* extra info about primitive   */   
   
/************************************************************************/   
   
static ST_VOID mvluDefGetVaDataBufFun (ST_INT service, MVL_VAR_ASSOC *va,    
                       ST_INT size);   
static ST_VOID mvluDefFreeVaDataBufFun (ST_INT service, MVL_VAR_ASSOC *va);   
   
/************************************************************************/   
/* Read/Write leaf indication override handlers */   
   
ST_VOID (*u_mvlu_leaf_rd_ind_fun)(MVLU_RD_VA_CTRL *mvluRdVaCtrl);   
ST_VOID (*u_mvlu_leaf_wr_ind_fun)(MVLU_WR_VA_CTRL *mvluWrVaCtrl);   
   
/************************************************************************/   
/* STATIC VARIABLES, LOCAL DEFINES                  */   
   
/* GNL Variables */   
static ST_CHAR *gnlNameBuf;   
static ST_CHAR *currGnlNamePos;   
   
#define _OBJ_NAME_CLONE_NAME_SIZE (65)   
#define _OBJ_NAME_CLONE_SIZE (sizeof(OBJECT_NAME)+(2 * _OBJ_NAME_CLONE_NAME_SIZE))   
   
/************************************************************************/   
/* STATIC FUNCTIONS                         */   
   
static ST_RET mvlu_find_struct_comp (ST_CHAR *compName,   
                 RUNTIME_TYPE **rtIo, ST_INT *numRtIo);   
   
static ST_VOID mvlu_handle_alt_acc (OBJECT_NAME *obj, ALT_ACCESS *alt_acc,    
                    MVL_ARR_CTRL *arrCtrl);   
static ST_RET getArrAARtType (MVL_ARR_CTRL *arrCtrl,    
            RUNTIME_TYPE **pRt, ST_INT *pNumRt);   
static ST_VOID cloneArrAA (ALT_ACC_EL *arrAa,  ALT_ACCESS *dest);   
   
static ST_VOID mvlu_find_base_va (MVL_VMD_CTRL *vmd_ctrl, OBJECT_NAME *obj, MVL_NET_INFO *net_info, MVL_VAR_ASSOC **vaOut);   
   
ST_VOID getGnlVarNames (MVL_VAR_ASSOC *va, ST_CHAR *caPtr,    
            ST_CHAR **dest, ST_INT maxNames,    
            ST_INT *numNames, ST_BOOLEAN *moreFollowsOut);   
static ST_VOID mvlu_clone_objname (ST_CHAR *dest, OBJECT_NAME *src);   
   
static ST_VOID startArrRds (MVL_IND_PEND *indCtrl, MVLAS_READ_CTRL *rdCtrl,   
             MVLAS_RD_VA_CTRL *rdVaCtrl, RUNTIME_TYPE *rt, PRIM_INFO *prim_info);   
static ST_VOID startElReads (MVL_IND_PEND *indCtrl, MVLAS_READ_CTRL *rdCtrl,   
             MVLAS_RD_VA_CTRL *rdVaCtrl,    
             RUNTIME_TYPE *rt, ST_INT rt_num, PRIM_INFO *prim_info);   
static ST_VOID startArrWrs (MVL_IND_PEND *indCtrl, MVLAS_WRITE_CTRL *wrCtrl,   
             MVLAS_WR_VA_CTRL *wrVaCtrl, RUNTIME_TYPE *rt, PRIM_INFO *prim_info);   
static ST_VOID startElWrites (MVL_IND_PEND *indCtrl, MVLAS_WRITE_CTRL *wrCtrl,   
              MVLAS_WR_VA_CTRL *wrVaCtrl,    
              RUNTIME_TYPE *rt, ST_INT rt_num, PRIM_INFO *prim_info);   
static ST_INT countPrimEl (MVL_VAR_ASSOC *va, RUNTIME_TYPE *rt, ST_INT rt_num);   
   
   
ST_RET init_prim_info_recursive (PRIM_INFO *prim_info, RUNTIME_TYPE *rt_first, ST_INT rt_num, ST_UINT first_offset_base);   
/************************************************************************/   
/*          init_prim_info_arr              */   
/* Updates prim_info->prim_num.                  */   
/* Must never find match within an array, because impossible to specify */   
/* IEC/UCA flattened name for array element, so just ignore returns */   
/* from init_prim_info_recursive, and finish loop anyway.       */   
/* Errors will be detected when this function returns.          */   
/************************************************************************/   
ST_VOID init_prim_info_arr (PRIM_INFO *prim_info,   
    RUNTIME_TYPE *rt_first,   
    ST_UINT first_offset_base)  /* mem offset from start of "base" var*/   
                    /* for "first" prim of this var */   
  {   
ST_RTINT rt_num;   
ST_RTINT num_arr_elements;  /* num of elements in array */   
ST_RTINT j;   
   
  assert (rt_first->el_tag == RT_ARR_START);  /* confirm this.   */   
   
  rt_num = rt_first->u.arr.num_rt_blks;  /* num RUNTIME_TYPE to define one element of array*/   
  num_arr_elements = rt_first->u.arr.num_elmnts;   
  assert (num_arr_elements > 0);   
   
  /* Call "init_prim_info_recursive" once for each array element.   */   
  for (j = 0; j < num_arr_elements; ++j)   
    {   
    init_prim_info_recursive (prim_info, rt_first+1, rt_num, first_offset_base);   
    }   
  return;   
  }   
/************************************************************************/   
/*          init_prim_info_recursive            */   
/* Updates prim_info->prim_num.                  */   
/* NOTE: when called recursively by init_prim_info_arr, this will   */   
/*       often fail. That is to be expected.                */   
/* RETURNS: SD_FAILURE  if NOT found yet.           */   
/*      SD_SUCCESS  if found.               */   
/************************************************************************/   
ST_RET init_prim_info_recursive (PRIM_INFO *prim_info,   
    RUNTIME_TYPE *rt_first,   
    ST_INT rt_num,   
    ST_UINT first_offset_base)  /* mem offset from start of "base" var*/   
                    /* for "first" prim of this var */   
  {   
RUNTIME_TYPE *rt_type;   
ST_INT j;   
ST_RET retcode = SD_FAILURE;   
   
  for (j = 0, rt_type = rt_first; j < rt_num; j++, rt_type++)   
    {   
    /* NOTE: Usually el_size=0 for non-primitives (RT_STR_START/END,    */   
    /* RT_ARR_START/END) but sometimes el_size!=0 for padding.      */   
    prim_info->prim_offset_base += rt_type->el_size;   
   
    switch (rt_type->el_tag)   
      {   
      case RT_STR_START:   
      case RT_STR_END:   
      case RT_ARR_END:   
        break;  /* do nothing   */   
      case RT_ARR_START:   
        init_prim_info_arr (prim_info, rt_type, first_offset_base);   
    /* CRITICAL: Index to just before RT_ARR_END. Don't skip over it*/   
        /* or its rt_type->el_size will not be added to prim_offset_base.*/   
        rt_type += rt_type->u.arr.num_rt_blks;   
        j += rt_type->u.arr.num_rt_blks;   
        break;   
      default:          /* Primitive    */   
        prim_info->prim_num++;   
        break;     
      } /* end "switch" */   
   
    if (prim_info->prim_offset_base == first_offset_base)   
      {   
      retcode = SD_SUCCESS; /* this is only way SD_SUCCESS returned */   
      break;            /* break out of main loop & return  */   
      }   
    if (prim_info->prim_offset_base > first_offset_base)   
      { /* Will NEVER find it. This is a SERIOUS error. */   
      /* NOTE: this error may occur more than once if it occurs in recursive call.*/   
      /*       Don't log error here. Log in init_prim_info if this funct fails.*/    
      break;    /* stop trying  */   
      }   
    }   
  return (retcode); /* SD_FAILURE if NOT found, SD_SUCCESS if found */   
  }   
     
/************************************************************************/   
/*          init_prim_info                  */   
/* Initializes prim_info struct. This is the main function. It calls    */   
/* init_prim_info_recursive & init_prim_info_arr recursively.       */   
/* NOTE: prim_info->prim_offset set = 0, never changed by this function.*/   
/************************************************************************/   
ST_RET init_prim_info (MVL_VAR_ASSOC *va, PRIM_INFO *prim_info)   
  {   
ST_RET retcode = SD_SUCCESS;   
MVL_TYPE_CTRL *type_ctrl;   
   
  /* Set initial values in prim_info. If this is top level var,  
   * these values will be used.  
   */   
  prim_info->prim_num = 0;   
  prim_info->prim_offset_base = 0;   
  prim_info->prim_offset = 0;    /* NOTE: never changed by this function */   
   
  /* If NOT top level var, must call init_prim_info_recursive to fix  
   * prim_info->prim_num.  
   */   
  if (va->offset_from_base)   
    {   
    type_ctrl = mvl_type_ctrl_find (va->base_va->type_id);   
    retcode = init_prim_info_recursive (prim_info,   
         type_ctrl->rt,   
         type_ctrl->num_rt,   
         va->offset_from_base);   
    if (retcode != SD_SUCCESS)   
      MVL_LOG_ERR3 ("No component in variable '%s' at offset=%d. Nearest is at offset=%d",   
                    va->base_va->name, va->offset_from_base, prim_info->prim_offset_base);   
    }   
   
  assert (prim_info->prim_offset == 0);  /* verify this was NOT changed  */   
  return (retcode);   
  }   
   
/************************************************************************/   
/************************************************************************/   
/* MANUFACTURED VARIABLE RESOLUTION FUNCTIONS               */   
/************************************************************************/   
/*          u_mvl_get_va_aa                 */   
/************************************************************************/   
/* This function is called from MVL when it is unable to find a     */   
/* configured MMS server variable for a READ, WRITE, or GET VARIABLE    */   
/* ACCESS ATTRIBUTES indication.                    */   
   
MVL_VAR_ASSOC *u_mvl_get_va_aa (MVL_VMD_CTRL *vmd_ctrl, ST_INT service, OBJECT_NAME *obj,    
               MVL_NET_INFO *netInfo,   
                   ST_BOOLEAN alt_access_pres,   
                   ALT_ACCESS *alt_acc,   
                   ST_BOOLEAN *alt_access_done_out)   
  {   
ST_CHAR *name;   
MVL_VAR_ASSOC *va;   
MVL_VAR_ASSOC *baseVa;   
ST_RET rc;   
OBJECT_NAME *objClone;    
ST_CHAR objNameBuf[_OBJ_NAME_CLONE_SIZE];    
RUNTIME_TYPE *baseRt;   
ST_INT numBaseRt;   
RUNTIME_TYPE *ucaRt;   
ST_INT numUcaRt;   
ST_CHAR *subStart;   
ST_INT subTypeId;   
MVL_ARR_CTRL arrCtrl;   
   
/* Make a working copy of the variable name (mvlu_handle_alt_acc may change it) */   
  objClone = (OBJECT_NAME *) objNameBuf;   
  mvlu_clone_objname (objNameBuf, obj);   
   
/* We will handle any alternate access here, by creating the UCA from   */   
/* variable name.                           */   
  memset (&arrCtrl, 0, sizeof (MVL_ARR_CTRL));  /* start with clean "arrCtrl"*/   
  if (alt_access_pres)   
    {   
    mvlu_handle_alt_acc (objClone, alt_acc, &arrCtrl);   
    *alt_access_done_out = SD_TRUE;   
    }   
   
/* We need to find the base type for this variable          */   
  mvlu_find_base_va (vmd_ctrl, objClone, netInfo, &baseVa);   
  if (baseVa == NULL)   
    {   
    MVL_LOG_NERR1 ("Could not resolve UCA variable '%s'", objClone->obj_name.vmd_spec);   
    return (NULL);   
    }   
   
/* Now resolve this variable's type given it's name and the base    */   
/* type.                                */   
  rc = mvl_get_runtime (baseVa->type_id, &baseRt, &numBaseRt);   
  if (rc != SD_SUCCESS)   
    {   
    MVL_LOG_NERR1 ("Could not get RT type for type id %d", baseVa->type_id);   
    return (NULL);   
    }   
  ucaRt = baseRt;   
  numUcaRt = numBaseRt;   
   
/* Check to see if this is for base var... name has no embedded '$' */   
/* If is a derived variable we need to create from the variable name    */   
/* 'path' and runtime type.                     */   
  name = objClone->obj_name.vmd_spec;    /* We know this is a union ...  */   
  subStart = strstr (name,"$");     /* Skip the outer (base) name   */   
  if (subStart)   
    {   
  /* This is a derived variable, and we have the base UCA variable  */   
  /* Using the base runtime table and the variable name, we can get the */   
  /* subset type for this variable                  */   
   
        ++subStart;   
        //lw:以下代码作了修改，主要为兼容3层目录服务器////////////////////   
        if ((baseVa->flags == MVL_VAR_FLAG_UCA) != 0)   
        {   
            rc = mvlu_find_uca_var (&ucaRt, &numUcaRt, subStart);   
        }   
        else if ((baseVa->flags & 0x02) != 0)   
        {   
            subStart = strstr(subStart, "$");   
            if (subStart)   
            {   
                ++subStart;   
                rc = mvlu_find_uca_var (&ucaRt, &numUcaRt, subStart);    
            }   
        }   
        ////////////////////////////////////////////////////////////////////   
   
      if (rc != SD_SUCCESS)   
      {   
        MVL_LOG_NERR1 ("Error - could not find subcomponent '%s'", subStart);   
        return (NULL);   
      }   
    }   
   
   
/* If this is an alternate access on an array, we need to copy the  */   
/* RT type so we can modify the number of elements and total size   */   
  if (arrCtrl.arrAltAccPres == SD_TRUE)   
    {   
    rc = getArrAARtType (&arrCtrl, &ucaRt, &numUcaRt);   
    if (rc != SD_SUCCESS)   
      {   
      M_FREE (MSMEM_MVLU_AA, arrCtrl.alt_acc.aa);   
      return (NULL);   
      }   
    }   
   
/* OK, now we have the sub-runtime type, go ahead and create a temp     */   
/* RT type and variable association.                    */   
  rc = mvlu_add_rt_type (ucaRt, numUcaRt, &subTypeId);   
  if (rc != SD_SUCCESS)   
    {   
    MVL_LOG_NERR0 ("Error - could not add temp RT type");   
    return (NULL);   
    }   
   
/* Create the variable association for this variable            */   
/* CRITICAL: Must use "mvl_var_create", not "mvl_var_add" because   */   
/*   this var must not be added to list (i.e. it will not be sent in    */   
/*   GetNameList responses).                        */   
  va = mvl_var_create (objClone, subTypeId,   
            NULL,   /* data (set later by mvluDefGet..) */   
            NULL,   /* (MVL_VAR_PROC *) not needed  */   
            SD_TRUE);   /* copy var name    */   
/* Set special "va" elements not set by mvl_var_create. */   
  va->base_va = baseVa;   
  va->user_info = baseVa->user_info;   
   
  va->offset_from_base = ucaRt->mvluTypeInfo.offSet;   
   
/* If the user elected to use offset, data or proc initialization,  */   
/* take care of it here.                        */   
   
#if defined(MVLU_USE_REF)   
  va->ref = ucaRt->mvluTypeInfo.ref;   
#endif   
   
/* Copy the array AA information */   
  memcpy (&va->arrCtrl, &arrCtrl, sizeof (MVL_ARR_CTRL));   
  va->arrCtrl.curr_index = arrCtrl.low_index;   
   
  /* Get the data buffer */   
  /* This function sets "va->data".  */   
  mvluDefGetVaDataBufFun (service, va, ucaRt->offset_to_last);   
   
/* Good work, we are done here. */   
  return (va);   
  }   
   
/************************************************************************/   
/*          getArrAARtType                  */   
/************************************************************************/   
   
static ST_RET getArrAARtType (MVL_ARR_CTRL *arrCtrl,    
            RUNTIME_TYPE **pRt, ST_INT *pNumRt)   
    {   
ST_INT numRt;   
ST_INT i, j;   
RUNTIME_TYPE *rt;   
RUNTIME_TYPE *endRt;   
RUNTIME_TYPE *newRt;   
ALT_ACCESS *alt_acc;   
ALT_ACC_EL *alt_acc_el; /* ptr to current entry in alt acc array    */   
ST_INT nest_level = 0;   
   
  rt = *pRt;   
  numRt = *pNumRt;   
   
/* Check to see if further nesting with this AA selection ...   */   
  if (arrCtrl->nested == SD_FALSE)   
    {   
  /* If just one element, need to lose the outer array */   
    if (arrCtrl->num_elmnts == 1)   
      {   
      numRt -= 2;   
      ++rt;   
      }   
    }   
  else  /* Further nesting is requested, let's do it!   */   
    {   
  /* We only support a single drill down AA */   
    if (arrCtrl->num_elmnts > 1)   
      {   
      MVL_LOG_NERR0 ("AA resolution problem - multiple elements and nested");   
      return (SD_FAILURE);   
      }   
    numRt -= 2;   
    ++rt;   
   
  /* OK, we now need to find the type of the component ... */     
    alt_acc = &arrCtrl->alt_acc;   
    /* Only support AA_COMP and AA_COMP_NEST for now.       */   
    /* If AA_COMP_NEST is used, this is recursive process.  */   
    for (j=0; j < alt_acc->num_aa; j++)   
      {   
      alt_acc_el = &alt_acc->aa[j];   
      if (alt_acc_el->sel_type == AA_COMP || alt_acc_el->sel_type == AA_COMP_NEST)   
        {   
        if (alt_acc_el->sel_type == AA_COMP_NEST)   
          nest_level++;   
        /* find this component name within the current RUNTIME_TYPE array*/   
        for (i = 0; i < numRt; ++i, ++rt)   
          {   
          if (!strcmp (alt_acc_el->u.component, ms_comp_name_find (rt)))   
            break;   
          }   
        if (i >= numRt)   
          {   
          MVL_LOG_NERR1 ("AA resolution problem - could not find component '%s'",    
                    alt_acc_el->u.component);   
          return (SD_FAILURE);   
      }   
        if (rt->el_tag == RT_STR_START)   
          numRt = rt->u.str.num_rt_blks+2;   
        else if (rt->el_tag == RT_ARR_START)   
          numRt = rt->u.arr.num_rt_blks+2;   
        else   
          numRt = 1;   
        }   
      else if (alt_acc_el->sel_type == AA_END_NEST)   
        nest_level--;   
      else   
        {   
        MVL_LOG_NERR0 ("AA resolution problem - complex nested AA on array");   
        return (SD_FAILURE);   
        }   
      }     /* end "for" loop   */   
    if (nest_level != 0)   
      {   
      MVL_LOG_NERR0 ("AA resolution problem - invalid nesting");   
      return (SD_FAILURE);   
      }   
    }   
   
/* OK, now copy the runtime type elements into a new one so we can fool */   
/* with it.                             */   
  newRt = (RUNTIME_TYPE *) M_MALLOC (MSMEM_DYN_RT, numRt * sizeof (RUNTIME_TYPE));   
  memcpy (newRt, rt, numRt * sizeof (RUNTIME_TYPE));   
   
/* Adjust the total size of the type, using scaling             */   
  if (arrCtrl->num_elmnts > 1)   
    {   
    newRt->offset_to_last = rt[1].offset_to_last * arrCtrl->num_elmnts;   
   
/* Now set the number of elements as desired                */   
    newRt->u.arr.num_elmnts = arrCtrl->num_elmnts;   
    endRt = newRt + (newRt->u.arr.num_rt_blks + 1);   
    endRt->u.arr.num_elmnts = arrCtrl->num_elmnts;   
   
/* We don't want end of array padding */   
    endRt->el_size = 0;   
    }   
   
  *pRt = newRt;   
  *pNumRt = numRt;   
  return (SD_SUCCESS);   
  }   
   
/************************************************************************/   
/*                       mvlu_handle_alt_acc                */   
/************************************************************************/   
/* Here we deal with an alternate access. We will simply add the    */   
/* sub-name components to the base name to create a fully qualified UCA */   
/* sub-var name                             */   
/* Of course, this only handles the most simple form of alternate   */   
/* access correctly ...                         */   
   
static ST_VOID mvlu_handle_alt_acc (OBJECT_NAME *obj, ALT_ACCESS *alt_acc,   
                    MVL_ARR_CTRL *arrCtrl)   
  {   
ST_INT i;   
ST_CHAR *name;   
ST_BOOLEAN done;   
   
  name = obj->obj_name.vmd_spec; /* We know this is a union ...  */   
  arrCtrl->nested = SD_FALSE;   
  done = SD_FALSE;   
  for (i = 0; i < alt_acc->num_aa && !done; ++i)   
    {   
    switch (alt_acc->aa[i].sel_type)   
      {   
      case AA_COMP :   
      case AA_COMP_NEST :   
        strcat (name, "$");   
        strcat (name, alt_acc->aa[i].u.component);   
      break;   
   
      case AA_INDEX_NEST :   
        arrCtrl->nested = SD_TRUE;   
    cloneArrAA (&alt_acc->aa[i],  &arrCtrl->alt_acc);   
    /* Lets fall through into common code ... */   
      case AA_INDEX :   
        arrCtrl->arrAltAccPres = SD_TRUE;   
        arrCtrl->low_index = (ST_RTINT) alt_acc->aa[i].u.index;   
        arrCtrl->num_elmnts = 1;   
        done = SD_TRUE;   
      break;   
   
      case AA_INDEX_RANGE_NEST :   
        arrCtrl->nested = SD_TRUE;   
    cloneArrAA (&alt_acc->aa[i],  &arrCtrl->alt_acc);   
    /* Lets fall through into common code ... */   
      case AA_INDEX_RANGE :   
        arrCtrl->arrAltAccPres = SD_TRUE;   
        arrCtrl->low_index = (ST_RTINT) alt_acc->aa[i].u.ir.low_index;   
        arrCtrl->num_elmnts = (ST_RTINT) alt_acc->aa[i].u.ir.num_elmnts;   
        done = SD_TRUE;   
      break;   
   
      case AA_ALL:   
      case AA_ALL_NEST :   
        arrCtrl->nested = SD_TRUE;   
    cloneArrAA (&alt_acc->aa[i],  &arrCtrl->alt_acc);   
        arrCtrl->arrAltAccPres = SD_TRUE;   
        arrCtrl->low_index = 0;   
        arrCtrl->num_elmnts = 0; /* 'all' flag */   
        done = SD_TRUE;   
      break;   
   
      case AA_END_NEST :   
        done = SD_TRUE;   
      break;   
   
      default:   
        MVL_LOG_NERR1 ("Error: Invalid alt access sel_type",    
                    alt_acc->aa[i].sel_type);   
      break;   
      }   
    }   
  }   
   
/************************************************************************/   
/*              cloneArrAA              */   
/************************************************************************/   
   
static ST_VOID cloneArrAA (ALT_ACC_EL *arrAa,  ALT_ACCESS *dest)   
  {   
ST_INT elCount;   
ST_INT nestLevel;   
ALT_ACC_EL *aaEl;   
   
/* First let's count how many we need */   
  nestLevel = 0;   
  elCount = 0;   
  aaEl = arrAa + 1;   
  while (SD_TRUE)   
    {   
    if (nestLevel == 0 && aaEl->sel_type == AA_END_NEST)   
      break;   
   
    ++elCount;   
    if (aaEl->sel_type == AA_COMP_NEST ||   
        aaEl->sel_type == AA_INDEX_NEST ||   
        aaEl->sel_type == AA_INDEX_RANGE_NEST ||   
        aaEl->sel_type == AA_ALL_NEST)   
      {   
      ++nestLevel;   
      }    
    if (aaEl->sel_type == AA_END_NEST)   
      {   
      --nestLevel;   
      }    
    ++aaEl;   
    }    
   
/* OK, now just calloc and copy the nested alternate access */   
  if (elCount)   
    {   
    dest->num_aa = elCount;   
    dest->aa = (ALT_ACC_EL *) M_MALLOC (MSMEM_MVLU_AA, elCount * sizeof (ALT_ACC_EL));   
    memcpy (dest->aa, arrAa+1, elCount * sizeof (ALT_ACC_EL));   
    }   
  else   
    {   
    MVL_LOG_NERR0 ("Nested AA construction problem");   
    }   
  }   
   
/************************************************************************/   
/*                       mvlu_find_base_va              */   
/************************************************************************/   
/* This function takes a MMS variable name and determines the type ID   */   
/* for the base type for the variable. This is done here by         */   
/* extracting the name root then looking for a configured variable  */   
/* of that name.                            */   
   
static ST_VOID mvlu_find_base_va (MVL_VMD_CTRL *vmd_ctrl, OBJECT_NAME *obj, MVL_NET_INFO *net_info, MVL_VAR_ASSOC **vaOut)   
  {   
MVL_VAR_ASSOC *va;   
ST_CHAR *p;   
ST_CHAR *name;   
ST_CHAR temp[128];   
OBJECT_NAME *objClone;    
ST_CHAR objNameBuf[_OBJ_NAME_CLONE_SIZE];    
   
  objClone = (OBJECT_NAME *) objNameBuf;   
  mvlu_clone_objname (objNameBuf, obj);   
  name = objClone->obj_name.vmd_spec;    /* We know this is a union ...  */   
  strcpy(temp, name);   
    
/* See if this variable name has embedded '$', and if so wack it so */   
/* that we have the base name to work with.             */   
  p = strstr (name,"$");   
  if (p)   
    *p = 0;   
   
  va = mvl_vmd_find_var (vmd_ctrl, objClone, net_info);   
  if (va)   
  {   
      if ((va->flags & MVL_VAR_FLAG_UCA) == 0)   
        va = NULL;  /* Found va but not UCA variable, so don't return it.   */   
         
      *vaOut = va;   
      return;   
  }   
     
  //lw：以下代码为兼容3层服务器所增加////////////////////////////////   
  strcpy(name, temp);   
  p++;   
  p = strstr(p, "$");   
  if (p)   
    *p = 0;   
     
  va = mvl_vmd_find_var(vmd_ctrl, objClone, net_info);   
   
  if (va && (va->flags & 0x02) == 0)   
  {   
      va = NULL;   
  }   
  *vaOut = va;   
  ///////////////////////////////////////////////////////////////////   
  }   
   
/************************************************************************/   
/*                       mvlu_find_uca_var              */   
/************************************************************************/   
/* This function searches a runtime type for the given UCA variable     */   
/* name. It does this by breaking the UCA name into its components  */   
/* and then finding the name in the current level of the runtime type.  */   
   
ST_RET mvlu_find_uca_var (RUNTIME_TYPE **rtIo, ST_INT *numRtIo,    
                 ST_CHAR *varName)   
  {   
ST_CHAR nameBuf[MAX_IDENT_LEN+1];   
ST_CHAR *nameToFind;   
ST_CHAR *compEnd;   
ST_BOOLEAN nameDone;   
ST_RET ret;   
   
/* Note that varName does not have the base name prefix */   
  strcpy (nameBuf, varName);       
  nameToFind = nameBuf;   
  nameDone = SD_FALSE;   
  while (nameDone == SD_FALSE)   
    {   
  /* Isolate the component name for this level, removing subcomp names  */   
    compEnd = strstr (nameToFind, "$");   
    if (compEnd != NULL)   
      *compEnd = 0;   
    else            /* This is the last nest level      */   
      nameDone = SD_TRUE;   
   
  /* Find the component name in the current runtime type nest level */   
    ret = mvlu_find_struct_comp (nameToFind, rtIo, numRtIo);   
    if (ret == SD_FAILURE)   
      {   
      /* Many things can cause this so just use FLOW Logging.   */   
      MVLU_LOG_FLOW2 ("Could not find name component %s from name %s",   
                nameToFind, varName);   
      return (SD_FAILURE);   
      }   
   
  /* OK, we now have found the component in the runtime type, and our   */   
  /* runtime pointer and numRt reflect the sub-runtime type.        */   
  /* next component.                            */   
   
  /* Prepare to find the next level component name          */   
    nameToFind = compEnd+1;   
    }   
  return (SD_SUCCESS);   
  }   
   
/************************************************************************/   
/*                       mvlu_find_struct_comp              */   
/************************************************************************/   
/* This function searches a structure runtime type for the given    */   
/* component name at the outer level.                   */   
   
static ST_RET mvlu_find_struct_comp (ST_CHAR *compName,   
                 RUNTIME_TYPE **rtIo, ST_INT *numRtIo)    
  {   
RUNTIME_TYPE *rt;   
RUNTIME_TYPE *endRt;   
ST_BOOLEAN foundRt;   
   
  foundRt = SD_FALSE;   
  rt = *rtIo;   
  endRt = rt + *numRtIo;   
  if (rt->el_tag != RT_STR_START)   
    {   
    MVL_LOG_NERR0 ("Find struct comp: First RT is not structure start");   
    return (SD_FAILURE);   
    }   
  ++rt;     /* Skip the structure start */   
   
  while (rt < endRt)   
    {   
    if (!strcmp (compName, ms_comp_name_find (rt)))   
      {   
      *rtIo = rt;   
      foundRt = SD_TRUE;   
      }   
    switch (rt->el_tag)   
      {   
      case RT_STR_START :   
    if (foundRt == SD_TRUE)   
          {   
      *numRtIo = rt->u.str.num_rt_blks+2;   
          return (SD_SUCCESS);   
          }   
        rt += rt->u.str.num_rt_blks+2;   /* Skip the structure contents  */   
      break;   
   
      case RT_ARR_START :   
    if (foundRt == SD_TRUE)   
          {   
      *numRtIo = rt->u.arr.num_rt_blks+2;   
          return (SD_SUCCESS);   
          }   
        rt += rt->u.arr.num_rt_blks+1; /* Skip the array contents    */   
      break;   
   
      case RT_STR_END :   
      case RT_ARR_END :   
        ++rt;   
      break;   
   
      default:   
    if (foundRt == SD_TRUE)   
          {   
      *numRtIo = 1;   
          return (SD_SUCCESS);   
          }   
        ++rt;   
      break;   
      }   
    }   
  return (SD_FAILURE);   
  }   
   
/************************************************************************/   
/*              u_mvl_free_va               */   
/************************************************************************/   
/* MVL calls this function when it is through with it. We will free */   
/* the data buffer, and then the VA (unless it was a base VA).      */   
   
ST_VOID u_mvl_free_va (ST_INT service, MVL_VAR_ASSOC *va,    
                MVL_NET_INFO *netInfo)   
  {   
RUNTIME_TYPE *ucaRt;   
ST_INT numUcaRt;   
ST_RET rc;   
   
/* We always 'allocate' the data buffer */   
   mvluDefFreeVaDataBufFun(service, va);   
   
/* If this was not a 'base' VA, free the derived type */   
  if ( (va->flags & MVL_VAR_FLAG_UCA) == 0)   
    {   
   
  /* See if we allocated the runtime type ... */   
    if (va->arrCtrl.arrAltAccPres == SD_TRUE)   
      {   
      rc = mvl_get_runtime (va->type_id, &ucaRt, &numUcaRt);   
      if (rc == SD_SUCCESS)   
        M_FREE (MSMEM_DYN_RT, ucaRt);   
      else   
        {   
        MVL_LOG_NERR0 ("Error: internal error");   
        }   
      if (va->arrCtrl.nested == SD_TRUE)   
        M_FREE (MSMEM_MVLU_AA, va->arrCtrl.alt_acc.aa);   
      }   
   
    mvlu_free_rt_type (va->type_id);   
    mvl_var_destroy (va);   
    }   
  }   
   
/************************************************************************/   
/************************************************************************/   
/* MANUFACTURED VARIABLE_LIST RESOLUTION FUNCTIONS          */   
/************************************************************************/   
/* u_mvl_get_nvl, u_mvl_free_nvl functions deleted (no longer needed).  */   
/* They were only needed because Foundry could not find the variables   */   
/* for the NVL to set "vl->entries". But new improved Foundry        */   
/* generates code like the following (only done once at startup,    */   
/* so it is much more efficient):                   */   
/*  
 *  varObjName.obj_name.vmd_spec = "DI$Name";  
 *  varObjName.object_tag = DOM_SPEC;  
 *  varObjName.domain_id = mvl_vmd.dom_tbl[6]->name;  
 *  vl->entries [0] = u_mvl_get_va_aa (MMSOP_INFO_RPT, &varObjName, NULL, SD_FALSE, NULL, NULL);  
 */   
   
/************************************************************************/   
/************************************************************************/   
/* GET NAMELIST HELPER FUNCTIONS                    */   
/* These functions are necessary because MVL does not know about our    */   
/* manufactured variables and variable lists. We will fill in part of   */   
/* the namelist response data structure.                */   
/************************************************************************/   
/************************************************************************/   
/*          u_gnl_ind_vars                  */   
/************************************************************************/   
   
ST_INT u_gnl_ind_vars (MVL_NET_INFO *net_info, NAMELIST_REQ_INFO *req_info,    
                 ST_CHAR **ptr, ST_BOOLEAN *moreFollowsOut,    
                 ST_INT maxNames)   
  {   
ST_INT i;   
ST_INT numRespNames;   
ST_CHAR caBuf[100];   
ST_CHAR *caPtr;   
ST_CHAR *p;   
ST_INT numVa;   
ST_INT v;   
MVL_VAR_ASSOC **va;   
MVL_AA_OBJ_CTRL *aa;   
MVL_DOM_CTRL *domCtrl;   
   
   
  gnlNameBuf = (ST_CHAR *) M_CALLOC (MSMEM_MVLU_GNL, 1, maxNames * (MAX_IDENT_LEN +1));   
   
/* Start by finding the VA that we should start with            */   
/* Get the variable associations in the selected scope          */   
  numVa = 0;   
  if (req_info->objscope == VMD_SPEC)   
    {   
    numVa = mvl_vmd.num_var_assoc;   
    va = mvl_vmd.var_assoc_tbl;   
    }   
  else if (req_info->objscope == DOM_SPEC)   
    {   
    domCtrl = mvl_vmd_find_dom (&mvl_vmd, req_info->dname);   
    if (domCtrl)   
      {   
      numVa = domCtrl->num_var_assoc;   
      va = domCtrl->var_assoc_tbl;   
      }   
    else   
      {   
      MVL_LOG_NERR1 ("GetNameList variables: Domain '%s' not found", req_info->dname);   
      *moreFollowsOut = SD_FALSE;   
      return (-1);  /* error. This triggers error response  */   
      }   
    }   
  else  /* AA_SPEC */   
    {   
    aa = (MVL_AA_OBJ_CTRL *) net_info->aa_objs;   
    if (aa)   
      {   
      numVa = aa->num_var_assoc;   
      va = aa->var_assoc_tbl;   
      }   
    else   
      {   
      *moreFollowsOut = SD_FALSE;   
      return (-1);  /* error. This triggers error response  */   
      }   
    }   
   
/* Take care of the 'continue after' business as necessary. Note that   */   
/* we will only look at the base name.                  */   
  i = 0;   
  if (req_info->cont_after_pres)   
    {   
    caPtr = req_info->continue_after;   
    strcpy (caBuf, req_info->continue_after);   
    p = strstr (caBuf,"$");   
    if (p)   
      *p = 0;   
   
    while (i < numVa)   
      {   
      p = va[i]->name;   
      v = strcmp (caBuf, p);   
      if (v == 0)       /* Exact match              */   
        break;   
   
      if (v < 0)         /* CA is less than real VA name     */   
        {   
        if (i > 0)       /* Start with the previous VA       */   
          --i;   
        break;   
        }   
      ++i;          /* We have not found our place yet ...  */   
      }   
    }   
  else   
    caPtr = NULL;   
   
  *moreFollowsOut = SD_FALSE;   
   
  currGnlNamePos = gnlNameBuf;   
   
/* OK, va[i] is where we start putting together our names       */   
  for (numRespNames = 0; numRespNames < maxNames && i < numVa; ++i)   
    {   
    ST_INT j;   
    ST_INT numRespNamesStart = numRespNames;    /* save count modified by fct*/   
    getGnlVarNames (va[i], caPtr, &ptr[numRespNames],    
          maxNames, &numRespNames, moreFollowsOut);   
   
    MVLU_LOG_DEBUG0 ("Names returned from getGnlVarNames");   
    for (j = numRespNamesStart; j<numRespNames; j++)   
      MVLU_LOG_CDEBUG2 ("[%d]%s", j, ptr[j]);   
   
    caPtr = NULL;   
    }   
  if (i < numVa)   
    *moreFollowsOut = SD_TRUE;   
       
  return (numRespNames);   
  }   
   
/************************************************************************/   
/*          getGnlVarNames                  */   
/************************************************************************/   
   
/* GNL Variables defines */   
#define MAX_NEST_LEVEL  10   
   
ST_VOID getGnlVarNames (MVL_VAR_ASSOC *va, ST_CHAR *caPtr,    
            ST_CHAR **dest, ST_INT maxNames,    
            ST_INT *numNames, ST_BOOLEAN *moreFollowsOut)   
  {   
RUNTIME_TYPE *ucaRt;   
ST_INT numUcaRt;   
ST_INT i;   
ST_CHAR nameBuf[MAX_IDENT_LEN+1];   
ST_INT nameCount;   
ST_INT maxSortedNum;   
ST_INT sortedNum;   
ST_INT startSortedNum;   
ST_CHAR *subStart;   
ST_RET rc;   
ST_INT baseIndexOffset;   
ST_INT maxRetNames;   
/* prefix len should never reach MAX_IDENT_LEN but could get very close*/   
ST_CHAR namePrefix[MAX_NEST_LEVEL][MAX_IDENT_LEN+1];   
ST_INT nestLevel;   
ST_INT strLen;   
ST_INT get;   
ST_INT put;   
ST_INT numNewNames;   
ST_BOOLEAN compress;   
ST_CHAR *comp_name;     /* component name   */   
   
  if ( (va->flags & MVL_VAR_FLAG_UCA) == 0)   
    {     
    ++(*numNames);   
    dest[0] = va->name;   
    return;   
    }   
   
  memset (namePrefix, 0, sizeof(namePrefix));   
  strcpy (namePrefix[0], va->name);   
   
/* First we need to find the starting 'sortedNum', based on the CA name */   
  nameCount = *numNames;   
  startSortedNum = 1;   
  if (caPtr != NULL)   
    {   
    baseIndexOffset = 0;   
    /* CA name could be base name, or derived name.         */   
    if (strcmp (caPtr, va->name) == 0)   
      { /* CA name equals base name. Start with next name AFTER base.   */   
      startSortedNum = 1;   
      }   
    else if ((subStart = strstr (caPtr,"$")) == NULL)   /* Skip the base name*/   
      {   
      MVL_LOG_ERR1 ("Problem finding sub-type in '%s'", caPtr);   
      return;   
      }   
    else   
      {   
      rc = mvl_get_runtime (va->type_id, &ucaRt, &numUcaRt);   
      ++subStart;    
      rc = mvlu_find_uca_var (&ucaRt, &numUcaRt, subStart);   
      if (rc == SD_SUCCESS)   
        startSortedNum = ucaRt->mvluTypeInfo.sortedNum + 1;   
      }   
    }   
  else  /* No continue after, we need to include the base name */   
    {   
    dest[0] = va->name;   
    ++nameCount;   
    baseIndexOffset = 1;   
    --maxNames;   
    }   
  maxRetNames = maxNames - *numNames;   
  maxSortedNum = startSortedNum + maxRetNames - 1;   
   
/* OK, now we start doing the real thing. Derive names for this     */   
/* type, put them into the dest array.                  */   
/* We will save those elements with 'sortedNum' between         */   
/* 'startSortedNum' and 'maxSortedNum'                  */   
   
  compress = SD_FALSE;   
  rc = mvl_get_runtime (va->type_id, &ucaRt, &numUcaRt);   
   
  /* This code assumes all UCA vars are structs, so first tag must be RT_STR_START*/   
  /* DEBUG: change this to "assert"?                    */   
  if (ucaRt->el_tag != RT_STR_START)   
    MVL_LOG_ERR1 ("IEC/UCA type (type_id=%d) is not a struct. Cannot derive variable names for GetNameList response.",   
                  va->type_id);   
   
  nestLevel = 0;   
  for (i = 0; i < numUcaRt; ++i, ++ucaRt)   
    {   
    sortedNum = ucaRt->mvluTypeInfo.sortedNum;   
    comp_name = ms_comp_name_find (ucaRt);   
    if (strlen (comp_name)  &&   
        sortedNum >= startSortedNum && sortedNum <= maxSortedNum)   
      {   
      /* Chk len is legal BEFORE writing (need room for 2 strings + '$'.*/   
      if (strlen (namePrefix[nestLevel]) + strlen (comp_name) + 1 <= MAX_IDENT_LEN)   
        {   
        sprintf (nameBuf, "%s$%s", namePrefix[nestLevel], comp_name);   
        strLen = strlen (nameBuf);   
        assert (strLen <= MAX_IDENT_LEN);    /* if this fails, len chk in "if" is wrong*/   
        dest[sortedNum-startSortedNum+baseIndexOffset] = currGnlNamePos;   
        strcpy (currGnlNamePos, nameBuf);   
        currGnlNamePos += strLen +1;   
        ++nameCount;   
        }   
      else   
        {   
        MVL_LOG_NERR2 ("Derived variable name '%s$%s' too long to access",   
                       namePrefix[nestLevel], comp_name);   
        compress = SD_TRUE;   
        }   
      }   
   
    if (ucaRt->el_tag == RT_STR_START)   
      {   
      comp_name = ms_comp_name_find (ucaRt);   
      if (strlen (comp_name))   
        {   
        ++nestLevel;   
        assert (nestLevel < MAX_NEST_LEVEL);   
        assert (nestLevel > 0);   
        strcpy (namePrefix[nestLevel], namePrefix[nestLevel-1]);   
        strcat (namePrefix[nestLevel], "$");   
        strcat (namePrefix[nestLevel], comp_name);   
        }   
      }   
    else if (ucaRt->el_tag == RT_STR_END)   
       --nestLevel;   
    else if (ucaRt->el_tag == RT_ARR_START)   
      {                      /* Skip the array elements */   
      /* There is no way to create UCA names for objects inside an  */   
      /* array, so skip over the array.                 */   
      i += (ucaRt->u.arr.num_rt_blks + 1);   
      ucaRt += (ucaRt->u.arr.num_rt_blks + 1);   
      }   
   
    if (sortedNum > maxSortedNum)   
      *moreFollowsOut = SD_TRUE;   
   
    }   
   
  /* DEBUG: at this point loop, 'nestLevel' should equal (-1). This may */   
  /* sound strange, but nestLevel NOT incremented on first RT_STR_START,*/   
  /* so last RT_STR_END makes it -1. Add assert here?           */   
   
  /* If we had to skip one or more names, we need to eliminate the  */   
  /* holes in the name pointer table.                   */   
  if (compress == SD_TRUE)   
    {   
    get = 0;   
    put = 0;   
    numNewNames = nameCount - *numNames;   
    while (put < numNewNames)   
      {   
      if (dest[get] != NULL)   
        dest[put++] = dest[get];   
      ++get;   
      }   
    }   
   
  *numNames = nameCount;   
  }   
   
/************************************************************************/   
/*                       u_gnl_done                 */   
/************************************************************************/   
/* This function is called after MVL has send the MMS response to a     */   
/* GET_NAME_LIST indication. We will clean up memory resources.     */   
   
ST_VOID u_gnl_done (ST_INT16 mms_class,   
                  NAMELIST_RESP_INFO *resp_info)   
  {   
  if (mms_class == MMS_CLASS_VAR)   
    M_FREE (MSMEM_MVLU_GNL, gnlNameBuf);   
  }   
   
/************************************************************************/   
/************************************************************************/   
/*          u_gnl_ind_nvls                  */   
/************************************************************************/   
   
ST_INT u_gnl_ind_nvls (MVL_NET_INFO *net_info, NAMELIST_REQ_INFO *req_info,    
                 ST_CHAR **ptr, ST_BOOLEAN *moreFollowsOut,    
                 ST_INT maxNames)   
  {   
ST_INT v;   
ST_INT i;   
ST_INT num_obj;   
MVL_AA_OBJ_CTRL *aa;   
MVL_NVLIST_CTRL **vl;   
MVL_DOM_CTRL *domCtrl;   
ST_INT startIndex;   
ST_INT numObjLeft;   
ST_INT numRetNames;   
ST_CHAR *name;   
   
/* First get the number of objects and pointer to the object table  */   
  num_obj = 0;   
  if (req_info->objscope == VMD_SPEC)   
    {   
    num_obj = mvl_vmd.num_nvlist;   
    vl = mvl_vmd.nvlist_tbl;   
    }   
  else if (req_info->objscope == DOM_SPEC)   
    {   
    domCtrl = mvl_vmd_find_dom (&mvl_vmd, req_info->dname);   
    if (domCtrl)   
      {   
      num_obj = domCtrl->num_nvlist;   
      vl = domCtrl->nvlist_tbl;   
      }   
    else   
      {   
      MVL_LOG_NERR1 ("GetNameList NVL: Domain '%s' not found", req_info->dname);   
      *moreFollowsOut = SD_FALSE;   
      return (-1);  /* error. This triggers error response  */   
      }   
    }   
  else  /* AA_SPEC */   
    {   
    aa = (MVL_AA_OBJ_CTRL *) net_info->aa_objs;   
    if (aa)   
      {   
      num_obj = aa->num_nvlist;   
      vl = aa->nvlist_tbl;   
      }   
    else   
      {   
      *moreFollowsOut = SD_FALSE;   
      return (-1);  /* error. This triggers error response  */   
      }   
    }   
   
  if (num_obj)   
    {   
  /* Take care of the 'continue after' business if necessary */   
    startIndex = 0;   
    if (req_info->cont_after_pres)   
      {   
      while (startIndex < num_obj)   
        {   
        name = vl[startIndex]->name;   
        v = strcmp (req_info->continue_after, name);   
        if (v == 0)   
          {   
          ++startIndex;     /* Index to the next one        */   
          break;   
          }   
        if (v < 0)    
          break;   
     
        ++startIndex;       /* We have not found our place yet ...  */   
        }   
      }   
   
    numObjLeft = num_obj-startIndex;   
    numRetNames = min(numObjLeft, maxNames);   
    if (numRetNames < numObjLeft)   
      *moreFollowsOut = SD_TRUE;   
   
  /* Now make the list for the response */   
    for (i = 0; i < numRetNames; ++i, ++ptr)   
      *ptr = vl[startIndex+i]->name;   
     
    return (numRetNames);   
    }   
  return (0);   
  }   
   
/************************************************************************/   
/************************************************************************/   
/*                       mvlu_clone_objname             */   
/************************************************************************/   
/* This function 'clones' a MMS OBJECT_NAME structure, which means that */   
/* it must allocate the various storage elements as required. Note  */   
/* that the name storage allocation is 65, to give calling routines */   
/* room to work.                            */    
   
static ST_VOID mvlu_clone_objname (ST_CHAR *dest, OBJECT_NAME *src)   
  {   
OBJECT_NAME *objClone;   
   
  objClone = (OBJECT_NAME *) dest;   
  memset (dest, 0, _OBJ_NAME_CLONE_SIZE);   
  objClone->object_tag = src->object_tag;   
  objClone->obj_name.vmd_spec = (ST_CHAR *) (objClone + 1);   
  strcpy (objClone->obj_name.vmd_spec, src->obj_name.vmd_spec);   
  if (src->object_tag == DOM_SPEC)   
    {   
    objClone->domain_id = objClone->obj_name.vmd_spec + _OBJ_NAME_CLONE_NAME_SIZE;   
    strcpy (objClone->domain_id, src->domain_id);   
    }   
  }   
   
/************************************************************************/   
/************************************************************************/   
/* MVLU READ/WRITE HANDLERS                     */   
/************************************************************************/   
   
static ST_VOID mvluDefAsyncWrIndFun (struct mvlu_wr_va_ctrl *mvluWrVaCtrl);   
static ST_VOID mvluDefAsyncRdIndFun (struct mvlu_rd_va_ctrl *mvluRdVaCtrl);    
   
/* Function pointers for non-UCA variable handling */   
ST_VOID(*mvluAsyncRdIndFun)(struct mvlu_rd_va_ctrl *mvluRdVaCtrl) =    
                mvluDefAsyncRdIndFun;   
ST_VOID(*mvluAsyncWrIndFun)(struct mvlu_wr_va_ctrl *mvluWrVaCtrl) =   
                mvluDefAsyncWrIndFun;   
   
/************************************************************************/   
/************************************************************************/   
/************************************************************************/   
/*          mvl_read_ind                    */   
/************************************************************************/   
   
ST_VOID u_mvl_read_ind (MVL_IND_PEND *indCtrl)   
  {   
MVLAS_READ_CTRL *rdCtrl;   
MVLAS_RD_VA_CTRL *rdVaCtrl;   
MVL_VAR_ASSOC *va;   
MVLU_RD_VA_CTRL *mvluRdVaCtrl;   
ST_INT i;   
ST_INT numVar;   
RUNTIME_TYPE *rt;   
ST_INT numRt;   
PRIM_INFO prim_info;   
   
  rdCtrl = &indCtrl->u.rd;   
  numVar = rdCtrl->numVar;   
   
/* First we will go through each variable being read and count the  */   
/* primitive elelemts.                          */   
   
  rdVaCtrl = rdCtrl->vaCtrlTbl;   
  for (i = 0; i < numVar; ++i, ++rdVaCtrl)   
    {   
    va = rdVaCtrl->va;   
    if (va)   /* VA was resolved, we can deal with it       */   
      {   
      mvl_get_runtime (va->type_id, &rt, &numRt);   
      rdVaCtrl->acc_rslt_tag = ACC_RSLT_SUCCESS;   
      rdVaCtrl->numPrimDataDone = 0;   
      if (va->base_va != NULL)   /* UCA variable handling ... */   
        {   
        rdVaCtrl->numPrimData = countPrimEl (va, rt, numRt);    
        }   
      else   
        rdVaCtrl->numPrimData = 1;   
      }   
    else   
      rdVaCtrl->numPrimData = 1;   
    }   
   
/* Now we will go through each var being read and invoke the rdInd  */   
/* function for it.                         */   
   
  rdVaCtrl = rdCtrl->vaCtrlTbl;   
  for (i = 0; i < numVar; ++i, ++rdVaCtrl)   
    {   
    va = rdVaCtrl->va;   
    if (va)   /* VA was resolved, we can deal with it       */   
      {   
   
    /* The VA's data pointer is valid, as is the type ID.       */   
    /* We want to call the handlers for all primitive level functions   */   
    /* for this data type                       */   
   
      if (va->base_va != NULL)   /* UCA variable handling ... */   
        {   
        mvl_get_runtime (va->type_id, &rt, &numRt);   
   
        /* Initialize prim_info struct. */   
        if (init_prim_info (va, &prim_info))   
          { /* Failed. Can't process this var. Skip to next var in list*/   
          MVL_LOG_ERR1 ("init_prim_info failed for 'Read' of variable '%s'", va->name);   
          rdVaCtrl->acc_rslt_tag = ACC_RSLT_FAILURE;   
          continue; /* skip to next variable    */   
          }   
   
#if defined (IEC_61850_SERVER_OPC_CLIENT)   
        /* OPC Client code runs in a separate thread and writes data to  
         * the static data buffer (i.e. va->base_va->data). Need this  
         * semaphore to protect access to the static data buffer.  
         * CRITICAL: OPC Client must use the same semaphore.  
         */   
        S_LOCK_COMMON_RESOURCES ();   
#endif   
        startElReads (indCtrl, rdCtrl, rdVaCtrl, rt, numRt, &prim_info);    
#if defined (IEC_61850_SERVER_OPC_CLIENT)   
        S_UNLOCK_COMMON_RESOURCES ();   
#endif   
        }   
      else      /* Non-UCA variable handling ... */   
        {   
        mvluRdVaCtrl = (MVLU_RD_VA_CTRL *) M_CALLOC (MSMEM_MVLU_VA_CTRL, 1,    
                    sizeof (MVLU_RD_VA_CTRL));   
        mvluRdVaCtrl->primData = (ST_CHAR *) va->data;   
        mvluRdVaCtrl->indCtrl = indCtrl;   
        mvluRdVaCtrl->rdVaCtrl = rdVaCtrl;   
        (*mvluAsyncRdIndFun)(mvluRdVaCtrl);   
        }   
      }   
    else        /* VA not found, let it be done */   
      {   
      mvluRdVaCtrl = (MVLU_RD_VA_CTRL *) M_CALLOC (MSMEM_MVLU_VA_CTRL, 1,    
                    sizeof (MVLU_RD_VA_CTRL));   
      mvluRdVaCtrl->indCtrl = indCtrl;   
      mvluRdVaCtrl->rdVaCtrl = rdVaCtrl;   
      mvlu_rd_prim_done (mvluRdVaCtrl, SD_SUCCESS);   
      }   
    }   
  }   
   
/************************************************************************/   
/*          mvlu_trim_branch_name               */   
/* Find last '$' in name and replace it with NULL.          */   
/************************************************************************/   
   
ST_VOID mvlu_trim_branch_name (ST_CHAR *branch_name)   
  {   
ST_CHAR *ptr;   
  if ((ptr = strrchr (branch_name, '$')) != NULL)   /* find last '$'*/   
    *ptr = 0;   /* replace '$' with NULL    */   
  return;   
  }   
   
/************************************************************************/   
/*          startElReads                    */   
/************************************************************************/   
   
static ST_VOID startElReads (MVL_IND_PEND *indCtrl, MVLAS_READ_CTRL *rdCtrl,   
             MVLAS_RD_VA_CTRL *rdVaCtrl,    
             RUNTIME_TYPE *rt, ST_INT rt_num, PRIM_INFO *prim_info)   
  {   
ST_INT i;   
MVLU_RD_VA_CTRL *mvluRdVaCtrl;   
ST_RTINT rdIndFunIndex;   
ST_UCHAR el_tag;   
ST_RTINT el_size;   
ST_RTINT num_rt_blks;   
#if defined(MVL_XNAME)   
ST_CHAR element_name[MAX_IDENT_LEN+1];     
ST_CHAR branch_name[MAX_IDENT_LEN+1];     
MVL_VAR_ASSOC *va;   
ST_CHAR *comp_name;   
#endif   
   
#if defined(MVL_XNAME)   
  va = rdVaCtrl->va;   
  element_name[0]=0;   
  strcpy (branch_name,va->name);   
   
/* if this is a substructure we need to trim back one level of the name */   
/* because the first rt element will be the name of the component   */   
/* already part of the va->name                      */   
   
  if ((rt_num > 1) && (strstr (branch_name,"$")))   
    {   
    mvlu_trim_branch_name (branch_name);   
    }   
#endif   
   
  for (i = 0; i < rt_num; ++i, ++rt)   
    {   
   
#if defined(MVL_XNAME)   
    if (rt_num > 1)   
      {   
      comp_name = ms_comp_name_find (rt);   
      if (strlen (comp_name))   
        {   
        if ((rt->el_tag == RT_STR_START))   
          {   
          strcat (branch_name, "$");   
          strcat (branch_name, comp_name);   
          strcpy (element_name, branch_name);   
          }   
        else   
          {   
      element_name[0]=0;   
      strcat (element_name, branch_name);   
      strcat (element_name, "$");   
      strcat (element_name, comp_name);   
      }   
        }   
   
      if (rt->el_tag == RT_STR_END)   
        {   
        strcpy (element_name, branch_name);   
        mvlu_trim_branch_name (branch_name);   
        }   
      }   
    else   
      {   /* only one element in the RT table assume it's a primitive   */   
      strcpy (element_name, branch_name);   
      }   
#endif   
   
    el_tag = rt->el_tag;   
    num_rt_blks = rt->u.arr.num_rt_blks;   
    el_size = rt->el_size;   
   
    if (ms_is_rt_prim (rt) == SD_TRUE)   
      {   
      rdIndFunIndex = rt->mvluTypeInfo.rdIndFunIndex;   
      if (u_mvlu_leaf_rd_ind_fun != NULL ||    
          (rdIndFunIndex >= 0 && rdIndFunIndex < mvluNumRdFunEntries))   
        {   
        mvluRdVaCtrl = (MVLU_RD_VA_CTRL *) M_CALLOC (MSMEM_MVLU_VA_CTRL, 1,    
                         sizeof (MVLU_RD_VA_CTRL));   
        mvluRdVaCtrl->rt = rt;   
#if defined(MVL_XNAME)   
        strcpy (mvluRdVaCtrl->xName, element_name);   
#endif   
        mvluRdVaCtrl->primData = (ST_CHAR *) rdVaCtrl->va->data +    
                             prim_info->prim_offset;   
        mvluRdVaCtrl->indCtrl = indCtrl;   
        mvluRdVaCtrl->rdVaCtrl = rdVaCtrl;   
#if defined(MVLU_USE_REF)   
        mvluRdVaCtrl->primRef = rt->mvluTypeInfo.ref;   
#endif   
        mvluRdVaCtrl->prim_num = prim_info->prim_num;   
        mvluRdVaCtrl->prim_offset_base = prim_info->prim_offset_base;   
        if (u_mvlu_leaf_rd_ind_fun == NULL)   
          (*mvluRdFunInfoTbl[rdIndFunIndex].fun_ptr)(mvluRdVaCtrl);   
    else   
          (*u_mvlu_leaf_rd_ind_fun)(mvluRdVaCtrl);   
        }   
      else   
        rdVaCtrl->acc_rslt_tag = ACC_RSLT_FAILURE;   
      prim_info->prim_num++;   
      }   
   
    if (el_tag == RT_ARR_START)   
      {   
      startArrRds (indCtrl, rdCtrl, rdVaCtrl, rt, prim_info);   
      i += (num_rt_blks + 1);   
      rt += (num_rt_blks + 1);   
      }   
    else   
      {   
      prim_info->prim_offset      += el_size;   
      prim_info->prim_offset_base += el_size;   
      }   
    }   
  }   
   
/************************************************************************/   
/*                  startArrRds             */   
/************************************************************************/   
   
static ST_VOID startArrRds (MVL_IND_PEND *indCtrl, MVLAS_READ_CTRL *rdCtrl,   
             MVLAS_RD_VA_CTRL *rdVaCtrl, RUNTIME_TYPE *rt, PRIM_INFO *prim_info)   
  {   
ST_RTINT i;   
MVL_VAR_ASSOC *va;   
ST_INT numRt;   
ST_RTINT low_index;   
ST_RTINT num_elmnts;   
   
  numRt = rt->u.arr.num_rt_blks+2;   
  va = rdVaCtrl->va;   
   
  if (va->arrCtrl.arrAltAccPres == SD_TRUE)   
    {   
    low_index = va->arrCtrl.low_index;   
    num_elmnts = va->arrCtrl.num_elmnts;   
    }   
  else   
    {   
    low_index = 0;   
    num_elmnts = rt->u.arr.num_elmnts;   
    }   
   
/* Let's check to see if the client is selecting a sub-object ...   */   
  prim_info->prim_offset      += rt->el_size;   
  prim_info->prim_offset_base += rt->el_size;   
  for (i = 0; i < num_elmnts; ++i)   
    {   
    va->arrCtrl.curr_index = low_index + i;   
    startElReads (indCtrl, rdCtrl, rdVaCtrl, rt+1, numRt-2, prim_info);   
    }   
  prim_info->prim_offset      += (rt+numRt-1)->el_size;   
  prim_info->prim_offset_base += (rt+numRt-1)->el_size;   
  }   
   
/************************************************************************/   
/*          mvlu_rd_prim_done               */   
/************************************************************************/   
/* The user calls this function acynchronously when the primitive data  */   
/* has been put in the 'primData' buffer.               */   
   
ST_VOID mvlu_rd_prim_done (MVLU_RD_VA_CTRL *mvluRdVaCtrl, ST_RET rc)   
  {   
MVL_IND_PEND *indCtrl;   
MVLAS_READ_CTRL *rdCtrl;   
MVLAS_RD_VA_CTRL *rdVaCtrl;   
ST_INT i;   
   
  rdVaCtrl = mvluRdVaCtrl->rdVaCtrl;   
   
  if (rc != SD_SUCCESS)   
    {   
/* DEBUG LIZ: added logging why read failed */   
    MVL_LOG_NERR2 ("Read failed for UCA variable '%s' rc=%d",   
      rdVaCtrl->va->name, rc);   
    rdVaCtrl->acc_rslt_tag = ACC_RSLT_FAILURE;   
    }   
       
  ++rdVaCtrl->numPrimDataDone;   
  if (rdVaCtrl->numPrimDataDone == rdVaCtrl->numPrimData)   
    {   
    /* All primitives for "one" variable are complete.      */   
    indCtrl = mvluRdVaCtrl->indCtrl;   
   
    if (indCtrl->scan_va_done_fun)   
      (*indCtrl->scan_va_done_fun)(indCtrl, rdVaCtrl->va);   
   
  /* If all primitives for all variables are complete, respond now */   
    rdCtrl = &indCtrl->u.rd;   
    rdVaCtrl = rdCtrl->vaCtrlTbl;   
    for (i = 0; i < rdCtrl->numVar; ++i, ++rdVaCtrl)   
      {   
      if (rdVaCtrl->numPrimDataDone != rdVaCtrl->numPrimData)   
        break;   
      }   
    if (i == rdCtrl->numVar)   
      {   
      if (indCtrl->op != MMSOP_READ)   
        {   /* Not normal read. Do special processing.  */   
        if (indCtrl->usr_resp_fun)   
          { /* If user set custom resp fun, call it.    */   
          (*indCtrl->usr_resp_fun) (indCtrl);   
          }   
        }   
      else   
        mvlas_read_resp (indCtrl);   
      }   
    }   
  M_FREE (MSMEM_MVLU_VA_CTRL, mvluRdVaCtrl);   
  }   
   
/************************************************************************/   
/*          mvluDefAsyncRdIndFun                */   
/************************************************************************/   
   
static ST_VOID mvluDefAsyncRdIndFun (struct mvlu_rd_va_ctrl *mvluRdVaCtrl)   
  {   
  mvlu_rd_prim_done (mvluRdVaCtrl, SD_SUCCESS);   
  }   
   
/************************************************************************/   
/************************************************************************/   
/*          mvlu_write_ind                  */   
/************************************************************************/   
   
ST_VOID u_mvl_write_ind (MVL_IND_PEND *indCtrl)   
  {   
MVLAS_WRITE_CTRL *wrCtrl;   
MVLAS_WR_VA_CTRL *wrVaCtrl;   
MVL_VAR_ASSOC *va;   
MVLU_WR_VA_CTRL *mvluWrVaCtrl;   
ST_INT i;   
ST_INT numVar;   
RUNTIME_TYPE *rt;   
ST_INT numRt;   
PRIM_INFO prim_info;   
   
  wrCtrl = &indCtrl->u.wr;   
  numVar = wrCtrl->numVar;   
   
/* First we will go through each variable being written and count the   */   
/* primitive elelemts.                          */   
  wrVaCtrl = wrCtrl->vaCtrlTbl;   
   
  for (i = 0; i < numVar; ++i, ++wrVaCtrl)   
    {   
    va = wrVaCtrl->va;   
    if (va)     /* VA was resolved, we can look it over     */   
      {   
      mvl_get_runtime (va->type_id, &rt, &numRt);   
      wrVaCtrl->resp_tag = WR_RSLT_SUCCESS;   
      wrVaCtrl->numPrimDataDone = 0;   
      if (va->base_va != NULL)   /* UCA variable handling ... */   
        wrVaCtrl->numPrimData = countPrimEl (va, rt, numRt);   
      else   
        wrVaCtrl->numPrimData = 1;   
      }   
    else   
      wrVaCtrl->numPrimData = 1;   
    }   
   
/* Now we will go through each var being written and invoke the wrInd   */   
/* function for it.                         */   
   
  wrVaCtrl = wrCtrl->vaCtrlTbl;   
  for (i = 0; i < numVar; ++i, ++wrVaCtrl)   
    {   
    va = wrVaCtrl->va;   
    if (va)     /* VA was resolved, we can look it over     */   
      {   
    /* The VA's data pointer is valid, as is the type ID.       */   
    /* We want to call the handlers for all primitive level functions   */   
    /* for this data type                       */   
      if (va->base_va != NULL)   /* UCA variable handling ... */   
        {   
        mvl_get_runtime (va->type_id, &rt, &numRt);   
   
        /* Initialize prim_info struct. */   
        if (init_prim_info (va, &prim_info))   
          { /* Failed. Can't process this var. Skip to next var in list*/   
          MVL_LOG_ERR1 ("init_prim_info failed for 'Write' of variable '%s'", va->name);   
          wrVaCtrl->resp_tag = WR_RSLT_FAILURE;   
          continue; /* skip to next variable    */   
          }   
        startElWrites (indCtrl, wrCtrl,wrVaCtrl, rt, numRt, &prim_info);    
        }   
      else      /* Non-UCA variable handling ... */   
        {   
        mvluWrVaCtrl = (MVLU_WR_VA_CTRL *) M_CALLOC (MSMEM_MVLU_VA_CTRL, 1,    
                    sizeof (MVLU_WR_VA_CTRL));   
        mvluWrVaCtrl->primData = (ST_CHAR *) va->data;   
        mvluWrVaCtrl->indCtrl = indCtrl;   
        mvluWrVaCtrl->wrVaCtrl = wrVaCtrl;   
        (*mvluAsyncWrIndFun)(mvluWrVaCtrl);   
        }   
      }   
    else       
      {    
      mvluWrVaCtrl = (MVLU_WR_VA_CTRL *) M_CALLOC (MSMEM_MVLU_VA_CTRL, 1,    
                    sizeof (MVLU_WR_VA_CTRL));   
      mvluWrVaCtrl->indCtrl = indCtrl;   
      mvluWrVaCtrl->wrVaCtrl = wrVaCtrl;   
      mvlu_wr_prim_done (mvluWrVaCtrl, SD_SUCCESS);   
      }   
    }   
  }   
   
/************************************************************************/   
/*          startElWrites                   */   
/************************************************************************/   
   
static ST_VOID startElWrites (MVL_IND_PEND *indCtrl, MVLAS_WRITE_CTRL *wrCtrl,   
              MVLAS_WR_VA_CTRL *wrVaCtrl,    
              RUNTIME_TYPE *rt, ST_INT rt_num, PRIM_INFO *prim_info)   
  {   
ST_INT i;   
MVLU_WR_VA_CTRL *mvluWrVaCtrl;   
ST_RTINT wrIndFunIndex;   
ST_UCHAR el_tag;   
ST_RTINT el_size;   
ST_RTINT num_rt_blks;   
#if defined(MVL_XNAME)   
ST_CHAR element_name[MAX_IDENT_LEN+1];     
ST_CHAR branch_name[MAX_IDENT_LEN+1];     
MVL_VAR_ASSOC *va;   
ST_CHAR *comp_name;   
#endif   
ST_CHAR sboName[MAX_SBO_NAME_SIZE+1];   
   
#if !defined(MVL61850_CTL_DISABLE)   
  ST_INT oper_nest_level = 0;   
  MVL_NET_INFO *save_net_info = indCtrl->event->net_info; /* save to use after indCtrl free*/   
  MVL_VAR_ASSOC *base_var = wrVaCtrl->va->base_va;   
#endif   
   
#if defined(MVL_XNAME)   
  va = wrVaCtrl->va;   
  element_name[0]=0;   
  strcpy (branch_name,va->name);   
   
/* if this is a substructure we need to trim back one level of the name */   
/* because the first rt element will be the name of the component   */   
/* already part of the va->name                          */   
   
  if ((rt_num > 1) && (strstr (branch_name, "$")))   
    {   
    mvlu_trim_branch_name (branch_name);   
    }   
#endif   
   
  for (i = 0; i < rt_num; ++i, ++rt)   
    {   
#if defined(MVL_XNAME)   
    if (rt_num > 1)  /* is this a collection of elements? */   
      {   
      comp_name = ms_comp_name_find (rt);   
      if (strlen (comp_name))   
        {   
        if ((rt->el_tag == RT_STR_START))   
          {   
          strcat (branch_name, "$");   
          strcat (branch_name, comp_name);   
          strcpy (element_name, branch_name);   
          }   
        else   
          {   
      element_name[0]=0;   
      strcat (element_name, branch_name);   
      strcat (element_name, "$");   
      strcat (element_name, comp_name);   
      }   
        }   
   
      if (rt->el_tag == RT_STR_END)   
        {   
        strcpy (element_name, branch_name);   
        mvlu_trim_branch_name (branch_name);   
        }   
      }   
    else   
      {   /* only one element in the RT table assume it's a primitive */   
      strcpy (element_name, branch_name);   
      }   
#endif   
   
    el_tag = rt->el_tag;   
    num_rt_blks = rt->u.arr.num_rt_blks;   
    el_size = rt->el_size;   
   
    /* Do some special stuff for IEC 61850 SBO controls.        */   
    /* If this comp is start of struct & comp name is "Oper" or "Cancel",*/   
    /* check SBO state.                         */   
    if (rt->el_tag == RT_STR_START  &&     
        (strcmp (ms_comp_name_find(rt), "Oper") == 0  ||  strcmp (ms_comp_name_find(rt), "Cancel") == 0))   
      {   
      /* Check "Select" timeouts for ALL SBO controls.  */   
      mvlu_sbo_chk_timers ();   
      /* Chk SBO state. Save in wrVaCtrl->sboCtrl (NULL if "Select" not done).*/   
      mvl61850_sbo_create_sboname (wrVaCtrl->va, &wrVaCtrl->va_scope, sboName);   
      wrVaCtrl->sboCtrl = mvlu_sbo_chk_state (sboName, indCtrl->event->net_info);   
      }   
   
#if !defined(MVL61850_CTL_DISABLE)   
    if (rt->el_tag == RT_STR_START)   
      {   
      /* only increment this if this is "Oper" or already inside "Oper".    */   
      if (oper_nest_level>0)   
        oper_nest_level++;   
      else if (strcmp (ms_comp_name_find(rt), "Oper") == 0)   
        {   
        u_mvl61850_ctl_oper_begin (sboName);   
        oper_nest_level++;   
        }   
      }   
    if (rt->el_tag == RT_STR_END)   
      {   
      if (oper_nest_level>0)   
        {   
        if (--oper_nest_level==0)   
          {   
          /* All leaf functs have been called for "Oper" struct.*/   
          /* CRITICAL: use save_net_info because indCtrl may have been freed.*/   
          u_mvl61850_ctl_oper_end (save_net_info, sboName, base_var);   
          }   
        }   
      }   
#endif  /* !defined(MVL61850_CTL_DISABLE)   */   
   
    if (ms_is_rt_prim (rt) == SD_TRUE)   
      {   
      wrIndFunIndex = rt->mvluTypeInfo.wrIndFunIndex;   
      if (u_mvlu_leaf_wr_ind_fun != NULL ||    
          (wrIndFunIndex >= 0 && wrIndFunIndex < mvluNumWrFunEntries))   
        {   
        mvluWrVaCtrl = (MVLU_WR_VA_CTRL *) M_CALLOC (MSMEM_MVLU_VA_CTRL, 1,   
                                             sizeof (MVLU_WR_VA_CTRL));   
#if defined(MVL_XNAME)   
        strcpy (mvluWrVaCtrl->xName, element_name);   
#endif   
        mvluWrVaCtrl->primData = (ST_CHAR *) wrVaCtrl->va->data +    
                             prim_info->prim_offset;   
        mvluWrVaCtrl->indCtrl = indCtrl;   
        mvluWrVaCtrl->wrVaCtrl = wrVaCtrl;   
        mvluWrVaCtrl->rt = rt;   
#if defined(MVLU_USE_REF)   
        mvluWrVaCtrl->primRef = rt->mvluTypeInfo.ref;   
#endif   
        mvluWrVaCtrl->prim_num = prim_info->prim_num;   
        mvluWrVaCtrl->prim_offset_base = prim_info->prim_offset_base;   
        if (u_mvlu_leaf_wr_ind_fun == NULL)   
          (*mvluWrFunInfoTbl[wrIndFunIndex].fun_ptr)(mvluWrVaCtrl);   
    else   
          (*u_mvlu_leaf_wr_ind_fun)(mvluWrVaCtrl);   
        }   
      else   
        wrVaCtrl->resp_tag = WR_RSLT_FAILURE;   
      prim_info->prim_num++;   
      }   
   
    if (el_tag == RT_ARR_START)   
      {   
      startArrWrs (indCtrl, wrCtrl, wrVaCtrl, rt, prim_info);   
      i += (num_rt_blks + 1);   
      rt += (num_rt_blks + 1);   
      }   
    else   
      {   
      prim_info->prim_offset      += el_size;   
      prim_info->prim_offset_base += el_size;   
      }   
    }   
  }   
   
/************************************************************************/   
/*          startArrWrs                 */   
/************************************************************************/   
   
static ST_VOID startArrWrs (MVL_IND_PEND *indCtrl, MVLAS_WRITE_CTRL *wrCtrl,   
             MVLAS_WR_VA_CTRL *wrVaCtrl, RUNTIME_TYPE *rt, PRIM_INFO *prim_info)   
  {   
ST_RTINT i;   
MVL_VAR_ASSOC *va;   
ST_INT numRt;   
ST_RTINT low_index;   
ST_RTINT num_elmnts;   
   
  numRt = rt->u.arr.num_rt_blks+2;   
  va = wrVaCtrl->va;   
   
  if (va->arrCtrl.arrAltAccPres == SD_TRUE)   
    {   
    low_index = va->arrCtrl.low_index;   
    num_elmnts = va->arrCtrl.num_elmnts;   
    }   
  else   
    {   
    low_index = 0;   
    num_elmnts = rt->u.arr.num_elmnts;   
    }   
   
/* Let's check to see if the client is selecting a sub-object ...   */   
  prim_info->prim_offset      += rt->el_size;   
  prim_info->prim_offset_base += rt->el_size;   
  for (i = 0; i < num_elmnts; ++i)   
    {   
    va->arrCtrl.curr_index = low_index + i;   
    startElWrites (indCtrl, wrCtrl, wrVaCtrl, rt+1, numRt-2, prim_info);   
    }   
  prim_info->prim_offset      += (rt+numRt-1)->el_size;   
  prim_info->prim_offset_base += (rt+numRt-1)->el_size;   
  }   
   
/************************************************************************/   
/*           mvlu_wr_prim_done              */   
/************************************************************************/   
/* The user calls this function acynchronously when the primitive data  */   
/* has been put in the 'primData' buffer.               */   
   
ST_VOID mvlu_wr_prim_done (MVLU_WR_VA_CTRL *mvluWrVaCtrl, ST_RET rc)   
  {   
MVL_IND_PEND *indCtrl;   
MVLAS_WRITE_CTRL *wrCtrl;   
MVLAS_WR_VA_CTRL *wrVaCtrl;   
ST_INT i;   
   
  wrVaCtrl = mvluWrVaCtrl->wrVaCtrl;   
  if (rc != SD_SUCCESS)   
    wrVaCtrl->resp_tag = WR_RSLT_FAILURE;   
       
  ++wrVaCtrl->numPrimDataDone;   
  if (wrVaCtrl->numPrimDataDone == wrVaCtrl->numPrimData)   
    {   
    indCtrl = mvluWrVaCtrl->indCtrl;   
    wrCtrl = &indCtrl->u.wr;   
   
  /* If all primitives for all variables are complete, respond now */   
    wrVaCtrl = wrCtrl->vaCtrlTbl;   
    for (i = 0; i < wrCtrl->numVar; ++i, ++wrVaCtrl)   
      {   
      if (wrVaCtrl->numPrimDataDone != wrVaCtrl->numPrimData)   
        break;   
      else   
        {               /* Done writing this var.   */   
        /* Do special cleanup for IEC 61850 SBO controls.       */   
        /* If wrVaCtrl->sboCtrl != NULL, this var is SBO Oper or Cancel  */   
        /* (see startElWrites), so do SBO cleanup.          */   
        if (wrVaCtrl->sboCtrl)   
          {   
          mvlu_sbo_ctrl_free (wrVaCtrl->sboCtrl);   
          wrVaCtrl->sboCtrl = NULL;  /* reset this so free not done twice.*/   
          }   
          
#if !defined(MVL61850_CTL_DISABLE)   
        /* If Write failed, and Var is IEC 61850 Oper, Cancel, or SBOw, */   
        /* must also send InformationReport containing LastApplError.   */   
        if (wrVaCtrl->resp_tag == WR_RSLT_FAILURE && wrVaCtrl->va != NULL)    /* var might not be found*/   
          {   
          ST_CHAR *lastdollar = strrchr (wrVaCtrl->va->name, '$');    /* find last '$'*/   
          /* lastdollar should ALWAYS be valid*/   
          /* If var name ends in Oper, etc, call user funct to send LastApplError.*/   
          if (strcmp (lastdollar+1, "Oper") == 0 ||   
              strcmp (lastdollar+1, "Cancel") == 0 ||   
              strcmp (lastdollar+1, "SBOw") == 0)   
        {   
            /* Construct & send LastApplError info report.*/   
            ST_CHAR *ptr;   /* ptr into CntrlObj string */   
   
            /* Fill in "LastApplError" structure.   */   
            /* Construct CntrlObj from var->name. Other members  (Error, */   
            /* AddCause, Origin, ctlNum) already set by leaf functions. */   
            strcpy (wrVaCtrl->LastApplError.CntrlObj, wrVaCtrl->va->name);   
            ptr = strrchr (wrVaCtrl->LastApplError.CntrlObj, '$');   /* find last '$'*/   
            strcpy (ptr+1, "Oper"); /* replace last component name with "Oper"*/   
            mvl61850_ctl_lastapplerror_send (indCtrl->event->net_info, &wrVaCtrl->LastApplError);   
            }   
          }   
#endif  /* !defined(MVL61850_CTL_DISABLE)   */   
        }   
      }   
    if (i == wrCtrl->numVar)   
      {   
      mvlas_write_resp (indCtrl);   
      }   
    }   
  M_FREE (MSMEM_MVLU_VA_CTRL, mvluWrVaCtrl);   
  }   
   
   
/************************************************************************/   
/*          mvluDefAsyncWrIndFun                */   
/************************************************************************/   
   
static ST_VOID mvluDefAsyncWrIndFun (struct mvlu_wr_va_ctrl *mvluWrVaCtrl)   
  {   
  mvlu_wr_prim_done (mvluWrVaCtrl, SD_SUCCESS);   
  }   
   
/************************************************************************/   
/************************************************************************/   
/*          countPrimEl                 */   
/************************************************************************/   
   
static ST_INT countPrimEl (MVL_VAR_ASSOC *va, RUNTIME_TYPE *rt, ST_INT rt_num)   
  {   
ST_INT i;   
ST_INT num_elmnts;   
ST_INT numPrimData;   
ST_INT subElCount;   
   
  numPrimData = 0;   
  for (i = 0; i < rt_num; ++i, ++rt)   
    {   
    if (ms_is_rt_prim (rt) == SD_TRUE)   
      ++numPrimData;   
    if (rt->el_tag == RT_ARR_START)   
      {   
      subElCount = countPrimEl (va, rt+1, rt->u.arr.num_rt_blks);   
   
      if (va->arrCtrl.arrAltAccPres == SD_FALSE)   
        num_elmnts = rt->u.arr.num_elmnts;   
      else   
        num_elmnts = va->arrCtrl.num_elmnts;   
   
      numPrimData += (subElCount * num_elmnts);   
   
      i += (rt->u.arr.num_rt_blks + 1);   
      rt += (rt->u.arr.num_rt_blks + 1);   
      }   
    }   
  return (numPrimData);   
  }   
   
/************************************************************************/   
/*          mvluDefGetVaDataBufFun              */   
/************************************************************************/   
   
static ST_VOID mvluDefGetVaDataBufFun (ST_INT service,    
                       MVL_VAR_ASSOC *va, ST_INT size)   
  {   
  if (service == MMSOP_READ || service == MMSOP_WRITE ||    
      service == MMSOP_INFO_RPT || service == MMSOP_RDWR_USR_HANDLED)   
    {   
    /* If static data used, va->data points within base_va.  */   
    if (!va->base_va->use_static_data)   
      va->data = M_MALLOC (MSMEM_MVLU_VA_DATA, size);   
    else   
      va->data = (((ST_CHAR *) va->base_va->data) + va->offset_from_base);   
    }   
  else if (service == MMSOP_MVLU_RPT_VA)   
    {   
    }   
  }   
   
/************************************************************************/   
/*          mvluDefFreeVaDataBufFun             */   
/************************************************************************/   
   
static ST_VOID mvluDefFreeVaDataBufFun (ST_INT service, MVL_VAR_ASSOC *va)   
  {   
  if (service == MMSOP_READ || service == MMSOP_WRITE ||   
      service == MMSOP_INFO_RPT || service == MMSOP_RDWR_USR_HANDLED)   
    {   
    if (!va->base_va->use_static_data)   
      M_FREE (MSMEM_MVLU_VA_DATA, va->data);   
    }   
  else if (service == MMSOP_MVLU_RPT_VA)   
    {   
    }   
  }   
   
   
/************************************************************************/   
/*          mvlu_find_rt_leaf               */   
/************************************************************************/   
   
RUNTIME_TYPE *mvlu_find_rt_leaf (ST_INT type_id, ST_CHAR *leafName)   
  {   
RUNTIME_TYPE *rt;   
ST_INT numRt;    
ST_RET ret;   
   
  ret = mvl_get_runtime (type_id, &rt, &numRt);   
  if (ret != SD_SUCCESS)   
    {   
    MVL_LOG_NERR1 ("Could not get RT type for type id %d", type_id);   
    return (NULL);   
    }   
   
  ret = mvlu_find_uca_var (&rt, &numRt, leafName);   
  if (ret != SD_SUCCESS)   
    {   
    MVL_LOG_NERR1 ("Could not find leaf '%s'", leafName);   
    return (NULL);   
    }   
   
  if (numRt != 1)   
    {   /* See if this is an array with a single primitive element */   
    if (rt->el_tag == RT_ARR_START && numRt == 3)    
      return (rt+1);   
   
    MVL_LOG_NERR1 ("'%s' is a branch, not a leaf", leafName);   
    return (NULL);   
    }   
   
   
  return (rt);   
  }   
   
/************************************************************************/   
/*          mvlu_find_comp_type             */   
/* Arguments:                               */   
/*  base_type_id    type ID for top level variable          */   
/*  flatname        flattened component name with top level var */   
/*          name (Logical Node) stripped off.       */   
/*  sub_rt_type     Ptr to (RUNTIME_TYPE *) to be filled in.    */   
/*  sub_rt_num      Ptr to "count" to be filled in.         */   
/************************************************************************/   
ST_RET mvlu_find_comp_type (ST_INT base_type_id, ST_CHAR *flatname,   
            RUNTIME_TYPE **sub_rt_type,     /* out  */   
            ST_INT *sub_rt_num)         /* out  */   
  {   
MVL_TYPE_CTRL *base_type_ctrl;   
ST_RET ret;   
   
  base_type_ctrl = mvl_type_ctrl_find (base_type_id);   
  if (base_type_ctrl)   
    {   
    *sub_rt_type = base_type_ctrl->rt;   
    *sub_rt_num = base_type_ctrl->num_rt;   
    ret = mvlu_find_uca_var (sub_rt_type, sub_rt_num, flatname);   
    }   
  else   
    ret = SD_FAILURE;   
   
  /* NOTE: don't log error here. Caller may often pass invalid names.   */   
  return (ret);   
  }   
   
/************************************************************************/   
/*          mvlu_get_leaf_val_int8          */   
/* Get leaf data value for type ST_INT8.                */    
/* Arguments:                               */   
/*   base_va    base variable (i.e. logical node)           */   
/*   flatname   flattened leaf name (e.g. ST$Mod$stVal)         */   
/*   data   ptr to data written by this function            */   
/* RETURNS: SD_SUCCESS or SD_FAILURE                */   
/************************************************************************/   
ST_RET mvlu_get_leaf_val_int8 (MVL_VAR_ASSOC *base_va, ST_CHAR *flatname, ST_INT8 *data)   
  {   
RUNTIME_TYPE *comp_rt_type; /* first rt_type of component   */   
ST_INT comp_rt_num;     /* num of rt_types in component */   
ST_RET ret = SD_FAILURE;   
   
  /* Find the attribute type.   */   
  if (mvlu_find_comp_type (base_va->type_id,   
           flatname,    /* flattened name   */   
       &comp_rt_type,   /* ptr to val set by function   */   
       &comp_rt_num)    /* ptr to val set by function   */   
      == SD_SUCCESS)   
    {   
    if (comp_rt_type->el_tag == RT_INTEGER && comp_rt_type->u.p.el_len == 1)   
      { /* must be INT8 */   
      *data = *(ST_INT8 *)((ST_CHAR *) base_va->data + comp_rt_type->mvluTypeInfo.offSet);   
      ret = SD_SUCCESS;   
      }   
    else   
      MVL_LOG_ERR1 ("Invalid type for '%s' attribute. Cannot use it.", flatname);   
    }   
  else   
    MVLU_LOG_FLOW2 ("Cannot find '%s' attribute in this variable '%s'",   
                  flatname, base_va->name);   
  return (ret);   
  }   
/************************************************************************/   
/*          mvlu_get_leaf_val_int32         */   
/* Get leaf data value for type ST_INT32.               */    
/* Arguments:                               */   
/*   base_va    base variable (i.e. logical node)           */   
/*   flatname   flattened leaf name (e.g. ST$Mod$stVal)         */   
/*   data   ptr to data written by this function            */   
/* RETURNS: SD_SUCCESS or SD_FAILURE                */   
/************************************************************************/   
ST_RET mvlu_get_leaf_val_int32 (MVL_VAR_ASSOC *base_va, ST_CHAR *flatname, ST_INT32 *data)   
  {   
RUNTIME_TYPE *comp_rt_type; /* first rt_type of component   */   
ST_INT comp_rt_num;     /* num of rt_types in component */   
ST_RET ret = SD_FAILURE;   
   
  /* Find the attribute type.   */   
  if (mvlu_find_comp_type (base_va->type_id,   
           flatname,    /* flattened name   */   
       &comp_rt_type,   /* ptr to val set by function   */   
       &comp_rt_num)    /* ptr to val set by function   */   
      == SD_SUCCESS)   
    {   
    if (comp_rt_type->el_tag == RT_INTEGER && comp_rt_type->u.p.el_len == 4)   
      { /* must be INT32    */   
      *data = *(ST_INT32 *)((ST_CHAR *) base_va->data + comp_rt_type->mvluTypeInfo.offSet);   
      ret = SD_SUCCESS;   
      }   
    else   
      MVL_LOG_ERR1 ("Invalid type for '%s' attribute. Cannot use it.", flatname);   
    }   
  else   
    MVLU_LOG_FLOW2 ("Cannot find '%s' attribute in this variable '%s'",   
                  flatname, base_va->name);   
  return (ret);   
  }   
/************************************************************************/   
/*          mvlu_get_leaf_val_int_any           */   
/* Get leaf value for any signed integer. Cast value to ST_INT32.   */   
/************************************************************************/   
ST_RET mvlu_get_leaf_val_int_any (MVL_VAR_ASSOC *base_va, ST_CHAR *flatname, ST_INT32 *data)   
  {     
RUNTIME_TYPE *comp_rt_type; /* first rt_type of component   */   
ST_INT comp_rt_num;     /* num of rt_types in component */   
ST_CHAR *raw_data;  /* ptr to data. May be any integer size */   
ST_INT8 tmp_int8;   /* Used to get val as INT8, then cast to INT32  */   
ST_INT16 tmp_int16; /* Used to get val as INT16, then cast to INT32 */   
ST_RET retcode = SD_SUCCESS;   
   
  /* Find the attribute type.   */   
  if (mvlu_find_comp_type (base_va->type_id,   
          flatname,     /* flattened name   */   
          &comp_rt_type,    /* ptr to val set by function   */   
          &comp_rt_num)     /* ptr to val set by function   */   
      == SD_SUCCESS)   
    {   
    raw_data = (ST_CHAR *) base_va->data + comp_rt_type->mvluTypeInfo.offSet;   
    if (comp_rt_type->el_tag == RT_INTEGER)   
      {   
      if (comp_rt_type->u.p.el_len == 1) /* INT8 */   
        {   
        tmp_int8 = *(ST_INT8 *) raw_data;   
        *data = (ST_INT32) tmp_int8;   
        }   
      else if (comp_rt_type->u.p.el_len == 2)    /* INT16    */   
        {   
        tmp_int16 = *(ST_INT16 *) raw_data;   
        *data = (ST_INT32) tmp_int16;   
        }   
      else if (comp_rt_type->u.p.el_len == 4)    /* INT32    */   
        {   
        *data = *(ST_INT32 *) raw_data;   
        }   
      else   
        {     
        MVL_LOG_ERR2 ("Unsupported integer size for '%s' attribute in variable '%s'",   
                  flatname, base_va->name);   
        retcode = SD_FAILURE;   
        }   
      }   
    else   
      {     
      MVL_LOG_ERR2 ("Invalid type for '%s' attribute in variable '%s'. Cannot get value.",   
                  flatname, base_va->name);   
      retcode = SD_FAILURE;   
      }   
    }   
  else   
    {     
    MVLU_LOG_FLOW2 ("Cannot find '%s' attribute in variable '%s'",   
                  flatname, base_va->name);   
    retcode = SD_FAILURE;   
    }   
  return (retcode);   
  }   
/************************************************************************/   
/*          mvlu_get_leaf_val_uint32            */   
/* Get leaf data value for type ST_UINT32.              */    
/* Arguments:                               */   
/*   base_va    base variable (i.e. logical node)           */   
/*   flatname   flattened leaf name (e.g. ST$Mod$stVal)         */   
/*   data   ptr to data written by this function            */   
/* RETURNS: SD_SUCCESS or SD_FAILURE                */   
/************************************************************************/   
ST_RET mvlu_get_leaf_val_uint32 (MVL_VAR_ASSOC *base_va, ST_CHAR *flatname, ST_UINT32 *data)   
  {   
RUNTIME_TYPE *comp_rt_type; /* first rt_type of component   */   
ST_INT comp_rt_num;     /* num of rt_types in component */   
ST_RET ret = SD_FAILURE;   
   
  /* Find the attribute type.   */   
  if (mvlu_find_comp_type (base_va->type_id,   
           flatname,        /* flattened name   */   
       &comp_rt_type,   /* ptr to val set by function   */   
       &comp_rt_num)    /* ptr to val set by function   */   
      == SD_SUCCESS)   
    {   
    if (comp_rt_type->el_tag == RT_UNSIGNED && comp_rt_type->u.p.el_len == 4)   
      { /* must be UINT32   */   
      *data = *(ST_UINT32 *)((ST_CHAR *) base_va->data + comp_rt_type->mvluTypeInfo.offSet);   
      ret = SD_SUCCESS;   
      }   
    else   
      MVL_LOG_ERR1 ("Invalid type for '%s' attribute. Cannot use it.", flatname);   
    }   
  else   
    MVLU_LOG_FLOW2 ("Cannot find '%s' attribute in this variable '%s'",   
                  flatname, base_va->name);   
  return (ret);   
  }   
/************************************************************************/   
/*          mvlu_get_leaf_val_boolean           */   
/* Get leaf data value for type ST_BOOLEAN.             */    
/* Arguments:                               */   
/*   base_va    base variable (i.e. logical node)           */   
/*   flatname   flattened leaf name (e.g. ST$Mod$stVal)         */   
/*   data   ptr to data written by this function            */   
/* RETURNS: SD_SUCCESS or SD_FAILURE                */   
/************************************************************************/   
ST_RET mvlu_get_leaf_val_boolean (MVL_VAR_ASSOC *base_va, ST_CHAR *flatname, ST_BOOLEAN *data)   
  {   
RUNTIME_TYPE *comp_rt_type; /* first rt_type of component   */   
ST_INT comp_rt_num;     /* num of rt_types in component */   
ST_RET ret = SD_FAILURE;   
   
  /* Find the attribute type.   */   
  if (mvlu_find_comp_type (base_va->type_id,   
           flatname,    /* flattened name   */   
       &comp_rt_type,   /* ptr to val set by function   */   
       &comp_rt_num)    /* ptr to val set by function   */   
      == SD_SUCCESS)   
    {   
    if (comp_rt_type->el_tag == RT_BOOL)   
      { /* must be BOOLEAN  */   
      *data = *(ST_BOOLEAN *)((ST_CHAR *) base_va->data + comp_rt_type->mvluTypeInfo.offSet);   
      ret = SD_SUCCESS;   
      }   
    else   
      MVL_LOG_ERR1 ("Invalid type for '%s' attribute. Cannot use it.", flatname);   
    }   
  else   
    MVLU_LOG_FLOW2 ("Cannot find '%s' attribute in this variable '%s'",   
                  flatname, base_va->name);   
  return (ret);   
  }   
/************************************************************************/   
/*          mvlu_get_leaf_val_bvstring          */   
/* Get leaf data value for type MMS_BVSTRING.               */    
/* Arguments:                               */   
/*   base_va    base variable (i.e. logical node)           */   
/*   flatname   flattened leaf name (e.g. ST$Mod$stVal)         */   
/*   data   ptr to data written by this function            */   
/* RETURNS: SD_SUCCESS or SD_FAILURE                */   
/************************************************************************/   
ST_RET mvlu_get_leaf_val_bvstring (MVL_VAR_ASSOC *base_va, ST_CHAR *flatname,   
                                   MMS_BVSTRING *data, ST_INT max_num_bits)   
  {   
RUNTIME_TYPE *comp_rt_type; /* first rt_type of component   */   
ST_INT comp_rt_num;     /* num of rt_types in component */   
ST_RET ret = SD_FAILURE;   
ST_INT num_bytes;   
   
  /* Find the attribute type.   */   
  if (mvlu_find_comp_type (base_va->type_id,   
           flatname,    /* flattened name   */   
       &comp_rt_type,   /* ptr to val set by function   */   
       &comp_rt_num)    /* ptr to val set by function   */   
      == SD_SUCCESS)   
    {   
    /* NOTE: for Bvstring, el_len is negative.  */   
    if (comp_rt_type->el_tag == RT_BIT_STRING && comp_rt_type->u.p.el_len < 0   
        && abs (comp_rt_type->u.p.el_len) <= max_num_bits)   
      {   
      num_bytes = 2 + (abs (comp_rt_type->u.p.el_len) + 7)/8;   
      memcpy (data, (ST_CHAR *)base_va->data + comp_rt_type->mvluTypeInfo.offSet, num_bytes);   
      ret = SD_SUCCESS;   
      }   
    else   
      MVL_LOG_ERR1 ("Invalid type for '%s' attribute. Cannot use it.", flatname);   
    }   
  else   
    MVLU_LOG_FLOW2 ("Cannot find '%s' attribute in this variable '%s'",   
                  flatname, base_va->name);   
  return (ret);   
  }   
/************************************************************************/   
/*          mvlu_get_leaf_data_ptr              */   
/* Get leaf data pointer (for any type of leaf).            */   
/* Arguments:                               */   
/*   base_va    base variable (i.e. logical node)           */   
/*   flatname   flattened leaf name (e.g. ST$Mod$stVal)         */   
/*   rt_type    ptr to ptr to type (function sets "*rt_type")       */   
/* RETURNS: pointer to leaf data (NULL on error)            */   
/************************************************************************/   
ST_VOID *mvlu_get_leaf_data_ptr (MVL_VAR_ASSOC *base_va, ST_CHAR *flatname, RUNTIME_TYPE **rt_type)   
  {   
RUNTIME_TYPE *comp_rt_type; /* first rt_type of component   */   
ST_INT comp_rt_num;     /* num of rt_types in component */   
ST_VOID *data;   
   
  /* Find the attribute type.   */   
  if (mvlu_find_comp_type (base_va->type_id,   
           flatname,    /* flattened name   */   
       &comp_rt_type,   /* ptr to val set by function   */   
       &comp_rt_num)    /* ptr to val set by function   */   
      == SD_SUCCESS)   
    {   
    data = ((ST_CHAR *) base_va->data + comp_rt_type->mvluTypeInfo.offSet);   
    *rt_type = comp_rt_type;   
    }   
  else   
    {   
    data = NULL;   
    MVLU_LOG_FLOW2 ("Cannot find '%s' attribute in this variable '%s'",   
                  flatname, base_va->name);   
    }   
  return (data);   
  }   
/************************************************************************/   
#endif  /* defined(MVL_UCA) */   
/************************************************************************/   

 