WeaveInterface compatibility on Android
Some checks are pending
Build Reticulum / test (push) Waiting to run
Build Reticulum / package (push) Blocked by required conditions
Build Reticulum / release (push) Blocked by required conditions

This commit is contained in:
Mark Qvist 2025-10-29 15:44:47 +01:00
parent ddf14e5636
commit 39a63b0643
3 changed files with 84 additions and 68 deletions

View file

@ -96,6 +96,7 @@ class WDCL():
RNS.log("You can install one with the command: python3 -m pip install pyserial", RNS.LOG_CRITICAL) RNS.log("You can install one with the command: python3 -m pip install pyserial", RNS.LOG_CRITICAL)
RNS.panic() RNS.panic()
if not RNS.vendor.platformutils.is_android():
if port == None: raise ValueError("No port specified") if port == None: raise ValueError("No port specified")
self.switch_identity = owner.switch_identity self.switch_identity = owner.switch_identity
@ -130,8 +131,7 @@ class WDCL():
if self.as_interface: if self.as_interface:
try: try:
self.open_port() self.open_port()
if self.serial and self.serial.is_open: self.configure_device()
if self.serial.is_open: self.configure_device()
else: raise IOError("Could not open serial port") else: raise IOError("Could not open serial port")
except Exception as e: except Exception as e:
@ -176,6 +176,7 @@ class WDCL():
dsrdtr = False) dsrdtr = False)
else: else:
if self.port != None:
# Get device parameters # Get device parameters
from usb4a import usb from usb4a import usb
device = usb.get_usb_device(self.port) device = usb.get_usb_device(self.port)
@ -280,9 +281,6 @@ class WDCL():
while self.serial.is_open: while self.serial.is_open:
data_in = self.serial.read(1500) data_in = self.serial.read(1500)
if len(data_in) > 0: if len(data_in) > 0:
# TODO: Remove debug
# self.device.receiver.log(f"Read {len(data_in)}")
# self.device.receiver.log(f"Raw:\n {RNS.hexrep(data_in).replace("7e", "[bold red]7e[/bold red]")}")
self.frame_buffer += data_in self.frame_buffer += data_in
flags_remaining = True flags_remaining = True
while flags_remaining: while flags_remaining:
@ -312,8 +310,6 @@ class WDCL():
self.owner.wlog("Will attempt to reconnect the interface periodically.") self.owner.wlog("Will attempt to reconnect the interface periodically.")
RNS.trace_exception(e) RNS.trace_exception(e)
RNS.log("READ LOOP EXIT")
self.online = False self.online = False
self.wdcl_connected = False self.wdcl_connected = False
try: self.serial.close() try: self.serial.close()
@ -327,21 +323,21 @@ class WDCL():
while not self.online: while not self.online:
try: try:
time.sleep(5) time.sleep(5)
if self.as_interface: RNS.log("Attempting to reconnect serial port "+str(self.port)+" for "+str(self)+"...", RNS.LOG_INFO) if self.as_interface: RNS.log("Attempting to reconnect serial port "+str(self.port)+" for "+str(self.owner)+"...", RNS.LOG_DEBUG)
else: self.owner.wlog("Attempting to reconnect serial port "+str(self.port.device)+" for "+str(self)+"...") else: self.owner.wlog("Attempting to reconnect serial port "+str(self.port.device)+" for "+str(self.owner)+"...")
self.open_port() self.open_port()
if self.serial.is_open: self.configure_device() if self.serial and self.serial.is_open: self.configure_device()
except Exception as e: except Exception as e:
if self.as_interface: RNS.log("Error while reconnecting port, the contained exception was: "+str(e), RNS.LOG_ERROR) if self.as_interface: RNS.log("Error while reconnecting port, the contained exception was: "+str(e), RNS.LOG_ERROR)
else: self.owner.wlog("Error while reconnecting port, the contained exception was: "+str(e)) else: self.owner.wlog("Error while reconnecting port, the contained exception was: "+str(e))
RNS.trace_exception(e)
self.reconnecting = False self.reconnecting = False
if self.as_interface: RNS.log("Reconnected serial port for "+str(self), RNS.LOG_INFO) if self.as_interface: RNS.log("Reconnected serial port for "+str(self), RNS.LOG_INFO)
else: self.owner.wlog("Reconnected serial port for "+str(self)) else: self.owner.wlog("Reconnected serial port for "+str(self))
def __str__(self): def __str__(self):
if self.as_interface: if self.as_interface: return f"WDCL over {self.port}"
return self.port
else: else:
if self.port.serial_number: sn_str = f" {self.port.serial_number}" if self.port.serial_number: sn_str = f" {self.port.serial_number}"
else: sn_str = "" else: sn_str = ""
@ -394,6 +390,7 @@ class Evt():
ET_PROTO_WEAVE_RUNNING = 0x3101 ET_PROTO_WEAVE_RUNNING = 0x3101
ET_PROTO_WEAVE_EP_ALIVE = 0x3102 ET_PROTO_WEAVE_EP_ALIVE = 0x3102
ET_PROTO_WEAVE_EP_TIMEOUT = 0x3103 ET_PROTO_WEAVE_EP_TIMEOUT = 0x3103
ET_PROTO_WEAVE_EP_VIA = 0x3104
ET_SRVCTL_REMOTE_DISPLAY = 0xA000 ET_SRVCTL_REMOTE_DISPLAY = 0xA000
ET_INTERFACE_REGISTERED = 0xD000 ET_INTERFACE_REGISTERED = 0xD000
ET_STAT_STATE = 0xE000 ET_STAT_STATE = 0xE000
@ -458,7 +455,7 @@ class Evt():
ET_PROTO_WDCL_HOST_ENDPOINT: "Weave host endpoint", ET_PROTO_WDCL_HOST_ENDPOINT: "Weave host endpoint",
ET_PROTO_WEAVE_INIT: "Weave protocol initialization", ET_PROTO_WEAVE_INIT: "Weave protocol initialization",
ET_PROTO_WEAVE_RUNNING: "Weave protocol activation", ET_PROTO_WEAVE_RUNNING: "Weave protocol activation",
ET_PROTO_WEAVE_EP_ALIVE: "Weave endpoint appeared", ET_PROTO_WEAVE_EP_ALIVE: "Weave endpoint alive",
ET_PROTO_WEAVE_EP_TIMEOUT: "Weave endpoint disappeared", ET_PROTO_WEAVE_EP_TIMEOUT: "Weave endpoint disappeared",
ET_SRVCTL_REMOTE_DISPLAY: "Remote display service control event", ET_SRVCTL_REMOTE_DISPLAY: "Remote display service control event",
ET_INTERFACE_REGISTERED: "Interface registration", ET_INTERFACE_REGISTERED: "Interface registration",
@ -566,6 +563,7 @@ class WeaveEndpoint():
def __init__(self, endpoint_addr): def __init__(self, endpoint_addr):
self.endpoint_addr = endpoint_addr self.endpoint_addr = endpoint_addr
self.alive = time.time() self.alive = time.time()
self.via = None
self.received = deque(maxlen=WeaveEndpoint.QUEUE_LEN) self.received = deque(maxlen=WeaveEndpoint.QUEUE_LEN)
def receive(self, data): def receive(self, data):
@ -701,6 +699,10 @@ class WeaveDevice():
if self.as_interface: self.rns_interface.add_peer(endpoint_id) if self.as_interface: self.rns_interface.add_peer(endpoint_id)
def endpoint_via(self, endpoint_id, via_switch_id):
if endpoint_id in self.endpoints: self.endpoints[endpoint_id].via = via_switch_id
if self.as_interface: self.rns_interface.endpoint_via(endpoint_id, via_switch_id)
def deliver_packet(self, endpoint_id, data): def deliver_packet(self, endpoint_id, data):
packet_data = endpoint_id+data packet_data = endpoint_id+data
self.wdcl_send_command(Cmd.WDCL_CMD_ENDPOINT_PKT, packet_data) self.wdcl_send_command(Cmd.WDCL_CMD_ENDPOINT_PKT, packet_data)
@ -760,8 +762,8 @@ class WeaveDevice():
# Handle system event signalling # Handle system event signalling
if frame.event == Evt.ET_PROTO_WDCL_CONNECTION: self.connection.wdcl_connected = True if frame.event == Evt.ET_PROTO_WDCL_CONNECTION: self.connection.wdcl_connected = True
if frame.event == Evt.ET_PROTO_WDCL_HOST_ENDPOINT and len(frame.data) == self.WEAVE_ENDPOINT_ID_LEN: self.endpoint_id = frame.data if frame.event == Evt.ET_PROTO_WDCL_HOST_ENDPOINT and len(frame.data) == self.WEAVE_ENDPOINT_ID_LEN: self.endpoint_id = frame.data
if frame.event == Evt.ET_PROTO_WEAVE_EP_ALIVE and len(frame.data) == 8: if frame.event == Evt.ET_PROTO_WEAVE_EP_ALIVE and len(frame.data) == self.WEAVE_ENDPOINT_ID_LEN: self.endpoint_alive(frame.data)
self.endpoint_alive(frame.data) if frame.event == Evt.ET_PROTO_WEAVE_EP_VIA and len(frame.data) == self.WEAVE_ENDPOINT_ID_LEN+self.WEAVE_SWITCH_ID_LEN: self.endpoint_via(frame.data[:self.WEAVE_ENDPOINT_ID_LEN], frame.data[self.WEAVE_ENDPOINT_ID_LEN:])
elif frame.event == Evt.ET_STAT_TASK_CPU: self.active_tasks[frame.data[1:].decode("utf-8")] = { "cpu_load": frame.data[0], "timestamp": time.time() } elif frame.event == Evt.ET_STAT_TASK_CPU: self.active_tasks[frame.data[1:].decode("utf-8")] = { "cpu_load": frame.data[0], "timestamp": time.time() }
elif frame.event == Evt.ET_STAT_CPU: elif frame.event == Evt.ET_STAT_CPU:
self.cpu_load = frame.data[0] self.cpu_load = frame.data[0]
@ -829,7 +831,7 @@ class WeaveInterface(Interface):
DEFAULT_IFAC_SIZE = 16 DEFAULT_IFAC_SIZE = 16
PEERING_TIMEOUT = 20.0 PEERING_TIMEOUT = 20.0
BITRATE_GUESS = 500*1000 BITRATE_GUESS = 250*1000
MULTI_IF_DEQUE_LEN = 48 MULTI_IF_DEQUE_LEN = 48
MULTI_IF_DEQUE_TTL = 0.75 MULTI_IF_DEQUE_TTL = 0.75
@ -871,6 +873,7 @@ class WeaveInterface(Interface):
self.port = port self.port = port
self.switch_identity = RNS.Identity() self.switch_identity = RNS.Identity()
self.owner = owner self.owner = owner
self.hw_errors = []
self._online = False self._online = False
self.final_init_done = False self.final_init_done = False
self.peers = {} self.peers = {}
@ -925,10 +928,11 @@ class WeaveInterface(Interface):
def peer_count(self): def peer_count(self):
return len(self.spawned_interfaces) return len(self.spawned_interfaces)
def endpoint_via(self, endpoint_addr, via_switch_addr):
if endpoint_addr in self.peers: self.peers[endpoint_addr][2].via_switch_id = via_switch_addr
def add_peer(self, endpoint_addr): def add_peer(self, endpoint_addr):
if not endpoint_addr in self.peers: if not endpoint_addr in self.peers:
self.peers[endpoint_addr] = [endpoint_addr, time.time()]
spawned_interface = WeaveInterfacePeer(self, endpoint_addr) spawned_interface = WeaveInterfacePeer(self, endpoint_addr)
spawned_interface.OUT = self.OUT spawned_interface.OUT = self.OUT
spawned_interface.IN = self.IN spawned_interface.IN = self.IN
@ -966,7 +970,9 @@ class WeaveInterface(Interface):
self.spawned_interfaces[endpoint_addr].detach() self.spawned_interfaces[endpoint_addr].detach()
self.spawned_interfaces[endpoint_addr].teardown() self.spawned_interfaces[endpoint_addr].teardown()
self.spawned_interfaces.pop(spawned_interface) self.spawned_interfaces.pop(spawned_interface)
self.spawned_interfaces[endpoint_addr] = spawned_interface self.spawned_interfaces[endpoint_addr] = spawned_interface
self.peers[endpoint_addr] = [endpoint_addr, time.time(), spawned_interface]
RNS.log(f"{self} added peer {RNS.hexrep(endpoint_addr)}", RNS.LOG_DEBUG) RNS.log(f"{self} added peer {RNS.hexrep(endpoint_addr)}", RNS.LOG_DEBUG)
else: else:
@ -1010,6 +1016,7 @@ class WeaveInterfacePeer(Interface):
self.owner = owner self.owner = owner
self.parent_interface = owner self.parent_interface = owner
self.endpoint_addr = endpoint_addr self.endpoint_addr = endpoint_addr
self.via_switch_id = None
self.peer_addr = None self.peer_addr = None
self.addr_info = None self.addr_info = None
self.HW_MTU = self.owner.HW_MTU self.HW_MTU = self.owner.HW_MTU

