reformat code

This commit is contained in:
Kevin Hester
2021-03-27 10:31:48 +08:00
parent fedf5c2a10
commit 61eb251369
7 changed files with 132 additions and 72 deletions

View File

@@ -97,6 +97,7 @@ OUR_APP_VERSION = 20200
publishingThread = DeferredExecution("publishing")
class ResponseHandler(NamedTuple):
"""A pending response callback, waiting for a response to one of our messages"""
# requestId: int - used only as a key
@@ -127,7 +128,7 @@ def pskToString(psk: bytes):
"""Given an array of PSK bytes, decode them into a human readable (but privacy protecting) string"""
if len(psk) == 0:
return "unencrypted"
elif len(psk) == 1:
elif len(psk) == 1:
b = psk[0]
if b == 0:
return "unencrypted"
@@ -138,6 +139,7 @@ def pskToString(psk: bytes):
else:
return "secret"
class Node:
"""A model of a (local or remote) node in the mesh
@@ -157,19 +159,20 @@ class Node:
for c in self.channels:
if c.role != channel_pb2.Channel.Role.DISABLED:
cStr = stripnl(MessageToJson(c.settings))
print(f" {channel_pb2.Channel.Role.Name(c.role)} psk={pskToString(c.settings.psk)} {cStr}")
publicURL = self.getURL(includeAll = False)
adminURL = self.getURL(includeAll = True)
print(
f" {channel_pb2.Channel.Role.Name(c.role)} psk={pskToString(c.settings.psk)} {cStr}")
publicURL = self.getURL(includeAll=False)
adminURL = self.getURL(includeAll=True)
print(f"\nPrimary channel URL: {publicURL}")
if adminURL != publicURL:
print(f"Complete URL (includes all channels): {adminURL}")
def showInfo(self):
"""Show human readable description of our node"""
print(f"Preferences: {stripnl(MessageToJson(self.radioConfig.preferences))}\n")
print(
f"Preferences: {stripnl(MessageToJson(self.radioConfig.preferences))}\n")
self.showChannels()
def requestConfig(self):
"""
Send regular MeshPackets to ask for settings and channels
@@ -195,7 +198,7 @@ class Node:
self._sendAdmin(p)
logging.debug("Wrote config")
def writeChannel(self, channelIndex, adminIndex = 0):
def writeChannel(self, channelIndex, adminIndex=0):
"""Write the current (edited) channel to the device"""
p = admin_pb2.AdminMessage()
@@ -215,7 +218,7 @@ class Node:
adminIndex = self.iface.localNode._getAdminChannelIndex()
self.channels.pop(channelIndex)
self._fixupChannels() # expand back to 8 channels
self._fixupChannels() # expand back to 8 channels
index = channelIndex
while index < self.iface.myInfo.max_channels:
@@ -224,7 +227,8 @@ class Node:
# if we are updating the local node, we might end up *moving* the admin channel index as we are writing
if (self.iface.localNode == self) and index >= adminIndex:
adminIndex = 0 # We've now passed the old location for admin index (and writen it), so we can start finding it by name again
# We've now passed the old location for admin index (and writen it), so we can start finding it by name again
adminIndex = 0
def getChannelByName(self, name):
"""Try to find the named channel or return None"""
@@ -332,7 +336,7 @@ class Node:
"""A closure to handle the response packet"""
self.radioConfig = p["decoded"]["admin"]["raw"].get_radio_response
logging.debug("Received radio config, now fetching channels...")
self._requestChannel(0) # now start fetching channels
self._requestChannel(0) # now start fetching channels
return self._sendAdmin(p,
wantResponse=True,
@@ -345,14 +349,24 @@ class Node:
p = admin_pb2.AdminMessage()
p.exit_simulator = True
return self._sendAdmin(p)
return self._sendAdmin(p)
def reboot(self, secs: int = 10):
"""
Tell the node to reboot
"""
p = admin_pb2.AdminMessage()
p.reboot_seconds = secs
logging.info(f"Telling node to reboot in {secs} seconds")
return self._sendAdmin(p)
def _fixupChannels(self):
"""Fixup indexes and add disabled channels as needed"""
# Add extra disabled channels as needed
for index, ch in enumerate(self.channels):
ch.index = index # fixup indexes
ch.index = index # fixup indexes
self._fillChannels()
@@ -406,11 +420,11 @@ class Node:
onResponse=onResponse)
def _sendAdmin(self, p: admin_pb2.AdminMessage, wantResponse=False,
onResponse=None,
onResponse=None,
adminIndex=0):
"""Send an admin message to the specified node (or the local node if destNodeNum is zero)"""
if adminIndex == 0: # unless a special channel index was used, we want to use the admin index
if adminIndex == 0: # unless a special channel index was used, we want to use the admin index
adminIndex = self.iface.localNode._getAdminChannelIndex()
return self.iface.sendData(p, self.nodeNum,
@@ -476,10 +490,11 @@ class MeshInterface:
if nodeId == LOCAL_ADDR:
return self.localNode
else:
logging.info("Requesting configuration from remote node (this could take a while)")
logging.info(
"Requesting configuration from remote node (this could take a while)")
n = Node(self, nodeId)
n.requestConfig()
if not n.waitForConfig(maxsecs = 60):
if not n.waitForConfig(maxsecs=60):
raise Exception("Timed out waiting for node config")
return n
@@ -606,7 +621,8 @@ class MeshInterface:
nodeNum = BROADCAST_NUM
elif destinationId == LOCAL_ADDR:
nodeNum = self.myInfo.my_node_num
elif destinationId.startswith("!"): # A simple hex style nodeid - we can parse this without needing the DB
# A simple hex style nodeid - we can parse this without needing the DB
elif destinationId.startswith("!"):
nodeNum = int(destinationId[1:], 16)
else:
node = self.nodes.get(destinationId)
@@ -630,7 +646,8 @@ class MeshInterface:
def waitForConfig(self):
"""Block until radio config is received. Returns True if config has been received."""
success = waitForSet(self, attrs=('myInfo', 'nodes')) and self.localNode.waitForConfig()
success = waitForSet(self, attrs=('myInfo', 'nodes')
) and self.localNode.waitForConfig()
if not success:
raise Exception("Timed out waiting for interface config")
@@ -700,7 +717,7 @@ class MeshInterface:
startConfig = mesh_pb2.ToRadio()
self.configId = random.randint(0, 0xffffffff)
startConfig.want_config_id = self.configId
startConfig.want_config_id = self.configId
self._sendToRadio(startConfig)
def _sendDisconnect(self):
@@ -769,7 +786,7 @@ class MeshInterface:
if "user" in node: # Some nodes might not have user/ids assigned yet
self.nodes[node["user"]["id"]] = node
publishingThread.queueWork(lambda: pub.sendMessage("meshtastic.node.updated",
node=node, interface=self))
node=node, interface=self))
elif fromRadio.config_complete_id == self.configId:
# we ignore the config_complete_id, it is unneeded for our stream API fromRadio.config_complete_id
logging.debug(f"Config complete ID {self.configId}")
@@ -856,7 +873,7 @@ class MeshInterface:
asDict["fromId"] = self._nodeNumToId(asDict["from"])
except Exception as ex:
logging.warn(f"Not populating fromId {ex}")
try:
try:
asDict["toId"] = self._nodeNumToId(asDict["to"])
except Exception as ex:
logging.warn(f"Not populating toId {ex}")
@@ -953,7 +970,7 @@ class BLEInterface(MeshInterface):
self.device.char_write(TORADIO_UUID, b)
def close(self):
MeshInterface.close(self)
MeshInterface.close(self)
self.adapter.stop()
def _readFromRadio(self):
@@ -964,6 +981,7 @@ class BLEInterface(MeshInterface):
if not wasEmpty:
self._handleFromRadio(b)
class StreamInterface(MeshInterface):
"""Interface class for meshtastic devices over a stream link (serial, TCP, etc)"""
@@ -1022,7 +1040,7 @@ class StreamInterface(MeshInterface):
def _writeBytes(self, b):
"""Write an array of bytes to our stream and flush"""
if self.stream: # ignore writes when stream is closed
if self.stream: # ignore writes when stream is closed
self.stream.write(b)
self.stream.flush()
@@ -1042,7 +1060,7 @@ class StreamInterface(MeshInterface):
def close(self):
"""Close a connection to the device"""
logging.debug("Closing stream")
MeshInterface.close(self)
MeshInterface.close(self)
# pyserial cancel_read doesn't seem to work, therefore we ask the reader thread to close things for us
self._wantExit = True
if self._rxThread != threading.current_thread():
@@ -1181,7 +1199,7 @@ class TCPInterface(StreamInterface):
def close(self):
"""Close a connection to the device"""
logging.debug("Closing TCP stream")
StreamInterface.close(self)
StreamInterface.close(self)
# Sometimes the socket read might be blocked in the reader thread. Therefore we force the shutdown by closing
# the socket here
self._wantExit = True

