diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 24bc3890..68c09bcb 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,51 +1,264 @@ -Issues, Tickets, however you may call them ------------------------------------------- +# Contribution Guidelines -Read the following short instructions **fully** and **follow them** if you want your ticket to be taken care of and not closed again directly! They are linked on top of every new issue form, so don't say nobody warned you afterwards. +This document outlines what you need to know before **[creating tickets](#issues-tickets-however-you-may-call-them)** +or **[creating pull requests](#pull-requests)**. -- **Read the [FAQ](https://github.com/foosel/OctoPrint/wiki/FAQ)** -- Always create **one ticket for one purpose**. So don't mix two or more feature requests, support requests, bugs etc into one ticket. If you do, your ticket will be closed! -- If you want to report a bug, **READ AND FOLLOW [How to file a bug report](https://github.com/foosel/OctoPrint/wiki/How-to-file-a-bug-report)!** Tickets will be automatically checked if they comply with the requirements outlined in that wiki node! Other then what's written in there (**and really EVERYTHING that is written in there!**) you don't have to do anything special with your ticket. Listen to what GitIssueBot might have to say to you! -- If you want to post a **request** of any kind (feature request, documentation request, ...), **add [Request] to your issue's title!** -- If you need **support** with a problem of your installation (e.g. if you have problems getting the webcam to work) or have a general **question**, the issue tracker is not the right place. Consult the [Mailinglist](https://groups.google.com/group/octoprint) or the [Google+ Community](https://plus.google.com/communities/102771308349328485741) instead! -- If you are a developer that wants to brainstorm a pull request or possible changes to the plugin system, **add [Brainstorming] to your issue's title!** (see below). -- If you have another reason for creating a ticket that doesn't fit any of the above categories, it's something better suited for the [Mailinglist](https://groups.google.com/group/octoprint) or the [Google+ Community](https://plus.google.com/communities/102771308349328485741). +## Contents -Following these guidelines (**especially EVERYTHING mentioned in ["How to file a bug report"](https://github.com/foosel/OctoPrint/wiki/How-to-file-a-bug-report)**) is necessary so the tickets stay manageable - you are not the only one with an open issue, so please respect that you have to **play by the rules** so that your problem can be taken care of. Tickets not playing by the rules **will be closed without further investigation!**. + * [Issues, Tickets, however you may call them](#issues-tickets-however-you-may-call-them) + * [How to file a bug report](#how-to-file-a-bug-report) + * [What should I do before submitting a bug report?](#what-should-i-do-before-submitting-a-bug-report) + * [What should I include in a bug report?](#what-should-i-include-in-a-bug-report) + * [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) + * [Pull requests](#pull-requests) + * [History](#history) + * [Footnotes](#footnotes) -Pull Requests -------------- +## Issues, Tickets, however you may call them -1. If you want to add a new feature to OctoPrint, **please always first consider if it wouldn't be better suited for a - plugin.** As a general rule of thumb, any feature that is only of interest to a small sub group should be moved into a - plugin. If the current plugin system doesn't allow you to implement your feature as a plugin, create a "Brainstorming" - ticket to get the discussion going how best to solve *this* in OctoPrint's plugin system - maybe that's the actual PR - you have been waiting for to contribute :) -2. If you plan to make **any large changes to the code or appearance, please open a "Brainstorming" ticket first** so that - we can determine if it's a good time for your specific pull request. It might be that I'm currently in the process of - making heavy changes to the code locations you'd target as well, or your approach doesn't fit the general "project - vision", and that would just cause unnecessary work and frustration for everyone or possibly get the PR rejected. -3. When adding code to OctoPrint, make sure you **follow the current coding style**. That means tabs instead of spaces in the - python files (yes, I know that this goes against PEP-8, I don't care) and space instead of tabs in the Javascript sources, - english language (that means code, variables, comments!), comments where necessary (tell why the code does something like - it does it, structure your code), following the general architecture. If your PR needs to make changes to the Stylesheets, - change the ``.less`` files from which the CSS is compiled. PRs that contain direct changes to the compiled - CSS will be closed. -4. **Test your changes thoroughly**. That also means testing with usage scenarios you don't normally use, e.g. if you only - use access control, test without and vice versa. If you only test with your printer, test with the virtual printer and - vice versa. State in your pull request how your tested your changes. -5. Please create all pull requests **against the `devel` branch**. -6. Create **one pull request per feature/bug fix**. -7. Create a **custom branch** for your feature/bug fix and use that as base for your pull request. Pull requests directly - against your version of `devel` will be closed. -8. In your pull request's description, **state what your pull request is doing**, as in, what feature does it implement, what - bug does it fix. The more thoroughly you explain your intent behind the PR here, the higher the chances it will get merged - fast. -9. Don't forget to **add yourself to the [AUTHORS](../AUTHORS.md) file** :) +Please read the following short instructions fully and follow them. You can +help the project tremendously this way: not only do you help the maintainers +to **address problems in a timely manner** but also keep it possible for them +to **fix bugs, add new and improve on existing functionality** instead of doing +nothing but ticket management. -History -------- +![Ticket flow chart](http://i.imgur.com/qYSZyuw.png) - * 2015-01-23: More guidelines for creating pull requests, support/questions redirected to Mailinglist/G+ community +- **[Read the FAQ](https://github.com/foosel/OctoPrint/wiki/FAQ)** +- If you want to report a **bug**, [read "How to file a bug report" below](#how-to-file-a-bug-report) + and *[use the provided template](#what-should-i-include-in-a-ticket)*. + You do not need to do anything else with your ticket. +- If you want to post a **request** of any kind (feature request, documentation + request, ...), add `[Request]` to your issue's title (e.g. `[Request] Awesome new feature`). +- If you are a **developer** that wants to brainstorm a pull request or possible + changes to the plugin system, add [Brainstorming] to your issue's title (e.g. + `[Brainstorming] New plugin hook for doing some cool stuff`). +- If you need **support**, have a **question** or some **other reason** that + doesn't fit any of the above categories, the issue tracker is not the right place. + Consult the [Mailinglist](https://groups.google.com/group/octoprint) or the + [Google+ Community](https://plus.google.com/communities/102771308349328485741) instead. + +No matter what kind of ticket you create, never mix two or more "ticket reasons" +into one ticket: One ticket per bug, request, brainstorming thread please. + +---- + +**Note**: A bot is in place that monitors new tickets, automatically +categorizes them and checks new bug reports for usage of the provided template. +That bot will only bother you if you open a ticket that appears to be a bug (no +`[Request]` or `[Brainstorming]` in the title) without the template, and it +will do that only to ensure that all information needed to solve the issue is +available for the maintainers to directly start tackling that problem. + +---- + +## How to file a bug report + +If you encounter an issue with OctoPrint, you are welcome to +[submit a bug report](https://goo.gl/GzkGv9). + +Before you do that for the first time though please take a moment to read the +following section *completely*. Thank you! :) + +### What should I do before submitting a bug report? + +1. **Make sure you are at the right location**. This is the Github repository + of the official version of OctoPrint, which is the 3D print server and + corresponding web interface itself. + + **This is not the Github respository of OctoPi**, which is the preconfigured + Raspberry Pi image including OctoPrint among other things - that one can be found + [here](https://github.com/guysoft/OctoPi). Please note that while we do have + some entries regarding OctoPi in the FAQ, any bugs should be reported in the + [proper bug tracker](https://github.com/guysoft/OctoPi/issues) which - again - + is not here. + + **This is also not the Github repository of any OctoPrint Plugins you + might have installed**. Report any issues with those in their corresponding + bug tracker (probably linked to from the plugin's homepage). + + Finally, **this is also not the right issue tracker if you are running an + forked version of OctoPrint**. Seek help for such unofficial versions from + the people maintaining them instead. + +2. Please make sure to **test out the current version** of OctoPrint to see + whether the problem you are encountering still exists. + + If you are feeling up to it you might also want to try the current development + version of OctoPrint (if you aren't already). Refer to the [FAQ](https://github.com/foosel/OctoPrint/wiki/FAQ) + for information on how to do this. + +3. The problem still exists? Then please **look through the + [existing tickets](https://github.com/foosel/OctoPrint/issues?state=open) + (use the [search](https://github.com/foosel/OctoPrint/search?q=&ref=cmdform&type=Issues))** + to check if there already exists a report of the issue you are encountering. + Sorting through duplicates of the same issue sometimes causes more work than + fixing it. Take the time to filter through possible duplicates and be really + sure that your problem definitely is a new one. Try more than one search query + (e.g. do not only search for "webcam" if you happen to run into an issue + with your webcam, also search for "timelapse" etc). + +### What should I include in a bug report? + +Always use the following template (you can remove what's within `[...]`, that's +only provided here as some additional information for you): + + #### What were you doing? + + [Please be as specific as possible here. The maintainers will need to reproduce + your issue in order to fix it and that is not possible if they don't know + what you did to get it to happen in the first place. If you encountered + a problem with specific files of any sorts, make sure to also include a link to a file + with which to reproduce the problem.] + + #### What did you expect to happen? + + #### What happened instead? + + #### Branch & Commit or Version of OctoPrint + + [Can be found in the lower left corner of the web interface.] + + #### Printer model & used firmware incl. version + + [If applicable, always include if unsure.] + + #### Browser and Version of Browser, Operating System running Browser + + [If applicable, always include if unsure.] + + #### Link to octoprint.log + + [On gist.github.com or pastebin.com. Always include and never truncate.] + + #### Link to contents of terminal tab or serial.log + + [On gist.github.com or pastebin.com. If applicable, always include if unsure or + reporting communication issues. Never truncate.] + + #### Link to contents of Javascript console in the browser + + [On gist.github.com or pastebin.com or alternatively a screenshot. If applicable - + always include if unsure or reporting UI issues.] + + #### Screenshot(s) showing the problem: + + [If applicable. Always include if unsure or reporting UI issues.] + + I have read the FAQ. + +### Where can I find which version and branch I'm on? + +You can find out all of them by taking a look into the lower left corner of the +OctoPrint UI: + +![Current version and git branch info in OctoPrint's UI](http://i.imgur.com/HyHMlY2.png) + +If you don't have access to the UI you can find out that information via the +command line as well. Either `octoprint --version` or `python setup.py version` +in OctoPrint's folder will tell you the version of OctoPrint you are running +(note: if it doesn't then you are running a version older than 1.1.0, +*upgrade now*). A `git branch` in your OctoPrint installation folder will mark +the branch you are on with a little *. `git rev-parse HEAD` will tell you the +current commit. + +### Where can I find those log files you keep talking about? + +OctoPrint by default provides two log outputs, a third one can be enabled if +more information is needed. + +One is contained in the **"Terminal" tab** within OctoPrint's UI and is a log +of the last 300 lines of communication with the printer. Please copy-paste +this somewhere (disable auto scroll to make copying the contents easier) - +e.g. http://pastebin.com or http://gist.github.com - and include a link in +your bug report. + +There is also **OctoPrint's application log file** or in short `octoprint.log`, +which is by default located at `~/.octoprint/logs/octoprint.log` on Linux, +`%APPDATA%\OctoPrint\logs\octoprint.log` on Windows and +`~/Library/Application Support/OctoPrint/logs/octoprint.log` on MacOS. Please +copy-paste this to pastebin or gist as well and include a link in your bug +report. + +It might happen that you are asked to provide a more **thorough log of the +communication with the printer** if you haven't already done so, the `serial.log`. +This is not written by default due to performance reasons, but you can enable +it in the settings dialog. After enabling that log, please reproduce the problem +again (connect to the printer, do whatever triggers it), then copy-paste +`~/.octoprint/logs/serial.log` (Windows: `%APPDATA%\OctoPrint\logs\serial.log`, +MacOS: `~/Library/Application Support/OctoPrint/logs/serial.log`) to pastebin +or gist and include the link in the bug report. + +You might also be asked to provide a log with an increased log level. You can +find information on how to do just that in the +[docs](http://docs.octoprint.org/en/master/configuration/logging_yaml.html). + +### Where can I find my browser's error console? + +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) + +## Pull requests + +1. If you want to add a new feature to OctoPrint, **please always first + consider if it wouldn't be better suited for a plugin.** As a general rule + of thumb, any feature that is only of interest to a small sub group should + be moved into a plugin. If the current plugin system doesn't allow you to + implement your feature as a plugin, create a "Brainstorming" ticket to get + the discussion going on how best to solve *this* in OctoPrint's plugin + system - maybe that's the actual PR you have been waiting for to contribute :) +2. If you plan to make **any large changes to the code or appearance, please + open a "Brainstorming" ticket first** so that we can determine if it's a + good time for your specific pull request. It might be that we're currently + in the process of making heavy changes to the code locations you'd target + as well, or your approach doesn't fit the general "project vision", and + that would just cause unnecessary work and frustration for everyone or + possibly get the PR rejected. +3. Create your pull request **from a custom branch** on your end (e.g. + `dev/myNewFeature`)[1] **against the `devel` branch**. Create **one pull request + per feature/bug fix**. If your PR contains an important bug fix, we will + make sure to backport it to the `maintenance` branch to also include it in + the next release. +4. Make sure you **follow the current coding style**. This means: + * Tabs instead of spaces in the Python files[2] + * Spaces instead of tabs in the Javascript sources + * English language (code, variables, comments, ...) + * Comments where necessary: Tell why the code does something like it does + it, structure your code + * Following the general architecture + If your PR needs to make changes to the Stylesheets, change the ``.less`` files + from which the CSS is compiled. +5. **Test your changes thoroughly**. That also means testing with usage + scenarios you don't normally use, e.g. if you only use access control, test + without and vice versa. If you only test with your printer, test with the + virtual printer and vice versa. State in your pull request how your tested + your changes. Ideally **add unit tests** - OctoPrint severly lacks in that + department, but we are trying to change that, so any new code already covered + with a test suite helps a lot! +6. In your pull request's description, **state what your pull request is doing**, + as in, what feature does it implement, what bug does it fix. The more + thoroughly you explain your intent behind the PR here, the higher the + chances it will get merged fast. +7. Important: Don't forget to **add yourself to the [AUTHORS](./AUTHORS.md) + file** :) + +## History + + * 2015-01-23: More guidelines for creating pull requests, support/questions + redirected to Mailinglist/G+ community * 2015-01-27: Added another explicit link to the FAQ * 2015-07-07: Added step to add yourself to AUTHORS when creating a PR :) + * 2015-12-01: Heavily reworked to include examples, better structure and + all information in one document. + +## Footnotes + * [1] - If you are wondering why, the problem is that anything that you add + to your PR's branch will also become part of your PR, so if you create a + PR from your version of `devel` chances are high you'll add changes to the + PR that do not belong to the PR. + * [2] - Yes, we know that this goes against PEP-8. OctoPrint started out as + a fork of Cura and hence stuck to the coding style found therein. Changing + it now would make the history and especially `git blame` completely + unusable, so for now we'll have to deal with it (this decision might be + revisited in the future). diff --git a/docs/api/printer.rst b/docs/api/printer.rst index 48072249..1184965f 100644 --- a/docs/api/printer.rst +++ b/docs/api/printer.rst @@ -25,7 +25,8 @@ Tool See :ref:`sec-api-printer-toolcommand`. Bed Bed commands allow setting the temperature and temperature offset for the printer's heated bed. Querying the - corresponding resource returns temperature information including an optional history. + corresponding resource returns temperature information including an optional history. Note that Bed commands + are only available if the currently selected printer profile has a heated bed. See :ref:`sec-api-printer-bedcommand`. SD card SD commands allow initialization, refresh and release of the printer's SD card (if available). Querying the @@ -564,6 +565,9 @@ Issue a bed command Upon success, a status code of :http:statuscode:`204` and an empty body is returned. + If no heated bed is configured for the currently selected printer profile, the resource will return + an :http:statuscode:`409`. + **Example Target Temperature Request** Set the target temperature for the printer's heated bed to 75°C. @@ -610,7 +614,8 @@ Issue a bed command :statuscode 204: No error :statuscode 400: If ``target`` or ``offset`` is not a valid number or outside of the supported range, or if the request is otherwise invalid. - :statuscode 409: If the printer is not operational. + :statuscode 409: If the printer is not operational or the selected printer profile + does not have a heated bed. .. _sec-api-printer-bedstate: @@ -627,6 +632,9 @@ Retrieve the current bed state Returns a :http:statuscode:`200` with a Temperature Response in the body upon success. + If no heated bed is configured for the currently selected printer profile, the resource will return + an :http:statuscode:`409`. + .. note:: If you want both tool and bed temperature information at the same time, take a look at :ref:`Retrieve the current printer state `. @@ -675,7 +683,8 @@ Retrieve the current bed state :query limit: If set to an integer (``n``), only the last ``n`` data points from the printer's temperature history will be returned. Will be ignored if ``history`` is not enabled. :statuscode 200: No error - :statuscode 409: If the printer is not operational. + :statuscode 409: If the printer is not operational or the selected printer profile + does not have a heated bed. .. _sec-api-printer-sdcommand: @@ -856,7 +865,7 @@ Send an arbitrary command to the printer .. sourcecode:: http HTTP/1.1 204 No Content - + :json string command: Single command to send to the printer, mutually exclusive with ``commands``. :json string commands: List of commands to send to the printer, mutually exclusive with ``command``. :statuscode 204: No error @@ -913,7 +922,8 @@ Temperature State * - ``bed`` - 0..1 - :ref:`Temperature Data ` - - Current temperature stats for the printer's heated bed. Not included if querying only tool state. + - Current temperature stats for the printer's heated bed. Not included if querying only tool state or if + the currently selected printer profile does not have a heated bed. * - ``history`` - 0..1 - List of :ref:`Historic Temperature Datapoint ` diff --git a/docs/features/gcode_scripts.rst b/docs/features/gcode_scripts.rst index 011e15c3..d1b5fde8 100644 --- a/docs/features/gcode_scripts.rst +++ b/docs/features/gcode_scripts.rst @@ -86,7 +86,7 @@ Out of the box, OctoPrint defaults to the following script setup for ``afterPrin ;disable all heaters {% snippet 'disable_hotends' %} - M140 S0 + [% snippet 'disable_bed' %} ;disable fan M106 S0 @@ -100,8 +100,19 @@ The ``disable_hotends`` snippet is defined as follows: M104 T{{ tool }} S0 {% endfor %} -As you can see, the ``disable_hotends`` snippet utilizes the ``printer_profile`` context variable in order to -iterate through all available extruders and set their temperature to 0. +The ``disable_bed`` snippet is defined as follows: + +.. code-block:: jinja + :caption: Default ``disable_bed`` snippet + + {% if printer_profile.heatedBed %} + M140 S0 + {% endif %} + +As you can see, the ``disable_hotends`` and ``disable_bed`` snippets utilize the +``printer_profile`` context variable in order to iterate through all available +extruders and set their temperature to 0, and to also set the bed temperature +to 0 if a heated bed is configured. .. seealso:: diff --git a/src/octoprint/server/api/printer.py b/src/octoprint/server/api/printer.py index f544b833..d22aefff 100644 --- a/src/octoprint/server/api/printer.py +++ b/src/octoprint/server/api/printer.py @@ -10,10 +10,9 @@ from werkzeug.exceptions import BadRequest import re from octoprint.settings import settings, valid_boolean_trues -from octoprint.server import printer, NO_CONTENT +from octoprint.server import printer, printerProfileManager, NO_CONTENT from octoprint.server.api import api from octoprint.server.util.flask import restricted_access, get_json_command_from_request -import octoprint.util as util from octoprint.printer import UnknownScript @@ -34,9 +33,13 @@ def printerState(): result = {} + processor = lambda x: x + if not printerProfileManager.get_current_or_default()["heatedBed"]: + processor = _delete_bed + # add temperature information if not "temperature" in excludes: - result.update({"temperature": _getTemperatureData(lambda x: x)}) + result.update({"temperature": _get_temperature_data(processor)}) # add sd information if not "sd" in excludes and settings().getBoolean(["feature", "sdSupport"]): @@ -145,14 +148,7 @@ def printerToolState(): if not printer.is_operational(): return make_response("Printer is not operational", 409) - def deleteBed(x): - data = dict(x) - - if "bed" in data.keys(): - del data["bed"] - return data - - return jsonify(_getTemperatureData(deleteBed)) + return jsonify(_get_temperature_data(_delete_bed)) ##~~ Heated bed @@ -164,6 +160,9 @@ def printerBedCommand(): if not printer.is_operational(): return make_response("Printer is not operational", 409) + if not printerProfileManager.get_current_or_default()["heatedBed"]: + return make_response("Printer does not have a heated bed", 409) + valid_commands = { "target": ["target"], "offset": ["offset"] @@ -204,15 +203,10 @@ def printerBedState(): if not printer.is_operational(): return make_response("Printer is not operational", 409) - def deleteTools(x): - data = dict(x) + if not printerProfileManager.get_current_or_default()["heatedBed"]: + return make_response("Printer does not have a heated bed", 409) - for k in data.keys(): - if k.startswith("tool"): - del data[k] - return data - - data = _getTemperatureData(deleteTools) + data = _get_temperature_data(_delete_tools) if isinstance(data, Response): return data else: @@ -382,7 +376,7 @@ def getCustomControls(): return jsonify(controls=customControls) -def _getTemperatureData(filter): +def _get_temperature_data(preprocessor): if not printer.is_operational(): return make_response("Printer is not operational", 409) @@ -399,8 +393,23 @@ def _getTemperatureData(filter): limit = min(limit, len(history)) tempData.update({ - "history": map(lambda x: filter(x), history[-limit:]) + "history": map(lambda x: preprocessor(x), history[-limit:]) }) - return filter(tempData) + return preprocessor(tempData) + +def _delete_tools(x): + return _delete_from_data(x, lambda k: k.startswith("tool")) + + +def _delete_bed(x): + return _delete_from_data(x, lambda k: k == "bed") + + +def _delete_from_data(x, key_matcher): + data = dict(x) + for k in data.keys(): + if key_matcher(k): + del data[k] + return data diff --git a/src/octoprint/server/util/watchdog.py b/src/octoprint/server/util/watchdog.py index 8bda659b..79a1cef5 100644 --- a/src/octoprint/server/util/watchdog.py +++ b/src/octoprint/server/util/watchdog.py @@ -6,6 +6,7 @@ __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agp __copyright__ = "Copyright (C) 2014 The OctoPrint Project - Released under terms of the AGPLv3 License" import logging +import os import watchdog.events import octoprint.filemanager @@ -14,6 +15,7 @@ import octoprint.util class GcodeWatchdogHandler(watchdog.events.PatternMatchingEventHandler): + """ Takes care of automatically "uploading" files that get added to the watched folder. """ @@ -27,40 +29,42 @@ class GcodeWatchdogHandler(watchdog.events.PatternMatchingEventHandler): self._printer = printer def _upload(self, path): - import os - file_wrapper = octoprint.filemanager.util.DiskFileWrapper(os.path.basename(path), path) - - # determine current job - currentFilename = None - currentOrigin = None - currentJob = self._printer.get_current_job() - if currentJob is not None and "file" in currentJob.keys(): - currentJobFile = currentJob["file"] - if "name" in currentJobFile.keys() and "origin" in currentJobFile.keys(): - currentFilename = currentJobFile["name"] - currentOrigin = currentJobFile["origin"] - - # determine future filename of file to be uploaded, abort if it can't be uploaded try: - futureFilename = self._file_manager.sanitize_name(octoprint.filemanager.FileDestinations.LOCAL, file_wrapper.filename) - except: - futureFilename = None - if futureFilename is None or (len(self._file_manager.registered_slicers) == 0 and not octoprint.filemanager.valid_file_type(futureFilename)): - return + file_wrapper = octoprint.filemanager.util.DiskFileWrapper(os.path.basename(path), path) - # prohibit overwriting currently selected file while it's being printed - if futureFilename == currentFilename and currentOrigin == octoprint.filemanager.FileDestinations.LOCAL and self._printer.is_printing() or self._printer.is_paused(): - return + # determine current job + currentFilename = None + currentOrigin = None + currentJob = self._printer.get_current_job() + if currentJob is not None and "file" in currentJob.keys(): + currentJobFile = currentJob["file"] + if "name" in currentJobFile.keys() and "origin" in currentJobFile.keys(): + currentFilename = currentJobFile["name"] + currentOrigin = currentJobFile["origin"] - self._file_manager.add_file(octoprint.filemanager.FileDestinations.LOCAL, - file_wrapper.filename, - file_wrapper, - allow_overwrite=True) - if os.path.exists(path): + # determine future filename of file to be uploaded, abort if it can't be uploaded try: - os.remove(path) + futureFilename = self._file_manager.sanitize_name(octoprint.filemanager.FileDestinations.LOCAL, file_wrapper.filename) except: - self._logger.exception("Error while trying to clear a file from the watched folder") + futureFilename = None + if futureFilename is None or (len(self._file_manager.registered_slicers) == 0 and not octoprint.filemanager.valid_file_type(futureFilename)): + return + + # prohibit overwriting currently selected file while it's being printed + if futureFilename == currentFilename and currentOrigin == octoprint.filemanager.FileDestinations.LOCAL and self._printer.is_printing() or self._printer.is_paused(): + return + + self._file_manager.add_file(octoprint.filemanager.FileDestinations.LOCAL, + file_wrapper.filename, + file_wrapper, + allow_overwrite=True) + if os.path.exists(path): + try: + os.remove(path) + except: + self._logger.exception("Error while trying to clear a file from the watched folder") + except: + self._logger.exception("There was an error while processing the file {} in the watched folder".format(path)) def on_created(self, event): self._upload(event.src_path) diff --git a/src/octoprint/settings.py b/src/octoprint/settings.py index b669ff55..a34319f5 100644 --- a/src/octoprint/settings.py +++ b/src/octoprint/settings.py @@ -257,9 +257,10 @@ default_settings = { }, "scripts": { "gcode": { - "afterPrintCancelled": "; disable motors\nM84\n\n;disable all heaters\n{% snippet 'disable_hotends' %}\nM140 S0\n\n;disable fan\nM106 S0", + "afterPrintCancelled": "; disable motors\nM84\n\n;disable all heaters\n{% snippet 'disable_hotends' %}\n{% snippet 'disable_bed' %}\n;disable fan\nM106 S0", "snippets": { - "disable_hotends": "{% for tool in range(printer_profile.extruder.count) %}M104 T{{ tool }} S0\n{% endfor %}" + "disable_hotends": "{% for tool in range(printer_profile.extruder.count) %}M104 T{{ tool }} S0\n{% endfor %}", + "disable_bed": "{% if printer_profile.heatedBed %}M140 S0\n{% endif %}" } } }, diff --git a/src/octoprint/static/js/app/dataupdater.js b/src/octoprint/static/js/app/dataupdater.js index d46ce715..4caf44ac 100644 --- a/src/octoprint/static/js/app/dataupdater.js +++ b/src/octoprint/static/js/app/dataupdater.js @@ -250,7 +250,6 @@ function DataUpdater(allViewModels) { text: _.sprintf(gettext("Streamed %(local)s to %(remote)s on SD, took %(time).2f seconds"), payload), type: "success" }); - gcodeFilesViewModel.requestData(payload.remote, "sdcard"); } var legacyEventHandlers = { diff --git a/src/octoprint/static/js/app/viewmodels/files.js b/src/octoprint/static/js/app/viewmodels/files.js index 885f5a40..ae510fee 100644 --- a/src/octoprint/static/js/app/viewmodels/files.js +++ b/src/octoprint/static/js/app/viewmodels/files.js @@ -600,6 +600,10 @@ $(function() { self.onEventMetadataStatisticsUpdated = function(payload) { self.requestData(); }; + + self.onEventTransferDone = function(payload) { + self.requestData(payload.remote, "sdcard"); + }; } OCTOPRINT_VIEWMODELS.push([ diff --git a/src/octoprint/static/js/app/viewmodels/temperature.js b/src/octoprint/static/js/app/viewmodels/temperature.js index 9b58d2ec..e05cfd6a 100644 --- a/src/octoprint/static/js/app/viewmodels/temperature.js +++ b/src/octoprint/static/js/app/viewmodels/temperature.js @@ -36,7 +36,7 @@ $(function() { self.heaterOptions = ko.observable({}); - self._numExtrudersUpdated = function() { + self._printerProfileUpdated = function() { var graphColors = ["red", "orange", "green", "brown", "purple"]; var heaterOptions = {}; var tools = self.tools(); @@ -69,15 +69,21 @@ $(function() { } // print bed - heaterOptions["bed"] = {name: gettext("Bed"), color: "blue"}; + if (self.settingsViewModel.printerProfiles.currentProfileData().heatedBed()) { + self.hasBed(true); + heaterOptions["bed"] = {name: gettext("Bed"), color: "blue"}; + } else { + self.hasBed(false); + } // write back self.heaterOptions(heaterOptions); self.tools(tools); }; self.settingsViewModel.printerProfiles.currentProfileData.subscribe(function() { - self._numExtrudersUpdated(); - self.settingsViewModel.printerProfiles.currentProfileData().extruder.count.subscribe(self._numExtrudersUpdated); + self._printerProfileUpdated(); + self.settingsViewModel.printerProfiles.currentProfileData().extruder.count.subscribe(self._printerProfileUpdated); + self.settingsViewModel.printerProfiles.currentProfileData().heatedBed.subscribe(self._printerProfileUpdated()); }); self.temperatures = []; @@ -152,11 +158,8 @@ $(function() { } if (lastData.hasOwnProperty("bed")) { - self.hasBed(true); self.bedTemp["actual"](lastData.bed.actual); self.bedTemp["target"](lastData.bed.target); - } else { - self.hasBed(false); } if (!CONFIG_TEMPERATURE_GRAPH) return; @@ -208,8 +211,6 @@ $(function() { if (!d[type]) return; result[type].actual.push([time, d[type].actual]); result[type].target.push([time, d[type].target]); - - self.hasBed(self.hasBed() || (type == "bed")); }) }); @@ -375,4 +376,4 @@ $(function() { ["loginStateViewModel", "settingsViewModel"], "#temp" ]); -}); \ No newline at end of file +}); diff --git a/src/octoprint/static/js/app/viewmodels/terminal.js b/src/octoprint/static/js/app/viewmodels/terminal.js index d7ec494d..7be593c6 100644 --- a/src/octoprint/static/js/app/viewmodels/terminal.js +++ b/src/octoprint/static/js/app/viewmodels/terminal.js @@ -7,6 +7,7 @@ $(function() { self.log = ko.observableArray([]); self.buffer = ko.observable(300); + self.upperLimit = ko.observable(3000); self.command = ko.observable(undefined); @@ -52,13 +53,21 @@ $(function() { self.lineCount = ko.computed(function() { var total = self.log().length; + var filtered = _.filter(self.displayedLines(), function(entry) { return entry.type == "filtered" }).length; var displayed = _.filter(self.displayedLines(), function(entry) { return entry.type == "line" }).length; - var filtered = total - displayed; - if (total == displayed) { - return _.sprintf(gettext("showing %(displayed)d lines"), {displayed: displayed}); + if (filtered > 0) { + if (total > self.upperLimit()) { + return _.sprintf(gettext("showing %(displayed)d lines (%(filtered)d of %(total)d total lines filtered, buffer full)"), {displayed: displayed, total: total, filtered: filtered}); + } else { + return _.sprintf(gettext("showing %(displayed)d lines (%(filtered)d of %(total)d total lines filtered)"), {displayed: displayed, total: total, filtered: filtered}); + } } else { - return _.sprintf(gettext("showing %(displayed)d lines (%(filtered)d of %(total)d total lines filtered)"), {displayed: displayed, total: total, filtered: filtered}); + if (total > self.upperLimit()) { + return _.sprintf(gettext("showing %(displayed)d lines (buffer full)"), {displayed: displayed}); + } else { + return _.sprintf(gettext("showing %(displayed)d lines"), {displayed: displayed}); + } } }); @@ -84,10 +93,25 @@ $(function() { }; self._processCurrentLogData = function(data) { - self.log(self.log().concat(_.map(data, function(line) { return self._toInternalFormat(line) }))); - if (self.autoscrollEnabled()) { - self.log(self.log.slice(-self.buffer())); + var length = self.log().length; + if (length >= self.upperLimit()) { + var cutoff = "--- too many lines to buffer, cut off ---"; + var last = self.log()[length-1]; + if (!last || last.type != "cut" || last.line != cutoff) { + self.log(self.log().concat(self._toInternalFormat(cutoff, "cut"))); + } + return; } + + var newLog = self.log().concat(_.map(data, function(line) { return self._toInternalFormat(line) })); + if (self.autoscrollEnabled()) { + // we only keep the last entries + newLog = newLog.slice(-self.buffer()); + } else if (newLog.length > self.upperLimit()) { + // we only keep the first entries + newLog = newLog.slice(0, self.upperLimit()); + } + self.log(newLog); }; self._processHistoryLogData = function(data) { @@ -232,4 +256,4 @@ $(function() { ["loginStateViewModel", "settingsViewModel"], "#term" ]); -}); \ No newline at end of file +}); diff --git a/src/octoprint/templates/tabs/terminal.jinja2 b/src/octoprint/templates/tabs/terminal.jinja2 index 18d2a30a..4389d725 100644 --- a/src/octoprint/templates/tabs/terminal.jinja2 +++ b/src/octoprint/templates/tabs/terminal.jinja2 @@ -1,5 +1,5 @@
-

