From 1a4c06d6f63f1d527577029123cc7cc9e2a70792 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Fri, 18 Mar 2016 14:59:13 +0100 Subject: [PATCH 1/2] Added flag to trigger ok for resend requests That's necessary to keep communications going in case of a resend request for firmwares that do not send an ok after a resend request. --- src/octoprint/server/api/settings.py | 4 +- src/octoprint/settings.py | 3 +- .../static/js/app/viewmodels/settings.js | 5 +- .../dialogs/settings/serialconnection.jinja2 | 5 + src/octoprint/util/comm.py | 152 +++++++++--------- 5 files changed, 93 insertions(+), 76 deletions(-) diff --git a/src/octoprint/server/api/settings.py b/src/octoprint/server/api/settings.py index 2a7a29e2..61c58b6c 100644 --- a/src/octoprint/server/api/settings.py +++ b/src/octoprint/server/api/settings.py @@ -90,7 +90,8 @@ def getSettings(): "helloCommand": s.get(["serial", "helloCommand"]), "ignoreErrorsFromFirmware": s.getBoolean(["serial", "ignoreErrorsFromFirmware"]), "disconnectOnErrors": s.getBoolean(["serial", "disconnectOnErrors"]), - "triggerOkForM29": s.getBoolean(["serial", "triggerOkForM29"]) + "triggerOkForM29": s.getBoolean(["serial", "triggerOkForM29"]), + "supportResendsWithoutOk": s.getBoolean(["serial", "supportResendsWithoutOk"]) }, "folder": { "uploads": s.getBaseFolder("uploads"), @@ -236,6 +237,7 @@ def setSettings(): if "ignoreErrorsFromFirmware" in data["serial"]: s.setBoolean(["serial", "ignoreErrorsFromFirmware"], data["serial"]["ignoreErrorsFromFirmware"]) if "disconnectOnErrors" in data["serial"]: s.setBoolean(["serial", "disconnectOnErrors"], data["serial"]["disconnectOnErrors"]) if "triggerOkForM29" in data["serial"]: s.setBoolean(["serial", "triggerOkForM29"], data["serial"]["triggerOkForM29"]) + if "supportResendsWithoutOk" in data["serial"]: s.setBoolean(["serial", "supportResendsWithoutOk"], data["serial"]["supportResendsWithoutOk"]) 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 863dede0..2c14ce94 100644 --- a/src/octoprint/settings.py +++ b/src/octoprint/settings.py @@ -90,7 +90,8 @@ default_settings = { "helloCommand": "M110 N0", "disconnectOnErrors": True, "ignoreErrorsFromFirmware": False, - "logResends": False, + "logResends": True, + "supportResendsWithoutOk": False, # command specific flags "triggerOkForM29": True diff --git a/src/octoprint/static/js/app/viewmodels/settings.js b/src/octoprint/static/js/app/viewmodels/settings.js index 34fc874e..44ab11c8 100644 --- a/src/octoprint/static/js/app/viewmodels/settings.js +++ b/src/octoprint/static/js/app/viewmodels/settings.js @@ -141,6 +141,7 @@ $(function() { self.serial_ignoreErrorsFromFirmware = ko.observable(undefined); self.serial_disconnectOnErrors = ko.observable(undefined); self.serial_triggerOkForM29 = ko.observable(undefined); + self.serial_supportResendsWithoutOk = ko.observable(undefined); self.folder_uploads = ko.observable(undefined); self.folder_timelapse = ko.observable(undefined); @@ -460,6 +461,7 @@ $(function() { self.serial_ignoreErrorsFromFirmware(response.serial.ignoreErrorsFromFirmware); self.serial_disconnectOnErrors(response.serial.disconnectOnErrors); self.serial_triggerOkForM29(response.serial.triggerOkForM29); + self.serial_supportResendsWithoutOk(response.serial.supportResendsWithoutOk); self.folder_uploads(response.folder.uploads); self.folder_timelapse(response.folder.timelapse); @@ -551,7 +553,8 @@ $(function() { "helloCommand": self.serial_helloCommand(), "ignoreErrorsFromFirmware": self.serial_ignoreErrorsFromFirmware(), "disconnectOnErrors": self.serial_disconnectOnErrors(), - "triggerOkForM29": self.serial_triggerOkForM29() + "triggerOkForM29": self.serial_triggerOkForM29(), + "supportResendsWithoutOk": self.serial_supportResendsWithoutOk() }, "folder": { "uploads": self.folder_uploads(), diff --git a/src/octoprint/templates/dialogs/settings/serialconnection.jinja2 b/src/octoprint/templates/dialogs/settings/serialconnection.jinja2 index 799796f9..697188c1 100644 --- a/src/octoprint/templates/dialogs/settings/serialconnection.jinja2 +++ b/src/octoprint/templates/dialogs/settings/serialconnection.jinja2 @@ -121,6 +121,11 @@ {{ _('Generate additional ok for M29') }} {{ _('Most Marlin < v1.1.0') }} +
+ +
diff --git a/src/octoprint/util/comm.py b/src/octoprint/util/comm.py index 5c25a726..5d8d1c4a 100644 --- a/src/octoprint/util/comm.py +++ b/src/octoprint/util/comm.py @@ -249,10 +249,11 @@ class MachineCom(object): self._lastCommError = None self._lastResendNumber = None self._currentResendCount = 0 - self._resendSwallowNextOk = False self._resendSwallowRepetitions = settings().getBoolean(["feature", "ignoreIdenticalResends"]) self._resendSwallowRepetitionsCounter = 0 + self._supportResendsWithoutOk = settings().getBoolean(["serial", "supportResendsWithoutOk"]) + self._resendActive = False self._terminal_log = deque([], 20) @@ -262,11 +263,11 @@ class MachineCom(object): self._log_resends = settings().getBoolean(["serial", "logResends"]) - # don't log more resends than 5 / 10s + # don't log more resends than 5 / 60s self._log_resends_rate_start = None self._log_resends_rate_count = 0 self._log_resends_max = 5 - self._log_resends_rate_frame = 10 + self._log_resends_rate_frame = 60 self._long_running_commands = settings().get(["serial", "longRunningCommands"]) self._checksum_requiring_commands = settings().get(["serial", "checksumRequiringCommands"]) @@ -1590,84 +1591,89 @@ class MachineCom(object): self._callback.on_comm_progress() def _handleResendRequest(self, line): - lineToResend = None try: - lineToResend = int(line.replace("N:", " ").replace("N", " ").replace(":", " ").split()[-1]) - except: - if "rs" in line: - lineToResend = int(line.split()[1]) + lineToResend = None + try: + lineToResend = int(line.replace("N:", " ").replace("N", " ").replace(":", " ").split()[-1]) + except: + if "rs" in line: + lineToResend = int(line.split()[1]) - if lineToResend is None: - return False + if lineToResend is None: + return False - if self._resendDelta is None and lineToResend == self._currentLine: - # We don't expect to have an active resend request and the printer is requesting a resend of - # a line we haven't yet sent. + if self._resendDelta is None and lineToResend == self._currentLine: + # We don't expect to have an active resend request and the printer is requesting a resend of + # a line we haven't yet sent. + # + # This means the printer got a line from us with N = self._currentLine - 1 but had already + # acknowledged that. This can happen if the last line was resent due to a timeout during + # an active (prior) resend request. + # + # We will ignore this resend request and just continue normally. + self._logger.debug("Ignoring resend request for line %d == current line, we haven't sent that yet so the printer got N-1 twice from us, probably due to a timeout" % lineToResend) + return False + + lastCommError = self._lastCommError + self._lastCommError = None + + resendDelta = self._currentLine - lineToResend + + if lastCommError is not None \ + and ("line number" in lastCommError.lower() or "expected line" in lastCommError.lower()) \ + and lineToResend == self._lastResendNumber \ + and self._resendDelta is not None and self._currentResendCount < self._resendDelta: + self._logger.debug("Ignoring resend request for line %d, that still originates from lines we sent before we got the first resend request" % lineToResend) + self._currentResendCount += 1 + return True + + # If we ignore resend repetitions (Repetier firmware...), check if we + # need to do this now. If the same line number has been requested we + # already saw and resent, we'll ignore it up to times. + if self._resendSwallowRepetitions and lineToResend == self._lastResendNumber and self._resendSwallowRepetitionsCounter > 0: + self._logger.debug("Ignoring resend request for line %d, that is probably a repetition sent by the firmware to ensure it arrives, not a real request" % lineToResend) + self._resendSwallowRepetitionsCounter -= 1 + return True + + self._resendActive = True + self._resendDelta = resendDelta + self._lastResendNumber = lineToResend + self._currentResendCount = 0 + self._resendSwallowRepetitionsCounter = settings().getInt(["feature", "identicalResendsCountdown"]) + + if self._resendDelta > len(self._lastLines) or len(self._lastLines) == 0 or self._resendDelta < 0: + self._errorValue = "Printer requested line %d but no sufficient history is available, can't resend" % lineToResend + self._log(self._errorValue) + self._logger.warn(self._errorValue + ". Printer requested line {}, current line is {}, line history has {} entries.".format(lineToResend, self._currentLine, len(self._lastLines))) + if self.isPrinting(): + # abort the print, there's nothing we can do to rescue it now + self._changeState(self.STATE_ERROR) + eventManager().fire(Events.ERROR, {"error": self.getErrorString()}) + else: + # reset resend delta, we can't do anything about it + self._resendDelta = None + + # if we log resends, make sure we don't log more resends than the set rate within a window # - # This means the printer got a line from us with N = self._currentLine - 1 but had already - # acknowledged that. This can happen if the last line was resent due to a timeout during - # an active (prior) resend request. - # - # We will ignore this resend request and just continue normally. - self._logger.debug("Ignoring resend request for line %d == current line, we haven't sent that yet so the printer got N-1 twice from us, probably due to a timeout" % lineToResend) - return False + # this it to prevent the log from getting flooded for extremely bad communication issues + if self._log_resends: + now = time.time() + new_rate_window = self._log_resends_rate_start is None or self._log_resends_rate_start + self._log_resends_rate_frame < now + in_rate = self._log_resends_rate_count < self._log_resends_max - lastCommError = self._lastCommError - self._lastCommError = None + if new_rate_window or in_rate: + if new_rate_window: + self._log_resends_rate_start = now + self._log_resends_rate_count = 0 - resendDelta = self._currentLine - lineToResend + self._to_logfile_with_terminal("Got a resend request from the printer: requested line = {}, current line = {}".format(lineToResend, self._currentLine)) + self._log_resends_rate_count += 1 - if lastCommError is not None \ - and ("line number" in lastCommError.lower() or "expected line" in lastCommError.lower()) \ - and lineToResend == self._lastResendNumber \ - and self._resendDelta is not None and self._currentResendCount < self._resendDelta: - self._logger.debug("Ignoring resend request for line %d, that still originates from lines we sent before we got the first resend request" % lineToResend) - self._currentResendCount += 1 return True - - # If we ignore resend repetitions (Repetier firmware...), check if we - # need to do this now. If the same line number has been requested we - # already saw and resent, we'll ignore it up to times. - if self._resendSwallowRepetitions and lineToResend == self._lastResendNumber and self._resendSwallowRepetitionsCounter > 0: - self._logger.debug("Ignoring resend request for line %d, that is probably a repetition sent by the firmware to ensure it arrives, not a real request" % lineToResend) - self._resendSwallowRepetitionsCounter -= 1 - return True - - self._resendActive = True - self._resendDelta = resendDelta - self._lastResendNumber = lineToResend - self._currentResendCount = 0 - self._resendSwallowRepetitionsCounter = settings().getInt(["feature", "identicalResendsCountdown"]) - - if self._resendDelta > len(self._lastLines) or len(self._lastLines) == 0 or self._resendDelta < 0: - self._errorValue = "Printer requested line %d but no sufficient history is available, can't resend" % lineToResend - self._log(self._errorValue) - self._logger.warn(self._errorValue + ". Printer requested line {}, current line is {}, line history has {} entries.".format(lineToResend, self._currentLine, len(self._lastLines))) - if self.isPrinting(): - # abort the print, there's nothing we can do to rescue it now - self._changeState(self.STATE_ERROR) - eventManager().fire(Events.ERROR, {"error": self.getErrorString()}) - else: - # reset resend delta, we can't do anything about it - self._resendDelta = None - - # if we log resends, make sure we don't log more resends than the set rate within a window - # - # this it to prevent the log from getting flooded for extremely bad communication issues - if self._log_resends: - now = time.time() - new_rate_window = self._log_resends_rate_start is None or self._log_resends_rate_start + self._log_resends_rate_frame < now - in_rate = self._log_resends_rate_count < self._log_resends_max - - if new_rate_window or in_rate: - if new_rate_window: - self._log_resends_rate_start = now - self._log_resends_rate_count = 0 - - self._to_logfile_with_terminal("Got a resend request from the printer: requested line = {}, current line = {}".format(lineToResend, self._currentLine)) - self._log_resends_rate_count += 1 - - return True + finally: + if self._supportResendsWithoutOk: + # simulate an ok if our flags indicate that the printer needs that for resend requests to work + self._handle_ok() def _resendSameCommand(self): self._resendNextCommand(again=True) From 183ca3291b2399f8f847fbd4ad2209f3a9e7e5af Mon Sep 17 00:00:00 2001 From: Mark Walker Date: Mon, 28 Mar 2016 12:59:08 -0700 Subject: [PATCH 2/2] If no SD card support, remove SD file filter When SD card support is disabled on the server, remove any client side filtering to just SD files since there aren't any and there won't be UI turn remove the filter either. Should address #1300 --- src/octoprint/static/js/app/viewmodels/files.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/octoprint/static/js/app/viewmodels/files.js b/src/octoprint/static/js/app/viewmodels/files.js index 304bc527..9f0d6cb2 100644 --- a/src/octoprint/static/js/app/viewmodels/files.js +++ b/src/octoprint/static/js/app/viewmodels/files.js @@ -396,6 +396,7 @@ $(function() { self.localTarget = $("#drop_locally"); } else { self.localTarget = $("#drop"); + self.listHelper.removeFilter('sd'); } self.sdTarget = $("#drop_sd");