# Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. import configparser #To read config files. import io #To write config files to strings as if they were files. import os.path #To get the path to write new user profiles to. from typing import Dict, List, Optional, Set, Tuple import urllib #To serialise the user container file name properly. import urllib.parse import UM.VersionUpgrade #To indicate that a file is of incorrect format. import UM.VersionUpgradeManager #To schedule more files to be upgraded. from UM.Resources import Resources #To get the config storage path. ## Creates a new machine instance instance by parsing a serialised machine # instance in version 1 of the file format. # # \param serialised The serialised form of a machine instance in version 1. # \param filename The supposed file name of this machine instance, without # extension. # \return A machine instance instance, or None if the file format is # incorrect. def importFrom(serialised: str, filename: str) -> Optional["MachineInstance"]: try: return MachineInstance(serialised, filename) except (configparser.Error, UM.VersionUpgrade.FormatException, UM.VersionUpgrade.InvalidVersionException): return None ## A representation of a machine instance used as intermediary form for # conversion from one format to the other. class MachineInstance: ## Reads version 1 of the file format, storing it in memory. # # \param serialised A string with the contents of a machine instance file, # without extension. # \param filename The supposed file name of this machine instance. def __init__(self, serialised: str, filename: str) -> None: self._filename = filename config = configparser.ConfigParser(interpolation = None) config.read_string(serialised) # Read the input string as config file. # Checking file correctness. if not config.has_section("general"): raise UM.VersionUpgrade.FormatException("No \"general\" section.") if not config.has_option("general", "version"): raise UM.VersionUpgrade.FormatException("No \"version\" in \"general\" section.") if not config.has_option("general", "name"): raise UM.VersionUpgrade.FormatException("No \"name\" in \"general\" section.") if not config.has_option("general", "type"): raise UM.VersionUpgrade.FormatException("No \"type\" in \"general\" section.") if int(config.get("general", "version")) != 1: # Explicitly hard-code version 1, since if this number changes the programmer MUST change this entire function. raise UM.VersionUpgrade.InvalidVersionException("The version of this machine instance is wrong. It must be 1.") self._type_name = config.get("general", "type") self._variant_name = config.get("general", "variant", fallback = "empty_variant") self._name = config.get("general", "name", fallback = "") self._key = config.get("general", "key", fallback = "") self._active_profile_name = config.get("general", "active_profile", fallback = "empty_quality") self._active_material_name = config.get("general", "material", fallback = "empty_material") self._machine_setting_overrides = {} # type: Dict[str, str] for key, value in config["machine_settings"].items(): self._machine_setting_overrides[key] = value ## Serialises this machine instance as file format version 2. # # This is where the actual translation happens in this case. # # \return A tuple containing the new filename and a serialised form of # this machine instance, serialised in version 2 of the file format. def export(self) -> Tuple[List[str], List[str]]: config = configparser.ConfigParser(interpolation = None) # Build a config file in the form of version 2. config.add_section("general") config.set("general", "name", self._name) config.set("general", "id", self._name) config.set("general", "version", "2") # Hard-code version 2, since if this number changes the programmer MUST change this entire function. import VersionUpgrade21to22 # Import here to prevent circular dependencies. has_machine_qualities = self._type_name in VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.machinesWithMachineQuality() type_name = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translatePrinter(self._type_name) active_material = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateMaterial(self._active_material_name) variant = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateVariant(self._variant_name, type_name) variant_materials = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateVariantForMaterials(self._variant_name, type_name) #Convert to quality profile if we have one of the built-in profiles, otherwise convert to a quality-changes profile. if self._active_profile_name in VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.builtInProfiles(): active_quality = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateProfile(self._active_profile_name) active_quality_changes = "empty_quality_changes" else: active_quality = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.getQualityFallback(type_name, variant, active_material) active_quality_changes = self._active_profile_name if has_machine_qualities: #This machine now has machine-quality profiles. active_material += "_" + variant_materials #Create a new user profile and schedule it to be upgraded. user_profile = configparser.ConfigParser(interpolation = None) user_profile["general"] = { "version": "2", "name": "Current settings", "definition": type_name } user_profile["metadata"] = { "type": "user", "machine": self._name } user_profile["values"] = {} version_upgrade_manager = UM.VersionUpgradeManager.VersionUpgradeManager.getInstance() user_version_to_paths_dict = version_upgrade_manager.getStoragePaths("user") paths_set = set() # type: Set[str] for paths in user_version_to_paths_dict.values(): paths_set |= paths user_storage = os.path.join(Resources.getDataStoragePath(), next(iter(paths_set))) user_profile_file = os.path.join(user_storage, urllib.parse.quote_plus(self._name) + "_current_settings.inst.cfg") if not os.path.exists(user_storage): os.makedirs(user_storage) with open(user_profile_file, "w", encoding = "utf-8") as file_handle: user_profile.write(file_handle) version_upgrade_manager.upgradeExtraFile(user_storage, urllib.parse.quote_plus(self._name), "user") containers = [ self._name + "_current_settings", #The current profile doesn't know the definition ID when it was upgraded, only the instance ID, so it will be invalid. Sorry, your current settings are lost now. active_quality_changes, active_quality, active_material, variant, type_name ] config.set("general", "containers", ",".join(containers)) config.add_section("metadata") config.set("metadata", "type", "machine") VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateSettings(self._machine_setting_overrides) config.add_section("values") for key, value in self._machine_setting_overrides.items(): config.set("values", key, str(value)) output = io.StringIO() config.write(output) return [self._filename], [output.getvalue()]