klippy: Store printer startup parameters in new "start_args" dictionary

Store pertinent information from the software startup in a dictionary
that the various printer components can access instead of in
individual variables in the Printer() class.  This makes it easier to
add future command-line options.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
Kevin O'Connor 2017-08-21 17:19:43 -04:00
parent b80c488d36
commit 268834e4ae
6 changed files with 64 additions and 71 deletions

View File

@ -9,15 +9,15 @@ import homing, extruder
# Parse out incoming GCode and find and translate head movements # Parse out incoming GCode and find and translate head movements
class GCodeParser: class GCodeParser:
RETRY_TIME = 0.100 RETRY_TIME = 0.100
def __init__(self, printer, fd, is_fileinput=False): def __init__(self, printer, fd):
self.printer = printer self.printer = printer
self.fd = fd self.fd = fd
self.is_fileinput = is_fileinput
# Input handling # Input handling
self.reactor = printer.reactor self.reactor = printer.reactor
self.is_processing_data = False self.is_processing_data = False
self.is_fileinput = not not printer.get_start_args().get("debuginput")
self.fd_handle = None self.fd_handle = None
if not is_fileinput: if not self.is_fileinput:
self.fd_handle = self.reactor.register_fd(self.fd, self.process_data) self.fd_handle = self.reactor.register_fd(self.fd, self.process_data)
self.partial_input = "" self.partial_input = ""
self.bytes_read = 0 self.bytes_read = 0
@ -140,8 +140,7 @@ class GCodeParser:
if not data and self.is_fileinput: if not data and self.is_fileinput:
self.motor_heater_off() self.motor_heater_off()
if self.toolhead is not None: if self.toolhead is not None:
if not self.printer.mcu.is_fileoutput(): self.toolhead.wait_moves()
self.toolhead.wait_moves()
self.printer.request_exit('exit_eof') self.printer.request_exit('exit_eof')
self.is_processing_data = False self.is_processing_data = False
if self.fd_handle is None: if self.fd_handle is None:
@ -392,8 +391,8 @@ class GCodeParser:
cmd_M115_when_not_ready = True cmd_M115_when_not_ready = True
def cmd_M115(self, params): def cmd_M115(self, params):
# Get Firmware Version and Capabilities # Get Firmware Version and Capabilities
kw = {"FIRMWARE_NAME": "Klipper" software_version = self.printer.get_start_args().get('software_version')
, "FIRMWARE_VERSION": self.printer.software_version} kw = {"FIRMWARE_NAME": "Klipper", "FIRMWARE_VERSION": software_version}
self.ack(" ".join(["%s:%s" % (k, v) for k, v in kw.items()])) self.ack(" ".join(["%s:%s" % (k, v) for k, v in kw.items()]))
def cmd_M140(self, params): def cmd_M140(self, params):
# Set Bed Temperature # Set Bed Temperature

View File

@ -112,8 +112,8 @@ class PrinterHeater:
self.min_extrude_temp = config.getfloat( self.min_extrude_temp = config.getfloat(
'min_extrude_temp', 170., minval=self.min_temp, maxval=self.max_temp) 'min_extrude_temp', 170., minval=self.min_temp, maxval=self.max_temp)
self.max_power = config.getfloat('max_power', 1., above=0., maxval=1.) self.max_power = config.getfloat('max_power', 1., above=0., maxval=1.)
self.can_extrude = (self.min_extrude_temp <= 0. is_fileoutput = printer.get_start_args().get('debugoutput') is not None
or printer.mcu.is_fileoutput()) self.can_extrude = self.min_extrude_temp <= 0. or is_fileoutput
self.lock = threading.Lock() self.lock = threading.Lock()
self.last_temp = 0. self.last_temp = 0.
self.last_temp_time = 0. self.last_temp_time = 0.

View File

