aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKevin O'Connor <kevin@koconnor.net>2020-02-16 15:46:04 -0500
committerKevin O'Connor <kevin@koconnor.net>2020-02-20 12:01:21 -0500
commit332038ea017d458c27f1bb69fa7c17bfe36fcbd8 (patch)
tree9a9e8c04efffcc425219f063c0acb04e00c6e077
parentc6c360c4e14374a56dcb0477e1e7759683841093 (diff)
downloadkutter-332038ea017d458c27f1bb69fa7c17bfe36fcbd8.tar.gz
kutter-332038ea017d458c27f1bb69fa7c17bfe36fcbd8.tar.xz
kutter-332038ea017d458c27f1bb69fa7c17bfe36fcbd8.zip
serialhdl: Convert queries to use new notify message ack system
Convert standard queries to use the acknowledgments of the sent command to determine if the response was received or not. This also controls command retransmissions (should the response have been lost). The tmc_uart.py will continue to use the old mechanism as the tmcuart responses are not sent synchronously with the query command. Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
-rw-r--r--klippy/extras/tmc_uart.py4
-rw-r--r--klippy/mcu.py43
-rw-r--r--klippy/serialhdl.py55
3 files changed, 67 insertions, 35 deletions
diff --git a/klippy/extras/tmc_uart.py b/klippy/extras/tmc_uart.py
index 7eab1240..7191c9b5 100644
--- a/klippy/extras/tmc_uart.py
+++ b/klippy/extras/tmc_uart.py
@@ -148,7 +148,7 @@ class MCU_TMC_uart_bitbang:
if self.analog_mux is not None:
self.analog_mux.activate(instance_id)
msg = self._encode_read(0xf5, addr, reg)
- params = self.tmcuart_send_cmd.send_with_response(
+ params = self.tmcuart_send_cmd.send_with_async_response(
[self.oid, msg, 10], 'tmcuart_response', self.oid)
return self._decode_read(reg, params['read'])
def reg_write(self, instance_id, addr, reg, val, print_time=None):
@@ -158,7 +158,7 @@ class MCU_TMC_uart_bitbang:
if self.analog_mux is not None:
self.analog_mux.activate(instance_id)
msg = self._encode_write(0xf5, addr, reg | 0x80, val)
- self.tmcuart_send_cmd.send_with_response(
+ self.tmcuart_send_cmd.send_with_async_response(
[self.oid, msg, 0], 'tmcuart_response', self.oid,
minclock=minclock)
diff --git a/klippy/mcu.py b/klippy/mcu.py
index 09f5d232..4be952f2 100644
--- a/klippy/mcu.py
+++ b/klippy/mcu.py
@@ -301,6 +301,35 @@ class MCU_adc:
if self._callback is not None:
self._callback(last_read_time, last_value)
+# Class to retry sending of a query command until a given response is received
+class RetryAsyncCommand:
+ TIMEOUT_TIME = 5.0
+ RETRY_TIME = 0.500
+ def __init__(self, mcu, serial, name, oid=None):
+ self.reactor = mcu.get_printer().get_reactor()
+ self.serial = serial
+ self.name = name
+ self.oid = oid
+ self.completion = self.reactor.completion()
+ self.min_query_time = self.reactor.monotonic()
+ self.serial.register_response(self.handle_callback, name, oid)
+ def handle_callback(self, params):
+ if params['#sent_time'] >= self.min_query_time:
+ self.min_query_time = self.reactor.NEVER
+ self.reactor.async_complete(self.completion, params)
+ def get_response(self, cmd, cmd_queue, minclock=0, minsystime=0.):
+ first_query_time = query_time = max(self.min_query_time, minsystime)
+ while 1:
+ self.serial.raw_send(cmd, minclock, minclock, cmd_queue)
+ params = self.completion.wait(query_time + self.RETRY_TIME)
+ if params is not None:
+ self.serial.register_response(None, self.name, self.oid)
+ return params
+ query_time = self.reactor.monotonic()
+ if query_time > first_query_time + self.TIMEOUT_TIME:
+ self.serial.register_response(None, self.name, self.oid)
+ raise error("Timeout on wait for '%s' response" % (self.name,))
+
# Wrapper around command sending
class CommandWrapper:
def __init__(self, mcu, serial, clocksync, cmd, cmd_queue):
@@ -314,16 +343,20 @@ class CommandWrapper:
self._serial.raw_send(cmd, minclock, reqclock, self._cmd_queue)
def send_with_response(self, data=(), response=None, response_oid=None,
minclock=0):
- minsystime = 0.
- if minclock:
- minsystime = self._clocksync.estimate_clock_systime(minclock)
cmd = self._cmd.encode(data)
src = serialhdl.SerialRetryCommand(self._serial, response, response_oid)
try:
- return src.get_response([cmd], self._cmd_queue,
- minclock=minclock, minsystime=minsystime)
+ return src.get_response(cmd, self._cmd_queue, minclock=minclock)
except serialhdl.error as e:
raise error(str(e))
+ def send_with_async_response(self, data=(),
+ response=None, response_oid=None, minclock=0):
+ minsystime = 0.
+ if minclock:
+ minsystime = self._clocksync.estimate_clock_systime(minclock)
+ cmd = self._cmd.encode(data)
+ src = RetryAsyncCommand(self._mcu, self._serial, response, response_oid)
+ return src.get_response(cmd, self._cmd_queue, minclock, minsystime)
class MCU:
error = error
diff --git a/klippy/serialhdl.py b/klippy/serialhdl.py
index 190fd745..c7c6e4d2 100644
--- a/klippy/serialhdl.py
+++ b/klippy/serialhdl.py
@@ -58,20 +58,22 @@ class SerialReader:
hdl(params)
except:
logging.exception("Exception in serial callback")
- def _get_identify_data(self, timeout):
+ def _get_identify_data(self, eventtime):
# Query the "data dictionary" from the micro-controller
identify_data = ""
while 1:
msg = "identify offset=%d count=%d" % (len(identify_data), 40)
- params = self.send_with_response(msg, 'identify_response')
+ try:
+ params = self.send_with_response(msg, 'identify_response')
+ except error as e:
+ logging.exception("Wait for identify_response")
+ return None
if params['offset'] == len(identify_data):
msgdata = params['data']
if not msgdata:
# Done
return identify_data
identify_data += msgdata
- if self.reactor.monotonic() > timeout:
- raise error("Timeout during identify")
def connect(self):
# Initial connection
logging.info("Starting serial connect")
@@ -97,13 +99,12 @@ class SerialReader:
self.background_thread = threading.Thread(target=self._bg_thread)
self.background_thread.start()
# Obtain and load the data dictionary from the firmware
- try:
- identify_data = self._get_identify_data(connect_time + 5.)
- except error as e:
- logging.exception("Timeout on serial connect")
- self.disconnect()
- continue
- break
+ completion = self.reactor.register_callback(self._get_identify_data)
+ identify_data = completion.wait(connect_time + 5.)
+ if identify_data is not None:
+ break
+ logging.info("Timeout on serial connect")
+ self.disconnect()
msgparser = msgproto.MessageParser()
msgparser.process_identify(identify_data)
self.msgparser = msgparser
@@ -176,7 +177,7 @@ class SerialReader:
def send_with_response(self, msg, response):
cmd = self.msgparser.create_command(msg)
src = SerialRetryCommand(self, response)
- return src.get_response([cmd], self.default_cmd_queue)
+ return src.get_response(cmd, self.default_cmd_queue)
def alloc_command_queue(self):
return self.ffi_main.gc(self.ffi_lib.serialqueue_alloc_commandqueue(),
self.ffi_lib.serialqueue_free_commandqueue)
@@ -218,34 +219,32 @@ class SerialReader:
def __del__(self):
self.disconnect()
-# Class to retry sending of a query command until a given response is received
+# Class to send a query command and return the received response
class SerialRetryCommand:
- TIMEOUT_TIME = 5.0
- RETRY_TIME = 0.500
def __init__(self, serial, name, oid=None):
self.serial = serial
self.name = name
self.oid = oid
- self.completion = serial.reactor.completion()
- self.min_query_time = serial.reactor.monotonic()
+ self.last_params = None
self.serial.register_response(self.handle_callback, name, oid)
def handle_callback(self, params):
- if params['#sent_time'] >= self.min_query_time:
- self.min_query_time = self.serial.reactor.NEVER
- self.serial.reactor.async_complete(self.completion, params)
- def get_response(self, cmds, cmd_queue, minclock=0, minsystime=0.):
- first_query_time = query_time = max(self.min_query_time, minsystime)
+ self.last_params = params
+ def get_response(self, cmd, cmd_queue, minclock=0):
+ retries = 5
+ retry_delay = .010
while 1:
- for cmd in cmds:
- self.serial.raw_send(cmd, minclock, minclock, cmd_queue)
- params = self.completion.wait(query_time + self.RETRY_TIME)
+ self.serial.raw_send_wait_ack(cmd, minclock, minclock, cmd_queue)
+ params = self.last_params
if params is not None:
self.serial.register_response(None, self.name, self.oid)
return params
- query_time = self.serial.reactor.monotonic()
- if query_time > first_query_time + self.TIMEOUT_TIME:
+ if retries <= 0:
self.serial.register_response(None, self.name, self.oid)
- raise error("Timeout on wait for '%s' response" % (self.name,))
+ raise error("Unable to obtain '%s' response" % (self.name,))
+ reactor = self.serial.reactor
+ reactor.pause(reactor.monotonic() + retry_delay)
+ retries -= 1
+ retry_delay *= 2.
# Attempt to place an AVR stk500v2 style programmer into normal mode
def stk500v2_leave(ser, reactor):