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);
}
}