@ -123,30 +123,25 @@ class ConfigLogger():
self.lines.append(data.strip()) self.lines.append(data.strip())
class Printer: class Printer:
def __init__(self, conffile, input_fd, startup_state def __init__(self, input_fd, bglogger, start_args):
, is_fileinput=False, version="?", bglogger=None):
self.conffile = conffile
self.startup_state = startup_state
self.software_version = version
self.bglogger = bglogger self.bglogger = bglogger
self.start_args = start_args
if bglogger is not None: if bglogger is not None:
bglogger.set_rollover_info("config", None) bglogger.set_rollover_info("config", None)
self.reactor = reactor.Reactor() self.reactor = reactor.Reactor()
self.objects = {} self.objects = {}
self.gcode = gcode.GCodeParser(self, input_fd, is_fileinput) self.gcode = gcode.GCodeParser(self, input_fd)
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.all_config_options = {} self.all_config_options = {}
self.need_dump_debug = False self.need_dump_debug = False
self.state_message = message_startup self.state_message = message_startup
self.debugoutput = self.dictionary = None
self.run_result = None self.run_result = None
self.fileconfig = None self.fileconfig = None
self.mcu = None self.mcu = None
def set_fileoutput(self, debugoutput, dictionary): def get_start_args(self):
self.debugoutput = debugoutput return self.start_args
self.dictionary = dictionary
def stats(self, eventtime, force_output=False): def stats(self, eventtime, force_output=False):
if self.need_dump_debug: if self.need_dump_debug:
# Call dump_debug here so it is executed in the main thread # Call dump_debug here so it is executed in the main thread
@ -168,15 +163,14 @@ class Printer:
self.objects[name] = obj self.objects[name] = obj
def load_config(self): def load_config(self):
self.fileconfig = ConfigParser.RawConfigParser() self.fileconfig = ConfigParser.RawConfigParser()
res = self.fileconfig.read(self.conffile) config_file = self.start_args['config_file']
res = self.fileconfig.read(config_file)
if not res: if not res:
raise ConfigParser.Error("Unable to open config file %s" % ( raise ConfigParser.Error("Unable to open config file %s" % (
self.conffile,)) config_file,))
if self.bglogger is not None: if self.bglogger is not None:
ConfigLogger(self.fileconfig, self.bglogger) ConfigLogger(self.fileconfig, self.bglogger)
self.mcu = mcu.MCU(self, ConfigWrapper(self, 'mcu')) self.mcu = mcu.MCU(self, ConfigWrapper(self, 'mcu'))
if self.debugoutput is not None:
self.mcu.connect_file(self.debugoutput, self.dictionary)
# Create printer components # Create printer components
config = ConfigWrapper(self, 'printer') config = ConfigWrapper(self, 'printer')
for m in [extruder, fan, heater, toolhead]: for m in [extruder, fan, heater, toolhead]:
@ -197,7 +191,7 @@ class Printer:
def connect(self, eventtime): def connect(self, eventtime):
try: try:
self.load_config() self.load_config()
if self.debugoutput is None: if self.start_args.get('debugoutput') is None:
self.reactor.update_timer(self.stats_timer, self.reactor.NOW) self.reactor.update_timer(self.stats_timer, self.reactor.NOW)
self.mcu.connect() self.mcu.connect()
self.gcode.set_printer_ready(True) self.gcode.set_printer_ready(True)
@ -259,8 +253,6 @@ class Printer:
self.mcu.disconnect() self.mcu.disconnect()
except: except:
logging.exception("Unhandled exception during firmware_restart") logging.exception("Unhandled exception during firmware_restart")
def get_startup_state(self):
return self.startup_state
def request_exit(self, result="exit"): def request_exit(self, result="exit"):
self.run_result = result self.run_result = result
self.reactor.end() self.reactor.end()
@ -270,18 +262,10 @@ class Printer:
# Startup # Startup
###################################################################### ######################################################################
def read_dictionary(filename):
dfile = open(filename, 'rb')
dictionary = dfile.read()
dfile.close()
return dictionary
def main(): def main():
usage = "%prog [options] <config file>" usage = "%prog [options] <config file>"
opts = optparse.OptionParser(usage) opts = optparse.OptionParser(usage)
opts.add_option("-o", "--debugoutput", dest="outputfile", opts.add_option("-i", "--debuginput", dest="debuginput",
help="write output to file instead of to serial port")
opts.add_option("-i", "--debuginput", dest="inputfile",
help="read commands from file instead of from tty port") help="read commands from file instead of from tty port")
opts.add_option("-I", "--input-tty", dest="inputtty", default='/tmp/printer', opts.add_option("-I", "--input-tty", dest="inputtty", default='/tmp/printer',
help="input tty name (default is /tmp/printer)") help="input tty name (default is /tmp/printer)")
@ -289,63 +273,60 @@ def main():
help="write log to file instead of stderr") help="write log to file instead of stderr")
opts.add_option("-v", action="store_true", dest="verbose", opts.add_option("-v", action="store_true", dest="verbose",
help="enable debug messages") help="enable debug messages")
opts.add_option("-d", dest="read_dictionary", opts.add_option("-o", "--debugoutput", dest="debugoutput",
help="write output to file instead of to serial port")
opts.add_option("-d", "--dictionary", dest="dictionary",
help="file to read for mcu protocol dictionary") help="file to read for mcu protocol dictionary")
options, args = opts.parse_args() options, args = opts.parse_args()
if len(args) != 1: if len(args) != 1:
opts.error("Incorrect number of arguments") opts.error("Incorrect number of arguments")
conffile = args[0] start_args = {'config_file': args[0], 'start_reason': 'startup'}
input_fd = debuginput = debugoutput = bglogger = None input_fd = bglogger = None
debuglevel = logging.INFO debuglevel = logging.INFO
if options.verbose: if options.verbose:
debuglevel = logging.DEBUG debuglevel = logging.DEBUG
if options.inputfile: if options.debuginput:
debuginput = open(options.inputfile, 'rb') start_args['debuginput'] = options.debuginput
debuginput = open(options.debuginput, 'rb')
input_fd = debuginput.fileno() input_fd = debuginput.fileno()
else: else:
input_fd = util.create_pty(options.inputtty) input_fd = util.create_pty(options.inputtty)
if options.outputfile: if options.debugoutput:
debugoutput = open(options.outputfile, 'wb') start_args['debugoutput'] = options.debugoutput
start_args['dictionary'] = options.dictionary
if options.logfile: if options.logfile:
bglogger = queuelogger.setup_bg_logging(options.logfile, debuglevel) bglogger = queuelogger.setup_bg_logging(options.logfile, debuglevel)
else: else:
logging.basicConfig(level=debuglevel) logging.basicConfig(level=debuglevel)
logging.info("Starting Klippy...") logging.info("Starting Klippy...")
software_version = util.get_git_version() start_args['software_version'] = util.get_git_version()
if bglogger is not None: if bglogger is not None:
lines = ["Args: %s" % (sys.argv,), lines = ["Args: %s" % (sys.argv,),
"Git version: %s" % (repr(software_version),), "Git version: %s" % (repr(start_args['software_version']),),
"CPU: %s" % (util.get_cpu_info(),), "CPU: %s" % (util.get_cpu_info(),),
"Python: %s" % (repr(sys.version),)] "Python: %s" % (repr(sys.version),)]
lines = "\n".join(lines) lines = "\n".join(lines)
logging.info(lines) logging.info(lines)
bglogger.set_rollover_info('versions', lines) bglogger.set_rollover_info('versions', lines)
# Start firmware # Start Printer() class
res = 'startup'
while 1: while 1:
is_fileinput = debuginput is not None printer = Printer(input_fd, bglogger, start_args)
printer = Printer(
conffile, input_fd, res, is_fileinput, software_version, bglogger)
if debugoutput:
proto_dict = read_dictionary(options.read_dictionary)
printer.set_fileoutput(debugoutput, proto_dict)
res = printer.run() res = printer.run()
if res == 'restart': if res == 'restart':
printer.disconnect() printer.disconnect()
time.sleep(1.)
logging.info("Restarting printer")
continue
elif res == 'firmware_restart': elif res == 'firmware_restart':
printer.firmware_restart() printer.firmware_restart()
time.sleep(1.)
logging.info("Restarting printer")
continue
elif res == 'exit_eof': elif res == 'exit_eof':
printer.disconnect() printer.disconnect()
break break
else:
break
time.sleep(1.)
logging.info("Restarting printer")
start_args['start_reason'] = res
if bglogger is not None: if bglogger is not None:
bglogger.stop() bglogger.stop()

