diff options
Diffstat (limited to 'klippy/extras/probe.py')
-rw-r--r-- | klippy/extras/probe.py | 445 |
1 files changed, 273 insertions, 172 deletions
diff --git a/klippy/extras/probe.py b/klippy/extras/probe.py index bfeb33ce..3811f883 100644 --- a/klippy/extras/probe.py +++ b/klippy/extras/probe.py @@ -13,13 +13,13 @@ consider reducing the Z axis minimum position so the probe can travel further (the Z minimum position can be negative). """ + # Calculate the average Z from a set of positions -def calc_probe_z_average(positions, method='average'): - if method != 'median': +def calc_probe_z_average(positions, method="average"): + if method != "median": # Use mean average count = float(len(positions)) - return [sum([pos[i] for pos in positions]) / count - for i in range(3)] + return [sum([pos[i] for pos in positions]) / count for i in range(3)] # Use median z_sorted = sorted(positions, key=(lambda p: p[2])) middle = len(positions) // 2 @@ -27,13 +27,14 @@ def calc_probe_z_average(positions, method='average'): # odd number of samples return z_sorted[middle] # even number of samples - return calc_probe_z_average(z_sorted[middle-1:middle+1], 'average') + return calc_probe_z_average(z_sorted[middle - 1 : middle + 1], "average") ###################################################################### # Probe device implementation helpers ###################################################################### + # Helper to implement common probing commands class ProbeCommandHelper: def __init__(self, config, probe, query_endstop=None): @@ -41,57 +42,75 @@ class ProbeCommandHelper: self.probe = probe self.query_endstop = query_endstop self.name = config.get_name() - gcode = self.printer.lookup_object('gcode') + gcode = self.printer.lookup_object("gcode") # QUERY_PROBE command self.last_state = False - gcode.register_command('QUERY_PROBE', self.cmd_QUERY_PROBE, - desc=self.cmd_QUERY_PROBE_help) + gcode.register_command( + "QUERY_PROBE", self.cmd_QUERY_PROBE, desc=self.cmd_QUERY_PROBE_help + ) # PROBE command - self.last_z_result = 0. - gcode.register_command('PROBE', self.cmd_PROBE, - desc=self.cmd_PROBE_help) + self.last_z_result = 0.0 + gcode.register_command("PROBE", self.cmd_PROBE, desc=self.cmd_PROBE_help) # PROBE_CALIBRATE command - self.probe_calibrate_z = 0. - gcode.register_command('PROBE_CALIBRATE', self.cmd_PROBE_CALIBRATE, - desc=self.cmd_PROBE_CALIBRATE_help) + self.probe_calibrate_z = 0.0 + gcode.register_command( + "PROBE_CALIBRATE", + self.cmd_PROBE_CALIBRATE, + desc=self.cmd_PROBE_CALIBRATE_help, + ) # Other commands - gcode.register_command('PROBE_ACCURACY', self.cmd_PROBE_ACCURACY, - desc=self.cmd_PROBE_ACCURACY_help) - gcode.register_command('Z_OFFSET_APPLY_PROBE', - self.cmd_Z_OFFSET_APPLY_PROBE, - desc=self.cmd_Z_OFFSET_APPLY_PROBE_help) + gcode.register_command( + "PROBE_ACCURACY", self.cmd_PROBE_ACCURACY, desc=self.cmd_PROBE_ACCURACY_help + ) + gcode.register_command( + "Z_OFFSET_APPLY_PROBE", + self.cmd_Z_OFFSET_APPLY_PROBE, + desc=self.cmd_Z_OFFSET_APPLY_PROBE_help, + ) + def _move(self, coord, speed): - self.printer.lookup_object('toolhead').manual_move(coord, speed) + self.printer.lookup_object("toolhead").manual_move(coord, speed) + def get_status(self, eventtime): - return {'name': self.name, - 'last_query': self.last_state, - 'last_z_result': self.last_z_result} + return { + "name": self.name, + "last_query": self.last_state, + "last_z_result": self.last_z_result, + } + cmd_QUERY_PROBE_help = "Return the status of the z-probe" + def cmd_QUERY_PROBE(self, gcmd): if self.query_endstop is None: raise gcmd.error("Probe does not support QUERY_PROBE") - toolhead = self.printer.lookup_object('toolhead') + toolhead = self.printer.lookup_object("toolhead") print_time = toolhead.get_last_move_time() res = self.query_endstop(print_time) self.last_state = res gcmd.respond_info("probe: %s" % (["open", "TRIGGERED"][not not res],)) + cmd_PROBE_help = "Probe Z-height at current XY position" + def cmd_PROBE(self, gcmd): pos = run_single_probe(self.probe, gcmd) gcmd.respond_info("Result is z=%.6f" % (pos[2],)) self.last_z_result = pos[2] + def probe_calibrate_finalize(self, kin_pos): if kin_pos is None: return z_offset = self.probe_calibrate_z - kin_pos[2] - gcode = self.printer.lookup_object('gcode') + gcode = self.printer.lookup_object("gcode") gcode.respond_info( "%s: z_offset: %.3f\n" "The SAVE_CONFIG command will update the printer config file\n" - "with the above and restart the printer." % (self.name, z_offset)) - configfile = self.printer.lookup_object('configfile') - configfile.set(self.name, 'z_offset', "%.3f" % (z_offset,)) + "with the above and restart the printer." % (self.name, z_offset) + ) + configfile = self.printer.lookup_object("configfile") + configfile.set(self.name, "z_offset", "%.3f" % (z_offset,)) + cmd_PROBE_CALIBRATE_help = "Calibrate the probe's z_offset" + def cmd_PROBE_CALIBRATE(self, gcmd): manual_probe.verify_no_manual_probe(self.printer) params = self.probe.get_probe_params(gcmd) @@ -99,32 +118,43 @@ class ProbeCommandHelper: curpos = run_single_probe(self.probe, gcmd) # Move away from the bed self.probe_calibrate_z = curpos[2] - curpos[2] += 5. - self._move(curpos, params['lift_speed']) + curpos[2] += 5.0 + self._move(curpos, params["lift_speed"]) # Move the nozzle over the probe point x_offset, y_offset, z_offset = self.probe.get_offsets() curpos[0] += x_offset curpos[1] += y_offset - self._move(curpos, params['probe_speed']) + self._move(curpos, params["probe_speed"]) # Start manual probe - manual_probe.ManualProbeHelper(self.printer, gcmd, - self.probe_calibrate_finalize) + manual_probe.ManualProbeHelper( + self.printer, gcmd, self.probe_calibrate_finalize + ) + cmd_PROBE_ACCURACY_help = "Probe Z-height accuracy at current XY position" + def cmd_PROBE_ACCURACY(self, gcmd): params = self.probe.get_probe_params(gcmd) sample_count = gcmd.get_int("SAMPLES", 10, minval=1) - toolhead = self.printer.lookup_object('toolhead') + toolhead = self.printer.lookup_object("toolhead") pos = toolhead.get_position() - gcmd.respond_info("PROBE_ACCURACY at X:%.3f Y:%.3f Z:%.3f" - " (samples=%d retract=%.3f" - " speed=%.1f lift_speed=%.1f)\n" - % (pos[0], pos[1], pos[2], - sample_count, params['sample_retract_dist'], - params['probe_speed'], params['lift_speed'])) + gcmd.respond_info( + "PROBE_ACCURACY at X:%.3f Y:%.3f Z:%.3f" + " (samples=%d retract=%.3f" + " speed=%.1f lift_speed=%.1f)\n" + % ( + pos[0], + pos[1], + pos[2], + sample_count, + params["sample_retract_dist"], + params["probe_speed"], + params["lift_speed"], + ) + ) # Create dummy gcmd with SAMPLES=1 fo_params = dict(gcmd.get_command_parameters()) - fo_params['SAMPLES'] = '1' - gcode = self.printer.lookup_object('gcode') + fo_params["SAMPLES"] = "1" + gcode = self.printer.lookup_object("gcode") fo_gcmd = gcode.create_gcode_command("", "", fo_params) # Probe bed sample_count times probe_session = self.probe.start_probe_session(fo_gcmd) @@ -135,30 +165,33 @@ class ProbeCommandHelper: probe_num += 1 # Retract pos = toolhead.get_position() - liftpos = [None, None, pos[2] + params['sample_retract_dist']] - self._move(liftpos, params['lift_speed']) + liftpos = [None, None, pos[2] + params["sample_retract_dist"]] + self._move(liftpos, params["lift_speed"]) positions = probe_session.pull_probed_results() probe_session.end_probe_session() # Calculate maximum, minimum and average values max_value = max([p[2] for p in positions]) min_value = min([p[2] for p in positions]) range_value = max_value - min_value - avg_value = calc_probe_z_average(positions, 'average')[2] - median = calc_probe_z_average(positions, 'median')[2] + avg_value = calc_probe_z_average(positions, "average")[2] + median = calc_probe_z_average(positions, "median")[2] # calculate the standard deviation deviation_sum = 0 for i in range(len(positions)): - deviation_sum += pow(positions[i][2] - avg_value, 2.) + deviation_sum += pow(positions[i][2] - avg_value, 2.0) sigma = (deviation_sum / len(positions)) ** 0.5 # Show information gcmd.respond_info( "probe accuracy results: maximum %.6f, minimum %.6f, range %.6f, " - "average %.6f, median %.6f, standard deviation %.6f" % ( - max_value, min_value, range_value, avg_value, median, sigma)) + "average %.6f, median %.6f, standard deviation %.6f" + % (max_value, min_value, range_value, avg_value, median, sigma) + ) + cmd_Z_OFFSET_APPLY_PROBE_help = "Adjust the probe's z_offset" + def cmd_Z_OFFSET_APPLY_PROBE(self, gcmd): gcode_move = self.printer.lookup_object("gcode_move") - offset = gcode_move.get_status()['homing_origin'].z + offset = gcode_move.get_status()["homing_origin"].z if offset == 0: gcmd.respond_info("Nothing to do: Z Offset is 0") return @@ -167,32 +200,37 @@ class ProbeCommandHelper: gcmd.respond_info( "%s: z_offset: %.3f\n" "The SAVE_CONFIG command will update the printer config file\n" - "with the above and restart the printer." - % (self.name, new_calibrate)) - configfile = self.printer.lookup_object('configfile') - configfile.set(self.name, 'z_offset', "%.3f" % (new_calibrate,)) + "with the above and restart the printer." % (self.name, new_calibrate) + ) + configfile = self.printer.lookup_object("configfile") + configfile.set(self.name, "z_offset", "%.3f" % (new_calibrate,)) + # Helper to lookup the minimum Z position for the printer def lookup_minimum_z(config): zconfig = manual_probe.lookup_z_endstop_config(config) if zconfig is not None: - return zconfig.getfloat('position_min', 0., note_valid=False) - pconfig = config.getsection('printer') - return pconfig.getfloat('minimum_z_position', 0., note_valid=False) + return zconfig.getfloat("position_min", 0.0, note_valid=False) + pconfig = config.getsection("printer") + return pconfig.getfloat("minimum_z_position", 0.0, note_valid=False) + # Helper to lookup all the Z axis steppers class LookupZSteppers: def __init__(self, config, add_stepper_cb): self.printer = config.get_printer() self.add_stepper_cb = add_stepper_cb - self.printer.register_event_handler('klippy:mcu_identify', - self._handle_mcu_identify) + self.printer.register_event_handler( + "klippy:mcu_identify", self._handle_mcu_identify + ) + def _handle_mcu_identify(self): - kin = self.printer.lookup_object('toolhead').get_kinematics() + kin = self.printer.lookup_object("toolhead").get_kinematics() for stepper in kin.get_steppers(): - if stepper.is_active_axis('z'): + if stepper.is_active_axis("z"): self.add_stepper_cb(stepper) + # Homing via probe:z_virtual_endstop class HomingViaProbeHelper: def __init__(self, config, mcu_probe, param_helper): @@ -204,34 +242,44 @@ class HomingViaProbeHelper: self.results = [] LookupZSteppers(config, self.mcu_probe.add_stepper) # Register z_virtual_endstop pin - self.printer.lookup_object('pins').register_chip('probe', self) + self.printer.lookup_object("pins").register_chip("probe", self) # Register event handlers - self.printer.register_event_handler("homing:homing_move_begin", - self._handle_homing_move_begin) - self.printer.register_event_handler("homing:homing_move_end", - self._handle_homing_move_end) - self.printer.register_event_handler("homing:home_rails_begin", - self._handle_home_rails_begin) - self.printer.register_event_handler("homing:home_rails_end", - self._handle_home_rails_end) - self.printer.register_event_handler("gcode:command_error", - self._handle_command_error) + self.printer.register_event_handler( + "homing:homing_move_begin", self._handle_homing_move_begin + ) + self.printer.register_event_handler( + "homing:homing_move_end", self._handle_homing_move_end + ) + self.printer.register_event_handler( + "homing:home_rails_begin", self._handle_home_rails_begin + ) + self.printer.register_event_handler( + "homing:home_rails_end", self._handle_home_rails_end + ) + self.printer.register_event_handler( + "gcode:command_error", self._handle_command_error + ) + def _handle_homing_move_begin(self, hmove): if self.mcu_probe in hmove.get_mcu_endstops(): self.mcu_probe.probe_prepare(hmove) + def _handle_homing_move_end(self, hmove): if self.mcu_probe in hmove.get_mcu_endstops(): self.mcu_probe.probe_finish(hmove) + def _handle_home_rails_begin(self, homing_state, rails): endstops = [es for rail in rails for es, name in rail.get_endstops()] if self.mcu_probe in endstops: self.mcu_probe.multi_probe_begin() self.multi_probe_pending = True + def _handle_home_rails_end(self, homing_state, rails): endstops = [es for rail in rails for es, name in rail.get_endstops()] if self.multi_probe_pending and self.mcu_probe in endstops: self.multi_probe_pending = False self.mcu_probe.multi_probe_end() + def _handle_command_error(self): if self.multi_probe_pending: self.multi_probe_pending = False @@ -239,82 +287,96 @@ class HomingViaProbeHelper: self.mcu_probe.multi_probe_end() except: logging.exception("Homing multi-probe end") + def setup_pin(self, pin_type, pin_params): - if pin_type != 'endstop' or pin_params['pin'] != 'z_virtual_endstop': + if pin_type != "endstop" or pin_params["pin"] != "z_virtual_endstop": raise pins.error("Probe virtual endstop only useful as endstop pin") - if pin_params['invert'] or pin_params['pullup']: + if pin_params["invert"] or pin_params["pullup"]: raise pins.error("Can not pullup/invert probe virtual endstop") return self.mcu_probe + # Helper to convert probe based commands to use homing module def start_probe_session(self, gcmd): self.mcu_probe.multi_probe_begin() self.results = [] return self + def run_probe(self, gcmd): - toolhead = self.printer.lookup_object('toolhead') + toolhead = self.printer.lookup_object("toolhead") pos = toolhead.get_position() pos[2] = self.z_min_position - speed = self.param_helper.get_probe_params(gcmd)['probe_speed'] - phoming = self.printer.lookup_object('homing') + speed = self.param_helper.get_probe_params(gcmd)["probe_speed"] + phoming = self.printer.lookup_object("homing") self.results.append(phoming.probing_move(self.mcu_probe, pos, speed)) + def pull_probed_results(self): res = self.results self.results = [] return res + def end_probe_session(self): self.results = [] self.mcu_probe.multi_probe_end() + class ProbeVirtualEndstopDeprecation: def __init__(self, config): self._name = config.get_name() self._printer = config.get_printer() # Register z_virtual_endstop pin - self._printer.lookup_object('pins').register_chip('probe', self) + self._printer.lookup_object("pins").register_chip("probe", self) + def setup_pin(self, pin_type, pin_params): raise self._printer.config_error( "Module [%s] does not support `probe:z_virtual_endstop`" - ", use a pin instead." % (self._name,)) + ", use a pin instead." % (self._name,) + ) + # Helper to read multi-sample parameters from config class ProbeParameterHelper: def __init__(self, config): - gcode = config.get_printer().lookup_object('gcode') + gcode = config.get_printer().lookup_object("gcode") self.dummy_gcode_cmd = gcode.create_gcode_command("", "", {}) # Configurable probing speeds - self.speed = config.getfloat('speed', 5.0, above=0.) - self.lift_speed = config.getfloat('lift_speed', self.speed, above=0.) + self.speed = config.getfloat("speed", 5.0, above=0.0) + self.lift_speed = config.getfloat("lift_speed", self.speed, above=0.0) # Multi-sample support (for improved accuracy) - self.sample_count = config.getint('samples', 1, minval=1) - self.sample_retract_dist = config.getfloat('sample_retract_dist', 2., - above=0.) - atypes = ['median', 'average'] - self.samples_result = config.getchoice('samples_result', atypes, - 'average') - self.samples_tolerance = config.getfloat('samples_tolerance', 0.100, - minval=0.) - self.samples_retries = config.getint('samples_tolerance_retries', 0, - minval=0) + self.sample_count = config.getint("samples", 1, minval=1) + self.sample_retract_dist = config.getfloat( + "sample_retract_dist", 2.0, above=0.0 + ) + atypes = ["median", "average"] + self.samples_result = config.getchoice("samples_result", atypes, "average") + self.samples_tolerance = config.getfloat("samples_tolerance", 0.100, minval=0.0) + self.samples_retries = config.getint("samples_tolerance_retries", 0, minval=0) + def get_probe_params(self, gcmd=None): if gcmd is None: gcmd = self.dummy_gcode_cmd - probe_speed = gcmd.get_float("PROBE_SPEED", self.speed, above=0.) - lift_speed = gcmd.get_float("LIFT_SPEED", self.lift_speed, above=0.) + probe_speed = gcmd.get_float("PROBE_SPEED", self.speed, above=0.0) + lift_speed = gcmd.get_float("LIFT_SPEED", self.lift_speed, above=0.0) samples = gcmd.get_int("SAMPLES", self.sample_count, minval=1) - sample_retract_dist = gcmd.get_float("SAMPLE_RETRACT_DIST", - self.sample_retract_dist, above=0.) - samples_tolerance = gcmd.get_float("SAMPLES_TOLERANCE", - self.samples_tolerance, minval=0.) - samples_retries = gcmd.get_int("SAMPLES_TOLERANCE_RETRIES", - self.samples_retries, minval=0) + sample_retract_dist = gcmd.get_float( + "SAMPLE_RETRACT_DIST", self.sample_retract_dist, above=0.0 + ) + samples_tolerance = gcmd.get_float( + "SAMPLES_TOLERANCE", self.samples_tolerance, minval=0.0 + ) + samples_retries = gcmd.get_int( + "SAMPLES_TOLERANCE_RETRIES", self.samples_retries, minval=0 + ) samples_result = gcmd.get("SAMPLES_RESULT", self.samples_result) - return {'probe_speed': probe_speed, - 'lift_speed': lift_speed, - 'samples': samples, - 'sample_retract_dist': sample_retract_dist, - 'samples_tolerance': samples_tolerance, - 'samples_tolerance_retries': samples_retries, - 'samples_result': samples_result} + return { + "probe_speed": probe_speed, + "lift_speed": lift_speed, + "samples": samples, + "sample_retract_dist": sample_retract_dist, + "samples_tolerance": samples_tolerance, + "samples_tolerance_retries": samples_retries, + "samples_result": samples_result, + } + # Helper to track multiple probe attempts in a single command class ProbeSessionHelper: @@ -326,23 +388,29 @@ class ProbeSessionHelper: self.hw_probe_session = None self.results = [] # Register event handlers - self.printer.register_event_handler("gcode:command_error", - self._handle_command_error) + self.printer.register_event_handler( + "gcode:command_error", self._handle_command_error + ) + def _handle_command_error(self): if self.hw_probe_session is not None: try: self.end_probe_session() except: logging.exception("Multi-probe end") + def _probe_state_error(self): raise self.printer.command_error( - "Internal probe error - start/end probe session mismatch") + "Internal probe error - start/end probe session mismatch" + ) + def start_probe_session(self, gcmd): if self.hw_probe_session is not None: self._probe_state_error() self.hw_probe_session = self.start_session_cb(gcmd) self.results = [] return self + def end_probe_session(self): hw_probe_session = self.hw_probe_session if hw_probe_session is None: @@ -350,10 +418,11 @@ class ProbeSessionHelper: self.results = [] self.hw_probe_session = None hw_probe_session.end_probe_session() + def _probe(self, gcmd): - toolhead = self.printer.lookup_object('toolhead') + toolhead = self.printer.lookup_object("toolhead") curtime = self.printer.get_reactor().monotonic() - if 'z' not in toolhead.get_status(curtime)['homed_axes']: + if "z" not in toolhead.get_status(curtime)["homed_axes"]: raise self.printer.command_error("Must home before probe") try: self.hw_probe_session.run_probe(gcmd) @@ -366,27 +435,27 @@ class ProbeSessionHelper: # Allow axis_twist_compensation to update results self.printer.send_event("probe:update_results", epos) # Report results - gcode = self.printer.lookup_object('gcode') - gcode.respond_info("probe at %.3f,%.3f is z=%.6f" - % (epos[0], epos[1], epos[2])) + gcode = self.printer.lookup_object("gcode") + gcode.respond_info("probe at %.3f,%.3f is z=%.6f" % (epos[0], epos[1], epos[2])) return epos[:3] + def run_probe(self, gcmd): if self.hw_probe_session is None: self._probe_state_error() params = self.param_helper.get_probe_params(gcmd) - toolhead = self.printer.lookup_object('toolhead') + toolhead = self.printer.lookup_object("toolhead") probexy = toolhead.get_position()[:2] retries = 0 positions = [] - sample_count = params['samples'] + sample_count = params["samples"] while len(positions) < sample_count: # Probe position pos = self._probe(gcmd) positions.append(pos) # Check samples tolerance z_positions = [p[2] for p in positions] - if max(z_positions)-min(z_positions) > params['samples_tolerance']: - if retries >= params['samples_tolerance_retries']: + if max(z_positions) - min(z_positions) > params["samples_tolerance"]: + if retries >= params["samples_tolerance_retries"]: raise gcmd.error("Probe samples exceed samples_tolerance") gcmd.respond_info("Probe samples exceed tolerance. Retrying...") retries += 1 @@ -394,22 +463,26 @@ class ProbeSessionHelper: # Retract if len(positions) < sample_count: toolhead.manual_move( - probexy + [pos[2] + params['sample_retract_dist']], - params['lift_speed']) + probexy + [pos[2] + params["sample_retract_dist"]], + params["lift_speed"], + ) # Calculate result - epos = calc_probe_z_average(positions, params['samples_result']) + epos = calc_probe_z_average(positions, params["samples_result"]) self.results.append(epos) + def pull_probed_results(self): res = self.results self.results = [] return res + # Helper to read the xyz probe offsets from the config class ProbeOffsetsHelper: def __init__(self, config): - self.x_offset = config.getfloat('x_offset', 0.) - self.y_offset = config.getfloat('y_offset', 0.) - self.z_offset = config.getfloat('z_offset') + self.x_offset = config.getfloat("x_offset", 0.0) + self.y_offset = config.getfloat("y_offset", 0.0) + self.z_offset = config.getfloat("z_offset") + def get_offsets(self): return self.x_offset, self.y_offset, self.z_offset @@ -418,6 +491,7 @@ class ProbeOffsetsHelper: # Tools for utilizing the probe ###################################################################### + # Helper code that can probe a series of points and report the # position at each point. class ProbePointsHelper: @@ -426,45 +500,55 @@ class ProbePointsHelper: self.finalize_callback = finalize_callback self.probe_points = default_points self.name = config.get_name() - self.gcode = self.printer.lookup_object('gcode') + self.gcode = self.printer.lookup_object("gcode") # Read config settings - if default_points is None or config.get('points', None) is not None: - self.probe_points = config.getlists('points', seps=(',', '\n'), - parser=float, count=2) - def_move_z = config.getfloat('horizontal_move_z', 5.) + if default_points is None or config.get("points", None) is not None: + self.probe_points = config.getlists( + "points", seps=(",", "\n"), parser=float, count=2 + ) + def_move_z = config.getfloat("horizontal_move_z", 5.0) self.default_horizontal_move_z = def_move_z - self.speed = config.getfloat('speed', 50., above=0.) + self.speed = config.getfloat("speed", 50.0, above=0.0) self.use_offsets = False # Internal probing state self.lift_speed = self.speed - self.probe_offsets = (0., 0., 0.) + self.probe_offsets = (0.0, 0.0, 0.0) self.manual_results = [] - def minimum_points(self,n): + + def minimum_points(self, n): if len(self.probe_points) < n: raise self.printer.config_error( - "Need at least %d probe points for %s" % (n, self.name)) + "Need at least %d probe points for %s" % (n, self.name) + ) + def update_probe_points(self, points, min_points): self.probe_points = points self.minimum_points(min_points) + def use_xy_offsets(self, use_offsets): self.use_offsets = use_offsets + def get_lift_speed(self): return self.lift_speed + def _move(self, coord, speed): - self.printer.lookup_object('toolhead').manual_move(coord, speed) + self.printer.lookup_object("toolhead").manual_move(coord, speed) + def _raise_tool(self, is_first=False): speed = self.lift_speed if is_first: # Use full speed to first probe position speed = self.speed self._move([None, None, self.horizontal_move_z], speed) + def _invoke_callback(self, results): # Flush lookahead queue - toolhead = self.printer.lookup_object('toolhead') + toolhead = self.printer.lookup_object("toolhead") toolhead.get_last_move_time() # Invoke callback res = self.finalize_callback(self.probe_offsets, results) return res != "retry" + def _move_next(self, probe_num): # Move to next XY probe point nextpos = list(self.probe_points[probe_num]) @@ -472,27 +556,26 @@ class ProbePointsHelper: nextpos[0] -= self.probe_offsets[0] nextpos[1] -= self.probe_offsets[1] self._move(nextpos, self.speed) + def start_probe(self, gcmd): manual_probe.verify_no_manual_probe(self.printer) # Lookup objects - probe = self.printer.lookup_object('probe', None) - method = gcmd.get('METHOD', 'automatic').lower() + probe = self.printer.lookup_object("probe", None) + method = gcmd.get("METHOD", "automatic").lower() def_move_z = self.default_horizontal_move_z - self.horizontal_move_z = gcmd.get_float('HORIZONTAL_MOVE_Z', - def_move_z) - if probe is None or method == 'manual': + self.horizontal_move_z = gcmd.get_float("HORIZONTAL_MOVE_Z", def_move_z) + if probe is None or method == "manual": # Manual probe self.lift_speed = self.speed - self.probe_offsets = (0., 0., 0.) + self.probe_offsets = (0.0, 0.0, 0.0) self.manual_results = [] self._manual_probe_start() return # Perform automatic probing - self.lift_speed = probe.get_probe_params(gcmd)['lift_speed'] + self.lift_speed = probe.get_probe_params(gcmd)["lift_speed"] self.probe_offsets = probe.get_offsets() if self.horizontal_move_z < self.probe_offsets[2]: - raise gcmd.error("horizontal_move_z can't be less than" - " probe's z_offset") + raise gcmd.error("horizontal_move_z can't be less than" " probe's z_offset") probe_session = probe.start_probe_session(gcmd) probe_num = 0 while 1: @@ -508,6 +591,7 @@ class ProbePointsHelper: probe_session.run_probe(gcmd) probe_num += 1 probe_session.end_probe_session() + def _manual_probe_start(self): self._raise_tool(not self.manual_results) if len(self.manual_results) >= len(self.probe_points): @@ -518,14 +602,15 @@ class ProbePointsHelper: self.manual_results = [] self._move_next(len(self.manual_results)) gcmd = self.gcode.create_gcode_command("", "", {}) - manual_probe.ManualProbeHelper(self.printer, gcmd, - self._manual_probe_finalize) + manual_probe.ManualProbeHelper(self.printer, gcmd, self._manual_probe_finalize) + def _manual_probe_finalize(self, kin_pos): if kin_pos is None: return self.manual_results.append(kin_pos) self._manual_probe_start() + # Helper to obtain a single probe measurement def run_single_probe(probe, gcmd): probe_session = probe.start_probe_session(gcmd) @@ -539,21 +624,21 @@ def run_single_probe(probe, gcmd): # Handle [probe] config ###################################################################### + # Endstop wrapper that enables probe specific features class ProbeEndstopWrapper: def __init__(self, config): self.printer = config.get_printer() - self.position_endstop = config.getfloat('z_offset') - self.stow_on_each_sample = config.getboolean( - 'deactivate_on_each_sample', True) - gcode_macro = self.printer.load_object(config, 'gcode_macro') - self.activate_gcode = gcode_macro.load_template( - config, 'activate_gcode', '') + self.position_endstop = config.getfloat("z_offset") + self.stow_on_each_sample = config.getboolean("deactivate_on_each_sample", True) + gcode_macro = self.printer.load_object(config, "gcode_macro") + self.activate_gcode = gcode_macro.load_template(config, "activate_gcode", "") self.deactivate_gcode = gcode_macro.load_template( - config, 'deactivate_gcode', '') + config, "deactivate_gcode", "" + ) # Create an "endstop" object to handle the probe pin - ppins = self.printer.lookup_object('pins') - self.mcu_endstop = ppins.setup_pin('endstop', config.get('pin')) + ppins = self.printer.lookup_object("pins") + self.mcu_endstop = ppins.setup_pin("endstop", config.get("pin")) # Wrappers self.get_mcu = self.mcu_endstop.get_mcu self.add_stepper = self.mcu_endstop.add_stepper @@ -562,62 +647,78 @@ class ProbeEndstopWrapper: self.home_wait = self.mcu_endstop.home_wait self.query_endstop = self.mcu_endstop.query_endstop # multi probes state - self.multi = 'OFF' + self.multi = "OFF" + def _raise_probe(self): - toolhead = self.printer.lookup_object('toolhead') + toolhead = self.printer.lookup_object("toolhead") start_pos = toolhead.get_position() self.deactivate_gcode.run_gcode_from_command() if toolhead.get_position()[:3] != start_pos[:3]: raise self.printer.command_error( - "Toolhead moved during probe deactivate_gcode script") + "Toolhead moved during probe deactivate_gcode script" + ) + def _lower_probe(self): - toolhead = self.printer.lookup_object('toolhead') + toolhead = self.printer.lookup_object("toolhead") start_pos = toolhead.get_position() self.activate_gcode.run_gcode_from_command() if toolhead.get_position()[:3] != start_pos[:3]: raise self.printer.command_error( - "Toolhead moved during probe activate_gcode script") + "Toolhead moved during probe activate_gcode script" + ) + def multi_probe_begin(self): if self.stow_on_each_sample: return - self.multi = 'FIRST' + self.multi = "FIRST" + def multi_probe_end(self): if self.stow_on_each_sample: return self._raise_probe() - self.multi = 'OFF' + self.multi = "OFF" + def probe_prepare(self, hmove): - if self.multi == 'OFF' or self.multi == 'FIRST': + if self.multi == "OFF" or self.multi == "FIRST": self._lower_probe() - if self.multi == 'FIRST': - self.multi = 'ON' + if self.multi == "FIRST": + self.multi = "ON" + def probe_finish(self, hmove): - if self.multi == 'OFF': + if self.multi == "OFF": self._raise_probe() + def get_position_endstop(self): return self.position_endstop + # Main external probe interface class PrinterProbe: def __init__(self, config): self.printer = config.get_printer() self.mcu_probe = ProbeEndstopWrapper(config) - self.cmd_helper = ProbeCommandHelper(config, self, - self.mcu_probe.query_endstop) + self.cmd_helper = ProbeCommandHelper(config, self, self.mcu_probe.query_endstop) self.probe_offsets = ProbeOffsetsHelper(config) self.param_helper = ProbeParameterHelper(config) - self.homing_helper = HomingViaProbeHelper(config, self.mcu_probe, - self.param_helper) + self.homing_helper = HomingViaProbeHelper( + config, self.mcu_probe, self.param_helper + ) self.probe_session = ProbeSessionHelper( - config, self.param_helper, self.homing_helper.start_probe_session) + config, self.param_helper, self.homing_helper.start_probe_session + ) + def get_probe_params(self, gcmd=None): return self.param_helper.get_probe_params(gcmd) + def get_offsets(self): return self.probe_offsets.get_offsets() + def get_status(self, eventtime): return self.cmd_helper.get_status(eventtime) + def start_probe_session(self, gcmd): return self.probe_session.start_probe_session(gcmd) + def load_config(config): return PrinterProbe(config) |