Skip to content

Commit

Permalink
noncritical mcus
Browse files Browse the repository at this point in the history
  • Loading branch information
bwnance committed Nov 5, 2023
1 parent 06d6b44 commit 55ac128
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 3 deletions.
3 changes: 3 additions & 0 deletions klippy/clocksync.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ def __init__(self, reactor):
self.prediction_variance = 0.0
self.last_prediction_time = 0.0

def disconnect(self):
self.reactor.update_timer(self.get_clock_timer, self.reactor.NEVER)

def connect(self, serial):
self.serial = serial
self.mcu_freq = serial.msgparser.get_constant_float("CLOCK_FREQ")
Expand Down
75 changes: 73 additions & 2 deletions klippy/mcu.py
Original file line number Diff line number Diff line change
Expand Up @@ -746,6 +746,7 @@ class MCU:
error = error

def __init__(self, config, clocksync):
self._config = config
self._printer = printer = config.get_printer()
self.danger_options = printer.lookup_object("danger_options")
self._clocksync = clocksync
Expand Down Expand Up @@ -807,6 +808,18 @@ def __init__(self, config, clocksync):
self._mcu_tick_avg = 0.0
self._mcu_tick_stddev = 0.0
self._mcu_tick_awake = 0.0

# noncritical mcus
self.non_critical_recon_timer = self._reactor.register_timer(
self.non_critical_recon_event
)
self.is_non_critical = config.getboolean("is_non_critical", False)
self._non_critical_disconnected = False
# self.last_noncrit_recon_eventtime = None
self.reconnect_interval = (
config.getfloat("reconnect_interval", 2.0) + 0.12
) # add small change to not collide with other events

# Register handlers
printer.register_event_handler(
"klippy:firmware_restart", self._firmware_restart
Expand Down Expand Up @@ -852,7 +865,7 @@ def _handle_shutdown(self, params):
self._printer.invoke_async_shutdown(prefix + msg + error_help(msg))

def _handle_starting(self, params):
if not self._is_shutdown:
if not self._is_shutdown and not self.is_non_critical:
self._printer.invoke_async_shutdown(
"MCU '%s' spontaneous restart" % (self._name,)
)
Expand Down Expand Up @@ -892,20 +905,35 @@ def dummy_estimated_print_time(eventtime):

self.estimated_print_time = dummy_estimated_print_time

def handle_non_critical_disconnect(self):
self._non_critical_disconnected = True
self._clocksync.disconnect()
self._disconnect()
self._reactor.update_timer(
self.non_critical_recon_timer, self._reactor.NOW
)
logging.info("mcu: %s disconnected", self._name)

def non_critical_recon_event(self, eventtime):
self.recon_mcu()
return eventtime + self.reconnect_interval

def _send_config(self, prev_crc):
# Build config commands
for cb in self._config_callbacks:
cb()
self._config_cmds.insert(
0, "allocate_oids count=%d" % (self._oid_count,)
)

# Resolve pin names
mcu_type = self._serial.get_msgparser().get_constant("MCU")
ppins = self._printer.lookup_object("pins")
pin_resolver = ppins.get_pin_resolver(self._name)
for cmdlist in (self._config_cmds, self._restart_cmds, self._init_cmds):
for i, cmd in enumerate(cmdlist):
cmdlist[i] = pin_resolver.update_command(cmd)
logging.info("command: %s", cmdlist[i])
# Calculate config CRC
encoded_config = "\n".join(self._config_cmds).encode()
config_crc = zlib.crc32(encoded_config) & 0xFFFFFFFF
Expand Down Expand Up @@ -975,7 +1003,35 @@ def _log_info(self):
]
return "\n".join(log_info)

def recon_mcu(self):
res = self._mcu_identify()
if not res:
return
self.reset_to_initial_state()
self._connect()
self._reactor.update_timer(
self.non_critical_recon_timer, self._reactor.NEVER
)
self._reactor.unregister_timer(self.non_critical_recon_timer)
self.last_noncrit_recon_eventtime = None
logging.info("mcu: %s reconnected", self._name)

