Exemple #1
0
	def test_convert_feedback_controls(self):
		def md5sum(input):
			import hashlib
			m = hashlib.md5()
			m.update(input)
			return m.hexdigest()

		temp_regex = "T:((\d*\.)\d+)"
		temp_template = "Temp: {}"
		temp2_template = "Temperature: {}"
		temp_key = md5sum(temp_regex)
		temp_template_key = md5sum(temp_template)
		temp2_template_key = md5sum(temp2_template)

		x_regex = "X:(?P<x>\d+)"
		x_template = "X: {x}"
		x_key = md5sum(x_regex)
		x_template_key = md5sum(x_template)

		configured_controls = [
			dict(key=temp_key, regex=temp_regex, template=temp_template, template_key=temp_template_key),
			dict(command="M117 Hello World", name="Test"),
			dict(children=[
				dict(key=x_key, regex=x_regex, template=x_template, template_key=x_template_key),
				dict(key=temp_key, regex=temp_regex, template=temp2_template, template_key=temp2_template_key)
			])
		]

		from octoprint.util import comm
		controls, matcher = comm.convert_feedback_controls(configured_controls)

		self.assertEquals(2, len(controls))

		# temp_regex is used twice, so we should have two templates for it
		self.assertIn(temp_key, controls)
		temp = controls[temp_key]

		self.assertIsNotNone(temp["matcher"])
		self.assertEquals(temp_regex, temp["matcher"].pattern)
		self.assertEquals(temp_regex, temp["pattern"])

		self.assertEquals(2, len(temp["templates"]))
		self.assertIn(temp_template_key, temp["templates"])
		self.assertEquals(temp_template, temp["templates"][temp_template_key])
		self.assertIn(temp2_template_key, temp["templates"])
		self.assertEquals(temp2_template, temp["templates"][temp2_template_key])

		# x_regex is used once, so we should have only one template for it
		self.assertIn(x_key, controls)
		x = controls[x_key]

		self.assertIsNotNone(x["matcher"])
		self.assertEquals(x_regex, x["matcher"].pattern)
		self.assertEquals(x_regex, x["pattern"])

		self.assertEquals(1, len(x["templates"]))
		self.assertIn(x_template_key, x["templates"])
		self.assertEquals(x_template, x["templates"][x_template_key])

		self.assertEquals("(?P<group{temp_key}>{temp_regex})|(?P<group{x_key}>{x_regex})".format(**locals()), matcher.pattern)
Exemple #2
0
	def test_convert_feedback_controls(self):
		def md5sum(input):
			import hashlib
			m = hashlib.md5()
			m.update(input)
			return m.hexdigest()

		temp_regex = "T:((\d*\.)\d+)"
		temp_template = "Temp: {}"
		temp2_template = "Temperature: {}"
		temp_key = md5sum(temp_regex)
		temp_template_key = md5sum(temp_template)
		temp2_template_key = md5sum(temp2_template)

		x_regex = "X:(?P<x>\d+)"
		x_template = "X: {x}"
		x_key = md5sum(x_regex)
		x_template_key = md5sum(x_template)

		configured_controls = [
			dict(key=temp_key, regex=temp_regex, template=temp_template, template_key=temp_template_key),
			dict(command="M117 Hello World", name="Test"),
			dict(children=[
				dict(key=x_key, regex=x_regex, template=x_template, template_key=x_template_key),
				dict(key=temp_key, regex=temp_regex, template=temp2_template, template_key=temp2_template_key)
			])
		]

		from octoprint.util import comm
		controls, matcher = comm.convert_feedback_controls(configured_controls)

		self.assertEquals(2, len(controls))

		# temp_regex is used twice, so we should have two templates for it
		self.assertIn(temp_key, controls)
		temp = controls[temp_key]

		self.assertIsNotNone(temp["matcher"])
		self.assertEquals(temp_regex, temp["matcher"].pattern)
		self.assertEquals(temp_regex, temp["pattern"])

		self.assertEquals(2, len(temp["templates"]))
		self.assertIn(temp_template_key, temp["templates"])
		self.assertEquals(temp_template, temp["templates"][temp_template_key])
		self.assertIn(temp2_template_key, temp["templates"])
		self.assertEquals(temp2_template, temp["templates"][temp2_template_key])

		# x_regex is used once, so we should have only one template for it
		self.assertIn(x_key, controls)
		x = controls[x_key]

		self.assertIsNotNone(x["matcher"])
		self.assertEquals(x_regex, x["matcher"].pattern)
		self.assertEquals(x_regex, x["pattern"])

		self.assertEquals(1, len(x["templates"]))
		self.assertIn(x_template_key, x["templates"])
		self.assertEquals(x_template, x["templates"][x_template_key])

		self.assertEquals("(?P<group{temp_key}>{temp_regex})|(?P<group{x_key}>{x_regex})".format(**locals()), matcher.pattern)
