示例#1
0
    def getDefaultVariantNode(self, machine_definition: "DefinitionContainer",
                              variant_type: VariantType) -> Optional["ContainerNode"]:
        machine_definition_id = machine_definition.getId()
        preferred_variant_name = None
        if variant_type == VariantType.BUILD_PLATE:
            if parseBool(machine_definition.getMetaDataEntry("has_variant_buildplates", False)):
                preferred_variant_name = machine_definition.getMetaDataEntry("preferred_variant_buildplate_name")
        else:
            if parseBool(machine_definition.getMetaDataEntry("has_variants", False)):
                preferred_variant_name = machine_definition.getMetaDataEntry("preferred_variant_name")

        node = None
        if preferred_variant_name:
            node = self.getVariantNode(machine_definition_id, preferred_variant_name, variant_type)
        return node
示例#2
0
    def _update(self):
        Logger.log("d", "Updating {model_class_name}.".format(model_class_name = self.__class__.__name__))

        global_stack = self._machine_manager.activeMachine
        if global_stack is None:
            self.setItems([])
            return

        has_variants = parseBool(global_stack.getMetaDataEntry("has_variants", False))
        if not has_variants:
            self.setItems([])
            return

        variant_node_dict = self._variant_manager.getVariantNodes(global_stack, VariantType.NOZZLE)
        if not variant_node_dict:
            self.setItems([])
            return

        item_list = []
        for hotend_name, container_node in sorted(variant_node_dict.items(), key = lambda i: i[0].upper()):
            item = {"id": hotend_name,
                    "hotend_name": hotend_name,
                    "container_node": container_node
                    }

            item_list.append(item)

        self.setItems(item_list)
示例#3
0
    def getDefaultMaterial(self, global_stack: "GlobalStack", position: str, nozzle_name: Optional[str],
                           extruder_definition: Optional["DefinitionContainer"] = None) -> Optional["MaterialNode"]:
        node = None

        buildplate_name = global_stack.getBuildplateName()
        machine_definition = global_stack.definition

        # The extruder-compatible material diameter in the extruder definition may not be the correct value because
        # the user can change it in the definition_changes container.
        if extruder_definition is None:
            extruder_stack_or_definition = global_stack.extruders[position]
            is_extruder_stack = True
        else:
            extruder_stack_or_definition = extruder_definition
            is_extruder_stack = False

        if extruder_stack_or_definition and parseBool(global_stack.getMetaDataEntry("has_materials", False)):
            if is_extruder_stack:
                material_diameter = extruder_stack_or_definition.getCompatibleMaterialDiameter()
            else:
                material_diameter = extruder_stack_or_definition.getProperty("material_diameter", "value")

            if isinstance(material_diameter, SettingFunction):
                material_diameter = material_diameter(global_stack)
            approximate_material_diameter = str(round(material_diameter))
            root_material_id = machine_definition.getMetaDataEntry("preferred_material")
            root_material_id = self.getRootMaterialIDForDiameter(root_material_id, approximate_material_diameter)
            node = self.getMaterialNode(machine_definition.getId(), nozzle_name, buildplate_name,
                                        material_diameter, root_material_id)
        return node
示例#4
0
    def getDefaultVariantNode(self, machine_definition: "DefinitionContainer",
                              variant_type: "VariantType",
                              global_stack: Optional["GlobalStack"] = None) -> Optional["ContainerNode"]:
        machine_definition_id = machine_definition.getId()
        container_for_metadata_fetching = global_stack if global_stack is not None else machine_definition

        preferred_variant_name = None
        if variant_type == VariantType.BUILD_PLATE:
            if parseBool(container_for_metadata_fetching.getMetaDataEntry("has_variant_buildplates", False)):
                preferred_variant_name = container_for_metadata_fetching.getMetaDataEntry("preferred_variant_buildplate_name")
        else:
            if parseBool(container_for_metadata_fetching.getMetaDataEntry("has_variants", False)):
                preferred_variant_name = container_for_metadata_fetching.getMetaDataEntry("preferred_variant_name")

        node = None
        if preferred_variant_name:
            node = self.getVariantNode(machine_definition_id, preferred_variant_name, variant_type)
        return node
示例#5
0
def getMachineDefinitionIDForQualitySearch(machine_definition: "DefinitionContainerInterface",
                                           default_definition_id: str = "fdmprinter") -> str:
    machine_definition_id = default_definition_id
    if parseBool(machine_definition.getMetaDataEntry("has_machine_quality", False)):
        # Only use the machine's own quality definition ID if this machine has machine quality.
        machine_definition_id = machine_definition.getMetaDataEntry("quality_definition")
        if machine_definition_id is None:
            machine_definition_id = machine_definition.getId()

    return machine_definition_id
示例#6
0
    def _getMaterialContainerIdForActiveMachine(self, base_file):
        global_stack = Application.getInstance().getGlobalContainerStack()
        if not global_stack:
            return base_file

        has_machine_materials = parseBool(global_stack.getMetaDataEntry("has_machine_materials", default = False))
        has_variant_materials = parseBool(global_stack.getMetaDataEntry("has_variant_materials", default = False))
        has_variants = parseBool(global_stack.getMetaDataEntry("has_variants", default = False))
        if has_machine_materials or has_variant_materials:
            if has_variants:
                materials = self._container_registry.findInstanceContainers(type = "material", base_file = base_file, definition = global_stack.getBottom().getId(), variant = self._machine_manager.activeVariantId)
            else:
                materials = self._container_registry.findInstanceContainers(type = "material", base_file = base_file, definition = global_stack.getBottom().getId())

            if materials:
                return materials[0].getId()

            Logger.log("w", "Unable to find a suitable container based on %s for the current machine .", base_file)
            return "" # do not activate a new material if a container can not be found

        return base_file
示例#7
0
 def getDefaultMaterial(self, global_stack: "GlobalStack", extruder_variant_name: Optional[str]) -> Optional["MaterialNode"]:
     node = None
     machine_definition = global_stack.definition
     if parseBool(global_stack.getMetaDataEntry("has_materials", False)):
         material_diameter = machine_definition.getProperty("material_diameter", "value")
         if isinstance(material_diameter, SettingFunction):
             material_diameter = material_diameter(global_stack)
         approximate_material_diameter = str(round(material_diameter))
         root_material_id = machine_definition.getMetaDataEntry("preferred_material")
         root_material_id = self.getRootMaterialIDForDiameter(root_material_id, approximate_material_diameter)
         node = self.getMaterialNode(machine_definition.getId(), extruder_variant_name,
                                     material_diameter, root_material_id)
     return node
    def reCheckConnections(self) -> None:
        global_container_stack = Application.getInstance().getGlobalContainerStack()
        if not global_container_stack:
            return

        for key in self._instances:
            if key == global_container_stack.getMetaDataEntry("octoprint_id"):
                api_key = global_container_stack.getMetaDataEntry("octoprint_api_key", "")
                self._instances[key].setApiKey(self._deobfuscateString(api_key))
                self._instances[key].setShowCamera(parseBool(global_container_stack.getMetaDataEntry("octoprint_show_camera", "false")))
                self._instances[key].connectionStateChanged.connect(self._onInstanceConnectionStateChanged)
                self._instances[key].connect()
            else:
                if self._instances[key].isConnected():
                    self._instances[key].close()
示例#9
0
    def discoveredPrinters(self) -> List["DiscoveredPrinter"]:
        item_list = list(
            x for x in self._discovered_printer_by_ip_dict.values() if not parseBool(x.device.getProperty("temporary")))

        # Split the printers into 2 lists and sort them ascending based on names.
        available_list = []
        not_available_list = []
        for item in item_list:
            if item.isUnknownMachineType or getattr(item.device, "clusterSize", 1) < 1:
                not_available_list.append(item)
            else:
                available_list.append(item)

        available_list.sort(key = lambda x: x.device.name)
        not_available_list.sort(key = lambda x: x.device.name)

        return available_list + not_available_list
示例#10
0
    def _update(self):
        Logger.log("d", "Updating {model_class_name}.".format(model_class_name = self.__class__.__name__))
        global_stack = self._machine_manager._global_container_stack
        if not global_stack:
            self.setItems([])
            return

        has_variants = parseBool(global_stack.getMetaDataEntry("has_variant_buildplates", False))
        if not has_variants:
            self.setItems([])
            return

        variant_dict = self._variant_manager.getVariantNodes(global_stack, variant_type = VariantType.BUILD_PLATE)

        item_list = []
        for name, variant_node in variant_dict.items():
            item = {"name": name,
                    "container_node": variant_node}
            item_list.append(item)
        self.setItems(item_list)
示例#11
0
    def getMaterialNodeByType(self, global_stack: "GlobalStack", extruder_variant_name: str, material_guid: str) -> Optional["MaterialNode"]:
        node = None
        machine_definition = global_stack.definition
        if parseBool(machine_definition.getMetaDataEntry("has_materials", False)):
            material_diameter = machine_definition.getProperty("material_diameter", "value")
            if isinstance(material_diameter, SettingFunction):
                material_diameter = material_diameter(global_stack)

            # Look at the guid to material dictionary
            root_material_id = None
            for material_group in self._guid_material_groups_map[material_guid]:
                if material_group.is_read_only:
                    root_material_id = material_group.root_material_node.metadata["id"]
                    break

            if not root_material_id:
                Logger.log("i", "Cannot find materials with guid [%s] ", material_guid)
                return None

            node = self.getMaterialNode(machine_definition.getId(), extruder_variant_name,
                                        material_diameter, root_material_id)
        return node
示例#12
0
    def getMaterialNodeByType(self, global_stack: "GlobalStack", position: str, nozzle_name: str,
                              buildplate_name: Optional[str], material_guid: str) -> Optional["MaterialNode"]:
        node = None
        machine_definition = global_stack.definition
        extruder_definition = global_stack.extruders[position].definition
        if parseBool(machine_definition.getMetaDataEntry("has_materials", False)):
            material_diameter = extruder_definition.getProperty("material_diameter", "value")
            if isinstance(material_diameter, SettingFunction):
                material_diameter = material_diameter(global_stack)

            # Look at the guid to material dictionary
            root_material_id = None
            for material_group in self._guid_material_groups_map[material_guid]:
                root_material_id = cast(str, material_group.root_material_node.getMetaDataEntry("id", ""))
                break

            if not root_material_id:
                Logger.log("i", "Cannot find materials with guid [%s] ", material_guid)
                return None

            node = self.getMaterialNode(machine_definition.getId(), nozzle_name, buildplate_name,
                                        material_diameter, root_material_id)
        return node
示例#13
0
    def _update(self) -> None:
        items = []

        container_stacks = CuraContainerRegistry.getInstance().findContainerStacks(type = "machine")
        for container_stack in container_stacks:
            has_remote_connection = False

            for connection_type in container_stack.configuredConnectionTypes:
                has_remote_connection |= connection_type in [ConnectionType.NetworkConnection.value,
                                                             ConnectionType.CloudConnection.value]

            if parseBool(container_stack.getMetaDataEntry("hidden", False)):
                continue

            section_name = "Network enabled printers" if has_remote_connection else "Local printers"
            section_name = self._catalog.i18nc("@info:title", section_name)

            items.append({"name": container_stack.getMetaDataEntry("group_name", container_stack.getName()),
                          "id": container_stack.getId(),
                          "hasRemoteConnection": has_remote_connection,
                          "metadata": container_stack.getMetaData().copy(),
                          "discoverySource": section_name})
        items.sort(key = lambda i: (not i["hasRemoteConnection"], i["name"]))
        self.setItems(items)
示例#14
0
    def startPrint(self):
        global_container_stack = Application.getInstance(
        ).getGlobalContainerStack()
        if not global_container_stack:
            return

        self._auto_print = parseBool(
            global_container_stack.getMetaDataEntry("octoprint_auto_print",
                                                    True))

        if self.jobState not in ["ready", ""]:
            if self.jobState == "offline":
                self._error_message = Message(
                    i18n_catalog.i18nc(
                        "@info:status",
                        "OctoPrint is offline. Unable to start a new job."))
            elif self._auto_print:
                self._error_message = Message(
                    i18n_catalog.i18nc(
                        "@info:status",
                        "OctoPrint is busy. Unable to start a new job."))
            else:
                # allow queueing the job even if OctoPrint is currently busy if autoprinting is disabled
                self._error_message = None

            if self._error_message:
                self._error_message.show()
                return

        self._preheat_timer.stop()

        if self._auto_print:
            Application.getInstance().showPrintMonitor.emit(True)

        try:
            self._progress_message = Message(
                i18n_catalog.i18nc("@info:status",
                                   "Sending data to OctoPrint"), 0, False, -1)
            self._progress_message.addAction(
                "Cancel", i18n_catalog.i18nc("@action:button", "Cancel"), None,
                "")
            self._progress_message.actionTriggered.connect(
                self._cancelSendGcode)
            self._progress_message.show()

            ## Mash the data into single string
            single_string_file_data = ""
            last_process_events = time()
            for line in self._gcode:
                single_string_file_data += line
                if time() > last_process_events + 0.05:
                    # Ensure that the GUI keeps updated at least 20 times per second.
                    QCoreApplication.processEvents()
                    last_process_events = time()

            job_name = Application.getInstance().getPrintInformation(
            ).jobName.strip()
            if job_name is "":
                job_name = "untitled_print"
            file_name = "%s.gcode" % job_name

            ##  Create multi_part request
            self._post_multi_part = QHttpMultiPart(QHttpMultiPart.FormDataType)

            ##  Create parts (to be placed inside multipart)
            self._post_part = QHttpPart()
            self._post_part.setHeader(QNetworkRequest.ContentDispositionHeader,
                                      "form-data; name=\"select\"")
            self._post_part.setBody(b"true")
            self._post_multi_part.append(self._post_part)

            if self._auto_print:
                self._post_part = QHttpPart()
                self._post_part.setHeader(
                    QNetworkRequest.ContentDispositionHeader,
                    "form-data; name=\"print\"")
                self._post_part.setBody(b"true")
                self._post_multi_part.append(self._post_part)

            self._post_part = QHttpPart()
            self._post_part.setHeader(
                QNetworkRequest.ContentDispositionHeader,
                "form-data; name=\"file\"; filename=\"%s\"" % file_name)
            self._post_part.setBody(single_string_file_data.encode())
            self._post_multi_part.append(self._post_part)

            destination = "local"
            if self._sd_supported and parseBool(
                    global_container_stack.getMetaDataEntry(
                        "octoprint_store_sd", False)):
                destination = "sdcard"

            ##  Post request + data
            post_request = self._createApiRequest("files/" + destination)
            self._post_reply = self._manager.post(post_request,
                                                  self._post_multi_part)
            self._post_reply.uploadProgress.connect(self._onUploadProgress)

            self._gcode = None

        except IOError:
            self._progress_message.hide()
            self._error_message = Message(
                i18n_catalog.i18nc("@info:status",
                                   "Unable to send data to OctoPrint."))
            self._error_message.show()
        except Exception as e:
            self._progress_message.hide()
            Logger.log(
                "e",
                "An exception occurred in network connection: %s" % str(e))
