Beispiel #1
0
def printer_command(command: str) -> str:
    printer_command = PrinterCommand(command)
    with ElegooMars() as elegoo_mars:
        if printer_command == PrinterCommand.START_PRINT:
            # TODO: validate filename before sending it to the printer
            filename = str(request.args.get("filename"))
            elegoo_mars.start_printing(filename)
        elif printer_command == PrinterCommand.PAUSE_PRINT:
            elegoo_mars.pause_printing()
        elif printer_command == PrinterCommand.RESUME_PRINT:
            elegoo_mars.resume_printing()
        elif printer_command == PrinterCommand.CANCEL_PRINT:
            elegoo_mars.stop_printing()
        elif printer_command == PrinterCommand.REBOOT:
            elegoo_mars.reboot()
        return jsonify({"success": True})
Beispiel #2
0
def print_status() -> str:
    with ElegooMars() as elegoo_mars:
        selected_file = elegoo_mars.get_selected_file()
        print_status = elegoo_mars.get_print_status()

        if print_status.state == PrinterState.IDLE:
            progress = 0.0
            print_details = {}
        else:
            sliced_model_file = read_cached_sliced_model_file(
                config.get_files_directory() / selected_file
            )

            if print_status.current_byte == 0:
                current_layer = 1
            else:
                current_layer = (
                    sliced_model_file.end_byte_offset_by_layer.index(
                        print_status.current_byte
                    )
                    + 1
                )

            progress = (
                100.0
                * none_throws(current_layer - 1)
                / none_throws(sliced_model_file.layer_count)
            )

            print_details = {
                "current_layer": current_layer,
                "layer_count": sliced_model_file.layer_count,
                "print_time_secs": sliced_model_file.print_time_secs,
                "time_left_secs": round(
                    sliced_model_file.print_time_secs * (100.0 - progress) / 100.0
                ),
            }

        return jsonify(
            {
                "state": print_status.state.value,
                "selected_file": selected_file,
                "progress": progress,
                **print_details,
            }
        )
Beispiel #3
0
 def setUp(self) -> None:
     self.serial_port_mock = Mock(spec=serial.Serial)
     self.serial_port_patcher = patch("mariner.mars.serial.Serial")
     serial_port_constructor = self.serial_port_patcher.start()
     serial_port_constructor.return_value = self.serial_port_mock
     self.printer = ElegooMars()
