- 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 LIB61850_INTERNAL bool
ControlObject_select(ControlObject* self, MmsServerConnection connection); ControlObject_select(ControlObject* self, MmsServerConnection connection);
LIB61850_INTERNAL bool
ControlObject_unselect(ControlObject* self, MmsServerConnection connection);
LIB61850_INTERNAL void LIB61850_INTERNAL void
ControlObject_installListener(ControlObject* self, ControlHandler listener, void* parameter); ControlObject_installListener(ControlObject* self, ControlHandler listener, void* parameter);

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

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

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

Loading…
Cancel
Save