From b44c40ca30c8078cf9639461d62b9943fd92c854 Mon Sep 17 00:00:00 2001 From: Martin Teichmann <martin.teichmann@xfel.eu> Date: Tue, 9 Apr 2024 16:09:39 +0000 Subject: [PATCH] support SDOs in terminals --- ebpfcat/ebpfcat.py | 34 +------------------------ ebpfcat/ethercat.py | 60 ++++++++++++++++++++++++++++++++++++++++++++ ebpfcat/terminals.py | 18 +++++++++++-- 3 files changed, 77 insertions(+), 35 deletions(-) diff --git a/ebpfcat/ebpfcat.py b/ebpfcat/ebpfcat.py index 30291c4..c3ddf2d 100644 --- a/ebpfcat/ebpfcat.py +++ b/ebpfcat/ebpfcat.py @@ -28,7 +28,7 @@ from time import time from .arraymap import ArrayMap, ArrayGlobalVarDesc from .ethercat import ( ECCmd, EtherCat, MachineState, Packet, Terminal, EtherCatError, - SyncManager) + Struct, SyncManager) from .ebpf import FuncId, MemoryDesc, SubProgram, prandom from .xdp import XDP, XDPExitCode, PacketVar as XDPPacketVar from .bpf import ( @@ -146,38 +146,6 @@ class PacketVar(MemoryDesc): self._start(device) + Packet.ETHERNET_HEADER) -class Struct: - """Define repetitive structures in a PDO - - Some terminals, especially multi-channel terminals, - 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 - - def __new__(cls, *args): - return StructDesc(cls, *args) - - -class StructDesc: - def __init__(self, struct, sm3=0, sm2=None): - self.struct = struct - if sm2 is None: - sm2 = sm3 - self.position_offset = {SyncManager.OUT: sm2, SyncManager.IN: sm3} - - def __get__(self, instance, owner): - if instance is None: - return self - ret = object.__new__(self.struct) - ret.position_offset = self.position_offset - ret.terminal = instance - return ret - - class TerminalVar: def __set__(self, instance, value): if isinstance(value, PacketVar): diff --git a/ebpfcat/ethercat.py b/ebpfcat/ethercat.py index 3f68be5..30eef3f 100644 --- a/ebpfcat/ethercat.py +++ b/ebpfcat/ethercat.py @@ -477,6 +477,50 @@ class EtherCat(Protocol): self.wait_futures[index].set_result(data) +class ServiceDesc: + def __init__(self, index, subidx): + self.index = index + self.subidx = subidx + + +class Struct: + """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. + """ + device = None + + def __new__(cls, *args): + return StructDesc(cls, *args) + + +class StructDesc: + def __init__(self, struct, sm3=0, sm2=None): + self.struct = struct + if sm2 is None: + sm2 = sm3 + self.position_offset = {SyncManager.OUT: sm2, SyncManager.IN: sm3} + + def __get__(self, instance, owner): + if instance is None: + return self + if (ret := instance.__dict__.get(self.name)) is not None: + return ret + ret = object.__new__(self.struct) + ret.position_offset = self.position_offset + ret.terminal = instance + instance.__dict__[self.name] = ret + return ret + + def __set_name__(self, owner, name): + self.name = name + + class Terminal: """Represent one terminal (*SubDevice* or *slave*) in the loop""" def __init__(self, ethercat): @@ -625,6 +669,22 @@ class Terminal: await parse(parse_eeprom(self.eeprom[50]), SyncManager.IN) if 50 in self.eeprom else 0) + async def parse_sdos(self): + sdos = {} + for cls in self.__class__.__mro__: + for k, v in cls.__dict__.items(): + if isinstance(v, ServiceDesc): + setattr(self, k, + await self.read_object_entry(v.index, v.subidx)) + elif isinstance(v, StructDesc): + struct = getattr(self, k) + offset = struct.position_offset[SyncManager.IN] + for kk, vv in struct.__class__.__dict__.items(): + if isinstance(vv, ServiceDesc): + setattr(struct, kk, + await self.read_object_entry( + vv.index + offset, vv.subidx)) + async def set_state(self, state): """try to set the state, and return the new state""" await self.ec.roundtrip(ECCmd.FPWR, self.position, 0x0120, "H", state) diff --git a/ebpfcat/terminals.py b/ebpfcat/terminals.py index d57464a..791ccb6 100644 --- a/ebpfcat/terminals.py +++ b/ebpfcat/terminals.py @@ -15,7 +15,8 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -from .ebpfcat import EBPFTerminal, PacketDesc, ProcessDesc, Struct +from .ethercat import ServiceDesc, Struct +from .ebpfcat import EBPFTerminal, PacketDesc, ProcessDesc class Generic(EBPFTerminal): @@ -136,10 +137,16 @@ class EL5042(EBPFTerminal): status = ProcessDesc(0x6000, 1, "H") invalid = ProcessDesc(0x6000, 0xE) + statusbits = ServiceDesc(0x8008, 2) + crc_invert = ServiceDesc(0x8008, 3) + multiturn = ServiceDesc(0x8008, 0x15) + singleturn = ServiceDesc(0x8008, 0x16) + frequency = ServiceDesc(0x8008, 0x13) + polynomial = ServiceDesc(0x8008, 0x11) + channel1 = Channel(0) channel2 = Channel(0x10) - class EL6022(EBPFTerminal): class Channel(Struct): transmit_accept = PacketDesc(3, 0, 0) @@ -172,6 +179,13 @@ class EL7041(EBPFTerminal): low_switch = ProcessDesc(0x6010, 0xd) stepcounter = ProcessDesc(0x6010, 0x14) + max_current = ServiceDesc(0x8010, 1) + max_voltage = ServiceDesc(0x8010, 3) + coil_resistance = ServiceDesc(0x8010, 4) + motor_emf = ServiceDesc(0x8010, 5) + invLogicLim1 = ServiceDesc(0x8012, 0x30) + invLogicLim2 = ServiceDesc(0x8012, 0x31) + class EL7332(EBPFTerminal): compatibility = {(2, 0x1CA43052)} -- GitLab