| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121 |
- """An A2L with an AMS 2 Pro and an AMS Lite shows both on the card (#3499).
- The firmware sends unit ids as strings ("0"); the AMS Lite's physical id 16 is
- normalised to the int 6. With both units attached the merge sorted "0" against
- 6 and raised, so the unit list was never stored. The frame's own AMS object
- stayed in its place, and every later frame (and the AMS labels API) failed on
- its string keys: the card showed only the external spool until one AMS was
- unplugged and the printer reconnected.
- Frame values follow the reporter's support package: AMS 2 Pro with four bare
- slots, AMS Lite with four Bambu PLA Basic spools, ams_exist_bits "1001",
- tray_exist_bits "f000000".
- """
- from unittest.mock import patch
- from backend.app.services.bambu_mqtt import BambuMQTTClient
- def _client() -> BambuMQTTClient:
- return BambuMQTTClient(ip_address="10.0.0.1", serial_number="A2L", access_code="c", model="A2L")
- def _lite_tray(slot: int, colour: str) -> dict:
- return {
- "id": str(slot),
- "state": 3,
- "remain": -1,
- "tray_type": "PLA",
- "tray_sub_brands": "PLA Basic",
- "tray_info_idx": "GFA00",
- "tray_color": colour,
- }
- def _frame(lite_id=16) -> dict:
- return {
- "ams": {
- "ams": [
- {"id": "0", "info": "2003", "tray": [{"id": str(n)} for n in range(4)]},
- {
- "id": lite_id,
- "info": "30001005",
- "tray": [
- _lite_tray(0, "000000FF"),
- _lite_tray(1, "3F8E43FF"),
- _lite_tray(2, "0056B8FF"),
- _lite_tray(3, "FFFFFFFF"),
- ],
- },
- ],
- "ams_exist_bits": "1001",
- "tray_exist_bits": "f000000",
- "tray_now": "255",
- "power_on_flag": True,
- },
- "gcode_state": "IDLE",
- }
- def _units(client: BambuMQTTClient) -> list:
- units = client.state.raw_data.get("ams")
- assert isinstance(units, list), f"AMS units are {type(units).__name__}, not a list"
- return units
- def test_both_units_are_stored():
- client = _client()
- client._process_message({"print": _frame()})
- units = _units(client)
- assert [str(u["id"]) for u in units] == ["0", "6"]
- assert [t["tray_color"] for t in units[1]["tray"]] == ["000000FF", "3F8E43FF", "0056B8FF", "FFFFFFFF"]
- def test_a_string_lite_id_is_stored_too():
- client = _client()
- client._process_message({"print": _frame(lite_id="16")})
- assert [str(u["id"]) for u in _units(client)] == ["0", "6"]
- def test_later_frames_keep_both_units():
- client = _client()
- for _ in range(3):
- client._process_message({"print": _frame()})
- assert [str(u["id"]) for u in _units(client)] == ["0", "6"]
- def test_a_frame_the_ams_handling_fails_on_leaves_no_ams_object_behind():
- client = _client()
- # Any failure in the AMS handling, as the mixed-id sort was.
- with patch.object(client, "_handle_ams_data", side_effect=TypeError("boom")):
- client._process_message({"print": _frame()})
- assert "ams" not in client.state.raw_data
- def test_a_stored_ams_object_does_not_block_later_frames():
- # An install already in the broken state: the AMS object in place of the list.
- client = _client()
- client.state.raw_data["ams"] = {"ams": [], "ams_exist_bits": "1001"}
- client._process_message({"print": _frame()})
- assert [str(u["id"]) for u in _units(client)] == ["0", "6"]
- def test_units_are_in_numeric_order():
- client = _client()
- frame = _frame()
- frame["ams"]["ams"].append({"id": "1", "tray": [{"id": str(n)} for n in range(4)]})
- frame["ams"]["ams_exist_bits"] = "1003"
- client._process_message({"print": frame})
- assert [str(u["id"]) for u in _units(client)] == ["0", "1", "6"]
|