Exemple #1
0
    def _generate_line(self, command, no_buffer=False, n=None):
        line = command
        # TODO add a "fast mode" remove spaces from commands and reduce number of decimals
        # removing spaces is in conflict with the emulator... Need to update the parser there also
        # fast mode test
        if self.is_fast_mode:
            line = command.split(" ")
            new_line = []
            for l in line:
                if l.startswith("X"):
                    l = "X" + self.command_resolution.format(float(
                        l[1:])).rstrip("0").rstrip(".")
                elif l.startswith("Y"):
                    l = "Y" + self.command_resolution.format(float(
                        l[1:])).rstrip("0").rstrip(".")
                new_line.append(l)
            line = "".join(new_line)

        # marlin needs line numbers and checksum (grbl doesn't)
        if firmware.is_marlin(self._firmware):
            # add line number
            if n is None:  # check if the line number was specified or if must increase the number of the sequential command
                self.line_number += 1
                n = self.line_number
            if self.is_fast_mode:
                line = "N{}{}".format(n, line)
            else:
                line = "N{} {} ".format(n, line)
            # calculate marlin checksum according to the wiki
            cs = 0
            for i in line:
                cs = cs ^ ord(i)
            cs &= 0xff

            line += "*{}\n".format(cs)  # add checksum to the line

        elif firmware.is_grbl(self._firmware):
            if line != firmware.GRBL.buffer_command:
                line += "\n"

        else:
            line += "\n"

        # store the line in the buffer
        with self.command_buffer_mutex:
            self.command_buffer.append(self.line_number)
            self.command_buffer_history["N{}".format(self.line_number)] = line
            if no_buffer:
                self.command_buffer.popleft(
                )  # remove an element to get a free ack from the non buffered command. Still must keep it in the buffer in the case of an error in sending the line

        return line
Exemple #2
0
    def _on_device_ready(self):
        if firmware.is_marlin(self._firmware):
            self._reset_line_number()

        # grbl status report mask setup
        # sandypi need to check the buffer to see if the machine has cleaned the buffer
        # setup grbl to show the buffer status with the $10 command
        #   Grbl 1.1 https://github.com/gnea/grbl/wiki/Grbl-v1.1-Configuration
        #   Grbl 0.9 https://github.com/grbl/grbl/wiki/Configuring-Grbl-v0.9
        # to be compatible with both will send $10=6 (4(for v0.9) + 2(for v1.1))
        # the status will then be prompted with the "?" command when necessary
        # the buffer will contain Bf:"usage of the buffer"
        if firmware.is_grbl(self._firmware):
            self.send_gcode_command("$10=6")

        # send the "on connection" script from the settings
        self.send_script(self.settings['scripts']['connected']["value"])
