Modules
TwinCAT and Simulink Interfacing
TwinCAT Connection
- class twinpy.twincat.connection.TwincatConnection(ams_net_id: str = '127.0.0.1.1.1', ams_net_port: int = 350, ip_address: Optional[str] = None)[source]
Bases:
pyads.ads.Connection
Extend default Connection object (typically named plc).
ADS connection with custom features.
Note that this version will connect on object creation, throwing an exception when it fails. pyads.Connection waits for .open() and will fail quietly.
- Parameters
ams_net_id – TwinCAT AMS address (default is localhost)
ams_net_port – ADS Port (default is 350)
ip_address – Target IP (automatically deduced from AMS address)
- Raises
pyads.ADSError – When connection failed
- get_parameter(name: Optional[str] = None, index_group: Optional[int] = None, index_offset: Optional[int] = None, symbol_type: Optional[Union[str, Type]] = None) twinpy.twincat.symbols.Parameter [source]
Get Parameter instance.
See
Parameter
.
- get_signal(name: Optional[str] = None, index_group: Optional[int] = None, index_offset: Optional[int] = None, symbol_type: Optional[Union[str, Type]] = None) twinpy.twincat.symbols.Signal [source]
Get Signal instance.
See
Signal
.
- get_symbol(name: Optional[str] = None, index_group: Optional[int] = None, index_offset: Optional[int] = None, plc_datatype: Optional[Union[Type[Union[_ctypes.Array, ctypes.c_bool, ctypes.c_ubyte, ctypes.c_uint, ctypes.c_int, ctypes.c_short, ctypes.c_double, ctypes.c_float, ctypes.c_byte, ctypes.c_char, ctypes.c_ushort, ctypes.c_long, ctypes.c_ulong]], str]] = None, comment: Optional[str] = None, auto_update: bool = False) pyads.symbol.AdsSymbol [source]
See
pyads.Connection.get_symbol()
.Return an AdsSymbol object.
- Parameters
name –
index_group –
index_offset –
plc_datatype –
comment –
auto_update –
- Returns
Simulink
Model to wrap around a Simulink model.
An object for a Simulink model is created first before a TwinCAT connection is made. We cannot get the original model structure from TwinCAT alone.
- twinpy.twincat.simulink.sanitize_name(name: str) str [source]
Reduce a string to characters which are allowed in a Python variable name.
This is needed because Simulink blocks can contain more characters than this. Python variables can only contain a-z, A-Z, 0-9 and ‘_’. Additionally, a variable cannot start with a digit, nor can it start with an underscore to prevent conflicts with semi-private properties.
SimulinkModel
- class twinpy.twincat.SimulinkModel(object_id: int, object_name: str, type_name: Optional[str] = None)[source]
Bases:
twinpy.twincat.simulink.SimulinkBlock
Wrapper for a compiled Simulink model in TwinCAT.
The model is built using the XML file, created when the model is compiled. Therefore the model can be loaded without TwinCAT running.
This model object is actually an extension of a SimulinkBlock. The complete model is basically just the root block.
By default the TWINCAT3DIR environment variable is used to locate the TwinCAT installation and look for the installed compiled XML files.
To work around this, you can pass either of the following to type_name:
A single name (TWINCAT3DIR will be used)
A path to a directory (default XML file name will be searched)
A path to the XML file, typically named like *_ModuleInfo.xml (no searching will be done)
- Parameters
object_id – ID of the TcCOM object in TwinCAt (the symbol group index)
object_name – Object Name (as shown in TwinCAT)
type_name – Type name (as shown in TwinCAT) (defaults to be the same as object_name).
- connect_to_twincat(connection: twinpy.twincat.connection.TwincatConnection)[source]
Connect model the one running in TwinCAT.
This will link all the symbols in the model to actual ADS symbols. And the remote model is compared to the local .xml file through the model checksum.
- Parameters
connection – Connection object to connect through
- static get_module_info(xmltree: xml.etree.ElementTree.Element) dict [source]
Get dictionary of module info fields.
The DefaultValues section is a list of names and values, this method creates a regular dict from it.
- get_plc() Optional[twinpy.twincat.connection.TwincatConnection] [source]
Return Connection (owned by model).
SimulinkBlock
- class twinpy.twincat.SimulinkBlock(xmltree: xml.etree.ElementTree.Element, model: twinpy.twincat.simulink.SimulinkModel)[source]
Bases:
object
A single Simulink Block (anything, e.g. constant, gain, a sub-system)
A SimulinkBlock can contain children, which are also SimulinkBlock objects. Using __getattr__ those subblocks (and their symbols) can be addressed directly:
model = … # Subblocks can be addressed smoothly: print(model.MySubsystem.MyConstant.Value)
Blocks contain parameters (Value) in the example above. When only a single parameter or signal is present, you can directly call it from the block itself:
print(model.MySubsystem.MyConstant.get()) # Short print(model.MySubsystem.MyConstant.Value.get()) # Same but longer
print(model.MySubsystem.MySineWave.Phase.get()) # Multiple parameters print(model.MySubsystem.MySineWave.Amplitude.get())
Create this block based on an XML tree
Sub-blocks are created too based on the remaining tree structure. This means the creation of blocks works recursively.
- Parameters
xmltree – A branch of a model XML tree (or the entire tree)
model – A reference back to the original model (the root of the structure)
- get_plc() Optional[twinpy.twincat.connection.TwincatConnection] [source]
Return Connection (owned by model).
- get_symbols_recursive() List[twinpy.twincat.symbols.Symbol] [source]
Recursively navigate subblocks and collect all parameters and signals.
- make_parameters(xmltree: xml.etree.ElementTree.Element) dict [source]
Find and create Parameters in the current block.
- make_signals(xmltree: xml.etree.ElementTree.Element) dict [source]
Find and create Signals in the current block.
- make_subblocks(xmltree: xml.etree.ElementTree.Element) Dict[str, twinpy.twincat.simulink.SimulinkBlock] [source]
Build sub-blocks (this makes the SimulinkBlocks recursive).
ADS Variables
Module with classes that wrap around TwinCAT symbols.
With ‘symbol’ we mean ADS variable.
Symbol
- class twinpy.twincat.Symbol(block: Optional[SimulinkBlock] = None, plc: pyads.Connection = None, name: Optional[str] = None, index_group: Optional[int] = None, index_offset: Optional[int] = None, symbol_type: Optional[Union[str, Type]] = None)[source]
Bases:
pyads.symbol.AdsSymbol
,abc.ABC
Base (abstract) class for a TwinCAT symbol.
Extends
pyads.AdsSymbol
- Introduced in pyads 3.3.1A symbol (or a Symbol sub-class) is typically owned by a block in a Simulink model. Each symbol contains a reference back to the block that owns it, which can be used to trace back to the model that owns that block. The symbol needs a reference to the connection object directly.
Symbols can be created from a block or manually (either based on name or by providing all information).
- Variables
value – The buffered value, not necessarily the latest value. The buffer is updated on each read, write and notification callback. It can be useful when the value needs to be applied multiple times, to avoid storing the value in your own variable.
See
pyads.Symbol
. If a block was passed, index_group and plc are automatically extracted from it and do not need to passed too.Additional arguments:
- Parameters
block – Block that owns this symbol (default: None)
- Raises
ValueError –
- add_device_notification(callback: Callable[[Any], None], attr: Optional[pyads.structs.NotificationAttrib] = None, user_handle: Optional[int] = None) Optional[Tuple[int, int]] [source]
Add on-change callback to symbol.
Superclass method is used, this version adds a wrapper for the callback to set the variable type. The user-defined callback will be called with the new symbol value as an argument.
- del_device_notification(handles: Tuple[int, int])[source]
Remove a single device notification by handles.
- read() Any [source]
Read the current value of this symbol.
The new read value is also saved in the buffer. Overridden from AdsSymbol, to work without an open Connection.
- set_connection(connection: Optional[pyads.ads.Connection])[source]
Update the connection reference.
- write(new_value: Optional[Any] = None) None [source]
Write a new value or the buffered value to the symbol.
When a new value was written, the buffer is updated. Overridden from AdsSymbol, to work without an open Connection
- :param new_value Value to be written to symbol (if None,
the buffered value is send instead)
Parameter
- class twinpy.twincat.Parameter(block: Optional[SimulinkBlock] = None, plc: pyads.Connection = None, name: Optional[str] = None, index_group: Optional[int] = None, index_offset: Optional[int] = None, symbol_type: Optional[Union[str, Type]] = None)[source]
Bases:
twinpy.twincat.symbols.Symbol
A TwinCAT parameter.
A constant setting, e.g. a gain block value, constant block value. For read/write access. Needs no changes, can use the default.
See
Symbol
.See
pyads.Symbol
. If a block was passed, index_group and plc are automatically extracted from it and do not need to passed too.Additional arguments:
- Parameters
block – Block that owns this symbol (default: None)
- Raises
ValueError –
Signal
- class twinpy.twincat.Signal(block: Optional[SimulinkBlock] = None, plc: pyads.Connection = None, name: Optional[str] = None, index_group: Optional[int] = None, index_offset: Optional[int] = None, symbol_type: Optional[Union[str, Type]] = None)[source]
Bases:
twinpy.twincat.symbols.Symbol
A TwinCAT signal.
Typically a port, e.g. a subsystem input or output
See
pyads.Symbol
. If a block was passed, index_group and plc are automatically extracted from it and do not need to passed too.Additional arguments:
- Parameters
block – Block that owns this symbol (default: None)
- Raises
ValueError –
GUI
TwinCAT UI Elements
TwinCAT widgets are Qt elements that are easily linked to an ADS symbol.
E.g. a label that shows an output value or an input box which changes a parameter.
The @pyqtSlot() is Qt decorator. In many cases it is not essential, but it’s good practice to add it anyway.
TcWidget
- class twinpy.ui.TcWidget(*args, **kwargs)[source]
Bases:
abc.ABC
Abstract class, to be multi-inherited together with a Qt item.
It is important to call this init() as late as possible from a subclass! The order should be:
Subclass specific stuff (e.g. number formatting)
Call to super().__init__(…) - Now the QWidget stuff has been made ready
QWidget related stuff
- Parameters
args –
kwargs – See list below - kwargs are passed along to connect_symbol too
- Kwargs
- symbol: twincat.symbols.Symbol to link to (i.e. to read from
and/or write to)
- format: Formatter symbol, e.g. ‘%.1f’ or ‘%d’ or callable
function with a single argument (‘%.3f’ by default) (ignored when not relevant)
- EVENT_NONE = 'none'
- EVENT_NOTIFICATION = 'notification'
- connect_symbol(new_symbol: Optional[twinpy.twincat.symbols.Symbol] = None, **kwargs)[source]
Connect a symbol (copy is left as property).
By default a device callback is created with an on-change event from TwinCAT. Old callbacks are deleted first. Pass None to only clear callbacks. The notification handles are stored locally. Extend (= override but call the parent first) this method to configure more of the widget, useful if e.g. widget callbacks depend on the symbol.
- Parameters
new_symbol – Symbol to link to (set None to only clear the previous)
kwargs – See list below - Keyword arguments are passed along as device notification settings too
- kwargs:
event_type
- format(value: Any) str [source]
“Use the stored formatting to created a formatted text.
In case the format specifier is a string and the new value is a list, element-wise string formatting will be concatenated automatically.
- abstract twincat_receive(value)[source]
Callback attached to the TwinCAT symbol.
Note: changing a state of a widget (e.g. checkbox being checked through setChecked(True)) will typically fire the on-change events again. So be careful to prevent an event loop when updating a widget based on a remote change: a change could result in a state change, which could result in a remote change, etc.
- Parameters
value – New remote value
TcLabel
- class twinpy.ui.TcLabel(*args, **kwargs)[source]
Bases:
PyQt5.QtWidgets.QLabel
,twinpy.ui.tc_widgets.TcWidget
Label that shows a value.
- Parameters
args –
kwargs – See
TcWidget
- twincat_receive(value)[source]
Callback attached to the TwinCAT symbol.
Note: changing a state of a widget (e.g. checkbox being checked through setChecked(True)) will typically fire the on-change events again. So be careful to prevent an event loop when updating a widget based on a remote change: a change could result in a state change, which could result in a remote change, etc.
- Parameters
value – New remote value
- value_format: Union[str, Callable[[Any], str]]
TcLineEdit
- class twinpy.ui.TcLineEdit(*args, **kwargs)[source]
Bases:
PyQt5.QtWidgets.QLineEdit
,twinpy.ui.tc_widgets.TcWidget
Readable and writable input box.
- Parameters
args –
kwargs – See
TcWidget
- twincat_receive(value) Any [source]
Callback attached to the TwinCAT symbol.
Note: changing a state of a widget (e.g. checkbox being checked through setChecked(True)) will typically fire the on-change events again. So be careful to prevent an event loop when updating a widget based on a remote change: a change could result in a state change, which could result in a remote change, etc.
- Parameters
value – New remote value
- value_format: Union[str, Callable[[Any], str]]
TcCheckBox
- class twinpy.ui.TcCheckBox(*args, **kwargs)[source]
Bases:
PyQt5.QtWidgets.QCheckBox
,twinpy.ui.tc_widgets.TcWidget
Checkbox to control a symbol.
Set either value to None to send nothing on that state. For the best results, use 1 and 0 for a boolean variable instead of True and False.
- Parameters
label (str) – Label of this radio button
args –
kwargs –
- Kwargs
value_checked: Value when checkbox becomes checked (default: 1)
value_unchecked: Value when checkbox becomes unchecked (default: 0)
See
TcWidget
- twincat_receive(value)[source]
Set checked state if the new value is equal to the is-checked value.
- value_format: Union[str, Callable[[Any], str]]
Base GUI
Module containing the Base GUI class.
This class should be extended to make your own GUI.
- class twinpy.ui.base_gui.BaseGUI(actuator: Optional[twinpy.twincat.simulink.SimulinkModel] = None, controller: Optional[twinpy.twincat.simulink.SimulinkModel] = None, **kwargs)[source]
Bases:
twinpy.ui.base_gui.TcMainWindow
Base TwinCAT GUI, specific for the WE2 actuators and controller model.
Extends this class if you want to use those models. For other models, using
twinpy.ui.TcMainWindow
might be more appropriate.- Parameters
actuator – The WE2_actuators model (or a derivative)
controller – The WE2_controller model (or a derivative)
kwargs –
- class twinpy.ui.base_gui.TcMainWindow(**kwargs)[source]
Bases:
PyQt5.QtWidgets.QMainWindow
Parent class for TwinCAT GUIs.
Extends QMainWindow. The resulting window is empty, but will have a destructor that neatly closes any TcWidgets first.
To make it easier to navigate the different elements, adhere to:
Create objects as late as possible
Save objects as property only if that is really necessary
Name objects by elements starting with the most general item (e.g. ‘layout_group_drives’)
To save space, create a Layout directly with its parent widget:
button_layout = QLayout(widget_parent)
# widget_parent.addLayout(button_layout) # < Not needed now
Widgets consist of layouts. Layouts contain widgets.
- find_tc_widgets() List[twinpy.ui.tc_widgets.TcWidget] [source]
Find all children of the TcWidget type (recursively).
Base Widgets
These widgets are specific implementations of TcWidgets.
They are not intended to be overridden again. They are separate classes mostly because their specific logic became significant.
TcErrorsLabel
- class twinpy.ui.TcErrorsLabel(*args, **kwargs)[source]
Bases:
twinpy.ui.tc_widgets.TcLabel
Extension of TcLabel for a list of joint errors.
This is separate class because the amount of logic got a little bit much.
When clicking on the widget, a new window pops up showing the decoded errors.
- Parameters
args –
kwargs –
- Kwargs
- format: Callback to format errors (default: show hexadecimal representation
of error values
popup: Whether or not to enable a detailed popup window (default: True)
See
TcLabel
- mousePressEvent(event: PyQt5.QtGui.QMouseEvent) None [source]
On clicking on the label.
QLabel does not have an on-click signal already.
- value_format: Union[str, Callable[[Any], str]]
Main - Example
Example of a script using a GUI.
You can run the BaseGUI itself (linked to the WE actuator model) by running
$ python -m twinpy
(I.e. by executing the module.) This provides a quick demo of how such a GUI can work, and a quick check to see everything is working on your system.
This script should not be used in your program! Instead, your application should have it’s own main script and create a GUI instance from there.
- class twinpy.__main__.GUI(actuator: Optional[twinpy.twincat.simulink.SimulinkModel] = None, controller: Optional[twinpy.twincat.simulink.SimulinkModel] = None, **kwargs)[source]
Bases:
twinpy.ui.base_gui.BaseGUI
Some extension of the BaseGUI.
Custom GUIs should extend the base class.
- Parameters
actuator – The WE2_actuators model (or a derivative)
controller – The WE2_controller model (or a derivative)
kwargs –
Module Info
TwinPy package.
- author
Robert Roos <robert.soor@gmail.com>
- license
MIT, see license file or https://opensource.org/licenses/MIT
- created on
2021-01-08 16:13:00