You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
676 lines
14 KiB
C
676 lines
14 KiB
C
/*
|
|
* model.c
|
|
*
|
|
* Copyright 2013 Michael Zillgith
|
|
*
|
|
* This file is part of libIEC61850.
|
|
*
|
|
* libIEC61850 is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* libIEC61850 is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with libIEC61850. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
* See COPYING file for the complete license text.
|
|
*/
|
|
|
|
#include "iec61850_model.h"
|
|
|
|
#include "libiec61850_platform_includes.h"
|
|
|
|
static void
|
|
setAttributeValuesToNull(ModelNode* node)
|
|
{
|
|
if (node->modelType == DataAttributeModelType) {
|
|
DataAttribute* da = (DataAttribute*) node;
|
|
|
|
da->mmsValue = NULL;
|
|
}
|
|
|
|
ModelNode* child = node->firstChild;
|
|
|
|
while (child != NULL) {
|
|
setAttributeValuesToNull(child);
|
|
child = child->sibling;
|
|
}
|
|
}
|
|
|
|
void
|
|
IedModel_setIedName(IedModel* self, const char* name)
|
|
{
|
|
self->name = (char*) name;
|
|
}
|
|
|
|
void
|
|
IedModel_setAttributeValuesToNull(IedModel* iedModel)
|
|
{
|
|
LogicalDevice* ld = iedModel->firstChild;
|
|
|
|
while (ld != NULL) {
|
|
|
|
LogicalNode* ln = (LogicalNode*) ld->firstChild;
|
|
|
|
while (ln != NULL) {
|
|
ModelNode* node = ln->firstChild;
|
|
|
|
while (node != NULL) {
|
|
setAttributeValuesToNull(node);
|
|
node = node->sibling;
|
|
}
|
|
|
|
ln = (LogicalNode*) ln->sibling;
|
|
}
|
|
|
|
ld = (LogicalDevice*) ld->sibling;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
int
|
|
IedModel_getLogicalDeviceCount(IedModel* iedModel)
|
|
{
|
|
if (iedModel->firstChild == NULL)
|
|
return 0;
|
|
|
|
LogicalDevice* logicalDevice = iedModel->firstChild;
|
|
|
|
int ldCount = 1;
|
|
|
|
while (logicalDevice->sibling != NULL) {
|
|
logicalDevice = (LogicalDevice*) logicalDevice->sibling;
|
|
ldCount++;
|
|
}
|
|
|
|
return ldCount;
|
|
}
|
|
|
|
DataSet*
|
|
IedModel_lookupDataSet(IedModel* model, const char* dataSetReference /* e.g. ied1Inverter/LLN0$dataset1 */)
|
|
{
|
|
DataSet* dataSet = model->dataSets;
|
|
|
|
const char* separator = strchr(dataSetReference, '/');
|
|
|
|
if (separator == NULL)
|
|
return NULL;
|
|
|
|
int ldNameLen = separator - dataSetReference;
|
|
|
|
char domainName[65];
|
|
|
|
int modelNameLen = strlen(model->name);
|
|
|
|
memcpy(domainName, model->name, modelNameLen);
|
|
|
|
while (dataSet != NULL) {
|
|
|
|
domainName[modelNameLen] = 0;
|
|
|
|
strncat(domainName, dataSet->logicalDeviceName, 64);
|
|
|
|
if (strncmp(domainName, dataSetReference, ldNameLen) == 0) {
|
|
if (strcmp(dataSet->name, separator + 1) == 0) {
|
|
return dataSet;
|
|
}
|
|
}
|
|
|
|
dataSet = dataSet->sibling;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
LogicalDevice*
|
|
IedModel_getDevice(IedModel* model, const char* deviceName)
|
|
{
|
|
LogicalDevice* device = model->firstChild;
|
|
|
|
while (device != NULL) {
|
|
|
|
char domainName[65];
|
|
|
|
strncpy(domainName, model->name, 64);
|
|
strncat(domainName, device->name, 64);
|
|
|
|
if (strcmp(domainName, deviceName) == 0)
|
|
return device;
|
|
|
|
device = (LogicalDevice*) device->sibling;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static DataAttribute*
|
|
ModelNode_getDataAttributeByMmsValue(ModelNode* self, MmsValue* value)
|
|
{
|
|
ModelNode* node = self->firstChild;
|
|
|
|
while (node != NULL) {
|
|
if (node->modelType == DataAttributeModelType) {
|
|
DataAttribute* da = (DataAttribute*) node;
|
|
|
|
if (da->mmsValue == value)
|
|
return da;
|
|
}
|
|
|
|
DataAttribute* da = ModelNode_getDataAttributeByMmsValue(node, value);
|
|
|
|
if (da != NULL)
|
|
return da;
|
|
|
|
node = node->sibling;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
DataAttribute*
|
|
IedModel_lookupDataAttributeByMmsValue(IedModel* model, MmsValue* value)
|
|
{
|
|
LogicalDevice* ld = model->firstChild;
|
|
|
|
while (ld != NULL) {
|
|
|
|
DataAttribute* da =
|
|
ModelNode_getDataAttributeByMmsValue((ModelNode*) ld, value);
|
|
|
|
if (da != NULL)
|
|
return da;
|
|
|
|
|
|
ld = (LogicalDevice*) ld->sibling;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static ModelNode*
|
|
getChildWithShortAddress(ModelNode* node, uint32_t sAddr)
|
|
{
|
|
ModelNode* child;
|
|
|
|
child = node->firstChild;
|
|
|
|
while (child != NULL) {
|
|
if (child->modelType == DataAttributeModelType) {
|
|
DataAttribute* da = (DataAttribute*) child;
|
|
|
|
if (da->sAddr == sAddr)
|
|
return child;
|
|
}
|
|
|
|
ModelNode* childChild = getChildWithShortAddress(child, sAddr);
|
|
|
|
if (childChild != NULL)
|
|
return childChild;
|
|
|
|
child = child->sibling;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
ModelNode*
|
|
IedModel_getModelNodeByShortAddress(IedModel* model, uint32_t sAddr)
|
|
{
|
|
ModelNode* node = NULL;
|
|
|
|
LogicalDevice* ld = (LogicalDevice*) model->firstChild;
|
|
|
|
while (ld != NULL) {
|
|
|
|
LogicalNode* ln = (LogicalNode*) ld->firstChild;
|
|
|
|
while (ln != NULL) {
|
|
|
|
ModelNode* doNode = ln->firstChild;
|
|
|
|
while (doNode != NULL) {
|
|
ModelNode* matchingNode = getChildWithShortAddress(doNode, sAddr);
|
|
|
|
if (matchingNode != NULL)
|
|
return matchingNode;
|
|
|
|
doNode = doNode->sibling;
|
|
}
|
|
|
|
ln = (LogicalNode*) ln->sibling;
|
|
}
|
|
|
|
ld = (LogicalDevice*) ld->sibling;
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
ModelNode*
|
|
IedModel_getModelNodeByObjectReference(IedModel* model, const char* objectReference)
|
|
{
|
|
assert(strlen(objectReference) < 129);
|
|
|
|
char objRef[130];
|
|
|
|
strncpy(objRef, objectReference, 129);
|
|
objRef[129] = 0;
|
|
|
|
char* separator = strchr(objRef, '/');
|
|
|
|
if (separator == NULL)
|
|
return NULL;
|
|
|
|
*separator = 0;
|
|
|
|
LogicalDevice* ld = IedModel_getDevice(model, objRef);
|
|
|
|
if (ld == NULL) return NULL;
|
|
|
|
return ModelNode_getChild((ModelNode*) ld, separator + 1);
|
|
}
|
|
|
|
#if (CONFIG_IEC61850_SAMPLED_VALUES_SUPPORT == 1)
|
|
|
|
SVControlBlock*
|
|
IedModel_getSVControlBlock(IedModel* self, LogicalNode* parentLN, const char* svcbName)
|
|
{
|
|
SVControlBlock* retVal = NULL;
|
|
|
|
SVControlBlock* svCb = self->svCBs;
|
|
|
|
while (svCb != NULL) {
|
|
if ((svCb->parent == parentLN) && (strcmp(svCb->name, svcbName) == 0)) {
|
|
retVal = svCb;
|
|
break;
|
|
}
|
|
|
|
|
|
svCb = svCb->sibling;
|
|
}
|
|
|
|
return retVal;
|
|
}
|
|
|
|
#endif /* (CONFIG_IEC61850_SAMPLED_VALUES_SUPPORT == 1) */
|
|
|
|
ModelNode*
|
|
IedModel_getModelNodeByShortObjectReference(IedModel* model, const char* objectReference)
|
|
{
|
|
assert((strlen(model->name) + strlen(objectReference)) < 130);
|
|
|
|
char objRef[130];
|
|
|
|
strncpy(objRef, objectReference, 129);
|
|
objRef[129] = 0;
|
|
|
|
char* separator = strchr(objRef, '/');
|
|
|
|
if (separator == NULL)
|
|
return NULL;
|
|
|
|
*separator = 0;
|
|
|
|
char ldName[65];
|
|
strcpy(ldName, model->name);
|
|
strcat(ldName, objRef);
|
|
|
|
LogicalDevice* ld = IedModel_getDevice(model, ldName);
|
|
|
|
if (ld == NULL) return NULL;
|
|
|
|
return ModelNode_getChild((ModelNode*) ld, separator + 1);
|
|
}
|
|
|
|
|
|
bool
|
|
DataObject_hasFCData(DataObject* dataObject, FunctionalConstraint fc)
|
|
{
|
|
ModelNode* modelNode = dataObject->firstChild;
|
|
|
|
while (modelNode != NULL) {
|
|
|
|
if (modelNode->modelType == DataAttributeModelType) {
|
|
DataAttribute* dataAttribute = (DataAttribute*) modelNode;
|
|
|
|
if (dataAttribute->fc == fc)
|
|
return true;
|
|
}
|
|
else if (modelNode->modelType == DataObjectModelType) {
|
|
|
|
if (DataObject_hasFCData((DataObject*) modelNode, fc))
|
|
return true;
|
|
}
|
|
|
|
modelNode = modelNode->sibling;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
LogicalNode_hasFCData(LogicalNode* node, FunctionalConstraint fc)
|
|
{
|
|
DataObject* dataObject = (DataObject*) node->firstChild;
|
|
|
|
while (dataObject != NULL) {
|
|
if (DataObject_hasFCData(dataObject, fc))
|
|
return true;
|
|
|
|
dataObject = (DataObject*) dataObject->sibling;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
DataSet*
|
|
LogicalNode_getDataSet(LogicalNode* self, const char* dataSetName)
|
|
{
|
|
assert(self->modelType == LogicalNodeModelType);
|
|
assert(dataSetName != NULL);
|
|
|
|
char dsName[66];
|
|
|
|
LogicalDevice* ld = (LogicalDevice*) self->parent;
|
|
|
|
if (strlen(dataSetName) > 32) {
|
|
|
|
if (DEBUG_IED_SERVER) {
|
|
printf("IED_SERVER: LogicalNode_getDataSet - data set name %s too long!\n", dataSetName);
|
|
}
|
|
|
|
goto exit_error;
|
|
}
|
|
|
|
StringUtils_createStringInBuffer(dsName, 3, self->name, "$", dataSetName);
|
|
|
|
IedModel* iedModel = (IedModel*) ld->parent;
|
|
|
|
DataSet* ds = iedModel->dataSets;
|
|
|
|
while (ds != NULL) {
|
|
if (strcmp(ds->logicalDeviceName, ld->name) == 0) {
|
|
if (strcmp(ds->name, dsName) == 0) {
|
|
return ds;
|
|
}
|
|
}
|
|
|
|
ds = ds->sibling;
|
|
}
|
|
|
|
|
|
exit_error:
|
|
return NULL;
|
|
}
|
|
|
|
int
|
|
LogicalDevice_getLogicalNodeCount(LogicalDevice* logicalDevice)
|
|
{
|
|
int lnCount = 0;
|
|
|
|
LogicalNode* logicalNode = (LogicalNode*) logicalDevice->firstChild;
|
|
|
|
while (logicalNode != NULL) {
|
|
logicalNode = (LogicalNode*) logicalNode->sibling;
|
|
lnCount++;
|
|
}
|
|
|
|
return lnCount;
|
|
}
|
|
|
|
ModelNode*
|
|
LogicalDevice_getChildByMmsVariableName(LogicalDevice* logicalDevice, const char* mmsVariableName)
|
|
{
|
|
|
|
|
|
char fcString[3];
|
|
char nameRef[65];
|
|
|
|
const char* separator = strchr(mmsVariableName,'$');
|
|
|
|
if (separator == NULL)
|
|
return NULL;
|
|
|
|
if (strlen(separator) > 4) {
|
|
fcString[0] = separator[1];
|
|
fcString[1] = separator[2];
|
|
fcString[2] = 0;
|
|
|
|
const char* strpos = mmsVariableName;
|
|
|
|
int targetPos = 0;
|
|
|
|
while (strpos < separator) {
|
|
nameRef[targetPos++] = strpos[0];
|
|
strpos++;
|
|
}
|
|
|
|
nameRef[targetPos++] = '.';
|
|
|
|
strpos = separator + 4;
|
|
|
|
while (strpos[0] != 0) {
|
|
nameRef[targetPos++] = strpos[0];
|
|
strpos++;
|
|
}
|
|
|
|
nameRef[targetPos++] = 0;
|
|
|
|
StringUtils_replace(nameRef, '$', '.');
|
|
|
|
FunctionalConstraint fc = FunctionalConstraint_fromString(fcString);
|
|
|
|
return ModelNode_getChildWithFc((ModelNode*) logicalDevice, nameRef, fc);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static int
|
|
createObjectReference(ModelNode* node, char* objectReference)
|
|
{
|
|
int bufPos;
|
|
|
|
if (node->modelType != LogicalNodeModelType) {
|
|
bufPos = createObjectReference(node->parent, objectReference);
|
|
|
|
objectReference[bufPos++] = '.';
|
|
}
|
|
else {
|
|
LogicalNode* lNode = (LogicalNode*) node;
|
|
|
|
LogicalDevice* lDevice = (LogicalDevice*) lNode->parent;
|
|
|
|
IedModel* iedModel = (IedModel*) lDevice->parent;
|
|
|
|
bufPos = 0;
|
|
|
|
int nameLength = strlen (iedModel->name) + strlen(lDevice->name);
|
|
|
|
strncpy(objectReference, iedModel->name, 64);
|
|
strncat(objectReference, lDevice->name, 64);
|
|
|
|
bufPos += nameLength;
|
|
|
|
objectReference[bufPos++] = '/';
|
|
}
|
|
|
|
/* append own name */
|
|
int nameLength = strlen(node->name);
|
|
|
|
int i;
|
|
for (i = 0; i < nameLength; i++) {
|
|
objectReference[bufPos++] = node->name[i];
|
|
}
|
|
|
|
return bufPos;
|
|
}
|
|
|
|
char*
|
|
ModelNode_getObjectReference(ModelNode* node, char* objectReference)
|
|
{
|
|
if (objectReference == NULL)
|
|
objectReference = (char*) GLOBAL_MALLOC(130);
|
|
|
|
int bufPos = createObjectReference(node, objectReference);
|
|
|
|
objectReference[bufPos] = 0;
|
|
|
|
return objectReference;
|
|
}
|
|
|
|
int
|
|
ModelNode_getChildCount(ModelNode* modelNode) {
|
|
int childCount = 0;
|
|
|
|
ModelNode* child = modelNode->firstChild;
|
|
|
|
while (child != NULL) {
|
|
childCount++;
|
|
child = child->sibling;
|
|
}
|
|
|
|
return childCount;
|
|
}
|
|
|
|
|
|
ModelNode*
|
|
ModelNode_getChild(ModelNode* self, const char* name)
|
|
{
|
|
// check for separator
|
|
const char* separator = strchr(name, '.');
|
|
|
|
int nameElementLength = 0;
|
|
|
|
if (separator != NULL)
|
|
nameElementLength = (separator - name);
|
|
else
|
|
nameElementLength = strlen(name);
|
|
|
|
ModelNode* nextNode = self->firstChild;
|
|
|
|
ModelNode* matchingNode = NULL;
|
|
|
|
while (nextNode != NULL) {
|
|
int nodeNameLen = strlen(nextNode->name);
|
|
|
|
if (nodeNameLen == nameElementLength) {
|
|
if (memcmp(nextNode->name, name, nodeNameLen) == 0) {
|
|
matchingNode = nextNode;
|
|
break;
|
|
}
|
|
}
|
|
|
|
nextNode = nextNode->sibling;
|
|
}
|
|
|
|
if ((separator != NULL) && (matchingNode != NULL)) {
|
|
return ModelNode_getChild(matchingNode, separator + 1);
|
|
}
|
|
else
|
|
return matchingNode;
|
|
}
|
|
|
|
ModelNode*
|
|
ModelNode_getChildWithFc(ModelNode* self, const char* name, FunctionalConstraint fc)
|
|
{
|
|
// check for separator
|
|
const char* separator = strchr(name, '.');
|
|
|
|
int nameElementLength = 0;
|
|
|
|
if (separator != NULL)
|
|
nameElementLength = (separator - name);
|
|
else
|
|
nameElementLength = strlen(name);
|
|
|
|
ModelNode* nextNode = self->firstChild;
|
|
|
|
ModelNode* matchingNode = NULL;
|
|
|
|
while (nextNode != NULL) {
|
|
int nodeNameLen = strlen(nextNode->name);
|
|
|
|
if (nodeNameLen == nameElementLength) {
|
|
if (memcmp(nextNode->name, name, nodeNameLen) == 0) {
|
|
|
|
if (separator == NULL) {
|
|
if (nextNode->modelType == DataAttributeModelType) {
|
|
DataAttribute* da = (DataAttribute*) nextNode;
|
|
|
|
if (da->fc == fc) {
|
|
matchingNode = nextNode;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
|
|
if (nextNode->modelType == DataAttributeModelType) {
|
|
DataAttribute* da = (DataAttribute*) nextNode;
|
|
|
|
if (da->fc == fc) {
|
|
matchingNode = nextNode;
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
matchingNode = nextNode;
|
|
break;
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
nextNode = nextNode->sibling;
|
|
}
|
|
|
|
if ((separator != NULL) && (matchingNode != NULL)) {
|
|
return ModelNode_getChildWithFc(matchingNode, separator + 1, fc);
|
|
}
|
|
else
|
|
return matchingNode;
|
|
}
|
|
|
|
LogicalNode*
|
|
LogicalDevice_getLogicalNode(LogicalDevice* self, const char* nodeName)
|
|
{
|
|
return (LogicalNode*) ModelNode_getChild((ModelNode*) self, nodeName);
|
|
}
|
|
|
|
SettingGroupControlBlock*
|
|
LogicalDevice_getSettingGroupControlBlock(LogicalDevice* self)
|
|
{
|
|
IedModel* model = (IedModel*) self->parent;
|
|
|
|
if (model == NULL)
|
|
return NULL;
|
|
|
|
LogicalNode* ln = LogicalDevice_getLogicalNode(self, "LLN0");
|
|
|
|
if (ln == NULL) {
|
|
if (DEBUG_IED_SERVER)
|
|
printf("IED_SERVER: logical node LLN0 not found!\n");
|
|
|
|
return NULL;
|
|
}
|
|
|
|
SettingGroupControlBlock* sgcb = model->sgcbs;
|
|
|
|
while (sgcb != NULL) {
|
|
if (sgcb->parent == ln)
|
|
return sgcb;
|
|
|
|
sgcb = sgcb->sibling;
|
|
}
|
|
|
|
return NULL;
|
|
}
|