mirror of
https://github.com/markqvist/Reticulum.git
synced 2026-05-19 06:14:47 -07:00
Updated WeaveInterface. Added support for Weave devices to rnstatus.
This commit is contained in:
@@ -1,3 +1,33 @@
|
|||||||
|
# Reticulum License
|
||||||
|
#
|
||||||
|
# Copyright (c) 2016-2025 Mark Qvist
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# - The Software shall not be used in any kind of system which includes amongst
|
||||||
|
# its functions the ability to purposefully do harm to human beings.
|
||||||
|
#
|
||||||
|
# - The Software shall not be used, directly or indirectly, in the creation of
|
||||||
|
# an artificial intelligence, machine learning or language model training
|
||||||
|
# dataset, including but not limited to any use that contributes to the
|
||||||
|
# training or development of such a model or algorithm.
|
||||||
|
#
|
||||||
|
# - The above copyright notice and this permission notice shall be included in
|
||||||
|
# all copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
# SOFTWARE.
|
||||||
|
|
||||||
import RNS
|
import RNS
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
@@ -27,8 +57,9 @@ class WDCL():
|
|||||||
|
|
||||||
WDCL_BROADCAST = bytes([0xFF, 0xFF, 0xFF, 0xFF])
|
WDCL_BROADCAST = bytes([0xFF, 0xFF, 0xFF, 0xFF])
|
||||||
|
|
||||||
|
WDCL_HANDSHAKE_TIMEOUT = 2
|
||||||
|
|
||||||
HEADER_MINSIZE = 4+1
|
HEADER_MINSIZE = 4+1
|
||||||
HW_MTU = 1500
|
|
||||||
MAX_CHUNK = 32768
|
MAX_CHUNK = 32768
|
||||||
port = None
|
port = None
|
||||||
speed = None
|
speed = None
|
||||||
@@ -39,8 +70,31 @@ class WDCL():
|
|||||||
|
|
||||||
def __init__(self, owner, device, port, as_interface=False):
|
def __init__(self, owner, device, port, as_interface=False):
|
||||||
import importlib.util
|
import importlib.util
|
||||||
if importlib.util.find_spec('serial') != None: import serial
|
if RNS.vendor.platformutils.is_android():
|
||||||
else: RNS.panic()
|
self.on_android = True
|
||||||
|
if importlib.util.find_spec('usbserial4a') != None:
|
||||||
|
from usbserial4a import serial4a as serial
|
||||||
|
parity = "N"
|
||||||
|
|
||||||
|
if importlib.util.find_spec('jnius') == None:
|
||||||
|
RNS.log("Could not load jnius API wrapper for Android, RNode interface cannot be created.", RNS.LOG_CRITICAL)
|
||||||
|
RNS.log("This probably means you are trying to use an USB-based interface from within Termux or similar.", RNS.LOG_CRITICAL)
|
||||||
|
RNS.log("This is currently not possible, due to this environment limiting access to the native Android APIs.", RNS.LOG_CRITICAL)
|
||||||
|
RNS.panic()
|
||||||
|
|
||||||
|
else:
|
||||||
|
RNS.log("Could not load USB serial module for Android, Weave interface cannot be created.", RNS.LOG_CRITICAL)
|
||||||
|
RNS.panic()
|
||||||
|
|
||||||
|
else:
|
||||||
|
self.on_android = False
|
||||||
|
if importlib.util.find_spec('serial') != None:
|
||||||
|
import serial
|
||||||
|
parity = serial.PARITY_NONE
|
||||||
|
else:
|
||||||
|
RNS.log("Using the Weave interface requires a serial communication module to be installed.", RNS.LOG_CRITICAL)
|
||||||
|
RNS.log("You can install one with the command: python3 -m pip install pyserial", RNS.LOG_CRITICAL)
|
||||||
|
RNS.panic()
|
||||||
|
|
||||||
if port == None: raise ValueError("No port specified")
|
if port == None: raise ValueError("No port specified")
|
||||||
|
|
||||||
@@ -59,7 +113,7 @@ class WDCL():
|
|||||||
self.port = port
|
self.port = port
|
||||||
self.speed = 3000000
|
self.speed = 3000000
|
||||||
self.databits = 8
|
self.databits = 8
|
||||||
self.parity = serial.PARITY_NONE
|
self.parity = parity
|
||||||
self.stopbits = 1
|
self.stopbits = 1
|
||||||
self.timeout = 100
|
self.timeout = 100
|
||||||
self.online = False
|
self.online = False
|
||||||
@@ -67,6 +121,8 @@ class WDCL():
|
|||||||
self.next_tx = 0
|
self.next_tx = 0
|
||||||
self.should_run = True
|
self.should_run = True
|
||||||
self.receiver = None
|
self.receiver = None
|
||||||
|
self.wdcl_connected = False
|
||||||
|
self.reconnecting = False
|
||||||
self.frame_queue = deque()
|
self.frame_queue = deque()
|
||||||
if not self.as_interface:
|
if not self.as_interface:
|
||||||
self.id = RNS.Identity.full_hash(port.hwid.encode("utf-8"))
|
self.id = RNS.Identity.full_hash(port.hwid.encode("utf-8"))
|
||||||
@@ -82,7 +138,7 @@ class WDCL():
|
|||||||
RNS.log("Could not open serial port for interface "+str(self), RNS.LOG_ERROR)
|
RNS.log("Could not open serial port for interface "+str(self), RNS.LOG_ERROR)
|
||||||
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
|
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||||
RNS.log("Reticulum will attempt to bring up this interface periodically", RNS.LOG_ERROR)
|
RNS.log("Reticulum will attempt to bring up this interface periodically", RNS.LOG_ERROR)
|
||||||
if not self.detached and not self.reconnecting:
|
if not self.owner.detached and not self.reconnecting:
|
||||||
thread = threading.Thread(target=self.reconnect_port)
|
thread = threading.Thread(target=self.reconnect_port)
|
||||||
thread.daemon = True
|
thread.daemon = True
|
||||||
thread.start()
|
thread.start()
|
||||||
@@ -98,6 +154,7 @@ class WDCL():
|
|||||||
|
|
||||||
|
|
||||||
def open_port(self):
|
def open_port(self):
|
||||||
|
if not self.on_android:
|
||||||
if self.as_interface:
|
if self.as_interface:
|
||||||
RNS.log(f"Opening serial port {self.port}...", RNS.LOG_VERBOSE)
|
RNS.log(f"Opening serial port {self.port}...", RNS.LOG_VERBOSE)
|
||||||
target_port = self.port
|
target_port = self.port
|
||||||
@@ -118,22 +175,91 @@ class WDCL():
|
|||||||
write_timeout = None,
|
write_timeout = None,
|
||||||
dsrdtr = False)
|
dsrdtr = False)
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Get device parameters
|
||||||
|
from usb4a import usb
|
||||||
|
device = usb.get_usb_device(self.port)
|
||||||
|
if device:
|
||||||
|
vid = device.getVendorId()
|
||||||
|
pid = device.getProductId()
|
||||||
|
|
||||||
|
# Driver overrides for speficic chips
|
||||||
|
proxy = self.pyserial.get_serial_port
|
||||||
|
if vid == 0x1A86 and pid == 0x55D4:
|
||||||
|
# Force CDC driver for Qinheng CH34x
|
||||||
|
RNS.log(str(self)+" using CDC driver for "+RNS.hexrep(vid)+":"+RNS.hexrep(pid), RNS.LOG_DEBUG)
|
||||||
|
from usbserial4a.cdcacmserial4a import CdcAcmSerial
|
||||||
|
proxy = CdcAcmSerial
|
||||||
|
|
||||||
|
self.serial = proxy(
|
||||||
|
self.port,
|
||||||
|
baudrate = self.speed,
|
||||||
|
bytesize = self.databits,
|
||||||
|
parity = self.parity,
|
||||||
|
stopbits = self.stopbits,
|
||||||
|
xonxoff = False,
|
||||||
|
rtscts = False,
|
||||||
|
timeout = None,
|
||||||
|
inter_byte_timeout = None,
|
||||||
|
# write_timeout = wtimeout,
|
||||||
|
dsrdtr = False,
|
||||||
|
)
|
||||||
|
|
||||||
|
if vid == 0x0403:
|
||||||
|
# Hardware parameters for FTDI devices @ 115200 baud
|
||||||
|
self.serial.DEFAULT_READ_BUFFER_SIZE = 16 * 1024
|
||||||
|
self.serial.USB_READ_TIMEOUT_MILLIS = 100
|
||||||
|
self.serial.timeout = 0.1
|
||||||
|
elif vid == 0x10C4:
|
||||||
|
# Hardware parameters for SiLabs CP210x @ 115200 baud
|
||||||
|
self.serial.DEFAULT_READ_BUFFER_SIZE = 64
|
||||||
|
self.serial.USB_READ_TIMEOUT_MILLIS = 12
|
||||||
|
self.serial.timeout = 0.012
|
||||||
|
elif vid == 0x1A86 and pid == 0x55D4:
|
||||||
|
# Hardware parameters for Qinheng CH34x @ 115200 baud
|
||||||
|
self.serial.DEFAULT_READ_BUFFER_SIZE = 64
|
||||||
|
self.serial.USB_READ_TIMEOUT_MILLIS = 12
|
||||||
|
self.serial.timeout = 0.1
|
||||||
|
else:
|
||||||
|
# Default values
|
||||||
|
self.serial.DEFAULT_READ_BUFFER_SIZE = 1 * 1024
|
||||||
|
self.serial.USB_READ_TIMEOUT_MILLIS = 100
|
||||||
|
self.serial.timeout = 0.1
|
||||||
|
|
||||||
|
RNS.log(str(self)+" USB read buffer size set to "+RNS.prettysize(self.serial.DEFAULT_READ_BUFFER_SIZE), RNS.LOG_DEBUG)
|
||||||
|
RNS.log(str(self)+" USB read timeout set to "+str(self.serial.USB_READ_TIMEOUT_MILLIS)+"ms", RNS.LOG_DEBUG)
|
||||||
|
RNS.log(str(self)+" USB write timeout set to "+str(self.serial.USB_WRITE_TIMEOUT_MILLIS)+"ms", RNS.LOG_DEBUG)
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
self.should_run = False
|
self.should_run = False
|
||||||
|
self.online = False
|
||||||
|
self.wdcl_connected = False
|
||||||
if self.serial:
|
if self.serial:
|
||||||
self.serial.close()
|
self.serial.close()
|
||||||
if self.as_interface: RNS.LOG((f"Closed serial port {str(self.port)} for {str(self)}"), RNS.LOG_VERBOSE)
|
if self.as_interface: RNS.log((f"Closed serial port {str(self.port)} for {str(self)}"), RNS.LOG_VERBOSE)
|
||||||
else: self.owner.wlog(f"Closed serial port {str(self.port.device)} for {str(self)}")
|
else: self.owner.wlog(f"Closed serial port {str(self.port.device)} for {str(self)}")
|
||||||
|
|
||||||
def configure_device(self):
|
def configure_device(self):
|
||||||
thread = threading.Thread(target=self.read_loop)
|
thread = threading.Thread(target=self.read_loop)
|
||||||
thread.daemon = True
|
thread.daemon = True
|
||||||
thread.start()
|
thread.start()
|
||||||
self.online = True
|
|
||||||
if self.as_interface: RNS.log(f"Serial port {self.port} is now open, discovering remote device...", RNS.LOG_VERBOSE)
|
if self.as_interface: RNS.log(f"Serial port {self.port} is now open, discovering remote device...", RNS.LOG_VERBOSE)
|
||||||
else: self.owner.wlog("Serial port "+self.port.device+" is now open")
|
else: self.owner.wlog("Serial port "+self.port.device+" is now open")
|
||||||
self.device.discover()
|
self.device.discover()
|
||||||
|
|
||||||
|
if self.as_interface:
|
||||||
|
timeout = time.time() + self.WDCL_HANDSHAKE_TIMEOUT
|
||||||
|
while time.time() < timeout and not self.wdcl_connected: time.sleep(0.1)
|
||||||
|
if not self.wdcl_connected:
|
||||||
|
raise IOError(f"WDCL connection handshake timed out for {self}")
|
||||||
|
self.online = False
|
||||||
|
self.wdcl_connected = False
|
||||||
|
if self.serial:
|
||||||
|
try: self.serial.close()
|
||||||
|
except Exception as e: RNS.log("Error while cleaning serial connection: {e}", RNS.LOG_ERROR)
|
||||||
|
|
||||||
|
self.online = True
|
||||||
|
|
||||||
def process_incoming(self, data):
|
def process_incoming(self, data):
|
||||||
self.rxb += len(data)
|
self.rxb += len(data)
|
||||||
if self.device:
|
if self.device:
|
||||||
@@ -142,7 +268,7 @@ class WDCL():
|
|||||||
else: self.frame_queue.append(data)
|
else: self.frame_queue.append(data)
|
||||||
|
|
||||||
def process_outgoing(self, data):
|
def process_outgoing(self, data):
|
||||||
if self.online:
|
if self.serial.is_open:
|
||||||
data = bytes([HDLC.FLAG])+HDLC.escape(data)+bytes([HDLC.FLAG])
|
data = bytes([HDLC.FLAG])+HDLC.escape(data)+bytes([HDLC.FLAG])
|
||||||
written = self.serial.write(data)
|
written = self.serial.write(data)
|
||||||
self.txb += len(data)
|
self.txb += len(data)
|
||||||
@@ -176,6 +302,7 @@ class WDCL():
|
|||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.online = False
|
self.online = False
|
||||||
|
self.wdcl_connected = False
|
||||||
if self.should_run:
|
if self.should_run:
|
||||||
if self.as_interface:
|
if self.as_interface:
|
||||||
RNS.log("A serial port error occurred, the contained exception was: "+str(e), RNS.LOG_ERROR)
|
RNS.log("A serial port error occurred, the contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||||
@@ -185,12 +312,18 @@ 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
|
||||||
try: self.serial.close()
|
try: self.serial.close()
|
||||||
except: pass
|
except: pass
|
||||||
if self.should_run: self.reconnect_port()
|
if self.should_run: self.reconnect_port()
|
||||||
|
|
||||||
def reconnect_port(self):
|
def reconnect_port(self):
|
||||||
|
if self.reconnecting: return
|
||||||
|
self.reconnecting = True
|
||||||
|
self.wdcl_connected = False
|
||||||
while not self.online:
|
while not self.online:
|
||||||
try:
|
try:
|
||||||
time.sleep(5)
|
time.sleep(5)
|
||||||
@@ -202,6 +335,7 @@ class WDCL():
|
|||||||
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))
|
||||||
|
|
||||||
|
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))
|
||||||
|
|
||||||
@@ -255,6 +389,7 @@ class Evt():
|
|||||||
ET_PROTO_WDCL_INIT = 0x3000
|
ET_PROTO_WDCL_INIT = 0x3000
|
||||||
ET_PROTO_WDCL_RUNNING = 0x3001
|
ET_PROTO_WDCL_RUNNING = 0x3001
|
||||||
ET_PROTO_WDCL_CONNECTION = 0x3002
|
ET_PROTO_WDCL_CONNECTION = 0x3002
|
||||||
|
ET_PROTO_WDCL_HOST_ENDPOINT = 0x3003
|
||||||
ET_PROTO_WEAVE_INIT = 0x3100
|
ET_PROTO_WEAVE_INIT = 0x3100
|
||||||
ET_PROTO_WEAVE_RUNNING = 0x3101
|
ET_PROTO_WEAVE_RUNNING = 0x3101
|
||||||
ET_PROTO_WEAVE_EP_ALIVE = 0x3102
|
ET_PROTO_WEAVE_EP_ALIVE = 0x3102
|
||||||
@@ -320,6 +455,7 @@ class Evt():
|
|||||||
ET_PROTO_WDCL_INIT: "WDCL protocol initialization",
|
ET_PROTO_WDCL_INIT: "WDCL protocol initialization",
|
||||||
ET_PROTO_WDCL_RUNNING: "WDCL protocol activation",
|
ET_PROTO_WDCL_RUNNING: "WDCL protocol activation",
|
||||||
ET_PROTO_WDCL_CONNECTION: "WDCL host connection",
|
ET_PROTO_WDCL_CONNECTION: "WDCL host connection",
|
||||||
|
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 appeared",
|
||||||
@@ -453,6 +589,7 @@ class WeaveDevice():
|
|||||||
self.identity = None
|
self.identity = None
|
||||||
self.receiver = None
|
self.receiver = None
|
||||||
self.switch_id = None
|
self.switch_id = None
|
||||||
|
self.endpoint_id = None
|
||||||
self.owner = None
|
self.owner = None
|
||||||
self.rns_interface = rns_interface
|
self.rns_interface = rns_interface
|
||||||
self.as_interface = as_interface
|
self.as_interface = as_interface
|
||||||
@@ -507,7 +644,8 @@ class WeaveDevice():
|
|||||||
data = self.connection.switch_pub_bytes
|
data = self.connection.switch_pub_bytes
|
||||||
data += signature
|
data += signature
|
||||||
self.wdcl_send(WDCL.WDCL_T_CONNECT, data)
|
self.wdcl_send(WDCL.WDCL_T_CONNECT, data)
|
||||||
if not self.as_interface: self.receiver.log("Connection handshake sent")
|
if self.as_interface: RNS.log(f"WDCL connection handshake sent", RNS.LOG_VERBOSE)
|
||||||
|
else: self.receiver.log("Connection handshake sent")
|
||||||
|
|
||||||
def capture_stats_cpu(self):
|
def capture_stats_cpu(self):
|
||||||
self.cpu_stats.append({"timestamp": time.time(), "cpu_load": self.cpu_load})
|
self.cpu_stats.append({"timestamp": time.time(), "cpu_load": self.cpu_load})
|
||||||
@@ -620,6 +758,8 @@ class WeaveDevice():
|
|||||||
|
|
||||||
def log_handle(self, frame):
|
def log_handle(self, frame):
|
||||||
# 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_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) == 8:
|
||||||
self.endpoint_alive(frame.data)
|
self.endpoint_alive(frame.data)
|
||||||
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() }
|
||||||
@@ -694,6 +834,26 @@ class WeaveInterface(Interface):
|
|||||||
MULTI_IF_DEQUE_LEN = 48
|
MULTI_IF_DEQUE_LEN = 48
|
||||||
MULTI_IF_DEQUE_TTL = 0.75
|
MULTI_IF_DEQUE_TTL = 0.75
|
||||||
|
|
||||||
|
@property
|
||||||
|
def cpu_load(self):
|
||||||
|
if not self.device: return None
|
||||||
|
else: return self.device.cpu_load
|
||||||
|
|
||||||
|
@property
|
||||||
|
def mem_load(self):
|
||||||
|
if not self.device: return None
|
||||||
|
else: return self.device.memory_used_pct
|
||||||
|
|
||||||
|
@property
|
||||||
|
def switch_id(self):
|
||||||
|
if not self.device: return None
|
||||||
|
else: return self.device.switch_id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def endpoint_id(self):
|
||||||
|
if not self.device: return None
|
||||||
|
else: return self.device.endpoint_id
|
||||||
|
|
||||||
def __init__(self, owner, configuration):
|
def __init__(self, owner, configuration):
|
||||||
c = Interface.get_config_obj(configuration)
|
c = Interface.get_config_obj(configuration)
|
||||||
name = c["name"]
|
name = c["name"]
|
||||||
@@ -710,10 +870,8 @@ class WeaveInterface(Interface):
|
|||||||
self.name = name
|
self.name = name
|
||||||
self.port = port
|
self.port = port
|
||||||
self.switch_identity = RNS.Identity()
|
self.switch_identity = RNS.Identity()
|
||||||
self.device = WeaveDevice(as_interface=True, rns_interface=self)
|
|
||||||
self.connection = WDCL(owner=self, device=self.device, port=self.port, as_interface=True)
|
|
||||||
self.owner = owner
|
self.owner = owner
|
||||||
self.online = False
|
self._online = False
|
||||||
self.final_init_done = False
|
self.final_init_done = False
|
||||||
self.peers = {}
|
self.peers = {}
|
||||||
self.timed_out_interfaces = {}
|
self.timed_out_interfaces = {}
|
||||||
@@ -730,14 +888,15 @@ class WeaveInterface(Interface):
|
|||||||
if configured_bitrate != None: self.bitrate = configured_bitrate
|
if configured_bitrate != None: self.bitrate = configured_bitrate
|
||||||
else: self.bitrate = WeaveInterface.BITRATE_GUESS
|
else: self.bitrate = WeaveInterface.BITRATE_GUESS
|
||||||
|
|
||||||
self.final_init()
|
|
||||||
|
|
||||||
def final_init(self):
|
def final_init(self):
|
||||||
|
self.device = WeaveDevice(as_interface=True, rns_interface=self)
|
||||||
|
self.connection = WDCL(owner=self, device=self.device, port=self.port, as_interface=True)
|
||||||
|
|
||||||
job_thread = threading.Thread(target=self.peer_jobs)
|
job_thread = threading.Thread(target=self.peer_jobs)
|
||||||
job_thread.daemon = True
|
job_thread.daemon = True
|
||||||
job_thread.start()
|
job_thread.start()
|
||||||
|
|
||||||
self.online = True
|
self._online = True
|
||||||
self.final_init_done = True
|
self.final_init_done = True
|
||||||
|
|
||||||
def peer_jobs(self):
|
def peer_jobs(self):
|
||||||
@@ -760,7 +919,7 @@ class WeaveInterface(Interface):
|
|||||||
spawned_interface = self.spawned_interfaces[peer_addr]
|
spawned_interface = self.spawned_interfaces[peer_addr]
|
||||||
spawned_interface.detach()
|
spawned_interface.detach()
|
||||||
spawned_interface.teardown()
|
spawned_interface.teardown()
|
||||||
RNS.log(str(self)+" removed peer "+str(peer_addr)+" on "+str(removed_peer[0]), RNS.LOG_DEBUG)
|
RNS.log(str(self)+" removed peer "+RNS.hexrep(peer_addr)+" on "+RNS.hexrep(removed_peer[0]), RNS.LOG_DEBUG)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def peer_count(self):
|
def peer_count(self):
|
||||||
@@ -801,7 +960,7 @@ class WeaveInterface(Interface):
|
|||||||
spawned_interface.announce_rate_penalty = self.announce_rate_penalty
|
spawned_interface.announce_rate_penalty = self.announce_rate_penalty
|
||||||
spawned_interface.mode = self.mode
|
spawned_interface.mode = self.mode
|
||||||
spawned_interface.HW_MTU = self.HW_MTU
|
spawned_interface.HW_MTU = self.HW_MTU
|
||||||
spawned_interface.online = True
|
spawned_interface._online = True
|
||||||
RNS.Transport.interfaces.append(spawned_interface)
|
RNS.Transport.interfaces.append(spawned_interface)
|
||||||
if endpoint_addr in self.spawned_interfaces:
|
if endpoint_addr in self.spawned_interfaces:
|
||||||
self.spawned_interfaces[endpoint_addr].detach()
|
self.spawned_interfaces[endpoint_addr].detach()
|
||||||
@@ -830,7 +989,16 @@ class WeaveInterface(Interface):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def detach(self):
|
def detach(self):
|
||||||
self.online = False
|
self._online = False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def online(self):
|
||||||
|
if not self._online: return False
|
||||||
|
else: return self.connection.online
|
||||||
|
|
||||||
|
@online.setter
|
||||||
|
def online(self, value):
|
||||||
|
self._online = value
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "WeaveInterface["+self.name+"]"
|
return "WeaveInterface["+self.name+"]"
|
||||||
@@ -846,12 +1014,22 @@ class WeaveInterfacePeer(Interface):
|
|||||||
self.addr_info = None
|
self.addr_info = None
|
||||||
self.HW_MTU = self.owner.HW_MTU
|
self.HW_MTU = self.owner.HW_MTU
|
||||||
self.FIXED_MTU = self.owner.FIXED_MTU
|
self.FIXED_MTU = self.owner.FIXED_MTU
|
||||||
|
self._online = False
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"WeaveInterfacePeer[{RNS.hexrep(self.endpoint_addr)}]"
|
return f"WeaveInterfacePeer[{RNS.hexrep(self.endpoint_addr)}]"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def online(self):
|
||||||
|
if not self._online or not self.owner: return false
|
||||||
|
else: return self.owner.online
|
||||||
|
|
||||||
|
@online.setter
|
||||||
|
def online(self, value):
|
||||||
|
self._online = value
|
||||||
|
|
||||||
def process_incoming(self, data, endpoint_addr=None):
|
def process_incoming(self, data, endpoint_addr=None):
|
||||||
if self.online and self.owner.online:
|
if self.online:
|
||||||
data_hash = RNS.Identity.full_hash(data)
|
data_hash = RNS.Identity.full_hash(data)
|
||||||
deque_hit = False
|
deque_hit = False
|
||||||
if data_hash in self.owner.mif_deque:
|
if data_hash in self.owner.mif_deque:
|
||||||
@@ -879,7 +1057,7 @@ class WeaveInterfacePeer(Interface):
|
|||||||
RNS.log("Could not transmit on "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
|
RNS.log("Could not transmit on "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||||
|
|
||||||
def detach(self):
|
def detach(self):
|
||||||
self.online = False
|
self._online = False
|
||||||
self.detached = True
|
self.detached = True
|
||||||
|
|
||||||
def teardown(self):
|
def teardown(self):
|
||||||
@@ -888,10 +1066,9 @@ class WeaveInterfacePeer(Interface):
|
|||||||
if RNS.Reticulum.panic_on_interface_error:
|
if RNS.Reticulum.panic_on_interface_error:
|
||||||
RNS.panic()
|
RNS.panic()
|
||||||
|
|
||||||
else:
|
else: RNS.log("The interface "+str(self)+" is being torn down.", RNS.LOG_VERBOSE)
|
||||||
RNS.log("The interface "+str(self)+" is being torn down.", RNS.LOG_VERBOSE)
|
|
||||||
|
|
||||||
self.online = False
|
self._online = False
|
||||||
self.OUT = False
|
self.OUT = False
|
||||||
self.IN = False
|
self.IN = False
|
||||||
|
|
||||||
|
|||||||
@@ -805,6 +805,7 @@ class Reticulum:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
RNS.log("The interface \""+name+"\" could not be created. Check your configuration file for errors!", RNS.LOG_ERROR)
|
RNS.log("The interface \""+name+"\" could not be created. Check your configuration file for errors!", RNS.LOG_ERROR)
|
||||||
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
|
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||||
|
RNS.trace_exception(e)
|
||||||
RNS.panic()
|
RNS.panic()
|
||||||
else:
|
else:
|
||||||
RNS.log("The interface name \""+name+"\" was already used. Check your configuration file for errors!", RNS.LOG_ERROR)
|
RNS.log("The interface name \""+name+"\" was already used. Check your configuration file for errors!", RNS.LOG_ERROR)
|
||||||
@@ -1026,6 +1027,20 @@ class Reticulum:
|
|||||||
if hasattr(interface, "r_noise_floor"):
|
if hasattr(interface, "r_noise_floor"):
|
||||||
ifstats["noise_floor"] = interface.r_noise_floor
|
ifstats["noise_floor"] = interface.r_noise_floor
|
||||||
|
|
||||||
|
if hasattr(interface, "cpu_load"):
|
||||||
|
ifstats["cpu_load"] = interface.cpu_load
|
||||||
|
|
||||||
|
if hasattr(interface, "mem_load"):
|
||||||
|
ifstats["mem_load"] = interface.mem_load
|
||||||
|
|
||||||
|
if hasattr(interface, "switch_id"):
|
||||||
|
if interface.switch_id != None: ifstats["switch_id"] = RNS.hexrep(interface.switch_id)
|
||||||
|
else: ifstats["switch_id"] = None
|
||||||
|
|
||||||
|
if hasattr(interface, "endpoint_id"):
|
||||||
|
if interface.endpoint_id != None: ifstats["endpoint_id"] = RNS.hexrep(interface.endpoint_id)
|
||||||
|
else: ifstats["endpoint_id"] = None
|
||||||
|
|
||||||
if hasattr(interface, "r_battery_state"):
|
if hasattr(interface, "r_battery_state"):
|
||||||
if interface.r_battery_state != 0x00:
|
if interface.r_battery_state != 0x00:
|
||||||
ifstats["battery_state"] = interface.get_battery_state_string()
|
ifstats["battery_state"] = interface.get_battery_state_string()
|
||||||
|
|||||||
@@ -263,6 +263,7 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None, json=
|
|||||||
name.startswith("TCPInterface[Client") or
|
name.startswith("TCPInterface[Client") or
|
||||||
name.startswith("BackboneInterface[Client on") or
|
name.startswith("BackboneInterface[Client on") or
|
||||||
name.startswith("AutoInterfacePeer[") or
|
name.startswith("AutoInterfacePeer[") or
|
||||||
|
name.startswith("WeaveInterfacePeer[") or
|
||||||
name.startswith("I2PInterfacePeer[Connected peer") or
|
name.startswith("I2PInterfacePeer[Connected peer") or
|
||||||
(name.startswith("I2PInterface[") and ("i2p_connectable" in ifstat and ifstat["i2p_connectable"] == False))
|
(name.startswith("I2PInterface[") and ("i2p_connectable" in ifstat and ifstat["i2p_connectable"] == False))
|
||||||
):
|
):
|
||||||
@@ -339,6 +340,14 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None, json=
|
|||||||
else:
|
else:
|
||||||
print(" Noise Fl. : Unknown")
|
print(" Noise Fl. : Unknown")
|
||||||
|
|
||||||
|
if "cpu_load" in ifstat:
|
||||||
|
if ifstat["cpu_load"] != None: print(" CPU load : {v} %".format(v=str(ifstat["cpu_load"])))
|
||||||
|
else: print(" CPU load : Unknown")
|
||||||
|
|
||||||
|
if "mem_load" in ifstat:
|
||||||
|
if ifstat["cpu_load"] != None: print(" Mem usage : {v} %".format(v=str(ifstat["mem_load"])))
|
||||||
|
else: print(" Mem usage : Unknown")
|
||||||
|
|
||||||
if "battery_percent" in ifstat and ifstat["battery_percent"] != None:
|
if "battery_percent" in ifstat and ifstat["battery_percent"] != None:
|
||||||
try:
|
try:
|
||||||
bpi = int(ifstat["battery_percent"])
|
bpi = int(ifstat["battery_percent"])
|
||||||
@@ -353,6 +362,14 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None, json=
|
|||||||
if "channel_load_short" in ifstat and "channel_load_long" in ifstat:
|
if "channel_load_short" in ifstat and "channel_load_long" in ifstat:
|
||||||
print(" Ch. Load : {ats}% (15s), {atl}% (1h)".format(ats=str(ifstat["channel_load_short"]),atl=str(ifstat["channel_load_long"])))
|
print(" Ch. Load : {ats}% (15s), {atl}% (1h)".format(ats=str(ifstat["channel_load_short"]),atl=str(ifstat["channel_load_long"])))
|
||||||
|
|
||||||
|
if "switch_id" in ifstat:
|
||||||
|
if ifstat["switch_id"] != None: print(" Switch ID : {v}".format(v=str(ifstat["switch_id"])))
|
||||||
|
else: print(" Switch ID : Unknown")
|
||||||
|
|
||||||
|
if "endpoint_id" in ifstat:
|
||||||
|
if ifstat["endpoint_id"] != None: print(" Endpoint : {v}".format(v=str(ifstat["endpoint_id"])))
|
||||||
|
else: print(" Endpoint : 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"]))
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user