Exemple #3
0
    def _monitor(self):
        """
        Monitor thread of responses from the commands sent to the printer
        :return:
        """
        feedback_controls, feedback_matcher = comm.convert_feedback_controls(settings().get(["controls"]))
        feedback_errors = []
        pause_triggers = comm.convert_pause_triggers(settings().get(["printerParameters", "pauseTriggers"]))

        #exits if no connection is active
        if not self._beeConn.isConnected():
            return

        startSeen = False
        supportWait = settings().getBoolean(["feature", "supportWait"])

        while self._monitoring_active:
            try:
                line = self._getResponse()
                if line is None:
                    continue

                ##~~ debugging output handling
                if line.startswith("//"):
                    debugging_output = line[2:].strip()
                    if debugging_output.startswith("action:"):
                        action_command = debugging_output[len("action:"):].strip()

                        if action_command == "pause":
                            self._log("Pausing on request of the printer...")
                            self.setPause(True)
                        elif action_command == "resume":
                            self._log("Resuming on request of the printer...")
                            self.setPause(False)
                        elif action_command == "disconnect":
                            self._log("Disconnecting on request of the printer...")
                            self._callback.on_comm_force_disconnect()
                        else:
                            for hook in self._printer_action_hooks:
                                try:
                                    self._printer_action_hooks[hook](self, line, action_command)
                                except:
                                    self._logger.exception("Error while calling hook {} with action command {}".format(self._printer_action_hooks[hook], action_command))
                                    continue
                    else:
                        continue

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

                ##~~ process oks
                if line.strip().startswith("ok") or (self.isPrinting() and supportWait and line.strip().startswith("wait")):
                    self._clear_to_send.set()
                    self._long_running_command = False

                ##~~ Temperature processing
                if ' T:' in line or line.startswith('T:') or ' T0:' in line or line.startswith('T0:') \
                        or ' B:' in line or line.startswith('B:'):

                    self._processTemperatures(line)
                    self._callback.on_comm_temperature_update(self._temp, self._bedTemp)

                ##~~ 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.on_comm_sd_state_change(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.on_comm_sd_state_change(self._sdAvailable)
                elif 'Begin file list' in line:
                    self._sdFiles = []
                    self._sdFileList = True
                elif 'End file list' in line:
                    self._sdFileList = False
                    self._callback.on_comm_sd_files(self._sdFiles)
                elif 'SD printing byte' in line and self.isSdPrinting():
                    # answer to M27, at least on Marlin, Repetier and Sprinter: "SD printing byte %d/%d"
                    match = regex_sdPrintingByte.search(line)
                    self._currentFile.setFilepos(int(match.group(1)))
                    self._callback.on_comm_progress()
                elif 'File opened' in line and not self._ignore_select:
                    # answer to M23, at least on Marlin, Repetier and Sprinter: "File opened:%s Size:%d"
                    match = regex_sdFileOpened.search(line)
                    if self._sdFileToSelect:
                        name = self._sdFileToSelect
                        self._sdFileToSelect = None
                    else:
                        name = match.group(1)
                    self._currentFile = comm.PrintingSdFileInformation(name, int(match.group(2)))
                elif 'File selected' in line:
                    if self._ignore_select:
                        self._ignore_select = False
                    elif self._currentFile is not None:
                        # final answer to M23, at least on Marlin, Repetier and Sprinter: "File selected"
                        self._callback.on_comm_file_selected(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:
                    # answer to M28, at least on Marlin, Repetier and Sprinter: "Writing to file: %s"
                    self._changeState(self.STATE_PRINTING)
                    self._clear_to_send.set()
                    line = "ok"

                elif 'Done saving file' in line:
                    self.refreshSdFiles()
                elif 'File deleted' in line and line.strip().endswith("ok"):
                    # buggy Marlin version that doesn't send a proper \r after the "File deleted" statement, fixed in
                    # current versions
                    self._clear_to_send.set()

                ##~~ 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.on_comm_message(line)

                ##~~ Parsing for feedback commands
                if feedback_controls and feedback_matcher and not "_all" in feedback_errors:
                    try:
                        self._process_registered_message(line, feedback_matcher, feedback_controls, feedback_errors)
                    except:
                        # something went wrong while feedback matching
                        self._logger.exception("Error while trying to apply feedback control matching, disabling it")
                        feedback_errors.append("_all")

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

                ### Connection attempt
                elif self._state == self.STATE_CONNECTING:
                    if "start" in line and not startSeen:
                        startSeen = True
                        self._sendCommand("M110")
                        self._clear_to_send.set()
                    elif "ok" in line:
                        self._onConnected()
                    elif time.time() > self._timeout:
                        self.close()

                ### Operational
                elif self._state == self.STATE_OPERATIONAL or self._state == self.STATE_PAUSED:
                    if "ok" in line:
                        # if we still have commands to process, process them
                        if self._resendDelta is not None:
                            self._resendNextCommand()
                        elif self._sendFromQueue():
                            pass

                    # resend -> start resend procedure from requested line
                    elif line.lower().startswith("resend") or line.lower().startswith("rs"):
                        self._handleResendRequest(line)

            except Exception as ex:
                self._logger.exception("Something crashed inside the USB connection.")

                errorMsg = "See octoprint.log for details"
                self._log(ex.message)
                self._errorValue = errorMsg
                self._changeState(self.STATE_ERROR)
                eventManager().fire(Events.ERROR, {"error": self.getErrorString()})
        self._log("Connection closed, closing down monitor")
Exemple #4
0
    def test_convert_feedback_controls(self):
        def md5sum(input):
            import hashlib

            m = hashlib.md5()
            m.update(input)
            return m.hexdigest()

        # rb'' doesn't exist in Python2
        temp_regex = br"T:((\d*\.)\d+)"
        temp_template = b"Temp: {}"
        temp2_template = b"Temperature: {}"
        temp_key = md5sum(temp_regex)
        temp_template_key = md5sum(temp_template)
        temp2_template_key = md5sum(temp2_template)

        x_regex = br"X:(?P<x>\d+)"
        x_template = b"X: {x}"
        x_key = md5sum(x_regex)
        x_template_key = md5sum(x_template)

        configured_controls = [
            {
                "key": temp_key,
                "regex": temp_regex,
                "template": temp_template,
                "template_key": temp_template_key,
            },
            {
                "command": "M117 Hello World",
                "name": "Test"
            },
            {
                "children": [
                    {
                        "key": x_key,
                        "regex": x_regex,
                        "template": x_template,
                        "template_key": x_template_key,
                    },
                    {
                        "key": temp_key,
                        "regex": temp_regex,
                        "template": temp2_template,
                        "template_key": temp2_template_key,
                    },
                ]
            },
        ]

        from octoprint.util import comm

        controls, matcher = comm.convert_feedback_controls(configured_controls)

        self.assertEqual(2, len(controls))

        # temp_regex is used twice, so we should have two templates for it
        self.assertIn(temp_key, controls)
        temp = controls[temp_key]

        self.assertIsNotNone(temp["matcher"])
        self.assertEqual(temp_regex, temp["matcher"].pattern)
        self.assertEqual(temp_regex, temp["pattern"])

        self.assertEqual(2, len(temp["templates"]))
        self.assertIn(temp_template_key, temp["templates"])
        self.assertEqual(temp_template, temp["templates"][temp_template_key])
        self.assertIn(temp2_template_key, temp["templates"])
        self.assertEqual(temp2_template, temp["templates"][temp2_template_key])

        # x_regex is used once, so we should have only one template for it
        self.assertIn(x_key, controls)
        x = controls[x_key]

        self.assertIsNotNone(x["matcher"])
        self.assertEqual(x_regex, x["matcher"].pattern)
        self.assertEqual(x_regex, x["pattern"])

        self.assertEqual(1, len(x["templates"]))
        self.assertIn(x_template_key, x["templates"])
        self.assertEqual(x_template, x["templates"][x_template_key])

        self.assertEqual(
            "(?P<group{temp_key}>{temp_regex})|(?P<group{x_key}>{x_regex})".
            format(**locals()),
            matcher.pattern,
        )
Exemple #5
0
    def _monitor(self):
        """
        Monitor thread of responses from the commands sent to the printer
        :return:
        """
        feedback_controls, feedback_matcher = comm.convert_feedback_controls(settings().get(["controls"]))
        feedback_errors = []
        pause_triggers = comm.convert_pause_triggers(settings().get(["printerParameters", "pauseTriggers"]))

        #exits if no connection is active
        if not self._beeConn.isConnected():
            return

        startSeen = False
        supportWait = settings().getBoolean(["feature", "supportWait"])

        while self._monitoring_active:
            try:
                line = self._getResponse()
                if line is None:
                    continue

                ##~~ debugging output handling
                if line.startswith("//"):
                    debugging_output = line[2:].strip()
                    if debugging_output.startswith("action:"):
                        action_command = debugging_output[len("action:"):].strip()

                        if action_command == "pause":
                            self._log("Pausing on request of the printer...")
                            self.setPause(True)
                        elif action_command == "resume":
                            self._log("Resuming on request of the printer...")
                            self.setPause(False)
                        elif action_command == "disconnect":
                            self._log("Disconnecting on request of the printer...")
                            self._callback.on_comm_force_disconnect()
                        else:
                            for hook in self._printer_action_hooks:
                                try:
                                    self._printer_action_hooks[hook](self, line, action_command)
                                except:
                                    self._logger.exception("Error while calling hook {} with action command {}".format(self._printer_action_hooks[hook], action_command))
                                    continue
                    else:
                        continue

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

                ##~~ process oks
                if line.strip().startswith("ok") or (self.isPrinting() and supportWait and line.strip().startswith("wait")):
                    self._clear_to_send.set()
                    self._long_running_command = False

                ##~~ Temperature processing
                if ' T:' in line or line.startswith('T:') or ' T0:' in line or line.startswith('T0:') \
                        or ' B:' in line or line.startswith('B:'):

                    self._processTemperatures(line)
                    self._callback.on_comm_temperature_update(self._temp, self._bedTemp)

                ##~~ 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.on_comm_sd_state_change(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.on_comm_sd_state_change(self._sdAvailable)
                elif 'Begin file list' in line:
                    self._sdFiles = []
                    self._sdFileList = True
                elif 'End file list' in line:
                    self._sdFileList = False
                    self._callback.on_comm_sd_files(self._sdFiles)
                elif 'SD printing byte' in line and self.isSdPrinting():
                    # answer to M27, at least on Marlin, Repetier and Sprinter: "SD printing byte %d/%d"
                    match = regex_sdPrintingByte.search(line)
                    self._currentFile.setFilepos(int(match.group(1)))
                    self._callback.on_comm_progress()
                elif 'File opened' in line and not self._ignore_select:
                    # answer to M23, at least on Marlin, Repetier and Sprinter: "File opened:%s Size:%d"
                    match = regex_sdFileOpened.search(line)
                    if self._sdFileToSelect:
                        name = self._sdFileToSelect
                        self._sdFileToSelect = None
                    else:
                        name = match.group(1)
                    self._currentFile = comm.PrintingSdFileInformation(name, int(match.group(2)))
                elif 'File selected' in line:
                    if self._ignore_select:
                        self._ignore_select = False
                    elif self._currentFile is not None:
                        # final answer to M23, at least on Marlin, Repetier and Sprinter: "File selected"
                        self._callback.on_comm_file_selected(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:
                    # answer to M28, at least on Marlin, Repetier and Sprinter: "Writing to file: %s"
                    self._changeState(self.STATE_PRINTING)
                    self._clear_to_send.set()
                    line = "ok"

                elif 'Done saving file' in line:
                    self.refreshSdFiles()
                elif 'File deleted' in line and line.strip().endswith("ok"):
                    # buggy Marlin version that doesn't send a proper \r after the "File deleted" statement, fixed in
                    # current versions
                    self._clear_to_send.set()

                ##~~ 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.on_comm_message(line)

                ##~~ Parsing for feedback commands
                if feedback_controls and feedback_matcher and not "_all" in feedback_errors:
                    try:
                        self._process_registered_message(line, feedback_matcher, feedback_controls, feedback_errors)
                    except:
                        # something went wrong while feedback matching
                        self._logger.exception("Error while trying to apply feedback control matching, disabling it")
                        feedback_errors.append("_all")

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

                ### Connection attempt
                elif self._state == self.STATE_CONNECTING:
                    if "start" in line and not startSeen:
                        startSeen = True
                        self._sendCommand("M110")
                        self._clear_to_send.set()
                    elif "ok" in line:
                        self._onConnected()
                    elif time.time() > self._timeout:
                        self.close()

                ### Operational
                elif self._state == self.STATE_OPERATIONAL or self._state == self.STATE_PAUSED:
                    if "ok" in line:
                        # if we still have commands to process, process them
                        if self._resendDelta is not None:
                            self._resendNextCommand()
                        elif self._sendFromQueue():
                            pass

                    # resend -> start resend procedure from requested line
                    elif line.lower().startswith("resend") or line.lower().startswith("rs"):
                        self._handleResendRequest(line)

            except Exception as ex:
                self._logger.exception("Something crashed inside the USB connection.")

                errorMsg = "See octoprint.log for details"
                self._log(ex.message)
                self._errorValue = errorMsg
                self._changeState(self.STATE_ERROR)
                eventManager().fire(Events.ERROR, {"error": self.getErrorString()})
        self._log("Connection closed, closing down monitor")