示例#15
0
    def upgradeStack(self, serialized: str,
                     filename: str) -> Tuple[List[str], List[str]]:
        """
        Upgrades stacks to have the new version number.
        :param serialized: The original contents of the stack.
        :param filename: The original file name of the stack.
        :return: A list of new file names, and a list of the new contents for
        those files.
        """
        parser = configparser.ConfigParser(interpolation=None)
        parser.read_string(serialized)

        # Update version number.
        if "metadata" not in parser:
            parser["metadata"] = {}
        parser["metadata"]["setting_version"] = "15"

        # Update Pause at Height script parameters if present.
        if "post_processing_scripts" in parser["metadata"]:
            new_scripts_entries = []
            for script_str in parser["metadata"][
                    "post_processing_scripts"].split("\n"):
                if not script_str:
                    continue
                script_str = script_str.replace(r"\\\n", "\n").replace(
                    r"\\\\", "\\\\")  # Unescape escape sequences.
                script_parser = configparser.ConfigParser(interpolation=None)
                script_parser.optionxform = str  # type: ignore  # Don't transform the setting keys as they are case-sensitive.
                script_parser.read_string(script_str)

                # Unify all Pause at Height
                script_id = script_parser.sections()[0]
                if script_id in [
                        "BQ_PauseAtHeight", "PauseAtHeightRepRapFirmwareDuet",
                        "PauseAtHeightforRepetier"
                ]:
                    script_settings = script_parser.items(script_id)
                    script_settings.append(("pause_method", {
                        "BQ_PauseAtHeight":
                        "bq",
                        "PauseAtHeightforRepetier":
                        "repetier",
                        "PauseAtHeightRepRapFirmwareDuet":
                        "reprap"
                    }[script_id]))

                    # Since we cannot rename a section, we remove the original section and create a new section with the new script id.
                    script_parser.remove_section(script_id)
                    script_id = "PauseAtHeight"
                    script_parser.add_section(script_id)
                    for setting_tuple in script_settings:
                        script_parser.set(script_id, setting_tuple[0],
                                          setting_tuple[1])

                # Update redo_layers to redo_layer
                if "PauseAtHeight" in script_parser:
                    if "redo_layers" in script_parser["PauseAtHeight"]:
                        script_parser["PauseAtHeight"]["redo_layer"] = str(
                            int(script_parser["PauseAtHeight"]["redo_layers"])
                            > 0)
                        del script_parser["PauseAtHeight"][
                            "redo_layers"]  # Has been renamed to without the S.

                # Migrate DisplayCompleteOnLCD to DisplayProgressOnLCD
                if script_id == "DisplayRemainingTimeOnLCD":
                    was_enabled = parseBool(
                        script_parser[script_id]["TurnOn"]
                    ) if "TurnOn" in script_parser[script_id] else False
                    script_parser.remove_section(script_id)

                    script_id = "DisplayProgressOnLCD"
                    script_parser.add_section(script_id)
                    if was_enabled:
                        script_parser.set(script_id, "time_remaining", "True")

                script_io = io.StringIO()
                script_parser.write(script_io)
                script_str = script_io.getvalue()
                script_str = script_str.replace("\\\\", r"\\\\").replace(
                    "\n", r"\\\n"
                )  # Escape newlines because configparser sees those as section delimiters.
                new_scripts_entries.append(script_str)
            parser["metadata"]["post_processing_scripts"] = "\n".join(
                new_scripts_entries)
        # check renamed definition
        if parser.has_option(
                "containers",
                "7") and parser["containers"]["7"] in _RENAMED_DEFINITION_DICT:
            parser["containers"]["7"] = _RENAMED_DEFINITION_DICT[
                parser["containers"]["7"]]
        result = io.StringIO()
        parser.write(result)
        return [filename], [result.getvalue()]
    def requestWrite(self,
                     nodes: List["SceneNode"],
                     file_name: Optional[str] = None,
                     limit_mimetypes: bool = False,
                     file_handler: Optional["FileHandler"] = None,
                     **kwargs: str) -> None:
        global_container_stack = CuraApplication.getInstance(
        ).getGlobalContainerStack()
        if not global_container_stack:
            return

        # Make sure post-processing plugin are run on the gcode
        self.writeStarted.emit(self)

        # Get the g-code through the GCodeWriter plugin
        # This produces the same output as "Save to File", adding the print settings to the bottom of the file
        gcode_writer = cast(
            MeshWriter,
            PluginRegistry.getInstance().getPluginObject("GCodeWriter"))
        self._gcode_stream = StringIO()
        if not gcode_writer.write(self._gcode_stream, None):
            Logger.log("e",
                       "GCodeWrite failed: %s" % gcode_writer.getInformation())
            return

        if self._error_message:
            self._error_message.hide()
            self._error_message = None

        if self._progress_message:
            self._progress_message.hide()
            self._progress_message = None

        self._auto_print = parseBool(
            global_container_stack.getMetaDataEntry("octoprint_auto_print",
                                                    True))
        self._forced_queue = False

        if self.activePrinter.state not in ["idle", ""]:
            Logger.log(
                "d", "Tried starting a print, but current state is %s" %
                self.activePrinter.state)
            if not self._auto_print:
                # Allow queueing the job even if OctoPrint is currently busy if autoprinting is disabled
                self._error_message = None
            elif self.activePrinter.state == "offline":
                self._error_message = Message(
                    i18n_catalog.i18nc(
                        "@info:status",
                        "The printer is offline. Unable to start a new job."))
            else:
                self._error_message = Message(
                    i18n_catalog.i18nc(
                        "@info:status",
                        "OctoPrint is busy. Unable to start a new job."))

            if self._error_message:
                self._error_message.addAction(
                    "Queue", i18n_catalog.i18nc("@action:button", "Queue job"),
                    "",
                    i18n_catalog.i18nc(
                        "@action:tooltip",
                        "Queue this print job so it can be printed later"))
                self._error_message.actionTriggered.connect(self._queuePrint)
                self._error_message.show()
                return

        self._startPrint()
示例#17
0
 def getHasVariants(self) -> bool:
     return parseBool(self.getMetaDataEntry("has_variants", False))
示例#18
0
    def _serialiseSettings(self, stack):
        prefix = ";SETTING_" + str(
            GCodeWriter.version) + " "  # The prefix to put before each line.
        prefix_length = len(prefix)

        container_with_profile = stack.qualityChanges
        if container_with_profile.getId() == "empty_quality_changes":
            Logger.log(
                "e",
                "No valid quality profile found, not writing settings to g-code!"
            )
            return ""

        flat_global_container = self._createFlattenedContainerInstance(
            stack.getTop(), container_with_profile)
        # If the quality changes is not set, we need to set type manually
        if flat_global_container.getMetaDataEntry("type", None) is None:
            flat_global_container.addMetaDataEntry("type", "quality_changes")

        # Ensure that quality_type is set. (Can happen if we have empty quality changes).
        if flat_global_container.getMetaDataEntry("quality_type",
                                                  None) is None:
            flat_global_container.addMetaDataEntry(
                "quality_type",
                stack.quality.getMetaDataEntry("quality_type", "normal"))

        # Change the default defintion
        default_machine_definition = "fdmprinter"
        if parseBool(stack.getMetaDataEntry("has_machine_quality", "False")):
            default_machine_definition = stack.getMetaDataEntry(
                "quality_definition")
            if not default_machine_definition:
                default_machine_definition = stack.definition.getId()
        flat_global_container.setMetaDataEntry("definition",
                                               default_machine_definition)

        serialized = flat_global_container.serialize()
        data = {"global_quality": serialized}

        for extruder in sorted(stack.extruders.values(),
                               key=lambda k: k.getMetaDataEntry("position")):
            extruder_quality = extruder.qualityChanges
            if extruder_quality.getId() == "empty_quality_changes":
                Logger.log(
                    "w",
                    "No extruder quality profile found, not writing quality for extruder %s to file!",
                    extruder.getId())
                continue
            flat_extruder_quality = self._createFlattenedContainerInstance(
                extruder.getTop(), extruder_quality)
            # If the quality changes is not set, we need to set type manually
            if flat_extruder_quality.getMetaDataEntry("type", None) is None:
                flat_extruder_quality.addMetaDataEntry("type",
                                                       "quality_changes")

            # Ensure that extruder is set. (Can happen if we have empty quality changes).
            if flat_extruder_quality.getMetaDataEntry("extruder",
                                                      None) is None:
                flat_extruder_quality.addMetaDataEntry(
                    "extruder",
                    extruder.getBottom().getId())

            # Ensure that quality_type is set. (Can happen if we have empty quality changes).
            if flat_extruder_quality.getMetaDataEntry("quality_type",
                                                      None) is None:
                flat_extruder_quality.addMetaDataEntry(
                    "quality_type",
                    extruder.quality.getMetaDataEntry("quality_type",
                                                      "normal"))

            # Change the default defintion
            default_extruder_definition = "fdmextruder"
            if parseBool(stack.getMetaDataEntry("has_machine_quality",
                                                "False")):
                default_extruder_definition = extruder.getMetaDataEntry(
                    "quality_definition")
                if not default_extruder_definition:
                    default_extruder_definition = extruder.definition.getId()
            flat_extruder_quality.setMetaDataEntry(
                "definition", default_extruder_definition)

            extruder_serialized = flat_extruder_quality.serialize()
            data.setdefault("extruder_quality", []).append(extruder_serialized)

        json_string = json.dumps(data)

        # Escape characters that have a special meaning in g-code comments.
        pattern = re.compile("|".join(GCodeWriter.escape_characters.keys()))

        # Perform the replacement with a regular expression.
        escaped_string = pattern.sub(
            lambda m: GCodeWriter.escape_characters[re.escape(m.group(0))],
            json_string)

        # Introduce line breaks so that each comment is no longer than 80 characters. Prepend each line with the prefix.
        result = ""

        # Lines have 80 characters, so the payload of each line is 80 - prefix.
        for pos in range(0, len(escaped_string), 80 - prefix_length):
            result += prefix + escaped_string[pos:pos + 80 -
                                              prefix_length] + "\n"
        return result
示例#19
0
 def hasVariants(self) -> bool:
     return parseBool(self.getMetaDataEntry("has_variants", False))
示例#20
0
 def _machineHasOwnQualities(self):
     global_container_stack = Application.getInstance().getGlobalContainerStack()
     if global_container_stack:
         return parseBool(global_container_stack.getMetaDataEntry("has_machine_quality", False))
     return False
示例#21
0
    def read(self, file_name):
        if file_name.split(".")[-1] != "ini":
            return None
        global_container_stack = Application.getInstance().getGlobalContainerStack()
        if not global_container_stack:
            return None

        multi_extrusion = global_container_stack.getProperty("machine_extruder_count", "value") > 1
        if multi_extrusion:
            Logger.log("e", "Unable to import legacy profile %s. Multi extrusion is not supported", file_name)
            raise Exception("Unable to import legacy profile. Multi extrusion is not supported")

        Logger.log("i", "Importing legacy profile from file " + file_name + ".")
        container_registry = ContainerRegistry.getInstance()
        profile_id = container_registry.uniqueName("Imported Legacy Profile")
        profile = InstanceContainer(profile_id)  # Create an empty profile.

        parser = configparser.ConfigParser(interpolation = None)
        try:
            parser.read([file_name])  # Parse the INI file.
        except Exception as e:
            Logger.log("e", "Unable to open legacy profile %s: %s", file_name, str(e))
            return None

        # Legacy Cura saved the profile under the section "profile_N" where N is the ID of a machine, except when you export in which case it saves it in the section "profile".
        # Since importing multiple machine profiles is out of scope, just import the first section we find.
        section = ""
        for found_section in parser.sections():
            if found_section.startswith("profile"):
                section = found_section
                break
        if not section:  # No section starting with "profile" was found. Probably not a proper INI file.
            return None

        try:
            with open(os.path.join(PluginRegistry.getInstance().getPluginPath("LegacyProfileReader"), "DictionaryOfDoom.json"), "r", encoding = "utf-8") as f:
                dict_of_doom = json.load(f)  # Parse the Dictionary of Doom.
        except IOError as e:
            Logger.log("e", "Could not open DictionaryOfDoom.json for reading: %s", str(e))
            return None
        except Exception as e:
            Logger.log("e", "Could not parse DictionaryOfDoom.json: %s", str(e))
            return None

        defaults = self.prepareDefaults(dict_of_doom)
        legacy_settings = self.prepareLocals(parser, section, defaults) #Gets the settings from the legacy profile.

        #Check the target version in the Dictionary of Doom with this application version.
        if "target_version" not in dict_of_doom:
            Logger.log("e", "Dictionary of Doom has no target version. Is it the correct JSON file?")
            return None
        if InstanceContainer.Version != dict_of_doom["target_version"]:
            Logger.log("e", "Dictionary of Doom of legacy profile reader (version %s) is not in sync with the current instance container version (version %s)!", dict_of_doom["target_version"], str(InstanceContainer.Version))
            return None

        if "translation" not in dict_of_doom:
            Logger.log("e", "Dictionary of Doom has no translation. Is it the correct JSON file?")
            return None
        current_printer_definition = global_container_stack.definition
        quality_definition = current_printer_definition.getMetaDataEntry("quality_definition")
        if not quality_definition:
            quality_definition = current_printer_definition.getId()
        profile.setDefinition(quality_definition)
        for new_setting in dict_of_doom["translation"]:  # Evaluate all new settings that would get a value from the translations.
            old_setting_expression = dict_of_doom["translation"][new_setting]
            compiled = compile(old_setting_expression, new_setting, "eval")
            try:
                new_value = eval(compiled, {"math": math}, legacy_settings)  # Pass the legacy settings as local variables to allow access to in the evaluation.
                value_using_defaults = eval(compiled, {"math": math}, defaults)  #Evaluate again using only the default values to try to see if they are default.
            except Exception:  # Probably some setting name that was missing or something else that went wrong in the ini file.
                Logger.log("w", "Setting " + new_setting + " could not be set because the evaluation failed. Something is probably missing from the imported legacy profile.")
                continue
            definitions = current_printer_definition.findDefinitions(key = new_setting)
            if definitions:
                if new_value != value_using_defaults and definitions[0].default_value != new_value:  # Not equal to the default in the new Cura OR the default in the legacy Cura.
                    profile.setProperty(new_setting, "value", new_value)  # Store the setting in the profile!

        if len(profile.getAllKeys()) == 0:
            Logger.log("i", "A legacy profile was imported but everything evaluates to the defaults, creating an empty profile.")

        profile.addMetaDataEntry("type", "profile")
        # don't know what quality_type it is based on, so use "normal" by default
        profile.addMetaDataEntry("quality_type", "normal")
        profile.setName(profile_id)
        profile.setDirty(True)

        #Serialise and deserialise in order to perform the version upgrade.
        parser = configparser.ConfigParser(interpolation=None)
        data = profile.serialize()
        parser.read_string(data)
        parser["general"]["version"] = "1"
        if parser.has_section("values"):
            parser["settings"] = parser["values"]
            del parser["values"]
        stream = io.StringIO()
        parser.write(stream)
        data = stream.getvalue()
        profile.deserialize(data)

        # The definition can get reset to fdmprinter during the deserialization's upgrade. Here we set the definition
        # again.
        profile.setDefinition(quality_definition)

        #We need to return one extruder stack and one global stack.
        global_container_id = container_registry.uniqueName("Global Imported Legacy Profile")
        global_profile = profile.duplicate(new_id = global_container_id, new_name = profile_id) #Needs to have the same name as the extruder profile.
        global_profile.setDirty(True)

        profile_definition = "fdmprinter"
        from UM.Util import parseBool
        if parseBool(global_container_stack.getMetaDataEntry("has_machine_quality", "False")):
            profile_definition = global_container_stack.getMetaDataEntry("quality_definition")
            if not profile_definition:
                profile_definition = global_container_stack.definition.getId()
        global_profile.setDefinition(profile_definition)

        return [global_profile]
示例#22
0
    def importProfile(self, file_name):
        Logger.log("d", "Attempting to import profile %s", file_name)
        if not file_name:
            return {
                "status":
                "error",
                "message":
                catalog.i18nc(
                    "@info:status Don't translate the XML tags <filename> or <message>!",
                    "Failed to import profile from <filename>{0}</filename>: <message>{1}</message>",
                    file_name, "Invalid path")
            }

        plugin_registry = PluginRegistry.getInstance()
        extension = file_name.split(".")[-1]

        global_container_stack = Application.getInstance(
        ).getGlobalContainerStack()
        if not global_container_stack:
            return

        machine_extruders = list(
            ExtruderManager.getInstance().getMachineExtruders(
                global_container_stack.getId()))
        machine_extruders.sort(key=lambda k: k.getMetaDataEntry("position"))

        for plugin_id, meta_data in self._getIOPlugins("profile_reader"):
            if meta_data["profile_reader"][0]["extension"] != extension:
                continue
            profile_reader = plugin_registry.getPluginObject(plugin_id)
            try:
                profile_or_list = profile_reader.read(
                    file_name)  # Try to open the file with the profile reader.
            except Exception as e:
                # Note that this will fail quickly. That is, if any profile reader throws an exception, it will stop reading. It will only continue reading if the reader returned None.
                Logger.log(
                    "e",
                    "Failed to import profile from %s: %s while using profile reader. Got exception %s",
                    file_name, profile_reader.getPluginId(), str(e))
                return {
                    "status":
                    "error",
                    "message":
                    catalog.i18nc(
                        "@info:status Don't translate the XML tags <filename> or <message>!",
                        "Failed to import profile from <filename>{0}</filename>: <message>{1}</message>",
                        file_name, str(e))
                }

            if profile_or_list:
                # Ensure it is always a list of profiles
                if not isinstance(profile_or_list, list):
                    profile_or_list = [profile_or_list]

                # First check if this profile is suitable for this machine
                global_profile = None
                if len(profile_or_list) == 1:
                    global_profile = profile_or_list[0]
                else:
                    for profile in profile_or_list:
                        if not profile.getMetaDataEntry("extruder"):
                            global_profile = profile
                            break
                if not global_profile:
                    Logger.log(
                        "e",
                        "Incorrect profile [%s]. Could not find global profile",
                        file_name)
                    return {
                        "status":
                        "error",
                        "message":
                        catalog.i18nc(
                            "@info:status Don't translate the XML tags <filename> or <message>!",
                            "This profile <filename>{0}</filename> contains incorrect data, could not import it.",
                            file_name)
                    }
                profile_definition = global_profile.getMetaDataEntry(
                    "definition")
                expected_machine_definition = "fdmprinter"
                if parseBool(
                        global_container_stack.getMetaDataEntry(
                            "has_machine_quality", "False")):
                    expected_machine_definition = global_container_stack.getMetaDataEntry(
                        "quality_definition")
                    if not expected_machine_definition:
                        expected_machine_definition = global_container_stack.definition.getId(
                        )
                if expected_machine_definition is not None and profile_definition is not None and profile_definition != expected_machine_definition:
                    Logger.log(
                        "e",
                        "Profile [%s] is for machine [%s] but the current active machine is [%s]. Will not import the profile",
                        file_name)
                    return {
                        "status":
                        "error",
                        "message":
                        catalog.i18nc(
                            "@info:status Don't translate the XML tags <filename> or <message>!",
                            "The machine defined in profile <filename>{0}</filename> doesn't match with your current machine, could not import it.",
                            file_name)
                    }

                name_seed = os.path.splitext(os.path.basename(file_name))[0]
                new_name = self.uniqueName(name_seed)

                # Ensure it is always a list of profiles
                if type(profile_or_list) is not list:
                    profile_or_list = [profile_or_list]

                # Import all profiles
                for profile_index, profile in enumerate(profile_or_list):
                    if profile_index == 0:
                        # This is assumed to be the global profile
                        profile_id = (
                            global_container_stack.getBottom().getId() + "_" +
                            name_seed).lower().replace(" ", "_")

                    elif profile_index < len(machine_extruders) + 1:
                        if profile.id.startswith("base/"):
                            break
                        # This is assumed to be an extruder profile
                        extruder_id = Application.getInstance(
                        ).getMachineManager().getQualityDefinitionId(
                            machine_extruders[profile_index - 1].getBottom())
                        if not profile.getMetaDataEntry("extruder"):
                            profile.addMetaDataEntry("extruder", extruder_id)
                        else:
                            profile.setMetaDataEntry("extruder", extruder_id)
                        profile_id = (extruder_id + "_" +
                                      name_seed).lower().replace(" ", "_")

                    else:  #More extruders in the imported file than in the machine.
                        continue  #Delete the additional profiles.

                    base_profile = None
                    for p in profile_or_list:
                        if p.id == "base/" + profile.id:
                            base_profile = p
                            break

                    result = self._configureProfile(profile, profile_id,
                                                    new_name, base_profile)
                    if result is not None:
                        return {
                            "status":
                            "error",
                            "message":
                            catalog.i18nc(
                                "@info:status Don't translate the XML tags <filename> or <message>!",
                                "Failed to import profile from <filename>{0}</filename>: <message>{1}</message>",
                                file_name, result)
                        }

                return {
                    "status":
                    "ok",
                    "message":
                    catalog.i18nc("@info:status",
                                  "Successfully imported profile {0}",
                                  profile_or_list[0].getName())
                }

            # This message is throw when the profile reader doesn't find any profile in the file
            return {
                "status":
                "error",
                "message":
                catalog.i18nc("@info:status",
                              "File {0} does not contain any valid profile.",
                              file_name)
            }

        # If it hasn't returned by now, none of the plugins loaded the profile successfully.
        return {
            "status":
            "error",
            "message":
            catalog.i18nc(
                "@info:status",
                "Profile {0} has an unknown file type or is corrupted.",
                file_name)
        }
