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

812 lines
34 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

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

/************************************************************************/
/* SISCO SOFTWARE MODULE HEADER *****************************************/
/************************************************************************/
/* (c) Copyright Systems Integration Specialists Company, Inc., */
/* 2004-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<6E><6B><EFBFBD><EFBFBD>ɾ<EFBFBD><C9BE><EFBFBD><EFBFBD>ԭǰ<D4AD>ó<EFBFBD><C3B3><EFBFBD>δʹ<CEB4><CAB9>
/* 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 <20><><EFBFBD><EFBFBD>*/
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 <20><><EFBFBD><EFBFBD>
// if (ctlModel != MVL61850_CTLMODEL_SBO_ENHANCED)*/
if ((ctlModel != MVL61850_CTLMODEL_SBO_ENHANCED)&&(ctlModel != MVL61850_CTLMODEL_SBO_NORMAL))
{
/* renxiaobao <20><><EFBFBD><EFBFBD>*/
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<6E><6B><EFBFBD><EFBFBD>ɾ<EFBFBD><C9BE><EFBFBD><EFBFBD>ԭǰ<D4AD>ó<EFBFBD><C3B3><EFBFBD>δʹ<CEB4><CAB9>
/* 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 <20><><EFBFBD><EFBFBD>*/
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<EFBFBD><EFBFBD>޸<EFBFBD>*/
/* 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 <20><><EFBFBD><EFBFBD>
//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<6E><6B><EFBFBD><EFBFBD>ɾ<EFBFBD><C9BE><EFBFBD><EFBFBD>ԭǰ<D4AD>ó<EFBFBD><C3B3><EFBFBD>δʹ<CEB4><CAB9>
#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.*/
}
}
}