VersionUpgrade25to26.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. # Copyright (c) 2018 Ultimaker B.V.
  2. # Cura is released under the terms of the LGPLv3 or higher.
  3. import configparser #To parse the files we need to upgrade and write the new files.
  4. import io #To serialise configparser output to a string.
  5. import os
  6. from typing import Dict, List, Set, Tuple
  7. from urllib.parse import quote_plus
  8. from UM.Resources import Resources
  9. from UM.VersionUpgrade import VersionUpgrade
  10. _removed_settings = { #Settings that were removed in 2.5.
  11. "start_layers_at_same_position",
  12. "sub_div_rad_mult"
  13. } # type: Set[str]
  14. _split_settings = { #These settings should be copied to all settings it was split into.
  15. "support_interface_line_distance": {"support_roof_line_distance", "support_bottom_line_distance"}
  16. } # type: Dict[str, Set[str]]
  17. ## A collection of functions that convert the configuration of the user in Cura
  18. # 2.5 to a configuration for Cura 2.6.
  19. #
  20. # All of these methods are essentially stateless.
  21. class VersionUpgrade25to26(VersionUpgrade):
  22. def __init__(self) -> None:
  23. super().__init__()
  24. self._current_fdm_printer_count = 2
  25. ## Upgrades the preferences file from version 2.5 to 2.6.
  26. #
  27. # \param serialised The serialised form of a preferences file.
  28. # \param filename The name of the file to upgrade.
  29. def upgradePreferences(self, serialised: str, filename: str) -> Tuple[List[str], List[str]]:
  30. parser = configparser.ConfigParser(interpolation = None)
  31. parser.read_string(serialised)
  32. #Remove settings from the visible_settings.
  33. if parser.has_section("general") and "visible_settings" in parser["general"]:
  34. visible_settings = parser["general"]["visible_settings"].split(";")
  35. new_visible_settings = []
  36. for setting in visible_settings:
  37. if setting in _removed_settings:
  38. continue #Skip.
  39. if setting in _split_settings:
  40. for replaced_setting in _split_settings[setting]:
  41. new_visible_settings.append(replaced_setting)
  42. continue #Don't add the original.
  43. new_visible_settings.append(setting) #No special handling, so just add the original visible setting back.
  44. parser["general"]["visible_settings"] = ";".join(new_visible_settings)
  45. #Change the version number in the file.
  46. if "general" not in parser:
  47. parser["general"] = {}
  48. parser.set("general", "version", "4")
  49. if "metadata" not in parser:
  50. parser["metadata"] = {}
  51. parser.set("metadata", "setting_version", "1")
  52. #Re-serialise the file.
  53. output = io.StringIO()
  54. parser.write(output)
  55. return [filename], [output.getvalue()]
  56. ## Upgrades an instance container from version 2.5 to 2.6.
  57. #
  58. # \param serialised The serialised form of a quality profile.
  59. # \param filename The name of the file to upgrade.
  60. def upgradeInstanceContainer(self, serialised: str, filename: str) -> Tuple[List[str], List[str]]:
  61. parser = configparser.ConfigParser(interpolation = None)
  62. parser.read_string(serialised)
  63. #Remove settings from the [values] section.
  64. if parser.has_section("values"):
  65. for removed_setting in (_removed_settings & parser["values"].keys()): #Both in keys that need to be removed and in keys present in the file.
  66. del parser["values"][removed_setting]
  67. for replaced_setting in (_split_settings.keys() & parser["values"].keys()):
  68. for replacement in _split_settings[replaced_setting]:
  69. parser["values"][replacement] = parser["values"][replaced_setting] #Copy to replacement before removing the original!
  70. del replaced_setting
  71. for each_section in ("general", "metadata"):
  72. if not parser.has_section(each_section):
  73. parser.add_section(each_section)
  74. # Update version numbers
  75. parser["general"]["version"] = "2"
  76. parser["metadata"]["setting_version"] = "1"
  77. #Re-serialise the file.
  78. output = io.StringIO()
  79. parser.write(output)
  80. return [filename], [output.getvalue()]
  81. ## Upgrades a machine stack from version 2.5 to 2.6
  82. #
  83. # \param serialised The serialised form of a quality profile.
  84. # \param filename The name of the file to upgrade.
  85. def upgradeMachineStack(self, serialised: str, filename: str) -> Tuple[List[str], List[str]]:
  86. parser = configparser.ConfigParser(interpolation = None)
  87. parser.read_string(serialised)
  88. # NOTE: This is for Custom FDM printers
  89. # In 2.5, Custom FDM printers don't support multiple extruders, but since 2.6 they do.
  90. machine_id = parser["general"]["id"]
  91. quality_container_id = parser["containers"]["2"]
  92. material_container_id = parser["containers"]["3"]
  93. # we don't have definition_changes container in 2.5
  94. if "6" in parser["containers"]:
  95. definition_container_id = parser["containers"]["6"]
  96. else:
  97. definition_container_id = parser["containers"]["5"]
  98. if definition_container_id == "custom" and not self._checkCustomFdmPrinterHasExtruderStack(machine_id):
  99. # go through all extruders and make sure that this custom FDM printer has 8 extruder stacks.
  100. self._acquireNextUniqueCustomFdmPrinterExtruderStackIdIndex()
  101. for position in range(8):
  102. self._createCustomFdmPrinterExtruderStack(machine_id, position, quality_container_id, material_container_id)
  103. # Update version numbers
  104. parser["general"]["version"] = "3"
  105. parser["metadata"]["setting_version"] = "1"
  106. # Re-serialise the file.
  107. output = io.StringIO()
  108. parser.write(output)
  109. return [filename], [output.getvalue()]
  110. ## Acquires the next unique extruder stack index number for the Custom FDM Printer.
  111. def _acquireNextUniqueCustomFdmPrinterExtruderStackIdIndex(self) -> int:
  112. extruder_stack_dir = os.path.join(Resources.getDataStoragePath(), "extruders")
  113. file_name_list = os.listdir(extruder_stack_dir)
  114. file_name_list = [os.path.basename(file_name) for file_name in file_name_list]
  115. while True:
  116. self._current_fdm_printer_count += 1
  117. stack_id_exists = False
  118. for position in range(8):
  119. stack_id = "custom_extruder_%s" % (position + 1)
  120. if self._current_fdm_printer_count > 1:
  121. stack_id += " #%s" % self._current_fdm_printer_count
  122. if stack_id in file_name_list:
  123. stack_id_exists = True
  124. break
  125. if not stack_id_exists:
  126. break
  127. return self._current_fdm_printer_count
  128. def _checkCustomFdmPrinterHasExtruderStack(self, machine_id: str) -> bool:
  129. # go through all extruders and make sure that this custom FDM printer has extruder stacks.
  130. extruder_stack_dir = os.path.join(Resources.getDataStoragePath(), "extruders")
  131. has_extruders = False
  132. for item in os.listdir(extruder_stack_dir):
  133. file_path = os.path.join(extruder_stack_dir, item)
  134. if not os.path.isfile(file_path):
  135. continue
  136. parser = configparser.ConfigParser()
  137. try:
  138. parser.read([file_path])
  139. except:
  140. # skip, it is not a valid stack file
  141. continue
  142. if "metadata" not in parser:
  143. continue
  144. if "machine" not in parser["metadata"]:
  145. continue
  146. if machine_id != parser["metadata"]["machine"]:
  147. continue
  148. has_extruders = True
  149. break
  150. return has_extruders
  151. def _createCustomFdmPrinterExtruderStack(self, machine_id: str, position: int, quality_id: str, material_id: str) -> None:
  152. stack_id = "custom_extruder_%s" % (position + 1)
  153. if self._current_fdm_printer_count > 1:
  154. stack_id += " #%s" % self._current_fdm_printer_count
  155. definition_id = "custom_extruder_%s" % (position + 1)
  156. # create a definition changes container for this stack
  157. definition_changes_parser = self._getCustomFdmPrinterDefinitionChanges(stack_id)
  158. definition_changes_id = definition_changes_parser["general"]["name"]
  159. # create a user settings container
  160. user_settings_parser = self._getCustomFdmPrinterUserSettings(stack_id)
  161. user_settings_id = user_settings_parser["general"]["name"]
  162. parser = configparser.ConfigParser()
  163. parser.add_section("general")
  164. parser["general"]["version"] = str(2)
  165. parser["general"]["name"] = "Extruder %s" % (position + 1)
  166. parser["general"]["id"] = stack_id
  167. parser.add_section("metadata")
  168. parser["metadata"]["type"] = "extruder_train"
  169. parser["metadata"]["machine"] = machine_id
  170. parser["metadata"]["position"] = str(position)
  171. parser.add_section("containers")
  172. parser["containers"]["0"] = user_settings_id
  173. parser["containers"]["1"] = "empty_quality_changes"
  174. parser["containers"]["2"] = quality_id
  175. parser["containers"]["3"] = material_id
  176. parser["containers"]["4"] = "empty_variant"
  177. parser["containers"]["5"] = definition_changes_id
  178. parser["containers"]["6"] = definition_id
  179. definition_changes_output = io.StringIO()
  180. definition_changes_parser.write(definition_changes_output)
  181. definition_changes_filename = quote_plus(definition_changes_id) + ".inst.cfg"
  182. user_settings_output = io.StringIO()
  183. user_settings_parser.write(user_settings_output)
  184. user_settings_filename = quote_plus(user_settings_id) + ".inst.cfg"
  185. extruder_output = io.StringIO()
  186. parser.write(extruder_output)
  187. extruder_filename = quote_plus(stack_id) + ".extruder.cfg"
  188. extruder_stack_dir = os.path.join(Resources.getDataStoragePath(), "extruders")
  189. definition_changes_dir = os.path.join(Resources.getDataStoragePath(), "definition_changes")
  190. user_settings_dir = os.path.join(Resources.getDataStoragePath(), "user")
  191. with open(os.path.join(definition_changes_dir, definition_changes_filename), "w", encoding = "utf-8") as f:
  192. f.write(definition_changes_output.getvalue())
  193. with open(os.path.join(user_settings_dir, user_settings_filename), "w", encoding = "utf-8") as f:
  194. f.write(user_settings_output.getvalue())
  195. with open(os.path.join(extruder_stack_dir, extruder_filename), "w", encoding = "utf-8") as f:
  196. f.write(extruder_output.getvalue())
  197. ## Creates a definition changes container which doesn't contain anything for the Custom FDM Printers.
  198. # The container ID will be automatically generated according to the given stack name.
  199. def _getCustomFdmPrinterDefinitionChanges(self, stack_id: str) -> configparser.ConfigParser:
  200. # In 2.5, there is no definition_changes container for the Custom FDM printer, so it should be safe to use the
  201. # default name unless some one names the printer as something like "Custom FDM Printer_settings".
  202. definition_changes_id = stack_id + "_settings"
  203. parser = configparser.ConfigParser()
  204. parser.add_section("general")
  205. parser["general"]["version"] = str(2)
  206. parser["general"]["name"] = definition_changes_id
  207. parser["general"]["definition"] = "custom"
  208. parser.add_section("metadata")
  209. parser["metadata"]["type"] = "definition_changes"
  210. parser["metadata"]["setting_version"] = str(1)
  211. parser.add_section("values")
  212. return parser
  213. ## Creates a user settings container which doesn't contain anything for the Custom FDM Printers.
  214. # The container ID will be automatically generated according to the given stack name.
  215. def _getCustomFdmPrinterUserSettings(self, stack_id: str) -> configparser.ConfigParser:
  216. # For the extruder stacks created in the upgrade, also create user_settings containers so the user changes
  217. # will be saved.
  218. user_settings_id = stack_id + "_user"
  219. parser = configparser.ConfigParser()
  220. parser.add_section("general")
  221. parser["general"]["version"] = str(2)
  222. parser["general"]["name"] = user_settings_id
  223. parser["general"]["definition"] = "custom"
  224. parser.add_section("metadata")
  225. parser["metadata"]["extruder"] = stack_id
  226. parser["metadata"]["type"] = "user"
  227. parser["metadata"]["setting_version"] = str(1)
  228. parser.add_section("values")
  229. return parser