mirror of
https://github.com/andreili/klipper.git
synced 2025-08-24 03:44:06 +02:00
Currently, `MANUAL_STEPPER STOP_ON_ENDSTOP=1` type commands will move until hitting the endstop, but it will still always consume the total amount of move time. That is, following moves can't be started until the total possible time of the homing move is completed. Implement "drip moves" so that the code only schedules the movement in small segments. This allows following movements to be scheduled without a significant delay. Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
144 lines
6.4 KiB
Python
144 lines
6.4 KiB
Python
# Support for a manual controlled stepper
|
|
#
|
|
# Copyright (C) 2019-2021 Kevin O'Connor <kevin@koconnor.net>
|
|
#
|
|
# This file may be distributed under the terms of the GNU GPLv3 license.
|
|
import stepper, chelper
|
|
from . import force_move
|
|
|
|
class ManualStepper:
|
|
def __init__(self, config):
|
|
self.printer = config.get_printer()
|
|
if config.get('endstop_pin', None) is not None:
|
|
self.can_home = True
|
|
self.rail = stepper.PrinterRail(
|
|
config, need_position_minmax=False, default_position_endstop=0.)
|
|
self.steppers = self.rail.get_steppers()
|
|
else:
|
|
self.can_home = False
|
|
self.rail = stepper.PrinterStepper(config)
|
|
self.steppers = [self.rail]
|
|
self.velocity = config.getfloat('velocity', 5., above=0.)
|
|
self.accel = self.homing_accel = config.getfloat('accel', 0., minval=0.)
|
|
self.next_cmd_time = 0.
|
|
# Setup iterative solver
|
|
ffi_main, ffi_lib = chelper.get_ffi()
|
|
self.trapq = ffi_main.gc(ffi_lib.trapq_alloc(), ffi_lib.trapq_free)
|
|
self.trapq_append = ffi_lib.trapq_append
|
|
self.trapq_finalize_moves = ffi_lib.trapq_finalize_moves
|
|
self.rail.setup_itersolve('cartesian_stepper_alloc', b'x')
|
|
self.rail.set_trapq(self.trapq)
|
|
# Register commands
|
|
stepper_name = config.get_name().split()[1]
|
|
gcode = self.printer.lookup_object('gcode')
|
|
gcode.register_mux_command('MANUAL_STEPPER', "STEPPER",
|
|
stepper_name, self.cmd_MANUAL_STEPPER,
|
|
desc=self.cmd_MANUAL_STEPPER_help)
|
|
def sync_print_time(self):
|
|
toolhead = self.printer.lookup_object('toolhead')
|
|
print_time = toolhead.get_last_move_time()
|
|
if self.next_cmd_time > print_time:
|
|
toolhead.dwell(self.next_cmd_time - print_time)
|
|
else:
|
|
self.next_cmd_time = print_time
|
|
def do_enable(self, enable):
|
|
self.sync_print_time()
|
|
stepper_enable = self.printer.lookup_object('stepper_enable')
|
|
if enable:
|
|
for s in self.steppers:
|
|
se = stepper_enable.lookup_enable(s.get_name())
|
|
se.motor_enable(self.next_cmd_time)
|
|
else:
|
|
for s in self.steppers:
|
|
se = stepper_enable.lookup_enable(s.get_name())
|
|
se.motor_disable(self.next_cmd_time)
|
|
self.sync_print_time()
|
|
def do_set_position(self, setpos):
|
|
self.rail.set_position([setpos, 0., 0.])
|
|
def _submit_move(self, movetime, movepos, speed, accel):
|
|
cp = self.rail.get_commanded_position()
|
|
dist = movepos - cp
|
|
axis_r, accel_t, cruise_t, cruise_v = force_move.calc_move_time(
|
|
dist, speed, accel)
|
|
self.trapq_append(self.trapq, movetime,
|
|
accel_t, cruise_t, accel_t,
|
|
cp, 0., 0., axis_r, 0., 0.,
|
|
0., cruise_v, accel)
|
|
return movetime + accel_t + cruise_t + accel_t
|
|
def do_move(self, movepos, speed, accel, sync=True):
|
|
self.sync_print_time()
|
|
self.next_cmd_time = self._submit_move(self.next_cmd_time, movepos,
|
|
speed, accel)
|
|
self.rail.generate_steps(self.next_cmd_time)
|
|
self.trapq_finalize_moves(self.trapq, self.next_cmd_time + 99999.9,
|
|
self.next_cmd_time + 99999.9)
|
|
toolhead = self.printer.lookup_object('toolhead')
|
|
toolhead.note_mcu_movequeue_activity(self.next_cmd_time)
|
|
if sync:
|
|
self.sync_print_time()
|
|
def do_homing_move(self, movepos, speed, accel, triggered, check_trigger):
|
|
if not self.can_home:
|
|
raise self.printer.command_error(
|
|
"No endstop for this manual stepper")
|
|
self.homing_accel = accel
|
|
pos = [movepos, 0., 0., 0.]
|
|
endstops = self.rail.get_endstops()
|
|
phoming = self.printer.lookup_object('homing')
|
|
phoming.manual_home(self, endstops, pos, speed,
|
|
triggered, check_trigger)
|
|
cmd_MANUAL_STEPPER_help = "Command a manually configured stepper"
|
|
def cmd_MANUAL_STEPPER(self, gcmd):
|
|
enable = gcmd.get_int('ENABLE', None)
|
|
if enable is not None:
|
|
self.do_enable(enable)
|
|
setpos = gcmd.get_float('SET_POSITION', None)
|
|
if setpos is not None:
|
|
self.do_set_position(setpos)
|
|
speed = gcmd.get_float('SPEED', self.velocity, above=0.)
|
|
accel = gcmd.get_float('ACCEL', self.accel, minval=0.)
|
|
homing_move = gcmd.get_int('STOP_ON_ENDSTOP', 0)
|
|
if homing_move:
|
|
movepos = gcmd.get_float('MOVE')
|
|
self.do_homing_move(movepos, speed, accel,
|
|
homing_move > 0, abs(homing_move) == 1)
|
|
elif gcmd.get_float('MOVE', None) is not None:
|
|
movepos = gcmd.get_float('MOVE')
|
|
sync = gcmd.get_int('SYNC', 1)
|
|
self.do_move(movepos, speed, accel, sync)
|
|
elif gcmd.get_int('SYNC', 0):
|
|
self.sync_print_time()
|
|
# Toolhead wrappers to support homing
|
|
def flush_step_generation(self):
|
|
self.sync_print_time()
|
|
def get_position(self):
|
|
return [self.rail.get_commanded_position(), 0., 0., 0.]
|
|
def set_position(self, newpos, homing_axes=""):
|
|
self.do_set_position(newpos[0])
|
|
def get_last_move_time(self):
|
|
self.sync_print_time()
|
|
return self.next_cmd_time
|
|
def dwell(self, delay):
|
|
self.next_cmd_time += max(0., delay)
|
|
def drip_move(self, newpos, speed, drip_completion):
|
|
# Submit move to trapq
|
|
self.sync_print_time()
|
|
maxtime = self._submit_move(self.next_cmd_time, newpos[0],
|
|
speed, self.homing_accel)
|
|
# Drip updates to motors
|
|
toolhead = self.printer.lookup_object('toolhead')
|
|
toolhead.drip_update_time(maxtime, drip_completion, self.steppers)
|
|
# Clear trapq of any remaining parts of movement
|
|
reactor = self.printer.get_reactor()
|
|
self.trapq_finalize_moves(self.trapq, reactor.NEVER, 0)
|
|
self.rail.set_position([newpos[0], 0., 0.])
|
|
self.sync_print_time()
|
|
def get_kinematics(self):
|
|
return self
|
|
def get_steppers(self):
|
|
return self.steppers
|
|
def calc_position(self, stepper_positions):
|
|
return [stepper_positions[self.rail.get_name()], 0., 0.]
|
|
|
|
def load_config_prefix(config):
|
|
return ManualStepper(config)
|