tools/mpremote: Avoid initial blocking read in read_until().
If the target does not return any data then `read_until()` will block indefinitely. Fix this by making the initial read part of the general read look, which always checks `inWaiting() > 0` before reading from the serial device. Also added the UART timeout to the constructor. This is not currently used but may be used as an additional safeguard. Signed-off-by: Hans Maerki <buhtig.hans.maerki@ergoinfo.ch>
This commit is contained in:
committed by
Damien George
parent
55ca3fd675
commit
0d46e45a1f
@@ -42,7 +42,7 @@ from .transport import TransportError, TransportExecError, Transport
|
|||||||
|
|
||||||
|
|
||||||
class SerialTransport(Transport):
|
class SerialTransport(Transport):
|
||||||
def __init__(self, device, baudrate=115200, wait=0, exclusive=True):
|
def __init__(self, device, baudrate=115200, wait=0, exclusive=True, timeout=None):
|
||||||
self.in_raw_repl = False
|
self.in_raw_repl = False
|
||||||
self.use_raw_paste = True
|
self.use_raw_paste = True
|
||||||
self.device_name = device
|
self.device_name = device
|
||||||
@@ -52,7 +52,11 @@ class SerialTransport(Transport):
|
|||||||
import serial.tools.list_ports
|
import serial.tools.list_ports
|
||||||
|
|
||||||
# Set options, and exclusive if pyserial supports it
|
# Set options, and exclusive if pyserial supports it
|
||||||
serial_kwargs = {"baudrate": baudrate, "interCharTimeout": 1}
|
serial_kwargs = {
|
||||||
|
"baudrate": baudrate,
|
||||||
|
"timeout": timeout,
|
||||||
|
"interCharTimeout": 1,
|
||||||
|
}
|
||||||
if serial.__version__ >= "3.3":
|
if serial.__version__ >= "3.3":
|
||||||
serial_kwargs["exclusive"] = exclusive
|
serial_kwargs["exclusive"] = exclusive
|
||||||
|
|
||||||
@@ -95,13 +99,20 @@ class SerialTransport(Transport):
|
|||||||
self.serial.close()
|
self.serial.close()
|
||||||
|
|
||||||
def read_until(self, min_num_bytes, ending, timeout=10, data_consumer=None):
|
def read_until(self, min_num_bytes, ending, timeout=10, data_consumer=None):
|
||||||
# if data_consumer is used then data is not accumulated and the ending must be 1 byte long
|
"""
|
||||||
assert data_consumer is None or len(ending) == 1
|
min_num_bytes: Obsolete.
|
||||||
|
ending: Return if 'ending' matches.
|
||||||
|
timeout [s]: Return if timeout between characters. None: Infinite timeout.
|
||||||
|
data_consumer: Use callback for incoming characters.
|
||||||
|
If data_consumer is used then data is not accumulated and the ending must be 1 byte long
|
||||||
|
|
||||||
data = self.serial.read(min_num_bytes)
|
It is not visible to the caller why the function returned. It could be ending or timeout.
|
||||||
if data_consumer:
|
"""
|
||||||
data_consumer(data)
|
assert data_consumer is None or len(ending) == 1
|
||||||
timeout_count = 0
|
assert isinstance(timeout, (type(None), int, float))
|
||||||
|
|
||||||
|
data = b""
|
||||||
|
begin_char_s = time.monotonic()
|
||||||
while True:
|
while True:
|
||||||
if data.endswith(ending):
|
if data.endswith(ending):
|
||||||
break
|
break
|
||||||
@@ -112,10 +123,9 @@ class SerialTransport(Transport):
|
|||||||
data = new_data
|
data = new_data
|
||||||
else:
|
else:
|
||||||
data = data + new_data
|
data = data + new_data
|
||||||
timeout_count = 0
|
begin_char_s = time.monotonic()
|
||||||
else:
|
else:
|
||||||
timeout_count += 1
|
if timeout is not None and time.monotonic() >= begin_char_s + timeout:
|
||||||
if timeout is not None and timeout_count >= 100 * timeout:
|
|
||||||
break
|
break
|
||||||
time.sleep(0.01)
|
time.sleep(0.01)
|
||||||
return data
|
return data
|
||||||
|
|||||||
Reference in New Issue
Block a user