diff --git a/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs b/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs
index 3cd8b548..ebb61dbd 100644
--- a/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs
+++ b/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs
@@ -2136,6 +2136,9 @@ namespace IEC61850
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr IedServer_getAttributeValue(IntPtr self, IntPtr dataAttribute);
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr IedServer_getFunctionalConstrainedData(IntPtr self, IntPtr dataObject, int fc);
+
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate int InternalControlPerformCheckHandler (IntPtr action, IntPtr parameter, IntPtr ctlVal, [MarshalAs(UnmanagedType.I1)] bool test, [MarshalAs(UnmanagedType.I1)] bool interlockCheck);
@@ -2757,6 +2760,22 @@ namespace IEC61850
return null;
}
+ ///
+ /// Get the MmsValue object related to a functional constrained data object (FCD)
+ ///
+ /// the data object to specify the FCD
+ /// the FC to specify the FCD
+ /// FCDO corresponding MmsValue object cached by the server
+ public MmsValue GetFunctionalConstrainedData(DataObject dataObject, FunctionalConstraint fc)
+ {
+ IntPtr mmsValuePtr = IedServer_getFunctionalConstrainedData(self, dataObject.self, (int)fc);
+
+ if (mmsValuePtr != IntPtr.Zero)
+ return new MmsValue(mmsValuePtr);
+ else
+ return null;
+ }
+
///
/// Enable all GOOSE control blocks.
///
diff --git a/examples/mms_utility/mms_utility.c b/examples/mms_utility/mms_utility.c
index ee658622..8b43d9d7 100644
--- a/examples/mms_utility/mms_utility.c
+++ b/examples/mms_utility/mms_utility.c
@@ -24,6 +24,7 @@ print_help()
printf("-x delete file\n");
printf("-j read journal\n");
printf("-v read domain variable list\n");
+ printf("-z get domain variable list directory\n");
printf("-y array index for read access\n");
printf("-m print raw MMS messages\n");
}
@@ -123,10 +124,11 @@ int main(int argc, char** argv)
int printRawMmsMessages = 0;
int deleteFile = 0;
int readVariableList = 0;
+ int readDataSetDirectory = 0;
int c;
- while ((c = getopt(argc, argv, "mifdh:p:l:t:a:r:g:j:x:v:c:y:")) != -1)
+ while ((c = getopt(argc, argv, "mifdh:p:l:t:a:r:g:j:x:v:c:y:z:")) != -1) {
switch (c) {
case 'm':
printRawMmsMessages = 1;
@@ -167,6 +169,11 @@ int main(int argc, char** argv)
readVariableList = 1;
variableName = StringUtils_copyString(optarg);
break;
+ case 'z':
+ readDataSetDirectory = 1;
+ variableName = StringUtils_copyString(optarg);
+ break;
+
case 'f':
showFileList = 1;
break;
@@ -192,12 +199,13 @@ int main(int argc, char** argv)
print_help();
return 0;
}
+ }
MmsConnection con = MmsConnection_create();
MmsError error;
- /* Set maximum MMS PDU size (local detail) to 2000 byte */
+ /* Set maximum MMS PDU size (local detail) */
MmsConnection_setLocalDetail(con, maxPduSize);
if (printRawMmsMessages)
@@ -404,6 +412,41 @@ int main(int argc, char** argv)
printf("Reading VMD scope variable list not yet supported!\n");
}
+ if (readDataSetDirectory) {
+ if (readWriteHasDomain) {
+
+ bool deletable = false;
+
+ LinkedList varListDir = MmsConnection_readNamedVariableListDirectory(con, &error, domainName, variableName, &deletable);
+
+ if (error != MMS_ERROR_NONE) {
+ printf("Reading variable list directory failed: (ERROR %i)\n", error);
+ }
+ else {
+ LinkedList varListElem = LinkedList_getNext(varListDir);
+
+ int listIdx = 0;
+
+ while (varListElem) {
+ MmsVariableAccessSpecification* varAccessSpec = (MmsVariableAccessSpecification*)LinkedList_getData(varListElem);
+
+ if (varAccessSpec->arrayIndex)
+ printf("[%i] %s/%s(%i)%s\n", listIdx, varAccessSpec->domainId, varAccessSpec->itemId, varAccessSpec->arrayIndex, varAccessSpec->componentName == NULL ? "" : varAccessSpec->componentName);
+ else
+ printf("[%i] %s/%s\n", listIdx, varAccessSpec->domainId, varAccessSpec->itemId);
+
+ listIdx++;
+
+ varListElem = LinkedList_getNext(varListElem);
+ }
+
+ printf("Read SUCCESS\n");
+ }
+ }
+ else
+ printf("Reading VMD scope variable list not yet supported!\n");
+ }
+
if (showFileList) {
char lastName[300];
lastName[0] = 0;
diff --git a/examples/server_example_dynamic/server_example_dynamic.c b/examples/server_example_dynamic/server_example_dynamic.c
index 43cd9fa9..956a32e7 100644
--- a/examples/server_example_dynamic/server_example_dynamic.c
+++ b/examples/server_example_dynamic/server_example_dynamic.c
@@ -49,6 +49,9 @@ int main(int argc, char** argv) {
DataAttribute* temperatureValue = (DataAttribute*) ModelNode_getChild((ModelNode*) ttmp1_tmpsv, "instMag.f");
DataAttribute* temperatureTimestamp = (DataAttribute*) ModelNode_getChild((ModelNode*) ttmp1_tmpsv, "t");
+ LogicalNode* ggio1 = LogicalNode_create("GGIO1", lDevice1);
+ DataObject* ggio1_anIn1 = CDC_APC_create("AnOut1", (ModelNode*)ggio1, 0, CDC_CTL_MODEL_HAS_CANCEL | CDC_CTL_MODEL_SBO_ENHANCED, false);
+
DataSet* dataSet = DataSet_create("events", lln0);
DataSetEntry_create(dataSet, "TTMP1$MX$TmpSv$instMag$f", -1, NULL);
diff --git a/src/iec61850/inc/iec61850_server.h b/src/iec61850/inc/iec61850_server.h
index 902e4de3..5a3dd29d 100644
--- a/src/iec61850/inc/iec61850_server.h
+++ b/src/iec61850/inc/iec61850_server.h
@@ -911,9 +911,8 @@ IedServer_getBitStringAttributeValue(IedServer self, const DataAttribute* dataAt
LIB61850_API const char*
IedServer_getStringAttributeValue(IedServer self, const DataAttribute* dataAttribute);
-
/**
- * \brief Get the MmsValue object related to a FunctionalConstrainedData object
+ * \brief Get the MmsValue object related to a functional constrained data object (FCD)
*
* Get the MmsValue from the server cache that is associated with the Functional Constrained Data (FCD)
* object that is specified by the DataObject and the given Function Constraint (FC).
diff --git a/src/iec61850/server/mms_mapping/reporting.c b/src/iec61850/server/mms_mapping/reporting.c
index 8efc160c..998fda06 100644
--- a/src/iec61850/server/mms_mapping/reporting.c
+++ b/src/iec61850/server/mms_mapping/reporting.c
@@ -170,7 +170,7 @@ ReportControl_unlockNotify(ReportControl* self)
static void
purgeBuf(ReportControl* rc)
{
- if (DEBUG_IED_SERVER) printf("IED_SERVER: reporting.c: run purgeBuf\n");
+ if (DEBUG_IED_SERVER) printf("IED_SERVER: RCB %s purgeBuf\n", rc->name);
/* reset trigger */
rc->triggered = false;
@@ -1978,7 +1978,7 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme
purgeBuf(rc);
if (self->rcbEventHandler) {
- self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_GI, NULL, DATA_ACCESS_ERROR_SUCCESS);
+ self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_PURGEBUF, NULL, DATA_ACCESS_ERROR_SUCCESS);
}
}
diff --git a/src/iec61850/server/model/cdc.c b/src/iec61850/server/model/cdc.c
index e8ac05cf..13967de6 100644
--- a/src/iec61850/server/model/cdc.c
+++ b/src/iec61850/server/model/cdc.c
@@ -190,8 +190,6 @@ CDA_Cancel(ModelNode* parent, DataAttributeType type, bool isTImeActivated)
return oper;
}
-
-
/************************************************
* Common Data Classes - helper functions
***********************************************/
@@ -674,7 +672,7 @@ addAnalogControls(DataObject* parent, uint32_t controlOptions, bool isIntegerNot
addCommonOperateElements(oper, isTimeActivated, true);
if (controlOptions & CDC_CTL_MODEL_HAS_CANCEL) {
- DataAttribute* cancel = DataAttribute_create("SBOw", (ModelNode*) parent, IEC61850_CONSTRUCTED, IEC61850_FC_CO, 0, 0, 0);
+ DataAttribute* cancel = DataAttribute_create("Cancel", (ModelNode*) parent, IEC61850_CONSTRUCTED, IEC61850_FC_CO, 0, 0, 0);
CAC_AnalogueValue_create("ctlVal", (ModelNode*) cancel, IEC61850_FC_CO, 0, isIntegerNotFloat);
diff --git a/src/mms/inc_private/mms_common_internal.h b/src/mms/inc_private/mms_common_internal.h
index f5431a87..62d07b17 100644
--- a/src/mms/inc_private/mms_common_internal.h
+++ b/src/mms/inc_private/mms_common_internal.h
@@ -118,6 +118,9 @@ mmsMsg_createStringFromAsnIdentifier(Identifier_t identifier);
LIB61850_INTERNAL void
mmsMsg_copyAsn1IdentifierToStringBuffer(Identifier_t identifier, char* buffer, int bufSize);
+LIB61850_INTERNAL char*
+mmsMsg_getComponentNameFromAlternateAccess(AlternateAccess_t* alternateAccess, char* componentNameBuf, int nameBufPos);
+
LIB61850_INTERNAL void
mmsMsg_deleteAccessResultList(AccessResult_t** accessResult, int variableCount);
diff --git a/src/mms/iso_mms/client/mms_client_named_variable_list.c b/src/mms/iso_mms/client/mms_client_named_variable_list.c
index 98c30ab0..8b960a2f 100644
--- a/src/mms/iso_mms/client/mms_client_named_variable_list.c
+++ b/src/mms/iso_mms/client/mms_client_named_variable_list.c
@@ -222,8 +222,11 @@ parseNamedVariableAttributes(GetNamedVariableListAttributesResponse_t* response,
for (i = 0; i < attributesCount; i++) {
- char* domainId;
- char* itemId;
+ char* domainId = NULL;
+ char* itemId = NULL;
+ int arrayIdx = -1;
+ char componentNameBuffer[129];
+ char* componentName = NULL;
if (response->listOfVariable.list.array[i]->variableSpecification.choice.name.present == ObjectName_PR_vmdspecific) {
@@ -240,7 +243,52 @@ parseNamedVariableAttributes(GetNamedVariableListAttributesResponse_t* response,
variableSpecification.choice.name.choice.domainspecific.itemId);
}
- MmsVariableAccessSpecification* listEntry = MmsVariableAccessSpecification_create(domainId, itemId);
+ if (response->listOfVariable.list.array[i]->alternateAccess) {
+ AlternateAccess_t* alternateAccess = response->listOfVariable.list.array[i]->alternateAccess;
+
+ if (alternateAccess->list.count > 0) {
+ if (alternateAccess->list.array[0]->choice.unnamed->present == AlternateAccessSelection_PR_selectAlternateAccess) {
+
+ if (alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.accessSelection.present
+ == AlternateAccessSelection__selectAlternateAccess__accessSelection_PR_index)
+ {
+ INTEGER_t* asnIndex =
+ &(alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.choice.index);
+
+ if (asnIndex) {
+ long indexValue;
+
+ if (asn_INTEGER2long(asnIndex, &indexValue) != -1) {
+ arrayIdx = (int)indexValue;
+ }
+ }
+
+ AlternateAccess_t* nestedAltAccess = alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.alternateAccess;
+
+ if (nestedAltAccess) {
+ componentName = mmsMsg_getComponentNameFromAlternateAccess(nestedAltAccess, componentNameBuffer, 0);
+ }
+ }
+ }
+ else if (alternateAccess->list.array[0]->choice.unnamed->present == AlternateAccessSelection_PR_selectAccess) {
+ INTEGER_t* asnIndex =
+ &(alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.choice.index);
+
+ if (asnIndex) {
+ long indexValue;
+
+ if (asn_INTEGER2long(asnIndex, &indexValue) != -1) {
+ arrayIdx = (int)indexValue;
+ }
+ }
+ }
+ }
+ }
+
+ if (componentName)
+ componentName = StringUtils_copyString(componentName);
+
+ MmsVariableAccessSpecification* listEntry = MmsVariableAccessSpecification_createAlternateAccess(domainId, itemId, arrayIdx, componentName);
LinkedList_add(attributes, listEntry);
}
diff --git a/src/mms/iso_mms/common/mms_common_msg.c b/src/mms/iso_mms/common/mms_common_msg.c
index eefd408f..fb7d7548 100644
--- a/src/mms/iso_mms/common/mms_common_msg.c
+++ b/src/mms/iso_mms/common/mms_common_msg.c
@@ -1,7 +1,7 @@
/*
* mms_common_msg.c
*
- * Copyright 2013-2019 Michael Zillgith
+ * Copyright 2013-2022 Michael Zillgith
*
* This file is part of libIEC61850.
*
@@ -489,6 +489,75 @@ mmsMsg_copyAsn1IdentifierToStringBuffer(Identifier_t identifier, char* buffer, i
}
}
+char*
+mmsMsg_getComponentNameFromAlternateAccess(AlternateAccess_t* alternateAccess, char* componentNameBuf, int nameBufPos)
+{
+ if (alternateAccess->list.count == 1) {
+
+ if (alternateAccess->list.array[0]->present == AlternateAccess__Member_PR_unnamed) {
+
+ if (alternateAccess->list.array[0]->choice.unnamed->present == AlternateAccessSelection_PR_selectAlternateAccess) {
+
+ if (alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.accessSelection.present ==
+ AlternateAccessSelection__selectAlternateAccess__accessSelection_PR_component)
+ {
+ Identifier_t componentIdentifier = alternateAccess->list.array[0]->choice.unnamed->
+ choice.selectAlternateAccess.accessSelection.choice.component;
+
+ AlternateAccess_t* nextAlternateAccess = alternateAccess->list.array[0]->choice.unnamed->
+ choice.selectAlternateAccess.alternateAccess;
+
+ if (nextAlternateAccess) {
+ if (nameBufPos + componentIdentifier.size + 1 < 65) {
+ memcpy(componentNameBuf + nameBufPos, componentIdentifier.buf, componentIdentifier.size);
+ nameBufPos += componentIdentifier.size;
+ componentNameBuf[nameBufPos++] = '$';
+ return mmsMsg_getComponentNameFromAlternateAccess(nextAlternateAccess, componentNameBuf, nameBufPos);
+ }
+ else {
+ if (DEBUG_MMS_SERVER)
+ printf("MMS_SERVER: component identifier name too long!\n");
+ }
+ }
+ else {
+ if (DEBUG_MMS_SERVER)
+ printf("MMS_SERVER: next alternate access specification is missing!\n");
+ }
+ }
+ }
+ else if (alternateAccess->list.array[0]->choice.unnamed->present == AlternateAccessSelection_PR_selectAccess) {
+
+ /* final component part */
+
+ if (alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.present ==
+ AlternateAccessSelection__selectAccess_PR_component)
+ {
+ Identifier_t componentIdentifier = alternateAccess->list.array[0]->choice.unnamed->
+ choice.selectAccess.choice.component;
+
+ if (nameBufPos + componentIdentifier.size + 1 < 65) {
+ memcpy(componentNameBuf + nameBufPos, componentIdentifier.buf, componentIdentifier.size);
+ nameBufPos += componentIdentifier.size;
+ componentNameBuf[nameBufPos++] = 0;
+ return componentNameBuf;
+ }
+ else {
+ if (DEBUG_MMS_SERVER)
+ printf("MMS_SERVER: component identifier name too long!\n");
+ }
+ }
+ }
+
+ }
+
+ }
+
+ if (DEBUG_MMS_SERVER)
+ printf("MMS_SERVER: invalid component access specification\n");
+
+ return NULL;
+}
+
#if (MMS_FILE_SERVICE == 1)
void
diff --git a/src/mms/iso_mms/server/mms_association_service.c b/src/mms/iso_mms/server/mms_association_service.c
index a3075e27..83a32f3e 100644
--- a/src/mms/iso_mms/server/mms_association_service.c
+++ b/src/mms/iso_mms/server/mms_association_service.c
@@ -284,12 +284,15 @@ parseInitRequestDetail(MmsServerConnection self, uint8_t* buffer, int bufPos, in
{
uint32_t protocolVersion = BerDecoder_decodeUint32(buffer, length, bufPos);
- if (protocolVersion != 1) {
+ if (protocolVersion < 1) {
if (DEBUG_MMS_SERVER)
printf("MMS_SERVER: invalid protocol version %u\n", protocolVersion);
return false;
}
+
+ if (DEBUG_MMS_SERVER)
+ printf("MMS_SERVER: proposed version number %u\n", protocolVersion);
}
break;
diff --git a/src/mms/iso_mms/server/mms_named_variable_list_service.c b/src/mms/iso_mms/server/mms_named_variable_list_service.c
index e7d152c2..dbe35ffb 100644
--- a/src/mms/iso_mms/server/mms_named_variable_list_service.c
+++ b/src/mms/iso_mms/server/mms_named_variable_list_service.c
@@ -296,75 +296,6 @@ checkIfVariableExists(MmsDevice* device, MmsAccessSpecifier* accessSpecifier)
return true;
}
-static char*
-getComponentNameFromAlternateAccess(AlternateAccess_t* alternateAccess, char* componentNameBuf, int nameBufPos)
-{
- if (alternateAccess->list.count == 1) {
-
- if (alternateAccess->list.array[0]->present == AlternateAccess__Member_PR_unnamed) {
-
- if (alternateAccess->list.array[0]->choice.unnamed->present == AlternateAccessSelection_PR_selectAlternateAccess) {
-
- if (alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.accessSelection.present ==
- AlternateAccessSelection__selectAlternateAccess__accessSelection_PR_component)
- {
- Identifier_t componentIdentifier = alternateAccess->list.array[0]->choice.unnamed->
- choice.selectAlternateAccess.accessSelection.choice.component;
-
- AlternateAccess_t* nextAlternateAccess = alternateAccess->list.array[0]->choice.unnamed->
- choice.selectAlternateAccess.alternateAccess;
-
- if (nextAlternateAccess) {
- if (nameBufPos + componentIdentifier.size + 1 < 65) {
- memcpy(componentNameBuf + nameBufPos, componentIdentifier.buf, componentIdentifier.size);
- nameBufPos += componentIdentifier.size;
- componentNameBuf[nameBufPos++] = '$';
- return getComponentNameFromAlternateAccess(nextAlternateAccess, componentNameBuf, nameBufPos);
- }
- else {
- if (DEBUG_MMS_SERVER)
- printf("MMS_SERVER: component identifier name too long!\n");
- }
- }
- else {
- if (DEBUG_MMS_SERVER)
- printf("MMS_SERVER: next alternate access specification is missing!\n");
- }
- }
- }
- else if (alternateAccess->list.array[0]->choice.unnamed->present == AlternateAccessSelection_PR_selectAccess) {
-
- /* final component part */
-
- if (alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.present ==
- AlternateAccessSelection__selectAccess_PR_component)
- {
- Identifier_t componentIdentifier = alternateAccess->list.array[0]->choice.unnamed->
- choice.selectAccess.choice.component;
-
- if (nameBufPos + componentIdentifier.size + 1 < 65) {
- memcpy(componentNameBuf + nameBufPos, componentIdentifier.buf, componentIdentifier.size);
- nameBufPos += componentIdentifier.size;
- componentNameBuf[nameBufPos++] = 0;
- return componentNameBuf;
- }
- else {
- if (DEBUG_MMS_SERVER)
- printf("MMS_SERVER: component identifier name too long!\n");
- }
- }
- }
-
- }
-
- }
-
- if (DEBUG_MMS_SERVER)
- printf("MMS_SERVER: invalid component access specification\n");
-
- return NULL;
-}
-
static MmsNamedVariableList
createNamedVariableList(MmsServer server, MmsDomain* domain, MmsDevice* device,
DefineNamedVariableListRequest_t* request,
@@ -426,7 +357,7 @@ createNamedVariableList(MmsServer server, MmsDomain* domain, MmsDevice* device,
if (alternateAccess->choice.unnamed->choice.selectAlternateAccess.alternateAccess) {
componentNameBuf[0] = 0;
- componentName = getComponentNameFromAlternateAccess(
+ componentName = mmsMsg_getComponentNameFromAlternateAccess(
alternateAccess->choice.unnamed->choice.selectAlternateAccess.alternateAccess,
componentNameBuf, 0);
}