The EtherCAT master

Getting started

Ethercat terminals are usually connected in a loop with the EtherCAT master, via an ethernet interface. So we create a master object, and connect to that interface an scan the loop. This takes time, so in a good asyncronous fashion we need to use await, which can only be done in an async function:

import asyncio
from ebpfcat.ebpfcat import FastEtherCat

async def main():
    master = FastEtherCat("eth0")
    await master.connect()
    print('Number of terminals:', await master.count())

asyncio.run(main())

Next we create an object for each terminal that we want to use. As an example, take some Beckhoff output terminal:

from ebpfcat.terminals import EL4104

out = EL4104(master)

This terminal needs to be initialized. The initialization method takes two arguments, the relative position in the loop, starting with 0 for the terminal directly connected to the interface, counting downwards to negative values. The second argument is the absolute address this terminal should be assigned to:

await out.initialize(-1, 20)  # assign address 20 to the first terminal

The terminals are usually controlled by devices, where one terminal may be controlled by several devices, or one device controls several terminals. The devices are represented by Device objects. Upon instantiation, they are connected to the terminals:

from ebpfcat.devices import AnalogOutput

ao = AnalogOutput(out.ch1_value)  # use channel 1 of terminal "out"

Devices are grouped into SyncGroup, which means that their terminals are always read and written at the same time. A device can only belong to one SyncGroup, but a terminal may be part of several devices or sync groups. The sync group is also responsible to constantly transfer data to and from the terminals such that they do not time out and go into a safe state:

from ebpfcat.ebpfcat import SyncGroup

sg = SyncGroup(master, [ao])  # this sync group only contains one terminal

sg.start()  # start operating the terminals

The AnalogOutput in the examples is a pretty boring device, it can only output a value like so:

ao.value = 5  # set the value on the terminal

For reference, here is a complete code example:

"""A simple example of an EtherCat control

this is only an illustrative example to be read. It will not work unless
you happen to have an EtherCat setup where a Beckhoff EL4101 terminal is
the second terminal in the line.
"""
import asyncio
from ebpfcat.ebpfcat import FastEtherCat, SyncGroup
from ebpfcat.devices import AnalogOutput
from ebpfcat.terminals import EL4104


async def main():
    master = FastEtherCat("eth0")
    await master.connect()
    print("Number of terminals:", await master.count())
    out = EL4104(master)
    await out.initialize(-2, 20)
    ao = AnalogOutput(out.ch2_value)  # use channel 1 of terminal "out"
    sg = SyncGroup(master, [ao])  # this sync group only contains one terminal
    task = sg.start()  # start operating the terminals
    for i in range(10):
        # we would measure an increasing value on the terminal output
        ao.value = i
        await asyncio.sleep(0.1)

    task.cancel()  # stop the sync group

asyncio.run(main())

Writing a device

Equipment controlled via the EtherCAT terminals often requires that a dedicated device is written for it. Devices inherit from ebpfcat.Device. They declare which kind of data they want to communicate to the terminals as a ebpfcat.TerminalVar like so:

from ebpfcat.ebpfcat import Device, TerminalVar

class Motor(Device):
    speed = TerminalVar()
    position = TerminalVar()

Before they can be used, their TerminalVars need to be initialized:

motor = Motor()
motor.speed = outputTerminal.speed
motor.position = encoderTerminal.value

whenever new data is read from the loop, the update() method of the device is called, in which one can evaluate the TerminalVars, or set them:

def update(self):
    """a idiotic speed controller"""
    self.speed = (self.position - self.target) * self.pConst

Three methods of control

The communication with the terminals can happen in three different ways:

  • 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 enough to around 100 Hz operation.

  • fast: the data is sent, received and processed using XDP in the Linux 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 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 sdo_read() and 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 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 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 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 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)

Inspecting the bus via the command line

Using the command line tool ec-info one can learn many details about the terminals on an EtherCat bus. As its first parameter, it always takes the interface the bus is connected to, in the following examples we always take eth0. The --terminal (or -t) parameter may be used with the position of the terminal on the bus to be inspected, otherwise all terminals will be inspected. All other parameters indicate which information should be shown.

The --ids (or -i) parameter shows the identification numbers of the terminal from its EEPROM. As an example:

$ sudo ec-info eth0 -i -t3
terminal no 3
2:1B813052 revision 100034 serial 0

This means that terminal number 3 has vendor ID 2 (that is Beckhoff Automation), with product code 0x1B813052 (an EL7041 terminal), revision 0x100034 and serial number 0. Note that most vendors will leave the serial number 0, though in principle this can be changed.

