From 13fd588451a29de4dccfc5da33d122663ea3712b Mon Sep 17 00:00:00 2001 From: Tim Kuipers Date: Tue, 14 Jun 2016 15:47:29 +0200 Subject: [PATCH 1/3] =?UTF-8?q?JSON=20fix:=20unit=20of=20jerk:=20mm/s?= =?UTF-8?q?=C2=B3=20=3D=3D>=20mm/s=20(CURA-1646)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- resources/definitions/fdmprinter.def.json | 34 +++++++++++------------ 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index 6e6474053f..5ede74aac7 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -1512,7 +1512,7 @@ "jerk_print": { "label": "Print Jerk", "description": "The maximal allowed jerk of the print head when starting to move or when changing direction.", - "unit": "mm/s³", + "unit": "mm/s", "type": "float", "minimum_value": "0.1", "minimum_value_warning": "5", @@ -1524,7 +1524,7 @@ "jerk_infill": { "label": "Infill Jerk", "description": "The jerk with which infill is printed.", - "unit": "mm/s³", + "unit": "mm/s", "type": "float", "minimum_value": "0.1", "minimum_value_warning": "5", @@ -1537,7 +1537,7 @@ "jerk_wall": { "label": "Wall Jerk", "description": "The jerk with which the walls are printed.", - "unit": "mm/s³", + "unit": "mm/s", "type": "float", "minimum_value": "0.1", "minimum_value_warning": "5", @@ -1550,7 +1550,7 @@ "jerk_wall_0": { "label": "Outer Wall Jerk", "description": "The jerk with which the outermost walls are printed.", - "unit": "mm/s³", + "unit": "mm/s", "type": "float", "minimum_value": "0.1", "minimum_value_warning": "5", @@ -1563,7 +1563,7 @@ "jerk_wall_x": { "label": "Inner Wall Jerk", "description": "The jerk with which all inner walls are printed.", - "unit": "mm/s³", + "unit": "mm/s", "type": "float", "minimum_value": "0.1", "minimum_value_warning": "5", @@ -1578,7 +1578,7 @@ "jerk_topbottom": { "label": "Top/Bottom Jerk", "description": "The jerk with which top/bottom layers are printed.", - "unit": "mm/s³", + "unit": "mm/s", "type": "float", "minimum_value": "0.1", "minimum_value_warning": "5", @@ -1591,7 +1591,7 @@ "jerk_support": { "label": "Support Jerk", "description": "The jerk with which the support structure is printed.", - "unit": "mm/s³", + "unit": "mm/s", "type": "float", "minimum_value": "0.1", "minimum_value_warning": "5", @@ -1605,7 +1605,7 @@ "jerk_support_infill": { "label": "Support Infill Jerk", "description": "The jerk with which the infill of support is printed.", - "unit": "mm/s³", + "unit": "mm/s", "type": "float", "default_value": 20, "value": "jerk_support", @@ -1619,7 +1619,7 @@ "jerk_support_roof": { "label": "Support Roof Jerk", "description": "The jerk with which the roofs of support are printed.", - "unit": "mm/s³", + "unit": "mm/s", "type": "float", "default_value": 20, "value": "jerk_support", @@ -1635,7 +1635,7 @@ "jerk_prime_tower": { "label": "Prime Tower Jerk", "description": "The jerk with which the prime tower is printed.", - "unit": "mm/s³", + "unit": "mm/s", "type": "float", "minimum_value": "0.1", "minimum_value_warning": "5", @@ -1650,7 +1650,7 @@ "jerk_travel": { "label": "Travel Jerk", "description": "The jerk with which travel moves are made.", - "unit": "mm/s³", + "unit": "mm/s", "type": "float", "default_value": 30, "minimum_value": "0.1", @@ -1663,7 +1663,7 @@ "jerk_layer_0": { "label": "Initial Layer Jerk", "description": "The print jerk for the initial layer.", - "unit": "mm/s³", + "unit": "mm/s", "type": "float", "default_value": 20, "value": "jerk_print", @@ -1676,7 +1676,7 @@ "jerk_skirt": { "label": "Skirt Jerk", "description": "The jerk with which the skirt and brim are printed.", - "unit": "mm/s³", + "unit": "mm/s", "type": "float", "default_value": 20, "minimum_value": "0.1", @@ -2599,7 +2599,7 @@ "raft_jerk": { "label": "Raft Print Jerk", "description": "The jerk with which the raft is printed.", - "unit": "mm/s³", + "unit": "mm/s", "type": "float", "default_value": 20, "minimum_value": "0.1", @@ -2612,7 +2612,7 @@ "raft_surface_jerk": { "label": "Raft Top Print Jerk", "description": "The jerk with which the top raft layers are printed.", - "unit": "mm/s³", + "unit": "mm/s", "type": "float", "default_value": 20, "value": "raft_jerk", @@ -2625,7 +2625,7 @@ "raft_interface_jerk": { "label": "Raft Middle Print Jerk", "description": "The jerk with which the middle raft layer is printed.", - "unit": "mm/s³", + "unit": "mm/s", "type": "float", "default_value": 20, "value": "raft_jerk", @@ -2638,7 +2638,7 @@ "raft_base_jerk": { "label": "Raft Base Print Jerk", "description": "The jerk with which the base raft layer is printed.", - "unit": "mm/s³", + "unit": "mm/s", "type": "float", "default_value": 20, "value": "raft_jerk", From 12915870017588aa3d3f56956e1c639eba18fe2f Mon Sep 17 00:00:00 2001 From: Tim Kuipers Date: Tue, 14 Jun 2016 17:06:46 +0200 Subject: [PATCH 2/3] JSON feat: firmware acceleration and jerk settings for ultimaker printers (CURA-1646) --- resources/definitions/fdmprinter.def.json | 131 ++++++++++++++++++ resources/definitions/ultimaker2.def.json | 15 ++ .../ultimaker_original_plus.def.json | 3 + 3 files changed, 149 insertions(+) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index 5ede74aac7..f7acaf36b7 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -303,6 +303,137 @@ "settable_per_mesh": false, "settable_per_extruder": false, "settable_per_meshgroup": false + }, + + "machine_max_feedrate_x": { + "label": "Maximum Speed X", + "description": "The maximum speed for the motor of the X-direction.", + "unit": "mm/s", + "type": "float", + "default_value": 500, + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false + }, + "machine_max_feedrate_y": { + "label": "Maximum Speed Y", + "description": "The maximum speed for the motor of the Y-direction.", + "unit": "mm/s", + "type": "float", + "default_value": 500, + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false + }, + "machine_max_feedrate_z": { + "label": "Maximum Speed Z", + "description": "The maximum speed for the motor of the Z-direction.", + "unit": "mm/s", + "type": "float", + "default_value": 5, + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false + }, + "machine_max_feedrate_e": { + "label": "Maximum Feedrate", + "description": "The maximum speed of the filament.", + "unit": "mm/s", + "type": "float", + "default_value": 25, + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false + }, + "machine_max_acceleration_x": { + "label": "Maximum Acceleration X", + "description": "Maximum acceleration for the motor of the X-direction", + "unit": "mm/s²", + "type": "float", + "default_value": 9000, + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false + }, + "machine_max_acceleration_y": { + "label": "Maximum Acceleration Y", + "description": "Maximum acceleration for the motor of the Y-direction.", + "unit": "mm/s²", + "type": "float", + "default_value": 9000, + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false + }, + "machine_max_acceleration_z": { + "label": "Maximum Acceleration Z", + "description": "Maximum acceleration for the motor of the Z-direction.", + "unit": "mm/s²", + "type": "float", + "default_value": 100, + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false + }, + "machine_max_acceleration_e": { + "label": "Maximum Filament Acceleration", + "description": "Maximum acceleration for the motor of the filament.", + "unit": "mm/s²", + "type": "float", + "default_value": 10000, + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false + }, + "machine_acceleration": { + "label": "Default Acceleration", + "description": "The default acceleration of print head movement.", + "unit": "mm/s²", + "type": "float", + "default_value": 4000, + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false + }, + "machine_max_jerk_xy": { + "label": "Default X-Y Jerk", + "description": "Default jerk for movement in the horizontal plane.", + "unit": "mm/s", + "type": "float", + "default_value": 20.0, + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false + }, + "machine_max_jerk_z": { + "label": "Default Z Jerk", + "description": "Default jerk for the motor of the Z-direction.", + "unit": "mm/s", + "type": "float", + "default_value": 0.4, + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false + }, + "machine_max_jerk_e": { + "label": "Default Filament Jerk", + "description": "Default jerk for the motor of the filament.", + "unit": "mm/s", + "type": "float", + "default_value": 5.0, + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false + }, + "machine_minimum_feedrate": { + "label": "Minimum Feedrate", + "description": "The minimal movement speed of the print head.", + "unit": "mm/s", + "type": "float", + "default_value": 0.0, + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false } } }, diff --git a/resources/definitions/ultimaker2.def.json b/resources/definitions/ultimaker2.def.json index 7b2222e5b3..a1ffcc8226 100644 --- a/resources/definitions/ultimaker2.def.json +++ b/resources/definitions/ultimaker2.def.json @@ -86,6 +86,21 @@ "material_bed_temperature": { "enabled": "False" }, + "machine_max_feedrate_x": { + "default_value": 300 + }, + "machine_max_feedrate_y": { + "default_value": 300 + }, + "machine_max_feedrate_z": { + "default_value": 40 + }, + "machine_max_feedrate_e": { + "default_value": 45 + }, + "machine_acceleration": { + "default_value": 3000 + }, "material_diameter": { "enabled": "False" }, diff --git a/resources/definitions/ultimaker_original_plus.def.json b/resources/definitions/ultimaker_original_plus.def.json index 830050beb0..e4bb24ec6f 100644 --- a/resources/definitions/ultimaker_original_plus.def.json +++ b/resources/definitions/ultimaker_original_plus.def.json @@ -21,6 +21,9 @@ "overrides": { "machine_heated_bed": { "default_value": true + }, + "machine_max_feedrate_z": { + "default_value": 30 } } } From 64ecb114b86f663ea096054e108db43b1867bfd6 Mon Sep 17 00:00:00 2001 From: Simon Edwards Date: Tue, 12 Jul 2016 12:10:07 +0200 Subject: [PATCH 3/3] Store the Quality profile for the 'global' and extruders in the gcode. Read in all of the quality profile during import. Contributes to CURA-1727 GCode Profile reading/writing: Broken and needs update --- cura/ProfileReader.py | 2 +- cura/Settings/CuraContainerRegistry.py | 45 +++++++++++------ .../CuraProfileReader/CuraProfileReader.py | 1 - .../GCodeProfileReader/GCodeProfileReader.py | 49 ++++++++++++------- plugins/GCodeWriter/GCodeWriter.py | 33 ++++++++----- 5 files changed, 84 insertions(+), 46 deletions(-) diff --git a/cura/ProfileReader.py b/cura/ProfileReader.py index 36bb2c7177..266abd50a4 100644 --- a/cura/ProfileReader.py +++ b/cura/ProfileReader.py @@ -12,6 +12,6 @@ class ProfileReader(PluginObject): ## Read profile data from a file and return a filled profile. # - # \return \type{Profile} The profile that was obtained from the file. + # \return \type{Profile|Profile[]} The profile that was obtained from the file or a list of Profiles. def read(self, file_name): raise NotImplementedError("Profile reader plug-in was not correctly implemented. The read function was not implemented.") \ No newline at end of file diff --git a/cura/Settings/CuraContainerRegistry.py b/cura/Settings/CuraContainerRegistry.py index af7ca2e87e..990143f7bb 100644 --- a/cura/Settings/CuraContainerRegistry.py +++ b/cura/Settings/CuraContainerRegistry.py @@ -133,32 +133,45 @@ class CuraContainerRegistry(ContainerRegistry): for plugin_id, meta_data in self._getIOPlugins("profile_reader"): profile_reader = plugin_registry.getPluginObject(plugin_id) try: - profile = profile_reader.read(file_name) #Try to open the file with the profile reader. + 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", file_name, str(e)) return { "status": "error", "message": catalog.i18nc("@info:status", "Failed to import profile from {0}: {1}", file_name, str(e))} - if profile: #Success! - profile.setReadOnly(False) - - new_name = self.createUniqueName("quality", "", os.path.splitext(os.path.basename(file_name))[0], - catalog.i18nc("@label", "Custom profile")) - profile.setName(new_name) - profile._id = new_name - - if self._machineHasOwnQualities(): - profile.setDefinition(self._activeDefinition()) - if self._machineHasOwnMaterials(): - profile.addMetaDataEntry("material", self._activeMaterialId()) + if profile_or_list: # Success! + name_seed = os.path.splitext(os.path.basename(file_name))[0] + if type(profile_or_list) is not list: + profile = profile_or_list + self._configureProfile(profile, name_seed) + return { "status": "ok", "message": catalog.i18nc("@info:status", "Successfully imported profile {0}", profile.getName()) } else: - profile.setDefinition(ContainerRegistry.getInstance().findDefinitionContainers(id="fdmprinter")[0]) - ContainerRegistry.getInstance().addContainer(profile) + for profile in profile_or_list: + self._configureProfile(profile, name_seed) - return { "status": "ok", "message": catalog.i18nc("@info:status", "Successfully imported profile {0}", profile.getName()) } + if len(profile_or_list) == 1: + return {"status": "ok", "message": catalog.i18nc("@info:status", "Successfully imported profile {0}", profile_or_list[0].getName())} + else: + profile_names = ", ".join([profile.getName() for profile in profile_or_list]) + return { "status": "ok", "message": catalog.i18nc("@info:status", "Successfully imported profiles {0}", profile_names) } #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.", file_name)} + def _configureProfile(self, profile, name_seed): + profile.setReadOnly(False) + + new_name = self.createUniqueName("quality", "", name_seed, catalog.i18nc("@label", "Custom profile")) + profile.setName(new_name) + profile._id = new_name + + if self._machineHasOwnQualities(): + profile.setDefinition(self._activeDefinition()) + if self._machineHasOwnMaterials(): + profile.addMetaDataEntry("material", self._activeMaterialId()) + else: + profile.setDefinition(ContainerRegistry.getInstance().findDefinitionContainers(id="fdmprinter")[0]) + ContainerRegistry.getInstance().addContainer(profile) + ## Gets a list of profile writer plugins # \return List of tuples of (plugin_id, meta_data). def _getIOPlugins(self, io_type): diff --git a/plugins/CuraProfileReader/CuraProfileReader.py b/plugins/CuraProfileReader/CuraProfileReader.py index b9c1f208ea..261e68a26b 100644 --- a/plugins/CuraProfileReader/CuraProfileReader.py +++ b/plugins/CuraProfileReader/CuraProfileReader.py @@ -3,7 +3,6 @@ import os.path -from UM.Application import Application #To get the machine manager to create the new profile in. from UM.Logger import Logger from UM.Settings.InstanceContainer import InstanceContainer #The new profile to make. from cura.ProfileReader import ProfileReader diff --git a/plugins/GCodeProfileReader/GCodeProfileReader.py b/plugins/GCodeProfileReader/GCodeProfileReader.py index 1e649b7dd4..24c92d08e9 100644 --- a/plugins/GCodeProfileReader/GCodeProfileReader.py +++ b/plugins/GCodeProfileReader/GCodeProfileReader.py @@ -1,10 +1,9 @@ # Copyright (c) 2015 Ultimaker B.V. # Cura is released under the terms of the AGPLv3 or higher. -import os import re #Regular expressions for parsing escape characters in the settings. +import json -from UM.Application import Application #To get the machine manager to create the new profile in. from UM.Settings.InstanceContainer import InstanceContainer from UM.Logger import Logger from UM.i18n import i18nCatalog @@ -22,7 +21,7 @@ class GCodeProfileReader(ProfileReader): # It can only read settings with the same version as the version it was # written with. If the file format is changed in a way that breaks reverse # compatibility, increment this version number! - version = 2 + version = 3 ## Dictionary that defines how characters are escaped when embedded in # g-code. @@ -66,21 +65,37 @@ class GCodeProfileReader(ProfileReader): Logger.log("e", "Unable to open file %s for reading: %s", file_name, str(e)) return None - # Un-escape the serialized profile. - pattern = re.compile("|".join(GCodeProfileReader.escape_characters.keys())) - - # Perform the replacement with a regular expression. - serialized = pattern.sub(lambda m: GCodeProfileReader.escape_characters[re.escape(m.group(0))], serialized) + serialized = unescapeGcodeComment(serialized) Logger.log("i", "Serialized the following from %s: %s" %(file_name, repr(serialized))) - # Create an empty profile - the id and name will be changed by the ContainerRegistry - profile = InstanceContainer("") - try: - profile.deserialize(serialized) - except Exception as e: # Not a valid g-code file. - Logger.log("e", "Unable to serialise the profile: %s", str(e)) - return None + json_data = json.loads(serialized) - profile.addMetaDataEntry("type", "quality") + profile_strings = [json_data["global_quality"]] + profile_strings.extend(json_data.get("extruder_quality", [])) - return profile + return [readQualityProfileFromString(profile_string) for profile_string in profile_strings] + +## Unescape a string which has been escaped for use in a gcode comment. +# +# \param string The string to unescape. +# \return \type{str} The unscaped string. +def unescapeGcodeComment(string): + # Un-escape the serialized profile. + pattern = re.compile("|".join(GCodeProfileReader.escape_characters.keys())) + + # Perform the replacement with a regular expression. + return pattern.sub(lambda m: GCodeProfileReader.escape_characters[re.escape(m.group(0))], string) + +## Read in a profile from a serialized string. +# +# \param profile_string The profile data in serialized form. +# \return \type{Profile} the resulting Profile object or None if it could not be read. +def readQualityProfileFromString(profile_string): + # Create an empty profile - the id and name will be changed by the ContainerRegistry + profile = InstanceContainer("") + try: + profile.deserialize(profile_string) + except Exception as e: # Not a valid g-code file. + Logger.log("e", "Unable to serialise the profile: %s", str(e)) + return None + return profile diff --git a/plugins/GCodeWriter/GCodeWriter.py b/plugins/GCodeWriter/GCodeWriter.py index 4eb1f89134..ea21e33109 100644 --- a/plugins/GCodeWriter/GCodeWriter.py +++ b/plugins/GCodeWriter/GCodeWriter.py @@ -4,8 +4,11 @@ from UM.Mesh.MeshWriter import MeshWriter from UM.Logger import Logger from UM.Application import Application -from UM.Settings.InstanceContainer import InstanceContainer #To create a complete setting profile to store in the g-code. + +from cura.Settings.ExtruderManager import ExtruderManager + import re #For escaping characters in the settings. +import json ## Writes g-code to a file. # @@ -23,7 +26,7 @@ class GCodeWriter(MeshWriter): # It can only read settings with the same version as the version it was # written with. If the file format is changed in a way that breaks reverse # compatibility, increment this version number! - version = 2 + version = 3 ## Dictionary that defines how characters are escaped when embedded in # g-code. @@ -64,25 +67,33 @@ class GCodeWriter(MeshWriter): # # \param settings A container stack to serialise. # \return A serialised string of the settings. - def _serialiseSettings(self, settings): + def _serialiseSettings(self, stack): prefix = ";SETTING_" + str(GCodeWriter.version) + " " # The prefix to put before each line. prefix_length = len(prefix) - global_stack = Application.getInstance().getGlobalContainerStack() - container_with_profile = global_stack.findContainer({"type": "quality"}) + container_with_profile = stack.findContainer({"type": "quality"}) serialized = container_with_profile.serialize() + data = {"global_quality": serialized} + + manager = ExtruderManager.getInstance() + for extruder in manager.getMachineExtruders(stack.getBottom().getId()): + extruder_quality = extruder.findContainer({"type": "quality"}) + extruder_serialized = 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. - serialized = pattern.sub(lambda m: GCodeWriter.escape_characters[re.escape(m.group(0))], serialized) + 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(serialized), 80 - prefix_length): - result += prefix + serialized[pos : pos + 80 - prefix_length] + "\n" - serialized = result - - return serialized \ No newline at end of file + for pos in range(0, len(escaped_string), 80 - prefix_length): + result += prefix + escaped_string[pos : pos + 80 - prefix_length] + "\n" + return result