From 0e817d43cc7187503382e0e3c9d4fa8633997505 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Mon, 1 Feb 2016 16:55:00 +0100 Subject: [PATCH 1/2] Added option to ignore any errors reported by the firmware Use this carefully. An error reported by your firmware usually hints at something being off about your printer (e.g. thermistor troubles) or the firmware not being fully compatible to OctoPrint since it doesn't recognize a lot of commands. If you print with this option enabled, it will still fail if you firmware goes into lockdown, but now it will keep trying to send data to your printer. That might cause issues. So best not use that option, only if you absolutely have no other way to get some misbehaving printer to work with OctoPrint. --- src/octoprint/plugins/virtual_printer/virtual.py | 2 ++ src/octoprint/server/api/settings.py | 4 +++- src/octoprint/settings.py | 3 ++- src/octoprint/static/js/app/viewmodels/settings.js | 5 ++++- .../dialogs/settings/serialconnection.jinja2 | 7 +++++++ src/octoprint/util/comm.py | 12 +++++++++--- 6 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/octoprint/plugins/virtual_printer/virtual.py b/src/octoprint/plugins/virtual_printer/virtual.py index 6a186d67..dff4f6d4 100644 --- a/src/octoprint/plugins/virtual_printer/virtual.py +++ b/src/octoprint/plugins/virtual_printer/virtual.py @@ -338,6 +338,8 @@ class VirtualPrinter(): self._triggerResend(expected=self.lastN) elif data == "drop_connection": self._debug_drop_connection = True + elif data == "maxtemp_error": + self.outgoing.put("Error: MAXTEMP triggered!") else: try: sleep_match = VirtualPrinter.sleep_regex.match(data) diff --git a/src/octoprint/server/api/settings.py b/src/octoprint/server/api/settings.py index 161b3294..39767cda 100644 --- a/src/octoprint/server/api/settings.py +++ b/src/octoprint/server/api/settings.py @@ -85,7 +85,8 @@ def getSettings(): "timeoutSdStatus": s.getFloat(["serial", "timeout", "sdStatus"]), "log": s.getBoolean(["serial", "log"]), "additionalPorts": s.get(["serial", "additionalPorts"]), - "longRunningCommands": s.get(["serial", "longRunningCommands"]) + "longRunningCommands": s.get(["serial", "longRunningCommands"]), + "ignoreErrorsFromFirmware": s.getBoolean(["serial", "ignoreErrorsFromFirmware"]) }, "folder": { "uploads": s.getBaseFolder("uploads"), @@ -226,6 +227,7 @@ def setSettings(): if "timeoutSdStatus" in data["serial"].keys(): s.setFloat(["serial", "timeout", "sdStatus"], data["serial"]["timeoutSdStatus"]) if "additionalPorts" in data["serial"] and isinstance(data["serial"]["additionalPorts"], (list, tuple)): s.set(["serial", "additionalPorts"], data["serial"]["additionalPorts"]) if "longRunningCommands" in data["serial"] and isinstance(data["serial"]["longRunningCommands"], (list, tuple)): s.set(["serial", "longRunningCommands"], data["serial"]["longRunningCommands"]) + if "ignoreErrorsFromFirmware" in data["serial"]: s.setBoolean(["serial", "ignoreErrorsFromFirmware"], data["serial"]["ignoreErrorsFromFirmware"]) oldLog = s.getBoolean(["serial", "log"]) if "log" in data["serial"].keys(): s.setBoolean(["serial", "log"], data["serial"]["log"]) diff --git a/src/octoprint/settings.py b/src/octoprint/settings.py index c90572c0..d58833c8 100644 --- a/src/octoprint/settings.py +++ b/src/octoprint/settings.py @@ -85,7 +85,8 @@ default_settings = { "sdStatus": 1 }, "additionalPorts": [], - "longRunningCommands": ["G4", "G28", "G29", "G30", "G32"] + "longRunningCommands": ["G4", "G28", "G29", "G30", "G32"], + "ignoreErrorsFromFirmware": False }, "server": { "host": "0.0.0.0", diff --git a/src/octoprint/static/js/app/viewmodels/settings.js b/src/octoprint/static/js/app/viewmodels/settings.js index 9bc36897..167946b6 100644 --- a/src/octoprint/static/js/app/viewmodels/settings.js +++ b/src/octoprint/static/js/app/viewmodels/settings.js @@ -136,6 +136,7 @@ $(function() { self.serial_log = ko.observable(undefined); self.serial_additionalPorts = ko.observable(undefined); self.serial_longRunningCommands = ko.observable(undefined); + self.serial_ignoreErrorsFromFirmware = ko.observable(undefined); self.folder_uploads = ko.observable(undefined); self.folder_timelapse = ko.observable(undefined); @@ -450,6 +451,7 @@ $(function() { self.serial_log(response.serial.log); self.serial_additionalPorts(response.serial.additionalPorts.join("\n")); self.serial_longRunningCommands(response.serial.longRunningCommands.join(", ")); + self.serial_ignoreErrorsFromFirmware(response.serial.ignoreErrorsFromFirmware); self.folder_uploads(response.folder.uploads); self.folder_timelapse(response.folder.timelapse); @@ -536,7 +538,8 @@ $(function() { "timeoutSdStatus": self.serial_timeoutSdStatus(), "log": self.serial_log(), "additionalPorts": commentableLinesToArray(self.serial_additionalPorts()), - "longRunningCommands": splitTextToArray(self.serial_longRunningCommands(), ",", true) + "longRunningCommands": splitTextToArray(self.serial_longRunningCommands(), ",", true), + "ignoreErrorsFromFirmware": self.serial_ignoreErrorsFromFirmware() }, "folder": { "uploads": self.folder_uploads(), diff --git a/src/octoprint/templates/dialogs/settings/serialconnection.jinja2 b/src/octoprint/templates/dialogs/settings/serialconnection.jinja2 index 2f4e00d0..84f3494e 100644 --- a/src/octoprint/templates/dialogs/settings/serialconnection.jinja2 +++ b/src/octoprint/templates/dialogs/settings/serialconnection.jinja2 @@ -84,4 +84,11 @@ {{ _('Use this to define additional glob patterns matching serial ports to list for connecting against, e.g. /dev/ttyAMA*. One entry per line.')|format(glob_url="http://docs.python.org/2/library/glob.html") }} +
+
+ +
+
diff --git a/src/octoprint/util/comm.py b/src/octoprint/util/comm.py index 02f8a9f8..f1bae87c 100644 --- a/src/octoprint/util/comm.py +++ b/src/octoprint/util/comm.py @@ -242,6 +242,8 @@ class MachineCom(object): self._resendSwallowRepetitions = settings().getBoolean(["feature", "ignoreIdenticalResends"]) self._resendSwallowRepetitionsCounter = 0 + self._ignore_errors = settings().getBoolean(["serial", "ignoreErrorsFromFirmware"]) + self._clear_to_send = CountedEvent(max=10, name="comm.clear_to_send") self._send_queue = TypedQueue() self._temperature_timer = None @@ -1371,9 +1373,13 @@ class MachineCom(object): #Ignore unkown command errors, it could be a typo or some missing feature pass elif not self.isError(): - self._errorValue = line[6:] if line.startswith("Error:") else line[2:] - self._changeState(self.STATE_ERROR) - eventManager().fire(Events.ERROR, {"error": self.getErrorString()}) + error_text = line[6:] if line.startswith("Error:") else line[2:] + if not self._ignore_errors: + self._errorValue = error_text + self._changeState(self.STATE_ERROR) + eventManager().fire(Events.ERROR, {"error": self.getErrorString()}) + else: + self._log("WARNING! Received an error from the printer's firmware, ignoring that as configured but you might want to investigate what happened here! Error: {}".format(error_text)) return line def _readline(self): From 6583691e853c71c53ab5ebb7ca8255dcdbd1b873 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Mon, 1 Feb 2016 18:02:21 +0100 Subject: [PATCH 2/2] Added setting to only cancel ongoing print on firmware error Also added error popup in such a case so the error will be known even if the terminal tab scrolled past it. --- src/octoprint/server/api/settings.py | 4 +++- src/octoprint/settings.py | 1 + src/octoprint/static/js/app/dataupdater.js | 16 ++++++++++++++++ .../static/js/app/viewmodels/settings.js | 5 ++++- .../dialogs/settings/serialconnection.jinja2 | 9 ++++++++- src/octoprint/util/comm.py | 17 ++++++++++++----- 6 files changed, 44 insertions(+), 8 deletions(-) diff --git a/src/octoprint/server/api/settings.py b/src/octoprint/server/api/settings.py index 39767cda..e108452c 100644 --- a/src/octoprint/server/api/settings.py +++ b/src/octoprint/server/api/settings.py @@ -86,7 +86,8 @@ def getSettings(): "log": s.getBoolean(["serial", "log"]), "additionalPorts": s.get(["serial", "additionalPorts"]), "longRunningCommands": s.get(["serial", "longRunningCommands"]), - "ignoreErrorsFromFirmware": s.getBoolean(["serial", "ignoreErrorsFromFirmware"]) + "ignoreErrorsFromFirmware": s.getBoolean(["serial", "ignoreErrorsFromFirmware"]), + "disconnectOnErrors": s.getBoolean(["serial", "disconnectOnErrors"]), }, "folder": { "uploads": s.getBaseFolder("uploads"), @@ -228,6 +229,7 @@ def setSettings(): if "additionalPorts" in data["serial"] and isinstance(data["serial"]["additionalPorts"], (list, tuple)): s.set(["serial", "additionalPorts"], data["serial"]["additionalPorts"]) if "longRunningCommands" in data["serial"] and isinstance(data["serial"]["longRunningCommands"], (list, tuple)): s.set(["serial", "longRunningCommands"], data["serial"]["longRunningCommands"]) if "ignoreErrorsFromFirmware" in data["serial"]: s.setBoolean(["serial", "ignoreErrorsFromFirmware"], data["serial"]["ignoreErrorsFromFirmware"]) + if "disconnectOnErrors" in data["serial"]: s.setBoolean(["serial", "disconnectOnErrors"], data["serial"]["disconnectOnErrors"]) oldLog = s.getBoolean(["serial", "log"]) if "log" in data["serial"].keys(): s.setBoolean(["serial", "log"], data["serial"]["log"]) diff --git a/src/octoprint/settings.py b/src/octoprint/settings.py index d58833c8..c7252043 100644 --- a/src/octoprint/settings.py +++ b/src/octoprint/settings.py @@ -86,6 +86,7 @@ default_settings = { }, "additionalPorts": [], "longRunningCommands": ["G4", "G28", "G29", "G30", "G32"], + "disconnectOnErrors": True, "ignoreErrorsFromFirmware": False }, "server": { diff --git a/src/octoprint/static/js/app/dataupdater.js b/src/octoprint/static/js/app/dataupdater.js index 9bd04766..130b5c1c 100644 --- a/src/octoprint/static/js/app/dataupdater.js +++ b/src/octoprint/static/js/app/dataupdater.js @@ -289,6 +289,22 @@ function DataUpdater(allViewModels) { text: _.sprintf(gettext("Streamed %(local)s to %(remote)s on SD, took %(time).2f seconds"), payload), type: "success" }); + } else if (type == "PrintCancelled") { + if (payload.firmwareError) { + new PNotify({ + title: gettext("Unhandled firmware error"), + text: _.sprintf(gettext("The firmware reported an unhandled error. Due to that the ongoing print job was cancelled. Error: %(firmwareError)s"), payload), + type: "error", + hide: false + }); + } + } else if (type == "Error") { + new PNotify({ + title: gettext("Unhandled firmware error"), + text: _.sprintf(gettext("The firmware reported an unhandled error. Due to that OctoPrint disconnected. Error: %(error)s"), payload), + type: "error", + hide: false + }); } var legacyEventHandlers = { diff --git a/src/octoprint/static/js/app/viewmodels/settings.js b/src/octoprint/static/js/app/viewmodels/settings.js index 167946b6..304038bb 100644 --- a/src/octoprint/static/js/app/viewmodels/settings.js +++ b/src/octoprint/static/js/app/viewmodels/settings.js @@ -137,6 +137,7 @@ $(function() { self.serial_additionalPorts = ko.observable(undefined); self.serial_longRunningCommands = ko.observable(undefined); self.serial_ignoreErrorsFromFirmware = ko.observable(undefined); + self.serial_disconnectOnErrors = ko.observable(undefined); self.folder_uploads = ko.observable(undefined); self.folder_timelapse = ko.observable(undefined); @@ -452,6 +453,7 @@ $(function() { self.serial_additionalPorts(response.serial.additionalPorts.join("\n")); self.serial_longRunningCommands(response.serial.longRunningCommands.join(", ")); self.serial_ignoreErrorsFromFirmware(response.serial.ignoreErrorsFromFirmware); + self.serial_disconnectOnErrors(response.serial.disconnectOnErrors); self.folder_uploads(response.folder.uploads); self.folder_timelapse(response.folder.timelapse); @@ -539,7 +541,8 @@ $(function() { "log": self.serial_log(), "additionalPorts": commentableLinesToArray(self.serial_additionalPorts()), "longRunningCommands": splitTextToArray(self.serial_longRunningCommands(), ",", true), - "ignoreErrorsFromFirmware": self.serial_ignoreErrorsFromFirmware() + "ignoreErrorsFromFirmware": self.serial_ignoreErrorsFromFirmware(), + "disconnectOnErrors": self.serial_disconnectOnErrors() }, "folder": { "uploads": self.folder_uploads(), diff --git a/src/octoprint/templates/dialogs/settings/serialconnection.jinja2 b/src/octoprint/templates/dialogs/settings/serialconnection.jinja2 index 84f3494e..afa14445 100644 --- a/src/octoprint/templates/dialogs/settings/serialconnection.jinja2 +++ b/src/octoprint/templates/dialogs/settings/serialconnection.jinja2 @@ -87,7 +87,14 @@
+
+
+
+
+
diff --git a/src/octoprint/util/comm.py b/src/octoprint/util/comm.py index f1bae87c..5b9a1aab 100644 --- a/src/octoprint/util/comm.py +++ b/src/octoprint/util/comm.py @@ -242,6 +242,7 @@ class MachineCom(object): self._resendSwallowRepetitions = settings().getBoolean(["feature", "ignoreIdenticalResends"]) self._resendSwallowRepetitionsCounter = 0 + self._disconnect_on_errors = settings().getBoolean(["serial", "disconnectOnErrors"]) self._ignore_errors = settings().getBoolean(["serial", "ignoreErrorsFromFirmware"]) self._clear_to_send = CountedEvent(max=10, name="comm.clear_to_send") @@ -649,7 +650,7 @@ class MachineCom(object): eventManager().fire(Events.FILE_DESELECTED) self._callback.on_comm_file_selected(None, None, False) - def cancelPrint(self): + def cancelPrint(self, firmware_error=None): if not self.isOperational() or self.isStreaming(): return @@ -667,7 +668,8 @@ class MachineCom(object): payload = { "file": self._currentFile.getFilename(), "filename": os.path.basename(self._currentFile.getFilename()), - "origin": self._currentFile.getFileLocation() + "origin": self._currentFile.getFileLocation(), + "firmwareError": firmware_error } self.sendGcodeScript("afterPrintCancelled", replacements=dict(event=payload)) @@ -1375,11 +1377,16 @@ class MachineCom(object): elif not self.isError(): error_text = line[6:] if line.startswith("Error:") else line[2:] if not self._ignore_errors: - self._errorValue = error_text - self._changeState(self.STATE_ERROR) - eventManager().fire(Events.ERROR, {"error": self.getErrorString()}) + if self._disconnect_on_errors: + self._errorValue = error_text + self._changeState(self.STATE_ERROR) + eventManager().fire(Events.ERROR, {"error": self.getErrorString()}) + elif self.isPrinting(): + self.cancelPrint(firmware_error=error_text) + self._clear_to_send.set() else: self._log("WARNING! Received an error from the printer's firmware, ignoring that as configured but you might want to investigate what happened here! Error: {}".format(error_text)) + self._clear_to_send.set() return line def _readline(self):