diff --git a/src/octoprint/server/api/settings.py b/src/octoprint/server/api/settings.py
index 8937f35d..34a7dffb 100644
--- a/src/octoprint/server/api/settings.py
+++ b/src/octoprint/server/api/settings.py
@@ -93,7 +93,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"),
@@ -248,6 +249,7 @@ def _saveSettings(data):
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 3faa63ad..a94e9072 100644
--- a/src/octoprint/settings.py
+++ b/src/octoprint/settings.py
@@ -92,7 +92,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/files.js b/src/octoprint/static/js/app/viewmodels/files.js
index ebcca1e0..30da0ada 100644
--- a/src/octoprint/static/js/app/viewmodels/files.js
+++ b/src/octoprint/static/js/app/viewmodels/files.js
@@ -506,7 +506,12 @@ $(function() {
self.uploadProgress = $("#gcode_upload_progress");
self.uploadProgressBar = $(".bar", self.uploadProgress);
- self.localTarget = CONFIG_SD_SUPPORT ? $("#drop_locally") : $("#drop");
+ if (CONFIG_SD_SUPPORT) {
+ self.localTarget = $("#drop_locally");
+ } else {
+ self.localTarget = $("#drop");
+ self.listHelper.removeFilter('sd');
+ }
self.sdTarget = $("#drop_sd");
function evaluateDropzones() {
diff --git a/src/octoprint/static/js/app/viewmodels/settings.js b/src/octoprint/static/js/app/viewmodels/settings.js
index 3bb9242a..ea3dcef0 100644
--- a/src/octoprint/static/js/app/viewmodels/settings.js
+++ b/src/octoprint/static/js/app/viewmodels/settings.js
@@ -146,6 +146,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);
diff --git a/src/octoprint/templates/dialogs/settings/serialconnection.jinja2 b/src/octoprint/templates/dialogs/settings/serialconnection.jinja2
index f628a485..cfac73a4 100644
--- a/src/octoprint/templates/dialogs/settings/serialconnection.jinja2
+++ b/src/octoprint/templates/dialogs/settings/serialconnection.jinja2
@@ -131,6 +131,16 @@
{{ _('Use this to specify which commands always need to be sent with a checksum. Comma separated list.') }}
+
+
+
+
+
+
diff --git a/src/octoprint/util/comm.py b/src/octoprint/util/comm.py
index d67c07cf..27181edb 100644
--- a/src/octoprint/util/comm.py
+++ b/src/octoprint/util/comm.py
@@ -262,11 +262,12 @@ 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._checksum_requiring_commands = settings().get(["serial", "checksumRequiringCommands"])
+ self._supportResendsWithoutOk = settings().getBoolean(["serial", "supportResendsWithoutOk"])
+
self._resendActive = False
self._terminal_log = deque([], 20)
@@ -276,11 +277,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"])
@@ -1644,84 +1645,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)