- added support for GOOSE minTime, maxTime in SCL parser

- configuration options for write access to GoCB elements
- extended file format and data structure to support configuration of minTime, maxTime
This commit is contained in:
Michael Zillgith 2015-02-24 17:37:35 +01:00
parent 98d91464cb
commit 8b27f57d15
17 changed files with 206 additions and 36 deletions

View file

@ -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

View file

@ -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

View file

@ -77,7 +77,7 @@
<OptFields seqNum="true" timeStamp="true" dataSet="true" reasonCode="true" entryID="true" configRef="true" />
<RptEnabled max="1" />
</ReportControl>
<GSEControl appID="events" name="gcbEvents" type="GOOSE" datSet="Events" confRev="2"/>
<GSEControl appID="events" name="gcbEvents" type="GOOSE" datSet="Events" confRev="2" minTime="1000" maxTime="3000" />
<GSEControl appID="analog" name="gcbAnalogValues" type="GOOSE" datSet="AnalogValues" confRev="2"/>
<DOI name="Mod">
<DAI name="ctlModel">

View file

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

View file

@ -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

View file

@ -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 */
};

View file

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

View file

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

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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 */

Binary file not shown.

Binary file not shown.

View file

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

View file

@ -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(");");
}

View file

@ -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++;