mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-10-18 21:47:39 +02:00
fix: H2D tray_now disambiguation produces bogus IDs for AMS-HT (#364)
When the snow field hadn't arrived yet, the ams_extruder_map fallback computed ams_id * 4 + slot for AMS-HT units (128-135), producing invalid tray IDs like 512+. AMS-HT has a single slot; global ID = unit ID. - Single-AMS fallback: return ams_id directly for AMS-HT (128-135) - Multi-AMS matching: decompose AMS-HT trays correctly (slot=0) - Multi-AMS narrowing: slot > 0 eliminates AMS-HT candidates - last_loaded_tray: tighten to valid IDs only (0-15, 128-135, 254) - Add 10 tests for AMS-HT fallback and last_loaded_tray validation
This commit is contained in:
@@ -990,29 +990,56 @@ class BambuMQTTClient:
|
||||
if len(ams_on_extruder) == 1:
|
||||
# Single AMS on this extruder - unambiguous
|
||||
active_ams_id = ams_on_extruder[0]
|
||||
global_tray_id = active_ams_id * 4 + parsed_tray_now
|
||||
if 128 <= active_ams_id <= 135:
|
||||
# AMS-HT: single slot per unit, global ID = unit ID
|
||||
global_tray_id = active_ams_id
|
||||
else:
|
||||
global_tray_id = active_ams_id * 4 + parsed_tray_now
|
||||
logger.debug(
|
||||
f"[{self.serial_number}] H2D tray_now fallback: "
|
||||
f"slot {parsed_tray_now} + single AMS {active_ams_id} -> global ID {global_tray_id}"
|
||||
)
|
||||
self.state.tray_now = global_tray_id
|
||||
elif len(ams_on_extruder) > 1:
|
||||
# Multiple AMS on this extruder - keep current if valid, else use slot as-is
|
||||
# Multiple AMS on this extruder - keep current if valid, else try to narrow down
|
||||
current_tray = self.state.tray_now
|
||||
current_ams = current_tray // 4 if current_tray < 128 else -1
|
||||
if current_ams in ams_on_extruder and (current_tray % 4) == parsed_tray_now:
|
||||
# Determine which AMS unit and slot the current tray belongs to
|
||||
if 0 <= current_tray <= 15:
|
||||
current_ams = current_tray // 4
|
||||
current_slot = current_tray % 4
|
||||
elif 128 <= current_tray <= 135:
|
||||
current_ams = current_tray # AMS-HT: ID = tray ID
|
||||
current_slot = 0
|
||||
else:
|
||||
current_ams = -1
|
||||
current_slot = -1
|
||||
if current_ams in ams_on_extruder and current_slot == parsed_tray_now:
|
||||
# Current is valid and matches slot - keep it
|
||||
logger.debug(
|
||||
f"[{self.serial_number}] H2D tray_now: multiple AMS {ams_on_extruder}, "
|
||||
f"keeping current {current_tray} (matches slot {parsed_tray_now})"
|
||||
)
|
||||
else:
|
||||
# Can't disambiguate - use slot as-is (will be wrong for non-first AMS)
|
||||
logger.warning(
|
||||
f"[{self.serial_number}] H2D tray_now: multiple AMS {ams_on_extruder} on extruder {active_ext}, "
|
||||
f"no snow field, using slot {parsed_tray_now} (may be incorrect)"
|
||||
)
|
||||
self.state.tray_now = parsed_tray_now
|
||||
# Filter candidates: AMS-HT (128-135) only valid for slot 0
|
||||
if parsed_tray_now > 0:
|
||||
candidates = [a for a in ams_on_extruder if a <= 3]
|
||||
else:
|
||||
candidates = ams_on_extruder
|
||||
if len(candidates) == 1:
|
||||
cand = candidates[0]
|
||||
resolved = cand if 128 <= cand <= 135 else cand * 4 + parsed_tray_now
|
||||
logger.debug(
|
||||
f"[{self.serial_number}] H2D tray_now: multiple AMS {ams_on_extruder}, "
|
||||
f"narrowed to AMS {cand} -> global ID {resolved}"
|
||||
)
|
||||
self.state.tray_now = resolved
|
||||
else:
|
||||
# Genuinely ambiguous - use slot as-is (will be wrong for non-first AMS)
|
||||
logger.warning(
|
||||
f"[{self.serial_number}] H2D tray_now: multiple AMS {ams_on_extruder} on extruder {active_ext}, "
|
||||
f"no snow field, using slot {parsed_tray_now} (may be incorrect)"
|
||||
)
|
||||
self.state.tray_now = parsed_tray_now
|
||||
else:
|
||||
# No AMS on this extruder - use slot as-is
|
||||
logger.warning(
|
||||
@@ -1029,7 +1056,9 @@ class BambuMQTTClient:
|
||||
self.state.tray_now = parsed_tray_now
|
||||
|
||||
# Track last valid tray for usage tracking (survives retract → 255 at print end)
|
||||
if 0 <= self.state.tray_now <= 253:
|
||||
# Valid physical trays: 0-15 (regular AMS), 128-135 (AMS-HT), 254 (external spool)
|
||||
tn = self.state.tray_now
|
||||
if (0 <= tn <= 15) or (128 <= tn <= 135) or tn == 254:
|
||||
self.state.last_loaded_tray = self.state.tray_now
|
||||
|
||||
logger.debug("[%s] tray_now updated: %s", self.serial_number, self.state.tray_now)
|
||||
|
||||
@@ -1632,6 +1632,92 @@ class TestTrayNowDualNozzleH2DFallback(_H2DFixtureMixin):
|
||||
h2d_client._process_message(_ams_payload(2))
|
||||
assert h2d_client.state.tray_now == 2
|
||||
|
||||
def test_single_ams_ht_on_extruder_returns_unit_id(self, h2d_client):
|
||||
"""AMS-HT 128 alone on left extruder, slot 0 → global ID 128 (not 512)."""
|
||||
# Switch to left extruder (where AMS-HT 128 is mapped)
|
||||
h2d_client._process_message(_extruder_state_payload(0x0100))
|
||||
# Only AMS-HT 128 on left extruder; no snow available
|
||||
h2d_client._process_message(_ams_payload(0))
|
||||
assert h2d_client.state.tray_now == 128
|
||||
|
||||
def test_single_ams_ht_ignores_nonzero_slot(self, h2d_client):
|
||||
"""AMS-HT has single slot; even if printer reports slot 1, global ID = unit ID."""
|
||||
h2d_client.state.ams_extruder_map = {"129": 0}
|
||||
h2d_client._process_message(_ams_payload(1))
|
||||
# AMS-HT 129: global ID = 129, not 129*4+1=517
|
||||
assert h2d_client.state.tray_now == 129
|
||||
|
||||
def test_multiple_ams_keeps_current_ams_ht(self, h2d_client):
|
||||
"""Current tray is AMS-HT 128, slot 0 reported → keeps 128."""
|
||||
h2d_client.state.ams_extruder_map = {"0": 0, "128": 0}
|
||||
h2d_client.state.tray_now = 128
|
||||
h2d_client._process_message(_ams_payload(0))
|
||||
assert h2d_client.state.tray_now == 128
|
||||
|
||||
def test_multiple_ams_slot_nonzero_excludes_ams_ht(self, h2d_client):
|
||||
"""Slot > 0 eliminates AMS-HT candidates; single regular AMS left → resolves."""
|
||||
# AMS 0 + AMS-HT 128 both on right extruder
|
||||
h2d_client.state.ams_extruder_map = {"0": 0, "128": 0}
|
||||
h2d_client.state.tray_now = 255 # no current match
|
||||
# Slot 2 → can't be AMS-HT → only AMS 0 → global = 0*4+2 = 2
|
||||
h2d_client._process_message(_ams_payload(2))
|
||||
assert h2d_client.state.tray_now == 2
|
||||
|
||||
def test_multiple_ams_slot_nonzero_narrows_to_single_ht_excluded(self, h2d_client):
|
||||
"""Two regular AMS + one AMS-HT, slot > 0 → AMS-HT excluded but still ambiguous."""
|
||||
h2d_client.state.ams_extruder_map = {"0": 0, "1": 0, "128": 0}
|
||||
h2d_client.state.tray_now = 255
|
||||
# Slot 3 → excludes AMS-HT, but AMS 0 and AMS 1 both remain → ambiguous
|
||||
h2d_client._process_message(_ams_payload(3))
|
||||
assert h2d_client.state.tray_now == 3 # raw slot fallback
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 6b. H2D last_loaded_tray validation
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestLastLoadedTrayValidation(_H2DFixtureMixin):
|
||||
"""last_loaded_tray only stores physically valid tray IDs."""
|
||||
|
||||
def test_regular_ams_tray_stored(self, h2d_client):
|
||||
"""Valid regular AMS tray (0-15) → stored in last_loaded_tray."""
|
||||
h2d_client.state.tray_now = 7
|
||||
# Trigger tray_now processing via AMS message
|
||||
h2d_client._process_message(
|
||||
_extruder_info_payload(
|
||||
[
|
||||
{"id": 0, "snow": 1 << 8 | 3}, # AMS 1 slot 3 → global 7
|
||||
{"id": 1, "snow": 0xFF00FF},
|
||||
]
|
||||
)
|
||||
)
|
||||
h2d_client._process_message(_ams_payload(3))
|
||||
assert h2d_client.state.tray_now == 7
|
||||
assert h2d_client.state.last_loaded_tray == 7
|
||||
|
||||
def test_ams_ht_tray_stored(self, h2d_client):
|
||||
"""Valid AMS-HT tray (128-135) → stored in last_loaded_tray."""
|
||||
h2d_client._process_message(_extruder_state_payload(0x0100))
|
||||
h2d_client._process_message(
|
||||
_extruder_info_payload(
|
||||
[
|
||||
{"id": 0, "snow": 0xFF00FF},
|
||||
{"id": 1, "snow": 128 << 8 | 0},
|
||||
]
|
||||
)
|
||||
)
|
||||
h2d_client._process_message(_ams_payload(0))
|
||||
assert h2d_client.state.tray_now == 128
|
||||
assert h2d_client.state.last_loaded_tray == 128
|
||||
|
||||
def test_unloaded_not_stored(self, h2d_client):
|
||||
"""tray_now=255 (unloaded) → last_loaded_tray unchanged."""
|
||||
h2d_client.state.last_loaded_tray = 5
|
||||
h2d_client._process_message(_ams_payload(255))
|
||||
assert h2d_client.state.tray_now == 255
|
||||
assert h2d_client.state.last_loaded_tray == 5
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 7. H2D Active extruder switching
|
||||
|
||||
Reference in New Issue
Block a user