WIP - adding TCP client, still need to use recv/send instead of read/write

This commit is contained in:
geeksville
2020-09-19 12:53:19 -07:00
parent e4a424ccee
commit d789bff9cb
3 changed files with 104 additions and 43 deletions

View File

@@ -47,12 +47,13 @@ def onConnection(interface, topic=pub.AUTO_TOPIC): # called when we (re)connect
pub.subscribe(onReceive, "meshtastic.receive")
pub.subscribe(onConnection, "meshtastic.connection.established")
# By default will try to find a meshtastic device, otherwise provide a device path like /dev/ttyUSB0
interface = meshtastic.StreamInterface()
interface = meshtastic.SerialInterface()
```
"""
import socket
import pygatt
import google.protobuf.json_format
import serial
@@ -417,9 +418,8 @@ class BLEInterface(MeshInterface):
class StreamInterface(MeshInterface):
"""Interface class for meshtastic devices over a stream link (serial, TCP, etc)"""
def __init__(self, devPath=None, debugOut=None, noProto=False, connectNow=True):
"""Constructor, opens a connection to a specified serial port, or if unspecified try to
find one Meshtastic device by probing
def __init__(self, debugOut=None, noProto=False, connectNow=True):
"""Constructor, opens a connection to self.stream
Keyword Arguments:
devPath {string} -- A filepath to a device, i.e. /dev/ttyUSB0 (default: {None})
@@ -430,35 +430,12 @@ class StreamInterface(MeshInterface):
Exception: [description]
"""
if devPath is None:
ports = util.findPorts()
if len(ports) == 0:
raise Exception("No Meshtastic devices detected")
elif len(ports) > 1:
raise Exception(
f"Multiple ports detected, you must specify a device, such as {ports[0].device}")
else:
devPath = ports[0]
logging.debug(f"Connecting to {devPath}")
self.devPath = devPath
if not hasattr(self, 'stream'):
raise Exception(
"StreamInterface is now abstract (to update existing code create SerialInterface instead)")
self._rxBuf = bytes() # empty
self._wantExit = False
# Note: we provide None for port here, because we will be opening it later
self.stream = serial.Serial(
None, 921600, exclusive=True, timeout=0.5)
# rts=False Needed to prevent TBEAMs resetting on OSX, because rts is connected to reset
self.stream.port = devPath
# OS-X seems to have a bug in its serial driver. It ignores that we asked for no RTSCTS
# control and will always drive RTS either high or low (rather than letting the CP102 leave
# it as an open-collector floating pin). Since it is going to drive it anyways we want to make
# sure it is driven low, so that the TBEAM won't reset
if platform.system() == 'Darwin':
self.stream.rts = False
self.stream.open()
self._rxThread = threading.Thread(target=self.__reader, args=())
MeshInterface.__init__(self, debugOut=debugOut, noProto=noProto)
@@ -481,11 +458,19 @@ class StreamInterface(MeshInterface):
self._rxThread.start()
def _disconnected(self):
"""We override the superclass implementation to close our port"""
MeshInterface._disconnected(self)
logging.debug("Closing our port")
self.stream.close()
def _sendToRadio(self, toRadio):
"""Send a ToRadio protobuf to the device"""
logging.debug(f"Sending: {toRadio}")
b = toRadio.SerializeToString()
bufLen = len(b)
# We convert into a string, because the TCP code doesn't work with byte arrays
header = bytes([START1, START2, (bufLen >> 8) & 0xff, bufLen & 0xff])
self.stream.write(header)
self.stream.write(b)
@@ -543,14 +528,82 @@ class StreamInterface(MeshInterface):
traceback.print_exc()
self._rxBuf = empty
else:
# logging.debug(f"timeout on {self.devPath}")
# logging.debug(f"timeout")
pass
except serial.SerialException as ex:
logging.warn(
f"Meshtastic serial port disconnected, disconnecting... {ex}")
finally:
logging.debug("reader is exiting")
if platform.system() == 'Darwin':
self.stream.rts = True # Return RTS high, so that the reset button still works
self.stream.close()
self._disconnected()
class SerialInterface(StreamInterface):
"""Interface class for meshtastic devices over a serial link"""
def __init__(self, devPath=None, debugOut=None, noProto=False, connectNow=True):
"""Constructor, opens a connection to a specified serial port, or if unspecified try to
find one Meshtastic device by probing
Keyword Arguments:
devPath {string} -- A filepath to a device, i.e. /dev/ttyUSB0 (default: {None})
debugOut {stream} -- If a stream is provided, any debug serial output from the device will be emitted to that stream. (default: {None})
"""
if devPath is None:
ports = util.findPorts()
if len(ports) == 0:
raise Exception("No Meshtastic devices detected")
elif len(ports) > 1:
raise Exception(
f"Multiple ports detected, you must specify a device, such as {ports[0].device}")
else:
devPath = ports[0]
logging.debug(f"Connecting to {devPath}")
# Note: we provide None for port here, because we will be opening it later
self.stream = serial.Serial(
None, 921600, exclusive=True, timeout=0.5)
# rts=False Needed to prevent TBEAMs resetting on OSX, because rts is connected to reset
self.stream.port = devPath
# OS-X seems to have a bug in its serial driver. It ignores that we asked for no RTSCTS
# control and will always drive RTS either high or low (rather than letting the CP102 leave
# it as an open-collector floating pin). Since it is going to drive it anyways we want to make
# sure it is driven low, so that the TBEAM won't reset
if platform.system() == 'Darwin':
self.stream.rts = False
self.stream.open()
StreamInterface.__init__(
self, debugOut=debugOut, noProto=noProto, connectNow=connectNow)
def _disconnected(self):
"""We override the superclass implementation to close our port"""
if platform.system() == 'Darwin':
self.stream.rts = True # Return RTS high, so that the reset button still works
StreamInterface._disconnected(self)
class TCPInterface(StreamInterface):
"""Interface class for meshtastic devices over a TCP link"""
def __init__(self, hostname, debugOut=None, noProto=False, connectNow=True, portNumber=4403):
"""Constructor, opens a connection to a specified IP address/hostname
Keyword Arguments:
hostname {string} -- Hostname/IP address of the device to connect to
"""
logging.debug(f"Connecting to {hostname}")
server_address = (hostname, portNumber)
sock = socket.create_connection(server_address)
self.stream = sock.makefile('rw')
StreamInterface.__init__(
self, debugOut=debugOut, noProto=noProto, connectNow=connectNow)

View File

@@ -1,7 +1,7 @@
#!python3
import argparse
from . import StreamInterface, BLEInterface, test
from . import SerialInterface, TCPInterface, BLEInterface, test
import logging
import sys
from pubsub import pub
@@ -162,10 +162,15 @@ def main():
parser = argparse.ArgumentParser()
parser.add_argument(
"--device",
"--port",
help="The port the Meshtastic device is connected to, i.e. /dev/ttyUSB0. If unspecified, we'll try to find it.",
default=None)
parser.add_argument(
"--host",
help="The hostname/ipaddr of the device to connect to (over TCP)",
default=None)
parser.add_argument(
"--seriallog",
help="Log device serial output to either 'stdout', 'none' or a filename to append to. Defaults to stdout.",
@@ -205,8 +210,8 @@ def main():
parser.add_argument("--test", help="Run stress test against all connected Meshtastic devices",
action="store_true")
parser.add_argument("--ble", help="hack for testing BLE code (BLE is not yet supported for this tool)",
action="store_true")
parser.add_argument("--ble", help="BLE mac address to connect to (BLE is not yet supported for this tool)",
default=None)
parser.add_argument("--noproto", help="Don't start the API, just function as a dumb serial terminal.",
action="store_true")
@@ -233,10 +238,13 @@ def main():
subscribe()
if args.ble:
client = BLEInterface(args.device, debugOut=logfile)
client = BLEInterface(args.ble, debugOut=logfile)
elif args.host:
client = TCPInterface(
args.host, debugOut=logfile, noProto=args.noproto)
else:
client = StreamInterface(
args.device, debugOut=logfile, noProto=args.noproto)
client = SerialInterface(
args.port, debugOut=logfile, noProto=args.noproto)
if __name__ == "__main__":

View File

@@ -1,6 +1,6 @@
import logging
from . import util
from . import StreamInterface, BROADCAST_NUM
from . import SerialInterface, BROADCAST_NUM
from pubsub import pub
import time
import sys
@@ -128,7 +128,7 @@ def testAll():
pub.subscribe(onConnection, "meshtastic.connection")
pub.subscribe(onReceive, "meshtastic.receive")
global interfaces
interfaces = list(map(lambda port: StreamInterface(
interfaces = list(map(lambda port: SerialInterface(
port, debugOut=openDebugLog(port), connectNow=False), ports))
for i in interfaces:
i.connect()