Beispiel #4
0
class ElegooMarsTest(TestCase):
    printer: ElegooMars
    # pyre-ignore[24]: Generic type `unittest.mock._patch` expects
    # 1 type parameter
    serial_port_patcher: unittest.mock._patch
    serial_port_mock: MagicMock

    def setUp(self) -> None:
        self.serial_port_mock = Mock(spec=serial.Serial)
        self.serial_port_patcher = patch("mariner.mars.serial.Serial")
        serial_port_constructor = self.serial_port_patcher.start()
        serial_port_constructor.return_value = self.serial_port_mock
        self.printer = ElegooMars()

    def tearDown(self) -> None:
        self.serial_port_patcher.stop()

    def test_open_and_close(self) -> None:
        self.serial_port_mock.open.assert_not_called()
        self.serial_port_mock.close.assert_not_called()

        self.printer.open()
        self.serial_port_mock.open.assert_called_once_with()
        self.serial_port_mock.close.assert_not_called()
        self.serial_port_mock.reset_mock()

        self.printer.close()
        self.serial_port_mock.open.assert_not_called()
        self.serial_port_mock.close.assert_called_once_with()

    def test_usage_with_context_manager(self) -> None:
        self.serial_port_mock.open.assert_not_called()
        self.serial_port_mock.close.assert_not_called()

        with self.printer:
            self.serial_port_mock.open.assert_called_once_with()
            self.serial_port_mock.close.assert_not_called()
            self.serial_port_mock.reset_mock()

        self.serial_port_mock.open.assert_not_called()
        self.serial_port_mock.close.assert_called_once_with()

    def test_get_firmware_version(self) -> None:
        self.serial_port_mock.readline.return_value = b"ok V4.3.4_LCDC\n"

        self.printer.open()
        firmware_version = self.printer.get_firmware_version()
        self.printer.close()

        self.serial_port_mock.write.assert_called_once_with(b"M4002")
        expect(firmware_version).to_equal("V4.3.4_LCDC")

    def test_get_state(self) -> None:
        self.serial_port_mock.readline.return_value = (
            b"ok B:0/0 X:0.000 Y:0.000 Z:151.900 F:0/0 D:31540/0/1 ")

        self.printer.open()
        # TODO: make this return a dataclass with the parsed information
        self.printer.get_state()
        self.printer.close()

        self.serial_port_mock.write.assert_called_once_with(b"M4000")

    def test_get_print_status_when_not_printing(self) -> None:
        self.serial_port_mock.readline.return_value = (
            b"ok B:0/0 X:0.000 Y:0.000 Z:8.233 F:0/0 D:0/0/1 ")

        self.printer.open()
        print_status = self.printer.get_print_status()
        self.printer.close()

        self.serial_port_mock.write.assert_called_once_with(b"M4000")
        expect(print_status.state).to_equal(PrinterState.IDLE)
        expect(print_status.current_byte).to_be_none()
        expect(print_status.total_bytes).to_be_none()

    def test_get_print_status_while_starting_print(self) -> None:
        self.serial_port_mock.readline.return_value = (
            b"ok B:0/0 X:0.000 Y:0.000 Z:-33.421 F:256/256 D:0/11494803/0 ")

        self.printer.open()
        print_status = self.printer.get_print_status()
        self.printer.close()

        self.serial_port_mock.write.assert_called_once_with(b"M4000")
        expect(print_status.state).to_equal(PrinterState.STARTING_PRINT)
        expect(print_status.current_byte).to_equal(0)
        expect(print_status.total_bytes).to_equal(11494803)

    def test_get_print_status_while_printing(self) -> None:
        self.serial_port_mock.readline.return_value = (
            b"ok B:0/0 X:0.000 Y:0.000 Z:0.100 F:256/256 D:76903/11494803/0 ")

        self.printer.open()
        print_status = self.printer.get_print_status()
        self.printer.close()

        self.serial_port_mock.write.assert_called_once_with(b"M4000")
        expect(print_status.state).to_equal(PrinterState.PRINTING)
        expect(print_status.current_byte).to_equal(76903)
        expect(print_status.total_bytes).to_equal(11494803)

    def test_get_print_status_when_paused(self) -> None:
        self.serial_port_mock.readline.return_value = (
            b"ok B:0/0 X:0.000 Y:0.000 Z:78.000 F:256/256 D:5957675/11494803/1 "
        )

        self.printer.open()
        print_status = self.printer.get_print_status()
        self.printer.close()

        self.serial_port_mock.write.assert_called_once_with(b"M4000")
        expect(print_status.state).to_equal(PrinterState.PAUSED)
        expect(print_status.current_byte).to_equal(5957675)
        expect(print_status.total_bytes).to_equal(11494803)

    def test_get_z_pos(self) -> None:
        self.serial_port_mock.readline.return_value = (
            b"ok C: X:0.000000 Y:0.000000 Z:155.000000 E:0.000000\r\n")

        self.printer.open()
        z_pos = self.printer.get_z_pos()
        self.printer.close()

        self.serial_port_mock.write.assert_called_once_with(b"M114")
        expect(z_pos).is_almost_equal(155.0, max_delta=1e-9)

    def test_get_selected_file(self) -> None:
        self.serial_port_mock.readline.return_value = b"ok 'LittleBBC.ctb'\r\n"

        self.printer.open()
        selected_file = self.printer.get_selected_file()
        self.printer.close()

        self.serial_port_mock.write.assert_called_once_with(b"M4006")
        expect(selected_file).equals("LittleBBC.ctb")

    def test_get_selected_file_with_leading_slash(self) -> None:
        self.serial_port_mock.readline.return_value = b"ok '/subdir/LittleBBC.ctb'\r\n"

        self.printer.open()
        selected_file = self.printer.get_selected_file()
        self.printer.close()

        self.serial_port_mock.write.assert_called_once_with(b"M4006")
        expect(selected_file).equals("subdir/LittleBBC.ctb")

    def test_select_file(self) -> None:
        self.serial_port_mock.readline.return_value = (
            # FIXME: this isn't right, readline would return only the first one
            b"File opened:lattice.ctb Size:26058253\r\nFile selected\r\nok N:0\r\n"
        )
        self.printer.open()
        self.printer.select_file("lattice.ctb")
        self.printer.close()
        self.serial_port_mock.write.assert_called_once_with(
            b"M23 /lattice.ctb")

    def test_select_nonexisting_file(self) -> None:
        self.serial_port_mock.readline.return_value = (
            # FIXME: this isn't right, readline would return only the first one
            b"//############Error!cann't open file foobar.ctb!\r\n" +
            b"open failed, File :foobar.ctb\r\n" + b"ok N:0\r\n")
        self.printer.open()
        with self.assertRaises(UnexpectedPrinterResponse):
            self.printer.select_file("foobar.ctb")
        self.printer.close()
        self.serial_port_mock.write.assert_called_once_with(b"M23 /foobar.ctb")

    def test_stop_printing(self) -> None:
        self.serial_port_mock.readline.return_value = b"ok N:0\r\n"
        self.printer.open()
        self.printer.stop_printing()
        self.serial_port_mock.write.assert_called_once_with(b"M33")
        self.printer.close()

    def test_stop_printing_when_not_printing(self) -> None:
        self.serial_port_mock.readline.return_value = (
            # FIXME: this isn't right, readline would return only the first one
            b"Error:It's not printing now!\r\nok N:0\r\n")
        self.printer.open()
        with self.assertRaises(UnexpectedPrinterResponse):
            self.printer.stop_printing()
        self.printer.close()
        self.serial_port_mock.write.assert_called_once_with(b"M33")

    def test_start_printing(self) -> None:
        self.serial_port_mock.readline.side_effect = [
            b"File opened:/benchy.ctb Size:11494803\r\n",
            b"ok N:0\r\n",
        ]
        self.printer.open()
        self.printer.start_printing("benchy.ctb")
        self.serial_port_mock.write.assert_has_calls([
            call(b"M23 /benchy.ctb"),
            call(b"M6030 'benchy.ctb'"),
        ])
        self.printer.close()

    def test_start_printing_from_subdirectory(self) -> None:
        self.serial_port_mock.readline.side_effect = [
            b"File opened:/more/model.ctb Size:11494803\r\n",
            b"ok N:0\r\n",
        ]
        self.printer.open()
        self.printer.start_printing("more/model.ctb")
        self.serial_port_mock.write.assert_has_calls([
            call(b"M23 /more/model.ctb"),
            call(b"M6030 'model.ctb'"),
        ])
        self.printer.close()

    def test_start_printing_with_invalid_response(self) -> None:
        self.serial_port_mock.readline.side_effect = [
            b"File opened:/benchy.ctb Size:11494803\r\n",
            b"foobar\r\n",
        ]
        self.printer.open()
        with self.assertRaises(UnexpectedPrinterResponse):
            self.printer.start_printing("benchy.ctb")
        self.serial_port_mock.write.assert_has_calls([
            call(b"M23 /benchy.ctb"),
            call(b"M6030 'benchy.ctb'"),
        ])
        self.printer.close()

    def test_resume_printing(self) -> None:
        self.serial_port_mock.readline.return_value = b"ok N:0\r\n"
        self.printer.open()
        self.printer.resume_printing()
        self.serial_port_mock.write.assert_called_once_with(b"M24")
        self.printer.close()

    def test_pause_printing(self) -> None:
        self.serial_port_mock.readline.return_value = b"ok N:0\r\n"
        self.printer.open()
        self.printer.pause_printing()
        self.serial_port_mock.write.assert_called_once_with(b"M25")
        self.printer.close()

    def test_move_by(self) -> None:
        self.serial_port_mock.readline.return_value = b"ok N:0\r\n"
        self.printer.open()

        self.printer.move_by(10)
        self.serial_port_mock.write.assert_called_once_with(
            b"G0 Z10.0 F600 I0")
        self.serial_port_mock.reset_mock()

        self.printer.move_by(-10)
        self.serial_port_mock.write.assert_called_once_with(
            b"G0 Z-10.0 F600 I0")
        self.serial_port_mock.reset_mock()

        self.printer.move_by(15.3, mm_per_min=30)
        self.serial_port_mock.write.assert_called_once_with(b"G0 Z15.3 F30 I0")
        self.serial_port_mock.reset_mock()

        self.printer.close()

    def test_move_to_home(self) -> None:
        self.serial_port_mock.readline.return_value = b"ok N:0\r\n"
        self.printer.open()
        self.printer.move_to_home()
        self.serial_port_mock.write.assert_called_once_with(b"G28")
        self.printer.close()

    def test_stop_motors(self) -> None:
        self.serial_port_mock.readline.return_value = b"ok N:0\r\n"
        self.printer.open()
        self.printer.stop_motors()
        self.serial_port_mock.write.assert_called_once_with(b"M112")
        self.printer.close()

    def test_reboot(self) -> None:
        self.printer.open()

        self.printer.reboot()
        self.serial_port_mock.write.assert_called_once_with(b"M6040 I0")
        self.serial_port_mock.reset_mock()

        self.printer.reboot(delay_in_ms=123)
        self.serial_port_mock.write.assert_called_once_with(b"M6040 I123")

        self.printer.close()