klippy: Improve error reporting during connect

Catch exceptions during the connect phase and report them via the
gcode interface to the user.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
Kevin O'Connor 2016-11-30 14:30:45 -05:00
parent dbdf1e137e
commit 524e0290bc
3 changed files with 69 additions and 19 deletions

View File

@ -109,7 +109,7 @@ class GCodeParser:
except: except:
logging.exception("Exception in command handler") logging.exception("Exception in command handler")
self.toolhead.force_shutdown() self.toolhead.force_shutdown()
self.respond('Error: Internal error on command:"%s"' % (cmd,)) self.respond_error('Internal error on command:"%s"' % (cmd,))
# Check if machine can process next command or must stall input # Check if machine can process next command or must stall input
if self.busy_state is not None: if self.busy_state is not None:
break break
@ -146,6 +146,11 @@ class GCodeParser:
if self.is_fileinput: if self.is_fileinput:
return return
os.write(self.fd, msg+"\n") os.write(self.fd, msg+"\n")
def respond_error(self, msg):
lines = msg.strip().split('\n')
for line in lines[:-1]:
self.respond('// %s' % (line.strip(),))
self.respond('!! %s' % (lines[-1].strip(),))
# Busy handling # Busy handling
def set_busy(self, busy_handler): def set_busy(self, busy_handler):
self.busy_state = busy_handler self.busy_state = busy_handler
@ -154,12 +159,12 @@ class GCodeParser:
try: try:
busy = self.busy_state.check_busy(eventtime) busy = self.busy_state.check_busy(eventtime)
except homing.EndstopError, e: except homing.EndstopError, e:
self.respond("Error: %s" % (e,)) self.respond_error(str(e))
busy = False busy = False
except: except:
logging.exception("Exception in busy handler") logging.exception("Exception in busy handler")
self.toolhead.force_shutdown() self.toolhead.force_shutdown()
self.respond('Error: Internal error in busy handler') self.respond_error('Internal error in busy handler')
busy = False busy = False
if busy: if busy:
self.toolhead.reset_motor_off_time(eventtime) self.toolhead.reset_motor_off_time(eventtime)
@ -208,7 +213,7 @@ class GCodeParser:
# Individual command handlers # Individual command handlers
def cmd_default(self, params): def cmd_default(self, params):
if not self.is_printer_ready: if not self.is_printer_ready:
self.respond('Error: Printer is not ready') self.respond_error(self.printer.get_state_message())
return return
cmd = params.get('#command') cmd = params.get('#command')
if not cmd: if not cmd:
@ -233,7 +238,7 @@ class GCodeParser:
try: try:
self.toolhead.move(self.last_position, self.speed, sloppy) self.toolhead.move(self.last_position, self.speed, sloppy)
except homing.EndstopError, e: except homing.EndstopError, e:
self.respond("Error: %s" % (e,)) self.respond_error(str(e))
self.last_position = self.toolhead.get_position() self.last_position = self.toolhead.get_position()
def cmd_G4(self, params): def cmd_G4(self, params):
# Dwell # Dwell
@ -244,7 +249,7 @@ class GCodeParser:
self.toolhead.dwell(delay) self.toolhead.dwell(delay)
def cmd_G20(self, params): def cmd_G20(self, params):
# Set units to inches # Set units to inches
self.respond('Error: Machine does not support G20 (inches) command') self.respond_error('Machine does not support G20 (inches) command')
def cmd_G21(self, params): def cmd_G21(self, params):
# Set units to millimeters # Set units to millimeters
pass pass

View File

