2571 lines
88 KiB
C
2571 lines
88 KiB
C
/************************************************************************/
|
||
/* 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*/
|
||
}
|
||
}
|
||
|