mirror of
https://github.com/andreili/klipper.git
synced 2025-08-23 19:34:06 +02:00
motion_queuing: Add new module to help with motion queues and flushing
Create a new module to assist with host management of motion queues. Register all MCU_stepper objects with this module and use the module for step generation. All steppers will now automatically generate steps whenever toolhead._advance_flush_time() is invoked. It is no longer necessary for callers to individually call stepper.generate_steps(). Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
parent
126275d1f4
commit
9399e738bc
@ -85,14 +85,13 @@ class ForceMove:
|
|||||||
self.trapq_append(self.trapq, print_time, accel_t, cruise_t, accel_t,
|
self.trapq_append(self.trapq, print_time, accel_t, cruise_t, accel_t,
|
||||||
0., 0., 0., axis_r, 0., 0., 0., cruise_v, accel)
|
0., 0., 0., axis_r, 0., 0., 0., cruise_v, accel)
|
||||||
print_time = print_time + accel_t + cruise_t + accel_t
|
print_time = print_time + accel_t + cruise_t + accel_t
|
||||||
stepper.generate_steps(print_time)
|
|
||||||
self.trapq_finalize_moves(self.trapq, print_time + 99999.9,
|
|
||||||
print_time + 99999.9)
|
|
||||||
stepper.set_trapq(prev_trapq)
|
|
||||||
stepper.set_stepper_kinematics(prev_sk)
|
|
||||||
toolhead.note_mcu_movequeue_activity(print_time)
|
toolhead.note_mcu_movequeue_activity(print_time)
|
||||||
toolhead.dwell(accel_t + cruise_t + accel_t)
|
toolhead.dwell(accel_t + cruise_t + accel_t)
|
||||||
toolhead.flush_step_generation()
|
toolhead.flush_step_generation()
|
||||||
|
stepper.set_trapq(prev_trapq)
|
||||||
|
stepper.set_stepper_kinematics(prev_sk)
|
||||||
|
self.trapq_finalize_moves(self.trapq, print_time + 99999.9,
|
||||||
|
print_time + 99999.9)
|
||||||
def _lookup_stepper(self, gcmd):
|
def _lookup_stepper(self, gcmd):
|
||||||
name = gcmd.get('STEPPER')
|
name = gcmd.get('STEPPER')
|
||||||
if name not in self.steppers:
|
if name not in self.steppers:
|
||||||
|
@ -76,7 +76,6 @@ class ManualStepper:
|
|||||||
self.sync_print_time()
|
self.sync_print_time()
|
||||||
self.next_cmd_time = self._submit_move(self.next_cmd_time, movepos,
|
self.next_cmd_time = self._submit_move(self.next_cmd_time, movepos,
|
||||||
speed, accel)
|
speed, accel)
|
||||||
self.rail.generate_steps(self.next_cmd_time)
|
|
||||||
self.trapq_finalize_moves(self.trapq, self.next_cmd_time + 99999.9,
|
self.trapq_finalize_moves(self.trapq, self.next_cmd_time + 99999.9,
|
||||||
self.next_cmd_time + 99999.9)
|
self.next_cmd_time + 99999.9)
|
||||||
toolhead = self.printer.lookup_object('toolhead')
|
toolhead = self.printer.lookup_object('toolhead')
|
||||||
@ -138,7 +137,6 @@ class ManualStepper:
|
|||||||
raise gcmd.error("Must unregister axis first")
|
raise gcmd.error("Must unregister axis first")
|
||||||
# Unregister
|
# Unregister
|
||||||
toolhead.remove_extra_axis(self)
|
toolhead.remove_extra_axis(self)
|
||||||
toolhead.unregister_step_generator(self.rail.generate_steps)
|
|
||||||
self.axis_gcode_id = None
|
self.axis_gcode_id = None
|
||||||
return
|
return
|
||||||
if (len(gcode_axis) != 1 or not gcode_axis.isupper()
|
if (len(gcode_axis) != 1 or not gcode_axis.isupper()
|
||||||
@ -155,7 +153,6 @@ class ManualStepper:
|
|||||||
self.gaxis_limit_velocity = limit_velocity
|
self.gaxis_limit_velocity = limit_velocity
|
||||||
self.gaxis_limit_accel = limit_accel
|
self.gaxis_limit_accel = limit_accel
|
||||||
toolhead.add_extra_axis(self, self.get_position()[0])
|
toolhead.add_extra_axis(self, self.get_position()[0])
|
||||||
toolhead.register_step_generator(self.rail.generate_steps)
|
|
||||||
def process_move(self, print_time, move, ea_index):
|
def process_move(self, print_time, move, ea_index):
|
||||||
axis_r = move.axes_r[ea_index]
|
axis_r = move.axes_r[ea_index]
|
||||||
start_pos = move.start_pos[ea_index]
|
start_pos = move.start_pos[ea_index]
|
||||||
@ -208,7 +205,7 @@ class ManualStepper:
|
|||||||
speed, self.homing_accel)
|
speed, self.homing_accel)
|
||||||
# Drip updates to motors
|
# Drip updates to motors
|
||||||
toolhead = self.printer.lookup_object('toolhead')
|
toolhead = self.printer.lookup_object('toolhead')
|
||||||
toolhead.drip_update_time(maxtime, drip_completion, self.steppers)
|
toolhead.drip_update_time(maxtime, drip_completion)
|
||||||
# Clear trapq of any remaining parts of movement
|
# Clear trapq of any remaining parts of movement
|
||||||
reactor = self.printer.get_reactor()
|
reactor = self.printer.get_reactor()
|
||||||
self.trapq_finalize_moves(self.trapq, reactor.NEVER, 0)
|
self.trapq_finalize_moves(self.trapq, reactor.NEVER, 0)
|
||||||
|
19
klippy/extras/motion_queuing.py
Normal file
19
klippy/extras/motion_queuing.py
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# Helper code for low-level motion queuing and flushing
|
||||||
|
#
|
||||||
|
# Copyright (C) 2025 Kevin O'Connor <kevin@koconnor.net>
|
||||||
|
#
|
||||||
|
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||||
|
import logging
|
||||||
|
|
||||||
|
class PrinterMotionQueuing:
|
||||||
|
def __init__(self, config):
|
||||||
|
self.printer = config.get_printer()
|
||||||
|
self.steppers = []
|
||||||
|
def register_stepper(self, config, stepper):
|
||||||
|
self.steppers.append(stepper)
|
||||||
|
def flush_motion_queues(self, must_flush_time, max_step_gen_time):
|
||||||
|
for stepper in self.steppers:
|
||||||
|
stepper.generate_steps(max_step_gen_time)
|
||||||
|
|
||||||
|
def load_config(config):
|
||||||
|
return PrinterMotionQueuing(config)
|
@ -36,7 +36,6 @@ class CartKinematics:
|
|||||||
'safe_distance', None, minval=0.))
|
'safe_distance', None, minval=0.))
|
||||||
for s in self.get_steppers():
|
for s in self.get_steppers():
|
||||||
s.set_trapq(toolhead.get_trapq())
|
s.set_trapq(toolhead.get_trapq())
|
||||||
toolhead.register_step_generator(s.generate_steps)
|
|
||||||
# Setup boundary checks
|
# Setup boundary checks
|
||||||
max_velocity, max_accel = toolhead.get_max_velocity()
|
max_velocity, max_accel = toolhead.get_max_velocity()
|
||||||
self.max_z_velocity = config.getfloat('max_z_velocity', max_velocity,
|
self.max_z_velocity = config.getfloat('max_z_velocity', max_velocity,
|
||||||
|
@ -20,7 +20,6 @@ class CoreXYKinematics:
|
|||||||
self.rails[2].setup_itersolve('cartesian_stepper_alloc', b'z')
|
self.rails[2].setup_itersolve('cartesian_stepper_alloc', b'z')
|
||||||
for s in self.get_steppers():
|
for s in self.get_steppers():
|
||||||
s.set_trapq(toolhead.get_trapq())
|
s.set_trapq(toolhead.get_trapq())
|
||||||
toolhead.register_step_generator(s.generate_steps)
|
|
||||||
# Setup boundary checks
|
# Setup boundary checks
|
||||||
max_velocity, max_accel = toolhead.get_max_velocity()
|
max_velocity, max_accel = toolhead.get_max_velocity()
|
||||||
self.max_z_velocity = config.getfloat(
|
self.max_z_velocity = config.getfloat(
|
||||||
|
@ -20,7 +20,6 @@ class CoreXZKinematics:
|
|||||||
self.rails[2].setup_itersolve('corexz_stepper_alloc', b'-')
|
self.rails[2].setup_itersolve('corexz_stepper_alloc', b'-')
|
||||||
for s in self.get_steppers():
|
for s in self.get_steppers():
|
||||||
s.set_trapq(toolhead.get_trapq())
|
s.set_trapq(toolhead.get_trapq())
|
||||||
toolhead.register_step_generator(s.generate_steps)
|
|
||||||
# Setup boundary checks
|
# Setup boundary checks
|
||||||
max_velocity, max_accel = toolhead.get_max_velocity()
|
max_velocity, max_accel = toolhead.get_max_velocity()
|
||||||
self.max_z_velocity = config.getfloat(
|
self.max_z_velocity = config.getfloat(
|
||||||
|
@ -52,7 +52,6 @@ class DeltaKinematics:
|
|||||||
r.setup_itersolve('delta_stepper_alloc', a, t[0], t[1])
|
r.setup_itersolve('delta_stepper_alloc', a, t[0], t[1])
|
||||||
for s in self.get_steppers():
|
for s in self.get_steppers():
|
||||||
s.set_trapq(toolhead.get_trapq())
|
s.set_trapq(toolhead.get_trapq())
|
||||||
toolhead.register_step_generator(s.generate_steps)
|
|
||||||
# Setup boundary checks
|
# Setup boundary checks
|
||||||
self.need_home = True
|
self.need_home = True
|
||||||
self.limit_xy2 = -1.
|
self.limit_xy2 = -1.
|
||||||
|
@ -40,7 +40,6 @@ class DeltesianKinematics:
|
|||||||
self.rails[2].setup_itersolve('cartesian_stepper_alloc', b'y')
|
self.rails[2].setup_itersolve('cartesian_stepper_alloc', b'y')
|
||||||
for s in self.get_steppers():
|
for s in self.get_steppers():
|
||||||
s.set_trapq(toolhead.get_trapq())
|
s.set_trapq(toolhead.get_trapq())
|
||||||
toolhead.register_step_generator(s.generate_steps)
|
|
||||||
self.limits = [(1.0, -1.0)] * 3
|
self.limits = [(1.0, -1.0)] * 3
|
||||||
# X axis limits
|
# X axis limits
|
||||||
min_angle = config.getfloat('min_angle', MIN_ANGLE,
|
min_angle = config.getfloat('min_angle', MIN_ANGLE,
|
||||||
|
@ -39,8 +39,6 @@ class ExtruderStepper:
|
|||||||
self.name, self.cmd_SYNC_EXTRUDER_MOTION,
|
self.name, self.cmd_SYNC_EXTRUDER_MOTION,
|
||||||
desc=self.cmd_SYNC_EXTRUDER_MOTION_help)
|
desc=self.cmd_SYNC_EXTRUDER_MOTION_help)
|
||||||
def _handle_connect(self):
|
def _handle_connect(self):
|
||||||
toolhead = self.printer.lookup_object('toolhead')
|
|
||||||
toolhead.register_step_generator(self.stepper.generate_steps)
|
|
||||||
self._set_pressure_advance(self.config_pa, self.config_smooth_time)
|
self._set_pressure_advance(self.config_pa, self.config_smooth_time)
|
||||||
def get_status(self, eventtime):
|
def get_status(self, eventtime):
|
||||||
return {'pressure_advance': self.pressure_advance,
|
return {'pressure_advance': self.pressure_advance,
|
||||||
|
@ -108,7 +108,6 @@ class GenericCartesianKinematics:
|
|||||||
self._load_kinematics(config)
|
self._load_kinematics(config)
|
||||||
for s in self.get_steppers():
|
for s in self.get_steppers():
|
||||||
s.set_trapq(toolhead.get_trapq())
|
s.set_trapq(toolhead.get_trapq())
|
||||||
toolhead.register_step_generator(s.generate_steps)
|
|
||||||
self.dc_module = None
|
self.dc_module = None
|
||||||
if self.dc_carriages:
|
if self.dc_carriages:
|
||||||
pcs = [dc.get_dual_carriage() for dc in self.dc_carriages]
|
pcs = [dc.get_dual_carriage() for dc in self.dc_carriages]
|
||||||
|
@ -39,7 +39,6 @@ class HybridCoreXYKinematics:
|
|||||||
'safe_distance', None, minval=0.))
|
'safe_distance', None, minval=0.))
|
||||||
for s in self.get_steppers():
|
for s in self.get_steppers():
|
||||||
s.set_trapq(toolhead.get_trapq())
|
s.set_trapq(toolhead.get_trapq())
|
||||||
toolhead.register_step_generator(s.generate_steps)
|
|
||||||
# Setup boundary checks
|
# Setup boundary checks
|
||||||
max_velocity, max_accel = toolhead.get_max_velocity()
|
max_velocity, max_accel = toolhead.get_max_velocity()
|
||||||
self.max_z_velocity = config.getfloat(
|
self.max_z_velocity = config.getfloat(
|
||||||
|
@ -39,7 +39,6 @@ class HybridCoreXZKinematics:
|
|||||||
'safe_distance', None, minval=0.))
|
'safe_distance', None, minval=0.))
|
||||||
for s in self.get_steppers():
|
for s in self.get_steppers():
|
||||||
s.set_trapq(toolhead.get_trapq())
|
s.set_trapq(toolhead.get_trapq())
|
||||||
toolhead.register_step_generator(s.generate_steps)
|
|
||||||
# Setup boundary checks
|
# Setup boundary checks
|
||||||
max_velocity, max_accel = toolhead.get_max_velocity()
|
max_velocity, max_accel = toolhead.get_max_velocity()
|
||||||
self.max_z_velocity = config.getfloat(
|
self.max_z_velocity = config.getfloat(
|
||||||
|
@ -21,7 +21,6 @@ class PolarKinematics:
|
|||||||
for s in r.get_steppers() ]
|
for s in r.get_steppers() ]
|
||||||
for s in self.get_steppers():
|
for s in self.get_steppers():
|
||||||
s.set_trapq(toolhead.get_trapq())
|
s.set_trapq(toolhead.get_trapq())
|
||||||
toolhead.register_step_generator(s.generate_steps)
|
|
||||||
# Setup boundary checks
|
# Setup boundary checks
|
||||||
max_velocity, max_accel = toolhead.get_max_velocity()
|
max_velocity, max_accel = toolhead.get_max_velocity()
|
||||||
self.max_z_velocity = config.getfloat(
|
self.max_z_velocity = config.getfloat(
|
||||||
|
@ -52,7 +52,6 @@ class RotaryDeltaKinematics:
|
|||||||
math.radians(a), ua, la)
|
math.radians(a), ua, la)
|
||||||
for s in self.get_steppers():
|
for s in self.get_steppers():
|
||||||
s.set_trapq(toolhead.get_trapq())
|
s.set_trapq(toolhead.get_trapq())
|
||||||
toolhead.register_step_generator(s.generate_steps)
|
|
||||||
# Setup boundary checks
|
# Setup boundary checks
|
||||||
self.need_home = True
|
self.need_home = True
|
||||||
self.limit_xy2 = -1.
|
self.limit_xy2 = -1.
|
||||||
|
@ -21,7 +21,6 @@ class WinchKinematics:
|
|||||||
self.anchors.append(a)
|
self.anchors.append(a)
|
||||||
s.setup_itersolve('winch_stepper_alloc', *a)
|
s.setup_itersolve('winch_stepper_alloc', *a)
|
||||||
s.set_trapq(toolhead.get_trapq())
|
s.set_trapq(toolhead.get_trapq())
|
||||||
toolhead.register_step_generator(s.generate_steps)
|
|
||||||
# Setup boundary checks
|
# Setup boundary checks
|
||||||
acoords = list(zip(*self.anchors))
|
acoords = list(zip(*self.anchors))
|
||||||
self.axes_min = toolhead.Coord(*[min(a) for a in acoords], e=0.)
|
self.axes_min = toolhead.Coord(*[min(a) for a in acoords], e=0.)
|
||||||
|
@ -273,7 +273,8 @@ def PrinterStepper(config, units_in_radians=False):
|
|||||||
rotation_dist, steps_per_rotation,
|
rotation_dist, steps_per_rotation,
|
||||||
step_pulse_duration, units_in_radians)
|
step_pulse_duration, units_in_radians)
|
||||||
# Register with helper modules
|
# Register with helper modules
|
||||||
for mname in ['stepper_enable', 'force_move', 'motion_report']:
|
mods = ['stepper_enable', 'force_move', 'motion_report', 'motion_queuing']
|
||||||
|
for mname in mods:
|
||||||
m = printer.load_object(config, mname)
|
m = printer.load_object(config, mname)
|
||||||
m.register_stepper(config, mcu_stepper)
|
m.register_stepper(config, mcu_stepper)
|
||||||
return mcu_stepper
|
return mcu_stepper
|
||||||
@ -442,9 +443,6 @@ class GenericPrinterRail:
|
|||||||
def setup_itersolve(self, alloc_func, *params):
|
def setup_itersolve(self, alloc_func, *params):
|
||||||
for stepper in self.steppers:
|
for stepper in self.steppers:
|
||||||
stepper.setup_itersolve(alloc_func, *params)
|
stepper.setup_itersolve(alloc_func, *params)
|
||||||
def generate_steps(self, flush_time):
|
|
||||||
for stepper in self.steppers:
|
|
||||||
stepper.generate_steps(flush_time)
|
|
||||||
def set_trapq(self, trapq):
|
def set_trapq(self, trapq):
|
||||||
for stepper in self.steppers:
|
for stepper in self.steppers:
|
||||||
stepper.set_trapq(trapq)
|
stepper.set_trapq(trapq)
|
||||||
|
@ -251,7 +251,7 @@ class ToolHead:
|
|||||||
self.trapq_append = ffi_lib.trapq_append
|
self.trapq_append = ffi_lib.trapq_append
|
||||||
self.trapq_finalize_moves = ffi_lib.trapq_finalize_moves
|
self.trapq_finalize_moves = ffi_lib.trapq_finalize_moves
|
||||||
# Motion flushing
|
# Motion flushing
|
||||||
self.step_generators = []
|
self.motion_queuing = self.printer.load_object(config, 'motion_queuing')
|
||||||
self.flush_trapqs = [self.trapq]
|
self.flush_trapqs = [self.trapq]
|
||||||
# Create kinematics class
|
# Create kinematics class
|
||||||
gcode = self.printer.lookup_object('gcode')
|
gcode = self.printer.lookup_object('gcode')
|
||||||
@ -280,8 +280,7 @@ class ToolHead:
|
|||||||
sg_flush_want = min(flush_time + STEPCOMPRESS_FLUSH_TIME,
|
sg_flush_want = min(flush_time + STEPCOMPRESS_FLUSH_TIME,
|
||||||
self.print_time - self.kin_flush_delay)
|
self.print_time - self.kin_flush_delay)
|
||||||
sg_flush_time = max(sg_flush_want, flush_time)
|
sg_flush_time = max(sg_flush_want, flush_time)
|
||||||
for sg in self.step_generators:
|
self.motion_queuing.flush_motion_queues(flush_time, sg_flush_time)
|
||||||
sg(sg_flush_time)
|
|
||||||
self.min_restart_time = max(self.min_restart_time, sg_flush_time)
|
self.min_restart_time = max(self.min_restart_time, sg_flush_time)
|
||||||
# Free trapq entries that are no longer needed
|
# Free trapq entries that are no longer needed
|
||||||
clear_history_time = self.clear_history_time
|
clear_history_time = self.clear_history_time
|
||||||
@ -517,7 +516,7 @@ class ToolHead:
|
|||||||
def get_extra_axes(self):
|
def get_extra_axes(self):
|
||||||
return [None, None, None] + self.extra_axes
|
return [None, None, None] + self.extra_axes
|
||||||
# Homing "drip move" handling
|
# Homing "drip move" handling
|
||||||
def drip_update_time(self, next_print_time, drip_completion, addstepper=()):
|
def drip_update_time(self, next_print_time, drip_completion):
|
||||||
# Transition from "NeedPrime"/"Priming"/main state to "Drip" state
|
# Transition from "NeedPrime"/"Priming"/main state to "Drip" state
|
||||||
self.special_queuing_state = "Drip"
|
self.special_queuing_state = "Drip"
|
||||||
self.need_check_pause = self.reactor.NEVER
|
self.need_check_pause = self.reactor.NEVER
|
||||||
@ -539,8 +538,6 @@ class ToolHead:
|
|||||||
continue
|
continue
|
||||||
npt = min(self.print_time + DRIP_SEGMENT_TIME, next_print_time)
|
npt = min(self.print_time + DRIP_SEGMENT_TIME, next_print_time)
|
||||||
self.note_mcu_movequeue_activity(npt + self.kin_flush_delay)
|
self.note_mcu_movequeue_activity(npt + self.kin_flush_delay)
|
||||||
for stepper in addstepper:
|
|
||||||
stepper.generate_steps(npt)
|
|
||||||
self._advance_move_time(npt)
|
self._advance_move_time(npt)
|
||||||
# Exit "Drip" state
|
# Exit "Drip" state
|
||||||
self.reactor.update_timer(self.flush_timer, self.reactor.NOW)
|
self.reactor.update_timer(self.flush_timer, self.reactor.NOW)
|
||||||
@ -617,11 +614,6 @@ class ToolHead:
|
|||||||
return self.kin
|
return self.kin
|
||||||
def get_trapq(self):
|
def get_trapq(self):
|
||||||
return self.trapq
|
return self.trapq
|
||||||
def register_step_generator(self, handler):
|
|
||||||
self.step_generators.append(handler)
|
|
||||||
def unregister_step_generator(self, handler):
|
|
||||||
if handler in self.step_generators:
|
|
||||||
self.step_generators.remove(handler)
|
|
||||||
def note_step_generation_scan_time(self, delay, old_delay=0.):
|
def note_step_generation_scan_time(self, delay, old_delay=0.):
|
||||||
self.flush_step_generation()
|
self.flush_step_generation()
|
||||||
if old_delay:
|
if old_delay:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user