示例#23
0
 def getHasVariantsBuildPlates(self) -> bool:
     return parseBool(self.getMetaDataEntry("has_variant_buildplates", False))
示例#24
0
 def getHasMachineQuality(self) -> bool:
     return parseBool(self.getMetaDataEntry("has_machine_quality", False))
示例#25
0
    def getQualityGroups(self, machine: "GlobalStack") -> dict:
        machine_definition_id = getMachineDefinitionIDForQualitySearch(
            machine.definition)

        # This determines if we should only get the global qualities for the global stack and skip the global qualities for the extruder stacks
        has_variant_materials = parseBool(
            machine.getMetaDataEntry("has_variant_materials", False))

        # To find the quality container for the GlobalStack, check in the following fall-back manner:
        #   (1) the machine-specific node
        #   (2) the generic node
        machine_node = self._machine_variant_material_quality_type_to_quality_dict.get(
            machine_definition_id)
        default_machine_node = self._machine_variant_material_quality_type_to_quality_dict.get(
            self._default_machine_definition_id)
        nodes_to_check = [machine_node, default_machine_node]

        # Iterate over all quality_types in the machine node
        quality_group_dict = {}
        for node in nodes_to_check:
            if node and node.quality_type_map:
                # Only include global qualities
                if has_variant_materials:
                    quality_node = list(node.quality_type_map.values())[0]
                    is_global_quality = parseBool(
                        quality_node.metadata.get("global_quality", False))
                    if not is_global_quality:
                        continue

                for quality_type, quality_node in node.quality_type_map.items(
                ):
                    quality_group = QualityGroup(quality_node.metadata["name"],
                                                 quality_type)
                    quality_group.node_for_global = quality_node
                    quality_group_dict[quality_type] = quality_group
                break

        # Iterate over all extruders to find quality containers for each extruder
        for position, extruder in machine.extruders.items():
            variant_name = None
            if extruder.variant.getId() != "empty_variant":
                variant_name = extruder.variant.getName()

            # This is a list of root material IDs to use for searching for suitable quality profiles.
            # The root material IDs in this list are in prioritized order.
            root_material_id_list = []
            has_material = False  # flag indicating whether this extruder has a material assigned
            if extruder.material.getId() != "empty_material":
                has_material = True
                root_material_id = extruder.material.getMetaDataEntry(
                    "base_file")
                # Convert possible generic_pla_175 -> generic_pla
                root_material_id = self._material_manager.getRootMaterialIDWithoutDiameter(
                    root_material_id)
                root_material_id_list.append(root_material_id)

                # Also try to get the fallback material
                material_type = extruder.material.getMetaDataEntry("material")
                fallback_root_material_id = self._material_manager.getFallbackMaterialIdByMaterialType(
                    material_type)
                if fallback_root_material_id:
                    root_material_id_list.append(fallback_root_material_id)

            # Here we construct a list of nodes we want to look for qualities with the highest priority first.
            # The use case is that, when we look for qualities for a machine, we first want to search in the following
            # order:
            #   1. machine-variant-and-material-specific qualities if exist
            #   2. machine-variant-specific qualities if exist
            #   3. machine-material-specific qualities if exist
            #   4. machine-specific qualities if exist
            #   5. generic qualities if exist
            # Each points above can be represented as a node in the lookup tree, so here we simply put those nodes into
            # the list with priorities as the order. Later, we just need to loop over each node in this list and fetch
            # qualities from there.
            nodes_to_check = []

            if variant_name:
                # In this case, we have both a specific variant and a specific material
                variant_node = machine_node.getChildNode(variant_name)
                if variant_node and has_material:
                    for root_material_id in root_material_id_list:
                        material_node = variant_node.getChildNode(
                            root_material_id)
                        if material_node:
                            nodes_to_check.append(material_node)
                            break
                nodes_to_check.append(variant_node)

            # In this case, we only have a specific material but NOT a variant
            if has_material:
                for root_material_id in root_material_id_list:
                    material_node = machine_node.getChildNode(root_material_id)
                    if material_node:
                        nodes_to_check.append(material_node)
                        break

            nodes_to_check += [machine_node, default_machine_node]
            for node in nodes_to_check:
                if node and node.quality_type_map:
                    if has_variant_materials:
                        # Only include variant qualities; skip non global qualities
                        quality_node = list(node.quality_type_map.values())[0]
                        is_global_quality = parseBool(
                            quality_node.metadata.get("global_quality", False))
                        if is_global_quality:
                            continue

                    for quality_type, quality_node in node.quality_type_map.items(
                    ):
                        if quality_type not in quality_group_dict:
                            quality_group = QualityGroup(
                                quality_node.metadata["name"], quality_type)
                            quality_group_dict[quality_type] = quality_group

                        quality_group = quality_group_dict[quality_type]
                        quality_group.nodes_for_extruders[
                            position] = quality_node
                    break

        # Update availabilities for each quality group
        self._updateQualityGroupsAvailability(machine,
                                              quality_group_dict.values())

        return quality_group_dict
示例#26
0
def test_negative(value):
    assert not parseBool(value)
示例#27
0
def test_positive(value):
    assert parseBool(value)
示例#28
0
    def deserialize(self, serialized):
        data = ET.fromstring(serialized)

        self.addMetaDataEntry("type", "material")
        self.addMetaDataEntry("base_file", self.id)

        # TODO: Add material verfication
        self.addMetaDataEntry("status", "unknown")

        inherits = data.find("./um:inherits", self.__namespaces)
        if inherits is not None:
            inherited = self._resolveInheritance(inherits.text)
            data = self._mergeXML(inherited, data)

        metadata = data.iterfind("./um:metadata/*", self.__namespaces)
        for entry in metadata:
            tag_name = _tag_without_namespace(entry)

            if tag_name == "name":
                brand = entry.find("./um:brand", self.__namespaces)
                material = entry.find("./um:material", self.__namespaces)
                color = entry.find("./um:color", self.__namespaces)
                label = entry.find("./um:label", self.__namespaces)

                if label is not None:
                    self.setName(label.text)
                else:
                    self.setName(self._profile_name(material.text, color.text))

                self.addMetaDataEntry("brand", brand.text)
                self.addMetaDataEntry("material", material.text)
                self.addMetaDataEntry("color_name", color.text)

                continue

            self.addMetaDataEntry(tag_name, entry.text)

        if not "description" in self.getMetaData():
            self.addMetaDataEntry("description", "")

        if not "adhesion_info" in self.getMetaData():
            self.addMetaDataEntry("adhesion_info", "")

        property_values = {}
        properties = data.iterfind("./um:properties/*", self.__namespaces)
        for entry in properties:
            tag_name = _tag_without_namespace(entry)
            property_values[tag_name] = entry.text

        diameter = float(property_values.get("diameter", 2.85)) # In mm
        density = float(property_values.get("density", 1.3)) # In g/cm3

        self.addMetaDataEntry("properties", property_values)

        self.setDefinition(UM.Settings.ContainerRegistry.getInstance().findDefinitionContainers(id = "fdmprinter")[0])

        global_compatibility = True
        global_setting_values = {}
        settings = data.iterfind("./um:settings/um:setting", self.__namespaces)
        for entry in settings:
            key = entry.get("key")
            if key in self.__material_property_setting_map:
                self.setProperty(self.__material_property_setting_map[key], "value", entry.text, self._definition)
                global_setting_values[self.__material_property_setting_map[key]] = entry.text
            elif key in self.__unmapped_settings:
                if key == "hardware compatible":
                    global_compatibility = parseBool(entry.text)
            else:
                Logger.log("d", "Unsupported material setting %s", key)

        self._dirty = False

        machines = data.iterfind("./um:settings/um:machine", self.__namespaces)
        for machine in machines:
            machine_compatibility = global_compatibility
            machine_setting_values = {}
            settings = machine.iterfind("./um:setting", self.__namespaces)
            for entry in settings:
                key = entry.get("key")
                if key in self.__material_property_setting_map:
                    machine_setting_values[self.__material_property_setting_map[key]] = entry.text
                elif key in self.__unmapped_settings:
                    if key == "hardware compatible":
                        machine_compatibility = parseBool(entry.text)
                else:
                    Logger.log("d", "Unsupported material setting %s", key)

            identifiers = machine.iterfind("./um:machine_identifier", self.__namespaces)
            for identifier in identifiers:
                machine_id = self.__product_id_map.get(identifier.get("product"), None)
                if machine_id is None:
                    Logger.log("w", "Cannot create material for unknown machine %s", identifier.get("product"))
                    continue

                definitions = UM.Settings.ContainerRegistry.getInstance().findDefinitionContainers(id = machine_id)
                if not definitions:
                    Logger.log("w", "No definition found for machine ID %s", machine_id)
                    continue

                definition = definitions[0]

                if machine_compatibility:
                    new_material = XmlMaterialProfile(self.id + "_" + machine_id)
                    new_material.setName(self.getName())
                    new_material.setMetaData(copy.deepcopy(self.getMetaData()))
                    new_material.setDefinition(definition)

                    for key, value in global_setting_values.items():
                        new_material.setProperty(key, "value", value, definition)

                    for key, value in machine_setting_values.items():
                        new_material.setProperty(key, "value", value, definition)

                    new_material._dirty = False

                    UM.Settings.ContainerRegistry.getInstance().addContainer(new_material)

                hotends = machine.iterfind("./um:hotend", self.__namespaces)
                for hotend in hotends:
                    hotend_id = hotend.get("id")
                    if hotend_id is None:
                        continue

                    variant_containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = hotend_id)
                    if not variant_containers:
                        # It is not really properly defined what "ID" is so also search for variants by name.
                        variant_containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(definition = definition.id, name = hotend_id)

                    if not variant_containers:
                        Logger.log("d", "No variants found with ID or name %s for machine %s", hotend_id, definition.id)
                        continue

                    hotend_compatibility = machine_compatibility
                    hotend_setting_values = {}
                    settings = hotend.iterfind("./um:setting", self.__namespaces)
                    for entry in settings:
                        key = entry.get("key")
                        if key in self.__material_property_setting_map:
                            hotend_setting_values[self.__material_property_setting_map[key]] = entry.text
                        elif key in self.__unmapped_settings:
                            if key == "hardware compatible":
                                hotend_compatibility = parseBool(entry.text)
                        else:
                            Logger.log("d", "Unsupported material setting %s", key)

                    if not hotend_compatibility:
                        continue

                    new_hotend_material = XmlMaterialProfile(self.id + "_" + machine_id + "_" + hotend_id.replace(" ", "_"))
                    new_hotend_material.setName(self.getName())
                    new_hotend_material.setMetaData(copy.deepcopy(self.getMetaData()))
                    new_hotend_material.setDefinition(definition)
                    new_hotend_material.addMetaDataEntry("variant", variant_containers[0].id)

                    for key, value in global_setting_values.items():
                        new_hotend_material.setProperty(key, "value", value, definition)

                    for key, value in machine_setting_values.items():
                        new_hotend_material.setProperty(key, "value", value, definition)

                    for key, value in hotend_setting_values.items():
                        new_hotend_material.setProperty(key, "value", value, definition)

                    new_hotend_material._dirty = False
                    UM.Settings.ContainerRegistry.getInstance().addContainer(new_hotend_material)

        if not global_compatibility:
            # Change the type of this container so it is not shown as an option in menus.
            # This uses InstanceContainer.setMetaDataEntry because otherwise all containers that
            # share this basefile are also updated.
            super().setMetaDataEntry("type", "incompatible_material")
    def _startPrint(self):
        if self._auto_print and not self._forced_queue:
            Application.getInstance().getController().setActiveStage(
                "MonitorStage")

            # cancel any ongoing preheat timer before starting a print
            try:
                self._printers[0].stopPreheatTimers()
            except AttributeError:
                # stopPreheatTimers was added after Cura 3.3 beta
                pass

        self._progress_message = Message(
            i18n_catalog.i18nc("@info:status", "Sending data to OctoPrint"), 0,
            False, -1)
        self._progress_message.addAction(
            "Cancel", i18n_catalog.i18nc("@action:button", "Cancel"), None, "")
        self._progress_message.actionTriggered.connect(self._cancelSendGcode)
        self._progress_message.show()

        ## Mash the data into single string
        single_string_file_data = ""
        last_process_events = time()
        for line in self._gcode:
            single_string_file_data += line
            if time() > last_process_events + 0.05:
                # Ensure that the GUI keeps updated at least 20 times per second.
                QCoreApplication.processEvents()
                last_process_events = time()

        job_name = Application.getInstance().getPrintInformation(
        ).jobName.strip()
        if job_name is "":
            job_name = "untitled_print"
        file_name = "%s.gcode" % job_name

        ##  Create multi_part request
        self._post_multi_part = QHttpMultiPart(QHttpMultiPart.FormDataType)

        ##  Create parts (to be placed inside multipart)
        post_part = QHttpPart()
        post_part.setHeader(QNetworkRequest.ContentDispositionHeader,
                            "form-data; name=\"select\"")
        post_part.setBody(b"true")
        self._post_multi_part.append(post_part)

        if self._auto_print and not self._forced_queue:
            post_part = QHttpPart()
            post_part.setHeader(QNetworkRequest.ContentDispositionHeader,
                                "form-data; name=\"print\"")
            post_part.setBody(b"true")
            self._post_multi_part.append(post_part)

        post_part = QHttpPart()
        post_part.setHeader(
            QNetworkRequest.ContentDispositionHeader,
            "form-data; name=\"file\"; filename=\"%s\"" % file_name)
        post_part.setBody(single_string_file_data.encode())
        self._post_multi_part.append(post_part)

        destination = "local"
        if self._sd_supported and parseBool(Application.getInstance(
        ).getGlobalContainerStack().getMetaDataEntry("octoprint_store_sd",
                                                     False)):
            destination = "sdcard"

        try:
            ##  Post request + data
            post_request = self._createApiRequest("files/" + destination)
            self._post_reply = self._manager.post(post_request,
                                                  self._post_multi_part)
            self._post_reply.uploadProgress.connect(self._onUploadProgress)

        except IOError:
            self._progress_message.hide()
            self._error_message = Message(
                i18n_catalog.i18nc("@info:status",
                                   "Unable to send data to OctoPrint."))
            self._error_message.show()
        except Exception as e:
            self._progress_message.hide()
            Logger.log(
                "e",
                "An exception occurred in network connection: %s" % str(e))

        self._gcode = None
