예제 #1
0
    def loadMetadata(self, container_id: str) -> Optional[Dict[str, Any]]:
        registry = ContainerRegistry.getInstance()
        if container_id in registry.metadata:
            return registry.metadata[container_id]

        filename = self._id_to_path[container_id] #Raises KeyError if container ID does not exist in the (cache of the) files!
        clazz = ContainerRegistry.mime_type_map[self._id_to_mime[container_id].name]

        requested_metadata = None
        try:
            with open(filename, "r", encoding = "utf-8") as f:
                result_metadatas = clazz.deserializeMetadata(f.read(), container_id) #pylint: disable=no-member
        except IOError as e:
            Logger.log("e", "Unable to load metadata from file {filename}: {error_msg}".format(filename = filename, error_msg = str(e)))
            ConfigurationErrorMessage.getInstance().addFaultyContainers(container_id)
            return None
        except Exception as e:
            Logger.logException("e", "Unable to deserialize metadata for container {filename}: {container_id}: {error_msg}".format(filename = filename, container_id = container_id, error_msg = str(e)))
            ConfigurationErrorMessage.getInstance().addFaultyContainers(container_id)
            return None

        for metadata in result_metadatas:
            if "id" not in metadata:
                Logger.log("w", "Metadata obtained from deserializeMetadata of {class_name} didn't contain an ID.".format(class_name = clazz.__name__))
                continue
            if metadata["id"] == container_id:
                requested_metadata = metadata
            #Side-load the metadata into the registry if we get multiple containers.
            if metadata["id"] not in registry.metadata: #This wouldn't get loaded normally.
                self._id_to_path[metadata["id"]] = filename
                self._id_to_mime[metadata["id"]] = self._id_to_mime[container_id] #Assume that they only return one MIME type.
                registry.metadata[metadata["id"]] = metadata
                registry.source_provider[metadata["id"]] = self
        return requested_metadata
예제 #2
0
    def loadMetadata(self, container_id: str) -> Dict[str, Any]:
        registry = ContainerRegistry.getInstance()
        if container_id in registry.metadata:
            return registry.metadata[container_id]

        filename = self._id_to_path[container_id] #Raises KeyError if container ID does not exist in the (cache of the) files!
        clazz = ContainerRegistry.mime_type_map[self._id_to_mime[container_id].name]

        requested_metadata = {}  # type: Dict[str, Any]
        try:
            with open(filename, "r", encoding = "utf-8") as f:
                result_metadatas = clazz.deserializeMetadata(f.read(), container_id) #pylint: disable=no-member
        except IOError as e:
            Logger.log("e", "Unable to load metadata from file {filename}: {error_msg}".format(filename = filename, error_msg = str(e)))
            ConfigurationErrorMessage.getInstance().addFaultyContainers(container_id)
            return {}
        except Exception as e:
            Logger.logException("e", "Unable to deserialize metadata for container {filename}: {container_id}: {error_msg}".format(filename = filename, container_id = container_id, error_msg = str(e)))
            ConfigurationErrorMessage.getInstance().addFaultyContainers(container_id)
            return {}

        for metadata in result_metadatas:
            if "id" not in metadata:
                Logger.log("w", "Metadata obtained from deserializeMetadata of {class_name} didn't contain an ID.".format(class_name = clazz.__name__))
                continue
            if metadata["id"] == container_id:
                requested_metadata = metadata
            #Side-load the metadata into the registry if we get multiple containers.
            if metadata["id"] not in registry.metadata: #This wouldn't get loaded normally.
                self._id_to_path[metadata["id"]] = filename
                self._id_to_mime[metadata["id"]] = self._id_to_mime[container_id] #Assume that they only return one MIME type.
                registry.metadata[metadata["id"]] = metadata
                registry.source_provider[metadata["id"]] = self
        return requested_metadata
예제 #3
0
    def initialize(self):
        self._machine_to_variant_dict_map = OrderedDict()
        self._machine_to_buildplate_dict_map = OrderedDict()

        # Cache all variants from the container registry to a variant map for better searching and organization.
        variant_metadata_list = self._container_registry.findContainersMetadata(
            type="variant")
        for variant_metadata in variant_metadata_list:
            if variant_metadata["id"] in self._exclude_variant_id_list:
                Logger.log("d", "Exclude variant [%s]", variant_metadata["id"])
                continue

            variant_name = variant_metadata["name"]
            variant_definition = variant_metadata["definition"]
            if variant_definition not in self._machine_to_variant_dict_map:
                self._machine_to_variant_dict_map[
                    variant_definition] = OrderedDict()
                for variant_type in ALL_VARIANT_TYPES:
                    self._machine_to_variant_dict_map[variant_definition][
                        variant_type] = dict()

            try:
                variant_type = variant_metadata["hardware_type"]
            except KeyError:
                Logger.log(
                    "w",
                    "Variant %s does not specify a hardware_type; assuming 'nozzle'",
                    variant_metadata["id"])
                variant_type = VariantType.NOZZLE
            variant_type = VariantType(variant_type)
            variant_dict = self._machine_to_variant_dict_map[
                variant_definition][variant_type]
            if variant_name in variant_dict:
                # ERROR: duplicated variant name.
                ConfigurationErrorMessage.getInstance().addFaultyContainers(
                    variant_metadata["id"])
                continue  #Then ignore this variant. This now chooses one of the two variants arbitrarily and deletes the other one! No guarantees!

            variant_dict[variant_name] = ContainerNode(
                metadata=variant_metadata)

            # If the variant is a buildplate then fill also the buildplate map
            if variant_type == VariantType.BUILD_PLATE:
                if variant_definition not in self._machine_to_buildplate_dict_map:
                    self._machine_to_buildplate_dict_map[
                        variant_definition] = OrderedDict()

                variant_container = self._container_registry.findContainers(
                    type="variant", id=variant_metadata["id"])[0]
                buildplate_type = variant_container.getProperty(
                    "machine_buildplate_type", "value")
                if buildplate_type not in self._machine_to_buildplate_dict_map[
                        variant_definition]:
                    self._machine_to_variant_dict_map[variant_definition][
                        buildplate_type] = dict()

                self._machine_to_buildplate_dict_map[variant_definition][
                    buildplate_type] = variant_dict[variant_name]
예제 #4
0
    def createMachine(cls, name: str,
                      definition_id: str) -> Optional[GlobalStack]:
        from cura.CuraApplication import CuraApplication
        application = CuraApplication.getInstance()
        registry = application.getContainerRegistry()
        container_tree = ContainerTree.getInstance()

        definitions = registry.findDefinitionContainers(id=definition_id)
        if not definitions:
            ConfigurationErrorMessage.getInstance().addFaultyContainers(
                definition_id)
            Logger.log("w",
                       "Definition {definition} was not found!",
                       definition=definition_id)
            return None

        machine_definition = definitions[0]
        # The container tree listens to the containerAdded signal to add the definition and build the tree,
        # but that signal is emitted with a delay which might not have passed yet.
        # Therefore we must make sure that it's manually added here.
        container_tree.addMachineNodeByDefinitionId(machine_definition.getId())
        machine_node = container_tree.machines[machine_definition.getId()]

        generated_name = registry.createUniqueName(
            "machine", "", name, machine_definition.getName())
        # Make sure the new name does not collide with any definition or (quality) profile
        # createUniqueName() only looks at other stacks, but not at definitions or quality profiles
        # Note that we don't go for uniqueName() immediately because that function matches with ignore_case set to true
        if registry.findContainersMetadata(id=generated_name):
            generated_name = registry.uniqueName(generated_name)

        new_global_stack = cls.createGlobalStack(
            new_stack_id=generated_name,
            definition=machine_definition,
            variant_container=application.empty_variant_container,
            material_container=application.empty_material_container,
            quality_container=machine_node.preferredGlobalQuality().container,
        )
        new_global_stack.setName(generated_name)

        # Create ExtruderStacks
        extruder_dict = machine_definition.getMetaDataEntry(
            "machine_extruder_trains")
        for position in extruder_dict:
            cls.createExtruderStackWithDefaultSetup(new_global_stack, position)

        for new_extruder in new_global_stack.extruders.values(
        ):  # Only register the extruders if we're sure that all of them are correct.
            registry.addContainer(new_extruder)

        # Register the global stack after the extruder stacks are created. This prevents the registry from adding another
        # extruder stack because the global stack didn't have one yet (which is enforced since Cura 3.1).
        registry.addContainer(new_global_stack)

        return new_global_stack
