PYTHON – User programmable block in Python

Block SymbolLicensing group: REXLANG
PIC

Function Description

The standard function blocks of the REXYGEN system cover the most typical needs in control applications. But there still exist situations where it is necessary (or more convenient) to implement an user-defined function. The REXLANG block covers this case for application where real-time behavior is strictly demanded. In the rest of the cases the PYTHON block can be used.

The PYTHON block implements an user-defined algorithm written in a Python scripting language and in comparison to the REXLANG block it provides a better user experience in the stage of development of the algorithm and can extend the feature set of REXYGEN system through various 3rd party libraries that are available in the Python environment.

Warning: the PYTHON block is intended for prototyping and experiments so please consider using the block in your application very carefully. It is an experimental block and always will be. There are many corner cases that may lead to unexpected behavior or even block the runtime. Packages may be poorly written or provide incorrect finalization and reinitialization which may even lead to a crash. Only a very limited support is provided for this block.

Scripting language
The scripting language is a standard Python v.3 language (see [13]). Every block references a script written in a *.py source file. The file can optionally contain functions with a reserved name that are then executed by REXYGEN. The main() function is executed periodically during runtime. Alongside the main() function the init() function is executed once at startup and after reset of the block, the exit() function is executed once when the control algorithm is stopped and before reset of the block and the parchange() function is executed on parameters change in REXYGEN.

Scripts on the target device
Standard python interpreter can load modules/scripts from various locations on the target device. The PYTHON block can reference any python script available for the standard interpreter and in addition the block can access scripts located in a directory /rex/scripts/python. User scripts can be directly uploaded to this directory or if the parameter embedded is set to on the script referenced by the block gets embedded in the REXYGEN configuration during compilation process and will be temporarily stored in the directory /rex/scripts/python/embedded during initialization of the block once the configuration is downloaded and executed on the target device.

Data exchange API
For the purpose of data exchange between a Python interpreter and REXYGEN system a module PyRexExt was developed as a native extension to the interpreter. The module contains an object REX that handles the data exchange operations. Use the following snippet at the start of the script to setup the data exchange API.

from PyRexExt import REX

I/O objects

REX.u0 - REX.u15

– objects representing block inputs in Python environment

REX.p0 - REX.p15

– objects representing block parameters in Python environment

REX.y0 - REX.y15

– objects representing block outputs in Python environment

Access to values
All I/O objects contain a property v. Reading of the property v performs a conversion from REXYGEN data types to Python data types. The value then can be stored in variables and used in the block algorithm. A REXYGEN array type converts into a list of values in case of one-dimensional array or into a list of lists in case of multidimensional array.

Example of reading a value of the block input:

x = REX.u0.v

Writing to the property v, on the other hand, performs a conversion from Python data types to REXYGEN data types and exports the value to the corresponding block output/parameter.

Example of writing a value to the block output:

REX.y0.v = 5

Arrays
Input and output objects have a readonly property size. It is a tuple with number of rows and columns. Arrays can be manipulated through property v but direct conversions between REXYGEN arrays and Python lists are not very memory efficient. However, input and output objects support indexing operator [] that restricts the conversion overhead only on the specified item.

Example of reading a value of the block input for one-dimensional array:

x = REX.u0[0]

Example of writing a value to the block output for multidimensional array:

REX.u0[1, 3] = 5

External items
The object REX contains a method Item that returns a handle to an external REXYGEN item based on a connection string specified in a parameter of the method.

Example of creating a handle to an external item and setting its value:

cns = REX.Item("myproject_task.CNS:scv")  
cns.v = "abc"

Tracing
The object REX contains methods Trace, TraceError, TraceWarning, TraceVerbose and TraceInfo can be used to write messages into REXYGEN system log. Every message has a stacktrace attached.

Example of logging a message:

REX.Trace("abc")

Additional features
REX.RexDataPath – RexDataPath is a string constant that contains a path to a data folder of the REX system on the given platform. That can come handy for writing a platform independent code that requires access to the file system using absolute paths.

Inputs

HLD

Hold – the block code is not executed if the input is set to on

Bool

RESET

Rising edge resets the block. The block gets initialized again (all global variables are cleared and the init() function is called).

Bool

u0..u15

Input signals which are accessible from the script.

Any

Outputs

iE

Runtime error code.

Error

0 ....

No error occurred, the whole main() function was executed (also the init() function).

xxx ..

Error code of the REXYGEN system, see Appendix C

iRes

Execution result code.

Long (I32)

y0..y15

Output signals which can be set from within the script.

Any

Parameters

srcname

Source file name  program.py

String

embedded

Embedding of the script  on

Bool

p0..p15

Parameters which are accessible from the script.

Any

Data types definition
For data exchange between REXYGEN system and Python environment the data types of block inputs signals u0..u15, outputs signals y0..y15 and parameters p0..p15 must be explicitly specified. For that purpose a configuration file must be created for every python script with the same name plus a suffix .cfg (e.g. program.py.cfg). If the file is missing during the compilation process it is created with all signal types set to double. It is not expected this file to be edited directly. User should use a build-in editor specific to the PYTHON block instead. Available types for inputs outputs and parameters are boolean, uint8, int16, uint16, int32, uint32, int64, float, double, string and in addition the inputs and outputs support array, numpy and image data types.

For types numpy and image the numpy python package must be installed on the target device. Inputs of the type numpy expect the connected signal to be of the type array that gets converted in the runtime to a native numpy representation. Inputs of the type image expects the connected signal to be of the type image data type from the RexVision module that also gets converted in the runtime to a native numpy representation and can therefore be directly used with the OpenCV Python package.

Outputs of the type numpy expect to be set in the script from a numpy array object that gets converted to a regular array. Outputs of the type image expect to be set in the script from a numpy array object that gets converted to image data type defined in the RexVision module.

Example data types definition
The following example shows a shortened JSON file describing the data types of the program inputs and outputs.

{  
    "types": {  
        "in": [  
            {  
                "idx": 0,  
                "type": "double"  
            },  
             . . .  
            {  
                "idx": 15,  
                "type": "double"  
            }  
        ],  
        "param": [  
            {  
                "idx": 0,  
                "type": "double"  
            },  
             . . .  
            {  
                "idx": 15,  
                "type": "double"  
            }  
        ],  
        "out": [  
            {  
                "idx": 0,  
                "type": "double"  
            },  
             . . .  
            {  
                "idx": 15,  
                "type": "double"  
            }  
        ]  
    }  
}

Example Python script
The following example shows a simple code to sum two input signals and also sum two user-defined parameters.

from PyRexExt import REX  
 
def main():  
    REX.y0.v = REX.u0.v + REX.u1.v  
    REX.y1.v = REX.p0.v + REX.p1.v  
    return

Installation - Debian
The Python environment should be correctly installed and configured just by installing the PythonBlk_T debian package. To install the package with optional numpy and OpenCV packages execute these commands from the terminal.

sudo apt install rex-pythonblkt  
sudo apt install python3-numpy python3-opencv

Installation - Windows
To install the correct version of Python the recommended way is to download and install the 64-bit version from official repository (https://www.python.org/ftp/python/3.9.6/). During the installation make sure to enable installation of the pip program and adding of the python binaries to the system variable PATH.

To install numpy and OpenCV as optional dependencies execute following commands from the command line.

pip install numpy  
pip install opencv-python

Limitations
Due to the limitations of the standard Python interpreter implementation it is not recommended to use multiple PYTHON block instances at the different levels of executive. Doing so can lead to an unpredictable behavior and instability of the RexCore program.

2024 © REX Controls s.r.o., www.rexygen.com