Files
microser/mmslib/mvlu/mvlu_rpt.c

2571 lines
88 KiB
C
Raw Normal View History

2026-06-15 15:48:16 +08:00
/************************************************************************/
/* SISCO SOFTWARE MODULE HEADER *****************************************/
/************************************************************************/
/* (c) Copyright Systems Integration Specialists Company, Inc., */
/* 1998 - 2006, All Rights Reserved. */
/* */
/* PROPRIETARY AND CONFIDENTIAL */
/* */
/* MODULE NAME : mvlu_rpt.c */
/* PRODUCT(S) : MMSEASE */
/* */
/* MODULE DESCRIPTION : */
/* */
/* GLOBAL FUNCTIONS DEFINED IN THIS MODULE : */
/* */
/* MODIFICATION LOG : */
/* Date Who Rev Comments */
/* -------- --- ------ ------------------------------------------- */
/* 07/08/08 JRB 62 mvlu_create_rpt_ctrl: clarify args. */
/* 04/15/08 JRB 61 Don't call mvlu_rpt_va_change for Integrity */
/* (save "reason" later when scan completes). */
/* mvl61850_brcb_handle_rptena_wr: fix retCode. */
/* mvlu_rptena_wr..: Fix warning newval not set.*/
/* 03/04/08 JRB 60 Remove AA_SPEC DatSet from RCB on disconnect */
/* or when 0 written to Resv. */
/* 03/16/07 JRB 59 Fix Resv init in ..urcb_handle_rptena_wr. */
/* 02/28/07 JRB 58 DO NOT PurgeBuf when OptFlds is changed. */
/* PurgeBuf when RptID, DatSet, BufTm, TrgOps, */
/* or IntgPd is changed (tissue 322 final text).*/
/* 02/13/07 JRB 57 mvl61850_get_rcb: del unused net_info arg. */
/* Chk net_info before trying to find UCA RCB. */
/* Add _rcb_writable & use it. */
/* 12/20/06 JRB 56 PurgeBuf only if val changed (tissue 322). */
/* PurgeBuf also when BufTm written. */
/* 10/30/06 JRB 55 Use new mvl_vmd_* object handling functions. */
/* u_mvl_get_va_aa: add args. */
/* Add mvlu_rpt_ctrl_destroy_all */
/* 09/13/06 JRB 54 mvlu_rpt_find_typeids: simplify. */
/* 08/09/06 JRB 53 If report buffered because of BufTm, send it */
/* BEFORE any Integrity report. */
/* 08/09/06 JRB 52 mvlu_create_rpt_ctrl, mvlu_free_rpt_ctrl, */
/* mvlu_rpt_service now ONLY for UCA (for 61850,*/
/* use new functions in mvl61850_rpt.c). */
/* Del brcb_enable_allowed (not needed now that */
/* segmenting done only when sending). */
/* Reset SqNum=0 when BRCB enabled. */
/* PurgeBuf when DatSet,OptFlds,TrgOps,IntgPd */
/* or RptID written. */
/* Reserve BRCB only when RptEna written (chged */
/* mvlu_get_wr_rcb, mvlu_rptena_wr_ind_fun). */
/* mvlu_rpt_find_typeids: create if not found. */
/* Add mvlu_rpt_ready, mvlu_rpt_rcb_type_find. */
/* Chg some static functs to extern. */
/* 07/27/05 JRB 51 mvlu_optflds_wr..: use rcb_type to figure */
/* out which bits are writable. */
/* 06/27/05 JRB 50 brcb_enable_allowed: reset netInfo on error. */
/* 06/27/05 JRB 49 Simplify allocation by deleting all use of */
/* MVLU_RPT_VA_CTRL & va->mvl_internal & moving */
/* last_data to MVL_VAR_ASSOC struct. */
/* Del mvlu_rpt_nvl_add/destroy, use mvl_nvl_add*/
/* Del unused mvlu_resolve_uca_nvl. */
/* 06/27/05 JRB 48 _mvlu_get_rd_rcb: add (MVLU_RPT_CLIENT **) arg*/
/* 05/24/05 CRM 47 Added mvlu_rpt_destroy_scan_ctrl */
/* 05/16/05 JRB 46 Set failure=ARE_TEMP_UNAVAIL when trying to */
/* write RCB elements while RptEna=TRUE. */
/* 05/09/05 JRB 45 Chk Integrity bit in TrgOps, as we did */
/* BEFORE change on 03/13/03 (61850 requires it)*/
/* 08/16/04 JRB 44 Reset SqNum when URCB is enabled. */
/* 07/20/04 JRB 43 ..chk_reason: make sure OLD data sent in */
/* first rpt when 2nd change detected. */
/* 07/07/04 JRB 42 ..chk_reason: for URCB or BRCB, call */
/* mvl61850_rcb_build (NOT mvlu_send_report). */
/* 07/06/04 JRB 41 Add client for BRCB only when needed, */
/* allows BRCB initial values to be set first. */
/* Add mvlu_rpt_create_scan_ctrl2. */
/* 06/29/04 JRB 40 mvlu_rpt_nvl_add: alloc new buffer for */
/* va->data ONLY if use_static_data==FALSE. */
/* 12/17/03 JRB 39 61850-8-1 FDIS changes: */
/* Increase num bits in OptFlds to 10 for ConfRev.*/
/* Find type RTYP_INT32U for ConfRev. */
/* 05/02/03 JRB 38 switch(rt->el_tag): Use default for most cases*/
/* 04/17/03 JRB 37 Add & use mvlu_setup_scan_read. */
/* 04/04/03 JRB 36 mvlu_rpt_va_change: save data at END of funct*/
/* 04/04/03 JRB 35 Fix integrity/GI scan code so multiple */
/* concurrent scans don't corrupt one another. */
/* 03/13/03 JRB 34 Add URCB support including Resv. */
/* Chg OptFlds back to 8 bits for UCA (9 for IEC).*/
/* Trigger Intg Rpt ONLY by setting IntgPd!=0. */
/* Increment SqNum after each report. */
/* Chg names in rpt_typeids struct (clearer). */
/* Fix _mvlu_rpt_find_rpt_va to set scope. */
/* Use tmp_va_arr for all rptNvl options. */
/* Simplify mvlu_rpt_va_change. */
/* Use domain name for NVL, not VA, in RptId string.*/
/* Chg TrgOps to Bvstring. */
/* 12/16/02 JRB 33 Chg args to mvlu_create_rpt_ctrl. */
/* Add mvlu_integrity_scan_* functions. */
/* Move some init/cleanup INTO mvlu_send_report.*/
/* Don't allow write of RCB if RptEna=TRUE. */
/* Use mvl_internal to AVOID conflict w/ user_info.*/
/* Write default RptID once in mvlu_create_rpt_ctrl*/
/* Start "automatic" scan when IntgPd expires. */
/* Use BSTR_BIT.. macros to access bitstring bits*/
/* Del buffer chain code. */
/* Del rptTim*, call usr func u_mvlu_rpt_time_get*/
/* Del outDat*, use basrcb->DatSetNa (same string)*/
/* Init OptFlds.len_1=8. Chg optflds_wr_ind_fun.*/
/* Del *typeId globals, use mvlu_rpt_find_typeids*/
/* Use BSTR_NUMBITS_TO_.. */
/* 12/09/02 MDE 32 Removed need for primRef for reports */
/* 03/29/02 JRB 31 Fix crash if _mvl_objname_to_va returns NULL.*/
/* 12/12/01 MDE 30 Include mvl_defs.h instead of mvl_acse.h */
/* 11/26/01 EJV 29 Added support for new MMS type UtcTime. */
/* 08/30/01 JRB 28 Fix "..sqnum_rd.." (chg UINT16 to UINT8). */
/* 12/21/00 JRB 27 Del return in main loop of mvlu_rpt_va_change*/
/* 10/25/00 JRB 26 Del u_mvl funct ptrs. Call functs directly. */
/* 09/08/00 JRB 25 Default to 10 second scan period. */
/* 07/13/00 JRB 24 Use new ms_comp_name_find to get comp names. */
/* 07/11/00 MDE 23 Changed seqNum to sqNum, made INT8U */
/* 05/01/00 JRB 22 Lint cleanup (uninitialized var). */
/* 03/10/00 JRB 21 Copy va_scope to appropriate structures. */
/* 03/07/00 MDE 20 Fixed integrity report problem */
/* 01/21/00 MDE 19 Now use MEM_SMEM for dynamic memory */
/* 09/27/99 NAV 18 Set OutData data for report */
/* 09/13/99 MDE 17 Added SD_CONST modifiers */
/* 09/07/99 MDE 16 Revised and enhanced the UCA report system */
/* 05/04/99 JRB 15 Fixed several write indication functions. */
/* 03/18/99 MDE 14 Changed for runtime connection limits */
/* 01/21/99 JRB 13 Del mvlu_create_nvl, mvlu_destroy_nvl. Call */
/* mvl_nvl_create, mvl_nvl_destroy instead. */
/* 12/11/98 MDE 12 Removed scope references from VA */
/* 11/16/98 MDE 11 Now use '_mvl_destroy_nvl_entries' */
/* 11/16/98 MDE 10 Renamed internal functions (prefix '_') */
/* 10/01/98 MDE 09 Changes to MVLU_RPT_CTRL rcb allocation */
/* 09/21/98 MDE 08 Minor lint cleanup */
/* 09/16/98 MDE 07 Moved RD/WR functions here from rdwrind.c */
/* 08/11/98 MDE 06 Changed 'mvlu_create_rpt_ctrl' parameters */
/* 07/13/98 MDE 05 We now use our own report control */
/* 06/29/98 MDE 04 Moved report function pointers to mvl_uca.c */
/* 06/22/98 MDE 03 SeqNum now ST_UINT16 */
/* 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"
/************************************************************************/
/* 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
/************************************************************************/
/************************************************************************/
/* These variables must be set by the user application, and select the */
/* types to be used by the elements listed below. */
MVLU_RPT_CTRL *mvlu_rpt_ctrl_list;
MVLU_RPT_CTRL *mvl61850_rpt_ctrl_list; /* List of only 61850 RCBs. */
MVLU_RPT_SCAN_CTRL *mvlu_rpt_scan_list;
/************************************************************************/
static MVLU_RPT_CLIENT *_mvlu_add_rpt_client (MVLU_RPT_CTRL *rptCtrl,
MVL_NET_INFO *netInfo);
static ST_VOID _mvlu_free_rpt_client (MVLU_RPT_CLIENT *rptClient);
static ST_VOID _mvlu_rpt_client_service (MVLU_RPT_CLIENT *rptClient,
ST_DOUBLE timeNow);
static ST_VOID _mvlu_rpt_va_reads_done (MVL_IND_PEND *indCtrl,
MVL_VAR_ASSOC *va);
static ST_VOID _mvlu_get_rcb (MVL_VAR_ASSOC *baseVa,
RUNTIME_TYPE *rt,
MVL_NET_INFO *netInfo,
MVLU_RPT_CTRL **rptCtrlOut,
MVLU_RPT_CLIENT **rptClientOut);
static ST_RET _mvlu_count_rpt_var (RUNTIME_TYPE *rt, ST_INT numRt,
ST_INT *numRptVarOut, ST_INT *nameSizeOut);
static ST_RET _mvlu_fill_rpt_var (ST_CHAR *baseName, ST_CHAR *domName,
RUNTIME_TYPE *rt, ST_INT numRt,
ST_INT numObjNames, OBJECT_NAME *objNames,
ST_CHAR **nameBufPtr);
static ST_VOID _mvlu_rpt_va_chk_reason (MVLU_RPT_CLIENT *rptClient,
MVL_VAR_ASSOC *va,
ST_INT entry_num,
ST_UCHAR reason);
/************************************************************************/
/************************************************************************/
/************************************************************************/
/* REPORT DATASET HANDLING */
/************************************************************************/
/************************************************************************/
/************************************************************************/
/* mvlu_derive_rpt_ds */
/************************************************************************/
MVL_NVLIST_CTRL *mvlu_derive_rpt_ds (ST_CHAR *domName, ST_CHAR *nvlName,
ST_INT numNodes, ST_CHAR **nodeNames)
{
MVL_NVLIST_CTRL *nvl;
OBJECT_NAME nvlObjName;
OBJECT_NAME *objNames;
ST_RET rc;
ST_INT numDsVar;
ST_CHAR *nameBuf;
rc = mvlu_derive_ds_va_names (domName, numNodes, nodeNames,
&numDsVar, &objNames, &nameBuf);
if (rc != SD_SUCCESS)
return (NULL);
/* Create the DataSet NVL */
nvlObjName.object_tag = DOM_SPEC;
nvlObjName.domain_id = domName;
nvlObjName.obj_name.item_id = nvlName;
/* Add the DataSet NVL to the MVL data structures */
nvl = mvl_vmd_nvl_add (&mvl_vmd,
&nvlObjName, /* NVL "object name" */
NULL, /* MVL_NET_INFO not used */
numDsVar, /* num of variables */
objNames, /* array of var "object names" */
SD_TRUE); /* copy nvl name to new nvl. */
M_FREE (MSMEM_GEN, nameBuf);
M_FREE (MSMEM_GEN, objNames);
return (nvl);
}
/************************************************************************/
/************************************************************************/
typedef struct
{
MVL_VAR_ASSOC *nodeVa;
ST_INT num_rpt_var;
RUNTIME_TYPE *rt;
ST_INT numRt;
} _MVLU_RPT_DS_NODE_INFO;
ST_RET mvlu_derive_ds_va_names (ST_CHAR *domName,
ST_INT numNodes, ST_CHAR **nodeNames,
ST_INT *numObjNamesOut,
OBJECT_NAME **objNameTblOut,
ST_CHAR **nameBufOut)
{
OBJECT_NAME nodeObjName;
OBJECT_NAME *objNames;
OBJECT_NAME *objNameDest;
_MVLU_RPT_DS_NODE_INFO *nodeInfoTbl;
MVL_VAR_ASSOC *nodeVa;
RUNTIME_TYPE *rt;
ST_INT numRt;
ST_INT i;
ST_RET rc;
ST_INT numNodeVar;
ST_INT numDsVar;
ST_INT nameSize;
ST_CHAR *nameBuf;
ST_CHAR *nameDest;
nodeInfoTbl = (_MVLU_RPT_DS_NODE_INFO *)
M_CALLOC (MSMEM_GEN, numNodes, sizeof (_MVLU_RPT_DS_NODE_INFO));
numDsVar = 0;
nameSize = 0;
/* Find each node VA */
for (i = 0; i < numNodes; ++i)
{
nodeObjName.object_tag = DOM_SPEC;
nodeObjName.domain_id = domName;
nodeObjName.obj_name.item_id = nodeNames[i];
nodeVa = u_mvl_get_va_aa (&mvl_vmd,
MMSOP_MVLU_RPT_VA,
&nodeObjName, NULL,
SD_FALSE, NULL, NULL);
nodeInfoTbl[i].nodeVa = nodeVa;
if (nodeVa == NULL)
{
MVL_LOG_NERR1 ("Error: Report node %s not found", nodeNames[i]);
return (SD_FAILURE);
}
rc = mvl_get_runtime (nodeVa->type_id, &rt, &numRt);
if (rc != SD_SUCCESS)
{
MVL_LOG_NERR0 ("Error: Runtime type not found");
return (SD_FAILURE);
}
nodeInfoTbl[i].rt = rt;
nodeInfoTbl[i].numRt = numRt;
numNodeVar = 0;
rc = _mvlu_count_rpt_var (rt, numRt, &numNodeVar, &nameSize);
if (rc != SD_SUCCESS)
return (SD_FAILURE);
numDsVar += numNodeVar;
nodeInfoTbl[i].num_rpt_var = numNodeVar;
/* the name buffer must add "base$" for each name */
nameSize += (numDsVar * (strlen (nodeVa->name) + 1));
}
objNames = (OBJECT_NAME *) M_CALLOC (MSMEM_GEN, numDsVar, sizeof (OBJECT_NAME));
nameBuf = M_CALLOC (MSMEM_GEN, 1, nameSize);
objNameDest = objNames;
nameDest = nameBuf;
for (i = 0; i < numNodes; ++i)
{
nodeVa = nodeInfoTbl[i].nodeVa;
rt = nodeInfoTbl[i].rt;
numRt = nodeInfoTbl[i].numRt;
rc = _mvlu_fill_rpt_var (nodeVa->name, domName, rt, numRt,
nodeInfoTbl[i].num_rpt_var,
objNameDest, &nameDest);
objNameDest += nodeInfoTbl[i].num_rpt_var;
}
*numObjNamesOut = numDsVar;
*objNameTblOut = objNames;
*nameBufOut = nameBuf;
M_FREE (MSMEM_GEN, nodeInfoTbl);
return (SD_SUCCESS);
}
/************************************************************************/
/* _mvlu_count_rpt_var */
/************************************************************************/
static ST_RET _mvlu_count_rpt_var (RUNTIME_TYPE *rt, ST_INT numRt,
ST_INT *numRptVarOut, ST_INT *nameSizeOut)
{
ST_INT numRptVar;
ST_INT nameSize;
RUNTIME_TYPE *lastRt;
ST_CHAR *comp_name; /* Component name */
if (numRt <= 2 || rt->el_tag != RT_STR_START)
{
MVL_LOG_NERR0 ("Error: Not structure, or no structure elements");
return (SD_FAILURE);
}
++rt;
numRt -= 2;
lastRt = rt + numRt;
numRptVar = 0;
nameSize = 0;
while (rt <= lastRt)
{
switch (rt->el_tag)
{
case RT_STR_END :
case RT_ARR_END :
break;
default:
++numRptVar;
comp_name = ms_comp_name_find (rt);
if (strlen (comp_name) == 0)
{
MVL_LOG_ERR0 ("Error: unnamed structure component");
return (SD_FAILURE);
}
nameSize += (strlen (comp_name) + 1);
break;
}
if (rt->el_tag == RT_STR_START)
rt += rt->u.str.num_rt_blks + 1; /* Skip the structure contents */
if (rt->el_tag == RT_ARR_START)
rt += rt->u.arr.num_rt_blks; /* Skip the array contents */
++rt;
}
*numRptVarOut += numRptVar;
*nameSizeOut += nameSize;
return (SD_SUCCESS);
}
/************************************************************************/
/* _mvlu_fill_rpt_var */
/************************************************************************/
static ST_RET _mvlu_fill_rpt_var (ST_CHAR *baseName, ST_CHAR *domName,
RUNTIME_TYPE *rt, ST_INT numRt,
ST_INT numObjNames, OBJECT_NAME *objNames,
ST_CHAR **nameBufPtr)
{
ST_INT dsDvCount;
RUNTIME_TYPE *lastRt;
ST_CHAR *nameBuf;
ST_CHAR *comp_name; /* Component name */
nameBuf = *nameBufPtr;
++rt;
numRt -= 2;
dsDvCount = 0;
lastRt = rt + numRt;
while (rt <= lastRt)
{
switch (rt->el_tag)
{
case RT_STR_END :
case RT_ARR_END :
break;
default:
comp_name = ms_comp_name_find (rt);
if (strlen (comp_name) == 0)
{
MVL_LOG_ERR0 ("Error: unnamed structure component");
return (SD_FAILURE);
}
sprintf (nameBuf, "%s$%s", baseName, comp_name);
objNames[dsDvCount].object_tag = DOM_SPEC;
objNames[dsDvCount].domain_id = domName;
objNames[dsDvCount].obj_name.item_id = nameBuf;
++dsDvCount;
nameBuf += (strlen (nameBuf) + 1);
break;
}
if (rt->el_tag == RT_STR_START)
rt += rt->u.str.num_rt_blks + 1; /* Skip the structure contents */
if (rt->el_tag == RT_ARR_START)
rt += rt->u.arr.num_rt_blks; /* Skip the array contents */
++rt;
}
if (dsDvCount != numObjNames)
{
MVL_LOG_ERR0 ("Internal Error: dsDvCount misnatch");
return (SD_FAILURE);
}
*nameBufPtr = nameBuf;
return (SD_SUCCESS);
}
/************************************************************************/
/************************************************************************/
/* REPORT CONTROL HANDLING */
/************************************************************************/
/************************************************************************/
/* mvlu_create_rpt_ctrl */
/************************************************************************/
/* Create a UCA Report Control (use mvl61850_create_rpt_ctrl for 61850).*/
/* This function creates a RPT NVL data structure and associated */
/* control data to handle UCA report activity. */
/* Note that the 'dsNvl' can be created by 'mvlu_create_rpt_nvl', */
/* created by 'u_mvl_get_nvl', or can be configured. The NVL */
/* must be completely resolved; that is, all VA must have a valid data */
/* pointer, name, and type_id. */
/* NOTE: The args brcb_bufsize and ConfRev were added once for IEC 61850*/
/* support, but now this function is ONLY for UCA, so they are ignored.*/
MVLU_RPT_CTRL *mvlu_create_rpt_ctrl (ST_CHAR *basrcbName,
MVL_NVLIST_CTRL *dsNvl,
MVL_VAR_ASSOC *base_va,
ST_INT rcb_type,
ST_INT buftim_action,
ST_INT brcb_bufsize, /* ignored */
ST_UINT32 ConfRev) /* ignored */
{
MVLU_RPT_CTRL *rptCtrl;
MVL_NVLIST_CTRL *rptNvl;
RUNTIME_TYPE *incRt;
ST_RET rc;
ST_INT i;
ST_INT numDsVars;
ST_INT rptCtrlSize;
ST_INT inc_size;
RUNTIME_TYPE *rcbHead;
ST_INT numRt;
/* This funct only works for UCA. For IEC 61850, call mvl61850_create_rpt_ctrl*/
assert (rcb_type == RCB_TYPE_UCA);
/* Find the runtime type of the RCB (saved later in rptCtrl). */
/* If successful, this function sets "rcbHead" & "numRt". */
if (mvlu_rpt_rcb_type_find (base_va->type_id, basrcbName, &rcbHead, &numRt))
return (NULL); /* error already logged. */
/* Alloc report control large enough to contain the inclusion BS data */
/* and reasons VA's (they depend on the number of VA in the report) */
numDsVars = dsNvl->num_of_entries;
inc_size = BSTR_NUMBITS_TO_NUMBYTES(numDsVars);
rptCtrlSize = sizeof (MVLU_RPT_CTRL) +
strlen (basrcbName) + 1;
rptCtrl = (MVLU_RPT_CTRL *) M_CALLOC (MSMEM_GEN, 1, rptCtrlSize);
/* Set the variable size element pointers */
rptCtrl->basrcb_name = (ST_CHAR *) (rptCtrl + 1);
strcpy (rptCtrl->basrcb_name, basrcbName);
/* Initialize the Report Control data elements */
rptCtrl->dsNvl = dsNvl;
rptCtrl->base_va = base_va;
rptCtrl->rcbRtHead = rcbHead;
/* If domname + '/' + basrcb_name will fit in RptID, then write it. */
if (strlen (dsNvl->nvl_scope.dom->name) + strlen (rptCtrl->basrcb_name) + 1
< sizeof (rptCtrl->common_basrcb.RptID))
{
sprintf (rptCtrl->common_basrcb.RptID, "%s/%s",
dsNvl->nvl_scope.dom->name,
rptCtrl->basrcb_name);
if (rcb_type == RCB_TYPE_UCA)
{ /* replace $ with '.'. Only for UCA. */
for (i = 0; i < (ST_INT) strlen (rptCtrl->common_basrcb.RptID); ++i)
{
if (rptCtrl->common_basrcb.RptID[i] == '$')
rptCtrl->common_basrcb.RptID[i] = '.';
}
}
}
else
{
MVL_LOG_ERR0 ("mvlu_create_rpt_ctrl RptID too long");
M_FREE (MSMEM_GEN, rptCtrl);
return (NULL);
}
rptCtrl->rcb_type = rcb_type;
rptCtrl->buftim_action = buftim_action;
if (mvlu_rpt_find_typeids (&rptCtrl->rpt_typeids))
{
MVL_LOG_ERR0 ("mvlu_create_rpt_ctrl error finding base types");
M_FREE (MSMEM_GEN, rptCtrl);
return (NULL);
}
/* Create the RPT NVL control element, used to send the actual reports */
rptNvl = &rptCtrl->rptNvl;
rptNvl->name = "RPT";
/* Alloc the entries table; allow for RptID, OptFlds, SqNum, RptTim, */
/* OutDat (61850 calls it DatSetName), BufOvfl, EntryID, ConfRev, */
/* SubSeqNum, MoreSegmentsFollow, & InclusionBitstring plus ARRAYS of */
/* dataRefs, values, and Reasons. */
/* "assert" later if "maxNumRptVars" exceeded. */
/* Allow (3 * numDsVars) to handle arrays of dataRef, value, and reason.*/
rptCtrl->maxNumRptVars = MVLU_MAX_RPT_OPTS + (3 * numDsVars);
rptNvl->entries = (MVL_VAR_ASSOC **) M_CALLOC (MSMEM_GEN, rptCtrl->maxNumRptVars,
sizeof (MVL_VAR_ASSOC *));
/* NOTE: The "rptNvl->entries" array is not filled in and
* and "rptNvl->num_of_entries" is not set
* until a Report is being built.
*/
/* We need to create a Runtime Type for the inclusion bitstring */
incRt = &rptCtrl->incRt;
incRt->el_tag = RT_BIT_STRING;
incRt->el_size = BSTR_NUMBITS_TO_NUMBYTES(numDsVars);
incRt->offset_to_last = incRt->el_size;
incRt->u.p.el_len = (ST_RTINT) numDsVars;
rc = mvlu_add_rt_type (&rptCtrl->incRt, 1, &rptCtrl->inclusion_typeid);
if (rc != SD_SUCCESS)
{
MVL_LOG_NERR0 ("Error - could not add temp RT type");
M_FREE (MSMEM_GEN, rptCtrl);
return (NULL);
}
/* That's all we can do ahead of time for the RPT variable associations */
/* Set a bit of the data for the common BASRCB */
sprintf (rptCtrl->common_basrcb.DatSetNa, "%s/%s",
dsNvl->nvl_scope.dom->name, dsNvl->name);
if (rcb_type == RCB_TYPE_UCA)
{
rptCtrl->common_basrcb.OptFlds.len_1 = 8;
rptCtrl->common_basrcb.TrgOps.len = 8;
}
else
{
rptCtrl->common_basrcb.OptFlds.len_1 = 10;
rptCtrl->common_basrcb.TrgOps.len = 6;
}
rptCtrl->common_basrcb.TrgOps.data[0] = MVLU_TRGOPS_DATA;
/* Put it on the list */
list_add_last((ST_VOID **) &mvlu_rpt_ctrl_list, (ST_VOID *) rptCtrl);
return (rptCtrl);
}
/************************************************************************/
/* mvlu_free_rpt_ctrl */
/************************************************************************/
/* This function is used to free a MVLU_RPT_CTRL allocated by */
/* 'mvlu_create_rpt_ctrl' */
ST_VOID mvlu_free_rpt_ctrl (MVLU_RPT_CTRL *rptCtrl)
{
MVLU_RPT_CLIENT *rptClient;
/* This funct only works for UCA. For IEC 61850, call mvl61850_free_rpt_ctrl*/
assert (rptCtrl->rcb_type == RCB_TYPE_UCA);
/* Take it off the report control list */
list_unlink ((ST_VOID **) &mvlu_rpt_ctrl_list, (ST_VOID *) rptCtrl);
/* Free up the dynamic type used for the inclusion bitstring type */
mvlu_free_rt_type (rptCtrl->inclusion_typeid);
/* Destroy all clients for this rptCtrl */
/* NOTE: list_get_first removes the entry from the linked list. */
while ((rptClient = (MVLU_RPT_CLIENT *) list_get_first (&rptCtrl->rpt_client_list))!=NULL)
_mvlu_free_rpt_client (rptClient); /* free client */
/* Now the rest of the report control */
M_FREE (MSMEM_GEN, rptCtrl->rptNvl.entries);
M_FREE (MSMEM_GEN, rptCtrl);
}
/************************************************************************/
/************************************************************************/
/* REPORT CONTROL SERVICING */
/************************************************************************/
/************************************************************************/
/* mvlu_rpt_service */
/************************************************************************/
ST_VOID mvlu_rpt_service ()
{
MVLU_RPT_CTRL *rptCtrl;
MVLU_RPT_CLIENT *rptClient;
ST_DOUBLE timeNow;
/* Do Report VA Scans */
mvlu_rpt_va_scan ();
/* See if it is time to send reports */
timeNow = sGetMsTime ();
for (rptCtrl = mvlu_rpt_ctrl_list;
rptCtrl != NULL;
rptCtrl = (MVLU_RPT_CTRL *) list_get_next (mvlu_rpt_ctrl_list, rptCtrl))
{
assert (rptCtrl->rcb_type == RCB_TYPE_UCA); /* must be UCA URCB*/
/* Loop through linked list of clients for UCA.*/
for (rptClient = rptCtrl->rpt_client_list;
rptClient != NULL;
rptClient = (MVLU_RPT_CLIENT *) list_get_next (rptCtrl->rpt_client_list, rptClient))
{
_mvlu_rpt_client_service (rptClient, timeNow);
}
}
}
/************************************************************************/
/* mvlu_rpt_va_scan */
/************************************************************************/
ST_VOID mvlu_rpt_va_scan ()
{
MVLU_RPT_SCAN_CTRL *scanCtrl;
ST_DOUBLE timeNow;
timeNow = sGetMsTime ();
scanCtrl = mvlu_rpt_scan_list;
while (scanCtrl != NULL) /* look through whole list */
{
if (scanCtrl->enable == SD_TRUE)
{
if (timeNow >= scanCtrl->next_scan_start)
{
if (scanCtrl->num_va_read_pend == 0)
{
scanCtrl->next_scan_start += scanCtrl->scan_period;
mvlu_rpt_scan_read (scanCtrl);
}
else
scanCtrl->saturated = SD_TRUE;
}
}
scanCtrl = (MVLU_RPT_SCAN_CTRL *) list_get_next (mvlu_rpt_scan_list, scanCtrl);
}
}
/************************************************************************/
/* mvlu_rpt_scan_read */
/************************************************************************/
ST_VOID mvlu_rpt_scan_read (MVLU_RPT_SCAN_CTRL *scanCtrl)
{
MVLAS_READ_CTRL *rdCtrl;
MVLAS_RD_VA_CTRL *vaCtrl;
MVL_IND_PEND *indCtrl;
ST_INT i;
if (scanCtrl->num_va_read_pend == 0)
{
scanCtrl->num_va_read_pend = scanCtrl->num_scan_va;
scanCtrl->num_va_changes = 0;
indCtrl = &scanCtrl->indCtrl;
rdCtrl = &indCtrl->u.rd;
rdCtrl->numVar = 0;
vaCtrl = rdCtrl->vaCtrlTbl;
for (i = 0; i < scanCtrl->num_scan_va; ++i)
{
vaCtrl->va = scanCtrl->scan_va[i];
memcpy (&vaCtrl->va_scope, &scanCtrl->scan_va_scope[i], sizeof(MVL_SCOPE));
++vaCtrl;
++rdCtrl->numVar;
}
/* OK, now fire off the read indication functions */
/* (*u_mvlu_report_reads_done_fun) will be invoked when all are ready */
u_mvl_read_ind (indCtrl);
}
else
scanCtrl->saturated = SD_TRUE;
}
/************************************************************************/
/* _mvlu_rpt_va_reads_done */
/************************************************************************/
/* This function is called by 'mvlu_rd_prim_done' when all primitive */
/* read indication functions for a VA have been completed. This means */
/* that the data is ready to be sent for this VA. */
static ST_VOID _mvlu_rpt_va_reads_done (MVL_IND_PEND *indCtrl,
MVL_VAR_ASSOC *va)
{
MVLU_RPT_SCAN_CTRL *scanCtrl;
ST_INT data_size;
/* Do some housekeeping */
scanCtrl = (MVLU_RPT_SCAN_CTRL *) indCtrl->usr_ind_ctrl;
--scanCtrl->num_va_read_pend;
/* Let's see if the data has changed */
data_size = mvl_type_ctrl[va->type_id].data_size;
if (memcmp (va->last_data, va->data, data_size) != 0)
{
MVLU_LOG_FLOW1 ("Report VA Scan detected change for rpt VA '%s'",
va->name);
mvlu_rpt_va_change (va, MVLU_TRGOPS_DATA, NULL);
++scanCtrl->num_va_changes;
}
}
/************************************************************************/
/* _mvlu_rpt_client_service */
/************************************************************************/
static ST_VOID _mvlu_rpt_client_service (MVLU_RPT_CLIENT *rptClient,
ST_DOUBLE timeNow)
{
MVLU_RPT_CTRL *rptCtrl;
MVLU_BASRCB *basrcb;
MVL_NVLIST_CTRL *dsNvl;
ST_RET rc;
ST_BOOLEAN sendRbe;
ST_INT incSize;
basrcb = &rptClient->basrcb;
/* Let's see if the report is enabled */
if (basrcb->RptEna == SD_FALSE) /* treat any non-zero as TRUE */
return;
/* Make sure we can send one if we want to ... */
if (a_buffers_avail (rptClient->netInfo->acse_conn_id) <= 0)
return;
/* Reporting is enabled, let's see if it is time to send the report */
rptCtrl = rptClient->rpt_ctrl;
dsNvl = rptCtrl->dsNvl;
incSize = BSTR_NUMBITS_TO_NUMBYTES(dsNvl->num_of_entries);
/* First, see if Integrity scan is enabled, and if so if time to send */
/* This function checks time & if necessary, starts integrity scan. */
mvlu_integrity_timeout (rptClient, timeNow);
/* Check for RBE enable */
/* NOTE: numTrgs ONLY incremented if changes matched the TrgOps */
/* for this client. See mvlu_rpt_va_change. */
if (rptClient->numTrgs > 0)
{
/* RBE is enabled, see if a RBE Period is selected ... */
if (basrcb->RBEPd != 0)
{
if (timeNow > rptClient->next_rbe_rpt_time)
{
/* OK, send the report! */
MVLU_LOG_FLOW1 ("UCA Report RBE Period timeout for client %08lx",
rptClient);
rc = mvlu_send_report (rptClient, MVLU_RPT_TYPE_RBE);
rptClient->next_rbe_rpt_time = timeNow + (ST_DOUBLE) basrcb->RBEPd;
return;
}
}
else /* NO RBE Period is selected, send right away, subject */
{ /* to BufTime and Trgs parameters */
/* If neither constraint is in place, send immediately, otherwise */
/* we must exceed one or both constraints before sending */
if (basrcb->Trgs == 0 && basrcb->BufTim == 0)
sendRbe = SD_TRUE;
else
sendRbe = SD_FALSE;
if (basrcb->Trgs != 0)
{
if (rptClient->numTrgs >= basrcb->Trgs)
sendRbe = SD_TRUE;
}
if (basrcb->BufTim != 0)
{
if (timeNow > rptClient->buf_time_done)
sendRbe = SD_TRUE;
}
/* OK, send the report! */
if (sendRbe == SD_TRUE)
{
MVLU_LOG_FLOW1 ("UCA Report RBE for client %08lx", rptClient);
rc = mvlu_send_report (rptClient, MVLU_RPT_TYPE_RBE);
return;
}
}
}
}
/************************************************************************/
/************************************************************************/
/* mvlu_rpt_va_change */
/* CRITICAL: this function does not save the data in "va->data" until */
/* the very end, because depending on RCB state, it may first need to */
/* send a report with the previous data (see _mvlu_rpt_va_chk_reason). */
/************************************************************************/
ST_VOID mvlu_rpt_va_change (MVL_VAR_ASSOC *va, ST_UCHAR reason,
ST_VOID *new_data)
{
MVLU_RPT_CTRL *rptCtrl;
MVLU_RPT_CLIENT *rptClient;
ST_INT va_index;
ST_INT data_size;
MVLU_LOG_FLOW2 ("Report VA '%s' change, reason=0x%02X", va->name, reason);
data_size = mvl_type_ctrl[va->type_id].data_size;
/* If reason is Integrity or GI, do nothing (handled elsewhere). */
if ((reason & (MVLU_TRGOPS_INTEGRITY | MVLU_TRGOPS_GI)) != 0)
return;
/* Check all UCA RCBs. */
for (rptCtrl = mvlu_rpt_ctrl_list;
rptCtrl != NULL;
rptCtrl = (MVLU_RPT_CTRL *) list_get_next (mvlu_rpt_ctrl_list, rptCtrl))
{ /* see if someone is interested */
assert (rptCtrl->rcb_type == RCB_TYPE_UCA); /* this loop only for UCA*/
va_index = mvlu_rpt_get_va_index (rptCtrl, va);
if (va_index >= 0)
{
MVLU_LOG_CFLOW1 ("VA is member of report NVL '%s'", rptCtrl->dsNvl->name);
for (rptClient = rptCtrl->rpt_client_list;
rptClient != NULL;
rptClient = (MVLU_RPT_CLIENT *) list_get_next (rptCtrl->rpt_client_list, rptClient))
{
/* If enabled, chk "reason" (i.e. compare to TrgOps, etc.)*/
if (rptClient->basrcb.RptEna)
{
_mvlu_rpt_va_chk_reason (rptClient, va, va_index, reason);
}
else
MVLU_LOG_CFLOW0 ("VA change ignored: Report not enabled");
}
}
}
/* Check all IEC 61850 RCBs. */
for (rptCtrl = mvl61850_rpt_ctrl_list;
rptCtrl != NULL;
rptCtrl = (MVLU_RPT_CTRL *) list_get_next (mvl61850_rpt_ctrl_list, rptCtrl))
{ /* see if someone is interested */
assert (rptCtrl->rcb_type != RCB_TYPE_UCA); /* this loop only for 61850*/
va_index = mvlu_rpt_get_va_index (rptCtrl, va);
if (va_index >= 0)
{
MVLU_LOG_CFLOW1 ("VA is member of report NVL '%s'", rptCtrl->dsNvl->name);
rptClient = &rptCtrl->only_client; /* only one client */
/* If enabled, or this is IEC BRCB & it was enabled ONCE, chk "reason".*/
if (rptClient->basrcb.RptEna
|| (rptCtrl->rcb_type == RCB_TYPE_IEC_BRCB
&& rptCtrl->brcbCtrl.enabled_once))
{
_mvlu_rpt_va_chk_reason (rptClient, va, va_index, reason);
}
else
MVLU_LOG_CFLOW0 ("VA change ignored: Report not enabled");
}
}
/* If the user is passing us the data, need to save it ... */
if (new_data != NULL)
{
MVLU_LOG_CFLOW0 ("Saving report VA data");
memcpy (va->data, new_data, data_size);
}
/* Now copy the data into the buffer chain */
/* NOTE: Multiple datasets may reference the same variable, but each */
/* creates its own "va", so each gets its own copy of "last_data". */
memcpy (va->last_data, va->data, data_size); /* save data for next compare */
}
/************************************************************************/
/* _mvlu_rpt_va_chk_reason */
/************************************************************************/
static ST_VOID _mvlu_rpt_va_chk_reason (MVLU_RPT_CLIENT *rptClient,
MVL_VAR_ASSOC *va,
ST_INT va_index,
ST_UCHAR reason)
{
ST_INT data_size;
ST_VOID *new_data; /* temp copy of new data while sending old data*/
if ((reason & (MVLU_TRGOPS_DATA | MVLU_TRGOPS_QUALITY | MVLU_TRGOPS_FREEZE)) != 0)
{
/* If this "reason" is enabled in TrgOps, process it. */
if ((rptClient->basrcb.TrgOps.data[0] & reason) != 0)
{
if (rptClient->basrcb.BufTim != 0)
{
/* If this is first change for "ANY" VA, start BufTim timer. */
if (rptClient->numTrgs == 0)
{ /* First va change. Start BufTim timer. */
rptClient->buf_time_done = sGetMsTime () +
(ST_DOUBLE) rptClient->basrcb.BufTim;
MVLU_LOG_CFLOW1 ("Setting buffer Time Done = %06.3fs",
rptClient->buf_time_done/1000);
}
/* If change already detected for "THIS" VA & buftim_action = SEND_NOW, send report.*/
if (BSTR_BIT_GET (rptClient->changed_flags, va_index)
&& rptClient->rpt_ctrl->buftim_action == MVLU_RPT_BUFTIM_SEND_NOW)
{
/* Send report now with last data, then process new data.*/
/* Report is built using "va->data", so must copy old data */
/* to "va->data" until after first rpt built. */
data_size = mvl_type_ctrl[va->type_id].data_size;
new_data = chk_malloc (data_size); /* alloc buf to save new data*/
memcpy (new_data, va->data, data_size); /* save new data */
memcpy (va->data, va->last_data, data_size); /* restore old data*/
MVLU_LOG_CFLOW0 ("Second change for this VA before BufTim. Sending Report with first change. Restarting BufTim.");
/* send or queue rpt */
mvlu_rpt_ready (rptClient, MVLU_RPT_TYPE_RBE);
rptClient->buf_time_done = sGetMsTime () +
(ST_DOUBLE) rptClient->basrcb.BufTim;
/* Put back new data in "va->data". */
memcpy (va->data, new_data, data_size); /* restore new data */
chk_free (new_data);
}
} /* end "if (..BufTim != 0)" */
/* Set numTrgs, changed_flags, reasons_data */
++rptClient->numTrgs;
MVLU_LOG_CFLOW1 ("Number Triggers is now %d", (int) rptClient->numTrgs);
BSTR_BIT_SET_ON (rptClient->changed_flags, va_index);
rptClient->reasons_data[va_index] = reason;
} /* end "if client TrgOps matches reason" */
else
MVLU_LOG_CFLOW0 ("VA change ignored: Reason doesn't match TrgOps");
}
else if ((reason & (MVLU_TRGOPS_INTEGRITY | MVLU_TRGOPS_GI)) != 0)
{
assert (0); /* Should never get here (see mvlu_rpt_va_change). */
}
else
{
assert (0); /* Unrecognized TRGOPS. */
}
}
/************************************************************************/
/* mvlu_rpt_get_va_index */
/************************************************************************/
/* Given a MVL report control and an associated VA, find the index of */
/* the Va. This index is then used for manipulating the inclusion bits, */
/* read bits, and reason codes. */
ST_INT mvlu_rpt_get_va_index (MVLU_RPT_CTRL *rptCtrl, MVL_VAR_ASSOC *va)
{
ST_INT i;
MVL_NVLIST_CTRL *dsNvl;
dsNvl = rptCtrl->dsNvl;
if (dsNvl!=NULL) /* NOTE: dsNvl may be NULL */
{
for (i = 0; i < dsNvl->num_of_entries; ++i)
{
if (dsNvl->entries[i] == va)
{
return (i);
}
}
}
return (-1);
}
/************************************************************************/
/************************************************************************/
/************************************************************************/
/* SENDING REPORTS */
/************************************************************************/
/************************************************************************/
/************************************************************************/
/* mvlu_send_report */
/************************************************************************/
ST_RET mvlu_send_report (MVLU_RPT_CLIENT *rptClient, ST_INT rpt_type)
{
MVLU_RPT_CTRL *rptCtrl;
MVL_NET_INFO *netInfo;
MVL_NVLIST_CTRL *dsNvl;
MVL_NVLIST_CTRL *rptNvl;
MVL_VAR_ASSOC *va;
ST_INT i;
ST_RET rc;
ST_INT sendIndex = 0;
ST_INT sendIndexSave; /* save index before data, compare after data */
ST_UINT8 optFld;
MVLU_BASRCB *basrcb;
ST_INT incSize; /* num bytes for inclusion bitstring */
MVL_VAR_ASSOC *tmp_va_arr; /* alloc array of structs */
ST_INT tmp_va_arr_size; /* num of entries in tmp_va_arr */
MVL_VAR_ASSOC *tmp_va; /* current entry in tmp_va_arr */
MMS_BTIME6 TimeOfEntry; /* Time this report generated. */
rptCtrl = rptClient->rpt_ctrl;
netInfo = rptClient->netInfo;
basrcb = &rptClient->basrcb;
optFld = basrcb->OptFlds.data_1[0];
dsNvl = rptCtrl->dsNvl;
rptNvl = &rptCtrl->rptNvl;
incSize = BSTR_NUMBITS_TO_NUMBYTES(dsNvl->num_of_entries);
MVLU_LOG_FLOW1 ("Sending UCA Report, MVL_NET_INFO %08lx", rptClient->netInfo);
/* Need tmp va's for options plus array of reasons_data. */
tmp_va_arr_size = MVLU_MAX_RPT_OPTS + dsNvl->num_of_entries;
tmp_va_arr = M_CALLOC (MSMEM_GEN, sizeof (MVL_VAR_ASSOC), tmp_va_arr_size);
tmp_va = tmp_va_arr; /* start out pointing to first entry */
/* Prepares a UCA Report NVL to be sent, based on the */
/* options, data, and inclusion bitstring in the rptCtrl. After this */
/* function completes, the rptCtrl->rptNvl is ready to be sent. */
/* If this is Integrity report, just set all inclusion bits. */
if (rpt_type == MVLU_RPT_TYPE_INTEGRITY_OR_GI)
memset (rptClient->changed_flags, 0xff, incSize);
/* We will create a NVL to send, using the dsNvl as a model ... */
/* RptID and OptFlds are always the first 2 entries in the Rpt NVL. */
tmp_va->type_id = rptCtrl->rpt_typeids.vstring32;
tmp_va->data = basrcb->RptID;
rptNvl->entries[sendIndex++] = tmp_va++; /* set entry & point to next*/
MVLU_LOG_CFLOW1 (" RptID='%s", basrcb->RptID);
tmp_va->type_id = rptCtrl->rpt_typeids.bvstring8;
tmp_va->data = &basrcb->OptFlds;
rptNvl->entries[sendIndex++] = tmp_va++; /* set entry & point to next*/
MVLU_LOG_CFLOW1 (" OptFld = 0x%02x", (int) optFld);
if (optFld & MVLU_SQNUM_MASK)
{
tmp_va->type_id = rptCtrl->rpt_typeids.int8u;
tmp_va->data = &basrcb->SqNum;
rptNvl->entries[sendIndex++] = tmp_va++; /* set entry & point to next*/
MVLU_LOG_CFLOW1 (" SqNum : %u", (unsigned) basrcb->SqNum);
}
if (optFld & MVLU_RPTTIM_MASK)
{
/* Call user function to get report time. */
u_mvlu_rpt_time_get (&TimeOfEntry);
MVLU_LOG_CFLOW2 (" RptTim : %lums, %lu days",
TimeOfEntry.ms, TimeOfEntry.day);
tmp_va->type_id = rptCtrl->rpt_typeids.btime6;
tmp_va->data = &TimeOfEntry;
rptNvl->entries[sendIndex++] = tmp_va++; /* set entry & point to next*/
}
if (optFld & MVLU_OUTDAT_MASK)
{
/* OutDat is same as DatSetNa */
MVLU_LOG_CFLOW1 (" OutDat : %s", basrcb->DatSetNa);
tmp_va->type_id = rptCtrl->rpt_typeids.vstring65; /* Obj Ref */
tmp_va->data = basrcb->DatSetNa;
rptNvl->entries[sendIndex++] = tmp_va++; /* set entry & point to next*/
}
/* Add inclusion bitstring. */
tmp_va->type_id = rptCtrl->inclusion_typeid;
tmp_va->data = rptClient->changed_flags;
rptNvl->entries[sendIndex++] = tmp_va++; /* set entry & point to next*/
/* Now go through the inclusion bitstring and figure out what to send */
sendIndexSave = sendIndex; /* save index before data included */
for (i = 0; i < dsNvl->num_of_entries; ++i)
{
if (BSTR_BIT_GET (rptClient->changed_flags, i))
{
va = dsNvl->entries[i];
MVLU_LOG_CFLOW3 (" Including variable %d ('%s'), reason=0x%02X",
i, va->name, rptClient->reasons_data[i]);
rptNvl->entries[sendIndex++] = va;
/* va->data already points to latest data. Done with this va. */
}
}
assert (sendIndex>sendIndexSave); /* make sure SOME data included */
if (optFld & MVLU_REASONS_MASK)
{
for (i = 0; i < dsNvl->num_of_entries; ++i)
{
if (BSTR_BIT_GET (rptClient->changed_flags, i))
{
/* Don't need BVSTR here, because size is fixed. */
tmp_va->type_id = rptCtrl->rpt_typeids.bstr8;
tmp_va->data = &rptClient->reasons_data[i];
rptNvl->entries[sendIndex++] = tmp_va++; /* set entry & point to next*/
}
}
}
rptNvl->num_of_entries = sendIndex;
assert (rptNvl->num_of_entries <= rptCtrl->maxNumRptVars); /* past end of array?*/
assert ((tmp_va-tmp_va_arr) <= tmp_va_arr_size); /* past end of array?*/
rc = mvl_info_variables (rptClient->netInfo, &rptClient->rpt_ctrl->rptNvl, SD_FALSE);
basrcb->SqNum++; /* Increment seq number for next rpt. */
/* Reset reasons, etc. to prepare for new report triggers. */
memset (rptClient->reasons_data, 0, dsNvl->num_of_entries);
memset (rptClient->changed_flags, 0, incSize);
rptClient->numTrgs = 0;
M_FREE (MSMEM_GEN, tmp_va_arr);
return (rc);
}
/************************************************************************/
/************************************************************************/
/* _mvlu_rpt_disconnect_rcvd */
/************************************************************************/
ST_VOID _mvlu_rpt_disconnect_rcvd (MVL_NET_INFO *netInfo)
{
MVLU_RPT_CTRL *rptCtrl;
MVLU_RPT_CLIENT *rptClient;
/* Loop through list of UCA RCBs. */
for (rptCtrl = mvlu_rpt_ctrl_list;
rptCtrl != NULL;
rptCtrl = (MVLU_RPT_CTRL *) list_get_next (mvlu_rpt_ctrl_list, rptCtrl))
{
assert (rptCtrl->rcb_type == RCB_TYPE_UCA);
/* Loop through list of clients. */
for (rptClient = rptCtrl->rpt_client_list;
rptClient != NULL;
rptClient = (MVLU_RPT_CLIENT *) list_get_next (rptCtrl->rpt_client_list, rptClient))
{
if (rptClient->netInfo == netInfo)
{
list_unlink ((ST_VOID **) &rptCtrl->rpt_client_list, (ST_VOID *) rptClient);
_mvlu_free_rpt_client (rptClient);
break; /* one client per connection */
}
}
}
/* Loop through list of IEC 61850 RCBs (URCB or BRCB). */
for (rptCtrl = mvl61850_rpt_ctrl_list;
rptCtrl != NULL;
rptCtrl = (MVLU_RPT_CTRL *) list_get_next (mvl61850_rpt_ctrl_list, rptCtrl))
{
assert (rptCtrl->rcb_type != RCB_TYPE_UCA);
rptClient = &rptCtrl->only_client; /* only one client */
if (rptClient->netInfo == netInfo)
{ /* Reset RCB parameters. */
rptClient->netInfo = NULL;
rptClient->basrcb.RptEna = SD_FALSE;
rptClient->basrcb.Resv = SD_FALSE; /* Ignored for BRCB */
/* If RCB DatSet is AA_SPEC, remove it (i.e. cleanup & set DatSet=""). */
if (rptCtrl->dsNvl != NULL && rptCtrl->dsNvl->nvl_scope.scope == AA_SPEC)
mvl61850_rpt_dataset_destroy (rptCtrl);
}
}
}
/************************************************************************/
/************************************************************************/
/* MVLU REPORT SCAN CONTROL */
/************************************************************************/
/************************************************************************/
/* mvlu_rpt_create_scan_ctrl2 */
/* DEBUG: pass "enable" as additional arg? */
/* DEBUG: delete older "mvlu_rpt_create_scan_ctrl" function? */
/************************************************************************/
MVLU_RPT_SCAN_CTRL *mvlu_rpt_create_scan_ctrl2 (
MVL_NVLIST_CTRL *nvl,
ST_RET (*scan_done_fun) (struct mvl_ind_pend *ind_pend),
ST_UINT report_scan_rate) /* report scan rate (millisec) */
{
MVLU_RPT_SCAN_CTRL *scanCtrl;
ST_INT scanCtrlSize;
ST_INT numScanVa = nvl->num_of_entries;
ST_INT j;
scanCtrlSize = sizeof (MVLU_RPT_SCAN_CTRL) +
(numScanVa * sizeof (MVLAS_RD_VA_CTRL)) +
(numScanVa * sizeof (MVL_SCOPE)) +
(numScanVa * sizeof (MVL_VAR_ASSOC *));
scanCtrl = (MVLU_RPT_SCAN_CTRL *) M_CALLOC (MSMEM_GEN, 1, scanCtrlSize);
scanCtrl->scan_va = (MVL_VAR_ASSOC **) (scanCtrl + 1);
scanCtrl->num_scan_va = numScanVa;
/* Point after array of (MVL_VAR_ASSOC *) */
scanCtrl->scan_va_scope = (MVL_SCOPE *) (scanCtrl->scan_va + numScanVa);
scanCtrl->indCtrl.event = &scanCtrl->commEvent;
scanCtrl->commEvent.event_type = MVLU_RPT_COMM_EVENT;
scanCtrl->indCtrl.op = MMSOP_MVLU_RPT_VA;
/* Point after array of (MVL_SCOPE) */
scanCtrl->indCtrl.u.rd.vaCtrlTbl =
(MVLAS_RD_VA_CTRL *) (scanCtrl->scan_va_scope + numScanVa);
scanCtrl->indCtrl.scan_va_done_fun = _mvlu_rpt_va_reads_done;
scanCtrl->indCtrl.usr_ind_ctrl = scanCtrl;
/* Fill in other structure elements from args passed. */
for (j = 0; j < nvl->num_of_entries; ++j)
scanCtrl->scan_va[j]= nvl->entries[j];
memcpy (scanCtrl->scan_va_scope, nvl->va_scope,
nvl->num_of_entries * sizeof (MVL_SCOPE));
scanCtrl->enable = SD_TRUE; /* ALWAYS enable */
/* Optional user function to be called when each scan completes. */
scanCtrl->indCtrl.usr_resp_fun = scan_done_fun;
scanCtrl->scan_period = (ST_DOUBLE) report_scan_rate;
list_add_last((ST_VOID **) &mvlu_rpt_scan_list, (ST_VOID *) scanCtrl);
return (scanCtrl);
}
/************************************************************************/
/* mvlu_rpt_create_scan_ctrl */
/************************************************************************/
MVLU_RPT_SCAN_CTRL *mvlu_rpt_create_scan_ctrl (ST_INT numScanVa)
{
MVLU_RPT_SCAN_CTRL *scanCtrl;
ST_INT scanCtrlSize;
scanCtrlSize = sizeof (MVLU_RPT_SCAN_CTRL) +
(numScanVa * sizeof (MVLAS_RD_VA_CTRL)) +
(numScanVa * sizeof (MVL_SCOPE)) +
(numScanVa * sizeof (MVL_VAR_ASSOC *));
scanCtrl = (MVLU_RPT_SCAN_CTRL *) M_CALLOC (MSMEM_GEN, 1, scanCtrlSize);
scanCtrl->scan_va = (MVL_VAR_ASSOC **) (scanCtrl + 1);
scanCtrl->num_scan_va = numScanVa;
/* Point after array of (MVL_VAR_ASSOC *) */
scanCtrl->scan_va_scope = (MVL_SCOPE *) (scanCtrl->scan_va + numScanVa);
scanCtrl->indCtrl.event = &scanCtrl->commEvent;
scanCtrl->commEvent.event_type = MVLU_RPT_COMM_EVENT;
scanCtrl->indCtrl.op = MMSOP_MVLU_RPT_VA;
/* Point after array of (MVL_SCOPE) */
scanCtrl->indCtrl.u.rd.vaCtrlTbl =
(MVLAS_RD_VA_CTRL *) (scanCtrl->scan_va_scope + numScanVa);
scanCtrl->indCtrl.scan_va_done_fun = _mvlu_rpt_va_reads_done;
scanCtrl->indCtrl.usr_ind_ctrl = scanCtrl;
scanCtrl->scan_period = 10000; /* Default to 10 second scan period*/
list_add_last((ST_VOID **) &mvlu_rpt_scan_list, (ST_VOID *) scanCtrl);
return (scanCtrl);
}
/************************************************************************/
/* mvlu_rpt_destroy_scan_ctrl */
/* This function destroys a MVLU_RPT_SCAN_CTRL created by */
/* 'mvlu_rpt_create_scan_ctrl' */
/************************************************************************/
ST_VOID mvlu_rpt_destroy_scan_ctrl (MVLU_RPT_SCAN_CTRL *scanCtrl)
{
/* Take it off the scan control list */
list_unlink ((ST_VOID **) &mvlu_rpt_scan_list, (ST_VOID *) scanCtrl);
M_FREE (MSMEM_GEN, scanCtrl);
}
/************************************************************************/
/************************************************************************/
/* BASRCB READ/WRITE HANDLERS */
/************************************************************************/
/************************************************************************/
/* _mvlu_get_rcb */
/************************************************************************/
static ST_VOID _mvlu_get_rcb (MVL_VAR_ASSOC *baseVa,
RUNTIME_TYPE *rt,
MVL_NET_INFO *netInfo,
MVLU_RPT_CTRL **rptCtrlOut,
MVLU_RPT_CLIENT **rptClientOut)
{
MVLU_RPT_CTRL *rptCtrl;
MVLU_RPT_CLIENT *rptClient;
*rptCtrlOut = NULL;
*rptClientOut = NULL;
/* Find the associated Report Control, using this RT as a key */
rptCtrl = mvlu_rpt_ctrl_list;
while (rptCtrl != NULL) /* look through whole list */
{
if (rptCtrl->base_va == baseVa &&
(rt > rptCtrl->rcbRtHead) &&
(rt <= rptCtrl->rcbRtHead + rptCtrl->rcbRtHead->u.str.num_rt_blks))
{
*rptCtrlOut = rptCtrl;
break;
}
rptCtrl = (MVLU_RPT_CTRL *) list_get_next (mvlu_rpt_ctrl_list, rptCtrl);
}
if (*rptCtrlOut == NULL)
{
MVL_LOG_NERR0 ("Warning: could not find report control");
return;
}
/* Report Control found. */
assert (rptCtrl->rcb_type == RCB_TYPE_UCA); /* must be UCA URCB*/
/* Now see if this connection has an active client */
rptClient = rptCtrl->rpt_client_list;
while (rptClient != NULL) /* look through whole list */
{
if (rptClient->netInfo == netInfo)
break; /* return a pointer to it */
rptClient = (MVLU_RPT_CLIENT *) list_get_next (rptCtrl->rpt_client_list, rptClient);
}
*rptClientOut = rptClient;
}
/************************************************************************/
/* _mvlu_get_rd_rcb */
/* NOTE: Caller can access MVLU_RPT_CTRL, if needed, by using "rpt_ctrl"*/
/* member of MVLU_RPT_CLIENT. */
/************************************************************************/
MVLU_BASRCB *_mvlu_get_rd_rcb (MVLU_RD_VA_CTRL *mvluRdVaCtrl,
MVLU_RPT_CLIENT **rptClientOut)
{
MVLU_RPT_CTRL *rptCtrl;
MVLU_RPT_CLIENT *rptClient;
MVLU_BASRCB *rcb;
/* Try to find 61850 RCB first. If found, return it. */
if ((rcb = mvl61850_get_rcb (mvluRdVaCtrl->rdVaCtrl->va->base_va,
mvluRdVaCtrl->rt,
rptClientOut)) != NULL)
return (rcb);
/* Could not find 61850 RCB, so now try to find UCA RCB. */
/* NOTE: Need net_info to find correct UCA RCB, so if it's not */
/* known, give up now. This problem can occur if user includes*/
/* RCB element in a Report DataSet (not supported for UCA). */
if (mvluRdVaCtrl->indCtrl->event == NULL ||
mvluRdVaCtrl->indCtrl->event->net_info == NULL)
return (NULL); /* don't know the connection, so can't find UCA RCB.*/
_mvlu_get_rcb (mvluRdVaCtrl->rdVaCtrl->va->base_va,
mvluRdVaCtrl->rt,
mvluRdVaCtrl->indCtrl->event->net_info,
&rptCtrl,
&rptClient);
if (rptClientOut != NULL)
*rptClientOut = rptClient;
/* If we have a report client, use it's BASRCB */
if (rptClient != NULL)
return (&rptClient->basrcb);
/* If not, and the report control was found, use the commom data */
if (rptCtrl != NULL)
return (&rptCtrl->common_basrcb);
/* Whoops, could not find the report control at all */
return (NULL);
}
/************************************************************************/
/* _mvlu_get_wr_rcb */
/* NOTE: Caller can access MVLU_RPT_CTRL, if needed, by using "rpt_ctrl"*/
/* member of MVLU_RPT_CLIENT. */
/************************************************************************/
MVLU_BASRCB *_mvlu_get_wr_rcb (MVLU_WR_VA_CTRL *mvluWrVaCtrl,
MVLU_RPT_CLIENT **rptClientOut)
{
MVLU_RPT_CTRL *rptCtrl;
MVLU_RPT_CLIENT *rptClient;
MVL_NET_INFO *netInfo;
MVLU_RPT_CLIENT *foundClient=NULL; /* If correct Client found, */
/* this is set to point to it. */
MVLU_BASRCB *rcb;
/* Try to find 61850 RCB first. If found, return it. */
if ((rcb = mvl61850_get_rcb (mvluWrVaCtrl->wrVaCtrl->va->base_va,
mvluWrVaCtrl->rt,
rptClientOut)) != NULL)
return (rcb);
/* Find the associated Report Control */
netInfo = mvluWrVaCtrl->indCtrl->event->net_info;
_mvlu_get_rcb (mvluWrVaCtrl->wrVaCtrl->va->base_va,
mvluWrVaCtrl->rt,
netInfo,
&rptCtrl,
&rptClient);
if (rptClient) /* if Client was found, */
foundClient = rptClient; /* use it */
else if (rptCtrl) /* else if rptCtrl was found, */
foundClient = _mvlu_add_rpt_client (rptCtrl, netInfo); /* add client, use it*/
/* Set caller's ptr. */
if (rptClientOut != NULL)
*rptClientOut = foundClient;
/* If client found, return its basrcb, else return NULL. */
return (foundClient ? (&foundClient->basrcb) : NULL);
}
/************************************************************************/
/* _rcb_writable */
/* Check if an RCB element is writable at this moment. */
/* NOTE: Special RCB elements may require additional checking. */
/* RETURNS: SD_TRUE if writable, SD_FALSE if NOT writable. */
/************************************************************************/
ST_RET _rcb_writable (MVLU_BASRCB *rcb, MVLU_RPT_CLIENT *rptClient,
MVLU_WR_VA_CTRL *mvluWrVaCtrl)
{
ST_RET writable;
writable = SD_FALSE; /* assume NOT writable. Change if conditions correct.*/
/* First, the "rcb" must be valid (!=NULL), and NOT enabled. */
if (rcb != NULL && rcb->RptEna == 0)
{ /* if rcb is valid, rptClient must also be valid */
assert (rptClient != NULL);
if (rptClient->rpt_ctrl->rcb_type == RCB_TYPE_IEC_URCB)
{ /* if not reserved OR reserved by us, allow write. */
if (rcb->Resv == 0 || rptClient->netInfo == mvluWrVaCtrl->indCtrl->event->net_info)
writable = SD_TRUE;
}
else /* rcb_type == RCB_TYPE_IEC_BRCB or RCB_TYPE_UCA */
{ /* nothing else to check, allow write. */
writable = SD_TRUE;
}
}
return (writable);
}
/************************************************************************/
/* _mvlu_add_rpt_client */
/************************************************************************/
static MVLU_RPT_CLIENT *_mvlu_add_rpt_client (MVLU_RPT_CTRL *rptCtrl,
MVL_NET_INFO *netInfo)
{
MVLU_RPT_CLIENT *rptClient;
ST_INT rptClientSize;
ST_INT numDsVars;
ST_INT changed_flags_size;
assert (rptCtrl->rcb_type == RCB_TYPE_UCA); /* this funct only for UCA*/
/* Alloc report control client enough to contain the reasons and */
/* changed_flags data */
numDsVars = rptCtrl->dsNvl->num_of_entries;
changed_flags_size = BSTR_NUMBITS_TO_NUMBYTES(numDsVars);
rptClientSize = sizeof (MVLU_RPT_CLIENT) +
(numDsVars * sizeof (ST_UINT8)) + /* reasons_data */
2 * changed_flags_size; /* changed_flags & segmented_inclusion */
rptClient = M_CALLOC (MSMEM_GEN, 1, rptClientSize);
rptClient->rpt_ctrl = rptCtrl;
rptClient->netInfo = netInfo;
memcpy (&rptClient->basrcb, &rptCtrl->common_basrcb, sizeof (MVLU_BASRCB));
rptClient->reasons_data = (ST_UINT8 *) (rptClient + 1);
rptClient->changed_flags = (ST_UINT8 *) (rptClient->reasons_data + numDsVars);
rptClient->segmented_inclusion = rptClient->changed_flags + changed_flags_size;
list_add_last((ST_VOID **) &rptCtrl->rpt_client_list, (ST_VOID *) rptClient);
++rptCtrl->num_rpt_clients;
_mvlu_rpt_disconnect_rcvd_fun = _mvlu_rpt_disconnect_rcvd;
return (rptClient);
}
/************************************************************************/
/* _mvlu_free_rpt_client */
/************************************************************************/
static ST_VOID _mvlu_free_rpt_client (MVLU_RPT_CLIENT *rptClient)
{
M_FREE (MSMEM_GEN, rptClient);
}
/************************************************************************/
/************************************************************************/
/* mvlu_rptid_rd_ind_fun */
/************************************************************************/
ST_VOID mvlu_rptid_rd_ind_fun (MVLU_RD_VA_CTRL *mvluRdVaCtrl)
{
ST_CHAR *dest;
MVLU_BASRCB *rcb;
ST_RET rc;
rc = SD_FAILURE;
rcb = _mvlu_get_rd_rcb (mvluRdVaCtrl, NULL);
if (rcb != NULL)
{
dest = (ST_CHAR *) mvluRdVaCtrl->primData;
strcpy (dest, rcb->RptID);
rc = SD_SUCCESS;
}
mvlu_rd_prim_done (mvluRdVaCtrl, rc);
}
/************************************************************************/
/* mvlu_rptena_rd_ind_fun */
/************************************************************************/
ST_VOID mvlu_rptena_rd_ind_fun (MVLU_RD_VA_CTRL *mvluRdVaCtrl)
{
ST_BOOLEAN *dest;
MVLU_BASRCB *rcb;
ST_RET rc;
rc = SD_FAILURE;
rcb = _mvlu_get_rd_rcb (mvluRdVaCtrl, NULL);
if (rcb != NULL)
{
dest = (ST_BOOLEAN *) mvluRdVaCtrl->primData;
*dest = rcb->RptEna;
rc = SD_SUCCESS;
}
mvlu_rd_prim_done (mvluRdVaCtrl, rc);
}
/************************************************************************/
/* mvlu_resv_rd_ind_fun */
/************************************************************************/
ST_VOID mvlu_resv_rd_ind_fun (MVLU_RD_VA_CTRL *mvluRdVaCtrl)
{
ST_BOOLEAN *dest;
MVLU_BASRCB *rcb;
ST_RET rc;
rc = SD_FAILURE;
rcb = _mvlu_get_rd_rcb (mvluRdVaCtrl, NULL);
if (rcb != NULL)
{
dest = (ST_BOOLEAN *) mvluRdVaCtrl->primData;
*dest = rcb->Resv;
rc = SD_SUCCESS;
}
mvlu_rd_prim_done (mvluRdVaCtrl, rc);
}
/************************************************************************/
/* mvlu_datsetna_rd_ind_fun */
/************************************************************************/
ST_VOID mvlu_datsetna_rd_ind_fun (MVLU_RD_VA_CTRL *mvluRdVaCtrl)
{
ST_CHAR *dest;
MVLU_BASRCB *rcb;
ST_RET rc;
rc = SD_FAILURE;
rcb = _mvlu_get_rd_rcb (mvluRdVaCtrl, NULL);
if (rcb != NULL)
{
assert (strlen(rcb->DatSetNa) <= (size_t)abs(mvluRdVaCtrl->rt->u.p.el_len));
dest = mvluRdVaCtrl->primData;
strcpy (dest, rcb->DatSetNa);
rc = SD_SUCCESS;
}
mvlu_rd_prim_done (mvluRdVaCtrl, rc);
}
/************************************************************************/
/* mvlu_optflds_rd_ind_fun */
/************************************************************************/
ST_VOID mvlu_optflds_rd_ind_fun (MVLU_RD_VA_CTRL *mvluRdVaCtrl)
{
MVLU_BASRCB *rcb;
ST_RET rc;
rc = SD_FAILURE;
rcb = _mvlu_get_rd_rcb (mvluRdVaCtrl, NULL);
if (rcb != NULL)
{
assert (rcb->OptFlds.len_1 <= sizeof(rcb->OptFlds.data_1)*8);
bvstrcpy ((MMS_BVSTRING *) mvluRdVaCtrl->primData,
(MMS_BVSTRING *) &rcb->OptFlds);
rc = SD_SUCCESS;
}
mvlu_rd_prim_done (mvluRdVaCtrl, rc);
}
/************************************************************************/
/* mvlu_buftim_rd_ind_fun */
/************************************************************************/
ST_VOID mvlu_buftim_rd_ind_fun (MVLU_RD_VA_CTRL *mvluRdVaCtrl)
{
ST_UINT32 *dest;
MVLU_BASRCB *rcb;
ST_RET rc;
rc = SD_FAILURE;
rcb = _mvlu_get_rd_rcb (mvluRdVaCtrl, NULL);
if (rcb != NULL)
{
dest = (ST_UINT32 *) mvluRdVaCtrl->primData;
*dest = rcb->BufTim;
rc = SD_SUCCESS;
}
mvlu_rd_prim_done (mvluRdVaCtrl, rc);
}
/************************************************************************/
/* mvlu_trgs_rd_ind_fun */
/************************************************************************/
ST_VOID mvlu_trgs_rd_ind_fun (MVLU_RD_VA_CTRL *mvluRdVaCtrl)
{
ST_UINT16 *dest;
MVLU_BASRCB *rcb;
ST_RET rc;
rc = SD_FAILURE;
rcb = _mvlu_get_rd_rcb (mvluRdVaCtrl, NULL);
if (rcb != NULL)
{
dest = (ST_UINT16 *) mvluRdVaCtrl->primData;
*dest = rcb->Trgs;
rc = SD_SUCCESS;
}
mvlu_rd_prim_done (mvluRdVaCtrl, rc);
}
/************************************************************************/
/* mvlu_sqnum_rd_ind_fun */
/************************************************************************/
ST_VOID mvlu_sqnum_rd_ind_fun (MVLU_RD_VA_CTRL *mvluRdVaCtrl)
{
ST_UINT8 *dest;
MVLU_BASRCB *rcb;
ST_RET rc;
rc = SD_FAILURE;
rcb = _mvlu_get_rd_rcb (mvluRdVaCtrl, NULL);
if (rcb != NULL)
{
dest = (ST_UINT8 *) mvluRdVaCtrl->primData;
*dest = rcb->SqNum;
rc = SD_SUCCESS;
}
mvlu_rd_prim_done (mvluRdVaCtrl, rc);
}
/************************************************************************/
/* mvlu_trgops_rd_ind_fun */
/************************************************************************/
ST_VOID mvlu_trgops_rd_ind_fun (MVLU_RD_VA_CTRL *mvluRdVaCtrl)
{
MVLU_BASRCB *rcb;
ST_RET rc;
rc = SD_FAILURE;
rcb = _mvlu_get_rd_rcb (mvluRdVaCtrl, NULL);
if (rcb != NULL)
{
assert (rcb->TrgOps.len <= sizeof(rcb->TrgOps.data)*8);
bvstrcpy ((MMS_BVSTRING *) mvluRdVaCtrl->primData,
(MMS_BVSTRING *) &rcb->TrgOps);
rc = SD_SUCCESS;
}
mvlu_rd_prim_done (mvluRdVaCtrl, rc);
}
/************************************************************************/
/* mvlu_rbepd_rd_ind_fun */
/************************************************************************/
ST_VOID mvlu_rbepd_rd_ind_fun (MVLU_RD_VA_CTRL *mvluRdVaCtrl)
{
ST_UINT32 *dest;
MVLU_BASRCB *rcb;
ST_RET rc;
rc = SD_FAILURE;
rcb = _mvlu_get_rd_rcb (mvluRdVaCtrl, NULL);
if (rcb != NULL)
{
dest = (ST_UINT32 *) mvluRdVaCtrl->primData;
*dest = rcb->RBEPd;
rc = SD_SUCCESS;
}
mvlu_rd_prim_done (mvluRdVaCtrl, rc);
}
/************************************************************************/
/* mvlu_intgpd_rd_ind_fun */
/************************************************************************/
ST_VOID mvlu_intgpd_rd_ind_fun (MVLU_RD_VA_CTRL *mvluRdVaCtrl)
{
ST_UINT32 *dest;
MVLU_BASRCB *rcb;
ST_RET rc;
rc = SD_FAILURE;
rcb = _mvlu_get_rd_rcb (mvluRdVaCtrl, NULL);
if (rcb != NULL)
{
dest = (ST_UINT32 *) mvluRdVaCtrl->primData;
*dest = rcb->IntgPd;
rc = SD_SUCCESS;
}
mvlu_rd_prim_done (mvluRdVaCtrl, rc);
}
/************************************************************************/
/* mvlu_rptid_wr_ind_fun */
/************************************************************************/
ST_VOID mvlu_rptid_wr_ind_fun (MVLU_WR_VA_CTRL *mvluWrVaCtrl)
{
ST_CHAR *src;
MVLU_BASRCB *rcb;
ST_RET rc;
MVLU_RPT_CLIENT *rptClient;
ST_BOOLEAN do_purge = SD_FALSE; /* Chg to SD_TRUE if Purge Buffer required*/
rc = SD_FAILURE;
rcb = _mvlu_get_wr_rcb (mvluWrVaCtrl, &rptClient);
if (_rcb_writable (rcb, rptClient, mvluWrVaCtrl))
{
src = mvluWrVaCtrl->primData;
if (rptClient->rpt_ctrl->rcb_type == RCB_TYPE_IEC_BRCB
&& strcmp (rcb->RptID, src))
do_purge = SD_TRUE; /* BRCB & val changed, must purge buffer*/
strcpy (rcb->RptID, src);
rc = SD_SUCCESS;
}
else
mvluWrVaCtrl->wrVaCtrl->failure = ARE_TEMP_UNAVAIL;
if (do_purge)
mvl61850_brcb_rpt_lists_clean (&rptClient->rpt_ctrl->brcbCtrl);
mvlu_wr_prim_done (mvluWrVaCtrl, rc);
}
/************************************************************************/
/* mvl61850_brcb_handle_rptena_wr */
/* Process "RptEna" write for 61850 BRCB. */
/************************************************************************/
ST_RET mvl61850_brcb_handle_rptena_wr (MVLU_RPT_CLIENT *rptClient, MVLU_BASRCB *rcb,
MVL_NET_INFO *net_info, ST_BOOLEAN newval)
{
ST_RET retCode;
if (rptClient->netInfo == NULL ||
rptClient->netInfo == net_info)
{ /* BRCB not enabled, or already enabled by us, so allow write.*/
retCode = SD_SUCCESS; /* allow write */
if (newval != 0)
{ /* trying to enable */
rptClient->rpt_ctrl->brcbCtrl.enabled_once = SD_TRUE;
rptClient->netInfo = net_info; /* reserve BRCB */
rcb->SqNumInt16u = 0; /* reset SqNum */
/*renxiaobao 61850 <20>ڶ<EFBFBD><DAB6><EFBFBD><EFBFBD>޸<EFBFBD>*/
/*ʹ<>ܺ<EFBFBD><DCBA>ĵ<EFBFBD>һ<EFBFBD><D2BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>λ*/
rptClient->rpt_ctrl->brcbCtrl.BufOvfl = SD_TRUE;
}
else
{ /* trying to disable */
rptClient->netInfo = NULL; /* release BRCB reservation */
}
}
else
{ /* RCB reserved by someone else */
retCode = SD_FAILURE; /* DO NOT allow write */
}
return (retCode);
}
/************************************************************************/
/* mvl61850_urcb_handle_rptena_wr */
/* Process "RptEna" write for 61850 URCB. */
/************************************************************************/
ST_RET mvl61850_urcb_handle_rptena_wr (MVLU_RPT_CLIENT *rptClient, MVLU_BASRCB *rcb,
MVL_NET_INFO *net_info, ST_BOOLEAN newval)
{
ST_RET retCode;
if (rptClient->netInfo == NULL ||
rptClient->netInfo == net_info)
{ /* RCB not reserved, or reserved by us */
retCode = SD_SUCCESS; /* allow write */
if (newval != 0)
{ /* enabling RCB */
if (rcb->Resv == 0) /* If URCB not reserved yet, */
rcb->Resv = 1; /* reserve it now. */
rcb->SqNum = 0; /* reset SqNum for URCB only */
rptClient->netInfo = net_info; /* reserve RCB */
}
/* If newval==0, do nothing (just allow write). RCB not released until Resv set to 0.*/
}
else
{ /* RCB reserved by someone else */
retCode = SD_FAILURE; /* DO NOT allow write */
}
return (retCode);
}
/************************************************************************/
/* mvlu_rptena_wr_ind_fun */
/************************************************************************/
ST_VOID mvlu_rptena_wr_ind_fun (MVLU_WR_VA_CTRL *mvluWrVaCtrl)
{
ST_BOOLEAN newval; /* val to write to RptEna */
MVLU_BASRCB *rcb;
MVLU_RPT_CLIENT *rptClient;
ST_RET rc;
MVL_NET_INFO *net_info; /* current connection info */
newval = *(ST_BOOLEAN *) mvluWrVaCtrl->primData;
net_info = mvluWrVaCtrl->indCtrl->event->net_info;
rc = SD_FAILURE;
rcb = _mvlu_get_wr_rcb (mvluWrVaCtrl, &rptClient);
if (rcb != NULL)
{
/* CRITICAL: don't allow enable if dataSet not set (dsNvl==NULL). */
if (newval != 0 && rptClient->rpt_ctrl->dsNvl == NULL)
{ /* Just log it. rc already set.*/
MVL_LOG_NERR0 ("Cannot enable RCB with NULL DataSet.");
}
else
{
switch (rptClient->rpt_ctrl->rcb_type)
{
case RCB_TYPE_IEC_BRCB:
rc = mvl61850_brcb_handle_rptena_wr (rptClient, rcb, net_info, newval);
break;
case RCB_TYPE_IEC_URCB:
rc = mvl61850_urcb_handle_rptena_wr (rptClient, rcb, net_info, newval);
break;
default: /* must be UCA URCB */
rc = SD_SUCCESS; /* ALWAYS allow write for URCB */
if (newval != 0) /* enabling RCB */
rcb->SqNum = 0; /* reset SqNum for URCB only */
break;
}
}
}
/* If allowing write, store new value. */
if (rc == SD_SUCCESS)
rcb->RptEna = newval;
else /* for any error, return TEMP_UNAVAIL */
mvluWrVaCtrl->wrVaCtrl->failure = ARE_TEMP_UNAVAIL;
mvlu_wr_prim_done (mvluWrVaCtrl, rc);
}
/************************************************************************/
/* mvlu_resv_wr_ind_fun */
/************************************************************************/
ST_VOID mvlu_resv_wr_ind_fun (MVLU_WR_VA_CTRL *mvluWrVaCtrl)
{
MVLU_BASRCB *rcb;
MVLU_RPT_CLIENT *rptClient;
ST_RET rc = SD_FAILURE;
/* Only for 61850, so use mvl61850_get_rcb, not mvlu_get_wr_rcb. */
rcb = mvl61850_get_rcb (mvluWrVaCtrl->wrVaCtrl->va->base_va,
mvluWrVaCtrl->rt,
&rptClient);
if (rcb != NULL)
{
assert (rptClient->rpt_ctrl->rcb_type == RCB_TYPE_IEC_URCB);
if (rptClient->netInfo == NULL ||
rptClient->netInfo == mvluWrVaCtrl->indCtrl->event->net_info)
{ /* RCB not reserved, or reserved by us, so allow write.*/
rc = SD_SUCCESS; /* allow write */
rcb->Resv = *(ST_BOOLEAN *) mvluWrVaCtrl->primData;
if (rcb->Resv != 0)
rptClient->netInfo = mvluWrVaCtrl->indCtrl->event->net_info; /* reserve RCB */
else
{
/* If RCB DatSet is AA_SPEC, remove it (i.e. cleanup & set DatSet=""). */
if (rptClient->rpt_ctrl->dsNvl != NULL &&
rptClient->rpt_ctrl->dsNvl->nvl_scope.scope == AA_SPEC)
mvl61850_rpt_dataset_destroy (rptClient->rpt_ctrl);
rptClient->netInfo = NULL; /* unreserve RCB*/
}
}
/* else rc=SD_FAILURE already set */
}
if (rc != SD_SUCCESS)
mvluWrVaCtrl->wrVaCtrl->failure = ARE_TEMP_UNAVAIL;
mvlu_wr_prim_done (mvluWrVaCtrl, rc);
}
/************************************************************************/
/* mvlu_optflds_wr_ind_fun */
/************************************************************************/
ST_VOID mvlu_optflds_wr_ind_fun (MVLU_WR_VA_CTRL *mvluWrVaCtrl)
{
MVLU_BASRCB *rcb;
ST_RET rc;
MMS_BVSTRING *bvstr;
ST_INT numbits;
MVLU_RPT_CLIENT *rptClient;
rc = SD_FAILURE;
rcb = _mvlu_get_wr_rcb (mvluWrVaCtrl, &rptClient);
if (_rcb_writable (rcb, rptClient, mvluWrVaCtrl))
{
/* Use 'rcb_type' to figure out how many bits are writable. */
ST_INT rcb_type = rptClient->rpt_ctrl->rcb_type;
bvstr = (MMS_BVSTRING *) mvluWrVaCtrl->primData;
/* NOTE: don't change OptFlds.len_1. May only be writing a few */
/* bits, but want to send ALL bits in read response or Info Rpt. */
if (rcb_type == RCB_TYPE_UCA)
{
numbits = min (bvstr->len, 5); /* 5 bits writable. Ignore higher bits.*/
bstrcpy (rcb->OptFlds.data_1, bvstr->data, numbits);
}
else
{ /* must be either IEC_URCB or IEC_BRCB */
numbits = min (bvstr->len, 9); /* 9 bits writable. Ignore higher bits.*/
/*renxiaobao 61850 <20>ڶ<EFBFBD><DAB6><EFBFBD><EFBFBD>޸<EFBFBD>*/
if(bstrcmp (rcb->OptFlds.data_1, bvstr->data, numbits))
memset(rcb->EntryID,0,8);
/*end*/
bstrcpy (rcb->OptFlds.data_1, bvstr->data, numbits);
if (rcb_type == RCB_TYPE_IEC_URCB)
{ /* 61850-8-1 says to ignore buffer-overflow, entryID bits, so clear them*/
BSTR_BIT_SET_OFF (rcb->OptFlds.data_1, OPTFLD_BITNUM_BUFOVFL);
BSTR_BIT_SET_OFF (rcb->OptFlds.data_1, OPTFLD_BITNUM_ENTRYID);
}
}
rc = SD_SUCCESS;
}
else
mvluWrVaCtrl->wrVaCtrl->failure = ARE_TEMP_UNAVAIL;
mvlu_wr_prim_done (mvluWrVaCtrl, rc);
}
/************************************************************************/
/* mvlu_buftim_wr_ind_fun */
/************************************************************************/
ST_VOID mvlu_buftim_wr_ind_fun (MVLU_WR_VA_CTRL *mvluWrVaCtrl)
{
ST_UINT32 *src;
MVLU_BASRCB *rcb;
ST_RET rc;
MVLU_RPT_CLIENT *rptClient;
ST_BOOLEAN do_purge = SD_FALSE; /* Chg to SD_TRUE if Purge Buffer required*/
rc = SD_FAILURE;
rcb = _mvlu_get_wr_rcb (mvluWrVaCtrl, &rptClient);
if (_rcb_writable (rcb, rptClient, mvluWrVaCtrl))
{
src = (ST_UINT32 *) mvluWrVaCtrl->primData;
if (rptClient->rpt_ctrl->rcb_type == RCB_TYPE_IEC_BRCB
&& rcb->BufTim != *src)
{
do_purge = SD_TRUE; /* BRCB & val changed, must purge buffer*/
/*renxiaobao 61850 <20>ڶ<EFBFBD><DAB6><EFBFBD><EFBFBD>޸<EFBFBD>*/
memset(rcb->EntryID,0,8);
}
rcb->BufTim = *src;
rc = SD_SUCCESS;
}
else
mvluWrVaCtrl->wrVaCtrl->failure = ARE_TEMP_UNAVAIL;
if (do_purge)
mvl61850_brcb_rpt_lists_clean (&rptClient->rpt_ctrl->brcbCtrl);
mvlu_wr_prim_done (mvluWrVaCtrl, rc);
}
/************************************************************************/
/* mvlu_trgs_wr_ind_fun */
/* NOTE: for UCA RCB only. */
/************************************************************************/
ST_VOID mvlu_trgs_wr_ind_fun (MVLU_WR_VA_CTRL *mvluWrVaCtrl)
{
ST_UINT16 *src;
MVLU_BASRCB *rcb;
ST_RET rc;
rc = SD_FAILURE;
rcb = _mvlu_get_wr_rcb (mvluWrVaCtrl, NULL);
if (rcb != NULL && rcb->RptEna == SD_FALSE) /* if enabled, can't write*/
{
src = (ST_UINT16 *) mvluWrVaCtrl->primData;
rcb->Trgs = *src;
rc = SD_SUCCESS;
}
else
mvluWrVaCtrl->wrVaCtrl->failure = ARE_TEMP_UNAVAIL;
mvlu_wr_prim_done (mvluWrVaCtrl, rc);
}
/************************************************************************/
/* mvlu_sqnum_wr_ind_fun */
/* NOTE: for UCA RCB only. In IEC 61850 RCB, SqNum may not be written. */
/************************************************************************/
ST_VOID mvlu_sqnum_wr_ind_fun (MVLU_WR_VA_CTRL *mvluWrVaCtrl)
{
ST_UINT8 *src;
MVLU_BASRCB *rcb;
ST_RET rc;
rc = SD_FAILURE;
rcb = _mvlu_get_wr_rcb (mvluWrVaCtrl, NULL);
if (rcb != NULL && rcb->RptEna == SD_FALSE) /* if enabled, can't write*/
{
src =(ST_UINT8 *) mvluWrVaCtrl->primData;
rcb->SqNum = *src;
rc = SD_SUCCESS;
}
else
mvluWrVaCtrl->wrVaCtrl->failure = ARE_TEMP_UNAVAIL;
mvlu_wr_prim_done (mvluWrVaCtrl, rc);
}
/************************************************************************/
/* mvlu_trgops_wr_ind_fun */
/************************************************************************/
extern int rcb_TrgOps_wr_check(char *rpt_id,char wr_data);
ST_VOID mvlu_trgops_wr_ind_fun (MVLU_WR_VA_CTRL *mvluWrVaCtrl)
{
MVLU_BASRCB *rcb;
ST_RET rc;
MMS_BVSTRING *bvstr;
MVLU_RPT_CLIENT *rptClient;
ST_BOOLEAN do_purge = SD_FALSE; /* Chg to SD_TRUE if Purge Buffer required*/
rc = SD_FAILURE;
rcb = _mvlu_get_wr_rcb (mvluWrVaCtrl, &rptClient);
if (_rcb_writable (rcb, rptClient, mvluWrVaCtrl))
{
bvstr = (MMS_BVSTRING *) mvluWrVaCtrl->primData;
/* NOTE: don't change TrgOps.len. May only be writing a few */
/* bits, but want to send ALL bits in read response or Info Rpt. */
/* assert (bvstr->len <= (sizeof (rcb->TrgOps.data)*8));*/
if (rptClient->rpt_ctrl->rcb_type == RCB_TYPE_IEC_BRCB
&& bstrcmp (rcb->TrgOps.data, bvstr->data, bvstr->len))
{
do_purge = SD_TRUE; /* BRCB & val changed, must purge buffer*/
/*renxiaobao 61850 <20>ڶ<EFBFBD><DAB6><EFBFBD><EFBFBD>޸<EFBFBD>*/
memset(rcb->EntryID,0,8);
}
if(rcb_TrgOps_wr_check(rptClient->rpt_ctrl->common_basrcb.RptID,bvstr->data[0]))
bstrcpy (rcb->TrgOps.data, bvstr->data, bvstr->len);
rc = SD_SUCCESS;
}
else
mvluWrVaCtrl->wrVaCtrl->failure = ARE_TEMP_UNAVAIL;
if (do_purge)
mvl61850_brcb_rpt_lists_clean (&rptClient->rpt_ctrl->brcbCtrl);
mvlu_wr_prim_done (mvluWrVaCtrl, rc);
}
/************************************************************************/
/* mvlu_rbepd_wr_ind_fun */
/* NOTE: for UCA RCB only. */
/************************************************************************/
ST_VOID mvlu_rbepd_wr_ind_fun (MVLU_WR_VA_CTRL *mvluWrVaCtrl)
{
ST_UINT32 *src;
MVLU_BASRCB *rcb;
ST_RET rc;
rc = SD_FAILURE;
rcb = _mvlu_get_wr_rcb (mvluWrVaCtrl, NULL);
if (rcb != NULL && rcb->RptEna == SD_FALSE) /* if enabled, can't write*/
{
src = (ST_UINT32 *) mvluWrVaCtrl->primData;
rcb->RBEPd = *src;
rc = SD_SUCCESS;
}
else
mvluWrVaCtrl->wrVaCtrl->failure = ARE_TEMP_UNAVAIL;
mvlu_wr_prim_done (mvluWrVaCtrl, rc);
}
/************************************************************************/
/* mvlu_intgpd_wr_ind_fun */
/************************************************************************/
ST_VOID mvlu_intgpd_wr_ind_fun (MVLU_WR_VA_CTRL *mvluWrVaCtrl)
{
ST_UINT32 *src;
MVLU_BASRCB *rcb;
ST_RET rc;
MVLU_RPT_CLIENT *rptClient;
ST_BOOLEAN do_purge = SD_FALSE; /* Chg to SD_TRUE if Purge Buffer required*/
rc = SD_FAILURE;
rcb = _mvlu_get_wr_rcb (mvluWrVaCtrl, &rptClient);
if (_rcb_writable (rcb, rptClient, mvluWrVaCtrl))
{
src = (ST_UINT32 *) mvluWrVaCtrl->primData;
if (rptClient->rpt_ctrl->rcb_type == RCB_TYPE_IEC_BRCB
&& rcb->IntgPd != *src)
{
do_purge = SD_TRUE; /* BRCB & val changed, must purge buffer*/
/*renxiaobao 61850 <20>ڶ<EFBFBD><DAB6><EFBFBD><EFBFBD>޸<EFBFBD>*/
rptClient->next_integ_rpt_time = 0; /* reset timer */
memset(rcb->EntryID,0,8);
}
rcb->IntgPd = *src;
rc = SD_SUCCESS;
}
else
mvluWrVaCtrl->wrVaCtrl->failure = ARE_TEMP_UNAVAIL;
if (do_purge)
mvl61850_brcb_rpt_lists_clean (&rptClient->rpt_ctrl->brcbCtrl);
mvlu_wr_prim_done (mvluWrVaCtrl, rc);
}
/************************************************************************/
/************************************************************************/
/* For backward compatibility ... do not use */
ST_VOID mvlu_seqnum_rd_ind_fun (MVLU_RD_VA_CTRL *mvluRdVaCtrl)
{
mvlu_sqnum_rd_ind_fun (mvluRdVaCtrl);
}
ST_VOID mvlu_seqnum_wr_ind_fun (MVLU_WR_VA_CTRL *mvluWrVaCtrl)
{
mvlu_sqnum_wr_ind_fun (mvluWrVaCtrl);
}
/*
* mvlu_integrity_scan_destroy
* Free data allocated by mvlu_integrity_scan_read.
*/
ST_VOID mvlu_integrity_scan_destroy (MVL_IND_PEND *indCtrl)
{
M_FREE (MSMEM_GEN, indCtrl);
}
/*
* mvlu_integrity_scan_read
* This function "BEGINS" the scan of ALL data for one Report DataSet (NVL).
* It is called automatically when an Integrity Report or a
* General Interrogation Report needs to be sent. This is much more
* efficient than constantly scanning.
* - It allocates & initializes a temporary MVL_IND_PEND structure.
* - It calls "u_mvl_read_ind" to begin the scan.
* - The scan may complete synchronously or asynchronously.
* - When the scan completes, the funct pointed to by "scan_done_fun" arg
* is called to build the report and cleanup.
* CRITICAL: Function pointed to by "scan_done_fun" must call
* "mvlu_integrity_scan_destroy" to free temporary MVL_IND_PEND struct.
*/
ST_VOID mvlu_integrity_scan_read (MVLU_RPT_CLIENT *rptClient,
ST_VOID (*scan_va_done_fun)(MVL_IND_PEND *indCtrl,
MVL_VAR_ASSOC *va),
ST_RET (*scan_done_fun)(MVL_IND_PEND *indCtrl)
)
{
MVL_IND_PEND *indCtrl;
/* Create a simulated read indication for the DataSet NVL. */
indCtrl = mvlu_setup_scan_read (rptClient->rpt_ctrl->dsNvl,
scan_va_done_fun, scan_done_fun);
indCtrl->usr_ind_ctrl = rptClient; /* Save rptClient */
/* OK, now fire off the read indication functions. */
u_mvl_read_ind (indCtrl);
}
/**
* mvlu_setup_scan_read
* This function sets up the scan of ALL data for one NVL.
* It allocates & initializes a temporary MVL_IND_PEND structure for a
* simulated read indication.
* RETURNS: (MVL_IND_PEND *)
*/
MVL_IND_PEND *mvlu_setup_scan_read (MVL_NVLIST_CTRL *nvl,
ST_VOID (*scan_va_done_fun)(MVL_IND_PEND *indCtrl,
MVL_VAR_ASSOC *va),
ST_RET (*scan_done_fun)(MVL_IND_PEND *indCtrl)
)
{
MVLAS_RD_VA_CTRL *vaCtrl;
MVL_IND_PEND *indCtrl;
ST_INT j;
/* Allocate space for MVL_IND_PEND & array of MVLAS_RD_VA_CTRL. */
indCtrl = M_CALLOC (MSMEM_GEN, 1,
sizeof (MVL_IND_PEND) +
nvl->num_of_entries * sizeof (MVLAS_RD_VA_CTRL));
indCtrl->u.rd.numVar = nvl->num_of_entries;
indCtrl->op = MMSOP_MVLU_RPT_VA; /* indicates this is not normal read*/
indCtrl->u.rd.vaCtrlTbl = (MVLAS_RD_VA_CTRL *)(indCtrl+1);/*point after indCtrl*/
for (j = 0, vaCtrl = indCtrl->u.rd.vaCtrlTbl;
j < nvl->num_of_entries;
j++, vaCtrl++)
{
vaCtrl->va = nvl->entries[j];
memcpy (&vaCtrl->va_scope, &nvl->va_scope[j], sizeof(MVL_SCOPE));
}
/* Save funct ptr. Called from "mvlu_rd_prim_done" when scan of ONE va complete.*/
indCtrl->scan_va_done_fun = scan_va_done_fun;
/* Save funct ptr. Called from "mvlu_rd_prim_done" when scan complete.*/
indCtrl->usr_resp_fun = scan_done_fun; /* CRITICAL: */
return (indCtrl);
}
/************************************************************************/
/* mvlu_integrity_scan_va_done */
/* This function is called by 'mvlu_rd_prim_done' when all "leaf" */
/* functions for a VA have been completed (i.e. data ready for this VA).*/
/************************************************************************/
ST_VOID mvlu_integrity_scan_va_done (MVL_IND_PEND *indCtrl,
MVL_VAR_ASSOC *va)
{
ST_INT data_size;
/* CRITICAL: DO NOT call mvlu_rpt_va_change. rptClient->reasons_data */
/* will be set in mvlu_integrity_scan_done when scan completes. */
data_size = mvl_type_ctrl[va->type_id].data_size;
memcpy (va->last_data, va->data, data_size);
}
/************************************************************************/
/* mvlu_integrity_scan_done */
/* Same as "_mvlu_rpt_scan_done" except arg is (MVL_IND_PEND *) */
/* instead of (MVLU_RPT_SCAN_CTRL *). */
/* */
/* This function is called when the integrity scan is complete. */
/* (i.e. 'mvlu_rd_prim_done' has been called for all "leafs" of all */
/* variables in the report). Everything is ready to build a report, */
/* so build it now. */
/************************************************************************/
ST_RET mvlu_integrity_scan_done (MVL_IND_PEND *indCtrl)
{
ST_RET retCode;
MVLU_RPT_CLIENT *rptClient;
ST_INT j;
/* Get "rptClient", saved in "indCtrl" when scan initialized. */
rptClient = (MVLU_RPT_CLIENT *) indCtrl->usr_ind_ctrl;
/* Set "reason" for each variable of Dataset now. */
for (j = 0; j < rptClient->rpt_ctrl->dsNvl->num_of_entries; j++)
rptClient->reasons_data[j] = MVLU_TRGOPS_INTEGRITY;
/* send or queue rpt */
retCode = mvlu_rpt_ready (rptClient, MVLU_RPT_TYPE_INTEGRITY_OR_GI);
if (retCode != SD_SUCCESS)
MVLU_LOG_FLOW1 ("Integrity report send failed: err=0x%X", retCode);
/* set time for next integrity report */
rptClient->next_integ_rpt_time = sGetMsTime ()
+ (ST_DOUBLE) rptClient->basrcb.IntgPd;
rptClient->integ_scan_in_progress = SD_FALSE;
mvlu_integrity_scan_destroy (indCtrl); /* destroy temporary struct*/
return (retCode);
}
/************************************************************************/
/* find_or_create_typeid */
/* Try to find the type_id. If that fails, try to create it. */
/************************************************************************/
ST_INT find_or_create_typeid (ST_CHAR *type_name, ST_CHAR *tdl)
{
ST_INT type_id;
if ((type_id = mvl_typename_to_typeid (type_name)) < 0) /* find */
type_id = mvl_type_id_create_from_tdl (type_name, tdl); /* create*/
return (type_id);
}
/************************************************************************/
/* mvlu_rpt_find_typeids */
/* Find or create ALL types needed to encode reports. */
/* These types may have been defined in the ODF file and created by */
/* Foundry or may have been created by an earlier call to this function.*/
/* RETURNS: SD_SUCCESS if ALL types found or created */
/* SD_FAILURE if ANY type could not be found or created. */
/************************************************************************/
ST_RET mvlu_rpt_find_typeids (MVLU_RPT_TYPEIDS *rpt_typeids)
{
ST_RET retCode = SD_FAILURE; /* assume FAILURE for now */
ST_CHAR *type_name; /* name of type to be found */
/* stop on any error */
do
{ /* "one-time" loop: just to have something to break out of */
if ((rpt_typeids->mmsbool = find_or_create_typeid (type_name = "RTYP_BOOL", "Bool")) < 0)
break;
if ((rpt_typeids->bstr6 = find_or_create_typeid (type_name = "RTYP_BSTR6", "Bstring6")) < 0)
break;
if ((rpt_typeids->bstr8 = find_or_create_typeid (type_name = "RTYP_BSTR8", "Bstring8")) < 0)
break;
if ((rpt_typeids->bvstring6 = find_or_create_typeid (type_name = "RTYP_BVSTR6", "Bvstring6")) < 0)
break;
if ((rpt_typeids->bvstring8 = find_or_create_typeid (type_name = "RTYP_BVSTR8", "Bvstring8")) < 0)
break;
if ((rpt_typeids->bvstring10 = find_or_create_typeid (type_name = "RTYP_BVSTR10", "Bvstring10")) < 0)
break;
if ((rpt_typeids->btime6 = find_or_create_typeid (type_name = "RTYP_BTIME6", "Btime6")) < 0)
break;
if ((rpt_typeids->int8u = find_or_create_typeid (type_name = "RTYP_INT8U", "Ubyte")) < 0)
break;
if ((rpt_typeids->int16u = find_or_create_typeid (type_name = "RTYP_INT16U", "Ushort")) < 0)
break;
if ((rpt_typeids->int32u = find_or_create_typeid (type_name = "RTYP_INT32U", "Ulong")) < 0)
break;
if ((rpt_typeids->ostring8 = find_or_create_typeid (type_name = "RTYP_OSTR8", "Ostring8")) < 0)
break;
if ((rpt_typeids->vstring32 = find_or_create_typeid (type_name = "RTYP_VSTR32", "Vstring32")) < 0)
break;
if ((rpt_typeids->vstring65 = find_or_create_typeid (type_name = "RTYP_VSTR65", "Vstring65")) < 0)
break;
if ((rpt_typeids->vstring129 = find_or_create_typeid (type_name = "RTYP_VSTR129", "Vstring129")) < 0)
break;
retCode = SD_SUCCESS; /* If we get here, all were successful */
} while (0); /* end of "one-time" loop */
if (retCode)
MVL_LOG_ERR1 ("Can't find or create type '%s'", type_name);
return (retCode); /* If ANY find failed, SD_FAILURE is returned */
}
/************************************************************************/
/* mvlu_integrity_timeout */
/* Check for Integrity period timeout. If timeout occurred, start */
/* integrity scan. */
/* RETURNS: SD_TRUE if IntgPd is set and timeout occurred */
/* SD_FALSE otherwise */
/************************************************************************/
ST_RET mvlu_integrity_timeout (MVLU_RPT_CLIENT *rptClient, ST_DOUBLE timeNow)
{
ST_RET ret;
if (rptClient->basrcb.IntgPd != 0
&& BSTR_BIT_GET (rptClient->basrcb.TrgOps.data, TRGOPS_BITNUM_INTEGRITY) /* enabled*/
&& (!rptClient->integ_scan_in_progress)
&& timeNow > rptClient->next_integ_rpt_time)
{
MVLU_LOG_FLOW1 ("UCA Report Integrity Period timeout for client %08lx",
rptClient);
/* Send last buffered entry (due to BufTm buffering) BEFORE this integrity report.*/
if (rptClient->numTrgs > 0)
{ /* send or queue report */
mvlu_rpt_ready (rptClient, MVLU_RPT_TYPE_RBE);
}
/* Set scan_in_progress flag so new scan can't start before last
* scan finished. Clear flag in "mvlu_integrity_scan_done".
*/
rptClient->integ_scan_in_progress = SD_TRUE;
/* This function "begins" the integrity scan. When the scan is
* complete, "mvlu_integrity_scan_done" is called to build the
* the report. If ALL "leaf" functions are synchronous,
* "mvlu_integrity_scan_done" is called BEFORE this function returns.
*/
mvlu_integrity_scan_read (rptClient,
mvlu_integrity_scan_va_done,
mvlu_integrity_scan_done);
ret = SD_TRUE;
}
else
ret = SD_FALSE;
return (ret);
}
/************************************************************************/
/* mvlu_rpt_rcb_type_find */
/* Find the runtime type of the RCB and save it in *rcbHeadOut & *numRtOut.*/
/************************************************************************/
ST_RET mvlu_rpt_rcb_type_find (ST_INT type_id, ST_CHAR *basrcbName,
RUNTIME_TYPE **rcbHeadOut, ST_INT *numRtOut)
{
ST_RET ret = SD_FAILURE;
ST_CHAR *ptr;
ptr = strstr (basrcbName, "$");
if (ptr)
{
++ptr; /* Skip the '$' */
if (mvlu_find_comp_type (type_id, ptr, rcbHeadOut, numRtOut) == SD_SUCCESS)
{
if ((*rcbHeadOut)->el_tag == RT_STR_START)
ret = SD_SUCCESS; /* only path to success */
else
MVL_LOG_NERR1 ("'%s' is not a structure", basrcbName);
}
else
MVL_LOG_NERR1 ("Could not find type for component '%s'", basrcbName);
}
else
MVL_LOG_NERR1 ("Invalid BASRCB name: %s", basrcbName);
return (ret);
}
/************************************************************************/
/* mvlu_rpt_ready */
/* Report is ready to send or save in "buffer". */
/* Check the RCB type and call appropriate function. */
/************************************************************************/
ST_RET mvlu_rpt_ready (MVLU_RPT_CLIENT *rptClient, ST_INT rpt_type)
{
ST_RET retCode;
switch (rptClient->rpt_ctrl->rcb_type)
{
case RCB_TYPE_UCA:
retCode = mvlu_send_report (rptClient, rpt_type);
break;
case RCB_TYPE_IEC_BRCB:
retCode = mvl61850_brcb_rpt_save (rptClient); /* save rpt in buffer*/
break;
case RCB_TYPE_IEC_URCB:
retCode = mvl61850_urcb_rpt_send (rptClient->rpt_ctrl, rptClient, rpt_type);
break;
default:
retCode = SD_FAILURE; /* should never happen */
break;
}
return (retCode);
}
/************************************************************************/
/* mvlu_rpt_ctrl_destroy_all */
/* Destroy all UCA report controls. */
/************************************************************************/
ST_VOID mvlu_rpt_ctrl_destroy_all ()
{
MVLU_RPT_CTRL *rptCtrl;
MVLU_RPT_SCAN_CTRL *scanCtrl;
/* Use list_find_last to avoid calling list_unlink twice. */
while ((scanCtrl = (MVLU_RPT_SCAN_CTRL *) list_find_last ((DBL_LNK *)mvlu_rpt_scan_list)) != NULL)
{
mvlu_rpt_destroy_scan_ctrl (scanCtrl); /* this also unlinks it from list*/
}
/* Use list_find_last to avoid calling list_unlink twice. */
while ((rptCtrl = (MVLU_RPT_CTRL *) list_find_last ((DBL_LNK *)mvlu_rpt_ctrl_list)) != NULL)
{
mvlu_free_rpt_ctrl (rptCtrl); /* this also unlinks it from list*/
}
}