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.
This commit is contained in:
Gina Häußge 2016-03-18 14:59:13 +01:00
parent a229cbfd49
commit 1a4c06d6f6
5 changed files with 93 additions and 76 deletions

View file

@ -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"])

View file

@ -90,7 +90,8 @@ default_settings = {
"helloCommand": "M110 N0",
"disconnectOnErrors": True,
"ignoreErrorsFromFirmware": False,
"logResends": False,
"logResends": True,
"supportResendsWithoutOk": False,
# command specific flags
"triggerOkForM29": True

View file

@ -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(),

View file

@ -121,6 +121,11 @@
<input type="checkbox" data-bind="checked: serial_triggerOkForM29" id="settings-triggerOkForM29"> {{ _('Generate additional <code>ok</code> for <code>M29</code>') }} <span class="label">{{ _('Most Marlin < v1.1.0') }}</span>
</label>
</div>
<div class="controls">
<label class="checkbox">
<input type="checkbox" data-bind="checked: serial_supportResendsWithoutOk" id="settings-supportResendsWithoutOk"> {{ _('Simulate an additional `ok` for resend requests') }}
</label>
</div>
</div>
</div>

View file

@ -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 <counter> 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 <counter> 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)