mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-09-30 03:01:21 +02:00
Everything the completion path needs to split a print's filament across the trays it fed from lived only in memory: the dispatched plate and slot-to-tray mapping, the spool-assignment snapshot, and the tray-change log. A print that outlived a restart lost all of it and fell back to what the printer reports at completion -- which, with AMS Filament Backup on, is the substitute tray. The whole print was charged to the spool that only finished it while the spool that ran dry was charged nothing. Persist that context in a new active_print_sessions row, append tray changes as they happen, and restore both the session and the printer's tray-change log at restart recovery. Seed the log from the current tray when there is nothing to restore, since last_loaded_tray advances even when no change is logged. Rank the queue item's stored ams_mapping above the printer's live mapping field, which is what backup rewrites. Recover plate_id from the archive or queue item, and give extract_layer_filament_usage_from_3mf a plate_id instead of taking the first .gcode member -- a Bambu Studio export stores plate 2 first, so per-layer figures were measured against the wrong plate for both inventory backends. Stop auto-unlinking a spool assignment when its slot reports empty during a running print. At a runout the spool is still in the AMS, and dropping the link leaves the completion path nothing to charge. Capture the print-start context for both inventory backends. Spoolman's own durable row (#1820) carries its plate-scoped figures and dispatched mapping but not the tray-change log, and its slot assignments -- the way. Registration in _active_sessions stays gated, since on_ams_change reads it to decide whether to skip the remain%-based weight sync (#880).
75 lines
2.7 KiB
Python
75 lines
2.7 KiB
Python
"""The tray-change log has to leave the process.
|
|
|
|
``PrinterState.tray_change_log`` is what the usage tracker splits filament
|
|
weight on when AMS filament backup swaps in a fresh spool mid-print. It lived
|
|
only in memory, so a restart during a long print erased the segment boundaries
|
|
and the whole job got charged to the tray that finished it. The client now
|
|
reports every appended entry so main.py can persist it.
|
|
"""
|
|
|
|
import pytest
|
|
|
|
from backend.app.services.bambu_mqtt import BambuMQTTClient
|
|
|
|
|
|
def _tray_msg(tray_now: int):
|
|
"""A partial AMS update carrying only tray_now, as P-series and H2D send."""
|
|
return {"print": {"ams": {"tray_now": str(tray_now)}}}
|
|
|
|
|
|
class TestTrayChangeCallback:
|
|
@pytest.fixture
|
|
def mqtt_client(self):
|
|
client = BambuMQTTClient(
|
|
ip_address="192.168.1.100",
|
|
serial_number="TEST123",
|
|
access_code="12345678",
|
|
)
|
|
client._was_running = True
|
|
client._completion_triggered = False
|
|
return client
|
|
|
|
def test_every_logged_change_is_reported(self, mqtt_client):
|
|
seen: list[tuple[int, int]] = []
|
|
mqtt_client.on_tray_change = lambda tray, layer: seen.append((tray, layer))
|
|
|
|
mqtt_client.state.layer_num = 0
|
|
mqtt_client._process_message(_tray_msg(2))
|
|
mqtt_client.state.layer_num = 670
|
|
mqtt_client._process_message(_tray_msg(254))
|
|
mqtt_client.state.layer_num = 675
|
|
mqtt_client._process_message(_tray_msg(3))
|
|
|
|
assert seen == [(2, 0), (254, 670), (3, 675)]
|
|
assert mqtt_client.state.tray_change_log == [(2, 0), (254, 670), (3, 675)]
|
|
|
|
def test_repeat_of_the_same_tray_is_not_reported(self, mqtt_client):
|
|
"""The printer republishes tray_now on every push; only transitions
|
|
are segment boundaries."""
|
|
seen: list[tuple[int, int]] = []
|
|
mqtt_client.on_tray_change = lambda tray, layer: seen.append((tray, layer))
|
|
|
|
mqtt_client._process_message(_tray_msg(2))
|
|
mqtt_client.state.layer_num = 40
|
|
mqtt_client._process_message(_tray_msg(2))
|
|
|
|
assert seen == [(2, 0)]
|
|
|
|
def test_no_callback_outside_a_running_print(self, mqtt_client):
|
|
seen: list[tuple[int, int]] = []
|
|
mqtt_client.on_tray_change = lambda tray, layer: seen.append((tray, layer))
|
|
mqtt_client._was_running = False
|
|
|
|
mqtt_client._process_message(_tray_msg(2))
|
|
|
|
assert seen == []
|
|
assert mqtt_client.state.tray_change_log == []
|
|
|
|
def test_missing_callback_does_not_break_logging(self, mqtt_client):
|
|
"""The callback is optional — the in-memory log still has to work."""
|
|
mqtt_client.on_tray_change = None
|
|
|
|
mqtt_client._process_message(_tray_msg(2))
|
|
|
|
assert mqtt_client.state.tray_change_log == [(2, 0)]
|