View File

@@ -2,7 +2,11 @@
import argparse
import platform
import logging, sys, codecs, base64, os
import logging
import sys
import codecs
import base64
import os
from . import SerialInterface, TCPInterface, BLEInterface, test, remote_hardware
from pubsub import pub
from . import mesh_pb2, portnums_pb2, channel_pb2
@@ -61,9 +65,11 @@ def onConnection(interface, topic=pub.AUTO_TOPIC):
trueTerms = {"t", "true", "yes"}
falseTerms = {"f", "false", "no"}
def genPSKS256():
return os.urandom(32)
def fromPSK(valstr):
"""A special version of fromStr that assumes the user is trying to set a PSK.
In that case we also allow "none", "default" or "random" (to have python generate one), or simpleN
@@ -75,10 +81,12 @@ def fromPSK(valstr):
elif valstr == "default":
return bytes([1]) # Use default channel psk
elif valstr.startswith("simple"):
return bytes([int(valstr[6:]) + 1]) # Use one of the single byte encodings
# Use one of the single byte encodings
return bytes([int(valstr[6:]) + 1])
else:
return fromStr(valstr)
def fromStr(valstr):
"""try to parse as int, float or bool (and fallback to a string as last resort)
@@ -206,8 +214,10 @@ def setPref(attributes, name, valStr):
except Exception as ex:
print(f"Can't set {name} due to {ex}")
targetNode = None
def onConnected(interface):
"""Callback invoked when we connect to a radio"""
closeNow = False # Should we drop the connection after we finish?
@@ -228,7 +238,7 @@ def onConnected(interface):
alt = 0
lat = 0.0
lon = 0.0
time = 0 # always set time, but based on the local clock
time = 0 # always set time, but based on the local clock
prefs = interface.localNode.radioConfig.preferences
if args.setalt:
alt = int(args.setalt)
@@ -258,13 +268,19 @@ def onConnected(interface):
if args.set_ham:
closeNow = True
print(f"Setting HAM ID to {args.set_ham} and turning off encryption")
print(
f"Setting HAM ID to {args.set_ham} and turning off encryption")
getNode().setOwner(args.set_ham)
ch = getNode().channels[0] # Must turn off crypt on primary channel
# Must turn off crypt on primary channel
ch = getNode().channels[0]
ch.settings.psk = fromPSK("none")
print(f"Writing modified channels to device")
getNode().writeChannel(0)
if args.reboot:
closeNow = True
getNode().reboot()
if args.sendtext:
closeNow = True
print(f"Sending text message {args.sendtext} to {args.destOrAll}")
@@ -324,7 +340,8 @@ def onConnected(interface):
n = getNode()
ch = n.getChannelByName(args.ch_add)
if ch:
logging.error(f"This node already has a '{args.ch_add}' channel - no changes.")
logging.error(
f"This node already has a '{args.ch_add}' channel - no changes.")
else:
ch = n.getDisabledChannel()
if not ch:
@@ -333,7 +350,7 @@ def onConnected(interface):
chs.psk = genPSKS256()
chs.name = args.ch_add
ch.settings.CopyFrom(chs)
ch.role = channel_pb2.Channel.Role.SECONDARY
ch.role = channel_pb2.Channel.Role.SECONDARY
print(f"Writing modified channels to device")
n.writeChannel(ch.index)
@@ -348,13 +365,14 @@ def onConnected(interface):
ch = getNode().channels[channelIndex]
enable = args.ch_enable # should we enable this channel?
enable = args.ch_enable # should we enable this channel?
if args.ch_longslow or args.ch_shortfast:
if channelIndex != 0:
raise Exception("standard channel settings can only be applied to the PRIMARY channel")
raise Exception(
"standard channel settings can only be applied to the PRIMARY channel")
enable = True # force enable
enable = True # force enable
def setSimpleChannel(modem_config):
"""Set one of the simple modem_config only based channels"""
@@ -378,13 +396,14 @@ def onConnected(interface):
# Handle the channel settings
for pref in (args.ch_set or []):
if pref[0] == "psk":
ch.settings.psk =fromPSK(pref[1])
ch.settings.psk = fromPSK(pref[1])
else:
setPref(ch.settings, pref[0], pref[1])
enable = True # If we set any pref, assume the user wants to enable the channel
enable = True # If we set any pref, assume the user wants to enable the channel
if enable:
ch.role = channel_pb2.Channel.Role.PRIMARY if (channelIndex == 0) else channel_pb2.Channel.Role.SECONDARY
ch.role = channel_pb2.Channel.Role.PRIMARY if (
channelIndex == 0) else channel_pb2.Channel.Role.SECONDARY
else:
ch.role = channel_pb2.Channel.Role.DISABLED
@@ -408,7 +427,7 @@ def onConnected(interface):
if args.qr:
closeNow = True
url = interface.localNode.getURL(includeAll = False)
url = interface.localNode.getURL(includeAll=False)
print(f"Primary channel URL {url}")
qr = pyqrcode.create(url)
print(qr.terminal())
@@ -462,12 +481,9 @@ def common():
args.destOrLocal = args.dest # FIXME, temp hack for debugging remove
if not args.seriallog:
if args.info or args.nodes or args.set or args.seturl or args.set_owner or args.setlat or args.setlon or \
args.ch_longslow or args.ch_shortfast or args.ch_set or args.sendtext or \
args.qr or args.ch_add or args.ch_del or args.set_ham:
args.seriallog = "none" # assume no debug output in this case
else:
args.seriallog = "stdout" # default to stdout
args.seriallog = "none" # assume no debug output in this case
else:
args.seriallog = "stdout" # default to stdout
if args.deprecated != None:
logging.error(
@@ -573,6 +589,9 @@ def initParser():
parser.add_argument(
"--sendping", help="Send a ping message (which requests a reply)", action="store_true")
parser.add_argument(
"--reboot", help="Tell the destination node to reboot", action="store_true")
# parser.add_argument(
# "--repeat", help="Normally the send commands send only one message, use this option to request repeated sends")

View File

@@ -22,7 +22,7 @@ DESCRIPTOR = _descriptor.FileDescriptor(
syntax='proto3',
serialized_options=b'\n\023com.geeksville.meshB\013AdminProtosH\003',
create_key=_descriptor._internal_create_key,
serialized_pb=b'\n\x0b\x61\x64min.proto\x1a\nmesh.proto\x1a\x11radioconfig.proto\x1a\rchannel.proto\"\xe1\x02\n\x0c\x41\x64minMessage\x12!\n\tset_radio\x18\x01 \x01(\x0b\x32\x0c.RadioConfigH\x00\x12\x1a\n\tset_owner\x18\x02 \x01(\x0b\x32\x05.UserH\x00\x12\x1f\n\x0bset_channel\x18\x03 \x01(\x0b\x32\x08.ChannelH\x00\x12\x1b\n\x11get_radio_request\x18\x04 \x01(\x08H\x00\x12*\n\x12get_radio_response\x18\x05 \x01(\x0b\x32\x0c.RadioConfigH\x00\x12\x1d\n\x13get_channel_request\x18\x06 \x01(\rH\x00\x12(\n\x14get_channel_response\x18\x07 \x01(\x0b\x32\x08.ChannelH\x00\x12\x1d\n\x13\x63onfirm_set_channel\x18 \x01(\x08H\x00\x12\x1b\n\x11\x63onfirm_set_radio\x18! \x01(\x08H\x00\x12\x18\n\x0e\x65xit_simulator\x18\" \x01(\x08H\x00\x42\t\n\x07variantB$\n\x13\x63om.geeksville.meshB\x0b\x41\x64minProtosH\x03\x62\x06proto3'
serialized_pb=b'\n\x0b\x61\x64min.proto\x1a\nmesh.proto\x1a\x11radioconfig.proto\x1a\rchannel.proto\"\xfb\x02\n\x0c\x41\x64minMessage\x12!\n\tset_radio\x18\x01 \x01(\x0b\x32\x0c.RadioConfigH\x00\x12\x1a\n\tset_owner\x18\x02 \x01(\x0b\x32\x05.UserH\x00\x12\x1f\n\x0bset_channel\x18\x03 \x01(\x0b\x32\x08.ChannelH\x00\x12\x1b\n\x11get_radio_request\x18\x04 \x01(\x08H\x00\x12*\n\x12get_radio_response\x18\x05 \x01(\x0b\x32\x0c.RadioConfigH\x00\x12\x1d\n\x13get_channel_request\x18\x06 \x01(\rH\x00\x12(\n\x14get_channel_response\x18\x07 \x01(\x0b\x32\x08.ChannelH\x00\x12\x1d\n\x13\x63onfirm_set_channel\x18 \x01(\x08H\x00\x12\x1b\n\x11\x63onfirm_set_radio\x18! \x01(\x08H\x00\x12\x18\n\x0e\x65xit_simulator\x18\" \x01(\x08H\x00\x12\x18\n\x0ereboot_seconds\x18# \x01(\x05H\x00\x42\t\n\x07variantB$\n\x13\x63om.geeksville.meshB\x0b\x41\x64minProtosH\x03\x62\x06proto3'
,
dependencies=[mesh__pb2.DESCRIPTOR,radioconfig__pb2.DESCRIPTOR,channel__pb2.DESCRIPTOR,])
@@ -107,6 +107,13 @@ _ADMINMESSAGE = _descriptor.Descriptor(
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='reboot_seconds', full_name='AdminMessage.reboot_seconds', index=10,
number=35, type=5, cpp_type=1, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
],
extensions=[
],
@@ -125,7 +132,7 @@ _ADMINMESSAGE = _descriptor.Descriptor(
fields=[]),
],
serialized_start=62,
serialized_end=415,
serialized_end=441,
)
_ADMINMESSAGE.fields_by_name['set_radio'].message_type = radioconfig__pb2._RADIOCONFIG
@@ -163,6 +170,9 @@ _ADMINMESSAGE.fields_by_name['confirm_set_radio'].containing_oneof = _ADMINMESSA
_ADMINMESSAGE.oneofs_by_name['variant'].fields.append(
_ADMINMESSAGE.fields_by_name['exit_simulator'])
_ADMINMESSAGE.fields_by_name['exit_simulator'].containing_oneof = _ADMINMESSAGE.oneofs_by_name['variant']
_ADMINMESSAGE.oneofs_by_name['variant'].fields.append(
_ADMINMESSAGE.fields_by_name['reboot_seconds'])
_ADMINMESSAGE.fields_by_name['reboot_seconds'].containing_oneof = _ADMINMESSAGE.oneofs_by_name['variant']
DESCRIPTOR.message_types_by_name['AdminMessage'] = _ADMINMESSAGE
_sym_db.RegisterFileDescriptor(DESCRIPTOR)