@ -7,6 +7,32 @@
import sys, optparse, ConfigParser, logging, time, threading import sys, optparse, ConfigParser, logging, time, threading
import gcode, toolhead, util, mcu, fan, heater, extruder, reactor, queuelogger import gcode, toolhead, util, mcu, fan, heater, extruder, reactor, queuelogger
message_startup = """
The klippy host software is attempting to connect. Please
retry in a few moments.
Printer is not ready
"""
message_restart = """
This is an unrecoverable error. Please correct the
underlying issue and then manually restart the klippy host
software.
Printer is halted
"""
message_mcu_connect_error = """
This is an unrecoverable error. Please manually restart
both the firmware and the host software.
Error configuring printer
"""
message_shutdown = """
This is an unrecoverable error. Please correct the
underlying issue and then manually restart both the
firmware and the host software.
Printer is shutdown
"""
class ConfigWrapper: class ConfigWrapper:
def __init__(self, printer, section): def __init__(self, printer, section):
self.printer = printer self.printer = printer
@ -38,6 +64,7 @@ class Printer:
self.stats_timer = self.reactor.register_timer(self.stats) self.stats_timer = self.reactor.register_timer(self.stats)
self.connect_timer = self.reactor.register_timer( self.connect_timer = self.reactor.register_timer(
self.connect, self.reactor.NOW) self.connect, self.reactor.NOW)
self.state_message = message_startup
self.debugoutput = self.dictionary = None self.debugoutput = self.dictionary = None
self.fileconfig = None self.fileconfig = None
self.mcu = None self.mcu = None
@ -74,6 +101,7 @@ class Printer:
self.gcode.build_config() self.gcode.build_config()
self.mcu.build_config() self.mcu.build_config()
def connect(self, eventtime): def connect(self, eventtime):
try:
self.load_config() self.load_config()
if self.debugoutput is None: if self.debugoutput is None:
self.reactor.update_timer(self.stats_timer, self.reactor.NOW) self.reactor.update_timer(self.stats_timer, self.reactor.NOW)
@ -82,15 +110,33 @@ class Printer:
self.mcu.connect() self.mcu.connect()
self.build_config() self.build_config()
self.gcode.set_printer_ready(True) self.gcode.set_printer_ready(True)
self.state_message = "Running"
except mcu.error, e:
logging.exception("MCU error during connect")
self.state_message = "%s%s" % (str(e), message_mcu_connect_error)
self.reactor.update_timer(self.stats_timer, self.reactor.NEVER)
except:
logging.exception("Unhandled exception during connect")
self.state_message = "Internal error during connect.%s" % (
message_restart)
self.reactor.update_timer(self.stats_timer, self.reactor.NEVER)
self.reactor.unregister_timer(self.connect_timer) self.reactor.unregister_timer(self.connect_timer)
return self.reactor.NEVER return self.reactor.NEVER
def run(self): def run(self):
try:
self.reactor.run() self.reactor.run()
except:
logging.exception("Unhandled exception during run")
return
# If gcode exits, then exit the MCU # If gcode exits, then exit the MCU
self.stats(time.time()) self.stats(time.time())
self.mcu.disconnect() self.mcu.disconnect()
self.stats(time.time()) self.stats(time.time())
def note_shutdown(self): def get_state_message(self):
return self.state_message
def note_shutdown(self, msg):
self.state_message = "Firmware shutdown: %s%s" % (
msg, message_shutdown)
self.gcode.set_printer_ready(False) self.gcode.set_printer_ready(False)

View File

@ -336,7 +336,7 @@ class MCU:
self.is_shutdown = True self.is_shutdown = True
logging.info("%s: %s" % (params['#name'], params['#msg'])) logging.info("%s: %s" % (params['#name'], params['#msg']))
self.serial.dump_debug() self.serial.dump_debug()
self._printer.note_shutdown() self._printer.note_shutdown(params['#msg'])
# Connection phase # Connection phase
def connect(self): def connect(self):
if not self._is_fileoutput: if not self._is_fileoutput:
@ -421,8 +421,7 @@ class MCU:
if not self._is_fileoutput: if not self._is_fileoutput:
config_params = self.serial.send_with_response(msg, 'config') config_params = self.serial.send_with_response(msg, 'config')
if self._config_crc != config_params['crc']: if self._config_crc != config_params['crc']:
logging.error("Printer CRC does not match config") raise error("Printer CRC does not match config")
sys.exit(1)
logging.info("Configured") logging.info("Configured")
stepqueues = tuple(s._stepqueue for s in self._steppers) stepqueues = tuple(s._stepqueue for s in self._steppers)
self._steppersync = self.ffi_lib.steppersync_alloc( self._steppersync = self.ffi_lib.steppersync_alloc(