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);
+ }
/// This exception is thrown if there is a connection or service error
public List 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 @@
+
+
+
+
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 ce950d4..a6d64c0 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
+ * \param 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/hal/socket/win32/socket_win32.c b/src/hal/socket/win32/socket_win32.c
index 789de3b..1d888e9 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;
@@ -321,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/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 846d807..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);
}
@@ -966,9 +989,8 @@ IedConnection_getDeviceModelFromServer(IedConnection self, IedClientError* error
LinkedList_destroy(logicalDeviceNames);
}
- else {
+ else
*error = iedConnection_mapMmsErrorToIedError(mmsError);
- }
}
LinkedList /**/
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/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/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 a00e853..a21e406 100644
--- a/src/iec61850/server/mms_mapping/mms_mapping.c
+++ b/src/iec61850/server/mms_mapping/mms_mapping.c
@@ -1602,6 +1602,7 @@ mmsWriteHandler(void* parameter, MmsDomain* domain,
if ((val > 0) && (val <= sg->sgcb->numOfSGs)) {
if (sg->editSgChangedHandler != NULL) {
+
if (sg->editSgChangedHandler(sg->editSgChangedHandlerParameter, sg->sgcb,
(uint8_t) val, (ClientConnection) connection))
{
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];
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;
}
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
+