diff --git a/ebpfcat/ebpfcat.py b/ebpfcat/ebpfcat.py index 12e1e941d97fb206ccd4c6972463403a5593e98d..5bb9e438ebf8d325500edbcaf4d67694ff42ce87 100644 --- a/ebpfcat/ebpfcat.py +++ b/ebpfcat/ebpfcat.py @@ -16,7 +16,7 @@ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. """The high-level API for EtherCAT loops""" -from asyncio import ensure_future, gather, wait_for, TimeoutError +from asyncio import ensure_future, gather, sleep, wait_for, TimeoutError from contextlib import asynccontextmanager, contextmanager import os from struct import pack, unpack, calcsize, pack_into, unpack_from @@ -220,12 +220,13 @@ class EBPFTerminal(Terminal): if (self.compatibility is not None and (self.vendorId, self.productCode) not in self.compatibility): raise RuntimeError( - f"Incompatible Terminal: {self.vendorId}:{self.productCode} " - f"({relative}, {absolute})") - await self.to_operational() + f"Incompatible Terminal: {self.vendorId}:{self.productCode}") + await self.to_operational(2) self.pdos = {} - if self.has_mailbox(): - await self.parse_pdos() + outbits, inbits = await self.parse_pdos() + self.pdo_out_sz = int((outbits + 7) // 8) + self.pdo_in_sz = int((inbits + 7) // 8) + await self.write_pdo_sm() def allocate(self, packet, readonly): """allocate space in packet for the pdos of this terminal @@ -350,6 +351,8 @@ class FastEtherCat(SimpleEtherCat): class SyncGroupBase: missed_counter = 0 + current_data = None + def __init__(self, ec, devices, **kwargs): super().__init__(**kwargs) self.ec = ec @@ -363,6 +366,9 @@ class SyncGroupBase: self.terminals = {t: None for t in sorted(terminals, key=lambda t: t.position)} + async def to_operational(self): + await gather(*[t.to_operational() for t in self.terminals]) + async def run(self): data = self.asm_packet while True: @@ -393,7 +399,9 @@ class SyncGroup(SyncGroupBase): self.packet_index = SyncGroup.packet_index SyncGroup.packet_index += 1 self.asm_packet = self.packet.assemble(self.packet_index) - return ensure_future(self.run()) + ret = ensure_future(self.run()) + ensure_future(self.to_operational()) + return ret def allocate(self): self.packet = Packet() @@ -404,8 +412,6 @@ class SyncGroup(SyncGroupBase): class FastSyncGroup(SyncGroupBase, XDP): license = "GPL" - current_data = None - properties = ArrayMap() def __init__(self, ec, devices, **kwargs): @@ -438,6 +444,7 @@ class FastSyncGroup(SyncGroupBase, XDP): def start(self): self.allocate() self.task = ensure_future(self.run()) + ensure_future(self.to_operational()) return self.task def cancel(self): diff --git a/ebpfcat/ethercat.py b/ebpfcat/ethercat.py index 4757bb57a36d6ca7adb1dec6f2aa2a4466ecac2b..c2f519d493e01cd2d79ad0c9cdfaf828c0fe9699 100644 --- a/ebpfcat/ethercat.py +++ b/ebpfcat/ethercat.py @@ -293,8 +293,10 @@ class EtherCat(Protocol): if wkc == 0: future.set_exception( EtherCatError("datagram was not processed")) - else: + elif not future.done(): future.set_result(data[start:stop]) + else: + print("dropped package") dgrams = [] packet = Packet() @@ -450,6 +452,16 @@ class Terminal: elif mode == 6: self.mbx_out_off = offset self.mbx_out_sz = size + else: + print("wrong mode") + + async def write_pdo_sm(self): + await self.write(0x816, "B", 0) + await self.write(0x812, "H", self.pdo_out_sz) + await self.write(0x816, "B", self.pdo_out_sz > 0) + await self.write(0x81E, "B", 0) + await self.write(0x81A, "H", self.pdo_in_sz) + await self.write(0x81E, "B", self.pdo_in_sz > 0) async def parse_pdos(self): async def parse_eeprom(s): @@ -490,16 +502,18 @@ class Terminal: (sm, bitpos // 8, {8: "B", 16: "H", 32: "I", 64: "Q"}[bits]) bitpos += bits + return bitpos self.pdos = {} if self.has_mailbox(): - await parse(parse_sdo(0x1c12, 2)) - await parse(parse_sdo(0x1c13, 3)) + return (await parse(parse_sdo(0x1c12, 2)), + await parse(parse_sdo(0x1c13, 3))) else: - if 50 in self.eeprom: + return ( await parse(parse_eeprom(self.eeprom[50])) - if 51 in self.eeprom: + if 50 in self.eeprom else 0, await parse(parse_eeprom(self.eeprom[51])) + if 51 in self.eeprom else 0) async def parse_pdo(self, index, sm): assignment = await self.sdo_read(index) @@ -557,7 +571,7 @@ class Terminal: 0x0130, "H2xH") if error != 0: raise EtherCatError(f"AL register {error}") - if state == target: + if state >= target: return async def get_error(self): diff --git a/ebpfcat/ethercat_test.py b/ebpfcat/ethercat_test.py index a895f3a3934c4f82f05371b8826f53daaf0c9eea..c736d1b448876879669a4a499b434744effde1a4 100644 --- a/ebpfcat/ethercat_test.py +++ b/ebpfcat/ethercat_test.py @@ -42,6 +42,8 @@ class MockEtherCat: async def roundtrip(self, *args, data=None): if data is not None: args += data, + if not self.expected: + self.test.fail(f"missing {args}") self.test.assertEqual(args, self.expected.pop(0)) return self.results.pop(0) @@ -64,17 +66,17 @@ class MockEtherCat: class MockTerminal(Terminal): async def initialize(self, relative, absolute): self.position = absolute - self.operational = False + self.operational = 1 data = self.ec.test_data[-relative] self.test_eeprom = data["eeprom"] self.test_sdo = data["sdo"] await self.apply_eeprom() - async def to_operational(self): - self.operational = True + async def to_operational(self, state=8): + self.operational = state async def sdo_read(self, index, subindex=None): - assert self.operational + assert self.operational >= 2 if subindex is None: r = b'' for i in count(1): @@ -119,8 +121,14 @@ class Tests(TestCase): (ECCmd.FPWR, 4, 0x800, 0x80), (ECCmd.FPWR, 4, 0x800, H('00108000260001018010800022000102' '00110000040000038011100020000104')), + (ECCmd.FPWR, 4, 2070, 'B', 0), # disable sync manager + (ECCmd.FPWR, 4, 2066, 'H', 0), # set sync manager size + (ECCmd.FPWR, 4, 2070, 'B', False), # disable 0-length sync manager + (ECCmd.FPWR, 4, 2078, 'B', 0), # disable other sync manager + (ECCmd.FPWR, 4, 2074, 'H', 16), # set sync manager size + (ECCmd.FPWR, 4, 2078, 'B', True), # enable sync manager ] - ec.results = [None, None] + ec.results = [None, None, None, None, None, None, None, None] await ti.initialize(-1, 4) ai = AnalogInput(ti.channel1.value) SyncGroup.packet_index = 0x66554433 @@ -169,8 +177,14 @@ class Tests(TestCase): (ECCmd.FPWR, 7, 0x800, 0x80), (ECCmd.FPWR, 7, 0x800, H('0010800026000101801080002200010' '200110800240001038011000000000004')), + (ECCmd.FPWR, 7, 2070, 'B', 0), # disable sync manager + (ECCmd.FPWR, 7, 2066, 'H', 8), # set sync manager size + (ECCmd.FPWR, 7, 2070, 'B', True), # enable sync manager + (ECCmd.FPWR, 7, 2078, 'B', 0), # disable other sync manager + (ECCmd.FPWR, 7, 2074, 'H', 0), # set sync manager size + (ECCmd.FPWR, 7, 2078, 'B', False), # disable 0-length sync manager ] - ec.results = [None, None] + ec.results = [None, None, None, None, None, None, None, None] await ti.initialize(-2, 7) ao = AnalogOutput(ti.ch1_value) SyncGroup.packet_index = 0x55443322 diff --git a/ebpfcat/scripts.py b/ebpfcat/scripts.py index 09f9efe1c9d7f4058fb02689c8f174c51a6cc699..3663ced04a66496cfbacf836e1357351298afc15 100644 --- a/ebpfcat/scripts.py +++ b/ebpfcat/scripts.py @@ -74,7 +74,7 @@ async def info(): print(f"{k:2}: {v}\n {v.hex()}") if args.sdo: - await t.to_operational(4) + await t.to_operational(2) ret = await t.read_ODlist() for k, v in ret.items(): print(f"{k:X}:") @@ -91,7 +91,7 @@ async def info(): else: print(f" {r}") if args.pdo: - await t.to_operational(4) + await t.to_operational(2) await t.parse_pdos() for (idx, subidx), (sm, pos, fmt) in t.pdos.items(): print(f"{idx:4X}:{subidx:02X} {sm} {pos} {fmt}") @@ -164,7 +164,7 @@ async def create_test(): await t.initialize(-i, await ec.find_free_address()) sdo = {} if t.has_mailbox(): - await t.to_operational(4) + await t.to_operational(2) odlist = await t.read_ODlist() for k, v in odlist.items(): diff --git a/ebpfcat/terminals.py b/ebpfcat/terminals.py index 82f47f049a482d430cd6fbf5eb04c5271b11d6ea..c7a78c8460f03a519422111da51929178e5d4a1c 100644 --- a/ebpfcat/terminals.py +++ b/ebpfcat/terminals.py @@ -123,3 +123,22 @@ class EL7041(EBPFTerminal): status = ProcessDesc(0x6000, 1, "H") low_switch = ProcessDesc(0x6010, 0xc) high_switch = ProcessDesc(0x6010, 0xd) + + +class TurboVac(EBPFTerminal): + compatibility = {(0x723, 0xb5)} + pump_on = ProcessDesc(0x20D3, 0, 0) + stand_by = ProcessDesc(0x20D3, 0, 5) + reset = ProcessDesc(0x20D3, 0, 7) + error_status = ProcessDesc(0x20CA, 0) + speed_status = ProcessDesc(0x20CB, 0) + pump_is_on = ProcessDesc(0x20CC, 0, 0) + pump_warning = ProcessDesc(0x20CC, 0, 2) + pump_alarm = ProcessDesc(0x20CC, 0, 3) + speed = ProcessDesc(0x20CD, 0, "H") + current = ProcessDesc(0x20D1, 0, "H") + + +class Inficon(EBPFTerminal): + compatibility = {(0x644, 0x21)} + value = ProcessDesc(0xF640, 0x11, "f")