From 91b9c2c64b10daf8f4b370dadcf86301c90db949 Mon Sep 17 00:00:00 2001 From: Michael Zillgith 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); + } /// 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/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 /**/ 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 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 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 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 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 +