VariantManager.py 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. # Copyright (c) 2018 Ultimaker B.V.
  2. # Cura is released under the terms of the LGPLv3 or higher.
  3. from enum import Enum
  4. from collections import OrderedDict
  5. from typing import Optional, TYPE_CHECKING
  6. from UM.ConfigurationErrorMessage import ConfigurationErrorMessage
  7. from UM.Logger import Logger
  8. from UM.Settings.ContainerRegistry import ContainerRegistry
  9. from UM.Util import parseBool
  10. from cura.Machines.ContainerNode import ContainerNode
  11. from cura.Settings.GlobalStack import GlobalStack
  12. if TYPE_CHECKING:
  13. from UM.Settings.DefinitionContainer import DefinitionContainer
  14. class VariantType(Enum):
  15. BUILD_PLATE = "buildplate"
  16. NOZZLE = "nozzle"
  17. ALL_VARIANT_TYPES = (VariantType.BUILD_PLATE, VariantType.NOZZLE)
  18. #
  19. # VariantManager is THE place to look for a specific variant. It maintains two variant lookup tables with the following
  20. # structure:
  21. #
  22. # [machine_definition_id] -> [variant_type] -> [variant_name] -> ContainerNode(metadata / container)
  23. # Example: "ultimaker3" -> "buildplate" -> "Glass" (if present) -> ContainerNode
  24. # -> ...
  25. # -> "nozzle" -> "AA 0.4"
  26. # -> "BB 0.8"
  27. # -> ...
  28. #
  29. # [machine_definition_id] -> [machine_buildplate_type] -> ContainerNode(metadata / container)
  30. # Example: "ultimaker3" -> "glass" (this is different from the variant name) -> ContainerNode
  31. #
  32. # Note that the "container" field is not loaded in the beginning because it would defeat the purpose of lazy-loading.
  33. # A container is loaded when getVariant() is called to load a variant InstanceContainer.
  34. #
  35. class VariantManager:
  36. def __init__(self, container_registry):
  37. self._container_registry = container_registry # type: ContainerRegistry
  38. self._machine_to_variant_dict_map = dict() # <machine_type> -> <variant_dict>
  39. self._machine_to_buildplate_dict_map = dict()
  40. self._exclude_variant_id_list = ["empty_variant"]
  41. #
  42. # Initializes the VariantManager including:
  43. # - initializing the variant lookup table based on the metadata in ContainerRegistry.
  44. #
  45. def initialize(self):
  46. self._machine_to_variant_dict_map = OrderedDict()
  47. self._machine_to_buildplate_dict_map = OrderedDict()
  48. # Cache all variants from the container registry to a variant map for better searching and organization.
  49. variant_metadata_list = self._container_registry.findContainersMetadata(type = "variant")
  50. for variant_metadata in variant_metadata_list:
  51. if variant_metadata["id"] in self._exclude_variant_id_list:
  52. Logger.log("d", "Exclude variant [%s]", variant_metadata["id"])
  53. continue
  54. variant_name = variant_metadata["name"]
  55. variant_definition = variant_metadata["definition"]
  56. if variant_definition not in self._machine_to_variant_dict_map:
  57. self._machine_to_variant_dict_map[variant_definition] = OrderedDict()
  58. for variant_type in ALL_VARIANT_TYPES:
  59. self._machine_to_variant_dict_map[variant_definition][variant_type] = dict()
  60. try:
  61. variant_type = variant_metadata["hardware_type"]
  62. except KeyError:
  63. Logger.log("w", "Variant %s does not specify a hardware_type; assuming 'nozzle'", variant_metadata["id"])
  64. variant_type = VariantType.NOZZLE
  65. variant_type = VariantType(variant_type)
  66. variant_dict = self._machine_to_variant_dict_map[variant_definition][variant_type]
  67. if variant_name in variant_dict:
  68. # ERROR: duplicated variant name.
  69. ConfigurationErrorMessage.getInstance().addFaultyContainers(variant_metadata["id"])
  70. continue #Then ignore this variant. This now chooses one of the two variants arbitrarily and deletes the other one! No guarantees!
  71. variant_dict[variant_name] = ContainerNode(metadata = variant_metadata)
  72. # If the variant is a buildplate then fill also the buildplate map
  73. if variant_type == VariantType.BUILD_PLATE:
  74. if variant_definition not in self._machine_to_buildplate_dict_map:
  75. self._machine_to_buildplate_dict_map[variant_definition] = OrderedDict()
  76. variant_container = self._container_registry.findContainers(type = "variant", id = variant_metadata["id"])[0]
  77. buildplate_type = variant_container.getProperty("machine_buildplate_type", "value")
  78. if buildplate_type not in self._machine_to_buildplate_dict_map[variant_definition]:
  79. self._machine_to_variant_dict_map[variant_definition][buildplate_type] = dict()
  80. self._machine_to_buildplate_dict_map[variant_definition][buildplate_type] = variant_dict[variant_name]
  81. #
  82. # Gets the variant InstanceContainer with the given information.
  83. # Almost the same as getVariantMetadata() except that this returns an InstanceContainer if present.
  84. #
  85. def getVariantNode(self, machine_definition_id: str, variant_name: str,
  86. variant_type: Optional["VariantType"] = None) -> Optional["ContainerNode"]:
  87. if variant_type is None:
  88. variant_node = None
  89. variant_type_dict = self._machine_to_variant_dict_map[machine_definition_id]
  90. for variant_dict in variant_type_dict.values():
  91. if variant_name in variant_dict:
  92. variant_node = variant_dict[variant_name]
  93. break
  94. return variant_node
  95. return self._machine_to_variant_dict_map[machine_definition_id].get(variant_type, {}).get(variant_name)
  96. def getVariantNodes(self, machine: "GlobalStack",
  97. variant_type: Optional["VariantType"] = None) -> dict:
  98. machine_definition_id = machine.definition.getId()
  99. return self._machine_to_variant_dict_map.get(machine_definition_id, {}).get(variant_type, {})
  100. #
  101. # Gets the default variant for the given machine definition.
  102. #
  103. def getDefaultVariantNode(self, machine_definition: "DefinitionContainer",
  104. variant_type: VariantType) -> Optional["ContainerNode"]:
  105. machine_definition_id = machine_definition.getId()
  106. preferred_variant_name = None
  107. if variant_type == VariantType.BUILD_PLATE:
  108. if parseBool(machine_definition.getMetaDataEntry("has_variant_buildplates", False)):
  109. preferred_variant_name = machine_definition.getMetaDataEntry("preferred_variant_buildplate_name")
  110. else:
  111. if parseBool(machine_definition.getMetaDataEntry("has_variants", False)):
  112. preferred_variant_name = machine_definition.getMetaDataEntry("preferred_variant_name")
  113. node = None
  114. if preferred_variant_name:
  115. node = self.getVariantNode(machine_definition_id, preferred_variant_name, variant_type)
  116. return node
  117. def getBuildplateVariantNode(self, machine_definition_id: str, buildplate_type: str) -> Optional["ContainerNode"]:
  118. if machine_definition_id in self._machine_to_buildplate_dict_map:
  119. return self._machine_to_buildplate_dict_map[machine_definition_id].get(buildplate_type)
  120. return None