From 2b501347f62e5a7e59bb22d1e7f70128c1dffdbb Mon Sep 17 00:00:00 2001
From: Martin Teichmann <martin.teichmann@gmail.com>
Date: Wed, 1 Feb 2023 09:42:02 +0000
Subject: [PATCH] improve addressing random terminals

if the status of the bus is unknown, we need to assure not
to address two terminals at the same time
---
 ebpfcat/ethercat.py | 18 ++++++++++++++++--
 ebpfcat/scripts.py  | 20 ++++++++++----------
 2 files changed, 26 insertions(+), 12 deletions(-)

diff --git a/ebpfcat/ethercat.py b/ebpfcat/ethercat.py
index 4354262..2214772 100644
--- a/ebpfcat/ethercat.py
+++ b/ebpfcat/ethercat.py
@@ -28,6 +28,7 @@ this modules contains the code to actually talk to EtherCAT terminals.
 """
 from asyncio import ensure_future, Event, Future, gather, get_event_loop, Protocol, Queue, Lock
 from enum import Enum
+from itertools import count
 from random import randint
 from socket import socket, AF_PACKET, SOCK_DGRAM
 from struct import pack, unpack, unpack_from, calcsize
@@ -281,7 +282,12 @@ class EtherCat(Protocol):
             if packet.full() or self.send_queue.empty():
                 data = await self.roundtrip_packet(packet)
                 for start, stop, future in dgrams:
-                    future.set_result(data[start:stop])
+                    wkc, = unpack("<H", data[stop:stop+2])
+                    if wkc == 0:
+                        future.set_exception(
+                            RuntimeError("datagram was not processed"))
+                    else:
+                        future.set_result(data[start:stop])
                 dgrams = []
                 packet = Packet()
 
@@ -355,6 +361,15 @@ class EtherCat(Protocol):
         no, = unpack("<h", ret[16:18])  # number of terminals
         return no
 
+    async def find_free_address(self):
+        """Find an absolute address currently not in use"""
+        no = await self.count()
+        for i in count(no):
+            try:
+                await self.roundtrip(ECCmd.FPRD, i, 0x10, "H", 0)
+            except RuntimeError:
+                return i  # this address is not in use
+
     def connection_made(self, transport):
         """start the send loop once the connection is made"""
         transport.get_extra_info("socket").bind(self.addr)
@@ -456,7 +471,6 @@ class Terminal:
         async def parse(func):
             bitpos = 0
             async for idx, subidx, sm, bits in func:
-                #print("k", idx, subidx, sm, bits)
                 if idx == 0:
                     pass
                 elif bits < 8:
diff --git a/ebpfcat/scripts.py b/ebpfcat/scripts.py
index 4a2ecd0..689b428 100644
--- a/ebpfcat/scripts.py
+++ b/ebpfcat/scripts.py
@@ -43,17 +43,17 @@ async def info():
 
     if args.terminal is None:
         terminals = range(await ec.count())
+        terms = [Terminal() for t in terminals]
+        for t in terms:
+            t.ec = ec
+        await asyncio.gather(*(t.initialize(-i, i + 7)
+                               for i, t in zip(terminals, terms)))
     else:
-        # former terminal: don't listen!
-        # this does not work with all terminals, dunno why
-        await ec.roundtrip(ECCmd.FPRW, 7, 0x10, "H", 0)
-        terminals = [args.terminal]
-
-    terms = [Terminal() for t in terminals]
-    for t in terms:
-        t.ec = ec
-    await asyncio.gather(*(t.initialize(-i, i + 7)
-                           for i, t in zip(terminals, terms)))
+        free = await ec.find_free_address()
+        term = Terminal()
+        term.ec = ec
+        await term.initialize(-args.terminal, free)
+        terms = [term]
 
     for i, t in enumerate(terms):
         print(f"terminal no {i}")
-- 
GitLab