From b714c59004716daa513a1de043cb2273a669e855 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Wed, 3 Jun 2015 12:34:00 +0200 Subject: [PATCH] Allow specification of known long running commands through settings This should allow users to adjust the behaviour of the communication stack more granularly in case the regular settings which only consider G4, G28, G29, G30, G32 to be real long runners without output are not sufficient. --- CHANGELOG.md | 3 ++- src/octoprint/server/api/settings.py | 4 ++- src/octoprint/settings.py | 3 ++- src/octoprint/static/js/app/helpers.js | 14 ++++++++++ .../static/js/app/viewmodels/settings.js | 11 +++----- .../dialogs/settings/serialconnection.jinja2 | 7 +++++ src/octoprint/util/comm.py | 27 +++++++++---------- 7 files changed, 45 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f09da2c8..da896aa0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -153,7 +153,8 @@ ``python setup.py sdist`` * [#330](https://github.com/foosel/OctoPrint/issues/330) - Ping pong sending to fix potential acknowledgement errors. Also affects [#166](https://github.com/foosel/OctoPrint/issues/166), [#470](https://github.com/foosel/OctoPrint/issues/470) - and [#490](https://github.com/foosel/OctoPrint/issues/490). + and [#490](https://github.com/foosel/OctoPrint/issues/490). A big thank you to all people involved in these tickets + in getting to the ground of this. * [#825](https://github.com/foosel/OctoPrint/issues/825) - Fixed "please visualize" button of large GCODE files * Various fixes of bugs in newly introduced features and improvements: * [#625](https://github.com/foosel/OctoPrint/pull/625) - Newly added GCODE files were not being added to the analysis diff --git a/src/octoprint/server/api/settings.py b/src/octoprint/server/api/settings.py index e5ffe39d..7bba8a02 100644 --- a/src/octoprint/server/api/settings.py +++ b/src/octoprint/server/api/settings.py @@ -80,7 +80,8 @@ def getSettings(): "timeoutTemperature": s.getFloat(["serial", "timeout", "temperature"]), "timeoutSdStatus": s.getFloat(["serial", "timeout", "sdStatus"]), "log": s.getBoolean(["serial", "log"]), - "additionalPorts": s.get(["serial", "additionalPorts"]) + "additionalPorts": s.get(["serial", "additionalPorts"]), + "longRunningCommands": s.get(["serial", "longRunningCommands"]) }, "folder": { "uploads": s.getBaseFolder("uploads"), @@ -192,6 +193,7 @@ def setSettings(): if "timeoutTemperature" in data["serial"].keys(): s.setFloat(["serial", "timeout", "temperature"], data["serial"]["timeoutTemperature"]) 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"]) 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 954b01e5..6d977027 100644 --- a/src/octoprint/settings.py +++ b/src/octoprint/settings.py @@ -82,7 +82,8 @@ default_settings = { "temperature": 5, "sdStatus": 1 }, - "additionalPorts": [] + "additionalPorts": [], + "longRunningCommands": ["G4", "G28", "G29", "G30", "G32"] }, "server": { "host": "0.0.0.0", diff --git a/src/octoprint/static/js/app/helpers.js b/src/octoprint/static/js/app/helpers.js index b83f315e..e64ee9a1 100644 --- a/src/octoprint/static/js/app/helpers.js +++ b/src/octoprint/static/js/app/helpers.js @@ -447,3 +447,17 @@ function showConfirmationDialog(message, onacknowledge) { }); confirmationDialog.modal("show"); } + +function commentableLinesToArray(lines) { + return splitTextToArray(lines, "\n", true, function(item) {return !_.startsWith(item, "#")}); +} + +function splitTextToArray(text, sep, stripEmpty, filter) { + return _.filter( + _.map( + text.split(sep), + function(item) { return (item) ? item.trim() : ""; } + ), + function(item) { return (stripEmpty ? item : true) && (filter ? filter(item) : true); } + ); +} diff --git a/src/octoprint/static/js/app/viewmodels/settings.js b/src/octoprint/static/js/app/viewmodels/settings.js index 53f924a4..257548a3 100644 --- a/src/octoprint/static/js/app/viewmodels/settings.js +++ b/src/octoprint/static/js/app/viewmodels/settings.js @@ -126,6 +126,7 @@ $(function() { self.serial_timeoutSdStatus = ko.observable(undefined); self.serial_log = ko.observable(undefined); self.serial_additionalPorts = ko.observable(undefined); + self.serial_longRunningCommands = ko.observable(undefined); self.folder_uploads = ko.observable(undefined); self.folder_timelapse = ko.observable(undefined); @@ -385,6 +386,7 @@ $(function() { self.serial_timeoutSdStatus(response.serial.timeoutSdStatus); self.serial_log(response.serial.log); self.serial_additionalPorts(response.serial.additionalPorts.join("\n")); + self.serial_longRunningCommands(response.serial.longRunningCommands.join(", ")); self.folder_uploads(response.folder.uploads); self.folder_timelapse(response.folder.timelapse); @@ -462,13 +464,8 @@ $(function() { "timeoutTemperature": self.serial_timeoutTemperature(), "timeoutSdStatus": self.serial_timeoutSdStatus(), "log": self.serial_log(), - "additionalPorts": _.filter( - _.map( - self.serial_additionalPorts().split("\n"), - function(item) { return (item) ? item.trim() : ""; } - ), - function(item) { return item && !_.startsWith(item, "#"); } - ) + "additionalPorts": commentableLinesToArray(self.serial_additionalPorts()), + "longRunningCommands": splitTextToArray(self.serial_longRunningCommands(), ",", true) }, "folder": { "uploads": self.folder_uploads(), diff --git a/src/octoprint/templates/dialogs/settings/serialconnection.jinja2 b/src/octoprint/templates/dialogs/settings/serialconnection.jinja2 index aacca84b..11c3b559 100644 --- a/src/octoprint/templates/dialogs/settings/serialconnection.jinja2 +++ b/src/octoprint/templates/dialogs/settings/serialconnection.jinja2 @@ -70,6 +70,13 @@ +
+ +
+ + {{ _('Use this to specify the commands known to take a long time to complete without output from your printer and hence might cause timeout issues. Just the G or M code, comma separated.')|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 2f8e0813..04fda534 100644 --- a/src/octoprint/util/comm.py +++ b/src/octoprint/util/comm.py @@ -149,7 +149,7 @@ class MachineCom(object): self._pauseWaitTimeLost = 0.0 self._currentTool = 0 - self._blocking_command = False + self._long_running_command = False self._heating = False self._timeout = None @@ -218,7 +218,7 @@ class MachineCom(object): self._regex_repetierTempExtr = re.compile("TargetExtr([0-9]+):(%s)" % positiveFloatPattern) self._regex_repetierTempBed = re.compile("TargetBed:(%s)" % positiveFloatPattern) - self._blocking_commands = ("G28", "G29", "G30", "G32") + self._long_running_commands = settings().get(["serial", "longRunningCommands"]) # multithreading locks self._sendNextLock = threading.Lock() @@ -883,7 +883,7 @@ class MachineCom(object): ##~~ process oks if line.strip().startswith("ok") or (self.isPrinting() and supportWait and line.strip().startswith("wait")): self._clear_to_send.set() - self._blocking_command = False + self._long_running_command = False ##~~ Temperature processing if ' T:' in line or line.startswith('T:') or ' T0:' in line or line.startswith('T0:') or ' B:' in line or line.startswith('B:'): @@ -1088,12 +1088,12 @@ class MachineCom(object): ### Printing elif self._state == self.STATE_PRINTING: if line == "" and time.time() > self._timeout: - if not self._blocking_command: + if not self._long_running_command: self._log("Communication timeout during printing, forcing a line") self._sendCommand("M105") self._clear_to_send.set() else: - self._logger.debug("Ran into a communication timeout, but a blocking command is currently active") + self._logger.debug("Ran into a communication timeout, but a command known to be a long runner is currently active") if "ok" in line or (supportWait and "wait" in line): # a wait while printing means our printer's buffer ran out, probably due to some ok getting @@ -1161,22 +1161,22 @@ class MachineCom(object): """ Polls the temperature after the temperature timeout, re-enqueues itself. - If the printer is not operational, not printing from sd, busy with a blocking command or heating, no poll + If the printer is not operational, not printing from sd, busy with a long running command or heating, no poll will be done. """ - if self.isOperational() and not self.isStreaming() and not self._blocking_command and not self._heating: + if self.isOperational() and not self.isStreaming() and not self._long_running_command and not self._heating: self.sendCommand("M105", cmd_type="temperature_poll") def _poll_sd_status(self): """ Polls the sd printing status after the sd status timeout, re-enqueues itself. - If the printer is not operational, not printing from sd, busy with a blocking command or heating, no poll + If the printer is not operational, not printing from sd, busy with a long running command or heating, no poll will be done. """ - if self.isOperational() and self.isSdPrinting() and not self._blocking_command and not self._heating: + if self.isOperational() and self.isSdPrinting() and not self._long_running_command and not self._heating: self.sendCommand("M27", cmd_type="sd_status_poll") def _onConnected(self): @@ -1693,13 +1693,13 @@ class MachineCom(object): def _gcode_M109_sent(self, cmd, cmd_type=None): self._heatupWaitStartTime = time.time() - self._blocking_command = True + self._long_running_command = True self._heating = True self._gcode_M104_sent(cmd, cmd_type) def _gcode_M190_sent(self, cmd, cmd_type=None): self._heatupWaitStartTime = time.time() - self._blocking_command = True + self._long_running_command = True self._heating = True self._gcode_M140_sent(cmd, cmd_type) @@ -1737,13 +1737,12 @@ class MachineCom(object): # dwell time is specified in seconds _timeout = float(cmd[s_idx+1:]) self._timeout = get_new_timeout("communication") + _timeout - self._blocking_command = True ##~~ command phase handlers def _command_phase_sending(self, cmd, cmd_type=None, gcode=None): - if gcode is not None and gcode in self._blocking_commands: - self._blocking_command = True + if gcode is not None and gcode in self._long_running_commands: + self._long_running_command = True ### MachineCom callback ################################################################################################