From 245917bf034804acfa3039ac308c7d4308e79e86 Mon Sep 17 00:00:00 2001
From: Kevin O'Connor <kevin@koconnor.net>
Date: Wed, 19 Feb 2020 16:46:06 -0500
Subject: [PATCH] mcu: Introduce new lookup_query_command() command wrapper

Use new mcu.lookup_query_command() for all commands that query
information from the micro-controller.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
---
 klippy/extras/bus.py      | 18 ++++----
 klippy/extras/tmc_uart.py | 13 +++---
 klippy/mcu.py             | 88 +++++++++++++++++++++++----------------
 klippy/serialhdl.py       |  2 +
 klippy/stepper.py         |  8 ++--
 5 files changed, 73 insertions(+), 56 deletions(-)

diff --git a/klippy/extras/bus.py b/klippy/extras/bus.py
index 0b50efc73..7c6ae4401 100644
--- a/klippy/extras/bus.py
+++ b/klippy/extras/bus.py
@@ -79,8 +79,10 @@ class MCU_SPI:
         self.mcu.add_config_cmd(self.config_fmt)
         self.spi_send_cmd = self.mcu.lookup_command(
             "spi_send oid=%c data=%*s", cq=self.cmd_queue)
-        self.spi_transfer_cmd = self.mcu.lookup_command(
-            "spi_transfer oid=%c data=%*s", cq=self.cmd_queue)
+        self.spi_transfer_cmd = self.mcu.lookup_query_command(
+            "spi_transfer oid=%c data=%*s",
+            "spi_transfer_response oid=%c response=%*s", oid=self.oid,
+            cq=self.cmd_queue)
     def spi_send(self, data, minclock=0, reqclock=0):
         if self.spi_send_cmd is None:
             # Send setup message via mcu initialization
@@ -91,8 +93,7 @@ class MCU_SPI:
         self.spi_send_cmd.send([self.oid, data],
                                minclock=minclock, reqclock=reqclock)
     def spi_transfer(self, data):