def reset_to_initial_state(self):
self._oid_count = 0
self._config_cmds = []
self._restart_cmds = []
self._init_cmds = []
self._reserved_move_slots = 0
self._stepqueues = []
self._steppersync = None

def _connect(self):
if self._non_critical_disconnected:
self.non_critical_recon_timer = self._reactor.register_timer(
self.non_critical_recon_event,
self._reactor.NOW + self.reconnect_interval,
)
return
config_params = self._send_get_config()
if not config_params["is_config"]:
if self._restart_method == "rpi_usb":
Expand Down Expand Up @@ -1015,7 +1071,16 @@ def _connect(self):
log_info = self._log_info() + "\n" + move_msg
self._printer.set_rollover_info(self._name, log_info, log=False)

def _check_serial_exists(self):
rts = self._restart_method != "cheetah"
return self._serial.check_connect(self._serialport, self._baud, rts)

def _mcu_identify(self):
if self.is_non_critical and not self._check_serial_exists():
self._non_critical_disconnected = True
return False
else:
self._non_critical_disconnected = False
if self.is_fileoutput():
self._connect_file()
else:
Expand Down Expand Up @@ -1069,6 +1134,7 @@ def _mcu_identify(self):
self.register_response(self._handle_shutdown, "shutdown")
self.register_response(self._handle_shutdown, "is_shutdown")
self.register_response(self._handle_mcu_stats, "stats")
return True

# Config creation helpers
def setup_pin(self, pin_type, pin_params):
Expand Down Expand Up @@ -1222,7 +1288,9 @@ def _restart_rpi_usb(self):
chelper.run_hub_ctrl(1)

def _firmware_restart(self, force=False):
if self._is_mcu_bridge and not force:
if (
self._is_mcu_bridge and not force
) or self._non_critical_disconnected:
return
if self._restart_method == "rpi_usb":
self._restart_rpi_usb()
Expand Down Expand Up @@ -1269,6 +1337,9 @@ def check_active(self, print_time, eventtime):
or self._is_timeout
):
return
if self.is_non_critical:
self.handle_non_critical_disconnect()
return
self._is_timeout = True
logging.info(
"Timeout with MCU '%s' (eventtime=%f)", self._name, eventtime
Expand Down
31 changes: 30 additions & 1 deletion klippy/serialhdl.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,11 @@ def connect_uart(self, serialport, baud, rts=True):
# Initial connection
logging.info("%sStarting serial connect", self.warn_prefix)
start_time = self.reactor.monotonic()
while True:
while 1:
if (
self.serialqueue is not None
): # if we're already connected, don't recon
break
if self.reactor.monotonic() > start_time + 90.0:
self._error("Unable to connect")
try:
Expand All @@ -224,6 +228,17 @@ def connect_uart(self, serialport, baud, rts=True):
if ret:
break

def check_connect(self, serialport, baud, rts=True):
serial_dev = serial.Serial(baudrate=baud, timeout=0, exclusive=False)
serial_dev.port = serialport
serial_dev.rts = rts
try:
serial_dev.open()
except Exception:
return False
serial_dev.close()
return True

def connect_file(self, debugoutput, dictionary, pace=False):
self.serial_dev = debugoutput
self.msgparser.process_identify(dictionary, decompress=False)
Expand Down Expand Up @@ -280,11 +295,25 @@ def register_response(self, callback, name, oid=None):

# Command sending
def raw_send(self, cmd, minclock, reqclock, cmd_queue):
if self.serialqueue is None:
logging.info(
"%sSerial connection closed, cmd: %s",
self.warn_prefix,
repr(cmd),
)
return
self.ffi_lib.serialqueue_send(
self.serialqueue, cmd_queue, cmd, len(cmd), minclock, reqclock, 0
)

def raw_send_wait_ack(self, cmd, minclock, reqclock, cmd_queue):
if self.serialqueue is None:
logging.info(
"%sSerial connection closed, in wait ack, cmd: %s",
self.warn_prefix,
repr(cmd),
)
return
self.last_notify_id += 1
nid = self.last_notify_id
completion = self.reactor.completion()
Expand Down

0 comments on commit 55ac128

Please sign in to comment.