Beispiel #1
0
	def _gcode_G4(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')
		s_idx = cmd.find('S')
		_timeout = 0
		if p_idx != -1:
			# dwell time is specified in milliseconds
			_timeout = int(cmd[p_idx+1:]) / 1000.0
		elif s_idx != -1:
			# dwell time is specified in seconds
			_timeout = int(cmd[s_idx+1:])
		self._timeout = getNewTimeout("communication") + _timeout
		return cmd
Beispiel #2
0
	def _monitor(self):
		feedbackControls = settings().getFeedbackControls()
		pauseTriggers = settings().getPauseTriggers()
		feedbackErrors = []

		#Open the serial port.
		if not self._openSerial():
			return

		self._log("Connected to: %s, starting monitor" % self._serial)
		if self._baudrate == 0:
			self._log("Starting baud rate detection")
			self._changeState(self.STATE_DETECT_BAUDRATE)
		else:
			self._changeState(self.STATE_CONNECTING)

		#Start monitoring the serial port.
		timeout = getNewTimeout("communication")
		tempRequestTimeout = getNewTimeout("temperature")
		sdStatusRequestTimeout = getNewTimeout("sdStatus")
		startSeen = not settings().getBoolean(["feature", "waitForStartOnConnect"])
		self._heatingUp = False
		swallowOk = False
		supportRepetierTargetTemp = settings().getBoolean(["feature", "repetierTargetTemp"])

		while True:
			try:
				line = self._readline()
				if line is None:
					break
				if line.strip() is not "" and line.isalnum():
					timeout = getNewTimeout("communication")

				##~~ Error handling
				line = self._handleErrors(line)

				##~~ SD file list
				# if we are currently receiving an sd file list, each line is just a filename, so just read it and abort processing
				if self._sdFileList and not "End file list" in line:
					fileinfo = line.strip().split(None, 2)
					if len(fileinfo) > 1:
						# we got extended file information here, so let's split filename and size and try to make them a bit nicer
						filename, size = fileinfo
						filename = filename.lower()
						try:
							size = int(size)
						except ValueError:
							# whatever that was, it was not an integer, so we'll just ignore it and set size to None
							size = None
					else:
						# no extended file information, so only the filename is there and we set size to None
						filename = fileinfo[0].lower()
						size = None

					if self._callback.fileManager.isValidFilename(filename):
						if filterNonAscii(filename):
							self._logger.warn("Got a file from printer's SD that has a non-ascii filename (%s), that shouldn't happen according to the protocol" % filename)
						else:
							self._sdFiles.append((filename, size))
						continue

				##~~ Temperature processing
				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)

					#If we are waiting for an M109 or M190 then measure the time we lost during heatup, so we can remove that time from our printing time estimate.
					if not 'ok' in line and self._heatupWaitStartTime != 0:
						t = time.time()
						self._heatupWaitTimeLost = t - self._heatupWaitStartTime
						self._heatupWaitStartTime = t
				elif supportRepetierTargetTemp and ('TargetExtr' in line or 'TargetBed' in line):
					matchExtr = self._regex_repetierTempExtr.match(line)
					matchBed = self._regex_repetierTempBed.match(line)

					if matchExtr is not None:
						toolNum = int(matchExtr.group(1))
						try:
							target = float(matchExtr.group(2))
							if toolNum in self._temp.keys() and self._temp[toolNum] is not None and isinstance(self._temp[toolNum], tuple):
								(actual, oldTarget) = self._temp[toolNum]
								self._temp[toolNum] = (actual, target)
							else:
								self._temp[toolNum] = (None, target)
							self._callback.mcTempUpdate(self._temp, self._bedTemp)
						except ValueError:
							pass
					elif matchBed is not None:
						try:
							target = float(matchBed.group(1))
							if self._bedTemp is not None and isinstance(self._bedTemp, tuple):
								(actual, oldTarget) = self._bedTemp
								self._bedTemp = (actual, target)
							else:
								self._bedTemp = (None, target)
							self._callback.mcTempUpdate(self._temp, self._bedTemp)
						except ValueError:
							pass

				##~~ SD Card handling
				elif 'SD init fail' in line or 'volume.init failed' in line or 'openRoot failed' in line:
					self._sdAvailable = False
					self._sdFiles = []
					self._callback.mcSdStateChange(self._sdAvailable)
				elif 'Not SD printing' in line:
					if self.isSdFileSelected() and self.isPrinting():
						# something went wrong, printer is reporting that we actually are not printing right now...
						self._sdFilePos = 0
						self._changeState(self.STATE_OPERATIONAL)
				elif 'SD card ok' in line and not self._sdAvailable:
					self._sdAvailable = True
					self.refreshSdFiles()
					self._callback.mcSdStateChange(self._sdAvailable)
				elif 'Begin file list' in line:
					self._sdFiles = []
					self._sdFileList = True
				elif 'End file list' in line:
					self._sdFileList = False
					self._callback.mcSdFiles(self._sdFiles)
				elif 'SD printing byte' in line:
					# answer to M27, at least on Marlin, Repetier and Sprinter: "SD printing byte %d/%d"
					match = self._regex_sdPrintingByte.search(line)
					self._currentFile.setFilepos(int(match.group(1)))
					self._callback.mcProgress()
				elif 'File opened' in line:
					# answer to M23, at least on Marlin, Repetier and Sprinter: "File opened:%s Size:%d"
					match = self._regex_sdFileOpened.search(line)
					self._currentFile = PrintingSdFileInformation(match.group(1), int(match.group(2)))
				elif 'File selected' in line:
					# final answer to M23, at least on Marlin, Repetier and Sprinter: "File selected"
					if self._currentFile is not None:
						self._callback.mcFileSelected(self._currentFile.getFilename(), self._currentFile.getFilesize(), True)
						eventManager().fire(Events.FILE_SELECTED, {
							"file": self._currentFile.getFilename(),
							"origin": self._currentFile.getFileLocation()
						})
				elif 'Writing to file' in line:
					# anwer to M28, at least on Marlin, Repetier and Sprinter: "Writing to file: %s"
					self._printSection = "CUSTOM"
					self._changeState(self.STATE_PRINTING)
					line = "ok"
				elif 'Done printing file' in line:
					# printer is reporting file finished printing
					self._sdFilePos = 0
					self._callback.mcPrintjobDone()
					self._changeState(self.STATE_OPERATIONAL)
					eventManager().fire(Events.PRINT_DONE, {
						"file": self._currentFile.getFilename(),
						"filename": os.path.basename(self._currentFile.getFilename()),
						"origin": self._currentFile.getFileLocation(),
						"time": self.getPrintTime(),
						"layerCount": self._currentLayer
					})
				elif 'Done saving file' in line:
					self.refreshSdFiles()

				##~~ Message handling
				elif line.strip() != '' \
						and line.strip() != 'ok' and not line.startswith("wait") \
						and not line.startswith('Resend:') \
						and line != 'echo:Unknown command:""\n' \
						and self.isOperational():
					self._callback.mcMessage(line)

				##~~ Parsing for feedback commands
				if feedbackControls:
					for name, matcher, template in feedbackControls:
						if name in feedbackErrors:
							# we previously had an error with that one, so we'll skip it now
							continue
						try:
							match = matcher.search(line)
							if match is not None:
								formatFunction = None
								if isinstance(template, str):
									formatFunction = str.format
								elif isinstance(template, unicode):
									formatFunction = unicode.format

								if formatFunction is not None:
									self._callback.mcReceivedRegisteredMessage(name, formatFunction(template, *(match.groups("n/a"))))
						except:
							if not name in feedbackErrors:
								self._logger.info("Something went wrong with feedbackControl \"%s\": " % name, exc_info=True)
								feedbackErrors.append(name)
							pass

				##~~ Parsing for pause triggers
				if pauseTriggers and not self.isStreaming():
					if "enable" in pauseTriggers.keys() and pauseTriggers["enable"].search(line) is not None:
						self.setPause(True)
					elif "disable" in pauseTriggers.keys() and pauseTriggers["disable"].search(line) is not None:
						self.setPause(False)
					elif "toggle" in pauseTriggers.keys() and pauseTriggers["toggle"].search(line) is not None:
						self.setPause(not self.isPaused())

				if "ok" in line and self._heatingUp:
					self._heatingUp = False
					self._callback.mcHeatingUpUpdate(self._heatingUp)

				### Baudrate detection
				if self._state == self.STATE_DETECT_BAUDRATE:
					if line == '' or time.time() > timeout:
						if len(self._baudrateDetectList) < 1:
							self.close()
							self._errorValue = "No more baudrates to test, and no suitable baudrate found."
							self._changeState(self.STATE_ERROR)
							eventManager().fire(Events.ERROR, {"error": self.getErrorString()})
						elif self._baudrateDetectRetry > 0:
							self._baudrateDetectRetry -= 1
							self._serial.write('\n')
							self._log("Baudrate test retry: %d" % (self._baudrateDetectRetry))
							self._sendCommand("M105")
							self._testingBaudrate = True
						else:
							baudrate = self._baudrateDetectList.pop(0)
							try:
								self._serial.baudrate = baudrate
								self._serial.timeout = settings().getFloat(["serial", "timeout", "detection"])
								self._log("Trying baudrate: %d" % (baudrate))
								self._baudrateDetectRetry = 5
								self._baudrateDetectTestOk = 0
								timeout = getNewTimeout("communication")
								self._serial.write('\n')
								self._sendCommand("M105")
								self._testingBaudrate = True
							except:
								self._log("Unexpected error while setting baudrate: %d %s" % (baudrate, getExceptionString()))
					elif 'ok' in line and 'T:' in line:
						self._baudrateDetectTestOk += 1
						if self._baudrateDetectTestOk < 10:
							self._log("Baudrate test ok: %d" % (self._baudrateDetectTestOk))
							self._sendCommand("M105")
						else:
							self._sendCommand("M999")
							self._serial.timeout = settings().getFloat(["serial", "timeout", "connection"])
							self._changeState(self.STATE_OPERATIONAL)
							if self._sdAvailable:
								self.refreshSdFiles()
							else:
								self.initSdCard()
							eventManager().fire(Events.CONNECTED, {"port": self._port, "baudrate": self._baudrate})
					else:
						self._testingBaudrate = False

				### Connection attempt
				elif self._state == self.STATE_CONNECTING:
					if (line == "" or "wait" in line) and startSeen:
						if time.time() > timeout:
							self.close()
						else:
							self._sendCommand("M105")
					elif "start" in line:
						startSeen = True
					elif "ok" in line and startSeen:
						self._changeState(self.STATE_OPERATIONAL)
						if self._sdAvailable:
							self.refreshSdFiles()
						else:
							self.initSdCard()
						eventManager().fire(Events.CONNECTED, {"port": self._port, "baudrate": self._baudrate})
					elif time.time() > timeout:
						self.close()

				### Operational
				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:
						if self._resendDelta is not None:
							self._resendNextCommand()
						elif not self._commandQueue.empty():
							self._sendCommand(self._commandQueue.get())
						else:
							self._sendCommand("M105")
						tempRequestTimeout = getNewTimeout("temperature")
					# resend -> start resend procedure from requested line
					elif line.lower().startswith("resend") or line.lower().startswith("rs"):
						if settings().get(["feature", "swallowOkAfterResend"]):
							swallowOk = True
						self._handleResendRequest(line)

					else:
						positionMatch = self._regex_M114Response.search(line)
						if positionMatch:
							self._positionWhenPaused = (
								float(positionMatch.group(1)),
								float(positionMatch.group(2)),
								float(positionMatch.group(3)),
								float(positionMatch.group(4))
							)

							if self.isPaused():
								self.sendCommand("G1 F9000 X0 Y0 Z%.4f E%.4f" % (self._positionWhenPaused[2] + 5, self._positionWhenPaused[3] - 5))

				### Printing
				elif self._state == self.STATE_PRINTING:
					if line == "" and time.time() > timeout:
						self._log("Communication timeout during printing, forcing a line")
						line = 'ok'

					if self.isSdPrinting():
						if time.time() > tempRequestTimeout and not self._heatingUp:
							self._sendCommand("M105")
							tempRequestTimeout = getNewTimeout("temperature")

						if time.time() > sdStatusRequestTimeout and not self._heatingUp:
							self._sendCommand("M27")
							sdStatusRequestTimeout = getNewTimeout("sdStatus")
					else:
						# Even when printing request the temperature every 5 seconds.
						if time.time() > tempRequestTimeout and not self.isStreaming():
							self._commandQueue.put("M105")
							tempRequestTimeout = getNewTimeout("temperature")

						if "ok" in line and swallowOk:
							swallowOk = False
						elif "ok" in line:
							if self._resendDelta is not None:
								self._resendNextCommand()
							elif not self._commandQueue.empty() and not self.isStreaming():
								self._sendCommand(self._commandQueue.get(), True)
							else:
								self._sendNext()
						elif line.lower().startswith("resend") or line.lower().startswith("rs"):
							if settings().get(["feature", "swallowOkAfterResend"]):
								swallowOk = True
							self._handleResendRequest(line)

			except:
				self._logger.exception("Something crashed inside the serial connection loop, please report this to AstroPrint:")

				errorMsg = "See astrobox.log for details"
				self._log(errorMsg)
				self._errorValue = errorMsg
				self._changeState(self.STATE_ERROR)
				eventManager().fire(Events.ERROR, {"error": self.getErrorString()})

		self._log("Connection closed, closing down monitor")