+

{{ _("Scroll to end") }} | {{ _("Select all") }}
diff --git a/src/octoprint/util/comm.py b/src/octoprint/util/comm.py index 9023a006..e9618a6a 100644 --- a/src/octoprint/util/comm.py +++ b/src/octoprint/util/comm.py @@ -1729,6 +1729,12 @@ class MachineCom(object): if self.isPrinting() and not self.isSdPrinting(): self.setPause(True) + def _gcode_M140_queuing(self, cmd, cmd_type=None): + if not self._printerProfileManager.get_current_or_default()["heatedBed"]: + self._log("Warn: Not sending \"{}\", printer profile has no heated bed".format(cmd)) + return None, # Don't send bed commands if we don't have a heated bed + _gcode_M190_queuing = _gcode_M140_queuing + def _gcode_M104_sent(self, cmd, cmd_type=None): toolNum = self._currentTool toolMatch = regexes_parameters["intT"].search(cmd) @@ -1802,7 +1808,8 @@ class MachineCom(object): # are going to shutdown the connection in a second anyhow. for tool in range(self._printerProfileManager.get_current_or_default()["extruder"]["count"]): self._doIncrementAndSendWithChecksum("M104 T{tool} S0".format(tool=tool)) - self._doIncrementAndSendWithChecksum("M140 S0") + if self._printerProfileManager.get_current_or_default()["heatedBed"]: + self._doIncrementAndSendWithChecksum("M140 S0") # close to reset host state self._errorValue = "Closing serial port due to emergency stop M112."