From 91b9c2c64b10daf8f4b370dadcf86301c90db949 Mon Sep 17 00:00:00 2001
From: Michael Zillgith <michael@zillgith.eu>
Date: Thu, 18 Dec 2014 18:12:02 +0100
Subject: [PATCH 1/5] - added documentation to HandleSet functions

---
 dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs | 17 +++++++++--
 .../IEC61850forCSharp.csproj                  |  4 +++
 src/hal/inc/hal_socket.h                      | 29 +++++++++++++++++++
 src/hal/socket/linux/socket_linux.c           |  2 +-
 src/iec61850/client/ied_connection.c          |  3 +-
 src/iec61850/inc/iec61850_client.h            | 16 ++++++----
 src/iec61850/server/mms_mapping/reporting.c   |  1 -
 7 files changed, 61 insertions(+), 11 deletions(-)

diff --git a/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs b/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs
index b6c993a..d23eefc 100644
--- a/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs
+++ b/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs
@@ -111,7 +111,10 @@ namespace IEC61850
 			static extern IntPtr IedConnection_getLogicalNodeDirectory (IntPtr self, out int error, string logicalNodeReference, int acsiClass);
         
 			[DllImport ("iec61850", CallingConvention=CallingConvention.Cdecl)]
-			static extern IntPtr IedConnection_getServerDirectory (IntPtr self, out int error, bool getFileNames);
+			static extern IntPtr IedConnection_getServerDirectory (IntPtr self, out int error, bool getFileNames);
+
+            [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+            static extern void IedConnection_getDeviceModelFromServer(IntPtr self, out int error);
 
 			[DllImport ("iec61850", CallingConvention=CallingConvention.Cdecl)]
 			static extern IntPtr IedConnection_getLogicalDeviceDirectory (IntPtr self, out int error, string logicalDeviceName);
@@ -253,7 +256,17 @@ namespace IEC61850
 				ControlObject controlObject = new ControlObject (objectReference, connection, this);
 
 				return controlObject;
-			}
+			}
+
+            public void UpdateDeviceModel()
+            {
+                int error;
+
+                IedConnection_getDeviceModelFromServer(connection, out error);
+
+                if (error != 0)
+                    throw new IedConnectionException("UpdateDeviceModel failed", error);
+            }
 
 			/// <exception cref="IedConnectionException">This exception is thrown if there is a connection or service error</exception>
 			public List<string> GetServerDirectory (bool fileDirectory = false)
diff --git a/dotnet/IEC61850forCSharp/IEC61850forCSharp.csproj b/dotnet/IEC61850forCSharp/IEC61850forCSharp.csproj
index be311af..d5d09da 100644
--- a/dotnet/IEC61850forCSharp/IEC61850forCSharp.csproj
+++ b/dotnet/IEC61850forCSharp/IEC61850forCSharp.csproj
@@ -30,6 +30,10 @@
   </PropertyGroup>
   <ItemGroup>
     <Reference Include="System" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Drawing" />
+    <Reference Include="System.Windows.Forms" />
+    <Reference Include="System.Xml" />
   </ItemGroup>
   <ItemGroup>
     <Compile Include="AssemblyInfo.cs" />
diff --git a/src/hal/inc/hal_socket.h b/src/hal/inc/hal_socket.h
index ce950d4..a06893d 100644
--- a/src/hal/inc/hal_socket.h
+++ b/src/hal/inc/hal_socket.h
@@ -54,15 +54,44 @@ typedef struct sSocket* Socket;
 /** Opaque reference for a set of server and socket handles */
 typedef struct sHandleSet* HandleSet;
 
+/**
+ * \brief Create a new connection handle set (HandleSet)
+ *
+ * \return new HandleSet instance
+ */
 HandleSet
 Handleset_new(void);
 
+/**
+ * \brief add a soecket to an existing handle set
+ *
+ * \param self the HandleSet instance
+ * \param sock the socket to add
+ */
 void
 Handleset_addSocket(HandleSet self, const Socket sock);
 
+
+/**
+ * \brief wait for a socket to become ready
+ *
+ * This function is corresponding to the BSD socket select function.
+ * It returns the number of sockets on which data is pending or 0 if no data is pending
+ * on any of the monitored connections. The function will return after "timeout" ms if no
+ * data is pending.
+ * The function shall return -1 if a socket error occures.
+ *
+ *  \param self the HandleSet instance
+ *  \oaram timeout in milliseconds (ms)
+ */
 int
 Handleset_waitReady(HandleSet self, unsigned int timeoutMs);
 
+/**
+ * \brief destroy the HandleSet instance
+ *
+ * \param self the HandleSet instance to destroy
+ */
 void
 Handleset_destroy(HandleSet self);
 
