diff --git a/examples/server_example_goose/server_example_goose.c b/examples/server_example_goose/server_example_goose.c
index 2fe77f47..d6548c35 100644
--- a/examples/server_example_goose/server_example_goose.c
+++ b/examples/server_example_goose/server_example_goose.c
@@ -104,6 +104,8 @@ int main(int argc, char** argv) {
float anIn1 = 0.f;
+ int eventCount = 10;
+
while (running) {
IedServer_lockDataModel(iedServer);
@@ -111,6 +113,21 @@ int main(int argc, char** argv) {
IedServer_updateUTCTimeAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn1_t, Hal_getTimeInMs());
IedServer_updateFloatAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn1_mag_f, anIn1);
+ if (eventCount) {
+ IedServer_updateUTCTimeAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO4_t, Hal_getTimeInMs());
+
+ if (eventCount % 2) {
+ IedServer_updateQuality(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO4_q, QUALITY_VALIDITY_GOOD);
+ IedServer_updateBooleanAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO4_stVal, true);
+ }
+ else {
+ IedServer_updateQuality(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO4_q, QUALITY_VALIDITY_INVALID);
+ IedServer_updateBooleanAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO4_stVal, false);
+ }
+
+ eventCount--;
+ }
+
IedServer_unlockDataModel(iedServer);
anIn1 += 0.1;
diff --git a/examples/server_example_goose/simpleIO_direct_control_goose.cid b/examples/server_example_goose/simpleIO_direct_control_goose.cid
index 8f443989..1701b9fe 100644
--- a/examples/server_example_goose/simpleIO_direct_control_goose.cid
+++ b/examples/server_example_goose/simpleIO_direct_control_goose.cid
@@ -68,6 +68,17 @@
+
+
+
+
+
+
+
+
+
+
+
@@ -88,7 +99,7 @@
-
+
diff --git a/examples/server_example_goose/static_model.c b/examples/server_example_goose/static_model.c
index e57b0596..245575f0 100644
--- a/examples/server_example_goose/static_model.c
+++ b/examples/server_example_goose/static_model.c
@@ -9,6 +9,7 @@ static void initializeValues();
extern DataSet iedModelds_GenericIO_LLN0_Events;
extern DataSet iedModelds_GenericIO_LLN0_Events2;
+extern DataSet iedModelds_GenericIO_LLN0_Events3;
extern DataSet iedModelds_GenericIO_LLN0_AnalogValues;
@@ -115,6 +116,103 @@ DataSet iedModelds_GenericIO_LLN0_Events2 = {
"LLN0$Events2",
4,
&iedModelds_GenericIO_LLN0_Events2_fcda0,
+ &iedModelds_GenericIO_LLN0_Events3
+};
+
+extern DataSetEntry iedModelds_GenericIO_LLN0_Events3_fcda0;
+extern DataSetEntry iedModelds_GenericIO_LLN0_Events3_fcda1;
+extern DataSetEntry iedModelds_GenericIO_LLN0_Events3_fcda2;
+extern DataSetEntry iedModelds_GenericIO_LLN0_Events3_fcda3;
+extern DataSetEntry iedModelds_GenericIO_LLN0_Events3_fcda4;
+extern DataSetEntry iedModelds_GenericIO_LLN0_Events3_fcda5;
+extern DataSetEntry iedModelds_GenericIO_LLN0_Events3_fcda6;
+extern DataSetEntry iedModelds_GenericIO_LLN0_Events3_fcda7;
+
+DataSetEntry iedModelds_GenericIO_LLN0_Events3_fcda0 = {
+ "GenericIO",
+ false,
+ "GGIO1$ST$SPCSO1$stVal",
+ -1,
+ NULL,
+ NULL,
+ &iedModelds_GenericIO_LLN0_Events3_fcda1
+};
+
+DataSetEntry iedModelds_GenericIO_LLN0_Events3_fcda1 = {
+ "GenericIO",
+ false,
+ "GGIO1$ST$SPCSO1$q",
+ -1,
+ NULL,
+ NULL,
+ &iedModelds_GenericIO_LLN0_Events3_fcda2
+};
+
+DataSetEntry iedModelds_GenericIO_LLN0_Events3_fcda2 = {
+ "GenericIO",
+ false,
+ "GGIO1$ST$SPCSO2$stVal",
+ -1,
+ NULL,
+ NULL,
+ &iedModelds_GenericIO_LLN0_Events3_fcda3
+};
+
+DataSetEntry iedModelds_GenericIO_LLN0_Events3_fcda3 = {
+ "GenericIO",
+ false,
+ "GGIO1$ST$SPCSO2$q",
+ -1,
+ NULL,
+ NULL,
+ &iedModelds_GenericIO_LLN0_Events3_fcda4
+};
+
+DataSetEntry iedModelds_GenericIO_LLN0_Events3_fcda4 = {
+ "GenericIO",
+ false,
+ "GGIO1$ST$SPCSO3$stVal",
+ -1,
+ NULL,
+ NULL,
+ &iedModelds_GenericIO_LLN0_Events3_fcda5
+};
+
+DataSetEntry iedModelds_GenericIO_LLN0_Events3_fcda5 = {
+ "GenericIO",
+ false,
+ "GGIO1$ST$SPCSO3$q",
+ -1,
+ NULL,
+ NULL,
+ &iedModelds_GenericIO_LLN0_Events3_fcda6
+};
+
+DataSetEntry iedModelds_GenericIO_LLN0_Events3_fcda6 = {
+ "GenericIO",
+ false,
+ "GGIO1$ST$SPCSO4$stVal",
+ -1,
+ NULL,
+ NULL,
+ &iedModelds_GenericIO_LLN0_Events3_fcda7
+};
+
+DataSetEntry iedModelds_GenericIO_LLN0_Events3_fcda7 = {
+ "GenericIO",
+ false,
+ "GGIO1$ST$SPCSO4$q",
+ -1,
+ NULL,
+ NULL,
+ NULL
+};
+
+DataSet iedModelds_GenericIO_LLN0_Events3 = {
+ "GenericIO",
+ "LLN0$Events3",
+ 8,
+ &iedModelds_GenericIO_LLN0_Events3_fcda0,
&iedModelds_GenericIO_LLN0_AnalogValues
};
@@ -1896,7 +1994,7 @@ static PhyComAddress iedModel_GenericIO_LLN0_gse0_address = {
{0x1, 0xc, 0xcd, 0x1, 0x0, 0x1}
};
-GSEControlBlock iedModel_GenericIO_LLN0_gse0 = {&iedModel_GenericIO_LLN0, "gcbEvents", "events", "Events", 2, false, &iedModel_GenericIO_LLN0_gse0_address, 1000, 3000, &iedModel_GenericIO_LLN0_gse1};
+GSEControlBlock iedModel_GenericIO_LLN0_gse0 = {&iedModel_GenericIO_LLN0, "gcbEvents", "events", "Events3", 2, false, &iedModel_GenericIO_LLN0_gse0_address, 1000, 3000, &iedModel_GenericIO_LLN0_gse1};
static PhyComAddress iedModel_GenericIO_LLN0_gse1_address = {
4,
diff --git a/src/iec61850/inc_private/mms_goose.h b/src/iec61850/inc_private/mms_goose.h
index 0b1a22d5..326e35dc 100644
--- a/src/iec61850/inc_private/mms_goose.h
+++ b/src/iec61850/inc_private/mms_goose.h
@@ -69,7 +69,10 @@ LIB61850_INTERNAL void
MmsGooseControlBlock_checkAndPublish(MmsGooseControlBlock self, uint64_t currentTime);
LIB61850_INTERNAL void
-MmsGooseControlBlock_observedObjectChanged(MmsGooseControlBlock self);
+MmsGooseControlBlock_setStateChangePending(MmsGooseControlBlock self);
+
+LIB61850_INTERNAL void
+MmsGooseControlBlock_publishNewState(MmsGooseControlBlock self);
LIB61850_INTERNAL void
MmsGooseControlBlock_enable(MmsGooseControlBlock self);
@@ -77,6 +80,9 @@ MmsGooseControlBlock_enable(MmsGooseControlBlock self);
LIB61850_INTERNAL void
MmsGooseControlBlock_disable(MmsGooseControlBlock self);
+LIB61850_INTERNAL void
+GOOSE_sendPendingEvents(MmsMapping* self);
+
LIB61850_INTERNAL MmsVariableSpecification*
GOOSE_createGOOSEControlBlocks(MmsMapping* self, MmsDomain* domain,
LogicalNode* logicalNode, int gseCount);
diff --git a/src/iec61850/server/impl/ied_server.c b/src/iec61850/server/impl/ied_server.c
index 086011be..1422f79a 100644
--- a/src/iec61850/server/impl/ied_server.c
+++ b/src/iec61850/server/impl/ied_server.c
@@ -34,6 +34,7 @@
#include "libiec61850_platform_includes.h"
#include "mms_sv.h"
+#include "mms_goose.h"
#ifndef DEBUG_IED_SERVER
#define DEBUG_IED_SERVER 0
@@ -728,6 +729,11 @@ IedServer_lockDataModel(IedServer self)
void
IedServer_unlockDataModel(IedServer self)
{
+#if (CONFIG_INCLUDE_GOOSE_SUPPORT == 1)
+ /* check if GOOSE messages have to be sent */
+ GOOSE_sendPendingEvents(self->mmsMapping);
+#endif /* (CONFIG_INCLUDE_GOOSE_SUPPORT == 1) */
+
/* check if reports have to be sent! */
Reporting_processReportEventsAfterUnlock(self->mmsMapping);
diff --git a/src/iec61850/server/mms_mapping/mms_goose.c b/src/iec61850/server/mms_mapping/mms_goose.c
index 09184a62..b109dafd 100644
--- a/src/iec61850/server/mms_mapping/mms_goose.c
+++ b/src/iec61850/server/mms_mapping/mms_goose.c
@@ -72,6 +72,8 @@ struct sMmsGooseControlBlock {
char* dataSetRef;
char* gooseInterfaceId;
+
+ bool stateChangePending;
};
#if (CONFIG_IEC61850_SERVICE_TRACKING == 1)
@@ -357,43 +359,50 @@ MmsGooseControlBlock_enable(MmsGooseControlBlock self)
else
self->publisher = GoosePublisher_createEx(&commParameters, self->mmsMapping->gooseInterfaceId, self->useVlanTag);
- self->minTime = MmsValue_toUint32(MmsValue_getElement(self->mmsValue, 6));
- self->maxTime = MmsValue_toUint32(MmsValue_getElement(self->mmsValue, 7));
+ if (self->publisher) {
+ self->minTime = MmsValue_toUint32(MmsValue_getElement(self->mmsValue, 6));
+ self->maxTime = MmsValue_toUint32(MmsValue_getElement(self->mmsValue, 7));
- GoosePublisher_setTimeAllowedToLive(self->publisher, self->maxTime * 3);
+ GoosePublisher_setTimeAllowedToLive(self->publisher, self->maxTime * 3);
- GoosePublisher_setDataSetRef(self->publisher, self->dataSetRef);
+ GoosePublisher_setDataSetRef(self->publisher, self->dataSetRef);
- GoosePublisher_setGoCbRef(self->publisher, self->goCBRef);
+ GoosePublisher_setGoCbRef(self->publisher, self->goCBRef);
- uint32_t confRev = MmsValue_toUint32(MmsValue_getElement(self->mmsValue, 3));
+ uint32_t confRev = MmsValue_toUint32(MmsValue_getElement(self->mmsValue, 3));
- GoosePublisher_setConfRev(self->publisher, confRev);
+ GoosePublisher_setConfRev(self->publisher, confRev);
- bool needsCom = MmsValue_getBoolean(MmsValue_getElement(self->mmsValue, 4));
+ bool needsCom = MmsValue_getBoolean(MmsValue_getElement(self->mmsValue, 4));
- GoosePublisher_setNeedsCommission(self->publisher, needsCom);
+ GoosePublisher_setNeedsCommission(self->publisher, needsCom);
- if (self->goId != NULL)
- GoosePublisher_setGoID(self->publisher, self->goId);
+ if (self->goId != NULL)
+ GoosePublisher_setGoID(self->publisher, self->goId);
- /* prepare data set values */
- self->dataSetValues = LinkedList_create();
+ /* prepare data set values */
+ self->dataSetValues = LinkedList_create();
- DataSetEntry* dataSetEntry = self->dataSet->fcdas;
+ DataSetEntry* dataSetEntry = self->dataSet->fcdas;
- while (dataSetEntry != NULL) {
- LinkedList_add(self->dataSetValues, dataSetEntry->value);
- dataSetEntry = dataSetEntry->sibling;
- }
+ while (dataSetEntry != NULL) {
+ LinkedList_add(self->dataSetValues, dataSetEntry->value);
+ dataSetEntry = dataSetEntry->sibling;
+ }
- self->goEna = true;
+ self->goEna = true;
#if (CONFIG_IEC61850_SERVICE_TRACKING == 1)
- MmsDataAccessError retVal = DATA_ACCESS_ERROR_SUCCESS;
- copyGCBValuesToTrackingObject(self);
- updateGenericTrackingObjectValues(self, IEC61850_SERVICE_TYPE_SET_GOCB_VALUES, retVal);
+ MmsDataAccessError retVal = DATA_ACCESS_ERROR_SUCCESS;
+ copyGCBValuesToTrackingObject(self);
+ updateGenericTrackingObjectValues(self, IEC61850_SERVICE_TYPE_SET_GOCB_VALUES, retVal);
#endif /* (CONFIG_IEC61850_SERVICE_TRACKING == 1) */
+ }
+ else {
+ if (DEBUG_IED_SERVER)
+ printf("IED_SERVER: Failed to create GOOSE publisher!\n");
+ }
+
}
}
@@ -475,8 +484,15 @@ MmsGooseControlBlock_checkAndPublish(MmsGooseControlBlock self, uint64_t current
}
void
-MmsGooseControlBlock_observedObjectChanged(MmsGooseControlBlock self)
+MmsGooseControlBlock_setStateChangePending(MmsGooseControlBlock self)
+{
+ self->stateChangePending = true;
+}
+
+void
+MmsGooseControlBlock_publishNewState(MmsGooseControlBlock self)
{
+ if (self->stateChangePending) {
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(self->publisherMutex);
#endif
@@ -498,9 +514,12 @@ MmsGooseControlBlock_observedObjectChanged(MmsGooseControlBlock self)
GoosePublisher_publish(self->publisher, self->dataSetValues);
+ self->stateChangePending = false;
+
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(self->publisherMutex);
#endif
+ }
}
static MmsVariableSpecification*
@@ -608,6 +627,20 @@ createDataSetReference(char* domainName, char* lnName, char* dataSetName)
return dataSetReference;
}
+void
+GOOSE_sendPendingEvents(MmsMapping* self)
+{
+ LinkedList element = self->gseControls;
+
+ while ((element = LinkedList_getNext(element)) != NULL) {
+ MmsGooseControlBlock gcb = (MmsGooseControlBlock) element->data;
+
+ if (MmsGooseControlBlock_isEnabled(gcb)) {
+ MmsGooseControlBlock_publishNewState(gcb);
+ }
+ }
+}
+
MmsVariableSpecification*
GOOSE_createGOOSEControlBlocks(MmsMapping* self, MmsDomain* domain,
LogicalNode* logicalNode, int gseCount)
@@ -718,6 +751,8 @@ GOOSE_createGOOSEControlBlocks(MmsMapping* self, MmsDomain* domain,
mmsGCB->mmsMapping = self;
+ mmsGCB->stateChangePending = false;
+
LinkedList_add(self->gseControls, mmsGCB);
currentGCB++;
diff --git a/src/iec61850/server/mms_mapping/mms_mapping.c b/src/iec61850/server/mms_mapping/mms_mapping.c
index 253753b8..798c1d47 100644
--- a/src/iec61850/server/mms_mapping/mms_mapping.c
+++ b/src/iec61850/server/mms_mapping/mms_mapping.c
@@ -3509,6 +3509,8 @@ MmsMapping_triggerGooseObservers(MmsMapping* self, MmsValue* value)
{
LinkedList element = self->gseControls;
+ bool modelLocked = self->isModelLocked;
+
while ((element = LinkedList_getNext(element)) != NULL) {
MmsGooseControlBlock gcb = (MmsGooseControlBlock) element->data;
@@ -3516,7 +3518,11 @@ MmsMapping_triggerGooseObservers(MmsMapping* self, MmsValue* value)
DataSet* dataSet = MmsGooseControlBlock_getDataSet(gcb);
if (DataSet_isMemberValue(dataSet, value, NULL)) {
- MmsGooseControlBlock_observedObjectChanged(gcb);
+ MmsGooseControlBlock_setStateChangePending(gcb);
+
+ if (modelLocked == false) {
+ MmsGooseControlBlock_publishNewState(gcb);
+ }
}
}
}
diff --git a/tools/model_generator/genmodel.jar b/tools/model_generator/genmodel.jar
index ec65eae5..25fb74df 100644
Binary files a/tools/model_generator/genmodel.jar and b/tools/model_generator/genmodel.jar differ
diff --git a/tools/model_generator/src/com/libiec61850/tools/StaticModelGenerator.java b/tools/model_generator/src/com/libiec61850/tools/StaticModelGenerator.java
index 6f0a41b3..4e1ae291 100644
--- a/tools/model_generator/src/com/libiec61850/tools/StaticModelGenerator.java
+++ b/tools/model_generator/src/com/libiec61850/tools/StaticModelGenerator.java
@@ -1028,77 +1028,83 @@ public class StaticModelGenerator {
for (GSEControl gseControlBlock : gseControlBlocks) {
GSE gse = connectedAP.lookupGSE(logicalDeviceName, gseControlBlock.getName());
-
- PhyComAddress gseAddress = gse.getAddress();
- String gseString = "";
+ if (gse != null) {
+ PhyComAddress gseAddress = gse.getAddress();
+
+ String gseString = "";
- String phyComAddrName = "";
+ String phyComAddrName = "";
- if (gseAddress != null) {
- phyComAddrName = lnPrefix + "_gse" + gseControlNumber + "_address";
+ if (gseAddress != null) {
+ phyComAddrName = lnPrefix + "_gse" + gseControlNumber + "_address";
- gseString += "\nstatic PhyComAddress " + phyComAddrName + " = {\n";
- gseString += " " + gseAddress.getVlanPriority() + ",\n";
- gseString += " " + gseAddress.getVlanId() + ",\n";
- gseString += " " + gseAddress.getAppId() + ",\n";
- gseString += " {";
+ gseString += "\nstatic PhyComAddress " + phyComAddrName + " = {\n";
+ gseString += " " + gseAddress.getVlanPriority() + ",\n";
+ gseString += " " + gseAddress.getVlanId() + ",\n";
+ gseString += " " + gseAddress.getAppId() + ",\n";
+ gseString += " {";
- for (int i = 0; i < 6; i++) {
- gseString += "0x" + Integer.toHexString(gseAddress.getMacAddress()[i]);
- if (i == 5)
- gseString += "}\n";
- else
- gseString += ", ";
+ for (int i = 0; i < 6; i++) {
+ gseString += "0x" + Integer.toHexString(gseAddress.getMacAddress()[i]);
+ if (i == 5)
+ gseString += "}\n";
+ else
+ gseString += ", ";
+ }
+
+ gseString += "};\n\n";
}
- gseString += "};\n\n";
- }
+ String gseVariableName = lnPrefix + "_gse" + gseControlNumber;
- String gseVariableName = lnPrefix + "_gse" + gseControlNumber;
+ gseString += "GSEControlBlock " + gseVariableName + " = {";
+ gseString += "&" + lnPrefix + ", ";
- gseString += "GSEControlBlock " + gseVariableName + " = {";
- gseString += "&" + lnPrefix + ", ";
+ gseString += "\"" + gseControlBlock.getName() + "\", ";
- gseString += "\"" + gseControlBlock.getName() + "\", ";
+ if (gseControlBlock.getAppID() == null)
+ gseString += "NULL, ";
+ else
+ gseString += "\"" + gseControlBlock.getAppID() + "\", ";
- if (gseControlBlock.getAppID() == null)
- gseString += "NULL, ";
- else
- gseString += "\"" + gseControlBlock.getAppID() + "\", ";
+ if (gseControlBlock.getDataSet() != null)
+ gseString += "\"" + gseControlBlock.getDataSet() + "\", ";
+ else
+ gseString += "NULL, ";
- if (gseControlBlock.getDataSet() != null)
- gseString += "\"" + gseControlBlock.getDataSet() + "\", ";
- else
- gseString += "NULL, ";
+ gseString += gseControlBlock.getConfRev() + ", ";
- gseString += gseControlBlock.getConfRev() + ", ";
+ if (gseControlBlock.isFixedOffs())
+ gseString += "true, ";
+ else
+ gseString += "false, ";
- if (gseControlBlock.isFixedOffs())
- gseString += "true, ";
- else
- gseString += "false, ";
+ if (gseAddress != null)
+ gseString += "&" + phyComAddrName + ", ";
+ else
+ gseString += "NULL, ";
+
+ gseString += gse.getMinTime() + ", ";
+ gseString += gse.getMaxTime() + ", ";
- if (gseAddress != null)
- gseString += "&" + phyComAddrName + ", ";
- else
- gseString += "NULL, ";
-
- gseString += gse.getMinTime() + ", ";
- gseString += gse.getMaxTime() + ", ";
+ currentGseVariableNumber++;
+
+ if (currentGseVariableNumber < gseVariableNames.size())
+ gseString += "&" + gseVariableNames.get(currentGseVariableNumber);
+ else
+ gseString += "NULL";
- currentGseVariableNumber++;
-
- if (currentGseVariableNumber < gseVariableNames.size())
- gseString += "&" + gseVariableNames.get(currentGseVariableNumber);
- else
- gseString += "NULL";
+ gseString += "};\n";
- gseString += "};\n";
+ this.gseControlBlocks.append(gseString);
- this.gseControlBlocks.append(gseString);
+ gseControlNumber++;
+ }
+ else {
+ System.out.println("GSE not found for GoCB " + gseControlBlock.getName());
+ }
- gseControlNumber++;
}
}