mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-09-30 19:21:33 +02:00
BambuStudio writes no bed_temperature key. It stores a per-filament array per plate type and names the fitted plate in curr_bed_type; bed_temperature is the Orca/Prusa spelling, so the lookup matched nothing and every archive from a Bambu slice stored NULL - 0 of 455 real 3MFs resolved. Preheat then fell back to the keep-warm bed temperature on jobs that had one all along. Plate names and the plate-to-key mapping are BambuStudio's own get_bed_temp_key and get_bed_temp_1st_layer_key. First-layer value preferred, highest entry in the per-filament array taken, an all-zero array left unrecorded.
165 lines
6.9 KiB
Python
165 lines
6.9 KiB
Python
"""BambuStudio does not write a ``bed_temperature`` key, so we never read one.
|
|
|
|
The 3MF stores one bed-temperature array per plate type -- ``cool_plate_temp``,
|
|
``eng_plate_temp``, ``hot_plate_temp``, ``textured_plate_temp``,
|
|
``supertack_plate_temp`` -- and names the plate the project is sliced for in
|
|
``curr_bed_type``. ``bed_temperature`` is the Orca/PrusaSlicer spelling. The
|
|
extractor looked only for that spelling, so ``PrintArchive.bed_temperature`` was
|
|
NULL for every archive produced from a Bambu slice: measured at 0 of 455 real
|
|
3MFs on a live install, against 455 of 455 once the plate keys are read.
|
|
|
|
That NULL is not cosmetic. Preheat reads ``archive.bed_temperature`` to decide
|
|
what to heat the bed to, and with nothing there it falls back to the configured
|
|
chamber-heating bed temperature (90 by default) on jobs that never wanted it --
|
|
which is how #2989's PLA print came to sit behind a 90°C bed.
|
|
|
|
Key names and the plate mapping are BambuStudio's own ``get_bed_temp_key`` /
|
|
``get_bed_temp_1st_layer_key`` (PrintConfig.hpp); the plate names are the
|
|
``curr_bed_type`` enum values (PrintConfig.cpp).
|
|
"""
|
|
|
|
import pytest
|
|
|
|
from backend.app.services.archive import ThreeMFParser
|
|
|
|
|
|
def _extract(config: dict) -> int | None:
|
|
parser = ThreeMFParser.__new__(ThreeMFParser)
|
|
parser.metadata = {}
|
|
parser._extract_print_settings(config)
|
|
return parser.metadata.get("bed_temperature")
|
|
|
|
|
|
class TestThePlateTheProjectIsSlicedFor:
|
|
@pytest.mark.parametrize(
|
|
"bed_type,key",
|
|
[
|
|
("Cool Plate", "cool_plate_temp"),
|
|
("Engineering Plate", "eng_plate_temp"),
|
|
("High Temp Plate", "hot_plate_temp"),
|
|
("Textured PEI Plate", "textured_plate_temp"),
|
|
("Supertack Plate", "supertack_plate_temp"),
|
|
],
|
|
)
|
|
def test_every_plate_type_reads_its_own_array(self, bed_type, key):
|
|
"""All five plates BambuStudio can name, each with its own key."""
|
|
assert _extract({"curr_bed_type": bed_type, key: ["65", "65"]}) == 65
|
|
|
|
def test_the_fitted_plate_wins_over_the_others(self):
|
|
"""The real failure mode this replaces: reading whichever key happened
|
|
to be present. A Textured slice must not take the cool plate's 0."""
|
|
config = {
|
|
"curr_bed_type": "Textured PEI Plate",
|
|
"cool_plate_temp": ["0", "0", "0"],
|
|
"eng_plate_temp": ["90", "90", "90"],
|
|
"hot_plate_temp": ["90", "90", "90"],
|
|
"textured_plate_temp": ["55", "55", "55"],
|
|
"supertack_plate_temp": ["0", "0", "0"],
|
|
}
|
|
assert _extract(config) == 55
|
|
|
|
def test_the_first_layer_value_is_preferred(self):
|
|
"""It is what the printer heats to before the print starts, which is
|
|
the number preheat is trying to reach."""
|
|
config = {
|
|
"curr_bed_type": "Textured PEI Plate",
|
|
"textured_plate_temp_initial_layer": ["65"],
|
|
"textured_plate_temp": ["60"],
|
|
}
|
|
assert _extract(config) == 65
|
|
|
|
def test_the_regular_value_when_there_is_no_first_layer_one(self):
|
|
config = {"curr_bed_type": "Textured PEI Plate", "textured_plate_temp": ["60"]}
|
|
assert _extract(config) == 60
|
|
|
|
|
|
class TestThePerFilamentArray:
|
|
def test_the_highest_entry_wins(self):
|
|
"""One bed, several filaments: the print runs at the highest its
|
|
filaments ask for."""
|
|
config = {"curr_bed_type": "High Temp Plate", "hot_plate_temp": ["55", "100", "90"]}
|
|
assert _extract(config) == 100
|
|
|
|
def test_a_leading_zero_does_not_win(self):
|
|
"""0 means that filament cannot print on this plate. Taking entry 0 --
|
|
what the neighbouring scalar settings do -- would store a cold bed for
|
|
any project whose first filament is not one this plate is heated for."""
|
|
config = {"curr_bed_type": "High Temp Plate", "hot_plate_temp": ["0", "90"]}
|
|
assert _extract(config) == 90
|
|
|
|
def test_a_scalar_rather_than_an_array(self):
|
|
assert _extract({"curr_bed_type": "Cool Plate", "cool_plate_temp": 35}) == 35
|
|
|
|
def test_numeric_strings_and_floats(self):
|
|
assert _extract({"curr_bed_type": "Cool Plate", "cool_plate_temp": ["35.0"]}) == 35
|
|
|
|
|
|
class TestWhatItRefusesToInvent:
|
|
def test_an_all_zero_plate_array_is_not_a_bed_temperature(self):
|
|
"""No filament in the project prints on this plate. Recording the 0
|
|
would read downstream as a deliberate cold bed."""
|
|
config = {"curr_bed_type": "Cool Plate", "cool_plate_temp": ["0", "0"]}
|
|
assert _extract(config) is None
|
|
|
|
def test_a_plate_bambustudio_maps_to_no_key(self):
|
|
"""``Default Plate`` has no temperature array of its own, and guessing
|
|
another plate's would invent a temperature the slice never specified."""
|
|
config = {"curr_bed_type": "Default Plate", "hot_plate_temp": ["90"]}
|
|
assert _extract(config) is None
|
|
|
|
def test_a_plate_name_we_do_not_know(self):
|
|
"""A plate a future BambuStudio adds must not silently read another
|
|
plate's array."""
|
|
config = {"curr_bed_type": "Cryo Plate", "textured_plate_temp": ["55"]}
|
|
assert _extract(config) is None
|
|
|
|
def test_a_config_with_no_bed_temperature_at_all(self):
|
|
assert _extract({"curr_bed_type": "Textured PEI Plate", "layer_height": ["0.2"]}) is None
|
|
|
|
def test_junk_entries_are_stepped_over(self):
|
|
config = {"curr_bed_type": "Cool Plate", "cool_plate_temp": [None, {}, "abc", "35"]}
|
|
assert _extract(config) == 35
|
|
|
|
|
|
class TestOrcaExportsStillWork:
|
|
"""The generic spelling is the fallback, not the primary -- Orca-exported
|
|
3MFs and anything else Prusa-shaped keep parsing exactly as before."""
|
|
|
|
def test_the_generic_first_layer_key(self):
|
|
assert _extract({"bed_temperature_initial_layer": ["60"]}) == 60
|
|
|
|
def test_the_generic_key(self):
|
|
assert _extract({"bed_temperature": 60}) == 60
|
|
|
|
def test_the_plate_array_is_preferred_when_both_are_present(self):
|
|
config = {
|
|
"curr_bed_type": "Textured PEI Plate",
|
|
"textured_plate_temp": ["55"],
|
|
"bed_temperature": ["90"],
|
|
}
|
|
assert _extract(config) == 55
|
|
|
|
|
|
class TestTheNeighbouringSettingsAreUntouched:
|
|
"""The same method extracts three other values; none of them changed."""
|
|
|
|
def test_nozzle_temperature_layer_height_and_diameter(self):
|
|
parser = ThreeMFParser.__new__(ThreeMFParser)
|
|
parser.metadata = {}
|
|
parser._extract_print_settings(
|
|
{
|
|
"curr_bed_type": "Textured PEI Plate",
|
|
"textured_plate_temp": ["55"],
|
|
"nozzle_temperature_initial_layer": ["220", "220"],
|
|
"layer_height": ["0.2"],
|
|
"nozzle_diameter": ["0.4"],
|
|
}
|
|
)
|
|
assert parser.metadata == {
|
|
"bed_type": "Textured PEI Plate",
|
|
"layer_height": 0.2,
|
|
"nozzle_diameter": 0.4,
|
|
"bed_temperature": 55,
|
|
"nozzle_temperature": 220,
|
|
}
|