-        return self.spi_transfer_cmd.send_with_response(
-            [self.oid, data], 'spi_transfer_response', self.oid)
+        return self.spi_transfer_cmd.send([self.oid, data])
 
 # Helper to setup an spi bus from settings in a config section
 def MCU_SPI_from_config(config, mode, pin_option="cs_pin",
@@ -155,8 +156,10 @@ class MCU_I2C:
         self.mcu.add_config_cmd(self.config_fmt % (bus,))
         self.i2c_write_cmd = self.mcu.lookup_command(
             "i2c_write oid=%c data=%*s", cq=self.cmd_queue)
-        self.i2c_read_cmd = self.mcu.lookup_command(
-            "i2c_read oid=%c reg=%*s read_len=%u", cq=self.cmd_queue)
+        self.i2c_read_cmd = self.mcu.lookup_query_command(
+            "i2c_read oid=%c reg=%*s read_len=%u",
+            "i2c_read_response oid=%c response=%*s", oid=self.oid,
+            cq=self.cmd_queue)
         self.i2c_modify_bits_cmd = self.mcu.lookup_command(
             "i2c_modify_bits oid=%c reg=%*s clear_set_bits=%*s",
             cq=self.cmd_queue)
@@ -170,8 +173,7 @@ class MCU_I2C:
         self.i2c_write_cmd.send([self.oid, data],
                                 minclock=minclock, reqclock=reqclock)
     def i2c_read(self, write, read_len):
-        return self.i2c_read_cmd.send_with_response(
-            [self.oid, write, read_len], 'i2c_read_response', self.oid)
+        return self.i2c_read_cmd.send([self.oid, write, read_len])
     def i2c_modify_bits(self, reg, clear_bits, set_bits,
                         minclock=0, reqclock=0):
         clearset = clear_bits + set_bits
diff --git a/klippy/extras/tmc_uart.py b/klippy/extras/tmc_uart.py
index 7191c9b5a..edcaab97b 100644
--- a/klippy/extras/tmc_uart.py
+++ b/klippy/extras/tmc_uart.py
@@ -75,8 +75,10 @@ class MCU_TMC_uart_bitbang:
         self.mcu.add_config_cmd(
             "config_tmcuart oid=%d rx_pin=%s pull_up=%d tx_pin=%s bit_time=%d"
             % (self.oid, self.rx_pin, self.pullup, self.tx_pin, bit_ticks))
-        self.tmcuart_send_cmd = self.mcu.lookup_command(
-            "tmcuart_send oid=%c write=%*s read=%c", cq=self.cmd_queue)
+        self.tmcuart_send_cmd = self.mcu.lookup_query_command(
+            "tmcuart_send oid=%c write=%*s read=%c",
+            "tmcuart_response oid=%c read=%*s", oid=self.oid,
+            cq=self.cmd_queue, async=True)
     def register_instance(self, rx_pin_params, tx_pin_params,
                           select_pins_desc, addr):
         if (rx_pin_params['pin'] != self.rx_pin
@@ -148,8 +150,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_async_response(
-            [self.oid, msg, 10], 'tmcuart_response', self.oid)
+        params = self.tmcuart_send_cmd.send([self.oid, msg, 10])
         return self._decode_read(reg, params['read'])
     def reg_write(self, instance_id, addr, reg, val, print_time=None):
         minclock = 0
@@ -158,9 +159,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_async_response(
-            [self.oid, msg, 0], 'tmcuart_response', self.oid,
-            minclock=minclock)
+        self.tmcuart_send_cmd.send([self.oid, msg, 0], minclock=minclock)
 
 # Lookup a (possibly shared) tmc uart
 def lookup_tmc_uart_bitbang(config, max_addr):
diff --git a/klippy/mcu.py b/klippy/mcu.py
index 4be952f2f..4064b8e02 100644
--- a/klippy/mcu.py
+++ b/klippy/mcu.py
@@ -1,6 +1,6 @@
 # Interface to Klipper micro-controller code
 #
-# Copyright (C) 2016-2019  Kevin O'Connor <kevin@koconnor.net>
+# Copyright (C) 2016-2020  Kevin O'Connor <kevin@koconnor.net>
 #
 # This file may be distributed under the terms of the GNU GPLv3 license.
 import sys, os, zlib, logging, math
@@ -18,7 +18,7 @@ class MCU_endstop:
         self._pullup = pin_params['pullup']
         self._invert = pin_params['invert']
         self._reactor = mcu.get_printer().get_reactor()
-        self._oid = self._home_cmd = self._query_cmd = None
+        self._oid = self._home_cmd = self._requery_cmd = self._query_cmd = None
         self._mcu.register_config_callback(self._build_config)
         self._min_query_time = self._last_sent_time = 0.
         self._next_query_print_time = self._end_home_time = 0.
@@ -49,8 +49,12 @@ class MCU_endstop:
         self._home_cmd = self._mcu.lookup_command(
             "endstop_home oid=%c clock=%u sample_ticks=%u sample_count=%c"
             " rest_ticks=%u pin_value=%c", cq=cmd_queue)
-        self._query_cmd = self._mcu.lookup_command(
+        self._requery_cmd = self._mcu.lookup_command(
             "endstop_query_state oid=%c", cq=cmd_queue)
+        self._query_cmd = self._mcu.lookup_query_command(
+            "endstop_query_state oid=%c",
+            "endstop_state oid=%c homing=%c pin_value=%c", oid=self._oid,
+            cq=cmd_queue)
     def home_start(self, print_time, sample_time, sample_count, rest_time,
                    triggered=True):
         clock = self._mcu.print_time_to_clock(print_time)
@@ -94,7 +98,7 @@ class MCU_endstop:
             est_print_time = self._mcu.estimated_print_time(eventtime)
             if est_print_time >= self._next_query_print_time:
                 self._next_query_print_time = est_print_time + self.RETRY_QUERY
-                self._query_cmd.send([self._oid])
+                self._requery_cmd.send([self._oid])
     def home_wait(self, home_end_time):
         self._home_end_time = home_end_time
         did_trigger = self._home_completion.wait()
@@ -109,8 +113,7 @@ class MCU_endstop:
         clock = self._mcu.print_time_to_clock(print_time)
         if self._mcu.is_fileoutput():
             return 0
-        params = self._query_cmd.send_with_response(
-            [self._oid], "endstop_state", self._oid, minclock=clock)
+        params = self._query_cmd.send([self._oid], minclock=clock)
         return params['pin_value'] ^ self._invert
 
 class MCU_digital_out:
@@ -305,11 +308,11 @@ class MCU_adc:
 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()
+    def __init__(self, serial, name, oid=None):
         self.serial = serial
         self.name = name
         self.oid = oid
+        self.reactor = serial.get_reactor()
         self.completion = self.reactor.completion()
         self.min_query_time = self.reactor.monotonic()
         self.serial.register_response(self.handle_callback, name, oid)
@@ -317,10 +320,10 @@ class RetryAsyncCommand:
         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)
+    def get_response(self, cmd, cmd_queue, minclock=0):
+        self.serial.raw_send_wait_ack(cmd, minclock, minclock, cmd_queue)
+        first_query_time = query_time = self.reactor.monotonic()
         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)
@@ -329,34 +332,42 @@ class RetryAsyncCommand:
             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,))
+            self.serial.raw_send(cmd, minclock, minclock, cmd_queue)
+
+# Wrapper around query commands
+class CommandQueryWrapper:
+    def __init__(self, serial, msgformat, respformat, oid=None,
+                 cmd_queue=None, async=False):
+        self._serial = serial
+        self._cmd = serial.get_msgparser().lookup_command(msgformat)
+        serial.get_msgparser().lookup_command(respformat)
+        self._response = respformat.split()[0]
+        self._oid = oid
+        self._xmit_helper = serialhdl.SerialRetryCommand
+        if async:
+            self._xmit_helper = RetryAsyncCommand
+        if cmd_queue is None:
+            cmd_queue = serial.get_default_command_queue()
+        self._cmd_queue = cmd_queue
+    def send(self, data=(), minclock=0):
+        cmd = self._cmd.encode(data)
+        xh = self._xmit_helper(self._serial, self._response, self._oid)
+        try:
+            return xh.get_response(cmd, self._cmd_queue, minclock=minclock)
+        except serialhdl.error as e:
+            raise error(str(e))
 
 # Wrapper around command sending
 class CommandWrapper:
-    def __init__(self, mcu, serial, clocksync, cmd, cmd_queue):
-        self._mcu = mcu
+    def __init__(self, serial, msgformat, cmd_queue=None):
         self._serial = serial
-        self._clocksync = clocksync
-        self._cmd = cmd
+        self._cmd = serial.get_msgparser().lookup_command(msgformat)
+        if cmd_queue is None:
+            cmd_queue = serial.get_default_command_queue()
         self._cmd_queue = cmd_queue
     def send(self, data=(), minclock=0, reqclock=0):
         cmd = self._cmd.encode(data)
         self._serial.raw_send(cmd, minclock, reqclock, self._cmd_queue)
-    def send_with_response(self, data=(), response=None, response_oid=None,
-                           minclock=0):
-        cmd = self._cmd.encode(data)
-        src = serialhdl.SerialRetryCommand(self._serial, response, response_oid)
-        try:
-            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
@@ -505,10 +516,12 @@ class MCU:
         for c in self._init_cmds:
             self._serial.send(c)
     def _send_get_config(self):
-        get_config_cmd = self.lookup_command("get_config")
+        get_config_cmd = self.lookup_query_command(
+            "get_config",
+            "config is_config=%c crc=%u move_count=%hu is_shutdown=%c")
         if self.is_fileoutput():
             return { 'is_config': 0, 'move_count': 500, 'crc': 0 }
-        config_params = get_config_cmd.send_with_response(response='config')
+        config_params = get_config_cmd.send()
         if self._is_shutdown:
             raise error("MCU '%s' error during config: %s" % (
                 self._name, self._shutdown_msg))
@@ -624,10 +637,11 @@ class MCU:
     def alloc_command_queue(self):
         return self._serial.alloc_command_queue()
     def lookup_command(self, msgformat, cq=None):
-        if cq is None:
-            cq = self._serial.get_default_command_queue()
-        cmd = self._serial.get_msgparser().lookup_command(msgformat)
-        return CommandWrapper(self, self._serial, self._clocksync, cmd, cq)
+        return CommandWrapper(self._serial, msgformat, cq)
+    def lookup_query_command(self, msgformat, respformat, oid=None,
+                             cq=None, async=False):
+        return CommandQueryWrapper(self._serial, msgformat, respformat, oid,
+                                   cq, async)
     def try_lookup_command(self, msgformat):
         try:
             return self.lookup_command(msgformat)
diff --git a/klippy/serialhdl.py b/klippy/serialhdl.py
index c7c6e4d28..98bef9439 100644
--- a/klippy/serialhdl.py
+++ b/klippy/serialhdl.py
@@ -145,6 +145,8 @@ class SerialReader:
         self.ffi_lib.serialqueue_get_stats(
             self.serialqueue, self.stats_buf, len(self.stats_buf))
         return self.ffi_main.string(self.stats_buf)
+    def get_reactor(self):
+        return self.reactor
     def get_msgparser(self):
         return self.msgparser
     def get_default_command_queue(self):
diff --git a/klippy/stepper.py b/klippy/stepper.py
index 8b3f943ba..1ec59974c 100644
--- a/klippy/stepper.py
+++ b/klippy/stepper.py
@@ -84,8 +84,9 @@ class MCU_stepper:
             "set_next_step_dir oid=%c dir=%c")
         self._reset_cmd_id = self._mcu.lookup_command_id(
             "reset_step_clock oid=%c clock=%u")
-        self._get_position_cmd = self._mcu.lookup_command(
-            "stepper_get_position oid=%c")
+        self._get_position_cmd = self._mcu.lookup_query_command(
+            "stepper_get_position oid=%c",
+            "stepper_position oid=%c pos=%i", oid=self._oid)
         self._ffi_lib.stepcompress_fill(
             self._stepqueue, self._mcu.seconds_to_clock(max_error),
             self._invert_dir, step_cmd_id, dir_cmd_id)
@@ -134,8 +135,7 @@ class MCU_stepper:
             raise error("Internal error in stepcompress")
         if not did_trigger or self._mcu.is_fileoutput():
             return
-        params = self._get_position_cmd.send_with_response(
-            [self._oid], response='stepper_position', response_oid=self._oid)
+        params = self._get_position_cmd.send([self._oid])
         mcu_pos_dist = params['pos'] * self._step_dist
         if self._invert_dir:
             mcu_pos_dist = -mcu_pos_dist