mirror of
https://github.com/Klipper4FlashForge/firmware.git
synced 2026-10-18 19:39:45 -05:00
Add Fluidd and custom boot scripts, migrate DC24V control, update the web UIs and filament macros, improve startup status reporting, and expand upgrade and replica coverage.
183 lines
5.9 KiB
Python
183 lines
5.9 KiB
Python
"""Regression tests for the multi-extruder exclude-object transform.
|
|
|
|
Upstream stores the skipped-move offset by extruder, but stores the maxima
|
|
used for retraction compensation globally. The old code therefore turns the
|
|
final XY-only move below into +99 mm of physical E after T2 -> T0.
|
|
"""
|
|
import importlib.util
|
|
import os
|
|
|
|
import pytest
|
|
|
|
from lib.paths import ROOT
|
|
|
|
pytestmark = pytest.mark.static
|
|
|
|
MODULE = os.path.join(
|
|
ROOT, "pkgs", "klipper", "payload", "klipper", "klippy", "extras",
|
|
"exclude_object.py")
|
|
|
|
|
|
def load_module():
|
|
spec = importlib.util.spec_from_file_location(
|
|
"anvil_exclude_object", MODULE)
|
|
module = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(module)
|
|
return module
|
|
|
|
|
|
class Extruder:
|
|
def __init__(self, name):
|
|
self.name = name
|
|
|
|
def get_name(self):
|
|
return self.name
|
|
|
|
|
|
class Toolhead:
|
|
def __init__(self, name):
|
|
self.extruder = Extruder(name)
|
|
|
|
def get_extruder(self):
|
|
return self.extruder
|
|
|
|
def select(self, name):
|
|
self.extruder = Extruder(name)
|
|
|
|
|
|
class Downstream:
|
|
def __init__(self, position):
|
|
self.position = list(position)
|
|
self.moves = []
|
|
|
|
def get_position(self):
|
|
return list(self.position)
|
|
|
|
def move(self, position, speed):
|
|
self.position = list(position)
|
|
self.moves.append((list(position), speed))
|
|
|
|
|
|
def build_transform(tool="extruder2", position=(0., 0., 0., 100.)):
|
|
module = load_module()
|
|
eo = module.ExcludeObject.__new__(module.ExcludeObject)
|
|
eo.toolhead = Toolhead(tool)
|
|
eo.next_transform = Downstream(position)
|
|
eo.xyz_offset = [0., 0., 0.]
|
|
eo.physical_xyz = None
|
|
eo.last_excluded_xyz = [0., 0., 0.]
|
|
eo.e_states = {}
|
|
eo.active_extruder = None
|
|
eo.initial_extrusion_moves = 0
|
|
eo.last_position = [0., 0., 0., 0.]
|
|
eo.current_object = None
|
|
eo.excluded_objects = ["BAD"]
|
|
eo.get_position()
|
|
return eo
|
|
|
|
|
|
def select(eo, name, physical_e):
|
|
eo.toolhead.select(name)
|
|
eo.next_transform.position[3] = physical_e
|
|
eo.get_position()
|
|
|
|
|
|
def test_tool_change_does_not_put_old_tools_e_history_on_xy_travel():
|
|
eo = build_transform()
|
|
|
|
# T2's E=100 must never enter T0's retraction calculation.
|
|
select(eo, "extruder", 0.)
|
|
eo.move([0., 0., 0., 1.], 20.)
|
|
|
|
eo.current_object = "BAD"
|
|
eo.move([10., 10., 0., 2.], 20.) # suppressed
|
|
assert eo.next_transform.position == [0., 0., 0., 1.]
|
|
|
|
eo.current_object = None
|
|
eo.move([20., 20., 0., 2.], 500.) # XY-only exit travel
|
|
|
|
assert eo.next_transform.position == [20., 20., 0., 1.]
|
|
assert eo.e_states["extruder2"]["max_normal"] == 100.
|
|
# The virtual E=2 travel becomes the new normal high-water mark for T0,
|
|
# but its downstream E correctly remains 1. It must not affect T2.
|
|
assert eo.e_states["extruder"]["max_normal"] == 2.
|
|
|
|
|
|
def test_each_tool_keeps_its_own_pending_retraction_adjustment():
|
|
eo = build_transform(tool="extruder", position=(0., 0., 0., 10.))
|
|
eo.move([0., 0., 0., 8.], 20.) # T0 retracted by 2
|
|
eo.current_object = "BAD"
|
|
eo.move([5., 5., 0., 7.], 20.) # skipped retract
|
|
eo.move([6., 6., 0., 9.], 20.) # skipped partial unretract
|
|
|
|
# T1 may consume the shared spatial offset, but not T0's E correction.
|
|
eo.current_object = None
|
|
select(eo, "extruder1", 0.)
|
|
eo.move([20., 20., 0., 0.], 100.)
|
|
assert eo.next_transform.position[3] == 0.
|
|
|
|
# Returning to T0 does not put its pending correction on an XY-only move.
|
|
# It is retained until T0's own next E change.
|
|
select(eo, "extruder", 8.)
|
|
eo.move([21., 20., 0., 9.], 100.)
|
|
assert eo.next_transform.position[3] == 8.
|
|
assert eo.e_states["extruder"]["pending_adjustment"] == -1.
|
|
assert eo.e_states["extruder1"]["pending_adjustment"] == 0.
|
|
|
|
eo.move([21., 20., 0., 10.], 20.)
|
|
assert eo.next_transform.position[3] == 10.
|
|
assert eo.e_states["extruder"]["pending_adjustment"] == 0.
|
|
|
|
|
|
def test_first_tool_state_uses_its_real_negative_e_origin():
|
|
eo = build_transform(tool="extruder", position=(0., 0., 0., -20.))
|
|
state = eo.e_states["extruder"]
|
|
assert state["last_normal"] == -20.
|
|
assert state["max_normal"] == -20.
|
|
assert state["last_excluded"] == -20.
|
|
assert state["max_excluded"] == -20.
|
|
|
|
|
|
def test_single_extruder_retraction_compensation_is_preserved():
|
|
eo = build_transform(tool="extruder", position=(0., 0., 0., 10.))
|
|
eo.move([0., 0., 0., 8.], 20.) # normal retract: 2 mm debt
|
|
eo.current_object = "BAD"
|
|
eo.move([5., 5., 0., 7.], 20.) # suppressed retract
|
|
eo.move([6., 6., 0., 9.], 20.) # suppressed partial unretract
|
|
|
|
eo.current_object = None
|
|
eo.move([20., 20., 0., 9.], 100.) # leaving object, XY only
|
|
|
|
# Excluded debt is 1 mm and normal debt was 2 mm, so the -1 adjustment
|
|
# combines with the skipped net +1 and virtual E9 maps to physical E9.
|
|
assert eo.next_transform.position == [20., 20., 0., 9.]
|
|
assert eo.e_states["extruder"]["pending_adjustment"] == 0.
|
|
|
|
|
|
def test_initial_five_extrusions_remain_one_global_grace_period():
|
|
eo = build_transform(tool="extruder", position=(0., 0., 0., 0.))
|
|
eo.initial_extrusion_moves = 5
|
|
eo.current_object = "BAD"
|
|
|
|
for e in range(1, 6):
|
|
eo.move([float(e), 0., 0., float(e)], 20.)
|
|
assert len(eo.next_transform.moves) == 5
|
|
|
|
select(eo, "extruder1", 0.)
|
|
eo.move([6., 0., 0., 1.], 20.)
|
|
assert len(eo.next_transform.moves) == 5
|
|
|
|
|
|
def test_large_adjustment_logs_complete_diagnostic(caplog):
|
|
eo = build_transform(tool="extruder", position=(0., 0., 0., 0.))
|
|
state = eo.e_states["extruder"]
|
|
state.update(max_normal=20., last_normal=0.,
|
|
max_excluded=1., last_excluded=1., was_ignored=True)
|
|
|
|
eo.move([1., 1., 0., 0.], 100.)
|
|
|
|
messages = "\n".join(record.getMessage() for record in caplog.records)
|
|
assert "large E correction -20.000000 mm for extruder" in messages
|
|
assert "normal=0.000000/20.000000" in messages
|
|
assert "xyz_offset=" in messages
|