예제 #5
0
    def createMachine(cls, name: str, definition_id: str) -> Optional[GlobalStack]:
        """Create a new instance of a machine.

        :param name: The name of the new machine.
        :param definition_id: The ID of the machine definition to use.

        :return: The new global stack or None if an error occurred.
        """

        from cura.CuraApplication import CuraApplication
        application = CuraApplication.getInstance()
        registry = application.getContainerRegistry()
        container_tree = ContainerTree.getInstance()

        definitions = registry.findDefinitionContainers(id = definition_id)
        if not definitions:
            ConfigurationErrorMessage.getInstance().addFaultyContainers(definition_id)
            Logger.log("w", "Definition {definition} was not found!", definition = definition_id)
            return None

        machine_definition = definitions[0]
        machine_node = container_tree.machines[machine_definition.getId()]

        generated_name = registry.createUniqueName("machine", "", name, machine_definition.getName())
        # Make sure the new name does not collide with any definition or (quality) profile
        # createUniqueName() only looks at other stacks, but not at definitions or quality profiles
        # Note that we don't go for uniqueName() immediately because that function matches with ignore_case set to true
        if registry.findContainersMetadata(id = generated_name):
            generated_name = registry.uniqueName(generated_name)

        new_global_stack = cls.createGlobalStack(
            new_stack_id = generated_name,
            definition = machine_definition,
            variant_container = application.empty_variant_container,
            material_container = application.empty_material_container,
            quality_container = machine_node.preferredGlobalQuality().container,
        )
        new_global_stack.setName(generated_name)

        # Create ExtruderStacks
        extruder_dict = machine_definition.getMetaDataEntry("machine_extruder_trains")
        for position in extruder_dict:
            try:
                cls.createExtruderStackWithDefaultSetup(new_global_stack, position)
            except IndexError:
                return None

        for new_extruder in new_global_stack.extruderList:  # Only register the extruders if we're sure that all of them are correct.
            registry.addContainer(new_extruder)

        # Register the global stack after the extruder stacks are created. This prevents the registry from adding another
        # extruder stack because the global stack didn't have one yet (which is enforced since Cura 3.1).
        registry.addContainer(new_global_stack)

        return new_global_stack
예제 #6
0
    def deserialize(self, serialized: str, file_name: Optional[str] = None) -> str:
        # Update the serialized data first
        serialized = super().deserialize(serialized, file_name)
        parser = self._readAndValidateSerialized(serialized)

        if parser.getint("general", "version") != self.Version:
            raise IncorrectVersionError()

        # Clear all data before starting.
        for container in self._containers:
            container.propertyChanged.disconnect(self._collectPropertyChanges)

        self._containers = []
        self._metadata = {}

        if "metadata" in parser:
            self._metadata = dict(parser["metadata"])
        self._metadata["id"] = parser["general"]["id"]
        self._metadata["name"] = parser["general"].get("name", self.getId())
        self._metadata["version"] = self.Version  # Guaranteed to be equal to what's in the container. See above.
        self._metadata["container_type"] = ContainerStack

        if "containers" in parser:
            for index, container_id in parser.items("containers"):
                containers = _containerRegistry.findContainers(id = container_id)
                if containers:
                    containers[0].propertyChanged.connect(self._collectPropertyChanges)
                    self._containers.append(containers[0])
                else:
                    self._containers.append(_containerRegistry.getEmptyInstanceContainer())
                    ConfigurationErrorMessage.getInstance().addFaultyContainers(container_id, self.getId())
                    Logger.log("e", "When trying to deserialize %s, we received an unknown container ID (%s)" % (self.getId(), container_id))
                    raise ContainerFormatError("When trying to deserialize %s, we received an unknown container ID (%s)" % (self.getId(), container_id))

        elif parser.has_option("general", "containers"):
            # Backward compatibility with 2.3.1: The containers used to be saved in a single comma-separated list.
            container_string = parser["general"].get("containers", "")
            Logger.log("d", "While deserializing, we got the following container string: %s", container_string)
            container_id_list = container_string.split(",")
            for container_id in container_id_list:
                if container_id != "":
                    containers = _containerRegistry.findContainers(id = container_id)
                    if containers:
                        containers[0].propertyChanged.connect(self._collectPropertyChanges)
                        self._containers.append(containers[0])
                    else:
                        self._containers.append(_containerRegistry.getEmptyInstanceContainer())
                        ConfigurationErrorMessage.getInstance().addFaultyContainers(container_id, self.getId())
                        Logger.log("e", "When trying to deserialize %s, we received an unknown container ID (%s)" % (self.getId(), container_id))
                        raise ContainerFormatError("When trying to deserialize %s, we received an unknown container ID (%s)" % (self.getId(), container_id))

        ## TODO; Deserialize the containers.

        return serialized
예제 #7
0
    def addNode(self, node: "QualityNode"):
        extruder_position = node.metadata.get("position")

        if extruder_position is None and self.node_for_global is not None or extruder_position in self.nodes_for_extruders: #We would be overwriting another node.
            ConfigurationErrorMessage.getInstance().addFaultyContainers(node.metadata["id"])
            return

        if extruder_position is None: #Then we're a global quality changes profile.
            self.node_for_global = node
        else: #This is an extruder's quality changes profile.
            self.nodes_for_extruders[extruder_position] = node
예제 #8
0
    def deserialize(self, serialized: str, file_name: Optional[str] = None) -> str:
        # Update the serialized data first
        serialized = super().deserialize(serialized, file_name)
        parser = self._readAndValidateSerialized(serialized)

        if parser.getint("general", "version") != self.Version:
            raise IncorrectVersionError()

        # Clear all data before starting.
        for container in self._containers:
            container.propertyChanged.disconnect(self._collectPropertyChanges)

        self._containers = []
        self._metadata = {}

        if "metadata" in parser:
            self._metadata = dict(parser["metadata"])
        self._metadata["id"] = parser["general"]["id"]
        self._metadata["name"] = parser["general"].get("name", self.getId())
        self._metadata["version"] = self.Version  # Guaranteed to be equal to what's in the container. See above.
        self._metadata["container_type"] = ContainerStack

        if "containers" in parser:
            for index, container_id in parser.items("containers"):
                containers = _containerRegistry.findContainers(id = container_id)
                if containers:
                    containers[0].propertyChanged.connect(self._collectPropertyChanges)
                    self._containers.append(containers[0])
                else:
                    self._containers.append(_containerRegistry.getEmptyInstanceContainer())
                    ConfigurationErrorMessage.getInstance().addFaultyContainers(container_id, self.getId())
                    Logger.log("e", "When trying to deserialize %s, we received an unknown container ID (%s)" % (self.getId(), container_id))
                    raise ContainerFormatError("When trying to deserialize %s, we received an unknown container ID (%s)" % (self.getId(), container_id))

        elif parser.has_option("general", "containers"):
            # Backward compatibility with 2.3.1: The containers used to be saved in a single comma-separated list.
            container_string = parser["general"].get("containers", "")
            Logger.log("d", "While deserializing, we got the following container string: %s", container_string)
            container_id_list = container_string.split(",")
            for container_id in container_id_list:
                if container_id != "":
                    containers = _containerRegistry.findContainers(id = container_id)
                    if containers:
                        containers[0].propertyChanged.connect(self._collectPropertyChanges)
                        self._containers.append(containers[0])
                    else:
                        self._containers.append(_containerRegistry.getEmptyInstanceContainer())
                        ConfigurationErrorMessage.getInstance().addFaultyContainers(container_id, self.getId())
                        Logger.log("e", "When trying to deserialize %s, we received an unknown container ID (%s)" % (self.getId(), container_id))
                        raise ContainerFormatError("When trying to deserialize %s, we received an unknown container ID (%s)" % (self.getId(), container_id))

        ## TODO; Deserialize the containers.

        return serialized
예제 #9
0
    def addNode(self, node: "QualityNode") -> None:
        extruder_position = node.getMetaDataEntry("position")

        if extruder_position is None and self.node_for_global is not None or extruder_position in self.nodes_for_extruders: #We would be overwriting another node.
            ConfigurationErrorMessage.getInstance().addFaultyContainers(node.getMetaDataEntry("id"))
            return

        if extruder_position is None:  # Then we're a global quality changes profile.
            self.node_for_global = node
        else:  # This is an extruder's quality changes profile.
            self.nodes_for_extruders[extruder_position] = node
예제 #10
0
 def _configurationErrorMessageActionTriggered(self, _, action_id):
     if action_id == "startoptimiser_clean":
         configuration_error_message = ConfigurationErrorMessage.getInstance(
         )
         configuration_error_message.hide()
         self._addToBlackList(
             configuration_error_message._faulty_containers)
예제 #11
0
 def container(self) -> Optional[InstanceContainer]:
     if not self._container:
         container_list = ContainerRegistry.getInstance().findInstanceContainers(id = self.container_id)
         if len(container_list) == 0:
             Logger.log("e", "Failed to lazy-load container [{container_id}]. Cannot find it.".format(container_id = self.container_id))
             error_message = ConfigurationErrorMessage.getInstance()
             error_message.addFaultyContainers(self.container_id)
             return None
         self._container = container_list[0]
     return self._container