示例#30
0
    def upgradeInstanceContainer(self, serialized: str,
                                 filename: str) -> Tuple[List[str], List[str]]:
        """
        Upgrades instance containers to have the new version number.

        This changes the maximum deviation setting if that setting was present
        in the profile.
        :param serialized: The original contents of the instance container.
        :param filename: The original file name of the instance container.
        :return: A list of new file names, and a list of the new contents for
        those files.
        """
        parser = configparser.ConfigParser(interpolation=None,
                                           comment_prefixes=())
        parser.read_string(serialized)

        # Update version number.
        parser["metadata"]["setting_version"] = "15"

        if "values" in parser:
            # Maximum Deviation's effect was corrected. Previously the deviation
            # ended up being only half of what the user had entered. This was
            # fixed in Cura 4.7 so there we need to halve the deviation that the
            # user had entered.
            #
            # This got accidentally merged in Cura 4.6.0. In 4.6.2 we removed
            # that. In 4.7 it's not unmerged, so there we need to revert all
            # that again.
            if "meshfix_maximum_deviation" in parser["values"]:
                maximum_deviation = parser["values"][
                    "meshfix_maximum_deviation"]
                if maximum_deviation.startswith("="):
                    maximum_deviation = maximum_deviation[1:]
                maximum_deviation = "=(" + maximum_deviation + ") / 2"
                parser["values"][
                    "meshfix_maximum_deviation"] = maximum_deviation

            # Ironing inset is now based on the flow-compensated line width to make the setting have a more logical UX.
            # Adjust so that the actual print result remains the same.
            if "ironing_inset" in parser["values"]:
                ironing_inset = parser["values"]["ironing_inset"]
                if ironing_inset.startswith("="):
                    ironing_inset = ironing_inset[1:]
                if "ironing_pattern" in parser["values"] and parser["values"][
                        "ironing_pattern"] == "concentric":
                    correction = " + ironing_line_spacing - skin_line_width * (1.0 + ironing_flow / 100) / 2"
                else:  # If ironing_pattern doesn't exist, it means the default (zigzag) is selected
                    correction = " + skin_line_width * (1.0 - ironing_flow / 100) / 2"
                ironing_inset = "=(" + ironing_inset + ")" + correction
                parser["values"]["ironing_inset"] = ironing_inset

            # Set support_structure if necessary
            if "support_tree_enable" in parser["values"]:
                if parseBool(parser["values"]["support_tree_enable"]):
                    parser["values"]["support_structure"] = "tree"
                    parser["values"]["support_enable"] = "True"

            for removed in set(
                    parser["values"].keys()).intersection(_removed_settings):
                del parser["values"][removed]

        # Check renamed definitions
        if "definition" in parser["general"] and parser["general"][
                "definition"] in _RENAMED_DEFINITION_DICT:
            parser["general"]["definition"] = _RENAMED_DEFINITION_DICT[
                parser["general"]["definition"]]

        result = io.StringIO()
        parser.write(result)
        return [filename], [result.getvalue()]
示例#31
0
 def hasVariants(self):
     global_container_stack = Application.getInstance().getGlobalContainerStack()
     if global_container_stack:
         return parseBool(global_container_stack.getMetaDataEntry("has_variants", "false"))
示例#32
0
    def _startPrint(self) -> None:
        global_container_stack = CuraApplication.getInstance(
        ).getGlobalContainerStack()
        if not global_container_stack:
            return

        if self._auto_print and not self._forced_queue:
            CuraApplication.getInstance().getController().setActiveStage(
                "MonitorStage")

            # cancel any ongoing preheat timer before starting a print
            try:
                self._printers[0].stopPreheatTimers()
            except AttributeError:
                # stopPreheatTimers was added after Cura 3.3 beta
                pass

        self._progress_message = Message(
            i18n_catalog.i18nc("@info:status", "Sending data to OctoPrint"), 0,
            False, -1)
        self._progress_message.addAction(
            "Cancel", i18n_catalog.i18nc("@action:button", "Cancel"), "", "")
        self._progress_message.actionTriggered.connect(self._cancelSendGcode)
        self._progress_message.show()

        job_name = CuraApplication.getInstance().getPrintInformation(
        ).jobName.strip()
        if job_name is "":
            job_name = "untitled_print"
        extension = "gcode" if not self._ufp_supported else "ufp"
        file_name = "%s.%s" % (job_name, extension)

        ##  Create multi_part request
        post_parts = []  # type: List[QHttpPart]

        ##  Create parts (to be placed inside multipart)
        post_part = QHttpPart()
        post_part.setHeader(QNetworkRequest.ContentDispositionHeader,
                            "form-data; name=\"select\"")
        post_part.setBody(b"true")
        post_parts.append(post_part)

        if self._auto_print and not self._forced_queue:
            post_part = QHttpPart()
            post_part.setHeader(QNetworkRequest.ContentDispositionHeader,
                                "form-data; name=\"print\"")
            post_part.setBody(b"true")
            post_parts.append(post_part)

        gcode_body = self._gcode_stream.getvalue()
        try:
            # encode StringIO result to bytes
            gcode_body = gcode_body.encode()
        except AttributeError:
            # encode BytesIO is already byte-encoded
            pass

        post_part = QHttpPart()
        post_part.setHeader(
            QNetworkRequest.ContentDispositionHeader,
            "form-data; name=\"file\"; filename=\"%s\"" % file_name)
        post_part.setBody(gcode_body)
        post_parts.append(post_part)

        destination = "local"
        if self._sd_supported and parseBool(
                global_container_stack.getMetaDataEntry(
                    "octoprint_store_sd", False)):
            destination = "sdcard"

        try:
            ##  Post request + data
            post_request = self._createEmptyRequest("files/" + destination)
            self._post_reply = self.postFormWithParts(
                "files/" + destination,
                post_parts,
                on_finished=self._onRequestFinished,
                on_progress=self._onUploadProgress)

        except IOError:
            self._progress_message.hide()
            self._error_message = Message(
                i18n_catalog.i18nc("@info:status",
                                   "Unable to send data to OctoPrint."))
            self._error_message.show()
        except Exception as e:
            self._progress_message.hide()
            Logger.log(
                "e",
                "An exception occurred in network connection: %s" % str(e))

        self._gcode_stream = None  # type: Optional[Union[StringIO, BytesIO]]
示例#33
0
    def getQualityGroups(self, machine: "GlobalStack") -> Dict[str, QualityGroup]:
        machine_definition_id = getMachineDefinitionIDForQualitySearch(machine.definition)

        # This determines if we should only get the global qualities for the global stack and skip the global qualities for the extruder stacks
        has_machine_specific_qualities = machine.getHasMachineQuality()

        # To find the quality container for the GlobalStack, check in the following fall-back manner:
        #   (1) the machine-specific node
        #   (2) the generic node
        machine_node = self._machine_nozzle_buildplate_material_quality_type_to_quality_dict.get(machine_definition_id)

        # Check if this machine has specific quality profiles for its extruders, if so, when looking up extruder
        # qualities, we should not fall back to use the global qualities.
        has_extruder_specific_qualities = False
        if machine_node:
            if machine_node.children_map:
                has_extruder_specific_qualities = True

        default_machine_node = self._machine_nozzle_buildplate_material_quality_type_to_quality_dict.get(self._default_machine_definition_id)

        nodes_to_check = []  # type: List[QualityNode]
        if machine_node is not None:
            nodes_to_check.append(machine_node)
        if default_machine_node is not None:
            nodes_to_check.append(default_machine_node)

        # Iterate over all quality_types in the machine node
        quality_group_dict = {}
        for node in nodes_to_check:
            if node and node.quality_type_map:
                quality_node = list(node.quality_type_map.values())[0]
                is_global_quality = parseBool(quality_node.getMetaDataEntry("global_quality", False))
                if not is_global_quality:
                    continue

                for quality_type, quality_node in node.quality_type_map.items():
                    quality_group = QualityGroup(quality_node.getMetaDataEntry("name", ""), quality_type)
                    quality_group.setGlobalNode(quality_node)
                    quality_group_dict[quality_type] = quality_group
                break

        buildplate_name = machine.getBuildplateName()

        # Iterate over all extruders to find quality containers for each extruder
        for position, extruder in machine.extruders.items():
            nozzle_name = None
            if extruder.variant.getId() != "empty_variant":
                nozzle_name = extruder.variant.getName()

            # This is a list of root material IDs to use for searching for suitable quality profiles.
            # The root material IDs in this list are in prioritized order.
            root_material_id_list = []
            has_material = False  # flag indicating whether this extruder has a material assigned
            root_material_id = None
            if extruder.material.getId() != "empty_material":
                has_material = True
                root_material_id = extruder.material.getMetaDataEntry("base_file")
                # Convert possible generic_pla_175 -> generic_pla
                root_material_id = self._material_manager.getRootMaterialIDWithoutDiameter(root_material_id)
                root_material_id_list.append(root_material_id)

                # Also try to get the fallback materials
                fallback_ids = self._material_manager.getFallBackMaterialIdsByMaterial(extruder.material)

                if fallback_ids:
                    root_material_id_list.extend(fallback_ids)

                # Weed out duplicates while preserving the order.
                seen = set()  # type: Set[str]
                root_material_id_list = [x for x in root_material_id_list if x not in seen and not seen.add(x)]  # type: ignore

            # Here we construct a list of nodes we want to look for qualities with the highest priority first.
            # The use case is that, when we look for qualities for a machine, we first want to search in the following
            # order:
            #   1. machine-nozzle-buildplate-and-material-specific qualities if exist
            #   2. machine-nozzle-and-material-specific qualities if exist
            #   3. machine-nozzle-specific qualities if exist
            #   4. machine-material-specific qualities if exist
            #   5. machine-specific global qualities if exist, otherwise generic global qualities
            #      NOTE: We DO NOT fail back to generic global qualities if machine-specific global qualities exist.
            #            This is because when a machine defines its own global qualities such as Normal, Fine, etc.,
            #            it is intended to maintain those specific qualities ONLY. If we still fail back to the generic
            #            global qualities, there can be unimplemented quality types e.g. "coarse", and this is not
            #            correct.
            # Each points above can be represented as a node in the lookup tree, so here we simply put those nodes into
            # the list with priorities as the order. Later, we just need to loop over each node in this list and fetch
            # qualities from there.
            node_info_list_0 = [nozzle_name, buildplate_name, root_material_id]  # type: List[Optional[str]]
            nodes_to_check = []

            # This function tries to recursively find the deepest (the most specific) branch and add those nodes to
            # the search list in the order described above. So, by iterating over that search node list, we first look
            # in the more specific branches and then the less specific (generic) ones.
            def addNodesToCheck(node: Optional[QualityNode], nodes_to_check_list: List[QualityNode], node_info_list, node_info_idx: int) -> None:
                if node is None:
                    return

                if node_info_idx < len(node_info_list):
                    node_name = node_info_list[node_info_idx]
                    if node_name is not None:
                        current_node = node.getChildNode(node_name)
                        if current_node is not None and has_material:
                            addNodesToCheck(current_node, nodes_to_check_list, node_info_list, node_info_idx + 1)

                if has_material:
                    for rmid in root_material_id_list:
                        material_node = node.getChildNode(rmid)
                        if material_node:
                            nodes_to_check_list.append(material_node)
                            break

                nodes_to_check_list.append(node)

            addNodesToCheck(machine_node, nodes_to_check, node_info_list_0, 0)

            # The last fall back will be the global qualities (either from the machine-specific node or the generic
            # node), but we only use one. For details see the overview comments above.

            if machine_node is not None and machine_node.quality_type_map:
                nodes_to_check += [machine_node]
            elif default_machine_node is not None:
                nodes_to_check += [default_machine_node]

            for node_idx, node in enumerate(nodes_to_check):
                if node and node.quality_type_map:
                    if has_extruder_specific_qualities:
                        # Only include variant qualities; skip non global qualities
                        quality_node = list(node.quality_type_map.values())[0]
                        is_global_quality = parseBool(quality_node.getMetaDataEntry("global_quality", False))
                        if is_global_quality:
                            continue

                    for quality_type, quality_node in node.quality_type_map.items():
                        if quality_type not in quality_group_dict:
                            quality_group = QualityGroup(quality_node.getMetaDataEntry("name", ""), quality_type)
                            quality_group_dict[quality_type] = quality_group

                        quality_group = quality_group_dict[quality_type]
                        if position not in quality_group.nodes_for_extruders:
                            quality_group.setExtruderNode(position, quality_node)

                # If the machine has its own specific qualities, for extruders, it should skip the global qualities
                # and use the material/variant specific qualities.
                if has_extruder_specific_qualities:
                    if node_idx == len(nodes_to_check) - 1:
                        break

        # Update availabilities for each quality group
        self._updateQualityGroupsAvailability(machine, quality_group_dict.values())

        return quality_group_dict
示例#34
0
    def importProfile(self, file_name: str) -> Dict[str, str]:
        Logger.log("d", "Attempting to import profile %s", file_name)
        if not file_name:
            return { "status": "error", "message": catalog.i18nc("@info:status Don't translate the XML tags <filename>!", "Failed to import profile from <filename>{0}</filename>: {1}", file_name, "Invalid path")}

        global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack()
        if not global_stack:
            return {"status": "error", "message": catalog.i18nc("@info:status Don't translate the XML tags <filename>!", "Can't import profile from <filename>{0}</filename> before a printer is added.", file_name)}
        container_tree = ContainerTree.getInstance()

        machine_extruders = global_stack.extruderList

        plugin_registry = PluginRegistry.getInstance()
        extension = file_name.split(".")[-1]

        for plugin_id, meta_data in self._getIOPlugins("profile_reader"):
            if meta_data["profile_reader"][0]["extension"] != extension:
                continue
            profile_reader = cast(ProfileReader, plugin_registry.getPluginObject(plugin_id))
            try:
                profile_or_list = profile_reader.read(file_name)  # Try to open the file with the profile reader.
            except NoProfileException:
                return { "status": "ok", "message": catalog.i18nc("@info:status Don't translate the XML tags <filename>!", "No custom profile to import in file <filename>{0}</filename>", file_name)}
            except Exception as e:
                # Note that this will fail quickly. That is, if any profile reader throws an exception, it will stop reading. It will only continue reading if the reader returned None.
                Logger.log("e", "Failed to import profile from %s: %s while using profile reader. Got exception %s", file_name, profile_reader.getPluginId(), str(e))
                return { "status": "error", "message": catalog.i18nc("@info:status Don't translate the XML tags <filename>!", "Failed to import profile from <filename>{0}</filename>:", file_name) + "\n<message>" + str(e) + "</message>"}

            if profile_or_list:
                # Ensure it is always a list of profiles
                if not isinstance(profile_or_list, list):
                    profile_or_list = [profile_or_list]

                # First check if this profile is suitable for this machine
                global_profile = None
                extruder_profiles = []
                if len(profile_or_list) == 1:
                    global_profile = profile_or_list[0]
                else:
                    for profile in profile_or_list:
                        if not profile.getMetaDataEntry("position"):
                            global_profile = profile
                        else:
                            extruder_profiles.append(profile)
                extruder_profiles = sorted(extruder_profiles, key = lambda x: int(x.getMetaDataEntry("position")))
                profile_or_list = [global_profile] + extruder_profiles

                if not global_profile:
                    Logger.log("e", "Incorrect profile [%s]. Could not find global profile", file_name)
                    return { "status": "error",
                             "message": catalog.i18nc("@info:status Don't translate the XML tags <filename>!", "This profile <filename>{0}</filename> contains incorrect data, could not import it.", file_name)}
                profile_definition = global_profile.getMetaDataEntry("definition")

                # Make sure we have a profile_definition in the file:
                if profile_definition is None:
                    break
                machine_definitions = self.findContainers(id = profile_definition)
                if not machine_definitions:
                    Logger.log("e", "Incorrect profile [%s]. Unknown machine type [%s]", file_name, profile_definition)
                    return {"status": "error",
                            "message": catalog.i18nc("@info:status Don't translate the XML tags <filename>!", "This profile <filename>{0}</filename> contains incorrect data, could not import it.", file_name)
                            }
                machine_definition = machine_definitions[0]

                # Get the expected machine definition.
                # i.e.: We expect gcode for a UM2 Extended to be defined as normal UM2 gcode...
                has_machine_quality = parseBool(machine_definition.getMetaDataEntry("has_machine_quality", "false"))
                profile_definition = machine_definition.getMetaDataEntry("quality_definition", machine_definition.getId()) if has_machine_quality else "fdmprinter"
                expected_machine_definition = container_tree.machines[global_stack.definition.getId()].quality_definition

                # And check if the profile_definition matches either one (showing error if not):
                if profile_definition != expected_machine_definition:
                    Logger.log("d", "Profile {file_name} is for machine {profile_definition}, but the current active machine is {expected_machine_definition}. Changing profile's definition.".format(file_name = file_name, profile_definition = profile_definition, expected_machine_definition = expected_machine_definition))
                    global_profile.setMetaDataEntry("definition", expected_machine_definition)
                    for extruder_profile in extruder_profiles:
                        extruder_profile.setMetaDataEntry("definition", expected_machine_definition)

                quality_name = global_profile.getName()
                quality_type = global_profile.getMetaDataEntry("quality_type")

                name_seed = os.path.splitext(os.path.basename(file_name))[0]
                new_name = self.uniqueName(name_seed)

                # Ensure it is always a list of profiles
                if type(profile_or_list) is not list:
                    profile_or_list = [profile_or_list]

                # Make sure that there are also extruder stacks' quality_changes, not just one for the global stack
                if len(profile_or_list) == 1:
                    global_profile = profile_or_list[0]
                    extruder_profiles = []
                    for idx, extruder in enumerate(global_stack.extruderList):
                        profile_id = ContainerRegistry.getInstance().uniqueName(global_stack.getId() + "_extruder_" + str(idx + 1))
                        profile = InstanceContainer(profile_id)
                        profile.setName(quality_name)
                        profile.setMetaDataEntry("setting_version", cura.CuraApplication.CuraApplication.SettingVersion)
                        profile.setMetaDataEntry("type", "quality_changes")
                        profile.setMetaDataEntry("definition", expected_machine_definition)
                        profile.setMetaDataEntry("quality_type", quality_type)
                        profile.setDirty(True)
                        if idx == 0:
                            # Move all per-extruder settings to the first extruder's quality_changes
                            for qc_setting_key in global_profile.getAllKeys():
                                settable_per_extruder = global_stack.getProperty(qc_setting_key, "settable_per_extruder")
                                if settable_per_extruder:
                                    setting_value = global_profile.getProperty(qc_setting_key, "value")

                                    setting_definition = global_stack.getSettingDefinition(qc_setting_key)
                                    if setting_definition is not None:
                                        new_instance = SettingInstance(setting_definition, profile)
                                        new_instance.setProperty("value", setting_value)
                                        new_instance.resetState()  # Ensure that the state is not seen as a user state.
                                        profile.addInstance(new_instance)
                                        profile.setDirty(True)

                                    global_profile.removeInstance(qc_setting_key, postpone_emit = True)
                        extruder_profiles.append(profile)

                    for profile in extruder_profiles:
                        profile_or_list.append(profile)

                # Import all profiles
                profile_ids_added = []  # type: List[str]
                for profile_index, profile in enumerate(profile_or_list):
                    if profile_index == 0:
                        # This is assumed to be the global profile
                        profile_id = (cast(ContainerInterface, global_stack.getBottom()).getId() + "_" + name_seed).lower().replace(" ", "_")

                    elif profile_index < len(machine_extruders) + 1:
                        # This is assumed to be an extruder profile
                        extruder_id = machine_extruders[profile_index - 1].definition.getId()
                        extruder_position = str(profile_index - 1)
                        if not profile.getMetaDataEntry("position"):
                            profile.setMetaDataEntry("position", extruder_position)
                        else:
                            profile.setMetaDataEntry("position", extruder_position)
                        profile_id = (extruder_id + "_" + name_seed).lower().replace(" ", "_")

                    else:  # More extruders in the imported file than in the machine.
                        continue  # Delete the additional profiles.

                    result = self._configureProfile(profile, profile_id, new_name, expected_machine_definition)
                    if result is not None:
                        # Remove any profiles that did got added.
                        for profile_id in profile_ids_added:
                            self.removeContainer(profile_id)

                        return {"status": "error", "message": catalog.i18nc(
                            "@info:status Don't translate the XML tag <filename>!",
                            "Failed to import profile from <filename>{0}</filename>:",
                            file_name) + " " + result}
                    profile_ids_added.append(profile.getId())
                return {"status": "ok", "message": catalog.i18nc("@info:status", "Successfully imported profile {0}", profile_or_list[0].getName())}

            # This message is throw when the profile reader doesn't find any profile in the file
            return {"status": "error", "message": catalog.i18nc("@info:status", "File {0} does not contain any valid profile.", file_name)}

        # If it hasn't returned by now, none of the plugins loaded the profile successfully.
        return {"status": "error", "message": catalog.i18nc("@info:status", "Profile {0} has an unknown file type or is corrupted.", file_name)}
