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 83e5e2ab..38428134 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 @@ -717,6 +718,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 91d8748d..4df50074 100644 --- a/src/iec61850/server/mms_mapping/mms_goose.c +++ b/src/iec61850/server/mms_mapping/mms_goose.c @@ -71,6 +71,8 @@ struct sMmsGooseControlBlock { char* dataSetRef; char* gooseInterfaceId; + + bool stateChangePending; }; MmsGooseControlBlock @@ -257,37 +259,44 @@ 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; + } + else { + if (DEBUG_IED_SERVER) + printf("IED_SERVER: Failed to create GOOSE publisher!\n"); } - self->goEna = true; } } @@ -363,8 +372,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 @@ -386,9 +402,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* @@ -496,6 +515,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) @@ -606,6 +639,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 71759e49..aa9ed330 100644 --- a/src/iec61850/server/mms_mapping/mms_mapping.c +++ b/src/iec61850/server/mms_mapping/mms_mapping.c @@ -2921,6 +2921,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; @@ -2928,7 +2930,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 54d838df..cbe2108b 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++; } }