basic stress tester

This commit is contained in:
geeksville
2020-05-01 19:28:01 -07:00
parent 74d134d96a
commit 6df00038bc
6 changed files with 164 additions and 21 deletions

3
.gitignore vendored
View File

@@ -1,4 +1,5 @@
README
build
dist
*.egg-info
*.egg-info
log_*

View File

@@ -47,13 +47,14 @@ interface = meshtastic.StreamInterface() # By default will try to find a meshtas
import google.protobuf.json_format
import serial
import serial.tools.list_ports
import threading
import logging
import sys
import traceback
from . import mesh_pb2
from . import util
from pubsub import pub
from dotmap import DotMap
START1 = 0x94
START2 = 0xc3
@@ -68,12 +69,19 @@ MY_CONFIG_ID = 42
class MeshInterface:
"""Interface class for meshtastic devices
Properties:
isConnected
nodes
debugOut
"""
def __init__(self, debugOut=None):
"""Constructor"""
self.debugOut = debugOut
self.nodes = None # FIXME
self.isConnected = False
self._startConfig()
def sendText(self, text, destinationId=BROADCAST_ADDR):
@@ -106,8 +114,15 @@ class MeshInterface:
def _disconnected(self):
"""Called by subclasses to tell clients this interface has disconnected"""
self.isConnected = False
pub.sendMessage("meshtastic.connection.lost")
def _connected(self):
"""Called by this class to tell clients we are now fully connected to a node
"""
self.isConnected = True
pub.sendMessage("meshtastic.connection.established")
def _startConfig(self):
"""Start device packets flowing"""
self.myInfo = None
@@ -142,7 +157,7 @@ class MeshInterface:
self.nodes[node.user.id] = node
elif fromRadio.config_complete_id == MY_CONFIG_ID:
# we ignore the config_complete_id, it is unneeded for our stream API fromRadio.config_complete_id
pub.sendMessage("meshtastic.connection.established")
self._connected()
elif fromRadio.HasField("packet"):
self._handlePacketFromRadio(fromRadio.packet)
elif fromRadio.rebooted:
@@ -160,15 +175,16 @@ class MeshInterface:
- meshtastic.receive.data(packet = MeshPacket dictionary)
"""
# FIXME, update node DB as needed
json = google.protobuf.json_format.MessageToDict(meshPacket)
# We provide our objects as DotMaps - which work with . notation or as dictionaries
asObj = DotMap(google.protobuf.json_format.MessageToDict(meshPacket))
if meshPacket.payload.HasField("position"):
pub.sendMessage("meshtastic.receive.position", packet=json)
pub.sendMessage("meshtastic.receive.position", packet=asObj)
if meshPacket.payload.HasField("user"):
pub.sendMessage("meshtastic.receive.user",
packet=json)
packet=asObj)
if meshPacket.payload.HasField("data"):
pub.sendMessage("meshtastic.receive.data",
packet=json)
packet=asObj)
class StreamInterface(MeshInterface):
@@ -188,15 +204,14 @@ class StreamInterface(MeshInterface):
"""
if devPath is None:
ports = list(filter(lambda port: port.vid != None,
serial.tools.list_ports.comports()))
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].device
devPath = ports[0]
logging.debug(f"Connecting to {devPath}")
self._rxBuf = bytes() # empty

View File

@@ -1,7 +1,7 @@
#!python3
import argparse
from . import StreamInterface
from . import StreamInterface, test
import logging
import sys
from pubsub import pub
@@ -47,20 +47,26 @@ def main():
parser.add_argument("--debug", help="Show API library debug log messages",
action="store_true")
parser.add_argument("--test", help="Run stress test against all connected Meshtastic devices",
action="store_true")
args = parser.parse_args()
logging.basicConfig(level=logging.DEBUG if args.debug else logging.INFO)
if args.seriallog == "stdout":
logfile = sys.stdout
elif args.seriallog == "none":
logging.debug("Not logging serial output")
logfile = None
if args.test:
test.testAll()
else:
logging.info(f"Logging serial output to {args.seriallog}")
logfile = open(args.seriallog, 'w+', buffering=1) # line buffering
if args.seriallog == "stdout":
logfile = sys.stdout
elif args.seriallog == "none":
logging.debug("Not logging serial output")
logfile = None
else:
logging.info(f"Logging serial output to {args.seriallog}")
logfile = open(args.seriallog, 'w+', buffering=1) # line buffering
subscribe()
client = StreamInterface(args.device, debugOut=logfile)
subscribe()
client = StreamInterface(args.device, debugOut=logfile)
if __name__ == "__main__":