示例#35
0
    def deserialize(self, serialized):
        # update the serialized data first
        from UM.Settings.Interfaces import ContainerInterface
        serialized = ContainerInterface.deserialize(self, serialized)

        try:
            data = ET.fromstring(serialized)
        except:
            Logger.logException(
                "e", "An exception occured while parsing the material profile")
            return

        # Reset previous metadata
        self.clearData()  # Ensure any previous data is gone.
        meta_data = {}
        meta_data["type"] = "material"
        meta_data["base_file"] = self.id
        meta_data["status"] = "unknown"  # TODO: Add material verfication

        common_setting_values = {}

        inherits = data.find("./um:inherits", self.__namespaces)
        if inherits is not None:
            inherited = self._resolveInheritance(inherits.text)
            data = self._mergeXML(inherited, data)

        if "version" in data.attrib:
            meta_data["setting_version"] = self.xmlVersionToSettingVersion(
                data.attrib["version"])
        else:
            meta_data["setting_version"] = self.xmlVersionToSettingVersion(
                "1.2"
            )  #1.2 and lower didn't have that version number there yet.
        metadata = data.iterfind("./um:metadata/*", self.__namespaces)
        for entry in metadata:
            tag_name = _tag_without_namespace(entry)

            if tag_name == "name":
                brand = entry.find("./um:brand", self.__namespaces)
                material = entry.find("./um:material", self.__namespaces)
                color = entry.find("./um:color", self.__namespaces)
                label = entry.find("./um:label", self.__namespaces)

                if label is not None:
                    self._name = label.text
                else:
                    self._name = self._profile_name(material.text, color.text)
                meta_data["brand"] = brand.text
                meta_data["material"] = material.text
                meta_data["color_name"] = color.text
                continue
            meta_data[tag_name] = entry.text

            if tag_name in self.__material_metadata_setting_map:
                common_setting_values[self.__material_metadata_setting_map[
                    tag_name]] = entry.text

        if "description" not in meta_data:
            meta_data["description"] = ""

        if "adhesion_info" not in meta_data:
            meta_data["adhesion_info"] = ""

        property_values = {}
        properties = data.iterfind("./um:properties/*", self.__namespaces)
        for entry in properties:
            tag_name = _tag_without_namespace(entry)
            property_values[tag_name] = entry.text

            if tag_name in self.__material_properties_setting_map:
                common_setting_values[self.__material_properties_setting_map[
                    tag_name]] = entry.text

        meta_data["approximate_diameter"] = str(
            round(float(property_values.get("diameter", 2.85))))  # In mm
        meta_data["properties"] = property_values

        self.setDefinition(
            ContainerRegistry.getInstance().findDefinitionContainers(
                id="fdmprinter")[0])

        common_compatibility = True
        settings = data.iterfind("./um:settings/um:setting", self.__namespaces)
        for entry in settings:
            key = entry.get("key")
            if key in self.__material_settings_setting_map:
                common_setting_values[
                    self.__material_settings_setting_map[key]] = entry.text
            elif key in self.__unmapped_settings:
                if key == "hardware compatible":
                    common_compatibility = parseBool(entry.text)
            else:
                Logger.log("d", "Unsupported material setting %s", key)
        self._cached_values = common_setting_values  # from InstanceContainer ancestor

        meta_data["compatible"] = common_compatibility
        self.setMetaData(meta_data)
        self._dirty = False

        machines = data.iterfind("./um:settings/um:machine", self.__namespaces)
        for machine in machines:
            machine_compatibility = common_compatibility
            machine_setting_values = {}
            settings = machine.iterfind("./um:setting", self.__namespaces)
            for entry in settings:
                key = entry.get("key")
                if key in self.__material_settings_setting_map:
                    machine_setting_values[
                        self.__material_settings_setting_map[key]] = entry.text
                elif key in self.__unmapped_settings:
                    if key == "hardware compatible":
                        machine_compatibility = parseBool(entry.text)
                else:
                    Logger.log("d", "Unsupported material setting %s", key)

            cached_machine_setting_properties = common_setting_values.copy()
            cached_machine_setting_properties.update(machine_setting_values)

            identifiers = machine.iterfind("./um:machine_identifier",
                                           self.__namespaces)
            for identifier in identifiers:
                machine_id = self.__product_id_map.get(
                    identifier.get("product"), None)
                if machine_id is None:
                    # Lets try again with some naive heuristics.
                    machine_id = identifier.get("product").replace(" ",
                                                                   "").lower()

                definitions = ContainerRegistry.getInstance(
                ).findDefinitionContainers(id=machine_id)
                if not definitions:
                    Logger.log("w", "No definition found for machine ID %s",
                               machine_id)
                    continue

                definition = definitions[0]

                if machine_compatibility:
                    new_material_id = self.id + "_" + machine_id

                    new_material = XmlMaterialProfile(new_material_id)

                    # Update the private directly, as we want to prevent the lookup that is done when using setName
                    new_material._name = self.getName()
                    new_material.setMetaData(copy.deepcopy(self.getMetaData()))
                    new_material.setDefinition(definition)
                    # Don't use setMetadata, as that overrides it for all materials with same base file
                    new_material.getMetaData(
                    )["compatible"] = machine_compatibility

                    new_material.setCachedValues(
                        cached_machine_setting_properties)

                    new_material._dirty = False

                    ContainerRegistry.getInstance().addContainer(new_material)

                hotends = machine.iterfind("./um:hotend", self.__namespaces)
                for hotend in hotends:
                    hotend_id = hotend.get("id")
                    if hotend_id is None:
                        continue

                    variant_containers = ContainerRegistry.getInstance(
                    ).findInstanceContainers(id=hotend_id)
                    if not variant_containers:
                        # It is not really properly defined what "ID" is so also search for variants by name.
                        variant_containers = ContainerRegistry.getInstance(
                        ).findInstanceContainers(definition=definition.id,
                                                 name=hotend_id)

                    if not variant_containers:
                        #Logger.log("d", "No variants found with ID or name %s for machine %s", hotend_id, definition.id)
                        continue

                    hotend_compatibility = machine_compatibility
                    hotend_setting_values = {}
                    settings = hotend.iterfind("./um:setting",
                                               self.__namespaces)
                    for entry in settings:
                        key = entry.get("key")
                        if key in self.__material_settings_setting_map:
                            hotend_setting_values[
                                self.__material_settings_setting_map[
                                    key]] = entry.text
                        elif key in self.__unmapped_settings:
                            if key == "hardware compatible":
                                hotend_compatibility = parseBool(entry.text)
                        else:
                            Logger.log("d", "Unsupported material setting %s",
                                       key)

                    new_hotend_id = self.id + "_" + machine_id + "_" + hotend_id.replace(
                        " ", "_")

                    new_hotend_material = XmlMaterialProfile(new_hotend_id)

                    # Update the private directly, as we want to prevent the lookup that is done when using setName
                    new_hotend_material._name = self.getName()
                    new_hotend_material.setMetaData(
                        copy.deepcopy(self.getMetaData()))
                    new_hotend_material.setDefinition(definition)
                    new_hotend_material.addMetaDataEntry(
                        "variant", variant_containers[0].id)
                    # Don't use setMetadata, as that overrides it for all materials with same base file
                    new_hotend_material.getMetaData(
                    )["compatible"] = hotend_compatibility

                    cached_hotend_setting_properties = cached_machine_setting_properties.copy(
                    )
                    cached_hotend_setting_properties.update(
                        hotend_setting_values)

                    new_hotend_material.setCachedValues(
                        cached_hotend_setting_properties)

                    new_hotend_material._dirty = False

                    ContainerRegistry.getInstance().addContainer(
                        new_hotend_material)
    def _onGetRemoteClustersFinished(
            self, clusters: List[CloudClusterResponse]) -> None:
        """Callback for when the request for getting the clusters is successful and finished."""

        self._um_cloud_printers = {
            m.getMetaDataEntry(self.META_CLUSTER_ID): m
            for m in CuraApplication.getInstance().getContainerRegistry().
            findContainerStacks(type="machine")
            if m.getMetaDataEntry(self.META_CLUSTER_ID, None)
        }
        new_clusters = []
        all_clusters = {c.cluster_id: c
                        for c in clusters
                        }  # type: Dict[str, CloudClusterResponse]
        online_clusters = {c.cluster_id: c
                           for c in clusters if c.is_online
                           }  # type: Dict[str, CloudClusterResponse]

        # Add the new printers in Cura.
        for device_id, cluster_data in all_clusters.items():
            if device_id not in self._remote_clusters:
                new_clusters.append(cluster_data)
            if device_id in self._um_cloud_printers:
                # Existing cloud printers may not have the host_guid meta-data entry. If that's the case, add it.
                if not self._um_cloud_printers[device_id].getMetaDataEntry(
                        self.META_HOST_GUID, None):
                    self._um_cloud_printers[device_id].setMetaDataEntry(
                        self.META_HOST_GUID, cluster_data.host_guid)
                # If a printer was previously not linked to the account and is rediscovered, mark the printer as linked
                # to the current account
                if not parseBool(
                        self._um_cloud_printers[device_id].getMetaDataEntry(
                            META_UM_LINKED_TO_ACCOUNT, "true")):
                    self._um_cloud_printers[device_id].setMetaDataEntry(
                        META_UM_LINKED_TO_ACCOUNT, True)
        self._onDevicesDiscovered(new_clusters)

        # Hide the current removed_printers_message, if there is any
        if self._removed_printers_message:
            self._removed_printers_message.actionTriggered.disconnect(
                self._onRemovedPrintersMessageActionTriggered)
            self._removed_printers_message.hide()

        # Remove the CloudOutput device for offline printers
        offline_device_keys = set(self._remote_clusters.keys()) - set(
            online_clusters.keys())
        for device_id in offline_device_keys:
            self._onDiscoveredDeviceRemoved(device_id)

        # Handle devices that were previously added in Cura but do not exist in the account anymore (i.e. they were
        # removed from the account)
        removed_device_keys = set(self._um_cloud_printers.keys()) - set(
            all_clusters.keys())
        if removed_device_keys:
            self._devicesRemovedFromAccount(removed_device_keys)

        if new_clusters or offline_device_keys or removed_device_keys:
            self.discoveredDevicesChanged.emit()
        if offline_device_keys:
            # If the removed device was active we should connect to the new active device
            self._connectToActiveMachine()

        self._syncing = False
        self._account.setSyncState(self.SYNC_SERVICE_NAME, SyncState.SUCCESS)
示例#37
0
 def hasMaterials(self) -> bool:
     return parseBool(self.getMetaDataEntry("has_materials", False))
    def _onDevicesDiscovered(self,
                             clusters: List[CloudClusterResponse]) -> None:
        """**Synchronously** create machines for discovered devices

        Any new machines are made available to the user.
        May take a long time to complete. As this code needs access to the Application
        and blocks the GIL, creating a Job for this would not make sense.
        Shows a Message informing the user of progress.
        """
        new_devices = []
        remote_clusters_added = False
        host_guid_map = {
            machine.getMetaDataEntry(self.META_HOST_GUID): device_cluster_id
            for device_cluster_id, machine in self._um_cloud_printers.items()
            if machine.getMetaDataEntry(self.META_HOST_GUID)
        }
        machine_manager = CuraApplication.getInstance().getMachineManager()

        for cluster_data in clusters:
            device = CloudOutputDevice(self._api, cluster_data)
            # If the machine already existed before, it will be present in the host_guid_map
            if cluster_data.host_guid in host_guid_map:
                machine = machine_manager.getMachine(
                    device.printerType,
                    {self.META_HOST_GUID: cluster_data.host_guid})
                if machine and machine.getMetaDataEntry(
                        self.META_CLUSTER_ID) != device.key:
                    # If the retrieved device has a different cluster_id than the existing machine, bring the existing
                    # machine up-to-date.
                    self._updateOutdatedMachine(outdated_machine=machine,
                                                new_cloud_output_device=device)

            # Create a machine if we don't already have it. Do not make it the active machine.
            # We only need to add it if it wasn't already added by "local" network or by cloud.
            if machine_manager.getMachine(device.printerType, {self.META_CLUSTER_ID: device.key}) is None \
                    and machine_manager.getMachine(device.printerType, {self.META_NETWORK_KEY: cluster_data.host_name + "*"}) is None:  # The host name is part of the network key.
                new_devices.append(device)
            elif device.getId() not in self._remote_clusters:
                self._remote_clusters[device.getId()] = device
                remote_clusters_added = True
            # If a printer that was removed from the account is re-added, change its metadata to mark it not removed
            # from the account
            elif not parseBool(
                    self._um_cloud_printers[device.key].getMetaDataEntry(
                        META_UM_LINKED_TO_ACCOUNT, "true")):
                self._um_cloud_printers[device.key].setMetaDataEntry(
                    META_UM_LINKED_TO_ACCOUNT, True)

        # Inform the Cloud printers model about new devices.
        new_devices_list_of_dicts = [{
            "key": d.getId(),
            "name": d.name,
            "machine_type": d.printerTypeName,
            "firmware_version": d.firmwareVersion
        } for d in new_devices]
        discovered_cloud_printers_model = CuraApplication.getInstance(
        ).getDiscoveredCloudPrintersModel()
        discovered_cloud_printers_model.addDiscoveredCloudPrinters(
            new_devices_list_of_dicts)

        if not new_devices:
            if remote_clusters_added:
                self._connectToActiveMachine()
            return

        # Sort new_devices on online status first, alphabetical second.
        # Since the first device might be activated in case there is no active printer yet,
        # it would be nice to prioritize online devices
        online_cluster_names = {
            c.friendly_name.lower()
            for c in clusters if c.is_online and not c.friendly_name is None
        }
        new_devices.sort(key=lambda x: ("a{}" if x.name.lower(
        ) in online_cluster_names else "b{}").format(x.name.lower()))

        image_path = os.path.join(
            CuraApplication.getInstance().getPluginRegistry().getPluginPath(
                "UM3NetworkPrinting") or "", "resources", "svg",
            "cloud-flow-completed.svg")

        message = Message(title=self.I18N_CATALOG.i18ncp(
            "info:status", "New printer detected from your Ultimaker account",
            "New printers detected from your Ultimaker account",
            len(new_devices)),
                          progress=0,
                          lifetime=0,
                          image_source=image_path)
        message.show()

        for idx, device in enumerate(new_devices):
            message_text = self.I18N_CATALOG.i18nc(
                "info:status", "Adding printer {} ({}) from your account",
                device.name, device.printerTypeName)
            message.setText(message_text)
            if len(new_devices) > 1:
                message.setProgress((idx / len(new_devices)) * 100)
            CuraApplication.getInstance().processEvents()
            self._remote_clusters[device.getId()] = device

            # If there is no active machine, activate the first available cloud printer
            activate = not CuraApplication.getInstance().getMachineManager(
            ).activeMachine
            self._createMachineFromDiscoveredDevice(device.getId(),
                                                    activate=activate)

        message.setProgress(None)

        max_disp_devices = 3
        if len(new_devices) > max_disp_devices:
            num_hidden = len(new_devices) - max_disp_devices
            device_name_list = [
                "<li>{} ({})</li>".format(device.name, device.printerTypeName)
                for device in new_devices[0:max_disp_devices]
            ]
            device_name_list.append(
                self.I18N_CATALOG.i18nc("info:hidden list items",
                                        "<li>... and {} others</li>",
                                        num_hidden))
            device_names = "".join(device_name_list)
        else:
            device_names = "".join([
                "<li>{} ({})</li>".format(device.name, device.printerTypeName)
                for device in new_devices
            ])

        message_text = self.I18N_CATALOG.i18nc(
            "info:status",
            "Cloud printers added from your account:<ul>{}</ul>", device_names)
        message.setText(message_text)
