3287 lines
126 KiB
C
3287 lines
126 KiB
C
|
|
/************************************************************************/
|
|||
|
|
/* SISCO SOFTWARE MODULE HEADER *****************************************/
|
|||
|
|
/************************************************************************/
|
|||
|
|
/* (c) Copyright Systems Integration Specialists Company, Inc., */
|
|||
|
|
/* 2004-2006 All Rights Reserved */
|
|||
|
|
/* */
|
|||
|
|
/* MODULE NAME : sclproc.c */
|
|||
|
|
/* PRODUCT(S) : MMS-EASE-LITE */
|
|||
|
|
/* */
|
|||
|
|
/* MODULE DESCRIPTION : */
|
|||
|
|
/* Functions to "post-proccess" information parsed from SCL file. */
|
|||
|
|
/* */
|
|||
|
|
/* GLOBAL FUNCTIONS DEFINED IN THIS MODULE : */
|
|||
|
|
/* scl2_datatype_create_all */
|
|||
|
|
/* scl2_ld_create_all */
|
|||
|
|
/* */
|
|||
|
|
/* NOTE: FC must be "ST" "MX" "CO" "SP" "SG" "SE" "SV" "CF" "DC" or "EX"*/
|
|||
|
|
/* */
|
|||
|
|
/* NOTE: scl2_datatype_create_all allocates an array of RESERVED_INFO */
|
|||
|
|
/* structures to save extra SCL data for each datatype created. */
|
|||
|
|
/* The array pointer is saved in the reserved_1 member of */
|
|||
|
|
/* RUNTIME_CTRL, to be used later by scl2_ld_create_all, */
|
|||
|
|
/* which then frees it (see reserved_free_all). */
|
|||
|
|
/* */
|
|||
|
|
/* MODIFICATION LOG : */
|
|||
|
|
/* Date Who Rev Comments */
|
|||
|
|
/* -------- --- ------ ------------------------------------------- */
|
|||
|
|
/* 07/30/08 JRB 47 Init DstAddress in GSSE control block. */
|
|||
|
|
/* 05/14/08 JRB 46 rtadd_da_or_bda: save init Val for Enum too. */
|
|||
|
|
/* Move FLOW log inside "if" for BasicType. */
|
|||
|
|
/* 04/23/08 JRB 45 If SGCB configured, add it to "SP". */
|
|||
|
|
/* Eliminate warnings. */
|
|||
|
|
/* 04/20/08 JRB 44 Add DstAddress to GSSE control block. */
|
|||
|
|
/* 03/03/08 JRB 43 Use new mvl_var_add_alloc (avoids extra alloc)*/
|
|||
|
|
/* 02/25/08 JRB 42 Add ObjRef type and map to VisString129. */
|
|||
|
|
/* Add INT64, INT64U types. */
|
|||
|
|
/* 02/25/08 JRB 41 For bType=Enum, convert Val text as a number */
|
|||
|
|
/* ONLY if text does not match any EnumVal. */
|
|||
|
|
/* 12/20/07 JRB 40 Add rcb_name_no_index flag. */
|
|||
|
|
/* 12/13/07 JRB 39 Allow nested SDO (needed for Wind Power). */
|
|||
|
|
/* scl_gse_find: match "cbName" also. */
|
|||
|
|
/* 10/29/07 JRB 38 strncat_safe obsolete, use strncat_maxstrlen.*/
|
|||
|
|
/* 09/05/07 JRB 37 Chg "Check" (PACKED LIST) to bvstring. */
|
|||
|
|
/* 08/13/07 MDE 36 Added more scl_debug_mode */
|
|||
|
|
/* 08/10/07 JRB 35 scl2_dai_set_value: if no "Val", do nothing. */
|
|||
|
|
/* 08/02/07 JRB 34 scl2_datatype_create: use new ms_rt_bld* */
|
|||
|
|
/* features to avoid realloc. */
|
|||
|
|
/* 07/03/07 JRB 33 Init DstAddress struct in MVL61850_GCB_DATA. */
|
|||
|
|
/* Add scl_gse_find. */
|
|||
|
|
/* 05/10/07 JRB 32 Reallocate reserved_1 smaller to save mem. */
|
|||
|
|
/* 03/06/07 JRB 31 Fix LogRef initial value (ObjectReference). */
|
|||
|
|
/* 02/13/07 JRB 30 Use RptEnabled max attribute for BRCB too. */
|
|||
|
|
/* Save rpt scan rate in rpt_ctrl->scan_rate. */
|
|||
|
|
/* 10/30/06 JRB 29 scl2_datatype_create_all: all args changed. */
|
|||
|
|
/* scl2_ld_create_all: add vmd_ctrl, is_client args.*/
|
|||
|
|
/* Use <Val> element of <DA> or <BDA> to init */
|
|||
|
|
/* all variables containing the attribute. */
|
|||
|
|
/* Instead of creating TDL, create RUNTIME_TYPE */
|
|||
|
|
/* array directly (allows saving extra type info).*/
|
|||
|
|
/* Don't need type names anymore. Only use names*/
|
|||
|
|
/* if caller requests it. */
|
|||
|
|
/* Allow bType="Check", "VisString???". */
|
|||
|
|
/* 08/21/06 MDE 28 Added more scl_debug_mode */
|
|||
|
|
/* 08/09/06 JRB 27 Create multiple RCBs for each URCB configured.*/
|
|||
|
|
/* Add 01, 02, etc suffixes to all RCB names. */
|
|||
|
|
/* Allow scl_rcb->datSet="" (i.e. datSet not */
|
|||
|
|
/* present in ReportControl element of SCL file).*/
|
|||
|
|
/* Allow datSet write with mvl61850_datset_wr_ind.*/
|
|||
|
|
/* Chg DatSet in BRCB, URCB, GCB, LCB, MSVCB, */
|
|||
|
|
/* and USVCB to Vstring129. */
|
|||
|
|
/* Chg LogRef in LCB to Vstring129. */
|
|||
|
|
/* Del mvlu_rpt_create_scan_ctrl2 call */
|
|||
|
|
/* (new mvl61850_rpt_service automatically scans).*/
|
|||
|
|
/* 08/04/06 MDE 26 Tweaked logging */
|
|||
|
|
/* 08/04/06 MDE 25 Added scl_debug_mode, scl2_add_btype, etc. */
|
|||
|
|
/* 06/22/06 RKR 24 Added Vstring129 */
|
|||
|
|
/* 03/20/06 JRB 23 Add code for Sampled Value config (see svcb).*/
|
|||
|
|
/* Del unused scl_rcb_counters. */
|
|||
|
|
/* 12/14/05 JRB 22 Fix init val of DatSet in LCB or GCB */
|
|||
|
|
/* (must be ObjectReference). */
|
|||
|
|
/* 11/22/05 JRB 21 Fix spelling of "BufTm" in brcb, urcb. */
|
|||
|
|
/* (Only UCA uses the spelling "BufTim"). */
|
|||
|
|
/* 09/15/05 JRB 20 Fix spelling. */
|
|||
|
|
/* 07/27/05 JRB 19 Don't set OptFlds len=9 (default=10 is good).*/
|
|||
|
|
/* Don't set OptFlds bits bufovfl, entryID for URCB.*/
|
|||
|
|
/* 07/25/05 JRB 18 scl2_ld_create_all: Add brcb_bufsize arg. */
|
|||
|
|
/* 07/21/05 JRB 17 Set initial vals for LCB, GCB, SCB. */
|
|||
|
|
/* 07/11/05 JRB 16 scl2_ld_create_all: Call */
|
|||
|
|
/* mvl61850_ctl_lastapplerror_create. */
|
|||
|
|
/* 06/27/05 JRB 15 Use mvl_nvl_add (mvlu_rpt_nvl_add is obsolete).*/
|
|||
|
|
/* 05/27/05 JRB 14 Construct NVL name from LN & DataSet name */
|
|||
|
|
/* as 61850-8-1 requires. */
|
|||
|
|
/* 05/25/05 JRB 13 Del all code to generate RCB name & just use */
|
|||
|
|
/* name configured in SCL file (scl_rcb->name). */
|
|||
|
|
/* Add errflag so if one call to tdladd_string */
|
|||
|
|
/* fails, subsequent calls fail too. */
|
|||
|
|
/* 05/09/05 JRB 12 Chg leaf write functions for SqNum (in BRCB */
|
|||
|
|
/* & URCB) to u_no_write_allowed. */
|
|||
|
|
/* 04/05/05 MDE 11 Supress invalid log messages */
|
|||
|
|
/* 03/18/05 JRB 10 Use mvl_max_dyn when creating domains. */
|
|||
|
|
/* 02/15/05 JRB 09 Use new generated scl_ld->domName, not */
|
|||
|
|
/* scl_ld->inst as domain name. */
|
|||
|
|
/* Use new generated scl_fcda->domName, not */
|
|||
|
|
/* scl_fcda->ldInst as domain name. */
|
|||
|
|
/* Use new scl_ln->varName, generated once, */
|
|||
|
|
/* instead of generating ln_name repeatedly. */
|
|||
|
|
/* Compute max_num_var for mvl_dom_add call. */
|
|||
|
|
/* 01/19/05 JRB 08 Add scl2_dai_set_value_all & use it to init */
|
|||
|
|
/* data from DOI/SDI/DAI entries in SCL file. */
|
|||
|
|
/* Fix len on all strncat calls. */
|
|||
|
|
/* scl2_rcb_create_all: init ln_name just once. */
|
|||
|
|
/* Improve some logging. */
|
|||
|
|
/* 09/01/04 JRB 07 Map Octet64 to OVstring64 (to match 61850-8-1)*/
|
|||
|
|
/* 08/29/04 JRB 06 Chg Quality type back to BVstring13 because */
|
|||
|
|
/* final IEC-61850-8-1 changed back. */
|
|||
|
|
/* 08/19/04 JRB 05 Init return value in scl2_ld_create_all. */
|
|||
|
|
/* 07/19/04 JRB 04 Add tdladd_lg, tdladd_go_or_gs. */
|
|||
|
|
/* 07/14/04 JRB 03 Chg Quality type to BVstring14. */
|
|||
|
|
/* 07/09/04 JRB 02 scl2_ld_create_all: add reportScanRate arg */
|
|||
|
|
/* to this and lower level functions. */
|
|||
|
|
/* 07/02/04 JRB 01 Initial Revision. */
|
|||
|
|
/************************************************************************/
|
|||
|
|
#include "glbtypes.h"
|
|||
|
|
#include "sysincs.h"
|
|||
|
|
#include "scl.h"
|
|||
|
|
#include "sx_log.h"
|
|||
|
|
#include "mvl_defs.h"
|
|||
|
|
#include "mvl_uca.h"
|
|||
|
|
#include "mvl_log.h"
|
|||
|
|
#include "str_util.h" /* for strn..._safe protos */
|
|||
|
|
#include "sx_arb.h" /* for sxaText.... proto */
|
|||
|
|
|
|||
|
|
|
|||
|
|
/*renxiaobao <20><><EFBFBD><EFBFBD>ӳ<EFBFBD><D3B3>*/
|
|||
|
|
DATA_MAP_LINK *DATA_MAP_saddr=0;
|
|||
|
|
int BRCB_NUM = 0,URCB_NUM = 0;
|
|||
|
|
#ifdef DEBUG_SISCO
|
|||
|
|
SD_CONST static ST_CHAR *SD_CONST thisFileName = __FILE__;
|
|||
|
|
#endif
|
|||
|
|
|
|||
|
|
/* RESERVED_INFO: structure for extra type related info that is not */
|
|||
|
|
/* contained in RUNTIME_TYPE or RUNTIME_CTRL. Allocate array of these. */
|
|||
|
|
typedef struct
|
|||
|
|
{
|
|||
|
|
SCL_ENUMTYPE *scl_enumtype; /* SCL enumtype def for this type */
|
|||
|
|
ST_CHAR *Val; /* SCL initial value for this type */
|
|||
|
|
} RESERVED_INFO;
|
|||
|
|
|
|||
|
|
/* CRITICAL: this struct must be compatible with RUNTIME_TYPE. */
|
|||
|
|
/* If either one changes, the other must change to match. */
|
|||
|
|
typedef struct
|
|||
|
|
{
|
|||
|
|
ST_UINT8 LogEna; /* Bool */
|
|||
|
|
ST_CHAR LogRef[MVL61850_MAX_OBJREF_LEN+1]; /* Vstring129 (ObjectReference) */
|
|||
|
|
ST_CHAR DatSet[MVL61850_MAX_OBJREF_LEN+1]; /* Vstring129 (ObjectReference) */
|
|||
|
|
MMS_BTIME6 OldEntrTim; /* Btime6 */
|
|||
|
|
MMS_BTIME6 NewEntrTim; /* Btime6 */
|
|||
|
|
ST_UINT8 OldEntr[8]; /* Ostring8 */
|
|||
|
|
ST_UINT8 NewEntr[8]; /* Ostring8 */
|
|||
|
|
MMS_BVSTRING TrgOps; /* BVstring6 - struct includes 1 byte data. Enough.*/
|
|||
|
|
ST_UINT32 IntgPd; /* Ulong */
|
|||
|
|
} MVL61850_LCB_DATA; /* struct to store data for LCB (IEC Log Control Block) */
|
|||
|
|
|
|||
|
|
/* CRITICAL: this struct must be compatible with RUNTIME_TYPE. */
|
|||
|
|
/* If either one changes, the other must change to match. */
|
|||
|
|
typedef struct
|
|||
|
|
{
|
|||
|
|
ST_BOOLEAN GoEna;
|
|||
|
|
ST_CHAR GoID[66];
|
|||
|
|
ST_CHAR DatSet[MVL61850_MAX_OBJREF_LEN+1]; /* Vstring129 (ObjectReference)*/
|
|||
|
|
ST_UINT32 ConfRev;
|
|||
|
|
ST_BOOLEAN NdsCom;
|
|||
|
|
struct
|
|||
|
|
{
|
|||
|
|
ST_UCHAR Addr[6];
|
|||
|
|
ST_UINT8 PRIORITY;
|
|||
|
|
ST_UINT16 VID;
|
|||
|
|
ST_UINT16 APPID;
|
|||
|
|
} DstAddress;
|
|||
|
|
} MVL61850_GCB_DATA; /* struct to store data for GCB (GOOSE Control Block) */
|
|||
|
|
|
|||
|
|
/* CRITICAL: this struct must be compatible with RUNTIME_TYPE. */
|
|||
|
|
/* If either one changes, the other must change to match. */
|
|||
|
|
typedef struct
|
|||
|
|
{
|
|||
|
|
ST_BOOLEAN GsEna;
|
|||
|
|
ST_CHAR GsID[66];
|
|||
|
|
ST_CHAR DNALabels[32][66];
|
|||
|
|
ST_CHAR UserSTLabels[128][66];
|
|||
|
|
struct
|
|||
|
|
{
|
|||
|
|
ST_CHAR GsID[66];
|
|||
|
|
MMS_BTIME6 t;
|
|||
|
|
ST_UINT32 SqNum;
|
|||
|
|
ST_UINT32 StNum;
|
|||
|
|
ST_UINT32 TAL;
|
|||
|
|
ST_UINT32 usec;
|
|||
|
|
ST_UINT16 PhsID;
|
|||
|
|
ST_UCHAR DNA[8];
|
|||
|
|
ST_UCHAR UserST[32];
|
|||
|
|
} LSentData;
|
|||
|
|
struct
|
|||
|
|
{
|
|||
|
|
ST_UCHAR Addr[6];
|
|||
|
|
ST_UINT8 PRIORITY;
|
|||
|
|
ST_UINT16 VID;
|
|||
|
|
ST_UINT16 APPID;
|
|||
|
|
} DstAddress;
|
|||
|
|
} MVL61850_SCB_DATA; /* struct to store data for SCB (GSSE Control Block) */
|
|||
|
|
|
|||
|
|
typedef struct
|
|||
|
|
{
|
|||
|
|
ST_UINT8 SvEna; /* Bool */
|
|||
|
|
ST_CHAR MsvID[MVL61850_MAX_RPTID_LEN+1]; /* Vstring65 */
|
|||
|
|
ST_CHAR DatSet[MVL61850_MAX_OBJREF_LEN+1]; /* Vstring129 (ObjectReference) */
|
|||
|
|
ST_UINT32 ConfRev; /* Ulong */
|
|||
|
|
ST_UINT32 SmpRate; /* Ulong */
|
|||
|
|
MMS_BVSTRING OptFlds; /* BVstring3 - struct includes 1 byte data. Enough.*/
|
|||
|
|
} MVL61850_MSVCB_DATA; /* struct to store data for MSVCB */
|
|||
|
|
|
|||
|
|
typedef struct
|
|||
|
|
{
|
|||
|
|
ST_UINT8 SvEna; /* Bool */
|
|||
|
|
ST_UINT8 Resv; /* Bool */
|
|||
|
|
ST_CHAR UsvID[MVL61850_MAX_RPTID_LEN+1]; /* Vstring65 */
|
|||
|
|
ST_CHAR DatSet[MVL61850_MAX_OBJREF_LEN+1]; /* Vstring129 (ObjectReference) */
|
|||
|
|
ST_UINT32 ConfRev; /* Ulong */
|
|||
|
|
ST_UINT32 SmpRate; /* Ulong */
|
|||
|
|
MMS_BVSTRING OptFlds; /* BVstring3 - struct includes 1 byte data. Enough.*/
|
|||
|
|
} MVL61850_USVCB_DATA; /* struct to store data for USVCB */
|
|||
|
|
|
|||
|
|
/* CRITICAL: this struct must be compatible with RUNTIME_TYPE. */
|
|||
|
|
/* If either one changes, the other must change to match. */
|
|||
|
|
typedef struct
|
|||
|
|
{
|
|||
|
|
ST_UINT8 NumOfSG;
|
|||
|
|
ST_UINT8 ActSG;
|
|||
|
|
ST_UINT8 EditSG;
|
|||
|
|
ST_BOOLEAN CnfEdit;
|
|||
|
|
MMS_UTC_TIME LActTm; /* TimeStamp */
|
|||
|
|
} MVL61850_SGCB_DATA; /* struct to store data for SGCB (Setting Group Control Block) */
|
|||
|
|
|
|||
|
|
/************************************************************************/
|
|||
|
|
/* STATIC FUNCTION PROTOTYPES */
|
|||
|
|
/* Need prototypes for these static functions because rtadd_da_struct */
|
|||
|
|
/* calls rtadd_da_or_bda and vice versa. */
|
|||
|
|
/* Other static functions are just defined in the order they are needed */
|
|||
|
|
/* so prototypes are not needed. */
|
|||
|
|
/************************************************************************/
|
|||
|
|
static ST_RET rtadd_da_struct (
|
|||
|
|
RUNTIME_BUILD_CTXT *ctxt,
|
|||
|
|
SCL_INFO *scl_info,
|
|||
|
|
ST_CHAR *type_id,
|
|||
|
|
ST_CHAR *comp_name);
|
|||
|
|
static ST_RET rtadd_da_or_bda (
|
|||
|
|
RUNTIME_BUILD_CTXT *ctxt,
|
|||
|
|
SCL_INFO *scl_info,
|
|||
|
|
ST_CHAR *name, /* DA or BDA name */
|
|||
|
|
ST_CHAR *bType, /* DA or BDA bType */
|
|||
|
|
ST_CHAR *type, /* DA or BDA type */
|
|||
|
|
ST_UINT count, /* DA or BDA count */
|
|||
|
|
ST_CHAR *Val); /* DA or BDA Val */
|
|||
|
|
|
|||
|
|
/************************************************************************/
|
|||
|
|
/************************************************************************/
|
|||
|
|
ST_BOOLEAN scl_debug_mode;
|
|||
|
|
ST_INT scl_debug_mode_error_count;
|
|||
|
|
SCL2_BTYPE *scl2_btype_list;
|
|||
|
|
/* NOTE: setting this flag changes the way RCB names generated. */
|
|||
|
|
ST_BOOLEAN rcb_name_no_index; /* change to SD_TRUE to skip RCB index */
|
|||
|
|
/* suffix when maxClient==1 */
|
|||
|
|
|
|||
|
|
/************************************************************************/
|
|||
|
|
/* Add a "custom" basic type to the RUNTIME_TYPE array. */
|
|||
|
|
/************************************************************************/
|
|||
|
|
static ST_RET ms_rt_bld_61850_add_custom_btype (RUNTIME_BUILD_CTXT *ctxt, ST_CHAR *btype, ST_CHAR *comp_name)
|
|||
|
|
{
|
|||
|
|
SCL2_BTYPE *sclBtype;
|
|||
|
|
sclBtype = scl2_btype_list;
|
|||
|
|
while (sclBtype != NULL)
|
|||
|
|
{
|
|||
|
|
if (!strcmp (sclBtype->btype, btype))
|
|||
|
|
{ /* already checked that this is simple type, so just use ...rt_first*/
|
|||
|
|
return (ms_rt_bld_add_special (ctxt, comp_name, sclBtype->rt_ctrl->rt_first));
|
|||
|
|
}
|
|||
|
|
sclBtype = list_get_next (scl2_btype_list, sclBtype);
|
|||
|
|
}
|
|||
|
|
SXLOG_ERR1 ("bType='%s' is not recognized", btype);
|
|||
|
|
return (SD_FAILURE);
|
|||
|
|
}
|
|||
|
|
/************************************************************************/
|
|||
|
|
/* Add a basic type to the RUNTIME_TYPE array. */
|
|||
|
|
/************************************************************************/
|
|||
|
|
static ST_RET ms_rt_bld_61850_add_btype (RUNTIME_BUILD_CTXT *ctxt, ST_CHAR *btype, ST_CHAR *comp_name)
|
|||
|
|
{
|
|||
|
|
/* First check for any of the PredefinedBasicTypes. */
|
|||
|
|
if (strcmp (btype, "BOOLEAN") == 0)
|
|||
|
|
return (ms_rt_bld_add_bool (ctxt, comp_name));
|
|||
|
|
if (strcmp (btype, "INT8") == 0)
|
|||
|
|
return (ms_rt_bld_add_int (ctxt, comp_name, 1));
|
|||
|
|
if (strcmp (btype, "INT16") == 0)
|
|||
|
|
return (ms_rt_bld_add_int (ctxt, comp_name, 2));
|
|||
|
|
if (strcmp (btype, "INT32") == 0)
|
|||
|
|
return (ms_rt_bld_add_int (ctxt, comp_name, 4));
|
|||
|
|
if (strcmp (btype, "INT64") == 0)
|
|||
|
|
{
|
|||
|
|
#if defined(INT64_SUPPORT)
|
|||
|
|
return (ms_rt_bld_add_int (ctxt, comp_name, 8));
|
|||
|
|
#else
|
|||
|
|
SXLOG_ERR0 ("INT64 not supported on this platform");
|
|||
|
|
return (SD_FAILURE);
|
|||
|
|
#endif
|
|||
|
|
}
|
|||
|
|
if (strcmp (btype, "INT8U") == 0)
|
|||
|
|
return (ms_rt_bld_add_uint (ctxt, comp_name, 1));
|
|||
|
|
if (strcmp (btype, "INT16U") == 0)
|
|||
|
|
return (ms_rt_bld_add_uint (ctxt, comp_name, 2));
|
|||
|
|
if (strcmp (btype, "INT32U") == 0)
|
|||
|
|
return (ms_rt_bld_add_uint (ctxt, comp_name, 4));
|
|||
|
|
if (strcmp (btype, "INT64U") == 0)
|
|||
|
|
{
|
|||
|
|
#if defined(INT64_SUPPORT)
|
|||
|
|
return (ms_rt_bld_add_uint (ctxt, comp_name, 8));
|
|||
|
|
#else
|
|||
|
|
SXLOG_ERR0 ("INT64U not supported on this platform");
|
|||
|
|
return (SD_FAILURE);
|
|||
|
|
#endif
|
|||
|
|
}
|
|||
|
|
if (strcmp (btype, "FLOAT32") == 0)
|
|||
|
|
return (ms_rt_bld_add_float (ctxt, comp_name, 4));
|
|||
|
|
if (strcmp (btype, "FLOAT64") == 0)
|
|||
|
|
return (ms_rt_bld_add_float (ctxt, comp_name, 8));
|
|||
|
|
if (strcmp (btype, "Dbpos") == 0
|
|||
|
|
|| strcmp (btype, "Tcmd") == 0)
|
|||
|
|
return (ms_rt_bld_add_bstring (ctxt, comp_name, 2)); /* 2 bits*/
|
|||
|
|
/* "Check" is PACKED LIST. Maps to BVstring. */
|
|||
|
|
if (strcmp (btype, "Check") == 0)
|
|||
|
|
return (ms_rt_bld_add_bvstring (ctxt, comp_name, 2)); /* max 2 bits*/
|
|||
|
|
if (strcmp (btype, "Quality") == 0)
|
|||
|
|
return (ms_rt_bld_add_bvstring (ctxt, comp_name, 13)); /* max 13 bits*/
|
|||
|
|
if (strcmp (btype, "Timestamp") == 0)
|
|||
|
|
return (ms_rt_bld_add_utctime (ctxt, comp_name));
|
|||
|
|
if (strcmp (btype, "VisString32") == 0)
|
|||
|
|
return (ms_rt_bld_add_vstring (ctxt, comp_name, 32)); /* max 32 char*/
|
|||
|
|
if (strcmp (btype, "VisString64") == 0)
|
|||
|
|
return (ms_rt_bld_add_vstring (ctxt, comp_name, 64)); /* max 64 char*/
|
|||
|
|
if (strcmp (btype, "VisString65") == 0)
|
|||
|
|
return (ms_rt_bld_add_vstring (ctxt, comp_name, 65)); /* max 65 char*/
|
|||
|
|
if (strcmp (btype, "VisString128") == 0)
|
|||
|
|
return (ms_rt_bld_add_vstring (ctxt, comp_name, 128)); /* max 128 char*/
|
|||
|
|
if (strcmp (btype, "VisString129") == 0)
|
|||
|
|
return (ms_rt_bld_add_vstring (ctxt, comp_name, 129)); /* max 129 char*/
|
|||
|
|
if (strcmp (btype, "ObjRef") == 0)
|
|||
|
|
return (ms_rt_bld_add_vstring (ctxt, comp_name, 129)); /* max 129 char*/
|
|||
|
|
if (strcmp (btype, "VisString255") == 0)
|
|||
|
|
return (ms_rt_bld_add_vstring (ctxt, comp_name, 255)); /* max 255 char*/
|
|||
|
|
if (strcmp (btype, "Octet64") == 0)
|
|||
|
|
/* 61850-8-1 says all octet strings are variable length*/
|
|||
|
|
return (ms_rt_bld_add_ovstring (ctxt, comp_name, 64)); /* max 64 bytes*/
|
|||
|
|
if (strcmp (btype, "EntryTime") == 0)
|
|||
|
|
return (ms_rt_bld_add_btime6 (ctxt, comp_name));
|
|||
|
|
if (strcmp (btype, "Unicode255") == 0)
|
|||
|
|
return (ms_rt_bld_add_utf8vstring (ctxt, comp_name, 255)); /* max 255 char*/
|
|||
|
|
/* If the "btype" does not match any PredefinedBasicTypes, */
|
|||
|
|
/* see if it matches a "custom" type (i.e. a type created earlier by */
|
|||
|
|
/* calling scl2_add_btype). */
|
|||
|
|
return (ms_rt_bld_61850_add_custom_btype (ctxt, btype, comp_name));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/************************************************************************/
|
|||
|
|
/* ms_rt_bld_61850_add_brcb */
|
|||
|
|
/* NOTE: check only last return code. If one "ms_rt_bld_add_*" call fails, */
|
|||
|
|
/* subsequent calls will fail too. */
|
|||
|
|
/************************************************************************/
|
|||
|
|
static ST_RET ms_rt_bld_61850_add_brcb (RUNTIME_BUILD_CTXT *ctxt, ST_CHAR *comp_name)
|
|||
|
|
{
|
|||
|
|
ST_RET retcode;
|
|||
|
|
ms_rt_bld_add_str_start (ctxt, comp_name);
|
|||
|
|
ms_rt_bld_add_vstring (ctxt, "RptID", MVL61850_MAX_RPTID_LEN);
|
|||
|
|
ms_rt_bld_add_bool (ctxt, "RptEna");
|
|||
|
|
ms_rt_bld_add_vstring (ctxt, "DatSet", MVL61850_MAX_OBJREF_LEN);
|
|||
|
|
ms_rt_bld_add_uint (ctxt, "ConfRev", 4);
|
|||
|
|
ms_rt_bld_add_bvstring (ctxt, "OptFlds", 10);
|
|||
|
|
ms_rt_bld_add_uint (ctxt, "BufTm", 4);
|
|||
|
|
ms_rt_bld_add_uint (ctxt, "SqNum", 2);
|
|||
|
|
ms_rt_bld_add_bvstring (ctxt, "TrgOps", 6);
|
|||
|
|
ms_rt_bld_add_uint (ctxt, "IntgPd", 4);
|
|||
|
|
ms_rt_bld_add_bool (ctxt, "GI");
|
|||
|
|
ms_rt_bld_add_bool (ctxt, "PurgeBuf");
|
|||
|
|
ms_rt_bld_add_ostring (ctxt, "EntryID", 8);
|
|||
|
|
ms_rt_bld_add_btime6 (ctxt, "TimeofEntry");
|
|||
|
|
retcode = ms_rt_bld_add_str_end (ctxt);
|
|||
|
|
return (retcode);
|
|||
|
|
}
|
|||
|
|
/************************************************************************/
|
|||
|
|
/* ms_rt_bld_61850_add_urcb */
|
|||
|
|
/* NOTE: check only last return code. If one "ms_rt_bld_add_*" call fails, */
|
|||
|
|
/* subsequent calls will fail too. */
|
|||
|
|
/************************************************************************/
|
|||
|
|
static ST_RET ms_rt_bld_61850_add_urcb (RUNTIME_BUILD_CTXT *ctxt, ST_CHAR *comp_name)
|
|||
|
|
{
|
|||
|
|
ST_RET retcode;
|
|||
|
|
ms_rt_bld_add_str_start (ctxt, comp_name);
|
|||
|
|
ms_rt_bld_add_vstring (ctxt, "RptID", MVL61850_MAX_RPTID_LEN);
|
|||
|
|
ms_rt_bld_add_bool (ctxt, "RptEna");
|
|||
|
|
ms_rt_bld_add_bool (ctxt, "Resv");
|
|||
|
|
ms_rt_bld_add_vstring (ctxt, "DatSet", MVL61850_MAX_OBJREF_LEN);
|
|||
|
|
ms_rt_bld_add_uint (ctxt, "ConfRev", 4);
|
|||
|
|
ms_rt_bld_add_bvstring (ctxt, "OptFlds", 10);
|
|||
|
|
ms_rt_bld_add_uint (ctxt, "BufTm", 4);
|
|||
|
|
ms_rt_bld_add_uint (ctxt, "SqNum", 1);
|
|||
|
|
ms_rt_bld_add_bvstring (ctxt, "TrgOps", 6);
|
|||
|
|
ms_rt_bld_add_uint (ctxt, "IntgPd", 4);
|
|||
|
|
ms_rt_bld_add_bool (ctxt, "GI");
|
|||
|
|
retcode = ms_rt_bld_add_str_end (ctxt);
|
|||
|
|
return (retcode);
|
|||
|
|
}
|
|||
|
|
/************************************************************************/
|
|||
|
|
/* ms_rt_bld_61850_add_lcb */
|
|||
|
|
/* NOTE: check only last return code. If one "ms_rt_bld_add_*" call fails, */
|
|||
|
|
/* subsequent calls will fail too. */
|
|||
|
|
/************************************************************************/
|
|||
|
|
static ST_RET ms_rt_bld_61850_add_lcb (RUNTIME_BUILD_CTXT *ctxt, ST_CHAR *comp_name)
|
|||
|
|
{
|
|||
|
|
ST_RET retcode;
|
|||
|
|
ms_rt_bld_add_str_start (ctxt, comp_name);
|
|||
|
|
ms_rt_bld_add_bool (ctxt, "LogEna");
|
|||
|
|
ms_rt_bld_add_vstring (ctxt, "LogRef", MVL61850_MAX_OBJREF_LEN);
|
|||
|
|
ms_rt_bld_add_vstring (ctxt, "DatSet", MVL61850_MAX_OBJREF_LEN);
|
|||
|
|
#if 0 /*renxiaobao 20170106 mod*/
|
|||
|
|
ms_rt_bld_add_btime6 (ctxt, "OldEntrTim");
|
|||
|
|
ms_rt_bld_add_btime6 (ctxt, "NewEntrTim");
|
|||
|
|
#else
|
|||
|
|
ms_rt_bld_add_btime6 (ctxt, "OldEntrTm");
|
|||
|
|
ms_rt_bld_add_btime6 (ctxt, "NewEntrTm");
|
|||
|
|
#endif
|
|||
|
|
ms_rt_bld_add_ostring (ctxt, "OldEntr", 8);
|
|||
|
|
ms_rt_bld_add_ostring (ctxt, "NewEntr", 8);
|
|||
|
|
ms_rt_bld_add_bvstring (ctxt, "TrgOps", 6);
|
|||
|
|
ms_rt_bld_add_uint (ctxt, "IntgPd", 4);
|
|||
|
|
retcode = ms_rt_bld_add_str_end (ctxt);
|
|||
|
|
return (retcode);
|
|||
|
|
}
|
|||
|
|
/************************************************************************/
|
|||
|
|
/* ms_rt_bld_61850_add_gcb */
|
|||
|
|
/* NOTE: check only last return code. If one "ms_rt_bld_add_*" call fails, */
|
|||
|
|
/* subsequent calls will fail too. */
|
|||
|
|
/************************************************************************/
|
|||
|
|
static ST_RET ms_rt_bld_61850_add_gcb (RUNTIME_BUILD_CTXT *ctxt, ST_CHAR *comp_name)
|
|||
|
|
{
|
|||
|
|
ST_RET retcode;
|
|||
|
|
ms_rt_bld_add_str_start (ctxt, comp_name);
|
|||
|
|
ms_rt_bld_add_bool (ctxt, "GoEna");
|
|||
|
|
ms_rt_bld_add_vstring (ctxt, "GoID", MVL61850_MAX_RPTID_LEN);
|
|||
|
|
ms_rt_bld_add_vstring (ctxt, "DatSet", MVL61850_MAX_OBJREF_LEN);
|
|||
|
|
ms_rt_bld_add_uint (ctxt, "ConfRev", 4);
|
|||
|
|
ms_rt_bld_add_bool (ctxt, "NdsCom");
|
|||
|
|
ms_rt_bld_add_str_start (ctxt, "DstAddress");
|
|||
|
|
ms_rt_bld_add_ostring (ctxt, "Addr", 6);
|
|||
|
|
ms_rt_bld_add_uint (ctxt, "PRIORITY", 1);
|
|||
|
|
ms_rt_bld_add_uint (ctxt, "VID", 2);
|
|||
|
|
ms_rt_bld_add_uint (ctxt, "APPID", 2);
|
|||
|
|
ms_rt_bld_add_str_end (ctxt);
|
|||
|
|
retcode = ms_rt_bld_add_str_end (ctxt);
|
|||
|
|
return (retcode);
|
|||
|
|
}
|
|||
|
|
/************************************************************************/
|
|||
|
|
/* ms_rt_bld_61850_add_scb */
|
|||
|
|
/* NOTE: check only last return code. If one "ms_rt_bld_add_*" call fails, */
|
|||
|
|
/* subsequent calls will fail too. */
|
|||
|
|
/************************************************************************/
|
|||
|
|
static ST_RET ms_rt_bld_61850_add_scb (RUNTIME_BUILD_CTXT *ctxt, ST_CHAR *comp_name)
|
|||
|
|
{
|
|||
|
|
ST_RET retcode;
|
|||
|
|
ms_rt_bld_add_str_start (ctxt, comp_name);
|
|||
|
|
ms_rt_bld_add_bool (ctxt, "GsEna");
|
|||
|
|
ms_rt_bld_add_vstring (ctxt, "GsID", MVL61850_MAX_RPTID_LEN);
|
|||
|
|
ms_rt_bld_add_arr_start (ctxt, "DNALabels", 32);
|
|||
|
|
ms_rt_bld_add_vstring (ctxt, "DNALabels", 65);
|
|||
|
|
ms_rt_bld_add_arr_end (ctxt);
|
|||
|
|
ms_rt_bld_add_arr_start (ctxt, "UserSTLabels", 128);
|
|||
|
|
ms_rt_bld_add_vstring (ctxt, "DNALabels", 65);
|
|||
|
|
ms_rt_bld_add_arr_end (ctxt);
|
|||
|
|
ms_rt_bld_add_str_start (ctxt, "LSentData");
|
|||
|
|
ms_rt_bld_add_vstring (ctxt, "GsID", MVL61850_MAX_RPTID_LEN);
|
|||
|
|
ms_rt_bld_add_btime6 (ctxt, "t");
|
|||
|
|
ms_rt_bld_add_uint (ctxt, "SqNum", 4);
|
|||
|
|
ms_rt_bld_add_uint (ctxt, "StNum", 4);
|
|||
|
|
ms_rt_bld_add_uint (ctxt, "TAL", 4);
|
|||
|
|
ms_rt_bld_add_uint (ctxt, "usec", 4);
|
|||
|
|
ms_rt_bld_add_uint (ctxt, "PhsID", 2);
|
|||
|
|
ms_rt_bld_add_bstring (ctxt, "DNA", 64);
|
|||
|
|
ms_rt_bld_add_bstring (ctxt, "UserST", 256);
|
|||
|
|
ms_rt_bld_add_str_end (ctxt);
|
|||
|
|
ms_rt_bld_add_str_start (ctxt, "DstAddress");
|
|||
|
|
ms_rt_bld_add_ostring (ctxt, "Addr", 6);
|
|||
|
|
ms_rt_bld_add_uint (ctxt, "PRIORITY", 1);
|
|||
|
|
ms_rt_bld_add_uint (ctxt, "VID", 2);
|
|||
|
|
ms_rt_bld_add_uint (ctxt, "APPID", 2);
|
|||
|
|
ms_rt_bld_add_str_end (ctxt);
|
|||
|
|
retcode = ms_rt_bld_add_str_end (ctxt);
|
|||
|
|
return (retcode);
|
|||
|
|
}
|
|||
|
|
/************************************************************************/
|
|||
|
|
/* ms_rt_bld_61850_add_msvcb */
|
|||
|
|
/* NOTE: check only last return code. If one "ms_rt_bld_add_*" call fails, */
|
|||
|
|
/* subsequent calls will fail too. */
|
|||
|
|
/************************************************************************/
|
|||
|
|
static ST_RET ms_rt_bld_61850_add_msvcb (RUNTIME_BUILD_CTXT *ctxt, ST_CHAR *comp_name)
|
|||
|
|
{
|
|||
|
|
ST_RET retcode;
|
|||
|
|
ms_rt_bld_add_str_start (ctxt, comp_name);
|
|||
|
|
ms_rt_bld_add_bool (ctxt, "SvEna");
|
|||
|
|
ms_rt_bld_add_vstring (ctxt, "MsvID", MVL61850_MAX_RPTID_LEN);
|
|||
|
|
ms_rt_bld_add_vstring (ctxt, "DatSet", MVL61850_MAX_OBJREF_LEN);
|
|||
|
|
ms_rt_bld_add_uint (ctxt, "ConfRev", 4);
|
|||
|
|
ms_rt_bld_add_uint (ctxt, "SmpRate", 4);
|
|||
|
|
ms_rt_bld_add_bvstring (ctxt, "OptFlds", 3);
|
|||
|
|
retcode = ms_rt_bld_add_str_end (ctxt);
|
|||
|
|
return (retcode);
|
|||
|
|
}
|
|||
|
|
/************************************************************************/
|
|||
|
|
/* ms_rt_bld_61850_add_usvcb */
|
|||
|
|
/* NOTE: check only last return code. If one "ms_rt_bld_add_*" call fails, */
|
|||
|
|
/* subsequent calls will fail too. */
|
|||
|
|
/************************************************************************/
|
|||
|
|
static ST_RET ms_rt_bld_61850_add_usvcb (RUNTIME_BUILD_CTXT *ctxt, ST_CHAR *comp_name)
|
|||
|
|
{
|
|||
|
|
ST_RET retcode;
|
|||
|
|
ms_rt_bld_add_str_start (ctxt, comp_name);
|
|||
|
|
ms_rt_bld_add_bool (ctxt, "SvEna");
|
|||
|
|
ms_rt_bld_add_bool (ctxt, "Resv");
|
|||
|
|
ms_rt_bld_add_vstring (ctxt, "UsvID", MVL61850_MAX_RPTID_LEN);
|
|||
|
|
ms_rt_bld_add_vstring (ctxt, "DatSet", MVL61850_MAX_OBJREF_LEN);
|
|||
|
|
ms_rt_bld_add_uint (ctxt, "ConfRev", 4);
|
|||
|
|
ms_rt_bld_add_uint (ctxt, "SmpRate", 4);
|
|||
|
|
ms_rt_bld_add_bvstring (ctxt, "OptFlds", 3);
|
|||
|
|
retcode = ms_rt_bld_add_str_end (ctxt);
|
|||
|
|
return (retcode);
|
|||
|
|
}
|
|||
|
|
/************************************************************************/
|
|||
|
|
/* ms_rt_bld_61850_add_sgcb */
|
|||
|
|
/* NOTE: check only last return code. If one "ms_rt_bld_add_*" call fails,*/
|
|||
|
|
/* subsequent calls will fail too. */
|
|||
|
|
/************************************************************************/
|
|||
|
|
static ST_RET ms_rt_bld_61850_add_sgcb (RUNTIME_BUILD_CTXT *ctxt)
|
|||
|
|
{
|
|||
|
|
ST_RET retcode;
|
|||
|
|
ms_rt_bld_add_str_start (ctxt, "SGCB"); /* fixed struct name */
|
|||
|
|
ms_rt_bld_add_uint (ctxt, "NumOfSG", 1);
|
|||
|
|
ms_rt_bld_add_uint (ctxt, "ActSG", 1);
|
|||
|
|
ms_rt_bld_add_uint (ctxt, "EditSG", 1);
|
|||
|
|
ms_rt_bld_add_bool (ctxt, "CnfEdit");
|
|||
|
|
ms_rt_bld_add_utctime (ctxt, "LActTm");
|
|||
|
|
retcode = ms_rt_bld_add_str_end (ctxt);
|
|||
|
|
return (retcode);
|
|||
|
|
}
|
|||
|
|
/************************************************************************/
|
|||
|
|
/* reserved_free_all */
|
|||
|
|
/* Find all types used by this VMD. If rt_ctrl->reserved_1 is not NULL, */
|
|||
|
|
/* we must have allocated it in "scl2_datatype_create", so free it and */
|
|||
|
|
/* set it to NULL. */
|
|||
|
|
/************************************************************************/
|
|||
|
|
static ST_VOID reserved_free_all (MVL_VMD_CTRL *vmd_ctrl)
|
|||
|
|
|
|||
|
|
{
|
|||
|
|
ST_INT type_id;
|
|||
|
|
MVL_TYPE_CTRL *type_ctrl;
|
|||
|
|
RUNTIME_CTRL *rt_ctrl;
|
|||
|
|
/* This only works for "dynamic" types so start with first dynamic type.*/
|
|||
|
|
for (type_id = mvl_num_types - mvl_max_dyn.types; type_id < mvl_num_types; type_id++)
|
|||
|
|
{
|
|||
|
|
type_ctrl = mvl_vmd_type_ctrl_find (vmd_ctrl, type_id);
|
|||
|
|
if (type_ctrl) /* type found in this VMD */
|
|||
|
|
{
|
|||
|
|
rt_ctrl = type_ctrl->rt_ctrl; /* always valid */
|
|||
|
|
if (rt_ctrl->reserved_1 != NULL)
|
|||
|
|
{
|
|||
|
|
chk_free (rt_ctrl->reserved_1);
|
|||
|
|
rt_ctrl->reserved_1 = NULL;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
/************************************************************************/
|
|||
|
|
/* scl_make_objref */
|
|||
|
|
/* Construct ObjectReference from SCL info. */
|
|||
|
|
/************************************************************************/
|
|||
|
|
static ST_RET scl_make_objref (
|
|||
|
|
SCL_LD *scl_ld,
|
|||
|
|
SCL_LN *scl_ln,
|
|||
|
|
ST_CHAR *objName, /* Object (DataSet, Data) name */
|
|||
|
|
ST_CHAR *objRef, /* ptr to resulting ObjRef string */
|
|||
|
|
ST_UINT max_len) /* max len of resulting ObjRef string */
|
|||
|
|
{
|
|||
|
|
ST_RET retcode;
|
|||
|
|
|
|||
|
|
if (strlen (scl_ld->domName) +
|
|||
|
|
strlen (scl_ln->prefix) + strlen (scl_ln->lnClass) + strlen (scl_ln->inst) +
|
|||
|
|
strlen (objName) + 2 > max_len) /* 2 added for '/' and '$' */
|
|||
|
|
{
|
|||
|
|
SXLOG_ERR1 ("Constructed ObjectReference would be too long for '%s'.", objName);
|
|||
|
|
retcode = SD_FAILURE;
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
sprintf (objRef, "%s/%s%s%s$%s", scl_ld->domName, scl_ln->prefix,
|
|||
|
|
scl_ln->lnClass, scl_ln->inst, objName);
|
|||
|
|
retcode = SD_SUCCESS;
|
|||
|
|
}
|
|||
|
|
return (retcode);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/************************************************************************/
|
|||
|
|
/* scl_make_objref2 */
|
|||
|
|
/* Construct ObjectReference from SCL info (LN Name not included). */
|
|||
|
|
/* Similar to scl_make_objref, but does not include LN name. */
|
|||
|
|
/************************************************************************/
|
|||
|
|
static ST_RET scl_make_objref2 (
|
|||
|
|
SCL_LD *scl_ld,
|
|||
|
|
ST_CHAR *objName, /* Object name */
|
|||
|
|
ST_CHAR *objRef, /* ptr to resulting ObjRef string */
|
|||
|
|
ST_UINT max_len) /* max len of resulting ObjRef string */
|
|||
|
|
{
|
|||
|
|
ST_RET retcode;
|
|||
|
|
if (strlen (scl_ld->domName) +
|
|||
|
|
strlen (objName) + 2 > max_len) /* 2 added for '/' and '$' */
|
|||
|
|
{
|
|||
|
|
SXLOG_ERR1 ("Constructed ObjectReference would be too long for '%s'.", objName);
|
|||
|
|
retcode = SD_FAILURE;
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
sprintf (objRef, "%s/%s", scl_ld->domName, objName);
|
|||
|
|
retcode = SD_SUCCESS;
|
|||
|
|
}
|
|||
|
|
return (retcode);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/************************************************************************/
|
|||
|
|
/* special_type_name */
|
|||
|
|
/* Create type name for a special LN (one that includes RCB, LCB, etc.).*/
|
|||
|
|
/* NOTE: do not use '$'. This will confuse "mvlu_set_leaf_param". */
|
|||
|
|
/************************************************************************/
|
|||
|
|
ST_RET special_type_name (ST_CHAR *prefix, SCL_LD *scl_ld, SCL_LN *scl_ln,
|
|||
|
|
ST_CHAR *type_name, size_t type_name_size)
|
|||
|
|
{ /* Create unique type name for this LN. */
|
|||
|
|
size_t len = 0;
|
|||
|
|
ST_RET retcode;
|
|||
|
|
if (prefix) /* NOTE: if prefix is not NULL, add prefix. */
|
|||
|
|
len += strlen (prefix);
|
|||
|
|
len += strlen (scl_ld->inst);
|
|||
|
|
len += strlen (scl_ln->prefix);
|
|||
|
|
len += strlen (scl_ln->lnClass);
|
|||
|
|
len += strlen (scl_ln->inst);
|
|||
|
|
|
|||
|
|
type_name [0] = '\0'; /* start with empty string */
|
|||
|
|
if (len < type_name_size)
|
|||
|
|
{ /* name will fit, so write it */
|
|||
|
|
if (prefix)
|
|||
|
|
strcat (type_name, prefix);
|
|||
|
|
strcat (type_name, scl_ld->inst);
|
|||
|
|
strcat (type_name, scl_ln->prefix);
|
|||
|
|
strcat (type_name, scl_ln->lnClass);
|
|||
|
|
strcat (type_name, scl_ln->inst);
|
|||
|
|
retcode = SD_SUCCESS;
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
retcode = SD_FAILURE; /* name would be too long */
|
|||
|
|
return (retcode);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/************************************************************************/
|
|||
|
|
/* simple_type_name */
|
|||
|
|
/* Create type name for a simple type. */
|
|||
|
|
/* NOTE: do not use '$'. This will confuse "mvlu_set_leaf_param". */
|
|||
|
|
/************************************************************************/
|
|||
|
|
ST_RET simple_type_name (ST_CHAR *prefix, SCL_LNTYPE *scl_lntype,
|
|||
|
|
ST_CHAR *type_name, size_t type_name_size)
|
|||
|
|
{ /* Create unique type name for this LN. */
|
|||
|
|
size_t len = 0;
|
|||
|
|
ST_RET retcode;
|
|||
|
|
|
|||
|
|
if (prefix) /* NOTE: if prefix is not NULL, add prefix. */
|
|||
|
|
len += strlen (prefix);
|
|||
|
|
len += strlen (scl_lntype->id);
|
|||
|
|
|
|||
|
|
type_name [0] = '\0'; /* start with empty string */
|
|||
|
|
if (len < type_name_size)
|
|||
|
|
{ /* name will fit, so write it */
|
|||
|
|
if (prefix)
|
|||
|
|
strcat (type_name, prefix);
|
|||
|
|
strcat (type_name, scl_lntype->id);
|
|||
|
|
retcode = SD_SUCCESS;
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
retcode = SD_FAILURE; /* name would be too long */
|
|||
|
|
return (retcode);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/************************************************************************/
|
|||
|
|
/* scl_find_dotype */
|
|||
|
|
/************************************************************************/
|
|||
|
|
static SCL_DOTYPE *scl_find_dotype (
|
|||
|
|
SCL_INFO *scl_info, /* main struct where all SCL info stored*/
|
|||
|
|
ST_CHAR *type_id)
|
|||
|
|
{
|
|||
|
|
SCL_DOTYPE *scl_dotype;
|
|||
|
|
|
|||
|
|
for (scl_dotype = (SCL_DOTYPE *) list_find_last ((DBL_LNK *) scl_info->doTypeHead);
|
|||
|
|
scl_dotype != NULL;
|
|||
|
|
scl_dotype = (SCL_DOTYPE *) list_find_prev ((DBL_LNK *) scl_info->doTypeHead, (DBL_LNK *) scl_dotype))
|
|||
|
|
{
|
|||
|
|
if (strcmp (scl_dotype->id, type_id) ==0)
|
|||
|
|
return (scl_dotype);
|
|||
|
|
}
|
|||
|
|
return (NULL); /* match not found */
|
|||
|
|
}
|
|||
|
|
/************************************************************************/
|
|||
|
|
/* scl_find_datype */
|
|||
|
|
/************************************************************************/
|
|||
|
|
static SCL_DATYPE *scl_find_datype (
|
|||
|
|
SCL_INFO *scl_info, /* main struct where all SCL info stored*/
|
|||
|
|
ST_CHAR *type_id)
|
|||
|
|
{
|
|||
|
|
SCL_DATYPE *scl_datype;
|
|||
|
|
|
|||
|
|
for (scl_datype = (SCL_DATYPE *) list_find_last ((DBL_LNK *) scl_info->daTypeHead);
|
|||
|
|
scl_datype != NULL;
|
|||
|
|
scl_datype = (SCL_DATYPE *) list_find_prev ((DBL_LNK *) scl_info->daTypeHead, (DBL_LNK *) scl_datype))
|
|||
|
|
{
|
|||
|
|
if (strcmp (scl_datype->id, type_id) ==0)
|
|||
|
|
return (scl_datype);
|
|||
|
|
}
|
|||
|
|
return (NULL); /* match not found */
|
|||
|
|
}
|
|||
|
|
/************************************************************************/
|
|||
|
|
/************************************************************************/
|
|||
|
|
static SCL_DATASET *scl_find_dataset (
|
|||
|
|
SCL_LN *scl_ln,
|
|||
|
|
ST_CHAR *dataset_name)
|
|||
|
|
{
|
|||
|
|
SCL_DATASET *scl_dataset;
|
|||
|
|
|
|||
|
|
for (scl_dataset = (SCL_DATASET *) list_find_last ((DBL_LNK *) scl_ln->datasetHead);
|
|||
|
|
scl_dataset != NULL;
|
|||
|
|
scl_dataset = (SCL_DATASET *) list_find_prev ((DBL_LNK *) scl_ln->datasetHead, (DBL_LNK *) scl_dataset))
|
|||
|
|
{
|
|||
|
|
if (strcmp (scl_dataset->name, dataset_name) ==0)
|
|||
|
|
return (scl_dataset);
|
|||
|
|
}
|
|||
|
|
return (NULL); /* match not found */
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/************************************************************************/
|
|||
|
|
/* scl2_find_enumtype */
|
|||
|
|
/************************************************************************/
|
|||
|
|
SCL_ENUMTYPE *scl2_find_enumtype (SCL_INFO *scl_info, ST_CHAR *name)
|
|||
|
|
{
|
|||
|
|
SCL_ENUMTYPE *scl_enumtype;
|
|||
|
|
for (scl_enumtype = (SCL_ENUMTYPE *) list_find_last ((DBL_LNK *) scl_info->enumTypeHead);
|
|||
|
|
scl_enumtype != NULL;
|
|||
|
|
scl_enumtype = (SCL_ENUMTYPE *) list_find_prev ((DBL_LNK *) scl_info->enumTypeHead, (DBL_LNK *) scl_enumtype))
|
|||
|
|
{
|
|||
|
|
if (strcmp (scl_enumtype->id, name) == 0)
|
|||
|
|
break; /* found a match */
|
|||
|
|
}
|
|||
|
|
return (scl_enumtype);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/************************************************************************/
|
|||
|
|
/* scl2_add_btype */
|
|||
|
|
/************************************************************************/
|
|||
|
|
SCL2_BTYPE *scl2_add_btype (ST_CHAR *btype, ST_CHAR *tdl)
|
|||
|
|
{
|
|||
|
|
SCL2_BTYPE *sclBtype;
|
|||
|
|
RUNTIME_CTRL *rt_ctrl;
|
|||
|
|
ST_UCHAR asn1_buf [200]; /* simple types only, so this should be big enough*/
|
|||
|
|
|
|||
|
|
sclBtype = NULL; /* assume failure for now */
|
|||
|
|
rt_ctrl = ms_tdl_to_runtime (tdl, asn1_buf, sizeof(asn1_buf));
|
|||
|
|
if (rt_ctrl)
|
|||
|
|
{
|
|||
|
|
if (rt_ctrl->rt_num == 1) /* must be simple type */
|
|||
|
|
{
|
|||
|
|
sclBtype = chk_calloc (1, sizeof (SCL2_BTYPE) + strlen (btype) + 1);
|
|||
|
|
sclBtype->btype = (ST_CHAR *) (sclBtype + 1); /* point after struct*/
|
|||
|
|
strcpy (sclBtype->btype, btype);
|
|||
|
|
sclBtype->rt_ctrl = rt_ctrl; /* rt_ctrl allocated, just save ptr*/
|
|||
|
|
list_add_last (&scl2_btype_list, sclBtype);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{ /* Complex type not allowed, so destroy it now. Err return already set*/
|
|||
|
|
ms_runtime_destroy (rt_ctrl);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return (sclBtype);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/************************************************************************/
|
|||
|
|
/* rtadd_sdo */
|
|||
|
|
/* NOTE: if (error) returned, caller should break out of loop. */
|
|||
|
|
/* RETURNS: SD_SUCCESS or error code */
|
|||
|
|
/************************************************************************/
|
|||
|
|
static ST_RET rtadd_sdo (
|
|||
|
|
RUNTIME_BUILD_CTXT *rt_ctxt,
|
|||
|
|
SCL_INFO *scl_info,
|
|||
|
|
ST_CHAR *FC, /* FC to match */
|
|||
|
|
ST_CHAR *name, /* SDO name */
|
|||
|
|
ST_CHAR *type) /* SDO type */
|
|||
|
|
{
|
|||
|
|
SCL_DOTYPE *scl_dotype;
|
|||
|
|
SCL_DA *scl_da;
|
|||
|
|
ST_RET retcode;
|
|||
|
|
ST_INT rt_num_after_str_start; /* index after str_start added */
|
|||
|
|
|
|||
|
|
/* First add SDO name (passed to this funct). May need rollback later */
|
|||
|
|
if (ms_rt_bld_add_str_start (rt_ctxt, name)!=SD_SUCCESS)
|
|||
|
|
return (SD_FAILURE); /* error (already logged) */
|
|||
|
|
|
|||
|
|
/* Save current index. Use later to check if any types added. */
|
|||
|
|
rt_num_after_str_start = ms_rt_bld_get_count (rt_ctxt);
|
|||
|
|
|
|||
|
|
/* Find DOType whose "id" matches the SDO "type". */
|
|||
|
|
scl_dotype = scl_find_dotype (scl_info, type);
|
|||
|
|
if (scl_dotype)
|
|||
|
|
{
|
|||
|
|
/* Find all DA or SDO in this DO that match the FC */
|
|||
|
|
/* NOTE: linked list is in reverse order from data in SCL file, */
|
|||
|
|
/* so get off list in reverse order. */
|
|||
|
|
for (scl_da = (SCL_DA *) list_find_last ((DBL_LNK *) scl_dotype->daHead);
|
|||
|
|
scl_da != NULL;
|
|||
|
|
scl_da = (SCL_DA *) list_find_prev ((DBL_LNK *) scl_dotype->daHead, (DBL_LNK *) scl_da))
|
|||
|
|
{
|
|||
|
|
if (scl_da->objtype == SCL_OBJTYPE_SDO)
|
|||
|
|
{
|
|||
|
|
/* This is a "nested" SDO. Just call rtadd_sdo again recursively. */
|
|||
|
|
retcode = rtadd_sdo (rt_ctxt, scl_info, FC, scl_da->name, scl_da->type);
|
|||
|
|
if (retcode != SD_SUCCESS) /* error (already logged) */
|
|||
|
|
return (retcode); /* error */
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{ /* objtype must be SCL_OBJTYPE_DA */
|
|||
|
|
if (strcmp (scl_da->fc, FC) == 0) /* FC of this DA matches FC */
|
|||
|
|
{
|
|||
|
|
retcode = rtadd_da_or_bda (rt_ctxt, scl_info, scl_da->name, scl_da->bType,
|
|||
|
|
scl_da->type, scl_da->count, scl_da->Val);
|
|||
|
|
if (retcode != SD_SUCCESS) /* error (already logged) */
|
|||
|
|
return (retcode); /* error */
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
} /* end "for (scl_da...)" loop */
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
SXLOG_ERR1 ("SDO type '%s' cannot be found", type);
|
|||
|
|
return (SD_FAILURE); /* error */
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* Check if any types were added after start of struct */
|
|||
|
|
if (ms_rt_bld_get_count (rt_ctxt) > rt_num_after_str_start)
|
|||
|
|
{ /* types were added, so finish struct now */
|
|||
|
|
if (ms_rt_bld_add_str_end (rt_ctxt) != SD_SUCCESS)
|
|||
|
|
return (SD_FAILURE); /* error (already logged) */
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{ /* types NOT added so remove start of struct */
|
|||
|
|
ms_rt_bld_remove_last (rt_ctxt);
|
|||
|
|
}
|
|||
|
|
return (SD_SUCCESS); /* only successful return */
|
|||
|
|
}
|
|||
|
|
/************************************************************************/
|
|||
|
|
/* rtadd_da_or_bda */
|
|||
|
|
/* NOTE: if (error) returned, caller should break out of loop. */
|
|||
|
|
/* RETURNS: SD_SUCCESS or error code */
|
|||
|
|
/************************************************************************/
|
|||
|
|
static ST_RET rtadd_da_or_bda (
|
|||
|
|
RUNTIME_BUILD_CTXT *rt_ctxt,
|
|||
|
|
SCL_INFO *scl_info,
|
|||
|
|
ST_CHAR *name, /* DA or BDA name */
|
|||
|
|
ST_CHAR *bType, /* DA or BDA bType */
|
|||
|
|
ST_CHAR *type, /* DA or BDA type */
|
|||
|
|
ST_UINT count, /* DA or BDA count */
|
|||
|
|
ST_CHAR *Val) /* DA or BDA Val (may be NULL) */
|
|||
|
|
{
|
|||
|
|
ST_RET retcode;
|
|||
|
|
RESERVED_INFO *reserved_info; /* ptr to array of extra type info. */
|
|||
|
|
|
|||
|
|
/* Cast "reserved_1" pointer to the type we allocated. */
|
|||
|
|
if (rt_ctxt->do_count)
|
|||
|
|
reserved_info = NULL; /* rt_ctrl & reserved_1 not allocated yet*/
|
|||
|
|
else
|
|||
|
|
reserved_info = (RESERVED_INFO *) rt_ctxt->rt_ctrl->reserved_1;
|
|||
|
|
|
|||
|
|
/* If count!=0, this is array. */
|
|||
|
|
if (count)
|
|||
|
|
{
|
|||
|
|
/* NOTE: comp_name stored on arr_start AND on first element of array.*/
|
|||
|
|
if (ms_rt_bld_add_arr_start (rt_ctxt, name, count))
|
|||
|
|
return (SD_FAILURE); /* error (already logged) */
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* Add different text depending on "bType" (Struct, Enum, or BasicType). */
|
|||
|
|
if (strcmp (bType, "Struct") == 0)
|
|||
|
|
{
|
|||
|
|
/* NOTE: this may cause recursion */
|
|||
|
|
/* (i.e. rtadd_da_struct may call rtadd_da_or_bda).*/
|
|||
|
|
retcode = rtadd_da_struct (rt_ctxt, scl_info, type, name);
|
|||
|
|
if (retcode != SD_SUCCESS)
|
|||
|
|
{
|
|||
|
|
SXLOG_ERR1 ("SCL ERROR: Can't add type definition for BDA Struct type=%s", type);
|
|||
|
|
return (retcode);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else if (strcmp (bType, "Enum") == 0)
|
|||
|
|
{
|
|||
|
|
SCL_ENUMTYPE *scl_enumtype;
|
|||
|
|
if ((scl_enumtype = scl2_find_enumtype (scl_info, type))==NULL)
|
|||
|
|
{
|
|||
|
|
SXLOG_ERR1 ("EnumType '%s' could not be found", type);
|
|||
|
|
return (SD_FAILURE);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
/* DEBUG: for now assume all enums are 8-bit */
|
|||
|
|
/* Someday may need to chk for maximum "EnumVal ord" to determine # of bits */
|
|||
|
|
/* Save the scl_enumtype pointer in reserved_info array. */
|
|||
|
|
/* Also save "Val", if present. */
|
|||
|
|
/* CRITICAL: Do this BEFORE calling "ms_rt_bld_add_*" so that */
|
|||
|
|
/* scl_enumtype is saved in the current array entry. */
|
|||
|
|
if (!rt_ctxt->do_count) /* if (do_count), reserved_info not set.*/
|
|||
|
|
{
|
|||
|
|
reserved_info[rt_ctxt->rt_ctrl->rt_num].scl_enumtype = scl_enumtype;
|
|||
|
|
if (Val != NULL)
|
|||
|
|
{
|
|||
|
|
SXLOG_FLOW2 ("Saving Val='%s' for DA or BDA='%s'", Val, name);
|
|||
|
|
reserved_info[rt_ctxt->rt_ctrl->rt_num].Val = Val;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (ms_rt_bld_add_int (rt_ctxt, name, 1) != SD_SUCCESS)
|
|||
|
|
return (SD_FAILURE); /* error (already logged) */
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{ /* must be BasicType */
|
|||
|
|
/* Save initial value of this component, if present. */
|
|||
|
|
/* CRITICAL: Do this BEFORE calling "ms_rt_bld_add_*" so that */
|
|||
|
|
/* "Val" is saved in the current array entry. */
|
|||
|
|
if (Val != NULL)
|
|||
|
|
{
|
|||
|
|
if (!rt_ctxt->do_count) /* if (do_count), reserved_info not set.*/
|
|||
|
|
{
|
|||
|
|
SXLOG_FLOW2 ("Saving Val='%s' for DA or BDA='%s'", Val, name);
|
|||
|
|
reserved_info[rt_ctxt->rt_ctrl->rt_num].Val = Val;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if (ms_rt_bld_61850_add_btype (rt_ctxt, bType, name))
|
|||
|
|
return (SD_FAILURE); /* error (already logged) */
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* If count!=0, this is array. */
|
|||
|
|
if (count)
|
|||
|
|
{ /* this BDA is array */
|
|||
|
|
if (ms_rt_bld_add_arr_end (rt_ctxt))
|
|||
|
|
return (SD_FAILURE); /* error (already logged) */
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return (SD_SUCCESS); /* only successful return */
|
|||
|
|
}
|
|||
|
|
/************************************************************************/
|
|||
|
|
/* rtadd_da_struct */
|
|||
|
|
/* Add the RUNTIME_TYPE definitions for a DA that is "Struct". */
|
|||
|
|
/* RETURNS: SD_SUCCESS or error code */
|
|||
|
|
/************************************************************************/
|
|||
|
|
static ST_RET rtadd_da_struct (
|
|||
|
|
RUNTIME_BUILD_CTXT *rt_ctxt,
|
|||
|
|
SCL_INFO *scl_info,
|
|||
|
|
ST_CHAR *type_id,
|
|||
|
|
ST_CHAR *comp_name)
|
|||
|
|
{
|
|||
|
|
SCL_DATYPE *scl_datype;
|
|||
|
|
SCL_BDA *scl_bda;
|
|||
|
|
ST_RET retcode;
|
|||
|
|
|
|||
|
|
scl_datype = scl_find_datype (scl_info, type_id);
|
|||
|
|
if (scl_datype)
|
|||
|
|
{
|
|||
|
|
if (ms_rt_bld_add_str_start (rt_ctxt, comp_name)!=SD_SUCCESS)
|
|||
|
|
return (SD_FAILURE); /* error (already logged) */
|
|||
|
|
|
|||
|
|
/* Add info for all BDA in this DAType. */
|
|||
|
|
/* NOTE: linked list is in reverse order from data in SCL file, */
|
|||
|
|
/* so get off list in reverse order. */
|
|||
|
|
for (scl_bda = (SCL_BDA *) list_find_last ((DBL_LNK *) scl_datype->bdaHead);
|
|||
|
|
scl_bda != NULL;
|
|||
|
|
scl_bda = (SCL_BDA *) list_find_prev ((DBL_LNK *) scl_datype->bdaHead, (DBL_LNK *) scl_bda))
|
|||
|
|
{
|
|||
|
|
retcode = rtadd_da_or_bda (rt_ctxt, scl_info, scl_bda->name, scl_bda->bType,
|
|||
|
|
scl_bda->type, scl_bda->count, scl_bda->Val);
|
|||
|
|
if (retcode != SD_SUCCESS) /* error (already logged) */
|
|||
|
|
return (retcode); /* error */
|
|||
|
|
} /* end "for (scl_bda...)" loop */
|
|||
|
|
|
|||
|
|
if (ms_rt_bld_add_str_end (rt_ctxt)!=SD_SUCCESS)
|
|||
|
|
return (SD_FAILURE); /* error (already logged) */
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
SXLOG_ERR1 ("SCL ERROR: DAType id=%s not found", type_id);
|
|||
|
|
return (SD_FAILURE); /* error */
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return (SD_SUCCESS); /* only successful return */
|
|||
|
|
}
|
|||
|
|
/************************************************************************/
|
|||
|
|
/* rtadd_do */
|
|||
|
|
/* Add the RUNTIME_TYPE definitions for one DO. */
|
|||
|
|
/* RETURNS: SD_SUCCESS or error code */
|
|||
|
|
/************************************************************************/
|
|||
|
|
static ST_RET rtadd_do (
|
|||
|
|
RUNTIME_BUILD_CTXT *rt_ctxt,
|
|||
|
|
char *FC,
|
|||
|
|
SCL_INFO *scl_info,
|
|||
|
|
ST_CHAR *do_name,
|
|||
|
|
SCL_DOTYPE *scl_dotype)
|
|||
|
|
{
|
|||
|
|
SCL_DA *scl_da;
|
|||
|
|
ST_RET retcode;
|
|||
|
|
ST_INT rt_num_after_str_start; /* index after str_start added */
|
|||
|
|
|
|||
|
|
if (ms_rt_bld_add_str_start (rt_ctxt, do_name)!=SD_SUCCESS)
|
|||
|
|
return (SD_FAILURE); /* error (already logged) */
|
|||
|
|
|
|||
|
|
/* Save current index. Use later to check if any types added. */
|
|||
|
|
rt_num_after_str_start = ms_rt_bld_get_count (rt_ctxt);
|
|||
|
|
|
|||
|
|
/* Find all DA or SDO in this DO that match the FC */
|
|||
|
|
/* NOTE: linked list is in reverse order from data in SCL file, */
|
|||
|
|
/* so get off list in reverse order. */
|
|||
|
|
for (scl_da = (SCL_DA *) list_find_last ((DBL_LNK *) scl_dotype->daHead);
|
|||
|
|
scl_da != NULL;
|
|||
|
|
scl_da = (SCL_DA *) list_find_prev ((DBL_LNK *) scl_dotype->daHead, (DBL_LNK *) scl_da))
|
|||
|
|
{
|
|||
|
|
if (scl_da->objtype == SCL_OBJTYPE_SDO)
|
|||
|
|
{
|
|||
|
|
/* Only "name" and "type" used for SDO. */
|
|||
|
|
retcode = rtadd_sdo (rt_ctxt, scl_info, FC, scl_da->name, scl_da->type);
|
|||
|
|
if (retcode != SD_SUCCESS) /* error (already logged) */
|
|||
|
|
return (retcode); /* error */
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
/*renxiaobao <20><>ֵ */
|
|||
|
|
if (strcmp ("SE", FC) == 0)
|
|||
|
|
{
|
|||
|
|
if (strcmp (scl_da->fc, "SG") == 0) /* FC of this DA matches FC */
|
|||
|
|
{
|
|||
|
|
retcode = rtadd_da_or_bda (rt_ctxt, scl_info, scl_da->name, scl_da->bType,
|
|||
|
|
scl_da->type, scl_da->count, scl_da->Val);
|
|||
|
|
if (retcode != SD_SUCCESS) /* error (already logged) */
|
|||
|
|
return (retcode); /* error */
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
/* objtype must be SCL_OBJTYPE_DA */
|
|||
|
|
if (strcmp (scl_da->fc, FC) == 0) /* FC of this DA matches FC */
|
|||
|
|
{
|
|||
|
|
retcode = rtadd_da_or_bda (rt_ctxt, scl_info, scl_da->name, scl_da->bType,
|
|||
|
|
scl_da->type, scl_da->count, scl_da->Val);
|
|||
|
|
if (retcode != SD_SUCCESS) /* error (already logged) */
|
|||
|
|
return (retcode); /* error */
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
} /* end "for (scl_da...)" loop */
|
|||
|
|
|
|||
|
|
/* Check if any types were added after start of struct */
|
|||
|
|
if (ms_rt_bld_get_count (rt_ctxt) > rt_num_after_str_start)
|
|||
|
|
{ /* types were added, so finish struct now */
|
|||
|
|
if (ms_rt_bld_add_str_end (rt_ctxt) != SD_SUCCESS)
|
|||
|
|
return (SD_FAILURE); /* error (already logged) */
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{ /* types NOT added so remove start of struct */
|
|||
|
|
ms_rt_bld_remove_last (rt_ctxt);
|
|||
|
|
}
|
|||
|
|
return (SD_SUCCESS); /* only successful return */
|
|||
|
|
}
|
|||
|
|
/************************************************************************/
|
|||
|
|
/* rtadd_fc */
|
|||
|
|
/* RETURNS: SD_SUCCESS or error code */
|
|||
|
|
/* Add the types for one FC to the RUNTIME_BUILD_CTXT. */
|
|||
|
|
/************************************************************************/
|
|||
|
|
static ST_RET rtadd_fc (
|
|||
|
|
RUNTIME_BUILD_CTXT *rt_ctxt,
|
|||
|
|
ST_CHAR *FC,
|
|||
|
|
SCL_INFO *scl_info,
|
|||
|
|
SCL_LNTYPE *scl_lntype,
|
|||
|
|
SCL_LN *scl_ln_special) /* needed for SGCB */
|
|||
|
|
{
|
|||
|
|
SCL_DO *scl_do;
|
|||
|
|
SCL_DOTYPE *scl_dotype;
|
|||
|
|
ST_RET retcode;
|
|||
|
|
ST_INT rt_num_after_str_start; /* index after str_start added */
|
|||
|
|
|
|||
|
|
/* Add start of struct for this FC. Remove later if no DA's found.*/
|
|||
|
|
if (ms_rt_bld_add_str_start (rt_ctxt, FC)!=SD_SUCCESS)
|
|||
|
|
return (SD_FAILURE);
|
|||
|
|
|
|||
|
|
/* Save current index. Use later to check if any types added. */
|
|||
|
|
rt_num_after_str_start = ms_rt_bld_get_count (rt_ctxt);
|
|||
|
|
|
|||
|
|
/* NOTE: linked list is in reverse order from data in SCL file, */
|
|||
|
|
/* so get off list in reverse order. */
|
|||
|
|
for (scl_do = (SCL_DO *) list_find_last ((DBL_LNK *) scl_lntype->doHead);
|
|||
|
|
scl_do != NULL;
|
|||
|
|
scl_do = (SCL_DO *) list_find_prev ((DBL_LNK *) scl_lntype->doHead, (DBL_LNK *) scl_do))
|
|||
|
|
{
|
|||
|
|
/* Find DOType for this DO. */
|
|||
|
|
scl_dotype = scl_find_dotype (scl_info, scl_do->type);
|
|||
|
|
if (scl_dotype) /* found DOType */
|
|||
|
|
{
|
|||
|
|
retcode = rtadd_do (rt_ctxt, FC, scl_info, scl_do->name, scl_dotype);
|
|||
|
|
if (retcode != SD_SUCCESS)
|
|||
|
|
{
|
|||
|
|
SXLOG_ERR1 ("ERROR adding type definitions for DO='%s'", scl_do->name);
|
|||
|
|
return (retcode);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{ /* can't find DOType for this DO. Cannot continue. */
|
|||
|
|
SXLOG_ERR2 ("Cannot find DOType='%s' for DO='%s'.",
|
|||
|
|
scl_do->type, scl_do->name);
|
|||
|
|
return (SD_FAILURE);
|
|||
|
|
}
|
|||
|
|
} /* end "for (scl_do...)" loop */
|
|||
|
|
|
|||
|
|
/* If SGCB configured and FC="SP", add SGCB here after other attributes.*/
|
|||
|
|
if (scl_ln_special && scl_ln_special->sgcb && strcmp (FC, "SP") == 0)
|
|||
|
|
{
|
|||
|
|
retcode = ms_rt_bld_61850_add_sgcb (rt_ctxt); /* Add SGCB struct.*/
|
|||
|
|
if (retcode != SD_SUCCESS)
|
|||
|
|
{
|
|||
|
|
SXLOG_ERR0 ("ERROR adding SGCB");
|
|||
|
|
return (retcode);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* Check if any types were added after start of struct */
|
|||
|
|
if (ms_rt_bld_get_count (rt_ctxt) > rt_num_after_str_start)
|
|||
|
|
{ /* types were added, so finish struct now */
|
|||
|
|
if (ms_rt_bld_add_str_end (rt_ctxt) != SD_SUCCESS)
|
|||
|
|
return (SD_FAILURE); /* error (already logged) */
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{ /* types NOT added so remove start of struct */
|
|||
|
|
ms_rt_bld_remove_last (rt_ctxt);
|
|||
|
|
}
|
|||
|
|
return (SD_SUCCESS); /* this is only "good" return */
|
|||
|
|
}
|
|||
|
|
/************************************************************************/
|
|||
|
|
/* rtadd_rp_or_br */
|
|||
|
|
/* RETURNS: SD_SUCCESS or error code */
|
|||
|
|
/* Add runtime type definition for special FC=RP or FC=BR. */
|
|||
|
|
/* CRITICAL: Last arg "buffered" must be SD_TRUE or SD_FALSE (other */
|
|||
|
|
/* non-zero values NOT treated the same as SD_TRUE). */
|
|||
|
|
/************************************************************************/
|
|||
|
|
static ST_RET rtadd_rp_or_br (
|
|||
|
|
RUNTIME_BUILD_CTXT *rt_ctxt,
|
|||
|
|
SCL_INFO *scl_info,
|
|||
|
|
SCL_LNTYPE *scl_lntype,
|
|||
|
|
SCL_LN *scl_ln,
|
|||
|
|
ST_BOOLEAN buffered) /* SD_TRUE to add "buffered" reports */
|
|||
|
|
/* SD_FALSE to add "unbuffered" reports */
|
|||
|
|
/* CRITICAL: no other values allowed */
|
|||
|
|
{
|
|||
|
|
SCL_RCB *scl_rcb;
|
|||
|
|
ST_INT count = 0;
|
|||
|
|
ST_RET retcode = SD_SUCCESS;
|
|||
|
|
ST_INT index, maxClient;
|
|||
|
|
|
|||
|
|
for (scl_rcb = (SCL_RCB *) list_find_last ((DBL_LNK *) scl_ln->rcbHead);
|
|||
|
|
scl_rcb != NULL;
|
|||
|
|
scl_rcb = (SCL_RCB *) list_find_prev ((DBL_LNK *) scl_ln->rcbHead, (DBL_LNK *) scl_rcb))
|
|||
|
|
{
|
|||
|
|
/* If RCB doesn't match the kind we're looking for, ignore it. */
|
|||
|
|
if (scl_rcb->buffered != buffered)
|
|||
|
|
continue;
|
|||
|
|
|
|||
|
|
/* NOTE: if NOT the type we're looking for, never get here (see 'continue' above)*/
|
|||
|
|
/* NOTE: don't chk every rtadd.. return. If one call fails, subsequent calls fail too.*/
|
|||
|
|
if (buffered) /* looking for buffered & this is buffered */
|
|||
|
|
{
|
|||
|
|
if (count==0)
|
|||
|
|
ms_rt_bld_add_str_start (rt_ctxt, "BR");
|
|||
|
|
}
|
|||
|
|
else /* looking for unbuffered & this is unbuffered */
|
|||
|
|
{
|
|||
|
|
if (count==0)
|
|||
|
|
ms_rt_bld_add_str_start (rt_ctxt, "RP");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
maxClient = scl_rcb->maxClient; /* use value of "RptEnabled max" in SCL*/
|
|||
|
|
|
|||
|
|
/* Add type definitions for each RCB in a loop. */
|
|||
|
|
for (index = 1; index<=maxClient; index++)
|
|||
|
|
{
|
|||
|
|
ST_CHAR rcb_name [MAX_IDENT_LEN + 1];
|
|||
|
|
if (strlen (scl_rcb->name) <= MAX_IDENT_LEN - 2) /* make sure there's room*/
|
|||
|
|
{
|
|||
|
|
if (rcb_name_no_index && maxClient == 1)
|
|||
|
|
sprintf (rcb_name, "%s", scl_rcb->name); /* do NOT add suffix*/
|
|||
|
|
else
|
|||
|
|
sprintf (rcb_name, "%s%02d", scl_rcb->name, index); /* construct RCB name*/
|
|||
|
|
if (buffered) /* looking for buffered & this is buffered */
|
|||
|
|
retcode = ms_rt_bld_61850_add_brcb (rt_ctxt, rcb_name);
|
|||
|
|
else /* looking for unbuffered & this is unbuffered */
|
|||
|
|
retcode = ms_rt_bld_61850_add_urcb (rt_ctxt, rcb_name);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
retcode = SD_FAILURE; /* name too long. Should NEVER get this error*/
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (retcode) /* if error, stop */
|
|||
|
|
return (retcode);
|
|||
|
|
|
|||
|
|
count++; /* increment count of rcb */
|
|||
|
|
} /* end loop */
|
|||
|
|
|
|||
|
|
/* If any found, add ending text. */
|
|||
|
|
if (count>0)
|
|||
|
|
retcode = ms_rt_bld_add_str_end (rt_ctxt);
|
|||
|
|
|
|||
|
|
return (retcode); /* NOTE: may have returned sooner on some errors*/
|
|||
|
|
} /* end rtadd_rp_or_br */
|
|||
|
|
|
|||
|
|
/************************************************************************/
|
|||
|
|
/* rtadd_lg */
|
|||
|
|
/* RETURNS: SD_SUCCESS or error code */
|
|||
|
|
/* Add runtime type definition for special FC=LG. */
|
|||
|
|
/* NOTE: OptFlds in 61850-7-2 not mapped to MMS. */
|
|||
|
|
/************************************************************************/
|
|||
|
|
static ST_RET rtadd_lg (
|
|||
|
|
RUNTIME_BUILD_CTXT *rt_ctxt,
|
|||
|
|
SCL_INFO *scl_info,
|
|||
|
|
SCL_LNTYPE *scl_lntype,
|
|||
|
|
SCL_LN *scl_ln)
|
|||
|
|
{
|
|||
|
|
SCL_LCB *scl_lcb;
|
|||
|
|
ST_INT count = 0;
|
|||
|
|
SCL_DATASET *scl_dataset;
|
|||
|
|
ST_RET retcode = SD_SUCCESS;
|
|||
|
|
|
|||
|
|
for (scl_lcb = (SCL_LCB *) list_find_last ((DBL_LNK *) scl_ln->lcbHead);
|
|||
|
|
scl_lcb != NULL;
|
|||
|
|
scl_lcb = (SCL_LCB *) list_find_prev ((DBL_LNK *) scl_ln->lcbHead, (DBL_LNK *) scl_lcb))
|
|||
|
|
{
|
|||
|
|
scl_dataset = scl_find_dataset (scl_ln, scl_lcb->datSet);
|
|||
|
|
if (scl_dataset == NULL)
|
|||
|
|
{
|
|||
|
|
SXLOG_ERR1 ("rtadd_lg: datSet='%s' not found", scl_lcb->datSet);
|
|||
|
|
return (SD_FAILURE);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (count==0)
|
|||
|
|
retcode = ms_rt_bld_add_str_start (rt_ctxt, "LG"); /* first one needs (LG) */
|
|||
|
|
|
|||
|
|
if (retcode) /* if error, stop */
|
|||
|
|
return (retcode);
|
|||
|
|
|
|||
|
|
/* Add lcb name as component name.*/
|
|||
|
|
retcode = ms_rt_bld_61850_add_lcb (rt_ctxt, scl_lcb->name);
|
|||
|
|
|
|||
|
|
if (retcode) /* if error, stop */
|
|||
|
|
return (retcode);
|
|||
|
|
|
|||
|
|
count++; /* increment count of lcb */
|
|||
|
|
} /* end loop */
|
|||
|
|
|
|||
|
|
/* If any found, add ending text. */
|
|||
|
|
if (count>0)
|
|||
|
|
retcode = ms_rt_bld_add_str_end (rt_ctxt);
|
|||
|
|
|
|||
|
|
return (retcode); /* NOTE: may have returned sooner on some errors*/
|
|||
|
|
} /* end rtadd_lg */
|
|||
|
|
|
|||
|
|
/************************************************************************/
|
|||
|
|
/* rtadd_go_or_gs */
|
|||
|
|
/* RETURNS: SD_SUCCESS or error code */
|
|||
|
|
/* Add runtime type definition for special FC=GO or FC=GS. */
|
|||
|
|
/************************************************************************/
|
|||
|
|
static ST_RET rtadd_go_or_gs (
|
|||
|
|
RUNTIME_BUILD_CTXT *rt_ctxt,
|
|||
|
|
SCL_INFO *scl_info,
|
|||
|
|
SCL_LNTYPE *scl_lntype,
|
|||
|
|
SCL_LN *scl_ln,
|
|||
|
|
ST_BOOLEAN isGoose) /* SD_TRUE to add GOOSE (FC=GO) */
|
|||
|
|
/* SD_FALSE to add GSSE (FC=GS) */
|
|||
|
|
/* CRITICAL: no other values allowed */
|
|||
|
|
{
|
|||
|
|
SCL_GCB *scl_gcb;
|
|||
|
|
ST_INT count = 0;
|
|||
|
|
SCL_DATASET *scl_dataset;
|
|||
|
|
ST_RET retcode = SD_SUCCESS;
|
|||
|
|
|
|||
|
|
for (scl_gcb = (SCL_GCB *) list_find_last ((DBL_LNK *) scl_ln->gcbHead);
|
|||
|
|
scl_gcb != NULL;
|
|||
|
|
scl_gcb = (SCL_GCB *) list_find_prev ((DBL_LNK *) scl_ln->gcbHead, (DBL_LNK *) scl_gcb))
|
|||
|
|
{
|
|||
|
|
/* If GCB doesn't match the kind we're looking for, ignore it. */
|
|||
|
|
if (scl_gcb->isGoose != isGoose)
|
|||
|
|
continue;
|
|||
|
|
|
|||
|
|
/* NOTE: if NOT the type we're looking for, never get here (see 'continue' above)*/
|
|||
|
|
if (scl_gcb->isGoose)
|
|||
|
|
{
|
|||
|
|
/* GOOSE needs dataset. Find it now. */
|
|||
|
|
scl_dataset = scl_find_dataset (scl_ln, scl_gcb->datSet);
|
|||
|
|
if (scl_dataset == NULL)
|
|||
|
|
{
|
|||
|
|
SXLOG_ERR1 ("rtadd_go_or_gs: datSet='%s' not found", scl_gcb->datSet);
|
|||
|
|
return (SD_FAILURE);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (count==0)
|
|||
|
|
ms_rt_bld_add_str_start (rt_ctxt, "GO"); /* first one needs "GO" */
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
if (count==0)
|
|||
|
|
ms_rt_bld_add_str_start (rt_ctxt, "GS"); /* first one needs "GS" */
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (retcode) /* if error, stop */
|
|||
|
|
return (retcode);
|
|||
|
|
|
|||
|
|
if (scl_gcb->isGoose) /* looking for GOOSE & this is GOOSE */
|
|||
|
|
retcode = ms_rt_bld_61850_add_gcb (rt_ctxt, scl_gcb->name);
|
|||
|
|
else /* looking for GSSE & this is GSSE */
|
|||
|
|
retcode = ms_rt_bld_61850_add_scb (rt_ctxt, scl_gcb->name);
|
|||
|
|
|
|||
|
|
if (retcode) /* if error, stop */
|
|||
|
|
return (retcode);
|
|||
|
|
|
|||
|
|
count++; /* increment count of gcb */
|
|||
|
|
} /* end loop */
|
|||
|
|
|
|||
|
|
/* If any found, add ending text. */
|
|||
|
|
if (count>0)
|
|||
|
|
retcode = ms_rt_bld_add_str_end (rt_ctxt);
|
|||
|
|
|
|||
|
|
return (retcode); /* NOTE: may have returned sooner on some errors*/
|
|||
|
|
} /* end rtadd_go_or_gs */
|
|||
|
|
|
|||
|
|
/************************************************************************/
|
|||
|
|
/* rtadd_ms_or_us */
|
|||
|
|
/* RETURNS: SD_SUCCESS or error code */
|
|||
|
|
/* Add runtime type definition for FC=MS or FC=US. */
|
|||
|
|
/* CRITICAL: Last arg "multicast" must be SD_TRUE or SD_FALSE (other */
|
|||
|
|
/* non-zero values NOT treated the same as SD_TRUE). */
|
|||
|
|
/************************************************************************/
|
|||
|
|
static ST_RET rtadd_ms_or_us (
|
|||
|
|
RUNTIME_BUILD_CTXT *rt_ctxt,
|
|||
|
|
SCL_INFO *scl_info,
|
|||
|
|
SCL_LNTYPE *scl_lntype,
|
|||
|
|
SCL_LN *scl_ln,
|
|||
|
|
ST_BOOLEAN multicast) /* SD_TRUE to add all MSVCB (FC=MS) */
|
|||
|
|
/* SD_FALSE to add all USVCB (FC=US) */
|
|||
|
|
/* CRITICAL: no other values allowed */
|
|||
|
|
{
|
|||
|
|
SCL_SVCB *scl_svcb;
|
|||
|
|
ST_INT count = 0;
|
|||
|
|
SCL_DATASET *scl_dataset;
|
|||
|
|
ST_RET retcode = SD_SUCCESS;
|
|||
|
|
|
|||
|
|
for (scl_svcb = (SCL_SVCB *) list_find_last ((DBL_LNK *) scl_ln->svcbHead);
|
|||
|
|
scl_svcb != NULL;
|
|||
|
|
scl_svcb = (SCL_SVCB *) list_find_prev ((DBL_LNK *) scl_ln->svcbHead, (DBL_LNK *) scl_svcb))
|
|||
|
|
{
|
|||
|
|
/* If svcb doesn't match the kind we're looking for, ignore it. */
|
|||
|
|
if (scl_svcb->multicast != multicast)
|
|||
|
|
continue;
|
|||
|
|
|
|||
|
|
scl_dataset = scl_find_dataset (scl_ln, scl_svcb->datSet);
|
|||
|
|
if (scl_dataset == NULL)
|
|||
|
|
{
|
|||
|
|
SXLOG_ERR1 ("rtadd_ms_or_us: datSet='%s' not found", scl_svcb->datSet);
|
|||
|
|
return (SD_FAILURE);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* NOTE: if NOT the type we're looking for, never get here (see 'continue' above)*/
|
|||
|
|
/* NOTE: don't chk every rtadd.. return. If one call fails, subsequent calls fail too.*/
|
|||
|
|
if (multicast) /* looking for multicast & this is multicast */
|
|||
|
|
{
|
|||
|
|
if (count==0)
|
|||
|
|
ms_rt_bld_add_str_start (rt_ctxt, "MS"); /* first one needs (MS) */
|
|||
|
|
}
|
|||
|
|
else /* looking for unicast & this is unicast */
|
|||
|
|
{
|
|||
|
|
if (count==0)
|
|||
|
|
ms_rt_bld_add_str_start (rt_ctxt, "US"); /* first one needs (US) */
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
/* NOTE: if NOT the type we're looking for, never get here (see 'continue' above)*/
|
|||
|
|
if (multicast) /* looking for multicast & this is multicast */
|
|||
|
|
retcode = ms_rt_bld_61850_add_msvcb (rt_ctxt, scl_svcb->name);
|
|||
|
|
else /* looking for unicast & this is unicast */
|
|||
|
|
retcode = ms_rt_bld_61850_add_usvcb (rt_ctxt, scl_svcb->name);
|
|||
|
|
|
|||
|
|
if (retcode) /* if error, stop */
|
|||
|
|
return (retcode);
|
|||
|
|
|
|||
|
|
count++; /* increment count of svcb */
|
|||
|
|
} /* end loop */
|
|||
|
|
|
|||
|
|
/* If any found, add ending text. */
|
|||
|
|
if (count>0)
|
|||
|
|
retcode = ms_rt_bld_add_str_end (rt_ctxt);
|
|||
|
|
|
|||
|
|
return (retcode); /* NOTE: may have returned sooner on some errors*/
|
|||
|
|
} /* end rtadd_ms_or_us */
|
|||
|
|
|
|||
|
|
/************************************************************************/
|
|||
|
|
/* scl2_datatype_create */
|
|||
|
|
/* Create MMS Data type for one Logical Node Type (LNodeType) */
|
|||
|
|
/* defined in SCL. */
|
|||
|
|
/* RETURNS: type_id (-1 on error) */
|
|||
|
|
/************************************************************************/
|
|||
|
|
static ST_INT scl2_datatype_create (
|
|||
|
|
MVL_VMD_CTRL *vmd_ctrl, /* VMD in which to add this type */
|
|||
|
|
SCL_INFO *scl_info, /* main struct where all SCL info stored*/
|
|||
|
|
SCL_LNTYPE *scl_lntype, /* info for this LNType */
|
|||
|
|
ST_INT max_rt_num, /* max num of RUNTIME_TYPE for this LNodeType*/
|
|||
|
|
/* If you don't care, set this = 0. */
|
|||
|
|
ST_BOOLEAN use_names, /* If set, use type names. */
|
|||
|
|
ST_CHAR *name_prefix, /* unique prefix to add to each type name*/
|
|||
|
|
/* only used if "use_names==SD_TRUE". */
|
|||
|
|
SCL_LD *scl_ld_special, /* LD containing LN with "special stuff"*/
|
|||
|
|
/* (i.e. ReportControl, LogControl, etc)*/
|
|||
|
|
/* NULL if nothing special in LN. */
|
|||
|
|
SCL_LN *scl_ln_special) /* LN with "special stuff" */
|
|||
|
|
/* NULL if nothing special in LN. */
|
|||
|
|
{
|
|||
|
|
ST_RET retcode;
|
|||
|
|
ST_INT type_id = -1; /* assumes failure. */
|
|||
|
|
RUNTIME_BUILD_CTXT rt_ctxt;
|
|||
|
|
MVL_TYPE_CTRL *type_ctrl;
|
|||
|
|
ST_CHAR type_name[MAX_IDENT_LEN+1];
|
|||
|
|
ST_INT loop; /* loop counter */
|
|||
|
|
ST_INT save_rt_num=0; /* RUNTIME_TYPE count computed on first loop */
|
|||
|
|
|
|||
|
|
if (use_names)
|
|||
|
|
{
|
|||
|
|
/* Generate type_name. */
|
|||
|
|
if (scl_ln_special)
|
|||
|
|
{ /* Create unique type name for this LN. */
|
|||
|
|
retcode = special_type_name (name_prefix, scl_ld_special, scl_ln_special, type_name, sizeof(type_name));
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
retcode = simple_type_name (name_prefix, scl_lntype, type_name, sizeof(type_name));
|
|||
|
|
if (retcode)
|
|||
|
|
{
|
|||
|
|
SXLOG_ERR1 ("Error generating type name for LNodeType id='%s'", scl_lntype->id);
|
|||
|
|
return (-1); /* return type_id of (-1) to indicate error */
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* Begin constructing the data type in rt_ctxt. */
|
|||
|
|
/* Loop exactly 2 times. First iteration, just compute "rt_num". */
|
|||
|
|
/* Call "ms_rt_bld_get_count" at end of loop to get the count. */
|
|||
|
|
/* Second iteration, build type (pass rt_num to ms_rt_bld_start). */
|
|||
|
|
/* CRITICAL: do not access rt_ctxt->rt_ctrl on first loop iteration. */
|
|||
|
|
/* It is not set yet. */
|
|||
|
|
for (loop = 0; loop < 2; loop++)
|
|||
|
|
{
|
|||
|
|
if (loop == 0)
|
|||
|
|
ms_rt_bld_start (&rt_ctxt, 0); /* 2nd arg = 0 so bld functions */
|
|||
|
|
/* just compute rt_num */
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
ms_rt_bld_start (&rt_ctxt, save_rt_num); /* first loop computed save_rt_num*/
|
|||
|
|
/* Allocate extra array of structs. Free this when all SCL processing*/
|
|||
|
|
/* done (see end of scl2_ld_create_all). */
|
|||
|
|
rt_ctxt.rt_ctrl->reserved_1 = (ST_VOID *) chk_calloc (save_rt_num, sizeof (RESERVED_INFO)); /* array of ptrs*/
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* Go through all linked lists and create COMPLETE type definition. */
|
|||
|
|
/* NOTE: order of FC specified in 61850-8-1 (section 6.1.3). */
|
|||
|
|
|
|||
|
|
/* NOTE: SCL only allows "ST" "MX" "CO" "SP" "SG" "SE" "SV" "CF" "DC" "EX".*/
|
|||
|
|
/* "RP", "LG", and "BR" are special cases. */
|
|||
|
|
do
|
|||
|
|
{ /* "do-while" loop: only needed so we can break on first error. */
|
|||
|
|
/* if any rtadd_* call fails, break and return error. */
|
|||
|
|
if ((retcode = ms_rt_bld_add_str_start (&rt_ctxt, NULL)) != SD_SUCCESS) /* no comp name*/
|
|||
|
|
break;
|
|||
|
|
if ((retcode = rtadd_fc (&rt_ctxt, "MX", scl_info, scl_lntype, scl_ln_special)) != SD_SUCCESS)
|
|||
|
|
break;
|
|||
|
|
if ((retcode = rtadd_fc (&rt_ctxt, "ST", scl_info, scl_lntype, scl_ln_special)) != SD_SUCCESS)
|
|||
|
|
break;
|
|||
|
|
if ((retcode = rtadd_fc (&rt_ctxt, "CO", scl_info, scl_lntype, scl_ln_special)) != SD_SUCCESS)
|
|||
|
|
break;
|
|||
|
|
if ((retcode = rtadd_fc (&rt_ctxt, "CF", scl_info, scl_lntype, scl_ln_special)) != SD_SUCCESS)
|
|||
|
|
break;
|
|||
|
|
if ((retcode = rtadd_fc (&rt_ctxt, "DC", scl_info, scl_lntype, scl_ln_special)) != SD_SUCCESS)
|
|||
|
|
break;
|
|||
|
|
if ((retcode = rtadd_fc (&rt_ctxt, "SP", scl_info, scl_lntype, scl_ln_special)) != SD_SUCCESS)
|
|||
|
|
break;
|
|||
|
|
if ((retcode = rtadd_fc (&rt_ctxt, "SG", scl_info, scl_lntype, scl_ln_special)) != SD_SUCCESS)
|
|||
|
|
break;
|
|||
|
|
|
|||
|
|
/* FC=RP is special case. Call special function. */
|
|||
|
|
if (scl_ln_special)
|
|||
|
|
{ /* special type for LN containing RCBs */
|
|||
|
|
if ((retcode = rtadd_rp_or_br (&rt_ctxt, scl_info, scl_lntype, scl_ln_special, SD_FALSE)) != SD_SUCCESS)
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* FC=LG is special case. Call special function. */
|
|||
|
|
if (scl_ln_special)
|
|||
|
|
{
|
|||
|
|
if ((retcode = rtadd_lg (&rt_ctxt, scl_info, scl_lntype, scl_ln_special)) != SD_SUCCESS)
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* FC=BR is special case. Call special function. */
|
|||
|
|
if (scl_ln_special)
|
|||
|
|
{ /* special type for LN containing RCBs */
|
|||
|
|
if ((retcode = rtadd_rp_or_br (&rt_ctxt, scl_info, scl_lntype, scl_ln_special, SD_TRUE)) != SD_SUCCESS)
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (scl_ln_special)
|
|||
|
|
{
|
|||
|
|
/* FC=GO is special case. Call special function (last arg SD_TRUE). */
|
|||
|
|
if ((retcode = rtadd_go_or_gs (&rt_ctxt, scl_info, scl_lntype, scl_ln_special, SD_TRUE)) != SD_SUCCESS)
|
|||
|
|
break;
|
|||
|
|
/* FC=GS is special case. Call special function (last arg SD_FALSE). */
|
|||
|
|
if ((retcode = rtadd_go_or_gs (&rt_ctxt, scl_info, scl_lntype, scl_ln_special, SD_FALSE)) != SD_SUCCESS)
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if ((retcode = rtadd_fc (&rt_ctxt, "SV", scl_info, scl_lntype, scl_ln_special)) != SD_SUCCESS)
|
|||
|
|
break;
|
|||
|
|
if ((retcode = rtadd_fc (&rt_ctxt, "SE", scl_info, scl_lntype, scl_ln_special)) != SD_SUCCESS)
|
|||
|
|
break;
|
|||
|
|
|
|||
|
|
/* Add types for "MS" and "US" (sampled value control blocks). */
|
|||
|
|
if (scl_ln_special)
|
|||
|
|
{
|
|||
|
|
/* FC=MS: This funct with arg = SD_TRUE adds types for FC=MS. */
|
|||
|
|
if ((retcode = rtadd_ms_or_us (&rt_ctxt, scl_info, scl_lntype, scl_ln_special, SD_TRUE)) != SD_SUCCESS)
|
|||
|
|
break;
|
|||
|
|
/* FC=US: This funct with arg = SD_FALSE adds types for FC=US. */
|
|||
|
|
if ((retcode = rtadd_ms_or_us (&rt_ctxt, scl_info, scl_lntype, scl_ln_special, SD_FALSE)) != SD_SUCCESS)
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if ((retcode = rtadd_fc (&rt_ctxt, "EX", scl_info, scl_lntype, scl_ln_special)) != SD_SUCCESS)
|
|||
|
|
break;
|
|||
|
|
if ((retcode = ms_rt_bld_add_str_end (&rt_ctxt)) != SD_SUCCESS)
|
|||
|
|
break;
|
|||
|
|
} while (0); /* end of "do-while" loop: only needed so we can break on first error. */
|
|||
|
|
|
|||
|
|
if (loop==0)
|
|||
|
|
{
|
|||
|
|
/* First loop: computing rt_num. Save rt_num to use on second loop.*/
|
|||
|
|
/* CRITICAL: use ..get_max_count here to get "maximum" count. */
|
|||
|
|
/* May be slightly larger than "current" count. */
|
|||
|
|
save_rt_num = ms_rt_bld_get_max_count (&rt_ctxt);
|
|||
|
|
/* NOTE: no need to call ms_rt_bld_finish (nothing to clean up). */
|
|||
|
|
|
|||
|
|
/* If max_rt_num != 0, and save_rt_num exceeds it, log and return error.*/
|
|||
|
|
if (max_rt_num != 0 && save_rt_num > max_rt_num)
|
|||
|
|
{
|
|||
|
|
SXLOG_ERR3 ("Number of RUNTIME_TYPE elements (%d) exceeds maximum (%d). Cannot create type for LNodeType id=%s",
|
|||
|
|
save_rt_num, max_rt_num, scl_lntype->id);
|
|||
|
|
return (-1); /* error. Cannot continue */
|
|||
|
|
}
|
|||
|
|
else if (save_rt_num == 0)
|
|||
|
|
{
|
|||
|
|
SXLOG_ERR1 ("Number of RUNTIME_TYPE elements = 0. Cannot create type for LNodeType id=%s",
|
|||
|
|
scl_lntype->id);
|
|||
|
|
return (-1); /* error. Cannot continue */
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
/* On Second loop, nothing else to do. */
|
|||
|
|
} /* end outer loop */
|
|||
|
|
|
|||
|
|
/* If retcode==SD_SUCCESS, ALL "rtadd_fc" calls were successful, so create type here */
|
|||
|
|
if (retcode == SD_SUCCESS)
|
|||
|
|
{
|
|||
|
|
ms_rt_bld_finish (&rt_ctxt);
|
|||
|
|
|
|||
|
|
/* "rt_num" must NEVER exceed size allocated, but may be slightly less.*/
|
|||
|
|
assert (rt_ctxt.rt_ctrl->rt_num <= save_rt_num);
|
|||
|
|
SXLOG_FLOW2 ("RUNTIME_TYPE count for LNodeType id='%s' = %d",
|
|||
|
|
scl_lntype->id, rt_ctxt.rt_ctrl->rt_num);
|
|||
|
|
|
|||
|
|
/* Create the type now. */
|
|||
|
|
if (use_names)
|
|||
|
|
type_id = mvl_vmd_type_id_create (vmd_ctrl, type_name, rt_ctxt.rt_ctrl);
|
|||
|
|
else
|
|||
|
|
type_id = mvl_vmd_type_id_create (vmd_ctrl, NULL, rt_ctxt.rt_ctrl);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{ /* build failed, so cleanup now */
|
|||
|
|
chk_free (rt_ctxt.rt_ctrl->reserved_1);
|
|||
|
|
ms_rt_bld_cancel (&rt_ctxt);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (type_id >= 0)
|
|||
|
|
{ /* type successfully created */
|
|||
|
|
type_ctrl = mvl_type_ctrl_find (type_id);
|
|||
|
|
if (mms_debug_sel & MMS_LOG_RT)
|
|||
|
|
{
|
|||
|
|
MLOG_ALWAYS1 ("RUNTIME_TYPE definition for LNodeType id='%s':", scl_lntype->id);
|
|||
|
|
ms_log_runtime (type_ctrl->rt_ctrl->rt_first, type_ctrl->rt_ctrl->rt_num);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* CRITICAL: we want the same leaf function for every leaf, so */
|
|||
|
|
/* set leaf functions now in every RUNTIME_TYPE element. */
|
|||
|
|
/* NOTE: leaf functions never used for RT_STR_START, RT_ARR_START,*/
|
|||
|
|
/* etc. but they're set anyway because it's easier to set all.*/
|
|||
|
|
/* NOTE: this function may be customized for each application. */
|
|||
|
|
/* It should be placed in a "user" module. */
|
|||
|
|
|
|||
|
|
if (u_set_all_leaf_functions (type_ctrl->rt_ctrl) != SD_SUCCESS)
|
|||
|
|
{
|
|||
|
|
SXLOG_ERR1 ("Error setting leaf functions for LNodeType id=%s", scl_lntype->id);
|
|||
|
|
mvl_type_id_destroy (type_id); /* destroy type just created */
|
|||
|
|
type_id = -1; /* set invalid type_id to indicate error */
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (type_id < 0)
|
|||
|
|
SXLOG_ERR1 ("scl2_datatype_create FAILED for LNodeType id=%s", scl_lntype->id);
|
|||
|
|
else
|
|||
|
|
SXLOG_FLOW1 ("scl2_datatype_create SUCCESSFUL for LNodeType id=%s", scl_lntype->id);
|
|||
|
|
|
|||
|
|
return (type_id);
|
|||
|
|
}
|
|||
|
|
/************************************************************************/
|
|||
|
|
/* find_scl_lntype */
|
|||
|
|
/* Search for lntype by name. */
|
|||
|
|
/************************************************************************/
|
|||
|
|
SCL_LNTYPE *find_scl_lntype (SCL_INFO *scl_info, ST_CHAR *name)
|
|||
|
|
{
|
|||
|
|
SCL_LNTYPE *scl_lntype;
|
|||
|
|
for (scl_lntype = (SCL_LNTYPE *) list_find_last ((DBL_LNK *) scl_info->lnTypeHead);
|
|||
|
|
scl_lntype != NULL;
|
|||
|
|
scl_lntype = (SCL_LNTYPE *) list_find_prev ((DBL_LNK *) scl_info->lnTypeHead, (DBL_LNK *) scl_lntype))
|
|||
|
|
{
|
|||
|
|
if (strcmp (name, scl_lntype->id) == 0) /* type name matches*/
|
|||
|
|
return (scl_lntype);
|
|||
|
|
}
|
|||
|
|
return (NULL); /* lntype not found*/
|
|||
|
|
}
|
|||
|
|
/************************************************************************/
|
|||
|
|
/* scl2_datatype_create_all */
|
|||
|
|
/* Create MMS Data types for all Logical Node Types (LNodeType) */
|
|||
|
|
/* defined in SCL. */
|
|||
|
|
/* RETURNS: SD_SUCCESS or error code */
|
|||
|
|
/************************************************************************/
|
|||
|
|
ST_RET scl2_datatype_create_all (
|
|||
|
|
MVL_VMD_CTRL *vmd_ctrl, /* VMD in which to add types. */
|
|||
|
|
SCL_INFO *scl_info, /* main struct where all SCL info stored*/
|
|||
|
|
ST_INT max_rt_num, /* max num of RUNTIME_TYPE for each LNodeType*/
|
|||
|
|
ST_BOOLEAN use_names, /* if SD_TRUE, generate a name for each type*/
|
|||
|
|
ST_CHAR *name_prefix) /* unique prefix to add to each type name*/
|
|||
|
|
/* only used if "use_names==SD_TRUE". */
|
|||
|
|
{
|
|||
|
|
ST_RET retcode;
|
|||
|
|
SCL_LNTYPE *scl_lntype;
|
|||
|
|
SCL_LD *scl_ld;
|
|||
|
|
SCL_LN *scl_ln;
|
|||
|
|
|
|||
|
|
/* NOTE: linked list is in reverse order from data in SCL file, */
|
|||
|
|
/* so get off list in reverse order. */
|
|||
|
|
retcode = SD_SUCCESS;
|
|||
|
|
|
|||
|
|
/* CRITICAL: Don't allow this to be called again for the same SCL_INFO struct.*/
|
|||
|
|
/* That would cause types to be created twice, and the "type_id" saved*/
|
|||
|
|
/* in each SCL_LN and SCL_LNTYPE would be overwritten. */
|
|||
|
|
if (scl_info->datatype_create_done)
|
|||
|
|
{
|
|||
|
|
SXLOG_ERR0 ("Data types already created for this SCL file. Cannot create again.");
|
|||
|
|
return (SD_FAILURE);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
scl_info->datatype_create_done = SD_TRUE; /* Set flag now */
|
|||
|
|
|
|||
|
|
/* Loop through all scl_lntype structs. For each one, create type & save type_id in scl_lntype->type_id.*/
|
|||
|
|
for (scl_lntype = (SCL_LNTYPE *) list_find_last ((DBL_LNK *) scl_info->lnTypeHead);
|
|||
|
|
scl_lntype != NULL;
|
|||
|
|
scl_lntype = (SCL_LNTYPE *) list_find_prev ((DBL_LNK *) scl_info->lnTypeHead, (DBL_LNK *) scl_lntype))
|
|||
|
|
{
|
|||
|
|
scl_lntype->type_id = scl2_datatype_create (vmd_ctrl, scl_info, scl_lntype,
|
|||
|
|
max_rt_num, use_names, name_prefix, NULL, NULL);
|
|||
|
|
/* NOTE: could fail (type_id < 0). OK for now. Might not be used by any LN.*/
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* Loop through all Logical Nodes. If a Locical Node
|
|||
|
|
* contains "special stuff" (i.e. ReportControl, LogControl, GSEControl),
|
|||
|
|
* create a special type that includes the "special stuff".
|
|||
|
|
* If not, use the appropriate type created by the above loop (scl_lntype->type_id).
|
|||
|
|
* Must check all Logical Nodes (scl_ln) in all Logical Devices (scl_ld).
|
|||
|
|
*/
|
|||
|
|
for (scl_ld = (SCL_LD *) list_find_last ((DBL_LNK *) scl_info->ldHead);
|
|||
|
|
scl_ld != NULL;
|
|||
|
|
scl_ld = (SCL_LD *) list_find_prev ((DBL_LNK *) scl_info->ldHead, (DBL_LNK *) scl_ld))
|
|||
|
|
{
|
|||
|
|
for (scl_ln = (SCL_LN *) list_find_last ((DBL_LNK *) scl_ld->lnHead);
|
|||
|
|
scl_ln != NULL;
|
|||
|
|
scl_ln = (SCL_LN *) list_find_prev ((DBL_LNK *) scl_ld->lnHead, (DBL_LNK *) scl_ln))
|
|||
|
|
{
|
|||
|
|
scl_lntype = find_scl_lntype (scl_info, scl_ln->lnType); /* search for lntype by name*/
|
|||
|
|
if (scl_lntype)
|
|||
|
|
{
|
|||
|
|
assert (scl_ln->type_id == 0); /* already set. Something's wrong*/
|
|||
|
|
/* If rcbHead!=NULL, this LN contains ReportControl. */
|
|||
|
|
/* If lcbHead!=NULL, this LN contains LogControl. */
|
|||
|
|
/* If gcbHead!=NULL, this LN contains GSEControl. */
|
|||
|
|
/* If svcbHead!=NULL, this LN contains SampledValueControl */
|
|||
|
|
/* NOTE: do nothing if it fails. Detected later when rcb created.*/
|
|||
|
|
if (scl_ln->rcbHead || scl_ln->lcbHead || scl_ln->gcbHead || scl_ln->svcbHead)
|
|||
|
|
{ /* "Special" LN. Create special type just for this LN. */
|
|||
|
|
/* Last 2 args point to LD & LN containing special info (rcb, gcb, etc.).*/
|
|||
|
|
scl_ln->type_id = scl2_datatype_create (vmd_ctrl, scl_info, scl_lntype,
|
|||
|
|
max_rt_num, use_names, name_prefix, scl_ld, scl_ln);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{ /* "Normal" LN. Save type_id created by first loop. */
|
|||
|
|
scl_ln->type_id = scl_lntype->type_id;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{ /* type not found. Set invalid type_id. Error logged below.*/
|
|||
|
|
scl_ln->type_id = -1;
|
|||
|
|
}
|
|||
|
|
if (scl_ln->type_id < 0)
|
|||
|
|
{
|
|||
|
|
SXLOG_ERR2 ("Could not find or create lnType '%s' for LN '%s'",
|
|||
|
|
scl_ln->lnType, scl_ln->varName);
|
|||
|
|
retcode = SD_FAILURE; /* create failed for this LN, return error.*/
|
|||
|
|
/* NOTE: function fails, but continue looping to log other errors too.*/
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if (retcode)
|
|||
|
|
{
|
|||
|
|
/* If anything failed, reserved_1 member of every */
|
|||
|
|
/* RUNTIME_CTRL should no longer be needed. */
|
|||
|
|
reserved_free_all (vmd_ctrl);
|
|||
|
|
}
|
|||
|
|
return (retcode);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/************************************************************************/
|
|||
|
|
/* scl2_ln_init_lcb_vals */
|
|||
|
|
/* Use mvlu_find_comp_type to find the data offset of each LCB in the LN.*/
|
|||
|
|
/* Then write the SCL data there. */
|
|||
|
|
/************************************************************************/
|
|||
|
|
static ST_VOID scl2_ln_init_lcb_vals (
|
|||
|
|
SCL_INFO *scl_info, /* main struct where all SCL info stored*/
|
|||
|
|
SCL_LD *scl_ld,
|
|||
|
|
SCL_LN *scl_ln)
|
|||
|
|
{
|
|||
|
|
ST_CHAR flatname [MAX_IDENT_LEN +1];
|
|||
|
|
SCL_LCB *scl_lcb;
|
|||
|
|
MVL61850_LCB_DATA *lcb_data;
|
|||
|
|
RUNTIME_TYPE *comp_rt_type; /* first rt_type of component */
|
|||
|
|
ST_INT comp_rt_num; /* num of rt_types in component */
|
|||
|
|
ST_RET retcode = SD_SUCCESS;
|
|||
|
|
|
|||
|
|
for (scl_lcb = (SCL_LCB *) list_find_last ((DBL_LNK *) scl_ln->lcbHead);
|
|||
|
|
scl_lcb != NULL;
|
|||
|
|
scl_lcb = (SCL_LCB *) list_find_prev ((DBL_LNK *) scl_ln->lcbHead, (DBL_LNK *) scl_lcb))
|
|||
|
|
{
|
|||
|
|
/* NOTE: mvlu_find_comp_type uses flattened name without LN prefix. */
|
|||
|
|
sprintf (flatname, "LG$%s", scl_lcb->name);
|
|||
|
|
retcode = mvlu_find_comp_type (scl_ln->mvl_var_assoc->type_id,
|
|||
|
|
flatname, &comp_rt_type, &comp_rt_num);
|
|||
|
|
if (retcode!=SD_SUCCESS)
|
|||
|
|
{
|
|||
|
|
SXLOG_ERR2 ("Cannot find component '%s' in var '%s'. Initial values not set.",
|
|||
|
|
flatname, scl_ln->mvl_var_assoc->name);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
lcb_data = (MVL61850_LCB_DATA *)((ST_CHAR *) scl_ln->mvl_var_assoc->data + comp_rt_type->mvluTypeInfo.offSet);
|
|||
|
|
/* Use SCL info to initialize LCB data. */
|
|||
|
|
/* Construct ObjectReference for lcb_data->DatSet. */
|
|||
|
|
scl_make_objref (scl_ld, scl_ln, scl_lcb->datSet,
|
|||
|
|
lcb_data->DatSet, sizeof (lcb_data->DatSet) - 1);
|
|||
|
|
lcb_data->IntgPd = (ST_UINT32) scl_lcb->intgPd;
|
|||
|
|
/* Construct ObjectReference for lcb_data->LogRef. */
|
|||
|
|
scl_make_objref2 (scl_ld, scl_lcb->logName,
|
|||
|
|
lcb_data->LogRef, sizeof (lcb_data->LogRef) - 1);
|
|||
|
|
lcb_data->LogEna = scl_lcb->logEna;
|
|||
|
|
/* NOTE: "scl_lcb->reasonCode" may be read from SCL file, but it */
|
|||
|
|
/* is not in LCB (according to 61850-8-1) so just ignore it. */
|
|||
|
|
lcb_data->TrgOps.len = 6; /* Var len 6-bit bitstring */
|
|||
|
|
lcb_data->TrgOps.data[0] = scl_lcb->TrgOps[0]; /* 1 byte of data */
|
|||
|
|
}
|
|||
|
|
} /* end loop */
|
|||
|
|
}
|
|||
|
|
/************************************************************************/
|
|||
|
|
/* scl2_ln_init_gcb_vals */
|
|||
|
|
/* Use mvlu_find_comp_type to find the data offset of each GCB in the LN.*/
|
|||
|
|
/* Then write the SCL data there. */
|
|||
|
|
/************************************************************************/
|
|||
|
|
static ST_VOID scl2_ln_init_gcb_vals (
|
|||
|
|
SCL_INFO *scl_info, /* main struct where all SCL info stored*/
|
|||
|
|
SCL_LD *scl_ld,
|
|||
|
|
SCL_LN *scl_ln)
|
|||
|
|
{
|
|||
|
|
ST_CHAR flatname [MAX_IDENT_LEN +1];
|
|||
|
|
SCL_GCB *scl_gcb;
|
|||
|
|
MVL61850_GCB_DATA *gcb_data; /* GOOSE Control Block data */
|
|||
|
|
MVL61850_SCB_DATA *scb_data; /* GSSE Control Block data */
|
|||
|
|
RUNTIME_TYPE *comp_rt_type; /* first rt_type of component */
|
|||
|
|
ST_INT comp_rt_num; /* num of rt_types in component */
|
|||
|
|
ST_RET retcode = SD_SUCCESS;
|
|||
|
|
ST_CHAR *dataptr;
|
|||
|
|
SCL_GSE *scl_gse; /* GOOSE addressing info */
|
|||
|
|
|
|||
|
|
for (scl_gcb = (SCL_GCB *) list_find_last ((DBL_LNK *) scl_ln->gcbHead);
|
|||
|
|
scl_gcb != NULL;
|
|||
|
|
scl_gcb = (SCL_GCB *) list_find_prev ((DBL_LNK *) scl_ln->gcbHead, (DBL_LNK *) scl_gcb))
|
|||
|
|
{
|
|||
|
|
/* NOTE: mvlu_find_comp_type uses flattened name without LN prefix. */
|
|||
|
|
if (scl_gcb->isGoose) /* GOOSE */
|
|||
|
|
sprintf (flatname, "GO$%s", scl_gcb->name);
|
|||
|
|
else /* GSSE */
|
|||
|
|
sprintf (flatname, "GS$%s", scl_gcb->name);
|
|||
|
|
retcode = mvlu_find_comp_type (scl_ln->mvl_var_assoc->type_id,
|
|||
|
|
flatname, &comp_rt_type, &comp_rt_num);
|
|||
|
|
if (retcode!=SD_SUCCESS)
|
|||
|
|
{
|
|||
|
|
SXLOG_ERR2 ("Cannot find component '%s' in var '%s'. Initial values not set.",
|
|||
|
|
flatname, scl_ln->mvl_var_assoc->name);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
dataptr = (ST_CHAR *) scl_ln->mvl_var_assoc->data
|
|||
|
|
+ comp_rt_type->mvluTypeInfo.offSet;
|
|||
|
|
/* Use SCL info to initialize GCB data. */
|
|||
|
|
if (scl_gcb->isGoose)
|
|||
|
|
{ /* GOOSE */
|
|||
|
|
gcb_data = (MVL61850_GCB_DATA *) dataptr;
|
|||
|
|
/* Construct ObjectReference for gcb_data->DatSet. */
|
|||
|
|
scl_make_objref (scl_ld, scl_ln, scl_gcb->datSet,
|
|||
|
|
gcb_data->DatSet, sizeof (gcb_data->DatSet) - 1);
|
|||
|
|
gcb_data->ConfRev = (ST_UINT32) scl_gcb->confRev;
|
|||
|
|
/* 61850-8-1 maps appID to GoID */
|
|||
|
|
strcpy (gcb_data->GoID, scl_gcb->appID);
|
|||
|
|
/* NOTE: NdsCom is not configured in SCL so initial value of */
|
|||
|
|
/* "gcb_data->NdsCom" is always 0. */
|
|||
|
|
if ((scl_gse = scl_gse_find (scl_info, scl_ld, scl_gcb)) != NULL)
|
|||
|
|
{
|
|||
|
|
/* Initialize DstAddress structure. */
|
|||
|
|
memcpy (gcb_data->DstAddress.Addr, scl_gse->MAC, CLNP_MAX_LEN_MAC);
|
|||
|
|
gcb_data->DstAddress.PRIORITY = (ST_UINT8) scl_gse->VLANPRI;
|
|||
|
|
gcb_data->DstAddress.VID = (ST_UINT16) scl_gse->VLANID;
|
|||
|
|
gcb_data->DstAddress.APPID = (ST_UINT16) scl_gse->APPID;
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
MVL_LOG_ERR1 ("Cannot find GSE element to set address for GOOSE Control '%s'. DstAddress NOT INITIALIZED.",
|
|||
|
|
scl_gcb->name);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{ /* GSSE */
|
|||
|
|
scb_data = (MVL61850_SCB_DATA *) dataptr;
|
|||
|
|
/* datSet, confRev ignored for GSSE. */
|
|||
|
|
/* 61850-8-1 maps appID to GsID */
|
|||
|
|
strcpy (scb_data->GsID, scl_gcb->appID);
|
|||
|
|
if ((scl_gse = scl_gse_find (scl_info, scl_ld, scl_gcb)) != NULL)
|
|||
|
|
{
|
|||
|
|
/* Initialize DstAddress structure. */
|
|||
|
|
memcpy (scb_data->DstAddress.Addr, scl_gse->MAC, CLNP_MAX_LEN_MAC);
|
|||
|
|
scb_data->DstAddress.PRIORITY = (ST_UINT8) scl_gse->VLANPRI;
|
|||
|
|
scb_data->DstAddress.VID = (ST_UINT16) scl_gse->VLANID;
|
|||
|
|
scb_data->DstAddress.APPID = (ST_UINT16) scl_gse->APPID;
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
MVL_LOG_ERR1 ("Cannot find GSE element to set address for GSSE Control '%s'. DstAddress NOT INITIALIZED.",
|
|||
|
|
scl_gcb->name);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
} /* end loop */
|
|||
|
|
}
|
|||
|
|
/************************************************************************/
|
|||
|
|
/* scl2_ln_init_svcb_vals */
|
|||
|
|
/* Use mvlu_find_comp_type to find the data offset of each SVCB in the LN.*/
|
|||
|
|
/* Then write the SCL data there. */
|
|||
|
|
/************************************************************************/
|
|||
|
|
static ST_VOID scl2_ln_init_svcb_vals (
|
|||
|
|
SCL_INFO *scl_info, /* main struct where all SCL info stored*/
|
|||
|
|
SCL_LD *scl_ld,
|
|||
|
|
SCL_LN *scl_ln)
|
|||
|
|
{
|
|||
|
|
ST_CHAR flatname [MAX_IDENT_LEN +1];
|
|||
|
|
SCL_SVCB *scl_svcb;
|
|||
|
|
RUNTIME_TYPE *comp_rt_type; /* first rt_type of component */
|
|||
|
|
ST_INT comp_rt_num; /* num of rt_types in component */
|
|||
|
|
ST_RET retcode = SD_SUCCESS;
|
|||
|
|
ST_CHAR *dataptr;
|
|||
|
|
|
|||
|
|
for (scl_svcb = (SCL_SVCB *) list_find_last ((DBL_LNK *) scl_ln->svcbHead);
|
|||
|
|
scl_svcb != NULL;
|
|||
|
|
scl_svcb = (SCL_SVCB *) list_find_prev ((DBL_LNK *) scl_ln->svcbHead, (DBL_LNK *) scl_svcb))
|
|||
|
|
{
|
|||
|
|
/* NOTE: mvlu_find_comp_type uses flattened name without LN prefix. */
|
|||
|
|
if (scl_svcb->multicast)
|
|||
|
|
sprintf (flatname, "MS$%s", scl_svcb->name);
|
|||
|
|
else
|
|||
|
|
sprintf (flatname, "US$%s", scl_svcb->name);
|
|||
|
|
retcode = mvlu_find_comp_type (scl_ln->mvl_var_assoc->type_id,
|
|||
|
|
flatname, &comp_rt_type, &comp_rt_num);
|
|||
|
|
if (retcode!=SD_SUCCESS)
|
|||
|
|
{
|
|||
|
|
SXLOG_ERR2 ("Cannot find component '%s' in var '%s'. Initial values not set.",
|
|||
|
|
flatname, scl_ln->mvl_var_assoc->name);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
dataptr = (ST_CHAR *) scl_ln->mvl_var_assoc->data
|
|||
|
|
+ comp_rt_type->mvluTypeInfo.offSet;
|
|||
|
|
/* Use SCL info to initialize SVCB data. */
|
|||
|
|
if (scl_svcb->multicast)
|
|||
|
|
{ /* MSVCB */
|
|||
|
|
MVL61850_MSVCB_DATA *msvcb_data = (MVL61850_MSVCB_DATA *) dataptr;
|
|||
|
|
msvcb_data->SvEna = SD_FALSE; /* can't be configured. Init to FALSE*/
|
|||
|
|
strcpy (msvcb_data->MsvID, scl_svcb->smvID);
|
|||
|
|
/* Construct ObjectReference to initialize msvcb_data->DatSet. */
|
|||
|
|
scl_make_objref (scl_ld, scl_ln, scl_svcb->datSet,
|
|||
|
|
msvcb_data->DatSet, sizeof (msvcb_data->DatSet) - 1);
|
|||
|
|
msvcb_data->ConfRev = scl_svcb->confRev;
|
|||
|
|
msvcb_data->SmpRate = scl_svcb->smpRate;
|
|||
|
|
msvcb_data->OptFlds.len = 3; /* Var len 3-bit bitstring */
|
|||
|
|
msvcb_data->OptFlds.data[0] = scl_svcb->OptFlds[0]; /* 1 byte of data*/
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{ /* USVCB */
|
|||
|
|
MVL61850_USVCB_DATA *usvcb_data = (MVL61850_USVCB_DATA *) dataptr;
|
|||
|
|
usvcb_data->SvEna = SD_FALSE; /* can't be configured. Init to FALSE*/
|
|||
|
|
usvcb_data->Resv = SD_FALSE; /* can't be configured. Init to FALSE*/
|
|||
|
|
strcpy (usvcb_data->UsvID, scl_svcb->smvID);
|
|||
|
|
/* Construct ObjectReference to initialize usvcb_data->DatSet. */
|
|||
|
|
scl_make_objref (scl_ld, scl_ln, scl_svcb->datSet,
|
|||
|
|
usvcb_data->DatSet, sizeof (usvcb_data->DatSet) - 1);
|
|||
|
|
usvcb_data->ConfRev = scl_svcb->confRev;
|
|||
|
|
usvcb_data->SmpRate = scl_svcb->smpRate;
|
|||
|
|
usvcb_data->OptFlds.len = 3; /* Var len 3-bit bitstring */
|
|||
|
|
usvcb_data->OptFlds.data[0] = scl_svcb->OptFlds[0]; /* 1 byte of data*/
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
} /* end loop */
|
|||
|
|
}
|
|||
|
|
/************************************************************************/
|
|||
|
|
/* scl2_ln_init_sgcb_vals */
|
|||
|
|
/* Use mvlu_find_comp_type to find the data offset of the SGCB in the LN.*/
|
|||
|
|
/* Then write the SCL data there. */
|
|||
|
|
/************************************************************************/
|
|||
|
|
static ST_VOID scl2_ln_init_sgcb_vals (
|
|||
|
|
SCL_INFO *scl_info, /* main struct where all SCL info stored*/
|
|||
|
|
SCL_LD *scl_ld,
|
|||
|
|
SCL_LN *scl_ln)
|
|||
|
|
{
|
|||
|
|
ST_CHAR flatname [MAX_IDENT_LEN +1];
|
|||
|
|
RUNTIME_TYPE *comp_rt_type; /* first rt_type of component */
|
|||
|
|
ST_INT comp_rt_num; /* num of rt_types in component */
|
|||
|
|
ST_RET retcode = SD_SUCCESS;
|
|||
|
|
ST_CHAR *dataptr;
|
|||
|
|
MVL61850_SGCB_DATA *sgcb_data;
|
|||
|
|
|
|||
|
|
/* Only 1 SGCB allowed. Don't need loop. */
|
|||
|
|
if (scl_ln->sgcb)
|
|||
|
|
{
|
|||
|
|
/* NOTE: mvlu_find_comp_type uses flattened name without LN prefix. */
|
|||
|
|
strcpy (flatname, "SP$SGCB");
|
|||
|
|
retcode = mvlu_find_comp_type (scl_ln->mvl_var_assoc->type_id,
|
|||
|
|
flatname, &comp_rt_type, &comp_rt_num);
|
|||
|
|
if (retcode!=SD_SUCCESS)
|
|||
|
|
{
|
|||
|
|
SXLOG_ERR2 ("Cannot find component '%s' in var '%s'. Initial values not set.",
|
|||
|
|
flatname, scl_ln->mvl_var_assoc->name);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
dataptr = (ST_CHAR *) scl_ln->mvl_var_assoc->data
|
|||
|
|
+ comp_rt_type->mvluTypeInfo.offSet;
|
|||
|
|
/* Use SCL info to initialize SGCB data. */
|
|||
|
|
sgcb_data = (MVL61850_SGCB_DATA *) dataptr;
|
|||
|
|
sgcb_data->NumOfSG = scl_ln->sgcb->numOfSGs;
|
|||
|
|
sgcb_data->ActSG = scl_ln->sgcb->actSG;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
/************************************************************************/
|
|||
|
|
/* scl2_ln_create */
|
|||
|
|
/* RETURNS: SD_SUCCESS or error code */
|
|||
|
|
/************************************************************************/
|
|||
|
|
static ST_RET scl2_ln_create (
|
|||
|
|
MVL_VMD_CTRL *vmd_ctrl,
|
|||
|
|
SCL_INFO *scl_info, /* main struct where all SCL info stored*/
|
|||
|
|
SCL_LD *scl_ld,
|
|||
|
|
SCL_LN *scl_ln)
|
|||
|
|
{
|
|||
|
|
ST_INT type_id;
|
|||
|
|
MVL_VAR_ASSOC *var_assoc;
|
|||
|
|
OBJECT_NAME object_name;
|
|||
|
|
ST_RET retcode;
|
|||
|
|
|
|||
|
|
retcode = SD_FAILURE; /* assume failure */
|
|||
|
|
|
|||
|
|
type_id = scl_ln->type_id;
|
|||
|
|
if (type_id < 0)
|
|||
|
|
SXLOG_ERR2 ("Cannot find type='%s' for logical node='%s'", scl_ln->lnType, scl_ln->varName);
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
/* Construct object name from LD & LN info. */
|
|||
|
|
object_name.object_tag = DOM_SPEC; /* ALWAYS Domain specific */
|
|||
|
|
object_name.domain_id = scl_ld->domName;
|
|||
|
|
object_name.obj_name.vmd_spec = scl_ln->varName;
|
|||
|
|
|
|||
|
|
/* Create the MMS variable (Logical Node). */
|
|||
|
|
/* This function also allocates "var_alloc->data" where this */
|
|||
|
|
/* variable's data is stored. */
|
|||
|
|
var_assoc = mvl_var_add_alloc (vmd_ctrl, &object_name,
|
|||
|
|
NULL, /* MVL_NET_INFO * */
|
|||
|
|
type_id,
|
|||
|
|
NULL); /* MVL_VAR_PROC * */
|
|||
|
|
if (var_assoc == NULL)
|
|||
|
|
SXLOG_ERR2 ("Cannot create LN='%s' in LD='%s'", scl_ln->varName, scl_ld->domName);
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
var_assoc->flags = MVL_VAR_FLAG_UCA; /* CRITICAL: must make it a UCA variable*/
|
|||
|
|
|
|||
|
|
/* NOTE: var_assoc->use_static_data flag is automatically set */
|
|||
|
|
/* by mvl_vmd_var_add because data is never NULL. */
|
|||
|
|
scl_ln->mvl_var_assoc = var_assoc; /* save var_assoc for later*/
|
|||
|
|
retcode = SD_SUCCESS;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if (retcode == SD_SUCCESS)
|
|||
|
|
{
|
|||
|
|
/* Set initial vals for LCB, GCB, & SCB (i.e. GSSE control block). */
|
|||
|
|
scl2_ln_init_lcb_vals (scl_info, scl_ld, scl_ln);
|
|||
|
|
scl2_ln_init_gcb_vals (scl_info, scl_ld, scl_ln);
|
|||
|
|
scl2_ln_init_svcb_vals (scl_info, scl_ld, scl_ln);
|
|||
|
|
scl2_ln_init_sgcb_vals (scl_info, scl_ld, scl_ln);
|
|||
|
|
}
|
|||
|
|
return (retcode);
|
|||
|
|
}
|
|||
|
|
/************************************************************************/
|
|||
|
|
/* scl2_dataset_create */
|
|||
|
|
/* Create one DataSet (SCL_DATASET) for this Logical Node (SCL_LN). */
|
|||
|
|
/* A DataSet maps to a MMS NamedVariableList (NVL). */
|
|||
|
|
/* The MMS NVL name follows the form "LogicalNodeName$DataSetName". */
|
|||
|
|
/* RETURNS: SD_SUCCESS or error code */
|
|||
|
|
/************************************************************************/
|
|||
|
|
static ST_RET scl2_dataset_create (
|
|||
|
|
MVL_VMD_CTRL *vmd_ctrl,
|
|||
|
|
SCL_LD *scl_ld,
|
|||
|
|
SCL_LN *scl_ln,
|
|||
|
|
SCL_DATASET *scl_dataset)
|
|||
|
|
{
|
|||
|
|
OBJECT_NAME nvl_obj;
|
|||
|
|
MVL_NVLIST_CTRL *nvlist_ctrl;
|
|||
|
|
ST_INT num_var = 0;
|
|||
|
|
OBJECT_NAME *var_obj; /* array of var names */
|
|||
|
|
SCL_FCDA *scl_fcda;
|
|||
|
|
ST_RET ret;
|
|||
|
|
/* allow extra char in var_name so strncat can go beyond limit. If it does, return error.*/
|
|||
|
|
ST_CHAR *var_name_buf; /* one buf allocated to hold all var names */
|
|||
|
|
ST_CHAR *var_name; /* ptr to current name being constructed */
|
|||
|
|
ST_CHAR *dot_ptr; /* ptr to '.' within var_name */
|
|||
|
|
ST_CHAR nvl_name [MAX_IDENT_LEN+1]; /* one extra byte for NULL */
|
|||
|
|
|
|||
|
|
ret = SD_SUCCESS; /* assume success */
|
|||
|
|
|
|||
|
|
/* Construct NVL object name from LD, LN, and DataSet info. */
|
|||
|
|
if (strlen (scl_ln->prefix) + strlen (scl_ln->lnClass) + strlen (scl_ln->inst)
|
|||
|
|
+ strlen (scl_dataset->name) + 1 > MAX_IDENT_LEN)
|
|||
|
|
{
|
|||
|
|
SXLOG_ERR1 ("Constructed NVL name would be too long for dataset '%s'.", scl_dataset->name);
|
|||
|
|
return (SD_FAILURE);
|
|||
|
|
}
|
|||
|
|
sprintf (nvl_name, "%s%s%s$%s", scl_ln->prefix, scl_ln->lnClass, scl_ln->inst, scl_dataset->name);
|
|||
|
|
nvl_obj.object_tag = DOM_SPEC; /* ALWAYS Domain specific */
|
|||
|
|
nvl_obj.domain_id = scl_ld->domName;
|
|||
|
|
nvl_obj.obj_name.vmd_spec = nvl_name; /* use constructed NVL name */
|
|||
|
|
|
|||
|
|
/* Go through list once just to count number of variables. */
|
|||
|
|
for (scl_fcda = (SCL_FCDA *) list_find_last ((DBL_LNK *) scl_dataset->fcdaHead);
|
|||
|
|
scl_fcda != NULL;
|
|||
|
|
scl_fcda = (SCL_FCDA *) list_find_prev ((DBL_LNK *) scl_dataset->fcdaHead, (DBL_LNK *) scl_fcda))
|
|||
|
|
{
|
|||
|
|
num_var++;
|
|||
|
|
}
|
|||
|
|
/* Allocate var_obj array. */
|
|||
|
|
var_obj = chk_calloc (num_var, sizeof (OBJECT_NAME));
|
|||
|
|
|
|||
|
|
/* NOTE: this is a bit tricky. OBJECT_NAME struct contains only a ptr
|
|||
|
|
* to name, no storage. Need to allocate storage for names. Here we
|
|||
|
|
* allocate one big buffer for all names. In the loop below,
|
|||
|
|
* we use part of this buffer for each name.
|
|||
|
|
*/
|
|||
|
|
var_name_buf = chk_calloc (num_var, MAX_IDENT_LEN+2); /* Note +2 for NULL plus extra char*/
|
|||
|
|
|
|||
|
|
/* Go through list again to fill in var_obj array. */
|
|||
|
|
num_var = 0;
|
|||
|
|
for (scl_fcda = (SCL_FCDA *) list_find_last ((DBL_LNK *) scl_dataset->fcdaHead);
|
|||
|
|
scl_fcda != NULL;
|
|||
|
|
scl_fcda = (SCL_FCDA *) list_find_prev ((DBL_LNK *) scl_dataset->fcdaHead, (DBL_LNK *) scl_fcda))
|
|||
|
|
{
|
|||
|
|
/* Set "var_name" pointing to proper section of big buffer. */
|
|||
|
|
var_name = var_name_buf + num_var*(MAX_IDENT_LEN+2);
|
|||
|
|
/* Fill in "var_name". */
|
|||
|
|
/* NOTE: allow 1 extra char. If it is used, name is too long, so return error.*/
|
|||
|
|
strncpy (var_name, scl_fcda->prefix, MAX_IDENT_LEN+1);
|
|||
|
|
strncat_maxstrlen (var_name, scl_fcda->lnClass, MAX_IDENT_LEN+1);
|
|||
|
|
strncat_maxstrlen (var_name, scl_fcda->lnInst, MAX_IDENT_LEN+1);
|
|||
|
|
strncat_maxstrlen (var_name, "$", MAX_IDENT_LEN+1);
|
|||
|
|
strncat_maxstrlen (var_name, scl_fcda->fc, MAX_IDENT_LEN+1);
|
|||
|
|
if (strlen (scl_fcda->doName))
|
|||
|
|
{
|
|||
|
|
strncat_maxstrlen (var_name, "$", MAX_IDENT_LEN+1);
|
|||
|
|
strncat_maxstrlen (var_name, scl_fcda->doName, MAX_IDENT_LEN+1);
|
|||
|
|
}
|
|||
|
|
if (strlen (scl_fcda->daName))
|
|||
|
|
{
|
|||
|
|
strncat_maxstrlen (var_name, "$", MAX_IDENT_LEN+1);
|
|||
|
|
strncat_maxstrlen (var_name, scl_fcda->daName, MAX_IDENT_LEN+1);
|
|||
|
|
}
|
|||
|
|
/* doName or daName may contain '.'. Each '.' must be replaced with '$'.*/
|
|||
|
|
dot_ptr = var_name;
|
|||
|
|
while ((dot_ptr = strchr (dot_ptr, '.')) != NULL)
|
|||
|
|
*dot_ptr++ = '$';
|
|||
|
|
if (strlen (var_name) > MAX_IDENT_LEN)
|
|||
|
|
{
|
|||
|
|
SXLOG_ERR1 ("Variable name generated from FCDA info too long '%s'", var_name);
|
|||
|
|
ret = SD_FAILURE;
|
|||
|
|
break; /* stop looping */
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* var_name generated, now fill in this "var_obj". */
|
|||
|
|
var_obj[num_var].object_tag = DOM_SPEC; /* ALWAYS Domain specific */
|
|||
|
|
var_obj[num_var].domain_id = scl_fcda->domName;
|
|||
|
|
var_obj[num_var].obj_name.vmd_spec = var_name;
|
|||
|
|
num_var++;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (ret == SD_SUCCESS)
|
|||
|
|
{
|
|||
|
|
nvlist_ctrl = mvl_vmd_nvl_add (vmd_ctrl, &nvl_obj, NULL, num_var, var_obj, SD_TRUE);
|
|||
|
|
if (nvlist_ctrl)
|
|||
|
|
ret = SD_SUCCESS;
|
|||
|
|
else
|
|||
|
|
ret = SD_FAILURE;
|
|||
|
|
}
|
|||
|
|
/* NOTE: can free these now because mvl_vmd_nvl_add copied this info.*/
|
|||
|
|
chk_free (var_obj);
|
|||
|
|
chk_free (var_name_buf);
|
|||
|
|
return (ret);
|
|||
|
|
}
|
|||
|
|
/************************************************************************/
|
|||
|
|
/* scl2_dataset_create_all */
|
|||
|
|
/* Create all DataSets (SCL_DATASET) for this Logical Node (SCL_LN). */
|
|||
|
|
/* RETURNS: SD_SUCCESS or error code */
|
|||
|
|
/************************************************************************/
|
|||
|
|
static ST_RET scl2_dataset_create_all (
|
|||
|
|
MVL_VMD_CTRL *vmd_ctrl,
|
|||
|
|
SCL_LD *scl_ld,
|
|||
|
|
SCL_LN *scl_ln)
|
|||
|
|
{
|
|||
|
|
SCL_DATASET *scl_dataset;
|
|||
|
|
ST_RET ret;
|
|||
|
|
|
|||
|
|
ret = SD_SUCCESS; /* assume success */
|
|||
|
|
|
|||
|
|
/* NOTE: linked list is in reverse order from data in SCL file, */
|
|||
|
|
/* so get off list in reverse order. */
|
|||
|
|
for (scl_dataset = (SCL_DATASET *) list_find_last ((DBL_LNK *) scl_ln->datasetHead);
|
|||
|
|
scl_dataset != NULL;
|
|||
|
|
scl_dataset = (SCL_DATASET *) list_find_prev ((DBL_LNK *) scl_ln->datasetHead, (DBL_LNK *) scl_dataset))
|
|||
|
|
{
|
|||
|
|
ST_UINT32 mvl_debug_sel_save;
|
|||
|
|
|
|||
|
|
mvl_debug_sel_save = mvl_debug_sel;
|
|||
|
|
mvl_debug_sel &= ~MVLLOG_NERR;
|
|||
|
|
ret = scl2_dataset_create (vmd_ctrl, scl_ld, scl_ln, scl_dataset);
|
|||
|
|
mvl_debug_sel = mvl_debug_sel_save;
|
|||
|
|
if (ret)
|
|||
|
|
{
|
|||
|
|
if (!scl_debug_mode)
|
|||
|
|
break; /* if one fails, stop processing */
|
|||
|
|
|
|||
|
|
++scl_debug_mode_error_count;
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
SXLOG_FLOW3 ("scl2_dataset_create DONE for '%s' (LD=%s LN=%s)",
|
|||
|
|
scl_dataset->name, scl_ld->domName, scl_ln->varName);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return (ret);
|
|||
|
|
}
|
|||
|
|
/************************************************************************/
|
|||
|
|
/* init_basrcb_data */
|
|||
|
|
/* Use other info in scl_rcb to initialize data in basrcb. */
|
|||
|
|
/************************************************************************/
|
|||
|
|
static ST_VOID init_basrcb_data (
|
|||
|
|
MVLU_RPT_CTRL *rpt_ctrl,
|
|||
|
|
SCL_RCB *scl_rcb)
|
|||
|
|
{
|
|||
|
|
MVLU_BASRCB *basrcb;
|
|||
|
|
|
|||
|
|
basrcb = &rpt_ctrl->only_client.basrcb;
|
|||
|
|
|
|||
|
|
/* If RptID configured in SCL, overwrite default RptID generated by mvlu_create_rpt_ctrl */
|
|||
|
|
if (strlen (scl_rcb->rptID))
|
|||
|
|
strncpy_safe (basrcb->RptID, scl_rcb->rptID, sizeof (basrcb->RptID)-1);
|
|||
|
|
/* Use other info in scl_rcb to initialize data in basrcb */
|
|||
|
|
/* NOTE: scl_rcb->name, scl_rcb->desc not used. */
|
|||
|
|
basrcb->IntgPd = scl_rcb->intgPd;
|
|||
|
|
basrcb->BufTim = scl_rcb->bufTime;
|
|||
|
|
|
|||
|
|
basrcb->TrgOps.len = 6; /* BVstring. Assume max len*/
|
|||
|
|
basrcb->TrgOps.data [0] = scl_rcb->TrgOps [0];
|
|||
|
|
/*renxiaobao <20><><EFBFBD><EFBFBD>*/
|
|||
|
|
basrcb->TrgOps.data [0]|= TRGOPS_BITNUM_GENERAL_INTERROGATION;
|
|||
|
|
|
|||
|
|
basrcb->OptFlds.data_1 [0] = scl_rcb->OptFlds [0];
|
|||
|
|
basrcb->OptFlds.data_1 [1] = scl_rcb->OptFlds [1];
|
|||
|
|
if (!scl_rcb->buffered)
|
|||
|
|
{ /* For URCB, 61850-8-1 says to ignore buffer-overflow, entryID bits, so clear them*/
|
|||
|
|
BSTR_BIT_SET_OFF (basrcb->OptFlds.data_1, OPTFLD_BITNUM_BUFOVFL);
|
|||
|
|
BSTR_BIT_SET_OFF (basrcb->OptFlds.data_1, OPTFLD_BITNUM_ENTRYID);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
/************************************************************************/
|
|||
|
|
/* init_rpt_ctrl */
|
|||
|
|
/* Used only by loop in "scl2_rcb_create". */
|
|||
|
|
/************************************************************************/
|
|||
|
|
ST_RET init_rpt_ctrl (
|
|||
|
|
SCL_INFO *scl_info, /* main struct where all SCL info stored*/
|
|||
|
|
SCL_RCB *scl_rcb,
|
|||
|
|
MVL_NVLIST_CTRL *nvl,
|
|||
|
|
MVL_VAR_ASSOC *base_va,
|
|||
|
|
ST_CHAR *basrcb_var_namexx,
|
|||
|
|
ST_CHAR *basrcb_type_namexx,
|
|||
|
|
ST_UINT reportScanRate) /* report scan rate (millisec) */
|
|||
|
|
{
|
|||
|
|
ST_INT buftim_action;
|
|||
|
|
ST_INT set_flags; /* flags for mvlu_set_leaf_param_name */
|
|||
|
|
ST_RET ret;
|
|||
|
|
ST_CHAR leaf_name [MAX_IDENT_LEN + 1];
|
|||
|
|
MVLU_RPT_CTRL *rpt_ctrl;
|
|||
|
|
|
|||
|
|
/* DEBUG: nothing in SCL to set buftim_action. Is there another way to get this info? */
|
|||
|
|
buftim_action = MVLU_RPT_BUFTIM_SEND_NOW;
|
|||
|
|
|
|||
|
|
/* Set all leaf functions required to implement RCB.
|
|||
|
|
* NOTE: these leaf functions replace the default leaf functions
|
|||
|
|
* set by "u_set_all_leaf_functions" when the datatype was created.
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
/* Use same flags for all leafs */
|
|||
|
|
set_flags = MVLU_SET_RD_FUN | MVLU_SET_WR_FUN | MVLU_SET_REF;
|
|||
|
|
|
|||
|
|
/* Start with good return value & OR all returns together. If ANY call
|
|||
|
|
* to mvlu_set_leaf_param_name fails, this function will fail.
|
|||
|
|
*/
|
|||
|
|
ret = SD_SUCCESS;
|
|||
|
|
/* CRITICAL: len of basrcb_type_name chked above, so none of these "sprintf"s
|
|||
|
|
* will cause leaf_name to be exceeded.
|
|||
|
|
*/
|
|||
|
|
sprintf (leaf_name, "%s$RptID", basrcb_type_namexx);
|
|||
|
|
ret |= mvlu_set_leaf_param_name2 (base_va->type_id, set_flags,
|
|||
|
|
leaf_name,
|
|||
|
|
"mvlu_rptid_rd_ind_fun",
|
|||
|
|
"mvlu_rptid_wr_ind_fun",
|
|||
|
|
"");
|
|||
|
|
sprintf (leaf_name, "%s$RptEna", basrcb_type_namexx);
|
|||
|
|
ret |= mvlu_set_leaf_param_name2 (base_va->type_id, set_flags,
|
|||
|
|
leaf_name,
|
|||
|
|
"mvlu_rptena_rd_ind_fun",
|
|||
|
|
"mvlu_rptena_wr_ind_fun",
|
|||
|
|
"");
|
|||
|
|
if (!scl_rcb->buffered) /* "Resv" only for Unbuffered */
|
|||
|
|
{
|
|||
|
|
sprintf (leaf_name, "%s$Resv", basrcb_type_namexx);
|
|||
|
|
ret |= mvlu_set_leaf_param_name2 (base_va->type_id, set_flags,
|
|||
|
|
leaf_name,
|
|||
|
|
"mvlu_resv_rd_ind_fun",
|
|||
|
|
"mvlu_resv_wr_ind_fun",
|
|||
|
|
"");
|
|||
|
|
}
|
|||
|
|
sprintf (leaf_name, "%s$DatSet", basrcb_type_namexx);
|
|||
|
|
ret |= mvlu_set_leaf_param_name2 (base_va->type_id, set_flags,
|
|||
|
|
leaf_name,
|
|||
|
|
"mvlu_datsetna_rd_ind_fun",
|
|||
|
|
"mvl61850_datset_wr_ind",
|
|||
|
|
"");
|
|||
|
|
sprintf (leaf_name, "%s$ConfRev", basrcb_type_namexx);
|
|||
|
|
ret |= mvlu_set_leaf_param_name2 (base_va->type_id, set_flags,
|
|||
|
|
leaf_name,
|
|||
|
|
"mvlu_confrev_rd_ind",
|
|||
|
|
"u_no_write_allowed", /* NOT writable */
|
|||
|
|
"");
|
|||
|
|
sprintf (leaf_name, "%s$OptFlds", basrcb_type_namexx);
|
|||
|
|
ret |= mvlu_set_leaf_param_name2 (base_va->type_id, set_flags,
|
|||
|
|
leaf_name,
|
|||
|
|
"mvlu_optflds_rd_ind_fun",
|
|||
|
|
"mvlu_optflds_wr_ind_fun",
|
|||
|
|
"");
|
|||
|
|
sprintf (leaf_name, "%s$BufTm", basrcb_type_namexx);
|
|||
|
|
ret |= mvlu_set_leaf_param_name2 (base_va->type_id, set_flags,
|
|||
|
|
leaf_name,
|
|||
|
|
"mvlu_buftim_rd_ind_fun",
|
|||
|
|
"mvlu_buftim_wr_ind_fun",
|
|||
|
|
"");
|
|||
|
|
sprintf (leaf_name, "%s$SqNum", basrcb_type_namexx);
|
|||
|
|
if (scl_rcb->buffered)
|
|||
|
|
{ /* Buffered uses INT16U for SqNum */
|
|||
|
|
ret |= mvlu_set_leaf_param_name2 (base_va->type_id, set_flags,
|
|||
|
|
leaf_name,
|
|||
|
|
"mvlu_sqnum_int16u_rd_ind_fun",
|
|||
|
|
"u_no_write_allowed",
|
|||
|
|
"");
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{ /* Unbuffered uses INT8U for SqNum */
|
|||
|
|
ret |= mvlu_set_leaf_param_name2 (base_va->type_id, set_flags,
|
|||
|
|
leaf_name,
|
|||
|
|
"mvlu_sqnum_rd_ind_fun",
|
|||
|
|
"u_no_write_allowed",
|
|||
|
|
"");
|
|||
|
|
}
|
|||
|
|
sprintf (leaf_name, "%s$TrgOps", basrcb_type_namexx);
|
|||
|
|
ret |= mvlu_set_leaf_param_name2 (base_va->type_id, set_flags,
|
|||
|
|
leaf_name,
|
|||
|
|
"mvlu_trgops_rd_ind_fun",
|
|||
|
|
"mvlu_trgops_wr_ind_fun",
|
|||
|
|
"");
|
|||
|
|
sprintf (leaf_name, "%s$IntgPd", basrcb_type_namexx);
|
|||
|
|
ret |= mvlu_set_leaf_param_name2 (base_va->type_id, set_flags,
|
|||
|
|
leaf_name,
|
|||
|
|
"mvlu_intgpd_rd_ind_fun",
|
|||
|
|
"mvlu_intgpd_wr_ind_fun",
|
|||
|
|
"");
|
|||
|
|
sprintf (leaf_name, "%s$GI", basrcb_type_namexx);
|
|||
|
|
ret |= mvlu_set_leaf_param_name2 (base_va->type_id, set_flags,
|
|||
|
|
leaf_name,
|
|||
|
|
"mvlu_gi_rd_ind",
|
|||
|
|
"mvlu_gi_wr_ind",
|
|||
|
|
"");
|
|||
|
|
if (scl_rcb->buffered) /* only for Buffered */
|
|||
|
|
{
|
|||
|
|
sprintf (leaf_name, "%s$PurgeBuf", basrcb_type_namexx);
|
|||
|
|
ret |= mvlu_set_leaf_param_name2 (base_va->type_id, set_flags,
|
|||
|
|
leaf_name,
|
|||
|
|
"mvlu_purgebuf_rd_ind",
|
|||
|
|
"mvlu_purgebuf_wr_ind",
|
|||
|
|
"");
|
|||
|
|
sprintf (leaf_name, "%s$EntryID", basrcb_type_namexx);
|
|||
|
|
ret |= mvlu_set_leaf_param_name2 (base_va->type_id, set_flags,
|
|||
|
|
leaf_name,
|
|||
|
|
"mvlu_entryid_rd_ind",
|
|||
|
|
"mvlu_entryid_wr_ind",
|
|||
|
|
"");
|
|||
|
|
sprintf (leaf_name, "%s$TimeofEntry", basrcb_type_namexx);
|
|||
|
|
ret |= mvlu_set_leaf_param_name2 (base_va->type_id, set_flags,
|
|||
|
|
leaf_name,
|
|||
|
|
"mvlu_timeofentry_rd_ind",
|
|||
|
|
"u_no_write_allowed", /* NOT writable */
|
|||
|
|
"");
|
|||
|
|
} /* only for Buffered */
|
|||
|
|
|
|||
|
|
if (ret)
|
|||
|
|
{
|
|||
|
|
SXLOG_ERR1 ("At least one leaf function could not be found for RCB '%s'", basrcb_var_namexx);
|
|||
|
|
if (scl_debug_mode)
|
|||
|
|
{
|
|||
|
|
++scl_debug_mode_error_count;
|
|||
|
|
return (SD_SUCCESS); /* Don't let this stop us for now ... */
|
|||
|
|
}
|
|||
|
|
return (ret);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* NOTE: nvl may be NULL. If so, it must be set later when client writes DatSet.*/
|
|||
|
|
rpt_ctrl = mvl61850_create_rpt_ctrl (basrcb_var_namexx, nvl,
|
|||
|
|
base_va,
|
|||
|
|
(scl_rcb->buffered ? RCB_TYPE_IEC_BRCB : RCB_TYPE_IEC_URCB),
|
|||
|
|
buftim_action,
|
|||
|
|
scl_info->brcb_bufsize, /* BRCB bufsize */
|
|||
|
|
scl_rcb->confRev); /* ConfRev */
|
|||
|
|
if (rpt_ctrl)
|
|||
|
|
{
|
|||
|
|
init_basrcb_data (rpt_ctrl, scl_rcb); /* use scl_rcb to init data*/
|
|||
|
|
rpt_ctrl->scan_rate = (ST_DOUBLE) reportScanRate; /* init scan rate*/
|
|||
|
|
ret = SD_SUCCESS;
|
|||
|
|
/* renxiaobao <20><><EFBFBD><EFBFBD>*/
|
|||
|
|
if(scl_rcb->buffered==RCB_TYPE_IEC_BRCB) BRCB_NUM++;
|
|||
|
|
else URCB_NUM++;
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
ret = SD_FAILURE;
|
|||
|
|
return (ret);
|
|||
|
|
}
|
|||
|
|
/************************************************************************/
|
|||
|
|
/* scl2_rcb_create */
|
|||
|
|
/* RETURNS: SD_SUCCESS or error code */
|
|||
|
|
/************************************************************************/
|
|||
|
|
static ST_RET scl2_rcb_create (
|
|||
|
|
MVL_VMD_CTRL *vmd_ctrl,
|
|||
|
|
SCL_INFO *scl_info,
|
|||
|
|
SCL_LD *scl_ld,
|
|||
|
|
SCL_LN *scl_ln,
|
|||
|
|
SCL_RCB *scl_rcb,
|
|||
|
|
ST_UINT reportScanRate) /* report scan rate (millisec) */
|
|||
|
|
{
|
|||
|
|
ST_RET ret = SD_FAILURE;
|
|||
|
|
ST_CHAR basrcb_var_name [MAX_IDENT_LEN + 1];
|
|||
|
|
ST_CHAR basrcb_type_name [MAX_IDENT_LEN + 1]; /* for setting leaf functions*/
|
|||
|
|
ST_CHAR basrcb_var_namexx [MAX_IDENT_LEN + 1]; /* name plus 2 char index*/
|
|||
|
|
ST_CHAR basrcb_type_namexx [MAX_IDENT_LEN + 1]; /* name plus 2 char index*/
|
|||
|
|
MVL_VAR_ASSOC *base_va;
|
|||
|
|
OBJECT_NAME base_var_oname;
|
|||
|
|
OBJECT_NAME nvl_oname;
|
|||
|
|
MVL_NVLIST_CTRL *nvl;
|
|||
|
|
SCL_DATASET *scl_dataset;
|
|||
|
|
ST_CHAR nvl_name [MAX_IDENT_LEN+1];
|
|||
|
|
ST_INT index, maxClient;
|
|||
|
|
|
|||
|
|
/* Initialize basrcb_type_name. Add $BR.. or $RP.. later. */
|
|||
|
|
/* First part of name shouldn't matter now that init_rpt_ctrl uses */
|
|||
|
|
/* mvlu_set_leaf_param_name2 to set leaf params. Use simple name. */
|
|||
|
|
strcpy (basrcb_type_name, "b");
|
|||
|
|
|
|||
|
|
/* Generate basrcb_var_name from SCL info. The code below should generate a unique
|
|||
|
|
* basrcb_var_name for every RCB in this LN.
|
|||
|
|
*/
|
|||
|
|
strcpy (basrcb_var_name, scl_ln->varName); /* never too long */
|
|||
|
|
if (scl_rcb->buffered)
|
|||
|
|
{
|
|||
|
|
strncat_maxstrlen (basrcb_var_name, "$BR$", MAX_IDENT_LEN);
|
|||
|
|
strncat_maxstrlen (basrcb_type_name, "$BR$", MAX_IDENT_LEN);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
strncat_maxstrlen (basrcb_var_name, "$RP$", MAX_IDENT_LEN);
|
|||
|
|
strncat_maxstrlen (basrcb_type_name, "$RP$", MAX_IDENT_LEN);
|
|||
|
|
}
|
|||
|
|
/* Add RCB name (read from SCL file). */
|
|||
|
|
strncat_maxstrlen (basrcb_var_name, scl_rcb->name, MAX_IDENT_LEN);
|
|||
|
|
strncat_maxstrlen (basrcb_type_name, scl_rcb->name, MAX_IDENT_LEN);
|
|||
|
|
|
|||
|
|
/* Make sure there's room for 2 char "index" plus longest suffix */
|
|||
|
|
if (strlen (basrcb_var_name) + 2 + sizeof("$TimeOfEntry") > MAX_IDENT_LEN)
|
|||
|
|
{
|
|||
|
|
SXLOG_ERR1 ("BasRCB variable name '%s' too long", basrcb_var_name);
|
|||
|
|
return (SD_FAILURE);
|
|||
|
|
}
|
|||
|
|
if (strlen (basrcb_type_name) + 2 + sizeof("$TimeOfEntry") > MAX_IDENT_LEN)
|
|||
|
|
{
|
|||
|
|
SXLOG_ERR1 ("BasRCB type name '%s' too long", basrcb_type_name);
|
|||
|
|
return (SD_FAILURE);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* If "datSet" is not NULL, try to find it. */
|
|||
|
|
if (scl_rcb->datSet[0] != '\0')
|
|||
|
|
{
|
|||
|
|
scl_dataset = scl_find_dataset (scl_ln, scl_rcb->datSet);
|
|||
|
|
if (scl_dataset == NULL)
|
|||
|
|
{
|
|||
|
|
SXLOG_ERR1 ("scl2_rcb_create: datSet='%s' not found", scl_rcb->datSet);
|
|||
|
|
return (SD_FAILURE);
|
|||
|
|
}
|
|||
|
|
/* Construct NVL object name from LD, LN, and DataSet info. */
|
|||
|
|
if (strlen (scl_ln->prefix) + strlen (scl_ln->lnClass) + strlen (scl_ln->inst)
|
|||
|
|
+ strlen (scl_rcb->datSet) + 1 > MAX_IDENT_LEN)
|
|||
|
|
{
|
|||
|
|
SXLOG_ERR1 ("Constructed NVL name would be too long for dataset '%s'.", scl_rcb->datSet);
|
|||
|
|
return (SD_FAILURE);
|
|||
|
|
}
|
|||
|
|
sprintf (nvl_name, "%s%s%s$%s", scl_ln->prefix, scl_ln->lnClass, scl_ln->inst, scl_rcb->datSet);
|
|||
|
|
nvl_oname.object_tag = DOM_SPEC;
|
|||
|
|
nvl_oname.domain_id = scl_ld->domName; /* domain name */
|
|||
|
|
nvl_oname.obj_name.item_id = nvl_name; /* use constructed NVL name */
|
|||
|
|
nvl = mvl_vmd_find_nvl (vmd_ctrl, &nvl_oname,
|
|||
|
|
NULL); /* DOM_SPEC so net_info doesn't matter*/
|
|||
|
|
if (nvl == NULL)
|
|||
|
|
{
|
|||
|
|
SXLOG_ERR3 ("Cannot find NVL '%s' in domain '%s' to create RCB '%s'",
|
|||
|
|
nvl_oname.obj_name.item_id,
|
|||
|
|
nvl_oname.domain_id,
|
|||
|
|
basrcb_var_name);
|
|||
|
|
return (SD_FAILURE);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
nvl = NULL; /* datSet="" so can't find NVL */
|
|||
|
|
|
|||
|
|
/* Find the "base_va". Needed as arg to mvlu_create_rpt_ctrl. */
|
|||
|
|
base_var_oname.object_tag = DOM_SPEC;
|
|||
|
|
base_var_oname.domain_id = scl_ld->domName;
|
|||
|
|
|
|||
|
|
base_var_oname.obj_name.item_id = scl_ln->varName;
|
|||
|
|
base_va = mvl_vmd_find_var (vmd_ctrl, &base_var_oname,
|
|||
|
|
NULL); /* DOM_SPEC so net_info doesn't matter*/
|
|||
|
|
if (base_va == NULL)
|
|||
|
|
{
|
|||
|
|
SXLOG_ERR1 ("Cannot find base variable='%s' for RCB", scl_ln->varName);
|
|||
|
|
return (SD_FAILURE);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
assert (scl_rcb->maxClient > 0 && scl_rcb->maxClient <= 99); /* was checked during parsing */
|
|||
|
|
|
|||
|
|
maxClient = scl_rcb->maxClient; /* use value of "RptEnabled max" in SCL*/
|
|||
|
|
|
|||
|
|
/* Create "maxClient" RCBs, each with "index" as a suffix. */
|
|||
|
|
for (index = 1; index <= maxClient; index++)
|
|||
|
|
{
|
|||
|
|
if (rcb_name_no_index && maxClient == 1)
|
|||
|
|
{ /* do NOT add suffix */
|
|||
|
|
sprintf (basrcb_var_namexx, "%s", basrcb_var_name);
|
|||
|
|
sprintf (basrcb_type_namexx, "%s", basrcb_type_name);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
/* Add "index" suffix to var and type name. */
|
|||
|
|
sprintf (basrcb_var_namexx, "%s%02d", basrcb_var_name, index);
|
|||
|
|
sprintf (basrcb_type_namexx, "%s%02d", basrcb_type_name, index);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
ret = init_rpt_ctrl (scl_info, scl_rcb, nvl, base_va,
|
|||
|
|
basrcb_var_namexx, basrcb_type_namexx, reportScanRate);
|
|||
|
|
if (ret)
|
|||
|
|
break; /* stop looping on any error. */
|
|||
|
|
} /* end of loop */
|
|||
|
|
return (ret);
|
|||
|
|
}
|
|||
|
|
/************************************************************************/
|
|||
|
|
/* scl2_rcb_create_all */
|
|||
|
|
/* Create all Report Control Blocks (SCL_RCB) for this Logical Node (SCL_LN).*/
|
|||
|
|
/* RETURNS: SD_SUCCESS or error code */
|
|||
|
|
/************************************************************************/
|
|||
|
|
static ST_RET scl2_rcb_create_all (
|
|||
|
|
MVL_VMD_CTRL *vmd_ctrl,
|
|||
|
|
SCL_INFO *scl_info,
|
|||
|
|
SCL_LD *scl_ld,
|
|||
|
|
SCL_LN *scl_ln,
|
|||
|
|
ST_UINT reportScanRate) /* report scan rate (millisec) */
|
|||
|
|
{
|
|||
|
|
SCL_RCB *scl_rcb;
|
|||
|
|
ST_RET ret;
|
|||
|
|
|
|||
|
|
ret = SD_SUCCESS; /* assume success */
|
|||
|
|
|
|||
|
|
/* NOTE: linked list is in reverse order from data in SCL file, */
|
|||
|
|
/* so get off list in reverse order. */
|
|||
|
|
for (scl_rcb = (SCL_RCB *) list_find_last ((DBL_LNK *) scl_ln->rcbHead);
|
|||
|
|
scl_rcb != NULL;
|
|||
|
|
scl_rcb = (SCL_RCB *) list_find_prev ((DBL_LNK *) scl_ln->rcbHead, (DBL_LNK *) scl_rcb))
|
|||
|
|
{
|
|||
|
|
ret = scl2_rcb_create (vmd_ctrl, scl_info, scl_ld, scl_ln, scl_rcb,
|
|||
|
|
reportScanRate);
|
|||
|
|
if (ret)
|
|||
|
|
break; /* if one fails, stop processing */
|
|||
|
|
else
|
|||
|
|
SXLOG_FLOW3 ("scl2_rcb_create DONE for '%s' (LD=%s LN=%s)",
|
|||
|
|
scl_rcb->name, scl_ld->domName, scl_ln->varName);
|
|||
|
|
}
|
|||
|
|
return (ret);
|
|||
|
|
}
|
|||
|
|
/************************************************************************/
|
|||
|
|
/* convert_initial_value */
|
|||
|
|
/* NOTE: This function does special conversion for "Enum" and "Boolean" */
|
|||
|
|
/* values. For all other types, it just calls sxaTextToLocal. */
|
|||
|
|
/************************************************************************/
|
|||
|
|
ST_RET convert_initial_value (SCL_INFO *scl_info,
|
|||
|
|
SCL_LD *scl_ld,
|
|||
|
|
SCL_LN *scl_ln,
|
|||
|
|
ST_CHAR *flattened_name, /* flattened variable name */
|
|||
|
|
RUNTIME_CTRL *rt_ctrl,
|
|||
|
|
ST_INT comp_rt_index, /* index to this comp in array */
|
|||
|
|
ST_VOID *data, /* Pointer to converted data */
|
|||
|
|
ST_CHAR *valstring) /* string to be converted. */
|
|||
|
|
{
|
|||
|
|
ST_RET ret = SD_SUCCESS;
|
|||
|
|
RUNTIME_TYPE *comp_rt_type;
|
|||
|
|
SCL_ENUMTYPE *scl_enumtype;
|
|||
|
|
RESERVED_INFO *reserved_info; /* ptr to array of extra type info. */
|
|||
|
|
short i;
|
|||
|
|
char *ptr0,*ptr1;
|
|||
|
|
|
|||
|
|
/* Cast "reserved_1" pointer to the type we allocated. */
|
|||
|
|
reserved_info = (RESERVED_INFO *) rt_ctrl->reserved_1;
|
|||
|
|
|
|||
|
|
comp_rt_type = rt_ctrl->rt_first + comp_rt_index;
|
|||
|
|
scl_enumtype = reserved_info[comp_rt_index].scl_enumtype; /* may be NULL*/
|
|||
|
|
|
|||
|
|
/* Component must be primitive. */
|
|||
|
|
assert (ms_is_rt_prim (comp_rt_type));
|
|||
|
|
if (comp_rt_type->el_tag == RT_INTEGER && scl_enumtype) /* must be Enum */
|
|||
|
|
{
|
|||
|
|
/* Try to match text with EnumType definition.*/
|
|||
|
|
SCL_ENUMVAL *scl_enumval;
|
|||
|
|
ST_BOOLEAN found = SD_FALSE;
|
|||
|
|
for (scl_enumval = (SCL_ENUMVAL *) list_find_last ((DBL_LNK *) scl_enumtype->enumvalHead);
|
|||
|
|
scl_enumval != NULL;
|
|||
|
|
scl_enumval = (SCL_ENUMVAL *) list_find_prev ((DBL_LNK *) scl_enumtype->enumvalHead, (DBL_LNK *) scl_enumval))
|
|||
|
|
{
|
|||
|
|
if (strcmp (scl_enumval->EnumVal, valstring) == 0)
|
|||
|
|
{
|
|||
|
|
*(ST_INT8*)data = (ST_INT8) scl_enumval->ord;
|
|||
|
|
found = SD_TRUE;
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if (!found)
|
|||
|
|
{
|
|||
|
|
SXLOG_ERR3("%s$%s does not match any EnumVal. Trying numeric conversion. %s",scl_ln->varName,flattened_name,valstring);
|
|||
|
|
/* SXLOG_ERR1 ("Val='$s' does not match any EnumVal. Trying numeric conversion.",valstring);*/
|
|||
|
|
/* This is not required by IEC 61850, but if valstring does not match */
|
|||
|
|
/* any EnumVal and it starts with a digit, do numeric conversion. */
|
|||
|
|
if (isdigit (valstring[0]))
|
|||
|
|
ret = sxaTextToLocal (valstring, data, 1, /* rt_num always 1 (primitive) */
|
|||
|
|
comp_rt_type);
|
|||
|
|
else
|
|||
|
|
ret = SD_FAILURE;
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else if (comp_rt_type->el_tag == RT_BOOL)
|
|||
|
|
{ /* convert boolean (may be "0", "1", "true", or "false") */
|
|||
|
|
if (strcmp (valstring, "true") == 0 || strcmp (valstring, "1") == 0)
|
|||
|
|
*(ST_INT8 *)data = 1;
|
|||
|
|
else if (strcmp (valstring, "false") == 0 || strcmp (valstring, "0") == 0)
|
|||
|
|
*(ST_INT8 *)data = 0;
|
|||
|
|
else
|
|||
|
|
ret = SD_FAILURE;
|
|||
|
|
}
|
|||
|
|
/* Do special checking for visible strings. */
|
|||
|
|
else if (comp_rt_type->el_tag == RT_VISIBLE_STRING)
|
|||
|
|
{
|
|||
|
|
ST_CHAR *ptr;
|
|||
|
|
/* Visible string must not contain any control chars. */
|
|||
|
|
for (ptr = valstring; *ptr!=0; ptr++)
|
|||
|
|
{
|
|||
|
|
if (*ptr < ' ') /* Check that character is not a control char. */
|
|||
|
|
{
|
|||
|
|
SXLOG_ERR1 ("Illegal character (0x%02x) in visible string",
|
|||
|
|
(ST_UINT)(ST_UCHAR) *ptr); /* 2 casts to avoid sign extension*/
|
|||
|
|
ret = SD_FAILURE;
|
|||
|
|
break; /* stop on first error */
|
|||
|
|
return SD_SUCCESS; /*renxiaobao da<64><61>ʼ<EFBFBD><CABC>*/
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if (ret == SD_SUCCESS)
|
|||
|
|
{
|
|||
|
|
if (comp_rt_type->u.p.el_len < 0 /* variable len */
|
|||
|
|
&& strlen (valstring) > (size_t) abs (comp_rt_type->u.p.el_len))
|
|||
|
|
{
|
|||
|
|
SXLOG_ERR2 ("String '%s' exceeds max len '%d'",
|
|||
|
|
valstring, abs (comp_rt_type->u.p.el_len));
|
|||
|
|
ret = SD_FAILURE;
|
|||
|
|
return SD_SUCCESS; /*renxiaobao da<64><61>ʼ<EFBFBD><CABC>*/
|
|||
|
|
}
|
|||
|
|
else if (comp_rt_type->u.p.el_len >= 0 /* fixed len */
|
|||
|
|
&& strlen (valstring) != (size_t) comp_rt_type->u.p.el_len)
|
|||
|
|
{
|
|||
|
|
/* Shouldn't get here because no fixed len in SCL, but just in case..*/
|
|||
|
|
SXLOG_ERR2 ("String '%s' does not match fixed len '%d'",
|
|||
|
|
valstring, comp_rt_type->u.p.el_len);
|
|||
|
|
ret = SD_FAILURE;
|
|||
|
|
return SD_SUCCESS; /*renxiaobao da<64><61>ʼ<EFBFBD><CABC>*/
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
strcpy (data, valstring); /* all is ok, so copy string*/
|
|||
|
|
}
|
|||
|
|
} /* end RT_VISIBLE_STRING */
|
|||
|
|
/*renxiaobao UTF8<46><38><EFBFBD><EFBFBD>*/
|
|||
|
|
else if (comp_rt_type->el_tag == RT_UTF8_STRING)
|
|||
|
|
{
|
|||
|
|
ptr0 = data;
|
|||
|
|
ptr1 = valstring;
|
|||
|
|
for(i=0;((i<comp_rt_type->el_size)&&ptr1[i]);i++)
|
|||
|
|
{
|
|||
|
|
ptr0[i] = ptr1[i];
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{ /* handle all other element types */
|
|||
|
|
ret = sxaTextToLocal (valstring, data, 1, /* rt_num always 1 (primitive) */
|
|||
|
|
comp_rt_type);
|
|||
|
|
}
|
|||
|
|
if (ret)
|
|||
|
|
{ /* Our conversion code failed. Let the user try. */
|
|||
|
|
SCL2_IV_TRANSLATE_CTRL translate_ctrl;
|
|||
|
|
memset (&translate_ctrl, 0, sizeof (SCL2_IV_TRANSLATE_CTRL));
|
|||
|
|
|
|||
|
|
translate_ctrl.scl_info = scl_info;
|
|||
|
|
translate_ctrl.scl_ld = scl_ld;
|
|||
|
|
translate_ctrl.scl_ln = scl_ln;
|
|||
|
|
translate_ctrl.valText = valstring;
|
|||
|
|
translate_ctrl.attrib = flattened_name;
|
|||
|
|
translate_ctrl.dest = data;
|
|||
|
|
translate_ctrl.numRt = 1; /* always 1 (primitive) */
|
|||
|
|
translate_ctrl.rtHead = comp_rt_type;
|
|||
|
|
|
|||
|
|
ret = u_mvl_scl_set_initial_value (&translate_ctrl);/* call user funct*/
|
|||
|
|
}
|
|||
|
|
return (ret);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/************************************************************************/
|
|||
|
|
/* scl2_dai_set_value */
|
|||
|
|
/* Set initial data value from DAI input from SCL file. */
|
|||
|
|
/* Use sxaTextToLocal to convert the text to data. */
|
|||
|
|
/* RETURNS: SD_SUCCESS or error code */
|
|||
|
|
/************************************************************************/
|
|||
|
|
int sAddr_num=0,sAddr_num_bak;
|
|||
|
|
static ST_RET scl2_dai_set_value (SCL_INFO *scl_info,
|
|||
|
|
SCL_LD *scl_ld,
|
|||
|
|
SCL_LN *scl_ln,
|
|||
|
|
SCL_DAI *scl_dai)
|
|||
|
|
{
|
|||
|
|
ST_CHAR fixed_name [MAX_IDENT_LEN +1];
|
|||
|
|
ST_CHAR fixed_name1 [MAX_IDENT_LEN +1];
|
|||
|
|
ST_CHAR leaf_name [MAX_IDENT_LEN +1];
|
|||
|
|
ST_RET ret = SD_FAILURE;
|
|||
|
|
MVL_VAR_ASSOC *var; /* MVL Variable Association created from LN info*/
|
|||
|
|
RUNTIME_TYPE *comp_rt_type; /* first rt_type of component */
|
|||
|
|
RUNTIME_TYPE *comp_rt_type1=0;
|
|||
|
|
ST_INT comp_rt_num; /* num of rt_types in component */
|
|||
|
|
ST_UINT32 mvl_debug_sel_save;
|
|||
|
|
MVL_TYPE_CTRL *type_ctrl;
|
|||
|
|
DATA_MAP_LINK *data_map;
|
|||
|
|
ST_CHAR *LeafToFind;
|
|||
|
|
ST_CHAR leafname [MAX_IDENT_LEN +1];
|
|||
|
|
unsigned char *pdat;
|
|||
|
|
|
|||
|
|
/* It seems strange but "Val" is optional for DAI (i.e. Instantiated */
|
|||
|
|
/* Data Attribute with no value). If "Val" not present, do nothing. */
|
|||
|
|
/* //if (scl_dai->Val == NULL)
|
|||
|
|
//{
|
|||
|
|
// SXLOG_FLOW2 ("scl2_dai_set_value: 'Val' not present for attribute '%s' in '%s'. Do nothing.",
|
|||
|
|
// scl_dai->flattened, scl_ln->varName);
|
|||
|
|
// return (SD_SUCCESS); // nothing to do, so stop now
|
|||
|
|
//}*/
|
|||
|
|
|
|||
|
|
var = scl_ln->mvl_var_assoc;
|
|||
|
|
if (strlen (scl_dai->flattened) <= (MAX_IDENT_LEN-3))
|
|||
|
|
{
|
|||
|
|
/* Turn off false NERR log messages */
|
|||
|
|
mvl_debug_sel_save = mvl_debug_sel;
|
|||
|
|
mvl_debug_sel &= ~MVLLOG_NERR;
|
|||
|
|
|
|||
|
|
/* Find the attribute type. Don't know the FC, so try all possible */
|
|||
|
|
/* FC (i.e."ST" "MX" "CO" "SP" "SG" "SE" "SV" "CF" "DC" or "EX"). */
|
|||
|
|
do /* use "do-while" loop only so we can break out on success*/
|
|||
|
|
{
|
|||
|
|
strcpy (fixed_name, "ST$"); /* Try "ST" first. */
|
|||
|
|
strcat (fixed_name, scl_dai->flattened);
|
|||
|
|
if ((ret = mvlu_find_comp_type (var->type_id, fixed_name, &comp_rt_type, &comp_rt_num)) == SD_SUCCESS)
|
|||
|
|
break; /* found it. done */
|
|||
|
|
|
|||
|
|
strncpy (fixed_name, "MX", 2); /* replace FC prefix*/
|
|||
|
|
if ((ret = mvlu_find_comp_type (var->type_id, fixed_name, &comp_rt_type, &comp_rt_num)) == SD_SUCCESS)
|
|||
|
|
break; /* found it. done */
|
|||
|
|
|
|||
|
|
strncpy (fixed_name, "CO", 2); /* replace prefix*/
|
|||
|
|
if ((ret = mvlu_find_comp_type (var->type_id, fixed_name, &comp_rt_type, &comp_rt_num)) == SD_SUCCESS)
|
|||
|
|
break; /* found it. done */
|
|||
|
|
|
|||
|
|
strncpy (fixed_name, "SP", 2); /* replace prefix*/
|
|||
|
|
if ((ret = mvlu_find_comp_type (var->type_id, fixed_name, &comp_rt_type, &comp_rt_num)) == SD_SUCCESS)
|
|||
|
|
break; /* found it. done */
|
|||
|
|
|
|||
|
|
strncpy (fixed_name, "SG", 2); /* replace prefix*/
|
|||
|
|
if ((ret = mvlu_find_comp_type (var->type_id, fixed_name, &comp_rt_type, &comp_rt_num)) == SD_SUCCESS)
|
|||
|
|
{
|
|||
|
|
/*renxiaobao <20><><EFBFBD><EFBFBD>ӳ<EFBFBD><D3B3>*/
|
|||
|
|
strcpy (fixed_name1, "SE$"); /* Try "ST" first. */
|
|||
|
|
strcat (fixed_name1, scl_dai->flattened);
|
|||
|
|
if ((ret = mvlu_find_comp_type (var->type_id, fixed_name1, &comp_rt_type1, &comp_rt_num)) != SD_SUCCESS)
|
|||
|
|
{
|
|||
|
|
printf("%s mvlu_find_comp_type error...\n",fixed_name1);
|
|||
|
|
}
|
|||
|
|
break; /* found it. done */
|
|||
|
|
}
|
|||
|
|
/*
|
|||
|
|
//strncpy (fixed_name, "SE", 2); // replace prefix
|
|||
|
|
//if ((ret = mvlu_find_comp_type (var->type_id, fixed_name, &comp_rt_type, &comp_rt_num)) == SD_SUCCESS)
|
|||
|
|
//break; found it. done
|
|||
|
|
*/
|
|||
|
|
strncpy (fixed_name, "SV", 2); /* replace prefix*/
|
|||
|
|
if ((ret = mvlu_find_comp_type (var->type_id, fixed_name, &comp_rt_type, &comp_rt_num)) == SD_SUCCESS)
|
|||
|
|
break; /* found it. done */
|
|||
|
|
|
|||
|
|
strncpy (fixed_name, "CF", 2); /* replace prefix*/
|
|||
|
|
if ((ret = mvlu_find_comp_type (var->type_id, fixed_name, &comp_rt_type, &comp_rt_num)) == SD_SUCCESS)
|
|||
|
|
break; /* found it. done */
|
|||
|
|
|
|||
|
|
strncpy (fixed_name, "DC", 2); /* replace prefix*/
|
|||
|
|
if ((ret = mvlu_find_comp_type (var->type_id, fixed_name, &comp_rt_type, &comp_rt_num)) == SD_SUCCESS)
|
|||
|
|
break; /* found it. done */
|
|||
|
|
|
|||
|
|
strncpy (fixed_name, "EX", 2); /* replace prefix*/
|
|||
|
|
if ((ret = mvlu_find_comp_type (var->type_id, fixed_name, &comp_rt_type, &comp_rt_num)) == SD_SUCCESS)
|
|||
|
|
break; /* found it. done */
|
|||
|
|
} while (0); /* end of do-while loop */
|
|||
|
|
|
|||
|
|
/* Restore MVL log mask settings */
|
|||
|
|
mvl_debug_sel = mvl_debug_sel_save;
|
|||
|
|
}
|
|||
|
|
/*renxiaobao <20><><EFBFBD><EFBFBD>ӳ<EFBFBD><D3B3>*/
|
|||
|
|
if(strlen(scl_dai->sAddr))
|
|||
|
|
{
|
|||
|
|
sAddr_num++;
|
|||
|
|
sAddr_num_bak = sAddr_num;
|
|||
|
|
sprintf(leaf_name,"%s$%s",scl_ln->varName,fixed_name);
|
|||
|
|
data_map = chk_calloc (1, sizeof (DATA_MAP_LINK));
|
|||
|
|
|
|||
|
|
// logprint("%s",leaf_name);
|
|||
|
|
|
|||
|
|
strcpy(data_map->leaf,leaf_name);
|
|||
|
|
strcpy(data_map->ldevice,scl_ld->domName);
|
|||
|
|
strcpy(data_map->usr_data_info,scl_dai->sAddr);
|
|||
|
|
list_add_last (&DATA_MAP_saddr, data_map);
|
|||
|
|
strcpy(leafname,leaf_name);
|
|||
|
|
LeafToFind = strstr(leafname,"$SG$");
|
|||
|
|
if(LeafToFind)
|
|||
|
|
{
|
|||
|
|
strncpy(LeafToFind,"$SE$",4);
|
|||
|
|
sAddr_num++;
|
|||
|
|
data_map = chk_calloc (1, sizeof (DATA_MAP_LINK));
|
|||
|
|
strcpy(data_map->leaf,leafname);
|
|||
|
|
strcpy(data_map->ldevice,scl_ld->domName);
|
|||
|
|
strcpy(data_map->usr_data_info,scl_dai->sAddr);
|
|||
|
|
list_add_last (&DATA_MAP_saddr, data_map);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
/* Tried all possible FC. Did we find a match. */
|
|||
|
|
if (ret != SD_SUCCESS)
|
|||
|
|
{
|
|||
|
|
SXLOG_ERR2 ("scl2_dai_set_value: Attribute '%s' in '%s' not found.",
|
|||
|
|
scl_dai->flattened, scl_ln->varName);
|
|||
|
|
if (strchr (scl_dai->flattened, '[') != NULL)
|
|||
|
|
SXLOG_CERR0 ("Initialization of array elements not yet supported.");
|
|||
|
|
}
|
|||
|
|
else if (comp_rt_num !=1)
|
|||
|
|
{
|
|||
|
|
SXLOG_ERR2 ("scl2_dai_set_value: Attribute '%s' in '%s' not a primitive type.",
|
|||
|
|
scl_dai->flattened, scl_ln->varName);
|
|||
|
|
ret = SD_FAILURE;
|
|||
|
|
}
|
|||
|
|
if(ret == SD_SUCCESS)
|
|||
|
|
{
|
|||
|
|
/*renxiaobao <20><><EFBFBD><EFBFBD>ӳ<EFBFBD><D3B3>*/
|
|||
|
|
if(strlen(scl_dai->sAddr))
|
|||
|
|
{
|
|||
|
|
pdat = (unsigned char*)(var->data)+comp_rt_type->mvluTypeInfo.offSet;
|
|||
|
|
*pdat = ((sAddr_num_bak-1)/1000)+0x80;
|
|||
|
|
if(scl_dai->Val != NULL)
|
|||
|
|
{
|
|||
|
|
printf("warning:scl_dai sAddr and Val both exist(%s %s)...",scl_dai->sAddr,scl_dai->Val);
|
|||
|
|
}
|
|||
|
|
if(comp_rt_type1)
|
|||
|
|
{
|
|||
|
|
pdat = (unsigned char*)(var->data)+comp_rt_type1->mvluTypeInfo.offSet;
|
|||
|
|
*pdat = ((sAddr_num-1)/1000)+0x80;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else if(scl_dai->Val != NULL)
|
|||
|
|
{
|
|||
|
|
type_ctrl = mvl_type_ctrl_find (var->type_id);
|
|||
|
|
ret = convert_initial_value (scl_info, scl_ld, scl_ln, fixed_name,
|
|||
|
|
type_ctrl->rt_ctrl,
|
|||
|
|
comp_rt_type - type_ctrl->rt_ctrl->rt_first, /* index to this comp*/
|
|||
|
|
(ST_INT8*)(var->data)+comp_rt_type->mvluTypeInfo.offSet,
|
|||
|
|
scl_dai->Val);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if (ret == SD_SUCCESS)
|
|||
|
|
{
|
|||
|
|
SXLOG_FLOW3 ("scl2_dai_set_value: Val '%s' conversion for '%s$%s' SUCCESSFUL",
|
|||
|
|
scl_dai->Val, scl_ln->varName, fixed_name);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
SXLOG_ERR3 ("scl2_dai_set_value: Val '%s' conversion for '%s$%s' FAILED",
|
|||
|
|
scl_dai->Val, scl_ln->varName, fixed_name);
|
|||
|
|
printf ("scl2_dai_set_value: Val '%s' conversion for '%s$%s' FAILED",scl_dai->Val, scl_ln->varName, fixed_name);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (scl_debug_mode)
|
|||
|
|
{
|
|||
|
|
if (ret != SD_SUCCESS)
|
|||
|
|
++scl_debug_mode_error_count;
|
|||
|
|
|
|||
|
|
return (SD_SUCCESS); /* Don't let this stop us for now ... */
|
|||
|
|
}
|
|||
|
|
return (ret);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/************************************************************************/
|
|||
|
|
/* scl2_da_or_bda_set_value_all */
|
|||
|
|
/* NOTE: the same Val text might get converted many times by this */
|
|||
|
|
/* function, but not enough info available to do conversion before now. */
|
|||
|
|
/************************************************************************/
|
|||
|
|
static ST_RET scl2_da_or_bda_set_value_all (SCL_INFO *scl_info,
|
|||
|
|
SCL_LD *scl_ld,
|
|||
|
|
SCL_LN *scl_ln)
|
|||
|
|
{
|
|||
|
|
ST_RET ret;
|
|||
|
|
MVL_TYPE_CTRL *type_ctrl;
|
|||
|
|
RUNTIME_TYPE *rt_type;
|
|||
|
|
ST_INT rt_idx; /* index into RUNTIME_TYPE array. */
|
|||
|
|
RESERVED_INFO *reserved_info; /* ptr to array of extra type info. */
|
|||
|
|
|
|||
|
|
ret = SD_SUCCESS; /* assume success */
|
|||
|
|
|
|||
|
|
/* If this var could not be created, don't crash */
|
|||
|
|
if (scl_ln->mvl_var_assoc == NULL)
|
|||
|
|
{
|
|||
|
|
if (scl_debug_mode)
|
|||
|
|
{
|
|||
|
|
if (ret != SD_SUCCESS)
|
|||
|
|
++scl_debug_mode_error_count;
|
|||
|
|
|
|||
|
|
return (SD_SUCCESS); /* Don't let this stop us for now ... */
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
type_ctrl = mvl_type_ctrl_find (scl_ln->mvl_var_assoc->type_id);
|
|||
|
|
assert (type_ctrl);
|
|||
|
|
/* Cast "reserved_1" pointer to the type we allocated. */
|
|||
|
|
reserved_info = (RESERVED_INFO *) type_ctrl->rt_ctrl->reserved_1;
|
|||
|
|
|
|||
|
|
/* Loop through all elements of RUNTIME_TYPE array */
|
|||
|
|
for (rt_idx = 0, rt_type = type_ctrl->rt_ctrl->rt_first;
|
|||
|
|
rt_idx < type_ctrl->rt_ctrl->rt_num;
|
|||
|
|
rt_idx++, rt_type++)
|
|||
|
|
{
|
|||
|
|
ST_CHAR *Val = reserved_info[rt_idx].Val;
|
|||
|
|
|
|||
|
|
if (Val) /* string to convert*/
|
|||
|
|
{
|
|||
|
|
/* Convert text to value & store in Variable. */
|
|||
|
|
ret = convert_initial_value (scl_info, scl_ld, scl_ln,
|
|||
|
|
NULL, /* don't know flattened_name in this case. */
|
|||
|
|
type_ctrl->rt_ctrl,
|
|||
|
|
rt_idx,
|
|||
|
|
(ST_INT8*)(scl_ln->mvl_var_assoc->data) + rt_type->mvluTypeInfo.offSet,
|
|||
|
|
Val); /* string to convert*/
|
|||
|
|
if (ret != SD_SUCCESS)
|
|||
|
|
{
|
|||
|
|
SXLOG_ERR2 ("Cannot convert DA or BDA Val='%s' to data for '%s' attribute.",
|
|||
|
|
Val, ms_comp_name_find(rt_type));
|
|||
|
|
break; /* stop now */
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return (ret);
|
|||
|
|
}
|
|||
|
|
/************************************************************************/
|
|||
|
|
/* scl2_dai_set_value_all */
|
|||
|
|
/* Initialize data for all DAI in this logical node. */
|
|||
|
|
/* RETURNS: SD_SUCCESS or error code */
|
|||
|
|
/************************************************************************/
|
|||
|
|
static ST_RET scl2_dai_set_value_all (SCL_INFO *scl_info,
|
|||
|
|
SCL_LD *scl_ld,
|
|||
|
|
SCL_LN *scl_ln)
|
|||
|
|
{
|
|||
|
|
SCL_DAI *scl_dai;
|
|||
|
|
ST_RET ret;
|
|||
|
|
|
|||
|
|
ret = SD_SUCCESS; /* assume success */
|
|||
|
|
|
|||
|
|
/* NOTE: linked list is in reverse order from data in SCL file, */
|
|||
|
|
/* so get off list in reverse order. */
|
|||
|
|
for (scl_dai = (SCL_DAI *) list_find_last ((DBL_LNK *) scl_ln->daiHead);
|
|||
|
|
scl_dai != NULL;
|
|||
|
|
scl_dai = (SCL_DAI *) list_find_prev ((DBL_LNK *) scl_ln->daiHead, (DBL_LNK *) scl_dai))
|
|||
|
|
{
|
|||
|
|
ret = scl2_dai_set_value (scl_info, scl_ld, scl_ln, scl_dai);
|
|||
|
|
if (ret)
|
|||
|
|
{
|
|||
|
|
SXLOG_ERR3 ("scl2_dai_set_value FAILED for '%s' (LD=%s LN=%s)",
|
|||
|
|
scl_dai->flattened, scl_ld->domName, scl_ln->varName);
|
|||
|
|
break; /* if one fails, stop processing */
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
SXLOG_FLOW3 ("scl2_dai_set_value DONE for '%s' (LD=%s LN=%s)",
|
|||
|
|
scl_dai->flattened, scl_ld->domName, scl_ln->varName);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
ret = SD_SUCCESS; /*renxiaobao20221014 */
|
|||
|
|
return (ret);
|
|||
|
|
}
|
|||
|
|
/************************************************************************/
|
|||
|
|
/* scl2_ld_create_part1 */
|
|||
|
|
/* Create Domain and all variables in it. */
|
|||
|
|
/* RETURNS: SD_SUCCESS or error code */
|
|||
|
|
/************************************************************************/
|
|||
|
|
static ST_RET scl2_ld_create_part1 (
|
|||
|
|
MVL_VMD_CTRL *vmd_ctrl,
|
|||
|
|
SCL_INFO *scl_info, /* main struct where all SCL info stored*/
|
|||
|
|
SCL_LD *scl_ld)
|
|||
|
|
{
|
|||
|
|
ST_RET ret = SD_SUCCESS; /* Assume success */
|
|||
|
|
MVL_DOM_CTRL *dom_ctrl;
|
|||
|
|
ST_INT max_num_var; /* compute from scl_ld info */
|
|||
|
|
ST_INT max_num_nvl;
|
|||
|
|
ST_INT max_num_jou;
|
|||
|
|
SCL_LN *scl_ln;
|
|||
|
|
|
|||
|
|
/* Make sure there's room for all Logical Nodes (variables) defined in SCL.*/
|
|||
|
|
/* plus room for the configured maximum number of dynamic variables. */
|
|||
|
|
max_num_var = list_get_sizeof (scl_ld->lnHead) + mvl_max_dyn.dom_vars;
|
|||
|
|
max_num_nvl = mvl_max_dyn.dom_nvls;
|
|||
|
|
max_num_jou = mvl_max_dyn.journals;
|
|||
|
|
|
|||
|
|
/* First create MMS domain */
|
|||
|
|
dom_ctrl = mvl_vmd_dom_add (vmd_ctrl,
|
|||
|
|
scl_ld->domName, /* domain name */
|
|||
|
|
max_num_var,
|
|||
|
|
max_num_nvl,
|
|||
|
|
max_num_jou,
|
|||
|
|
SD_TRUE); /* always copy name to dom_ctrl */
|
|||
|
|
if (!dom_ctrl)
|
|||
|
|
ret = SD_FAILURE;
|
|||
|
|
|
|||
|
|
/* If MMS domain created successfully, create all MMS variables (logical nodes). */
|
|||
|
|
if (ret == SD_SUCCESS)
|
|||
|
|
{
|
|||
|
|
/* NOTE: linked list is in reverse order from data in SCL file, */
|
|||
|
|
/* so get off list in reverse order. */
|
|||
|
|
for (scl_ln = (SCL_LN *) list_find_last ((DBL_LNK *) scl_ld->lnHead);
|
|||
|
|
scl_ln != NULL;
|
|||
|
|
scl_ln = (SCL_LN *) list_find_prev ((DBL_LNK *) scl_ld->lnHead, (DBL_LNK *) scl_ln))
|
|||
|
|
{
|
|||
|
|
ret = scl2_ln_create (vmd_ctrl, scl_info, scl_ld, scl_ln);
|
|||
|
|
if (ret)
|
|||
|
|
{
|
|||
|
|
SXLOG_ERR1 ("scl2_ln_create FAILED for %s", scl_ln->varName);
|
|||
|
|
break; /* if one fails, stop processing */
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
SXLOG_FLOW1 ("scl2_ln_create for %s SUCCESSFUL", scl_ln->varName);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return (ret);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/************************************************************************/
|
|||
|
|
/* scl2_ld_create_part2 */
|
|||
|
|
/* Create all NVLs and RCBs in this domain. */
|
|||
|
|
/* NOTE: this cannot be done in "scl2_ld_create_part1" because NVL */
|
|||
|
|
/* may include VAR from another Domain that was not defined yet. */
|
|||
|
|
/* RETURNS: SD_SUCCESS or error code */
|
|||
|
|
/************************************************************************/
|
|||
|
|
static ST_RET scl2_ld_create_part2 (
|
|||
|
|
MVL_VMD_CTRL *vmd_ctrl,
|
|||
|
|
SCL_INFO *scl_info, /* main struct where all SCL info stored*/
|
|||
|
|
SCL_LD *scl_ld,
|
|||
|
|
ST_UINT reportScanRate, /* report scan rate (millisec) */
|
|||
|
|
ST_BOOLEAN is_client) /* see description in scl2_ld_create_all*/
|
|||
|
|
{
|
|||
|
|
ST_RET ret = SD_SUCCESS; /* Assume success */
|
|||
|
|
SCL_LN *scl_ln;
|
|||
|
|
|
|||
|
|
/* Create all MMS NVLs (DataSets).
|
|||
|
|
* IEC 61850 defines a DataSet "inside" a Logical Node.
|
|||
|
|
* This should seem a little strange, because
|
|||
|
|
* a Logical Node maps to a MMS Variable, and a DataSet maps to a
|
|||
|
|
* MMS Named Variable List, and of course, a Named Variable List CANNOT
|
|||
|
|
* be "inside" of a Variable.
|
|||
|
|
* This is resolved by simply creating a normal Named Variable List, and just
|
|||
|
|
* adding it to the same Domain as the Variable.
|
|||
|
|
* CRITICAL: THIS IS DONE AFTER ALL VARIABLES (LOGICAL NODES) CREATED BECAUSE
|
|||
|
|
* DATASET MAY REFERENCE OTHER LOGICAL NODES.
|
|||
|
|
*/
|
|||
|
|
if (ret == SD_SUCCESS)
|
|||
|
|
{
|
|||
|
|
/* NOTE: linked list is in reverse order from data in SCL file, */
|
|||
|
|
/* so get off list in reverse order. */
|
|||
|
|
for (scl_ln = (SCL_LN *) list_find_last ((DBL_LNK *) scl_ld->lnHead);
|
|||
|
|
scl_ln != NULL;
|
|||
|
|
scl_ln = (SCL_LN *) list_find_prev ((DBL_LNK *) scl_ld->lnHead, (DBL_LNK *) scl_ln))
|
|||
|
|
{
|
|||
|
|
ret = scl2_dataset_create_all (vmd_ctrl, scl_ld, scl_ln);
|
|||
|
|
if (ret)
|
|||
|
|
{
|
|||
|
|
break; /* if one fails, stop processing (error already logged) */
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (is_client)
|
|||
|
|
{
|
|||
|
|
SXLOG_FLOW0 ("Client configuration. Control blocks not created.");
|
|||
|
|
return (ret); /* Stop now. Do NOT create RCB, LCB, etc. */
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* If all successful up to here, create all RCBs. */
|
|||
|
|
if (ret == SD_SUCCESS)
|
|||
|
|
{
|
|||
|
|
/* NOTE: linked list is in reverse order from data in SCL file, */
|
|||
|
|
/* so get off list in reverse order. */
|
|||
|
|
for (scl_ln = (SCL_LN *) list_find_last ((DBL_LNK *) scl_ld->lnHead);
|
|||
|
|
scl_ln != NULL;
|
|||
|
|
scl_ln = (SCL_LN *) list_find_prev ((DBL_LNK *) scl_ld->lnHead, (DBL_LNK *) scl_ln))
|
|||
|
|
{
|
|||
|
|
ret = scl2_rcb_create_all (vmd_ctrl, scl_info, scl_ld, scl_ln, reportScanRate);
|
|||
|
|
if (ret)
|
|||
|
|
{
|
|||
|
|
break; /* if one fails, stop processing (error already logged) */
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
/* If all successful up to here, set initial values from DA & BDA. */
|
|||
|
|
if (ret == SD_SUCCESS)
|
|||
|
|
{
|
|||
|
|
/* NOTE: linked list is in reverse order from data in SCL file, */
|
|||
|
|
/* so get off list in reverse order. */
|
|||
|
|
for (scl_ln = (SCL_LN *) list_find_last ((DBL_LNK *) scl_ld->lnHead);
|
|||
|
|
scl_ln != NULL;
|
|||
|
|
scl_ln = (SCL_LN *) list_find_prev ((DBL_LNK *) scl_ld->lnHead, (DBL_LNK *) scl_ln))
|
|||
|
|
{
|
|||
|
|
ret = scl2_da_or_bda_set_value_all (scl_info, scl_ld, scl_ln);
|
|||
|
|
if (ret)
|
|||
|
|
{
|
|||
|
|
break; /* if one fails, stop processing (error already logged) */
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* If all successful up to here, set initial data values from */
|
|||
|
|
/* DOI/SDI/DAI entries found in the SCL file. */
|
|||
|
|
if (ret == SD_SUCCESS)
|
|||
|
|
{
|
|||
|
|
/* NOTE: linked list is in reverse order from data in SCL file, */
|
|||
|
|
/* so get off list in reverse order. */
|
|||
|
|
for (scl_ln = (SCL_LN *) list_find_last ((DBL_LNK *) scl_ld->lnHead);
|
|||
|
|
scl_ln != NULL;
|
|||
|
|
scl_ln = (SCL_LN *) list_find_prev ((DBL_LNK *) scl_ld->lnHead, (DBL_LNK *) scl_ln))
|
|||
|
|
{
|
|||
|
|
ret = scl2_dai_set_value_all (scl_info, scl_ld, scl_ln);
|
|||
|
|
if (ret)
|
|||
|
|
{
|
|||
|
|
break; /* if one fails, stop processing (error already logged) */
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return (ret);
|
|||
|
|
}
|
|||
|
|
/************************************************************************/
|
|||
|
|
/* scl2_ld_create_all */
|
|||
|
|
/* Create all Logical Devices from SCL info saved in "scl_info". */
|
|||
|
|
/* NOTE: must be done in 2 parts, because NVLs created in part2, may */
|
|||
|
|
/* reference Vars from different domains created in part1. */
|
|||
|
|
/* RETURNS: SD_SUCCESS or error code */
|
|||
|
|
/************************************************************************/
|
|||
|
|
ST_RET scl2_ld_create_all (
|
|||
|
|
MVL_VMD_CTRL *vmd_ctrl, /* VMD in which to add Logical Devices. */
|
|||
|
|
SCL_INFO *scl_info, /* main struct where all SCL info stored*/
|
|||
|
|
ST_UINT reportScanRate, /* report scan rate (millisec) */
|
|||
|
|
ST_INT brcb_bufsize, /* BRCB buffer size */
|
|||
|
|
ST_BOOLEAN is_client) /* If this flag is set, Client model is */
|
|||
|
|
/* created (i.e. Control Blocks NOT created).*/
|
|||
|
|
{
|
|||
|
|
ST_RET ret;
|
|||
|
|
SCL_LD *scl_ld;
|
|||
|
|
|
|||
|
|
/* CRITICAL: Don't allow this to be called again for the same SCL_INFO struct.*/
|
|||
|
|
/* That would cause duplicate objects. */
|
|||
|
|
if (scl_info->ld_create_done)
|
|||
|
|
{
|
|||
|
|
SXLOG_ERR0 ("Logical Devices already created for this SCL file. Cannot create again.");
|
|||
|
|
return (SD_FAILURE);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
scl_info->ld_create_done = SD_TRUE; /* Set flag now */
|
|||
|
|
|
|||
|
|
/* Save BRCB buffer size in scl_info so lower functions can access it.*/
|
|||
|
|
scl_info->brcb_bufsize = brcb_bufsize;
|
|||
|
|
|
|||
|
|
ret = SD_SUCCESS; /* default ret val: returned if LD linked list empty.*/
|
|||
|
|
|
|||
|
|
/* NOTE: linked list is in reverse order from data in SCL file, */
|
|||
|
|
/* so get off list in reverse order. */
|
|||
|
|
for (scl_ld = (SCL_LD *) list_find_last ((DBL_LNK *) scl_info->ldHead);
|
|||
|
|
scl_ld != NULL;
|
|||
|
|
scl_ld = (SCL_LD *) list_find_prev ((DBL_LNK *) scl_info->ldHead, (DBL_LNK *) scl_ld))
|
|||
|
|
{
|
|||
|
|
ret = scl2_ld_create_part1 (vmd_ctrl, scl_info, scl_ld);
|
|||
|
|
if (ret)
|
|||
|
|
{
|
|||
|
|
SXLOG_ERR1 ("scl2_ld_create_part1 for '%s' FAILED", scl_ld->domName);
|
|||
|
|
break; /* if one fails, stop processing */
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
SXLOG_FLOW1 ("scl2_ld_create_part1 for '%s' SUCCESSFUL", scl_ld->domName);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
/* NOTE: linked list is in reverse order from data in SCL file, */
|
|||
|
|
/* so get off list in reverse order. */
|
|||
|
|
for (scl_ld = (SCL_LD *) list_find_last ((DBL_LNK *) scl_info->ldHead);
|
|||
|
|
scl_ld != NULL;
|
|||
|
|
scl_ld = (SCL_LD *) list_find_prev ((DBL_LNK *) scl_info->ldHead, (DBL_LNK *) scl_ld))
|
|||
|
|
{
|
|||
|
|
ret = scl2_ld_create_part2 (vmd_ctrl, scl_info, scl_ld, reportScanRate, is_client);
|
|||
|
|
if (ret)
|
|||
|
|
{
|
|||
|
|
SXLOG_ERR1 ("scl2_ld_create_part2 for '%s' FAILED", scl_ld->domName);
|
|||
|
|
if (!scl_debug_mode)
|
|||
|
|
break; /* if one fails, stop processing */
|
|||
|
|
|
|||
|
|
++scl_debug_mode_error_count;
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
SXLOG_FLOW1 ("scl2_ld_create_part2 for '%s' SUCCESSFUL", scl_ld->domName);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
/* Create "LastApplError" variable (required if "Control with */
|
|||
|
|
/* enhanced security" supported). */
|
|||
|
|
if (ret == SD_SUCCESS)
|
|||
|
|
ret = mvl61850_ctl_lastapplerror_create ();
|
|||
|
|
|
|||
|
|
/* The reserved_1 member of every RUNTIME_CTRL is must now be freed. */
|
|||
|
|
reserved_free_all (vmd_ctrl);
|
|||
|
|
return (ret);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/************************************************************************/
|
|||
|
|
/* scl_gse_find */
|
|||
|
|
/* Find addressing info for this GCB in this LD. */
|
|||
|
|
/************************************************************************/
|
|||
|
|
SCL_GSE *scl_gse_find (SCL_INFO *scl_info, SCL_LD *scl_ld, SCL_GCB *scl_gcb)
|
|||
|
|
{
|
|||
|
|
SCL_SUBNET *scl_subnet;
|
|||
|
|
SCL_CAP *scl_cap;
|
|||
|
|
SCL_GSE *scl_gse;
|
|||
|
|
|
|||
|
|
for (scl_subnet = (SCL_SUBNET *) list_find_last ((DBL_LNK *) scl_info->subnetHead);
|
|||
|
|
scl_subnet != NULL;
|
|||
|
|
scl_subnet = (SCL_SUBNET *) list_find_prev ((DBL_LNK *) scl_info->subnetHead, (DBL_LNK *) scl_subnet))
|
|||
|
|
{
|
|||
|
|
for (scl_cap = (SCL_CAP *) list_find_last ((DBL_LNK *) scl_subnet->capHead);
|
|||
|
|
scl_cap != NULL;
|
|||
|
|
scl_cap = (SCL_CAP *) list_find_prev ((DBL_LNK *) scl_subnet->capHead, (DBL_LNK *) scl_cap))
|
|||
|
|
{
|
|||
|
|
if (strcmp (scl_info->iedName, scl_cap->iedName) == 0)
|
|||
|
|
{ /* found matching iedName */
|
|||
|
|
for (scl_gse = (SCL_GSE *) list_find_last ((DBL_LNK *) scl_cap->gseHead);
|
|||
|
|
scl_gse != NULL;
|
|||
|
|
scl_gse = (SCL_GSE *) list_find_prev ((DBL_LNK *) scl_cap->gseHead, (DBL_LNK *) scl_gse))
|
|||
|
|
{
|
|||
|
|
if (strcmp (scl_ld->inst, scl_gse->ldInst) == 0 &&
|
|||
|
|
strcmp (scl_gcb->name, scl_gse->cbName) == 0)
|
|||
|
|
{ /* Found it. Too many loops to break out of, so just return.*/
|
|||
|
|
return (scl_gse);
|
|||
|
|
}
|
|||
|
|
} /* end scl_gse loop */
|
|||
|
|
}
|
|||
|
|
} /* end scl_cap loop */
|
|||
|
|
} /* end scl_subnet loop */
|
|||
|
|
|
|||
|
|
return (NULL); /* Not found */
|
|||
|
|
}
|
|||
|
|
|