diff --git a/CHANGELOG b/CHANGELOG index ba7c406e..96ca7ca2 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,10 @@ Changes to version 0.9.1 ------------------------ +- client: added function MmsConnection_getMmsConnectionParameters +- client: new functions to reset synchro and interlock bits for control clients +- some extensions to C# API +- integrated some under the hood changes to be more compliant with certification tests. +- added CMake recipe to create python bindings - added functions MmsValue_encodeMmsData, MmsValue_decodeMmsData to serialize/deserialize MmsValue objects to BER encoded MMS Data - changed signature of IedConnection_deleteDataSet, MmsConnection_deleteAssociationSpecificNamedVariableList, MmsConnection_deleteNamedVariableList. added boolean return value to indicate if data set/named variable list has been deleted. diff --git a/CMakeLists.txt b/CMakeLists.txt index b303bdd5..e374c898 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,6 +8,7 @@ if(DEFINED ENV{TOOLCHAIN}) endif() project(libiec61850) +ENABLE_TESTING() set(LIB_VERSION_MAJOR "0") set(LIB_VERSION_MINOR "9") @@ -25,6 +26,7 @@ set(CONFIG_MMS_MAXIMUM_PDU_SIZE "65000" CACHE STRING "Configure the maximum size set(CONFIG_MAXIMUM_TCP_CLIENT_CONNECTIONS 5 CACHE STRING "Configure the maximum number of clients allowed to connect to the server") option(BUILD_EXAMPLES "Build the examples" ON) +option(BUILD_PYTHON_BINDINGS "Build Python bindings" OFF) option(CONFIG_MMS_SINGLE_THREADED "Compile for single threaded version" OFF) option(CONFIG_MMS_THREADLESS_STACK "Optimize stack for threadless operation (warning: single- or multi-threaded server will not work!)" OFF) @@ -102,11 +104,11 @@ set(API_HEADERS src/sampled_values/sv_publisher.h ) -IF(WIN32) +IF(MSVC) include_directories( src/vs ) -ENDIF(WIN32) +ENDIF(MSVC) # write the detected stuff to this file configure_file(config/stack_config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config/stack_config.h) @@ -119,6 +121,9 @@ add_subdirectory(src) INSTALL(FILES ${API_HEADERS} DESTINATION include/libiec61850) +IF(BUILD_PYTHON_BINDINGS) + add_subdirectory(pyiec61850) +ENDIF(BUILD_PYTHON_BINDINGS) IF(EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake") INCLUDE(InstallRequiredSystemLibraries) @@ -144,6 +149,3 @@ SET(CPACK_COMPONENTS_ALL Libraries ApplicationData) INCLUDE(CPack) ENDIF(EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake") - - - diff --git a/dotnet/IEC61850forCSharp/Control.cs b/dotnet/IEC61850forCSharp/Control.cs index cd425d88..f0faa906 100644 --- a/dotnet/IEC61850forCSharp/Control.cs +++ b/dotnet/IEC61850forCSharp/Control.cs @@ -137,11 +137,11 @@ namespace IEC61850 [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] private static extern void ControlObjectClient_setOrigin(IntPtr self, string orIdent, int orCat); - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - private static extern void ControlObjectClient_enableInterlockCheck(IntPtr self); + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern void ControlObjectClient_setInterlockCheck(IntPtr self, bool value); - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - private static extern void ControlObjectClient_enableSynchroCheck(IntPtr self); + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern void ControlObjectClient_setSynchroCheck(IntPtr self, bool value); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate void InternalCommandTerminationHandler(IntPtr parameter,IntPtr controlClient); @@ -373,19 +373,37 @@ namespace IEC61850 /// /// Enables the synchro check for operate commands /// + [Obsolete("use SetSynchroCheck instead")] public void EnableSynchroCheck () { - ControlObjectClient_enableSynchroCheck(controlObject); + ControlObjectClient_setSynchroCheck (controlObject, true); } /// /// Enables the interlock check for operate and select commands /// - public void EnableInterlockCheck () + [Obsolete("use SetInterlockCheck instead")] + public void EnableInterlockCheck () { - ControlObjectClient_enableInterlockCheck(controlObject); + ControlObjectClient_setInterlockCheck (controlObject, true); } + /// + /// Sets the value of the interlock check flag for operate and select commands + /// + public void SetInterlockCheck (bool value) + { + ControlObjectClient_setInterlockCheck (controlObject, value); + } + + /// + /// Sets the value of the synchro check flag for operate command + /// + public void SetSynchroCheck (bool value) + { + ControlObjectClient_setSynchroCheck (controlObject, value); + } + /// /// Gets the last received LastApplError (Additional Cause Diagnostics) value. /// diff --git a/dotnet/example3/Main.cs b/dotnet/example3/Main.cs index 343efc8d..86fe0644 100644 --- a/dotnet/example3/Main.cs +++ b/dotnet/example3/Main.cs @@ -24,7 +24,7 @@ namespace example3 { IsoConnectionParameters parameters = con.GetConnectionParameters(); - parameters.SetRemoteAddresses(1,1, new byte[] {0x00, 0x01, 0x02, 0x03}); + parameters.SetRemoteAddresses(1, new byte[] {0x00, 0x01}, new byte[] {0x00, 0x01, 0x02, 0x03}); con.ConnectTimeout = 10000; diff --git a/examples/iec61850_client_example4/CMakeLists.txt b/examples/iec61850_client_example4/CMakeLists.txt index 2c14b05c..cacd87bf 100644 --- a/examples/iec61850_client_example4/CMakeLists.txt +++ b/examples/iec61850_client_example4/CMakeLists.txt @@ -3,10 +3,10 @@ set(iec61850_client_example4_SRCS client_example4.c ) -IF(WIN32) +IF(MSVC) set_source_files_properties(${iec61850_client_example4_SRCS} PROPERTIES LANGUAGE CXX) -ENDIF(WIN32) +ENDIF(MSVC) add_executable(iec61850_client_example4 ${iec61850_client_example4_SRCS} diff --git a/examples/iec61850_client_example_files/CMakeLists.txt b/examples/iec61850_client_example_files/CMakeLists.txt index 10b7ab02..36cba273 100644 --- a/examples/iec61850_client_example_files/CMakeLists.txt +++ b/examples/iec61850_client_example_files/CMakeLists.txt @@ -3,10 +3,10 @@ set(iec61850_client_example_files_SRCS client_example_files.c ) -IF(WIN32) +IF(MSVC) set_source_files_properties(${iec61850_client_example_files_SRCS} PROPERTIES LANGUAGE CXX) -ENDIF(WIN32) +ENDIF(MSVC) add_executable(iec61850_client_example_files ${iec61850_client_example_files_SRCS} diff --git a/examples/iec61850_client_example_reporting/CMakeLists.txt b/examples/iec61850_client_example_reporting/CMakeLists.txt index 621f1298..8fa60dfb 100644 --- a/examples/iec61850_client_example_reporting/CMakeLists.txt +++ b/examples/iec61850_client_example_reporting/CMakeLists.txt @@ -3,10 +3,10 @@ set(iec61850_client_example_reporting_SRCS client_example_reporting.c ) -IF(WIN32) +IF(MSVC) set_source_files_properties(${iec61850_client_example_reporting_SRCS} PROPERTIES LANGUAGE CXX) -ENDIF(WIN32) +ENDIF(MSVC) add_executable(iec61850_client_example_reporting ${iec61850_client_example_reporting_SRCS} diff --git a/examples/mms_client_example2/CMakeLists.txt b/examples/mms_client_example2/CMakeLists.txt index b56f7a51..15016df7 100644 --- a/examples/mms_client_example2/CMakeLists.txt +++ b/examples/mms_client_example2/CMakeLists.txt @@ -3,10 +3,10 @@ set(mms_client_example2_SRCS mms_client_example2.c ) -IF(WIN32) +IF(MSVC) set_source_files_properties(${mms_client_example2_SRCS} PROPERTIES LANGUAGE CXX) -ENDIF(WIN32) +ENDIF(MSVC) add_executable(mms_client_example2 ${mms_client_example2_SRCS} diff --git a/examples/mms_client_example3/CMakeLists.txt b/examples/mms_client_example3/CMakeLists.txt index 4a5cab0a..df110000 100644 --- a/examples/mms_client_example3/CMakeLists.txt +++ b/examples/mms_client_example3/CMakeLists.txt @@ -3,10 +3,10 @@ set(mms_client_example3_SRCS mms_client_example3.c ) -IF(WIN32) +IF(MSVC) set_source_files_properties(${mms_client_example3_SRCS} PROPERTIES LANGUAGE CXX) -ENDIF(WIN32) +ENDIF(MSVC) add_executable(mms_client_example3 ${mms_client_example3_SRCS} diff --git a/examples/mms_client_example4/CMakeLists.txt b/examples/mms_client_example4/CMakeLists.txt index 6335c595..96b9edfc 100644 --- a/examples/mms_client_example4/CMakeLists.txt +++ b/examples/mms_client_example4/CMakeLists.txt @@ -3,10 +3,10 @@ set(mms_client_example4_SRCS mms_client_example4.c ) -IF(WIN32) +IF(MSVC) set_source_files_properties(${mms_client_example4_SRCS} PROPERTIES LANGUAGE CXX) -ENDIF(WIN32) +ENDIF(MSVC) add_executable(mms_client_example4 ${mms_client_example4_SRCS} diff --git a/pyiec61850/CMakeLists.txt b/pyiec61850/CMakeLists.txt new file mode 100644 index 00000000..328a9966 --- /dev/null +++ b/pyiec61850/CMakeLists.txt @@ -0,0 +1,23 @@ +FIND_PACKAGE(SWIG REQUIRED) +INCLUDE(${SWIG_USE_FILE}) +FIND_PACKAGE(PythonLibs REQUIRED) +FIND_PACKAGE ( PythonInterp REQUIRED ) +INCLUDE_DIRECTORIES(${PYTHON_INCLUDE_PATH}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) +SET(CMAKE_SWIG_FLAGS "") +SET_PROPERTY(SOURCE iec61850.i PROPERTY CPLUSPLUS ON) +SWIG_ADD_MODULE(iec61850 python iec61850.i) +IF(WIN32) + SWIG_LINK_LIBRARIES(iec61850 ${PYTHON_LIBRARIES} iec61850 ws2_32) +ELSE() + SWIG_LINK_LIBRARIES(iec61850 ${PYTHON_LIBRARIES} iec61850-shared) +ENDIF(WIN32) +EXECUTE_PROCESS ( #Finding python modules install path + COMMAND ${PYTHON_EXECUTABLE} -c + "import site, sys; sys.stdout.write(site.getsitepackages()[-1])" + OUTPUT_VARIABLE PYTHON_SITE_DIR + ) +INSTALL ( FILES ${CMAKE_BINARY_DIR}/iec61850.py DESTINATION ${PYTHON_SITE_DIR}) +INSTALL ( TARGETS _iec61850 LIBRARY DESTINATION ${PYTHON_SITE_DIR}) + +add_test(test_pyiec61850 ${PYTHON_EXECUTABLE} test_pyiec61850.py) diff --git a/pyiec61850/iec61850.i b/pyiec61850/iec61850.i new file mode 100644 index 00000000..2af52e80 --- /dev/null +++ b/pyiec61850/iec61850.i @@ -0,0 +1,41 @@ +/* File : iec61850.i */ +%module iec61850 +%ignore ControlObjectClient_setTestMode(ControlObjectClient self); +%ignore CDA_OperBoolean(ModelNode* parent, bool isTImeActivated); +%ignore LogicalNode_hasBufferedReports(LogicalNode* node); +%ignore LogicalNode_hasUnbufferedReports(LogicalNode* node); +%ignore MmsConnection_setIsoConnectionParameters(MmsConnection self, IsoConnectionParameters* params); +%include "stdint.i" +%{ +#include +#include +#include +ModelNode* toModelNode(LogicalNode * ln) +{ + return (ModelNode*) ln; +} +ModelNode* toModelNode(DataObject * DO) +{ + return (ModelNode*) DO; +} +DataAttribute* toDataAttribute(DataObject * DO) +{ return (DataAttribute*)DO;} +DataAttribute* toDataAttribute(ModelNode * MN) +{ return (DataAttribute*)MN;} +%} +%apply int *OUTPUT {IedClientError* error}; + +%include "iec61850_client.h" +%include "iso_connection_parameters.h" +%include "mms_client_connection.h" +%include "iso_connection_parameters.h" +%include "iec61850_common.h" +%include "mms_value.h" +%include "iec61850_model.h" +%include "iec61850_server.h" +%include "iec61850_dynamic_model.h" +%include "iec61850_cdc.h" +ModelNode* toModelNode(LogicalNode *); +ModelNode* toModelNode(DataObject *); +DataAttribute* toDataAttribute(DataObject *); +DataAttribute* toDataAttribute(ModelNode *); diff --git a/pyiec61850/test_pyiec61850.py b/pyiec61850/test_pyiec61850.py new file mode 100755 index 00000000..7f5d3655 --- /dev/null +++ b/pyiec61850/test_pyiec61850.py @@ -0,0 +1,82 @@ +#!/usr/bin/python +import sys +import time +import iec61850 +import threading +import traceback +import signal +import sys +def signal_handler(signal, frame): + global running + running =0 + print('You pressed Ctrl+C!') + #sys.exit(0) +signal.signal(signal.SIGINT, signal_handler) +tcpPort = 8102 +running = 1 +class myIECServer(): + def __init__(self): + self.__model = iec61850.IedModel_create("testmodel") + lDevice1 = iec61850.LogicalDevice_create("SENSORS", self.__model); + lln0 = iec61850.LogicalNode_create("LLN0", lDevice1); + ttmp1 = iec61850.LogicalNode_create("TTMP1", lDevice1); + iec61850.CDC_SAV_create("TmpSv", iec61850.toModelNode(ttmp1), 0, False) + iec61850.CDC_ASG_create("TmpSp", iec61850.toModelNode(ttmp1), 0, False) + self.__iedServer = iec61850.IedServer_create(self.__model) + iec61850.IedServer_start(self.__iedServer, tcpPort); + if not(iec61850.IedServer_isRunning(self.__iedServer)) : + print("Starting server failed! Exit.\n") + iec61850.IedServer_destroy(self.__iedServer) + sys.exit(-1) + def run(self): + global running + while running: + time.sleep(0.1) + self.stop() + def stop(self): + iec61850.IedServer_stop(self.__iedServer) + iec61850.IedServer_destroy(self.__iedServer) + iec61850.IedModel_destroy(self.__model) + +def testClient(): + con = iec61850.IedConnection_create() + error = iec61850.IedConnection_connect(con, "localhost", tcpPort) + if (error == iec61850.IED_ERROR_OK): + # Accessing to SAV values + theVal = "testmodelSENSORS/TTMP1.TmpSv.instMag.f" + theValType = iec61850.IEC61850_FC_MX + temperatureValue = iec61850.IedConnection_readFloatValue(con, theVal, theValType) + assert(temperatureValue[1]==0) + newValue= temperatureValue[0]+10 + err = iec61850.IedConnection_writeFloatValue(con, theVal, theValType, newValue) + assert(err==21) + # Accessing to ASG values + theVal = "testmodelSENSORS/TTMP1.TmpSp.setMag.f" + theValType = iec61850.IEC61850_FC_SP + temperatureSetpoint = iec61850.IedConnection_readFloatValue(con, theVal, theValType) + print temperatureSetpoint + assert(temperatureValue[1]==0) + newValue= temperatureValue[0]+10 + err = iec61850.IedConnection_writeFloatValue(con, theVal, theValType, newValue) + assert(err==0) + temperatureSetpoint = iec61850.IedConnection_readFloatValue(con, theVal, theValType) + print temperatureSetpoint + assert(temperatureSetpoint[0]==newValue) + iec61850.IedConnection_close(con) + else: + print "Connection error" + sys.exit(-1) + iec61850.IedConnection_destroy(con) + print "client ok" +try: + srv=myIECServer() + srvThread = threading.Thread(target = srv.run) + srvThread.start() + testClient() + running = 0 + #signal.pause() +except: + running = 0 + print "Error :" + traceback.print_exc(file=sys.stdout) + sys.exit(-1) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 31a74e92..266c2799 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -213,14 +213,18 @@ endif() set_source_files_properties(${lib_asn1c_SRCS} PROPERTIES LANGUAGE C) +IF(MSVC) set_source_files_properties(${lib_common_SRCS} ${lib_windows_SRCS} PROPERTIES LANGUAGE CXX) +ENDIF() IF(WITH_WPCAP) +IF(MSVC) set_source_files_properties(${lib_goose_SRCS} PROPERTIES LANGUAGE CXX) set_source_files_properties(${lib_sv_SRCS} PROPERTIES LANGUAGE CXX) +ENDIF() ELSE() add_definitions(-DEXCLUDE_ETHERNET_WINDOWS) ENDIF() @@ -311,8 +315,10 @@ IF(UNIX) ) ENDIF (CONFIG_SYSTEM_HAS_CLOCK_GETTIME) ENDIF(UNIX) - - +IF(MINGW) + target_link_libraries(iec61850-shared ws2_32 iphlpapi) + target_link_libraries(iec61850 ws2_32 iphlpapi) +ENDIF(MINGW) iF(WITH_WPCAP) target_link_libraries(iec61850 ${CMAKE_CURRENT_SOURCE_DIR}/../third_party/winpcap/lib/wpcap.lib @@ -330,7 +336,6 @@ if(MSVC) ) endif() - ELSE(WITH_WPCAP) if(MSVC) set_target_properties(iec61850-shared PROPERTIES diff --git a/src/iec61850/client/client_control.c b/src/iec61850/client/client_control.c index ce2823e0..d638c0a3 100644 --- a/src/iec61850/client/client_control.c +++ b/src/iec61850/client/client_control.c @@ -848,12 +848,25 @@ ControlObjectClient_enableInterlockCheck(ControlObjectClient self) self->interlockCheck = true; } +void +ControlObjectClient_setInterlockCheck(ControlObjectClient self, bool value) +{ + self->interlockCheck = value; +} + + void ControlObjectClient_enableSynchroCheck(ControlObjectClient self) { self->synchroCheck = true; } +void +ControlObjectClient_setSynchroCheck(ControlObjectClient self, bool value) +{ + self->synchroCheck = value; +} + void ControlObjectClient_setLastApplError(ControlObjectClient self, LastApplError lastApplError) { diff --git a/src/iec61850/inc/iec61850_client.h b/src/iec61850/inc/iec61850_client.h index ed5c8911..b5ce446c 100644 --- a/src/iec61850/inc/iec61850_client.h +++ b/src/iec61850/inc/iec61850_client.h @@ -1510,12 +1510,36 @@ ControlObjectClient_setOrigin(ControlObjectClient self, const char* orIdent, int void ControlObjectClient_useConstantT(ControlObjectClient self, bool useConstantT); +/** + * \deprecated use ControlObjectClient_setInterlockCheck instead + */ void ControlObjectClient_enableInterlockCheck(ControlObjectClient self); +/** + * \deprecated use ControlObjectClient_setSynchroCheck instead + */ void ControlObjectClient_enableSynchroCheck(ControlObjectClient self); +/** + * \brief Set the value of the interlock check flag when a control command is sent + * + * \param self the ControlObjectClient instance + * \param value if true the server will perform a interlock check if supported + */ +void +ControlObjectClient_setInterlockCheck(ControlObjectClient self, bool value); + +/** + * \brief Set the value of the synchro check flag when a control command is sent + * + * \param self the ControlObjectClient instance + * \param value if true the server will perform a synchro check if supported + */ +void +ControlObjectClient_setSynchroCheck(ControlObjectClient self, bool value); + /** * \brief Private a callback handler that is invoked when a command termination message is received. diff --git a/src/mms/inc/mms_client_connection.h b/src/mms/inc/mms_client_connection.h index 9b895e65..5dfa60d8 100644 --- a/src/mms/inc/mms_client_connection.h +++ b/src/mms/inc/mms_client_connection.h @@ -1,7 +1,7 @@ /* * mms_client_connection.h * - * Copyright 2013 Michael Zillgith + * Copyright 2013-2016 Michael Zillgith * * This file is part of libIEC61850. * @@ -49,6 +49,7 @@ typedef struct sMmsConnectionParameters { int maxServOutstandingCalled; int dataStructureNestingLevel; int maxPduSize; /* local detail */ + uint8_t servicesSupported[11]; } MmsConnectionParameters; typedef struct { @@ -112,7 +113,7 @@ MmsConnection_setInformationReportHandler(MmsConnection self, MmsInformationRepo void* parameter); /** - * \brief Get the connection parameters for an MmsConnection instance + * \brief Get the ISO connection parameters for an MmsConnection instance * * \param self MmsConnection instance to operate on * \return params the to be used by this connection @@ -120,6 +121,15 @@ MmsConnection_setInformationReportHandler(MmsConnection self, MmsInformationRepo IsoConnectionParameters MmsConnection_getIsoConnectionParameters(MmsConnection self); +/** + * \brief Get the MMS specific connection parameters for an MmsConnection instance + * + * \param self MmsConnection instance to operate on + * \return params the to be used by this connection + */ +MmsConnectionParameters +MmsConnection_getMmsConnectionParameters(MmsConnection self); + /** * \brief User provided handler function that will be called if the connection to the server is lost * diff --git a/src/mms/iso_mms/client/mms_client_connection.c b/src/mms/iso_mms/client/mms_client_connection.c index dd4dbff7..9a50fe17 100644 --- a/src/mms/iso_mms/client/mms_client_connection.c +++ b/src/mms/iso_mms/client/mms_client_connection.c @@ -841,6 +841,12 @@ MmsConnection_getIsoConnectionParameters(MmsConnection self) return self->isoParameters; } +MmsConnectionParameters +MmsConnection_getMmsConnectionParameters(MmsConnection self) +{ + return self->parameters; +} + static void waitForConnectResponse(MmsConnection self) { diff --git a/src/mms/iso_mms/client/mms_client_initiate.c b/src/mms/iso_mms/client/mms_client_initiate.c index 6458ae21..e8c7d7d4 100644 --- a/src/mms/iso_mms/client/mms_client_initiate.c +++ b/src/mms/iso_mms/client/mms_client_initiate.c @@ -130,6 +130,11 @@ mmsClient_parseInitiateResponse(MmsConnection self) self->parameters.maxServOutstandingCalling = initiateResponse->negotiatedMaxServOutstandingCalling; + int i; + + for (i = 0; i < 11; i++) + self->parameters.servicesSupported[i] = initiateResponse->mmsInitResponseDetail.servicesSupportedCalled.buf[i]; + result = true; } else diff --git a/src/vs/libiec61850-wo-goose.def b/src/vs/libiec61850-wo-goose.def index 06594bb8..acec3203 100644 --- a/src/vs/libiec61850-wo-goose.def +++ b/src/vs/libiec61850-wo-goose.def @@ -526,4 +526,6 @@ EXPORTS MmsValue_setUtcTimeQuality MmsValue_decodeMmsData MmsValue_encodeMmsData + ControlObjectClient_setInterlockCheck + ControlObjectClient_setSynchroCheck diff --git a/src/vs/libiec61850.def b/src/vs/libiec61850.def index 73853692..d3322ec0 100644 --- a/src/vs/libiec61850.def +++ b/src/vs/libiec61850.def @@ -600,4 +600,7 @@ EXPORTS MmsValue_getUtcTimeQuality MmsValue_decodeMmsData MmsValue_encodeMmsData - + MmsConnection_getMmsConnectionParameters + IedServer_updateVisibleStringAttributeValue + ControlObjectClient_setInterlockCheck + ControlObjectClient_setSynchroCheck