View File

@ -390,7 +390,6 @@ class MCU:
printer.reactor, self._serialport, baud) printer.reactor, self._serialport, baud)
self.is_shutdown = False self.is_shutdown = False
self._shutdown_msg = "" self._shutdown_msg = ""
self._is_fileoutput = False
self._timeout_timer = printer.reactor.register_timer( self._timeout_timer = printer.reactor.register_timer(
self.timeout_handler) self.timeout_handler)
rmethods = {m: m for m in ['arduino', 'command', 'rpi_usb']} rmethods = {m: m for m in ['arduino', 'command', 'rpi_usb']}
@ -442,14 +441,17 @@ class MCU:
self._printer.note_shutdown(self._shutdown_msg) self._printer.note_shutdown(self._shutdown_msg)
# Connection phase # Connection phase
def _check_restart(self, reason): def _check_restart(self, reason):
if self._printer.get_startup_state() == 'firmware_restart': start_reason = self._printer.get_start_args().get("start_reason")
if start_reason == 'firmware_restart':
return return
logging.info("Attempting automated firmware restart: %s" % (reason,)) logging.info("Attempting automated firmware restart: %s" % (reason,))
self._printer.request_exit('firmware_restart') self._printer.request_exit('firmware_restart')
self._printer.reactor.pause(self._printer.reactor.monotonic() + 2.000) self._printer.reactor.pause(self._printer.reactor.monotonic() + 2.000)
raise error("Attempt firmware restart failed") raise error("Attempt firmware restart failed")
def connect(self): def connect(self):
if not self._is_fileoutput: if self.is_fileoutput():
self._connect_file()
else:
if (self._restart_method == 'rpi_usb' if (self._restart_method == 'rpi_usb'
and not os.path.exists(self._serialport)): and not os.path.exists(self._serialport)):
# Try toggling usb power # Try toggling usb power
@ -470,9 +472,16 @@ class MCU:
self.register_msg(self.handle_mcu_stats, 'stats') self.register_msg(self.handle_mcu_stats, 'stats')
self._build_config() self._build_config()
self._send_config() self._send_config()
def connect_file(self, debugoutput, dictionary, pace=False): def _connect_file(self, pace=False):
self._is_fileoutput = True # In a debugging mode. Open debug output file and read data dictionary
self.serial.connect_file(debugoutput, dictionary) out_fname = self._printer.get_start_args().get('debugoutput')
outfile = open(out_fname, 'wb')
dict_fname = self._printer.get_start_args().get('dictionary')
dfile = open(dict_fname, 'rb')
dict_data = dfile.read()
dfile.close()
self.serial.connect_file(outfile, dict_data)
# Handle pacing
if not pace: if not pace:
def dummy_set_print_start_time(eventtime): def dummy_set_print_start_time(eventtime):
pass pass
@ -527,7 +536,7 @@ class MCU:
self.disconnect() self.disconnect()
serialhdl.arduino_reset(self._serialport, reactor) serialhdl.arduino_reset(self._serialport, reactor)
def is_fileoutput(self): def is_fileoutput(self):
return self._is_fileoutput return self._printer.get_start_args().get('debugoutput') is not None
# Configuration phase # Configuration phase
def _add_custom(self): def _add_custom(self):
for line in self._custom.split('\n'): for line in self._custom.split('\n'):
@ -564,7 +573,7 @@ class MCU:
self.add_config_cmd("finalize_config crc=%d" % (self._config_crc,)) self.add_config_cmd("finalize_config crc=%d" % (self._config_crc,))
def _send_config(self): def _send_config(self):
msg = self.create_command("get_config") msg = self.create_command("get_config")
if self._is_fileoutput: if self.is_fileoutput():
config_params = { config_params = {
'is_config': 0, 'move_count': 500, 'crc': self._config_crc} 'is_config': 0, 'move_count': 500, 'crc': self._config_crc}
else: else:
@ -577,15 +586,17 @@ class MCU:
logging.info("Sending printer configuration...") logging.info("Sending printer configuration...")
for c in self._config_cmds: for c in self._config_cmds:
self.send(self.create_command(c)) self.send(self.create_command(c))
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 not config_params['is_config']: if not config_params['is_config']:
if self.is_shutdown: if self.is_shutdown:
raise error("Firmware error during config: %s" % ( raise error("Firmware error during config: %s" % (
self._shutdown_msg,)) self._shutdown_msg,))
raise error("Unable to configure printer") raise error("Unable to configure printer")
elif self._printer.get_startup_state() == 'firmware_restart': else:
raise error("Failed automated reset of micro-controller") start_reason = self._printer.get_start_args().get("start_reason")
if start_reason == 'firmware_restart':
raise error("Failed automated reset of micro-controller")
if self._config_crc != config_params['crc']: if self._config_crc != config_params['crc']:
self._check_restart("CRC mismatch") self._check_restart("CRC mismatch")
raise error("Printer CRC does not match config") raise error("Printer CRC does not match config")