예제 #12
0
    def initialize(self) -> None:
        self._machine_to_variant_dict_map = OrderedDict()
        self._machine_to_buildplate_dict_map = OrderedDict()

        # Cache all variants from the container registry to a variant map for better searching and organization.
        variant_metadata_list = self._container_registry.findContainersMetadata(type = "variant")
        for variant_metadata in variant_metadata_list:
            if variant_metadata["id"] in self._exclude_variant_id_list:
                Logger.log("d", "Exclude variant [%s]", variant_metadata["id"])
                continue

            variant_name = variant_metadata["name"]
            variant_definition = variant_metadata["definition"]
            if variant_definition not in self._machine_to_variant_dict_map:
                self._machine_to_variant_dict_map[variant_definition] = OrderedDict()
                for variant_type in ALL_VARIANT_TYPES:
                    self._machine_to_variant_dict_map[variant_definition][variant_type] = dict()

            try:
                variant_type = variant_metadata["hardware_type"]
            except KeyError:
                Logger.log("w", "Variant %s does not specify a hardware_type; assuming 'nozzle'", variant_metadata["id"])
                variant_type = VariantType.NOZZLE
            variant_type = VariantType(variant_type)
            variant_dict = self._machine_to_variant_dict_map[variant_definition][variant_type]
            if variant_name in variant_dict:
                # ERROR: duplicated variant name.
                ConfigurationErrorMessage.getInstance().addFaultyContainers(variant_metadata["id"])
                continue #Then ignore this variant. This now chooses one of the two variants arbitrarily and deletes the other one! No guarantees!

            variant_dict[variant_name] = ContainerNode(metadata = variant_metadata)

            # If the variant is a buildplate then fill also the buildplate map
            if variant_type == VariantType.BUILD_PLATE:
                if variant_definition not in self._machine_to_buildplate_dict_map:
                    self._machine_to_buildplate_dict_map[variant_definition] = OrderedDict()

                variant_container = self._container_registry.findContainers(type = "variant", id = variant_metadata["id"])[0]
                buildplate_type = variant_container.getProperty("machine_buildplate_type", "value")
                if buildplate_type not in self._machine_to_buildplate_dict_map[variant_definition]:
                    self._machine_to_variant_dict_map[variant_definition][buildplate_type] = dict()

                self._machine_to_buildplate_dict_map[variant_definition][buildplate_type] = variant_dict[variant_name]
예제 #13
0
    def getContainer(self) -> Optional["InstanceContainer"]:
        if self._metadata is None:
            Logger.log("e", "Cannot get container for a ContainerNode without metadata.")
            return None

        if self._container is None:
            container_id = self._metadata["id"]
            from UM.Settings.ContainerRegistry import ContainerRegistry
            container_list = ContainerRegistry.getInstance().findInstanceContainers(id = container_id)
            if not container_list:
                Logger.log("e", "Failed to lazy-load container [{container_id}]. Cannot find it.".format(container_id = container_id))
                error_message = ConfigurationErrorMessage.getInstance()
                error_message.addFaultyContainers(container_id)
                return None
            self._container = container_list[0]

        return self._container
예제 #14
0
    def _onPluginsLoaded(self) -> None:
        local_container_provider = self._application.getPluginRegistry(
        ).getPluginObject("LocalContainerProvider")
        self._local_container_provider_patches = LocalContainerProviderPatches.LocalContainerProviderPatches(
            local_container_provider)

        configuration_error_message = ConfigurationErrorMessage.getInstance()
        configuration_error_message.addAction(
            action_id="startoptimiser_clean",
            name=catalog.i18nc("@action:button", "Disable affected profiles"),
            icon="",
            description=catalog.i18nc(
                "@action:tooltip",
                "Disable loading the corrupted configuration files but attempt to leave the rest intact."
            ))
        configuration_error_message.actionTriggered.connect(
            self._configurationErrorMessageActionTriggered)
예제 #15
0
    def container(self) -> Optional[InstanceContainer]:
        """The container that this node's container ID refers to.

        This can be used to finally instantiate the container in order to put it in the container stack.

        :return: A container.
        """

        if not self._container:
            container_list = ContainerRegistry.getInstance().findInstanceContainers(id = self.container_id)
            if len(container_list) == 0:
                Logger.log("e", "Failed to lazy-load container [{container_id}]. Cannot find it.".format(container_id = self.container_id))
                error_message = ConfigurationErrorMessage.getInstance()
                error_message.addFaultyContainers(self.container_id)
                return None
            self._container = container_list[0]
        return self._container
예제 #16
0
    def getContainer(self) -> Optional["InstanceContainer"]:
        if self.metadata is None:
            Logger.log("e", "Cannot get container for a ContainerNode without metadata.")
            return None

        if self.container is None:
            container_id = self.metadata["id"]
            from UM.Settings.ContainerRegistry import ContainerRegistry
            container_list = ContainerRegistry.getInstance().findInstanceContainers(id = container_id)
            if not container_list:
                Logger.log("e", "Failed to lazy-load container [{container_id}]. Cannot find it.".format(container_id = container_id))
                error_message = ConfigurationErrorMessage.getInstance()
                error_message.addFaultyContainers(container_id)
                return None
            self.container = container_list[0]

        return self.container
예제 #17
0
    def createMachine(cls, name: str, definition_id: str) -> Optional[GlobalStack]:
        from cura.CuraApplication import CuraApplication
        application = CuraApplication.getInstance()
        variant_manager = application.getVariantManager()
        material_manager = application.getMaterialManager()
        quality_manager = application.getQualityManager()
        registry = ContainerRegistry.getInstance()

        definitions = registry.findDefinitionContainers(id = definition_id)
        if not definitions:
            ConfigurationErrorMessage.getInstance().addFaultyContainers(definition_id)
            Logger.log("w", "Definition {definition} was not found!", definition = definition_id)
            return None

        machine_definition = definitions[0]

        # get variant container for the global stack
        global_variant_container = application.empty_variant_container
        global_variant_node = variant_manager.getDefaultVariantNode(machine_definition, VariantType.BUILD_PLATE)
        if global_variant_node:
            global_variant_container = global_variant_node.getContainer()
        if not global_variant_container:
            global_variant_container = application.empty_variant_container

        # get variant container for extruders
        extruder_variant_container = application.empty_variant_container
        extruder_variant_node = variant_manager.getDefaultVariantNode(machine_definition, VariantType.NOZZLE)
        extruder_variant_name = None
        if extruder_variant_node:
            extruder_variant_container = extruder_variant_node.getContainer()
            if not extruder_variant_container:
                extruder_variant_container = application.empty_variant_container
            extruder_variant_name = extruder_variant_container.getName()

        generated_name = registry.createUniqueName("machine", "", name, machine_definition.getName())
        # Make sure the new name does not collide with any definition or (quality) profile
        # createUniqueName() only looks at other stacks, but not at definitions or quality profiles
        # Note that we don't go for uniqueName() immediately because that function matches with ignore_case set to true
        if registry.findContainersMetadata(id = generated_name):
            generated_name = registry.uniqueName(generated_name)

        new_global_stack = cls.createGlobalStack(
            new_stack_id = generated_name,
            definition = machine_definition,
            variant_container = global_variant_container,
            material_container = application.empty_material_container,
            quality_container = application.empty_quality_container,
        )
        new_global_stack.setName(generated_name)

        # get material container for extruders
        material_container = application.empty_material_container
        material_node = material_manager.getDefaultMaterial(new_global_stack, extruder_variant_name)
        if material_node and material_node.getContainer():
            material_container = material_node.getContainer()

        # Create ExtruderStacks
        extruder_dict = machine_definition.getMetaDataEntry("machine_extruder_trains")

        for position, extruder_definition_id in extruder_dict.items():
            # Sanity check: make sure that the positions in the extruder definitions are same as in the machine
            # definition
            extruder_definition = registry.findDefinitionContainers(id = extruder_definition_id)[0]
            position_in_extruder_def = extruder_definition.getMetaDataEntry("position")
            if position_in_extruder_def != position:
                ConfigurationErrorMessage.getInstance().addFaultyContainers(extruder_definition_id)
                return None #Don't return any container stack then, not the rest of the extruders either.

            new_extruder_id = registry.uniqueName(extruder_definition_id)
            new_extruder = cls.createExtruderStack(
                new_extruder_id,
                extruder_definition = extruder_definition,
                machine_definition_id = definition_id,
                position = position,
                variant_container = extruder_variant_container,
                material_container = material_container,
                quality_container = application.empty_quality_container,
                global_stack = new_global_stack,
            )
            new_extruder.setNextStack(new_global_stack)
            new_global_stack.addExtruder(new_extruder)

        for new_extruder in new_global_stack.extruders.values(): #Only register the extruders if we're sure that all of them are correct.
            registry.addContainer(new_extruder)

        preferred_quality_type = machine_definition.getMetaDataEntry("preferred_quality_type")
        quality_group_dict = quality_manager.getQualityGroups(new_global_stack)
        quality_group = quality_group_dict.get(preferred_quality_type)

        new_global_stack.quality = quality_group.node_for_global.getContainer()
        if not new_global_stack.quality:
            new_global_stack.quality = application.empty_quality_container
        for position, extruder_stack in new_global_stack.extruders.items():
            if position in quality_group.nodes_for_extruders and quality_group.nodes_for_extruders[position].getContainer():
                extruder_stack.quality = quality_group.nodes_for_extruders[position].getContainer()
            else:
                extruder_stack.quality = application.empty_quality_container

        # Register the global stack after the extruder stacks are created. This prevents the registry from adding another
        # extruder stack because the global stack didn't have one yet (which is enforced since Cura 3.1).
        registry.addContainer(new_global_stack)

        return new_global_stack
