diff --git a/config/stack_config.h b/config/stack_config.h index 3c8467f..da921a3 100644 --- a/config/stack_config.h +++ b/config/stack_config.h @@ -98,6 +98,16 @@ /* The number of GOOSE retransmissions after an event */ #define CONFIG_GOOSE_EVENT_RETRANSMISSION_COUNT 2 +/* Define if GOOSE control block elements are writable (1) or read-only (0) */ +#define CONFIG_GOOSE_GOID_WRITABLE 1 +#define CONFIG_GOOSE_DATSET_WRITABLE 1 +#define CONFIG_GOOSE_CONFREV_WRITABLE 0 +#define CONFIG_GOOSE_NDSCOM_WRITABLE 0 +#define CONFIG_GOOSE_DSTADDRESS_WRITABLE 0 +#define CONFIG_GOOSE_MINTIME_WRITABLE 0 +#define CONFIG_GOOSE_MAXTIME_WRITABLE 0 +#define CONFIG_GOOSE_FIXEDOFFS_WRITABLE 0 + /* The default value for the priority field of the 802.1Q header (allowed range 0-7) */ #define CONFIG_GOOSE_DEFAULT_PRIORITY 4 diff --git a/config/stack_config.h.cmake b/config/stack_config.h.cmake index 46c76f8..d91bc67 100644 --- a/config/stack_config.h.cmake +++ b/config/stack_config.h.cmake @@ -96,6 +96,16 @@ /* The number of GOOSE retransmissions after an event */ #define CONFIG_GOOSE_EVENT_RETRANSMISSION_COUNT 2 +/* Define if GOOSE control block elements are writable (1) or read-only (0) */ +#define CONFIG_GOOSE_GOID_WRITABLE 1 +#define CONFIG_GOOSE_DATSET_WRITABLE 1 +#define CONFIG_GOOSE_CONFREV_WRITABLE 1 +#define CONFIG_GOOSE_NDSCOM_WRITABLE 1 +#define CONFIG_GOOSE_DSTADDRESS_WRITABLE 1 +#define CONFIG_GOOSE_MINTIME_WRITABLE 1 +#define CONFIG_GOOSE_MAXTIME_WRITABLE 1 +#define CONFIG_GOOSE_FIXEDOFFS_WRITABLE 1 + /* The default value for the priority field of the 802.1Q header (allowed range 0-7) */ #define CONFIG_GOOSE_DEFAULT_PRIORITY 4 diff --git a/examples/server_example_goose/simpleIO_direct_control_goose.icd b/examples/server_example_goose/simpleIO_direct_control_goose.icd index 5f68fa0..3efbf70 100644 --- a/examples/server_example_goose/simpleIO_direct_control_goose.icd +++ b/examples/server_example_goose/simpleIO_direct_control_goose.icd @@ -77,7 +77,7 @@ - + diff --git a/examples/server_example_goose/static_model.c b/examples/server_example_goose/static_model.c index e1bcf92..0bddaaf 100644 --- a/examples/server_example_goose/static_model.c +++ b/examples/server_example_goose/static_model.c @@ -1983,7 +1983,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, &iedModel_GenericIO_LLN0_gse1}; +GSEControlBlock iedModel_GenericIO_LLN0_gse0 = {&iedModel_GenericIO_LLN0, "gcbEvents", "events", "Events", 2, false, &iedModel_GenericIO_LLN0_gse0_address, 1000, 3000, &iedModel_GenericIO_LLN0_gse1}; static PhyComAddress iedModel_GenericIO_LLN0_gse1_address = { 4, @@ -1992,7 +1992,7 @@ static PhyComAddress iedModel_GenericIO_LLN0_gse1_address = { {0x1, 0xc, 0xcd, 0x1, 0x0, 0x1} }; -GSEControlBlock iedModel_GenericIO_LLN0_gse1 = {&iedModel_GenericIO_LLN0, "gcbAnalogValues", "analog", "AnalogValues", 2, false,&iedModel_GenericIO_LLN0_gse1_address, NULL}; +GSEControlBlock iedModel_GenericIO_LLN0_gse1 = {&iedModel_GenericIO_LLN0, "gcbAnalogValues", "analog", "AnalogValues", 2, false, &iedModel_GenericIO_LLN0_gse1_address, -1, -1, NULL}; diff --git a/src/iec61850/inc/iec61850_dynamic_model.h b/src/iec61850/inc/iec61850_dynamic_model.h index 43d1182..84b0239 100644 --- a/src/iec61850/inc/iec61850_dynamic_model.h +++ b/src/iec61850/inc/iec61850_dynamic_model.h @@ -171,11 +171,14 @@ SettingGroupControlBlock_create(LogicalNode* parent, uint8_t actSG, uint8_t numO * \param dataSet the data set reference to be used by the GoCB * \param confRef the configuration revision * \param fixedOffs indicates if GOOSE publisher shall use fixed offsets (NOT YET SUPPORTED) + * \param minTime minimum GOOSE retransmission time (-1 if not specified - uses stack default then) + * \param maxTime GOOSE retransmission time in stable state (-1 if not specified - uses stack default then) * * \return the new GoCB instance */ GSEControlBlock* -GSEControlBlock_create(const char* name, LogicalNode* parent, char* appId, char* dataSet, uint32_t confRef, bool fixedOffs); +GSEControlBlock_create(const char* name, LogicalNode* parent, char* appId, char* dataSet, uint32_t confRef, + bool fixedOffs, int minTime, int maxTime); /** * \brief create a PhyComAddress object and add it to a GoCB diff --git a/src/iec61850/inc/iec61850_model.h b/src/iec61850/inc/iec61850_model.h index a4a091a..f8d36e6 100644 --- a/src/iec61850/inc/iec61850_model.h +++ b/src/iec61850/inc/iec61850_model.h @@ -247,7 +247,8 @@ struct sGSEControlBlock { uint32_t confRef; /* ConfRef - configuration revision */ bool fixedOffs; /* fixed offsets */ PhyComAddress* address; /* GSE communication parameters */ - + int minTime; /* optional minTime parameter --> -1 if not present */ + int maxTime; /* optional maxTime parameter --> -1 if not present */ GSEControlBlock* sibling; /* next control block in list or NULL if this is the last entry */ }; diff --git a/src/iec61850/server/mms_mapping/mms_goose.c b/src/iec61850/server/mms_mapping/mms_goose.c index 15bf0dd..e4c0678 100644 --- a/src/iec61850/server/mms_mapping/mms_goose.c +++ b/src/iec61850/server/mms_mapping/mms_goose.c @@ -57,6 +57,9 @@ struct sMmsGooseControlBlock { uint64_t nextPublishTime; int retransmissionsLeft; /* number of retransmissions left for the last event */ + int minTime; + int maxTime; + #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore publisherMutex; #endif @@ -223,7 +226,10 @@ MmsGooseControlBlock_enable(MmsGooseControlBlock self) self->publisher = GoosePublisher_create(&commParameters, self->mmsMapping->gooseInterfaceId); - GoosePublisher_setTimeAllowedToLive(self->publisher, CONFIG_GOOSE_STABLE_STATE_TRANSMISSION_INTERVAL * 3); + 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_setDataSetRef(self->publisher, self->dataSetRef); @@ -555,6 +561,22 @@ GOOSE_createGOOSEControlBlocks(MmsMapping* self, MmsDomain* domain, mmsGCB->domain = domain; mmsGCB->logicalNode = logicalNode; + if (gooseControlBlock->minTime == -1) + mmsGCB->minTime = CONFIG_GOOSE_EVENT_RETRANSMISSION_INTERVAL; + else + mmsGCB->minTime = gooseControlBlock->minTime; + + if (gooseControlBlock->maxTime == -1) + mmsGCB->maxTime = CONFIG_GOOSE_STABLE_STATE_TRANSMISSION_INTERVAL; + else + mmsGCB->maxTime = gooseControlBlock->maxTime; + + MmsValue* minTime = MmsValue_getElement(gseValues, 6); + MmsValue_setUint32(minTime, mmsGCB->minTime); + + MmsValue* maxTime = MmsValue_getElement(gseValues, 7); + MmsValue_setUint32(maxTime, mmsGCB->maxTime); + mmsGCB->mmsMapping = self; LinkedList_add(self->gseControls, mmsGCB); diff --git a/src/iec61850/server/mms_mapping/mms_mapping.c b/src/iec61850/server/mms_mapping/mms_mapping.c index 07ee68f..881d1b6 100644 --- a/src/iec61850/server/mms_mapping/mms_mapping.c +++ b/src/iec61850/server/mms_mapping/mms_mapping.c @@ -1321,6 +1321,38 @@ lookupGCB(MmsMapping* self, MmsDomain* domain, char* lnName, char* objectName) return NULL; } +#ifndef CONFIG_GOOSE_GOID_WRITABLE +#define CONFIG_GOOSE_GOID_WRITABLE 0 +#endif + +#ifndef CONFIG_GOOSE_DATSET_WRITABLE +#define CONFIG_GOOSE_DATSET_WRITABLE 0 +#endif + +#ifndef CONFIG_GOOSE_CONFREV_WRITABLE +#define CONFIG_GOOSE_CONFREV_WRITABLE 0 +#endif + +#ifndef CONFIG_GOOSE_NDSCOM_WRITABLE +#define CONFIG_GOOSE_NDSCOM_WRITABLE 0 +#endif + +#ifndef CONFIG_GOOSE_DSTADDRESS_WRITABLE +#define CONFIG_GOOSE_DSTADDRESS_WRITABLE 0 +#endif + +#ifndef CONFIG_GOOSE_MINTIME_WRITABLE +#define CONFIG_GOOSE_MINTIME_WRITABLE 0 +#endif + +#ifndef CONFIG_GOOSE_MAXTIME_WRITABLE +#define CONFIG_GOOSE_MAXTIME_WRITABLE 0 +#endif + +#ifndef CONFIG_GOOSE_FIXEDOFFS_WRITABLE +#define CONFIG_GOOSE_FIXEDOFFS_WRITABLE 0 +#endif + static MmsDataAccessError writeAccessGooseControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig, MmsValue* value) @@ -1370,16 +1402,77 @@ writeAccessGooseControlBlock(MmsMapping* self, MmsDomain* domain, char* variable if (MmsGooseControlBlock_isEnabled(mmsGCB)) return DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE; else { - MmsValue* subValue = MmsValue_getSubElement(MmsGooseControlBlock_getMmsValues(mmsGCB), + bool allowAccess = false; + +#if (CONFIG_GOOSE_GOID_WRITABLE == 1) + if (strcmp(varName, "GoID") == 0) { + MmsValue_update(MmsValue_getElement(MmsGooseControlBlock_getMmsValues(mmsGCB), 1), value); + allowAccess = true; + } +#endif + +#if (CONFIG_GOOSE_DATSET_WRITABLE == 1) + if (strcmp(varName, "DatSet") == 0) { + MmsValue_update(MmsValue_getElement(MmsGooseControlBlock_getMmsValues(mmsGCB), 2), value); + allowAccess = true; + } +#endif + +#if (CONFIG_GOOSE_CONFREV_WRITABLE == 1) + if (strcmp(varName, "ConfRev") == 0) { + MmsValue_update(MmsValue_getElement(MmsGooseControlBlock_getMmsValues(mmsGCB), 3), value); + allowAccess = true; + } +#endif + +#if (CONFIG_GOOSE_NDSCOM_WRITABLE == 1) + if (strcmp(varName, "NdsCom") == 0) { + MmsValue_update(MmsValue_getElement(MmsGooseControlBlock_getMmsValues(mmsGCB), 4), value); + allowAccess = true; + } +#endif + +#if (CONFIG_GOOSE_DSTADDRESS_WRITABLE == 1) + if (memcmp(varName, "DstAddress", 9) == 0) { + MmsValue* subValue = MmsValue_getSubElement(MmsGooseControlBlock_getMmsValues(mmsGCB), MmsGooseControlBlock_getVariableSpecification(mmsGCB), varName); - if (subValue == NULL) - return DATA_ACCESS_ERROR_INVALID_ADDRESS; + if (subValue == NULL) + return DATA_ACCESS_ERROR_INVALID_ADDRESS; - if (MmsValue_update(subValue, value)) + if (MmsValue_update(subValue, value)) + return DATA_ACCESS_ERROR_SUCCESS; + else + return DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID; + } +#endif + +#if (CONFIG_GOOSE_MINTIME_WRITABLE == 1) + if (strcmp(varName, "MinTime") == 0) { + MmsValue_update(MmsValue_getElement(MmsGooseControlBlock_getMmsValues(mmsGCB), 6), value); + allowAccess = true; + } +#endif + +#if (CONFIG_GOOSE_MAXTIME_WRITABLE == 1) + if (strcmp(varName, "MaxTime") == 0) { + MmsValue_update(MmsValue_getElement(MmsGooseControlBlock_getMmsValues(mmsGCB), 7), value); + allowAccess = true; + } +#endif + +#if (CONFIG_GOOSE_FIXEDOFFS_WRITABLE == 1) + if (strcmp(varName, "FixedOffs") == 0) { + MmsValue_update(MmsValue_getElement(MmsGooseControlBlock_getMmsValues(mmsGCB), 8), value); + allowAccess = true; + } +#endif + + if (allowAccess) return DATA_ACCESS_ERROR_SUCCESS; else - return DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID; + return DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; + } } } diff --git a/src/iec61850/server/model/config_file_parser.c b/src/iec61850/server/model/config_file_parser.c index 0e4d353..fb22fb9 100644 --- a/src/iec61850/server/model/config_file_parser.c +++ b/src/iec61850/server/model/config_file_parser.c @@ -221,15 +221,16 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle) else if (StringUtils_startsWith((char*) lineBuffer, "GC")) { uint32_t confRef; int fixedOffs; + int minTime = -1; + int maxTime = -1; - int matchedItems = sscanf((char*) lineBuffer, "GC(%s %s %s %u %i)", - nameString, nameString2, nameString3, &confRef, &fixedOffs); - + int matchedItems = sscanf((char*) lineBuffer, "GC(%s %s %s %u %i %i %i)", + nameString, nameString2, nameString3, &confRef, &fixedOffs, &minTime, &maxTime); if (matchedItems < 5) goto exit_error; currentGoCB = GSEControlBlock_create(nameString, currentLN, nameString2, - nameString3, confRef, fixedOffs); + nameString3, confRef, fixedOffs, minTime, maxTime); indendation = 4; @@ -257,7 +258,7 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle) #endif /* (CONFIG_IEC61850_SETTING_GROUPS == 1) */ else { - // if (DEBUG_IED_SERVER) + if (DEBUG_IED_SERVER) printf("IED_SERVER: Unknown identifier (%s)\n", lineBuffer); goto exit_error; @@ -430,7 +431,7 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle) return model; exit_error: - // if (DEBUG_IED_SERVER) + if (DEBUG_IED_SERVER) printf("IED_SERVER: error parsing line %i (indendation level = %i)\n", currentLine, indendation); IedModel_destroy(model); return NULL; diff --git a/src/iec61850/server/model/dynamic_model.c b/src/iec61850/server/model/dynamic_model.c index f536d47..5be7348 100644 --- a/src/iec61850/server/model/dynamic_model.c +++ b/src/iec61850/server/model/dynamic_model.c @@ -302,7 +302,8 @@ LogicalNode_addGSEControlBlock(LogicalNode* self, GSEControlBlock* gcb) } GSEControlBlock* -GSEControlBlock_create(const char* name, LogicalNode* parent, char* appId, char* dataSet, uint32_t confRef, bool fixedOffs) +GSEControlBlock_create(const char* name, LogicalNode* parent, char* appId, char* dataSet, uint32_t confRef, bool fixedOffs, + int minTime, int maxTime) { GSEControlBlock* self = (GSEControlBlock*) GLOBAL_MALLOC(sizeof(GSEControlBlock)); @@ -321,6 +322,8 @@ GSEControlBlock_create(const char* name, LogicalNode* parent, char* appId, char* self->confRef = confRef; self->fixedOffs = fixedOffs; + self->minTime = minTime; + self->maxTime = maxTime; self->address = NULL; diff --git a/src/mms/inc_private/mms_server_internal.h b/src/mms/inc_private/mms_server_internal.h index 4dede14..000936d 100644 --- a/src/mms/inc_private/mms_server_internal.h +++ b/src/mms/inc_private/mms_server_internal.h @@ -124,7 +124,7 @@ struct sMmsServerConnection { int maxServOutstandingCalling; int maxServOutstandingCalled; int dataStructureNestingLevel; - int maxPduSize; /* local detail */ + uint32_t maxPduSize; /* local detail */ IsoConnection isoConnection; MmsServer server; uint32_t lastInvokeId; diff --git a/src/mms/iso_mms/server/mms_read_service.c b/src/mms/iso_mms/server/mms_read_service.c index 30cf343..6ad75e4 100644 --- a/src/mms/iso_mms/server/mms_read_service.c +++ b/src/mms/iso_mms/server/mms_read_service.c @@ -322,27 +322,27 @@ static int encodeVariableAccessSpecification(VarAccessSpec* accessSpec, uint8_t* buffer, int bufPos, bool encode) { /* determine size */ - int varAccessSpecSize = 0; + uint32_t varAccessSpecSize = 0; - int itemIdLen = strlen(accessSpec->itemId); + uint32_t itemIdLen = strlen(accessSpec->itemId); varAccessSpecSize += itemIdLen + BerEncoder_determineLengthSize(itemIdLen) + 1; if (accessSpec->domainId != NULL) { - int domainIdLen = strlen(accessSpec->domainId); + uint32_t domainIdLen = strlen(accessSpec->domainId); varAccessSpecSize += domainIdLen + BerEncoder_determineLengthSize(domainIdLen) + 1; } - int specificityLength = varAccessSpecSize; + uint32_t specificityLength = varAccessSpecSize; varAccessSpecSize += 1 + BerEncoder_determineLengthSize(specificityLength); - int variableListNameLength = varAccessSpecSize; + uint32_t variableListNameLength = varAccessSpecSize; varAccessSpecSize += 1 + BerEncoder_determineLengthSize(variableListNameLength); - int varAccessSpecLength = varAccessSpecSize; + uint32_t varAccessSpecLength = varAccessSpecSize; varAccessSpecSize += 1 + BerEncoder_determineLengthSize(varAccessSpecLength); @@ -389,14 +389,14 @@ encodeReadResponse(MmsServerConnection connection, int variableCount = LinkedList_size(values); - int varAccessSpecSize = 0; + uint32_t varAccessSpecSize = 0; if (accessSpec != NULL) { varAccessSpecSize = encodeVariableAccessSpecification(accessSpec, NULL, 0, false); } /* determine BER encoded message sizes */ - int accessResultSize = 0; + uint32_t accessResultSize = 0; /* iterate values list to determine encoded size */ LinkedList value = LinkedList_getNext(values); @@ -410,21 +410,21 @@ encodeReadResponse(MmsServerConnection connection, value = LinkedList_getNext(value); } - int listOfAccessResultsLength = 1 + + uint32_t listOfAccessResultsLength = 1 + BerEncoder_determineLengthSize(accessResultSize) + accessResultSize; - int confirmedServiceResponseContentLength = listOfAccessResultsLength + varAccessSpecSize; + uint32_t confirmedServiceResponseContentLength = listOfAccessResultsLength + varAccessSpecSize; - int confirmedServiceResponseLength = 1 + + uint32_t confirmedServiceResponseLength = 1 + BerEncoder_determineLengthSize(confirmedServiceResponseContentLength) + confirmedServiceResponseContentLength; - int invokeIdSize = BerEncoder_UInt32determineEncodedSize(invokeId) + 2; + uint32_t invokeIdSize = BerEncoder_UInt32determineEncodedSize(invokeId) + 2; - int confirmedResponseContentSize = confirmedServiceResponseLength + invokeIdSize; + uint32_t confirmedResponseContentSize = confirmedServiceResponseLength + invokeIdSize; - int mmsPduSize = 1 + BerEncoder_determineLengthSize(confirmedResponseContentSize) + + uint32_t mmsPduSize = 1 + BerEncoder_determineLengthSize(confirmedResponseContentSize) + confirmedResponseContentSize; /* Check if message would fit in the MMS PDU */ diff --git a/tools/model_generator/genconfig.jar b/tools/model_generator/genconfig.jar index bd3666a..625d0eb 100644 Binary files a/tools/model_generator/genconfig.jar and b/tools/model_generator/genconfig.jar differ diff --git a/tools/model_generator/genmodel.jar b/tools/model_generator/genmodel.jar index 4ad96ab..e2af5a1 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/scl/model/GSEControl.java b/tools/model_generator/src/com/libiec61850/scl/model/GSEControl.java index 68d79a1..40ee837 100644 --- a/tools/model_generator/src/com/libiec61850/scl/model/GSEControl.java +++ b/tools/model_generator/src/com/libiec61850/scl/model/GSEControl.java @@ -34,6 +34,8 @@ public class GSEControl { private int confRev = 1; private String appID; private boolean fixedOffs = false; + private int minTime = -1; + private int maxTime = -1; public GSEControl(Node gseControlNode) throws SclParserException { @@ -53,6 +55,15 @@ public class GSEControl { if (fixedOffs != null) this.fixedOffs = fixedOffs; + String minTimeStr = ParserUtils.parseAttribute(gseControlNode, "minTime"); + String maxTimeStr = ParserUtils.parseAttribute(gseControlNode, "maxTime"); + + if (minTimeStr != null) + minTime = new Integer(minTimeStr); + + if (maxTimeStr != null) + maxTime = new Integer(maxTimeStr); + String typeString = ParserUtils.parseAttribute(gseControlNode, "type"); if (typeString != null) @@ -84,5 +95,13 @@ public class GSEControl { public boolean isFixedOffs() { return fixedOffs; } + + public int getMinTime() { + return minTime; + } + + public int getMaxTime() { + return maxTime; + } } diff --git a/tools/model_generator/src/com/libiec61850/tools/DynamicModelGenerator.java b/tools/model_generator/src/com/libiec61850/tools/DynamicModelGenerator.java index 03d3cc6..ac47251 100644 --- a/tools/model_generator/src/com/libiec61850/tools/DynamicModelGenerator.java +++ b/tools/model_generator/src/com/libiec61850/tools/DynamicModelGenerator.java @@ -161,7 +161,12 @@ public class DynamicModelGenerator { output.print('1'); else output.print('0'); - + output.print(' '); + output.print(gcb.getMinTime()); + output.print(' '); + output.print(gcb.getMaxTime()); + output.print(' '); + if (gseAddress == null) { output.println(");"); } diff --git a/tools/model_generator/src/com/libiec61850/tools/StaticModelGenerator.java b/tools/model_generator/src/com/libiec61850/tools/StaticModelGenerator.java index ba23d76..22ad738 100644 --- a/tools/model_generator/src/com/libiec61850/tools/StaticModelGenerator.java +++ b/tools/model_generator/src/com/libiec61850/tools/StaticModelGenerator.java @@ -754,14 +754,17 @@ public class StaticModelGenerator { gseString += gseControlBlock.getConfRev() + ", "; if (gseControlBlock.isFixedOffs()) - gseString += "true,"; + gseString += "true, "; else - gseString += "false,"; + gseString += "false, "; if (gseAddress != null) gseString += "&" + phyComAddrName + ", "; else gseString += "NULL, "; + + gseString += gseControlBlock.getMinTime() + ", "; + gseString += gseControlBlock.getMaxTime() + ", "; currentGseVariableNumber++;