Beispiel #3
0
	def _monitor(self):
		feedbackControls = settings().getFeedbackControls()
		pauseTriggers = settings().getPauseTriggers()
		feedbackErrors = []

		#Open the serial port.
		if not self._openSerial():
			return

		self._log("Connected to: %s, starting monitor" % self._serial)
		if self._baudrate == 0:
			self._log("Starting baud rate detection")
			self._changeState(self.STATE_DETECT_BAUDRATE)
		else:
			self._changeState(self.STATE_CONNECTING)

		#Start monitoring the serial port.
		timeout = getNewTimeout("communication")
		tempRequestTimeout = getNewTimeout("temperature")
		sdStatusRequestTimeout = getNewTimeout("sdStatus")
		startSeen = not settings().getBoolean(["feature", "waitForStartOnConnect"])
		heatingUp = False
		swallowOk = False
		supportRepetierTargetTemp = settings().getBoolean(["feature", "repetierTargetTemp"])

		while True:
			try:
				line = self._readline()
				if line is None:
					break
				if line.strip() is not "":
					timeout = getNewTimeout("communication")

				##~~ Error handling
				line = self._handleErrors(line)

				##~~ SD file list
				# if we are currently receiving an sd file list, each line is just a filename, so just read it and abort processing
				if self._sdFileList and isGcodeFileName(line.strip().lower()) and not 'End file list' in line:
					filename = line.strip().lower()
					if filterNonAscii(filename):
						self._logger.warn("Got a file from printer's SD that has a non-ascii filename (%s), that shouldn't happen according to the protocol" % filename)
					else:
						self._sdFiles.append(filename)
					continue

				##~~ Temperature processing
				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)

					#If we are waiting for an M109 or M190 then measure the time we lost during heatup, so we can remove that time from our printing time estimate.
					if not 'ok' in line:
						heatingUp = True
						if self._heatupWaitStartTime != 0:
							t = time.time()
							self._heatupWaitTimeLost = t - self._heatupWaitStartTime
							self._heatupWaitStartTime = t
				elif supportRepetierTargetTemp:
					matchExtr = self._regex_repetierTempExtr.match(line)
					matchBed = self._regex_repetierTempBed.match(line)

					if matchExtr is not None:
						toolNum = int(matchExtr.group(1))
						try:
							target = float(matchExtr.group(2))
							if toolNum in self._temp.keys() and self._temp[toolNum] is not None and isinstance(self._temp[toolNum], tuple):
								(actual, oldTarget) = self._temp[toolNum]
								self._temp[toolNum] = (actual, target)
							else:
								self._temp[toolNum] = (None, target)
							self._callback.mcTempUpdate(self._temp, self._bedTemp)
						except ValueError:
							pass
					elif matchBed is not None:
						try:
							target = float(matchBed.group(1))
							if self._bedTemp is not None and isinstance(self._bedTemp, tuple):
								(actual, oldTarget) = self._bedTemp
								self._bedTemp = (actual, target)
							else:
								self._bedTemp = (None, target)
							self._callback.mcTempUpdate(self._temp, self._bedTemp)
						except ValueError:
							pass

				##~~ SD Card handling
				elif 'SD init fail' in line or 'volume.init failed' in line or 'openRoot failed' in line:
					self._sdAvailable = False
					self._sdFiles = []
					self._callback.mcSdStateChange(self._sdAvailable)
				elif 'Not SD printing' in line:
					if self.isSdFileSelected() and self.isPrinting():
						# something went wrong, printer is reporting that we actually are not printing right now...
						self._sdFilePos = 0
						self._changeState(self.STATE_OPERATIONAL)
				elif 'SD card ok' in line and not self._sdAvailable:
					self._sdAvailable = True
					self.refreshSdFiles()
					self._callback.mcSdStateChange(self._sdAvailable)
				elif 'Begin file list' in line:
					self._sdFiles = []
					self._sdFileList = True
				elif 'End file list' in line:
					self._sdFileList = False
					self._callback.mcSdFiles(self._sdFiles)
				elif 'SD printing byte' in line:
					# answer to M27, at least on Marlin, Repetier and Sprinter: "SD printing byte %d/%d"
					match = self._regex_sdPrintingByte.search(line)
					self._currentFile.setFilepos(int(match.group(1)))
					self._callback.mcProgress()
				elif 'File opened' in line:
					# answer to M23, at least on Marlin, Repetier and Sprinter: "File opened:%s Size:%d"
					match = self._regex_sdFileOpened.search(line)
					self._currentFile = PrintingSdFileInformation(match.group(1), int(match.group(2)))
				elif 'File selected' in line:
					# final answer to M23, at least on Marlin, Repetier and Sprinter: "File selected"
					if self._currentFile is not None:
						self._callback.mcFileSelected(self._currentFile.getFilename(), self._currentFile.getFilesize(), True)
						eventManager().fire(Events.FILE_SELECTED, {
							"file": self._currentFile.getFilename(),
							"origin": self._currentFile.getFileLocation()
						})
				elif 'Writing to file' in line:
					# anwer to M28, at least on Marlin, Repetier and Sprinter: "Writing to file: %s"
					self._printSection = "CUSTOM"
					self._changeState(self.STATE_PRINTING)
					line = "ok"
				elif 'Done printing file' in line:
					# printer is reporting file finished printing
					self._sdFilePos = 0
					self._callback.mcPrintjobDone()
					self._changeState(self.STATE_OPERATIONAL)
					eventManager().fire(Events.PRINT_DONE, {
						"file": self._currentFile.getFilename(),
						"filename": os.path.basename(self._currentFile.getFilename()),
						"origin": self._currentFile.getFileLocation(),
						"time": self.getPrintTime()
					})
				elif 'Done saving file' in line:
					self.refreshSdFiles()

				##~~ Message handling
				elif line.strip() != '' \
						and line.strip() != 'ok' and not line.startswith("wait") \
						and not line.startswith('Resend:') \
						and line != 'echo:Unknown command:""\n' \
						and self.isOperational():
					self._callback.mcMessage(line)

				##~~ Parsing for feedback commands
				if feedbackControls:
					for name, matcher, template in feedbackControls:
						if name in feedbackErrors:
							# we previously had an error with that one, so we'll skip it now
							continue
						try:
							match = matcher.search(line)
							if match is not None:
								formatFunction = None
								if isinstance(template, str):
									formatFunction = str.format
								elif isinstance(template, unicode):
									formatFunction = unicode.format

								if formatFunction is not None:
									self._callback.mcReceivedRegisteredMessage(name, formatFunction(template, *(match.groups("n/a"))))
						except:
							if not name in feedbackErrors:
								self._logger.info("Something went wrong with feedbackControl \"%s\": " % name, exc_info=True)
								feedbackErrors.append(name)
							pass

				##~~ Parsing for pause triggers
				if pauseTriggers and not self.isStreaming():
					if "enable" in pauseTriggers.keys() and pauseTriggers["enable"].search(line) is not None:
						self.setPause(True)
					elif "disable" in pauseTriggers.keys() and pauseTriggers["disable"].search(line) is not None:
						self.setPause(False)
					elif "toggle" in pauseTriggers.keys() and pauseTriggers["toggle"].search(line) is not None:
						self.setPause(not self.isPaused())

				if "ok" in line and heatingUp:
					heatingUp = False

				### Baudrate detection
				if self._state == self.STATE_DETECT_BAUDRATE:
					if line == '' or time.time() > timeout:
						if len(self._baudrateDetectList) < 1:
							self.close()
							self._errorValue = "No more baudrates to test, and no suitable baudrate found."
							self._changeState(self.STATE_ERROR)
							eventManager().fire(Events.ERROR, {"error": self.getErrorString()})
						elif self._baudrateDetectRetry > 0:
							self._baudrateDetectRetry -= 1
							self._serial.write('\n')
							self._log("Baudrate test retry: %d" % (self._baudrateDetectRetry))
							self._sendCommand("M105")
							self._testingBaudrate = True
						else:
							baudrate = self._baudrateDetectList.pop(0)
							try:
								self._serial.baudrate = baudrate
								self._serial.timeout = settings().getFloat(["serial", "timeout", "detection"])
								self._log("Trying baudrate: %d" % (baudrate))
								self._baudrateDetectRetry = 5
								self._baudrateDetectTestOk = 0
								timeout = getNewTimeout("communication")
								self._serial.write('\n')
								self._sendCommand("M105")
								self._testingBaudrate = True
							except:
								self._log("Unexpected error while setting baudrate: %d %s" % (baudrate, getExceptionString()))
					elif 'ok' in line and 'T:' in line:
						self._baudrateDetectTestOk += 1
						if self._baudrateDetectTestOk < 10:
							self._log("Baudrate test ok: %d" % (self._baudrateDetectTestOk))
							self._sendCommand("M105")
						else:
							self._sendCommand("M999")
							self._serial.timeout = settings().getFloat(["serial", "timeout", "connection"])
							self._changeState(self.STATE_OPERATIONAL)
							if self._sdAvailable:
								self.refreshSdFiles()
							else:
								self.initSdCard()
							eventManager().fire(Events.CONNECTED, {"port": self._port, "baudrate": self._baudrate})
					else:
						self._testingBaudrate = False

				### Connection attempt
				elif self._state == self.STATE_CONNECTING:
					if (line == "" or "wait" in line) and startSeen:
						self._sendCommand("M105")
					elif "start" in line:
						startSeen = True
					elif "ok" in line and startSeen:
						self._changeState(self.STATE_OPERATIONAL)
						if self._sdAvailable:
							self.refreshSdFiles()
						else:
							self.initSdCard()
						eventManager().fire(Events.CONNECTED, {"port": self._port, "baudrate": self._baudrate})
					elif time.time() > timeout:
						self.close()

				### Operational
				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:
						if self._resendDelta is not None:
							self._resendNextCommand()
						elif not self._commandQueue.empty():
							self._sendCommand(self._commandQueue.get())
						else:
							self._sendCommand("M105")
						tempRequestTimeout = getNewTimeout("temperature")
					# resend -> start resend procedure from requested line
					elif line.lower().startswith("resend") or line.lower().startswith("rs"):
						if settings().get(["feature", "swallowOkAfterResend"]):
							swallowOk = True
						self._handleResendRequest(line)

				### Printing
				elif self._state == self.STATE_PRINTING:
					if line == "" and time.time() > timeout:
						self._log("Communication timeout during printing, forcing a line")
						line = 'ok'

					if self.isSdPrinting():
						if time.time() > tempRequestTimeout and not heatingUp:
							self._sendCommand("M105")
							tempRequestTimeout = getNewTimeout("temperature")

						if time.time() > sdStatusRequestTimeout and not heatingUp:
							self._sendCommand("M27")
							sdStatusRequestTimeout = getNewTimeout("sdStatus")
					else:
						# Even when printing request the temperature every 5 seconds.
						if time.time() > tempRequestTimeout and not self.isStreaming():
							self._commandQueue.put("M105")
							tempRequestTimeout = getNewTimeout("temperature")

						if "ok" in line and swallowOk:
							swallowOk = False
						elif "ok" in line:
							if self._resendDelta is not None:
								self._resendNextCommand()
							elif not self._commandQueue.empty() and not self.isStreaming():
								self._sendCommand(self._commandQueue.get())
							else:
								self._sendNext()
						elif line.lower().startswith("resend") or line.lower().startswith("rs"):
							if settings().get(["feature", "swallowOkAfterResend"]):
								swallowOk = True
							self._handleResendRequest(line)
			except:
				self._logger.exception("Something crashed inside the serial connection loop, please report this in OctoPrint's bug tracker:")

				errorMsg = "See octoprint.log for details"
				self._log(errorMsg)
				self._errorValue = errorMsg
				self._changeState(self.STATE_ERROR)
				eventManager().fire(Events.ERROR, {"error": self.getErrorString()})
		self._log("Connection closed, closing down monitor")
