diff --git a/conf.py b/conf.py index 6e6da2cb4e0765b09966210695a9fd38f0f69d7f..082f84bb354d9c1658356ca495132c5505094b18 100644 --- a/conf.py +++ b/conf.py @@ -14,7 +14,7 @@ author = 'Martin Teichmann' release = "0.1" version = "0.1.0" -language = None +language = "en" exclude_patterns = ['_build'] pygments_style = 'sphinx' todo_include_todos = False diff --git a/ebpfcat/ebpfcat.py b/ebpfcat/ebpfcat.py index dad8462e5c14022212ddc0a9b163f2c47f375429..5d03c47788b488324332a210af92047d540c41f5 100644 --- a/ebpfcat/ebpfcat.py +++ b/ebpfcat/ebpfcat.py @@ -69,8 +69,8 @@ class ProcessDesc: in the terminal's documentation :param subindex: the subindex, also found in the documentation :param size: usually the size is taken from the PDO mapping. A - different size as in a `struct` definition may be given here, - or the number of a bit for a bit field. + different size as in a :mod:`python:struct` definition may be + given here, or the number of a bit for a bit field. """ def __init__(self, index, subindex, size=None): self.index = index @@ -150,8 +150,11 @@ class Struct: """Define repetitive structures in a PDO Some terminals, especially multi-channel terminals, - have repetitive structures in their PDO. Use this to group - them. + have repetitive structures in their PDO. Inherit from this + class to create a structure for them. Each instance + will then define one channel. It takes one parameter, which + is the offset in the CoE address space from the template + structure to the one of the channel. """ device = None @@ -241,6 +244,12 @@ class Device(SubProgram): class EBPFTerminal(Terminal): + """This is the base class for all supported terminal types + + inheriting classes should define a ``compatibility`` class variable + which is a set of tuples, each of which is a pair of Ethercat vendor and + product id of all supported terminal types. + """ compatibility = None position_offset = {SyncManager.OUT: 0, SyncManager.IN: 0} use_fmmu = True diff --git a/ebpfcat/ethercat.py b/ebpfcat/ethercat.py index 1eb23e4bdae976d02c05ed33622cd83a8915ed0c..c7d54aa8e917a11fbec4379b7c314ac5de09fd17 100644 --- a/ebpfcat/ethercat.py +++ b/ebpfcat/ethercat.py @@ -445,7 +445,7 @@ class EtherCat(Protocol): class Terminal: - """Represent one terminal ("slave") in the loop""" + """Represent one terminal (*SubDevice* or *slave*) in the loop""" def __init__(self, ethercat): self.ec = ethercat @@ -733,6 +733,11 @@ class Terminal: return b"".join(ret) async def sdo_read(self, index, subindex=None): + """read a single SDO entry + + given an adress for a CoE entry like 6020:12, you may read + the value like ``await master.sdo_read(0x6020, 0x12)``. + """ async with self.mbx_lock: await self.mbx_send( MBXType.COE, "HBHB4x", CoECmd.SDOREQ.value << 12, @@ -785,6 +790,13 @@ class Terminal: return b"".join(ret) async def sdo_write(self, data, index, subindex=None): + """write a single SDO entry + + given a CoE address like 1200:2, one may write the value as + in ``await master.sdo_write(b'abc', 0x1200, 0x2)``. Note that the + data needs to already be a binary string matching the binary type of + the parameter. + """ if len(data) <= 4 and subindex is not None: async with self.mbx_lock: await self.mbx_send( diff --git a/ebpfcat/ethercat.rst b/ebpfcat/ethercat.rst index 45be9caddab435da04a8b7794566967e8caac210..9957beffb0d5b71278c262e2aff11d833e86b20a 100644 --- a/ebpfcat/ethercat.rst +++ b/ebpfcat/ethercat.rst @@ -89,7 +89,7 @@ Three methods of control The communication with the terminals can happen in three different ways: -- out-of-order: the communication happens ad-hoc whenever needed. This is +- asynchronous: the communication happens ad-hoc whenever needed. This is done during initialization and for reading and writing configuration data, like CoE. - slow: the data is sent, received and processed via Python. This is good @@ -98,6 +98,74 @@ The communication with the terminals can happen in three different ways: Kernel. Only very limited operations can be done, but the loop cycle frequency exceeds 10 kHz. +Adding new terminals +-------------------- + +The elements of an EtherCat loop were used to be called *slaves*, but nowadays +are referred to as *SubDevices*. As in a typical installation most of them are +simply terminals, we call them such. + +Everything in a terminal is controlled by reading or writing parameters in the +CoE address space. These addresses are a pair of a 16 bit and an 8 bit number, +usually seperated by a colon, as in 6010:13. Most terminals allow these +parameters to be set asynchronously. Some of the parameters may be read or +written synchronously, so with every communication cycle. + +The meaning of all these parameters can usually be found in the documentation of the terminal. Additionally, terminals often have a self-description, which can be read with the command line tool `ec-info`:: + + $ ec-info eth0 --terminal -1 --sdo + +this reads the first (-1th) terminal's self description (``--sdo``). Add a +``--value`` to also get the current values of the parameters. This prints out +all known self descriptions of CoE parameters. + +Once we know the meaning of parameters, they may be read or written +asynchronously using :meth:`~ebpfcat.ethercat.Terminal.sdo_read` and +:meth:`~ebpfcat.ethercat.Terminal.sdo_write`. + +For synchronous data access, a class needs to be defined that defines the +parameters one want to use synchronously. The parameters available for +synchronous operations can be found with the ``--pdo`` parameter of the +``ec-info`` command. The class should inherit from +:class:`~ebpfcat.ebpfcat.EBPFTerminal` and define a set of tuples called +``comptibility``. The tuples should be the pairs of Ethercat product and vendor +id for all terminals supported by this class. Those can be found out with the +``--ids`` parameter of the ``ec-info`` command. + +Within the class, the synchronous parameters are defined via +:class:`~ebpfcat.ebpfcat.ProcessDesc`. This descriptor takes the two parts of +the CoE address as parameters, plus an optional size parameter. This is usually +determined automatically, but this sometimes fails, in which case it may either +be defined via a format string like in the :mod:`python:struct` module, or it +is an integer which is then a reference to the position of the bit in the +parameter to define a boolean flag. + +For terminals which have several equivalent channels, one can define a +structure by inheriting from :class:`~ebpfcat.ebpfcat.Struct`. Within this +class one defines the first set of parameters the same way one would do it +without. Once the class is defined, it can be instantiated in the terminal +class with a single argument which defines the offset in the CoE address space +for this structure. As an example, if on a two-channel terminal the first +channel has an address of ``0x6000:12`` and the following two ``0x6010:12`` and +``0x6020:12``, one would instantiate three structs with arguments ``0``, +``0x10`` and ``0x20``. + +A complete example of a four channel terminal looks as follows:: + + class EL3164(EBPFTerminal): + compatibility = {(2, 0x0c5c3052)} + + class Channel(Struct): + attrs = ProcessDesc(0x6000, 1, 'H') # this is 2 bytes ('H') + value = ProcessDesc(0x6000, 0x11) + factor = 10/32767 # add bonus information as desired + offset = 0 + + channel1 = Channel(0) # adress 0x6000 + channel2 = Channel(0x10) # address 0x6010 + channel3 = Channel(0x20) + channel4 = Channel(0x30) + Reference Documentation ----------------------- @@ -107,3 +175,6 @@ Reference Documentation .. automodule:: ebpfcat.ethercat :members: + +.. automodule:: ebpfcat.ebpfcat + :members: diff --git a/ebpfcat/terminals.py b/ebpfcat/terminals.py index 512d137baad332290a90be1ccea0f67011165e5f..b30dec338d2b274a2e6903b6c96b1c4d617e45db 100644 --- a/ebpfcat/terminals.py +++ b/ebpfcat/terminals.py @@ -98,6 +98,8 @@ class EL4104(EBPFTerminal): class EL3164(EBPFTerminal): + compatibility = {(2, 0x0c5c3052)} + class Channel(Struct): attrs = ProcessDesc(0x6000, 1, 'H') value = ProcessDesc(0x6000, 0x11)