123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298 |
- # Copyright (c) 2018 Ultimaker B.V.
- # Cura is released under the terms of the LGPLv3 or higher.
- import configparser #To parse the files we need to upgrade and write the new files.
- import io #To serialise configparser output to a string.
- import os
- from typing import Dict, List, Set, Tuple
- from urllib.parse import quote_plus
- from UM.Resources import Resources
- from UM.VersionUpgrade import VersionUpgrade
- _removed_settings = { #Settings that were removed in 2.5.
- "start_layers_at_same_position",
- "sub_div_rad_mult"
- } # type: Set[str]
- _split_settings = { #These settings should be copied to all settings it was split into.
- "support_interface_line_distance": {"support_roof_line_distance", "support_bottom_line_distance"}
- } # type: Dict[str, Set[str]]
- ## A collection of functions that convert the configuration of the user in Cura
- # 2.5 to a configuration for Cura 2.6.
- #
- # All of these methods are essentially stateless.
- class VersionUpgrade25to26(VersionUpgrade):
- def __init__(self) -> None:
- super().__init__()
- self._current_fdm_printer_count = 2
- ## Gets the version number from a CFG file in Uranium's 2.5 format.
- #
- # Since the format may change, this is implemented for the 2.5 format only
- # and needs to be included in the version upgrade system rather than
- # globally in Uranium.
- #
- # \param serialised The serialised form of a CFG file.
- # \return The version number stored in the CFG file.
- # \raises ValueError The format of the version number in the file is
- # incorrect.
- # \raises KeyError The format of the file is incorrect.
- def getCfgVersion(self, serialised: str) -> int:
- parser = configparser.ConfigParser(interpolation = None)
- parser.read_string(serialised)
- format_version = int(parser.get("general", "version")) #Explicitly give an exception when this fails. That means that the file format is not recognised.
- setting_version = int(parser.get("metadata", "setting_version", fallback = "0"))
- return format_version * 1000000 + setting_version
- ## Upgrades the preferences file from version 2.5 to 2.6.
- #
- # \param serialised The serialised form of a preferences file.
- # \param filename The name of the file to upgrade.
- def upgradePreferences(self, serialised: str, filename: str) -> Tuple[List[str], List[str]]:
- parser = configparser.ConfigParser(interpolation = None)
- parser.read_string(serialised)
- #Remove settings from the visible_settings.
- if parser.has_section("general") and "visible_settings" in parser["general"]:
- visible_settings = parser["general"]["visible_settings"].split(";")
- new_visible_settings = []
- for setting in visible_settings:
- if setting in _removed_settings:
- continue #Skip.
- if setting in _split_settings:
- for replaced_setting in _split_settings[setting]:
- new_visible_settings.append(replaced_setting)
- continue #Don't add the original.
- new_visible_settings.append(setting) #No special handling, so just add the original visible setting back.
- parser["general"]["visible_settings"] = ";".join(new_visible_settings)
- #Change the version number in the file.
- if "general" not in parser:
- parser["general"] = {}
- parser.set("general", "version", "4")
- if "metadata" not in parser:
- parser["metadata"] = {}
- parser.set("metadata", "setting_version", "1")
- #Re-serialise the file.
- output = io.StringIO()
- parser.write(output)
- return [filename], [output.getvalue()]
- ## Upgrades an instance container from version 2.5 to 2.6.
- #
- # \param serialised The serialised form of a quality profile.
- # \param filename The name of the file to upgrade.
- def upgradeInstanceContainer(self, serialised: str, filename: str) -> Tuple[List[str], List[str]]:
- parser = configparser.ConfigParser(interpolation = None)
- parser.read_string(serialised)
- #Remove settings from the [values] section.
- if parser.has_section("values"):
- for removed_setting in (_removed_settings & parser["values"].keys()): #Both in keys that need to be removed and in keys present in the file.
- del parser["values"][removed_setting]
- for replaced_setting in (_split_settings.keys() & parser["values"].keys()):
- for replacement in _split_settings[replaced_setting]:
- parser["values"][replacement] = parser["values"][replaced_setting] #Copy to replacement before removing the original!
- del replaced_setting
- for each_section in ("general", "metadata"):
- if not parser.has_section(each_section):
- parser.add_section(each_section)
- # Update version numbers
- parser["general"]["version"] = "2"
- parser["metadata"]["setting_version"] = "1"
- #Re-serialise the file.
- output = io.StringIO()
- parser.write(output)
- return [filename], [output.getvalue()]
- ## Upgrades a machine stack from version 2.5 to 2.6
- #
- # \param serialised The serialised form of a quality profile.
- # \param filename The name of the file to upgrade.
- def upgradeMachineStack(self, serialised: str, filename: str) -> Tuple[List[str], List[str]]:
- parser = configparser.ConfigParser(interpolation = None)
- parser.read_string(serialised)
- # NOTE: This is for Custom FDM printers
- # In 2.5, Custom FDM printers don't support multiple extruders, but since 2.6 they do.
- machine_id = parser["general"]["id"]
- quality_container_id = parser["containers"]["2"]
- material_container_id = parser["containers"]["3"]
- # we don't have definition_changes container in 2.5
- if "6" in parser["containers"]:
- definition_container_id = parser["containers"]["6"]
- else:
- definition_container_id = parser["containers"]["5"]
- if definition_container_id == "custom" and not self._checkCustomFdmPrinterHasExtruderStack(machine_id):
- # go through all extruders and make sure that this custom FDM printer has 8 extruder stacks.
- self._acquireNextUniqueCustomFdmPrinterExtruderStackIdIndex()
- for position in range(8):
- self._createCustomFdmPrinterExtruderStack(machine_id, position, quality_container_id, material_container_id)
- # Update version numbers
- parser["general"]["version"] = "3"
- parser["metadata"]["setting_version"] = "1"
- # Re-serialise the file.
- output = io.StringIO()
- parser.write(output)
- return [filename], [output.getvalue()]
- ## Acquires the next unique extruder stack index number for the Custom FDM Printer.
- def _acquireNextUniqueCustomFdmPrinterExtruderStackIdIndex(self) -> int:
- extruder_stack_dir = os.path.join(Resources.getDataStoragePath(), "extruders")
- file_name_list = os.listdir(extruder_stack_dir)
- file_name_list = [os.path.basename(file_name) for file_name in file_name_list]
- while True:
- self._current_fdm_printer_count += 1
- stack_id_exists = False
- for position in range(8):
- stack_id = "custom_extruder_%s" % (position + 1)
- if self._current_fdm_printer_count > 1:
- stack_id += " #%s" % self._current_fdm_printer_count
- if stack_id in file_name_list:
- stack_id_exists = True
- break
- if not stack_id_exists:
- break
- return self._current_fdm_printer_count
- def _checkCustomFdmPrinterHasExtruderStack(self, machine_id: str) -> bool:
- # go through all extruders and make sure that this custom FDM printer has extruder stacks.
- extruder_stack_dir = os.path.join(Resources.getDataStoragePath(), "extruders")
- has_extruders = False
- for item in os.listdir(extruder_stack_dir):
- file_path = os.path.join(extruder_stack_dir, item)
- if not os.path.isfile(file_path):
- continue
- parser = configparser.ConfigParser()
- try:
- parser.read([file_path])
- except:
- # skip, it is not a valid stack file
- continue
- if "metadata" not in parser:
- continue
- if "machine" not in parser["metadata"]:
- continue
- if machine_id != parser["metadata"]["machine"]:
- continue
- has_extruders = True
- break
- return has_extruders
- def _createCustomFdmPrinterExtruderStack(self, machine_id: str, position: int, quality_id: str, material_id: str) -> None:
- stack_id = "custom_extruder_%s" % (position + 1)
- if self._current_fdm_printer_count > 1:
- stack_id += " #%s" % self._current_fdm_printer_count
- definition_id = "custom_extruder_%s" % (position + 1)
- # create a definition changes container for this stack
- definition_changes_parser = self._getCustomFdmPrinterDefinitionChanges(stack_id)
- definition_changes_id = definition_changes_parser["general"]["name"]
- # create a user settings container
- user_settings_parser = self._getCustomFdmPrinterUserSettings(stack_id)
- user_settings_id = user_settings_parser["general"]["name"]
- parser = configparser.ConfigParser()
- parser.add_section("general")
- parser["general"]["version"] = str(2)
- parser["general"]["name"] = "Extruder %s" % (position + 1)
- parser["general"]["id"] = stack_id
- parser.add_section("metadata")
- parser["metadata"]["type"] = "extruder_train"
- parser["metadata"]["machine"] = machine_id
- parser["metadata"]["position"] = str(position)
- parser.add_section("containers")
- parser["containers"]["0"] = user_settings_id
- parser["containers"]["1"] = "empty_quality_changes"
- parser["containers"]["2"] = quality_id
- parser["containers"]["3"] = material_id
- parser["containers"]["4"] = "empty_variant"
- parser["containers"]["5"] = definition_changes_id
- parser["containers"]["6"] = definition_id
- definition_changes_output = io.StringIO()
- definition_changes_parser.write(definition_changes_output)
- definition_changes_filename = quote_plus(definition_changes_id) + ".inst.cfg"
- user_settings_output = io.StringIO()
- user_settings_parser.write(user_settings_output)
- user_settings_filename = quote_plus(user_settings_id) + ".inst.cfg"
- extruder_output = io.StringIO()
- parser.write(extruder_output)
- extruder_filename = quote_plus(stack_id) + ".extruder.cfg"
- extruder_stack_dir = os.path.join(Resources.getDataStoragePath(), "extruders")
- definition_changes_dir = os.path.join(Resources.getDataStoragePath(), "definition_changes")
- user_settings_dir = os.path.join(Resources.getDataStoragePath(), "user")
- with open(os.path.join(definition_changes_dir, definition_changes_filename), "w", encoding = "utf-8") as f:
- f.write(definition_changes_output.getvalue())
- with open(os.path.join(user_settings_dir, user_settings_filename), "w", encoding = "utf-8") as f:
- f.write(user_settings_output.getvalue())
- with open(os.path.join(extruder_stack_dir, extruder_filename), "w", encoding = "utf-8") as f:
- f.write(extruder_output.getvalue())
- ## Creates a definition changes container which doesn't contain anything for the Custom FDM Printers.
- # The container ID will be automatically generated according to the given stack name.
- def _getCustomFdmPrinterDefinitionChanges(self, stack_id: str) -> configparser.ConfigParser:
- # In 2.5, there is no definition_changes container for the Custom FDM printer, so it should be safe to use the
- # default name unless some one names the printer as something like "Custom FDM Printer_settings".
- definition_changes_id = stack_id + "_settings"
- parser = configparser.ConfigParser()
- parser.add_section("general")
- parser["general"]["version"] = str(2)
- parser["general"]["name"] = definition_changes_id
- parser["general"]["definition"] = "custom"
- parser.add_section("metadata")
- parser["metadata"]["type"] = "definition_changes"
- parser["metadata"]["setting_version"] = str(1)
- parser.add_section("values")
- return parser
- ## Creates a user settings container which doesn't contain anything for the Custom FDM Printers.
- # The container ID will be automatically generated according to the given stack name.
- def _getCustomFdmPrinterUserSettings(self, stack_id: str) -> configparser.ConfigParser:
- # For the extruder stacks created in the upgrade, also create user_settings containers so the user changes
- # will be saved.
- user_settings_id = stack_id + "_user"
- parser = configparser.ConfigParser()
- parser.add_section("general")
- parser["general"]["version"] = str(2)
- parser["general"]["name"] = user_settings_id
- parser["general"]["definition"] = "custom"
- parser.add_section("metadata")
- parser["metadata"]["extruder"] = stack_id
- parser["metadata"]["type"] = "user"
- parser["metadata"]["setting_version"] = str(1)
- parser.add_section("values")
- return parser
|