예제 #18
0
    def createMachine(cls, name: str,
                      definition_id: str) -> Optional[GlobalStack]:
        from cura.CuraApplication import CuraApplication
        application = CuraApplication.getInstance()
        variant_manager = application.getVariantManager()
        material_manager = application.getMaterialManager()
        quality_manager = application.getQualityManager()
        registry = application.getContainerRegistry()

        definitions = registry.findDefinitionContainers(id=definition_id)
        if not definitions:
            ConfigurationErrorMessage.getInstance().addFaultyContainers(
                definition_id)
            Logger.log("w",
                       "Definition {definition} was not found!",
                       definition=definition_id)
            return None

        machine_definition = definitions[0]

        # get variant container for the global stack
        global_variant_container = application.empty_variant_container
        global_variant_node = variant_manager.getDefaultVariantNode(
            machine_definition, VariantType.BUILD_PLATE)
        if global_variant_node:
            global_variant_container = global_variant_node.getContainer()
        if not global_variant_container:
            global_variant_container = application.empty_variant_container

        # get variant container for extruders
        extruder_variant_container = application.empty_variant_container
        extruder_variant_node = variant_manager.getDefaultVariantNode(
            machine_definition, VariantType.NOZZLE)
        extruder_variant_name = None
        if extruder_variant_node:
            extruder_variant_container = extruder_variant_node.getContainer()
            if not extruder_variant_container:
                extruder_variant_container = application.empty_variant_container
            extruder_variant_name = extruder_variant_container.getName()

        generated_name = registry.createUniqueName(
            "machine", "", name, machine_definition.getName())
        # Make sure the new name does not collide with any definition or (quality) profile
        # createUniqueName() only looks at other stacks, but not at definitions or quality profiles
        # Note that we don't go for uniqueName() immediately because that function matches with ignore_case set to true
        if registry.findContainersMetadata(id=generated_name):
            generated_name = registry.uniqueName(generated_name)

        new_global_stack = cls.createGlobalStack(
            new_stack_id=generated_name,
            definition=machine_definition,
            variant_container=global_variant_container,
            material_container=application.empty_material_container,
            quality_container=application.empty_quality_container,
        )
        new_global_stack.setName(generated_name)

        # Create ExtruderStacks
        extruder_dict = machine_definition.getMetaDataEntry(
            "machine_extruder_trains")

        for position, extruder_definition_id in extruder_dict.items():
            # Sanity check: make sure that the positions in the extruder definitions are same as in the machine
            # definition
            extruder_definition = registry.findDefinitionContainers(
                id=extruder_definition_id)[0]
            position_in_extruder_def = extruder_definition.getMetaDataEntry(
                "position")
            if position_in_extruder_def != position:
                ConfigurationErrorMessage.getInstance().addFaultyContainers(
                    extruder_definition_id)
                return None  #Don't return any container stack then, not the rest of the extruders either.

            # get material container for extruders
            material_container = application.empty_material_container
            material_node = material_manager.getDefaultMaterial(
                new_global_stack,
                position,
                extruder_variant_name,
                extruder_definition=extruder_definition)
            if material_node and material_node.getContainer():
                material_container = material_node.getContainer()

            new_extruder_id = registry.uniqueName(extruder_definition_id)
            new_extruder = cls.createExtruderStack(
                new_extruder_id,
                extruder_definition=extruder_definition,
                machine_definition_id=definition_id,
                position=position,
                variant_container=extruder_variant_container,
                material_container=material_container,
                quality_container=application.empty_quality_container)
            new_extruder.setNextStack(new_global_stack)
            new_global_stack.addExtruder(new_extruder)

        for new_extruder in new_global_stack.extruders.values(
        ):  #Only register the extruders if we're sure that all of them are correct.
            registry.addContainer(new_extruder)

        preferred_quality_type = machine_definition.getMetaDataEntry(
            "preferred_quality_type")
        quality_group_dict = quality_manager.getQualityGroups(new_global_stack)
        quality_group = quality_group_dict.get(preferred_quality_type)

        new_global_stack.quality = quality_group.node_for_global.getContainer()
        if not new_global_stack.quality:
            new_global_stack.quality = application.empty_quality_container
        for position, extruder_stack in new_global_stack.extruders.items():
            if position in quality_group.nodes_for_extruders and quality_group.nodes_for_extruders[
                    position].getContainer():
                extruder_stack.quality = quality_group.nodes_for_extruders[
                    position].getContainer()
            else:
                extruder_stack.quality = application.empty_quality_container

        # Register the global stack after the extruder stacks are created. This prevents the registry from adding another
        # extruder stack because the global stack didn't have one yet (which is enforced since Cura 3.1).
        registry.addContainer(new_global_stack)

        return new_global_stack
예제 #19
0
    def initialize(self):
        # Initialize the lookup tree for quality profiles with following structure:
        # <machine> -> <variant> -> <material>
        #           -> <material>

        self._machine_variant_material_quality_type_to_quality_dict = {
        }  # for quality lookup
        self._machine_quality_type_to_quality_changes_dict = {
        }  # for quality_changes lookup

        quality_metadata_list = self._container_registry.findContainersMetadata(
            type="quality")
        for metadata in quality_metadata_list:
            if metadata["id"] == "empty_quality":
                continue

            definition_id = metadata["definition"]
            quality_type = metadata["quality_type"]

            root_material_id = metadata.get("material")
            variant_name = metadata.get("variant")
            is_global_quality = metadata.get("global_quality", False)
            is_global_quality = is_global_quality or (root_material_id is None
                                                      and variant_name is None)

            # Sanity check: material+variant and is_global_quality cannot be present at the same time
            if is_global_quality and (root_material_id or variant_name):
                ConfigurationErrorMessage.getInstance().addFaultyContainers(
                    metadata["id"])
                continue

            if definition_id not in self._machine_variant_material_quality_type_to_quality_dict:
                self._machine_variant_material_quality_type_to_quality_dict[
                    definition_id] = QualityNode()
            machine_node = self._machine_variant_material_quality_type_to_quality_dict[
                definition_id]

            if is_global_quality:
                # For global qualities, save data in the machine node
                machine_node.addQualityMetadata(quality_type, metadata)
                continue

            if variant_name is not None:
                # If variant_name is specified in the quality/quality_changes profile, check if material is specified,
                # too.
                if variant_name not in machine_node.children_map:
                    machine_node.children_map[variant_name] = QualityNode()
                variant_node = machine_node.children_map[variant_name]

                if root_material_id is None:
                    # If only variant_name is specified but material is not, add the quality/quality_changes metadata
                    # into the current variant node.
                    variant_node.addQualityMetadata(quality_type, metadata)
                else:
                    # If only variant_name and material are both specified, go one level deeper: create a material node
                    # under the current variant node, and then add the quality/quality_changes metadata into the
                    # material node.
                    if root_material_id not in variant_node.children_map:
                        variant_node.children_map[
                            root_material_id] = QualityNode()
                    material_node = variant_node.children_map[root_material_id]

                    material_node.addQualityMetadata(quality_type, metadata)

            else:
                # If variant_name is not specified, check if material is specified.
                if root_material_id is not None:
                    if root_material_id not in machine_node.children_map:
                        machine_node.children_map[
                            root_material_id] = QualityNode()
                    material_node = machine_node.children_map[root_material_id]

                    material_node.addQualityMetadata(quality_type, metadata)

        # Initialize the lookup tree for quality_changes profiles with following structure:
        # <machine> -> <quality_type> -> <name>
        quality_changes_metadata_list = self._container_registry.findContainersMetadata(
            type="quality_changes")
        for metadata in quality_changes_metadata_list:
            if metadata["id"] == "empty_quality_changes":
                continue

            machine_definition_id = metadata["definition"]
            quality_type = metadata["quality_type"]

            if machine_definition_id not in self._machine_quality_type_to_quality_changes_dict:
                self._machine_quality_type_to_quality_changes_dict[
                    machine_definition_id] = QualityNode()
            machine_node = self._machine_quality_type_to_quality_changes_dict[
                machine_definition_id]
            machine_node.addQualityChangesMetadata(quality_type, metadata)

        Logger.log("d", "Lookup tables updated.")
        self.qualitiesUpdated.emit()
