From 57651804628dad62d011738454d12e9771db56ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Mon, 27 Apr 2015 17:15:57 +0200 Subject: [PATCH] Fix: moved line number incrementing to send loop Should fix issues where too fast a temperature polling could cause skipped line numbers, triggering nasty resend loops in the process. --- src/octoprint/util/comm.py | 171 ++++++++++++++++++++++--------------- 1 file changed, 101 insertions(+), 70 deletions(-) diff --git a/src/octoprint/util/comm.py b/src/octoprint/util/comm.py index 88f0e098..f1155bb1 100644 --- a/src/octoprint/util/comm.py +++ b/src/octoprint/util/comm.py @@ -420,7 +420,7 @@ class MachineCom(object): if self.isPrinting() and not self.isSdFileSelected(): self._commandQueue.put((cmd, cmd_type)) elif self.isOperational(): - self._sendCommand((cmd, cmd_type)) + self._sendCommand(cmd, cmd_type=cmd_type) def sendGcodeScript(self, scriptName, replacements=None): context = dict() @@ -520,7 +520,7 @@ class MachineCom(object): self.sendCommand(line) # now make sure we actually do something, up until now we only filled up the queue - self._sendFromQueue(sendChecksum=True) + self._sendFromQueue() except: self._logger.exception("Error while trying to start printing") self._errorValue = get_exception_string() @@ -621,7 +621,7 @@ class MachineCom(object): self.sendCommand(line) # now make sure we actually do something, up until now we only filled up the queue - self._sendFromQueue(sendChecksum=True) + self._sendFromQueue() eventManager().fire(Events.PRINT_RESUMED, payload) elif pause and self.isPrinting(): @@ -1074,7 +1074,7 @@ class MachineCom(object): self._resendNextCommand() else: - if self._sendFromQueue(sendChecksum=True): + if self._sendFromQueue(): pass elif not self.isSdPrinting(): self._sendNext() @@ -1164,9 +1164,18 @@ class MachineCom(object): eventManager().fire(Events.CONNECTED, payload) self.sendGcodeScript("afterPrinterConnected", replacements=dict(event=payload)) - def _sendFromQueue(self, sendChecksum=False): + def _sendFromQueue(self): if not self._commandQueue.empty() and not self.isStreaming(): - self._sendCommand(self._commandQueue.get(), sendChecksum) + entry = self._commandQueue.get() + if isinstance(entry, tuple): + if not len(entry) == 2: + return False + cmd, cmd_type = entry + else: + cmd = entry + cmd_type = None + + self._sendCommand(cmd, cmd_type=cmd_type) return True else: return False @@ -1325,7 +1334,7 @@ class MachineCom(object): with self._sendNextLock: line = self._getNext() if line is not None: - self._sendCommand(line, True) + self._sendCommand(line) self._callback.on_comm_progress() def _handleResendRequest(self, line): @@ -1385,64 +1394,53 @@ class MachineCom(object): self._lastResendNumber = None self._currentResendCount = 0 - def _sendCommand(self, cmd_tuple, sendChecksum=False): - if isinstance(cmd_tuple, tuple): - cmd, cmd_type = cmd_tuple - else: - cmd = cmd_tuple - cmd_type = None - + def _sendCommand(self, cmd, cmd_type=None): # Make sure we are only handling one sending job at a time with self._sendingLock: if self._serial is None: return if not self.isStreaming(): - for hook in self._gcode_hooks: - hook_cmd = self._gcode_hooks[hook](self, cmd, cmd_type=cmd_type, send_checksum=sendChecksum) + for name, hook in self._gcode_hooks.items(): + hook_cmd = hook(self, cmd, cmd_type=cmd_type) + + if hook_cmd is None: + cmd = None # hook might have returned (cmd, sendChecksum) or (cmd, sendChecksum, cmd_type), split that - if isinstance(hook_cmd, tuple): + elif isinstance(hook_cmd, tuple): if len(hook_cmd) == 2: hook_cmd, cmd_type = hook_cmd elif len(hook_cmd) == 3: - hook_cmd, cmd_type, sendChecksum = hook_cmd - - # hook might have returned None for cmd, if so stop processing, we won't send this command - # to the printer - if hook_cmd is None: - cmd = None - break + # legacy hook handler, ignore returned send_checksum + hook_cmd, cmd_type, _ = hook_cmd # if hook_cmd is a string, we'll replace cmd with it (it's been rewritten by the hook handler - elif isinstance(hook_cmd, basestring): + if isinstance(hook_cmd, basestring): cmd = hook_cmd + else: + self._logger.warn("Hook {name} returned unintelligible result, ignoring it: {hook_cmd!r}".format(**locals())) + continue + + if cmd is None: + break + # try to parse the cmd and extract the gcode type - gcode = self._regex_command.search(cmd) - if gcode: - gcode = gcode.group(1) + if cmd is not None: + gcode = self._regex_command.search(cmd) + if gcode: + gcode = gcode.group(1) - # fire events if necessary - if gcode in gcodeToEvent: - eventManager().fire(gcodeToEvent[gcode]) + # fire events if necessary + if gcode in gcodeToEvent: + eventManager().fire(gcodeToEvent[gcode]) - # send it through the specific handler if it exists - gcodeHandler = "_gcode_" + gcode - if hasattr(self, gcodeHandler): - cmd = getattr(self, gcodeHandler)(cmd) + # send it through the specific handler if it exists + cmd = self._process_command_phase("queue", cmd, gcode=gcode) if cmd is not None: - self._doSend(cmd, send_checksum=sendChecksum, cmd_type=cmd_type) - - def _doSend(self, cmd, send_checksum=False, cmd_type=None): - if send_checksum or self._alwaysSendChecksum: - lineNumber = self._currentLine - self._addToLastLines(cmd) - self._currentLine += 1 - self._enqueue_for_sending(cmd, linenumber=lineNumber, command_type=cmd_type) - else: - self._enqueue_for_sending(cmd, command_type=cmd_type) + self._enqueue_for_sending(cmd, command_type=cmd_type) def gcode_command_for_cmd(self, cmd): """ @@ -1497,20 +1495,54 @@ class MachineCom(object): if not self._send_queue_active: break - # fetch command and optional linenumber from queue, send it + # fetch command and optional linenumber from queue command, linenumber, _ = entry + + gcode_match = self._regex_command.search(command) + is_gcode = gcode_match is not None + + if is_gcode: + # trigger "sending" phase + command = self._process_command_phase("sending", command, gcode=gcode_match.group(1)) + if linenumber is not None: + # line number predetermined, use that self._doSendWithChecksum(command, linenumber) else: - self._doSendWithoutChecksum(command) + if self.isPrinting() or self._alwaysSendChecksum: + linenumber = self._currentLine + self._addToLastLines(command) + self._currentLine += 1 + self._doSendWithChecksum(command, linenumber) + else: + self._doSendWithoutChecksum(command) - # we just used up one ok, clear it, wait for the next clear + if is_gcode: + # trigger "sent" phase + self._process_command_phase("sent", command, gcode=gcode_match.group(1)) + + # wait for the next clear self._clear_to_send.clear() self._clear_to_send.wait() except: self._logger.exception("Caught an exception in the send loop") self._log("Closing down send loop") + def _process_command_phase(self, phase, command, gcode=None): + if gcode is None: + gcode_match = self._regex_command.search(command) + if gcode_match is None: + return command + + gcode = gcode.group(1) + + # send it through the specific handler if it exists + gcodeHandler = "_gcode_" + gcode + "_" + phase + if hasattr(self, gcodeHandler): + command = getattr(self, gcodeHandler)(command) + + return command + ##~~ actual sending via serial def _doSendWithChecksum(self, cmd, lineNumber): @@ -1538,13 +1570,13 @@ class MachineCom(object): ##~~ command handlers - def _gcode_T(self, cmd): + def _gcode_T_sent(self, cmd): toolMatch = self._regex_paramTInt.search(cmd) if toolMatch: self._currentTool = int(toolMatch.group(1)) return cmd - def _gcode_G0(self, cmd): + def _gcode_G0_sent(self, cmd): if 'Z' in cmd: match = self._regex_paramZFloat.search(cmd) if match: @@ -1556,14 +1588,14 @@ class MachineCom(object): except ValueError: pass return cmd - _gcode_G1 = _gcode_G0 + _gcode_G0_sent = _gcode_G0_sent - def _gcode_M0(self, cmd): + def _gcode_M0_queue(self, cmd): self.setPause(True) - return "M105" # Don't send the M0 or M1 to the machine, as M0 and M1 are handled as an LCD menu pause. - _gcode_M1 = _gcode_M0 + return None # Don't send the M0 or M1 to the machine, as M0 and M1 are handled as an LCD menu pause. + _gcode_M1_queued = _gcode_M0_queue - def _gcode_M104(self, cmd): + def _gcode_M104_sent(self, cmd): toolNum = self._currentTool toolMatch = self._regex_paramTInt.search(cmd) if toolMatch: @@ -1581,7 +1613,7 @@ class MachineCom(object): pass return cmd - def _gcode_M140(self, cmd): + def _gcode_M140_sent(self, cmd): match = self._regex_paramSInt.search(cmd) if match: try: @@ -1595,19 +1627,19 @@ class MachineCom(object): pass return cmd - def _gcode_M109(self, cmd): + def _gcode_M109_sent(self, cmd): self._heatupWaitStartTime = time.time() self._blocking_command = True self._heating = True - return self._gcode_M104(cmd) + return self._gcode_M104_sent(cmd) - def _gcode_M190(self, cmd): + def _gcode_M190_sent(self, cmd): self._heatupWaitStartTime = time.time() self._blocking_command = True self._heating = True - return self._gcode_M140(cmd) + return self._gcode_M140_sent(cmd) - def _gcode_M110(self, cmd): + def _gcode_M110_sending(self, cmd): newLineNumber = None match = self._regex_paramNInt.search(cmd) if match: @@ -1619,20 +1651,19 @@ class MachineCom(object): newLineNumber = 0 # send M110 command with new line number - self._enqueue_for_sending(cmd, linenumber=newLineNumber) - self._currentLine = newLineNumber + 1 + self._currentLine = newLineNumber # after a reset of the line number we have no way to determine what line exactly the printer now wants self._lastLines.clear() self._resendDelta = None - return None + return cmd - def _gcode_M112(self, cmd): # It's an emergency what todo? Canceling the print should be the minimum + def _gcode_M112_queue(self, cmd): # It's an emergency what todo? Canceling the print should be the minimum self.cancelPrint() return cmd - def _gcode_G4(self, cmd): + def _gcode_G4_sent(self, cmd): # we are intending to dwell for a period of time, increase the timeout to match cmd = cmd.upper() p_idx = cmd.find('P') @@ -1648,12 +1679,12 @@ class MachineCom(object): self._blocking_command = True return cmd - def _gcode_G28(self, cmd): + def _gcode_G28_sending(self, cmd): self._blocking_command = True return cmd - _gcode_G29 = _gcode_G28 - _gcode_G30 = _gcode_G28 - _gcode_G32 = _gcode_G28 + _gcode_G29_sending = _gcode_G28_sending + _gcode_G30_sending = _gcode_G28_sending + _gcode_G32_sending = _gcode_G28_sending ### MachineCom callback ################################################################################################