The --names (or -n) parameter shows some readable text found in the EEPROM of the terminal. This may be anything, but often is helpful in identifying the terminal:

$ sudo ec-info eth0 -n -t12
terminal no 12
EL7031
DriveAxisTerminals
Antriebs- und Achsklemmen (EL7xxx)
EL7031 1K. Schrittmotor-Endstufe (24V, 1.5A)
Synchron
DC

A little less user friendly, but sometimes more informative variant is the --eeprom (or -e) parameter, showing the content of the sections of the EEPROM once as text and once as a hexadecimal representation:

$ sudo ec-info eth0 -e -t12
terminal no 12
 3: b'1P079532SBTN000jb1061KES7031                        Q1    2P242213130026'
    31503037393533325342544e3030306a62313036314b4553373033312020202020202020202020202020202020202020202020205131202020203250323432323133313330303236
10: b'\x06\x06EL7031\x12DriveAxisTerminals"Antriebs- und Achsklemmen (EL7xxx),EL7031 1K. Schrittmotor-Endstufe (24V, 1.5A)\x08Synchron\x02DC\xff'
    0606454c37303331124472697665417869735465726d696e616c7322416e7472696562732d20756e6420416368736b6c656d6d656e2028454c37787878292c454c3730333120314b2e20536368726974746d6f746f722d456e64737475666520283234562c20312e3541290853796e6368726f6e024443ff
30: b"\x02\x00\x01\x04\x0c'\x01\x00\x00\x00\x00\x04x\x00\x03\x003\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
    020001040c270100000000047800030033000000000000000000000000000000
40: b'\x01\x02\x03\xff'
    010203ff
41: b'\x00\x10\x80\x00&\x00\x01\x01\x80\x10\x80\x00"\x00\x01\x02\x00\x11\x08\x00$\x00\x01\x03\x80\x11\x08\x00 \x00\x01\x04'
    0010800026000101801080002200010200110800240001038011080020000104
60: b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x01\x00\x06\x00\x00\x00\x00\x00'
    000000000000000000000000000000000000050000000000000000000000000000000000000000030100060000000000

Using the --pdo (or -p) parameter one can inspect the current PDO configuration, this are the CoE parameters available for synchronous read and write:

$ sudo ec-info eth0 -p -t12
terminal no 12
7000:02 OUT 0 1
7000:03 OUT 0 2
7000:04 OUT 0 3
7000:11 OUT 2 H
7010:01 OUT 4 0
7010:02 OUT 4 1
7010:03 OUT 4 2
7010:21 OUT 6 H
6000:02 IN 0 1
6000:03 IN 0 2
6000:04 IN 0 3
...

The first columns shows the CoE address, the second shows OUT for data written to the terminal, IN for those read from it. The third column indicates the byte adress in the synchronous datagram, and the last column either the bit within that byte, or an indicator of the ssize of the parameter. The meaning of the parameters can be found in the terminal’s documentation, or possibly via the --sdo parameter.

The CoE adresses shown here following a pattern that at least Beckhoff Automation follows: the 7xxx:xx range are the output parameters, the 6xxx:xx range are the input parameters.

The --sdo (or -s) paramter shows the terminal’s self description of parameters. This self description, however, varies in quality depending on the vendor. Let’s go through some of the output:

$ sudo ec-info eth0 -s -t12
terminal no 12
1000:
    0: Device type UNSIGNED32 (32 bit) flags 7
1008:
    0: Device name VISIBLE_STRING (48 bit) flags 7
1009:
    0: Hardware version VISIBLE_STRING (16 bit) flags 7
100A:
    0: Software version VISIBLE_STRING (16 bit) flags 7
1011:
    1: SubIndex 001 UNSIGNED32 (32 bit) flags 3F
1018:
    1: Vendor ID UNSIGNED32 (32 bit) flags 7
    2: Product code UNSIGNED32 (32 bit) flags 7
    3: Revision UNSIGNED32 (32 bit) flags 7
    4: Serial number UNSIGNED32 (32 bit) flags 7
...

The output usually starts with some identification of the device itself. Note that the output is grouped by CoE groups, so in the example the adress of the serial number (last line) would be 1018:4. Adding the --values (or -v) parameter also shows the current values of the CoE parameter, for numbers both in decimal and hexadecimal:

$ sudo ec-info eth0 -s -v -t12
terminal no 12
1000:
    0: Device type UNSIGNED32 (32 bit) flags 7
              5001     1389
1008:
    0: Device name VISIBLE_STRING (48 bit) flags 7
        ES7031
        'ES7031'
