/************************************************************************/ /* 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 element of or 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 数据映射*/ 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 定值 */ 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 增加*/ 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 报告*/ 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初始化*/ } } 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初始化*/ } 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初始化*/ } else strcpy (data, valstring); /* all is ok, so copy string*/ } } /* end RT_VISIBLE_STRING */ /*renxiaobao UTF8编码*/ else if (comp_rt_type->el_tag == RT_UTF8_STRING) { ptr0 = data; ptr1 = valstring; for(i=0;((iel_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 数据映射*/ 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 数据映射*/ 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 数据映射*/ 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 */ }