示例#39
0
 def hasVariantBuildplates(self) -> bool:
     return parseBool(self.getMetaDataEntry("has_variant_buildplates", False))
    def _devicesRemovedFromAccount(self, removed_device_ids: Set[str]) -> None:
        """
        Removes the CloudOutputDevice from the received device ids and marks the specific printers as "removed from
        account". In addition, it generates a message to inform the user about the printers that are no longer linked to
        his/her account. The message is not generated if all the printers have been previously reported as not linked
        to the account.

        :param removed_device_ids: Set of device ids, whose CloudOutputDevice needs to be removed
        :return: None
        """

        if not CuraApplication.getInstance().getCuraAPI().account.isLoggedIn:
            return

        # Do not report device ids which have been previously marked as non-linked to the account
        ignored_device_ids = set()
        for device_id in removed_device_ids:
            if not parseBool(
                    self._um_cloud_printers[device_id].getMetaDataEntry(
                        META_UM_LINKED_TO_ACCOUNT, "true")):
                ignored_device_ids.add(device_id)
        # Keep the reported_device_ids list in a class variable, so that the message button actions can access it and
        # take the necessary steps to fulfill their purpose.
        self.reported_device_ids = removed_device_ids - ignored_device_ids
        if not self.reported_device_ids:
            return

        # Generate message
        self._removed_printers_message = Message(
            title=self.I18N_CATALOG.i18ncp(
                "info:status",
                "Cloud connection is not available for a printer",
                "Cloud connection is not available for some printers",
                len(self.reported_device_ids)))
        device_names = "\n".join([
            "<li>{} ({})</li>".format(
                self._um_cloud_printers[device].name,
                self._um_cloud_printers[device].definition.name)
            for device in self.reported_device_ids
        ])
        message_text = self.I18N_CATALOG.i18ncp(
            "info:status",
            "The following cloud printer is not linked to your account:\n",
            "The following cloud printers are not linked to your account:\n",
            len(self.reported_device_ids))
        message_text += self.I18N_CATALOG.i18nc(
            "info:status",
            "<ul>{}</ul>\nTo establish a connection, please visit the "
            "<a href='https://mycloud.ultimaker.com/'>Ultimaker Digital Factory</a>.",
            device_names)
        self._removed_printers_message.setText(message_text)
        self._removed_printers_message.addAction(
            "keep_printer_configurations_action",
            name=self.I18N_CATALOG.i18nc("@action:button",
                                         "Keep printer configurations"),
            icon="",
            description=
            "Keep the configuration of the cloud printer(s) synced with Cura which are not linked to your account.",
            button_align=Message.ActionButtonAlignment.ALIGN_RIGHT)
        self._removed_printers_message.addAction(
            "remove_printers_action",
            name=self.I18N_CATALOG.i18nc("@action:button", "Remove printers"),
            icon="",
            description=
            "Remove the cloud printer(s) which are not linked to your account.",
            button_style=Message.ActionButtonStyle.SECONDARY,
            button_align=Message.ActionButtonAlignment.ALIGN_LEFT)
        self._removed_printers_message.actionTriggered.connect(
            self._onRemovedPrintersMessageActionTriggered)

        output_device_manager = CuraApplication.getInstance(
        ).getOutputDeviceManager()

        # Remove the output device from the printers
        for device_id in removed_device_ids:
            device = self._um_cloud_printers.get(
                device_id, None)  # type: Optional[GlobalStack]
            if not device:
                continue
            if device_id in output_device_manager.getOutputDeviceIds():
                output_device_manager.removeOutputDevice(device_id)
            if device_id in self._remote_clusters:
                del self._remote_clusters[device_id]

            # Update the printer's metadata to mark it as not linked to the account
            device.setMetaDataEntry(META_UM_LINKED_TO_ACCOUNT, False)

        self._removed_printers_message.show()
示例#41
0
 def getHasMaterials(self) -> bool:
     return parseBool(self.getMetaDataEntry("has_materials", False))
示例#42
0
 def hasVariants(self):
     if self._current_global_stack:
         return parseBool(self._current_global_stack.getMetaDataEntry("has_variants", "false"))
示例#43
0
 def getHasMachineQuality(self) -> bool:
     return parseBool(self.getMetaDataEntry("has_machine_quality", False))
示例#44
0
    def read(self, file_name):
        """Reads a legacy Cura profile from a file and returns it.

        :param file_name: The file to read the legacy Cura profile from.
        :return: The legacy Cura profile that was in the file, if any. If the
            file could not be read or didn't contain a valid profile,  None is returned.
        """

        if file_name.split(".")[-1] != "ini":
            return None
        global_container_stack = Application.getInstance().getGlobalContainerStack()
        if not global_container_stack:
            return None

        multi_extrusion = global_container_stack.getProperty("machine_extruder_count", "value") > 1
        if multi_extrusion:
            Logger.log("e", "Unable to import legacy profile %s. Multi extrusion is not supported", file_name)
            raise Exception("Unable to import legacy profile. Multi extrusion is not supported")

        Logger.log("i", "Importing legacy profile from file " + file_name + ".")
        container_registry = ContainerRegistry.getInstance()
        profile_id = container_registry.uniqueName("Imported Legacy Profile")

        input_parser = configparser.ConfigParser(interpolation = None)
        try:
            input_parser.read([file_name])  # Parse the INI file.
        except Exception as e:
            Logger.log("e", "Unable to open legacy profile %s: %s", file_name, str(e))
            return None

        # Legacy Cura saved the profile under the section "profile_N" where N is the ID of a machine, except when you export in which case it saves it in the section "profile".
        # Since importing multiple machine profiles is out of scope, just import the first section we find.
        section = ""
        for found_section in input_parser.sections():
            if found_section.startswith("profile"):
                section = found_section
                break
        if not section:  # No section starting with "profile" was found. Probably not a proper INI file.
            return None

        try:
            with open(os.path.join(PluginRegistry.getInstance().getPluginPath("LegacyProfileReader"), "DictionaryOfDoom.json"), "r", encoding = "utf-8") as f:
                dict_of_doom = json.load(f)  # Parse the Dictionary of Doom.
        except IOError as e:
            Logger.log("e", "Could not open DictionaryOfDoom.json for reading: %s", str(e))
            return None
        except Exception as e:
            Logger.log("e", "Could not parse DictionaryOfDoom.json: %s", str(e))
            return None

        defaults = self.prepareDefaults(dict_of_doom)
        legacy_settings = self.prepareLocals(input_parser, section, defaults) #Gets the settings from the legacy profile.

        # Serialised format into version 4.5. Do NOT upgrade this, let the version upgrader handle it.
        output_parser = configparser.ConfigParser(interpolation = None)
        output_parser.add_section("general")
        output_parser.add_section("metadata")
        output_parser.add_section("values")

        if "translation" not in dict_of_doom:
            Logger.log("e", "Dictionary of Doom has no translation. Is it the correct JSON file?")
            return None
        current_printer_definition = global_container_stack.definition
        quality_definition = current_printer_definition.getMetaDataEntry("quality_definition")
        if not quality_definition:
            quality_definition = current_printer_definition.getId()
        output_parser["general"]["definition"] = quality_definition
        for new_setting in dict_of_doom["translation"]:  # Evaluate all new settings that would get a value from the translations.
            old_setting_expression = dict_of_doom["translation"][new_setting]
            compiled = compile(old_setting_expression, new_setting, "eval")
            try:
                new_value = eval(compiled, {"math": math}, legacy_settings)  # Pass the legacy settings as local variables to allow access to in the evaluation.
                value_using_defaults = eval(compiled, {"math": math}, defaults)  #Evaluate again using only the default values to try to see if they are default.
            except Exception:  # Probably some setting name that was missing or something else that went wrong in the ini file.
                Logger.log("w", "Setting " + new_setting + " could not be set because the evaluation failed. Something is probably missing from the imported legacy profile.")
                continue
            definitions = current_printer_definition.findDefinitions(key = new_setting)
            if definitions:
                if new_value != value_using_defaults and definitions[0].default_value != new_value:  # Not equal to the default in the new Cura OR the default in the legacy Cura.
                    output_parser["values"][new_setting] = str(new_value) # Store the setting in the profile!

        if len(output_parser["values"]) == 0:
            Logger.log("i", "A legacy profile was imported but everything evaluates to the defaults, creating an empty profile.")

        output_parser["general"]["version"] = "4"
        output_parser["general"]["name"] = profile_id
        output_parser["metadata"]["type"] = "quality_changes"
        output_parser["metadata"]["quality_type"] = "normal" # Don't know what quality_type it is based on, so use "normal" by default.
        output_parser["metadata"]["position"] = "0" # We only support single extrusion.
        output_parser["metadata"]["setting_version"] = "5" # What the dictionary of doom is made for.

        # Serialise in order to perform the version upgrade.
        stream = io.StringIO()
        output_parser.write(stream)
        data = stream.getvalue()

        profile = InstanceContainer(profile_id)
        profile.deserialize(data, file_name) # Also performs the version upgrade.
        profile.setDirty(True)

        #We need to return one extruder stack and one global stack.
        global_container_id = container_registry.uniqueName("Global Imported Legacy Profile")
        # We duplicate the extruder profile into the global stack.
        # This may introduce some settings that are global in the extruder stack and some settings that are per-extruder in the global stack.
        # We don't care about that. The engine will ignore them anyway.
        global_profile = profile.duplicate(new_id = global_container_id, new_name = profile_id) #Needs to have the same name as the extruder profile.
        del global_profile.getMetaData()["position"] # Has no position because it's global.
        global_profile.setDirty(True)

        profile_definition = "fdmprinter"
        from UM.Util import parseBool
        if parseBool(global_container_stack.getMetaDataEntry("has_machine_quality", "False")):
            profile_definition = global_container_stack.getMetaDataEntry("quality_definition")
            if not profile_definition:
                profile_definition = global_container_stack.definition.getId()
        global_profile.setDefinition(profile_definition)

        return [global_profile]
示例#45
0
 def isEnabled(self) -> bool:
     return parseBool(self.getMetaDataEntry("enabled", "True"))
示例#46
0
    def setGlobalNode(self, node: "ContainerNode") -> None:
        self.node_for_global = node

        # Update is_experimental flag
        is_experimental = parseBool(node.getMetaDataEntry("is_experimental", False))
        self.is_experimental |= is_experimental
示例#47
0
    def deserialize(self, serialized):
        data = ET.fromstring(serialized)

        # Reset previous metadata
        self.clearData() # Ensure any previous data is gone.
        meta_data = {}
        meta_data["type"] = "material"
        meta_data["base_file"] = self.id
        meta_data["status"] = "unknown"  # TODO: Add material verfication

        inherits = data.find("./um:inherits", self.__namespaces)
        if inherits is not None:
            inherited = self._resolveInheritance(inherits.text)
            data = self._mergeXML(inherited, data)

        metadata = data.iterfind("./um:metadata/*", self.__namespaces)
        for entry in metadata:
            tag_name = _tag_without_namespace(entry)

            if tag_name == "name":
                brand = entry.find("./um:brand", self.__namespaces)
                material = entry.find("./um:material", self.__namespaces)
                color = entry.find("./um:color", self.__namespaces)
                label = entry.find("./um:label", self.__namespaces)

                if label is not None:
                    self._name = label.text
                else:
                    self._name = self._profile_name(material.text, color.text)
                meta_data["brand"] = brand.text
                meta_data["material"] = material.text
                meta_data["color_name"] = color.text
                continue
            meta_data[tag_name] = entry.text

        if not "description" in meta_data:
            meta_data["description"] = ""

        if not "adhesion_info" in meta_data:
            meta_data["adhesion_info"] = ""

        property_values = {}
        properties = data.iterfind("./um:properties/*", self.__namespaces)
        for entry in properties:
            tag_name = _tag_without_namespace(entry)
            property_values[tag_name] = entry.text

        diameter = float(property_values.get("diameter", 2.85)) # In mm
        density = float(property_values.get("density", 1.3)) # In g/cm3
        meta_data["properties"] = property_values

        self.setDefinition(ContainerRegistry.getInstance().findDefinitionContainers(id = "fdmprinter")[0])

        global_compatibility = True
        global_setting_values = {}
        settings = data.iterfind("./um:settings/um:setting", self.__namespaces)
        for entry in settings:
            key = entry.get("key")
            if key in self.__material_property_setting_map:
                global_setting_values[self.__material_property_setting_map[key]] = entry.text
            elif key in self.__unmapped_settings:
                if key == "hardware compatible":
                    global_compatibility = parseBool(entry.text)
            else:
                Logger.log("d", "Unsupported material setting %s", key)
        self._cached_values = global_setting_values

        meta_data["compatible"] = global_compatibility
        self.setMetaData(meta_data)
        self._dirty = False

        machines = data.iterfind("./um:settings/um:machine", self.__namespaces)
        for machine in machines:
            machine_compatibility = global_compatibility
            machine_setting_values = {}
            settings = machine.iterfind("./um:setting", self.__namespaces)
            for entry in settings:
                key = entry.get("key")
                if key in self.__material_property_setting_map:
                    machine_setting_values[self.__material_property_setting_map[key]] = entry.text
                elif key in self.__unmapped_settings:
                    if key == "hardware compatible":
                        machine_compatibility = parseBool(entry.text)
                else:
                    Logger.log("d", "Unsupported material setting %s", key)

            cached_machine_setting_properties = global_setting_values.copy()
            cached_machine_setting_properties.update(machine_setting_values)

            identifiers = machine.iterfind("./um:machine_identifier", self.__namespaces)
            for identifier in identifiers:
                machine_id = self.__product_id_map.get(identifier.get("product"), None)
                if machine_id is None:
                    # Lets try again with some naive heuristics.
                    machine_id = identifier.get("product").replace(" ", "").lower()

                definitions = ContainerRegistry.getInstance().findDefinitionContainers(id = machine_id)
                if not definitions:
                    Logger.log("w", "No definition found for machine ID %s", machine_id)
                    continue

                definition = definitions[0]

                if machine_compatibility:
                    new_material_id = self.id + "_" + machine_id

                    new_material = XmlMaterialProfile(new_material_id)

                    # Update the private directly, as we want to prevent the lookup that is done when using setName
                    new_material._name = self.getName()
                    new_material.setMetaData(copy.deepcopy(self.getMetaData()))
                    new_material.setDefinition(definition)
                    # Don't use setMetadata, as that overrides it for all materials with same base file
                    new_material.getMetaData()["compatible"] = machine_compatibility

                    new_material.setCachedValues(cached_machine_setting_properties)

                    new_material._dirty = False

                    ContainerRegistry.getInstance().addContainer(new_material)

                hotends = machine.iterfind("./um:hotend", self.__namespaces)
                for hotend in hotends:
                    hotend_id = hotend.get("id")
                    if hotend_id is None:
                        continue

                    variant_containers = ContainerRegistry.getInstance().findInstanceContainers(id = hotend_id)
                    if not variant_containers:
                        # It is not really properly defined what "ID" is so also search for variants by name.
                        variant_containers = ContainerRegistry.getInstance().findInstanceContainers(definition = definition.id, name = hotend_id)

                    if not variant_containers:
                        Logger.log("d", "No variants found with ID or name %s for machine %s", hotend_id, definition.id)
                        continue

                    hotend_compatibility = machine_compatibility
                    hotend_setting_values = {}
                    settings = hotend.iterfind("./um:setting", self.__namespaces)
                    for entry in settings:
                        key = entry.get("key")
                        if key in self.__material_property_setting_map:
                            hotend_setting_values[self.__material_property_setting_map[key]] = entry.text
                        elif key in self.__unmapped_settings:
                            if key == "hardware compatible":
                                hotend_compatibility = parseBool(entry.text)
                        else:
                            Logger.log("d", "Unsupported material setting %s", key)

                    new_hotend_id = self.id + "_" + machine_id + "_" + hotend_id.replace(" ", "_")

                    new_hotend_material = XmlMaterialProfile(new_hotend_id)

                    # Update the private directly, as we want to prevent the lookup that is done when using setName
                    new_hotend_material._name = self.getName()
                    new_hotend_material.setMetaData(copy.deepcopy(self.getMetaData()))
                    new_hotend_material.setDefinition(definition)
                    new_hotend_material.addMetaDataEntry("variant", variant_containers[0].id)
                    # Don't use setMetadata, as that overrides it for all materials with same base file
                    new_hotend_material.getMetaData()["compatible"] = hotend_compatibility

                    cached_hotend_setting_properties = cached_machine_setting_properties.copy()
                    cached_hotend_setting_properties.update(hotend_setting_values)

                    new_hotend_material.setCachedValues(cached_hotend_setting_properties)

                    new_hotend_material._dirty = False

                    ContainerRegistry.getInstance().addContainer(new_hotend_material)