Beispiel #4
0
	def _monitor(self):
		feedbackControls = settings().getFeedbackControls()
		pauseTriggers = settings().getPauseTriggers()
		feedbackErrors = []

		#Open the serial port.
		if not self._openSerial():
			return

		self._log("Connected to: %s, starting monitor" % self._serial)
		if self._baudrate == 0:
			self._log("Starting baud rate detection")
			self._changeState(self.STATE_DETECT_BAUDRATE)
		else:
			self._changeState(self.STATE_CONNECTING)

		#Start monitoring the serial port.
		timeout = getNewTimeout("communication")
		tempRequestTimeout = timeout
		sdStatusRequestTimeout = timeout
		startSeen = not settings().getBoolean(["feature", "waitForStartOnConnect"])
		heatingUp = False
		swallowOk = False
		while True:
			try:
				line = self._readline()
				if line is None:
					break

				##~~ Error handling
				line = self._handleErrors(line)

				##~~ SD file list
				# if we are currently receiving an sd file list, each line is just a filename, so just read it and abort processing
				if self._sdFileList and isGcodeFileName(line.strip().lower()) and not 'End file list' in line:
					filename = line.strip().lower()
					if filterNonAscii(filename):
						self._logger.warn("Got a file from printer's SD that has a non-ascii filename (%s), that shouldn't happen according to the protocol" % filename)
					else:
						self._sdFiles.append(filename)
					continue

				##~~ Temperature processing
				if ' T:' in line or line.startswith('T:'):
					try:
						self._temp = float(self._regex_float.search(line.split('T:')[1]).group(0))
						if ' B:' in line:
							self._bedTemp = float(self._regex_float.search(line.split(' B:')[1]).group(0))

						self._callback.mcTempUpdate(self._temp, self._bedTemp, self._targetTemp, self._bedTargetTemp)
					except ValueError:
						# catch conversion issues, we'll rather just not get the temperature update instead of killing the connection
						pass

					#If we are waiting for an M109 or M190 then measure the time we lost during heatup, so we can remove that time from our printing time estimate.
					if not 'ok' in line:
						heatingUp = True
						if self._heatupWaitStartTime != 0:
							t = time.time()
							self._heatupWaitTimeLost = t - self._heatupWaitStartTime
							self._heatupWaitStartTime = t

				##~~ SD Card handling
				elif 'SD init fail' in line or 'volume.init failed' in line or 'openRoot failed' in line:
					self._sdAvailable = False
					self._sdFiles = []
					self._callback.mcSdStateChange(self._sdAvailable)
				elif 'Not SD printing' in line:
					if self.isSdFileSelected() and self.isPrinting():
						# something went wrong, printer is reporting that we actually are not printing right now...
						self._sdFilePos = 0
						self._changeState(self.STATE_OPERATIONAL)
				elif 'SD card ok' in line:
					self._sdAvailable = True
					self.refreshSdFiles()
					self._callback.mcSdStateChange(self._sdAvailable)
				elif 'Begin file list' in line:
					self._sdFiles = []
					self._sdFileList = True
				elif 'End file list' in line:
					self._sdFileList = False
					self._callback.mcSdFiles(self._sdFiles)
				elif 'SD printing byte' in line:
					# answer to M27, at least on Marlin, Repetier and Sprinter: "SD printing byte %d/%d"
					match = self._regex_sdPrintingByte.search(line)
					self._currentFile.setFilepos(int(match.group(1)))
					self._callback.mcProgress()
				elif 'File opened' in line:
					# answer to M23, at least on Marlin, Repetier and Sprinter: "File opened:%s Size:%d"
					match = self._regex_sdFileOpened.search(line)
					self._currentFile = PrintingSdFileInformation(match.group(1), int(match.group(2)))
				elif 'File selected' in line:
					# final answer to M23, at least on Marlin, Repetier and Sprinter: "File selected"
					if self._currentFile is not None:
						self._callback.mcFileSelected(self._currentFile.getFilename(), self._currentFile.getFilesize(), True)
						eventManager().fire("FileSelected", self._currentFile.getFilename())
				elif 'Writing to file' in line:
					# anwer to M28, at least on Marlin, Repetier and Sprinter: "Writing to file: %s"
					self._changeState(self.STATE_PRINTING)
					line = "ok"
				elif 'Done printing file' in line:
					# printer is reporting file finished printing
					self._sdFilePos = 0
					self._callback.mcPrintjobDone()
					self._changeState(self.STATE_OPERATIONAL)
					eventManager().fire("PrintDone")
				elif 'Done saving file' in line:
					self.refreshSdFiles()

				##~~ Message handling
				elif line.strip() != '' \
						and line.strip() != 'ok' and not line.startswith("wait") \
						and not line.startswith('Resend:') \
						and line != 'echo:Unknown command:""\n' \
						and self.isOperational():
					self._callback.mcMessage(line)

				##~~ Parsing for feedback commands
				if feedbackControls:
					for name, matcher, template in feedbackControls:
						if name in feedbackErrors:
							# we previously had an error with that one, so we'll skip it now
							continue
						try:
							match = matcher.search(line)
							if match is not None:
								formatFunction = None
								if isinstance(template, str):
									formatFunction = str.format
								elif isinstance(template, unicode):
									formatFunction = unicode.format

								if formatFunction is not None:
									self._callback.mcReceivedRegisteredMessage(name, formatFunction(template, *(match.groups("n/a"))))
						except:
							if not name in feedbackErrors:
								self._logger.info("Something went wrong with feedbackControl \"%s\": " % name, exc_info=True)
								feedbackErrors.append(name)
							pass

				##~~ Parsing for pause triggers
				if pauseTriggers and not self.isStreaming():
					if "enable" in pauseTriggers.keys() and pauseTriggers["enable"].search(line) is not None:
						self.setPause(True)
					elif "disable" in pauseTriggers.keys() and pauseTriggers["disable"].search(line) is not None:
						self.setPause(False)
					elif "toggle" in pauseTriggers.keys() and pauseTriggers["toggle"].search(line) is not None:
						self.setPause(not self.isPaused())

				if "ok" in line and heatingUp:
					heatingUp = False

				### Baudrate detection
				if self._state == self.STATE_DETECT_BAUDRATE:
					if line == '' or time.time() > timeout:
						if len(self._baudrateDetectList) < 1:
							self.close()
							self._errorValue = "No more baudrates to test, and no suitable baudrate found."
							self._changeState(self.STATE_ERROR)
							eventManager().fire("Error", self.getErrorString())
						elif self._baudrateDetectRetry > 0:
							self._baudrateDetectRetry -= 1
							self._serial.write('\n')
							self._log("Baudrate test retry: %d" % (self._baudrateDetectRetry))
							self._sendCommand("M105")
							self._testingBaudrate = True
						else:
							baudrate = self._baudrateDetectList.pop(0)
							try:
								self._serial.baudrate = baudrate
								self._serial.timeout = settings().getFloat(["serial", "timeout", "detection"])
								self._log("Trying baudrate: %d" % (baudrate))
								self._baudrateDetectRetry = 5
								self._baudrateDetectTestOk = 0
								timeout = getNewTimeout("communication")
								self._serial.write('\n')
								self._sendCommand("M105")
								self._testingBaudrate = True
							except:
								self._log("Unexpected error while setting baudrate: %d %s" % (baudrate, getExceptionString()))
					elif 'ok' in line and 'T:' in line:
						self._baudrateDetectTestOk += 1
						if self._baudrateDetectTestOk < 10:
							self._log("Baudrate test ok: %d" % (self._baudrateDetectTestOk))
							self._sendCommand("M105")
						else:
							self._sendCommand("M999")
							self._serial.timeout = settings().getFloat(["serial", "timeout", "connection"])
							self._changeState(self.STATE_OPERATIONAL)
							if self._sdAvailable:
								self.refreshSdFiles()
							eventManager().fire("Connected", "%s at %s baud" % (self._port, self._baudrate))
					else:
						self._testingBaudrate = False

				### Connection attempt
				elif self._state == self.STATE_CONNECTING:
					if (line == "" or "wait" in line) and startSeen:
						self._sendCommand("M105")
					elif "start" in line:
						startSeen = True
					elif "ok" in line and startSeen:
						self._changeState(self.STATE_OPERATIONAL)
						if not self._sdAvailable:
							self.initSdCard()
						eventManager().fire("Connected", "%s at %s baud" % (self._port, self._baudrate))
					elif time.time() > timeout:
						self.close()

				### Operational
				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:
						if self._resendDelta is not None:
							self._resendNextCommand()
						elif not self._commandQueue.empty():
							self._sendCommand(self._commandQueue.get())
						else:
							self._sendCommand("M105")
						tempRequestTimeout = getNewTimeout("communication")
					# resend -> start resend procedure from requested line
					elif line.lower().startswith("resend") or line.lower().startswith("rs"):
						if settings().get(["feature", "swallowOkAfterResend"]):
							swallowOk = True
						self._handleResendRequest(line)

				### Printing
				elif self._state == self.STATE_PRINTING:
					if line == "" and time.time() > timeout:
						self._log("Communication timeout during printing, forcing a line")
						line = 'ok'

					if self.isSdPrinting():
						if time.time() > tempRequestTimeout and not heatingUp:
							self._sendCommand("M105")
							tempRequestTimeout = getNewTimeout("communication")

						if time.time() > sdStatusRequestTimeout and not heatingUp:
							self._sendCommand("M27")
							sdStatusRequestTimeout = time.time() + 1

						if 'ok' or 'SD printing byte' in line:
							timeout = getNewTimeout("communication")
					else:
						# Even when printing request the temperature every 5 seconds.
						if time.time() > tempRequestTimeout and not self.isStreaming():
							self._commandQueue.put("M105")
							tempRequestTimeout = getNewTimeout("communication")

						if "ok" in line and swallowOk:
							swallowOk = False
						elif "ok" in line:
							timeout = getNewTimeout("communication")
							if self._resendDelta is not None:
								self._resendNextCommand()
							elif not self._commandQueue.empty() and not self.isStreaming():
								self._sendCommand(self._commandQueue.get())
							else:
								self._sendNext()
						elif line.lower().startswith("resend") or line.lower().startswith("rs"):
							if settings().get(["feature", "swallowOkAfterResend"]):
								swallowOk = True
							self._handleResendRequest(line)
			except:
				self._logger.exception("Something crashed inside the serial connection loop, please report this in OctoPrint's bug tracker:")

				errorMsg = "See octoprint.log for details"
				self._log(errorMsg)
				self._errorValue = errorMsg
				self._changeState(self.STATE_ERROR)
				eventManager().fire("Error", self.getErrorString())
		self._log("Connection closed, closing down monitor")
