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 +