View file

@ -39,6 +39,7 @@ if get_platform() == "android":
from .Interfaces import UDPInterface from .Interfaces import UDPInterface
from .Interfaces import I2PInterface from .Interfaces import I2PInterface
from .Interfaces import RNodeMultiInterface from .Interfaces import RNodeMultiInterface
from .Interfaces import WeaveInterface
from .Interfaces.Android import RNodeInterface from .Interfaces.Android import RNodeInterface
from .Interfaces.Android import SerialInterface from .Interfaces.Android import SerialInterface
from .Interfaces.Android import KISSInterface from .Interfaces.Android import KISSInterface
@ -1037,6 +1038,10 @@ class Reticulum:
if interface.switch_id != None: ifstats["switch_id"] = RNS.hexrep(interface.switch_id) if interface.switch_id != None: ifstats["switch_id"] = RNS.hexrep(interface.switch_id)
else: ifstats["switch_id"] = None else: ifstats["switch_id"] = None
if hasattr(interface, "via_switch_id"):
if interface.via_switch_id != None: ifstats["via_switch_id"] = RNS.hexrep(interface.via_switch_id)
else: ifstats["via_switch_id"] = None
if hasattr(interface, "endpoint_id"): if hasattr(interface, "endpoint_id"):
if interface.endpoint_id != None: ifstats["endpoint_id"] = RNS.hexrep(interface.endpoint_id) if interface.endpoint_id != None: ifstats["endpoint_id"] = RNS.hexrep(interface.endpoint_id)
else: ifstats["endpoint_id"] = None else: ifstats["endpoint_id"] = None

View file

@ -370,6 +370,10 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None, json=
if ifstat["endpoint_id"] != None: print(" Endpoint : {v}".format(v=str(ifstat["endpoint_id"]))) if ifstat["endpoint_id"] != None: print(" Endpoint : {v}".format(v=str(ifstat["endpoint_id"])))
else: print(" Endpoint : Unknown") else: print(" Endpoint : Unknown")
if "via_switch_id" in ifstat:
if ifstat["via_switch_id"] != None: print(" Via : {v}".format(v=str(ifstat["via_switch_id"])))
else: print(" Via : Unknown")
if "peers" in ifstat and ifstat["peers"] != None: if "peers" in ifstat and ifstat["peers"] != None:
print(" Peers : {np} reachable".format(np=ifstat["peers"])) print(" Peers : {np} reachable".format(np=ifstat["peers"]))