"""Sparse paint_index / Bambu-T-Nummern für ACE-Map.""" import json import unittest from grotti import acm, parse, usage PAINT_SPARSE = json.dumps( [ {"material_type": "PLA", "paint_color": [255, 128, 0], "paint_index": 1}, {"material_type": "PLA", "paint_color": [0, 0, 0], "paint_index": 10}, ] ) class PaintIndexTests(unittest.TestCase): def test_filaments_keep_slicer_paint_index(self): rows = parse.filaments_from_paint_info(PAINT_SPARSE) self.assertEqual(len(rows), 2) self.assertEqual(rows[0]["paint_index"], 1) self.assertEqual(rows[0]["type"], "PLA") self.assertEqual(rows[0]["color"], "#ff8000") self.assertEqual(rows[1]["paint_index"], 10) self.assertEqual(rows[1]["color"], "#000000") def test_build_acm_uses_paint_index_not_array_slot(self): filaments = parse.filaments_from_paint_info(PAINT_SPARSE) mapping = [ {"paint_index": 1, "ams_index": 3}, {"paint_index": 10, "ams_index": 2}, ] slots = [ {"index": 2, "type": "PLA", "color": "#f4f4f4"}, {"index": 3, "type": "PLA", "color": "#c23b3b"}, ] payload = acm.build_acm(filaments, mapping, slots) paints = [item["paint_index"] for item in payload["ams_box_mapping"]] self.assertEqual(paints, [1, 10]) by_paint = {item["paint_index"]: item for item in payload["ams_box_mapping"]} self.assertEqual(by_paint[1]["ams_index"], 3) self.assertEqual(by_paint[10]["ams_index"], 2) self.assertEqual(by_paint[1]["paint_color"], [255, 128, 0]) self.assertEqual(by_paint[10]["paint_color"], [0, 0, 0]) def test_densify_mapping_and_rewrite_avoids_invalid_filament_id(self): mapping = [ {"paint_index": 1, "ams_index": 3}, {"paint_index": 10, "ams_index": 2}, ] remap, dense = acm.densify_mapping(mapping) self.assertEqual(remap, {1: 0, 10: 1}) self.assertEqual( dense, [ {"paint_index": 0, "ams_index": 3}, {"paint_index": 1, "ams_index": 2}, ], ) gcode = b"; hdr\nT10\nG1 X0\nT1 ; change\n; referenced_tools = 1,10\n" out = acm.rewrite_gcode_tools(gcode, remap) self.assertIn(b"T1\n", out) self.assertIn(b"T0 ; change", out) self.assertNotIn(b"T10", out) self.assertIn(b"; referenced_tools = 0,1", out) filaments = acm.remap_filaments( parse.filaments_from_paint_info(PAINT_SPARSE), remap ) slots = [ {"index": 2, "type": "PLA", "color": "#f4f4f4"}, {"index": 3, "type": "PLA", "color": "#c23b3b"}, ] payload = acm.build_acm(filaments, dense, slots) paints = [item["paint_index"] for item in payload["ams_box_mapping"]] self.assertEqual(paints, [0, 1]) self.assertEqual(payload["ams_box_mapping"][0]["paint_color"], [255, 128, 0]) def test_densify_identity_when_already_dense(self): mapping = [{"paint_index": 0, "ams_index": 1}, {"paint_index": 1, "ams_index": 2}] remap, dense = acm.densify_mapping(mapping) self.assertEqual(remap, {}) self.assertEqual(dense, mapping) def test_align_rewrites_when_ui_map_is_already_dense(self): """Regression 10011700: UI schickt 0/1, G-code hat noch T1/T10.""" gcode = b"; hdr\nT10\nG1 X0\nT1 ; change\n; referenced_tools = 1,10\n" mapping = [ {"paint_index": 0, "ams_index": 3}, {"paint_index": 1, "ams_index": 2}, ] filaments = [ {"type": "PLA", "color": "#ff8000", "paint_index": 0}, {"type": "PLA", "color": "#000000", "paint_index": 1}, ] out, dense, fils, remap = acm.align_tools_for_ace(gcode, mapping, filaments) self.assertEqual(remap, {1: 0, 10: 1}) self.assertEqual( dense, [ {"paint_index": 0, "ams_index": 3}, {"paint_index": 1, "ams_index": 2}, ], ) self.assertIn(b"T1\n", out) self.assertIn(b"T0 ; change", out) self.assertNotIn(b"T10", out) self.assertIn(b"; referenced_tools = 0,1", out) self.assertEqual([row["paint_index"] for row in fils], [0, 1]) self.assertEqual(fils[0]["color"], "#ff8000") self.assertEqual(fils[1]["color"], "#000000") def test_align_with_sparse_ui_map(self): gcode = b"T10\nT1\n" mapping = [ {"paint_index": 1, "ams_index": 3}, {"paint_index": 10, "ams_index": 2}, ] out, dense, fils, remap = acm.align_tools_for_ace( gcode, mapping, parse.filaments_from_paint_info(PAINT_SPARSE) ) self.assertEqual(remap, {1: 0, 10: 1}) self.assertEqual(dense[0]["ams_index"], 3) self.assertEqual(dense[1]["ams_index"], 2) self.assertNotIn(b"T10", out) self.assertEqual([row["paint_index"] for row in fils], [0, 1]) def test_tools_used_finds_t10(self): gcode = b"; header\nT10\nG1 X0\nT1 ; change\n" self.assertEqual(parse.tools_used_in_gcode(gcode), {1, 10}) def test_planned_shares_sparse_paint_falls_back_to_order(self): meta = {"filament_weight_total": [12.0, 8.0]} mapping = [ {"paint_index": 1, "ams_index": 0}, {"paint_index": 10, "ams_index": 1}, ] self.assertEqual(usage.planned_shares(meta, mapping, 2), [12.0, 8.0]) def test_profile_name_strips_machine(self): self.assertEqual(parse.profile_name('Sunlu PLA Meta @Kobra 3'), "Sunlu PLA Meta") self.assertEqual(parse.profile_name('"Sunlu PLA Matte @Kobra S1"'), "Sunlu PLA Matte") def test_filaments_of_attaches_settings_id_profiles(self): # Slot 1 = PLA, Slot 10 = Matte — wie sparse paint_info profiles = ";".join( ['"Sunlu PLA @Kobra 3"' if i not in (1, 10) else ( '"Sunlu PLA @Kobra 3"' if i == 1 else '"Sunlu PLA Matte @Kobra 3"' ) for i in range(15)] ) comments = { "paint_info": PAINT_SPARSE, "filament_settings_id": profiles, } rows = parse.filaments_of(comments) self.assertEqual(rows[0]["profile"], "Sunlu PLA") self.assertEqual(rows[1]["profile"], "Sunlu PLA Matte") def test_filaments_of_single_without_paint(self): comments = { "filament_type": "PLA", "filament_colour": "#00FF00", "filament_settings_id": '"Sunlu PLA Meta @Kobra 3"', } rows = parse.filaments_of(comments) self.assertEqual(len(rows), 1) self.assertEqual(rows[0]["profile"], "Sunlu PLA Meta") self.assertEqual(rows[0]["type"], "PLA") if __name__ == "__main__": unittest.main()