- IED server: added ControlSelectStateChangedHandler callback for control model

pull/291/head
Michael Zillgith 5 years ago
parent fb5ed11001
commit 14f4f1ccf0

@ -1211,22 +1211,22 @@ IedServer_setEditSettingGroupConfirmationHandler(IedServer self, SettingGroupCon
* \brief result code for ControlPerformCheckHandler * \brief result code for ControlPerformCheckHandler
*/ */
typedef enum { typedef enum {
CONTROL_ACCEPTED = -1, /** check passed */ CONTROL_ACCEPTED = -1, /**< check passed */
CONTROL_WAITING_FOR_SELECT = 0, /** select operation in progress - handler will be called again later */ CONTROL_WAITING_FOR_SELECT = 0, /**< select operation in progress - handler will be called again later */
CONTROL_HARDWARE_FAULT = 1, /** check failed due to hardware fault */ CONTROL_HARDWARE_FAULT = 1, /**< check failed due to hardware fault */
CONTROL_TEMPORARILY_UNAVAILABLE = 2, /** control is already selected or operated */ CONTROL_TEMPORARILY_UNAVAILABLE = 2, /**< control is already selected or operated */
CONTROL_OBJECT_ACCESS_DENIED = 3, /** check failed due to access control reason - access denied for this client or state */ CONTROL_OBJECT_ACCESS_DENIED = 3, /**< check failed due to access control reason - access denied for this client or state */
CONTROL_OBJECT_UNDEFINED = 4, /** object not visible in this security context ??? */ CONTROL_OBJECT_UNDEFINED = 4, /**< object not visible in this security context ??? */
CONTROL_VALUE_INVALID = 11 /** ctlVal out of range */ CONTROL_VALUE_INVALID = 11 /**< ctlVal out of range */
} CheckHandlerResult; } CheckHandlerResult;
/** /**
* \brief result codes for control handler (ControlWaitForExecutionHandler and ControlHandler) * \brief result codes for control handler (ControlWaitForExecutionHandler and ControlHandler)
*/ */
typedef enum { typedef enum {
CONTROL_RESULT_FAILED = 0, /** check or operation failed */ CONTROL_RESULT_FAILED = 0, /**< check or operation failed */
CONTROL_RESULT_OK = 1, /** check or operation was successful */ CONTROL_RESULT_OK = 1, /**< check or operation was successful */
CONTROL_RESULT_WAITING = 2 /** check or operation is in progress */ CONTROL_RESULT_WAITING = 2 /**< check or operation is in progress */
} ControlHandlerResult; } ControlHandlerResult;
typedef void* ControlAction; typedef void* ControlAction;
@ -1387,6 +1387,30 @@ typedef ControlHandlerResult (*ControlWaitForExecutionHandler) (ControlAction ac
*/ */
typedef ControlHandlerResult (*ControlHandler) (ControlAction action, void* parameter, MmsValue* ctlVal, bool test); typedef ControlHandlerResult (*ControlHandler) (ControlAction action, void* parameter, MmsValue* ctlVal, bool test);
/**
* \brief Reason why a select state of a control object changed
*/
typedef enum {
SELECT_STATE_REASON_SELECTED, /**< control has been selected */
SELECT_STATE_REASON_CANCELED, /**< cancel received for the control */
SELECT_STATE_REASON_TIMEOUT, /**< unselected due to timeout (sboTimeout) */
SELECT_STATE_REASON_OPERATED, /**< unselected due to successful operate */
SELECT_STATE_REASON_OPERATE_FAILED, /**< unselected due to failed operate */
SELECT_STATE_REASON_DISCONNECTED /**< unselected due to disconnection of selecting client */
} SelectStateChangedReason;
/**
* \brief Control model callback that is called when the select state of a control changes
*
* New in version 1.5
*
* \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 isSelected true when the control is selected, false otherwise
* \param reason reason why the select state changed
*/
typedef void (*ControlSelectStateChangedHandler) (ControlAction action, void* parameter, bool isSelected, SelectStateChangedReason reason);
/** /**
* \brief Set control handler for controllable data object * \brief Set control handler for controllable data object
* *
@ -1437,6 +1461,25 @@ IedServer_setPerformCheckHandler(IedServer self, DataObject* node, ControlPerfor
LIB61850_API void LIB61850_API void
IedServer_setWaitForExecutionHandler(IedServer self, DataObject* node, ControlWaitForExecutionHandler handler, void* parameter); IedServer_setWaitForExecutionHandler(IedServer self, DataObject* node, ControlWaitForExecutionHandler handler, void* parameter);
/**
* \brief Set a callback handler for a controllable data object to track select state changes
*
* The callback is called whenever the select state of a control changes. Reason for changes can be:
* - a successful select or select-with-value by a client
* - select timeout
* - operate or failed operate
* - cancel request by a client
* - the client that selected the control has been disconnected
*
* \param self the instance of IedServer to operate on.
* \param node the controllable data object handle
* \param handler a callback function of type ControlHandler
* \param parameter a user provided parameter that is passed to the control handler.
*/
LIB61850_API void
IedServer_setSelectStateChangedHandler(IedServer self, DataObject* node, ControlSelectStateChangedHandler handler, void* parameter);
/** /**
* \brief Update the control model for the specified controllable data object with the given value and * \brief Update the control model for the specified controllable data object with the given value and
* update "ctlModel" attribute value. * update "ctlModel" attribute value.

@ -129,6 +129,9 @@ struct sControlObject
ControlWaitForExecutionHandler waitForExecutionHandler; ControlWaitForExecutionHandler waitForExecutionHandler;
void* waitForExecutionHandlerParameter; void* waitForExecutionHandlerParameter;
ControlSelectStateChangedHandler selectStateChangedHandler;
void* selectStateChangedHandlerParameter;
#if (CONFIG_IEC61850_SERVICE_TRACKING == 1) #if (CONFIG_IEC61850_SERVICE_TRACKING == 1)
/* Common data class (CDC) of control object */ /* Common data class (CDC) of control object */
ControlServiceCDC cdc; ControlServiceCDC cdc;
@ -182,6 +185,10 @@ ControlObject_installCheckHandler(ControlObject* self, ControlPerformCheckHandle
LIB61850_INTERNAL void LIB61850_INTERNAL void
ControlObject_installWaitForExecutionHandler(ControlObject* self, ControlWaitForExecutionHandler handler, void* parameter); ControlObject_installWaitForExecutionHandler(ControlObject* self, ControlWaitForExecutionHandler handler, void* parameter);
LIB61850_INTERNAL void
ControlObject_installSelectStateChangedHandler(ControlObject* self, ControlSelectStateChangedHandler handler,
void* parameter);
LIB61850_INTERNAL void LIB61850_INTERNAL void
ControlObject_updateControlModel(ControlObject* self, ControlModel value, DataObject* ctlObject); ControlObject_updateControlModel(ControlObject* self, ControlModel value, DataObject* ctlObject);

@ -853,7 +853,7 @@ IedServer_setControlHandler(
{ {
ControlObject* controlObject = lookupControlObject(self, node); ControlObject* controlObject = lookupControlObject(self, node);
if (controlObject != NULL) { if (controlObject) {
ControlObject_installListener(controlObject, listener, parameter); ControlObject_installListener(controlObject, listener, parameter);
if (DEBUG_IED_SERVER) if (DEBUG_IED_SERVER)
printf("IED_SERVER: Installed control handler for %s!\n", node->name); printf("IED_SERVER: Installed control handler for %s!\n", node->name);
@ -868,7 +868,7 @@ IedServer_setPerformCheckHandler(IedServer self, DataObject* node, ControlPerfor
{ {
ControlObject* controlObject = lookupControlObject(self, node); ControlObject* controlObject = lookupControlObject(self, node);
if (controlObject != NULL) if (controlObject)
ControlObject_installCheckHandler(controlObject, handler, parameter); ControlObject_installCheckHandler(controlObject, handler, parameter);
} }
@ -878,10 +878,20 @@ IedServer_setWaitForExecutionHandler(IedServer self, DataObject* node, ControlWa
{ {
ControlObject* controlObject = lookupControlObject(self, node); ControlObject* controlObject = lookupControlObject(self, node);
if (controlObject != NULL) if (controlObject)
ControlObject_installWaitForExecutionHandler(controlObject, handler, parameter); ControlObject_installWaitForExecutionHandler(controlObject, handler, parameter);
} }
void
IedServer_setSelectStateChangedHandler(IedServer self, DataObject* node, ControlSelectStateChangedHandler handler,
void* parameter)
{
ControlObject* controlObject = lookupControlObject(self, node);
if (controlObject)
ControlObject_installSelectStateChangedHandler(controlObject, handler, parameter);
}
void void
IedServer_updateCtlModel(IedServer self, DataObject* ctlObject, ControlModel value) IedServer_updateCtlModel(IedServer self, DataObject* ctlObject, ControlModel value)
{ {

@ -408,7 +408,7 @@ updateGenericTrackingObjectValues(MmsMapping* self, ControlObject* controlObject
#endif /* (CONFIG_IEC61850_SERVICE_TRACKING == 1) */ #endif /* (CONFIG_IEC61850_SERVICE_TRACKING == 1) */
static void static void
unselectObject(ControlObject* self); unselectObject(ControlObject* self, SelectStateChangedReason reason);
static void static void
setState(ControlObject* self, int newState) setState(ControlObject* self, int newState)
@ -488,18 +488,34 @@ selectObject(ControlObject* self, uint64_t selectTime, MmsServerConnection conne
self->mmsConnection = connection; self->mmsConnection = connection;
setStSeld(self, true); setStSeld(self, true);
setState(self, STATE_READY); setState(self, STATE_READY);
if (self->selectStateChangedHandler) {
self->selectStateChangedHandler((ControlAction) self,
self->selectStateChangedHandlerParameter,
true,
SELECT_STATE_REASON_SELECTED);
}
} }
static void static void
unselectObject(ControlObject* self) unselectObject(ControlObject* self, SelectStateChangedReason reason)
{ {
if (getState(self) != STATE_UNSELECTED) {
setState(self, STATE_UNSELECTED); setState(self, STATE_UNSELECTED);
setStSeld(self, false); setStSeld(self, false);
if (self->selectStateChangedHandler) {
self->selectStateChangedHandler((ControlAction) self,
self->selectStateChangedHandlerParameter,
false,
reason);
}
if (DEBUG_IED_SERVER) if (DEBUG_IED_SERVER)
printf("IED_SERVER: control %s/%s.%s unselected\n", MmsDomain_getName(self->mmsDomain), self->lnName, self->name); printf("IED_SERVER: control %s/%s.%s unselected\n", MmsDomain_getName(self->mmsDomain), self->lnName, self->name);
} }
}
static void static void
checkSelectTimeout(ControlObject* self, uint64_t currentTime) checkSelectTimeout(ControlObject* self, uint64_t currentTime)
@ -513,7 +529,7 @@ checkSelectTimeout(ControlObject* self, uint64_t currentTime)
printf("IED_SERVER: select-timeout (timeout-val = %i) for control %s/%s.%s\n", printf("IED_SERVER: select-timeout (timeout-val = %i) for control %s/%s.%s\n",
self->selectTimeout, MmsDomain_getName(self->mmsDomain), self->lnName, self->name); self->selectTimeout, MmsDomain_getName(self->mmsDomain), self->lnName, self->name);
unselectObject(self); unselectObject(self, SELECT_STATE_REASON_TIMEOUT);
} }
} }
} }
@ -620,16 +636,16 @@ exitControlTask(ControlObject* self)
} }
static void static void
abortControlOperation(ControlObject* self, bool unconditional) abortControlOperation(ControlObject* self, bool unconditional, SelectStateChangedReason reason)
{ {
if ((self->ctlModel == 2) || (self->ctlModel == 4)) { if ((self->ctlModel == 2) || (self->ctlModel == 4)) {
if (unconditional) { if (unconditional) {
unselectObject(self); unselectObject(self, reason);
} }
else { else {
if (isSboClassOperateOnce(self)) if (isSboClassOperateOnce(self))
unselectObject(self); unselectObject(self, reason);
else else
setState(self, STATE_READY); setState(self, STATE_READY);
} }
@ -804,7 +820,7 @@ executeStateMachine:
resetAddCause(controlObject); resetAddCause(controlObject);
abortControlOperation(controlObject, false); abortControlOperation(controlObject, false, SELECT_STATE_REASON_OPERATE_FAILED);
exitControlTask(controlObject); exitControlTask(controlObject);
} }
else if (dynamicCheckResult == CONTROL_RESULT_OK) { else if (dynamicCheckResult == CONTROL_RESULT_OK) {
@ -854,6 +870,8 @@ executeStateMachine:
updateGenericTrackingObjectValues(self, controlObject, IEC61850_SERVICE_TYPE_COMMAND_TERMINATION, IEC61850_SERVICE_ERROR_NO_ERROR); updateGenericTrackingObjectValues(self, controlObject, IEC61850_SERVICE_TYPE_COMMAND_TERMINATION, IEC61850_SERVICE_ERROR_NO_ERROR);
#endif /* (CONFIG_IEC61850_SERVICE_TRACKING == 1) */ #endif /* (CONFIG_IEC61850_SERVICE_TRACKING == 1) */
} }
abortControlOperation(controlObject, false, SELECT_STATE_REASON_OPERATED);
} }
else { else {
@ -867,9 +885,10 @@ executeStateMachine:
#if (CONFIG_IEC61850_SERVICE_TRACKING == 1) #if (CONFIG_IEC61850_SERVICE_TRACKING == 1)
updateGenericTrackingObjectValues(self, controlObject, IEC61850_SERVICE_TYPE_COMMAND_TERMINATION, IEC61850_SERVICE_ERROR_FAILED_DUE_TO_SERVER_CONSTRAINT); updateGenericTrackingObjectValues(self, controlObject, IEC61850_SERVICE_TYPE_COMMAND_TERMINATION, IEC61850_SERVICE_ERROR_FAILED_DUE_TO_SERVER_CONSTRAINT);
#endif /* (CONFIG_IEC61850_SERVICE_TRACKING == 1) */ #endif /* (CONFIG_IEC61850_SERVICE_TRACKING == 1) */
abortControlOperation(controlObject, false, SELECT_STATE_REASON_OPERATE_FAILED);
} }
abortControlOperation(controlObject, false);
exitControlTask(controlObject); exitControlTask(controlObject);
setOpOk(controlObject, false, currentTimeInMs); setOpOk(controlObject, false, currentTimeInMs);
@ -1292,7 +1311,7 @@ bool
ControlObject_unselect(ControlObject* self, MmsServerConnection connection) ControlObject_unselect(ControlObject* self, MmsServerConnection connection)
{ {
if (self->mmsConnection == connection) { if (self->mmsConnection == connection) {
abortControlOperation(self, true); abortControlOperation(self, true, SELECT_STATE_REASON_DISCONNECTED);
return true; return true;
} }
else else
@ -1321,6 +1340,14 @@ ControlObject_installWaitForExecutionHandler(ControlObject* self, ControlWaitFor
self->waitForExecutionHandlerParameter = parameter; self->waitForExecutionHandlerParameter = parameter;
} }
void
ControlObject_installSelectStateChangedHandler(ControlObject* self, ControlSelectStateChangedHandler handler,
void* parameter)
{
self->selectStateChangedHandler = handler;
self->selectStateChangedHandlerParameter = parameter;
}
void void
ControlObject_updateControlModel(ControlObject* self, ControlModel value, DataObject* ctlObject) ControlObject_updateControlModel(ControlObject* self, ControlModel value, DataObject* ctlObject)
{ {
@ -1394,7 +1421,7 @@ Control_processControlActions(MmsMapping* self, uint64_t currentTimeInMs)
/* leave state Perform Test */ /* leave state Perform Test */
setOpRcvd(controlObject, false); setOpRcvd(controlObject, false);
abortControlOperation(controlObject, false); abortControlOperation(controlObject, false, SELECT_STATE_REASON_OPERATE_FAILED);
resetAddCause(controlObject); resetAddCause(controlObject);
} }
@ -2056,7 +2083,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
CONTROL_ERROR_NO_ERROR, ADD_CAUSE_INCONSISTENT_PARAMETERS, CONTROL_ERROR_NO_ERROR, ADD_CAUSE_INCONSISTENT_PARAMETERS,
ctlNum, origin, true); ctlNum, origin, true);
unselectObject(controlObject); unselectObject(controlObject, SELECT_STATE_REASON_OPERATE_FAILED);
} }
goto free_and_return; goto free_and_return;
@ -2112,7 +2139,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
CONTROL_ERROR_NO_ERROR, ADD_CAUSE_INCONSISTENT_PARAMETERS, CONTROL_ERROR_NO_ERROR, ADD_CAUSE_INCONSISTENT_PARAMETERS,
ctlNum, origin, true); ctlNum, origin, true);
unselectObject(controlObject); unselectObject(controlObject, SELECT_STATE_REASON_OPERATE_FAILED);
goto free_and_return; goto free_and_return;
} }
@ -2197,10 +2224,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
/* leave state Perform Test */ /* leave state Perform Test */
setOpRcvd(controlObject, false); setOpRcvd(controlObject, false);
abortControlOperation(controlObject, false); abortControlOperation(controlObject, false, SELECT_STATE_REASON_OPERATE_FAILED);
if ((controlObject->ctlModel == 2) || (controlObject->ctlModel == 4))
unselectObject(controlObject);
} }
} }
@ -2254,7 +2278,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
if (state != STATE_UNSELECTED) { if (state != STATE_UNSELECTED) {
if (controlObject->mmsConnection == connection) { if (controlObject->mmsConnection == connection) {
indication = DATA_ACCESS_ERROR_SUCCESS; indication = DATA_ACCESS_ERROR_SUCCESS;
unselectObject(controlObject); unselectObject(controlObject, SELECT_STATE_REASON_CANCELED);
goto free_and_return; goto free_and_return;
} }
else { else {
@ -2271,7 +2295,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
if (controlObject->timeActivatedOperate) { if (controlObject->timeActivatedOperate) {
controlObject->timeActivatedOperate = false; controlObject->timeActivatedOperate = false;
abortControlOperation(controlObject, false); abortControlOperation(controlObject, false, SELECT_STATE_REASON_CANCELED);
indication = DATA_ACCESS_ERROR_SUCCESS; indication = DATA_ACCESS_ERROR_SUCCESS;

Loading…
Cancel
Save