diff --git a/examples/server_example_control/simpleIO_control_tests.icd b/examples/server_example_control/simpleIO_control_tests.icd index 4ff831ec..78b1a68f 100644 --- a/examples/server_example_control/simpleIO_control_tests.icd +++ b/examples/server_example_control/simpleIO_control_tests.icd @@ -7,7 +7,7 @@ Station bus
-

192.168.2.223

+

0.0.0.0

255.255.255.0

10.0.0.1

0001

@@ -35,6 +35,28 @@ + + + + + + + + + + + + + + + + + + + + + + status-only @@ -57,6 +79,9 @@ sbo-with-normal-security + + 2000 + @@ -228,7 +253,12 @@ + + + + + diff --git a/examples/server_example_control/static_model.c b/examples/server_example_control/static_model.c index 6921b4f4..d39a9706 100644 --- a/examples/server_example_control/static_model.c +++ b/examples/server_example_control/static_model.c @@ -7,7 +7,149 @@ static void initializeValues(); +extern DataSet iedModelds_GenericIO_LLN0_ControlEvents; + + +extern DataSetEntry iedModelds_GenericIO_LLN0_ControlEvents_fcda0; +extern DataSetEntry iedModelds_GenericIO_LLN0_ControlEvents_fcda1; +extern DataSetEntry iedModelds_GenericIO_LLN0_ControlEvents_fcda2; +extern DataSetEntry iedModelds_GenericIO_LLN0_ControlEvents_fcda3; +extern DataSetEntry iedModelds_GenericIO_LLN0_ControlEvents_fcda4; +extern DataSetEntry iedModelds_GenericIO_LLN0_ControlEvents_fcda5; +extern DataSetEntry iedModelds_GenericIO_LLN0_ControlEvents_fcda6; +extern DataSetEntry iedModelds_GenericIO_LLN0_ControlEvents_fcda7; +extern DataSetEntry iedModelds_GenericIO_LLN0_ControlEvents_fcda8; +extern DataSetEntry iedModelds_GenericIO_LLN0_ControlEvents_fcda9; +extern DataSetEntry iedModelds_GenericIO_LLN0_ControlEvents_fcda10; +extern DataSetEntry iedModelds_GenericIO_LLN0_ControlEvents_fcda11; + +DataSetEntry iedModelds_GenericIO_LLN0_ControlEvents_fcda0 = { + "GenericIO", + false, + "GGIO1$ST$SPCSO1$stVal", + -1, + NULL, + NULL, + &iedModelds_GenericIO_LLN0_ControlEvents_fcda1 +}; + +DataSetEntry iedModelds_GenericIO_LLN0_ControlEvents_fcda1 = { + "GenericIO", + false, + "GGIO1$ST$SPCSO2$stVal", + -1, + NULL, + NULL, + &iedModelds_GenericIO_LLN0_ControlEvents_fcda2 +}; + +DataSetEntry iedModelds_GenericIO_LLN0_ControlEvents_fcda2 = { + "GenericIO", + false, + "GGIO1$ST$SPCSO3$stVal", + -1, + NULL, + NULL, + &iedModelds_GenericIO_LLN0_ControlEvents_fcda3 +}; + +DataSetEntry iedModelds_GenericIO_LLN0_ControlEvents_fcda3 = { + "GenericIO", + false, + "GGIO1$ST$SPCSO4$stVal", + -1, + NULL, + NULL, + &iedModelds_GenericIO_LLN0_ControlEvents_fcda4 +}; + +DataSetEntry iedModelds_GenericIO_LLN0_ControlEvents_fcda4 = { + "GenericIO", + false, + "GGIO1$ST$SPCSO5$stVal", + -1, + NULL, + NULL, + &iedModelds_GenericIO_LLN0_ControlEvents_fcda5 +}; + +DataSetEntry iedModelds_GenericIO_LLN0_ControlEvents_fcda5 = { + "GenericIO", + false, + "GGIO1$ST$SPCSO6$stVal", + -1, + NULL, + NULL, + &iedModelds_GenericIO_LLN0_ControlEvents_fcda6 +}; + +DataSetEntry iedModelds_GenericIO_LLN0_ControlEvents_fcda6 = { + "GenericIO", + false, + "GGIO1$ST$SPCSO7$stVal", + -1, + NULL, + NULL, + &iedModelds_GenericIO_LLN0_ControlEvents_fcda7 +}; + +DataSetEntry iedModelds_GenericIO_LLN0_ControlEvents_fcda7 = { + "GenericIO", + false, + "GGIO1$ST$SPCSO8$stVal", + -1, + NULL, + NULL, + &iedModelds_GenericIO_LLN0_ControlEvents_fcda8 +}; +DataSetEntry iedModelds_GenericIO_LLN0_ControlEvents_fcda8 = { + "GenericIO", + false, + "GGIO1$ST$SPCSO9$stVal", + -1, + NULL, + NULL, + &iedModelds_GenericIO_LLN0_ControlEvents_fcda9 +}; + +DataSetEntry iedModelds_GenericIO_LLN0_ControlEvents_fcda9 = { + "GenericIO", + false, + "GGIO1$ST$SPCSO2$stSeld", + -1, + NULL, + NULL, + &iedModelds_GenericIO_LLN0_ControlEvents_fcda10 +}; + +DataSetEntry iedModelds_GenericIO_LLN0_ControlEvents_fcda10 = { + "GenericIO", + false, + "GGIO1$OR$SPCSO2$opRcvd", + -1, + NULL, + NULL, + &iedModelds_GenericIO_LLN0_ControlEvents_fcda11 +}; + +DataSetEntry iedModelds_GenericIO_LLN0_ControlEvents_fcda11 = { + "GenericIO", + false, + "GGIO1$OR$SPCSO2$opOk", + -1, + NULL, + NULL, + NULL +}; + +DataSet iedModelds_GenericIO_LLN0_ControlEvents = { + "GenericIO", + "LLN0$ControlEvents", + 12, + &iedModelds_GenericIO_LLN0_ControlEvents_fcda0, + NULL +}; LogicalDevice iedModel_GenericIO = { LogicalDeviceModelType, @@ -1264,7 +1406,7 @@ DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_t = { DataAttributeModelType, "t", (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2, - (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_ctlModel, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_stSeld, NULL, 0, IEC61850_FC_ST, @@ -1273,11 +1415,63 @@ DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_t = { NULL, 0}; +DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_stSeld = { + DataAttributeModelType, + "stSeld", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_opRcvd, + NULL, + 0, + IEC61850_FC_ST, + IEC61850_BOOLEAN, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_opRcvd = { + DataAttributeModelType, + "opRcvd", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_opOk, + NULL, + 0, + IEC61850_FC_OR, + IEC61850_BOOLEAN, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_opOk = { + DataAttributeModelType, + "opOk", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_tOpOk, + NULL, + 0, + IEC61850_FC_OR, + IEC61850_BOOLEAN, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_tOpOk = { + DataAttributeModelType, + "tOpOk", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_ctlModel, + NULL, + 0, + IEC61850_FC_OR, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_ctlModel = { DataAttributeModelType, "ctlModel", (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2, - (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_sboClass, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_sboTimeout, NULL, 0, IEC61850_FC_CF, @@ -1286,6 +1480,19 @@ DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_ctlModel = { NULL, 0}; +DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_sboTimeout = { + DataAttributeModelType, + "sboTimeout", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_sboClass, + NULL, + 0, + IEC61850_FC_CF, + IEC61850_INT32U, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_sboClass = { DataAttributeModelType, "sboClass", @@ -3868,7 +4075,11 @@ DataAttribute iedModel_GenericIO_GGIO1_Ind4_t = { NULL, 0}; +extern ReportControlBlock iedModel_GenericIO_LLN0_report0; +extern ReportControlBlock iedModel_GenericIO_LLN0_report1; +ReportControlBlock iedModel_GenericIO_LLN0_report0 = {&iedModel_GenericIO_LLN0, "ControlEventsRCB01", "ControlEvents", false, "ControlEvents", 1, 17, 239, 0, 1000, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, &iedModel_GenericIO_LLN0_report1}; +ReportControlBlock iedModel_GenericIO_LLN0_report1 = {&iedModel_GenericIO_LLN0, "ControlEventsRCB02", "ControlEvents", false, "ControlEvents", 1, 17, 239, 0, 1000, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, NULL}; @@ -3879,8 +4090,8 @@ DataAttribute iedModel_GenericIO_GGIO1_Ind4_t = { IedModel iedModel = { "simpleIO", &iedModel_GenericIO, - NULL, - NULL, + &iedModelds_GenericIO_LLN0_ControlEvents, + &iedModel_GenericIO_LLN0_report0, NULL, NULL, NULL, @@ -3901,6 +4112,8 @@ iedModel_GenericIO_GGIO1_SPCSO1_ctlModel.mmsValue = MmsValue_newIntegerFromInt32 iedModel_GenericIO_GGIO1_SPCSO2_ctlModel.mmsValue = MmsValue_newIntegerFromInt32(2); +iedModel_GenericIO_GGIO1_SPCSO2_sboTimeout.mmsValue = MmsValue_newUnsignedFromUint32(2000); + iedModel_GenericIO_GGIO1_SPCSO3_ctlModel.mmsValue = MmsValue_newIntegerFromInt32(3); iedModel_GenericIO_GGIO1_SPCSO4_ctlModel.mmsValue = MmsValue_newIntegerFromInt32(4); diff --git a/examples/server_example_control/static_model.h b/examples/server_example_control/static_model.h index 67c3967f..67d98597 100644 --- a/examples/server_example_control/static_model.h +++ b/examples/server_example_control/static_model.h @@ -115,7 +115,12 @@ extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Cancel_Test; extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_stVal; extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_q; extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_t; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_stSeld; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_opRcvd; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_opOk; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_tOpOk; extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_ctlModel; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_sboTimeout; extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_sboClass; extern DataObject iedModel_GenericIO_GGIO1_SPCSO3; extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper; @@ -425,7 +430,12 @@ extern DataAttribute iedModel_GenericIO_GGIO1_Ind4_t; #define IEDMODEL_GenericIO_GGIO1_SPCSO2_stVal (&iedModel_GenericIO_GGIO1_SPCSO2_stVal) #define IEDMODEL_GenericIO_GGIO1_SPCSO2_q (&iedModel_GenericIO_GGIO1_SPCSO2_q) #define IEDMODEL_GenericIO_GGIO1_SPCSO2_t (&iedModel_GenericIO_GGIO1_SPCSO2_t) +#define IEDMODEL_GenericIO_GGIO1_SPCSO2_stSeld (&iedModel_GenericIO_GGIO1_SPCSO2_stSeld) +#define IEDMODEL_GenericIO_GGIO1_SPCSO2_opRcvd (&iedModel_GenericIO_GGIO1_SPCSO2_opRcvd) +#define IEDMODEL_GenericIO_GGIO1_SPCSO2_opOk (&iedModel_GenericIO_GGIO1_SPCSO2_opOk) +#define IEDMODEL_GenericIO_GGIO1_SPCSO2_tOpOk (&iedModel_GenericIO_GGIO1_SPCSO2_tOpOk) #define IEDMODEL_GenericIO_GGIO1_SPCSO2_ctlModel (&iedModel_GenericIO_GGIO1_SPCSO2_ctlModel) +#define IEDMODEL_GenericIO_GGIO1_SPCSO2_sboTimeout (&iedModel_GenericIO_GGIO1_SPCSO2_sboTimeout) #define IEDMODEL_GenericIO_GGIO1_SPCSO2_sboClass (&iedModel_GenericIO_GGIO1_SPCSO2_sboClass) #define IEDMODEL_GenericIO_GGIO1_SPCSO3 (&iedModel_GenericIO_GGIO1_SPCSO3) #define IEDMODEL_GenericIO_GGIO1_SPCSO3_Oper (&iedModel_GenericIO_GGIO1_SPCSO3_Oper) diff --git a/src/iec61850/inc_private/control.h b/src/iec61850/inc_private/control.h index ce26a75c..1793d37e 100644 --- a/src/iec61850/inc_private/control.h +++ b/src/iec61850/inc_private/control.h @@ -1,7 +1,7 @@ /* * control.h * - * Copyright 2013-2018 Michael Zillgith + * Copyright 2013-2019 Michael Zillgith * * This file is part of libIEC61850. * @@ -45,8 +45,11 @@ struct sControlObject int state; + int pendingEvents:8; + #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore stateLock; + Semaphore pendingEventsLock; #endif MmsValue* mmsValue; @@ -65,8 +68,18 @@ struct sControlObject MmsValue* ctlNumSt; MmsValue* originSt; + /* for automatic update of stSeld attribute */ DataAttribute* stSeld; + /* for automatic update of opRcvd attribute */ + DataAttribute* opRcvd; + + /* for automatic update of opOk attribute */ + DataAttribute* opOk; + + /* for automatic update of tOpOk attribute */ + DataAttribute* tOpOk; + char ctlObjectName[130]; /* for LastAppIError */ diff --git a/src/iec61850/server/mms_mapping/control.c b/src/iec61850/server/mms_mapping/control.c index 435eb4f8..9715a39a 100644 --- a/src/iec61850/server/mms_mapping/control.c +++ b/src/iec61850/server/mms_mapping/control.c @@ -46,6 +46,13 @@ #define STATE_WAIT_FOR_EXECUTION 4 #define STATE_OPERATE 5 +#define PENDING_EVENT_SELECTED 1 +#define PENDING_EVENT_UNSELECTED 2 +#define PENDING_EVENT_OP_RCVD_TRUE 4 +#define PENDING_EVENT_OP_RCVD_FALSE 8 +#define PENDING_EVENT_OP_OK_TRUE 16 +#define PENDING_EVENT_OP_OK_FALSE 32 + void ControlObject_sendLastApplError(ControlObject* self, MmsServerConnection connection, char* ctlVariable, int error, ControlAddCause addCause, MmsValue* ctlNum, MmsValue* origin, bool handlerMode); @@ -95,6 +102,73 @@ getState(ControlObject* self) return state; } +static void +setStSeld(ControlObject* self, bool value) +{ + if (self->stSeld) { +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_wait(self->pendingEventsLock); +#endif + + if (value) + self->pendingEvents |= PENDING_EVENT_SELECTED; + else + self->pendingEvents |= PENDING_EVENT_UNSELECTED; + +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_post(self->pendingEventsLock); +#endif + } +} + +static void +setOpRcvd(ControlObject* self, bool value) +{ + if (self->opRcvd) { +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_wait(self->pendingEventsLock); +#endif + + if (value) + self->pendingEvents |= PENDING_EVENT_OP_RCVD_TRUE; + else + self->pendingEvents |= PENDING_EVENT_OP_RCVD_FALSE; + +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_post(self->pendingEventsLock); +#endif + } +} + +static void +setOpOk(ControlObject* self, bool value, uint64_t currentTimeInMs) +{ + if (self->opOk) { +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_wait(self->pendingEventsLock); +#endif + + if (value) { + if (self->tOpOk) { + MmsValue* timestamp = self->tOpOk->mmsValue; + + MmsValue_setUtcTimeMs(timestamp, currentTimeInMs); + + /* TODO update time quality */ + } + + + self->pendingEvents |= PENDING_EVENT_OP_OK_TRUE; + } + else + self->pendingEvents |= PENDING_EVENT_OP_OK_FALSE; + +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_post(self->pendingEventsLock); +#endif + } +} + static void updateSboTimeoutValue(ControlObject* self) { @@ -192,7 +266,7 @@ operateControl(ControlObject* self, MmsValue* value, uint64_t currentTime, bool } static void -executeControlTask(ControlObject* self) +executeControlTask(ControlObject* self, uint64_t currentTimeInMs) { int state; @@ -244,6 +318,8 @@ executeStateMachine: setState(self, STATE_OPERATE); + setOpOk(self, true, currentTimeInMs); + goto executeStateMachine; } } @@ -275,6 +351,8 @@ executeStateMachine: abortControlOperation(self); exitControlTask(self); + + setOpOk(self, false, currentTimeInMs); } } break; @@ -295,8 +373,9 @@ ControlObject_create(IedServer iedServer, MmsDomain* domain, char* lnName, char* #if (CONFIG_MMS_THREADLESS_STACK != 1) self->stateLock = Semaphore_create(1); + self->pendingEventsLock = Semaphore_create(1); - if (self->stateLock == NULL) { + if ((self->stateLock == NULL) || (self->pendingEventsLock == NULL)) { ControlObject_destroy(self); self = NULL; goto exit_function; @@ -404,6 +483,39 @@ ControlObject_initialize(ControlObject* self) printf("IED_SERVER: ERROR - stSeld of wrong type!\n"); } + char* opRcvdName = StringUtils_createStringInBuffer(strBuf, 6, self->mmsDomain->domainName, "/", self->lnName, ".", self->name, ".opRcvd"); + + self->opRcvd = (DataAttribute*) IedModel_getModelNodeByObjectReference(self->iedServer->model, opRcvdName); + + if ((self->opRcvd) && (self->opRcvd->type != IEC61850_BOOLEAN)) { + self->opRcvd = NULL; + + if (DEBUG_IED_SERVER) + printf("IED_SERVER: ERROR - opRcvd of wrong type!\n"); + } + + char* opOkName = StringUtils_createStringInBuffer(strBuf, 6, self->mmsDomain->domainName, "/", self->lnName, ".", self->name, ".opOk"); + + self->opOk = (DataAttribute*) IedModel_getModelNodeByObjectReference(self->iedServer->model, opOkName); + + if ((self->opOk) && (self->opOk->type != IEC61850_BOOLEAN)) { + self->opOk = NULL; + + if (DEBUG_IED_SERVER) + printf("IED_SERVER: ERROR - opOk of wrong type!\n"); + } + + char* tOpOkName = StringUtils_createStringInBuffer(strBuf, 6, self->mmsDomain->domainName, "/", self->lnName, ".", self->name, ".tOpOk"); + + self->tOpOk = (DataAttribute*) IedModel_getModelNodeByObjectReference(self->iedServer->model, tOpOkName); + + if ((self->tOpOk) && (self->tOpOk->type != IEC61850_TIMESTAMP)) { + self->tOpOk = NULL; + + if (DEBUG_IED_SERVER) + printf("IED_SERVER: ERROR - tOpOk of wrong type!\n"); + } + self->error = MmsValue_newIntegerFromInt32(0); self->addCause = MmsValue_newIntegerFromInt32(0); @@ -422,36 +534,96 @@ ControlObject_initialize(ControlObject* self) } } +static void +ControlObject_handlePendingEvents(ControlObject* self) +{ +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_wait(self->pendingEventsLock); +#endif + + if (self->pendingEvents > 0) { + + if (self->pendingEvents & PENDING_EVENT_SELECTED) { + if (self->stSeld) + IedServer_updateBooleanAttributeValue(self->iedServer, self->stSeld, true); + + self->pendingEvents &= ~(PENDING_EVENT_SELECTED); + } + + if (self->pendingEvents & PENDING_EVENT_UNSELECTED) { + if (self->stSeld) + IedServer_updateBooleanAttributeValue(self->iedServer, self->stSeld, false); + + self->pendingEvents &= ~(PENDING_EVENT_UNSELECTED); + } + + if (self->pendingEvents & PENDING_EVENT_OP_RCVD_TRUE) { + if (self->opRcvd) + IedServer_updateBooleanAttributeValue(self->iedServer, self->opRcvd, true); + + self->pendingEvents &= ~(PENDING_EVENT_OP_RCVD_TRUE); + } + + if (self->pendingEvents & PENDING_EVENT_OP_RCVD_FALSE) { + if (self->opRcvd) + IedServer_updateBooleanAttributeValue(self->iedServer, self->opRcvd, false); + + self->pendingEvents &= ~(PENDING_EVENT_OP_RCVD_FALSE); + } + + if (self->pendingEvents & PENDING_EVENT_OP_OK_TRUE) { + if (self->opOk) + IedServer_updateBooleanAttributeValue(self->iedServer, self->opOk, true); + + self->pendingEvents &= ~(PENDING_EVENT_OP_OK_TRUE); + } + + if (self->pendingEvents & PENDING_EVENT_OP_OK_FALSE) { + if (self->opOk) + IedServer_updateBooleanAttributeValue(self->iedServer, self->opOk, false); + + self->pendingEvents &= ~(PENDING_EVENT_OP_OK_FALSE); + } + } + +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_post(self->pendingEventsLock); +#endif +} + void ControlObject_destroy(ControlObject* self) { - if (self->mmsValue != NULL) + if (self->mmsValue) MmsValue_delete(self->mmsValue); - if (self->emptyString != NULL) + if (self->emptyString) MmsValue_delete(self->emptyString); - if (self->error != NULL) + if (self->error) MmsValue_delete(self->error); - if (self->addCause != NULL) + if (self->addCause) MmsValue_delete(self->addCause); - if (self->ctlVal != NULL) + if (self->ctlVal) MmsValue_delete(self->ctlVal); - if (self->ctlNum != NULL) + if (self->ctlNum) MmsValue_delete(self->ctlNum); - if (self->origin != NULL) + if (self->origin) MmsValue_delete(self->origin); - if (self->name != NULL) + if (self->name) GLOBAL_FREEMEM(self->name); #if (CONFIG_MMS_THREADLESS_STACK != 1) - if (self->stateLock != NULL) + if (self->stateLock) Semaphore_destroy(self->stateLock); + + if (self->pendingEventsLock) + Semaphore_destroy(self->pendingEventsLock); #endif GLOBAL_FREEMEM(self); @@ -499,14 +671,6 @@ ControlObject_getMmsValue(ControlObject* self) return self->mmsValue; } -static void -setStSeld(ControlObject* self, bool value) -{ - if (self->stSeld) { - IedServer_updateBooleanAttributeValue(self->iedServer, self->stSeld, value); - } -} - static void selectObject(ControlObject* self, uint64_t selectTime, MmsServerConnection connection) { @@ -613,8 +777,11 @@ Control_processControlActions(MmsMapping* self, uint64_t currentTimeInMs) if (controlObject->operateTime <= currentTimeInMs) { + /* enter state Perform Test */ + setOpRcvd(controlObject, true); + if (DEBUG_IED_SERVER) - printf("time activated operate: start operation\n"); + printf("time activated operate: perform test\n"); controlObject->timeActivatedOperate = false; @@ -631,22 +798,37 @@ Control_processControlActions(MmsMapping* self, uint64_t currentTimeInMs) } if (checkResult == CONTROL_ACCEPTED) { - executeControlTask(controlObject); + + if (DEBUG_IED_SERVER) + printf("time activated operate: command accepted\n"); + + /* leave state Perform Test */ + setOpRcvd(controlObject, false); + + executeControlTask(controlObject, currentTimeInMs); } else { ControlObject_sendLastApplError(controlObject, controlObject->mmsConnection, "Oper", CONTROL_ERROR_NO_ERROR, ADD_CAUSE_BLOCKED_BY_INTERLOCKING, controlObject->ctlNum, controlObject->origin, false); + /* leave state Perform Test */ + setOpRcvd(controlObject, false); + abortControlOperation(controlObject); } } } /* if (controlObject->state == STATE_WAIT_FOR_ACTICATION_TIME) */ else if (!((controlObject->state == STATE_UNSELECTED) || (controlObject->state == STATE_READY))) { - executeControlTask(controlObject); + executeControlTask(controlObject, currentTimeInMs); + } + else if (controlObject->state == STATE_READY) { + checkSelectTimeout(controlObject, currentTimeInMs); } + ControlObject_handlePendingEvents(controlObject); + element = LinkedList_getNext(element); } } @@ -1059,6 +1241,8 @@ Control_readAccessControlObject(MmsMapping* self, MmsDomain* domain, char* varia if (getState(controlObject) == STATE_UNSELECTED) { CheckHandlerResult checkResult = CONTROL_ACCEPTED; + /* opRcvd must not be set here! */ + if (controlObject->checkHandler != NULL) { /* perform operative tests */ ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, @@ -1252,6 +1436,8 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari CheckHandlerResult checkResult = CONTROL_ACCEPTED; + /* opRcvd must not be set here! */ + bool interlockCheck = MmsValue_getBitStringBit(check, 1); bool testCondition = MmsValue_getBoolean(test); @@ -1393,6 +1579,9 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari CheckHandlerResult checkResult = CONTROL_ACCEPTED; + /* enter state Perform Test */ + setOpRcvd(controlObject, true); + if (controlObject->checkHandler != NULL) { /* perform operative tests */ ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, @@ -1403,7 +1592,6 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari clientConnection); } - if (checkResult == CONTROL_ACCEPTED) { indication = DATA_ACCESS_ERROR_NO_RESPONSE; @@ -1413,11 +1601,17 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari setState(controlObject, STATE_WAIT_FOR_EXECUTION); + /* leave state Perform Test */ + setOpRcvd(controlObject, false); + initiateControlTask(controlObject); } else { indication = (MmsDataAccessError) checkResult; + /* leave state Perform Test */ + setOpRcvd(controlObject, false); + abortControlOperation(controlObject); } }