예제 #20
0
    def __addMaterialMetadataIntoLookupTree(self, material_metadata: Dict[str, Any]) -> None:
        material_id = material_metadata["id"]

        # We don't store empty material in the lookup tables
        if material_id == "empty_material":
            return

        root_material_id = material_metadata["base_file"]
        definition = material_metadata["definition"]
        approximate_diameter = material_metadata["approximate_diameter"]

        if approximate_diameter not in self._diameter_machine_nozzle_buildplate_material_map:
            self._diameter_machine_nozzle_buildplate_material_map[approximate_diameter] = {}

        machine_nozzle_buildplate_material_map = self._diameter_machine_nozzle_buildplate_material_map[
            approximate_diameter]
        if definition not in machine_nozzle_buildplate_material_map:
            machine_nozzle_buildplate_material_map[definition] = MaterialNode()

        # This is a list of information regarding the intermediate nodes:
        #    nozzle -> buildplate
        nozzle_name = material_metadata.get("variant_name")
        buildplate_name = material_metadata.get("buildplate_name")
        intermediate_node_info_list = [(nozzle_name, VariantType.NOZZLE),
                                       (buildplate_name, VariantType.BUILD_PLATE),
                                       ]

        variant_manager = self._application.getVariantManager()

        machine_node = machine_nozzle_buildplate_material_map[definition]
        current_node = machine_node
        current_intermediate_node_info_idx = 0
        error_message = None  # type: Optional[str]
        while current_intermediate_node_info_idx < len(intermediate_node_info_list):
            variant_name, variant_type = intermediate_node_info_list[current_intermediate_node_info_idx]
            if variant_name is not None:
                # The new material has a specific variant, so it needs to be added to that specific branch in the tree.
                variant = variant_manager.getVariantNode(definition, variant_name, variant_type)
                if variant is None:
                    error_message = "Material {id} contains a variant {name} that does not exist.".format(
                        id = material_metadata["id"], name = variant_name)
                    break

                # Update the current node to advance to a more specific branch
                if variant_name not in current_node.children_map:
                    current_node.children_map[variant_name] = MaterialNode()
                current_node = current_node.children_map[variant_name]

            current_intermediate_node_info_idx += 1

        if error_message is not None:
            Logger.log("e", "%s It will not be added into the material lookup tree.", error_message)
            self._container_registry.addWrongContainerId(material_metadata["id"])
            return

        # Add the material to the current tree node, which is the deepest (the most specific) branch we can find.
        # Sanity check: Make sure that there is no duplicated materials.
        if root_material_id in current_node.material_map:
            Logger.log("e", "Duplicated material [%s] with root ID [%s]. It has already been added.",
                       material_id, root_material_id)
            ConfigurationErrorMessage.getInstance().addFaultyContainers(root_material_id)
            return

        current_node.material_map[root_material_id] = MaterialNode(material_metadata)
예제 #21
0
    def createMachine(cls, name: str,
                      definition_id: str) -> Optional[GlobalStack]:
        from cura.CuraApplication import CuraApplication
        application = CuraApplication.getInstance()
        variant_manager = application.getVariantManager()
        quality_manager = application.getQualityManager()
        registry = application.getContainerRegistry()

        definitions = registry.findDefinitionContainers(id=definition_id)
        if not definitions:
            ConfigurationErrorMessage.getInstance().addFaultyContainers(
                definition_id)
            Logger.log("w",
                       "Definition {definition} was not found!",
                       definition=definition_id)
            return None

        machine_definition = definitions[0]

        # get variant container for the global stack
        global_variant_container = application.empty_variant_container
        global_variant_node = variant_manager.getDefaultVariantNode(
            machine_definition, VariantType.BUILD_PLATE)
        if global_variant_node:
            global_variant_container = global_variant_node.getContainer()
        if not global_variant_container:
            global_variant_container = application.empty_variant_container

        generated_name = registry.createUniqueName(
            "machine", "", name, machine_definition.getName())
        # Make sure the new name does not collide with any definition or (quality) profile
        # createUniqueName() only looks at other stacks, but not at definitions or quality profiles
        # Note that we don't go for uniqueName() immediately because that function matches with ignore_case set to true
        if registry.findContainersMetadata(id=generated_name):
            generated_name = registry.uniqueName(generated_name)

        new_global_stack = cls.createGlobalStack(
            new_stack_id=generated_name,
            definition=machine_definition,
            variant_container=global_variant_container,
            material_container=application.empty_material_container,
            quality_container=application.empty_quality_container,
        )
        new_global_stack.setName(generated_name)

        # Create ExtruderStacks
        extruder_dict = machine_definition.getMetaDataEntry(
            "machine_extruder_trains")
        for position in extruder_dict:
            cls.createExtruderStackWithDefaultSetup(new_global_stack, position)

        for new_extruder in new_global_stack.extruders.values(
        ):  #Only register the extruders if we're sure that all of them are correct.
            registry.addContainer(new_extruder)

        preferred_quality_type = machine_definition.getMetaDataEntry(
            "preferred_quality_type")
        quality_group_dict = quality_manager.getQualityGroups(new_global_stack)
        if not quality_group_dict:
            # There is no available quality group, set all quality containers to empty.
            new_global_stack.quality = application.empty_quality_container
            for extruder_stack in new_global_stack.extruders.values():
                extruder_stack.quality = application.empty_quality_container
        else:
            # Set the quality containers to the preferred quality type if available, otherwise use the first quality
            # type that's available.
            if preferred_quality_type not in quality_group_dict:
                Logger.log(
                    "w",
                    "The preferred quality {quality_type} doesn't exist for this set-up. Choosing a random one."
                    .format(quality_type=preferred_quality_type))
                preferred_quality_type = next(iter(quality_group_dict))
            quality_group = quality_group_dict.get(preferred_quality_type)

            new_global_stack.quality = quality_group.node_for_global.getContainer(
            )
            if not new_global_stack.quality:
                new_global_stack.quality = application.empty_quality_container
            for position, extruder_stack in new_global_stack.extruders.items():
                if position in quality_group.nodes_for_extruders and quality_group.nodes_for_extruders[
                        position].getContainer():
                    extruder_stack.quality = quality_group.nodes_for_extruders[
                        position].getContainer()
                else:
                    extruder_stack.quality = application.empty_quality_container

        # Register the global stack after the extruder stacks are created. This prevents the registry from adding another
        # extruder stack because the global stack didn't have one yet (which is enforced since Cura 3.1).
        registry.addContainer(new_global_stack)

        return new_global_stack
예제 #22
0
    def createMachine(cls, name: str, definition_id: str) -> Optional[GlobalStack]:
        from cura.CuraApplication import CuraApplication
        application = CuraApplication.getInstance()
        variant_manager = application.getVariantManager()
        quality_manager = application.getQualityManager()
        registry = application.getContainerRegistry()

        definitions = registry.findDefinitionContainers(id = definition_id)
        if not definitions:
            ConfigurationErrorMessage.getInstance().addFaultyContainers(definition_id)
            Logger.log("w", "Definition {definition} was not found!", definition = definition_id)
            return None

        machine_definition = definitions[0]

        # get variant container for the global stack
        global_variant_container = application.empty_variant_container
        global_variant_node = variant_manager.getDefaultVariantNode(machine_definition, VariantType.BUILD_PLATE)
        if global_variant_node:
            global_variant_container = global_variant_node.getContainer()
        if not global_variant_container:
            global_variant_container = application.empty_variant_container

        generated_name = registry.createUniqueName("machine", "", name, machine_definition.getName())
        # Make sure the new name does not collide with any definition or (quality) profile
        # createUniqueName() only looks at other stacks, but not at definitions or quality profiles
        # Note that we don't go for uniqueName() immediately because that function matches with ignore_case set to true
        if registry.findContainersMetadata(id = generated_name):
            generated_name = registry.uniqueName(generated_name)

        new_global_stack = cls.createGlobalStack(
            new_stack_id = generated_name,
            definition = machine_definition,
            variant_container = global_variant_container,
            material_container = application.empty_material_container,
            quality_container = application.empty_quality_container,
        )
        new_global_stack.setName(generated_name)

        # Create ExtruderStacks
        extruder_dict = machine_definition.getMetaDataEntry("machine_extruder_trains")
        for position in extruder_dict:
            cls.createExtruderStackWithDefaultSetup(new_global_stack, position)

        for new_extruder in new_global_stack.extruders.values(): #Only register the extruders if we're sure that all of them are correct.
            registry.addContainer(new_extruder)

        preferred_quality_type = machine_definition.getMetaDataEntry("preferred_quality_type")
        quality_group_dict = quality_manager.getQualityGroups(new_global_stack)
        if not quality_group_dict:
            # There is no available quality group, set all quality containers to empty.
            new_global_stack.quality = application.empty_quality_container
            for extruder_stack in new_global_stack.extruders.values():
                extruder_stack.quality = application.empty_quality_container
        else:
            # Set the quality containers to the preferred quality type if available, otherwise use the first quality
            # type that's available.
            if preferred_quality_type not in quality_group_dict:
                Logger.log("w", "The preferred quality {quality_type} doesn't exist for this set-up. Choosing a random one.".format(quality_type = preferred_quality_type))
                preferred_quality_type = next(iter(quality_group_dict))
            quality_group = quality_group_dict.get(preferred_quality_type)

            new_global_stack.quality = quality_group.node_for_global.getContainer()
            if not new_global_stack.quality:
                new_global_stack.quality = application.empty_quality_container
            for position, extruder_stack in new_global_stack.extruders.items():
                if position in quality_group.nodes_for_extruders and quality_group.nodes_for_extruders[position].getContainer():
                    extruder_stack.quality = quality_group.nodes_for_extruders[position].getContainer()
                else:
                    extruder_stack.quality = application.empty_quality_container

        # Register the global stack after the extruder stacks are created. This prevents the registry from adding another
        # extruder stack because the global stack didn't have one yet (which is enforced since Cura 3.1).
        registry.addContainer(new_global_stack)

        return new_global_stack
