diff --git a/octoprint/server.py b/octoprint/server.py index 575aa75..1025f58 100644 --- a/octoprint/server.py +++ b/octoprint/server.py @@ -360,6 +360,7 @@ def getSettings(): "feature": { "gcodeViewer": s.getBoolean(["feature", "gCodeVisualizer"]), "waitForStart": s.getBoolean(["feature", "waitForStartOnConnect"]), + "waitForWaitAfterStart": s.getBoolean(["feature", "waitForWaitAfterStartOnConnect"]), "alwaysSendChecksum": s.getBoolean(["feature", "alwaysSendChecksum"]), "resetLineNumbersWithPrefixedN": s.getBoolean(["feature", "resetLineNumbersWithPrefixedN"]) }, @@ -403,6 +404,7 @@ def setSettings(): if "feature" in data.keys(): if "gcodeViewer" in data["feature"].keys(): s.setBoolean(["feature", "gCodeVisualizer"], data["feature"]["gcodeViewer"]) if "waitForStart" in data["feature"].keys(): s.setBoolean(["feature", "waitForStartOnConnect"], data["feature"]["waitForStart"]) + if "waitForWait" in data["feature"].keys(): s.setBoolean(["feature", "waitForWaitOnConnect"], data["feature"]["waitForWait"]), if "alwaysSendChecksum" in data["feature"].keys(): s.setBoolean(["feature", "alwaysSendChecksum"], data["feature"]["alwaysSendChecksum"]) if "resetLineNumbersWithPrefixedN" in data["feature"].keys(): s.setBoolean(["feature", "resetLineNumbersWithPrefixedN"], data["feature"]["resetLineNumbersWithPrefixedN"]) diff --git a/octoprint/settings.py b/octoprint/settings.py index 7418251..10e99b4 100644 --- a/octoprint/settings.py +++ b/octoprint/settings.py @@ -40,6 +40,7 @@ default_settings = { "feature": { "gCodeVisualizer": True, "waitForStartOnConnect": False, + "waitForWaitOnConnect": False, "alwaysSendChecksum": False, "resetLineNumbersWithPrefixedN": False }, diff --git a/octoprint/static/js/ui.js b/octoprint/static/js/ui.js index 36dee20..1cb4364 100644 --- a/octoprint/static/js/ui.js +++ b/octoprint/static/js/ui.js @@ -618,6 +618,7 @@ function TerminalViewModel() { if (!self.log) self.log = [] self.log = self.log.concat(data) + self.log = self.log.slice(-300) self.updateOutput(); } @@ -959,6 +960,7 @@ function SettingsViewModel() { self.feature_gcodeViewer = ko.observable(undefined); self.feature_waitForStart = ko.observable(undefined); + self.feature_waitForWait = ko.observable(undefined); self.feature_alwaysSendChecksum = ko.observable(undefined); self.feature_resetLineNumbersWithPrefixedN = ko.observable(undefined); @@ -1005,6 +1007,7 @@ function SettingsViewModel() { self.feature_gcodeViewer(response.feature.gcodeViewer); self.feature_waitForStart(response.feature.waitForStart); + self.feature_waitForWait(response.feature.waitForWait); self.feature_alwaysSendChecksum(response.feature.alwaysSendChecksum); self.feature_resetLineNumbersWithPrefixedN(response.feature.resetLineNumbersWithPrefixedN); @@ -1040,6 +1043,7 @@ function SettingsViewModel() { "feature": { "gcodeViewer": self.feature_gcodeViewer(), "waitForStart": self.feature_waitForStart(), + "waitForWait": self.feature_waitForWait(), "alwaysSendChecksum": self.feature_alwaysSendChecksum(), "resetLineNumbersWithPrefixedN": self.feature_resetLineNumbersWithPrefixedN() }, diff --git a/octoprint/templates/settings.html b/octoprint/templates/settings.html index 57f6c2b..6d8af47 100644 --- a/octoprint/templates/settings.html +++ b/octoprint/templates/settings.html @@ -102,21 +102,28 @@
+
+
+
+
+
diff --git a/octoprint/util/comm.py b/octoprint/util/comm.py index 8bad3b6..c6b8a0a 100644 --- a/octoprint/util/comm.py +++ b/octoprint/util/comm.py @@ -66,6 +66,9 @@ class VirtualPrinter(): self.currentLine = 0 + waitThread = threading.Thread(target=self._sendWaitAfterTimeout) + waitThread.start() + def write(self, data): if self.readList is None: return @@ -125,6 +128,10 @@ class VirtualPrinter(): def close(self): self.readList = None + def _sendWaitAfterTimeout(self, timeout=5): + time.sleep(timeout) + self.readList.append("wait") + class MachineComPrintCallback(object): def mcLog(self, message): pass @@ -197,7 +204,9 @@ class MachineCom(object): self._currentLine = 1 self._resendDelta = None self._lastLines = [] - self._sending = False + + self._sendingLock = threading.Lock() + self.thread = threading.Thread(target=self._monitor) self.thread.daemon = True self.thread.start() @@ -338,6 +347,7 @@ class MachineCom(object): timeout = time.time() + 5 tempRequestTimeout = timeout startSeen = not settings().getBoolean(["feature", "waitForStartOnConnect"]) + waitSeen = not settings().getBoolean(["feature", "waitForWaitOnConnect"]) while True: line = self._readline() if line == None: @@ -429,13 +439,13 @@ class MachineCom(object): ### Connection attempt elif self._state == self.STATE_CONNECTING: - #if (line == "" or "wait" in line) and startSeen: - #This modification allows more reliable initial connection. - if ("wait" in line) and startSeen: + if line == "" and startSeen and waitSeen: self._sendCommand("M105") elif "start" in line: startSeen = True - elif "ok" in line and startSeen: + elif "wait" in line: + waitSeen = True + elif "ok" in line and startSeen and waitSeen: self._changeState(self.STATE_OPERATIONAL) elif time.time() > timeout: self.close() @@ -444,8 +454,16 @@ class MachineCom(object): elif self._state == self.STATE_OPERATIONAL or self._state == self.STATE_PAUSED: #Request the temperature on comm timeout (every 5 seconds) when we are not printing. if line == "" or "wait" in line: - self._sendCommand("M105") + if self._resendDelta is not None: + self._resendNextCommand() + elif not self._commandQueue.empty(): + self._sendCommand(self._commandQueue.get()) + else: + self._sendCommand("M105") tempRequestTimeout = time.time() + 5 + # resend -> start resend procedure from requested line + elif "resend" in line.lower() or "rs" in line: + self._handleResendRequest(line) ### Printing elif self._state == self.STATE_PRINTING: @@ -468,23 +486,26 @@ class MachineCom(object): self._sendNext() # resend -> start resend procedure from requested line elif "resend" in line.lower() or "rs" in line: - 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 not None: - self._resendDelta = self._currentLine - lineToResend - if self._resendDelta > len(self._lastLines): - self._errorValue = "Printer requested line %d but history is only available up to line %d" % (lineToResend, self._currentLine - len(self._lastLines)) - self._changeState(self.STATE_ERROR) - self._logger.warn(self._errorValue) - else: - self._resendNextCommand() + self._handleResendRequest(line) self._log("Connection closed, closing down monitor") - + + 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]) + + if lineToResend is not None: + self._resendDelta = self._currentLine - lineToResend + if self._resendDelta > len(self._lastLines): + self._errorValue = "Printer requested line %d but history is only available up to line %d" % (lineToResend, self._currentLine - len(self._lastLines)) + self._changeState(self.STATE_ERROR) + self._logger.warn(self._errorValue) + else: + self._resendNextCommand() + def _log(self, message): self._callback.mcLog(message) self._serialLogger.debug(message) @@ -527,62 +548,63 @@ class MachineCom(object): self.close() def _resendNextCommand(self): - self._logger.debug("Resending line %d, delta is %d, history log is %s items strong" % (self._currentLine - self._resendDelta, self._resendDelta, len(self._lastLines))) - cmd = self._lastLines[-self._resendDelta] - lineNumber = self._currentLine - self._resendDelta + # Make sure we are only handling one sending job at a time + with self._sendingLock: + self._logger.debug("Resending line %d, delta is %d, history log is %s items strong" % (self._currentLine - self._resendDelta, self._resendDelta, len(self._lastLines))) + cmd = self._lastLines[-self._resendDelta] + lineNumber = self._currentLine - self._resendDelta - self._doSendWithChecksum(cmd, lineNumber) + self._doSendWithChecksum(cmd, lineNumber) - self._resendDelta -= 1 - if self._resendDelta <= 0: - self._resendDelta = None + self._resendDelta -= 1 + if self._resendDelta <= 0: + self._resendDelta = None def _sendCommand(self, cmd, sendChecksum=False): - cmd = cmd.upper() - #Wait for current send to finish. - while self._sending: - pass - self._sending = True - if self._serial is None: - return - if 'M109' in cmd or 'M190' in cmd: - self._heatupWaitStartTime = time.time() - if 'M104' in cmd or 'M109' in cmd: - try: - self._targetTemp = float(re.search('S([0-9]+)', cmd).group(1)) - except: - pass - if 'M140' in cmd or 'M190' in cmd: - try: - self._bedTargetTemp = float(re.search('S([0-9]+)', cmd).group(1)) - except: - pass + # Make sure we are only handling one sending job at a time + with self._sendingLock: + cmd = cmd.upper() - if "M110" in cmd: - newLineNumber = None - if " N" in cmd: + if self._serial is None: + return + if 'M109' in cmd or 'M190' in cmd: + self._heatupWaitStartTime = time.time() + if 'M104' in cmd or 'M109' in cmd: try: - newLineNumber = int(re.search("N([0-9]+)", cmd).group(1)) + self._targetTemp = float(re.search('S([0-9]+)', cmd).group(1)) + except: + pass + if 'M140' in cmd or 'M190' in cmd: + try: + self._bedTargetTemp = float(re.search('S([0-9]+)', cmd).group(1)) except: pass - else: - newLineNumber = 0 - if settings().getBoolean(["feature", "resetLineNumbersWithPrefixedN"]) and newLineNumber is not None: - # let's rewrite the M110 command to fit repetier syntax - self._doSendWithChecksum("M110", newLineNumber) - self._addToLastLines(cmd) - self._currentLine = newLineNumber + 1 - else: - self._doSend(cmd, sendChecksum) + if "M110" in cmd: + newLineNumber = None + if " N" in cmd: + try: + newLineNumber = int(re.search("N([0-9]+)", cmd).group(1)) + except: + pass + else: + newLineNumber = 0 + + if settings().getBoolean(["feature", "resetLineNumbersWithPrefixedN"]) and newLineNumber is not None: + # let's rewrite the M110 command to fit repetier syntax + self._addToLastLines(cmd) + self._doSendWithChecksum("M110", newLineNumber) + else: + self._doSend(cmd, sendChecksum) + if newLineNumber is not None: self._currentLine = newLineNumber + 1 - # after a reset of the line number we have no way to determine what line exactly the printer now wants - self._lastLines = [] - self._resendDelta = None - else: - self._doSend(cmd, sendChecksum) + # after a reset of the line number we have no way to determine what line exactly the printer now wants + self._lastLines = [] + self._resendDelta = None + else: + self._doSend(cmd, sendChecksum) def _addToLastLines(self, cmd): self._lastLines.append(cmd) @@ -596,17 +618,15 @@ class MachineCom(object): lineNumber = self._currentLine else: lineNumber = self._gcodePos - self._doSendWithChecksum(cmd, lineNumber) self._addToLastLines(cmd) self._currentLine += 1 + self._doSendWithChecksum(cmd, lineNumber) else: self._doSendWithoutChecksum(cmd) - def _doSendWithChecksum(self, cmd, lineNumber=None): + def _doSendWithChecksum(self, cmd, lineNumber): self._logger.debug("Sending cmd '%s' with lineNumber %r" % (cmd, lineNumber)) - if lineNumber is None: - lineNumber = self._currentLine checksum = reduce(lambda x,y:x^y, map(ord, "N%d%s" % (lineNumber, cmd))) commandToSend = "N%d%s*%d" % (lineNumber, cmd, checksum) self._doSendWithoutChecksum(commandToSend) @@ -627,8 +647,6 @@ class MachineCom(object): self._log("Unexpected error while writing serial port: %s" % (getExceptionString())) self._errorValue = getExceptionString() self.close(True) - #clear sending flag - self._sending = False def _sendNext(self): if self._gcodePos >= len(self._gcodeList):