示例#48
0
 def hasVariants(self):
     if self._current_global_stack:
         return parseBool(
             self._current_global_stack.getMetaDataEntry(
                 "has_variants", "false"))
示例#49
0
    def getQualityGroups(self,
                         machine: "GlobalStack") -> Dict[str, QualityGroup]:
        machine_definition_id = getMachineDefinitionIDForQualitySearch(
            machine.definition)

        # To find the quality container for the GlobalStack, check in the following fall-back manner:
        #   (1) the machine-specific node
        #   (2) the generic node
        machine_node = self._machine_nozzle_buildplate_material_quality_type_to_quality_dict.get(
            machine_definition_id)

        # Check if this machine has specific quality profiles for its extruders, if so, when looking up extruder
        # qualities, we should not fall back to use the global qualities.
        has_extruder_specific_qualities = False
        if machine_node:
            if machine_node.children_map:
                has_extruder_specific_qualities = True

        default_machine_node = self._machine_nozzle_buildplate_material_quality_type_to_quality_dict.get(
            self._default_machine_definition_id)

        nodes_to_check = []  # type: List[QualityNode]
        if machine_node is not None:
            nodes_to_check.append(machine_node)
        if default_machine_node is not None:
            nodes_to_check.append(default_machine_node)

        # Iterate over all quality_types in the machine node
        quality_group_dict = {}
        for node in nodes_to_check:
            if node and node.quality_type_map:
                quality_node = list(node.quality_type_map.values())[0]
                is_global_quality = parseBool(
                    quality_node.getMetaDataEntry("global_quality", False))
                if not is_global_quality:
                    continue

                for quality_type, quality_node in node.quality_type_map.items(
                ):
                    quality_group = QualityGroup(
                        quality_node.getMetaDataEntry("name", ""),
                        quality_type)
                    quality_group.setGlobalNode(quality_node)
                    quality_group_dict[quality_type] = quality_group
                break

        buildplate_name = machine.getBuildplateName()

        # Iterate over all extruders to find quality containers for each extruder
        for position, extruder in machine.extruders.items():
            nozzle_name = None
            if extruder.variant.getId() != "empty_variant":
                nozzle_name = extruder.variant.getName()

            # This is a list of root material IDs to use for searching for suitable quality profiles.
            # The root material IDs in this list are in prioritized order.
            root_material_id_list = []
            has_material = False  # flag indicating whether this extruder has a material assigned
            root_material_id = None
            if extruder.material.getId() != "empty_material":
                has_material = True
                root_material_id = extruder.material.getMetaDataEntry(
                    "base_file")
                # Convert possible generic_pla_175 -> generic_pla
                root_material_id = self._material_manager.getRootMaterialIDWithoutDiameter(
                    root_material_id)
                root_material_id_list.append(root_material_id)

                # Also try to get the fallback materials
                fallback_ids = self._material_manager.getFallBackMaterialIdsByMaterial(
                    extruder.material)

                if fallback_ids:
                    root_material_id_list.extend(fallback_ids)

                # Weed out duplicates while preserving the order.
                seen = set()  # type: Set[str]
                root_material_id_list = [
                    x for x in root_material_id_list
                    if x not in seen and not seen.add(x)
                ]  # type: ignore

            # Here we construct a list of nodes we want to look for qualities with the highest priority first.
            # The use case is that, when we look for qualities for a machine, we first want to search in the following
            # order:
            #   1. machine-nozzle-buildplate-and-material-specific qualities if exist
            #   2. machine-nozzle-and-material-specific qualities if exist
            #   3. machine-nozzle-specific qualities if exist
            #   4. machine-material-specific qualities if exist
            #   5. machine-specific global qualities if exist, otherwise generic global qualities
            #      NOTE: We DO NOT fail back to generic global qualities if machine-specific global qualities exist.
            #            This is because when a machine defines its own global qualities such as Normal, Fine, etc.,
            #            it is intended to maintain those specific qualities ONLY. If we still fail back to the generic
            #            global qualities, there can be unimplemented quality types e.g. "coarse", and this is not
            #            correct.
            # Each points above can be represented as a node in the lookup tree, so here we simply put those nodes into
            # the list with priorities as the order. Later, we just need to loop over each node in this list and fetch
            # qualities from there.
            node_info_list_0 = [
                nozzle_name, buildplate_name, root_material_id
            ]  # type: List[Optional[str]]
            nodes_to_check = []

            # This function tries to recursively find the deepest (the most specific) branch and add those nodes to
            # the search list in the order described above. So, by iterating over that search node list, we first look
            # in the more specific branches and then the less specific (generic) ones.
            def addNodesToCheck(node: Optional[QualityNode],
                                nodes_to_check_list: List[QualityNode],
                                node_info_list, node_info_idx: int) -> None:
                if node is None:
                    return

                if node_info_idx < len(node_info_list):
                    node_name = node_info_list[node_info_idx]
                    if node_name is not None:
                        current_node = node.getChildNode(node_name)
                        if current_node is not None and has_material:
                            addNodesToCheck(current_node, nodes_to_check_list,
                                            node_info_list, node_info_idx + 1)

                if has_material:
                    for rmid in root_material_id_list:
                        material_node = node.getChildNode(rmid)
                        if material_node:
                            nodes_to_check_list.append(material_node)
                            break

                nodes_to_check_list.append(node)

            addNodesToCheck(machine_node, nodes_to_check, node_info_list_0, 0)

            # The last fall back will be the global qualities (either from the machine-specific node or the generic
            # node), but we only use one. For details see the overview comments above.

            if machine_node is not None and machine_node.quality_type_map:
                nodes_to_check += [machine_node]
            elif default_machine_node is not None:
                nodes_to_check += [default_machine_node]

            for node_idx, node in enumerate(nodes_to_check):
                if node and node.quality_type_map:
                    if has_extruder_specific_qualities:
                        # Only include variant qualities; skip non global qualities
                        quality_node = list(node.quality_type_map.values())[0]
                        is_global_quality = parseBool(
                            quality_node.getMetaDataEntry(
                                "global_quality", False))
                        if is_global_quality:
                            continue

                    for quality_type, quality_node in node.quality_type_map.items(
                    ):
                        if quality_type not in quality_group_dict:
                            quality_group = QualityGroup(
                                quality_node.getMetaDataEntry("name", ""),
                                quality_type)
                            quality_group_dict[quality_type] = quality_group

                        quality_group = quality_group_dict[quality_type]
                        if position not in quality_group.nodes_for_extruders:
                            quality_group.setExtruderNode(
                                position, quality_node)

                # If the machine has its own specific qualities, for extruders, it should skip the global qualities
                # and use the material/variant specific qualities.
                if has_extruder_specific_qualities:
                    if node_idx == len(nodes_to_check) - 1:
                        break

        # Update availabilities for each quality group
        self._updateQualityGroupsAvailability(machine,
                                              quality_group_dict.values())

        return quality_group_dict
示例#50
0
    def importProfile(self, file_name):
        Logger.log("d", "Attempting to import profile %s", file_name)
        if not file_name:
            return {
                "status":
                "error",
                "message":
                catalog.i18nc(
                    "@info:status Don't translate the XML tags <filename> or <message>!",
                    "Failed to import profile from <filename>{0}</filename>: <message>{1}</message>",
                    file_name, "Invalid path")
            }

        plugin_registry = PluginRegistry.getInstance()
        extension = file_name.split(".")[-1]

        global_container_stack = Application.getInstance(
        ).getGlobalContainerStack()
        if not global_container_stack:
            return

        machine_extruders = list(
            ExtruderManager.getInstance().getMachineExtruders(
                global_container_stack.getId()))
        machine_extruders.sort(key=lambda k: k.getMetaDataEntry("position"))

        for plugin_id, meta_data in self._getIOPlugins("profile_reader"):
            if meta_data["profile_reader"][0]["extension"] != extension:
                continue
            profile_reader = plugin_registry.getPluginObject(plugin_id)
            try:
                profile_or_list = profile_reader.read(
                    file_name)  # Try to open the file with the profile reader.
            except NoProfileException:
                return {
                    "status":
                    "ok",
                    "message":
                    catalog.i18nc(
                        "@info:status Don't translate the XML tags <filename> or <message>!",
                        "No custom profile to import in file <filename>{0}</filename>",
                        file_name)
                }
            except Exception as e:
                # Note that this will fail quickly. That is, if any profile reader throws an exception, it will stop reading. It will only continue reading if the reader returned None.
                Logger.log(
                    "e",
                    "Failed to import profile from %s: %s while using profile reader. Got exception %s",
                    file_name, profile_reader.getPluginId(), str(e))
                return {
                    "status":
                    "error",
                    "message":
                    catalog.i18nc(
                        "@info:status Don't translate the XML tags <filename> or <message>!",
                        "Failed to import profile from <filename>{0}</filename>: <message>{1}</message>",
                        file_name, "\n" + str(e))
                }

            if profile_or_list:
                # Ensure it is always a list of profiles
                if not isinstance(profile_or_list, list):
                    profile_or_list = [profile_or_list]

                # First check if this profile is suitable for this machine
                global_profile = None
                if len(profile_or_list) == 1:
                    global_profile = profile_or_list[0]
                else:
                    for profile in profile_or_list:
                        if not profile.getMetaDataEntry("extruder"):
                            global_profile = profile
                            break
                if not global_profile:
                    Logger.log(
                        "e",
                        "Incorrect profile [%s]. Could not find global profile",
                        file_name)
                    return {
                        "status":
                        "error",
                        "message":
                        catalog.i18nc(
                            "@info:status Don't translate the XML tags <filename> or <message>!",
                            "This profile <filename>{0}</filename> contains incorrect data, could not import it.",
                            file_name)
                    }
                profile_definition = global_profile.getMetaDataEntry(
                    "definition")
                expected_machine_definition = "fdmprinter"
                if parseBool(
                        global_container_stack.getMetaDataEntry(
                            "has_machine_quality", "False")):
                    expected_machine_definition = global_container_stack.getMetaDataEntry(
                        "quality_definition")
                    if not expected_machine_definition:
                        expected_machine_definition = global_container_stack.definition.getId(
                        )
                if expected_machine_definition is not None and profile_definition is not None and profile_definition != expected_machine_definition:
                    Logger.log(
                        "e",
                        "Profile [%s] is for machine [%s] but the current active machine is [%s]. Will not import the profile",
                        file_name, profile_definition,
                        expected_machine_definition)
                    return {
                        "status":
                        "error",
                        "message":
                        catalog.i18nc(
                            "@info:status Don't translate the XML tags <filename> or <message>!",
                            "The machine defined in profile <filename>{0}</filename> ({1}) doesn't match with your current machine ({2}), could not import it.",
                            file_name, profile_definition,
                            expected_machine_definition)
                    }

                name_seed = os.path.splitext(os.path.basename(file_name))[0]
                new_name = self.uniqueName(name_seed)

                # Ensure it is always a list of profiles
                if type(profile_or_list) is not list:
                    profile_or_list = [profile_or_list]

                # Make sure that there are also extruder stacks' quality_changes, not just one for the global stack
                if len(profile_or_list) == 1:
                    global_profile = profile_or_list[0]
                    extruder_profiles = []
                    for idx, extruder in enumerate(
                            global_container_stack.extruders.values()):
                        profile_id = ContainerRegistry.getInstance(
                        ).uniqueName(global_container_stack.getId() +
                                     "_extruder_" + str(idx + 1))
                        profile = InstanceContainer(profile_id)
                        profile.setName(global_profile.getName())
                        profile.addMetaDataEntry(
                            "setting_version", CuraApplication.SettingVersion)
                        profile.addMetaDataEntry("type", "quality_changes")
                        profile.addMetaDataEntry(
                            "definition",
                            global_profile.getMetaDataEntry("definition"))
                        profile.addMetaDataEntry(
                            "quality_type",
                            global_profile.getMetaDataEntry("quality_type"))
                        profile.addMetaDataEntry("position", "0")
                        profile.setDirty(True)
                        if idx == 0:
                            # move all per-extruder settings to the first extruder's quality_changes
                            for qc_setting_key in global_profile.getAllKeys():
                                settable_per_extruder = global_container_stack.getProperty(
                                    qc_setting_key, "settable_per_extruder")
                                if settable_per_extruder:
                                    setting_value = global_profile.getProperty(
                                        qc_setting_key, "value")

                                    setting_definition = global_container_stack.getSettingDefinition(
                                        qc_setting_key)
                                    new_instance = SettingInstance(
                                        setting_definition, profile)
                                    new_instance.setProperty(
                                        "value", setting_value)
                                    new_instance.resetState(
                                    )  # Ensure that the state is not seen as a user state.
                                    profile.addInstance(new_instance)
                                    profile.setDirty(True)

                                    global_profile.removeInstance(
                                        qc_setting_key, postpone_emit=True)
                        extruder_profiles.append(profile)

                    for profile in extruder_profiles:
                        profile_or_list.append(profile)

                # Import all profiles
                for profile_index, profile in enumerate(profile_or_list):
                    if profile_index == 0:
                        # This is assumed to be the global profile
                        profile_id = (
                            global_container_stack.getBottom().getId() + "_" +
                            name_seed).lower().replace(" ", "_")

                    elif profile_index < len(machine_extruders) + 1:
                        # This is assumed to be an extruder profile
                        extruder_id = machine_extruders[profile_index -
                                                        1].definition.getId()
                        extuder_position = str(profile_index - 1)
                        if not profile.getMetaDataEntry("position"):
                            profile.addMetaDataEntry("position",
                                                     extuder_position)
                        else:
                            profile.setMetaDataEntry("position",
                                                     extuder_position)
                        profile_id = (extruder_id + "_" +
                                      name_seed).lower().replace(" ", "_")

                    else:  #More extruders in the imported file than in the machine.
                        continue  #Delete the additional profiles.

                    result = self._configureProfile(profile, profile_id,
                                                    new_name)
                    if result is not None:
                        return {
                            "status":
                            "error",
                            "message":
                            catalog.i18nc(
                                "@info:status Don't translate the XML tags <filename> or <message>!",
                                "Failed to import profile from <filename>{0}</filename>: <message>{1}</message>",
                                file_name, result)
                        }

                return {
                    "status":
                    "ok",
                    "message":
                    catalog.i18nc("@info:status",
                                  "Successfully imported profile {0}",
                                  profile_or_list[0].getName())
                }

            # This message is throw when the profile reader doesn't find any profile in the file
            return {
                "status":
                "error",
                "message":
                catalog.i18nc("@info:status",
                              "File {0} does not contain any valid profile.",
                              file_name)
            }

        # If it hasn't returned by now, none of the plugins loaded the profile successfully.
        return {
            "status":
            "error",
            "message":
            catalog.i18nc(
                "@info:status",
                "Profile {0} has an unknown file type or is corrupted.",
                file_name)
        }
示例#51
0
 def _machineHasOwnQualities(self):
     global_container_stack = Application.getInstance().getGlobalContainerStack()
     if global_container_stack:
         return parseBool(global_container_stack.getMetaDataEntry("has_machine_quality", False))
     return False
