Files
microser/mmslib/mvlu/mvlu_rpt.c
2026-06-15 15:48:16 +08:00

2571 lines
88 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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