예제 #23
0
    def initialize(self):
        # Find all materials and put them in a matrix for quick search.
        material_metadatas = {metadata["id"]: metadata for metadata in
                              self._container_registry.findContainersMetadata(type = "material") if
                              metadata.get("GUID")}

        self._material_group_map = dict()
                
        # Map #1
        #    root_material_id -> MaterialGroup
        for material_id, material_metadata in material_metadatas.items():
            # We don't store empty material in the lookup tables
            if material_id == "empty_material":
                continue

            root_material_id = material_metadata.get("base_file")
            if root_material_id not in self._material_group_map:
                self._material_group_map[root_material_id] = MaterialGroup(root_material_id, MaterialNode(material_metadatas[root_material_id]))
                self._material_group_map[root_material_id].is_read_only = self._container_registry.isReadOnly(root_material_id)
            group = self._material_group_map[root_material_id]

            # Store this material in the group of the appropriate root material.
            if material_id != root_material_id:
                new_node = MaterialNode(material_metadata)
                group.derived_material_node_list.append(new_node)

        # Order this map alphabetically so it's easier to navigate in a debugger
        self._material_group_map = OrderedDict(sorted(self._material_group_map.items(), key = lambda x: x[0]))

        # Map #1.5
        #    GUID -> material group list
        self._guid_material_groups_map = defaultdict(list)
        for root_material_id, material_group in self._material_group_map.items():
            guid = material_group.root_material_node.metadata["GUID"]
            self._guid_material_groups_map[guid].append(material_group)

        # Map #2
        # Lookup table for material type -> fallback material metadata, only for read-only materials
        grouped_by_type_dict = dict()
        material_types_without_fallback = set()
        for root_material_id, material_node in self._material_group_map.items():
            material_type = material_node.root_material_node.metadata["material"]
            if material_type not in grouped_by_type_dict:
                grouped_by_type_dict[material_type] = {"generic": None,
                                                       "others": []}
                material_types_without_fallback.add(material_type)
            brand = material_node.root_material_node.metadata["brand"]
            if brand.lower() == "generic":
                to_add = True
                if material_type in grouped_by_type_dict:
                    diameter = material_node.root_material_node.metadata.get("approximate_diameter")
                    if diameter != self._default_approximate_diameter_for_quality_search:
                        to_add = False  # don't add if it's not the default diameter

                if to_add:
                    # Checking this first allow us to differentiate between not read only materials:
                    #  - if it's in the list, it means that is a new material without fallback
                    #  - if it is not, then it is a custom material with a fallback material (parent)
                    if material_type in material_types_without_fallback:
                        grouped_by_type_dict[material_type] = material_node.root_material_node.metadata
                        material_types_without_fallback.remove(material_type)

        # Remove the materials that have no fallback materials
        for material_type in material_types_without_fallback:
            del grouped_by_type_dict[material_type]
        self._fallback_materials_map = grouped_by_type_dict

        # Map #3
        # There can be multiple material profiles for the same material with different diameters, such as "generic_pla"
        # and "generic_pla_175". This is inconvenient when we do material-specific quality lookup because a quality can
        # be for either "generic_pla" or "generic_pla_175", but not both. This map helps to get the correct material ID
        # for quality search.
        self._material_diameter_map = defaultdict(dict)
        self._diameter_material_map = dict()

        # Group the material IDs by the same name, material, brand, and color but with different diameters.
        material_group_dict = dict()
        keys_to_fetch = ("name", "material", "brand", "color")
        for root_material_id, machine_node in self._material_group_map.items():
            root_material_metadata = machine_node.root_material_node.metadata

            key_data = []
            for key in keys_to_fetch:
                key_data.append(root_material_metadata.get(key))
            key_data = tuple(key_data)

            # If the key_data doesn't exist, it doesn't matter if the material is read only...
            if key_data not in material_group_dict:
                material_group_dict[key_data] = dict()
            else:
                # ...but if key_data exists, we just overwrite it if the material is read only, otherwise we skip it
                if not machine_node.is_read_only:
                    continue
            approximate_diameter = root_material_metadata.get("approximate_diameter")
            material_group_dict[key_data][approximate_diameter] = root_material_metadata["id"]

        # Map [root_material_id][diameter] -> root_material_id for this diameter
        for data_dict in material_group_dict.values():
            for root_material_id1 in data_dict.values():
                if root_material_id1 in self._material_diameter_map:
                    continue
                diameter_map = data_dict
                for root_material_id2 in data_dict.values():
                    self._material_diameter_map[root_material_id2] = diameter_map

            default_root_material_id = data_dict.get(self._default_approximate_diameter_for_quality_search)
            if default_root_material_id is None:
                default_root_material_id = list(data_dict.values())[0]  # no default diameter present, just take "the" only one
            for root_material_id in data_dict.values():
                self._diameter_material_map[root_material_id] = default_root_material_id

        # Map #4
        #    "machine" -> "variant_name" -> "root material ID" -> specific material InstanceContainer
        # Construct the "machine" -> "variant" -> "root material ID" -> specific material InstanceContainer
        self._diameter_machine_variant_material_map = dict()
        for material_metadata in material_metadatas.values():
            # We don't store empty material in the lookup tables
            if material_metadata["id"] == "empty_material":
                continue

            root_material_id = material_metadata["base_file"]
            definition = material_metadata["definition"]
            approximate_diameter = material_metadata["approximate_diameter"]

            if approximate_diameter not in self._diameter_machine_variant_material_map:
                self._diameter_machine_variant_material_map[approximate_diameter] = {}

            machine_variant_material_map = self._diameter_machine_variant_material_map[approximate_diameter]
            if definition not in machine_variant_material_map:
                machine_variant_material_map[definition] = MaterialNode()

            machine_node = machine_variant_material_map[definition]
            variant_name = material_metadata.get("variant_name")
            if not variant_name:
                # if there is no variant, this material is for the machine, so put its metadata in the machine node.
                machine_node.material_map[root_material_id] = MaterialNode(material_metadata)
            else:
                # this material is variant-specific, so we save it in a variant-specific node under the
                # machine-specific node

                # Check first if the variant exist in the manager
                existing_variant = self._application.getVariantManager().getVariantNode(definition, variant_name)
                if existing_variant is not None:
                    if variant_name not in machine_node.children_map:
                        machine_node.children_map[variant_name] = MaterialNode()

                    variant_node = machine_node.children_map[variant_name]
                    if root_material_id in variant_node.material_map:  # We shouldn't have duplicated variant-specific materials for the same machine.
                        ConfigurationErrorMessage.getInstance().addFaultyContainers(root_material_id)
                        continue
                    variant_node.material_map[root_material_id] = MaterialNode(material_metadata)
                else:
                    # Add this container id to the wrong containers list in the registry
                    Logger.log("w", "Not adding {id} to the material manager because the variant does not exist.".format(id = material_metadata["id"]))
                    self._container_registry.addWrongContainerId(material_metadata["id"])

        self.materialsUpdated.emit()
예제 #24
0
    def __addMaterialMetadataIntoLookupTree(
            self, material_metadata: Dict[str, Any]) -> None:
        material_id = material_metadata["id"]

        # We don't store empty material in the lookup tables
        if material_id == "empty_material":
            return

        root_material_id = material_metadata["base_file"]
        definition = material_metadata["definition"]
        approximate_diameter = material_metadata["approximate_diameter"]

        if approximate_diameter not in self._diameter_machine_nozzle_buildplate_material_map:
            self._diameter_machine_nozzle_buildplate_material_map[
                approximate_diameter] = {}

        machine_nozzle_buildplate_material_map = self._diameter_machine_nozzle_buildplate_material_map[
            approximate_diameter]
        if definition not in machine_nozzle_buildplate_material_map:
            machine_nozzle_buildplate_material_map[definition] = MaterialNode()

        # This is a list of information regarding the intermediate nodes:
        #    nozzle -> buildplate
        nozzle_name = material_metadata.get("variant_name")
        buildplate_name = material_metadata.get("buildplate_name")
        intermediate_node_info_list = [
            (nozzle_name, VariantType.NOZZLE),
            (buildplate_name, VariantType.BUILD_PLATE),
        ]

        variant_manager = self._application.getVariantManager()

        machine_node = machine_nozzle_buildplate_material_map[definition]
        current_node = machine_node
        current_intermediate_node_info_idx = 0
        error_message = None  # type: Optional[str]
        while current_intermediate_node_info_idx < len(
                intermediate_node_info_list):
            variant_name, variant_type = intermediate_node_info_list[
                current_intermediate_node_info_idx]
            if variant_name is not None:
                # The new material has a specific variant, so it needs to be added to that specific branch in the tree.
                variant = variant_manager.getVariantNode(
                    definition, variant_name, variant_type)
                if variant is None:
                    error_message = "Material {id} contains a variant {name} that does not exist.".format(
                        id=material_metadata["id"], name=variant_name)
                    break

                # Update the current node to advance to a more specific branch
                if variant_name not in current_node.children_map:
                    current_node.children_map[variant_name] = MaterialNode()
                current_node = current_node.children_map[variant_name]

            current_intermediate_node_info_idx += 1

        if error_message is not None:
            Logger.log(
                "e", "%s It will not be added into the material lookup tree.",
                error_message)
            self._container_registry.addWrongContainerId(
                material_metadata["id"])
            return

        # Add the material to the current tree node, which is the deepest (the most specific) branch we can find.
        # Sanity check: Make sure that there is no duplicated materials.
        if root_material_id in current_node.material_map:
            Logger.log(
                "e",
                "Duplicated material [%s] with root ID [%s]. It has already been added.",
                material_id, root_material_id)
            ConfigurationErrorMessage.getInstance().addFaultyContainers(
                root_material_id)
            return

        current_node.material_map[root_material_id] = MaterialNode(
            material_metadata)