View File

@ -91,7 +91,7 @@ class PrinterHomingStepper(PrinterStepper):
logging.info("Endstop for %s is not accurate enough for stepper" logging.info("Endstop for %s is not accurate enough for stepper"
" phase adjustment" % (name,)) " phase adjustment" % (name,))
self.homing_stepper_phases = None self.homing_stepper_phases = None
if printer.mcu.is_fileoutput(): if printer.get_start_args().get('debugoutput') is not None:
self.homing_endstop_accuracy = self.homing_stepper_phases self.homing_endstop_accuracy = self.homing_stepper_phases
def enable_endstop_checking(self, move_time, step_time): def enable_endstop_checking(self, move_time, step_time):
mcu_time = self.mcu_endstop.print_to_mcu_time(move_time) mcu_time = self.mcu_endstop.print_to_mcu_time(move_time)

View File

@ -359,6 +359,8 @@ class ToolHead:
logging.debug('; Max time of %f' % (last_move_time,)) logging.debug('; Max time of %f' % (last_move_time,))
def wait_moves(self): def wait_moves(self):
self._flush_lookahead() self._flush_lookahead()
if self.printer.get_start_args().get('debugoutput') is not None:
return
eventtime = self.reactor.monotonic() eventtime = self.reactor.monotonic()
while self.print_time: while self.print_time:
eventtime = self.reactor.pause(eventtime + 0.100) eventtime = self.reactor.pause(eventtime + 0.100)