MachineInstance.py 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. # Copyright (c) 2018 Ultimaker B.V.
  2. # Cura is released under the terms of the LGPLv3 or higher.
  3. import configparser #To read config files.
  4. import io #To write config files to strings as if they were files.
  5. import os.path #To get the path to write new user profiles to.
  6. from typing import Dict, List, Optional, Set, Tuple
  7. import urllib #To serialise the user container file name properly.
  8. import urllib.parse
  9. import UM.VersionUpgrade #To indicate that a file is of incorrect format.
  10. import UM.VersionUpgradeManager #To schedule more files to be upgraded.
  11. from UM.Resources import Resources #To get the config storage path.
  12. ## Creates a new machine instance instance by parsing a serialised machine
  13. # instance in version 1 of the file format.
  14. #
  15. # \param serialised The serialised form of a machine instance in version 1.
  16. # \param filename The supposed file name of this machine instance, without
  17. # extension.
  18. # \return A machine instance instance, or None if the file format is
  19. # incorrect.
  20. def importFrom(serialised: str, filename: str) -> Optional["MachineInstance"]:
  21. try:
  22. return MachineInstance(serialised, filename)
  23. except (configparser.Error, UM.VersionUpgrade.FormatException, UM.VersionUpgrade.InvalidVersionException):
  24. return None
  25. ## A representation of a machine instance used as intermediary form for
  26. # conversion from one format to the other.
  27. class MachineInstance:
  28. ## Reads version 1 of the file format, storing it in memory.
  29. #
  30. # \param serialised A string with the contents of a machine instance file,
  31. # without extension.
  32. # \param filename The supposed file name of this machine instance.
  33. def __init__(self, serialised: str, filename: str) -> None:
  34. self._filename = filename
  35. config = configparser.ConfigParser(interpolation = None)
  36. config.read_string(serialised) # Read the input string as config file.
  37. # Checking file correctness.
  38. if not config.has_section("general"):
  39. raise UM.VersionUpgrade.FormatException("No \"general\" section.")
  40. if not config.has_option("general", "version"):
  41. raise UM.VersionUpgrade.FormatException("No \"version\" in \"general\" section.")
  42. if not config.has_option("general", "name"):
  43. raise UM.VersionUpgrade.FormatException("No \"name\" in \"general\" section.")
  44. if not config.has_option("general", "type"):
  45. raise UM.VersionUpgrade.FormatException("No \"type\" in \"general\" section.")
  46. if int(config.get("general", "version")) != 1: # Explicitly hard-code version 1, since if this number changes the programmer MUST change this entire function.
  47. raise UM.VersionUpgrade.InvalidVersionException("The version of this machine instance is wrong. It must be 1.")
  48. self._type_name = config.get("general", "type")
  49. self._variant_name = config.get("general", "variant", fallback = "empty_variant")
  50. self._name = config.get("general", "name", fallback = "")
  51. self._key = config.get("general", "key", fallback = "")
  52. self._active_profile_name = config.get("general", "active_profile", fallback = "empty_quality")
  53. self._active_material_name = config.get("general", "material", fallback = "empty_material")
  54. self._machine_setting_overrides = {} # type: Dict[str, str]
  55. for key, value in config["machine_settings"].items():
  56. self._machine_setting_overrides[key] = value
  57. ## Serialises this machine instance as file format version 2.
  58. #
  59. # This is where the actual translation happens in this case.
  60. #
  61. # \return A tuple containing the new filename and a serialised form of
  62. # this machine instance, serialised in version 2 of the file format.
  63. def export(self) -> Tuple[List[str], List[str]]:
  64. config = configparser.ConfigParser(interpolation = None) # Build a config file in the form of version 2.
  65. config.add_section("general")
  66. config.set("general", "name", self._name)
  67. config.set("general", "id", self._name)
  68. config.set("general", "version", "2") # Hard-code version 2, since if this number changes the programmer MUST change this entire function.
  69. import VersionUpgrade21to22 # Import here to prevent circular dependencies.
  70. has_machine_qualities = self._type_name in VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.machinesWithMachineQuality()
  71. type_name = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translatePrinter(self._type_name)
  72. active_material = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateMaterial(self._active_material_name)
  73. variant = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateVariant(self._variant_name, type_name)
  74. variant_materials = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateVariantForMaterials(self._variant_name, type_name)
  75. #Convert to quality profile if we have one of the built-in profiles, otherwise convert to a quality-changes profile.
  76. if self._active_profile_name in VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.builtInProfiles():
  77. active_quality = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateProfile(self._active_profile_name)
  78. active_quality_changes = "empty_quality_changes"
  79. else:
  80. active_quality = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.getQualityFallback(type_name, variant, active_material)
  81. active_quality_changes = self._active_profile_name
  82. if has_machine_qualities: #This machine now has machine-quality profiles.
  83. active_material += "_" + variant_materials
  84. #Create a new user profile and schedule it to be upgraded.
  85. user_profile = configparser.ConfigParser(interpolation = None)
  86. user_profile["general"] = {
  87. "version": "2",
  88. "name": "Current settings",
  89. "definition": type_name
  90. }
  91. user_profile["metadata"] = {
  92. "type": "user",
  93. "machine": self._name
  94. }
  95. user_profile["values"] = {}
  96. version_upgrade_manager = UM.VersionUpgradeManager.VersionUpgradeManager.getInstance()
  97. user_version_to_paths_dict = version_upgrade_manager.getStoragePaths("user")
  98. paths_set = set() # type: Set[str]
  99. for paths in user_version_to_paths_dict.values():
  100. paths_set |= paths
  101. user_storage = os.path.join(Resources.getDataStoragePath(), next(iter(paths_set)))
  102. user_profile_file = os.path.join(user_storage, urllib.parse.quote_plus(self._name) + "_current_settings.inst.cfg")
  103. if not os.path.exists(user_storage):
  104. os.makedirs(user_storage)
  105. with open(user_profile_file, "w", encoding = "utf-8") as file_handle:
  106. user_profile.write(file_handle)
  107. version_upgrade_manager.upgradeExtraFile(user_storage, urllib.parse.quote_plus(self._name), "user")
  108. containers = [
  109. 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.
  110. active_quality_changes,
  111. active_quality,
  112. active_material,
  113. variant,
  114. type_name
  115. ]
  116. config.set("general", "containers", ",".join(containers))
  117. config.add_section("metadata")
  118. config.set("metadata", "type", "machine")
  119. VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateSettings(self._machine_setting_overrides)
  120. config.add_section("values")
  121. for key, value in self._machine_setting_overrides.items():
  122. config.set("values", key, str(value))
  123. output = io.StringIO()
  124. config.write(output)
  125. return [self._filename], [output.getvalue()]