예제 #25
0
    def startSplashWindowPhase(self) -> None:
        super().startSplashWindowPhase()
        i18n_catalog = i18nCatalog("uranium")
        self.showSplashMessage(
            i18n_catalog.i18nc("@info:progress",
                               "Initializing package manager..."))
        self._package_manager.initialize()

        # Read preferences here (upgrade won't work) to get the language in use, so the splash window can be shown in
        # the correct language.
        try:
            preferences_filename = Resources.getPath(Resources.Preferences,
                                                     self._app_name + ".cfg")
            self._preferences.readFromFile(preferences_filename)
        except FileNotFoundError:
            Logger.log(
                "i",
                "Preferences file not found, ignore and use default language '%s'",
                self._default_language)

        signal.signal(signal.SIGINT, signal.SIG_DFL)
        # This is done here as a lot of plugins require a correct gl context. If you want to change the framework,
        # these checks need to be done in your <framework>Application.py class __init__().

        self._configuration_error_message = ConfigurationErrorMessage(
            self,
            i18n_catalog.i18nc("@info:status",
                               "Your configuration seems to be corrupt."),
            lifetime=0,
            title=i18n_catalog.i18nc("@info:title", "Configuration errors"))
        # Remove, install, and then loading plugins
        self.showSplashMessage(
            i18n_catalog.i18nc("@info:progress", "Loading plugins..."))
        # Remove and install the plugins that have been scheduled
        self._plugin_registry.initializeBeforePluginsAreLoaded()
        self._loadPlugins()
        self._plugin_registry.checkRequiredPlugins(self.getRequiredPlugins())
        self.pluginsLoaded.emit()

        self.showSplashMessage(
            i18n_catalog.i18nc("@info:progress", "Updating configuration..."))
        with self._container_registry.lockFile():
            VersionUpgradeManager.getInstance().upgrade()

        # Load preferences again because before we have loaded the plugins, we don't have the upgrade routine for
        # the preferences file. Now that we have, load the preferences file again so it can be upgraded and loaded.
        self.showSplashMessage(
            i18n_catalog.i18nc("@info:progress", "Loading preferences..."))
        try:
            preferences_filename = Resources.getPath(Resources.Preferences,
                                                     self._app_name + ".cfg")
            with open(preferences_filename, "r", encoding="utf-8") as f:
                serialized = f.read()
            # This performs the upgrade for Preferences
            self._preferences.deserialize(serialized)
            self._preferences.setValue("general/plugins_to_remove", "")
            self._preferences.writeToFile(preferences_filename)
        except (FileNotFoundError, UnicodeDecodeError):
            Logger.log(
                "i",
                "The preferences file cannot be found or it is corrupted, so we will use default values"
            )

        self.processEvents()
        # Force the configuration file to be written again since the list of plugins to remove maybe changed
        try:
            self._preferences_filename = Resources.getPath(
                Resources.Preferences, self._app_name + ".cfg")
            self._preferences.readFromFile(self._preferences_filename)
        except FileNotFoundError:
            Logger.log(
                "i",
                "The preferences file '%s' cannot be found, will use default values",
                self._preferences_filename)
            self._preferences_filename = Resources.getStoragePath(
                Resources.Preferences, self._app_name + ".cfg")

        # FIXME: This is done here because we now use "plugins.json" to manage plugins instead of the Preferences file,
        # but the PluginRegistry will still import data from the Preferences files if present, such as disabled plugins,
        # so we need to reset those values AFTER the Preferences file is loaded.
        self._plugin_registry.initializeAfterPluginsAreLoaded()

        # Check if we have just updated from an older version
        self._preferences.addPreference("general/last_run_version", "")
        last_run_version_str = self._preferences.getValue(
            "general/last_run_version")
        if not last_run_version_str:
            last_run_version_str = self._version
        last_run_version = Version(last_run_version_str)
        current_version = Version(self._version)
        if last_run_version < current_version:
            self._just_updated_from_old_version = True
        self._preferences.setValue("general/last_run_version",
                                   str(current_version))
        self._preferences.writeToFile(self._preferences_filename)

        # Preferences: recent files
        self._preferences.addPreference("%s/recent_files" % self._app_name, "")
        file_names = self._preferences.getValue("%s/recent_files" %
                                                self._app_name).split(";")
        for file_name in file_names:
            if not os.path.isfile(file_name):
                continue
            self._recent_files.append(QUrl.fromLocalFile(file_name))

        if not self.getIsHeadLess():
            # Initialize System tray icon and make it invisible because it is used only to show pop up messages
            self._tray_icon = None
            if self._tray_icon_name:
                self._tray_icon = QIcon(
                    Resources.getPath(Resources.Images, self._tray_icon_name))
                self._tray_icon_widget = QSystemTrayIcon(self._tray_icon)
                self._tray_icon_widget.setVisible(False)
예제 #26
0
    def initialize(self) -> None:
        # Initialize the lookup tree for quality profiles with following structure:
        # <machine> -> <nozzle> -> <buildplate> -> <material>
        # <machine> -> <material>

        self._machine_nozzle_buildplate_material_quality_type_to_quality_dict = {
        }  # for quality lookup
        self._machine_quality_type_to_quality_changes_dict = {
        }  # for quality_changes lookup

        quality_metadata_list = self._container_registry.findContainersMetadata(
            type="quality")
        for metadata in quality_metadata_list:
            if metadata["id"] == "empty_quality":
                continue

            definition_id = metadata["definition"]
            quality_type = metadata["quality_type"]

            root_material_id = metadata.get("material")
            nozzle_name = metadata.get("variant")
            buildplate_name = metadata.get("buildplate")
            is_global_quality = metadata.get("global_quality", False)
            is_global_quality = is_global_quality or (
                root_material_id is None and nozzle_name is None
                and buildplate_name is None)

            # Sanity check: material+variant and is_global_quality cannot be present at the same time
            if is_global_quality and (root_material_id or nozzle_name):
                ConfigurationErrorMessage.getInstance().addFaultyContainers(
                    metadata["id"])
                continue

            if definition_id not in self._machine_nozzle_buildplate_material_quality_type_to_quality_dict:
                self._machine_nozzle_buildplate_material_quality_type_to_quality_dict[
                    definition_id] = QualityNode()
            machine_node = cast(
                QualityNode, self.
                _machine_nozzle_buildplate_material_quality_type_to_quality_dict[
                    definition_id])

            if is_global_quality:
                # For global qualities, save data in the machine node
                machine_node.addQualityMetadata(quality_type, metadata)
                continue

            current_node = machine_node
            intermediate_node_info_list = [
                nozzle_name, buildplate_name, root_material_id
            ]
            current_intermediate_node_info_idx = 0

            while current_intermediate_node_info_idx < len(
                    intermediate_node_info_list):
                node_name = intermediate_node_info_list[
                    current_intermediate_node_info_idx]
                if node_name is not None:
                    # There is specific information, update the current node to go deeper so we can add this quality
                    # at the most specific branch in the lookup tree.
                    if node_name not in current_node.children_map:
                        current_node.children_map[node_name] = QualityNode()
                    current_node = cast(QualityNode,
                                        current_node.children_map[node_name])

                current_intermediate_node_info_idx += 1

            current_node.addQualityMetadata(quality_type, metadata)

        # Initialize the lookup tree for quality_changes profiles with following structure:
        # <machine> -> <quality_type> -> <name>
        quality_changes_metadata_list = self._container_registry.findContainersMetadata(
            type="quality_changes")
        for metadata in quality_changes_metadata_list:
            if metadata["id"] == "empty_quality_changes":
                continue

            machine_definition_id = metadata["definition"]
            quality_type = metadata["quality_type"]

            if machine_definition_id not in self._machine_quality_type_to_quality_changes_dict:
                self._machine_quality_type_to_quality_changes_dict[
                    machine_definition_id] = QualityNode()
            machine_node = self._machine_quality_type_to_quality_changes_dict[
                machine_definition_id]
            machine_node.addQualityChangesMetadata(quality_type, metadata)

        Logger.log("d", "Lookup tables updated.")
        self.qualitiesUpdated.emit()