Exemple #3
0
    def _parse_device_line(self, line):
        # setting to avoid sending the message to the frontend in particular situations (i.e. status checking in grbl)
        # will still print the status in the command line
        hide_line = False

        if firmware.get_ACK(
                self._firmware
        ) in line:  # when an "ack" is received free one place in the buffer
            self._ack_received()

        # check if the received line is for the device being ready
        if firmware.get_ready_message(self._firmware) in line:
            if self.serial.is_fake:
                self._on_device_ready()
            else:
                self._on_device_ready_delay(
                )  # if the device is ready will allow the communication after a small delay

        # check marlin specific messages
        if firmware.is_grbl(self._firmware):
            if line.startswith("<"):
                try:
                    # interested in the "Bf:xx," part where xx is the content of the buffer
                    # select buffer content lines
                    res = line.split("Bf:")[1]
                    res = int(res.split(",")[0])
                    if res == 15:  # 15 => buffer is empty on the device (should include also 14 to make it more flexible?)
                        with self.command_buffer_mutex:
                            self.command_buffer.clear()
                    if res != 0:  # 0 -> buffer is full
                        with self.command_buffer_mutex:
                            if len(self.command_buffer
                                   ) > 0 and self.is_running():
                                self.command_buffer.popleft()
                    self._check_buffer_mutex_status()

                    if (self.is_running() or self.is_paused()):
                        hide_line = True
                    self.logger.log(settings_utils.LINE_SERVICE, line)
                except:  # sometimes may not receive the entire line thus it may throw an error
                    pass
                return

            # errors
            elif "error:22" in line:
                self.stop()
                with self.command_buffer_mutex:
                    self.command_buffer.clear()
            elif "error:" in line:
                self.logger.error("Grbl error: {}".format(line))
                # TODO check/parse error types and give some hint about the problem?

        # TODO divide parser between firmwares?
        # TODO set firmware type automatically on connection
        # TODO add feedrate control with something like a knob on the user interface to make the drawing slower or faster

        # Marlin messages
        else:
            # Marlin resend command if a message is not received correctly
            # Quick note: if the buffer_command is sent too often will fill the buffer with "M114" and if a line is request will not be able to send it back
            # TODO Should add some sort of filter that if the requested line number is older than the requested ones can send from that number to the first an empty command or the buffer_command
            # Otherwise should not put a buffer_command in the buffer and if a line with the requested number should send the buffer_command
            if "Resend: " in line:
                line_found = False
                line_number = int(
                    line.replace("Resend: ", "").replace("\r\n", ""))
                items = deepcopy(self.command_buffer_history)
                missing_lines = True
                first_available_line = None
                for n, c in items.items():
                    n_line_number = int(n.strip("N"))
                    if n_line_number == line_number:
                        line_found = True
                    if n_line_number >= line_number:
                        if first_available_line is None:
                            first_available_line = line_number
                        # All the lines after the required one must be resent. Cannot break the loop now
                        self.serial.send(c)
                        self.logger.error(
                            "Line not received correctly. Resending: {}".
                            format(c.strip("\n")))

                if (not line_found) and not (first_available_line is None):
                    for i in range(line_number, first_available_line):
                        self.serial.send(
                            self._generate_line(firmware.MARLIN.buffer_command,
                                                no_buffer=True,
                                                n=i))

                self._ack_received(safe_line_number=line_number - 1,
                                   append_left_extra=True)
                # the resend command is sending an ack. should add an entry to the buffer to keep the right lenght (because the line has been sent 2 times)
                if not line_found:
                    self.logger.error(
                        "No line was found for the number required. Restart numeration."
                    )
                    self._reset_line_number()

            # Marlin "unknow command"
            elif "echo:Unknown command:" in line:
                self.logger.error(
                    "Error: command not found. Can also be a communication error"
                )
            # M114 response contains the "Count" word
            # the response looks like: X:115.22 Y:116.38 Z:0.00 E:0.00 Count A:9218 B:9310 Z:0
            # still, M114 will receive the last position in the look-ahead planner thus the drawing will end first on the interface and then in the real device
            elif "Count" in line:
                l = line.split(" ")
                x = float(l[0][2:])  # remove "X:" from the string
                y = float(l[1][2:])  # remove "Y:" from the string
                # if the last commanded position coincides with the current position it means the buffer on the device is empty (could happen that the position is the same between different points but the M114 command should not be that frequent to run into this problem.) TODO check if it is good enough or if should implement additional checks like a timeout
                # use a tolerance instead of equality because marlin is using strange rounding for the coordinates
                if (abs(float(self.last_commanded_position.x) - x) <
                        firmware.MARLIN.position_tolerance) and (
                            abs(float(self.last_commanded_position.y) - y) <
                            firmware.MARLIN.position_tolerance):
                    if self.is_running():
                        self._ack_received()
                    else:
                        with self.command_buffer_mutex:
                            self.command_buffer.clear()
                        self._check_buffer_mutex_status()

                if not self.is_running():
                    hide_line = True

            # TODO check feedrate response for M220 and set feedrate
            #elif "_______" in line: # must see the real output from marlin
            #    self.feedrate = .... # must see the real output from marlin

        self.logger.log(settings_utils.LINE_RECEIVED, line)
        if not hide_line:
            self.handler.on_message_received(line)