/************************************************************************/ /* SISCO SOFTWARE MODULE HEADER *****************************************/ /************************************************************************/ /* (c) Copyright Systems Integration Specialists Company, Inc., */ /* 2004-2005, All Rights Reserved. */ /* */ /* PROPRIETARY AND CONFIDENTIAL */ /* */ /* MODULE NAME : mvl61850_ctl.c */ /* PRODUCT(S) : MMSEASE */ /* */ /* MODULE DESCRIPTION : */ /* IEC 61850 Control Model Server functions. */ /* */ /* GLOBAL FUNCTIONS DEFINED IN THIS MODULE : */ /* mvl61850_sbo_create_sboname */ /* mvl61850_ctl_chk_sbo */ /* mvl61850_ctl_chk_sbow */ /* mvl61850_ctl_chk_state */ /* */ /* MODIFICATION LOG : */ /* Date Who Rev Comments */ /* -------- --- ------ ------------------------------------------- */ /* 07/23/08 JRB 17 Fix creation of CntrlObj string again. */ /* Fix CntrlObj size (129 char) in TDL. */ /* Send AddCause=3(select-failed) if already selected*/ /* 07/02/08 JRB 16 Repl cmd_executing w/ ctlState (more flexible)*/ /* Make sure SBO name <= MVL61850_MAX_OBJREF_LEN.*/ /* Fix CntrlObj string. Del unused var. */ /* 05/14/08 JRB 15 mvl61850_ctl_req_done: first chk (va!=NULL). */ /* 05/08/08 JRB 14 Handle sboClass=operate-many. For sbo-with- */ /* enhanced-sec, don't unselect until CmdTerm. */ /* 05/08/08 JRB 13 mvl61850_ctl_chk_state: set LastApplError. */ /* 05/06/08 JRB 12 Don't allow re-select until after command or */ /* sboTimeout (use new getSboCtrl function). */ /* Set default Error/AddCause if not already set*/ /* 03/03/08 JRB 11 Add mvl61850_ctl_req_done & fix it to NOT send*/ /* LastApplError for Oper if normal security. */ /* Find and save sboClass to use later. */ /* 02/26/07 JRB 10 initSboCtrl: add use_ms_timer arg. */ /* 02/21/07 JRB 09 Fix comment. */ /* 11/21/06 JRB 08 Add mvl61850_ctl_lastapplerror_send. */ /* 10/30/06 JRB 07 Use new mvl_vmd_* object handling functions. */ /* Fix check that ALL of Oper and ONLY Oper is */ /* being written. */ /* 04/17/06 JRB 06 Do NOT create LastApplError var at startup, */ /* but create temporary var only when needed. */ /* 09/12/05 JRB 05 Add mvl61850_ctl_chk_sbow. */ /* Add NERR log if Loc$stVal==TRUE. */ /* Chk ctlModel before SBO read or SBOw write. */ /* 07/11/05 JRB 04 Add mvl61850_ctl_command_termination. */ /* Add mvl61850_ctl_lastapplerror_create. */ /* 03/28/05 JRB 03 Don't allow SBO read if Loc$stVal==TRUE. */ /* 01/27/05 JRB 02 Del unused variable. */ /* 12/10/04 JRB 01 New. Moved 61850 Control code from mvlu_sbo.c*/ /* to this module. */ /* Add mvl61850_ctl_chk_sbo, mvl61850_ctl_chk_state.*/ /* Del leaf funct mvl61850_sbo_select_rd_ind. */ /************************************************************************/ #include "glbtypes.h" #include "sysincs.h" #include "glbsem.h" #include "mmsdefs.h" #include "mvl_uca.h" #include "mvl_log.h" /************************************************************************/ /* For debug version, use a static pointer to avoid duplication of */ /* __FILE__ strings. */ #ifdef DEBUG_SISCO SD_CONST static ST_CHAR *SD_CONST thisFileName = __FILE__; #endif /************************************************************************/ /* getSboCtrl */ /* RETURNS: pointer to a structure from the global pool "sbo_pool" */ /* (same pool used by "initSboCtrl" for UCA controls). */ /* NOTE: On error, this function returns NULL. If LastApplError!=NULL, */ /* it also sets the Error and AddCause members of LastApplError. */ /************************************************************************/ static MVL_SBO_CTRL *getSboCtrl (MVL_NET_INFO *net_info, ST_CHAR *sbo_name, ST_INT8 ctlModel, ST_UINT32 sboTimeout, ST_INT8 sboClass, MVL61850_LAST_APPL_ERROR *LastApplError) { ST_INT i; MVL_SBO_CTRL *sboCtrl; /* Stop now if name is too long (strcpy later would not be safe). */ if (strlen (sbo_name) >= sizeof (sboCtrl->sbo_var)) { /* should NEVER happen */ MVL_LOG_ERR1 ("Error finding IEC 61850 Control object '%s'. Name too long",sbo_name); return (NULL); } /* See if the element is already selected. */ sboCtrl = sbo_pool; for (i = 0; i < MAX_NUM_SBO_PEND; ++i, ++sboCtrl) { if (sboCtrl->in_use == SD_TRUE && !strcmp (sboCtrl->sbo_var, sbo_name)) { /* Already selected. Do not allow re-select until after timeout. */ /* DEBUG: change this to use defines for Error and AddCause? */ if (LastApplError != NULL) { LastApplError->Error = 1; /* Unknown */ if (sboCtrl->ctlState > MVL61850_CTLSTATE_UNSELECTED) LastApplError->AddCause = 12; /* Command-already-in-execution */ else LastApplError->AddCause = 3; /* Select-failed */ } return (NULL); } } /* The protected element is not selected, find unused SBO control */ sboCtrl = sbo_pool; for (i = 0; i < MAX_NUM_SBO_PEND; ++i, ++sboCtrl) { if (sboCtrl->in_use == SD_FALSE) break; } if (i >= MAX_NUM_SBO_PEND) { if (LastApplError != NULL) { LastApplError->Error = 1; /* Unknown */ LastApplError->AddCause = 0; /* Unknown */ } return (NULL); } /* Found available SBO control, set the parameters. */ memset (sboCtrl, 0, sizeof (MVL_SBO_CTRL)); /* start w/ clean struct*/ sboCtrl->in_use = SD_TRUE; sboCtrl->use_ms_timer = SD_TRUE; /* ALWAYS "ms" timer for 61850 */ sboCtrl->expire_time_ms = sGetMsTime () + sboTimeout; sboCtrl->net_info = net_info; /* NOTE: sbo_name length checked above, so this strcpy is safe. */ strcpy (sboCtrl->sbo_var, sbo_name); sboCtrl->ctlModel = ctlModel; sboCtrl->sboClass = sboClass; return (sboCtrl); } /************************************************************************/ /* mvl61850_sbo_create_sboname */ /* Creates a name to return when an IEC 61850 SBO attribute is read. */ /* sboName must point to buffer of at least [MVL61850_MAX_OBJREF_LEN+1] char*/ /* NOTE: this name may also be passed to "mvlu_sbo_chk_state" */ /* to find the correct MVL_SBO_CTRL struct. */ /************************************************************************/ ST_VOID mvl61850_sbo_create_sboname (MVL_VAR_ASSOC *va, MVL_SCOPE *va_scope, ST_CHAR *sboName) { ST_CHAR *tmp_ptr; /* use to find last '$' in var name */ /* Create ObjectReference for this variable. */ if (mvl61850_objref_create (va->name, va_scope, sboName) != SD_SUCCESS) { /* should NEVER happen */ MVL_LOG_ERR0 ("Can't generate SBO ObjectReference"); sboName [0] = '\0'; /* return empty string */ return; } /* Replace end of var name with name of protected element (Oper). */ /* Var name may end with Oper, SBO, Cancel, etc. Replace with "Oper". */ /* This is a waste if it already ends with "Oper", but it takes more */ /* code to check. */ if ((tmp_ptr = strrchr (sboName, '$')) != NULL && /* find last '$'*/ tmp_ptr - sboName + 5 <= MVL61850_MAX_OBJREF_LEN) /* room for Oper*/ strcpy (tmp_ptr+1, "Oper"); /* Write name of protected element (Oper) AFTER it.*/ else { /* should NEVER happen */ MVL_LOG_ERR1 ("Can't generate SBO ObjectReference from '%s' (too long to replace last comp with 'Oper')", sboName); sboName [0] = '\0'; /* return empty string */ } } /************************************************************************/ /* mvl61850_mkname_ctlmodel */ /* Generate the "flattened" name of the "ctlModel" attribute that */ /* corresponds to the attribute passed as "var_name" arg. */ /* The var_name arg should always be of the following form: */ /* LNodeName$CO$yyy$Oper */ /* LNodeName$CO$yyy$SBO, etc. */ /* This funct generates a name like this ("LNodeName$" stripped off): */ /* CF$yyy$ctlModel */ /************************************************************************/ ST_RET mvl61850_mkname_ctlmodel (ST_CHAR *var_name, ST_CHAR *flatname, size_t flatname_len) { ST_RET retcode = SD_FAILURE; ST_CHAR *firstdollar; /* ptr to first '$' in var name */ ST_CHAR *lastdollar; /* ptr to last '$' in var name */ ST_INT prefixlen; /* len of first half of generated string */ ST_CHAR *suffix = "ctlModel"; firstdollar = strchr (var_name, '$'); /* find first '$' */ lastdollar = strrchr (var_name, '$'); /* find last '$' */ if (firstdollar && lastdollar) { prefixlen = lastdollar - firstdollar; if (prefixlen + strlen (suffix) <= flatname_len) { strcpy (flatname, firstdollar+1); /* Copy text after first '$'*/ strncpy (flatname, "CF", 2); /* Replace CO at start with CF */ strcpy (flatname + prefixlen, suffix); /* Write new suffix after last '$'*/ retcode = SD_SUCCESS; } } return (retcode); } /************************************************************************/ /* mvl61850_mkname_sbotimeout */ /* Generate the "flattened" name of the "sboTimeout" attribute that */ /* corresponds to the attribute passed as "var_name" arg. */ /* The var_name arg should always be of the following form: */ /* LNodeName$CO$yyy$SBO */ /* This funct generates a name like this ("LNodeName$" stripped off): */ /* CF$yyy$sboTimeout */ /************************************************************************/ ST_RET mvl61850_mkname_sbotimeout (ST_CHAR *var_name, ST_CHAR *flatname, size_t flatname_len) { ST_CHAR *firstdollar; /* ptr to first '$' in var name */ ST_CHAR *lastdollar; /* ptr to last '$' in var name */ ST_INT prefixlen; /* len of first half of generated string */ ST_CHAR *suffix = "sboTimeout"; /* replacement text to put after last '$'*/ ST_RET retcode = SD_FAILURE; firstdollar = strchr (var_name, '$'); /* find first '$' */ lastdollar = strrchr (var_name, '$'); /* find last '$' */ if (firstdollar && lastdollar) { prefixlen = lastdollar - firstdollar; if (prefixlen + strlen (suffix) <= flatname_len) /* room to replace*/ { strcpy (flatname, firstdollar+1); /* Copy text after first '$'*/ strncpy (flatname, "CF", 2); /* Replace CO at start with CF */ strcpy (flatname + prefixlen, suffix); /* Write new suffix after last '$'*/ retcode = SD_SUCCESS; } } return (retcode); } /************************************************************************/ /* mvl61850_mkname_sboclass */ /* Generate the "flattened" name of the "sboClass" attribute that */ /* corresponds to the attribute passed as "var_name" arg. */ /* The var_name arg should always be of the following form: */ /* LNodeName$CO$yyy$SBO */ /* This funct generates a name like this ("LNodeName$" stripped off): */ /* CF$yyy$sboClass */ /************************************************************************/ ST_RET mvl61850_mkname_sboclass (ST_CHAR *var_name, ST_CHAR *flatname, size_t flatname_len) { ST_CHAR *firstdollar; /* ptr to first '$' in var name */ ST_CHAR *lastdollar; /* ptr to last '$' in var name */ ST_INT prefixlen; /* len of first half of generated string */ ST_CHAR *suffix = "sboClass"; /* replacement text to put after last '$'*/ ST_RET retcode = SD_FAILURE; firstdollar = strchr (var_name, '$'); /* find first '$' */ lastdollar = strrchr (var_name, '$'); /* find last '$' */ if (firstdollar && lastdollar) { prefixlen = lastdollar - firstdollar; if (prefixlen + strlen (suffix) <= flatname_len) /* room to replace*/ { strcpy (flatname, firstdollar+1); /* Copy text after first '$'*/ strncpy (flatname, "CF", 2); /* Replace CO at start with CF */ strcpy (flatname + prefixlen, suffix); /* Write new suffix after last '$'*/ retcode = SD_SUCCESS; } } return (retcode); } /************************************************************************/ /* mvl61850_sboclass_find */ /* Find the corresponding 'sboClass' attribute for this variable and */ /* read its current value. */ /************************************************************************/ static ST_INT8 mvl61850_sboclass_find (MVL_VAR_ASSOC *va) { ST_CHAR sboClassName [MAX_IDENT_LEN+1]; /* "CF$...$sboClass" */ ST_INT8 sboClass; if (mvl61850_mkname_sboclass (va->name, sboClassName, MAX_IDENT_LEN) != SD_SUCCESS || mvlu_get_leaf_val_int8 (va->base_va, sboClassName, &sboClass) != SD_SUCCESS) sboClass = MVL61850_SBOCLASS_OPERATE_ONCE; /* set default value */ return (sboClass); } /************************************************************************/ /* mvl61850_ctl_chk_sbo */ /* This function should be called from "leaf" function when IEC 61850 */ /* "SBO" attribute is being read (i.e. performing Control Model 'Select'*/ /* Service). It checks if client is allowed to perform 'Select'. */ /* If 'Select' allowed, it reserves a MVL_SBO_CTRL struct and returns */ /* a ptr to it. */ /* RETURNS: ptr to struct if successful (sbo_var member of struct */ /* contains name to send in read response). */ /* NULL if failed. */ /* CRITICAL: if return is NOT NULL & caller decides not to allow */ /* 'Select', must pass this ptr to mvlu_sbo_ctrl_free. */ /************************************************************************/ MVL_SBO_CTRL *mvl61850_ctl_chk_sbo (MVLU_RD_VA_CTRL *mvluRdVaCtrl) { MVL_VAR_ASSOC *va; ST_CHAR sboName[MVL61850_MAX_OBJREF_LEN+1]; MVL_SBO_CTRL *sboCtrl = NULL; ST_CHAR *lastdollar; /* ptr to last '$' in var name */ /* Init sboTimeout=0. If ..$CF$..$sboTimeout==0, or not found, set to default val.*/ ST_UINT32 sboTimeout = 0; /* SBO timeout value. */ ST_CHAR sboTimeoutName [MAX_IDENT_LEN+1]; /* "CF$...$sboTimeout" */ ST_BOOLEAN loc_stval; ST_INT8 sboClass; /* First take care of timeout housekeeping */ mvlu_sbo_chk_timers (); /* find the name of the attribute being selected */ va = mvluRdVaCtrl->rdVaCtrl->va; /* Check value of "ST$Loc$stVal". */ if (mvlu_get_leaf_val_boolean (va->base_va, "ST$Loc$stVal", &loc_stval)!=SD_SUCCESS) loc_stval = SD_FALSE; /* if can't read ST$Loc$stVal, assume FALSE*/ if (loc_stval) { MVL_LOG_NERR1 ("Reading of '%s' not allowed: Local mode is set.", va->name); return (NULL); /* error return */ } /* Only perform "Select" if SBO is ONLY attribute being read. */ /* Check this by seeing if variable name ends with "$SBO". */ /* Point to where "$SBO" should be, then see if it's there. */ lastdollar = strrchr (va->name, '$'); /* find last '$' */ if (lastdollar != NULL && strcmp (lastdollar+1, "SBO") == 0) { ST_INT8 ctlModel; ST_CHAR flatname [MAX_IDENT_LEN + 1]; /* attribute to find */ /* Generate "ctlModel" leaf name and try to read its value. */ /* CRITICAL: this only works if va->name checked first (i.e. it ends in "$SBO")*/ if (mvl61850_mkname_ctlmodel (va->name, flatname, MAX_IDENT_LEN) != SD_SUCCESS || mvlu_get_leaf_val_int8 (va->base_va, flatname, &ctlModel) != SD_SUCCESS) ctlModel = MVL61850_CTLMODEL_STATUS_ONLY; /* can't read ctlModel. assume status-only*/ if (ctlModel != MVL61850_CTLMODEL_SBO_NORMAL) { MVL_LOG_NERR3 ("Reading of '%s' not allowed: ctlModel=%d, expected ctlModel=%d (sbo-with-normal-security)", va->name, ctlModel, MVL61850_CTLMODEL_SBO_NORMAL); return (NULL); /* error return */ } /* Create sboName from var name. */ //mvl61850_sbo_create_sboname (va, &mvluRdVaCtrl->rdVaCtrl->va_scope, sboName);//lnk编译删除,原前置程序未使用 /* Find "sboTimeout" value. */ if (mvl61850_mkname_sbotimeout (va->name, sboTimeoutName, MAX_IDENT_LEN) != SD_SUCCESS || mvlu_get_leaf_val_uint32 (va->base_va, sboTimeoutName, &sboTimeout) != SD_SUCCESS || sboTimeout==0) /* ..$CF$..$sboTimeout==0, or not found */ sboTimeout = SBO_SELECT_TIMEOUT; /* set timeout to default val */ /* Find "sboClass" value. */ sboClass = mvl61850_sboclass_find (va); /* Get a SBO control element */ sboCtrl = getSboCtrl (mvluRdVaCtrl->indCtrl->event->net_info,sboName, ctlModel, sboTimeout, sboClass, NULL); /* LastApplError not used*/ } return (sboCtrl); } /************************************************************************/ /* mvl61850_ctl_chk_sbow */ /* This function should be called from "leaf" function when IEC 61850 */ /* an attribute inside the "SBOw" structure is being written */ /* (i.e. performing Control Model 'Select' Service for */ /* sbo-with-enhanced-security). */ /* It checks if client is allowed to perform 'Select'. */ /* If 'Select' allowed, it reserves a MVL_SBO_CTRL struct and returns */ /* a ptr to it. */ /* RETURNS: ptr to struct if successful (sbo_var member of struct */ /* contains name to send in read response). */ /* NULL if failed. */ /* CRITICAL: if return is NOT NULL & caller decides not to allow */ /* 'Select', caller must pass this ptr to mvlu_sbo_ctrl_free. */ /* NOTE: this is very similar to mvl61850_ctl_chk_sbo, but it is used */ /* when writing "SBOw" instead of when reading "SBO". */ /************************************************************************/ MVL_SBO_CTRL *mvl61850_ctl_chk_sbow (MVLU_WR_VA_CTRL *mvluWrVaCtrl) { MVL_VAR_ASSOC *va; ST_CHAR sboName[MVL61850_MAX_OBJREF_LEN+1]; MVL_SBO_CTRL *sboCtrl = NULL; ST_CHAR *lastdollar; /* ptr to last '$' in var name */ /* Init sboTimeout=0. If ..$CF$..$sboTimeout==0, or not found, set to default val.*/ ST_UINT32 sboTimeout = 0; /* SBO timeout value. */ ST_CHAR sboTimeoutName [MAX_IDENT_LEN+1]; /* "CF$...$sboTimeout" */ ST_BOOLEAN loc_stval; ST_INT8 sboClass; /* First take care of timeout housekeeping */ mvlu_sbo_chk_timers (); va = mvluWrVaCtrl->wrVaCtrl->va; /* Check value of "ST$Loc$stVal". */ if (mvlu_get_leaf_val_boolean (va->base_va, "ST$Loc$stVal", &loc_stval)!=SD_SUCCESS) { loc_stval = SD_FALSE; /* if can't read ST$Loc$stVal, assume FALSE*/ } if (loc_stval) { /* renxiaobao 控制*/ mvluWrVaCtrl->wrVaCtrl->LastApplError.Error = 1; /* Unknown */ mvluWrVaCtrl->wrVaCtrl->LastApplError.AddCause = Blocked_CB; /* Blocked-by-switching-hierarchy */ MVL_LOG_NERR1 ("Writing of '%s' not allowed: Local mode is set.", va->name); return (NULL); /* error return */ } /* Only perform "Select" if entire SBOw is written & nothing else.*/ /* Check this by seeing if variable name ends with "$SBOw". */ /* Point to where "$SBOw" should be, then see if it's there. */ lastdollar = strrchr (va->name, '$'); /* find last '$' */ if (lastdollar != NULL && strcmp (lastdollar+1, "SBOw") == 0) { ST_INT8 ctlModel; ST_CHAR flatname [MAX_IDENT_LEN + 1]; /* attribute to find */ /* Generate "ctlModel" leaf name and try to read its value. */ /* CRITICAL: this only works if va->name checked first (i.e. it ends in "$SBOw")*/ if (mvl61850_mkname_ctlmodel (va->name, flatname, MAX_IDENT_LEN) != SD_SUCCESS || mvlu_get_leaf_val_int8 (va->base_va, flatname, &ctlModel) != SD_SUCCESS) { ctlModel = MVL61850_CTLMODEL_STATUS_ONLY; /* can't read ctlModel. assume status-only*/ } /*renxiaobao 控制 // if (ctlModel != MVL61850_CTLMODEL_SBO_ENHANCED)*/ if ((ctlModel != MVL61850_CTLMODEL_SBO_ENHANCED)&&(ctlModel != MVL61850_CTLMODEL_SBO_NORMAL)) { /* renxiaobao 控制*/ mvluWrVaCtrl->wrVaCtrl->LastApplError.Error = 1; /* Unknown */ mvluWrVaCtrl->wrVaCtrl->LastApplError.AddCause = Blocked_by_Mode; /* Blocked-by-Mode */ MVL_LOG_NERR3 ("Writing of '%s' not allowed: ctlModel=%d, expected ctlModel=%d (sbo-with-enhanced-security)", va->name, ctlModel, MVL61850_CTLMODEL_SBO_ENHANCED); return (NULL); } /* Create sboName from var name. */ /* NOTE: the sboName is not really used for "SBOw", but the "initSboCtrl"*/ /* function (originally written only for "SBO") needs it. */ //mvl61850_sbo_create_sboname (va, &mvluWrVaCtrl->wrVaCtrl->va_scope, sboName);//lnk编译删除,原前置程序未使用 /* Find "sboTimeout" value. */ if (mvl61850_mkname_sbotimeout (va->name, sboTimeoutName, MAX_IDENT_LEN) != SD_SUCCESS || mvlu_get_leaf_val_uint32 (va->base_va, sboTimeoutName, &sboTimeout) != SD_SUCCESS || sboTimeout==0) /* ..$CF$..$sboTimeout==0, or not found */ { /*renxiaobao 控制*/ sboTimeout = SBO_SELECT_TIMEOUT*1000; /* set timeout to default val */ } /* Find "sboClass" value. */ sboClass = mvl61850_sboclass_find (va); /* Get a SBO control element */ sboCtrl = getSboCtrl (mvluWrVaCtrl->indCtrl->event->net_info, sboName, ctlModel, sboTimeout, sboClass, &mvluWrVaCtrl->wrVaCtrl->LastApplError); } return (sboCtrl); } /************************************************************************/ /* mvl61850_ctl_chk_state */ /* Check ctlModel, etc. to determine if control is right type & */ /* in right state. */ /* RETURNS: SD_SUCCESS if OK to perform Operate command. */ /* SD_FAILURE if NOT OK (also sets appropriate Error and */ /* AddCause members of LastApplError). */ /************************************************************************/ ST_RET mvl61850_ctl_chk_state (MVLU_WR_VA_CTRL *mvluWrVaCtrl) { MVL_VAR_ASSOC *base_var = mvluWrVaCtrl->wrVaCtrl->va->base_va; ST_INT8 ctlModel; ST_BOOLEAN loc_stval; ST_CHAR flatname [MAX_IDENT_LEN + 1]; /* attribute to find */ ST_CHAR *lastdollar; /* ptr to last '$' in var name */ MVL61850_LAST_APPL_ERROR *LastApplError; /* renxiaobao 5.3版本修改*/ /* Send this DAE in the MMS Write response on any error. If the Write */ /* succeeds, this value is ignored (i.e. not sent). */ mvluWrVaCtrl->wrVaCtrl->failure = ARE_OBJ_ACCESS_DENIED; LastApplError = &mvluWrVaCtrl->wrVaCtrl->LastApplError; /* Only allow "Operate" if writing ALL of "Oper" and ONLY "Oper". */ /* Check this by seeing if variable name ends with "$Oper". */ /* Point to where "$Oper" should be, then see if it's there. */ lastdollar = strrchr (mvluWrVaCtrl->wrVaCtrl->va->name, '$'); /* find last '$'*/ if (lastdollar == NULL || strcmp (lastdollar+1, "Oper") != 0) { /* LastApplError not sent in this case, so don't bother setting it. */ return (SD_FAILURE); /* ERROR: not writing ALL of Oper and ONLY Oper.*/ } /* Check value of "ST$Loc$stVal". */ if (mvlu_get_leaf_val_boolean (base_var, "ST$Loc$stVal", &loc_stval)!=SD_SUCCESS) { loc_stval = SD_FALSE; /* if can't read ST$Loc$stVal, assume FALSE*/ } if (loc_stval == SD_FALSE) { /* Generate "ctlModel" leaf name and try to read its value. */ if (mvl61850_mkname_ctlmodel (mvluWrVaCtrl->wrVaCtrl->va->name, flatname, MAX_IDENT_LEN) != SD_SUCCESS || mvlu_get_leaf_val_int8 (base_var, flatname, &ctlModel) != SD_SUCCESS) { ctlModel = MVL61850_CTLMODEL_STATUS_ONLY; /* can't read ctlModel. assume status-only*/ } if (ctlModel == MVL61850_CTLMODEL_DIRECT_NORMAL||ctlModel == MVL61850_CTLMODEL_DIRECT_ENHANCED) { /* This is Direct control. */ /* OK. Nothing else to check. Continue to successful return. */ } else if (ctlModel == MVL61850_CTLMODEL_SBO_NORMAL||ctlModel == MVL61850_CTLMODEL_SBO_ENHANCED) { /* This is SBO control. */ /* Chk sboCtrl (set by startElWrites if client writing "Oper"). */ if (!mvluWrVaCtrl->wrVaCtrl->sboCtrl) { LastApplError->Error = 1; /* Unknown*/ LastApplError->AddCause = 3; /* Select-failed*/ return (SD_FAILURE); } } else { /* ctlModel="status-only" or invalid. */ LastApplError->Error = 1; /* Unknown*/ LastApplError->AddCause = 8; /* Blocked-by-mode*/ return (SD_FAILURE); } } /* end "if (loc_stval == SD_FALSE)" (i.e. "remote" mode) */ else { /* loc_stval == SD_TRUE (i.e. "local" mode) */ LastApplError->Error = 1; /* Unknown*/ LastApplError->AddCause = 8; /* Blocked-by-mode*/ return (SD_FAILURE); } /* Passed all checks. Allow the control operation. */ return (SD_SUCCESS); } /************************************************************************/ /* mvl61850_ctl_command_termination */ /* Called by user to send "Command Termination" request for controls */ /* with "Enhanced Security". */ /************************************************************************/ ST_RET mvl61850_ctl_command_termination (MVL_NET_INFO *net_info, ST_CHAR *oper_ref, ST_RET status, MVL61850_LAST_APPL_ERROR *last_appl_error) { OBJECT_NAME nvl_obj; /* NVL object name */ MVL_NVLIST_CTRL *nvlist_ctrl; ST_INT num_var; OBJECT_NAME var_obj[2]; /* array of var object names (1 or 2 depending on status)*/ MVL_VAR_ASSOC *last_appl_var = NULL; /* var assoc for "LastApplError"*/ ST_RET ret; ST_CHAR oper_ref_local[MAX_IDENT_LEN+1]; /* local copy of oper_ref arg */ MVL_SBO_CTRL *sboCtrl; /* ptr to SBO control if this is SBO */ ret = SD_SUCCESS; /* assume success */ /* Create tmp NVL used only for building this rpt. */ nvl_obj.object_tag = VMD_SPEC; nvl_obj.obj_name.vmd_spec = "tmp_nvl_for_rpt"; /* Fill in var_obj array. */ num_var = 0; if (status != SD_SUCCESS) { /* On error, include "LastApplError" in report. */ ST_INT type_id; /* Create temporary VMD_SPEC "LastApplError" variable. */ /* If this fails, mvl_vmd_nvl_add will also fail later. */ type_id = mvl_typename_to_typeid ("LastApplErrorType"); if (type_id >= 0) { var_obj[num_var].object_tag = VMD_SPEC; var_obj[num_var].obj_name.vmd_spec = "LastApplError"; /* NOTE: last_appl_error points to temporary data. This var must be deleted before returning*/ last_appl_var = mvl_vmd_var_add (&mvl_vmd, &var_obj[num_var], NULL, /* (MVL_NET_INFO *) */ type_id, last_appl_error, /* data ptr (saved in var->data) */ NULL, /* (MVL_VAR_PROC *) */ SD_FALSE); /* DO NOT copy the var name */ } num_var++; } /* Add this control object (LN$FC$xxx$Oper) to "var_obj". */ var_obj[num_var].object_tag = DOM_SPEC; /* ALWAYS Domain specific*/ /* strtok modifies input buffer, so copy oper_ref to local buffer first. */ strcpy (oper_ref_local, oper_ref); var_obj[num_var].domain_id = strtok (oper_ref_local, "/"); /*extract dom name*/ var_obj[num_var].obj_name.vmd_spec = strtok (NULL, ""); if (var_obj[num_var].obj_name.vmd_spec == NULL) { MVL_LOG_ERR1 ("Invalid ControlObjectReference '%s', cannot send CommandTermination", oper_ref); ret = SD_FAILURE; } else { num_var++; nvlist_ctrl = mvl_vmd_nvl_add (&mvl_vmd, &nvl_obj, NULL, /* net_info: not needed*/ num_var, var_obj, SD_TRUE); /* copy name */ /* Now send report */ if (nvlist_ctrl) { ret = mvl_info_variables (net_info, nvlist_ctrl, SD_TRUE); /* listOfVariables*/ mvl_vmd_nvl_remove (&mvl_vmd, &nvl_obj, NULL); /* done with tmp NVL */ } else ret = SD_FAILURE; } /* If temporary "LastApplError" var created, destroy it now. */ if (last_appl_var) mvl_vmd_var_remove (&mvl_vmd, &var_obj[0], NULL); /* always first in "var_obj" array*/ sboCtrl = mvlu_sbo_chk_state (oper_ref, net_info); if (sboCtrl != NULL && sboCtrl->ctlModel == MVL61850_CTLMODEL_SBO_ENHANCED) { /* ctlModel=sbo-with-enhanced-security. If sboClass=operate-once, */ /* "unselect" now (i.e. free sboCtrl), else go back to READY state. */ if (sboCtrl->sboClass == MVL61850_SBOCLASS_OPERATE_ONCE) mvlu_sbo_ctrl_free (sboCtrl); /* like "UNSELECTED" state*/ else sboCtrl->ctlState = MVL61850_CTLSTATE_READY; } return (ret); } /************************************************************************/ /* mvl61850_ctl_lastapplerror_create */ /* Create type to use later when "temporary" LastApplError var created. */ /************************************************************************/ ST_RET mvl61850_ctl_lastapplerror_create () { ST_RET retcode = SD_SUCCESS; ST_CHAR *LastApplErrorTdl = "{\ (CntrlObj)Vstring129,\ (Error)Byte,\ (Origin){(orCat)Byte,(orIdent)OVstring64},\ (ctlNum)Ubyte,\ (AddCause)Byte}"; /* Maybe this type was already created. If it does NOT exist, create it.*/ if (mvl_typename_to_typeid ("LastApplErrorType") < 0) { if (mvl_type_id_create_from_tdl ("LastApplErrorType",LastApplErrorTdl) < 0) retcode = SD_FAILURE; /* create failed */ } return (retcode); } /************************************************************************/ /* mvl61850_ctl_lastapplerror_send */ /* Called by user to send "LastApplError" information report. */ /************************************************************************/ ST_RET mvl61850_ctl_lastapplerror_send (MVL_NET_INFO *net_info, MVL61850_LAST_APPL_ERROR *last_appl_error) { OBJECT_NAME nvl_obj; /* NVL object name */ MVL_NVLIST_CTRL *nvlist_ctrl; ST_INT num_var; OBJECT_NAME var_obj; /* temporary var object name */ MVL_VAR_ASSOC *last_appl_var = NULL; /* var assoc for "LastApplError"*/ ST_RET ret; ST_INT type_id; ret = SD_SUCCESS; /* assume success */ /* Create tmp NVL used only for building this rpt. */ nvl_obj.object_tag = VMD_SPEC; nvl_obj.obj_name.vmd_spec = "tmp_nvl_for_rpt"; /* Fill in var_obj. */ num_var = 1; /* Create temporary VMD_SPEC "LastApplError" variable. */ /* If this fails, mvl_vmd_nvl_add will also fail later. */ type_id = mvl_typename_to_typeid ("LastApplErrorType"); if (type_id >= 0) { var_obj.object_tag = VMD_SPEC; var_obj.obj_name.vmd_spec = "LastApplError"; /* NOTE: last_appl_error points to temporary data. This var must be deleted before returning*/ last_appl_var = mvl_vmd_var_add (&mvl_vmd, &var_obj, NULL, /* (MVL_NET_INFO *) */ type_id, last_appl_error, /* data ptr (saved in var->data) */ NULL, /* (MVL_VAR_PROC *) */ SD_FALSE); /* DO NOT copy the var name */ nvlist_ctrl = mvl_vmd_nvl_add (&mvl_vmd, &nvl_obj, NULL, /* net_info: not needed*/ num_var, &var_obj, SD_TRUE); /* copy name */ /* Now send report */ if (nvlist_ctrl) { ret = mvl_info_variables (net_info, nvlist_ctrl, SD_TRUE); /* listOfVariables*/ mvl_vmd_nvl_remove (&mvl_vmd, &nvl_obj, NULL); /* done with tmp NVL */ } else ret = SD_FAILURE; } else ret = SD_FAILURE; /* If temporary "LastApplError" var created, destroy it now. */ if (last_appl_var) mvl_vmd_var_remove (&mvl_vmd, &var_obj, NULL); if (ret != SD_SUCCESS) MVL_LOG_ERR0 ("Error sending LastApplError"); return (ret); } /************************************************************************/ /* mvl61850_ctl_req_done */ /* Do some necessary cleanup IF the MMS Write request represents an IEC */ /* 61850 control request. */ /* NOTE: For any other MMS Write request, this function does nothing. */ /************************************************************************/ ST_VOID mvl61850_ctl_req_done (MVLAS_WR_VA_CTRL *wrVaCtrl, MVL_NET_INFO *net_info) { ST_INT8 ctlModel; ST_BOOLEAN send_lastappl = SD_FALSE; ST_CHAR flatname [MAX_IDENT_LEN+1]; ST_CHAR *lastdollar; /* ptr to last '$' in variable name */ MVL_SBO_CTRL *sboCtrl; /* ptr to SBO control if this is SBO */ /* CRITICAL: make sure variable was found before doing anything else. */ if (wrVaCtrl->va == NULL) return; /* var not found. Do nothing. */ lastdollar = strrchr (wrVaCtrl->va->name, '$'); /* find last '$'*/ /* If Write failed, and Var is IEC 61850 Oper, Cancel, or SBOw, */ /* must also send InformationReport containing LastApplError. */ if (wrVaCtrl->resp_tag == WR_RSLT_FAILURE) { /* Var name must end in $Oper, $Cancel, or $SBOw. */ if (lastdollar != NULL) { /* Send LastApplError if writing Cancel or SBOw. */ if (strcmp (lastdollar+1, "Cancel") == 0 || strcmp (lastdollar+1, "SBOw") == 0) send_lastappl = SD_TRUE; /* Send LastApplError if writing Oper AND Enhanced security. */ else if (strcmp (lastdollar+1, "Oper") == 0) { /* Generate "ctlModel" leaf name like "CF$...$ctlModel" from */ /* variable name "...$Oper" and try to read its value. */ if (mvl61850_mkname_ctlmodel (wrVaCtrl->va->name, flatname, MAX_IDENT_LEN) != SD_SUCCESS || mvlu_get_leaf_val_int8 (wrVaCtrl->va->base_va, flatname, &ctlModel) != SD_SUCCESS) ctlModel = MVL61850_CTLMODEL_STATUS_ONLY; /* can't read ctlModel. assume status-only*/ /*renxiaobao 控制 //if (ctlModel == MVL61850_CTLMODEL_DIRECT_ENHANCED || // ctlModel == MVL61850_CTLMODEL_SBO_ENHANCED) */ send_lastappl = SD_TRUE; } if (send_lastappl) { /* Construct & send LastApplError info report.*/ /* Fill in "LastApplError" structure. */ /* This function builds a string in "CntrlObj". */ /* If it fails, the string is empty. */ #if 1 mvl61850_objref_create (wrVaCtrl->va->name, &wrVaCtrl->va_scope, wrVaCtrl->LastApplError.CntrlObj); #else /* OLD WAY (similar but CntrlObj always ended in '$Oper'). */ //mvl61850_sbo_create_sboname (wrVaCtrl->va, &wrVaCtrl->va_scope, wrVaCtrl->LastApplError.CntrlObj);//lnk编译删除,原前置程序未使用 #endif /* Other members (Error, AddCause, Origin, ctlNum) already set by leaf functions.*/ /* If Error/AddCause not set yet, set default values. */ if (wrVaCtrl->LastApplError.Error == 0) { wrVaCtrl->LastApplError.Error = 1; /* Unknown */ wrVaCtrl->LastApplError.AddCause = 0; /* Unknown */ } mvl61850_ctl_lastapplerror_send (net_info, &wrVaCtrl->LastApplError); } } } /* Do special cleanup for IEC 61850 SBO controls. */ /* If wrVaCtrl->sboCtrl != NULL, this var is SBO Oper or Cancel */ /* (see startElWrites), so do appropriate SBO cleanup. */ /* If this is a Cancel request, or if ctlModel=sbo-with-normal-security*/ /* AND sboClass=operate-once, we must "unselect" the control */ /* (i.e. free "sboCtrl"). */ sboCtrl = wrVaCtrl->sboCtrl; if (sboCtrl != NULL) { if (strcmp (lastdollar+1, "Cancel") == 0 || (sboCtrl->ctlModel == MVL61850_CTLMODEL_SBO_NORMAL && sboCtrl->sboClass == MVL61850_SBOCLASS_OPERATE_ONCE) ) { /* NOTE: for sbo-with-enhanced-security, this is done AFTER CommandTerm.*/ mvlu_sbo_ctrl_free (sboCtrl); wrVaCtrl->sboCtrl = NULL; /* reset this so free not done twice.*/ } } }