104
meshtastic/test.py Normal file
View File

@@ -0,0 +1,104 @@
import logging
from . import util
from . import StreamInterface
from pubsub import pub
import time
"""The interfaces we are using for our tests"""
interfaces = None
"""A list of all packets we received while the current test was running"""
receivedPackets = None
testsRunning = False
testNumber = 0
def onReceive(packet):
"""Callback invoked when a packet arrives"""
print(f"Received: {packet}")
if packet.payload.data.typ == "CLEAR_TEXT":
# We only care a about clear text packets
receivedPackets.extend(packet)
def onNode(node):
"""Callback invoked when the node DB changes"""
print(f"Node changed: {node}")
def subscribe():
"""Subscribe to the topics the user probably wants to see, prints output to stdout"""
pub.subscribe(onNode, "meshtastic.node")
def testSend(fromInterface, toInterface):
"""
Sends one test packet between two nodes and then returns success or failure
Arguments:
fromInterface {[type]} -- [description]
toInterface {[type]} -- [description]
Returns:
boolean -- True for success
"""
global receivedPackets
receivedPackets = []
fromNode = fromInterface.myInfo.my_node_num
toNode = toInterface.myInfo.my_node_num
logging.info(f"Sending test packet from {fromNode} to {toNode}")
fromInterface.sendText(f"Test {testNumber}", toNode)
time.sleep(10)
if (len(receivedPackets) < 1):
logging.error("Test failed, expected packet not received")
return True
else:
logging.info("Test succeeded")
return False
def startTests():
logging.info("Found devices, starting tests...")
while True:
global testNumber
testNumber = testNumber + 1
testSend(interfaces[0], interfaces[1])
time.sleep(20)
def onConnection(topic=pub.AUTO_TOPIC):
"""Callback invoked when we connect/disconnect from a radio"""
print(f"Connection changed: {topic.getName()}")
global testsRunning
if (all(iface.isConnected for iface in interfaces) and not testsRunning):
testsRunning = True
startTests()
def openDebugLog(portName):
debugname = "log" + portName.replace("/", "_")
logging.info(f"Writing serial debugging to {debugname}")
return open(debugname, 'w+', buffering=1)
def testAll():
"""
Run a series of tests using devices we can find.
Raises:
Exception: If not enough devices are found
"""
ports = util.findPorts()
if (len(ports) != 2):
raise Exception("Must have at least two devices connected to USB")
pub.subscribe(onConnection, "meshtastic.connection")
pub.subscribe(onReceive, "meshtastic.receive")
global interfaces
interfaces = list(map(lambda port: StreamInterface(
port, debugOut=openDebugLog(port)), ports))
logging.info("Ports opened, waiting for device to complete connection")

16
meshtastic/util.py Normal file
View File

@@ -0,0 +1,16 @@
import serial
import serial.tools.list_ports
def findPorts():
"""Find all ports that might have meshtastic devices
Returns:
list -- a list of device paths
"""
l = list(map(lambda port: port.device,
filter(lambda port: port.vid != None,
serial.tools.list_ports.comports())))
l.sort()
return l

View File

@@ -25,7 +25,8 @@ setup(
],
packages=["meshtastic"],
include_package_data=True,
install_requires=["pyserial>=3.4", "protobuf>=3.6.1", "pypubsub>=4.0.3"],
install_requires=["pyserial>=3.4", "protobuf>=3.6.1",
"pypubsub>=4.0.3", "dotmap>=1.3.14"],
python_requires='>=3',
entry_points={
"console_scripts": [