diff --git a/src/octoprint/util/comm.py b/src/octoprint/util/comm.py index 9a571bda..394f9cc9 100644 --- a/src/octoprint/util/comm.py +++ b/src/octoprint/util/comm.py @@ -1243,7 +1243,7 @@ class MachineCom(object): return stripped_line, stripped_line.lower() ##~~ Error handling - line = self._handleErrors(line) + line = self._handle_errors(line) line, lower_line = convert_line(line) ##~~ SD file list @@ -1950,36 +1950,53 @@ class MachineCom(object): return False - def _handleErrors(self, line): + _resend_request_communication_errors = ("line number", + "wrong checksum", + "missing checksum", + "format error", + "expected line") + _recoverable_communication_errors = ("no line number with checksum",) + _sd_card_errors = ("volume.init", + "openroot", + "workdir", + "error writing to file", + "cannot open", + "cannot enter") + def _handle_errors(self, line): if line is None: return lower_line = line.lower() - # No matter the state, if we see an error, goto the error state and store the error for reference. if lower_line.startswith('error:') or line.startswith('!!'): - #Oh YEAH, consistency. - # Marlin reports an MIN/MAX temp error as "Error:x\n: Extruder switched off. MAXTEMP triggered !\n" - # But a bed temp error is reported as "Error: Temperature heated bed switched off. MAXTEMP triggered !!" - # So we can have an extra newline in the most common case. Awesome work people. if regex_minMaxError.match(line): + # special delivery for firmware that goes "Error:x\n: Extruder switched off. MAXTEMP triggered !\n" line = line.rstrip() + self._readline() - if 'line number' in lower_line or 'checksum' in lower_line or 'format error' in lower_line or 'expected line' in lower_line: - #Skip the communication errors, as those get corrected. + if any(map(lambda x: x in lower_line, self._recoverable_communication_errors)): + # manually trigger an ack for comm errors the printer doesn't send a resend request for but + # from which we can recover from by just pushing on (because that then WILL trigger a fitting + # resend request) + self._handle_ok() + + elif any(map(lambda x: x in lower_line, self._resend_request_communication_errors)): + # skip comm errors that the printer sends a resend request for anyhow self._lastCommError = line[6:] if lower_line.startswith("error:") else line[2:] + + elif any(map(lambda x: x in lower_line, self._sd_card_errors)): + # skip errors with the SD card pass - elif 'volume.init' in lower_line or "openroot" in lower_line or 'workdir' in lower_line\ - or "error writing to file" in lower_line or "cannot open" in lower_line\ - or "cannot enter" in lower_line: - #Also skip errors with the SD card - pass + elif 'unknown command' in lower_line: - #Ignore unknown command errors, it could be a typo or some missing feature + # ignore unknown command errors, it could be a typo or some missing feature pass + elif not self.isError(): + # handle everything else error_text = line[6:] if lower_line.startswith("error:") else line[2:] - self._to_logfile_with_terminal("Received an error from the printer's firmware: {}".format(error_text), level=logging.WARN) + self._to_logfile_with_terminal("Received an error from the printer's firmware: {}".format(error_text), + level=logging.WARN) + if not self._ignore_errors: if self._disconnect_on_errors: self._errorValue = error_text @@ -1991,6 +2008,8 @@ class MachineCom(object): 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() + + # finally return the line return line def _readline(self): @@ -2077,7 +2096,8 @@ class MachineCom(object): # 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) + self._logger.info("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 @@ -2089,7 +2109,8 @@ class MachineCom(object): 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._logger.info("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 @@ -2097,7 +2118,8 @@ class MachineCom(object): # 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._logger.info("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