diff --git a/src/octoprint/settings.py b/src/octoprint/settings.py index 2dd49557..629f785b 100644 --- a/src/octoprint/settings.py +++ b/src/octoprint/settings.py @@ -132,7 +132,7 @@ default_settings = { "key": ''.join('%02X' % ord(z) for z in uuid.uuid4().bytes) }, "terminalFilters": [ - { "name": "Suppress M105 requests/responses", "regex": "(Send: M105)|(Recv: ok T:)" }, + { "name": "Suppress M105 requests/responses", "regex": "(Send: M105)|(Recv: ok T\d*:)" }, { "name": "Suppress M27 requests/responses", "regex": "(Send: M27)|(Recv: SD printing byte)" } ], "devel": { @@ -142,7 +142,8 @@ default_settings = { "okAfterResend": False, "forceChecksum": False, "okWithLinenumber": False, - "numExtruders": 1 + "numExtruders": 1, + "includeCurrentToolInTemps": True } } } diff --git a/src/octoprint/static/js/app/viewmodels/terminal.js b/src/octoprint/static/js/app/viewmodels/terminal.js index d4bc5b0b..d739ea0e 100644 --- a/src/octoprint/static/js/app/viewmodels/terminal.js +++ b/src/octoprint/static/js/app/viewmodels/terminal.js @@ -91,7 +91,7 @@ function TerminalViewModel(loginStateViewModel, settingsViewModel) { self.sendCommand = function() { var command = self.command(); - var re = /^([gm][0-9]+)(\s.*)?/; + var re = /^([gmt][0-9]+)(\s.*)?/; var commandMatch = command.match(re); if (commandMatch != null) { command = commandMatch[1].toUpperCase() + ((commandMatch[2] !== undefined) ? commandMatch[2] : ""); diff --git a/src/octoprint/util/comm.py b/src/octoprint/util/comm.py index ac25b15b..1a970495 100644 --- a/src/octoprint/util/comm.py +++ b/src/octoprint/util/comm.py @@ -145,6 +145,7 @@ class MachineCom(object): self._currentZ = None self._heatupWaitStartTime = 0 self._heatupWaitTimeLost = 0.0 + self._currentExtruder = 0 # Regex matching temperature entries in line. Groups will be as follows: # - 1: whole tool designator incl. optional toolNumber ("T", "Tn", "B") @@ -152,7 +153,7 @@ class MachineCom(object): # - 3: actual temperature # - 4: whole target substring, if given (e.g. " / 22.0") # - 5: target temperature - self._tempRegex = re.compile("(B|T(\d*)):([-+]?\d*\.?\d*)(\s*\/?\s*([-+]?\d*\.?\d*))?") + self._tempRegex = re.compile("(B|T(\d*)):\s*([-+]?\d*\.?\d*)(\s*\/?\s*([-+]?\d*\.?\d*))?") self._alwaysSendChecksum = settings().getBoolean(["feature", "alwaysSendChecksum"]) self._currentLine = 1 @@ -603,7 +604,7 @@ class MachineCom(object): continue ##~~ Temperature processing - if ' T:' in line or line.startswith('T:'): + if ' T:' in line or line.startswith('T:') or ' T0:' in line or line.startswith('T0:'): self._processTemperatures(line) self._callback.mcTempUpdate(self._temp, self._bedTemp) @@ -992,7 +993,7 @@ class MachineCom(object): return if not self.isStreaming(): - gcode = re.search("^\s*([GM]\d+)", cmd) + gcode = re.search("^\s*([GM]\d+|T)", cmd) if gcode: gcode = gcode.group(1) @@ -1040,6 +1041,12 @@ class MachineCom(object): self._errorValue = getExceptionString() self.close(True) + def _gcode_T(self, cmd): + toolMatch = re.search('T([0-9]+)', cmd) + if toolMatch: + self._currentExtruder = int(toolMatch.group(1)) + return cmd + def _gcode_G0(self, cmd): if 'Z' in cmd: try: @@ -1058,10 +1065,14 @@ class MachineCom(object): _gcode_M1 = _gcode_M0 def _gcode_M104(self, cmd): + toolNum = self._currentExtruder + toolMatch = re.search('T([0-9]+)', cmd) + if toolMatch: + toolNum = int(toolMatch.group(1)) match = re.search('S([0-9]+)', cmd) if match: try: - self._targetTemp = float(match.group(1)) + self._targetTemp[toolNum] = float(match.group(1)) except ValueError: pass return cmd diff --git a/src/octoprint/util/virtual.py b/src/octoprint/util/virtual.py index 8f5e7f8b..8b6f57b6 100644 --- a/src/octoprint/util/virtual.py +++ b/src/octoprint/util/virtual.py @@ -31,6 +31,7 @@ class VirtualPrinter(): self._selectedSdFilePos = None self._writingToSd = False self._newSdFilePos = None + self._heatupThread = None self.currentLine = 0 self.lastN = 0 @@ -90,12 +91,11 @@ class VirtualPrinter(): #print "Send: %s" % (data.rstrip()) if 'M104' in data or 'M109' in data: - self._parseHotendCommand(data) + if not self._parseHotendCommand(data): + return if 'M140' in data or 'M190' in data: - try: - self.bedTargetTemp = float(re.search('S([0-9]+)', data).group(1)) - except: - pass + if not self._parseBedCommand(data): + return if 'M105' in data: # send simulated temperature data @@ -104,7 +104,10 @@ class VirtualPrinter(): for i in range(len(self.temp)): allTemps.append((i, self.temp[i], self.targetTemp[i])) allTempsString = " ".join(map(lambda x: "T%d:%.2f /%.2f" % x, allTemps)) - self.readList.append("ok T:%.2f /%.2f B:%.2f /%.2f %s @:64\n" % (self.temp[self.currentExtruder], self.targetTemp[self.currentExtruder] + 1, self.bedTemp, self.bedTargetTemp, allTempsString)) + if settings().getBoolean(["devel", "virtualPrinted", "includeCurrentToolInTemps"]): + self.readList.append("ok T:%.2f /%.2f B:%.2f /%.2f %s @:64\n" % (self.temp[self.currentExtruder], self.targetTemp[self.currentExtruder] + 1, self.bedTemp, self.bedTargetTemp, allTempsString)) + else: + self.readList.append("ok %s B:%.2f /%.2f @:64\n" % (allTempsString, self.bedTemp, self.bedTargetTemp)) else: self.readList.append("ok T:%.2f /%.2f B:%.2f /%.2f @:64\n" % (self.temp[0], self.targetTemp[0], self.bedTemp, self.bedTargetTemp)) elif 'M20' in data: @@ -214,6 +217,24 @@ class VirtualPrinter(): except: pass + if "M109" in line: + self._heatupThread = threading.Thread(target=self._waitForHeatup, args=["tool%d" % tool]) + self._heatupThread.start() + return False + return True + + def _parseBedCommand(self, line): + try: + self.bedTargetTemp = float(re.search('S([0-9]+)', line).group(1)) + except: + pass + + if "M190" in line: + self._heatupThread = threading.Thread(target=self._waitForHeatup, args=["bed"]) + self._heatupThread.start() + return False + return True + def _writeSdFile(self, filename): file = os.path.join(self._virtualSd, filename).lower() if os.path.exists(file): @@ -251,10 +272,7 @@ class VirtualPrinter(): if 'M104' in line or 'M109' in line: self._parseHotendCommand(line) if 'M140' in line or 'M190' in line: - try: - self.bedTargetTemp = float(re.search('S([0-9]+)', line).group(1)) - except: - pass + self._parseBedCommand(line) time.sleep(0.01) @@ -263,16 +281,29 @@ class VirtualPrinter(): self._sdPrinter = None self.readList.append("Done printing file") + def _waitForHeatup(self, heater): + delta = 0.5 + delay = 1 + if heater.startswith("tool"): + toolNum = int(heater[len("tool"):]) + while self.temp[toolNum] < self.targetTemp[toolNum] - delta or self.temp[toolNum] > self.targetTemp[toolNum] + delta: + self._simulateTemps() + self.readList.append("T:%0.2f /%0.2f" % (self.temp[toolNum], self.targetTemp[toolNum])) + time.sleep(delay) + elif heater == "bed": + while self.bedTemp < self.bedTargetTemp - delta or self.bedTemp > self.bedTargetTemp + delta: + self._simulateTemps() + self.readList.append("B:%0.2f /%0.2f" % (self.bedTemp, self.bedTargetTemp)) + time.sleep(delay) + self.readList.append("ok") + def _deleteSdFile(self, filename): f = os.path.join(self._virtualSd, filename) if os.path.exists(f) and os.path.isfile(f): os.remove(f) self._sendOk() - def readline(self): - if self.readList is None: - return '' - n = 0 + def _simulateTemps(self): timeDiff = self.lastTempAt - time.time() self.lastTempAt = time.time() for i in range(len(self.temp)): @@ -290,6 +321,14 @@ class VirtualPrinter(): self.bedTemp = self.bedTargetTemp if self.bedTemp < 0: self.bedTemp = 0 + + def readline(self): + if self.readList is None: + return '' + n = 0 + + self._simulateTemps() + while len(self.readList) < 1: time.sleep(0.1) n += 1