This commit is contained in:
Kevin Hester
2021-05-03 16:17:21 +08:00
parent a0d025a56c
commit 0588952e43
3 changed files with 81 additions and 23 deletions

View File

@@ -231,12 +231,15 @@ class MeshInterface:
self.responseHandlers = {} # A map from request ID to the handler
self.failure = None # If we've encountered a fatal exception it will be kept here
self._timeout = Timeout()
self.heartbeatTimer = None
random.seed() # FIXME, we should not clobber the random seedval here, instead tell user they must call it
self.currentPacketId = random.randint(0, 0xffffffff)
self._startConfig()
def close(self):
"""Shutdown this interface"""
if self.heartbeatTimer:
self.heartbeatTimer.cancel()
self._sendDisconnect()
def __enter__(self):
@@ -521,6 +524,22 @@ class MeshInterface:
publishingThread.queueWork(lambda: pub.sendMessage(
"meshtastic.connection.lost", interface=self))
def _startHeartbeat(self):
"""We need to send a heartbeat message to the device every X seconds"""
def callback():
self.heartbeatTimer = None
prefs = self.localNode.radioConfig.preferences
i = prefs.phone_timeout_secs / 2
logging.debug(f"Sending heartbeat, interval {i}")
if i != 0:
self.heartbeatTimer = threading.Timer(i, callback)
self.heartbeatTimer.start()
p = mesh_pb2.ToRadio()
self._sendToRadio(p)
callback() # run our periodic callback now, it will make another timer if necessary
def _connected(self):
"""Called by this class to tell clients we are now fully connected to a node
"""
@@ -529,6 +548,7 @@ class MeshInterface:
# for the local interface
if not self.isConnected.is_set():
self.isConnected.set()
self._startHeartbeat()
publishingThread.queueWork(lambda: pub.sendMessage(
"meshtastic.connection.established", interface=self))
@@ -576,7 +596,7 @@ class MeshInterface:
fromRadio = mesh_pb2.FromRadio()
fromRadio.ParseFromString(fromRadioBytes)
asDict = google.protobuf.json_format.MessageToDict(fromRadio)
# logging.debug(f"Received from radio: {fromRadio}")
#logging.debug(f"Received from radio: {fromRadio}")
if fromRadio.HasField("my_info"):
self.myInfo = fromRadio.my_info
self.localNode.nodeNum = self.myInfo.my_node_num
@@ -788,7 +808,7 @@ class BLEInterface(MeshInterface):
def _sendToRadioImpl(self, toRadio):
"""Send a ToRadio protobuf to the device"""
# logging.debug(f"Sending: {stripnl(toRadio)}")
#logging.debug(f"Sending: {stripnl(toRadio)}")
b = toRadio.SerializeToString()
self.device.char_write(TORADIO_UUID, b)
@@ -844,11 +864,16 @@ class StreamInterface(MeshInterface):
start the reading thread later.
"""
# Send some bogus UART characters to force a sleeping device to wake
self._writeBytes(bytes([START1, START1, START1, START1]))
# Send some bogus UART characters to force a sleeping device to wake, and if the reading statemachine was parsing a bad packet make sure
# we write enought start bytes to force it to resync
p = bytearray([START1] * 32)
self._writeBytes(p)
time.sleep(0.1) # wait 100ms to give device time to start running
self._rxThread.start()
self._startConfig()
if not self.noProto: # Wait for the db download if using the protocol
self._waitConnected()
@@ -918,15 +943,15 @@ class StreamInterface(MeshInterface):
elif ptr == 1: # looking for START2
if c != START2:
self._rxBuf = empty # failed to find start2
elif ptr >= HEADER_LEN: # we've at least got a header
elif ptr >= HEADER_LEN - 1: # we've at least got a header
# big endian length follos header
packetlen = (self._rxBuf[2] << 8) + self._rxBuf[3]
if ptr == HEADER_LEN: # we _just_ finished reading the header, validate length
if ptr == HEADER_LEN - 1: # we _just_ finished reading the header, validate length
if packetlen > MAX_TO_FROM_RADIO_SIZE:
self._rxBuf = empty # length ws out out bounds, restart
if len(self._rxBuf) != 0 and ptr + 1 == packetlen + HEADER_LEN:
if len(self._rxBuf) != 0 and ptr + 1 >= packetlen + HEADER_LEN:
try:
self._handleFromRadio(self._rxBuf[HEADER_LEN:])
except Exception as ex:
@@ -1216,7 +1241,7 @@ noProto – If True, don't try to run our protocol on the link - just be a d
def _sendToRadioImpl(self, toRadio):
"""Send a ToRadio protobuf to the device"""
# logging.debug(f"Sending: {stripnl(toRadio)}")
#logging.debug(f"Sending: {stripnl(toRadio)}")
b = toRadio.SerializeToString()
self.device.char_write(TORADIO_UUID, b)
@@ -1333,12 +1358,15 @@ noProto – If True, don't try to run our protocol on the link - just be a d
self.responseHandlers = {} # A map from request ID to the handler
self.failure = None # If we've encountered a fatal exception it will be kept here
self._timeout = Timeout()
self.heartbeatTimer = None
random.seed() # FIXME, we should not clobber the random seedval here, instead tell user they must call it
self.currentPacketId = random.randint(0, 0xffffffff)
self._startConfig()
def close(self):
"""Shutdown this interface"""
if self.heartbeatTimer:
self.heartbeatTimer.cancel()
self._sendDisconnect()
def __enter__(self):
@@ -1623,6 +1651,22 @@ noProto – If True, don't try to run our protocol on the link - just be a d
publishingThread.queueWork(lambda: pub.sendMessage(
"meshtastic.connection.lost", interface=self))
def _startHeartbeat(self):
"""We need to send a heartbeat message to the device every X seconds"""
def callback():
self.heartbeatTimer = None
prefs = self.localNode.radioConfig.preferences
i = prefs.phone_timeout_secs / 2
logging.debug(f"Sending heartbeat, interval {i}")
if i != 0:
self.heartbeatTimer = threading.Timer(i, callback)
self.heartbeatTimer.start()
p = mesh_pb2.ToRadio()
self._sendToRadio(p)
callback() # run our periodic callback now, it will make another timer if necessary
def _connected(self):
"""Called by this class to tell clients we are now fully connected to a node
"""
@@ -1631,6 +1675,7 @@ noProto – If True, don't try to run our protocol on the link - just be a d
# for the local interface
if not self.isConnected.is_set():
self.isConnected.set()
self._startHeartbeat()
publishingThread.queueWork(lambda: pub.sendMessage(
"meshtastic.connection.established", interface=self))
@@ -1678,7 +1723,7 @@ noProto – If True, don't try to run our protocol on the link - just be a d
fromRadio = mesh_pb2.FromRadio()
fromRadio.ParseFromString(fromRadioBytes)
asDict = google.protobuf.json_format.MessageToDict(fromRadio)
# logging.debug(f"Received from radio: {fromRadio}")
#logging.debug(f"Received from radio: {fromRadio}")
if fromRadio.HasField("my_info"):
self.myInfo = fromRadio.my_info
self.localNode.nodeNum = self.myInfo.my_node_num
@@ -1879,6 +1924,9 @@ noProto – If True, don't try to run our protocol on the link - just be a d
</summary>
<pre><code class="python">def close(self):
&#34;&#34;&#34;Shutdown this interface&#34;&#34;&#34;
if self.heartbeatTimer:
self.heartbeatTimer.cancel()
self._sendDisconnect()</code></pre>
</details>
</dd>
@@ -2377,11 +2425,16 @@ debugOut {stream} &ndash; If a stream is provided, any debug serial output from
start the reading thread later.
&#34;&#34;&#34;
# Send some bogus UART characters to force a sleeping device to wake
self._writeBytes(bytes([START1, START1, START1, START1]))
# Send some bogus UART characters to force a sleeping device to wake, and if the reading statemachine was parsing a bad packet make sure
# we write enought start bytes to force it to resync
p = bytearray([START1] * 32)
self._writeBytes(p)
time.sleep(0.1) # wait 100ms to give device time to start running
self._rxThread.start()
self._startConfig()
if not self.noProto: # Wait for the db download if using the protocol
self._waitConnected()
@@ -2451,15 +2504,15 @@ debugOut {stream} &ndash; If a stream is provided, any debug serial output from
elif ptr == 1: # looking for START2
if c != START2:
self._rxBuf = empty # failed to find start2
elif ptr &gt;= HEADER_LEN: # we&#39;ve at least got a header
elif ptr &gt;= HEADER_LEN - 1: # we&#39;ve at least got a header
# big endian length follos header
packetlen = (self._rxBuf[2] &lt;&lt; 8) + self._rxBuf[3]
if ptr == HEADER_LEN: # we _just_ finished reading the header, validate length
if ptr == HEADER_LEN - 1: # we _just_ finished reading the header, validate length
if packetlen &gt; MAX_TO_FROM_RADIO_SIZE:
self._rxBuf = empty # length ws out out bounds, restart
if len(self._rxBuf) != 0 and ptr + 1 == packetlen + HEADER_LEN:
if len(self._rxBuf) != 0 and ptr + 1 &gt;= packetlen + HEADER_LEN:
try:
self._handleFromRadio(self._rxBuf[HEADER_LEN:])
except Exception as ex:
@@ -2533,11 +2586,16 @@ start the reading thread later.</p></div>
start the reading thread later.
&#34;&#34;&#34;
# Send some bogus UART characters to force a sleeping device to wake
self._writeBytes(bytes([START1, START1, START1, START1]))
# Send some bogus UART characters to force a sleeping device to wake, and if the reading statemachine was parsing a bad packet make sure
# we write enought start bytes to force it to resync
p = bytearray([START1] * 32)
self._writeBytes(p)
time.sleep(0.1) # wait 100ms to give device time to start running
self._rxThread.start()
self._startConfig()
if not self.noProto: # Wait for the db download if using the protocol
self._waitConnected()</code></pre>
</details>

View File

@@ -124,11 +124,11 @@ def runTests(numTests=50, wantAck=False, maxFailures=0):
if not success:
numFail = numFail + 1
logging.error(
f&#34;Test failed, expected packet not received ({numFail} failures so far)&#34;)
f&#34;Test {testNumber} failed, expected packet not received ({numFail} failures so far)&#34;)
else:
numSuccess = numSuccess + 1
logging.info(
f&#34;Test succeeded {numSuccess} successes {numFail} failures so far&#34;)
f&#34;Test {testNumber} succeeded {numSuccess} successes {numFail} failures so far&#34;)
# if numFail &gt;= 3:
# for i in interfaces:
@@ -314,11 +314,11 @@ def testSimulator():
if not success:
numFail = numFail + 1
logging.error(
f&#34;Test failed, expected packet not received ({numFail} failures so far)&#34;)
f&#34;Test {testNumber} failed, expected packet not received ({numFail} failures so far)&#34;)
else:
numSuccess = numSuccess + 1
logging.info(
f&#34;Test succeeded {numSuccess} successes {numFail} failures so far&#34;)
f&#34;Test {testNumber} succeeded {numSuccess} successes {numFail} failures so far&#34;)
# if numFail &gt;= 3:
# for i in interfaces:

View File

@@ -12,7 +12,7 @@ with open("README.md", "r") as fh:
# This call to setup() does all the work
setup(
name="meshtastic",
version="1.2.31",
version="1.2.32",
description="Python API & client shell for talking to Meshtastic devices",
long_description=long_description,
long_description_content_type="text/markdown",