Beispiel #5
0
	def _monitor(self):
		feedbackControls = settings().getFeedbackControls()
		pauseTriggers = settings().getPauseTriggers()
		feedbackErrors = []

		#Open the serial port.
		if self._port == 'AUTO':
			self._changeState(self.STATE_DETECT_SERIAL)
			programmer = stk500v2.Stk500v2()
			self._log("Serial port list: %s" % (str(serialList())))
			for p in serialList():
				try:
					self._log("Connecting to: %s" % (p))
					programmer.connect(p)
					self._serial = programmer.leaveISP()
					break
				except ispBase.IspError as (e):
					self._log("Error while connecting to %s: %s" % (p, str(e)))
					pass
				except:
					self._log("Unexpected error while connecting to serial port: %s %s" % (p, getExceptionString()))
				programmer.close()
		elif self._port == 'VIRTUAL':
			self._changeState(self.STATE_OPEN_SERIAL)
			self._serial = VirtualPrinter()
		else:
			self._changeState(self.STATE_OPEN_SERIAL)
			try:
				self._log("Connecting to: %s" % (self._port))
				if self._baudrate == 0:
					self._serial = serial.Serial(str(self._port), 115200, timeout=0.1, writeTimeout=10000)
				else:
					self._serial = serial.Serial(str(self._port), self._baudrate, timeout=settings().getFloat(["serial", "timeout", "connection"]), writeTimeout=10000)
			except:
				self._log("Unexpected error while connecting to serial port: %s %s" % (self._port, getExceptionString()))
		if self._serial == None:
			self._log("Failed to open serial port (%s)" % (self._port))
			self._errorValue = 'Failed to autodetect serial port.'
			self._changeState(self.STATE_ERROR)
			eventManager().fire("Error", self.getErrorString())
			return
		self._log("Connected to: %s, starting monitor" % (self._serial))
		if self._baudrate == 0:
			self._changeState(self.STATE_DETECT_BAUDRATE)
		else:
			self._changeState(self.STATE_CONNECTING)

		#Start monitoring the serial port.
		timeout = getNewTimeout("communication")
		tempRequestTimeout = timeout
		sdStatusRequestTimeout = timeout
		startSeen = not settings().getBoolean(["feature", "waitForStartOnConnect"])
		heatingUp = False
		swallowOk = False
		while True:
			try:
				line = self._readline()
				if line == None:
					break

				##~~ Error handling
				# No matter the state, if we see an error, goto the error state and store the error for reference.
				if line.startswith('Error:'):
					#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 re.match('Error:[0-9]\n', line):
						line = line.rstrip() + self._readline()
					#Skip the communication errors, as those get corrected.
					if 'checksum mismatch' in line \
							or 'Wrong checksum' in line \
							or 'Line Number is not Last Line Number' in line \
							or 'expected line' in line \
							or 'No Line Number with checksum' in line \
							or 'No Checksum with line number' in line \
							or 'Missing checksum' in line:
						pass
					elif not self.isError():
						self._errorValue = line[6:]
						self._changeState(self.STATE_ERROR)
						eventManager().fire("Error", self.getErrorString())

				##~~ SD file list
				# if we are currently receiving an sd file list, each line is just a filename, so just read it and abort processing
				if self._sdFileList and not 'End file list' in line:
					self._sdFiles.append(line.strip().lower())
					continue

				##~~ Temperature processing
				if ' T:' in line or line.startswith('T:'):
					try:
						self._temp = float(re.search("-?[0-9\.]*", line.split('T:')[1]).group(0))
						if ' B:' in line:
							self._bedTemp = float(re.search("-?[0-9\.]*", line.split(' B:')[1]).group(0))

						self._callback.mcTempUpdate(self._temp, self._bedTemp, self._targetTemp, self._bedTargetTemp)
					except ValueError:
						# catch conversion issues, we'll rather just not get the temperature update instead of killing the connection
						pass

					#If we are waiting for an M109 or M190 then measure the time we lost during heatup, so we can remove that time from our printing time estimate.
					if not 'ok' in line:
						heatingUp = True
						if self._heatupWaitStartTime != 0:
							t = time.time()
							self._heatupWaitTimeLost = t - self._heatupWaitStartTime
							self._heatupWaitStartTime = t

				##~~ SD Card handling
				elif 'SD init fail' in line:
					self._sdAvailable = False
					self._sdFiles = []
					self._callback.mcSdStateChange(self._sdAvailable)
				elif 'Not SD printing' in line:
					if self.isSdFileSelected() and self.isPrinting():
						# something went wrong, printer is reporting that we actually are not printing right now...
						self._sdFilePos = 0
						self._changeState(self.STATE_OPERATIONAL)
				elif 'SD card ok' in line:
					self._sdAvailable = True
					self.refreshSdFiles()
					self._callback.mcSdStateChange(self._sdAvailable)
				elif 'Begin file list' in line:
					self._sdFiles = []
					self._sdFileList = True
				elif 'End file list' in line:
					self._sdFileList = False
					self._callback.mcSdFiles(self._sdFiles)
				elif 'SD printing byte' in line:
					# answer to M27, at least on Marlin, Repetier and Sprinter: "SD printing byte %d/%d"
					match = re.search("([0-9]*)/([0-9]*)", line)
					self._currentFile.setFilepos(int(match.group(1)))
					self._callback.mcProgress()
				elif 'File opened' in line:
					# answer to M23, at least on Marlin, Repetier and Sprinter: "File opened:%s Size:%d"
					match = re.search("File opened:\s*(.*?)\s+Size:\s*([0-9]*)", line)
					self._currentFile = PrintingSdFileInformation(match.group(1), int(match.group(2)))
				elif 'File selected' in line:
					# final answer to M23, at least on Marlin, Repetier and Sprinter: "File selected"
					self._callback.mcFileSelected(self._currentFile.getFilename(), self._currentFile.getFilesize(), True)
					eventManager().fire("FileSelected", self._currentFile.getFilename())
				elif 'Writing to file' in line:
					# anwer to M28, at least on Marlin, Repetier and Sprinter: "Writing to file: %s"
					self._printSection = "CUSTOM"
					self._changeState(self.STATE_PRINTING)
				elif 'Done printing file' in line:
					# printer is reporting file finished printing
					self._sdFilePos = 0
					self._callback.mcPrintjobDone()
					self._changeState(self.STATE_OPERATIONAL)
					eventManager().fire("PrintDone")

				##~~ Message handling
				elif line.strip() != '' and line.strip() != 'ok' and not line.startswith("wait") and not line.startswith('Resend:') and line != 'echo:Unknown command:""\n' and self.isOperational():
					self._callback.mcMessage(line)

				##~~ Parsing for feedback commands
				if feedbackControls:
					for name, matcher, template in feedbackControls:
						try:
							match = matcher.search(line)
							if match is not None:
								format = None
								if isinstance(template, str):
									format = str.format
								elif isinstance(template, unicode):
									format = unicode.format

								if format is not None:
									self._callback.mcReceivedRegisteredMessage(name, format(template, *(match.groups("n/a"))))
						except:
							if not name in feedbackErrors:
								self._logger.info("Something went wrong with feedbackControl \"%s\": " % name, exc_info=True)
								feedbackErrors.append(name)
							pass

				##~~ Parsing for pause triggers
				if pauseTriggers:
					if "enable" in pauseTriggers.keys() and pauseTriggers["enable"].search(line) is not None:
						self.setPause(True)
					elif "disable" in pauseTriggers.keys() and pauseTriggers["disable"].search(line) is not None:
						self.setPause(False)
					elif "toggle" in pauseTriggers.keys() and pauseTriggers["toggle"].search(line) is not None:
						self.setPause(not self.isPaused())

				if "ok" in line and heatingUp:
					heatingUp = False

				### Baudrate detection
				if self._state == self.STATE_DETECT_BAUDRATE:
					if line == '' or time.time() > timeout:
						if len(self._baudrateDetectList) < 1:
							self.close()
							self._errorValue = "No more baudrates to test, and no suitable baudrate found."
							self._changeState(self.STATE_ERROR)
							eventManager().fire("Error", self.getErrorString())
						elif self._baudrateDetectRetry > 0:
							self._baudrateDetectRetry -= 1
							self._serial.write('\n')
							self._log("Baudrate test retry: %d" % (self._baudrateDetectRetry))
							self._sendCommand("M105")
							self._testingBaudrate = True
						else:
							baudrate = self._baudrateDetectList.pop(0)
							try:
								self._serial.baudrate = baudrate
								self._serial.timeout = settings().getFloat(["serial", "timeout", "detection"])
								self._log("Trying baudrate: %d" % (baudrate))
								self._baudrateDetectRetry = 5
								self._baudrateDetectTestOk = 0
								timeout = getNewTimeout("communication")
								self._serial.write('\n')
								self._sendCommand("M105")
								self._testingBaudrate = True
							except:
								self._log("Unexpected error while setting baudrate: %d %s" % (baudrate, getExceptionString()))
					elif 'ok' in line and 'T:' in line:
						self._baudrateDetectTestOk += 1
						if self._baudrateDetectTestOk < 10:
							self._log("Baudrate test ok: %d" % (self._baudrateDetectTestOk))
							self._sendCommand("M105")
						else:
							self._sendCommand("M999")
							self._serial.timeout = settings().getFloat(["serial", "timeout", "connection"])
							self._changeState(self.STATE_OPERATIONAL)
							if self._sdAvailable:
								self.refreshSdFiles()
							eventManager().fire("Connected", "%s at %s baud" % (self._port, self._baudrate))
					else:
						self._testingBaudrate = False

				### Connection attempt
				elif self._state == self.STATE_CONNECTING:
					if (line == "" or "wait" in line) and startSeen:
						self._sendCommand("M105")
					elif "start" in line:
						startSeen = True
					elif "ok" in line and startSeen:
						self._changeState(self.STATE_OPERATIONAL)
						if self._sdAvailable:
							self.refreshSdFiles()
						eventManager().fire("Connected", "%s at %s baud" % (self._port, self._baudrate))
					elif time.time() > timeout:
						self.close()

				### Operational
				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:
						if self._resendDelta is not None:
							self._resendNextCommand()
						elif not self._commandQueue.empty():
							self._sendCommand(self._commandQueue.get())
						else:
							self._sendCommand("M105")
						tempRequestTimeout = getNewTimeout("communication")
					# resend -> start resend procedure from requested line
					elif line.lower().startswith("resend") or line.lower().startswith("rs"):
						if settings().get(["feature", "swallowOkAfterResend"]):
							swallowOk = True
						self._handleResendRequest(line)

				### Printing
				elif self._state == self.STATE_PRINTING:
					if line == "" and time.time() > timeout:
						self._log("Communication timeout during printing, forcing a line")
						line = 'ok'

					if self.isSdPrinting():
						if time.time() > tempRequestTimeout and not heatingUp:
							self._sendCommand("M105")
							tempRequestTimeout = getNewTimeout("communication")

						if time.time() > sdStatusRequestTimeout and not heatingUp:
							self._sendCommand("M27")
							sdStatusRequestTimeout = time.time() + 1

						if 'ok' or 'SD printing byte' in line:
							timeout = getNewTimeout("communication")
					else:
						# Even when printing request the temperature every 5 seconds.
						if time.time() > tempRequestTimeout and not self.isStreaming():
							self._commandQueue.put("M105")
							tempRequestTimeout = getNewTimeout("communication")

						if "ok" in line and swallowOk:
							swallowOk = False
						elif "ok" in line:
							timeout = getNewTimeout("communication")
							if self._resendDelta is not None:
								self._resendNextCommand()
							elif not self._commandQueue.empty() and not self.isStreaming():
								self._sendCommand(self._commandQueue.get())
							else:
								self._sendNext()
						elif line.lower().startswith("resend") or line.lower().startswith("rs"):
							if settings().get(["feature", "swallowOkAfterResend"]):
								swallowOk = True
							self._handleResendRequest(line)
			except:
				self._logger.exception("Something crashed inside the serial connection loop, please report this in OctoPrint's bug tracker:")

				errorMsg = "See octoprint.log for details"
				self._log(errorMsg)
				self._errorValue = errorMsg
				self._changeState(self.STATE_ERROR)
				eventManager().fire("Error", self.getErrorString())
		self._log("Connection closed, closing down monitor")
Beispiel #6
0
    def _monitor(self):
        feedbackControls = settings().getFeedbackControls()
        pauseTriggers = settings().getPauseTriggers()
        feedbackErrors = []

        #Open the serial port.
        if self._port == 'AUTO':
            self._changeState(self.STATE_DETECT_SERIAL)
            programmer = stk500v2.Stk500v2()
            self._log("Serial port list: %s" % (str(serialList())))
            for p in serialList():
                try:
                    self._log("Connecting to: %s" % (p))
                    programmer.connect(p)
                    self._serial = programmer.leaveISP()
                    break
                except ispBase.IspError as (e):
                    self._log("Error while connecting to %s: %s" % (p, str(e)))
                    pass
                except:
                    self._log(
                        "Unexpected error while connecting to serial port: %s %s"
                        % (p, getExceptionString()))
                programmer.close()
        elif self._port == 'VIRTUAL':
            self._changeState(self.STATE_OPEN_SERIAL)
            self._serial = VirtualPrinter()
        else:
            self._changeState(self.STATE_OPEN_SERIAL)
            try:
                self._log("Connecting to: %s" % (self._port))
                if self._baudrate == 0:
                    self._serial = serial.Serial(str(self._port),
                                                 115200,
                                                 timeout=0.1,
                                                 writeTimeout=10000)
                else:
                    self._serial = serial.Serial(str(self._port),
                                                 self._baudrate,
                                                 timeout=settings().getFloat([
                                                     "serial", "timeout",
                                                     "connection"
                                                 ]),
                                                 writeTimeout=10000)
            except:
                self._log(
                    "Unexpected error while connecting to serial port: %s %s" %
                    (self._port, getExceptionString()))
        if self._serial == None:
            self._log("Failed to open serial port (%s)" % (self._port))
            self._errorValue = 'Failed to autodetect serial port.'
            self._changeState(self.STATE_ERROR)
            eventManager().fire("Error", self.getErrorString())
            return
        self._log("Connected to: %s, starting monitor" % (self._serial))
        if self._baudrate == 0:
            self._changeState(self.STATE_DETECT_BAUDRATE)
        else:
            self._changeState(self.STATE_CONNECTING)

        #Start monitoring the serial port.
        timeout = getNewTimeout("communication")
        tempRequestTimeout = timeout
        sdStatusRequestTimeout = timeout
        startSeen = not settings().getBoolean(
            ["feature", "waitForStartOnConnect"])
        heatingUp = False
        swallowOk = False
        while True:
            try:
                line = self._readline()
                if line == None:
                    break

                ##~~ Error handling
                # No matter the state, if we see an error, goto the error state and store the error for reference.
                if line.startswith('Error:'):
                    #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 self._regex_minMaxError.match(line):
                        line = line.rstrip() + self._readline()
                    #Skip the communication errors, as those get corrected.
                    if 'checksum mismatch' in line \
                      or 'Wrong checksum' in line \
                      or 'Line Number is not Last Line Number' in line \
                      or 'expected line' in line \
                      or 'No Line Number with checksum' in line \
                      or 'No Checksum with line number' in line \
                      or 'Missing checksum' in line:
                        pass
                    elif not self.isError():
                        self._errorValue = line[6:]
                        self._changeState(self.STATE_ERROR)
                        eventManager().fire("Error", self.getErrorString())

                ##~~ SD file list
                # if we are currently receiving an sd file list, each line is just a filename, so just read it and abort processing
                if self._sdFileList and not 'End file list' in line:
                    self._sdFiles.append(line.strip().lower())
                    continue

                ##~~ Temperature processing
                if ' T:' in line or line.startswith('T:'):
                    try:
                        self._temp = float(
                            self._regex_float.search(
                                line.split('T:')[1]).group(0))
                        if ' B:' in line:
                            self._bedTemp = float(
                                self._regex_float.search(
                                    line.split(' B:')[1]).group(0))

                        self._callback.mcTempUpdate(self._temp, self._bedTemp,
                                                    self._targetTemp,
                                                    self._bedTargetTemp)
                    except ValueError:
                        # catch conversion issues, we'll rather just not get the temperature update instead of killing the connection
                        pass

                    #If we are waiting for an M109 or M190 then measure the time we lost during heatup, so we can remove that time from our printing time estimate.
                    if not 'ok' in line:
                        heatingUp = True
                        if self._heatupWaitStartTime != 0:
                            t = time.time()
                            self._heatupWaitTimeLost = t - self._heatupWaitStartTime
                            self._heatupWaitStartTime = t

                ##~~ SD Card handling
                elif 'SD init fail' in line:
                    self._sdAvailable = False
                    self._sdFiles = []
                    self._callback.mcSdStateChange(self._sdAvailable)
                elif 'Not SD printing' in line:
                    if self.isSdFileSelected() and self.isPrinting():
                        # something went wrong, printer is reporting that we actually are not printing right now...
                        self._sdFilePos = 0
                        self._changeState(self.STATE_OPERATIONAL)
                elif 'SD card ok' in line:
                    self._sdAvailable = True
                    self.refreshSdFiles()
                    self._callback.mcSdStateChange(self._sdAvailable)
                elif 'Begin file list' in line:
                    self._sdFiles = []
                    self._sdFileList = True
                elif 'End file list' in line:
                    self._sdFileList = False
                    self._callback.mcSdFiles(self._sdFiles)
                elif 'SD printing byte' in line:
                    # answer to M27, at least on Marlin, Repetier and Sprinter: "SD printing byte %d/%d"
                    match = self._regex_sdPrintingByte.search(
                        "([0-9]*)/([0-9]*)", line)
                    self._currentFile.setFilepos(int(match.group(1)))
                    self._callback.mcProgress()
                elif 'File opened' in line:
                    # answer to M23, at least on Marlin, Repetier and Sprinter: "File opened:%s Size:%d"
                    match = self._regex_sdFileOpened.search(line)
                    self._currentFile = PrintingSdFileInformation(
                        match.group(1), int(match.group(2)))
                elif 'File selected' in line:
                    # final answer to M23, at least on Marlin, Repetier and Sprinter: "File selected"
                    self._callback.mcFileSelected(
                        self._currentFile.getFilename(),
                        self._currentFile.getFilesize(), True)
                    eventManager().fire("FileSelected",
                                        self._currentFile.getFilename())
                elif 'Writing to file' in line:
                    # anwer to M28, at least on Marlin, Repetier and Sprinter: "Writing to file: %s"
                    self._changeState(self.STATE_PRINTING)
                    line = "ok"
                elif 'Done printing file' in line:
                    # printer is reporting file finished printing
                    self._sdFilePos = 0
                    self._callback.mcPrintjobDone()
                    self._changeState(self.STATE_OPERATIONAL)
                    eventManager().fire("PrintDone")

                ##~~ Message handling
                elif line.strip() != '' and line.strip(
                ) != 'ok' and not line.startswith(
                        "wait"
                ) and not line.startswith(
                        'Resend:'
                ) and line != 'echo:Unknown command:""\n' and self.isOperational(
                ):
                    self._callback.mcMessage(line)

                ##~~ Parsing for feedback commands
                if feedbackControls:
                    for name, matcher, template in feedbackControls:
                        try:
                            match = matcher.search(line)
                            if match is not None:
                                format = None
                                if isinstance(template, str):
                                    format = str.format
                                elif isinstance(template, unicode):
                                    format = unicode.format

                                if format is not None:
                                    self._callback.mcReceivedRegisteredMessage(
                                        name,
                                        format(template,
                                               *(match.groups("n/a"))))
                        except:
                            if not name in feedbackErrors:
                                self._logger.info(
                                    "Something went wrong with feedbackControl \"%s\": "
                                    % name,
                                    exc_info=True)
                                feedbackErrors.append(name)
                            pass

                ##~~ Parsing for pause triggers
                if pauseTriggers and not self.isStreaming():
                    if "enable" in pauseTriggers.keys(
                    ) and pauseTriggers["enable"].search(line) is not None:
                        self.setPause(True)
                    elif "disable" in pauseTriggers.keys(
                    ) and pauseTriggers["disable"].search(line) is not None:
                        self.setPause(False)
                    elif "toggle" in pauseTriggers.keys(
                    ) and pauseTriggers["toggle"].search(line) is not None:
                        self.setPause(not self.isPaused())

                if "ok" in line and heatingUp:
                    heatingUp = False

                ### Baudrate detection
                if self._state == self.STATE_DETECT_BAUDRATE:
                    if line == '' or time.time() > timeout:
                        if len(self._baudrateDetectList) < 1:
                            self.close()
                            self._errorValue = "No more baudrates to test, and no suitable baudrate found."
                            self._changeState(self.STATE_ERROR)
                            eventManager().fire("Error", self.getErrorString())
                        elif self._baudrateDetectRetry > 0:
                            self._baudrateDetectRetry -= 1
                            self._serial.write('\n')
                            self._log("Baudrate test retry: %d" %
                                      (self._baudrateDetectRetry))
                            self._sendCommand("M105")
                            self._testingBaudrate = True
                        else:
                            baudrate = self._baudrateDetectList.pop(0)
                            try:
                                self._serial.baudrate = baudrate
                                self._serial.timeout = settings().getFloat(
                                    ["serial", "timeout", "detection"])
                                self._log("Trying baudrate: %d" % (baudrate))
                                self._baudrateDetectRetry = 5
                                self._baudrateDetectTestOk = 0
                                timeout = getNewTimeout("communication")
                                self._serial.write('\n')
                                self._sendCommand("M105")
                                self._testingBaudrate = True
                            except:
                                self._log(
                                    "Unexpected error while setting baudrate: %d %s"
                                    % (baudrate, getExceptionString()))
                    elif 'ok' in line and 'T:' in line:
                        self._baudrateDetectTestOk += 1
                        if self._baudrateDetectTestOk < 10:
                            self._log("Baudrate test ok: %d" %
                                      (self._baudrateDetectTestOk))
                            self._sendCommand("M105")
                        else:
                            self._sendCommand("M999")
                            self._serial.timeout = settings().getFloat(
                                ["serial", "timeout", "connection"])
                            self._changeState(self.STATE_OPERATIONAL)
                            if self._sdAvailable:
                                self.refreshSdFiles()
                            eventManager().fire(
                                "Connected",
                                "%s at %s baud" % (self._port, self._baudrate))
                    else:
                        self._testingBaudrate = False

                ### Connection attempt
                elif self._state == self.STATE_CONNECTING:
                    if (line == "" or "wait" in line) and startSeen:
                        self._sendCommand("M105")
                    elif "start" in line:
                        startSeen = True
                    elif "ok" in line and startSeen:
                        self._changeState(self.STATE_OPERATIONAL)
                        if self._sdAvailable:
                            self.refreshSdFiles()
                        eventManager().fire(
                            "Connected",
                            "%s at %s baud" % (self._port, self._baudrate))
                    elif time.time() > timeout:
                        self.close()

                ### Operational
                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:
                        if self._resendDelta is not None:
                            self._resendNextCommand()
                        elif not self._commandQueue.empty():
                            self._sendCommand(self._commandQueue.get())
                        else:
                            self._sendCommand("M105")
                        tempRequestTimeout = getNewTimeout("communication")
                    # resend -> start resend procedure from requested line
                    elif line.lower().startswith(
                            "resend") or line.lower().startswith("rs"):
                        if settings().get(["feature", "swallowOkAfterResend"]):
                            swallowOk = True
                        self._handleResendRequest(line)

                ### Printing
                elif self._state == self.STATE_PRINTING:
                    if line == "" and time.time() > timeout:
                        self._log(
                            "Communication timeout during printing, forcing a line"
                        )
                        line = 'ok'

                    if self.isSdPrinting():
                        if time.time() > tempRequestTimeout and not heatingUp:
                            self._sendCommand("M105")
                            tempRequestTimeout = getNewTimeout("communication")

                        if time.time(
                        ) > sdStatusRequestTimeout and not heatingUp:
                            self._sendCommand("M27")
                            sdStatusRequestTimeout = time.time() + 1

                        if 'ok' or 'SD printing byte' in line:
                            timeout = getNewTimeout("communication")
                    else:
                        # Even when printing request the temperature every 5 seconds.
                        if time.time(
                        ) > tempRequestTimeout and not self.isStreaming():
                            self._commandQueue.put("M105")
                            tempRequestTimeout = getNewTimeout("communication")

                        if "ok" in line and swallowOk:
                            swallowOk = False
                        elif "ok" in line:
                            timeout = getNewTimeout("communication")
                            if self._resendDelta is not None:
                                self._resendNextCommand()
                            elif not self._commandQueue.empty(
                            ) and not self.isStreaming():
                                self._sendCommand(self._commandQueue.get())
                            else:
                                self._sendNext()
                        elif line.lower().startswith(
                                "resend") or line.lower().startswith("rs"):
                            if settings().get(
                                ["feature", "swallowOkAfterResend"]):
                                swallowOk = True
                            self._handleResendRequest(line)
            except:
                self._logger.exception(
                    "Something crashed inside the serial connection loop, please report this in OctoPrint's bug tracker:"
                )

                errorMsg = "See octoprint.log for details"
                self._log(errorMsg)
                self._errorValue = errorMsg
                self._changeState(self.STATE_ERROR)
                eventManager().fire("Error", self.getErrorString())
        self._log("Connection closed, closing down monitor")