MachineNode.py 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. # Copyright (c) 2019 Ultimaker B.V.
  2. # Cura is released under the terms of the LGPLv3 or higher.
  3. from typing import List, TYPE_CHECKING
  4. from UM.Logger import Logger
  5. from UM.Util import parseBool
  6. from UM.Settings.ContainerRegistry import ContainerRegistry # To find all the variants for this machine.
  7. from UM.Settings.Interfaces import ContainerInterface
  8. from cura.Machines.ContainerNode import ContainerNode
  9. from cura.Machines.QualityGroup import QualityGroup # To construct groups of quality profiles that belong together.
  10. from cura.Machines.QualityNode import QualityNode
  11. from cura.Machines.VariantNode import VariantNode
  12. if TYPE_CHECKING:
  13. from typing import Dict
  14. ## This class represents a machine in the container tree.
  15. #
  16. # The subnodes of these nodes are variants.
  17. class MachineNode(ContainerNode):
  18. def __init__(self, container_id: str) -> None:
  19. super().__init__(container_id)
  20. self.variants = {} # type: Dict[str, VariantNode] # Mapping variant names to their nodes.
  21. self.global_qualities = {} # type: Dict[str, QualityNode] # Mapping quality types to the global quality for those types.
  22. container_registry = ContainerRegistry.getInstance()
  23. try:
  24. my_metadata = container_registry.findContainersMetadata(id = container_id)[0]
  25. except IndexError:
  26. Logger.log("Unable to find metadata for container %s", container_id)
  27. my_metadata = {}
  28. # Some of the metadata is cached upon construction here.
  29. # ONLY DO THAT FOR METADATA THAT DOESN'T CHANGE DURING RUNTIME!
  30. # Otherwise you need to keep it up-to-date during runtime.
  31. self.has_machine_materials = parseBool(my_metadata.get("has_machine_materials", "false"))
  32. self.has_machine_quality = parseBool(my_metadata.get("has_machine_quality", "false"))
  33. self.quality_definition = my_metadata.get("quality_definition", container_id)
  34. self.exclude_materials = my_metadata.get("exclude_materials", [])
  35. self.preferred_variant_name = my_metadata.get("preferred_variant_name", "")
  36. container_registry.containerAdded.connect(self._variantAdded)
  37. self._loadAll()
  38. ## Get the available quality groups for this machine.
  39. #
  40. # This returns all quality groups, regardless of whether they are
  41. # available to the combination of extruders or not. On the resulting
  42. # quality groups, the is_available property is set to indicate whether the
  43. # quality group can be selected according to the combination of extruders
  44. # in the parameters.
  45. # \param variant_names The names of the variants loaded in each extruder.
  46. # \param material_bases The base file names of the materials loaded in
  47. # each extruder.
  48. # \param extruder_enabled Whether or not the extruders are enabled. This
  49. # allows the function to set the is_available properly.
  50. # \return For each available quality type, a QualityGroup instance.
  51. def getQualityGroups(self, variant_names: List[str], material_bases: List[str], extruder_enabled: List[bool]) -> Dict[str, QualityGroup]:
  52. if len(variant_names) != len(material_bases) or len(variant_names) != len(extruder_enabled):
  53. Logger.log("e", "The number of extruders in the list of variants (" + str(len(variant_names)) + ") is not equal to the number of extruders in the list of materials (" + str(len(material_bases)) + ") or the list of enabled extruders (" + str(len(extruder_enabled)) + ").")
  54. return {}
  55. # For each extruder, find which quality profiles are available. Later we'll intersect the quality types.
  56. qualities_per_type_per_extruder = [] # type: List[Dict[str, QualityNode]]
  57. for extruder_nr, variant_name in enumerate(variant_names):
  58. if not extruder_enabled[extruder_nr]:
  59. continue # No qualities are available in this extruder. It'll get skipped when calculating the available quality types.
  60. material_base = material_bases[extruder_nr]
  61. if variant_name not in self.variants or material_base not in self.variants[variant_name].materials:
  62. # The printer has no variant/material-specific quality profiles. Use the global quality profiles.
  63. qualities_per_type_per_extruder[extruder_nr] = self.global_qualities
  64. else:
  65. # Use the actually specialised quality profiles.
  66. qualities_per_type_per_extruder[extruder_nr] = self.variants[variant_name].materials[material_base].qualities
  67. # Create the quality group for each available type.
  68. quality_groups = {}
  69. for quality_type, global_quality_node in self.global_qualities.items():
  70. quality_groups[quality_type] = QualityGroup(name = global_quality_node.getMetaDataEntry("name", "Unnamed profile"), quality_type = quality_type)
  71. quality_groups[quality_type].node_for_global = global_quality_node
  72. for extruder, qualities_per_type in qualities_per_type_per_extruder:
  73. quality_groups[quality_type].nodes_for_extruders[extruder] = qualities_per_type[quality_type]
  74. available_quality_types = set(quality_groups.keys())
  75. for extruder_nr, qualities_per_type in enumerate(qualities_per_type_per_extruder):
  76. if not extruder_enabled[extruder_nr]:
  77. continue
  78. available_quality_types.intersection_update(qualities_per_type.keys())
  79. for quality_type in available_quality_types:
  80. quality_groups[quality_type].is_available = True
  81. return quality_groups
  82. ## (Re)loads all variants under this printer.
  83. def _loadAll(self):
  84. # Find all the variants for this definition ID.
  85. container_registry = ContainerRegistry.getInstance()
  86. variants = container_registry.findInstanceContainersMetadata(type = "variant", definition = self.container_id, hardware_type = "nozzle")
  87. for variant in variants:
  88. variant_name = variant["name"]
  89. if variant_name not in self.variants:
  90. self.variants[variant_name] = VariantNode(variant["id"], machine = self)
  91. # Find the global qualities for this printer.
  92. global_qualities = container_registry.findInstanceContainersMetadata(type = "quality", definition = self.container_id, global_quality = True) # First try specific to this printer.
  93. if len(global_qualities) == 0: # This printer doesn't override the global qualities.
  94. global_qualities = container_registry.findInstanceContainersMetadata(type = "quality", definition = "fdmprinter", global_quality = True) # Otherwise pick the global global qualities.
  95. for global_quality in global_qualities:
  96. self.global_qualities[global_quality["quality_type"]] = QualityNode(global_quality["id"], parent = self)
  97. ## When a variant gets added to the set of profiles, we need to update our
  98. # tree here.
  99. def _variantAdded(self, container: ContainerInterface):
  100. if container.getMetaDataEntry("type") != "variant":
  101. return # Not interested.
  102. name = container.getMetaDataEntry("name")
  103. if name in self.variants:
  104. return # Already have this one.
  105. if container.getMetaDataEntry("hardware_type") != "nozzle":
  106. return # Only want nozzles in my tree.
  107. if container.getMetaDataEntry("definition") != self.container_id:
  108. return # Not a nozzle that fits in my machine.
  109. self.variants[name] = VariantNode(container.getId(), machine = self)