| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161 |
- """A printer with no AMS gets use_ams=False (#3356).
- A P1S with no AMS and nothing set on its spool holder: the matcher had nothing
- to match, so the print went out with no mapping and use_ams=True, which the
- firmware refuses with 07FF_8012 "Failed to get AMS mapping table". The
- mapping-based reconcile (#2595) never ran without a mapping. The printer's own
- push says it has no AMS (``"ams": []``, from the reporter's support package),
- so the command builder sends use_ams=False, as the slicer does.
- """
- import json
- from unittest.mock import MagicMock
- import pytest
- from backend.app.services.bambu_mqtt import BambuMQTTClient
- @pytest.fixture
- def client():
- client = BambuMQTTClient(ip_address="192.168.1.100", serial_number="01P00A000000001", access_code="12345678")
- client.model = "P1S"
- client._client = MagicMock()
- client.state.connected = True
- return client
- def _use_ams(client) -> bool:
- assert client._client.publish.called, "start_print did not publish"
- return json.loads(client._client.publish.call_args.args[1])["print"]["use_ams"]
- def _no_ams(client):
- client._handle_ams_data({"ams": []})
- def test_no_ams_and_no_mapping(client):
- _no_ams(client)
- assert client.start_print("case_body.3mf", ams_mapping=None, use_ams=True)
- assert _use_ams(client) is False
- @pytest.mark.parametrize("mapping", [[254], [-1], [-1, 254]])
- def test_no_ams_with_a_mapping(client, mapping):
- _no_ams(client)
- assert client.start_print("case_body.3mf", ams_mapping=mapping, use_ams=True)
- assert _use_ams(client) is False
- def test_not_heard_from_the_ams_yet(client):
- """No AMS push handled yet (just reconnected): the AMS may be there."""
- assert "ams" not in client.state.raw_data
- assert client.start_print("case_body.3mf", ams_mapping=None, use_ams=True)
- assert _use_ams(client) is True
- def test_with_an_ams(client):
- client._handle_ams_data({"ams": [{"id": "0", "tray": [{"id": "0"}, {"id": "1"}, {"id": "2"}, {"id": "3"}]}]})
- assert client.start_print("case_body.3mf", ams_mapping=None, use_ams=True)
- assert _use_ams(client) is True
- def test_dual_nozzle_is_left_alone(client):
- """On an H2D use_ams is nozzle routing, not an AMS on/off flag."""
- client.model = "H2D"
- _no_ams(client)
- assert client.start_print("case_body.3mf", ams_mapping=None, use_ams=True)
- assert _use_ams(client) is True
- # The AMS blocks below are shaped like Martin's X1C and H2C captures: the unit
- # list and ams_exist_bits arrive together in every push.
- def _unit(unit_id, trays=4):
- return {"id": str(unit_id), "tray": [{"id": str(t), "tray_type": "PLA"} for t in range(trays)]}
- def _push(units, bits):
- return {"ams": units, "ams_exist_bits": bits, "tray_now": "255", "power_on_flag": False}
- def _unit_ids(client):
- return sorted((u["id"] for u in client.state.raw_data["ams"]), key=int)
- class TestAnUnpluggedAms:
- def test_the_x1c_unplug_and_replug(self, client):
- client.model = "X1C"
- client._handle_ams_data(_push([_unit(1)], "2"))
- client._handle_ams_data(_push([], "0"))
- assert _unit_ids(client) == []
- client._handle_ams_data(_push([_unit(1)], "2"))
- assert _unit_ids(client) == ["1"]
- def test_printing_from_the_spool_holder_after_the_unplug(self, client):
- """No restart needed: the no-AMS rule sees the unplug."""
- client._handle_ams_data(_push([_unit(0)], "1"))
- client._handle_ams_data(_push([], "0"))
- assert client.start_print("case_body.3mf", ams_mapping=None, use_ams=True)
- assert _use_ams(client) is False
- def test_a_unit_whose_bit_is_cleared_goes(self, client):
- client.model = "H2C"
- client._handle_ams_data(_push([_unit(0), _unit(1), _unit(2), _unit(128, 1)], "17"))
- client._handle_ams_data(_push([_unit(0), _unit(2), _unit(128, 1)], "15"))
- assert _unit_ids(client) == ["0", "2", "128"]
- def test_a_partial_list_keeps_the_units_it_leaves_out(self, client):
- client.model = "H2C"
- client._handle_ams_data(_push([_unit(0), _unit(1), _unit(2), _unit(128, 1)], "17"))
- client._handle_ams_data(_push([_unit(0)], "17"))
- assert _unit_ids(client) == ["0", "1", "2", "128"]
- def test_an_ams_ht_next_to_other_units_is_kept(self, client):
- """Its bit is not read: measured once, on one printer."""
- client.model = "H2C"
- client._handle_ams_data(_push([_unit(0), _unit(128, 1)], "11"))
- client._handle_ams_data(_push([_unit(0)], "1"))
- assert _unit_ids(client) == ["0", "128"]
- def test_the_last_ams_ht_goes_with_everything_else(self, client):
- client._handle_ams_data(_push([_unit(128, 1)], "10"))
- client._handle_ams_data(_push([], "0"))
- assert _unit_ids(client) == []
- def test_a_push_without_bits_changes_nothing(self, client):
- client._handle_ams_data(_push([_unit(0)], "1"))
- client._handle_ams_data({"ams": []})
- assert _unit_ids(client) == ["0"]
- def test_no_bits_next_to_a_listed_unit_keeps_an_ams_ht(self, client):
- """All-zero bits while units are still listed is the shutdown pattern
- (#765), not a report that the AMS HT is gone."""
- client._handle_ams_data(_push([_unit(0), _unit(128, 1)], "11"))
- client._handle_ams_data(_push([_unit(0)], "0"))
- assert _unit_ids(client) == ["0", "128"]
|