示例#52
0
    def deserialize(self, serialized):
        data = ET.fromstring(serialized)

        # Reset previous metadata
        self.clearData()  # Ensure any previous data is gone.

        self.addMetaDataEntry("type", "material")
        self.addMetaDataEntry("base_file", self.id)

        # TODO: Add material verfication
        self.addMetaDataEntry("status", "unknown")

        inherits = data.find("./um:inherits", self.__namespaces)
        if inherits is not None:
            inherited = self._resolveInheritance(inherits.text)
            data = self._mergeXML(inherited, data)

        metadata = data.iterfind("./um:metadata/*", self.__namespaces)
        for entry in metadata:
            tag_name = _tag_without_namespace(entry)

            if tag_name == "name":
                brand = entry.find("./um:brand", self.__namespaces)
                material = entry.find("./um:material", self.__namespaces)
                color = entry.find("./um:color", self.__namespaces)
                label = entry.find("./um:label", self.__namespaces)

                if label is not None:
                    self.setName(label.text)
                else:
                    self.setName(self._profile_name(material.text, color.text))

                self.addMetaDataEntry("brand", brand.text)
                self.addMetaDataEntry("material", material.text)
                self.addMetaDataEntry("color_name", color.text)

                continue

            self.addMetaDataEntry(tag_name, entry.text)

        if not "description" in self.getMetaData():
            self.addMetaDataEntry("description", "")

        if not "adhesion_info" in self.getMetaData():
            self.addMetaDataEntry("adhesion_info", "")

        property_values = {}
        properties = data.iterfind("./um:properties/*", self.__namespaces)
        for entry in properties:
            tag_name = _tag_without_namespace(entry)
            property_values[tag_name] = entry.text

        diameter = float(property_values.get("diameter", 2.85))  # In mm
        density = float(property_values.get("density", 1.3))  # In g/cm3

        self.addMetaDataEntry("properties", property_values)

        self.setDefinition(UM.Settings.ContainerRegistry.getInstance().
                           findDefinitionContainers(id="fdmprinter")[0])

        global_compatibility = True
        global_setting_values = {}
        settings = data.iterfind("./um:settings/um:setting", self.__namespaces)
        for entry in settings:
            key = entry.get("key")
            if key in self.__material_property_setting_map:
                self.setProperty(self.__material_property_setting_map[key],
                                 "value", entry.text)
                global_setting_values[
                    self.__material_property_setting_map[key]] = entry.text
            elif key in self.__unmapped_settings:
                if key == "hardware compatible":
                    global_compatibility = parseBool(entry.text)
            else:
                Logger.log("d", "Unsupported material setting %s", key)

        self.addMetaDataEntry("compatible", global_compatibility)

        self._dirty = False

        machines = data.iterfind("./um:settings/um:machine", self.__namespaces)
        for machine in machines:
            machine_compatibility = global_compatibility
            machine_setting_values = {}
            settings = machine.iterfind("./um:setting", self.__namespaces)
            for entry in settings:
                key = entry.get("key")
                if key in self.__material_property_setting_map:
                    machine_setting_values[
                        self.__material_property_setting_map[key]] = entry.text
                elif key in self.__unmapped_settings:
                    if key == "hardware compatible":
                        machine_compatibility = parseBool(entry.text)
                else:
                    Logger.log("d", "Unsupported material setting %s", key)

            identifiers = machine.iterfind("./um:machine_identifier",
                                           self.__namespaces)
            for identifier in identifiers:
                machine_id = self.__product_id_map.get(
                    identifier.get("product"), None)
                if machine_id is None:
                    # Lets try again with some naive heuristics.
                    machine_id = identifier.get("product").replace(" ",
                                                                   "").lower()

                definitions = UM.Settings.ContainerRegistry.getInstance(
                ).findDefinitionContainers(id=machine_id)
                if not definitions:
                    Logger.log("w", "No definition found for machine ID %s",
                               machine_id)
                    continue

                definition = definitions[0]

                if machine_compatibility:
                    new_material_id = self.id + "_" + machine_id

                    # It could be that we are overwriting, so check if the ID already exists.
                    materials = UM.Settings.ContainerRegistry.getInstance(
                    ).findInstanceContainers(id=new_material_id)
                    if materials:
                        new_material = materials[0]
                        new_material.clearData()
                    else:
                        new_material = XmlMaterialProfile(new_material_id)

                    new_material.setName(self.getName())
                    new_material.setMetaData(copy.deepcopy(self.getMetaData()))
                    new_material.setDefinition(definition)
                    # Don't use setMetadata, as that overrides it for all materials with same base file
                    new_material.getMetaData(
                    )["compatible"] = machine_compatibility

                    for key, value in global_setting_values.items():
                        new_material.setProperty(key, "value", value)

                    for key, value in machine_setting_values.items():
                        new_material.setProperty(key, "value", value)

                    new_material._dirty = False
                    if not materials:
                        UM.Settings.ContainerRegistry.getInstance(
                        ).addContainer(new_material)

                hotends = machine.iterfind("./um:hotend", self.__namespaces)
                for hotend in hotends:
                    hotend_id = hotend.get("id")
                    if hotend_id is None:
                        continue

                    variant_containers = UM.Settings.ContainerRegistry.getInstance(
                    ).findInstanceContainers(id=hotend_id)
                    if not variant_containers:
                        # It is not really properly defined what "ID" is so also search for variants by name.
                        variant_containers = UM.Settings.ContainerRegistry.getInstance(
                        ).findInstanceContainers(definition=definition.id,
                                                 name=hotend_id)

                    if not variant_containers:
                        Logger.log(
                            "d",
                            "No variants found with ID or name %s for machine %s",
                            hotend_id, definition.id)
                        continue

                    hotend_compatibility = machine_compatibility
                    hotend_setting_values = {}
                    settings = hotend.iterfind("./um:setting",
                                               self.__namespaces)
                    for entry in settings:
                        key = entry.get("key")
                        if key in self.__material_property_setting_map:
                            hotend_setting_values[
                                self.__material_property_setting_map[
                                    key]] = entry.text
                        elif key in self.__unmapped_settings:
                            if key == "hardware compatible":
                                hotend_compatibility = parseBool(entry.text)
                        else:
                            Logger.log("d", "Unsupported material setting %s",
                                       key)

                    # It could be that we are overwriting, so check if the ID already exists.
                    new_hotend_id = self.id + "_" + machine_id + "_" + hotend_id.replace(
                        " ", "_")
                    materials = UM.Settings.ContainerRegistry.getInstance(
                    ).findInstanceContainers(id=new_hotend_id)
                    if materials:
                        new_hotend_material = materials[0]
                        new_hotend_material.clearData()
                    else:
                        new_hotend_material = XmlMaterialProfile(new_hotend_id)

                    new_hotend_material.setName(self.getName())
                    new_hotend_material.setMetaData(
                        copy.deepcopy(self.getMetaData()))
                    new_hotend_material.setDefinition(definition)
                    new_hotend_material.addMetaDataEntry(
                        "variant", variant_containers[0].id)
                    # Don't use setMetadata, as that overrides it for all materials with same base file
                    new_hotend_material.getMetaData(
                    )["compatible"] = hotend_compatibility

                    for key, value in global_setting_values.items():
                        new_hotend_material.setProperty(key, "value", value)

                    for key, value in machine_setting_values.items():
                        new_hotend_material.setProperty(key, "value", value)

                    for key, value in hotend_setting_values.items():
                        new_hotend_material.setProperty(key, "value", value)

                    new_hotend_material._dirty = False
                    if not materials:  # It was not added yet, do so now.
                        UM.Settings.ContainerRegistry.getInstance(
                        ).addContainer(new_hotend_material)
示例#53
0
    def deserialize(self, serialized):
        data = ET.fromstring(serialized)

        self.addMetaDataEntry("type", "material")
        self.addMetaDataEntry("base_file", self.id)

        # TODO: Add material verfication
        self.addMetaDataEntry("status", "unknown")

        inherits = data.find("./um:inherits", self.__namespaces)
        if inherits is not None:
            inherited = self._resolveInheritance(inherits.text)
            data = self._mergeXML(inherited, data)

        metadata = data.iterfind("./um:metadata/*", self.__namespaces)
        for entry in metadata:
            tag_name = _tag_without_namespace(entry)

            if tag_name == "name":
                brand = entry.find("./um:brand", self.__namespaces)
                material = entry.find("./um:material", self.__namespaces)
                color = entry.find("./um:color", self.__namespaces)
                label = entry.find("./um:label", self.__namespaces)

                if label is not None:
                    self.setName(label.text)
                else:
                    self.setName(self._profile_name(material.text, color.text))

                self.addMetaDataEntry("brand", brand.text)
                self.addMetaDataEntry("material", material.text)
                self.addMetaDataEntry("color_name", color.text)

                continue

            self.addMetaDataEntry(tag_name, entry.text)

        if not "description" in self.getMetaData():
            self.addMetaDataEntry("description", "")

        if not "adhesion_info" in self.getMetaData():
            self.addMetaDataEntry("adhesion_info", "")

        property_values = {}
        properties = data.iterfind("./um:properties/*", self.__namespaces)
        for entry in properties:
            tag_name = _tag_without_namespace(entry)
            property_values[tag_name] = entry.text

        diameter = float(property_values.get("diameter", 2.85)) # In mm
        density = float(property_values.get("density", 1.3)) # In g/cm3

        self.addMetaDataEntry("properties", property_values)

        self.setDefinition(UM.Settings.ContainerRegistry.getInstance().findDefinitionContainers(id = "fdmprinter")[0])

        global_compatibility = True
        global_setting_values = {}
        settings = data.iterfind("./um:settings/um:setting", self.__namespaces)
        for entry in settings:
            key = entry.get("key")
            if key in self.__material_property_setting_map:
                self.setProperty(self.__material_property_setting_map[key], "value", entry.text, self._definition)
                global_setting_values[self.__material_property_setting_map[key]] = entry.text
            elif key in self.__unmapped_settings:
                if key == "hardware compatible":
                    global_compatibility = parseBool(entry.text)
            else:
                Logger.log("d", "Unsupported material setting %s", key)

        self.addMetaDataEntry("compatible", global_compatibility)

        self._dirty = False

        machines = data.iterfind("./um:settings/um:machine", self.__namespaces)
        for machine in machines:
            machine_compatibility = global_compatibility
            machine_setting_values = {}
            settings = machine.iterfind("./um:setting", self.__namespaces)
            for entry in settings:
                key = entry.get("key")
                if key in self.__material_property_setting_map:
                    machine_setting_values[self.__material_property_setting_map[key]] = entry.text
                elif key in self.__unmapped_settings:
                    if key == "hardware compatible":
                        machine_compatibility = parseBool(entry.text)
                else:
                    Logger.log("d", "Unsupported material setting %s", key)

            identifiers = machine.iterfind("./um:machine_identifier", self.__namespaces)
            for identifier in identifiers:
                machine_id = self.__product_id_map.get(identifier.get("product"), None)
                if machine_id is None:
                    # Lets try again with some naive heuristics.
                    machine_id = identifier.get("product").replace(" ", "").lower()

                definitions = UM.Settings.ContainerRegistry.getInstance().findDefinitionContainers(id = machine_id)
                if not definitions:
                    Logger.log("w", "No definition found for machine ID %s", machine_id)
                    continue

                definition = definitions[0]

                if machine_compatibility:
                    new_material = XmlMaterialProfile(self.id + "_" + machine_id)
                    new_material.setName(self.getName())
                    new_material.setMetaData(copy.deepcopy(self.getMetaData()))
                    new_material.setDefinition(definition)
                    # Don't use setMetadata, as that overrides it for all materials with same base file
                    new_material.getMetaData()["compatible"] = machine_compatibility

                    for key, value in global_setting_values.items():
                        new_material.setProperty(key, "value", value, definition)

                    for key, value in machine_setting_values.items():
                        new_material.setProperty(key, "value", value, definition)

                    new_material._dirty = False

                    UM.Settings.ContainerRegistry.getInstance().addContainer(new_material)


                hotends = machine.iterfind("./um:hotend", self.__namespaces)
                for hotend in hotends:
                    hotend_id = hotend.get("id")
                    if hotend_id is None:
                        continue

                    variant_containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = hotend_id)
                    if not variant_containers:
                        # It is not really properly defined what "ID" is so also search for variants by name.
                        variant_containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(definition = definition.id, name = hotend_id)

                    if not variant_containers:
                        Logger.log("d", "No variants found with ID or name %s for machine %s", hotend_id, definition.id)
                        continue

                    hotend_compatibility = machine_compatibility
                    hotend_setting_values = {}
                    settings = hotend.iterfind("./um:setting", self.__namespaces)
                    for entry in settings:
                        key = entry.get("key")
                        if key in self.__material_property_setting_map:
                            hotend_setting_values[self.__material_property_setting_map[key]] = entry.text
                        elif key in self.__unmapped_settings:
                            if key == "hardware compatible":
                                hotend_compatibility = parseBool(entry.text)
                        else:
                            Logger.log("d", "Unsupported material setting %s", key)

                    new_hotend_material = XmlMaterialProfile(self.id + "_" + machine_id + "_" + hotend_id.replace(" ", "_"))
                    new_hotend_material.setName(self.getName())
                    new_hotend_material.setMetaData(copy.deepcopy(self.getMetaData()))
                    new_hotend_material.setDefinition(definition)
                    new_hotend_material.addMetaDataEntry("variant", variant_containers[0].id)
                    # Don't use setMetadata, as that overrides it for all materials with same base file
                    new_hotend_material.getMetaData()["compatible"] = hotend_compatibility

                    for key, value in global_setting_values.items():
                        new_hotend_material.setProperty(key, "value", value, definition)

                    for key, value in machine_setting_values.items():
                        new_hotend_material.setProperty(key, "value", value, definition)

                    for key, value in hotend_setting_values.items():
                        new_hotend_material.setProperty(key, "value", value, definition)

                    new_hotend_material._dirty = False
                    UM.Settings.ContainerRegistry.getInstance().addContainer(new_hotend_material)
示例#54
0
 def isEnabled(self):
     return parseBool(self.getMetaDataEntry("enabled", "True"))
示例#55
0
    def setExtruderNode(self, position: int, node: "ContainerNode") -> None:
        self.nodes_for_extruders[position] = node

        # Update is_experimental flag
        is_experimental = parseBool(node.getMetaDataEntry("is_experimental", False))
        self.is_experimental |= is_experimental
    def startPrint(self):
        global_container_stack = Application.getInstance(
        ).getGlobalContainerStack()
        if not global_container_stack:
            return

        self._auto_print = parseBool(
            global_container_stack.getMetaDataEntry("octoprint_auto_print",
                                                    True))
        if self._auto_print:
            Application.getInstance().showPrintMonitor.emit(True)

        if self.jobState != "ready" and self.jobState != "":
            self._error_message = Message(
                i18n_catalog.i18nc(
                    "@info:status",
                    "OctoPrint is printing. Unable to start a new job."))
            self._error_message.show()
            return
        try:
            self._progress_message = Message(
                i18n_catalog.i18nc("@info:status",
                                   "Sending data to OctoPrint"), 0, False, -1)
            self._progress_message.show()

            ## Mash the data into single string
            single_string_file_data = ""
            last_process_events = time()
            for line in self._gcode:
                single_string_file_data += line
                if time() > last_process_events + 0.05:
                    # Ensure that the GUI keeps updated at least 20 times per second.
                    QCoreApplication.processEvents()
                    last_process_events = time()

            file_name = "%s.gcode" % Application.getInstance(
            ).getPrintInformation().jobName

            ##  Create multi_part request
            self._post_multi_part = QHttpMultiPart(QHttpMultiPart.FormDataType)

            ##  Create parts (to be placed inside multipart)
            self._post_part = QHttpPart()
            self._post_part.setHeader(QNetworkRequest.ContentDispositionHeader,
                                      "form-data; name=\"select\"")
            self._post_part.setBody(b"true")
            self._post_multi_part.append(self._post_part)

            if self._auto_print:
                self._post_part = QHttpPart()
                self._post_part.setHeader(
                    QNetworkRequest.ContentDispositionHeader,
                    "form-data; name=\"print\"")
                self._post_part.setBody(b"true")
                self._post_multi_part.append(self._post_part)

            self._post_part = QHttpPart()
            self._post_part.setHeader(
                QNetworkRequest.ContentDispositionHeader,
                "form-data; name=\"file\"; filename=\"%s\"" % file_name)
            self._post_part.setBody(single_string_file_data.encode())
            self._post_multi_part.append(self._post_part)

            destination = "local"
            if parseBool(
                    global_container_stack.getMetaDataEntry(
                        "octoprint_store_sd", False)):
                destination = "sdcard"

            url = QUrl(self._api_url + "files/" + destination)

            ##  Create the QT request
            self._post_request = QNetworkRequest(url)
            self._post_request.setRawHeader(self._api_header.encode(),
                                            self._api_key.encode())

            ##  Post request + data
            self._post_reply = self._manager.post(self._post_request,
                                                  self._post_multi_part)
            self._post_reply.uploadProgress.connect(self._onUploadProgress)

            self._gcode = None

        except IOError:
            self._progress_message.hide()
            self._error_message = Message(
                i18n_catalog.i18nc("@info:status",
                                   "Unable to send data to OctoPrint."))
            self._error_message.show()
        except Exception as e:
            self._progress_message.hide()
            Logger.log(
                "e",
                "An exception occurred in network connection: %s" % str(e))