From 101471235cc6923709b5e99d927ae21b07a8c029 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Mon, 9 Jan 2017 14:09:49 +0100 Subject: [PATCH 01/15] Pinning Jinja to <2.9 for now Variables defined in an outer scope can no longer be set from an inner scope (see pallets/jinja#641). Regardless of whether that is right or wrong, we can't control if people are using such constructs in their plugins, which versions of Jinja >= 2.9 would now break out of the blue, regardless of OctoPrint version. That is unacceptable sadly and requires pinning for now, until plugin authors have had a chance to adapt accordingly. Also see #1697. --- setup.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/setup.py b/setup.py index 9bc6796e..6fa05440 100644 --- a/setup.py +++ b/setup.py @@ -15,6 +15,9 @@ import octoprint_setuptools # Requirements for our application INSTALL_REQUIRES = [ "flask>=0.9,<0.11", + "Jinja2>=2.8,<2.9", # Jinja 2.9 has breaking changes WRT template scope - we can't + # guarantee backwards compatibility for plugins and such with that + # version, hence we need to pin to a lower version for now. See #1697 "werkzeug>=0.8.3,<0.9", "tornado==4.0.2", # pinned for now, we need to migrate to a newer tornado, but due # to some voodoo needed to get large streamed uploads and downloads From dc3182c872aa615e4b946fcab5722c26fd4fb5f9 Mon Sep 17 00:00:00 2001 From: Marc Hannappel Date: Wed, 21 Dec 2016 09:44:24 +0100 Subject: [PATCH 02/15] Fix folders doesn't get listed when performing a filtering in file section, issue #1667 (cherry picked from commit 8a8b88a) --- src/octoprint/static/js/app/viewmodels/files.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/octoprint/static/js/app/viewmodels/files.js b/src/octoprint/static/js/app/viewmodels/files.js index 24695b2d..2563ebe8 100644 --- a/src/octoprint/static/js/app/viewmodels/files.js +++ b/src/octoprint/static/js/app/viewmodels/files.js @@ -708,11 +708,12 @@ $(function() { return false; } - if (entry["type"] == "folder" && entry["children"]) { + var success = entry["name"].toLocaleLowerCase().indexOf(query) > -1; + if (!success && entry["type"] == "folder" && entry["children"]) { return _.any(entry["children"], recursiveSearch); - } else { - return entry["name"].toLocaleLowerCase().indexOf(query) > -1; } + + return success; }; self.listHelper.changeSearchFunction(recursiveSearch); From f3f60674fc454942e45e5117be02a886ed4c6469 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Mon, 9 Jan 2017 17:00:04 +0100 Subject: [PATCH 03/15] Allow a retraction z hop of 0 in timelapse configuration See comment by @markwal at https://github.com/foosel/OctoPrint/pull/1148#pullrequestreview-14802655 --- src/octoprint/server/api/timelapse.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/octoprint/server/api/timelapse.py b/src/octoprint/server/api/timelapse.py index 78cf69c2..e7272eb6 100644 --- a/src/octoprint/server/api/timelapse.py +++ b/src/octoprint/server/api/timelapse.py @@ -230,7 +230,7 @@ def setTimelapseConfig(): except ValueError: return make_response("Invalid value for retraction Z-Hop: %r" % data["retractionZHop"]) else: - if retractionZHop > 0: + if retractionZHop >= 0: config["options"]["retractionZHop"] = retractionZHop else: return make_response("Invalid value for retraction Z-Hop: %d" % retractionZHop) From 63c588fce9f3a710c69b27c2cc75bf237a799309 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Mon, 9 Jan 2017 17:07:27 +0100 Subject: [PATCH 04/15] Update command line examples in README --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 47e9d7a2..f6d3be2a 100644 --- a/README.md +++ b/README.md @@ -86,27 +86,27 @@ following usage examples assume that said `octoprint` script is on your `PATH`. You can start the server via - octoprint + octoprint serve By default it binds to all interfaces on port 5000 (so pointing your browser to `http://127.0.0.1:5000` will do the trick). If you want to change that, use the additional command line parameters `host` and `port`, which accept the host ip to bind to and the numeric port number respectively. If for example you want the server to only listen on the local interface on port 8080, the command line would be - octoprint --host=127.0.0.1 --port=8080 + octoprint serve --host=127.0.0.1 --port=8080 Alternatively, the host and port on which to bind can be defined via the configuration. If you want to run OctoPrint as a daemon (only supported on Linux), use - octoprint --daemon {start|stop|restart} [--pid PIDFILE] + octoprint daemon {start|stop|restart} [--pid PIDFILE] If you do not supply a custom pidfile location via `--pid PIDFILE`, it will be created at `/tmp/octoprint.pid`. You can also specify the configfile or the base directory (for basing off the `uploads`, `timelapse` and `logs` folders), e.g.: - octoprint --config /path/to/another/config.yaml --basedir /path/to/my/basedir + octoprint serve --config /path/to/another/config.yaml --basedir /path/to/my/basedir See `octoprint --help` for further information. From 6e474d9096dd6e7cae79eb9ee917f548f430c5da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Tue, 10 Jan 2017 12:16:33 +0100 Subject: [PATCH 05/15] Fix model size calculation during GCODE analysis * Properly handle G0/G1 with no X, Y, Z coordinates in relative mode instead of duplicating coordinates - should fix #1675 * Only take move commands with X, Y, Z coordinates into account for model size calculation - this makes our internal GCODE analysis behave like the GCODE viewer's analysis and produce the same model size. The downside is that extrusions on the origin are no longer taken into account for checking if a model is within bounds of the print bed, but that should hopefully not produce any issues in the real world. --- src/octoprint/util/gcodeInterpreter.py | 28 +++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/src/octoprint/util/gcodeInterpreter.py b/src/octoprint/util/gcodeInterpreter.py index d06766b8..0cc0aab2 100644 --- a/src/octoprint/util/gcodeInterpreter.py +++ b/src/octoprint/util/gcodeInterpreter.py @@ -294,15 +294,28 @@ class gcode(object): e = getCodeFloat(line, 'E') f = getCodeFloat(line, 'F') + if x is not None or y is not None or z is not None: + # this is a move + move = True + else: + # print head stays on position + move = False + oldPos = pos - newPos = Vector3D(x if x is not None else pos.x, - y if y is not None else pos.y, - z if z is not None else pos.z) + + # Use new coordinates if provided. If not provided, use prior coordinates in absolute + # and 0.0 in relative mode. + newPos = Vector3D(x if x is not None else (pos.x if posAbs else 0.0), + y if y is not None else (pos.y if posAbs else 0.0), + z if z is not None else (pos.z if posAbs else 0.0)) if posAbs: + # Absolute mode: scale coordinates and apply offsets pos = newPos * scale + posOffset else: + # Relative mode: scale and add to current position pos += newPos * scale + if f is not None and f != 0: feedrate = f @@ -310,10 +323,12 @@ class gcode(object): if absoluteE: # make sure e is relative e -= currentE[currentExtruder] - # If move includes extrusion, calculate new min/max coordinates of model - if e > 0.0: - # extrusion -> relevant for print area & dimensions + + # If move with extrusion, calculate new min/max coordinates of model + if e > 0.0 and move: + # extrusion and move -> relevant for print area & dimensions self._minMax.record(pos) + totalExtrusion[currentExtruder] += e currentE[currentExtruder] += e maxExtrusion[currentExtruder] = max(maxExtrusion[currentExtruder], @@ -417,7 +432,6 @@ class gcode(object): if throttle is not None: throttle() - if self.progressCallback is not None: self.progressCallback(100.0) From 50965d69ce87eda94338769802860d69838d8898 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Tue, 10 Jan 2017 19:00:58 +0100 Subject: [PATCH 06/15] Include info about setting up dev env in CONTRIBUTING.md --- CONTRIBUTING.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e7c89f36..98ee68d0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -12,6 +12,7 @@ or **[creating pull requests](#pull-requests)**. * [Where can I find which version and branch I'm on?](#where-can-i-find-which-version-and-branch-im-on) * [Where can I find those log files you keep talking about?](#where-can-i-find-those-log-files-you-keep-talking-about) * [Where can I find my browser's error console?](#where-can-i-find-my-browsers-error-console) + * [Setting up a development environment](#setting-up-a-development-environment) * [Pull requests](#pull-requests) * [What do the branches mean?](#what-do-the-branches-mean) * [How OctoPrint is versioned](#how-octoprint-is-versioned) @@ -235,6 +236,10 @@ find information on how to do just that in the See [How to open the Javascript Console in different browsers](https://webmasters.stackexchange.com/questions/8525/how-to-open-the-javascript-console-in-different-browsers) +## Setting up a development environment + +See [the corresponding chapter in the documentation](http://docs.octoprint.org/en/master/development/index.html#setting-up-a-development-environment). + ## Pull requests 1. If you want to add a new feature to OctoPrint, **please always first From 06eae381e433cf16d0416066608e05e094948a82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Tue, 10 Jan 2017 19:01:23 +0100 Subject: [PATCH 07/15] Fixed an error in the dev env setup docs --- docs/development/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/development/index.rst b/docs/development/index.rst index 3cdef357..585561c8 100644 --- a/docs/development/index.rst +++ b/docs/development/index.rst @@ -167,7 +167,7 @@ IDE Setup PyCharm ....... - - "File" > "Open ...", select OctoPrint checkout folder (e.g. ``~/devel/OctoPrint/venv`` or ``C:\Devel\OctoPrint``) + - "File" > "Open ...", select OctoPrint checkout folder (e.g. ``~/devel/OctoPrint`` or ``C:\Devel\OctoPrint``) - "File" > "Settings ..." > "Project: OctoPrint" > "Project Interpreter" > "Add local ...", select OctoPrint venv folder (e.g. ``~/devel/OctoPrint/venv`` or ``C:\Devel\OctoPrint\venv``) - Right click "src" in project tree, mark as source folder From 58f934e2c1c891d2c579d116e1a19c56a1ac93b6 Mon Sep 17 00:00:00 2001 From: derpicknicker1 <10minutemail@web.de> Date: Tue, 10 Jan 2017 17:05:39 +0100 Subject: [PATCH 08/15] Fix issue in getting file metadata after slicing This fixes the issue that there were no informations about filament usage in metadata after slicing with cura plugin. Trying to call profile.get_float("filament_diameter") ended in en exception with message " 'module' object has no attribute 'get_float' ". So i defined profile before using profile and now it works. See issue #1685 Also inserted a check to determine if filament usage is > 0 to exclude tools with no filament usage in metadata. (cherry picked from commit c9b38bd) --- src/octoprint/plugins/cura/__init__.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/octoprint/plugins/cura/__init__.py b/src/octoprint/plugins/cura/__init__.py index cca716cc..e4a2f3d1 100644 --- a/src/octoprint/plugins/cura/__init__.py +++ b/src/octoprint/plugins/cura/__init__.py @@ -372,10 +372,12 @@ class CuraPlugin(octoprint.plugin.SlicerPlugin, analysis = dict() if not "filament" in analysis: analysis["filament"] = dict() - if not tool_key in analysis["filament"]: + if not tool_key in analysis["filament"] and filament > 0: analysis["filament"][tool_key] = dict() - if profile.get_float("filament_diameter") != None: + profile = Profile(self._load_profile(profile_path), printer_profile, posX, posY) + + if profile.get_float("filament_diameter") != None and filament > 0: if profile.get("gcode_flavor") == GcodeFlavors.ULTIGCODE or profile.get("gcode_flavor") == GcodeFlavors.REPRAP_VOLUME: analysis["filament"][tool_key] = _get_usage_from_volume(filament, profile.get_float("filament_diameter")) else: From f20e985d0da3d197dabe350cda67ead4cff0117d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Wed, 11 Jan 2017 11:55:39 +0100 Subject: [PATCH 09/15] profile => slicing_profile & removed filament 0 check If the slicer returns values for a tool we want it in our analysis result, even if it's zero. That way the result will be the same as if we have our own built in gcode analyser take a look at the file. (cherry picked from commit 818ae92) --- src/octoprint/plugins/cura/__init__.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/octoprint/plugins/cura/__init__.py b/src/octoprint/plugins/cura/__init__.py index e4a2f3d1..3633cf5e 100644 --- a/src/octoprint/plugins/cura/__init__.py +++ b/src/octoprint/plugins/cura/__init__.py @@ -266,6 +266,7 @@ class CuraPlugin(octoprint.plugin.SlicerPlugin, working_dir = os.path.dirname(executable) + slicing_profile = Profile(self._load_profile(profile_path), printer_profile, posX, posY) engine_settings = self._convert_to_engine(profile_path, printer_profile, posX, posY) # Start building the argument list for the CuraEngine command execution @@ -372,16 +373,14 @@ class CuraPlugin(octoprint.plugin.SlicerPlugin, analysis = dict() if not "filament" in analysis: analysis["filament"] = dict() - if not tool_key in analysis["filament"] and filament > 0: + if not tool_key in analysis["filament"]: analysis["filament"][tool_key] = dict() - profile = Profile(self._load_profile(profile_path), printer_profile, posX, posY) - - if profile.get_float("filament_diameter") != None and filament > 0: - if profile.get("gcode_flavor") == GcodeFlavors.ULTIGCODE or profile.get("gcode_flavor") == GcodeFlavors.REPRAP_VOLUME: - analysis["filament"][tool_key] = _get_usage_from_volume(filament, profile.get_float("filament_diameter")) + if slicing_profile.get_float("filament_diameter") is not None: + if slicing_profile.get("gcode_flavor") == GcodeFlavors.ULTIGCODE or slicing_profile.get("gcode_flavor") == GcodeFlavors.REPRAP_VOLUME: + analysis["filament"][tool_key] = _get_usage_from_volume(filament, slicing_profile.get_float("filament_diameter")) else: - analysis["filament"][tool_key] = _get_usage_from_length(filament, profile.get_float("filament_diameter")) + analysis["filament"][tool_key] = _get_usage_from_length(filament, slicing_profile.get_float("filament_diameter")) except: pass From 2ab7fa4d3a4f2c31a59f3d40237d1cd920fbbecc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Wed, 11 Jan 2017 12:11:16 +0100 Subject: [PATCH 10/15] Support volume calculate for S3D files --- src/octoprint/util/gcodeInterpreter.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/octoprint/util/gcodeInterpreter.py b/src/octoprint/util/gcodeInterpreter.py index 0cc0aab2..432eb0db 100644 --- a/src/octoprint/util/gcodeInterpreter.py +++ b/src/octoprint/util/gcodeInterpreter.py @@ -260,6 +260,7 @@ class gcode(object): if ';' in line: comment = line[line.find(';')+1:].strip() if comment.startswith("filament_diameter"): + # Slic3r filamentValue = comment.split("=", 1)[1].strip() try: self._filamentDiameter = float(filamentValue) @@ -269,6 +270,7 @@ class gcode(object): except ValueError: self._filamentDiameter = 0.0 elif comment.startswith("CURA_PROFILE_STRING") or comment.startswith("CURA_OCTO_PROFILE_STRING"): + # Cura 15.04.* & OctoPrint Cura plugin if comment.startswith("CURA_PROFILE_STRING"): prefix = "CURA_PROFILE_STRING:" else: @@ -280,6 +282,13 @@ class gcode(object): self._filamentDiameter = float(curaOptions["filament_diameter"]) except: self._filamentDiameter = 0.0 + elif comment.startswith("filamentDiameter,"): + # Simplify3D + filamentValue = comment.split(",", 1)[1].strip() + try: + self._filamentDiameter = float(filamentValue) + except ValueError: + self._filamentDiameter = 0.0 line = line[0:line.find(';')] G = getCodeInt(line, 'G') From c0dff1359b56c57b3d5c30b8353da5d6ae38adc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Wed, 11 Jan 2017 13:51:40 +0100 Subject: [PATCH 11/15] Fix file_in_path for windows environments --- src/octoprint/filemanager/storage.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/octoprint/filemanager/storage.py b/src/octoprint/filemanager/storage.py index f26a21ae..65a7c0bc 100644 --- a/src/octoprint/filemanager/storage.py +++ b/src/octoprint/filemanager/storage.py @@ -511,7 +511,7 @@ class LocalFileStorage(StorageInterface): filepath = self.sanitize_path(filepath) path = self.sanitize_path(path) - return filepath == path or filepath.startswith(path + "/") + return filepath == path or filepath.startswith(path + os.sep) def file_exists(self, path): path, name = self.sanitize(path) From d83440d32d12974b685696c19c9e6a3709b5972c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Wed, 11 Jan 2017 13:53:28 +0100 Subject: [PATCH 12/15] Use full path instead of only the file name for busy detection --- src/octoprint/server/api/files.py | 16 ++++++++-------- src/octoprint/server/util/sockjs.py | 8 ++++---- .../static/js/app/viewmodels/printerstate.js | 4 ++-- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/octoprint/server/api/files.py b/src/octoprint/server/api/files.py index 12012087..5cd33834 100644 --- a/src/octoprint/server/api/files.py +++ b/src/octoprint/server/api/files.py @@ -248,8 +248,8 @@ def _verifyFolderExists(origin, foldername): def _isBusy(target, path): - currentOrigin, currentFilename = _getCurrentFile() - if currentFilename is not None and currentOrigin == target and fileManager.file_in_path(FileDestinations.LOCAL, path, currentFilename) and (printer.is_printing() or printer.is_paused()): + currentOrigin, currentPath = _getCurrentFile() + if currentPath is not None and currentOrigin == target and fileManager.file_in_path(FileDestinations.LOCAL, path, currentPath) and (printer.is_printing() or printer.is_paused()): return True return any(target == x[0] and fileManager.file_in_path(FileDestinations.LOCAL, path, x[1]) for x in fileManager.get_busy_files()) @@ -705,8 +705,8 @@ def deleteGcodeFile(filename, target): return make_response("Trying to delete a file that is currently in use: %s" % filename, 409) # deselect the file if it's currently selected - currentOrigin, currentFilename = _getCurrentFile() - if currentFilename is not None and currentOrigin == target and filename == currentFilename: + currentOrigin, currentPath = _getCurrentFile() + if currentPath is not None and currentOrigin == target and filename == currentPath: printer.unselect_file() # delete it @@ -723,8 +723,8 @@ def deleteGcodeFile(filename, target): return make_response("Trying to delete a folder that contains a file that is currently in use: %s" % filename, 409) # deselect the file if it's currently selected - currentOrigin, currentFilename = _getCurrentFile() - if currentFilename is not None and currentOrigin == target and fileManager.file_in_path(target, filename, currentFilename): + currentOrigin, currentPath = _getCurrentFile() + if currentPath is not None and currentOrigin == target and fileManager.file_in_path(target, filename, currentPath): printer.unselect_file() # delete it @@ -734,8 +734,8 @@ def deleteGcodeFile(filename, target): def _getCurrentFile(): currentJob = printer.get_current_job() - if currentJob is not None and "file" in currentJob.keys() and "name" in currentJob["file"] and "origin" in currentJob["file"]: - return currentJob["file"]["origin"], currentJob["file"]["name"] + if currentJob is not None and "file" in currentJob.keys() and "path" in currentJob["file"] and "origin" in currentJob["file"]: + return currentJob["file"]["origin"], currentJob["file"]["path"] else: return None, None diff --git a/src/octoprint/server/util/sockjs.py b/src/octoprint/server/util/sockjs.py index d4df75b0..af2f106b 100644 --- a/src/octoprint/server/util/sockjs.py +++ b/src/octoprint/server/util/sockjs.py @@ -154,12 +154,12 @@ class PrinterStateConnection(sockjs.tornado.SockJSConnection, octoprint.printer. messages = self._messageBacklog self._messageBacklog = [] - busy_files = [dict(origin=v[0], name=v[1]) for v in self._fileManager.get_busy_files()] + busy_files = [dict(origin=v[0], path=v[1]) for v in self._fileManager.get_busy_files()] if "job" in data and data["job"] is not None \ - and "file" in data["job"] and "name" in data["job"]["file"] and "origin" in data["job"]["file"] \ - and data["job"]["file"]["name"] is not None and data["job"]["file"]["origin"] is not None \ + and "file" in data["job"] and "path" in data["job"]["file"] and "origin" in data["job"]["file"] \ + and data["job"]["file"]["path"] is not None and data["job"]["file"]["origin"] is not None \ and (self._printer.is_printing() or self._printer.is_paused()): - busy_files.append(dict(origin=data["job"]["file"]["origin"], name=data["job"]["file"]["name"])) + busy_files.append(dict(origin=data["job"]["file"]["origin"], path=data["job"]["file"]["path"])) data.update({ "serverTime": time.time(), diff --git a/src/octoprint/static/js/app/viewmodels/printerstate.js b/src/octoprint/static/js/app/viewmodels/printerstate.js index 9a820720..1ccd6b83 100644 --- a/src/octoprint/static/js/app/viewmodels/printerstate.js +++ b/src/octoprint/static/js/app/viewmodels/printerstate.js @@ -256,8 +256,8 @@ $(function() { self._processBusyFiles = function(data) { var busyFiles = []; _.each(data, function(entry) { - if (entry.hasOwnProperty("name") && entry.hasOwnProperty("origin")) { - busyFiles.push(entry.origin + ":" + entry.name); + if (entry.hasOwnProperty("path") && entry.hasOwnProperty("origin")) { + busyFiles.push(entry.origin + ":" + entry.path); } }); self.busyFiles(busyFiles); From 7c74594f0677e45e6c9218d3b8735cdf32da8a7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Wed, 11 Jan 2017 14:50:09 +0100 Subject: [PATCH 13/15] Adjusted Cura profile handling to match latest Cura Legacy version * Added/changed mappings of profile to engine settings to match Cura Legacy mapping: * perimeterBeforeInfill: taken from perimeter_before_infill (new, fixes #1693) * skinSpeed: taken from solidarea_speed (new) * raftAirGapLayer0: sum of raft_airgap and raft_airgap_all * raftAirGap: taken from raft_airgap_all (new) * raftFanSpeed: changed to 0 * raftSurfaceThickness: taken from raft_surface_thickness (new) * raftSurfaceLinewidth & raftSurfaceLineSpacing: taken from raft_surface_linewidth (new) * Mach3 Gcode Flavor replaces S parameter with P parameter in temperature commands within generated GCODE, like in Cura Legacy --- src/octoprint/plugins/cura/profile.py | 41 ++++++++++++++++++--------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/src/octoprint/plugins/cura/profile.py b/src/octoprint/plugins/cura/profile.py index 9e3ce7be..65c81e08 100644 --- a/src/octoprint/plugins/cura/profile.py +++ b/src/octoprint/plugins/cura/profile.py @@ -63,12 +63,14 @@ defaults = dict( solid_top=True, solid_bottom=True, fill_overlap=15, + perimeter_before_infill=False, # speeds print_speed=50.0, travel_speed=150.0, bottom_layer_speed=20.0, infill_speed=0.0, + solidarea_speed=0.0, outer_shell_speed=0.0, inner_shell_speed=0.0, @@ -117,8 +119,11 @@ defaults = dict( raft_base_linewidth=1.0, raft_interface_thickness=0.27, raft_interface_linewidth=0.4, + raft_airgap_all=0.0, raft_airgap=0.22, raft_surface_layers=2, + raft_surface_thickness=0.27, + raft_surface_linewidth=0.4, # repairing fix_horrible_union_all_type_a=True, @@ -754,9 +759,13 @@ class Profile(object): prefix = "" + gcode_parameter_key = "S" + if self.get("gcode_flavor") == GcodeFlavors.MACH3: + gcode_parameter_key = "P" + e_steps = self.get_float("steps_per_e") if e_steps > 0: - prefix += "M92 E{e_steps}\n" % (e_steps) + prefix += "M92 E{e_steps}\n".format(e_steps=e_steps) temp = self.get_float("print_temperature") bed_temp = 0 @@ -765,30 +774,30 @@ class Profile(object): include_bed_temp = bed_temp > 0 and not "{print_bed_temperature}" in Profile.regex_strip_comments.sub("", contents) if include_bed_temp: - prefix += "M140 S{bed_temp}\n".format(bed_temp=bed_temp) + prefix += "M140 {param}{bed_temp}\n".format(param=gcode_parameter_key, bed_temp=bed_temp) if temp > 0 and not "{print_temperature}" in Profile.regex_strip_comments.sub("", contents): if extruder_count > 0: - def temp_line(temp, extruder, template): + def temp_line(temp, extruder, param, template): t = temp if extruder > 0: print_temp = self.get_float("print_temperature%d" % (extruder + 1)) if print_temp > 0: t = print_temp - return template.format(extruder=extruder, temp=t) + return template.format(extruder=extruder, temp=t, param=param) prefix_preheat = "" prefix_waitheat = "" for n in range(0, extruder_count): if n > 0: - prefix_preheat += temp_line(temp, n, "M104 T{extruder} S{temp}\n") - prefix_waitheat += temp_line(temp, n, "M109 T{extruder} S{temp}\n") + prefix_preheat += temp_line(temp, n, gcode_parameter_key, "M104 T{extruder} {param}{temp}\n") + prefix_waitheat += temp_line(temp, n, gcode_parameter_key, "M109 T{extruder} {param}{temp}\n") prefix += prefix_preheat + prefix_waitheat + "T0\n" else: - prefix += "M109 S{temp}\n".format(temp=temp) + prefix += "M109 {param}{temp}\n".format(param=gcode_parameter_key, temp=temp) if include_bed_temp: - prefix += "M190 S{bed_temp}\n".format(bed_temp=bed_temp) + prefix += "M190 {param}{bed_temp}\n".format(param=gcode_parameter_key, bed_temp=bed_temp) return prefix @@ -871,12 +880,14 @@ class Profile(object): "downSkinCount": solid_layer_count if self.get_boolean("solid_bottom") else 0, "upSkinCount": solid_layer_count if self.get_boolean("solid_top") else 0, "infillOverlap": self.get_int("fill_overlap"), + "perimeterBeforeInfill": 1 if self.get_boolean("perimeter_before_infill") else 0, "initialSpeedupLayers": int(4), "initialLayerSpeed": self.get_int("bottom_layer_speed"), "printSpeed": self.get_int("print_speed"), "infillSpeed": self.get_int("infill_speed") if self.get_int("infill_speed") > 0 else self.get_int("print_speed"), "inset0Speed": self.get_int("outer_shell_speed") if self.get_int("outer_shell_speed") > 0 else self.get_int("print_speed"), "insetXSpeed": self.get_int("inner_shell_speed") if self.get_int("inner_shell_speed") > 0 else self.get_int("print_speed"), + "skinSpeed": self.get_int("solidarea_speed") if self.get_int("solidarea_speed") > 0 > 0 else self.get_int("print_speed"), "moveSpeed": self.get_int("travel_speed"), "fanSpeedMin": self.get_int("fan_speed") if self.get_boolean("fan_enabled") else 0, "fanSpeedMax": self.get_int("fan_speed_max") if self.get_boolean("fan_enabled") else 0, @@ -892,13 +903,14 @@ class Profile(object): "retractionAmountExtruderSwitch": self.get_microns("retraction_dual_amount"), "retractionZHop": self.get_microns("retraction_hop"), "minimalExtrusionBeforeRetraction": self.get_microns("retraction_minimal_extrusion"), - "enableCombing": 1 if self.get("retraction_combing") == RetractionCombingTypes.ALL else (2 if self.get("retraction_combing") == RetractionCombingTypes.NO_SKIN else 0), "multiVolumeOverlap": self.get_microns("overlap_dual"), "objectSink": max(0, self.get_microns("object_sink")), "minimalLayerTime": self.get_int("cool_min_layer_time"), "minimalFeedrate": self.get_int("cool_min_feedrate"), "coolHeadLift": 1 if self.get_boolean("cool_head_lift") else 0, + "enableCombing": 1 if self.get("retraction_combing") == RetractionCombingTypes.ALL else (2 if self.get("retraction_combing") == RetractionCombingTypes.NO_SKIN else 0), + # model positioning "posx": self.get_pos_x() * 1000, # in microns "posy": self.get_pos_y() * 1000, # in microns @@ -952,12 +964,13 @@ class Profile(object): settings["raftInterfaceThickness"] = self.get_microns("raft_interface_thickness") settings["raftInterfaceLinewidth"] = self.get_microns("raft_interface_linewidth") settings["raftInterfaceLineSpacing"] = self.get_microns("raft_interface_linewidth") * 2 - settings["raftAirGapLayer0"] = self.get_microns("raft_airgap") + settings["raftAirGapLayer0"] = self.get_microns("raft_airgap") + self.get_microns("raft_airgap_all") + settings["raftAirGap"] = self.get_microns("raft_airgap_all") settings["raftBaseSpeed"] = self.get_int("bottom_layer_speed") - settings["raftFanSpeed"] = 100 - settings["raftSurfaceThickness"] = settings["raftInterfaceThickness"] - settings["raftSurfaceLinewidth"] = int(edge_width * 1000) - settings["raftSurfaceLineSpacing"] = int(edge_width * 1000 * 0.9) + settings["raftFanSpeed"] = 0 + settings["raftSurfaceThickness"] = self.get_microns("raft_surface_thickness") + settings["raftSurfaceLinewidth"] = self.get_microns("raft_surface_linewidth") + settings["raftSurfaceLineSpacing"] = self.get_microns("raft_surface_linewidth") settings["raftSurfaceLayers"] = self.get_int("raft_surface_layers") settings["raftSurfaceSpeed"] = self.get_int("bottom_layer_speed") From 071c32fff18cdf56c3f57137b2b8f555edc2ea22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Wed, 11 Jan 2017 16:15:43 +0100 Subject: [PATCH 14/15] Ported fix of solid layer count from Ultimaker/CuraEngine#140 Fixes #1692 --- src/octoprint/plugins/cura/profile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/octoprint/plugins/cura/profile.py b/src/octoprint/plugins/cura/profile.py index 65c81e08..e8783d12 100644 --- a/src/octoprint/plugins/cura/profile.py +++ b/src/octoprint/plugins/cura/profile.py @@ -831,7 +831,7 @@ class Profile(object): if layer_height == 0.0: return 1 import math - return int(math.ceil(solid_thickness / (layer_height - 0.0001))) + return int(math.ceil((solid_thickness - 0.0001) / layer_height)) def calculate_minimal_extruder_count(self): extruder_count = self.get("extruder_amount") From fb5d5682e679a825c31cdb8c226a9bf99a6d9ca2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Wed, 11 Jan 2017 17:57:45 +0100 Subject: [PATCH 15/15] Do not eat 0% progress Fixes #1344 (this time for good I hope) --- src/octoprint/printer/standard.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/octoprint/printer/standard.py b/src/octoprint/printer/standard.py index 1d8ebd1f..b0211d89 100644 --- a/src/octoprint/printer/standard.py +++ b/src/octoprint/printer/standard.py @@ -168,7 +168,7 @@ class Printer(PrinterInterface, comm.MachineComPrintCallback): #~~ progress plugin reporting def _reportPrintProgressToPlugins(self, progress): - if not progress or not self._selectedFile or not "sd" in self._selectedFile or not "filename" in self._selectedFile: + if progress is None or not self._selectedFile or not "sd" in self._selectedFile or not "filename" in self._selectedFile: return storage = "sdcard" if self._selectedFile["sd"] else "local"