diff --git a/ebpfcat/devices.py b/ebpfcat/devices.py index 9005d123567f662ca87710778713683f36d807a1..b6d0adb5fbc334c6aecfa4d3c9f41d541e5d7faf 100644 --- a/ebpfcat/devices.py +++ b/ebpfcat/devices.py @@ -110,11 +110,11 @@ class DigitalOutput(Device): self.data = self.value -class PWM(Device): - """Generic digital output device +class RandomOutput(Device): + """Randomized digital output - This device can be linked to an analog output of a terminal. - It will write the `value` to that terminal. + This device randomly switches its linked digital output + on or off, with a probability given by :attr:`probability`. """ seed = DeviceVar("I", write=True) value = DeviceVar("I", write=True) @@ -127,8 +127,13 @@ class PWM(Device): self.seed = self.seed * 0xcf019d85 + 1 self.data = self.value > self.seed - def update(self): - self.data = self.value + @property + def probability(self): + return self.value / 0xffffffff + + @probability.setter + def probability(self, value): + self.value = int(value * 0xffffffff) class Counter(Device): @@ -156,14 +161,67 @@ class Counter(Device): class Motor(Device): + """A simple closed-loop motor + + This device implements a closed loop between an encoder and a + velocity-control motor. + + .. attribute:: velocity + :type: TerminalVar + + link this to the velocity output of a motor terminal + + .. attribute:: encoder + :type: TerminalVar + + link this to the position input of an encoder + + .. attribute:: low_switch + :type: TerminalVar + + link to a digital input for a low limit switch + + .. attribute:: high_switch + :type: TerminalVar + + link to a digital input for a high limit switch + + .. attribute:: enable + :type: TerminalVar + + link to the enable parameter of the motor terminal + + .. attribute:: current_position + :type: TerminalVar + + .. attribute:: set_enable + :type: DeviceVar + + set to whether the motor should be enabled, i.e. moving + + .. attribute:: target + :type: DeviceVar + + the current target the motor should move to + + .. attribute:: max_velocity + :type: 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. + + .. attribute:: proportional + :type: DeviceVar + + the proportionality factor between the distance from target and the + desired velocity. + """ velocity = TerminalVar() encoder = TerminalVar() low_switch = TerminalVar() high_switch = TerminalVar() enable = TerminalVar() - current_position = DeviceVar() - set_velocity = DeviceVar(write=True) set_enable = DeviceVar(write=True) max_velocity = DeviceVar(write=True) max_acceleration = DeviceVar(write=True) @@ -176,7 +234,6 @@ class Motor(Device): velocity = self.max_velocity elif velocity < -self.max_velocity: velocity = -self.max_velocity - self.current_position = self.encoder self.velocity = velocity self.enable = self.set_enable @@ -197,6 +254,7 @@ class Motor(Device): with self.high_switch, self.velocity > 0: self.velocity = 0 + class Dummy(Device): """A placeholder device assuring a terminal is initialized""" def __init__(self, terminals): @@ -210,6 +268,11 @@ class Dummy(Device): class RandomDropper(Device): + """Randomly drop packets + + This fake device randomly drops EtherCat packets, to simulate bad + connections. + """ rate = DeviceVar("I", write=True) def program(self): diff --git a/ebpfcat/ethercat.rst b/ebpfcat/ethercat.rst index 03890239fd05631e545849053af79f5483faf3b2..fc4642b26d8f6018059e1bdb07eb1fce3ea8468c 100644 --- a/ebpfcat/ethercat.rst +++ b/ebpfcat/ethercat.rst @@ -11,12 +11,15 @@ 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 - # later, in an async function: - master = FastEtherCat("eth0") - await master.connect() - await master.scan_bus() + async def main(): + master = FastEtherCat("eth0") + await master.connect() + await master.scan_bus() + + asyncio.run(main()) Next we create an object for each terminal that we want to use. As an example, take some Beckhoff output terminal:: @@ -39,7 +42,7 @@ instantiation, they are connected to the terminals:: from ebpfcat.devices import AnalogOutput - ao = AnalogOutput(out.ch1_value) + ao = AnalogOutput(out.ch1_value) # use channel 1 of terminal "out" Devices are grouped into :class:`~ebpfcat.SyncGroup`, which means that their terminals are always read and written at the same time. A device can only @@ -89,6 +92,7 @@ method of the device is called, in which one can evaluate the """a idiotic speed controller""" self.speed = (self.position - self.target) * self.pConst + Three methods of control ------------------------ @@ -120,11 +124,11 @@ 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 + $ 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. +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 :meth:`~ethercat.Terminal.sdo_read` and