View File

File diff suppressed because one or more lines are too long

View File

@@ -23,7 +23,7 @@ sendingInterface = None
def onReceive(packet, interface):
"""Callback invoked when a packet arrives"""
if sendingInterface == interface:
pass
pass
# print("Ignoring sending interface")
else:
# print(f"From {interface.stream.port}: {packet}")
@@ -65,7 +65,8 @@ def testSend(fromInterface, toInterface, isBroadcast=False, asBinary=False, want
else:
toNode = toInterface.myInfo.my_node_num
logging.debug(f"Sending test wantAck={wantAck} packet from {fromNode} to {toNode}")
logging.debug(
f"Sending test wantAck={wantAck} packet from {fromNode} to {toNode}")
global sendingInterface
sendingInterface = fromInterface
if not asBinary:
@@ -100,7 +101,7 @@ def runTests(numTests=50, wantAck=False, maxFailures=0):
logging.info(
f"Test succeeded {numSuccess} successes {numFail} failures so far")
#if numFail >= 3:
# if numFail >= 3:
# for i in interfaces:
# i.close()
# return
@@ -112,10 +113,12 @@ def runTests(numTests=50, wantAck=False, maxFailures=0):
return numFail
def testThread(numTests=50):
logging.info("Found devices, starting tests...")
runTests(numTests, wantAck=True)
runTests(numTests, wantAck=False, maxFailures=5) # Allow a few dropped packets
# Allow a few dropped packets
runTests(numTests, wantAck=False, maxFailures=5)
def onConnection(topic=pub.AUTO_TOPIC):
@@ -152,6 +155,7 @@ def testAll():
for i in interfaces:
i.close()
def testSimulator():
"""
Assume that someone has launched meshtastic-native as a simulated node.
@@ -169,4 +173,4 @@ def testSimulator():
iface.localNode.exitSimulator()
iface.close()
logging.info("Integration test successful!")
sys.exit(0)
sys.exit(0)

View File

@@ -3,7 +3,9 @@ from collections import defaultdict
import serial
import serial.tools.list_ports
from queue import Queue
import threading, sys, logging
import threading
import sys
import logging
"""Some devices such as a seger jlink we never want to accidentally open"""
blacklistVids = dict.fromkeys([0x1366])

2
proto

Submodule proto updated: e8d2a96a00...f9c4f87581