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

2505 lines
94 KiB
C

1 year ago
/************************************************************************/
/* 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 <20><><EFBFBD><EFBFBD>֧<EFBFBD><D6A7>3<EFBFBD><33>Ŀ¼ģ<C2BC>͵ķ<CDB5><C4B7><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EBA3A8><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ע<EFBFBD>Ͱ<EFBFBD><CDB0><EFBFBD>//lw<6C><77>
/* 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:<3A><><EFBFBD>´<EFBFBD><C2B4><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>޸ģ<DEB8><C4A3><EFBFBD>ҪΪ<D2AA><CEAA><EFBFBD><EFBFBD>3<EFBFBD><33>Ŀ¼<C4BF><C2BC><EFBFBD><EFBFBD><EFBFBD><EFBFBD>////////////////////
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<6C><77><EFBFBD><EFBFBD><EFBFBD>´<EFBFBD><C2B4><EFBFBD>Ϊ<EFBFBD><CEAA><EFBFBD><EFBFBD>3<EFBFBD><33><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>////////////////////////////////
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) */
/************************************************************************/