1009:
    0: Hardware version VISIBLE_STRING (16 bit) flags 7
        13
        '13'
100A:
    0: Software version VISIBLE_STRING (16 bit) flags 7
        13
        '13'
1011:
    1: SubIndex 001 UNSIGNED32 (32 bit) flags 3F
                 0        0
1018:
    1: Vendor ID UNSIGNED32 (32 bit) flags 7
                 2        2
    2: Product code UNSIGNED32 (32 bit) flags 7
         460795986 1B773052
    3: Revision UNSIGNED32 (32 bit) flags 7
           1703936   1A0000
    4: Serial number UNSIGNED32 (32 bit) flags 7
             72315    11A7B
...

Later on, the actual functionality of the terminal is shown. As an example, a stepper motor terminal might be enabled with a boolean value, and a velocity may be set:

7010:
    1: Enable BOOLEAN (1 bit) flags 47
                 0        0
    2: Reset BOOLEAN (1 bit) flags 47
                 0        0
    3: Reduce torque BOOLEAN (1 bit) flags 47
                 0        0
    11: Position UNSIGNED32 (32 bit) flags 47
                 0        0
    21: Velocity INTEGER16 (16 bit) flags 47
                 0        0

So in this example, CoE address 7010:21 is a 16 bit integer that sets the drive velocity of a stepper motor.

Reference Documentation

A collection of devices

This modules contains a collection of devices which may be helpful in many projects.

class ebpfcat.devices.AnalogInput(data)

Generic analog input device

This device can be linked to an analog input of a terminal. It will read from there and return the result in its parameter value.

class ebpfcat.devices.AnalogOutput(data)

Generic analog output device

This device can be linked to an analog output of a terminal. It will write the value to that terminal.

class ebpfcat.devices.Counter

A fake device counting the loops

class ebpfcat.devices.DigitalInput(data)

Generic digital input device

This device can be linked to an analog input of a terminal. It will read from there and return the result in its parameter value.

class ebpfcat.devices.DigitalOutput(data)

Generic digital output device

This device can be linked to an analog output of a terminal. It will write the value to that terminal.

class ebpfcat.devices.Dummy(terminals)

A placeholder device assuring a terminal is initialized

get_terminals()

return the terminals used by this device

return a dictionary of terminal vs. a boolean indicating whether access is read-write.

class ebpfcat.devices.Motor

A simple closed-loop motor

This device implements a closed loop between an encoder and a velocity-control motor.

velocity: TerminalVar

link this to the velocity output of a motor terminal

encoder: TerminalVar

link this to the position input of an encoder

low_switch: TerminalVar

link to a digital input for a low limit switch

high_switch: TerminalVar

link to a digital input for a high limit switch

enable: TerminalVar

link to the enable parameter of the motor terminal

current_position: TerminalVar
set_enable: DeviceVar

set to whether the motor should be enabled, i.e. moving

target: DeviceVar

the current target the motor should move to

max_velocity: DeviceVar

the maximum allowed velocity for the motor. If the motor is far away from its target, this is the velocity the motor will go with.

proportional: DeviceVar

the proportionality factor between the distance from target and the desired velocity.

class ebpfcat.devices.RandomDropper

Randomly drop packets

This fake device randomly drops EtherCat packets, to simulate bad connections.

class ebpfcat.devices.RandomOutput(data)

Randomized digital output

This device randomly switches its linked digital output on or off, with a probability given by probability.

Low-level access to EtherCAT

this modules contains the code to actually talk to EtherCAT terminals.

class ebpfcat.ethercat.CoECmd(value, names=None, *values, module=None, qualname=None, type=None, start=1, boundary=None)
class ebpfcat.ethercat.ECCmd(value, names=None, *values, module=None, qualname=None, type=None, start=1, boundary=None)
class ebpfcat.ethercat.ECDataType(value, names=None, *values, module=None, qualname=None, type=None, start=1, boundary=None)
class ebpfcat.ethercat.EEPROM(value, names=None, *values, module=None, qualname=None, type=None, start=1, boundary=None)
class ebpfcat.ethercat.EtherCat(network)

The EtherCAT connection

An object of this class represents one connection to an EtherCAT loop. It keeps the socket, and eventually all data flows through it.

This class supports both to send individual datagrams and wait for their response, but also to send and receive entire packets.

async assigned_address(position)

return the set adress of terminal at position, if none set one

async connect()

connect to the EtherCAT loop

connection_made(transport)

start the send loop once the connection is made

async count()

Count the number of terminals on the bus

datagram_received(data, addr)

distribute received packets to the recipients

