Introduction
The eta_utility.connectors module is meant to provide a standardized interface for multiple different protocols which are used in factory operations or for the optimization of factory operations. Two important protocols which we encounter regularly are Modbus TCP and OPC UA. In addition to this we have also created connectors for additional API which we work with.
The eta_utility connector format has the advantage that it can be used for many different kinds of protocols
and APIs, with the limitation that some of them do not support all functions (for example, specific APIs/protocols
may not provide write access). Each connection can contain multiple Nodes (see below). These are used as the
default data points when reading data from the connection. Read data will be returned in a
pandas.DataFrame
with the node names as column names.
The connectors module also provides subscription handlers which take data read from connections in regular intervals and for example store it in files or in memory for later access. These subscription handlers can handle multiple different connections (with different protocols) at the same time.
The LiveConnect class is specifically designed to combine the functionality from the eta_x and connectors modules. It can establish connections and provides an interface equivalent to the classes in the simulators module. This allows easy substitution of simulation models with actual connections to real machines (or the other way). When trying to deploy a model into operation this substitution can be very useful.
The connectors are based on the concept of Nodes to which we establish connections. A Node object is a unique description of a specific data point and includes all information required to establish a connection and read data from the specified data point. Each data point has its own node, not just each device (or connection) that we are connecting to. Therefore, Nodes are the easiest way to instantiate connections, however they can be a bit unwieldy to work with when trying to read many different data points from the same device.
See Connection instantiation for more information on how to create connections.
Nodes
Each Node object uniquely identifies a specific data point. All Node objects have some information in common. This information identifies the device which the data point belongs to and can also contain information required for authentication with the device. Depending on the protocol the Node object contains additional information to correctly identify the data points.
The URL may contain the username and password (schema://username:password@hostname:port/path
). This is handled
automatically by the connectors and the username and password will be removed before creating a connection.
The Node class should always be used to instantiate nodes. The type of the node can be specified using the protocol parameter.
- class eta_utility.connectors.Node(name: str, url: str, protocol: str, *args: Any, **kwargs: Any)[source]
The node objects represents a single variable. Valid keyword arguments depend on the protocol.
The following classes are there to document the required parameters for each type of node.
Note
Always use the Node class to instantiate nodes! (not its subclasses)
- class eta_utility.connectors.node.NodeLocal(name: str, url: str, protocol: str, *args: Any, **kwargs: Any)[source]
Local Node (no specific protocol), useful for example to manually provide data to subscription handlers.
- name: str
Name for the node.
- url: str
URL of the connection.
- url_parsed: ParseResult
Parse result object of the URL (in case more post-processing is required).
- dtype: Callable | None
Data type of the node (for value conversion). Note that strings will be interpreted as utf-8 encoded. If you do not want this behaviour, use ‘bytes’.
- class eta_utility.connectors.node.NodeModbus(name: str, url: str, protocol: str, *args: Any, **kwargs: Any)[source]
Node for the Modbus protocol.
- mb_register: str
Modbus Register name. One of input, discrete_input, coils and holding. Note that only coils and holding can be written to.
- mb_channel: int
Modbus Channel (Address of the value)
- mb_bit_length: int
Length of the value in bits (default 32). This determines, how much data is read from the server. The value must be a multiple of 16.
- mb_byteorder: str
Byteorder of values returned by modbus
- mb_wordorder: str
Wordorder of values returned by modbus
- name: str
Name for the node.
- url: str
URL of the connection.
- url_parsed: ParseResult
Parse result object of the URL (in case more post-processing is required).
- dtype: Callable | None
Data type of the node (for value conversion). Note that strings will be interpreted as utf-8 encoded. If you do not want this behaviour, use ‘bytes’.
- class eta_utility.connectors.node.NodeOpcUa(name: str, url: str, protocol: str, *args: Any, **kwargs: Any)[source]
Node for the OPC UA protocol.
- opc_id_type: str
Type of the OPC UA Node ID Specification.
- opc_name: str
Name of the OPC UA Node.
- opc_path: list[NodeOpcUa]
Path to the OPC UA node in list representation. Nodes in this list can be used to access any parent objects.
- evolve(**kwargs: Any) Node [source]
Returns a new node instance by copying the current node and changing only specified keyword arguments.
This allows for seamless node instantiation with only a few changes.
Adjusted attributes handling according to OpcUa node instantiation logic as in ‘__attrs_post_init__’.
- Parameters:
kwargs – Keyword arguments to change.
- Returns:
New instance of the node.
- name: str
Name for the node.
- url: str
URL of the connection.
- url_parsed: ParseResult
Parse result object of the URL (in case more post-processing is required).
- dtype: Callable | None
Data type of the node (for value conversion). Note that strings will be interpreted as utf-8 encoded. If you do not want this behaviour, use ‘bytes’.
- class eta_utility.connectors.node.NodeEnEffCo(name: str, url: str, protocol: str, *args: Any, **kwargs: Any)[source]
Node for the EnEffCo API.
- eneffco_code: str
EnEffCo datapoint code / ID.
- name: str
Name for the node.
- url: str
URL of the connection.
- url_parsed: ParseResult
Parse result object of the URL (in case more post-processing is required).
- dtype: Callable | None
Data type of the node (for value conversion). Note that strings will be interpreted as utf-8 encoded. If you do not want this behaviour, use ‘bytes’.
- class eta_utility.connectors.node.NodeEntsoE(name: str, url: str, protocol: str, *args: Any, **kwargs: Any)[source]
Node for the EntsoE API (see ENTSO-E Transparency Platform API).
Endpoint
Description
ActualGenerationPerType
Actual Generation Per Energy Type
Price
Price day ahead
Currently, there is only two endpoints available, due to the parameter managing required by the API documentation. The other possible endpoints are listed in
eta_utility.connectors.entso_e._ConnectionConfiguration._doc_types
Bidding Zone
Description
DEU-LUX
Deutschland-Luxemburg
The other possible bidding zones are listed in
eta_utility.connectors.entso_e._ConnectionConfiguration._bidding_zones
- endpoint: str
REST endpoint.
- bidding_zone: str
Bidding zone.
- name: str
Name for the node.
- url: str
URL of the connection.
- url_parsed: ParseResult
Parse result object of the URL (in case more post-processing is required).
- dtype: Callable | None
Data type of the node (for value conversion). Note that strings will be interpreted as utf-8 encoded. If you do not want this behaviour, use ‘bytes’.
- class eta_utility.connectors.node.NodeWetterdienstObservation(name: str, url: str, protocol: str, *args: Any, **kwargs: Any)[source]
Node for the Wetterdienst API to get weather observations. For more information see: https://wetterdienst.readthedocs.io/en/latest/data/coverage/dwd/observation.html
- interval: str
Redeclare interval attribute, but don’t allow it to be optional
- parameter: str
Parameter to read from wetterdienst (e.g HUMIDITY or TEMPERATURE_AIR_200)
- name: str
Name for the node.
- url: str
URL of the connection.
- url_parsed: ParseResult
Parse result object of the URL (in case more post-processing is required).
- dtype: Callable | None
Data type of the node (for value conversion). Note that strings will be interpreted as utf-8 encoded. If you do not want this behaviour, use ‘bytes’.
- class eta_utility.connectors.node.NodeWetterdienstPrediction(name: str, url: str, protocol: str, *args: Any, **kwargs: Any)[source]
Node for the Wetterdienst API to get weather predictions. For more information see: https://wetterdienst.readthedocs.io/en/latest/data/coverage/dwd/mosmix.html
- mosmix_type: str
Type of the MOSMIX prediction. Either ‘SMALL’ or ‘LARGE’
- parameter: str
Parameter to read from wetterdienst (e.g HUMIDITY or TEMPERATURE_AIR_200)
- name: str
Name for the node.
- url: str
URL of the connection.
- url_parsed: ParseResult
Parse result object of the URL (in case more post-processing is required).
- dtype: Callable | None
Data type of the node (for value conversion). Note that strings will be interpreted as utf-8 encoded. If you do not want this behaviour, use ‘bytes’.
- class eta_utility.connectors.node.NodeEmonio(name: str, url: str, protocol: str, *args: Any, **kwargs: Any)[source]
Node for the emonio. The parameter to read is specified by the name of the node. Available parameters are defined in the parameter_map class attribute. Additionally, the phase of the parameter can be specified, with ‘a’, ‘b’, ‘c’ or ‘abc’.
https://wiki.emonio.de/de/Emonio_P3
- address: int
Modbus address of the parameter to read
- phase: str
Phase of the parameter (a, b, c). If not set, all phases are read
- evolve(**kwargs: Any) Node
Returns a new node instance by copying the current node and changing only specified keyword arguments.
This allows for seamless node instantiation with only a few changes.
- Parameters:
kwargs – Keyword arguments to change.
- Returns:
New instance of the node.
- name: str
Name for the node.
- url: str
URL of the connection.
- url_parsed: ParseResult
Parse result object of the URL (in case more post-processing is required).
- dtype: Callable | None
Data type of the node (for value conversion). Note that strings will be interpreted as utf-8 encoded. If you do not want this behaviour, use ‘bytes’.
- class eta_utility.connectors.node.NodeForecastSolar(name: str, url: str, protocol: str, *args: Any, **kwargs: Any)[source]
Node for using the Forecast.Solar API.
Mandatory parameters are:
The location of the forecast solar plane(s): latitude, longitude,
Plane parameters: declination, azimuth and kwp.
Additionally api_key must be set for endpoints other than ‘estimate’, multiple planes or if requests capacity is exceeded.
For multiple planes, the parameters shall be passed as lists of the same length (e.g. [0, 30], [180, 180], [5, 5]).
- endpoint: str
Endpoint in (estimate, history, clearsky), defaults to estimate; string
- data: str | None
What data to query, i.e. only watts, watt hours, watt hours per period or watt hours per day; string
- latitude: int
Latitude of plane location, -90 (south) … 90 (north); handled with a precision of 0.0001 or abt. 10 m
- longitude: int
Longitude of plane location, -180 (west) … 180 (east); handled with a precision of 0.0001 or abt. 10 m
- declination: int | list[int]
Plane declination, 0 (horizontal) … 90 (vertical) - always in relation to earth’s surface; integer
- azimuth: int | list[int]
Plane azimuth, -180 … 180 (-180 = north, -90 = east, 0 = south, 90 = west, 180 = north); integer
- no_sun: int | None
Format of timestamps in the response, see API doc for values; string Forecast for full day or only sunrise to sunset, 0|1 (API defaults to 0); int
- horizon: int | list[int] | None
Horizon information; string, (comma-separated list of numerics) See API doc
- name: str
Name for the node.
- url: str
URL of the connection.
- url_parsed: ParseResult
Parse result object of the URL (in case more post-processing is required).
- dtype: Callable | None
Data type of the node (for value conversion). Note that strings will be interpreted as utf-8 encoded. If you do not want this behaviour, use ‘bytes’.
Node Typing
Eta-utility provides a generic type Nodes[N]
which can be used to type a sequence or set of nodes. This is useful when
working with multiple nodes and you want to ensure that all nodes are of the same type.
from eta_utility.connectors import Node, NodeModbus, NodeOpcUa from eta_utility.type_hints import Nodes # Example of typing nodes modbus_node: NodeModbus = Node("modbus://localhost:502/0/0", "modbus", ...) opcua_node: NodeOpcUa = Node("opcua://localhost:4840/0/0", "opcua", ...) nodes: Nodes[NodeModbus] = [modbus_node] # This will raise a type error nodes: Nodes[NodeModbus] = [modbus_node, opcua_node] # Use *Node* to allow different types of nodes nodes: Nodes[Node] = [modbus_node, opcua_node] # or explicitly list the types nodes: Nodes[NodeModbus | NodeOpcUa] = [modbus_node, opcua_node]
Connection Instantiation
Connection.from_node()
and Connection.from_nodes()
.from_node(s)
is useful if you already have created some node(s) and would like to create connection(s)
from them._from_node()
method, since the necessary/accepted keywords might differ. To create connections, a password
and a username are often required. For setting these the following prioritization applies:If a password is given in the node, take it.
If there is no password there, take as “default” a password from the arguments.
If there is neither, the username and password are empty.
Creating one or more Connections from node(s)
If you have one or multiple nodes, use from_nodes
. Create all of the Node
objects first and pass them in a list.
from_nodes
then returns a dictionary of connections and automatically assigns the nodes to their correct connection.
It requires less duplicate information than direct instantiation.
- Connection.from_nodes(nodes: Nodes[Node], **kwargs: Any) dict[str, Connection[Node]] [source]
- Returns a dictionary of connections for nodes with the same url netloc.
This method handles different Connections, unlike from_node(). The keys of the dictionary are the netlocs of the nodes and each connection contains the nodes with the same netloc. (Uses from_node to initialize connections from nodes.)
- Parameters:
nodes – List of nodes to initialize from.
kwargs – Other arguments are ignored.
- Returns:
Dictionary of Connection objects with the netloc as key.
Create one Connection
If you have one or more Node
objects for the same hostname/protocol and just want to create one connection, you should use the from_node
method of
the Connection
class.
- Connection.from_node(node: Nodes[Node], usr: str | None = None, pwd: str | None = None, **kwargs: Any) Connection [source]
- Return a single connection for nodes with the same url netloc.
Initialize the connection object from a node object. When a list of Node objects is provided, from_node checks if all nodes match the same connection; it throws an error if they don’t. A node matches a connection if it has the same url netloc.
- Parameters:
node – Node to initialize from.
kwargs – Other arguments are ignored.
- Raises:
ValueError: if not all nodes match the same connection.
- Returns:
Connection object
Direct Instantiation (not recommended)
Node
objects to tell the connection where (from which data points) to read data from or write data to.from_node
method is recommended.# Example for a Modbus connection
from eta_utility.connectors import ModbusConnection
url = "modbus://192.168.178.123:502"
username = ("admin",)
password = "admin"
connection = ModbusConnection(url=url, usr=username, pwd=password)
Using from_ids
The from_ids()
method is helpful if you do not require access to the nodes and just want to quickly create a single
connection.
Note
This is not available for all connectors, since the concept of IDs does not apply universally. An example is shown here. Refer to the API documentation of the connector you would like to use to see if the method exists and which parameters are required.
- EnEffCoConnection.from_ids(ids: Sequence[str], url: str, usr: str, pwd: str, api_token: str) EnEffCoConnection [source]
Initialize the connection object from an EnEffCo protocol through the node IDs
- Parameters:
ids – Identification of the Node.
url – URL for EnEffco connection.
usr – Username for EnEffCo login.
pwd – Password for EnEffCo login.
api_token – Token for API authentication.
- Returns:
EnEffCoConnection object.