- IEC 61850 server: refactored control model API (changed handler signatures, added ControlAction object to access origin and set addCause value, ...)

- IEC 61850 client: added ControlObjectClient_getLastError function
pull/179/head
Michael Zillgith 6 years ago
parent 5a3c3ba4b3
commit 6c14425ca8

@ -25,7 +25,7 @@ sigint_handler(int signalId)
}
static CheckHandlerResult
checkHandler(void* parameter, MmsValue* ctlVal, bool test, bool interlockCheck, ClientConnection connection)
checkHandler(ControlAction action, void* parameter, MmsValue* ctlVal, bool test, bool interlockCheck)
{
printf("check handler called!\n");
@ -51,7 +51,7 @@ checkHandler(void* parameter, MmsValue* ctlVal, bool test, bool interlockCheck,
}
static ControlHandlerResult
controlHandlerForBinaryOutput(void* parameter, MmsValue* value, bool test)
controlHandlerForBinaryOutput(ControlAction action, void* parameter, MmsValue* value, bool test)
{
uint64_t timestamp = Hal_getTimeInMs();
@ -102,7 +102,6 @@ writeAccessHandler (DataAttribute* dataAttribute, MmsValue* value, ClientConnect
int
main(int argc, char** argv)
{
iedServer = IedServer_create(&iedModel);
IedServer_setControlHandler(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO1,

@ -56,6 +56,7 @@ struct sControlObjectClient
uint64_t constantT; /* timestamp of select/operate to be used when constant T option is selected */
LastApplError lastApplError;
MmsError lastMmsError;
CommandTerminationHandler commandTerminationHandler;
void* commandTerminaionHandlerParameter;
@ -312,6 +313,12 @@ ControlObjectClient_getCtlValType(ControlObjectClient self)
return MmsValue_getType(self->ctlVal);
}
IedClientError
ControlObjectClient_getLastError(ControlObjectClient self)
{
return iedConnection_mapMmsErrorToIedError(self->lastMmsError);
}
void
ControlObjectClient_setOrigin(ControlObjectClient self, const char* orIdent, int orCat)
{
@ -500,6 +507,8 @@ ControlObjectClient_operate(ControlObjectClient self, MmsValue* ctlVal, uint64_t
MmsValue_setElement(operParameters, 0, NULL);
MmsValue_delete(operParameters);
self->lastMmsError = mmsError;
if (mmsError != MMS_ERROR_NONE) {
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: operate failed!\n");
@ -718,6 +727,8 @@ ControlObjectClient_selectWithValue(ControlObjectClient self, MmsValue* ctlVal)
MmsValue_setElement(selValParameters, 0, NULL);
MmsValue_delete(selValParameters);
self->lastMmsError = mmsError;
if (mmsError != MMS_ERROR_NONE) {
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: select-with-value failed!\n");
@ -869,6 +880,8 @@ ControlObjectClient_select(ControlObjectClient self)
self->ctlNum++;
self->lastMmsError = mmsError;
if (value == NULL) {
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: select: read SBO failed!\n");
@ -1068,6 +1081,8 @@ ControlObjectClient_cancel(ControlObjectClient self)
MmsDataAccessError writeResult = MmsConnection_writeVariable(IedConnection_getMmsConnection(self->connection),
&mmsError, domainId, itemId, cancelParameters);
self->lastMmsError = mmsError;
MmsValue_setElement(cancelParameters, 0, NULL);
MmsValue_delete(cancelParameters);

@ -2001,6 +2001,16 @@ ControlObjectClient_changeServerControlModel(ControlObjectClient self, ControlMo
LIB61850_API MmsType
ControlObjectClient_getCtlValType(ControlObjectClient self);
/**
* \brief Get the error code of the last synchronous control action (operate, select, select-with-value, cancel)
*
* \param self the control object instance to use
*
* \return the client error code
*/
LIB61850_API IedClientError
ControlObjectClient_getLastError(ControlObjectClient self);
/**
* \brief Send an operate command to the server
*

@ -3,7 +3,7 @@
*
* IEC 61850 server API for libiec61850.
*
* Copyright 2013-2018 Michael Zillgith
* Copyright 2013-2019 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -1083,9 +1083,62 @@ typedef enum {
CONTROL_RESULT_WAITING = 2 /** check or operation is in progress */
} ControlHandlerResult;
typedef void* ControlAction;
/**
* \brief Set the add cause for the next command termination or application error message
*
* \param self the control action instance
* \param addCause the additional cause
*/
LIB61850_API void
ControlAction_setAddCause(ControlAction self, ControlAddCause addCause);
/**
* \brief Get the originator category provided by the client
*
* \param self the control action instance
*
* \return the originator category
*/
LIB61850_API int
ControlAction_getOrCat(ControlAction self);
/**
* \brief Get the originator identifier provided by the client
*
* \param self the control action instance
*
* \return the originator identifier
*/
LIB61850_API uint8_t*
ControlAction_getOrIdent(ControlAction self, int* orIdentSize);
/**
* \brief Get the client object associated with the client that caused the control action
*
* \param self the control action instance
*
* \return client connection instance
*/
LIB61850_API ClientConnection
ControlAction_getClientConnection(ControlAction self);
/**
* \brief Get the control object that is subject to this action
*
* \param self the control action instance
*
* \return the controllable data object instance
*/
LIB61850_API DataObject*
ControlAction_getControlObject(ControlAction self);
/**
* \brief Control model callback to perform the static tests (optional).
*
* NOTE: Signature changed in version 1.4!
*
* User provided callback function for the control model. It will be invoked after
* a control operation has been invoked by the client. This callback function is
* intended to perform the static tests. It should check if the interlock conditions
@ -1093,20 +1146,21 @@ typedef enum {
* This handler can also be check if the client has the required permissions to execute the
* operation and allow or deny the operation accordingly.
*
* \param action the control action parameter that provides access to additional context information
* \param parameter the parameter that was specified when setting the control handler
* \param ctlVal the control value of the control operation.
* \param test indicates if the operate request is a test operation
* \param interlockCheck the interlockCheck parameter provided by the client
* \param connection the connection object of the client connection that invoked the control operation
*
* \return CONTROL_ACCEPTED if the static tests had been successful, one of the error codes otherwise
*/
typedef CheckHandlerResult (*ControlPerformCheckHandler) (void* parameter, MmsValue* ctlVal, bool test, bool interlockCheck,
ClientConnection connection);
typedef CheckHandlerResult (*ControlPerformCheckHandler) (ControlAction action, void* parameter, MmsValue* ctlVal, bool test, bool interlockCheck);
/**
* \brief Control model callback to perform the dynamic tests (optional).
*
* NOTE: Signature changed in version 1.4!
*
* User provided callback function for the control model. It will be invoked after
* a control operation has been invoked by the client. This callback function is
* intended to perform the dynamic tests. It should check if the synchronization conditions
@ -1115,6 +1169,7 @@ typedef CheckHandlerResult (*ControlPerformCheckHandler) (void* parameter, MmsVa
* cannot be performed immediately the function SHOULD return CONTROL_RESULT_WAITING and the
* handler will be invoked again later.
*
* \param action the control action parameter that provides access to additional context information
* \param parameter the parameter that was specified when setting the control handler
* \param ctlVal the control value of the control operation.
* \param test indicates if the operate request is a test operation
@ -1123,11 +1178,13 @@ typedef CheckHandlerResult (*ControlPerformCheckHandler) (void* parameter, MmsVa
* \return CONTROL_RESULT_OK if the dynamic tests had been successful, CONTROL_RESULT_FAILED otherwise,
* CONTROL_RESULT_WAITING if the test is not yet finished
*/
typedef ControlHandlerResult (*ControlWaitForExecutionHandler) (void* parameter, MmsValue* ctlVal, bool test, bool synchroCheck);
typedef ControlHandlerResult (*ControlWaitForExecutionHandler) (ControlAction action, void* parameter, MmsValue* ctlVal, bool test, bool synchroCheck);
/**
* \brief Control model callback to actually perform the control operation.
*
* NOTE: Signature changed in version 1.4!
*
* User provided callback function for the control model. It will be invoked when
* a control operation happens (Oper). Here the user should perform the control operation
* (e.g. by setting an digital output or switching a relay).
@ -1135,6 +1192,7 @@ typedef ControlHandlerResult (*ControlWaitForExecutionHandler) (void* parameter,
* cannot be performed immediately the function SHOULD return CONTROL_RESULT_WAITING and the
* handler will be invoked again later.
*
* \param action the control action parameter that provides access to additional context information
* \param parameter the parameter that was specified when setting the control handler
* \param ctlVal the control value of the control operation.
* \param test indicates if the operate request is a test operation
@ -1142,7 +1200,7 @@ typedef ControlHandlerResult (*ControlWaitForExecutionHandler) (void* parameter,
* \return CONTROL_RESULT_OK if the control action bas been successful, CONTROL_RESULT_FAILED otherwise,
* CONTROL_RESULT_WAITING if the test is not yet finished
*/
typedef ControlHandlerResult (*ControlHandler) (void* parameter, MmsValue* ctlVal, bool test);
typedef ControlHandlerResult (*ControlHandler) (ControlAction action, void* parameter, MmsValue* ctlVal, bool test);
/**
* \brief Set control handler for controllable data object

@ -51,6 +51,7 @@ struct sControlObject
int synchroCheck:1;
int timeActivatedOperate:1;
int operateOnce:1;
ControlAddCause addCauseValue:6;
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore stateLock;
@ -85,8 +86,6 @@ struct sControlObject
/* for automatic update of tOpOk attribute */
DataAttribute* tOpOk;
// char ctlObjectName[130];
/* for LastAppIError */
MmsValue* error;
MmsValue* addCause;
@ -110,6 +109,8 @@ struct sControlObject
ControlWaitForExecutionHandler waitForExecutionHandler;
void* waitForExecutionHandlerParameter;
DataObject* dataObject;
};
LIB61850_INTERNAL ControlObject*

@ -751,6 +751,9 @@ lookupControlObject(IedServer self, DataObject* node)
ControlObject* controlObject = MmsMapping_getControlObject(self->mmsMapping, domain,
lnName, objectName);
if (controlObject)
controlObject->dataObject = node;
return controlObject;
}

@ -261,8 +261,10 @@ operateControl(ControlObject* self, MmsValue* value, uint64_t currentTime, bool
{
self->selectTime = currentTime;
self->addCauseValue = ADD_CAUSE_UNKNOWN;
if (self->operateHandler != NULL)
return self->operateHandler(self->operateHandlerParameter, value, testCondition);
return self->operateHandler((ControlAction) self, self->operateHandlerParameter, value, testCondition);
return CONTROL_RESULT_OK;
}
@ -287,15 +289,17 @@ executeStateMachine:
if (state == STATE_WAIT_FOR_ACTIVATION_TIME)
isTimeActivatedControl = true;
self->addCauseValue = ADD_CAUSE_BLOCKED_BY_SYNCHROCHECK;
if (self->waitForExecutionHandler != NULL) {
dynamicCheckResult = self->waitForExecutionHandler(self->waitForExecutionHandlerParameter, self->ctlVal,
dynamicCheckResult = self->waitForExecutionHandler((ControlAction) self, self->waitForExecutionHandlerParameter, self->ctlVal,
self->testMode, self->synchroCheck);
}
if (dynamicCheckResult == CONTROL_RESULT_FAILED) {
if (isTimeActivatedControl) {
ControlObject_sendLastApplError(self, self->mmsConnection, "Oper",
CONTROL_ERROR_NO_ERROR, ADD_CAUSE_BLOCKED_BY_SYNCHROCHECK,
CONTROL_ERROR_NO_ERROR, self->addCauseValue,
self->ctlNum, self->origin, false);
}
else
@ -795,12 +799,11 @@ Control_processControlActions(MmsMapping* self, uint64_t currentTimeInMs)
if (controlObject->checkHandler != NULL) { /* perform operative tests */
ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer,
controlObject->mmsConnection);
controlObject->addCauseValue = ADD_CAUSE_BLOCKED_BY_INTERLOCKING;
checkResult = controlObject->checkHandler(
checkResult = controlObject->checkHandler((ControlAction) self,
controlObject->checkHandlerParameter, controlObject->ctlVal, controlObject->testMode,
controlObject->interlockCheck, clientConnection);
controlObject->interlockCheck);
}
if (checkResult == CONTROL_ACCEPTED) {
@ -814,9 +817,10 @@ Control_processControlActions(MmsMapping* self, uint64_t currentTimeInMs)
executeControlTask(controlObject, currentTimeInMs);
}
else {
ControlObject_sendLastApplError(controlObject, controlObject->mmsConnection, "Oper",
CONTROL_ERROR_NO_ERROR, ADD_CAUSE_BLOCKED_BY_INTERLOCKING,
controlObject->ctlNum, controlObject->origin, false);
CONTROL_ERROR_NO_ERROR, controlObject->addCauseValue,
controlObject->ctlNum, controlObject->origin, false);
/* leave state Perform Test */
setOpRcvd(controlObject, false);
@ -1053,7 +1057,7 @@ ControlObject_sendCommandTerminationNegative(ControlObject* self)
MmsValue_setElement(lastApplError, 0, ctlObjValue);
MmsValue_setInt32(self->error, CONTROL_ERROR_UNKOWN);
MmsValue_setInt32(self->addCause, ADD_CAUSE_UNKNOWN);
MmsValue_setInt32(self->addCause, self->addCauseValue);
MmsValue_setElement(lastApplError, 1, self->error);
MmsValue_setElement(lastApplError, 2, self->origin);
@ -1262,14 +1266,12 @@ Control_readAccessControlObject(MmsMapping* self, MmsDomain* domain, char* varia
/* opRcvd must not be set here! */
if (controlObject->checkHandler != NULL) { /* perform operative tests */
controlObject->addCauseValue = ADD_CAUSE_UNKNOWN;
ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer,
connection);
if (controlObject->checkHandler != NULL) { /* perform operative tests */
checkResult = controlObject->checkHandler(
controlObject->checkHandlerParameter, NULL, false, false,
clientConnection);
checkResult = controlObject->checkHandler((ControlAction) controlObject,
controlObject->checkHandlerParameter, NULL, false, false);
}
if (checkResult == CONTROL_ACCEPTED) {
@ -1442,10 +1444,10 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
indication = DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE;
if (connection != controlObject->mmsConnection)
ControlObject_sendLastApplError(controlObject, connection, "SBOw", 0,
ControlObject_sendLastApplError(controlObject, connection, "SBOw", CONTROL_ERROR_NO_ERROR,
ADD_CAUSE_LOCKED_BY_OTHER_CLIENT, ctlNum, origin, true);
else
ControlObject_sendLastApplError(controlObject, connection, "SBOw", 0,
ControlObject_sendLastApplError(controlObject, connection, "SBOw", CONTROL_ERROR_NO_ERROR,
ADD_CAUSE_OBJECT_ALREADY_SELECTED, ctlNum, origin, true);
if (DEBUG_IED_SERVER)
@ -1461,14 +1463,12 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
bool testCondition = MmsValue_getBoolean(test);
if (controlObject->checkHandler != NULL) { /* perform operative tests */
controlObject->addCauseValue = ADD_CAUSE_SELECT_FAILED;
ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer,
connection);
if (controlObject->checkHandler != NULL) { /* perform operative tests */
checkResult = controlObject->checkHandler(
controlObject->checkHandlerParameter, ctlVal, testCondition, interlockCheck,
clientConnection);
checkResult = controlObject->checkHandler((ControlAction) controlObject,
controlObject->checkHandlerParameter, ctlVal, testCondition, interlockCheck);
}
if (checkResult == CONTROL_ACCEPTED) {
@ -1485,7 +1485,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
indication = (MmsDataAccessError) checkResult;
ControlObject_sendLastApplError(controlObject, connection, "SBOw", 0,
ADD_CAUSE_SELECT_FAILED, ctlNum, origin, true);
controlObject->addCauseValue, ctlNum, origin, true);
if (DEBUG_IED_SERVER)
printf("SBOw: select rejected by application!\n");
@ -1601,14 +1601,12 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
/* enter state Perform Test */
setOpRcvd(controlObject, true);
if (controlObject->checkHandler != NULL) { /* perform operative tests */
controlObject->addCauseValue = ADD_CAUSE_UNKNOWN;
ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer,
connection);
if (controlObject->checkHandler != NULL) { /* perform operative tests */
checkResult = controlObject->checkHandler(
controlObject->checkHandlerParameter, ctlVal, testCondition, interlockCheck,
clientConnection);
checkResult = controlObject->checkHandler((ControlAction) controlObject,
controlObject->checkHandlerParameter, ctlVal, testCondition, interlockCheck);
}
if (checkResult == CONTROL_ACCEPTED) {
@ -1693,5 +1691,64 @@ free_and_return:
return indication;
}
void
ControlAction_setAddCause(ControlAction self, ControlAddCause addCause)
{
ControlObject* controlObject = (ControlObject*) self;
controlObject->addCauseValue = addCause;
}
int
ControlAction_getOrCat(ControlAction self)
{
ControlObject* controlObject = (ControlObject*) self;
if (controlObject->origin) {
MmsValue* orCat = MmsValue_getElement(controlObject->origin, 0);
if (orCat) {
return MmsValue_toInt32(orCat);
}
}
return 0;
}
uint8_t*
ControlAction_getOrIdent(ControlAction self, int* orIdentSize)
{
ControlObject* controlObject = (ControlObject*) self;
if (controlObject->origin) {
MmsValue* orIdent = MmsValue_getElement(controlObject->origin, 1);
if (orIdent) {
if (MmsValue_getType(orIdent) == MMS_OCTET_STRING) {
*orIdentSize = MmsValue_getOctetStringSize(orIdent);
return MmsValue_getOctetStringBuffer(orIdent);
}
}
}
return NULL;
}
ClientConnection
ControlAction_getClientConnection(ControlAction self)
{
ControlObject* controlObject = (ControlObject*) self;
return private_IedServer_getClientConnectionByHandle(controlObject->iedServer, controlObject->mmsConnection);
}
DataObject*
ControlAction_getControlObject(ControlAction self)
{
ControlObject* controlObject = (ControlObject*) self;
return controlObject->dataObject;
}
#endif /* (CONFIG_IEC61850_CONTROL_SERVICE == 1) */

Loading…
Cancel
Save