diff --git a/src/hal/socket/linux/socket_linux.c b/src/hal/socket/linux/socket_linux.c
index 87c22a5..a206da8 100644
--- a/src/hal/socket/linux/socket_linux.c
+++ b/src/hal/socket/linux/socket_linux.c
@@ -89,7 +89,7 @@ Handleset_waitReady(HandleSet self, unsigned int timeoutMs)
 {
    int result;
 
-   if (self != NULL && self->maxHandle >= 0) {
+   if ((self != NULL) && (self->maxHandle >= 0)) {
        struct timeval timeout;
 
        timeout.tv_sec = timeoutMs / 1000;
diff --git a/src/iec61850/client/ied_connection.c b/src/iec61850/client/ied_connection.c
index 846d807..6dbedc9 100644
--- a/src/iec61850/client/ied_connection.c
+++ b/src/iec61850/client/ied_connection.c
@@ -966,9 +966,8 @@ IedConnection_getDeviceModelFromServer(IedConnection self, IedClientError* error
 
         LinkedList_destroy(logicalDeviceNames);
     }
-    else {
+    else
         *error = iedConnection_mapMmsErrorToIedError(mmsError);
-    }
 }
 
 LinkedList /*<char*>*/
diff --git a/src/iec61850/inc/iec61850_client.h b/src/iec61850/inc/iec61850_client.h
index 1f93a81..748da87 100644
--- a/src/iec61850/inc/iec61850_client.h
+++ b/src/iec61850/inc/iec61850_client.h
@@ -607,13 +607,16 @@ typedef void (*ReportCallbackFunction) (void* parameter, ClientReport report);
 /**
  * \brief Install a report handler function for the specified report control block (RCB)
  *
- * It is important that you provide a ClientDataSet instance that is already populated with an MmsValue object
- * of type MMS_STRUCTURE that contains the data set entries as structure elements. This is required because otherwise
- * the report handler is not able to correctly parse the report message from the server.
+ * This function will replace a report handler set earlier for the specified RCB. The report handler
+ * will be called whenever a report for the specified RCB is received.
+ * Please note that this function should be called whenever the RCB data set is changed or updated.
+ * Otherwise the internal data structures storing the received data set values will not be updated
+ * correctly.
  *
- * This function will replace a formerly set report handler function for the specified RCB.
+ * When replacing a report handler you only have to call this function. There is no separate call to
+ * IedConnection_uninstallReportHandler() required.
  *
- * \param connection the connection object
+ * \param self the connection object
  * \param rcbReference object reference of the report control block
  * \param rptId a string that identifies the report. If the rptId is not available then the
  *        rcbReference is used to identify the report.
@@ -626,6 +629,9 @@ IedConnection_installReportHandler(IedConnection self, char* rcbReference, char*
 
 /**
  * \brief uninstall a report handler function for the specified report control block (RCB)
+ *
+ * \param self the connection object
+ * \param rcbReference object reference of the report control block
  */
 void
 IedConnection_uninstallReportHandler(IedConnection self, char* rcbReference);
diff --git a/src/iec61850/server/mms_mapping/reporting.c b/src/iec61850/server/mms_mapping/reporting.c
index 3a994aa..d49a606 100644
--- a/src/iec61850/server/mms_mapping/reporting.c
+++ b/src/iec61850/server/mms_mapping/reporting.c
@@ -328,7 +328,6 @@ sendReport(ReportControl* self, bool isIntegrity, bool isGI)
                 if (self->inclusionFlags[i] != REPORT_CONTROL_NONE)
                     addReferenceForEntry = true;
 
-
             if (addReferenceForEntry) {
 
                 char dataReference[130];

From 7f1714e4af3a48d64c49175a6cd1f5973aaa6681 Mon Sep 17 00:00:00 2001
From: Michael Zillgith <michael@zillgith.eu>
Date: Thu, 8 Jan 2015 12:08:57 +0100
Subject: [PATCH 2/5] - changed behaviour of CommandTermination- and
 LastApplError message

---
 .../server_example_sg.c                       |  8 ++--
 src/common/string_utilities.c                 |  1 -
 src/hal/inc/hal_socket.h                      |  2 +-
 src/hal/socket/win32/socket_win32.c           | 20 +++------
 src/iec61850/client/client_goose_control.c    |  4 +-
 src/iec61850/client/client_report.c           |  2 +
 src/iec61850/client/ied_connection.c          | 27 +++++++++---
 src/iec61850/server/mms_mapping/control.c     | 41 ++++++++-----------
 src/iec61850/server/mms_mapping/mms_mapping.c |  5 +++
 .../server/model/config_file_parser.c         | 17 +++++---
 10 files changed, 69 insertions(+), 58 deletions(-)

diff --git a/examples/server_example_setting_groups/server_example_sg.c b/examples/server_example_setting_groups/server_example_sg.c
index f01235f..9ddfecd 100644
--- a/examples/server_example_setting_groups/server_example_sg.c
+++ b/examples/server_example_setting_groups/server_example_sg.c
@@ -85,10 +85,10 @@ editSgConfirmedHandler(void* parameter, SettingGroupControlBlock* sgcb,
 {
     printf("Received edit sg confirm for sg %i\n", editSg);
 
-    ptoc1Settings[editSg - 1].strVal = MmsValue_toFloat(IEDMODEL_PROT_PTOC1_StrVal_setMag_f->mmsValue);
-    ptoc1Settings[editSg - 1].opDlTmms = MmsValue_toInt32(IEDMODEL_PROT_PTOC1_OpDlTmms_setVal->mmsValue);
-    ptoc1Settings[editSg - 1].rsDlTmms = MmsValue_toInt32(IEDMODEL_PROT_PTOC1_RsDlTmms_setVal->mmsValue);
-    ptoc1Settings[editSg - 1].rstTms = MmsValue_toInt32(IEDMODEL_PROT_PTOC1_RstTms_setVal->mmsValue);
+    ptoc1Settings[editSg - 1].strVal = MmsValue_toFloat(IEDMODEL_SE_PROT_PTOC1_StrVal_setMag_f->mmsValue);
+    ptoc1Settings[editSg - 1].opDlTmms = MmsValue_toInt32(IEDMODEL_SE_PROT_PTOC1_OpDlTmms_setVal->mmsValue);
+    ptoc1Settings[editSg - 1].rsDlTmms = MmsValue_toInt32(IEDMODEL_SE_PROT_PTOC1_RsDlTmms_setVal->mmsValue);
+    ptoc1Settings[editSg - 1].rstTms = MmsValue_toInt32(IEDMODEL_SE_PROT_PTOC1_RstTms_setVal->mmsValue);
 
     if (IedServer_getActiveSettingGroup(iedServer, sgcb) == editSg) {
         loadActiveSgValues(editSg);
diff --git a/src/common/string_utilities.c b/src/common/string_utilities.c
index 702f945..86d9368 100644
--- a/src/common/string_utilities.c
+++ b/src/common/string_utilities.c
@@ -231,4 +231,3 @@ StringUtils_startsWith(char* string, char* prefix)
     return false;
 }
 
-
diff --git a/src/hal/inc/hal_socket.h b/src/hal/inc/hal_socket.h
index a06893d..a6d64c0 100644
--- a/src/hal/inc/hal_socket.h
+++ b/src/hal/inc/hal_socket.h
@@ -82,7 +82,7 @@ Handleset_addSocket(HandleSet self, const Socket sock);
  * The function shall return -1 if a socket error occures.
  *
  *  \param self the HandleSet instance
- *  \oaram timeout in milliseconds (ms)
+ *  \param timeout in milliseconds (ms)
  */
 int
 Handleset_waitReady(HandleSet self, unsigned int timeoutMs);
diff --git a/src/hal/socket/win32/socket_win32.c b/src/hal/socket/win32/socket_win32.c
index 789de3b..11c5405 100644
--- a/src/hal/socket/win32/socket_win32.c
+++ b/src/hal/socket/win32/socket_win32.c
@@ -56,7 +56,7 @@ struct sServerSocket {
 
 struct sHandleSet {
    fd_set handles;
-   int maxHandle;
+   SOCKET maxHandle;
 };
 
 HandleSet
@@ -66,7 +66,7 @@ Handleset_new(void)
 
    if (result != NULL) {
        FD_ZERO(&result->handles);
-       result->maxHandle = -1;
+       result->maxHandle = INVALID_SOCKET;
    }
    return result;
 }
@@ -74,11 +74,11 @@ Handleset_new(void)
 void
 Handleset_addSocket(HandleSet self, const Socket sock)
 {
-   if (self != NULL && sock != NULL && sock->fd != -1) {
+   if (self != NULL && sock != NULL && sock->fd != INVALID_SOCKET) {
        FD_SET(sock->fd, &self->handles);
-       if (sock->fd > self->maxHandle) {
+
+       if ((sock->fd > self->maxHandle) || (self->maxHandle == INVALID_SOCKET))
            self->maxHandle = sock->fd;
-       }
    }
 }
 
@@ -302,16 +302,6 @@ Socket_connect(Socket self, const char* address, int port)
     FD_ZERO(&fdSet);
     FD_SET(self->fd, &fdSet);
 
-//	if (connect(self->fd, (struct sockaddr *) &serverAddress,sizeof(serverAddress)) < 0) {
-//	    if (DEBUG_SOCKET)
-//	        printf("WIN32_SOCKET: Socket failed connecting!\n");
-//		return false;
-//	}
-//	else {
-//
-//	    return true;
-//	}
-
     if (connect(self->fd, (struct sockaddr *) &serverAddress, sizeof(serverAddress)) == SOCKET_ERROR) {
         if (WSAGetLastError() != WSAEWOULDBLOCK)
             return false;
diff --git a/src/iec61850/client/client_goose_control.c b/src/iec61850/client/client_goose_control.c
index ead9672..e61a2ec 100644
--- a/src/iec61850/client/client_goose_control.c
+++ b/src/iec61850/client/client_goose_control.c
@@ -237,7 +237,7 @@ ClientGooseControlBlock_setDstAddress_vid(ClientGooseControlBlock self, uint16_t
         self->dstAddress = newEmptyPhyCommAddress();
 
     MmsValue* vid = MmsValue_getElement(self->dstAddress, 2);
-    MmsValue_setUint8(vid, vidValue);
+    MmsValue_setUint16(vid, vidValue);
 }
 
 uint16_t
@@ -256,7 +256,7 @@ ClientGooseControlBlock_setDstAddress_appid(ClientGooseControlBlock self, uint16
         self->dstAddress = newEmptyPhyCommAddress();
 
     MmsValue* appid = MmsValue_getElement(self->dstAddress, 3);
-    MmsValue_setUint8(appid, appidValue);
+    MmsValue_setUint16(appid, appidValue);
 }
 
 static void
diff --git a/src/iec61850/client/client_report.c b/src/iec61850/client/client_report.c
index 1087be7..45a31ca 100644
--- a/src/iec61850/client/client_report.c
+++ b/src/iec61850/client/client_report.c
@@ -328,6 +328,8 @@ private_IedConnection_handleReport(IedConnection self, MmsValue* value)
 
         char* rptId = report->rptId;
 
+        printf("Report ID is null!\n");
+
         if (rptId == NULL)
             rptId = report->rcbReference;
 
diff --git a/src/iec61850/client/ied_connection.c b/src/iec61850/client/ied_connection.c
index 6dbedc9..f80b326 100644
--- a/src/iec61850/client/ied_connection.c
+++ b/src/iec61850/client/ied_connection.c
@@ -198,6 +198,9 @@ ClientDataSet_getDataSetSize(ClientDataSet self)
 static bool
 doesControlObjectMatch(char* objRef, char* cntrlObj)
 {
+
+    printf("objRef: (%s) cntrlObj: (%s)\n", objRef, cntrlObj);
+
     int objRefLen = strlen(objRef);
 
     char* separator = strchr(cntrlObj, '$');
@@ -222,17 +225,31 @@ doesControlObjectMatch(char* objRef, char* cntrlObj)
     if (separator[3] != '$')
         return false;
 
-    char* nextSeparator = strchr(separator + 4, '$');
+    printf("Compare strings: (%s) vs (%s)\n", cntrlObjName, separator +4);
 
-    if (nextSeparator == NULL)
-        return false;
+//    char* nextSeparator = strchr(separator + 4, '$');
+
+//    if (nextSeparator == NULL)
+//        return false;
 
     int cntrlObjNameLen = strlen(cntrlObjName);
 
-    if (cntrlObjNameLen != nextSeparator - (separator + 4))
+    char* secondCntrlObjName = separator + 4;
+
+    if (cntrlObjNameLen != strlen(secondCntrlObjName))
         return false;
 
-    if (memcmp(cntrlObjName, separator + 4, cntrlObjNameLen) == 0)
+//    if (cntrlObjNameLen != nextSeparator - (separator + 4))
+//        return false;
+
+
+
+ //   int i;
+ //   for (i = 0; i < cntrlObjNameLen; i++) {
+ //       if (controlObjName )
+ //   }
+
+    if (memcmp(cntrlObjName, secondCntrlObjName, cntrlObjNameLen) == 0)
         return true;
 
     return false;
diff --git a/src/iec61850/server/mms_mapping/control.c b/src/iec61850/server/mms_mapping/control.c
index 84c4387..3b0c694 100644
--- a/src/iec61850/server/mms_mapping/control.c
+++ b/src/iec61850/server/mms_mapping/control.c
@@ -115,7 +115,7 @@ struct sControlObject
 };
 
 void
-ControlObject_sendLastApplError(ControlObject* self, MmsServerConnection* connection, char* ctlVariable, int error,
+ControlObject_sendLastApplError(ControlObject* self, MmsServerConnection* connection, int error,
         ControlAddCause addCause, MmsValue* ctlNum, MmsValue* origin, bool handlerMode);
 
 void
@@ -361,7 +361,7 @@ executeStateMachine:
 
         if (dynamicCheckResult == CONTROL_RESULT_FAILED) {
             if (isTimeActivatedControl) {
-                ControlObject_sendLastApplError(self, self->mmsConnection, "Oper",
+                ControlObject_sendLastApplError(self, self->mmsConnection,
                         CONTROL_ERROR_NO_ERROR, ADD_CAUSE_BLOCKED_BY_SYNCHROCHECK,
                         self->ctlNum, self->origin, false);
             }
@@ -688,7 +688,7 @@ Control_processControlActions(MmsMapping* self, uint64_t currentTimeInMs)
                     executeControlTask(controlObject);
                 }
                 else {
-                    ControlObject_sendLastApplError(controlObject, controlObject->mmsConnection, "Oper",
+                    ControlObject_sendLastApplError(controlObject, controlObject->mmsConnection,
                     CONTROL_ERROR_NO_ERROR, ADD_CAUSE_BLOCKED_BY_INTERLOCKING,
                             controlObject->ctlNum, controlObject->origin, false);
 
@@ -887,16 +887,12 @@ ControlObject_sendCommandTerminationNegative(ControlObject* self)
 
     lastApplError->value.structure.components = componentContainer;
 
-    char ctlObj[130];
-
-    createStringInBuffer(ctlObj, 2, self->ctlObjectName, "$Oper");
-
     MmsValue ctlObjValueMemory;
 
     MmsValue* ctlObjValue = &ctlObjValueMemory;
     ctlObjValue->type = MMS_VISIBLE_STRING;
-    ctlObjValue->value.visibleString.buf = ctlObj;
-    ctlObjValue->value.visibleString.size = sizeof(ctlObj);
+    ctlObjValue->value.visibleString.buf = self->ctlObjectName;
+    ctlObjValue->value.visibleString.size = sizeof(self->ctlObjectName);
 
     MmsValue_setElement(lastApplError, 0, ctlObjValue);
 
@@ -926,7 +922,6 @@ ControlObject_sendCommandTerminationNegative(ControlObject* self)
     operVarSpec.itemId = itemId;
     operVarSpec.domainId = domainId;
 
-
     /* create response */
 
     if (DEBUG_IED_SERVER)
@@ -949,7 +944,7 @@ ControlObject_sendCommandTerminationNegative(ControlObject* self)
 
 
 void
-ControlObject_sendLastApplError(ControlObject* self, MmsServerConnection* connection, char* ctlVariable, int error,
+ControlObject_sendLastApplError(ControlObject* self, MmsServerConnection* connection, int error,
         ControlAddCause addCause, MmsValue* ctlNum, MmsValue* origin, bool handlerMode)
 {
     MmsValue lastApplErrorMemory;
@@ -962,13 +957,9 @@ ControlObject_sendLastApplError(ControlObject* self, MmsServerConnection* connec
 
     lastApplError->value.structure.components =componentContainer;
 
-    char ctlObj[130];
-
-    createStringInBuffer(ctlObj, 3, self->ctlObjectName, "$", ctlVariable);
-
     if (DEBUG_IED_SERVER) {
         printf("IED_SERVER: sendLastApplError:\n");
-        printf("IED_SERVER:    control object: %s\n", ctlObj);
+        printf("IED_SERVER:    control object: %s\n", self->ctlObjectName);
         printf("IED_SERVER:    ctlNum: %u\n", MmsValue_toUint32(ctlNum));
     }
 
@@ -976,8 +967,8 @@ ControlObject_sendLastApplError(ControlObject* self, MmsServerConnection* connec
 
     MmsValue* ctlObjValue = &ctlObjValueMemory;
     ctlObjValue->type = MMS_VISIBLE_STRING;
-    ctlObjValue->value.visibleString.buf = ctlObj;
-    ctlObjValue->value.visibleString.size = sizeof(ctlObj);
+    ctlObjValue->value.visibleString.buf = self->ctlObjectName;
+    ctlObjValue->value.visibleString.size = sizeof(self->ctlObjectName);
 
     MmsValue_setElement(lastApplError, 0, ctlObjValue);
 
@@ -1313,10 +1304,10 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
                     indication = DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE;
 
                     if (connection != controlObject->mmsConnection)
-                        ControlObject_sendLastApplError(controlObject, connection, "SBOw", 0,
+                        ControlObject_sendLastApplError(controlObject, connection, 0,
                                 ADD_CAUSE_LOCKED_BY_OTHER_CLIENT, ctlNum, origin, true);
                     else
-                        ControlObject_sendLastApplError(controlObject, connection, "SBOw", 0,
+                        ControlObject_sendLastApplError(controlObject, connection, 0,
                                 ADD_CAUSE_OBJECT_ALREADY_SELECTED, ctlNum, origin, true);
 
                     if (DEBUG_IED_SERVER)
@@ -1353,7 +1344,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
                     else {
                         indication = getDataAccessErrorFromCheckHandlerResult(checkResult);
 
-                        ControlObject_sendLastApplError(controlObject, connection, "SBOw", 0,
+                        ControlObject_sendLastApplError(controlObject, connection, 0,
                                 ADD_CAUSE_SELECT_FAILED, ctlNum, origin, true);
 
                         if (DEBUG_IED_SERVER)
@@ -1398,7 +1389,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
         if (state == STATE_WAIT_FOR_ACTICATION_TIME) {
             indication = DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE;
 
-            ControlObject_sendLastApplError(controlObject, connection, "Oper",
+            ControlObject_sendLastApplError(controlObject, connection,
                     CONTROL_ERROR_NO_ERROR, ADD_CAUSE_COMMAND_ALREADY_IN_EXECUTION,
                         ctlNum, origin, true);
 
@@ -1428,7 +1419,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
                     {
 
                         indication = DATA_ACCESS_ERROR_TYPE_INCONSISTENT;
-                        ControlObject_sendLastApplError(controlObject, connection, "Oper",
+                        ControlObject_sendLastApplError(controlObject, connection,
                                 CONTROL_ERROR_NO_ERROR, ADD_CAUSE_INCONSISTENT_PARAMETERS,
                                     ctlNum, origin, true);
 
@@ -1508,7 +1499,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
                 printf("IED_SERVER: Oper failed - control not selected!\n");
 
             indication = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
-            ControlObject_sendLastApplError(controlObject, connection, "Oper",
+            ControlObject_sendLastApplError(controlObject, connection,
                     CONTROL_ERROR_NO_ERROR, ADD_CAUSE_OBJECT_NOT_SELECTED,
                         ctlNum, origin, true);
 
@@ -1540,7 +1531,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
                 }
                 else {
                     indication = DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE;
-                    ControlObject_sendLastApplError(controlObject, connection, "Cancel",
+                    ControlObject_sendLastApplError(controlObject, connection,
                             CONTROL_ERROR_NO_ERROR, ADD_CAUSE_LOCKED_BY_OTHER_CLIENT,
                                 ctlNum, origin, true);
                 }
diff --git a/src/iec61850/server/mms_mapping/mms_mapping.c b/src/iec61850/server/mms_mapping/mms_mapping.c
index a00e853..08c44ec 100644
--- a/src/iec61850/server/mms_mapping/mms_mapping.c
+++ b/src/iec61850/server/mms_mapping/mms_mapping.c
@@ -1602,9 +1602,14 @@ mmsWriteHandler(void* parameter, MmsDomain* domain,
                     if ((val > 0) && (val <= sg->sgcb->numOfSGs)) {
 
                         if (sg->editSgChangedHandler != NULL) {
+
+                            printf("has editSgChangedHandler\n");
+
                             if (sg->editSgChangedHandler(sg->editSgChangedHandlerParameter, sg->sgcb,
                                     (uint8_t) val, (ClientConnection) connection))
                             {
+                                printf("handler returned true\n");
+
                                 sg->sgcb->editSG = val;
                                 sg->editingClient = (ClientConnection) connection;
 
diff --git a/src/iec61850/server/model/config_file_parser.c b/src/iec61850/server/model/config_file_parser.c
index b4186f5..0e4d353 100644
--- a/src/iec61850/server/model/config_file_parser.c
+++ b/src/iec61850/server/model/config_file_parser.c
@@ -58,7 +58,6 @@ readLine(FileHandle fileHandle, uint8_t* buffer, int maxSize)
         }
     }
 
-
     if (fileReadResult > 0) {
         while (fileReadResult > 0) {
             fileReadResult = FileSystem_readFile(fileHandle, buffer + bufPos, 1);
@@ -238,8 +237,12 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle)
 #if (CONFIG_IEC61850_SETTING_GROUPS == 1)
                     else if (StringUtils_startsWith((char*) lineBuffer, "SG")) {
 
-                        if (strcmp(currentLN->name, "LLN0") != 0)
+                        if (strcmp(currentLN->name, "LLN0") != 0) {
+                            if (DEBUG_IED_SERVER)
+                                printf("Setting group control is not defined in LLN0\n");
+
                             goto exit_error;
+                        }
 
                         int actSG;
                         int numOfSGs;
@@ -253,8 +256,12 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle)
                     }
 #endif /* (CONFIG_IEC61850_SETTING_GROUPS == 1) */
 
-                    else
+                    else {
+                      //  if (DEBUG_IED_SERVER)
+                            printf("IED_SERVER: Unknown identifier (%s)\n", lineBuffer);
+
                         goto exit_error;
+                    }
 
                 }
                 else if (indendation > 3) {
@@ -423,8 +430,8 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle)
     return model;
 
 exit_error:
-    if (DEBUG_IED_SERVER)
-        printf("error parsing line %i\n", currentLine);
+   // if (DEBUG_IED_SERVER)
+        printf("IED_SERVER: error parsing line %i (indendation level = %i)\n", currentLine, indendation);
     IedModel_destroy(model);
     return NULL;
 }

From 98ec51346c2ab014c1210286b0c2ab7efb881435 Mon Sep 17 00:00:00 2001
From: Michael Zillgith <michael@zillgith.eu>
Date: Thu, 8 Jan 2015 12:28:06 +0100
Subject: [PATCH 3/5] - changed back behaviour of LastApplError

---
 src/iec61850/client/ied_connection.c      | 27 +++------------
 src/iec61850/server/mms_mapping/control.c | 41 ++++++++++++++---------
 2 files changed, 30 insertions(+), 38 deletions(-)

diff --git a/src/iec61850/client/ied_connection.c b/src/iec61850/client/ied_connection.c
index f80b326..6dbedc9 100644
--- a/src/iec61850/client/ied_connection.c
+++ b/src/iec61850/client/ied_connection.c
@@ -198,9 +198,6 @@ ClientDataSet_getDataSetSize(ClientDataSet self)
 static bool
 doesControlObjectMatch(char* objRef, char* cntrlObj)
 {
-
-    printf("objRef: (%s) cntrlObj: (%s)\n", objRef, cntrlObj);
-
     int objRefLen = strlen(objRef);
 
     char* separator = strchr(cntrlObj, '$');
@@ -225,31 +222,17 @@ doesControlObjectMatch(char* objRef, char* cntrlObj)
     if (separator[3] != '$')
         return false;
 
-    printf("Compare strings: (%s) vs (%s)\n", cntrlObjName, separator +4);
+    char* nextSeparator = strchr(separator + 4, '$');
 
-//    char* nextSeparator = strchr(separator + 4, '$');
-
-//    if (nextSeparator == NULL)
-//        return false;
+    if (nextSeparator == NULL)
+        return false;
 
     int cntrlObjNameLen = strlen(cntrlObjName);
 
-    char* secondCntrlObjName = separator + 4;
-
-    if (cntrlObjNameLen != strlen(secondCntrlObjName))
+    if (cntrlObjNameLen != nextSeparator - (separator + 4))
         return false;
 
-//    if (cntrlObjNameLen != nextSeparator - (separator + 4))
-//        return false;
-
-
-
- //   int i;
- //   for (i = 0; i < cntrlObjNameLen; i++) {
- //       if (controlObjName )
- //   }
-
-    if (memcmp(cntrlObjName, secondCntrlObjName, cntrlObjNameLen) == 0)
+    if (memcmp(cntrlObjName, separator + 4, cntrlObjNameLen) == 0)
         return true;
 
     return false;
diff --git a/src/iec61850/server/mms_mapping/control.c b/src/iec61850/server/mms_mapping/control.c
index 3b0c694..84c4387 100644
--- a/src/iec61850/server/mms_mapping/control.c
+++ b/src/iec61850/server/mms_mapping/control.c
@@ -115,7 +115,7 @@ struct sControlObject
 };
 
 void
-ControlObject_sendLastApplError(ControlObject* self, MmsServerConnection* connection, int error,
+ControlObject_sendLastApplError(ControlObject* self, MmsServerConnection* connection, char* ctlVariable, int error,
         ControlAddCause addCause, MmsValue* ctlNum, MmsValue* origin, bool handlerMode);
 
 void
@@ -361,7 +361,7 @@ executeStateMachine:
 
         if (dynamicCheckResult == CONTROL_RESULT_FAILED) {
             if (isTimeActivatedControl) {
-                ControlObject_sendLastApplError(self, self->mmsConnection,
+                ControlObject_sendLastApplError(self, self->mmsConnection, "Oper",
                         CONTROL_ERROR_NO_ERROR, ADD_CAUSE_BLOCKED_BY_SYNCHROCHECK,
                         self->ctlNum, self->origin, false);
             }
@@ -688,7 +688,7 @@ Control_processControlActions(MmsMapping* self, uint64_t currentTimeInMs)
                     executeControlTask(controlObject);
                 }
                 else {
-                    ControlObject_sendLastApplError(controlObject, controlObject->mmsConnection,
+                    ControlObject_sendLastApplError(controlObject, controlObject->mmsConnection, "Oper",
                     CONTROL_ERROR_NO_ERROR, ADD_CAUSE_BLOCKED_BY_INTERLOCKING,
                             controlObject->ctlNum, controlObject->origin, false);
 
@@ -887,12 +887,16 @@ ControlObject_sendCommandTerminationNegative(ControlObject* self)
 
     lastApplError->value.structure.components = componentContainer;
 
+    char ctlObj[130];
+
+    createStringInBuffer(ctlObj, 2, self->ctlObjectName, "$Oper");
+
     MmsValue ctlObjValueMemory;
 
     MmsValue* ctlObjValue = &ctlObjValueMemory;
     ctlObjValue->type = MMS_VISIBLE_STRING;
-    ctlObjValue->value.visibleString.buf = self->ctlObjectName;
-    ctlObjValue->value.visibleString.size = sizeof(self->ctlObjectName);
+    ctlObjValue->value.visibleString.buf = ctlObj;
+    ctlObjValue->value.visibleString.size = sizeof(ctlObj);
 
     MmsValue_setElement(lastApplError, 0, ctlObjValue);
 
@@ -922,6 +926,7 @@ ControlObject_sendCommandTerminationNegative(ControlObject* self)
     operVarSpec.itemId = itemId;
     operVarSpec.domainId = domainId;
 
+
     /* create response */
 
     if (DEBUG_IED_SERVER)
@@ -944,7 +949,7 @@ ControlObject_sendCommandTerminationNegative(ControlObject* self)
 
 
 void
-ControlObject_sendLastApplError(ControlObject* self, MmsServerConnection* connection, int error,
+ControlObject_sendLastApplError(ControlObject* self, MmsServerConnection* connection, char* ctlVariable, int error,
         ControlAddCause addCause, MmsValue* ctlNum, MmsValue* origin, bool handlerMode)
 {
     MmsValue lastApplErrorMemory;
@@ -957,9 +962,13 @@ ControlObject_sendLastApplError(ControlObject* self, MmsServerConnection* connec
 
     lastApplError->value.structure.components =componentContainer;
 
+    char ctlObj[130];
+
+    createStringInBuffer(ctlObj, 3, self->ctlObjectName, "$", ctlVariable);
+
     if (DEBUG_IED_SERVER) {
         printf("IED_SERVER: sendLastApplError:\n");
-        printf("IED_SERVER:    control object: %s\n", self->ctlObjectName);
+        printf("IED_SERVER:    control object: %s\n", ctlObj);
         printf("IED_SERVER:    ctlNum: %u\n", MmsValue_toUint32(ctlNum));
     }
 
@@ -967,8 +976,8 @@ ControlObject_sendLastApplError(ControlObject* self, MmsServerConnection* connec
 
     MmsValue* ctlObjValue = &ctlObjValueMemory;
     ctlObjValue->type = MMS_VISIBLE_STRING;
-    ctlObjValue->value.visibleString.buf = self->ctlObjectName;
-    ctlObjValue->value.visibleString.size = sizeof(self->ctlObjectName);
+    ctlObjValue->value.visibleString.buf = ctlObj;
+    ctlObjValue->value.visibleString.size = sizeof(ctlObj);
 
     MmsValue_setElement(lastApplError, 0, ctlObjValue);
 
@@ -1304,10 +1313,10 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
                     indication = DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE;
 
                     if (connection != controlObject->mmsConnection)
-                        ControlObject_sendLastApplError(controlObject, connection, 0,
+                        ControlObject_sendLastApplError(controlObject, connection, "SBOw", 0,
                                 ADD_CAUSE_LOCKED_BY_OTHER_CLIENT, ctlNum, origin, true);
                     else
-                        ControlObject_sendLastApplError(controlObject, connection, 0,
+                        ControlObject_sendLastApplError(controlObject, connection, "SBOw", 0,
                                 ADD_CAUSE_OBJECT_ALREADY_SELECTED, ctlNum, origin, true);
 
                     if (DEBUG_IED_SERVER)
@@ -1344,7 +1353,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
                     else {
                         indication = getDataAccessErrorFromCheckHandlerResult(checkResult);
 
-                        ControlObject_sendLastApplError(controlObject, connection, 0,
+                        ControlObject_sendLastApplError(controlObject, connection, "SBOw", 0,
                                 ADD_CAUSE_SELECT_FAILED, ctlNum, origin, true);
 
                         if (DEBUG_IED_SERVER)
@@ -1389,7 +1398,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
         if (state == STATE_WAIT_FOR_ACTICATION_TIME) {
             indication = DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE;
 
-            ControlObject_sendLastApplError(controlObject, connection,
+            ControlObject_sendLastApplError(controlObject, connection, "Oper",
                     CONTROL_ERROR_NO_ERROR, ADD_CAUSE_COMMAND_ALREADY_IN_EXECUTION,
                         ctlNum, origin, true);
 
@@ -1419,7 +1428,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
                     {
 
                         indication = DATA_ACCESS_ERROR_TYPE_INCONSISTENT;
-                        ControlObject_sendLastApplError(controlObject, connection,
+                        ControlObject_sendLastApplError(controlObject, connection, "Oper",
                                 CONTROL_ERROR_NO_ERROR, ADD_CAUSE_INCONSISTENT_PARAMETERS,
                                     ctlNum, origin, true);
 
@@ -1499,7 +1508,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
                 printf("IED_SERVER: Oper failed - control not selected!\n");
 
             indication = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
-            ControlObject_sendLastApplError(controlObject, connection,
+            ControlObject_sendLastApplError(controlObject, connection, "Oper",
                     CONTROL_ERROR_NO_ERROR, ADD_CAUSE_OBJECT_NOT_SELECTED,
                         ctlNum, origin, true);
 
@@ -1531,7 +1540,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
                 }
                 else {
                     indication = DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE;
-                    ControlObject_sendLastApplError(controlObject, connection,
+                    ControlObject_sendLastApplError(controlObject, connection, "Cancel",
                             CONTROL_ERROR_NO_ERROR, ADD_CAUSE_LOCKED_BY_OTHER_CLIENT,
                                 ctlNum, origin, true);
                 }

From 83a7e7cfdcf57c24d12309c1a59a9c75f6cdfcbf Mon Sep 17 00:00:00 2001
From: Michael Zillgith <michael@zillgith.eu>
Date: Thu, 8 Jan 2015 14:17:09 +0100
Subject: [PATCH 4/5] - solve client problem with some implementations of
 LastApplError (missing $Oper as by TMW Anvil)

---
 src/iec61850/client/ied_connection.c          | 75 ++++++++++++-------
 .../inc_private/ied_connection_private.h      |  3 +
 src/iec61850/server/mms_mapping/mms_mapping.c |  4 -
 3 files changed, 52 insertions(+), 30 deletions(-)

diff --git a/src/iec61850/client/ied_connection.c b/src/iec61850/client/ied_connection.c
index 6dbedc9..8ef2843 100644
--- a/src/iec61850/client/ied_connection.c
+++ b/src/iec61850/client/ied_connection.c
@@ -195,47 +195,70 @@ ClientDataSet_getDataSetSize(ClientDataSet self)
         return 0;
 }
 
-static bool
-doesControlObjectMatch(char* objRef, char* cntrlObj)
+bool
+private_IedConnection_doesControlObjectMatch(char* objRef, char* cntrlObj)
 {
-    int objRefLen = strlen(objRef);
+    int i = 0;
 
-    char* separator = strchr(cntrlObj, '$');
+    while (objRef[i] != '/') {
+        if (objRef[i] != cntrlObj[i])
+            return false;
 
-    if (separator == NULL)
+        i++;
+    }
+
+    if (cntrlObj[i] != '/')
         return false;
 
-    int sepLen = separator - cntrlObj;
+    // --> LD is equal
 
-    if (sepLen >= objRefLen)
+    i++;
+
+    while (objRef[i] != '.') {
+        if (objRef[i] != cntrlObj[i])
+            return false;
+        i++;
+    }
+
+    int j = i;
+
+    if (cntrlObj[j++] != '$')
         return false;
 
-    if (memcmp(objRef, cntrlObj, sepLen) != 0)
+    // --> LN is equal
+
+    if (cntrlObj[j++] != 'C')
+        return false;
+    if (cntrlObj[j++] != 'O')
+        return false;
+    if (cntrlObj[j++] != '$')
         return false;
 
-    char* cntrlObjName = objRef + sepLen + 1;
+    // --> FC is ok
 
-    if (separator[1] != 'C')
-        return false;
-    if (separator[2] != 'O')
-        return false;
-    if (separator[3] != '$')
-        return false;
+    i++;
 
-    char* nextSeparator = strchr(separator + 4, '$');
+    while (objRef[i] != 0) {
+        if (cntrlObj[j] == 0)
+            return false;
 
-    if (nextSeparator == NULL)
-        return false;
+        if (objRef[i] == '.') {
+            if (cntrlObj[j] != '$')
+                return false;
+        }
+        else {
+            if (objRef[i] != cntrlObj[j])
+                return false;
+        }
 
-    int cntrlObjNameLen = strlen(cntrlObjName);
+        i++;
+        j++;
+    }
 
-    if (cntrlObjNameLen != nextSeparator - (separator + 4))
-        return false;
-
-    if (memcmp(cntrlObjName, separator + 4, cntrlObjNameLen) == 0)
+    if ((cntrlObj[j] == 0) || (cntrlObj[j] == '$'))
         return true;
-
-    return false;
+    else
+        return false;
 }
 
 static bool
@@ -357,7 +380,7 @@ handleLastApplErrorMessage(IedConnection self, MmsValue* lastApplError)
 
         char* objectRef = ControlObjectClient_getObjectReference(object);
 
-        if (doesControlObjectMatch(objectRef, MmsValue_toString(cntrlObj))) {
+        if (private_IedConnection_doesControlObjectMatch(objectRef, MmsValue_toString(cntrlObj))) {
             ControlObjectClient_setLastApplError(object, self->lastApplError);
         }
 
diff --git a/src/iec61850/inc_private/ied_connection_private.h b/src/iec61850/inc_private/ied_connection_private.h
index 6955300..09ff9e4 100644
--- a/src/iec61850/inc_private/ied_connection_private.h
+++ b/src/iec61850/inc_private/ied_connection_private.h
@@ -71,6 +71,9 @@ struct sClientReportControlBlock {
 IedClientError
 private_IedConnection_mapMmsErrorToIedError(MmsError mmsError);
 
+bool
+private_IedConnection_doesControlObjectMatch(char* objRef, char* cntrlObj);
+
 void
 private_IedConnection_addControlClient(IedConnection self, ControlObjectClient control);
 
diff --git a/src/iec61850/server/mms_mapping/mms_mapping.c b/src/iec61850/server/mms_mapping/mms_mapping.c
index 08c44ec..a21e406 100644
--- a/src/iec61850/server/mms_mapping/mms_mapping.c
+++ b/src/iec61850/server/mms_mapping/mms_mapping.c
@@ -1603,13 +1603,9 @@ mmsWriteHandler(void* parameter, MmsDomain* domain,
 
                         if (sg->editSgChangedHandler != NULL) {
 
-                            printf("has editSgChangedHandler\n");
-
                             if (sg->editSgChangedHandler(sg->editSgChangedHandlerParameter, sg->sgcb,
                                     (uint8_t) val, (ClientConnection) connection))
                             {
-                                printf("handler returned true\n");
-
                                 sg->sgcb->editSG = val;
                                 sg->editingClient = (ClientConnection) connection;
 

From df6ac20092059c5b430bc256e2695e74d1c9de2d Mon Sep 17 00:00:00 2001
From: Michael Zillgith <michael@zillgith.eu>
Date: Mon, 12 Jan 2015 11:32:09 +0100
Subject: [PATCH 5/5] - added function ModelNode_getChildWithFc

---
 src/hal/socket/win32/socket_win32.c |  2 +-
 src/iec61850/inc/iec61850_model.h   | 30 +++++++++++++----
 src/iec61850/server/model/model.c   | 50 +++++++++++++++++++++++++++++
 src/mms/iso_session/iso_session.c   |  5 +--
 src/vs/libiec61850-wo-goose.def     |  1 +
 src/vs/libiec61850.def              |  3 +-
 6 files changed, 78 insertions(+), 13 deletions(-)

diff --git a/src/hal/socket/win32/socket_win32.c b/src/hal/socket/win32/socket_win32.c
index 11c5405..1d888e9 100644
--- a/src/hal/socket/win32/socket_win32.c
+++ b/src/hal/socket/win32/socket_win32.c
@@ -311,7 +311,7 @@ Socket_connect(Socket self, const char* address, int port)
     timeout.tv_sec = self->connectTimeout / 1000;
     timeout.tv_usec = (self->connectTimeout % 1000) * 1000;
 
-    if (select(self->fd + 1, NULL, &fdSet, NULL, &timeout) == SOCKET_ERROR)
+    if (select(self->fd + 1, NULL, &fdSet, NULL, &timeout) < 0)
         return false;
     else
         return true;
diff --git a/src/iec61850/inc/iec61850_model.h b/src/iec61850/inc/iec61850_model.h
index b44378d..01d1a1e 100644
--- a/src/iec61850/inc/iec61850_model.h
+++ b/src/iec61850/inc/iec61850_model.h
@@ -253,36 +253,52 @@ struct sGSEControlBlock {
 /**
  * \brief get the number of direct children of a model node
  *
- * \param node the model node instance
+ * \param self the model node instance
  *
  * \return the number of children of the model node
  * ΒΈ
  */
 int
-ModelNode_getChildCount(ModelNode* modelNode);
+ModelNode_getChildCount(ModelNode* self);
 
 /**
  * \brief return a child model node
  *
- * \param node the model node instance
- * \param the name of the child model node
+ * \param self the model node instance
+ * \param name the name of the child model node
  *
  * \return  the model node instance or NULL if model node does not exist.
  */
 ModelNode*
-ModelNode_getChild(ModelNode* modelNode, const char* name);
+ModelNode_getChild(ModelNode* self, const char* name);
+
+/**
+ * \brief return a child model node with a given functional constraint
+ *
+ * Sometimes the name is not enough to identify a model node. This is the case when
+ * editable setting groups are used. In this case the setting group members have two different
+ * model nodes associated that differ in their FC (SG and SE).
+ *
+ * \param self the model node instance
+ * \param name the name of the child model node
+ * \param fc the functional constraint of the model node
+ *
+ * \return  the model node instance or NULL if model node does not exist.
+ */
+ModelNode*
+ModelNode_getChildWithFc(ModelNode* self, const char* name, FunctionalConstraint fc);
 
 /**
  * \brief Return the IEC 61850 object reference of a model node
  *
- * \param node the model node instance
+ * \param self the model node instance
  * \param objectReference pointer to a buffer where to write the object reference string. If NULL
  *        is given the buffer is allocated by the function.
  *
  * \return the object reference string
  */
 char*
-ModelNode_getObjectReference(ModelNode* node, char* objectReference);
+ModelNode_getObjectReference(ModelNode* self, char* objectReference);
 
 /**
  * \brief Set the name of the IED
diff --git a/src/iec61850/server/model/model.c b/src/iec61850/server/model/model.c
index d01ac1a..89e3512 100644
--- a/src/iec61850/server/model/model.c
+++ b/src/iec61850/server/model/model.c
@@ -457,6 +457,56 @@ ModelNode_getChild(ModelNode* self, const char* name)
        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 {
+                   matchingNode = nextNode;
+                   break;
+               }
+           }
+       }
+
+       nextNode = nextNode->sibling;
+   }
+
+   if ((separator != NULL) && (matchingNode != NULL)) {
+       return ModelNode_getChildWithFc(matchingNode, separator + 1, fc);
+   }
+   else
+       return matchingNode;
+}
+
 
 inline
 LogicalNode*
diff --git a/src/mms/iso_session/iso_session.c b/src/mms/iso_session/iso_session.c
index 8e0fe0c..e3db269 100644
--- a/src/mms/iso_session/iso_session.c
+++ b/src/mms/iso_session/iso_session.c
@@ -338,10 +338,7 @@ IsoSession_createFinishSpdu(IsoSession* self, BufferChain buffer, BufferChain pa
 
     buf[offset++] = 9; /* FINISH-SPDU code */
 
-    buf[offset++] = 5 + payload->length; /* LI */
-    buf[offset++] = 17; /* PI-Code transport-disconnect */
-    buf[offset++] = 1; /* LI = 1 */
-    buf[offset++] = 2; /* transport-connection-released */
+    buf[offset++] = 2 + payload->length; /* LI */
     buf[offset++] = 193; /* PGI-Code user data */
     buf[offset++] = payload->length; /* LI of user data */
 
diff --git a/src/vs/libiec61850-wo-goose.def b/src/vs/libiec61850-wo-goose.def
index ddfae6b..dbbebc3 100644
--- a/src/vs/libiec61850-wo-goose.def
+++ b/src/vs/libiec61850-wo-goose.def
@@ -481,4 +481,5 @@ EXPORTS
     IedServer_getUTCTimeAttributeValue
     IedServer_getBitStringAttributeValue
     IedServer_getStringAttributeValue
+    ModelNode_getChildWithFc
 
diff --git a/src/vs/libiec61850.def b/src/vs/libiec61850.def
index 18a6306..0878e2f 100644
--- a/src/vs/libiec61850.def
+++ b/src/vs/libiec61850.def
@@ -505,4 +505,5 @@ EXPORTS
     IedServer_getUTCTimeAttributeValue
     IedServer_getBitStringAttributeValue
     IedServer_getStringAttributeValue
-    
+    ModelNode_getChildWithFc
+