예제 #27
0
    def initialize(self):
        # Find all materials and put them in a matrix for quick search.
        material_metadatas = {
            metadata["id"]: metadata
            for metadata in self._container_registry.findContainersMetadata(
                type="material") if metadata.get("GUID")
        }

        self._material_group_map = dict()

        # Map #1
        #    root_material_id -> MaterialGroup
        for material_id, material_metadata in material_metadatas.items():
            # We don't store empty material in the lookup tables
            if material_id == "empty_material":
                continue

            root_material_id = material_metadata.get("base_file")
            if root_material_id not in self._material_group_map:
                self._material_group_map[root_material_id] = MaterialGroup(
                    root_material_id,
                    MaterialNode(material_metadatas[root_material_id]))
                self._material_group_map[
                    root_material_id].is_read_only = self._container_registry.isReadOnly(
                        root_material_id)
            group = self._material_group_map[root_material_id]

            # Store this material in the group of the appropriate root material.
            if material_id != root_material_id:
                new_node = MaterialNode(material_metadata)
                group.derived_material_node_list.append(new_node)

        # Order this map alphabetically so it's easier to navigate in a debugger
        self._material_group_map = OrderedDict(
            sorted(self._material_group_map.items(), key=lambda x: x[0]))

        # Map #1.5
        #    GUID -> material group list
        self._guid_material_groups_map = defaultdict(list)
        for root_material_id, material_group in self._material_group_map.items(
        ):
            guid = material_group.root_material_node.metadata["GUID"]
            self._guid_material_groups_map[guid].append(material_group)

        # Map #2
        # Lookup table for material type -> fallback material metadata, only for read-only materials
        grouped_by_type_dict = dict()
        material_types_without_fallback = set()
        for root_material_id, material_node in self._material_group_map.items(
        ):
            material_type = material_node.root_material_node.metadata[
                "material"]
            if material_type not in grouped_by_type_dict:
                grouped_by_type_dict[material_type] = {
                    "generic": None,
                    "others": []
                }
                material_types_without_fallback.add(material_type)
            brand = material_node.root_material_node.metadata["brand"]
            if brand.lower() == "generic":
                to_add = True
                if material_type in grouped_by_type_dict:
                    diameter = material_node.root_material_node.metadata.get(
                        "approximate_diameter")
                    if diameter != self._default_approximate_diameter_for_quality_search:
                        to_add = False  # don't add if it's not the default diameter

                if to_add:
                    # Checking this first allow us to differentiate between not read only materials:
                    #  - if it's in the list, it means that is a new material without fallback
                    #  - if it is not, then it is a custom material with a fallback material (parent)
                    if material_type in material_types_without_fallback:
                        grouped_by_type_dict[
                            material_type] = material_node.root_material_node.metadata
                        material_types_without_fallback.remove(material_type)

        # Remove the materials that have no fallback materials
        for material_type in material_types_without_fallback:
            del grouped_by_type_dict[material_type]
        self._fallback_materials_map = grouped_by_type_dict

        # Map #3
        # There can be multiple material profiles for the same material with different diameters, such as "generic_pla"
        # and "generic_pla_175". This is inconvenient when we do material-specific quality lookup because a quality can
        # be for either "generic_pla" or "generic_pla_175", but not both. This map helps to get the correct material ID
        # for quality search.
        self._material_diameter_map = defaultdict(dict)
        self._diameter_material_map = dict()

        # Group the material IDs by the same name, material, brand, and color but with different diameters.
        material_group_dict = dict()
        keys_to_fetch = ("name", "material", "brand", "color")
        for root_material_id, machine_node in self._material_group_map.items():
            root_material_metadata = machine_node.root_material_node.metadata

            key_data = []
            for key in keys_to_fetch:
                key_data.append(root_material_metadata.get(key))
            key_data = tuple(key_data)

            # If the key_data doesn't exist, it doesn't matter if the material is read only...
            if key_data not in material_group_dict:
                material_group_dict[key_data] = dict()
            else:
                # ...but if key_data exists, we just overwrite it if the material is read only, otherwise we skip it
                if not machine_node.is_read_only:
                    continue
            approximate_diameter = root_material_metadata.get(
                "approximate_diameter")
            material_group_dict[key_data][
                approximate_diameter] = root_material_metadata["id"]

        # Map [root_material_id][diameter] -> root_material_id for this diameter
        for data_dict in material_group_dict.values():
            for root_material_id1 in data_dict.values():
                if root_material_id1 in self._material_diameter_map:
                    continue
                diameter_map = data_dict
                for root_material_id2 in data_dict.values():
                    self._material_diameter_map[
                        root_material_id2] = diameter_map

            default_root_material_id = data_dict.get(
                self._default_approximate_diameter_for_quality_search)
            if default_root_material_id is None:
                default_root_material_id = list(data_dict.values())[
                    0]  # no default diameter present, just take "the" only one
            for root_material_id in data_dict.values():
                self._diameter_material_map[
                    root_material_id] = default_root_material_id

        # Map #4
        #    "machine" -> "variant_name" -> "root material ID" -> specific material InstanceContainer
        # Construct the "machine" -> "variant" -> "root material ID" -> specific material InstanceContainer
        self._diameter_machine_variant_material_map = dict()
        for material_metadata in material_metadatas.values():
            # We don't store empty material in the lookup tables
            if material_metadata["id"] == "empty_material":
                continue

            root_material_id = material_metadata["base_file"]
            definition = material_metadata["definition"]
            approximate_diameter = material_metadata["approximate_diameter"]

            if approximate_diameter not in self._diameter_machine_variant_material_map:
                self._diameter_machine_variant_material_map[
                    approximate_diameter] = {}

            machine_variant_material_map = self._diameter_machine_variant_material_map[
                approximate_diameter]
            if definition not in machine_variant_material_map:
                machine_variant_material_map[definition] = MaterialNode()

            machine_node = machine_variant_material_map[definition]
            variant_name = material_metadata.get("variant_name")
            if not variant_name:
                # if there is no variant, this material is for the machine, so put its metadata in the machine node.
                machine_node.material_map[root_material_id] = MaterialNode(
                    material_metadata)
            else:
                # this material is variant-specific, so we save it in a variant-specific node under the
                # machine-specific node

                # Check first if the variant exist in the manager
                existing_variant = self._application.getVariantManager(
                ).getVariantNode(definition, variant_name)
                if existing_variant is not None:
                    if variant_name not in machine_node.children_map:
                        machine_node.children_map[variant_name] = MaterialNode(
                        )

                    variant_node = machine_node.children_map[variant_name]
                    if root_material_id in variant_node.material_map:  # We shouldn't have duplicated variant-specific materials for the same machine.
                        ConfigurationErrorMessage.getInstance(
                        ).addFaultyContainers(root_material_id)
                        continue
                    variant_node.material_map[root_material_id] = MaterialNode(
                        material_metadata)
                else:
                    # Add this container id to the wrong containers list in the registry
                    Logger.log(
                        "w",
                        "Not adding {id} to the material manager because the variant does not exist."
                        .format(id=material_metadata["id"]))
                    self._container_registry.addWrongContainerId(
                        material_metadata["id"])

        self.materialsUpdated.emit()
예제 #28
0
    def initialize(self) -> None:
        # Initialize the lookup tree for quality profiles with following structure:
        # <machine> -> <nozzle> -> <buildplate> -> <material>
        # <machine> -> <material>

        self._machine_nozzle_buildplate_material_quality_type_to_quality_dict = {}  # for quality lookup
        self._machine_quality_type_to_quality_changes_dict = {}  # for quality_changes lookup

        quality_metadata_list = self._container_registry.findContainersMetadata(type = "quality")
        for metadata in quality_metadata_list:
            if metadata["id"] == "empty_quality":
                continue

            definition_id = metadata["definition"]
            quality_type = metadata["quality_type"]

            root_material_id = metadata.get("material")
            nozzle_name = metadata.get("variant")
            buildplate_name = metadata.get("buildplate")
            is_global_quality = metadata.get("global_quality", False)
            is_global_quality = is_global_quality or (root_material_id is None and nozzle_name is None and buildplate_name is None)

            # Sanity check: material+variant and is_global_quality cannot be present at the same time
            if is_global_quality and (root_material_id or nozzle_name):
                ConfigurationErrorMessage.getInstance().addFaultyContainers(metadata["id"])
                continue

            if definition_id not in self._machine_nozzle_buildplate_material_quality_type_to_quality_dict:
                self._machine_nozzle_buildplate_material_quality_type_to_quality_dict[definition_id] = QualityNode()
            machine_node = cast(QualityNode, self._machine_nozzle_buildplate_material_quality_type_to_quality_dict[definition_id])

            if is_global_quality:
                # For global qualities, save data in the machine node
                machine_node.addQualityMetadata(quality_type, metadata)
                continue

            current_node = machine_node
            intermediate_node_info_list = [nozzle_name, buildplate_name, root_material_id]
            current_intermediate_node_info_idx = 0

            while current_intermediate_node_info_idx < len(intermediate_node_info_list):
                node_name = intermediate_node_info_list[current_intermediate_node_info_idx]
                if node_name is not None:
                    # There is specific information, update the current node to go deeper so we can add this quality
                    # at the most specific branch in the lookup tree.
                    if node_name not in current_node.children_map:
                        current_node.children_map[node_name] = QualityNode()
                    current_node = cast(QualityNode, current_node.children_map[node_name])

                current_intermediate_node_info_idx += 1

            current_node.addQualityMetadata(quality_type, metadata)

        # Initialize the lookup tree for quality_changes profiles with following structure:
        # <machine> -> <quality_type> -> <name>
        quality_changes_metadata_list = self._container_registry.findContainersMetadata(type = "quality_changes")
        for metadata in quality_changes_metadata_list:
            if metadata["id"] == "empty_quality_changes":
                continue

            machine_definition_id = metadata["definition"]
            quality_type = metadata["quality_type"]

            if machine_definition_id not in self._machine_quality_type_to_quality_changes_dict:
                self._machine_quality_type_to_quality_changes_dict[machine_definition_id] = QualityNode()
            machine_node = self._machine_quality_type_to_quality_changes_dict[machine_definition_id]
            machine_node.addQualityChangesMetadata(quality_type, metadata)

        Logger.log("d", "Lookup tables updated.")
        self.qualitiesUpdated.emit()