async eeprom_read(position, start)

read 4 bytes from the eeprom of terminal position at start

async find_free_address()

Find an absolute address not in use

an address once returned by this method is assumed to be used in the future and will never be handed out again

async receive_index(index)

Wait for packet identified by index

async roundtrip(cmd, pos, offset, *args, data=None, idx=0)

Send a datagram and wait for its response

Parameters:
  • cmd (ECCmd) – the EtherCAT command

  • pos – the positional address of the terminal

  • offset – the offset within the terminal

  • idx – the EtherCAT datagram index

  • data – the data to be sent, or and integer for the number of zeros to be sent as placeholder

Any additional parameters will be interpreted as follows: every str is interpreted as a format for a struct.pack, everything else is the data for those format. Upon returning, the received data will be unpacked accoding to the format strings.

async roundtrip_packet(packet)

Send a packet and return the response

Send the packet to the loop and wait that it comes back, and return that to the caller.

send_packet(packet)

simply send the packet, fire-and-forget

async sendloop()

the eternal datagram sending loop

This method runs while we are connected, takes the datagrams to be sent from a queue, packs them in a packet and ships them out.

exception ebpfcat.ethercat.EtherCatError
class ebpfcat.ethercat.MBXType(value, names=None, *values, module=None, qualname=None, type=None, start=1, boundary=None)
class ebpfcat.ethercat.MachineState(value, names=None, *values, module=None, qualname=None, type=None, start=1, boundary=None)

The states of the EtherCAT state machine

The states are in the order in which they should be taken, BOOTSTRAP is at the end as this is a state we usually do not go to.

class ebpfcat.ethercat.ODCmd(value, names=None, *values, module=None, qualname=None, type=None, start=1, boundary=None)
class ebpfcat.ethercat.Packet

An EtherCAT packet representation

A packet contains one or more datagrams which are sent as EtherNet packets. We implicitly add a datagram in the front which later serves as an identifier for the packet.

append(cmd, data, idx, *address)

Append a datagram to the packet

Parameters:
  • cmd (ECCmd) – EtherCAT command

  • data – the data in the datagram

  • idx – the datagram index, unchanged by terminals

Depending on the command, one or two more parameters represent the address, either terminal and offset for position or node addressing, or one value for logical addressing.

assemble(index)

Assemble the datagrams into a packet

Parameters:

index – an identifier for the packet

An implicit empty datagram is added at the beginning of the packet that may be used as an identifier for the packet.

full()

Is the data limit reached?

class ebpfcat.ethercat.Struct(*args)

Define repetitive structures in CoE objects

Some terminals, especially multi-channel terminals, have repetitive structures in their CoE. 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.

class ebpfcat.ethercat.SyncManager(value, names=None, *values, module=None, qualname=None, type=None, start=1, boundary=None)
class ebpfcat.ethercat.Terminal(ethercat)

Represent one terminal (SubDevice or slave) in the loop

async eeprom_write_one(start, data)

write 2 bytes to the eeprom at start

async get_state()

get the current state, error flag and status word

async initialize(relative=None, absolute=None)

Initialize the terminal

this sets up the connection to the terminal we represent.

Parameters:
  • relative – the position of the terminal in the loop, a negative number counted down from 0 for the first terminal If None, we assume the address is already initialized

  • absolute – the number used to identify the terminal henceforth If None take a free one

If only one parameter is given, it is taken to be an absolute position, the terminal address is supposed to be already initialized.

This also reads the EEPROM and sets up the sync manager as defined therein. It still leaves the terminal in the init state.

map_fmmu(logical, write)

map the pdo to logical address.

Parameters:

write – a boolean indicating whether this is to be used for writing (instead of reading).

async mbx_recv()

receive data from the mailbox

async mbx_send(type, *args, data=None, address=0, priority=0, channel=0)

send data to the mailbox

async read(start, *args, **kwargs)

read data from the terminal at offset start

see EtherCat.roundtrip for details on more parameters.

async read_eeprom()

read the entire eeprom

async read_object_entry(index, subidx)

read a object entry from the CoE self description

