mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-12-11 16:00:47 -07:00
Merge branch 'feature_local_container_server'
This commit is contained in:
commit
75df653d47
112 changed files with 689 additions and 609 deletions
|
|
@ -117,7 +117,7 @@ class ThreeMFReader(MeshReader):
|
|||
|
||||
# Get the definition & set it
|
||||
definition = QualityManager.getInstance().getParentMachineDefinition(global_container_stack.getBottom())
|
||||
um_node.callDecoration("getStack").getTop().setDefinition(definition)
|
||||
um_node.callDecoration("getStack").getTop().setDefinition(definition.getId())
|
||||
|
||||
setting_container = um_node.callDecoration("getStack").getTop()
|
||||
|
||||
|
|
|
|||
|
|
@ -122,7 +122,6 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
|||
Logger.log("w", "Could not find reader that was able to read the scene data for 3MF workspace")
|
||||
return WorkspaceReader.PreReadResult.failed
|
||||
|
||||
machine_name = ""
|
||||
machine_type = ""
|
||||
variant_type_name = i18n_catalog.i18nc("@label", "Nozzle")
|
||||
|
||||
|
|
@ -133,9 +132,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
|||
# A few lists of containers in this project files.
|
||||
# When loading the global stack file, it may be associated with those containers, which may or may not be
|
||||
# in Cura already, so we need to provide them as alternative search lists.
|
||||
definition_container_list = []
|
||||
instance_container_list = []
|
||||
material_container_list = []
|
||||
|
||||
resolve_strategy_keys = ["machine", "material", "quality_changes"]
|
||||
self._resolve_strategies = {k: None for k in resolve_strategy_keys}
|
||||
|
|
@ -149,21 +146,20 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
|||
definition_container_files = [name for name in cura_file_names if name.endswith(self._definition_container_suffix)]
|
||||
for each_definition_container_file in definition_container_files:
|
||||
container_id = self._stripFileToId(each_definition_container_file)
|
||||
definitions = self._container_registry.findDefinitionContainers(id=container_id)
|
||||
definitions = self._container_registry.findDefinitionContainersMetadata(id = container_id)
|
||||
|
||||
if not definitions:
|
||||
definition_container = DefinitionContainer(container_id)
|
||||
definition_container.deserialize(archive.open(each_definition_container_file).read().decode("utf-8"),
|
||||
file_name = each_definition_container_file)
|
||||
definition_container.deserialize(archive.open(each_definition_container_file).read().decode("utf-8"), file_name = each_definition_container_file)
|
||||
definition_container = definition_container.getMetaData()
|
||||
|
||||
else:
|
||||
definition_container = definitions[0]
|
||||
definition_container_list.append(definition_container)
|
||||
|
||||
definition_container_type = definition_container.getMetaDataEntry("type")
|
||||
definition_container_type = definition_container.get("type")
|
||||
if definition_container_type == "machine":
|
||||
machine_type = definition_container.getName()
|
||||
variant_type_name = definition_container.getMetaDataEntry("variants_name", variant_type_name)
|
||||
machine_type = definition_container["name"]
|
||||
variant_type_name = definition_container.get("variants_name", variant_type_name)
|
||||
|
||||
machine_definition_container_count += 1
|
||||
elif definition_container_type == "extruder":
|
||||
|
|
@ -187,11 +183,10 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
|||
material_container_files = [name for name in cura_file_names if name.endswith(self._material_container_suffix)]
|
||||
for material_container_file in material_container_files:
|
||||
container_id = self._stripFileToId(material_container_file)
|
||||
materials = self._container_registry.findInstanceContainers(id=container_id)
|
||||
material_labels.append(self._getMaterialLabelFromSerialized(archive.open(material_container_file).read().decode("utf-8")))
|
||||
if materials:
|
||||
if self._container_registry.findContainersMetadata(id = container_id): #This material already exists.
|
||||
containers_found_dict["material"] = True
|
||||
if not materials[0].isReadOnly(): # Only non readonly materials can be in conflict
|
||||
if not self._container_registry.isReadOnly(container_id): # Only non readonly materials can be in conflict
|
||||
material_conflict = True
|
||||
Job.yieldThread()
|
||||
|
||||
|
|
@ -462,7 +457,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
|||
extruder_stack_id_map = {} # new and old ExtruderStack IDs map
|
||||
if self._resolve_strategies["machine"] == "new":
|
||||
# We need a new id if the id already exists
|
||||
if self._container_registry.findContainerStacks(id = global_stack_id_original):
|
||||
if self._container_registry.findContainerStacksMetadata(id = global_stack_id_original):
|
||||
global_stack_id_new = self.getNewId(global_stack_id_original)
|
||||
global_stack_need_rename = True
|
||||
|
||||
|
|
@ -471,7 +466,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
|||
for each_extruder_stack_file in extruder_stack_files:
|
||||
old_container_id = self._stripFileToId(each_extruder_stack_file)
|
||||
new_container_id = old_container_id
|
||||
if self._container_registry.findContainerStacks(id = old_container_id):
|
||||
if self._container_registry.findContainerStacksMetadata(id = old_container_id):
|
||||
# get a new name for this extruder
|
||||
new_container_id = self.getNewId(old_container_id)
|
||||
|
||||
|
|
@ -485,7 +480,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
|||
definition_container_files = [name for name in cura_file_names if name.endswith(self._definition_container_suffix)]
|
||||
for definition_container_file in definition_container_files:
|
||||
container_id = self._stripFileToId(definition_container_file)
|
||||
definitions = self._container_registry.findDefinitionContainers(id = container_id)
|
||||
definitions = self._container_registry.findDefinitionContainersMetadata(id = container_id)
|
||||
if not definitions:
|
||||
definition_container = DefinitionContainer(container_id)
|
||||
definition_container.deserialize(archive.open(definition_container_file).read().decode("utf-8"),
|
||||
|
|
@ -512,7 +507,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
|||
containers_to_add.append(material_container)
|
||||
else:
|
||||
material_container = materials[0]
|
||||
if not material_container.isReadOnly(): # Only create new materials if they are not read only.
|
||||
if not self._container_registry.isReadOnly(container_id): # Only create new materials if they are not read only.
|
||||
if self._resolve_strategies["material"] == "override":
|
||||
material_container.deserialize(archive.open(material_container_file).read().decode("utf-8"),
|
||||
file_name = material_container_file)
|
||||
|
|
@ -579,7 +574,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
|||
if old_extruder_id:
|
||||
new_extruder_id = extruder_stack_id_map[old_extruder_id]
|
||||
new_id = new_extruder_id + "_current_settings"
|
||||
instance_container._id = new_id
|
||||
instance_container.setMetaDataEntry("id", new_id)
|
||||
instance_container.setName(new_id)
|
||||
instance_container.setMetaDataEntry("extruder", new_extruder_id)
|
||||
containers_to_add.append(instance_container)
|
||||
|
|
@ -588,7 +583,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
|||
if machine_id:
|
||||
new_machine_id = self.getNewId(machine_id)
|
||||
new_id = new_machine_id + "_current_settings"
|
||||
instance_container._id = new_id
|
||||
instance_container.setMetadataEntry("id", new_id)
|
||||
instance_container.setName(new_id)
|
||||
instance_container.setMetaDataEntry("machine", new_machine_id)
|
||||
containers_to_add.append(instance_container)
|
||||
|
|
@ -644,7 +639,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
|||
machine_extruder_count = definition_changes_extruder_count
|
||||
|
||||
else:
|
||||
existing_container = self._container_registry.findInstanceContainers(id = container_id)
|
||||
existing_container = self._container_registry.findInstanceContainersMetadata(id = container_id)
|
||||
if not existing_container:
|
||||
containers_to_add.append(instance_container)
|
||||
if global_stack_need_rename:
|
||||
|
|
@ -670,14 +665,13 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
|||
|
||||
# HACK
|
||||
# There is a machine, check if it has authentication data. If so, keep that data.
|
||||
network_authentication_id = container_stacks[0].getMetaDataEntry("network_authentication_id")
|
||||
network_authentication_key = container_stacks[0].getMetaDataEntry("network_authentication_key")
|
||||
container_stacks[0].deserialize(archive.open(global_stack_file).read().decode("utf-8"),
|
||||
file_name = global_stack_file)
|
||||
network_authentication_id = stack.getMetaDataEntry("network_authentication_id")
|
||||
network_authentication_key = stack.getMetaDataEntry("network_authentication_key")
|
||||
stack.deserialize(archive.open(global_stack_file).read().decode("utf-8"), file_name = global_stack_file)
|
||||
if network_authentication_id:
|
||||
container_stacks[0].addMetaDataEntry("network_authentication_id", network_authentication_id)
|
||||
stack.addMetaDataEntry("network_authentication_id", network_authentication_id)
|
||||
if network_authentication_key:
|
||||
container_stacks[0].addMetaDataEntry("network_authentication_key", network_authentication_key)
|
||||
stack.addMetaDataEntry("network_authentication_key", network_authentication_key)
|
||||
|
||||
elif self._resolve_strategies["machine"] == "new":
|
||||
# create a new global stack
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ class ThreeMFWorkspaceWriter(WorkspaceWriter):
|
|||
file_in_archive.compress_type = zipfile.ZIP_DEFLATED
|
||||
|
||||
# Do not include the network authentication keys
|
||||
ignore_keys = ["network_authentication_id", "network_authentication_key"]
|
||||
ignore_keys = {"network_authentication_id", "network_authentication_key"}
|
||||
serialized_data = container.serialize(ignored_metadata_keys = ignore_keys)
|
||||
|
||||
archive.writestr(file_in_archive, serialized_data)
|
||||
|
|
|
|||
|
|
@ -74,12 +74,13 @@ class GCodeWriter(MeshWriter):
|
|||
## Create a new container with container 2 as base and container 1 written over it.
|
||||
def _createFlattenedContainerInstance(self, instance_container1, instance_container2):
|
||||
flat_container = InstanceContainer(instance_container2.getName())
|
||||
if instance_container1.getDefinition():
|
||||
flat_container.setDefinition(instance_container1.getDefinition())
|
||||
else:
|
||||
flat_container.setDefinition(instance_container2.getDefinition())
|
||||
|
||||
# The metadata includes id, name and definition
|
||||
flat_container.setMetaData(copy.deepcopy(instance_container2.getMetaData()))
|
||||
|
||||
if instance_container1.getDefinition():
|
||||
flat_container.setDefinition(instance_container1.getDefinition().getId())
|
||||
|
||||
for key in instance_container2.getAllKeys():
|
||||
flat_container.setProperty(key, "value", instance_container2.getProperty(key, "value"))
|
||||
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@ class LegacyProfileReader(ProfileReader):
|
|||
Logger.log("e", "Dictionary of Doom has no translation. Is it the correct JSON file?")
|
||||
return None
|
||||
current_printer_definition = global_container_stack.getBottom()
|
||||
profile.setDefinition(current_printer_definition)
|
||||
profile.setDefinition(current_printer_definition.getId())
|
||||
for new_setting in dict_of_doom["translation"]: # Evaluate all new settings that would get a value from the translations.
|
||||
old_setting_expression = dict_of_doom["translation"][new_setting]
|
||||
compiled = compile(old_setting_expression, new_setting, "eval")
|
||||
|
|
|
|||
|
|
@ -726,10 +726,10 @@ class NetworkPrinterOutputDevice(PrinterOutputDevice):
|
|||
remote_material_guid,
|
||||
material.getMetaDataEntry("GUID"))
|
||||
|
||||
remote_materials = UM.Settings.ContainerRegistry.ContainerRegistry.getInstance().findInstanceContainers(type = "material", GUID = remote_material_guid, read_only = True)
|
||||
remote_materials = UM.Settings.ContainerRegistry.ContainerRegistry.getInstance().findInstanceContainersMetadata(type = "material", GUID = remote_material_guid, read_only = True)
|
||||
remote_material_name = "Unknown"
|
||||
if remote_materials:
|
||||
remote_material_name = remote_materials[0].getName()
|
||||
remote_material_name = remote_materials[0]["name"]
|
||||
warnings.append(i18n_catalog.i18nc("@label", "Different material (Cura: {0}, Printer: {1}) selected for extruder {2}").format(material.getName(), remote_material_name, index + 1))
|
||||
|
||||
try:
|
||||
|
|
@ -971,7 +971,8 @@ class NetworkPrinterOutputDevice(PrinterOutputDevice):
|
|||
|
||||
## Send all material profiles to the printer.
|
||||
def sendMaterialProfiles(self):
|
||||
for container in UM.Settings.ContainerRegistry.ContainerRegistry.getInstance().findInstanceContainers(type = "material"):
|
||||
registry = UM.Settings.ContainerRegistry.ContainerRegistry.getInstance()
|
||||
for container in registry.findInstanceContainers(type = "material"):
|
||||
try:
|
||||
xml_data = container.serialize()
|
||||
if xml_data == "" or xml_data is None:
|
||||
|
|
@ -980,7 +981,7 @@ class NetworkPrinterOutputDevice(PrinterOutputDevice):
|
|||
names = ContainerManager.getInstance().getLinkedMaterials(container.getId())
|
||||
if names:
|
||||
# There are other materials that share this GUID.
|
||||
if not container.isReadOnly():
|
||||
if not registry.isReadOnly(container.getId()):
|
||||
continue # If it's not readonly, it's created by user, so skip it.
|
||||
|
||||
material_multi_part = QHttpMultiPart(QHttpMultiPart.FormDataType)
|
||||
|
|
|
|||
|
|
@ -3,7 +3,10 @@
|
|||
|
||||
import copy
|
||||
import io
|
||||
from typing import List, Optional
|
||||
import json #To parse the product-to-id mapping file.
|
||||
import os.path #To find the product-to-id mapping.
|
||||
import sys
|
||||
from typing import Any, Dict, List, Optional
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
from UM.Resources import Resources
|
||||
|
|
@ -14,7 +17,6 @@ import UM.Dictionary
|
|||
from UM.Settings.InstanceContainer import InstanceContainer
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
|
||||
|
||||
## Handles serializing and deserializing material containers from an XML file
|
||||
class XmlMaterialProfile(InstanceContainer):
|
||||
CurrentFdmMaterialVersion = "1.3"
|
||||
|
|
@ -42,25 +44,18 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
def getInheritedFiles(self):
|
||||
return self._inherited_files
|
||||
|
||||
## Overridden from InstanceContainer
|
||||
def setReadOnly(self, read_only):
|
||||
super().setReadOnly(read_only)
|
||||
|
||||
basefile = self.getMetaDataEntry("base_file", self._id) # if basefile is self.id, this is a basefile.
|
||||
for container in ContainerRegistry.getInstance().findInstanceContainers(base_file = basefile):
|
||||
container._read_only = read_only # prevent loop instead of calling setReadOnly
|
||||
|
||||
## Overridden from InstanceContainer
|
||||
# set the meta data for all machine / variant combinations
|
||||
def setMetaDataEntry(self, key, value):
|
||||
if self.isReadOnly():
|
||||
registry = ContainerRegistry.getInstance()
|
||||
if registry.isReadOnly(self.getId()):
|
||||
return
|
||||
|
||||
super().setMetaDataEntry(key, value)
|
||||
|
||||
basefile = self.getMetaDataEntry("base_file", self._id) #if basefile is self.id, this is a basefile.
|
||||
basefile = self.getMetaDataEntry("base_file", self.getId()) #if basefile is self.getId, this is a basefile.
|
||||
# Update all containers that share basefile
|
||||
for container in ContainerRegistry.getInstance().findInstanceContainers(base_file = basefile):
|
||||
for container in registry.findInstanceContainers(base_file = basefile):
|
||||
if container.getMetaDataEntry(key, None) != value: # Prevent recursion
|
||||
container.setMetaDataEntry(key, value)
|
||||
|
||||
|
|
@ -68,7 +63,8 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
# without this function the setName would only set the name of the specific nozzle / material / machine combination container
|
||||
# The function is a bit tricky. It will not set the name of all containers if it has the correct name itself.
|
||||
def setName(self, new_name):
|
||||
if self.isReadOnly():
|
||||
registry = ContainerRegistry.getInstance()
|
||||
if registry.isReadOnly(self.getId()):
|
||||
return
|
||||
|
||||
# Not only is this faster, it also prevents a major loop that causes a stack overflow.
|
||||
|
|
@ -77,10 +73,10 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
|
||||
super().setName(new_name)
|
||||
|
||||
basefile = self.getMetaDataEntry("base_file", self._id) # if basefile is self.id, this is a basefile.
|
||||
basefile = self.getMetaDataEntry("base_file", self.getId()) # if basefile is self.getId, this is a basefile.
|
||||
# Update the basefile as well, this is actually what we're trying to do
|
||||
# Update all containers that share GUID and basefile
|
||||
containers = ContainerRegistry.getInstance().findInstanceContainers(base_file = basefile)
|
||||
containers = registry.findInstanceContainers(base_file = basefile)
|
||||
for container in containers:
|
||||
container.setName(new_name)
|
||||
|
||||
|
|
@ -88,33 +84,20 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
def setDirty(self, dirty):
|
||||
super().setDirty(dirty)
|
||||
base_file = self.getMetaDataEntry("base_file", None)
|
||||
if base_file is not None and base_file != self._id:
|
||||
containers = ContainerRegistry.getInstance().findContainers(id=base_file)
|
||||
registry = ContainerRegistry.getInstance()
|
||||
if base_file is not None and base_file != self.getId() and not registry.isReadOnly(base_file):
|
||||
containers = registry.findContainers(id = base_file)
|
||||
if containers:
|
||||
base_container = containers[0]
|
||||
if not base_container.isReadOnly():
|
||||
base_container.setDirty(dirty)
|
||||
|
||||
## Overridden from InstanceContainer
|
||||
# def setProperty(self, key, property_name, property_value, container = None):
|
||||
# if self.isReadOnly():
|
||||
# return
|
||||
#
|
||||
# super().setProperty(key, property_name, property_value)
|
||||
#
|
||||
# basefile = self.getMetaDataEntry("base_file", self._id) #if basefile is self.id, this is a basefile.
|
||||
# for container in UM.Settings.ContainerRegistry.ContainerRegistry.getInstance().findInstanceContainers(base_file = basefile):
|
||||
# if not container.isReadOnly():
|
||||
# container.setDirty(True)
|
||||
containers[0].setDirty(dirty)
|
||||
|
||||
## Overridden from InstanceContainer
|
||||
# base file: common settings + supported machines
|
||||
# machine / variant combination: only changes for itself.
|
||||
def serialize(self, ignored_metadata_keys: Optional[List] = None):
|
||||
def serialize(self, ignored_metadata_keys: Optional[set] = None):
|
||||
registry = ContainerRegistry.getInstance()
|
||||
|
||||
base_file = self.getMetaDataEntry("base_file", "")
|
||||
if base_file and self.id != base_file:
|
||||
if base_file and self.getId() != base_file:
|
||||
# Since we create an instance of XmlMaterialProfile for each machine and nozzle in the profile,
|
||||
# we should only serialize the "base" material definition, since that can then take care of
|
||||
# serializing the machine/nozzle specific profiles.
|
||||
|
|
@ -132,8 +115,8 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
metadata = copy.deepcopy(self.getMetaData())
|
||||
# setting_version is derived from the "version" tag in the schema, so don't serialize it into a file
|
||||
if ignored_metadata_keys is None:
|
||||
ignored_metadata_keys = []
|
||||
ignored_metadata_keys = ignored_metadata_keys + ["setting_version"]
|
||||
ignored_metadata_keys = set()
|
||||
ignored_metadata_keys |= {"setting_version"}
|
||||
# remove the keys that we want to ignore in the metadata
|
||||
for key in ignored_metadata_keys:
|
||||
if key in metadata:
|
||||
|
|
@ -146,6 +129,9 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
metadata.pop("type", "")
|
||||
metadata.pop("base_file", "")
|
||||
metadata.pop("approximate_diameter", "")
|
||||
metadata.pop("id", "")
|
||||
metadata.pop("container_type", "")
|
||||
metadata.pop("name", "")
|
||||
|
||||
## Begin Name Block
|
||||
builder.start("name")
|
||||
|
|
@ -163,7 +149,7 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
builder.end("color")
|
||||
|
||||
builder.start("label")
|
||||
builder.data(self._name)
|
||||
builder.data(self.getName())
|
||||
builder.end("label")
|
||||
|
||||
builder.end("name")
|
||||
|
|
@ -195,16 +181,16 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
## Begin Settings Block
|
||||
builder.start("settings")
|
||||
|
||||
if self.getDefinition().id == "fdmprinter":
|
||||
if self.getDefinition().getId() == "fdmprinter":
|
||||
for instance in self.findInstances():
|
||||
self._addSettingElement(builder, instance)
|
||||
|
||||
machine_container_map = {}
|
||||
machine_nozzle_map = {}
|
||||
|
||||
all_containers = registry.findInstanceContainers(GUID = self.getMetaDataEntry("GUID"), base_file = self._id)
|
||||
all_containers = registry.findInstanceContainers(GUID = self.getMetaDataEntry("GUID"), base_file = self.getId())
|
||||
for container in all_containers:
|
||||
definition_id = container.getDefinition().id
|
||||
definition_id = container.getDefinition().getId()
|
||||
if definition_id == "fdmprinter":
|
||||
continue
|
||||
|
||||
|
|
@ -222,9 +208,7 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
machine_container_map[definition_id] = container
|
||||
|
||||
# Map machine human-readable names to IDs
|
||||
product_id_map = {}
|
||||
for container in registry.findDefinitionContainers(type = "machine"):
|
||||
product_id_map[container.getName()] = container.getId()
|
||||
product_id_map = self.getProductIdMap()
|
||||
|
||||
for definition_id, container in machine_container_map.items():
|
||||
definition = container.getDefinition()
|
||||
|
|
@ -242,7 +226,7 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
builder.end("machine_identifier")
|
||||
|
||||
for instance in container.findInstances():
|
||||
if self.getDefinition().id == "fdmprinter" and self.getInstance(instance.definition.key) and self.getProperty(instance.definition.key, "value") == instance.value:
|
||||
if self.getDefinition().getId() == "fdmprinter" and self.getInstance(instance.definition.key) and self.getProperty(instance.definition.key, "value") == instance.value:
|
||||
# If the settings match that of the base profile, just skip since we inherit the base profile.
|
||||
continue
|
||||
|
||||
|
|
@ -250,11 +234,12 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
|
||||
# Find all hotend sub-profiles corresponding to this material and machine and add them to this profile.
|
||||
for hotend_id, hotend in machine_nozzle_map[definition_id].items():
|
||||
variant_containers = registry.findInstanceContainers(id = hotend.getMetaDataEntry("variant"))
|
||||
variant_containers = registry.findInstanceContainersMetadata(id = hotend.getMetaDataEntry("variant"))
|
||||
if not variant_containers:
|
||||
continue
|
||||
|
||||
builder.start("hotend", {"id": variant_containers[0].getName()})
|
||||
# The hotend identifier is not the containers name, but its "name".
|
||||
builder.start("hotend", {"id": variant_containers[0]["name"]})
|
||||
|
||||
# Compatible is a special case, as it's added as a meta data entry (instead of an instance).
|
||||
compatible = hotend.getMetaDataEntry("compatible")
|
||||
|
|
@ -397,15 +382,18 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
first.append(element)
|
||||
|
||||
def clearData(self):
|
||||
self._metadata = {}
|
||||
self._name = ""
|
||||
self._metadata = {
|
||||
"id": self.getId(),
|
||||
"name": ""
|
||||
}
|
||||
self._definition = None
|
||||
self._instances = {}
|
||||
self._read_only = False
|
||||
self._dirty = False
|
||||
self._path = ""
|
||||
|
||||
def getConfigurationTypeFromSerialized(self, serialized: str) -> Optional[str]:
|
||||
@classmethod
|
||||
def getConfigurationTypeFromSerialized(cls, serialized: str) -> Optional[str]:
|
||||
return "materials"
|
||||
|
||||
@classmethod
|
||||
|
|
@ -415,9 +403,9 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
version = XmlMaterialProfile.Version
|
||||
# get setting version
|
||||
if "version" in data.attrib:
|
||||
setting_version = XmlMaterialProfile.xmlVersionToSettingVersion(data.attrib["version"])
|
||||
setting_version = cls.xmlVersionToSettingVersion(data.attrib["version"])
|
||||
else:
|
||||
setting_version = XmlMaterialProfile.xmlVersionToSettingVersion("1.2")
|
||||
setting_version = cls.xmlVersionToSettingVersion("1.2")
|
||||
|
||||
return version * 1000000 + setting_version
|
||||
|
||||
|
|
@ -431,15 +419,18 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
try:
|
||||
data = ET.fromstring(serialized)
|
||||
except:
|
||||
Logger.logException("e", "An exception occured while parsing the material profile")
|
||||
Logger.logException("e", "An exception occurred while parsing the material profile")
|
||||
return
|
||||
|
||||
# Reset previous metadata
|
||||
old_id = self.getId()
|
||||
self.clearData() # Ensure any previous data is gone.
|
||||
meta_data = {}
|
||||
meta_data["type"] = "material"
|
||||
meta_data["base_file"] = self.id
|
||||
meta_data["base_file"] = self.getId()
|
||||
meta_data["status"] = "unknown" # TODO: Add material verification
|
||||
meta_data["id"] = old_id
|
||||
meta_data["container_type"] = XmlMaterialProfile
|
||||
|
||||
common_setting_values = {}
|
||||
|
||||
|
|
@ -454,8 +445,8 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
else:
|
||||
meta_data["setting_version"] = self.xmlVersionToSettingVersion("1.2") #1.2 and lower didn't have that version number there yet.
|
||||
|
||||
metadata = data.iterfind("./um:metadata/*", self.__namespaces)
|
||||
for entry in metadata:
|
||||
meta_data["name"] = "Unknown Material" #In case the name tag is missing.
|
||||
for entry in data.iterfind("./um:metadata/*", self.__namespaces):
|
||||
tag_name = _tag_without_namespace(entry)
|
||||
|
||||
if tag_name == "name":
|
||||
|
|
@ -465,9 +456,9 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
label = entry.find("./um:label", self.__namespaces)
|
||||
|
||||
if label is not None:
|
||||
self._name = label.text
|
||||
meta_data["name"] = label.text
|
||||
else:
|
||||
self._name = self._profile_name(material.text, color.text)
|
||||
meta_data["name"] = self._profile_name(material.text, color.text)
|
||||
meta_data["brand"] = brand.text
|
||||
meta_data["material"] = material.text
|
||||
meta_data["color_name"] = color.text
|
||||
|
|
@ -499,8 +490,7 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
|
||||
meta_data["approximate_diameter"] = str(round(float(property_values.get("diameter", 2.85)))) # In mm
|
||||
meta_data["properties"] = property_values
|
||||
|
||||
self.setDefinition(ContainerRegistry.getInstance().findDefinitionContainers(id = "fdmprinter")[0])
|
||||
meta_data["definition"] = "fdmprinter"
|
||||
|
||||
common_compatibility = True
|
||||
settings = data.iterfind("./um:settings/um:setting", self.__namespaces)
|
||||
|
|
@ -518,9 +508,7 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
self._dirty = False
|
||||
|
||||
# Map machine human-readable names to IDs
|
||||
product_id_map = {}
|
||||
for container in ContainerRegistry.getInstance().findDefinitionContainers(type = "machine"):
|
||||
product_id_map[container.getName()] = container.getId()
|
||||
product_id_map = self.getProductIdMap()
|
||||
|
||||
machines = data.iterfind("./um:settings/um:machine", self.__namespaces)
|
||||
for machine in machines:
|
||||
|
|
@ -547,37 +535,37 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
# Lets try again with some naive heuristics.
|
||||
machine_id = identifier.get("product").replace(" ", "").lower()
|
||||
|
||||
definitions = ContainerRegistry.getInstance().findDefinitionContainers(id = machine_id)
|
||||
definitions = ContainerRegistry.getInstance().findDefinitionContainersMetadata(id = machine_id)
|
||||
if not definitions:
|
||||
# Logger.log("w", "No definition found for machine ID %s", machine_id)
|
||||
Logger.log("w", "No definition found for machine ID %s", machine_id)
|
||||
continue
|
||||
|
||||
definition = definitions[0]
|
||||
|
||||
machine_manufacturer = identifier.get("manufacturer", definition.getMetaDataEntry("manufacturer", "Unknown")) #If the XML material doesn't specify a manufacturer, use the one in the actual printer definition.
|
||||
machine_manufacturer = identifier.get("manufacturer", definition.get("manufacturer", "Unknown")) #If the XML material doesn't specify a manufacturer, use the one in the actual printer definition.
|
||||
|
||||
if machine_compatibility:
|
||||
new_material_id = self.id + "_" + machine_id
|
||||
new_material_id = self.getId() + "_" + machine_id
|
||||
|
||||
# The child or derived material container may already exist. This can happen when a material in a
|
||||
# project file and the a material in Cura have the same ID.
|
||||
# In the case if a derived material already exists, override that material container because if
|
||||
# the data in the parent material has been changed, the derived ones should be updated too.
|
||||
found_materials = ContainerRegistry.getInstance().findInstanceContainers(id = new_material_id)
|
||||
is_new_material = False
|
||||
if found_materials:
|
||||
new_material = found_materials[0]
|
||||
if ContainerRegistry.getInstance().isLoaded(new_material_id):
|
||||
new_material = ContainerRegistry.getInstance().findContainers(id = new_material_id)[0]
|
||||
is_new_material = False
|
||||
else:
|
||||
new_material = XmlMaterialProfile(new_material_id)
|
||||
is_new_material = True
|
||||
|
||||
# Update the private directly, as we want to prevent the lookup that is done when using setName
|
||||
new_material._name = self.getName()
|
||||
new_material.setMetaData(copy.deepcopy(self.getMetaData()))
|
||||
new_material.setDefinition(definition)
|
||||
new_material.getMetaData()["id"] = new_material_id
|
||||
new_material.getMetaData()["name"] = self.getName()
|
||||
new_material.setDefinition(machine_id)
|
||||
# Don't use setMetadata, as that overrides it for all materials with same base file
|
||||
new_material.getMetaData()["compatible"] = machine_compatibility
|
||||
new_material.getMetaData()["machine_manufacturer"] = machine_manufacturer
|
||||
new_material.getMetaData()["definition"] = machine_id
|
||||
|
||||
new_material.setCachedValues(cached_machine_setting_properties)
|
||||
|
||||
|
|
@ -592,13 +580,12 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
if hotend_id is None:
|
||||
continue
|
||||
|
||||
variant_containers = ContainerRegistry.getInstance().findInstanceContainers(id = hotend_id)
|
||||
variant_containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(id = hotend_id)
|
||||
if not variant_containers:
|
||||
# It is not really properly defined what "ID" is so also search for variants by name.
|
||||
variant_containers = ContainerRegistry.getInstance().findInstanceContainers(definition = definition.id, name = hotend_id)
|
||||
variant_containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(definition = definition["id"], name = hotend_id)
|
||||
|
||||
if not variant_containers:
|
||||
#Logger.log("d", "No variants found with ID or name %s for machine %s", hotend_id, definition.id)
|
||||
continue
|
||||
|
||||
hotend_compatibility = machine_compatibility
|
||||
|
|
@ -614,26 +601,25 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
else:
|
||||
Logger.log("d", "Unsupported material setting %s", key)
|
||||
|
||||
new_hotend_id = self.id + "_" + machine_id + "_" + hotend_id.replace(" ", "_")
|
||||
new_hotend_id = self.getId() + "_" + machine_id + "_" + hotend_id.replace(" ", "_")
|
||||
|
||||
# Same as machine compatibility, keep the derived material containers consistent with the parent
|
||||
# material
|
||||
found_materials = ContainerRegistry.getInstance().findInstanceContainers(id = new_hotend_id)
|
||||
is_new_material = False
|
||||
if found_materials:
|
||||
new_hotend_material = found_materials[0]
|
||||
if ContainerRegistry.getInstance().isLoaded(new_hotend_id):
|
||||
new_hotend_material = ContainerRegistry.getInstance().findContainers(id = new_hotend_id)[0]
|
||||
is_new_material = False
|
||||
else:
|
||||
new_hotend_material = XmlMaterialProfile(new_hotend_id)
|
||||
is_new_material = True
|
||||
|
||||
# Update the private directly, as we want to prevent the lookup that is done when using setName
|
||||
new_hotend_material._name = self.getName()
|
||||
new_hotend_material.setMetaData(copy.deepcopy(self.getMetaData()))
|
||||
new_hotend_material.setDefinition(definition)
|
||||
new_hotend_material.addMetaDataEntry("variant", variant_containers[0].id)
|
||||
new_hotend_material.getMetaData()["id"] = new_hotend_id
|
||||
new_hotend_material.getMetaData()["name"] = self.getName()
|
||||
new_hotend_material.getMetaData()["variant"] = variant_containers[0]["id"]
|
||||
# Don't use setMetadata, as that overrides it for all materials with same base file
|
||||
new_hotend_material.getMetaData()["compatible"] = hotend_compatibility
|
||||
new_hotend_material.getMetaData()["machine_manufacturer"] = machine_manufacturer
|
||||
new_hotend_material.getMetaData()["definition"] = machine_id
|
||||
|
||||
cached_hotend_setting_properties = cached_machine_setting_properties.copy()
|
||||
cached_hotend_setting_properties.update(hotend_setting_values)
|
||||
|
|
@ -648,6 +634,168 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
for container_to_add in containers_to_add:
|
||||
ContainerRegistry.getInstance().addContainer(container_to_add)
|
||||
|
||||
@classmethod
|
||||
def deserializeMetadata(cls, serialized: str, container_id: str) -> List[Dict[str, Any]]:
|
||||
result_metadata = [] #All the metadata that we found except the base (because the base is returned).
|
||||
|
||||
#Update the serialized data to the latest version.
|
||||
serialized = cls._updateSerialized(serialized)
|
||||
|
||||
base_metadata = {
|
||||
"type": "material",
|
||||
"status": "unknown", #TODO: Add material verification.
|
||||
"container_type": XmlMaterialProfile,
|
||||
"id": container_id,
|
||||
"base_file": container_id
|
||||
}
|
||||
|
||||
try:
|
||||
data = ET.fromstring(serialized)
|
||||
except:
|
||||
Logger.logException("e", "An exception occurred while parsing the material profile")
|
||||
return []
|
||||
|
||||
#TODO: Implement the <inherits> tag. It's unused at the moment though.
|
||||
|
||||
if "version" in data.attrib:
|
||||
base_metadata["setting_version"] = cls.xmlVersionToSettingVersion(data.attrib["version"])
|
||||
else:
|
||||
base_metadata["setting_version"] = cls.xmlVersionToSettingVersion("1.2") #1.2 and lower didn't have that version number there yet.
|
||||
|
||||
for entry in data.iterfind("./um:metadata/*", cls.__namespaces):
|
||||
tag_name = _tag_without_namespace(entry)
|
||||
|
||||
if tag_name == "name":
|
||||
brand = entry.find("./um:brand", cls.__namespaces)
|
||||
material = entry.find("./um:material", cls.__namespaces)
|
||||
color = entry.find("./um:color", cls.__namespaces)
|
||||
label = entry.find("./um:label", cls.__namespaces)
|
||||
|
||||
if label is not None:
|
||||
base_metadata["name"] = label.text
|
||||
else:
|
||||
base_metadata["name"] = cls._profile_name(material.text, color.text)
|
||||
base_metadata["brand"] = brand.text
|
||||
base_metadata["material"] = material.text
|
||||
base_metadata["color_name"] = color.text
|
||||
continue
|
||||
|
||||
#Setting_version is derived from the "version" tag in the schema earlier, so don't set it here.
|
||||
if tag_name == "setting_version":
|
||||
continue
|
||||
|
||||
base_metadata[tag_name] = entry.text
|
||||
|
||||
if "description" not in base_metadata:
|
||||
base_metadata["description"] = ""
|
||||
if "adhesion_info" not in base_metadata:
|
||||
base_metadata["adhesion_info"] = ""
|
||||
|
||||
property_values = {}
|
||||
properties = data.iterfind("./um:properties/*", cls.__namespaces)
|
||||
for entry in properties:
|
||||
tag_name = _tag_without_namespace(entry)
|
||||
property_values[tag_name] = entry.text
|
||||
|
||||
base_metadata["approximate_diameter"] = str(round(float(property_values.get("diameter", 2.85)))) # In mm
|
||||
base_metadata["properties"] = property_values
|
||||
base_metadata["definition"] = "fdmprinter"
|
||||
|
||||
compatible_entries = data.iterfind("./um:settings/um:setting[@key='hardware compatible']", cls.__namespaces)
|
||||
try:
|
||||
common_compatibility = cls._parseCompatibleValue(next(compatible_entries).text)
|
||||
except StopIteration: #No 'hardware compatible' setting.
|
||||
common_compatibility = True
|
||||
base_metadata["compatible"] = common_compatibility
|
||||
result_metadata.append(base_metadata)
|
||||
|
||||
# Map machine human-readable names to IDs
|
||||
product_id_map = cls.getProductIdMap()
|
||||
|
||||
for machine in data.iterfind("./um:settings/um:machine", cls.__namespaces):
|
||||
machine_compatibility = common_compatibility
|
||||
for entry in machine.iterfind("./um:setting", cls.__namespaces):
|
||||
key = entry.get("key")
|
||||
if key == "hardware compatible":
|
||||
machine_compatibility = cls._parseCompatibleValue(entry.text)
|
||||
|
||||
for identifier in machine.iterfind("./um:machine_identifier", cls.__namespaces):
|
||||
machine_id = product_id_map.get(identifier.get("product"), None)
|
||||
if machine_id is None:
|
||||
# Lets try again with some naive heuristics.
|
||||
machine_id = identifier.get("product").replace(" ", "").lower()
|
||||
definition_metadata = ContainerRegistry.getInstance().findDefinitionContainersMetadata(id = machine_id)
|
||||
if not definition_metadata:
|
||||
Logger.log("w", "No definition found for machine ID %s", machine_id)
|
||||
continue
|
||||
definition_metadata = definition_metadata[0]
|
||||
|
||||
machine_manufacturer = identifier.get("manufacturer", definition_metadata.get("manufacturer", "Unknown")) #If the XML material doesn't specify a manufacturer, use the one in the actual printer definition.
|
||||
|
||||
if machine_compatibility:
|
||||
new_material_id = container_id + "_" + machine_id
|
||||
|
||||
# The child or derived material container may already exist. This can happen when a material in a
|
||||
# project file and the a material in Cura have the same ID.
|
||||
# In the case if a derived material already exists, override that material container because if
|
||||
# the data in the parent material has been changed, the derived ones should be updated too.
|
||||
found_materials = ContainerRegistry.getInstance().findInstanceContainersMetadata(id = new_material_id)
|
||||
if found_materials:
|
||||
new_material_metadata = found_materials[0]
|
||||
else:
|
||||
new_material_metadata = {}
|
||||
|
||||
new_material_metadata.update(base_metadata)
|
||||
new_material_metadata["id"] = new_material_id
|
||||
new_material_metadata["compatible"] = machine_compatibility
|
||||
new_material_metadata["machine_manufacturer"] = machine_manufacturer
|
||||
new_material_metadata["definition"] = machine_id
|
||||
|
||||
if len(found_materials) == 0: #This is a new material.
|
||||
result_metadata.append(new_material_metadata)
|
||||
|
||||
for hotend in machine.iterfind("./um:hotend", cls.__namespaces):
|
||||
hotend_id = hotend.get("id")
|
||||
if hotend_id is None:
|
||||
continue
|
||||
|
||||
variant_containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(id = hotend_id)
|
||||
if not variant_containers:
|
||||
# It is not really properly defined what "ID" is so also search for variants by name.
|
||||
variant_containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(definition = machine_id, name = hotend_id)
|
||||
|
||||
hotend_compatibility = machine_compatibility
|
||||
for entry in hotend.iterfind("./um:setting", cls.__namespaces):
|
||||
key = entry.get("key")
|
||||
if key == "hardware compatible":
|
||||
hotend_compatibility = cls._parseCompatibleValue(entry.text)
|
||||
|
||||
new_hotend_id = container_id + "_" + machine_id + "_" + hotend_id.replace(" ", "_")
|
||||
|
||||
# Same as machine compatibility, keep the derived material containers consistent with the parent
|
||||
# material
|
||||
found_materials = ContainerRegistry.getInstance().findInstanceContainersMetadata(id = new_hotend_id)
|
||||
if found_materials:
|
||||
new_hotend_material_metadata = found_materials[0]
|
||||
else:
|
||||
new_hotend_material_metadata = {}
|
||||
|
||||
new_hotend_material_metadata.update(base_metadata)
|
||||
if variant_containers:
|
||||
new_hotend_material_metadata["variant"] = variant_containers[0]["id"]
|
||||
else:
|
||||
new_hotend_material_metadata["variant"] = hotend_id
|
||||
_with_missing_variants.append(new_hotend_material_metadata)
|
||||
new_hotend_material_metadata["compatible"] = hotend_compatibility
|
||||
new_hotend_material_metadata["machine_manufacturer"] = machine_manufacturer
|
||||
new_hotend_material_metadata["id"] = new_hotend_id
|
||||
new_hotend_material_metadata["definition"] = machine_id
|
||||
|
||||
if len(found_materials) == 0:
|
||||
result_metadata.append(new_hotend_material_metadata)
|
||||
|
||||
return result_metadata
|
||||
|
||||
def _addSettingElement(self, builder, instance):
|
||||
try:
|
||||
key = UM.Dictionary.findKey(self.__material_settings_setting_map, instance.definition.key)
|
||||
|
|
@ -658,16 +806,32 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
builder.data(str(instance.value))
|
||||
builder.end("setting")
|
||||
|
||||
def _profile_name(self, material_name, color_name):
|
||||
@classmethod
|
||||
def _profile_name(cls, material_name, color_name):
|
||||
if color_name != "Generic":
|
||||
return "%s %s" % (color_name, material_name)
|
||||
else:
|
||||
return material_name
|
||||
|
||||
## Gets a mapping from product names in the XML files to their definition
|
||||
# IDs.
|
||||
#
|
||||
# This loads the mapping from a file.
|
||||
@classmethod
|
||||
def getProductIdMap(cls) -> Dict[str, str]:
|
||||
product_to_id_file = os.path.join(os.path.dirname(sys.modules[cls.__module__].__file__), "product_to_id.json")
|
||||
with open(product_to_id_file) as f:
|
||||
return json.load(f)
|
||||
|
||||
## Parse the value of the "material compatible" property.
|
||||
def _parseCompatibleValue(self, value: str):
|
||||
@classmethod
|
||||
def _parseCompatibleValue(cls, value: str):
|
||||
return value in {"yes", "unknown"}
|
||||
|
||||
## Small string representation for debugging.
|
||||
def __str__(self):
|
||||
return "<XmlMaterialProfile '{my_id}' ('{name}') from base file '{base_file}'>".format(my_id = self.getId(), name = self.getName(), base_file = self.getMetaDataEntry("base_file"))
|
||||
|
||||
# Map XML file setting names to internal names
|
||||
__material_settings_setting_map = {
|
||||
"print temperature": "default_material_print_temperature",
|
||||
|
|
@ -716,4 +880,22 @@ def _indent(elem, level = 0):
|
|||
# We are only interested in the actual tag name, so discard everything
|
||||
# before the last }
|
||||
def _tag_without_namespace(element):
|
||||
return element.tag[element.tag.rfind("}") + 1:]
|
||||
return element.tag[element.tag.rfind("}") + 1:]
|
||||
|
||||
#While loading XML profiles, some of these profiles don't know what variant
|
||||
#they belong to. We'd like to search by the machine ID and the variant's
|
||||
#name, but we don't know the variant's ID. Not all variants have been loaded
|
||||
#yet so we can't run a filter on the name and machine. The ID is unknown
|
||||
#so we can't lazily load the variant either. So we have to wait until all
|
||||
#the rest is loaded properly and then assign the correct variant to the
|
||||
#material files that were missing it.
|
||||
_with_missing_variants = []
|
||||
def _fillMissingVariants():
|
||||
registry = ContainerRegistry.getInstance()
|
||||
for variant_metadata in _with_missing_variants:
|
||||
variants = registry.findContainersMetadata(definition = variant_metadata["definition"], name = variant_metadata["variant"])
|
||||
if not variants:
|
||||
Logger.log("w", "Could not find variant for variant-specific material {material_id}.".format(material_id = variant_metadata["id"]))
|
||||
continue
|
||||
variant_metadata["variant"] = variants[0]["id"]
|
||||
ContainerRegistry.allMetadataLoaded.connect(_fillMissingVariants)
|
||||
|
|
|
|||
12
plugins/XmlMaterialProfile/product_to_id.json
Normal file
12
plugins/XmlMaterialProfile/product_to_id.json
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"Ultimaker 2": "ultimaker2",
|
||||
"Ultimaker 2 Extended": "ultimaker2_extended",
|
||||
"Ultimaker 2 Extended+": "ultimaker2_extended_plus",
|
||||
"Ultimaker 2 Go": "ultimaker2_go",
|
||||
"Ultimaker 2+": "ultimaker2_plus",
|
||||
"Ultimaker 3": "ultimaker3",
|
||||
"Ultimaker 3 Extended": "ultimaker3_extended",
|
||||
"Ultimaker Original": "ultimaker_original",
|
||||
"Ultimaker Original+": "ultimaker_original_plus",
|
||||
"IMADE3D JellyBOX": "imade3d_jellybox"
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue