diff --git a/pyiec61850/CMakeLists.txt b/pyiec61850/CMakeLists.txt index 159ef4bc..a42960f9 100644 --- a/pyiec61850/CMakeLists.txt +++ b/pyiec61850/CMakeLists.txt @@ -1,18 +1,22 @@ -# The SWIG functions/macros used in this module, swig_add_module and swig_add_library -# are not available in CMake versions earlier than 3.8 -# cmake_minimum_required(VERSION 3.8) +cmake_minimum_required(VERSION 3.12) + +cmake_policy(SET CMP0078 NEW) + +if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.14.0") + cmake_policy(SET CMP0086 NEW) +endif() find_package(SWIG REQUIRED) include(${SWIG_USE_FILE}) -find_package(PythonInterp ${BUILD_PYTHON_VERSION} REQUIRED) -find_package(PythonLibs ${PYTHON_VERSION_STRING} EXACT REQUIRED) +find_package(Python COMPONENTS Interpreter Development REQUIRED) -include_directories(${PYTHON_INCLUDE_PATH}) +include_directories(${Python_INCLUDE_DIRS}) include_directories(${CMAKE_CURRENT_SOURCE_DIR}) set(CMAKE_SWIG_FLAGS "") set_property(SOURCE iec61850.i PROPERTY CPLUSPLUS ON) +set_property(SOURCE iec61850.i PROPERTY SWIG_MODULE_NAME pyiec61850) if(WIN32) set(LIBS iec61850 ws2_32) @@ -20,25 +24,21 @@ else() set(LIBS iec61850-shared) endif() -if(${CMAKE_VERSION} VERSION_LESS 3.8) - swig_add_module(iec61850 python iec61850.i) -else() - swig_add_library(iec61850 - LANGUAGE python - SOURCES iec61850.i - ) -endif() +swig_add_library(pyiec61850 + LANGUAGE python + SOURCES iec61850.i +) -swig_link_libraries(iec61850 ${PYTHON_LIBRARIES} ${LIBS}) +swig_link_libraries(pyiec61850 ${LIBS}) # Finding python modules install path execute_process( - COMMAND ${PYTHON_EXECUTABLE} -c - "from distutils.sysconfig import get_python_lib; import sys; sys.stdout.write(get_python_lib())" + COMMAND ${Python_EXECUTABLE} -c + "from sysconfig import get_path; import sys; sys.stdout.write(get_path('platlib'))" OUTPUT_VARIABLE PYTHON_SITE_DIR ) -install(FILES ${CMAKE_CURRENT_BINARY_DIR}/iec61850.py DESTINATION ${PYTHON_SITE_DIR}) -install(TARGETS _iec61850 LIBRARY DESTINATION ${PYTHON_SITE_DIR}) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/pyiec61850.py DESTINATION ${PYTHON_SITE_DIR}) +install(TARGETS pyiec61850 LIBRARY DESTINATION ${PYTHON_SITE_DIR}) -add_test(test_pyiec61850 ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_LIST_DIR}/test_pyiec61850.py) +add_test(test_pyiec61850 ${Python_EXECUTABLE} ${CMAKE_CURRENT_LIST_DIR}/test_pyiec61850.py) diff --git a/pyiec61850/README.md b/pyiec61850/README.md new file mode 100644 index 00000000..b2398919 --- /dev/null +++ b/pyiec61850/README.md @@ -0,0 +1,95 @@ +# Python wrapper for libIEC61850 + +## Building +Before building you should install SWIG and Python +(see the '[Setup development environment on Linux](#setup-development-environment-on-linux-ubuntu)' section for help). + +To build the Python bindings you have to turn on the BUILD\_PYTHON\_BINDINGS flag in CMake from cmake-gui or in command line: +```sh +$ mkdir build && cd build +$ cmake -DBUILD_PYTHON_BINDINGS=ON .. +``` + +Then compile the library and install it: +```sh +$ make +$ sudo make install +``` +(Eventually, update your ld cache with: `sudo ldconfig`) + +CMake and SWIG will automatically detect your Python version and install the Python library in Python library directories. + +For running the integrated tests: +```sh +$ make test +``` + +pyiec61850 library is to be imported calling: +```python +import pyiec61850 as iec61850 +``` + +## Client tutorial + +The Python bindings works similarly to the basic C library. However there are some differences: + +* a specific function is to be called to cast variables from one type to another +* arguments passed by pointer are to be removed from arguments and append to the return list + + +For example to create a connection, call: +```python + con = iec61850.IedConnection_create() + error = iec61850.IedConnection_connect(con, "localhost", 102) + if (error == iec61850.IED_ERROR_OK): + # Do some work + iec61850.IedConnection_close(con) + iec61850.IedConnection_destroy(con) +``` + +To iterate over a list of logical devices, the code becomes: +```python +[deviceList, error] = iec61850.IedConnection_getLogicalDeviceList(con) +device = iec61850.LinkedList_getNext(deviceList) + +while device: + print("LD: %s" % iec61850.toCharP(device.data)) + [logicalNodes, error] = iec61850.IedConnection_getLogicalDeviceDirectory( + con, iec61850.toCharP(device.data)) + device = iec61850.LinkedList_getNext(device) +iec61850.LinkedList_destroy(deviceList) +``` + +Reading and writing operations can be performed using this syntax: +```python +[floatValue, error] = iec61850.IedConnection_readFloatValue(con, + "simpleIOGenericIO/GGIO1.AnIn1.mag.f", iec61850.IEC61850_FC_MX) +err = iec61850.IedConnection_writeFloatValue(con, + "simpleIOGenericIO/GGIO1.AnIn1.mag.f", iec61850.IEC61850_FC_MX, 10.0) +``` + +## Appendix + +## Setup development environment on Linux Ubuntu + +_[Tested on Ubuntu 20.04 LTS, Ubuntu 22.04 LTS, Ubuntu 23.10]_ + +Here the minimum required packages for compiling libiec61850 and the Python +wrapper (without TLS, SQlite, ...): + +```sh +$ sudo apt-get update +$ sudo apt-get install g++ cmake swig git python3 python3-all-dev +``` + +## Setup development environment on Linux Alpine + +_[Tested on Alpine 3.18]_ + +Here the minimum required packages for compiling libiec61850 and the Python +wrapper (without TLS, SQlite, ...): + +```sh +$ apk update +$ apk add git g++ swig make cmake python3 python3-dev linux-headers +``` diff --git a/pyiec61850/examples/dispServerStruct.py b/pyiec61850/examples/dispServerStruct.py index aea02b4f..2b49479b 100755 --- a/pyiec61850/examples/dispServerStruct.py +++ b/pyiec61850/examples/dispServerStruct.py @@ -1,6 +1,6 @@ #!/usr/bin/python import os,sys -import iec61850 +import pyiec61850 as iec61850 if __name__=="__main__": hostname = "localhost"; tcpPort = 102 diff --git a/pyiec61850/examples/rcbSubscriptionExample.py b/pyiec61850/examples/rcbSubscriptionExample.py index d4d05aa5..7d1243be 100644 --- a/pyiec61850/examples/rcbSubscriptionExample.py +++ b/pyiec61850/examples/rcbSubscriptionExample.py @@ -27,7 +27,7 @@ The user needs to: import time import sys -import iec61850 +import pyiec61850 as iec61850 def open_connection(ip_address, mms_port): diff --git a/pyiec61850/iec61850.i b/pyiec61850/iec61850.i index 254fec64..4cdbe0ea 100644 --- a/pyiec61850/iec61850.i +++ b/pyiec61850/iec61850.i @@ -1,5 +1,5 @@ /* File : iec61850.i */ -%module(directors="1") iec61850 +%module(directors="1") pyiec61850 %ignore ControlObjectClient_setTestMode(ControlObjectClient self); %ignore CDA_OperBoolean(ModelNode* parent, bool isTImeActivated); %ignore LogicalNode_hasBufferedReports(LogicalNode* node); diff --git a/pyiec61850/test_pyiec61850.py b/pyiec61850/test_pyiec61850.py index aceb3fdb..6f6d8cdd 100755 --- a/pyiec61850/test_pyiec61850.py +++ b/pyiec61850/test_pyiec61850.py @@ -6,7 +6,7 @@ import traceback import signal import sys sys.path.append('.') -import iec61850 +import pyiec61850 as iec61850 def signal_handler(signal, frame): global running running =0 diff --git a/pyiec61850/tutorial.md b/pyiec61850/tutorial.md deleted file mode 100644 index ce766cab..00000000 --- a/pyiec61850/tutorial.md +++ /dev/null @@ -1,50 +0,0 @@ -# Building -Before building you should install swig and python. -To build python bindings you have to turn on the BUILD\_PYTHON\_BINDINGS flag in CMake from cmake-gui or in command line: -```sh -$ cmake -DBUILD_PYTHON_BINDINGS=ON . -``` -Then compile the library and install it. CMake and swig will automatically detect your python version and install the python library in python library directories. - -pyiec61850 library is to be imported calling -```python -import iec61850 -``` -# Client tutorial - -The python bindings works similarly to the basic C library. However there are some differences: - -* a specific function is to be called to cast variables from one type to another -* arguments passed by pointer are to be removed from arguments and append to the return list - - -For example to create a connection, call: -```python - con = iec61850.IedConnection_create() - error = iec61850.IedConnection_connect(con, "localhost", 102) - if (error == iec61850.IED_ERROR_OK): - # Do some work - iec61850.IedConnection_close(con) - iec61850.IedConnection_destroy(con) -``` - -To iterate over a list of logical devices, the code becomes: -```python -[deviceList, error] = iec61850.IedConnection_getLogicalDeviceList(con) -device = iec61850.LinkedList_getNext(deviceList) - -while device: - print("LD: %s" % iec61850.toCharP(device.data)) - [logicalNodes, error] = iec61850.IedConnection_getLogicalDeviceDirectory( - con, iec61850.toCharP(device.data)) - device = iec61850.LinkedList_getNext(device) -iec61850.LinkedList_destroy(deviceList) -``` - -Reading and writing operations can be performed using this syntax: -```python -[floatValue, error] = iec61850.IedConnection_readFloatValue(con, - "simpleIOGenericIO/GGIO1.AnIn1.mag.f", iec61850.IEC61850_FC_MX) -err = iec61850.IedConnection_writeFloatValue(con, - "simpleIOGenericIO/GGIO1.AnIn1.mag.f", iec61850.IEC61850_FC_MX, 10.0) -```