diff --git a/klippy/chipmisc.py b/klippy/chipmisc.py index c6f0df64..2c2ff329 100644 --- a/klippy/chipmisc.py +++ b/klippy/chipmisc.py @@ -1,6 +1,6 @@ # Code to configure miscellaneous chips # -# Copyright (C) 2017 Kevin O'Connor +# Copyright (C) 2017,2018 Kevin O'Connor # # This file may be distributed under the terms of the GNU GPLv3 license. import pins, mcu @@ -22,7 +22,7 @@ PIN_MIN_TIME = 0.100 class PrinterPin: def __init__(self, printer, config): self.printer = printer - self.is_pwm = 'pwm' in config.section.split()[0] + self.is_pwm = 'pwm' in config.get_name().split()[0] if self.is_pwm: self.mcu_pin = pins.setup_pin(printer, 'pwm', config.get('pin')) hard_pwm = config.getint('hard_pwm', None, minval=1) @@ -40,7 +40,7 @@ class PrinterPin: self.last_value_time = 0. self.last_value = config.getfloat( 'value', 0., minval=0., maxval=self.scale) / self.scale - self.is_static = config.section.startswith('static_') + self.is_static = config.get_name().startswith('static_') if self.is_static: self.mcu_pin.setup_start_value( self.last_value, self.last_value, True) @@ -48,13 +48,13 @@ class PrinterPin: shutdown_value = config.getfloat( 'shutdown_value', 0., minval=0., maxval=self.scale) / self.scale self.mcu_pin.setup_start_value(self.last_value, shutdown_value) - self.gcode = printer.objects['gcode'] + self.gcode = printer.lookup_object('gcode') self.gcode.register_command("SET_PIN", self.cmd_SET_PIN, desc=self.cmd_SET_PIN_help) cmd_SET_PIN_help = "Set the value of an output pin" def cmd_SET_PIN(self, params): pin_name = self.gcode.get_str('PIN', params) - pin = self.printer.objects.get('pin ' + pin_name) + pin = self.printer.lookup_object('pin ' + pin_name, None) if pin is not self: if pin is None: raise self.gcode.error("Pin not configured") @@ -64,7 +64,7 @@ class PrinterPin: value = self.gcode.get_float('VALUE', params) / self.scale if value == self.last_value: return - print_time = self.printer.objects['toolhead'].get_last_move_time() + print_time = self.printer.lookup_object('toolhead').get_last_move_time() print_time = max(print_time, self.last_value_time + PIN_MIN_TIME) if self.is_pwm: if value < 0. or value > 1.: @@ -89,7 +89,7 @@ class PrinterMultiPin: self.mcu_pins = [] def setup_pin(self, pin_params): pin_name = pin_params['pin'] - pin = self.printer.objects.get('multi_pin ' + pin_name) + pin = self.printer.lookup_object('multi_pin ' + pin_name, None) if pin is not self: if pin is None: raise pins.error("multi_pin %s not configured" % (pin_name,)) @@ -147,7 +147,7 @@ class PrinterServo: self.angle_to_width = (self.max_width - self.min_width) / self.max_angle self.width_to_value = 1. / SERVO_SIGNAL_PERIOD self.last_value = self.last_value_time = 0. - self.gcode = printer.objects['gcode'] + self.gcode = printer.lookup_object('gcode') self.gcode.register_command("SET_SERVO", self.cmd_SET_SERVO, desc=self.cmd_SET_SERVO_help) def set_pwm(self, print_time, value): @@ -167,12 +167,12 @@ class PrinterServo: cmd_SET_SERVO_help = "Set servo angle" def cmd_SET_SERVO(self, params): servo_name = self.gcode.get_str('SERVO', params) - servo = self.printer.objects.get('servo ' + servo_name) + servo = self.printer.lookup_object('servo ' + servo_name, None) if servo is not self: if servo is None: raise self.gcode.error("Servo not configured") return servo.cmd_SET_SERVO(params) - print_time = self.printer.objects['toolhead'].get_last_move_time() + print_time = self.printer.lookup_object('toolhead').get_last_move_time() if 'WIDTH' in params: self.set_pulse_width(print_time, self.gcode.get_float('WIDTH', params)) diff --git a/klippy/extruder.py b/klippy/extruder.py index 82c89c47..2e890025 100644 --- a/klippy/extruder.py +++ b/klippy/extruder.py @@ -1,6 +1,6 @@ # Code for handling printer nozzle extruders # -# Copyright (C) 2016 Kevin O'Connor +# Copyright (C) 2016-2018 Kevin O'Connor # # This file may be distributed under the terms of the GNU GPLv3 license. import math, logging @@ -24,7 +24,7 @@ class PrinterExtruder: 'max_extrude_cross_section', 4. * self.nozzle_diameter**2 , above=0.) self.max_extrude_ratio = max_cross_section / self.filament_area - toolhead = printer.objects['toolhead'] + toolhead = printer.lookup_object('toolhead') max_velocity, max_accel = toolhead.get_max_velocity() self.max_e_velocity = config.getfloat( 'max_extrude_only_velocity', max_velocity * self.max_extrude_ratio @@ -248,17 +248,17 @@ def add_printer_objects(printer, config): def get_printer_extruders(printer): out = [] for i in range(99): - extruder = printer.objects.get('extruder%d' % (i,)) + extruder = printer.lookup_object('extruder%d' % (i,), None) if extruder is None: break out.append(extruder) return out def get_printer_heater(printer, name): - if name == 'heater_bed' and name in printer.objects: - return printer.objects[name] + if name == 'heater_bed': + return printer.lookup_object(name) if name == 'extruder': name = 'extruder0' - if name.startswith('extruder') and name in printer.objects: - return printer.objects[name].get_heater() + if name.startswith('extruder'): + return printer.lookup_object(name).get_heater() raise printer.config_error("Unknown heater '%s'" % (name,)) diff --git a/klippy/fan.py b/klippy/fan.py index 5cf05861..50dc2500 100644 --- a/klippy/fan.py +++ b/klippy/fan.py @@ -1,6 +1,6 @@ # Printer fan support # -# Copyright (C) 2016,2017 Kevin O'Connor +# Copyright (C) 2016-2018 Kevin O'Connor # # This file may be distributed under the terms of the GNU GPLv3 license. import extruder, pins @@ -35,7 +35,7 @@ class PrinterFan: class PrinterHeaterFan: def __init__(self, printer, config): self.fan = PrinterFan(printer, config) - self.mcu = printer.objects['mcu'] + self.mcu = printer.lookup_object('mcu') heater = config.get("heater", "extruder0") self.heater = extruder.get_printer_heater(printer, heater) self.heater_temp = config.getfloat("heater_temp", 50.0) @@ -43,7 +43,8 @@ class PrinterHeaterFan: self.fan_speed = config.getfloat( "fan_speed", max_power, minval=0., maxval=max_power) self.fan.mcu_fan.setup_start_value(0., max_power) - printer.reactor.register_timer(self.callback, printer.reactor.NOW) + reactor = printer.get_reactor() + reactor.register_timer(self.callback, reactor.NOW) def callback(self, eventtime): current_temp, target_temp = self.heater.get_temp(eventtime) if not current_temp and not target_temp and not self.fan.last_fan_time: diff --git a/klippy/gcode.py b/klippy/gcode.py index 0afd3ff1..261bbf22 100644 --- a/klippy/gcode.py +++ b/klippy/gcode.py @@ -1,6 +1,6 @@ # Parse gcode commands # -# Copyright (C) 2016,2017 Kevin O'Connor +# Copyright (C) 2016-2018 Kevin O'Connor # # This file may be distributed under the terms of the GNU GPLv3 license. import os, re, logging, collections @@ -17,7 +17,7 @@ class GCodeParser: self.printer = printer self.fd = fd # Input handling - self.reactor = printer.reactor + self.reactor = printer.get_reactor() self.is_processing_data = False self.is_fileinput = not not printer.get_start_args().get("debuginput") self.fd_handle = None @@ -72,14 +72,14 @@ class GCodeParser: self.is_printer_ready = True self.gcode_handlers = self.ready_gcode_handlers # Lookup printer components - self.toolhead = self.printer.objects.get('toolhead') + self.toolhead = self.printer.lookup_object('toolhead') extruders = extruder.get_printer_extruders(self.printer) if extruders: self.extruder = extruders[0] self.toolhead.set_extruder(self.extruder) self.heaters = [ e.get_heater() for e in extruders ] - self.heaters.append(self.printer.objects.get('heater_bed')) - self.fan = self.printer.objects.get('fan') + self.heaters.append(self.printer.lookup_object('heater_bed', None)) + self.fan = self.printer.lookup_object('fan', None) if self.is_fileinput and self.fd_handle is None: self.fd_handle = self.reactor.register_fd(self.fd, self.process_data) def reset_last_position(self): diff --git a/klippy/heater.py b/klippy/heater.py index e3bbe78e..8dff7dbc 100644 --- a/klippy/heater.py +++ b/klippy/heater.py @@ -1,6 +1,6 @@ # Printer heater support # -# Copyright (C) 2016,2017 Kevin O'Connor +# Copyright (C) 2016-2018 Kevin O'Connor # # This file may be distributed under the terms of the GNU GPLv3 license. import math, logging, threading @@ -106,7 +106,7 @@ class PrinterHeater: error = error def __init__(self, printer, config): self.printer = printer - self.name = config.section + self.name = config.get_name() sensor_params = config.getchoice('sensor_type', Sensors) self.sensor = sensor_params['class'](config, sensor_params) self.min_temp = config.getfloat('min_temp', minval=0.) @@ -326,7 +326,7 @@ class ControlAutoTune: midpoint_pos = sorted(cycle_times)[len(cycle_times)/2][1] Kp, Ki, Kd = self.calc_pid(midpoint_pos) logging.info("Autotune: final: Kp=%f Ki=%f Kd=%f", Kp, Ki, Kd) - gcode = self.heater.printer.objects['gcode'] + gcode = self.heater.printer.lookup_object('gcode') gcode.respond_info( "PID parameters: pid_Kp=%.3f pid_Ki=%.3f pid_Kd=%.3f\n" "To use these parameters, update the printer config file with\n" diff --git a/klippy/klippy.py b/klippy/klippy.py index 472a10bd..b21eb56c 100644 --- a/klippy/klippy.py +++ b/klippy/klippy.py @@ -1,7 +1,7 @@ #!/usr/bin/env python2 # Main code for host side printer firmware # -# Copyright (C) 2016,2017 Kevin O'Connor +# Copyright (C) 2016-2018 Kevin O'Connor # # This file may be distributed under the terms of the GNU GPLv3 license. import sys, optparse, ConfigParser, logging, time, threading @@ -52,8 +52,12 @@ class ConfigWrapper: def __init__(self, printer, section): self.printer = printer self.section = section - def get_wrapper(self, parser, option, default - , minval=None, maxval=None, above=None, below=None): + def get_printer(self): + return self.printer + def get_name(self): + return self.section + def _get_wrapper(self, parser, option, default, + minval=None, maxval=None, above=None, below=None): if (default is not self.sentinel and not self.printer.fileconfig.has_option(self.section, option)): return default @@ -84,17 +88,17 @@ class ConfigWrapper: option, self.section, below)) return v def get(self, option, default=sentinel): - return self.get_wrapper(self.printer.fileconfig.get, option, default) + return self._get_wrapper(self.printer.fileconfig.get, option, default) def getint(self, option, default=sentinel, minval=None, maxval=None): - return self.get_wrapper( + return self._get_wrapper( self.printer.fileconfig.getint, option, default, minval, maxval) def getfloat(self, option, default=sentinel , minval=None, maxval=None, above=None, below=None): - return self.get_wrapper( + return self._get_wrapper( self.printer.fileconfig.getfloat, option, default , minval, maxval, above, below) def getboolean(self, option, default=sentinel): - return self.get_wrapper( + return self._get_wrapper( self.printer.fileconfig.getboolean, option, default) def getchoice(self, option, choices, default=sentinel): c = self.get(option, default) @@ -144,6 +148,31 @@ class Printer: self.mcus = [] def get_start_args(self): return self.start_args + def get_reactor(self): + return self.reactor + def get_state_message(self): + return self.state_message + def add_object(self, name, obj): + if obj in self.objects: + raise self.config_error( + "Printer object '%s' already created" % (name,)) + self.objects[name] = obj + def lookup_object(self, name, default=ConfigWrapper.sentinel): + if name in self.objects: + return self.objects[name] + if default is ConfigWrapper.sentinel: + raise self.config_error("Unknown config object '%s'" % (name,)) + return default + def lookup_module_objects(self, module_name): + prefix = module_name + ' ' + objs = [self.objects[n] + for n in sorted(self.objects) if n.startswith(prefix)] + if module_name in self.objects: + return [self.objects[module_name]] + objs + return objs + def set_rollover_info(self, name, info): + if self.bglogger is not None: + self.bglogger.set_rollover_info(name, info) def _stats(self, eventtime, force_output=False): toolhead = self.objects.get('toolhead') if toolhead is None: @@ -158,8 +187,6 @@ class Printer: out.append(m.stats(eventtime)) logging.info("Stats %.1f: %s", eventtime, ' '.join(out)) return eventtime + 1. - def add_object(self, name, obj): - self.objects[name] = obj def _load_config(self): self.fileconfig = ConfigParser.RawConfigParser() config_file = self.start_args['config_file'] @@ -173,7 +200,7 @@ class Printer: config = ConfigWrapper(self, 'printer') for m in [pins, mcu, chipmisc, toolhead, extruder, heater, fan]: m.add_printer_objects(self, config) - self.mcus = mcu.get_printer_mcus(self) + self.mcus = self.lookup_module_objects('mcu') # Validate that there are no undefined parameters in the config file valid_sections = { s: 1 for s, o in self.all_config_options } for section in self.fileconfig.sections(): @@ -237,8 +264,6 @@ class Printer: except: logging.exception("Unhandled exception during post run") return run_result - def get_state_message(self): - return self.state_message def invoke_shutdown(self, msg): if self.is_shutdown: return diff --git a/klippy/mcu.py b/klippy/mcu.py index a415ba25..15f9fcea 100644 --- a/klippy/mcu.py +++ b/klippy/mcu.py @@ -1,6 +1,6 @@ # Interface to Klipper micro-controller code # -# Copyright (C) 2016,2017 Kevin O'Connor +# Copyright (C) 2016-2018 Kevin O'Connor # # This file may be distributed under the terms of the GNU GPLv3 license. import sys, os, zlib, logging, math @@ -409,7 +409,8 @@ class MCU: def __init__(self, printer, config, clocksync): self._printer = printer self._clocksync = clocksync - self._name = config.section + self._reactor = printer.get_reactor() + self._name = config.get_name() if self._name.startswith('mcu '): self._name = self._name[4:] # Serial port @@ -419,7 +420,7 @@ class MCU: or self._serialport.startswith("/tmp/klipper_host_")): baud = config.getint('baud', 250000, minval=2400) self._serial = serialhdl.SerialReader( - printer.reactor, self._serialport, baud) + self._reactor, self._serialport, baud) # Restarts self._restart_method = 'command' if baud: @@ -430,8 +431,7 @@ class MCU: self._emergency_stop_cmd = None self._is_shutdown = self._is_timeout = False self._shutdown_msg = "" - if printer.bglogger is not None: - printer.bglogger.set_rollover_info(self._name, None) + printer.set_rollover_info(self._name, None) # Config building pins.get_printer_pins(printer).register_chip(self._name, self) self._oid_count = 0 @@ -482,7 +482,7 @@ class MCU: logging.info("Attempting automated MCU '%s' restart: %s", self._name, reason) self._printer.request_exit('firmware_restart') - self._printer.reactor.pause(self._printer.reactor.monotonic() + 2.000) + self._reactor.pause(self._reactor.monotonic() + 2.000) raise error("Attempt MCU '%s' restart failed" % (self._name,)) def _connect_file(self, pace=False): # In a debugging mode. Open debug output file and read data dictionary @@ -566,16 +566,15 @@ class MCU: raise error("MCU '%s' CRC does not match config" % (self._name,)) move_count = config_params['move_count'] logging.info("Configured MCU '%s' (%d moves)", self._name, move_count) - if self._printer.bglogger is not None: - msgparser = self._serial.msgparser - info = [ - "Configured MCU '%s' (%d moves)" % (self._name, move_count), - "Loaded MCU '%s' %d commands (%s / %s)" % ( - self._name, len(msgparser.messages_by_id), - msgparser.version, msgparser.build_versions), - "MCU '%s' config: %s" % (self._name, " ".join( - ["%s=%s" % (k, v) for k, v in msgparser.config.items()]))] - self._printer.bglogger.set_rollover_info(self._name, "\n".join(info)) + msgparser = self._serial.msgparser + info = [ + "Configured MCU '%s' (%d moves)" % (self._name, move_count), + "Loaded MCU '%s' %d commands (%s / %s)" % ( + self._name, len(msgparser.messages_by_id), + msgparser.version, msgparser.build_versions), + "MCU '%s' config: %s" % (self._name, " ".join( + ["%s=%s" % (k, v) for k, v in msgparser.config.items()]))] + self._printer.set_rollover_info(self._name, "\n".join(info)) self._steppersync = self._ffi_lib.steppersync_alloc( self._serial.serialqueue, self._stepqueues, len(self._stepqueues), move_count) @@ -663,18 +662,17 @@ class MCU: def clock32_to_clock64(self, clock32): return self._clocksync.clock32_to_clock64(clock32) def pause(self, waketime): - return self._printer.reactor.pause(waketime) + return self._reactor.pause(waketime) def monotonic(self): - return self._printer.reactor.monotonic() + return self._reactor.monotonic() # Restarts def _restart_arduino(self): logging.info("Attempting MCU '%s' reset", self._name) self.disconnect() - serialhdl.arduino_reset(self._serialport, self._printer.reactor) + serialhdl.arduino_reset(self._serialport, self._reactor) def _restart_via_command(self): - reactor = self._printer.reactor if ((self._reset_cmd is None and self._config_reset_cmd is None) - or not self._clocksync.is_active(reactor.monotonic())): + or not self._clocksync.is_active(self._reactor.monotonic())): logging.info("Unable to issue reset command on MCU '%s'", self._name) return if self._reset_cmd is None: @@ -682,19 +680,19 @@ class MCU: logging.info("Attempting MCU '%s' config_reset command", self._name) self._is_shutdown = True self.do_shutdown(force=True) - reactor.pause(reactor.monotonic() + 0.015) + self._reactor.pause(self._reactor.monotonic() + 0.015) self.send(self._config_reset_cmd.encode()) else: # Attempt reset via reset command logging.info("Attempting MCU '%s' reset command", self._name) self.send(self._reset_cmd.encode()) - reactor.pause(reactor.monotonic() + 0.015) + self._reactor.pause(self._reactor.monotonic() + 0.015) self.disconnect() def _restart_rpi_usb(self): logging.info("Attempting MCU '%s' reset via rpi usb power", self._name) self.disconnect() chelper.run_hub_ctrl(0) - self._printer.reactor.pause(self._printer.reactor.monotonic() + 2.) + self._reactor.pause(self._reactor.monotonic() + 2.) chelper.run_hub_ctrl(1) def microcontroller_restart(self): if self._restart_method == 'rpi_usb': @@ -773,20 +771,14 @@ def error_help(msg): return "" def add_printer_objects(printer, config): - mainsync = clocksync.ClockSync(printer.reactor) + reactor = printer.get_reactor() + mainsync = clocksync.ClockSync(reactor) printer.add_object('mcu', MCU(printer, config.getsection('mcu'), mainsync)) for s in config.get_prefix_sections('mcu '): printer.add_object(s.section, MCU( - printer, s, clocksync.SecondarySync(printer.reactor, mainsync))) - -def get_printer_mcus(printer): - return [printer.objects[n] for n in sorted(printer.objects) - if n.startswith('mcu')] + printer, s, clocksync.SecondarySync(reactor, mainsync))) def get_printer_mcu(printer, name): - mcu_name = name - if name != 'mcu': - mcu_name = 'mcu ' + name - if mcu_name not in printer.objects: - raise printer.config_error("Unknown MCU %s" % (name,)) - return printer.objects[mcu_name] + if name == 'mcu': + return printer.lookup_object(name) + return printer.lookup_object('mcu ' + name) diff --git a/klippy/pins.py b/klippy/pins.py index c7983280..f07771a0 100644 --- a/klippy/pins.py +++ b/klippy/pins.py @@ -234,7 +234,7 @@ def add_printer_objects(printer, config): printer.add_object('pins', PrinterPins()) def get_printer_pins(printer): - return printer.objects['pins'] + return printer.lookup_object('pins') def setup_pin(printer, pin_type, pin_desc): return get_printer_pins(printer).setup_pin(pin_type, pin_desc) diff --git a/klippy/stepper.py b/klippy/stepper.py index 61d59568..682b306b 100644 --- a/klippy/stepper.py +++ b/klippy/stepper.py @@ -36,7 +36,7 @@ def lookup_enable_pin(printer, pin): # Code storing the definitions for a stepper motor class PrinterStepper: def __init__(self, printer, config): - self.name = config.section + self.name = config.get_name() if self.name.startswith('stepper_'): self.name = self.name[8:] self.need_motor_enable = True @@ -101,7 +101,7 @@ class PrinterHomingStepper(PrinterStepper): else: raise config.error( "Unable to infer homing_positive_dir in section '%s'" % ( - config.section,)) + config.get_name(),)) # Endstop stepper phase position tracking self.homing_stepper_phases = config.getint( 'homing_stepper_phases', None, minval=0) @@ -169,9 +169,9 @@ class PrinterMultiStepper(PrinterHomingStepper): self.extras = [] self.all_step_const = [self.step_const] for i in range(1, 99): - if not config.has_section(config.section + str(i)): + if not config.has_section(config.get_name() + str(i)): break - extraconfig = config.getsection(config.section + str(i)) + extraconfig = config.getsection(config.get_name() + str(i)) extra = PrinterStepper(printer, extraconfig) self.extras.append(extra) self.all_step_const.append(extra.step_const) @@ -202,6 +202,6 @@ class PrinterMultiStepper(PrinterHomingStepper): return self.endstops def LookupMultiHomingStepper(printer, config): - if not config.has_section(config.section + '1'): + if not config.has_section(config.get_name() + '1'): return PrinterHomingStepper(printer, config) return PrinterMultiStepper(printer, config) diff --git a/klippy/toolhead.py b/klippy/toolhead.py index ce9db286..83357732 100644 --- a/klippy/toolhead.py +++ b/klippy/toolhead.py @@ -1,6 +1,6 @@ # Code for coordinating events on the printer toolhead # -# Copyright (C) 2016 Kevin O'Connor +# Copyright (C) 2016-2018 Kevin O'Connor # # This file may be distributed under the terms of the GNU GPLv3 license. import math, logging @@ -183,8 +183,8 @@ STALL_TIME = 0.100 class ToolHead: def __init__(self, printer, config): self.printer = printer - self.reactor = printer.reactor - self.all_mcus = mcu.get_printer_mcus(printer) + self.reactor = printer.get_reactor() + self.all_mcus = printer.lookup_module_objects('mcu') self.mcu = self.all_mcus[0] self.max_velocity = config.getfloat('max_velocity', above=0.) self.max_accel = config.getfloat('max_accel', above=0.)