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

3287 lines
126 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

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

/************************************************************************/
/* SISCO SOFTWARE MODULE HEADER *****************************************/
/************************************************************************/
/* (c) Copyright Systems Integration Specialists Company, Inc., */
/* 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 */
}