- IED server: extended config file format to support arrays of data objects (LIB61850-415)

pull/515/head
Michael Zillgith 1 year ago
parent 4eebcb96a2
commit f536d1c324

@ -1,7 +1,7 @@
/*
* config_file_parser.c
*
* Copyright 2014-2023 Michael Zillgith
* Copyright 2014-2024 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -304,7 +304,6 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle)
currentModelNode = currentModelNode->parent;
}
}
else if (indendation == 1)
{
if (StringUtils_startsWith((char*) lineBuffer, "LD"))
@ -514,8 +513,15 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle)
if (matchedItems != 2) goto exit_error;
currentModelNode = (ModelNode*) DataObject_create(nameString, currentModelNode, arrayElements);
if (arrayElements > 0)
{
inArray = true;
currentArrayNode = currentModelNode;
}
}
else if (StringUtils_startsWith((char*) lineBuffer, "[")) {
else if (StringUtils_startsWith((char*) lineBuffer, "["))
{
if (inArray == false) {
goto exit_error;
}
@ -526,27 +532,55 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle)
goto exit_error;
}
if (StringUtils_endsWith((char*)lineBuffer, ";"))
if (arrayIndex < 0) {
goto exit_error;
}
if (currentArrayNode->modelType == DataAttributeModelType)
{
/* array of basic data attribute */
ModelNode* arrayElementNode = ModelNode_getChildWithIdx(currentArrayNode, arrayIndex);
if (StringUtils_endsWith((char*)lineBuffer, ";"))
{
/* array of basic data attribute */
ModelNode* arrayElementNode = ModelNode_getChildWithIdx(currentArrayNode, arrayIndex);
if (arrayElementNode) {
setValue((char*)lineBuffer, (DataAttribute*)arrayElementNode);
if (arrayElementNode) {
setValue((char*)lineBuffer, (DataAttribute*)arrayElementNode);
}
else {
goto exit_error;
}
}
else {
goto exit_error;
else if (StringUtils_endsWith((char*)lineBuffer, "{"))
{
/* array of constructed data attribtute */
currentModelNode = ModelNode_getChildWithIdx(currentArrayNode, arrayIndex);
if (currentModelNode) {
inArrayElement = true;
}
else {
goto exit_error;
}
}
}
else if (StringUtils_endsWith((char*)lineBuffer, "{"))
else if (currentArrayNode->modelType == DataObjectModelType)
{
/* array of constructed data attribtute */
currentModelNode = ModelNode_getChildWithIdx(currentArrayNode, arrayIndex);
if (StringUtils_endsWith((char*)lineBuffer, "{"))
{
/* array of constructed data attribtute */
currentModelNode = ModelNode_getChildWithIdx(currentArrayNode, arrayIndex);
if (currentModelNode) {
inArrayElement = true;
if (currentModelNode) {
inArrayElement = true;
}
else {
goto exit_error;
}
}
else {
else
{
if (DEBUG_IED_SERVER)
printf("Unexpected character at end of line: %s\n", lineBuffer);
goto exit_error;
}
}
@ -567,7 +601,8 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle)
DataAttribute* dataAttribute = DataAttribute_create(nameString, currentModelNode,
(DataAttributeType) attributeType, (FunctionalConstraint) functionalConstraint, triggerOptions, arrayElements, sAddr);
if (arrayElements > 0) {
if (arrayElements > 0)
{
inArray = true;
currentArrayNode = (ModelNode*)dataAttribute;
}
@ -576,7 +611,8 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle)
int lineLength = (int) strlen((char*) lineBuffer);
if (lineBuffer[lineLength - 1] == '{') {
if (lineBuffer[lineLength - 1] == '{')
{
indendation++;
currentModelNode = (ModelNode*) dataAttribute;
}
@ -585,7 +621,8 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle)
{
char* start = strchr((char*) lineBuffer, '(');
if (start) {
if (start)
{
start++;
StringUtils_copyStringMax(nameString, 130, start);
@ -598,7 +635,8 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle)
/* check for index */
char* sep = strchr(nameString, ' ');
if (sep) {
if (sep)
{
char* indexStr = sep + 1;
*sep = 0;
@ -633,7 +671,6 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle)
if (StringUtils_createBufferFromHexString(nameString, (uint8_t*) nameString2) != 6)
goto exit_error;
PhyComAddress* dstAddress =
PhyComAddress_create((uint8_t) vlanPrio, (uint16_t) vlanId, (uint16_t) appId,
(uint8_t*) nameString2);

@ -1,7 +1,7 @@
/*
* dynamic_model.c
*
* Copyright 2014-2022 Michael Zillgith
* Copyright 2014-2024 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -75,10 +75,12 @@ IedModel_addDataSet(IedModel* self, DataSet* dataSet)
{
if (self->dataSets == NULL)
self->dataSets = dataSet;
else {
else
{
DataSet* lastDataSet = self->dataSets;
while (lastDataSet != NULL) {
while (lastDataSet != NULL)
{
if (lastDataSet->sibling == NULL) {
lastDataSet->sibling = dataSet;
break;
@ -94,7 +96,8 @@ IedModel_addLogicalDevice(IedModel* self, LogicalDevice* lDevice)
{
if (self->firstChild == NULL)
self->firstChild = lDevice;
else {
else
{
LogicalDevice* sibling = self->firstChild;
while (sibling->sibling != NULL)
@ -109,7 +112,8 @@ IedModel_addLog(IedModel* self, Log* log)
{
if (self->logs == NULL)
self->logs = log;
else {
else
{
Log* lastLog = self->logs;
while (lastLog->sibling != NULL)
@ -124,7 +128,8 @@ IedModel_addLogControlBlock(IedModel* self, LogControlBlock* lcb)
{
if (self->lcbs == NULL)
self->lcbs = lcb;
else {
else
{
LogControlBlock* lastLcb = self->lcbs;
while (lastLcb->sibling != NULL)
@ -139,7 +144,8 @@ IedModel_addReportControlBlock(IedModel* self, ReportControlBlock* rcb)
{
if (self->rcbs == NULL)
self->rcbs = rcb;
else {
else
{
ReportControlBlock* lastRcb = self->rcbs;
while (lastRcb->sibling != NULL)
@ -155,7 +161,8 @@ IedModel_addSettingGroupControlBlock(IedModel* self, SettingGroupControlBlock* s
{
if (self->sgcbs == NULL)
self->sgcbs = sgcb;
else {
else
{
SettingGroupControlBlock* lastSgcb = self->sgcbs;
while (lastSgcb->sibling != NULL)
@ -171,7 +178,8 @@ IedModel_addGSEControlBlock(IedModel* self, GSEControlBlock* gcb)
{
if (self->gseCBs == NULL)
self->gseCBs = gcb;
else {
else
{
GSEControlBlock* lastGcb = self->gseCBs;
while (lastGcb->sibling)
@ -187,7 +195,8 @@ IedModel_addSMVControlBlock(IedModel* self, SVControlBlock* smvcb)
if (self->svCBs == NULL) {
self->svCBs = smvcb;
}
else {
else
{
SVControlBlock* lastSvCB = self->svCBs;
while (lastSvCB->sibling)
@ -202,7 +211,8 @@ LogicalDevice_createEx(const char* inst, IedModel* parent, const char* ldName)
{
LogicalDevice* self = (LogicalDevice*) GLOBAL_CALLOC(1, sizeof(LogicalDevice));
if (self) {
if (self)
{
self->name = StringUtils_copyString(inst);
self->modelType = LogicalDeviceModelType;
self->parent = (ModelNode*) parent;
@ -257,7 +267,8 @@ LogicalNode_create(const char* name, LogicalDevice* parent)
{
LogicalNode* self = (LogicalNode*) GLOBAL_MALLOC(sizeof(LogicalNode));
if (self) {
if (self)
{
self->name = StringUtils_copyString(name);
self->parent = (ModelNode*) parent;
self->modelType = LogicalNodeModelType;
@ -311,7 +322,8 @@ Log_create(const char* name, LogicalNode* parent)
{
Log* self = (Log*) GLOBAL_MALLOC(sizeof(Log));
if (self) {
if (self)
{
self->name = StringUtils_copyString(name);
self->parent = parent;
self->sibling = NULL;
@ -336,7 +348,8 @@ LogControlBlock_create(const char* name, LogicalNode* parent, const char* dataSe
{
LogControlBlock* self = (LogControlBlock*) GLOBAL_MALLOC(sizeof(LogControlBlock));
if (self) {
if (self)
{
self->name = StringUtils_copyString(name);
self->parent = parent;
self->sibling = NULL;
@ -388,7 +401,8 @@ ReportControlBlock_create(const char* name, LogicalNode* parent, const char* rpt
{
ReportControlBlock* self = (ReportControlBlock*) GLOBAL_MALLOC(sizeof(ReportControlBlock));
if (self) {
if (self)
{
self->name = StringUtils_copyString(name);
self->parent = parent;
@ -469,7 +483,8 @@ SettingGroupControlBlock_create(LogicalNode* parent, uint8_t actSG, uint8_t numO
SettingGroupControlBlock* self = (SettingGroupControlBlock*) GLOBAL_MALLOC(sizeof(SettingGroupControlBlock));
if (self) {
if (self)
{
self->parent = parent;
self->actSG = actSG;
self->numOfSGs = numOfSGs;
@ -497,7 +512,8 @@ GSEControlBlock_create(const char* name, LogicalNode* parent, const char* appId,
{
GSEControlBlock* self = (GSEControlBlock*) GLOBAL_MALLOC(sizeof(GSEControlBlock));
if (self) {
if (self)
{
self->name = StringUtils_copyString(name);
self->parent = parent;
@ -541,7 +557,8 @@ SVControlBlock_create(const char* name, LogicalNode* parent, const char* svID, c
{
SVControlBlock* self = (SVControlBlock*) GLOBAL_MALLOC(sizeof(SVControlBlock));
if (self) {
if (self)
{
self->name = StringUtils_copyString(name);
self->parent = parent;
@ -587,7 +604,8 @@ PhyComAddress_create(uint8_t vlanPriority, uint16_t vlanId, uint16_t appId, uint
{
PhyComAddress* self = (PhyComAddress*) GLOBAL_MALLOC(sizeof(PhyComAddress));
if (self) {
if (self)
{
self->vlanPriority = vlanPriority;
self->vlanId = vlanId;
self->appId = appId;
@ -630,7 +648,8 @@ DataObject_create(const char* name, ModelNode* parent, int arrayElements)
{
DataObject* self = (DataObject*) GLOBAL_MALLOC(sizeof(DataObject));
if (self) {
if (self)
{
self->name = StringUtils_copyString(name);
self->modelType = DataObjectModelType;
self->firstChild = NULL;
@ -640,21 +659,23 @@ DataObject_create(const char* name, ModelNode* parent, int arrayElements)
self->elementCount = arrayElements;
self->arrayIndex = -1;
if (arrayElements > 0) {
if (arrayElements > 0)
{
int i;
for (i = 0; i < arrayElements; i++) {
for (i = 0; i < arrayElements; i++)
{
DataObject* arrayElement = (DataObject*) GLOBAL_MALLOC(sizeof(DataObject));
if (self) {
self->name = NULL;
self->modelType = DataObjectModelType;
self->firstChild = NULL;
self->parent = parent;
self->sibling = NULL;
if (arrayElement) {
arrayElement->name = NULL;
arrayElement->modelType = DataObjectModelType;
arrayElement->firstChild = NULL;
arrayElement->parent = (ModelNode*) self;
arrayElement->sibling = NULL;
self->elementCount = 0;
self->arrayIndex = i;
arrayElement->elementCount = 0;
arrayElement->arrayIndex = i;
DataObject_addChild(self, (ModelNode*) arrayElement);
}
@ -703,7 +724,8 @@ DataAttribute_create(const char* name, ModelNode* parent, DataAttributeType type
{
DataAttribute* self = (DataAttribute*) GLOBAL_MALLOC(sizeof(DataAttribute));
if (self) {
if (self)
{
self->name = StringUtils_copyString(name);
self->elementCount = arrayElements;
self->arrayIndex = -1;
@ -717,13 +739,16 @@ DataAttribute_create(const char* name, ModelNode* parent, DataAttributeType type
self->triggerOptions = triggerOptions;
self->sAddr = sAddr;
if (arrayElements > 0) {
if (arrayElements > 0)
{
int i;
for (i = 0; i < arrayElements; i++) {
for (i = 0; i < arrayElements; i++)
{
DataAttribute* arrayElement = (DataAttribute*) GLOBAL_MALLOC(sizeof(DataAttribute));
if (arrayElement) {
if (arrayElement)
{
arrayElement->name = NULL;
arrayElement->elementCount = 0;
arrayElement->arrayIndex = i;
@ -785,7 +810,8 @@ DataSet_create(const char* name, LogicalNode* parent)
{
DataSet* self = (DataSet*) GLOBAL_MALLOC(sizeof(DataSet));
if (self) {
if (self)
{
LogicalDevice* ld = (LogicalDevice*) parent->parent;
self->name = StringUtils_createString(3, parent->name, "$", name);
@ -831,11 +857,12 @@ DataSet_addEntry(DataSet* self, DataSetEntry* newEntry)
if (self->fcdas == NULL)
self->fcdas = newEntry;
else {
else
{
DataSetEntry* lastEntry = self->fcdas;
while (lastEntry != NULL) {
while (lastEntry != NULL)
{
if (lastEntry->sibling == NULL) {
lastEntry->sibling = newEntry;
break;
@ -851,21 +878,24 @@ DataSetEntry_create(DataSet* dataSet, const char* variable, int index, const cha
{
DataSetEntry* self = (DataSetEntry*) GLOBAL_MALLOC(sizeof(DataSetEntry));
if (self) {
if (self)
{
char variableName[130];
StringUtils_copyStringMax(variableName, 130, variable);
char* separator = strchr(variableName, '/');
if (separator != NULL) {
if (separator != NULL)
{
*separator = 0;
self->variableName = StringUtils_copyString(separator + 1);
self->logicalDeviceName = StringUtils_copyString(variableName);
self->isLDNameDynamicallyAllocated = true;
}
else {
else
{
self->variableName = StringUtils_copyString(variable);
self->logicalDeviceName = dataSet->logicalDeviceName;
self->isLDNameDynamicallyAllocated = false;
@ -891,14 +921,15 @@ DataSetEntry_create(DataSet* dataSet, const char* variable, int index, const cha
static void
ModelNode_destroy(ModelNode* modelNode)
{
if (modelNode) {
if (modelNode)
{
if (modelNode->name)
GLOBAL_FREEMEM(modelNode->name);
ModelNode* currentChild = modelNode->firstChild;
while (currentChild != NULL) {
while (currentChild != NULL)
{
ModelNode* nextChild = currentChild->sibling;
ModelNode_destroy(currentChild);
@ -906,7 +937,8 @@ ModelNode_destroy(ModelNode* modelNode)
currentChild = nextChild;
}
if (modelNode->modelType == DataAttributeModelType) {
if (modelNode->modelType == DataAttributeModelType)
{
DataAttribute* dataAttribute = (DataAttribute*) modelNode;
if (dataAttribute->mmsValue != NULL) {
@ -929,8 +961,8 @@ IedModel_destroy(IedModel* model)
LogicalDevice* ld = model->firstChild;
while (ld != NULL) {
while (ld != NULL)
{
if (ld->name)
GLOBAL_FREEMEM(ld->name);
@ -939,7 +971,8 @@ IedModel_destroy(IedModel* model)
LogicalNode* ln = (LogicalNode*) ld->firstChild;
while (ln != NULL) {
while (ln != NULL)
{
GLOBAL_FREEMEM(ln->name);
/* delete all data objects */
@ -960,7 +993,6 @@ IedModel_destroy(IedModel* model)
GLOBAL_FREEMEM(currentLn);
}
LogicalDevice* currentLd = ld;
ld = (LogicalDevice*) ld->sibling;
@ -1092,4 +1124,3 @@ IedModel_destroy(IedModel* model)
GLOBAL_FREEMEM(model);
}
}

@ -808,12 +808,14 @@ ModelNode_getChildWithIdx(ModelNode* self, int idx)
{
ModelNode* foundElement = NULL;
if (self->modelType == DataObjectModelType || self->modelType == DataAttributeModelType) {
if (self->modelType == DataObjectModelType || self->modelType == DataAttributeModelType)
{
ModelNode* nextNode = self->firstChild;
int currentIdx = 0;
while (nextNode) {
while (nextNode)
{
if (currentIdx == idx) {
foundElement = nextNode;
break;
@ -845,14 +847,17 @@ ModelNode_getChildWithFc(ModelNode* self, const char* name, FunctionalConstraint
ModelNode* matchingNode = NULL;
while (nextNode != NULL) {
while (nextNode != NULL)
{
int nodeNameLen = strlen(nextNode->name);
if (nodeNameLen == nameElementLength) {
if (memcmp(nextNode->name, name, nodeNameLen) == 0) {
if (nodeNameLen == nameElementLength)
{
if (memcmp(nextNode->name, name, nodeNameLen) == 0)
{
if (separator == NULL) {
if (nextNode->modelType == DataAttributeModelType) {
if (nextNode->modelType == DataAttributeModelType)
{
DataAttribute* da = (DataAttribute*) nextNode;
if (da->fc == fc) {
@ -861,9 +866,10 @@ ModelNode_getChildWithFc(ModelNode* self, const char* name, FunctionalConstraint
}
}
}
else {
if (nextNode->modelType == DataAttributeModelType) {
else
{
if (nextNode->modelType == DataAttributeModelType)
{
DataAttribute* da = (DataAttribute*) nextNode;
if (da->fc == fc) {

@ -3,7 +3,7 @@ package com.libiec61850.tools;
/*
* DynamicModelGenerator.java
*
* Copyright 2014-2020 Michael Zillgith
* Copyright 2014-2024 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -447,11 +447,7 @@ public class DynamicModelGenerator {
output.println("}");
}
private void exportDataObject(PrintStream output, DataObject dataObject, boolean isTransient) {
if (dataObject.isTransient())
isTransient = true;
private void exportDataObjectChild(PrintStream output, DataObject dataObject, boolean isTransient) {
for (DataObject subDataObject : dataObject.getSubDataObjects()) {
output.print("DO(" + subDataObject.getName() + " " + subDataObject.getCount() + "){\n");
@ -465,6 +461,105 @@ public class DynamicModelGenerator {
}
}
private void exportDataObject(PrintStream output, DataObject dataObject, boolean isTransient) {
if (dataObject.isTransient())
isTransient = true;
if (dataObject.getCount() > 0) {
/* data object is an array */
for (int i = 0; i < dataObject.getCount(); i++) {
output.print("[" + i + "]{\n");
exportDataObjectChild(output, dataObject, isTransient);
output.print("}\n");
}
}
else {
exportDataObjectChild(output, dataObject, isTransient);
}
}
private void printDataAttributeValue(PrintStream output, DataAttribute dataAttribute, boolean isTransient)
{
if (dataAttribute.isBasicAttribute()) {
DataModelValue value = dataAttribute.getValue();
/* if no value is given use default value for type if present */
if (value == null) {
value = dataAttribute.getDefinition().getValue();
if (value != null)
if (value.getValue() == null)
value.updateEnumOrdValue(ied.getTypeDeclarations());
}
if (value != null) {
switch (dataAttribute.getType()) {
case ENUMERATED:
case INT8:
case INT16:
case INT32:
case INT64:
output.print("=" + value.getIntValue());
break;
case INT8U:
case INT16U:
case INT24U:
case INT32U:
output.print("=" + value.getLongValue());
break;
case BOOLEAN:
{
Boolean boolVal = (Boolean) value.getValue();
if (boolVal.booleanValue())
output.print("=1");
}
break;
case UNICODE_STRING_255:
output.print("=\"" + value.getValue()+ "\"");
break;
case CURRENCY:
case VISIBLE_STRING_32:
case VISIBLE_STRING_64:
case VISIBLE_STRING_129:
case VISIBLE_STRING_255:
case VISIBLE_STRING_65:
output.print("=\"" + value.getValue()+ "\"");
break;
case FLOAT32:
case FLOAT64:
output.print("=" + value.getValue());
break;
case TIMESTAMP:
case ENTRY_TIME:
output.print("=" + value.getLongValue());
break;
default:
System.out.println("Unknown default value for " + dataAttribute.getName() + " type: " + dataAttribute.getType());
break;
}
}
output.println(";");
}
else {
output.println("{");
for (DataAttribute subDataAttribute : dataAttribute.getSubDataAttributes()) {
exportDataAttribute(output, subDataAttribute, isTransient);
}
output.println("}");
}
}
private void exportDataAttribute(PrintStream output, DataAttribute dataAttribute, boolean isTransient) {
output.print("DA(" + dataAttribute.getName() + " ");
@ -493,83 +588,22 @@ public class DynamicModelGenerator {
else
output.print("0");
output.print(")");
if (dataAttribute.isBasicAttribute()) {
DataModelValue value = dataAttribute.getValue();
/* if no value is given use default value for type if present */
if (value == null) {
value = dataAttribute.getDefinition().getValue();
if (value != null)
if (value.getValue() == null)
value.updateEnumOrdValue(ied.getTypeDeclarations());
}
if (value != null) {
switch (dataAttribute.getType()) {
case ENUMERATED:
case INT8:
case INT16:
case INT32:
case INT64:
output.print("=" + value.getIntValue());
break;
case INT8U:
case INT16U:
case INT24U:
case INT32U:
output.print("=" + value.getLongValue());
break;
case BOOLEAN:
{
Boolean boolVal = (Boolean) value.getValue();
if (boolVal.booleanValue())
output.print("=1");
}
break;
case UNICODE_STRING_255:
output.print("=\"" + value.getValue()+ "\"");
break;
case CURRENCY:
case VISIBLE_STRING_32:
case VISIBLE_STRING_64:
case VISIBLE_STRING_129:
case VISIBLE_STRING_255:
case VISIBLE_STRING_65:
output.print("=\"" + value.getValue()+ "\"");
break;
case FLOAT32:
case FLOAT64:
output.print("=" + value.getValue());
break;
case TIMESTAMP:
case ENTRY_TIME:
output.print("=" + value.getLongValue());
break;
default:
System.out.println("Unknown default value for " + dataAttribute.getName() + " type: " + dataAttribute.getType());
break;
}
}
output.println(";");
}
else {
output.println("{");
output.print(")");
for (DataAttribute subDataAttribute : dataAttribute.getSubDataAttributes()) {
exportDataAttribute(output, subDataAttribute, isTransient);
if (dataAttribute.getCount() > 0) {
output.print("{\n");
for (int i = 0; i < dataAttribute.getCount(); i++) {
output.print("[" + i + "]");
printDataAttributeValue(output, dataAttribute, isTransient);
}
output.println("}");
output.print("}\n");
}
else {
printDataAttributeValue(output, dataAttribute, isTransient);
}
}
public static void main(String[] args) throws FileNotFoundException {

Loading…
Cancel
Save