- IED server: improved control state machine performance (LIB61850-312)

pull/383/head
Michael Zillgith 4 years ago
parent f0651adb36
commit 9d9f03585f

@ -173,9 +173,6 @@ ControlObject_getDomain(ControlObject* self);
LIB61850_INTERNAL bool
ControlObject_select(ControlObject* self, MmsServerConnection connection);
LIB61850_INTERNAL bool
ControlObject_unselect(ControlObject* self, MmsServerConnection connection);
LIB61850_INTERNAL void
ControlObject_installListener(ControlObject* self, ControlHandler listener, void* parameter);

@ -285,6 +285,7 @@ struct sMmsMapping {
#endif
LinkedList controlObjects;
uint64_t nextControlTimeout; /* next timeout in one of the control state machines */
LinkedList attributeAccessHandlers;

@ -68,6 +68,9 @@ ControlObject_sendCommandTerminationPositive(ControlObject* self);
void
ControlObject_sendCommandTerminationNegative(ControlObject* self);
bool
ControlObject_unselect(ControlObject* self, MmsServerConnection connection, MmsMapping* mmsMapping);
static MmsValue*
getOperParameterCtlNum(MmsValue* operParameters)
{
@ -413,7 +416,14 @@ updateGenericTrackingObjectValues(MmsMapping* self, ControlObject* controlObject
#endif /* (CONFIG_IEC61850_SERVICE_TRACKING == 1) */
static void
unselectObject(ControlObject* self, SelectStateChangedReason reason);
unselectObject(ControlObject* self, SelectStateChangedReason reason, MmsMapping* mmsMapping);
static void
updateNextControlTimeout(MmsMapping* self, uint64_t timeout)
{
if (timeout < self->nextControlTimeout)
self->nextControlTimeout = timeout;
}
static void
setState(ControlObject* self, int newState)
@ -482,7 +492,7 @@ updateSboTimeoutValue(ControlObject* self)
}
static void
selectObject(ControlObject* self, uint64_t selectTime, MmsServerConnection connection)
selectObject(ControlObject* self, uint64_t selectTime, MmsServerConnection connection, MmsMapping* mmsMapping)
{
if (DEBUG_IED_SERVER)
printf("IED_SERVER: control %s/%s.%s selected\n", MmsDomain_getName(self->mmsDomain), self->lnName, self->name);
@ -494,6 +504,8 @@ selectObject(ControlObject* self, uint64_t selectTime, MmsServerConnection conne
setStSeld(self, true);
setState(self, STATE_READY);
updateNextControlTimeout(mmsMapping, selectTime);
if (self->selectStateChangedHandler) {
self->selectStateChangedHandler((ControlAction) self,
self->selectStateChangedHandlerParameter,
@ -503,13 +515,16 @@ selectObject(ControlObject* self, uint64_t selectTime, MmsServerConnection conne
}
static void
unselectObject(ControlObject* self, SelectStateChangedReason reason)
unselectObject(ControlObject* self, SelectStateChangedReason reason, MmsMapping* mmsMapping)
{
if (getState(self) != STATE_UNSELECTED) {
setState(self, STATE_UNSELECTED);
setStSeld(self, false);
/* trigger timeout check in next cycle to update the next timeout value */
mmsMapping->nextControlTimeout = 0;
if (self->selectStateChangedHandler) {
self->selectStateChangedHandler((ControlAction) self,
self->selectStateChangedHandlerParameter,
@ -523,7 +538,7 @@ unselectObject(ControlObject* self, SelectStateChangedReason reason)
}
static void
checkSelectTimeout(ControlObject* self, uint64_t currentTime)
checkSelectTimeout(ControlObject* self, uint64_t currentTime, MmsMapping* mmsMapping)
{
if ((self->ctlModel == 2) || (self->ctlModel == 4)) {
@ -534,7 +549,10 @@ checkSelectTimeout(ControlObject* self, uint64_t currentTime)
printf("IED_SERVER: select-timeout (timeout-val = %i) for control %s/%s.%s\n",
self->selectTimeout, MmsDomain_getName(self->mmsDomain), self->lnName, self->name);
unselectObject(self, SELECT_STATE_REASON_TIMEOUT);
unselectObject(self, SELECT_STATE_REASON_TIMEOUT, mmsMapping);
}
else {
updateNextControlTimeout(mmsMapping, self->selectTime + self->selectTimeout);
}
}
}
@ -641,16 +659,16 @@ exitControlTask(ControlObject* self)
}
static void
abortControlOperation(ControlObject* self, bool unconditional, SelectStateChangedReason reason)
abortControlOperation(ControlObject* self, bool unconditional, SelectStateChangedReason reason, MmsMapping* mmsMapping)
{
if ((self->ctlModel == 2) || (self->ctlModel == 4)) {
if (unconditional) {
unselectObject(self, reason);
unselectObject(self, reason, mmsMapping);
}
else {
if (isSboClassOperateOnce(self))
unselectObject(self, reason);
unselectObject(self, reason, mmsMapping);
else
setState(self, STATE_READY);
}
@ -716,7 +734,7 @@ executeStateMachine:
if (checkHandlerResult == CONTROL_ACCEPTED) {
LinkedList_add(values, controlObject->sbo);
selectObject(controlObject, Hal_getTimeInMs(), controlObject->mmsConnection);
selectObject(controlObject, Hal_getTimeInMs(), controlObject->mmsConnection, self);
#if (CONFIG_IEC61850_SERVICE_TRACKING == 1)
updateGenericTrackingObjectValues(self, controlObject, IEC61850_SERVICE_TYPE_SELECT, IEC61850_SERVICE_ERROR_NO_ERROR);
@ -740,7 +758,7 @@ executeStateMachine:
else if (controlObject->ctlModel == 4) {
if (checkHandlerResult == CONTROL_ACCEPTED) {
selectObject(controlObject, Hal_getTimeInMs(), controlObject->mmsConnection);
selectObject(controlObject, Hal_getTimeInMs(), controlObject->mmsConnection, self);
if (controlObject->ctlNumSt)
MmsValue_update(controlObject->ctlNumSt, controlObject->ctlNum);
@ -828,7 +846,7 @@ executeStateMachine:
resetAddCause(controlObject);
abortControlOperation(controlObject, false, SELECT_STATE_REASON_OPERATE_FAILED);
abortControlOperation(controlObject, false, SELECT_STATE_REASON_OPERATE_FAILED, self);
exitControlTask(controlObject);
}
else if (dynamicCheckResult == CONTROL_RESULT_OK) {
@ -879,7 +897,7 @@ executeStateMachine:
#endif /* (CONFIG_IEC61850_SERVICE_TRACKING == 1) */
}
abortControlOperation(controlObject, false, SELECT_STATE_REASON_OPERATED);
abortControlOperation(controlObject, false, SELECT_STATE_REASON_OPERATED, self);
}
else {
@ -894,7 +912,7 @@ executeStateMachine:
updateGenericTrackingObjectValues(self, controlObject, IEC61850_SERVICE_TYPE_COMMAND_TERMINATION, IEC61850_SERVICE_ERROR_FAILED_DUE_TO_SERVER_CONSTRAINT);
#endif /* (CONFIG_IEC61850_SERVICE_TRACKING == 1) */
abortControlOperation(controlObject, false, SELECT_STATE_REASON_OPERATE_FAILED);
abortControlOperation(controlObject, false, SELECT_STATE_REASON_OPERATE_FAILED, self);
}
exitControlTask(controlObject);
@ -903,6 +921,9 @@ executeStateMachine:
resetAddCause(controlObject);
}
else {
updateNextControlTimeout(self, currentTimeInMs + 10);
}
}
break;
@ -1081,7 +1102,6 @@ ControlObject_initialize(ControlObject* self)
mxValType = da->type;
}
if (stValType == IEC61850_BOOLEAN && ctlValType == IEC61850_BOOLEAN)
{
self->cdc = CST_SPCTRK; /* SPC */
@ -1331,10 +1351,10 @@ ControlObject_getMmsValue(ControlObject* self)
}
bool
ControlObject_unselect(ControlObject* self, MmsServerConnection connection)
ControlObject_unselect(ControlObject* self, MmsServerConnection connection, MmsMapping* mmsMapping)
{
if (self->mmsConnection == connection) {
abortControlOperation(self, true, SELECT_STATE_REASON_DISCONNECTED);
abortControlOperation(self, true, SELECT_STATE_REASON_DISCONNECTED, mmsMapping);
return true;
}
else
@ -1391,76 +1411,95 @@ ControlObject_updateControlModel(ControlObject* self, ControlModel value, DataOb
void
Control_processControlActions(MmsMapping* self, uint64_t currentTimeInMs)
{
LinkedList element = LinkedList_getNext(self->controlObjects);
if (currentTimeInMs >= self->nextControlTimeout) {
while (element != NULL) {
ControlObject* controlObject = (ControlObject*) element->data;
/* invalidate nextControlTimeout */
self->nextControlTimeout = (uint64_t) 0xFFFFFFFFFFFFFFFFLLU;
if (controlObject->state == STATE_WAIT_FOR_ACTIVATION_TIME) {
LinkedList element = LinkedList_getNext(self->controlObjects);
if (controlObject->operateTime <= currentTimeInMs) {
while (element != NULL) {
ControlObject* controlObject = (ControlObject*) element->data;
/* enter state Perform Test */
setOpRcvd(controlObject, true);
if (controlObject->state != STATE_UNSELECTED) {
if (DEBUG_IED_SERVER)
printf("IED_SERVER: time activated operate: perform test\n");
if ((controlObject->ctlModel == 1) || (controlObject->ctlModel == 3)) {
if (controlObject->state == STATE_READY) {
element = LinkedList_getNext(element);
continue;
}
}
controlObject->timeActivatedOperate = false;
if (controlObject->state == STATE_WAIT_FOR_ACTIVATION_TIME) {
CheckHandlerResult checkResult = CONTROL_ACCEPTED;
if (controlObject->operateTime <= currentTimeInMs) {
if (controlObject->checkHandler != NULL) { /* perform operative tests */
/* enter state Perform Test */
setOpRcvd(controlObject, true);
controlObject->errorValue = CONTROL_ERROR_NO_ERROR;
controlObject->addCauseValue = ADD_CAUSE_BLOCKED_BY_INTERLOCKING;
if (DEBUG_IED_SERVER)
printf("IED_SERVER: time activated operate: perform test\n");
checkResult = controlObject->checkHandler((ControlAction) controlObject,
controlObject->checkHandlerParameter, controlObject->ctlVal, controlObject->testMode,
controlObject->interlockCheck);
}
controlObject->timeActivatedOperate = false;
if (checkResult == CONTROL_ACCEPTED) {
CheckHandlerResult checkResult = CONTROL_ACCEPTED;
if (DEBUG_IED_SERVER)
printf("IED_SERVER: time activated operate: command accepted\n");
if (controlObject->checkHandler != NULL) { /* perform operative tests */
/* leave state Perform Test */
setOpRcvd(controlObject, false);
controlObject->errorValue = CONTROL_ERROR_NO_ERROR;
controlObject->addCauseValue = ADD_CAUSE_BLOCKED_BY_INTERLOCKING;
executeControlTask(self, controlObject, currentTimeInMs);
}
else {
checkResult = controlObject->checkHandler((ControlAction) controlObject,
controlObject->checkHandlerParameter, controlObject->ctlVal, controlObject->testMode,
controlObject->interlockCheck);
}
if (checkResult == CONTROL_ACCEPTED) {
ControlObject_sendLastApplError(controlObject, controlObject->mmsConnection, "Oper",
controlObject->errorValue, controlObject->addCauseValue,
controlObject->ctlNum, controlObject->origin, false);
if (DEBUG_IED_SERVER)
printf("IED_SERVER: time activated operate: command accepted\n");
/* leave state Perform Test */
setOpRcvd(controlObject, false);
executeControlTask(self, controlObject, currentTimeInMs);
}
else {
ControlObject_sendLastApplError(controlObject, controlObject->mmsConnection, "Oper",
controlObject->errorValue, controlObject->addCauseValue,
controlObject->ctlNum, controlObject->origin, false);
#if (CONFIG_IEC61850_SERVICE_TRACKING == 1)
updateGenericTrackingObjectValues(self, controlObject, IEC61850_SERVICE_TYPE_TIME_ACTIVATED_OPERATE,
convertCheckHandlerResultToServiceError(checkResult));
updateGenericTrackingObjectValues(self, controlObject, IEC61850_SERVICE_TYPE_TIME_ACTIVATED_OPERATE,
convertCheckHandlerResultToServiceError(checkResult));
#endif /* (CONFIG_IEC61850_SERVICE_TRACKING == 1) */
/* leave state Perform Test */
setOpRcvd(controlObject, false);
/* leave state Perform Test */
setOpRcvd(controlObject, false);
abortControlOperation(controlObject, false, SELECT_STATE_REASON_OPERATE_FAILED);
abortControlOperation(controlObject, false, SELECT_STATE_REASON_OPERATE_FAILED, self);
resetAddCause(controlObject);
}
}
resetAddCause(controlObject);
}
}
else {
updateNextControlTimeout(self, controlObject->operateTime);
}
} /* if (controlObject->state == STATE_WAIT_FOR_ACTICATION_TIME) */
else if (!((controlObject->state == STATE_UNSELECTED) || (controlObject->state == STATE_READY))) {
executeControlTask(self, controlObject, currentTimeInMs);
}
else if (controlObject->state == STATE_READY) {
checkSelectTimeout(controlObject, currentTimeInMs);
}
} /* if (controlObject->state == STATE_WAIT_FOR_ACTICATION_TIME) */
else if (!((controlObject->state == STATE_UNSELECTED) || (controlObject->state == STATE_READY))) {
executeControlTask(self, controlObject, currentTimeInMs);
}
else if (controlObject->state == STATE_READY) {
checkSelectTimeout(controlObject, currentTimeInMs, self);
}
}
ControlObject_handlePendingEvents(controlObject);
ControlObject_handlePendingEvents(controlObject);
element = LinkedList_getNext(element);
element = LinkedList_getNext(element);
}
}
}
@ -1784,7 +1823,7 @@ Control_readAccessControlObject(MmsMapping* self, MmsDomain* domain, char* varia
value = &emptyString;
if (isDirectAccess == true) {
checkSelectTimeout(controlObject, currentTime);
checkSelectTimeout(controlObject, currentTime, self);
if (getState(controlObject) == STATE_UNSELECTED) {
CheckHandlerResult checkResult = CONTROL_ACCEPTED;
@ -1805,7 +1844,7 @@ Control_readAccessControlObject(MmsMapping* self, MmsDomain* domain, char* varia
}
if (checkResult == CONTROL_ACCEPTED) {
selectObject(controlObject, currentTime, connection);
selectObject(controlObject, currentTime, connection, self);
value = controlObject->sbo;
#if (CONFIG_IEC61850_SERVICE_TRACKING == 1)
@ -2008,7 +2047,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
uint64_t currentTime = Hal_getTimeInMs();
checkSelectTimeout(controlObject, currentTime);
checkSelectTimeout(controlObject, currentTime, self);
if (state != STATE_UNSELECTED) {
@ -2065,7 +2104,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
}
if (checkResult == CONTROL_ACCEPTED) {
selectObject(controlObject, currentTime, connection);
selectObject(controlObject, currentTime, connection, self);
indication = DATA_ACCESS_ERROR_SUCCESS;
@ -2131,7 +2170,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
ctlNum, origin, true);
if ((controlObject->ctlModel == 2) || (controlObject->ctlModel == 4)) {
unselectObject(controlObject, SELECT_STATE_REASON_OPERATE_FAILED);
unselectObject(controlObject, SELECT_STATE_REASON_OPERATE_FAILED, self);
}
goto free_and_return;
@ -2139,7 +2178,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
uint64_t currentTime = Hal_getTimeInMs();
checkSelectTimeout(controlObject, currentTime);
checkSelectTimeout(controlObject, currentTime, self);
int state = getState(controlObject);
@ -2186,7 +2225,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
CONTROL_ERROR_NO_ERROR, ADD_CAUSE_INCONSISTENT_PARAMETERS,
ctlNum, origin, true);
unselectObject(controlObject, SELECT_STATE_REASON_OPERATE_FAILED);
unselectObject(controlObject, SELECT_STATE_REASON_OPERATE_FAILED, self);
goto free_and_return;
}
@ -2221,6 +2260,8 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
setState(controlObject, STATE_WAIT_FOR_ACTIVATION_TIME);
updateNextControlTimeout(self, controlObject->operateTime);
if (DEBUG_IED_SERVER)
printf("IED_SERVER: Oper - activate time activated control\n");
@ -2267,6 +2308,8 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
setOpRcvd(controlObject, false);
initiateControlTask(controlObject);
updateNextControlTimeout(self, currentTime);
}
else {
indication = (MmsDataAccessError) checkResult;
@ -2274,7 +2317,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
/* leave state Perform Test */
setOpRcvd(controlObject, false);
abortControlOperation(controlObject, false, SELECT_STATE_REASON_OPERATE_FAILED);
abortControlOperation(controlObject, false, SELECT_STATE_REASON_OPERATE_FAILED, self);
if ((controlObject->ctlModel == 3) || (controlObject->ctlModel == 4)) {
ControlObject_sendLastApplError(controlObject, connection, "Oper",
@ -2344,7 +2387,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
if (state != STATE_UNSELECTED) {
if (controlObject->mmsConnection == connection) {
indication = DATA_ACCESS_ERROR_SUCCESS;
unselectObject(controlObject, SELECT_STATE_REASON_CANCELED);
unselectObject(controlObject, SELECT_STATE_REASON_CANCELED, self);
goto free_and_return;
}
else {
@ -2361,7 +2404,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
if (controlObject->timeActivatedOperate) {
controlObject->timeActivatedOperate = false;
abortControlOperation(controlObject, false, SELECT_STATE_REASON_CANCELED);
abortControlOperation(controlObject, false, SELECT_STATE_REASON_CANCELED, self);
indication = DATA_ACCESS_ERROR_SUCCESS;

@ -1,7 +1,7 @@
/*
* mms_mapping.c
*
* Copyright 2013-2021 Michael Zillgith
* Copyright 2013-2022 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -72,6 +72,9 @@ typedef struct
MmsValue*
Control_readAccessControlObject(MmsMapping* self, MmsDomain* domain, char* variableIdOrig,
MmsServerConnection connection, bool isDirectAccess);
bool
ControlObject_unselect(ControlObject* self, MmsServerConnection connection, MmsMapping* mmsMapping);
#endif
void /* Create PHYCOMADDR ACSI type instance */
@ -2019,6 +2022,7 @@ MmsMapping_create(IedModel* model, IedServer iedServer)
#if (CONFIG_IEC61850_CONTROL_SERVICE == 1)
self->controlObjects = LinkedList_create();
self->nextControlTimeout = 0xffffffffffffffffLLU;
#endif
#if (CONFIG_IEC61850_SETTING_GROUPS == 1)
@ -3148,7 +3152,7 @@ unselectControlsForConnection(MmsMapping* self, MmsServerConnection connection)
while (controlObjectElement != NULL) {
ControlObject* controlObject = (ControlObject*) controlObjectElement->data;
ControlObject_unselect(controlObject, connection);
ControlObject_unselect(controlObject, connection, self);
controlObjectElement = LinkedList_getNext(controlObjectElement);
}
@ -3166,11 +3170,6 @@ mmsConnectionHandler(void* parameter, MmsServerConnection connection, MmsServerE
else if (event == MMS_SERVER_CONNECTION_CLOSED) {
ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection);
if (clientConnection == NULL) {
printf("clientConnection == NULL -> exit\n");
exit(-1);
}
/* call user provided handler function */
if (self->connectionIndicationHandler != NULL)
self->connectionIndicationHandler(self->iedServer, clientConnection, false,

Loading…
Cancel
Save