From 7ba48bfa9783f9305023048ad8b156e921068236 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Wed, 10 Jul 2024 13:13:26 +0200 Subject: [PATCH 1/7] Add the slicemetadata.json file into the makerbot file archive CURA-12005 --- cura/API/Interface/Settings.py | 62 +++++++++++++++++++++++- plugins/MakerbotWriter/MakerbotWriter.py | 3 ++ plugins/UFPWriter/UFPWriter.py | 58 ++-------------------- 3 files changed, 67 insertions(+), 56 deletions(-) diff --git a/cura/API/Interface/Settings.py b/cura/API/Interface/Settings.py index 706a6d8c74..084023b9bd 100644 --- a/cura/API/Interface/Settings.py +++ b/cura/API/Interface/Settings.py @@ -1,7 +1,13 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from typing import TYPE_CHECKING +from dataclasses import asdict + +from typing import cast, Dict, TYPE_CHECKING + +from UM.Settings.InstanceContainer import InstanceContainer +from UM.Settings.SettingFunction import SettingFunction +from cura.Settings.GlobalStack import GlobalStack if TYPE_CHECKING: from cura.CuraApplication import CuraApplication @@ -47,3 +53,57 @@ class Settings: """ return self.application.getSidebarCustomMenuItems() + + def getSliceMetadata(self) -> Dict[str, Dict[str, Dict[str, str]]]: + """Get all changed settings and all settings. For each extruder and the global stack""" + print_information = self.application.getPrintInformation() + machine_manager = self.application.getMachineManager() + settings = { + "material": { + "length": print_information.materialLengths, + "weight": print_information.materialWeights, + "cost": print_information.materialCosts, + }, + "global": { + "changes": {}, + "all_settings": {}, + }, + "quality": asdict(machine_manager.activeQualityDisplayNameMap()), + } + + def _retrieveValue(container: InstanceContainer, setting_: str): + value_ = container.getProperty(setting_, "value") + for _ in range(0, 1024): # Prevent possibly endless loop by not using a limit. + if not isinstance(value_, SettingFunction): + return value_ # Success! + value_ = value_(container) + return 0 # Fallback value after breaking possibly endless loop. + + global_stack = cast(GlobalStack, self.application.getGlobalContainerStack()) + + # Add global user or quality changes + global_flattened_changes = InstanceContainer.createMergedInstanceContainer(global_stack.userChanges, global_stack.qualityChanges) + for setting in global_flattened_changes.getAllKeys(): + settings["global"]["changes"][setting] = _retrieveValue(global_flattened_changes, setting) + + # Get global all settings values without user or quality changes + for setting in global_stack.getAllKeys(): + settings["global"]["all_settings"][setting] = _retrieveValue(global_stack, setting) + + for i, extruder in enumerate(global_stack.extruderList): + # Add extruder fields to settings dictionary + settings[f"extruder_{i}"] = { + "changes": {}, + "all_settings": {}, + } + + # Add extruder user or quality changes + extruder_flattened_changes = InstanceContainer.createMergedInstanceContainer(extruder.userChanges, extruder.qualityChanges) + for setting in extruder_flattened_changes.getAllKeys(): + settings[f"extruder_{i}"]["changes"][setting] = _retrieveValue(extruder_flattened_changes, setting) + + # Get extruder all settings values without user or quality changes + for setting in extruder.getAllKeys(): + settings[f"extruder_{i}"]["all_settings"][setting] = _retrieveValue(extruder, setting) + + return settings diff --git a/plugins/MakerbotWriter/MakerbotWriter.py b/plugins/MakerbotWriter/MakerbotWriter.py index 233575029c..a29d01873f 100644 --- a/plugins/MakerbotWriter/MakerbotWriter.py +++ b/plugins/MakerbotWriter/MakerbotWriter.py @@ -137,6 +137,9 @@ class MakerbotWriter(MeshWriter): for png_file in png_files: file, data = png_file["file"], png_file["data"] zip_stream.writestr(file, data) + api = CuraApplication.getInstance().getCuraAPI() + slice_metadata = json.dumps(api.interface.settings.getSliceMetadata(), separators=(", ", ": "), indent=4) + zip_stream.writestr("slicemetadata.json", slice_metadata) except (IOError, OSError, BadZipFile) as ex: Logger.log("e", f"Could not write to (.makerbot) file because: '{ex}'.") self.setInformation(catalog.i18nc("@error", "MakerbotWriter could not save to the designated path.")) diff --git a/plugins/UFPWriter/UFPWriter.py b/plugins/UFPWriter/UFPWriter.py index 475e5fc01a..0cf756b6a4 100644 --- a/plugins/UFPWriter/UFPWriter.py +++ b/plugins/UFPWriter/UFPWriter.py @@ -24,6 +24,7 @@ from UM.Settings.InstanceContainer import InstanceContainer from cura.CuraApplication import CuraApplication from cura.Settings.GlobalStack import GlobalStack from cura.Utils.Threading import call_on_qt_thread +from cura.API import CuraAPI from UM.i18n import i18nCatalog @@ -85,7 +86,8 @@ class UFPWriter(MeshWriter): try: archive.addContentType(extension="json", mime_type="application/json") setting_textio = StringIO() - json.dump(self._getSliceMetadata(), setting_textio, separators=(", ", ": "), indent=4) + api = CuraApplication.getInstance().getCuraAPI() + json.dump(api.interface.settings.getSliceMetadata(), setting_textio, separators=(", ", ": "), indent=4) steam = archive.getStream(SLICE_METADATA_PATH) steam.write(setting_textio.getvalue().encode("UTF-8")) except EnvironmentError as e: @@ -210,57 +212,3 @@ class UFPWriter(MeshWriter): return [{"name": item.getName()} for item in DepthFirstIterator(node) if item.getMeshData() is not None and not item.callDecoration("isNonPrintingMesh")] - - def _getSliceMetadata(self) -> Dict[str, Dict[str, Dict[str, str]]]: - """Get all changed settings and all settings. For each extruder and the global stack""" - print_information = CuraApplication.getInstance().getPrintInformation() - machine_manager = CuraApplication.getInstance().getMachineManager() - settings = { - "material": { - "length": print_information.materialLengths, - "weight": print_information.materialWeights, - "cost": print_information.materialCosts, - }, - "global": { - "changes": {}, - "all_settings": {}, - }, - "quality": asdict(machine_manager.activeQualityDisplayNameMap()), - } - - def _retrieveValue(container: InstanceContainer, setting_: str): - value_ = container.getProperty(setting_, "value") - for _ in range(0, 1024): # Prevent possibly endless loop by not using a limit. - if not isinstance(value_, SettingFunction): - return value_ # Success! - value_ = value_(container) - return 0 # Fallback value after breaking possibly endless loop. - - global_stack = cast(GlobalStack, Application.getInstance().getGlobalContainerStack()) - - # Add global user or quality changes - global_flattened_changes = InstanceContainer.createMergedInstanceContainer(global_stack.userChanges, global_stack.qualityChanges) - for setting in global_flattened_changes.getAllKeys(): - settings["global"]["changes"][setting] = _retrieveValue(global_flattened_changes, setting) - - # Get global all settings values without user or quality changes - for setting in global_stack.getAllKeys(): - settings["global"]["all_settings"][setting] = _retrieveValue(global_stack, setting) - - for i, extruder in enumerate(global_stack.extruderList): - # Add extruder fields to settings dictionary - settings[f"extruder_{i}"] = { - "changes": {}, - "all_settings": {}, - } - - # Add extruder user or quality changes - extruder_flattened_changes = InstanceContainer.createMergedInstanceContainer(extruder.userChanges, extruder.qualityChanges) - for setting in extruder_flattened_changes.getAllKeys(): - settings[f"extruder_{i}"]["changes"][setting] = _retrieveValue(extruder_flattened_changes, setting) - - # Get extruder all settings values without user or quality changes - for setting in extruder.getAllKeys(): - settings[f"extruder_{i}"]["all_settings"][setting] = _retrieveValue(extruder, setting) - - return settings From f81a3f0e11949387198a13adf4233f164dcf53a6 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Thu, 1 Aug 2024 13:59:49 +0200 Subject: [PATCH 2/7] Centralize all of these mappings. done as part of CURA-12005 --- cura/PrinterOutput/FormatMaps.py | 40 +++++++++++++++++++ .../Models/ExtruderConfigurationModel.py | 15 ++----- .../Models/MaterialOutputModel.py | 30 +++----------- .../NetworkedPrinterOutputDevice.py | 13 ++---- 4 files changed, 53 insertions(+), 45 deletions(-) create mode 100644 cura/PrinterOutput/FormatMaps.py diff --git a/cura/PrinterOutput/FormatMaps.py b/cura/PrinterOutput/FormatMaps.py new file mode 100644 index 0000000000..2aa9ccc86e --- /dev/null +++ b/cura/PrinterOutput/FormatMaps.py @@ -0,0 +1,40 @@ +# Copyright (c) 2024 UltiMaker +# Cura is released under the terms of the LGPLv3 or higher. + +class FormatMaps: + + PRINTER_TYPE_NAME = { + "fire_e": "ultimaker_method", + "lava_f": "ultimaker_methodx", + "magma_10": "ultimaker_methodxl", + "sketch": "ultimaker_sketch" + } + + EXTRUDER_NAME_MAP = { + "mk14_hot": "1XA", + "mk14_hot_s": "2XA", + "mk14_c": "1C", + "mk14": "1A", + "mk14_s": "2A", + "mk14_e": "LABS" + } + + MATERIAL_MAP = { + "abs": {"name": "ABS", "guid": "2780b345-577b-4a24-a2c5-12e6aad3e690"}, + "abs-cf10": {"name": "ABS-CF", "guid": "495a0ce5-9daf-4a16-b7b2-06856d82394d"}, + "abs-wss1": {"name": "ABS-R", "guid": "88c8919c-6a09-471a-b7b6-e801263d862d"}, + "asa": {"name": "ASA", "guid": "f79bc612-21eb-482e-ad6c-87d75bdde066"}, + "nylon12-cf": {"name": "Nylon 12 CF", "guid": "3c6f2877-71cc-4760-84e6-4b89ab243e3b"}, + "nylon": {"name": "Nylon", "guid": "283d439a-3490-4481-920c-c51d8cdecf9c"}, + "pc": {"name": "PC", "guid": "62414577-94d1-490d-b1e4-7ef3ec40db02"}, + "petg": {"name": "PETG", "guid": "69386c85-5b6c-421a-bec5-aeb1fb33f060"}, + "pla": {"name": "PLA", "guid": "abb9c58e-1f56-48d1-bd8f-055fde3a5b56"}, + "pva": {"name": "PVA", "guid": "add51ef2-86eb-4c39-afd5-5586564f0715"}, + "wss1": {"name": "RapidRinse", "guid": "a140ef8f-4f26-4e73-abe0-cfc29d6d1024"}, + "sr30": {"name": "SR-30", "guid": "77873465-83a9-4283-bc44-4e542b8eb3eb"}, + "bvoh": {"name": "BVOH", "guid": "923e604c-8432-4b09-96aa-9bbbd42207f4"}, + "cpe": {"name": "CPE", "guid": "da1872c1-b991-4795-80ad-bdac0f131726"}, + "hips": {"name": "HIPS", "guid": "a468d86a-220c-47eb-99a5-bbb47e514eb0"}, + "tpu": {"name": "TPU 95A", "guid": "19baa6a9-94ff-478b-b4a1-8157b74358d2"}, + "im-pla": {"name": "Tough", "guid": "de031137-a8ca-4a72-bd1b-17bb964033ad"} + } diff --git a/cura/PrinterOutput/Models/ExtruderConfigurationModel.py b/cura/PrinterOutput/Models/ExtruderConfigurationModel.py index ac924c684e..c5c480e224 100644 --- a/cura/PrinterOutput/Models/ExtruderConfigurationModel.py +++ b/cura/PrinterOutput/Models/ExtruderConfigurationModel.py @@ -1,9 +1,10 @@ -# Copyright (c) 2018 Ultimaker B.V. +# Copyright (c) 2024 UltiMaker # Cura is released under the terms of the LGPLv3 or higher. from typing import Optional from PyQt6.QtCore import pyqtProperty, QObject, pyqtSignal +from cura.PrinterOutput.FormatMaps import FormatMaps from .MaterialOutputModel import MaterialOutputModel @@ -45,16 +46,8 @@ class ExtruderConfigurationModel(QObject): @staticmethod def applyNameMappingHotend(hotendId) -> str: - _EXTRUDER_NAME_MAP = { - "mk14_hot":"1XA", - "mk14_hot_s":"2XA", - "mk14_c":"1C", - "mk14":"1A", - "mk14_s":"2A", - "mk14_e": "LABS" - } - if hotendId in _EXTRUDER_NAME_MAP: - return _EXTRUDER_NAME_MAP[hotendId] + if hotendId in FormatMaps.EXTRUDER_NAME_MAP: + return FormatMaps.EXTRUDER_NAME_MAP[hotendId] return hotendId @pyqtProperty(str, fset = setHotendID, notify = extruderConfigurationChanged) diff --git a/cura/PrinterOutput/Models/MaterialOutputModel.py b/cura/PrinterOutput/Models/MaterialOutputModel.py index 854226c6d4..8790d1626e 100644 --- a/cura/PrinterOutput/Models/MaterialOutputModel.py +++ b/cura/PrinterOutput/Models/MaterialOutputModel.py @@ -1,9 +1,10 @@ -# Copyright (c) 2017 Ultimaker B.V. +# Copyright (c) 2024 UltiMaker # Cura is released under the terms of the LGPLv3 or higher. from typing import Optional from PyQt6.QtCore import pyqtProperty, QObject +from cura.PrinterOutput.FormatMaps import FormatMaps class MaterialOutputModel(QObject): @@ -23,30 +24,9 @@ class MaterialOutputModel(QObject): @staticmethod def getMaterialFromDefinition(guid, type, brand, name): - - _MATERIAL_MAP = { "abs" :{"name" :"ABS" ,"guid": "2780b345-577b-4a24-a2c5-12e6aad3e690"}, - "abs-cf10" :{"name": "ABS-CF" ,"guid": "495a0ce5-9daf-4a16-b7b2-06856d82394d"}, - "abs-wss1" :{"name" :"ABS-R" ,"guid": "88c8919c-6a09-471a-b7b6-e801263d862d"}, - "asa" :{"name" :"ASA" ,"guid": "f79bc612-21eb-482e-ad6c-87d75bdde066"}, - "nylon12-cf":{"name": "Nylon 12 CF" ,"guid": "3c6f2877-71cc-4760-84e6-4b89ab243e3b"}, - "nylon" :{"name" :"Nylon" ,"guid": "283d439a-3490-4481-920c-c51d8cdecf9c"}, - "pc" :{"name" :"PC" ,"guid": "62414577-94d1-490d-b1e4-7ef3ec40db02"}, - "petg" :{"name" :"PETG" ,"guid": "69386c85-5b6c-421a-bec5-aeb1fb33f060"}, - "pla" :{"name" :"PLA" ,"guid": "abb9c58e-1f56-48d1-bd8f-055fde3a5b56"}, - "pva" :{"name" :"PVA" ,"guid": "add51ef2-86eb-4c39-afd5-5586564f0715"}, - "wss1" :{"name" :"RapidRinse" ,"guid": "a140ef8f-4f26-4e73-abe0-cfc29d6d1024"}, - "sr30" :{"name" :"SR-30" ,"guid": "77873465-83a9-4283-bc44-4e542b8eb3eb"}, - "bvoh" :{"name" :"BVOH" ,"guid": "923e604c-8432-4b09-96aa-9bbbd42207f4"}, - "cpe" :{"name" :"CPE" ,"guid": "da1872c1-b991-4795-80ad-bdac0f131726"}, - "hips" :{"name" :"HIPS" ,"guid": "a468d86a-220c-47eb-99a5-bbb47e514eb0"}, - "tpu" :{"name" :"TPU 95A" ,"guid": "19baa6a9-94ff-478b-b4a1-8157b74358d2"}, - "im-pla" :{"name": "Tough" ,"guid": "de031137-a8ca-4a72-bd1b-17bb964033ad"} - } - - - if guid is None and brand != "empty" and type in _MATERIAL_MAP: - name = _MATERIAL_MAP[type]["name"] - guid = _MATERIAL_MAP[type]["guid"] + if guid is None and brand != "empty" and type in FormatMaps.MATERIAL_MAP: + name = FormatMaps.MATERIAL_MAP[type]["name"] + guid = FormatMaps.MATERIAL_MAP[type]["guid"] return name, guid diff --git a/cura/PrinterOutput/NetworkedPrinterOutputDevice.py b/cura/PrinterOutput/NetworkedPrinterOutputDevice.py index 2a683966db..3dc245d468 100644 --- a/cura/PrinterOutput/NetworkedPrinterOutputDevice.py +++ b/cura/PrinterOutput/NetworkedPrinterOutputDevice.py @@ -1,4 +1,4 @@ -# Copyright (c) 2021 Ultimaker B.V. +# Copyright (c) 2024 UltiMaker # Cura is released under the terms of the LGPLv3 or higher. from UM.FileHandler.FileHandler import FileHandler #For typing. @@ -6,6 +6,7 @@ from UM.Logger import Logger from UM.Scene.SceneNode import SceneNode #For typing. from cura.API import Account from cura.CuraApplication import CuraApplication +from cura.PrinterOutput.FormatMaps import FormatMaps from cura.PrinterOutput.PrinterOutputDevice import PrinterOutputDevice, ConnectionState, ConnectionType @@ -419,14 +420,8 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice): @staticmethod def applyPrinterTypeMapping(printer_type): - _PRINTER_TYPE_NAME = { - "fire_e": "ultimaker_method", - "lava_f": "ultimaker_methodx", - "magma_10": "ultimaker_methodxl", - "sketch": "ultimaker_sketch" - } - if printer_type in _PRINTER_TYPE_NAME: - return _PRINTER_TYPE_NAME[printer_type] + if printer_type in FormatMaps.PRINTER_TYPE_NAME: + return FormatMaps.PRINTER_TYPE_NAME[printer_type] return printer_type @pyqtProperty(str, constant = True) From ddf4f4c6fef74751d43db55cb8410583e5ee4c5e Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Thu, 1 Aug 2024 14:33:24 +0200 Subject: [PATCH 3/7] Also move printer name mapping to new FormatMaps utility. done as part of CURA-12005 --- cura/PrinterOutput/FormatMaps.py | 27 +++++++++++++++++ .../XmlMaterialProfile/XmlMaterialProfile.py | 30 +++---------------- .../texts}/product_to_id.json | 0 3 files changed, 31 insertions(+), 26 deletions(-) rename {plugins/XmlMaterialProfile => resources/texts}/product_to_id.json (100%) diff --git a/cura/PrinterOutput/FormatMaps.py b/cura/PrinterOutput/FormatMaps.py index 2aa9ccc86e..8c785eb2ff 100644 --- a/cura/PrinterOutput/FormatMaps.py +++ b/cura/PrinterOutput/FormatMaps.py @@ -1,6 +1,11 @@ # Copyright (c) 2024 UltiMaker # Cura is released under the terms of the LGPLv3 or higher. +from UM.Resources import Resources + +import json +from typing import Dict, List, Optional + class FormatMaps: PRINTER_TYPE_NAME = { @@ -38,3 +43,25 @@ class FormatMaps: "tpu": {"name": "TPU 95A", "guid": "19baa6a9-94ff-478b-b4a1-8157b74358d2"}, "im-pla": {"name": "Tough", "guid": "de031137-a8ca-4a72-bd1b-17bb964033ad"} } + + __product_to_id_map: Optional[Dict[str, List[str]]] = None + + @classmethod + def getProductIdMap(cls) -> Dict[str, List[str]]: + """Gets a mapping from product names in the XML files to their definition IDs. + + This loads the mapping from a file. + """ + if cls.__product_to_id_map is not None: + return cls.__product_to_id_map + + product_to_id_file = Resources.getPath(Resources.Texts, "product_to_id.json") + with open(product_to_id_file, encoding = "utf-8") as f: + contents = "" + for line in f: + contents += line if "#" not in line else "".join([line.replace("#", str(n)) for n in range(1, 12)]) + cls.__product_to_id_map = json.loads(contents) + cls.__product_to_id_map = {key: [value] for key, value in cls.__product_to_id_map.items()} + #This also loads "Ultimaker S5" -> "ultimaker_s5" even though that is not strictly necessary with the default to change spaces into underscores. + #However it is not always loaded with that default; this mapping is also used in serialize() without that default. + return cls.__product_to_id_map diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index 980288e3c9..49b5e7661d 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -17,6 +17,7 @@ from UM.Settings.ContainerRegistry import ContainerRegistry from UM.ConfigurationErrorMessage import ConfigurationErrorMessage from cura.CuraApplication import CuraApplication +from cura.PrinterOutput.FormatMaps import FormatMaps from cura.Machines.VariantType import VariantType try: @@ -249,7 +250,7 @@ class XmlMaterialProfile(InstanceContainer): machine_variant_map[definition_id][variant_name] = variant_dict # Map machine human-readable names to IDs - product_id_map = self.getProductIdMap() + product_id_map = FormatMaps.getProductIdMap() for definition_id, container in machine_container_map.items(): definition_id = container.getMetaDataEntry("definition") @@ -647,7 +648,7 @@ class XmlMaterialProfile(InstanceContainer): self._dirty = False # Map machine human-readable names to IDs - product_id_map = self.getProductIdMap() + product_id_map = FormatMaps.getProductIdMap() machines = data.iterfind("./um:settings/um:machine", self.__namespaces) for machine in machines: @@ -923,7 +924,7 @@ class XmlMaterialProfile(InstanceContainer): result_metadata.append(base_metadata) # Map machine human-readable names to IDs - product_id_map = cls.getProductIdMap() + product_id_map = FormatMaps.getProductIdMap() for machine in data.iterfind("./um:settings/um:machine", cls.__namespaces): machine_compatibility = common_compatibility @@ -1128,29 +1129,6 @@ class XmlMaterialProfile(InstanceContainer): id_list = list(id_list) return id_list - __product_to_id_map: Optional[Dict[str, List[str]]] = None - - @classmethod - def getProductIdMap(cls) -> Dict[str, List[str]]: - """Gets a mapping from product names in the XML files to their definition IDs. - - This loads the mapping from a file. - """ - if cls.__product_to_id_map is not None: - return cls.__product_to_id_map - - plugin_path = cast(str, PluginRegistry.getInstance().getPluginPath("XmlMaterialProfile")) - product_to_id_file = os.path.join(plugin_path, "product_to_id.json") - with open(product_to_id_file, encoding = "utf-8") as f: - contents = "" - for line in f: - contents += line if "#" not in line else "".join([line.replace("#", str(n)) for n in range(1, 12)]) - cls.__product_to_id_map = json.loads(contents) - cls.__product_to_id_map = {key: [value] for key, value in cls.__product_to_id_map.items()} - #This also loads "Ultimaker S5" -> "ultimaker_s5" even though that is not strictly necessary with the default to change spaces into underscores. - #However it is not always loaded with that default; this mapping is also used in serialize() without that default. - return cls.__product_to_id_map - @staticmethod def _parseCompatibleValue(value: str): """Parse the value of the "material compatible" property.""" diff --git a/plugins/XmlMaterialProfile/product_to_id.json b/resources/texts/product_to_id.json similarity index 100% rename from plugins/XmlMaterialProfile/product_to_id.json rename to resources/texts/product_to_id.json From 832e59ec8ff8dfc106eb1b37691ae06f891cac53 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Thu, 1 Aug 2024 15:29:06 +0200 Subject: [PATCH 4/7] Add 'inverse' naming maps. done as part of CURA-12005 --- cura/PrinterOutput/FormatMaps.py | 41 +++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/cura/PrinterOutput/FormatMaps.py b/cura/PrinterOutput/FormatMaps.py index 8c785eb2ff..b8ad4df2a2 100644 --- a/cura/PrinterOutput/FormatMaps.py +++ b/cura/PrinterOutput/FormatMaps.py @@ -8,6 +8,7 @@ from typing import Dict, List, Optional class FormatMaps: + # A map from the printer-type in their native file-formats to the internal name we use. PRINTER_TYPE_NAME = { "fire_e": "ultimaker_method", "lava_f": "ultimaker_methodx", @@ -15,6 +16,7 @@ class FormatMaps: "sketch": "ultimaker_sketch" } + # A map from the extruder-name in their native file-formats to the internal name we use. EXTRUDER_NAME_MAP = { "mk14_hot": "1XA", "mk14_hot_s": "2XA", @@ -24,6 +26,7 @@ class FormatMaps: "mk14_e": "LABS" } + # A map from the material-name in their native file-formats to some info, including the internal name we use. MATERIAL_MAP = { "abs": {"name": "ABS", "guid": "2780b345-577b-4a24-a2c5-12e6aad3e690"}, "abs-cf10": {"name": "ABS-CF", "guid": "495a0ce5-9daf-4a16-b7b2-06856d82394d"}, @@ -44,11 +47,47 @@ class FormatMaps: "im-pla": {"name": "Tough", "guid": "de031137-a8ca-4a72-bd1b-17bb964033ad"} } + __inverse_printer_name: Optional[Dict[str, str]] = None + __inverse_extruder_type: Optional[Dict[str, str]] = None + __inverse_material_map: Optional[Dict[str, str]] = None __product_to_id_map: Optional[Dict[str, List[str]]] = None + @classmethod + def getInversePrinterNameMap(cls) -> Dict[str, str]: + """Returns the inverse of the printer name map, that is, from the internal name to the name used in output.""" + if cls.__inverse_printer_name is not None: + return cls.__inverse_printer_name + cls.__inverse_printer_name = {} + for key, value in cls.PRINTER_TYPE_NAME.items(): + cls.__inverse_printer_name[value] = key + return cls.__inverse_printer_name + + @classmethod + def getInverseExtruderTypeMap(cls) -> Dict[str, str]: + """Returns the inverse of the extruder type map, that is, from the internal name to the name used in output.""" + if cls.__inverse_extruder_type is not None: + return cls.__inverse_extruder_type + cls.__inverse_extruder_type = {} + for key, value in cls.EXTRUDER_NAME_MAP.items(): + cls.__inverse_extruder_type[value] = key + return cls.__inverse_extruder_type + + @classmethod + def getInverseMaterialMap(cls) -> Dict[str, str]: + """Returns the inverse of the material map, that is, from the internal name to the name used in output. + + Note that this drops the extra info saved in the non-inverse material map, use that if you need it. + """ + if cls.__inverse_material_map is not None: + return cls.__inverse_material_map + cls.__inverse_material_map = {} + for key, value in cls.MATERIAL_MAP.items(): + cls.__inverse_material_map[value["name"]] = key + return cls.__inverse_material_map + @classmethod def getProductIdMap(cls) -> Dict[str, List[str]]: - """Gets a mapping from product names in the XML files to their definition IDs. + """Gets a mapping from product names (for example, in the XML files) to their definition IDs. This loads the mapping from a file. """ From 9c457b0d20f9023a8539a6235939ade1a642d40e Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Thu, 1 Aug 2024 15:31:04 +0200 Subject: [PATCH 5/7] Map internal name reps to .makerbot-file reps. Not only is this more congruent with the file-format as it exists, the Digital Library handles the metadata in this format, and can do little with it otherwise. last step needed for CURA-12005 --- plugins/MakerbotWriter/MakerbotWriter.py | 26 ++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/plugins/MakerbotWriter/MakerbotWriter.py b/plugins/MakerbotWriter/MakerbotWriter.py index 156a6371eb..34ef562fd3 100644 --- a/plugins/MakerbotWriter/MakerbotWriter.py +++ b/plugins/MakerbotWriter/MakerbotWriter.py @@ -1,4 +1,4 @@ -# Copyright (c) 2023 UltiMaker +# Copyright (c) 2024 UltiMaker # Cura is released under the terms of the LGPLv3 or higher. from io import StringIO, BufferedIOBase import json @@ -18,6 +18,7 @@ from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator from UM.i18n import i18nCatalog from cura.CuraApplication import CuraApplication +from cura.PrinterOutput.FormatMaps import FormatMaps from cura.Snapshot import Snapshot from cura.Utils.Threading import call_on_qt_thread from cura.CuraVersion import ConanInstalls @@ -138,7 +139,28 @@ class MakerbotWriter(MeshWriter): file, data = png_file["file"], png_file["data"] zip_stream.writestr(file, data) api = CuraApplication.getInstance().getCuraAPI() - slice_metadata = json.dumps(api.interface.settings.getSliceMetadata(), separators=(", ", ": "), indent=4) + metadata_json = api.interface.settings.getSliceMetadata() + + # All the mapping stuff we have to do: + product_to_id_map = FormatMaps.getProductIdMap() + printer_name_map = FormatMaps.getInversePrinterNameMap() + extruder_type_map = FormatMaps.getInverseExtruderTypeMap() + material_map = FormatMaps.getInverseMaterialMap() + for key, value in metadata_json.items(): + if "all_settings" in value: + if "machine_name" in value["all_settings"]: + machine_name = value["all_settings"]["machine_name"] + if machine_name in product_to_id_map: + machine_name = product_to_id_map[machine_name][0] + value["all_settings"]["machine_name"] = printer_name_map.get(machine_name, machine_name) + if "machine_nozzle_id" in value["all_settings"]: + extruder_type = value["all_settings"]["machine_nozzle_id"] + value["all_settings"]["machine_nozzle_id"] = extruder_type_map.get(extruder_type, extruder_type) + if "material_type" in value["all_settings"]: + material_type = value["all_settings"]["material_type"] + value["all_settings"]["material_type"] = material_map.get(material_type, material_type) + + slice_metadata = json.dumps(metadata_json, separators=(", ", ": "), indent=4) zip_stream.writestr("slicemetadata.json", slice_metadata) except (IOError, OSError, BadZipFile) as ex: Logger.log("e", f"Could not write to (.makerbot) file because: '{ex}'.") From ad0f6eb21dac16a2e446546238255a48874c57aa Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 5 Aug 2024 12:52:40 +0200 Subject: [PATCH 6/7] Add settings for aux fan control CURA-11849 --- resources/definitions/fdmprinter.def.json | 44 +++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index 045bec05a4..1bc62a814c 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -28,6 +28,18 @@ "icon": "Printer", "children": { + "build_volume_fan_nr": + { + "label": "Build volume fan number", + "description": "The number of the fan that cools the build volume. If this is set to 0, it's means that there is no build volume fan", + "default_value": 0, + "minimum_value": "0", + "maximum_value": "999999", + "type": "int", + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false + }, "machine_name": { "label": "Machine Type", @@ -4467,6 +4479,38 @@ "settable_per_mesh": false, "settable_per_extruder": true }, + + "build_fan_full_at_height": + { + "label": "Build Fan Speed at Height", + "description": "The height at which the fans spin on regular fan speed. At the layers below the fan speed gradually increases from Initial Fan Speed to Regular Fan Speed.", + "unit": "mm", + "type": "float", + "default_value": 0, + "minimum_value": "0", + "enabled": "build_volume_fan_nr != 0", + "maximum_value_warning": "10.0", + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false, + "children": + { + "build_fan_full_layer": + { + "label": "Build Fan Speed at Layer", + "description": "The layer at which the build fans spin on full fan speed. This value is calculated and rounded to a whole number.", + "type": "int", + "default_value": 0, + "minimum_value": "0", + "enabled": "build_volume_fan_nr != 0", + "maximum_value_warning": "10 / resolveOrValue('layer_height')", + "value": "max(1, int(math.floor((build_fan_full_at_height - resolveOrValue('layer_height_0')) / resolveOrValue('layer_height')) + 2))", + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false + } + } + }, "cool_fan_speed": { "label": "Fan Speed", From c1a3b8f073e92c999cdff4d0d0d1c8c6c9b77b55 Mon Sep 17 00:00:00 2001 From: nallath Date: Mon, 5 Aug 2024 10:54:08 +0000 Subject: [PATCH 7/7] Applied printer-linter format --- resources/definitions/fdmprinter.def.json | 1 - 1 file changed, 1 deletion(-) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index 1bc62a814c..3d23d1d040 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -4479,7 +4479,6 @@ "settable_per_mesh": false, "settable_per_extruder": true }, - "build_fan_full_at_height": { "label": "Build Fan Speed at Height",