async sdo_read(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 sdo_write(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.

async set_state(state)

try to set the state, and return the new state

async to_operational(target=MachineState.OPERATIONAL)

try to bring the terminal to operational state

this tries to push the terminal through its state machine to the target state. Note that even if it reaches there, the terminal will quickly return to pre-operational if no packets are sent to keep it operational.

return the state, error flag and status before the operation.

async write(start, *args, **kwargs)

write data to the terminal at offset start

see EtherCat.roundtrip for details on more parameters

The high-level API for EtherCAT loops

class ebpfcat.ebpfcat.BaseType(value, names=None, *values, module=None, qualname=None, type=None, start=1, boundary=None)
class ebpfcat.ebpfcat.Device

A device is a functional unit in an EtherCAT loop

A device aggregates data coming in and going to terminals to serve a common goal. A terminal may be used by several devices.

get_terminals()

return the terminals used by this device

return a dictionary of terminal vs. a boolean indicating whether access is read-write.

class ebpfcat.ebpfcat.DeviceVar(size='I', write=False)

A variable in a device for higher-level use

define a variable within a device which the device’s user can access. This is especially important for fast devices, this is the way data is communicated to and from the EBPF program.

For non-fast devices, this acts like normal Python variables.

Parameters:
  • size – the size of a variable in struct letters

  • write – whether the variable will be written to by the user

For example:

class MyDevice(Device):
    my_data = DeviceVar()

    def program(self):
        self.my_data = 7

device = MyDevice()
print(self.my_data)  # should print 7 once the program is running
class ebpfcat.ebpfcat.EBPFTerminal(ethercat)

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.

allocate(packet, readwrite)

allocate space in packet for the pdos of this terminal

return a dict that contains the datagram number and starting offset therein for each sync manager.

Negative datagram numbers are for the future FMMU datagrams.

class ebpfcat.ebpfcat.EtherXDP(**kwargs)

The EtherCat packet dispatcher

This class creates an EBPF program that receives EtherCAT packet from the network and dispatches them to the sync group they belong to, or passes them on to user space if they do not belong to them.

For each sync group, there are always two packets on the wire, one that only reads value from the terminals, the other one also writes. Usually only the read-write packet is handed over to the sync group’s program. If, however, that packet gets lost, the next read-only packet is handed over.

User space is supposed to constantly feed in new packets, and the then-superfluous packets are sent back to user space. This way user space can constantly read data independent of the EBPF program. It cannot write, however, as this would cause priority issues.

program()

overwrite this method with your program while subclassing

class ebpfcat.ebpfcat.FastEtherCat(network)

An EtherCAT driver class for fast and slow sync groups

async connect()

connect to the EtherCAT loop

class ebpfcat.ebpfcat.FastSyncGroup(ec, devices, **kwargs)
program()

overwrite this method with your program while subclassing

async run()

attach this program to a network during context

attach this program to the network while the context manager is running, and detach it afterwards.

Parameters:
  • network – the name of the network interface, like "eth0"

  • flags – one of the XDPFlags

class ebpfcat.ebpfcat.PacketDesc(sm, position, size)

A single value in a process data

This describes some data in the process data coming from or sent to a terminal. This is the low-level version of ProcessDesc, which can be used if the terminal’s self-desciption is lacking.

Parameters:
  • sm – the sync manager, either SyncManager.IN or SyncManager.OUT.

  • position – the byte position in the process data

  • size – either a struct definition of a data type, or an integer denoting the bit within a byte to be adressed.

class ebpfcat.ebpfcat.PacketVar(terminal, sm, position, size)
class ebpfcat.ebpfcat.ProcessDesc(index, subindex, size=None)

A process variable as described in the current mapping

This describes a process variable as found in the current PDO mapping read from the terminal.

Parameters:
  • index – the index of the process variable, usually found in the terminal’s documentation

  • subindex – the subindex, also found in the documentation

  • 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.

class ebpfcat.ebpfcat.SimpleEtherCat(network)
class ebpfcat.ebpfcat.SterilePacket

a sterile packet has all its sets exchanged by NOPs

append(cmd, *args, counter=1)

Append a datagram to the packet

Parameters:
  • cmd (ECCmd) – EtherCAT command

  • data – the data in the datagram

  • idx – the datagram index, unchanged by terminals

Depending on the command, one or two more parameters represent the address, either terminal and offset for position or node addressing, or one value for logical addressing.

class ebpfcat.ebpfcat.SyncGroup(ec, devices, **kwargs)

A group of devices communicating at the same time

class ebpfcat.ebpfcat.TerminalVar

a device variable to be linked to a process variable

Whithin a Device, one can refer to process variables that should later be linked to process variables of a terminal. Within the device, one can access the process variable generically. Upon instantiation one would then assign a ProcessDesc (or PacketDesc) to it to link the variable to an actual terminal.

For example:

class MyDevice(Device):
    the_output = TerminalVar()

    def program(self):
        self.the_output = 5  # write 5 to whatever variable linked

terminal = MyTerminal()
device = MyDevice()
device.the_output = terminal.output5  # link the_output to output5