class TestGcodeTrigger(unittest.TestCase):
    def setUp(self):
        self.Settings = OctolapseSettings(NamedTemporaryFile().name)
        self.Settings.current_printer().auto_detect_position = False
        self.Settings.current_printer().origin_x = 0
        self.Settings.current_printer().origin_y = 0
        self.Settings.current_printer().origin_z = 0
        self.OctoprintPrinterProfile = self.create_octoprint_printer_profile()

    def tearDown(self):
        del self.Settings
        del self.OctoprintPrinterProfile

    @staticmethod
    def create_octoprint_printer_profile():
        return {
            "volume": {
                "custom_box": False,
                "width": 250,
                "depth": 200,
                "height": 200
            }
        }

    def test_GcodeTrigger(self):
        """Test the gcode triggers"""
        self.Settings.current_snapshot().gcode_trigger_require_zhop = False

        position = Position(self.Settings, self.OctoprintPrinterProfile, False)
        trigger = GcodeTrigger(self.Settings)
        # test initial state
        self.assertFalse(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # send a command that is NOT the snapshot command using the defaults
        trigger.update(position, "NotTheSnapshotCommand")
        self.assertFalse(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # send a command that is the snapshot command without the axis being homes
        trigger.update(position, "snap")
        self.assertFalse(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # reset, set relative extruder and absolute xyz, home the axis, and resend the snap command, should wait
        # since we require the home command to complete (sent to printer) before triggering
        position.update("M83")
        position.update("G90")
        position.update("G28")
        trigger.update(position, "snap")
        self.assertFalse(trigger.is_triggered(0))
        self.assertTrue(trigger.is_waiting(0))

        # try again, Snap is encountered, but it must be the previous command to trigger
        position.update("G0 X0 Y0 Z0 E1 F0")
        trigger.update(position, "G0 X0 Y0 Z0 E1 F0")
        self.assertTrue(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # try again, but this time set RequireZHop to true
        trigger.RequireZHop = True
        trigger.update(position, "snap")
        self.assertFalse(trigger.is_triggered(0))
        self.assertTrue(trigger.is_waiting(0))
        # send another command to see if we are still waiting
        trigger.update(position, "NotTheSnapshotCommand")
        self.assertFalse(trigger.is_triggered(0))
        self.assertTrue(trigger.is_waiting(0))
        # fake a zhop
        position.is_zhop = lambda x: True
        trigger.update(position, "NotTheSnapshotCommand")
        self.assertTrue(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # send a command that is NOT the snapshot command using the defaults
        trigger.update(position, "NotTheSnapshotCommand")
        self.assertFalse(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # change the snapshot triggers and make sure they are working
        self.Settings.current_snapshot().gcode_trigger_require_zhop = None
        self.Settings.current_snapshot().gcode_trigger_on_extruding = True
        self.Settings.current_snapshot(
        ).gcode_trigger_on_extruding_start = None
        self.Settings.current_snapshot().gcode_trigger_on_primed = None
        self.Settings.current_snapshot().gcode_trigger_on_retracting = None
        self.Settings.current_snapshot(
        ).gcode_trigger_on_partially_retracted = None
        self.Settings.current_snapshot().gcode_trigger_on_retracted = None
        self.Settings.current_snapshot(
        ).gcode_trigger_on_detracting_start = None
        self.Settings.current_snapshot().gcode_trigger_on_detracting = None
        self.Settings.current_snapshot().gcode_trigger_on_detracted = None
        trigger = GcodeTrigger(self.Settings)

        # send a command that is the snapshot command using the defaults
        trigger.update(position, "snap")
        self.assertFalse(trigger.is_triggered(0))
        self.assertTrue(trigger.is_waiting(0))
        # change the extruder state and test
        # should not trigger because trigger tests the previous command
        position.update("G0 X0 Y0 Z0 E10 F0")
        trigger.update(position, "NotTheSnapshotCommand")
        self.assertTrue(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))
Esempio n. 2
0
class TestTrigger(unittest.TestCase):
    def setUp(self):
        self.Settings = OctolapseSettings(NamedTemporaryFile().name)
        self.PrinterTolerance = 0.0

    def tearDown(self):
        del self.Settings

    def test_IsInPosition_Rect_Forbidden(self):
        restrictions_dict = [{
            "Shape": "rect",
            "X": 10.0,
            "Y": 10.0,
            "X2": 20.0,
            "Y2": 20.0,
            "Type": "forbidden",
            "R": 1.0
        }]
        restrictions = self.Settings.current_snapshot(
        ).get_trigger_position_restrictions(restrictions_dict)
        self.assertTrue(
            trigger.is_in_position(restrictions, 0, 0, self.PrinterTolerance))
        self.assertTrue(
            trigger.is_in_position(restrictions, 100, 0,
                                   self.PrinterTolerance))
        self.assertTrue(
            trigger.is_in_position(restrictions, 20.1, 20.1,
                                   self.PrinterTolerance))
        self.assertTrue(
            trigger.is_in_position(restrictions, 15, 25,
                                   self.PrinterTolerance))
        self.assertTrue(
            trigger.is_in_position(restrictions, 25, 15,
                                   self.PrinterTolerance))

        self.assertFalse(
            trigger.is_in_position(restrictions, 10, 10,
                                   self.PrinterTolerance))
        self.assertFalse(
            trigger.is_in_position(restrictions, 15, 15,
                                   self.PrinterTolerance))
        self.assertFalse(
            trigger.is_in_position(restrictions, 20, 20,
                                   self.PrinterTolerance))

    def test_IsInPosition_Rect_Required(self):
        restrictions_dict = [{
            "Shape": "rect",
            "X": 10.0,
            "Y": 10.0,
            "X2": 20.0,
            "Y2": 20.0,
            "Type": "required",
            "R": 1.0
        }]
        restrictions = self.Settings.current_snapshot(
        ).get_trigger_position_restrictions(restrictions_dict)
        self.assertFalse(
            trigger.is_in_position(restrictions, 0, 0, self.PrinterTolerance))
        self.assertFalse(
            trigger.is_in_position(restrictions, 100, 0,
                                   self.PrinterTolerance))
        self.assertFalse(
            trigger.is_in_position(restrictions, 20.1, 20.1,
                                   self.PrinterTolerance))
        self.assertFalse(
            trigger.is_in_position(restrictions, 15, 25,
                                   self.PrinterTolerance))
        self.assertFalse(
            trigger.is_in_position(restrictions, 25, 15,
                                   self.PrinterTolerance))

        self.assertTrue(
            trigger.is_in_position(restrictions, 10, 10,
                                   self.PrinterTolerance))
        self.assertTrue(
            trigger.is_in_position(restrictions, 15, 15,
                                   self.PrinterTolerance))
        self.assertTrue(
            trigger.is_in_position(restrictions, 20, 20,
                                   self.PrinterTolerance))

    def test_IsInPosition_Rect_ForbiddenAndRequired(self):
        # test to restrictions, forbidden and required, have them overlap.
        restrictions_dict = [
            {
                "Shape": "rect",
                "X": 10.0,
                "Y": 10.0,
                "X2": 20.0,
                "Y2": 20.0,
                "Type": "required",
                "R": 1.0
            },
            {
                "Shape": "rect",
                "X": 15.0,
                "Y": 15.0,
                "X2": 25.0,
                "Y2": 25.0,
                "Type": "forbidden",
                "R": 1.0
            },
        ]
        restrictions = self.Settings.current_snapshot(
        ).get_trigger_position_restrictions(restrictions_dict)
        # out of all areas, restricted and forbidden
        self.assertFalse(
            trigger.is_in_position(restrictions, 0, 0, self.PrinterTolerance))
        self.assertFalse(
            trigger.is_in_position(restrictions, 100, 0,
                                   self.PrinterTolerance))
        self.assertFalse(
            trigger.is_in_position(restrictions, 12.5, 25,
                                   self.PrinterTolerance))
        self.assertFalse(
            trigger.is_in_position(restrictions, 25, 12.5,
                                   self.PrinterTolerance))

        # test only in forbidden area
        self.assertFalse(
            trigger.is_in_position(restrictions, 20.1, 25,
                                   self.PrinterTolerance))
        self.assertFalse(
            trigger.is_in_position(restrictions, 20.1, 20.1,
                                   self.PrinterTolerance))
        self.assertFalse(
            trigger.is_in_position(restrictions, 25, 20.1,
                                   self.PrinterTolerance))

        # test in required area only
        self.assertTrue(
            trigger.is_in_position(restrictions, 10, 10,
                                   self.PrinterTolerance))
        self.assertTrue(
            trigger.is_in_position(restrictions, 12.5, 12.5,
                                   self.PrinterTolerance))
        self.assertTrue(
            trigger.is_in_position(restrictions, 14.99, 14.99,
                                   self.PrinterTolerance))

        # test overlapping area
        self.assertFalse(
            trigger.is_in_position(restrictions, 15, 15,
                                   self.PrinterTolerance))
        self.assertFalse(
            trigger.is_in_position(restrictions, 20, 20,
                                   self.PrinterTolerance))
        self.assertFalse(
            trigger.is_in_position(restrictions, 15, 20,
                                   self.PrinterTolerance))
        self.assertFalse(
            trigger.is_in_position(restrictions, 20, 15,
                                   self.PrinterTolerance))
        self.assertFalse(
            trigger.is_in_position(restrictions, 17.5, 17.5,
                                   self.PrinterTolerance))

    def test_IsInPosition_Circle_Forbidden(self):
        restrictions_dict = [{
            "Shape": "circle",
            "R": 1.0,
            "Y": 10.0,
            "X": 10.0,
            "Type": "forbidden",
            "X2": 0,
            "Y2": 0
        }]
        restrictions = self.Settings.current_snapshot(
        ).get_trigger_position_restrictions(restrictions_dict)
        # tests outside forbidden area
        self.assertTrue(
            trigger.is_in_position(restrictions, 0, 0, self.PrinterTolerance))
        self.assertTrue(
            trigger.is_in_position(restrictions, 100, 0,
                                   self.PrinterTolerance))
        self.assertTrue(
            trigger.is_in_position(restrictions, 9, 9, self.PrinterTolerance))
        self.assertTrue(
            trigger.is_in_position(restrictions, 11, 11,
                                   self.PrinterTolerance))
        self.assertTrue(
            trigger.is_in_position(restrictions, 9, 11, self.PrinterTolerance))
        self.assertTrue(
            trigger.is_in_position(restrictions, 11, 9, self.PrinterTolerance))
        # tests inside forbidden area
        self.assertFalse(
            trigger.is_in_position(restrictions, 10, 10,
                                   self.PrinterTolerance))
        self.assertFalse(
            trigger.is_in_position(restrictions, 10, 9, self.PrinterTolerance))
        self.assertFalse(
            trigger.is_in_position(restrictions, 9, 10, self.PrinterTolerance))
        self.assertFalse(
            trigger.is_in_position(restrictions, 10, 11,
                                   self.PrinterTolerance))
        self.assertFalse(
            trigger.is_in_position(restrictions, 11, 10,
                                   self.PrinterTolerance))

    def test_IsInPosition_Circle_Required(self):
        restrictions_dict = [{
            "Shape": "circle",
            "R": 1.0,
            "Y": 10.0,
            "X": 10.0,
            "Type": "required",
            "X2": 20.0,
            "Y2": 20.0
        }]
        restrictions = self.Settings.current_snapshot(
        ).get_trigger_position_restrictions(restrictions_dict)
        # tests outside area
        self.assertFalse(
            trigger.is_in_position(restrictions, 0, 0, self.PrinterTolerance))
        self.assertFalse(
            trigger.is_in_position(restrictions, 100, 0,
                                   self.PrinterTolerance))
        self.assertFalse(
            trigger.is_in_position(restrictions, 9, 9, self.PrinterTolerance))
        self.assertFalse(
            trigger.is_in_position(restrictions, 11, 11,
                                   self.PrinterTolerance))
        self.assertFalse(
            trigger.is_in_position(restrictions, 9, 11, self.PrinterTolerance))
        self.assertFalse(
            trigger.is_in_position(restrictions, 11, 9, self.PrinterTolerance))

        # tests inside area
        self.assertTrue(
            trigger.is_in_position(restrictions, 10, 10,
                                   self.PrinterTolerance))
        self.assertTrue(
            trigger.is_in_position(restrictions, 10, 9, self.PrinterTolerance))
        self.assertTrue(
            trigger.is_in_position(restrictions, 9, 10, self.PrinterTolerance))
        self.assertTrue(
            trigger.is_in_position(restrictions, 10, 11,
                                   self.PrinterTolerance))
        self.assertTrue(
            trigger.is_in_position(restrictions, 11, 10,
                                   self.PrinterTolerance))

    def test_IsInPosition_Circle_ForbiddenAndRequired(self):
        # test to restrictions, forbidden and required, have them overlap.
        restrictions_dict = [
            {
                "Shape": "circle",
                "R": 1.0,
                "Y": 10.0,
                "X": 10.0,
                "Type": "required",
                "X2": 20.0,
                "Y2": 20.0
            },
            {
                "Shape": "circle",
                "R": 1.0,
                "Y": 10.0,
                "X": 11.0,
                "Type": "forbidden",
                "X2": 25.0,
                "Y2": 25.0
            },
        ]
        restrictions = self.Settings.current_snapshot(
        ).get_trigger_position_restrictions(restrictions_dict)
        # out of all areas, restricted and forbidden
        self.assertFalse(
            trigger.is_in_position(restrictions, 0, 0, self.PrinterTolerance))
        self.assertFalse(
            trigger.is_in_position(restrictions, 100, 0,
                                   self.PrinterTolerance))

        # test only in forbidden area
        self.assertFalse(
            trigger.is_in_position(restrictions, 12, 10,
                                   self.PrinterTolerance))
        self.assertFalse(
            trigger.is_in_position(restrictions, 11, 11,
                                   self.PrinterTolerance))
        self.assertFalse(
            trigger.is_in_position(restrictions, 11, 9, self.PrinterTolerance))

        # test in required area only
        self.assertTrue(
            trigger.is_in_position(restrictions, 10, 11,
                                   self.PrinterTolerance))
        self.assertTrue(
            trigger.is_in_position(restrictions, 10, 9, self.PrinterTolerance))
        self.assertTrue(
            trigger.is_in_position(restrictions, 9, 10, self.PrinterTolerance))

        # test overlapping area
        self.assertFalse(
            trigger.is_in_position(restrictions, 10, 10,
                                   self.PrinterTolerance))
        self.assertFalse(
            trigger.is_in_position(restrictions, 11, 10,
                                   self.PrinterTolerance))
        self.assertFalse(
            trigger.is_in_position(restrictions, 10.5, 10,
                                   self.PrinterTolerance))
Esempio n. 3
0
class TestSnapshotGcode(unittest.TestCase):
    def setUp(self):
        self.Settings = OctolapseSettings(NamedTemporaryFile().name)
        self.OctoprintPrinterProfile = self.create_octoprint_printer_profile()
        printer = get_printer_profile()
        self.Settings.printers.update({printer["guid"]: Printer(printer=printer)})
        self.Settings.current_printer_profile_guid = printer["guid"]
        self.Extruder = Extruder(self.Settings)
        self.Position = Position(self.Settings, self.OctoprintPrinterProfile, False)
        # set starting position


    def tearDown(self):
        del self.Settings
        del self.Extruder


    @staticmethod
    def create_octoprint_printer_profile():
        return dict(
            volume=dict(
                width=250,
                depth=200,
                height=200,
                formFactor="Not A Circle",
                custom_box=False,
            )
        )

    def test_GetSnapshotPosition_Absolute(self):
        """Test getting absolute snapshot positions for x and y"""
        # adjust the settings for absolute position and create the snapshot gcode generator
        self.Settings.current_stabilization().x_type = "fixed_coordinate"
        self.Settings.current_stabilization().x_fixed_coordinate = 10
        self.Settings.current_stabilization().y_type = "fixed_coordinate"
        self.Settings.current_stabilization().y_fixed_coordinate = 20
        snapshot_gcode_generator = SnapshotGcodeGenerator(
            self.Settings, self.create_octoprint_printer_profile())

        # get the coordinates and test
        coords = snapshot_gcode_generator.get_snapshot_position(0, 0)
        self.assertTrue(coords["X"] == 10 and coords["Y"] == 20)
        # get the coordinates and test
        coords = snapshot_gcode_generator.get_snapshot_position(1, 1)
        self.assertTrue(coords["X"] == 10 and coords["Y"] == 20)
        # get the coordinates and test
        coords = snapshot_gcode_generator.get_snapshot_position(100, 100)
        self.assertTrue(coords["X"] == 10 and coords["Y"] == 20)

    def test_GetSnapshotPosition_AbsolutePath(self):
        """Test getting absolute path snapshot positions for x and y"""
        # adjust the settings for absolute position and create the snapshot gcode generator
        self.Settings.current_stabilization().x_type = "fixed_path"
        self.Settings.current_stabilization().x_fixed_path = "0,1,2,3,4,5"
        self.Settings.current_stabilization().y_type = "fixed_path"
        self.Settings.current_stabilization().y_fixed_path = "5,4,3,2,1,0"

        # test with no loop
        self.Settings.current_stabilization().x_fixed_path_loop = False
        self.Settings.current_stabilization().x_fixed_path_invert_loop = False
        self.Settings.current_stabilization().y_fixed_path_loop = False
        self.Settings.current_stabilization().y_fixed_path_invert_loop = False
        snapshot_gcode_generator = SnapshotGcodeGenerator(
            self.Settings, self.create_octoprint_printer_profile())
        coordinates = snapshot_gcode_generator.get_snapshot_position(0, 0)
        self.assertTrue(coordinates["X"] == 0 and coordinates["Y"] == 5)
        coordinates = snapshot_gcode_generator.get_snapshot_position(0, 0)
        self.assertTrue(coordinates["X"] == 1 and coordinates["Y"] == 4)
        coordinates = snapshot_gcode_generator.get_snapshot_position(1, 0)
        self.assertTrue(coordinates["X"] == 2 and coordinates["Y"] == 3)
        coordinates = snapshot_gcode_generator.get_snapshot_position(1, 1)
        self.assertTrue(coordinates["X"] == 3 and coordinates["Y"] == 2)
        coordinates = snapshot_gcode_generator.get_snapshot_position(0, 1)
        self.assertTrue(coordinates["X"] == 4 and coordinates["Y"] == 1)
        coordinates = snapshot_gcode_generator.get_snapshot_position(0, 0)
        self.assertTrue(coordinates["X"] == 5 and coordinates["Y"] == 0)
        coordinates = snapshot_gcode_generator.get_snapshot_position(0, 0)
        self.assertTrue(coordinates["X"] == 5 and coordinates["Y"] == 0)
        coordinates = snapshot_gcode_generator.get_snapshot_position(0, 0)
        self.assertTrue(coordinates["X"] == 5 and coordinates["Y"] == 0)

        # test with loop, no invert
        self.Settings.current_stabilization().x_fixed_path_loop = True
        self.Settings.current_stabilization().x_fixed_path_invert_loop = False
        self.Settings.current_stabilization().y_fixed_path_loop = True
        self.Settings.current_stabilization().y_fixed_path_invert_loop = False
        snapshot_gcode_generator = SnapshotGcodeGenerator(
            self.Settings, self.create_octoprint_printer_profile())
        coordinates = snapshot_gcode_generator.get_snapshot_position(0, 0)
        self.assertTrue(coordinates["X"] == 0 and coordinates["Y"] == 5)
        coordinates = snapshot_gcode_generator.get_snapshot_position(0, 0)
        self.assertTrue(coordinates["X"] == 1 and coordinates["Y"] == 4)
        coordinates = snapshot_gcode_generator.get_snapshot_position(1, 0)
        self.assertTrue(coordinates["X"] == 2 and coordinates["Y"] == 3)
        coordinates = snapshot_gcode_generator.get_snapshot_position(1, 1)
        self.assertTrue(coordinates["X"] == 3 and coordinates["Y"] == 2)
        coordinates = snapshot_gcode_generator.get_snapshot_position(0, 1)
        self.assertTrue(coordinates["X"] == 4 and coordinates["Y"] == 1)
        coordinates = snapshot_gcode_generator.get_snapshot_position(0, 0)
        self.assertTrue(coordinates["X"] == 5 and coordinates["Y"] == 0)
        coordinates = snapshot_gcode_generator.get_snapshot_position(0, 0)
        self.assertTrue(coordinates["X"] == 0 and coordinates["Y"] == 5)
        coordinates = snapshot_gcode_generator.get_snapshot_position(0, 0)
        self.assertTrue(coordinates["X"] == 1 and coordinates["Y"] == 4)

        # test with loop and invert
        self.Settings.current_stabilization().x_fixed_path_loop = True
        self.Settings.current_stabilization().x_fixed_path_invert_loop = True
        self.Settings.current_stabilization().y_fixed_path_loop = True
        self.Settings.current_stabilization().y_fixed_path_invert_loop = True
        snapshot_gcode_generator = SnapshotGcodeGenerator(
            self.Settings, self.create_octoprint_printer_profile())
        coordinates = snapshot_gcode_generator.get_snapshot_position(0, 0)
        self.assertTrue(coordinates["X"] == 0 and coordinates["Y"] == 5)
        coordinates = snapshot_gcode_generator.get_snapshot_position(0, 0)
        self.assertTrue(coordinates["X"] == 1 and coordinates["Y"] == 4)
        coordinates = snapshot_gcode_generator.get_snapshot_position(1, 0)
        self.assertTrue(coordinates["X"] == 2 and coordinates["Y"] == 3)
        coordinates = snapshot_gcode_generator.get_snapshot_position(1, 1)
        self.assertTrue(coordinates["X"] == 3 and coordinates["Y"] == 2)
        coordinates = snapshot_gcode_generator.get_snapshot_position(0, 1)
        self.assertTrue(coordinates["X"] == 4 and coordinates["Y"] == 1)
        coordinates = snapshot_gcode_generator.get_snapshot_position(0, 0)
        self.assertTrue(coordinates["X"] == 5 and coordinates["Y"] == 0)
        coordinates = snapshot_gcode_generator.get_snapshot_position(0, 0)
        self.assertTrue(coordinates["X"] == 4 and coordinates["Y"] == 1)
        coordinates = snapshot_gcode_generator.get_snapshot_position(0, 0)
        self.assertTrue(coordinates["X"] == 3 and coordinates["Y"] == 2)
        coordinates = snapshot_gcode_generator.get_snapshot_position(0, 0)
        self.assertTrue(coordinates["X"] == 2 and coordinates["Y"] == 3)
        coordinates = snapshot_gcode_generator.get_snapshot_position(0, 0)
        self.assertTrue(coordinates["X"] == 1 and coordinates["Y"] == 4)
        coordinates = snapshot_gcode_generator.get_snapshot_position(0, 0)
        self.assertTrue(coordinates["X"] == 0 and coordinates["Y"] == 5)
        coordinates = snapshot_gcode_generator.get_snapshot_position(0, 0)
        self.assertTrue(coordinates["X"] == 1 and coordinates["Y"] == 4)

    def test_GetSnapshotPosition_BedRelative(self):
        """Test getting bed relative snapshot positions for x and y"""
        # adjust the settings for absolute position and create the snapshot gcode generator
        self.Settings.current_stabilization().x_type = "relative"
        self.Settings.current_stabilization().x_relative = 0
        self.Settings.current_stabilization().y_type = "relative"
        self.Settings.current_stabilization().y_relative = 100
        snapshot_gcode_generator = SnapshotGcodeGenerator(
            self.Settings, self.create_octoprint_printer_profile())

        # get the coordinates and test
        coords = snapshot_gcode_generator.get_snapshot_position(0, 0)
        self.assertTrue(coords["X"] == 0 and coords["Y"] == 200)
        # get the coordinates and test
        coords = snapshot_gcode_generator.get_snapshot_position(1, 1)
        self.assertTrue(coords["X"] == 0 and coords["Y"] == 200)
        # get the coordinates and test
        coords = snapshot_gcode_generator.get_snapshot_position(100, 100)
        self.assertTrue(coords["X"] == 0 and coords["Y"] == 200)

    def test_GetSnapshotPosition_BedRelativePath(self):
        """Test getting bed relative path snapshot positions for x and y"""
        # adjust the settings for absolute position and create the snapshot gcode generator
        self.Settings.current_stabilization().x_type = "relative_path"
        self.Settings.current_stabilization().x_relative_path = "0,25,50,75,100"
        self.Settings.current_stabilization().y_type = "relative_path"
        self.Settings.current_stabilization().y_relative_path = "100,75,50,25,0"

        # test with no loop
        self.Settings.current_stabilization().x_relative_path_loop = False
        self.Settings.current_stabilization().x_relative_path_invert_loop = False
        self.Settings.current_stabilization().y_relative_path_loop = False
        self.Settings.current_stabilization().y_relative_path_invert_loop = False
        snapshot_gcode_generator = SnapshotGcodeGenerator(
            self.Settings, self.create_octoprint_printer_profile())
        coordinates = snapshot_gcode_generator.get_snapshot_position(0, 0)
        self.assertTrue(coordinates["X"] == 0 and coordinates["Y"] == 200)
        coordinates = snapshot_gcode_generator.get_snapshot_position(0, 0)
        self.assertTrue(coordinates["X"] == 62.5 and coordinates["Y"] == 150)
        coordinates = snapshot_gcode_generator.get_snapshot_position(1, 0)
        self.assertTrue(coordinates["X"] == 125 and coordinates["Y"] == 100)
        coordinates = snapshot_gcode_generator.get_snapshot_position(1, 1)
        self.assertTrue(coordinates["X"] == 187.5 and coordinates["Y"] == 50)
        coordinates = snapshot_gcode_generator.get_snapshot_position(0, 1)
        self.assertTrue(coordinates["X"] == 250 and coordinates["Y"] == 0)
        coordinates = snapshot_gcode_generator.get_snapshot_position(0, 0)
        self.assertTrue(coordinates["X"] == 250 and coordinates["Y"] == 0)
        coordinates = snapshot_gcode_generator.get_snapshot_position(0, 0)
        self.assertTrue(coordinates["X"] == 250 and coordinates["Y"] == 0)

        # test with loop, no invert
        self.Settings.current_stabilization().x_relative_path_loop = True
        self.Settings.current_stabilization().x_relative_path_invert_loop = False
        self.Settings.current_stabilization().y_relative_path_loop = True
        self.Settings.current_stabilization().y_relative_path_invert_loop = False
        snapshot_gcode_generator = SnapshotGcodeGenerator(
            self.Settings, self.create_octoprint_printer_profile())
        coordinates = snapshot_gcode_generator.get_snapshot_position(0, 0)
        self.assertTrue(coordinates["X"] == 0 and coordinates["Y"] == 200)
        coordinates = snapshot_gcode_generator.get_snapshot_position(0, 0)
        self.assertTrue(coordinates["X"] == 62.5 and coordinates["Y"] == 150)
        coordinates = snapshot_gcode_generator.get_snapshot_position(1, 0)
        self.assertTrue(coordinates["X"] == 125 and coordinates["Y"] == 100)
        coordinates = snapshot_gcode_generator.get_snapshot_position(1, 1)
        self.assertTrue(coordinates["X"] == 187.5 and coordinates["Y"] == 50)
        coordinates = snapshot_gcode_generator.get_snapshot_position(0, 1)
        self.assertTrue(coordinates["X"] == 250 and coordinates["Y"] == 0)
        coordinates = snapshot_gcode_generator.get_snapshot_position(0, 0)
        self.assertTrue(coordinates["X"] == 0 and coordinates["Y"] == 200)
        coordinates = snapshot_gcode_generator.get_snapshot_position(0, 0)
        self.assertTrue(coordinates["X"] == 62.5 and coordinates["Y"] == 150)

        # test with loop and invert
        self.Settings.current_stabilization().x_relative_path_loop = True
        self.Settings.current_stabilization().x_relative_path_invert_loop = True
        self.Settings.current_stabilization().y_relative_path_loop = True
        self.Settings.current_stabilization().y_relative_path_invert_loop = True
        snapshot_gcode_generator = SnapshotGcodeGenerator(
            self.Settings, self.create_octoprint_printer_profile())
        coordinates = snapshot_gcode_generator.get_snapshot_position(0, 0)
        self.assertTrue(coordinates["X"] == 0 and coordinates["Y"] == 200)
        coordinates = snapshot_gcode_generator.get_snapshot_position(0, 0)
        self.assertTrue(coordinates["X"] == 62.5 and coordinates["Y"] == 150)
        coordinates = snapshot_gcode_generator.get_snapshot_position(1, 0)
        self.assertTrue(coordinates["X"] == 125 and coordinates["Y"] == 100)
        coordinates = snapshot_gcode_generator.get_snapshot_position(1, 1)
        self.assertTrue(coordinates["X"] == 187.5 and coordinates["Y"] == 50)
        coordinates = snapshot_gcode_generator.get_snapshot_position(0, 1)
        self.assertTrue(coordinates["X"] == 250 and coordinates["Y"] == 0)
        coordinates = snapshot_gcode_generator.get_snapshot_position(0, 0)
        self.assertTrue(coordinates["X"] == 187.5 and coordinates["Y"] == 50)
        coordinates = snapshot_gcode_generator.get_snapshot_position(0, 0)
        self.assertTrue(coordinates["X"] == 125 and coordinates["Y"] == 100)

    def test_GetSnapshotGcode_Fixed_AbsoluteCoordintes_ExtruderRelative(self):
        """Test snapshot gcode in absolute coordinate system with relative extruder and fixed coordinate
        stabilization """
        # adjust the settings for absolute position and create the snapshot gcode generator
        self.Settings.current_stabilization().x_type = "fixed_coordinate"
        self.Settings.current_stabilization().x_fixed_coordinate = 10
        self.Settings.current_stabilization().y_type = "fixed_coordinate"
        self.Settings.current_stabilization().y_fixed_coordinate = 20
        snapshot_gcode_generator = SnapshotGcodeGenerator(
            self.Settings, self.create_octoprint_printer_profile())
        self.Extruder.is_retracted = lambda: True

        self.Position.update(Commands.parse("G90"))
        self.Position.update(Commands.parse("M83"))
        self.Position.update(Commands.parse("G28"))
        self.Position.update(Commands.parse("G0 X95 Y95 Z0.2 F3600"))
        parsed_command = Commands.parse("G0 X100 Y100")
        self.Position.update(parsed_command)
        snapshot_gcode = snapshot_gcode_generator.create_snapshot_gcode(
            self.Position, None, parsed_command)
        gcode_commands = snapshot_gcode.snapshot_gcode()

        # verify the created gcodegcode_commands
        self.assertEqual(gcode_commands[0], "G1 E-4.00000 F4800")
        self.assertEqual(gcode_commands[1], "G1 X10.000 Y20.000 F10800")
        self.assertEqual(gcode_commands[2], "G1 X100.000 Y100.000")
        self.assertEqual(gcode_commands[3], "G1 E4.00000 F3000")
        self.assertEqual(gcode_commands[4], "G1 F3600")
        self.assertEqual(gcode_commands[5], parsed_command.gcode)

        # verify the return coordinates
        self.assertEqual(snapshot_gcode.ReturnX, 100)
        self.assertEqual(snapshot_gcode.ReturnY, 100)
        self.assertEqual(snapshot_gcode.ReturnZ, 0.2)

        self.assertEqual(snapshot_gcode.X, 10)
        self.assertEqual(snapshot_gcode.Y, 20)
        self.assertEqual(snapshot_gcode.Z, None)

    def test_GetSnapshotGcode_RelativePath_RelativeCoordinates_ExtruderAbsolute_ZHop_Retraction(self):
        # test with relative paths, absolute extruder coordinates, retract and z hop
        # use relative coordinates for stabilizations
        self.Settings.current_stabilization().x_type = "relative_path"
        self.Settings.current_stabilization().x_relative_path = "50,100"  # 125,250
        self.Settings.current_stabilization().x_relative_path_loop = False
        self.Settings.current_stabilization().x_relative_path_invert_loop = False
        self.Settings.current_stabilization().y_type = "relative_path"
        self.Settings.current_stabilization().y_relative_path = "50,100"  # 100,200
        self.Settings.current_stabilization().y_relative_path_loop = False
        self.Settings.current_stabilization().y_relative_path_invert_loop = False
        self.Settings.current_snapshot().retract_before_move = True
        snapshot_gcode_generator = SnapshotGcodeGenerator(
            self.Settings, self.create_octoprint_printer_profile())

        snapshot_gcode = snapshot_gcode_generator.create_snapshot_gcode(
            10, 10, 10, 3600, True, False, self.Extruder, 0.5, "SavedCommand")
        # verify the created gcode
        self.assertEqual(snapshot_gcode.GcodeCommands[0], "M83")
        self.assertEqual(snapshot_gcode.GcodeCommands[1], "G1 F4000")
        self.assertEqual(snapshot_gcode.GcodeCommands[2], "G1 E-2.000")
        self.assertEqual(snapshot_gcode.GcodeCommands[3], "G1 F6000")
        self.assertEqual(snapshot_gcode.GcodeCommands[4], "G1 Z0.500")
        self.assertEqual(snapshot_gcode.GcodeCommands[5], "G90")
        self.assertEqual(snapshot_gcode.GcodeCommands[6], "G1 F6000")
        self.assertEqual(snapshot_gcode.GcodeCommands[7], "G1 X125.000 Y100.000")
        self.assertEqual(snapshot_gcode.GcodeCommands[8], "M400")
        self.assertEqual(snapshot_gcode.GcodeCommands[9], "M114")
        self.assertEqual(snapshot_gcode.GcodeCommands[10], "G1 X10.000 Y10.000")
        self.assertEqual(snapshot_gcode.GcodeCommands[11], "G91")
        self.assertEqual(snapshot_gcode.GcodeCommands[12], "G1 F6000")
        self.assertEqual(snapshot_gcode.GcodeCommands[13], "G1 Z-0.500")
        self.assertEqual(snapshot_gcode.GcodeCommands[14], "G1 F3000")
        self.assertEqual(snapshot_gcode.GcodeCommands[15], "G1 E2.000")
        self.assertEqual(snapshot_gcode.GcodeCommands[16], "M82")
        self.assertEqual(snapshot_gcode.GcodeCommands[17], "G1 F3600")
        self.assertEqual(snapshot_gcode.GcodeCommands[18], "SAVEDCOMMAND")
        self.assertEqual(snapshot_gcode.GcodeCommands[19], "M400")
        self.assertEqual(snapshot_gcode.GcodeCommands[20], "M114")

        # verify the indexes of the generated gcode
        self.assertTrue(snapshot_gcode.SnapshotIndex == 9)
        self.assertTrue(snapshot_gcode.end_index() == 20)
        # verify the return coordinates
        self.assertTrue(snapshot_gcode.ReturnX == 10)
        self.assertTrue(snapshot_gcode.ReturnY == 10)
        self.assertTrue(snapshot_gcode.ReturnZ == 10)

    def test_GetSnapshotGcode_FixedPath_RelativeCoordinates_ExtruderAbsolute_ZHop_AlreadyRetracted(self):
        # test with relative paths, absolute extruder coordinates, retract and z hop
        # use relative coordinates for stabilizations
        self.Settings.current_stabilization().x_type = "fixed_path"
        self.Settings.current_stabilization().x_fixed_path = "50,100"  # 125,250
        self.Settings.current_stabilization().x_fixed_path_loop = False
        self.Settings.current_stabilization().x_fixed_path_invert_loop = False
        self.Settings.current_stabilization().y_type = "fixed_path"
        self.Settings.current_stabilization().y_fixed_path = "50,100"  # 100,200
        self.Settings.current_stabilization().y_fixed_path_loop = False
        self.Settings.current_stabilization().y_fixed_path_invert_loop = False
        self.Settings.current_snapshot().retract_before_move = True
        snapshot_gcode_generator = SnapshotGcodeGenerator(
            self.Settings, self.create_octoprint_printer_profile())
        self.Extruder.is_retracted = lambda: True
        snapshot_gcode = snapshot_gcode_generator.create_snapshot_gcode(
            100, 50, 0, 3600, True, False, self.Extruder, 0.5, "SavedCommand")

        # verify the created gcode
        self.assertEqual(snapshot_gcode.GcodeCommands[0], "G1 F6000")
        self.assertEqual(snapshot_gcode.GcodeCommands[1], "G1 Z0.500")
        self.assertEqual(snapshot_gcode.GcodeCommands[2], "G90")
        self.assertEqual(snapshot_gcode.GcodeCommands[3], "G1 F6000")
        self.assertEqual(snapshot_gcode.GcodeCommands[4], "G1 X50.000 Y50.000")
        self.assertEqual(snapshot_gcode.GcodeCommands[5], "M400")
        self.assertEqual(snapshot_gcode.GcodeCommands[6], "M114")
        self.assertEqual(snapshot_gcode.GcodeCommands[7], "G1 X100.000 Y50.000")
        self.assertEqual(snapshot_gcode.GcodeCommands[8], "G91")
        self.assertEqual(snapshot_gcode.GcodeCommands[9], "G1 F6000")
        self.assertEqual(snapshot_gcode.GcodeCommands[10], "G1 Z-0.500")
        self.assertEqual(snapshot_gcode.GcodeCommands[11], "G1 F3600")
        self.assertEqual(snapshot_gcode.GcodeCommands[12], "SAVEDCOMMAND")
        self.assertEqual(snapshot_gcode.GcodeCommands[13], "M400")
        self.assertEqual(snapshot_gcode.GcodeCommands[14], "M114")

        # verify the indexes of the generated gcode
        self.assertEqual(snapshot_gcode.SnapshotIndex, 6)
        self.assertEqual(snapshot_gcode.end_index(), 14)
        # verify the return coordinates
        self.assertEqual(snapshot_gcode.ReturnX, 100)
        self.assertEqual(snapshot_gcode.ReturnY, 50)
        self.assertEqual(snapshot_gcode.ReturnZ, 0)

        # Get the next coordinate in the path
        snapshot_gcode = snapshot_gcode_generator.create_snapshot_gcode(
            101, 51, 0, 3600, True, False, self.Extruder, 0.5, "SavedCommand")
        # verify the created gcode
        self.assertEqual(snapshot_gcode.GcodeCommands[0], "G1 F6000")
        self.assertEqual(snapshot_gcode.GcodeCommands[1], "G1 Z0.500")
        self.assertEqual(snapshot_gcode.GcodeCommands[2], "G90")
        self.assertEqual(snapshot_gcode.GcodeCommands[3], "G1 F6000")
        self.assertEqual(snapshot_gcode.GcodeCommands[4], "G1 X100.000 Y100.000")
        self.assertEqual(snapshot_gcode.GcodeCommands[5], "M400")
        self.assertEqual(snapshot_gcode.GcodeCommands[6], "M114")
        self.assertEqual(snapshot_gcode.GcodeCommands[7], "G1 X101.000 Y51.000")
        self.assertEqual(snapshot_gcode.GcodeCommands[8], "G91")
        self.assertEqual(snapshot_gcode.GcodeCommands[9], "G1 F6000")
        self.assertEqual(snapshot_gcode.GcodeCommands[10], "G1 Z-0.500")
        self.assertEqual(snapshot_gcode.GcodeCommands[11], "G1 F3600")
        self.assertEqual(snapshot_gcode.GcodeCommands[12], "SAVEDCOMMAND")
        self.assertEqual(snapshot_gcode.GcodeCommands[13], "M400")
        self.assertEqual(snapshot_gcode.GcodeCommands[14], "M114")

        # verify the indexes of the generated gcode
        self.assertEqual(snapshot_gcode.SnapshotIndex, 6)
        self.assertEqual(snapshot_gcode.end_index(), 14)
        # verify the return coordinates
        self.assertEqual(snapshot_gcode.ReturnX, 101)
        self.assertEqual(snapshot_gcode.ReturnY, 51)
        self.assertEqual(snapshot_gcode.ReturnZ, 0)

    def test_GetSnapshotGcode_Relative_RelativeCoordinates_AbsoluteExtruder_ZhopTooHigh(self):
        """Test snapshot gcode with relative stabilization, relative coordinates, absolute extruder, z is too high to
        hop, no retraction """

        # test with relative coordinates, absolute extruder coordinates, z hop impossible (current z height will not
        # allow this since it puts things outside of the bounds) use relative coordinates for stabilizations
        self.Settings.current_stabilization().x_type = "relative"
        self.Settings.current_stabilization().x_relative = 50  # 125
        self.Settings.current_stabilization().y_type = "relative"
        self.Settings.current_stabilization().y_relative = 100  # 200
        self.Settings.current_snapshot().retract_before_move = False
        snapshot_gcode_generator = SnapshotGcodeGenerator(
            self.Settings, self.create_octoprint_printer_profile())
        # create
        snapshot_gcode = snapshot_gcode_generator.create_snapshot_gcode(
            10, 10, 200, 3600, True, False, self.Extruder, 0.5, "SavedCommand")
        # verify the created gcode
        self.assertEqual(snapshot_gcode.GcodeCommands[0], "G90")
        self.assertEqual(snapshot_gcode.GcodeCommands[1], "G1 F6000")
        self.assertEqual(snapshot_gcode.GcodeCommands[2], "G1 X125.000 Y200.000")
        self.assertEqual(snapshot_gcode.GcodeCommands[3], "M400")
        self.assertEqual(snapshot_gcode.GcodeCommands[4], "M114")
        self.assertEqual(snapshot_gcode.GcodeCommands[5], "G1 X10.000 Y10.000")
        self.assertEqual(snapshot_gcode.GcodeCommands[6], "G91")
        self.assertEqual(snapshot_gcode.GcodeCommands[7], "G1 F3600")
        self.assertEqual(snapshot_gcode.GcodeCommands[8], "SAVEDCOMMAND")
        self.assertEqual(snapshot_gcode.GcodeCommands[9], "M400")
        self.assertEqual(snapshot_gcode.GcodeCommands[10], "M114")

        # verify the indexes of the generated gcode
        self.assertTrue(snapshot_gcode.SnapshotIndex == 4)
        self.assertTrue(snapshot_gcode.end_index() == 10)
        # verify the return coordinates
        self.assertTrue(snapshot_gcode.ReturnX == 10)
        self.assertTrue(snapshot_gcode.ReturnY == 10)
        self.assertTrue(snapshot_gcode.ReturnZ == 200)

    def test_GetSnapshotGcode_SnapshotCommands(self):
        # test with relative paths, absolute extruder coordinates, retract and z hop
        # use relative coordinates for stabilizations
        self.Settings.current_stabilization().x_type = "fixed_path"
        self.Settings.current_stabilization().x_fixed_path = "50,100"  # 125,250
        self.Settings.current_stabilization().x_fixed_path_loop = False
        self.Settings.current_stabilization().x_fixed_path_invert_loop = False
        self.Settings.current_stabilization().y_type = "fixed_path"
        self.Settings.current_stabilization().y_fixed_path = "50,100"  # 100,200
        self.Settings.current_stabilization().y_fixed_path_loop = False
        self.Settings.current_stabilization().y_fixed_path_invert_loop = False
        self.Settings.current_snapshot().retract_before_move = True
        snapshot_gcode_generator = SnapshotGcodeGenerator(
            self.Settings, self.create_octoprint_printer_profile())
        self.Extruder.is_retracted = lambda: True
        snapshot_gcode = snapshot_gcode_generator.create_snapshot_gcode(
            100, 50, 0, 3600, True, False, self.Extruder, 0.5, "SavedCommand")

        # verify the snapshot commands
        self.assertEqual(snapshot_gcode.get_snapshot_commands()[0], "G1 F6000")
        self.assertEqual(snapshot_gcode.get_snapshot_commands()[1], "G1 Z0.500")
        self.assertEqual(snapshot_gcode.get_snapshot_commands()[2], "G90")
        self.assertEqual(snapshot_gcode.get_snapshot_commands()[3], "G1 F6000")
        self.assertEqual(snapshot_gcode.get_snapshot_commands()[4], "G1 X50.000 Y50.000")
        self.assertEqual(snapshot_gcode.get_snapshot_commands()[5], "M400")
        self.assertEqual(snapshot_gcode.get_snapshot_commands()[6], "M114")

    def test_GetSnapshotGcode_ReturnCommands(self):
        # test with relative paths, absolute extruder coordinates, retract and z hop
        # use relative coordinates for stabilizations
        self.Settings.current_stabilization().x_type = "fixed_path"
        self.Settings.current_stabilization().x_fixed_path = "50,100"  # 125,250
        self.Settings.current_stabilization().x_fixed_path_loop = False
        self.Settings.current_stabilization().x_fixed_path_invert_loop = False
        self.Settings.current_stabilization().y_type = "fixed_path"
        self.Settings.current_stabilization().y_fixed_path = "50,100"  # 100,200
        self.Settings.current_stabilization().y_fixed_path_loop = False
        self.Settings.current_stabilization().y_fixed_path_invert_loop = False
        self.Settings.current_snapshot().retract_before_move = True
        snapshot_gcode_generator = SnapshotGcodeGenerator(
            self.Settings, self.create_octoprint_printer_profile())
        self.Extruder.is_retracted = lambda: True
        snapshot_gcode = snapshot_gcode_generator.create_snapshot_gcode(
            100, 50, 0, 3600, True, False, self.Extruder, 0.5, "SavedCommand")

        # verify the return commands
        self.assertEqual(snapshot_gcode.get_return_commands()[0], "G1 X100.000 Y50.000")
        self.assertEqual(snapshot_gcode.get_return_commands()[1], "G91")
        self.assertEqual(snapshot_gcode.get_return_commands()[2], "G1 F6000")
        self.assertEqual(snapshot_gcode.get_return_commands()[3], "G1 Z-0.500")
        self.assertEqual(snapshot_gcode.get_return_commands()[4], "G1 F3600")
        self.assertEqual(snapshot_gcode.get_return_commands()[5], "SAVEDCOMMAND")
        self.assertEqual(snapshot_gcode.get_return_commands()[6], "M400")
        self.assertEqual(snapshot_gcode.get_return_commands()[7], "M114")
Esempio n. 4
0
class TestLayerTrigger(unittest.TestCase):
    def setUp(self):
        self.Settings = OctolapseSettings(NamedTemporaryFile().name)
        self.Settings.current_printer().e_axis_default_mode = 'relative'
        self.Settings.current_printer().xyz_axes_default_mode = 'absolute'
        self.Settings.current_printer().auto_detect_position = False
        self.Settings.current_printer().origin_x = 0
        self.Settings.current_printer().origin_y = 0
        self.Settings.current_printer().origin_z = 0
        self.OctoprintPrinterProfile = self.create_octoprint_printer_profile()

    def tearDown(self):
        del self.Settings
        del self.OctoprintPrinterProfile

    @staticmethod
    def create_octoprint_printer_profile():
        return dict(volume=dict(
            width=250,
            depth=200,
            height=200,
            formFactor="Not A Circle",
            custom_box=False,
        ))

    def test_LayerTrigger_LayerChange(self):
        """Test the layer trigger for layer changes triggers"""

        position = Position(self.Settings, self.OctoprintPrinterProfile, False)
        trigger = LayerTrigger(self.Settings)
        trigger.ExtruderTriggers = ExtruderTriggers(None, None, None, None,
                                                    None, None, None, None,
                                                    None,
                                                    None)  # Ignore extruder
        trigger.RequireZHop = False  # no zhop required
        trigger.HeightIncrement = 0  # Trigger on any height change
        # test initial state
        self.assertFalse(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # send commands that normally would trigger a layer change, but without all axis homed.
        position.update("g0 x0 y0 z.2 e1")
        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # Home all axis and try again
        position.update("g28")
        trigger.update(position)
        position.update("g0 x0 y0 z.2 e1")
        trigger.update(position)
        self.assertTrue(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # extrude again on the same layer and make sure it does NOT trigger
        position.update("g0 x1 y1 z.2 e1")
        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # move to higher layer, but do not extrude (no layer change)
        position.update("g0 x1 y1 z.4")
        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))
        position.update("g0 x2 y2 z.4")
        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # return to previous layer, do not extrude
        position.update("g0 x2 y2 z.2")
        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))
        position.update("g0 x4 y4 z.2")
        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # extrude again on current layer
        position.update("g0 x2 y2 z.2 e1")
        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # move up two times, down and extrude (this should trigger after the final command
        position.update("g0 x2 y2 z.4")
        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))
        position.update("g0 x2 y2 z.6")
        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))
        position.update("g0 x2 y2 z.4 e1")
        trigger.update(position)
        self.assertTrue(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # This should never happen in a print, but test extruding on previous layers
        # move down to previous layer, extrude,
        position.update("g0 x2 y2 z.2 e1")
        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))
        # move back to current layer (.4), extrude (no trigger)
        position.update("g0 x2 y2 z.4 e1")
        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))
        # move up one more layer and extrude (trigger)
        position.update("g0 x2 y2 z.6 e1")
        trigger.update(position)
        self.assertTrue(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

    def test_LayerTrigger_LayerChange_DefaultExtruderTriggers(self):
        position = Position(self.Settings, self.OctoprintPrinterProfile, False)
        trigger = LayerTrigger(self.Settings)
        trigger.ExtruderTriggers = ExtruderTriggers(False, True, True, False,
                                                    None, None, True, True,
                                                    None, False)
        trigger.RequireZHop = False  # no zhop required
        trigger.HeightIncrement = 0  # Trigger on any height change
        # create some gcode
        gcode = []
        # get the startup gcode
        gcode.extend(self.GetPrintStartGcode)
        # start layer 1
        gcode.append(('G1 Z0.250 F7200.000', False, ""))
        # start priming extruder
        gcode.append(
            ('G1 X50.0 E80.0  F1000.0', False, "ExtrudingStart"))  # forbidden
        gcode.append(('G1 X160.0 E20.0 F1000.0', True, "Extruding"))
        gcode.append(('G1 Z0.200 F7200.000', False, "Extruding"))
        gcode.append(('G1 X220.0 E13 F1000.0', False, "Extruding"))
        gcode.append(('G1 X240.0 E0 F1000.0', False, "Extruding"))
        # Object print is starting
        gcode.append(('G1 E-4.00000 F3000.00000', False,
                      "On Retracting, OnRetractingStart"))
        gcode.append(('G1 Z0.700 F7200.000', False, "FullyRetracted, Zhop"))
        gcode.append(
            ('G1 X117.061 Y98.921 F7200.000', False, "FullyRetracted, Zhop"))
        gcode.append(('G1 Z0.200 F7200.000', False, "FullyRetracted"))
        gcode.append(
            ('G1 E4.00000 F3000.00000', False, "DetractingStart, Detracted"))
        gcode.append(('M204 S1000', False, "Primed"))
        gcode.append(('G1 F1800', False, "Primed"))
        # start extruding
        gcode.append(('G1 X117.508 Y98.104 E0.02922', False,
                      "ExtrudingStart"))  # forbidden
        gcode.append(('G1 X117.947 Y97.636 E0.02011', False, "Extruding"))
        gcode.append(('G1 X118.472 Y97.267 E0.02011', False, "Extruding"))
        gcode.append(('G1 X119.061 Y97.013 E0.02011', False, "Extruding"))
        gcode.append(('G1 X119.690 Y96.884 E0.02011', False, "Extruding"))
        gcode.append(('G1 X130.004 Y96.869 E0.32341', False, "Extruding"))
        gcode.append(('G1 X131.079 Y97.061 E0.03423', False, "Extruding"))
        # Retraction
        gcode.append(('G1 E-2.40000 F3000.00000', False,
                      "RetractingStart, Retracting, PartiallyRetracted"))
        gcode.append(('G1 F5760', False, "Retracting, PartiallyRetracted"))
        gcode.append(('G1 X119.824 Y97.629 E-0.50464', False,
                      "Retracting, PartiallyRetracted"))
        gcode.append(('G1 F5760', False, "Retracting, PartiallyRetracted"))
        gcode.append(('G1 X121.876 Y97.628 E-1.01536', False,
                      "Retracting, PartiallyRetracted"))
        gcode.append(
            ('G1 E-0.08000 F3000.00000', False, "Retracting, Fully Retracted"))
        # Retracted, Zhop
        gcode.append(('G1 Z0.700 F7200.000', False, "FullyRetracted, Zhop"))
        # Moved while lifted
        gcode.append(
            ('G1 X120.587 Y100.587 F7200.000', False, "FullyRetracted, Zhop"))
        gcode.append(('G1 Z0.200 F7200.000', False, "FullyRetracted"))
        # Zhop complete
        gcode.append(
            ('G1 E4.00000 F3000.00000', False, "DetractingStart, Detracted"))
        # Retraction Complete
        gcode.append(('G1 F1800', False, "Primed"))  # primed
        gcode.append(
            ('G1 X129.413 Y100.587 E0.27673', False, "ExtrudingStart"))
        gcode.append(('G1 X129.413 Y109.413 E0.27673', False, "Extruding"))
        gcode.append(('G1 X120.587 Y109.413 E0.27673', False, "Extruding"))
        gcode.append(('G1 X120.587 Y100.647 E0.27485', False, "Extruding"))
        gcode.append(('G1 X120.210 Y100.210 F7200.000', False, "Extruding"))

        # layer 2
        # after layer change
        # retract
        gcode.append(('G1 E-4.00000 F3000.00000', False, "RetractingStart"))
        # zhop
        gcode.append(('G1 Z0.900 F7200.000', False, "FullyRetracted, Zhop"))
        # move while lifted
        gcode.append(
            ('G1 X133.089 Y99.490 F7200.000', False, "FullyRetracted, Zhop"))
        # end zhop
        gcode.append(('G1 Z0.400 F7200.000', False, "FullyRetracted"))
        # detract
        gcode.append(('G1 E4.00000 F3000.00000', False, "DetractingStart"))
        gcode.append(('G1 F3000', False, "Detracted, Primed"))
        # start etruding
        gcode.append(
            ('G1 X133.128 Y110.149 E0.33418', False, "ExtrudingStart"))
        gcode.append(('G1 X132.942 Y111.071 E0.02950', True, "Extruding"))
        gcode.append(('G1 X132.492 Y111.896 E0.02950', False, "Extruding"))
        gcode.append(('G1 X132.020 Y112.393 E0.02148', False, "Extruding"))
        gcode.append(('G1 X131.447 Y112.777 E0.02161', False, "Extruding"))

        # layer 3
        gcode.append(('G1 Z2.600 F7200.000', False, "Primed"))
        gcode.append(('G1 X120.632 Y100.632 F7200.000', False, "Primed"))
        gcode.append(('M204 S800', False, "Primed"))
        gcode.append(('G1 F1200', False, "Primed"))
        gcode.append(
            ('G1 X129.368 Y100.632 E0.29570', False, "ExtrudingStart"))
        gcode.append(('G1 X129.368 Y109.368 E0.29570', True, "Extruding"))
        gcode.append(('G1 X120.632 Y109.368 E0.29570', False, "Extruding"))
        gcode.append(('G1 X120.632 Y100.692 E0.29367', False, "Extruding"))
        gcode.append(('M204 S1000', False, "Primed"))
        gcode.append(('G1 X120.225 Y100.225 F7200.000', False, "Extruding"))
        gcode.append(('M204 S800', False, "Primed"))
        gcode.append(('G1 F1200', False, "Extruding"))
        gcode.append(('G1 X129.775 Y100.225 E0.32326', False, "Extruding"))

        # layer 4
        gcode.append(('G1 Z2.800 F7200.000', False, "Primed"))
        gcode.append(('G1 X120.632 Y109.368 F7200.000', False, "Primed"))
        gcode.append(('M204 S800', False, "Primed"))
        gcode.append(('G1 F1200', False, "Primed"))
        gcode.append(
            ('G1 X120.632 Y100.632 E0.29570', False, "ExtrudingStart"))
        gcode.append(('G1 X129.368 Y100.632 E0.29570', True, "Extruding"))
        gcode.append(('G1 X129.368 Y109.368 E0.29570', False, "Extruding"))
        gcode.append(('G1 X120.692 Y109.368 E0.29367', False, "Extruding"))
        gcode.append(('M204 S1000', False, "Primed"))
        gcode.append(('G1 X120.225 Y109.775 F7200.000', False, ""))
        gcode.append(('M204 S800', False, "Primed"))
        gcode.append(('G1 F1200', False, "Primed"))
        gcode.append(
            ('G1 X120.225 Y100.225 E0.32326', False, "ExtrudingStart"))
        gcode.append(('G1 X129.775 Y100.225 E0.32326', False, "Extruding"))
        gcode.append(('G1 X129.775 Y109.775 E0.32326', False, "Extruding"))
        gcode.append(('G1 X120.285 Y109.775 E0.32123', False, "Extruding"))

        # loop through all of the Gcode and test triggering
        for command in gcode:
            gcode_command = command[0]
            should_trigger = command[1]
            comment = command[2]
            position.update(gcode_command)
            trigger.update(position)
            self.assertTrue(
                trigger.is_triggered(0) == should_trigger,
                "Should have triggered on {0} command.  Command comment:".
                format(gcode_command, comment))

    @property
    def GetPrintStartGcode(self):
        # create gcode list
        gcode = [('T0', False, "select tool 0"),
                 ('M104 S255', False, "set extruder temp"),
                 ('M140 S100', False, "set bed temp"),
                 ('M190 S100', False, "wait for bed temp"),
                 ('M109 S255', False, "wait for extruder temp"),
                 ('G21', False, "set units to millimeters"),
                 ('G90', False, "use absolute coordinates"),
                 ('M83', False, "use relative distances for extrusion"),
                 ('G28 W', False, ""), ('G80', False, ""),
                 ('G92 E0.0', False, ""), ('M203 E100', False, ""),
                 ('M92 E140', False, ""), ('G92 E0.0', False, ""),
                 ('M900 K200', False, "")]
        # Print Start Code

        return gcode

    def test_LayerTrigger_HeightChange(self):
        """Test the layer trigger height change """

        position = Position(self.Settings, self.OctoprintPrinterProfile, False)
        trigger = LayerTrigger(self.Settings)
        trigger.ExtruderTriggers = ExtruderTriggers(None, None, None, None,
                                                    None, None, None, None,
                                                    None,
                                                    None)  # Ignore extruder
        trigger.RequireZHop = False  # no zhop required
        trigger.HeightIncrement = .25  # Trigger every .25

        # test initial state
        self.assertFalse(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # send commands that normally would trigger a layer change, but without all axis homed.
        position.update("g0 x0 y0 z.2 e1")
        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # cur increment 0.25
        position.update("g28")
        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # extrude at height 0.2, should trigger
        position.update("g0 x0 y0 z.2 e1")
        trigger.update(position)
        self.assertTrue(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # cur increment 0.25
        # move to higher layer, but do not extrude (no layer change)
        position.update("g0 x1 y1 z.4")
        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))
        position.update("g0 x2 y2 z.4")
        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # cur increment 0.25
        # return to previous layer, do not extrude
        position.update("g0 x2 y2 z.2")
        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))
        position.update("g0 x4 y4 z.2")
        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # cur increment 0.25
        # extrude again on current layer
        position.update("g0 x2 y2 z.2 e1")
        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # cur increment 0.25
        # move up two times, down and extrude (this should trigger after the final command
        position.update("g0 x2 y2 z.4")
        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))
        position.update("g0 x2 y2 z.6")
        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))
        position.update("g0 x2 y2 z.4 e1")
        trigger.update(position)
        self.assertTrue(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # cur increment 0.5
        # This should never happen in a print, but test extruding on previous layers
        # move down to previous layer, extrude,
        position.update("g0 x2 y2 z.2 e1")
        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))
        # move back to current layer (.4), extrude (no trigger)
        position.update("g0 x2 y2 z.4 e1")
        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))
        # move up one more layer and extrude (trigger)
        position.update("g0 x2 y2 z.6 e1")
        trigger.update(position)
        self.assertTrue(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # test very close to height increment (.74)
        # move up one more layer and extrude (trigger)
        position.update("g0 x2 y2 z0.74  e1")
        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))
        # now it should trigger
        position.update("m114")
        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # Test at the increment (.75)
        position.update("g0 x2 y2 z0.7500 e1")
        trigger.update(position)
        self.assertTrue(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

    def test_LayerTrigger_ExtruderTriggers_NotHomed(self):
        """Make sure nothing triggers when the axis aren't homed"""
        position = Position(self.Settings, self.OctoprintPrinterProfile, False)
        trigger = LayerTrigger(self.Settings)
        trigger.RequireZHop = False  # no zhop required
        trigger.HeightIncrement = 0  # Trigger on every layer change
        position.Extruder.PrinterRetractionLength = 4

        # Try on extruding start
        trigger.ExtruderTriggers = ExtruderTriggers(True, None, None, None,
                                                    None, None, None, None,
                                                    None, None)
        position.update("g0 x0 y0 z.2 e1")
        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # try out on extruding
        trigger.ExtruderTriggers = ExtruderTriggers(None, True, None, None,
                                                    None, None, None, None,
                                                    None, None)
        position.update("g0 x0 y0 z.3 e1")
        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # try out on primed
        trigger.ExtruderTriggers = ExtruderTriggers(None, None, True, None,
                                                    None, None, None, None,
                                                    None, None)
        position.update("g0 x0 y0 z.4 e1")
        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # try out on retracting start
        trigger.ExtruderTriggers = ExtruderTriggers(None, None, None, True,
                                                    None, None, None, None,
                                                    None, None)
        position.update("g0 x0 y0 z.5 e-1")
        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # try out on retracting
        trigger.ExtruderTriggers = ExtruderTriggers(None, None, None, None,
                                                    True, None, None, None,
                                                    None, None)
        position.update("g0 x0 y0 z.5 e-1")
        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # try out on partially retracted
        trigger.ExtruderTriggers = ExtruderTriggers(None, None, None, None,
                                                    None, True, None, None,
                                                    None, None)
        position.update("g0 x0 y0 z.5 e-1")
        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # try out on retracted
        trigger.ExtruderTriggers = ExtruderTriggers(None, None, None, None,
                                                    None, None, True, None,
                                                    None, None)
        position.update("g0 x0 y0 z.5 e-1")
        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # try out on detracting
        trigger.ExtruderTriggers = ExtruderTriggers(None, None, None, None,
                                                    None, None, True, None,
                                                    None, None)
        position.update("g0 x0 y0 z.5 e1")
        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

    def test_LayerTrigger_ExtruderTriggers(self):
        """Test All Extruder Triggers"""
        position = Position(self.Settings, self.OctoprintPrinterProfile, False)
        # home the axis
        position.update("G28")
        trigger = LayerTrigger(self.Settings)
        trigger.RequireZHop = False  # no zhop required
        trigger.HeightIncrement = 0  # Trigger on every layer change

        # get the current extruder state
        state = position.Extruder.get_state(0)
        # Try on extruding start right after home, should fail since we haven't extruded yet
        trigger.ExtruderTriggers = ExtruderTriggers(True, None, None, None,
                                                    None, None, None, None,
                                                    None, None)
        state.IsExtrudingStart = True
        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # Try again, should trigger after the extrusion
        position.update("G1 E1")
        trigger.update(position)
        self.assertTrue(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # Reset the previous extruder state
        state = ExtruderState()
        position.Extruder.StateHistory[0] = state
        state.IsPrimed = False

        # try out on extruding
        state.IsExtruding = True
        trigger.ExtruderTriggers = ExtruderTriggers(None, True, None, None,
                                                    None, None, None, None,
                                                    None, None)

        trigger.update(position)
        self.assertTrue(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # Reset the previous extruder state
        state = ExtruderState()
        position.Extruder.StateHistory[0] = state
        state.IsPrimed = False

        # try out on primed
        trigger.ExtruderTriggers = ExtruderTriggers(None, None, True, None,
                                                    None, None, None, None,
                                                    None, None)
        state.IsPrimed = True
        trigger.update(position)
        self.assertTrue(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # Reset the previous extruder state
        state = ExtruderState()
        position.Extruder.StateHistory[0] = state
        state.IsPrimed = False

        # try out on retracting start
        trigger.ExtruderTriggers = ExtruderTriggers(None, None, None, True,
                                                    None, None, None, None,
                                                    None, None)
        state.IsRetractingStart = True
        trigger.update(position)
        self.assertTrue(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # Reset the previous extruder state
        state = ExtruderState()
        position.Extruder.StateHistory[0] = state
        state.IsPrimed = False

        # try out on retracting
        trigger.ExtruderTriggers = ExtruderTriggers(None, None, None, None,
                                                    True, None, None, None,
                                                    None, None)
        state.IsRetracting = True
        trigger.update(position)
        self.assertTrue(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # Reset the previous extruder state
        state = ExtruderState()
        position.Extruder.StateHistory[0] = state
        state.IsPrimed = False
        # try out on partially retracted
        trigger.ExtruderTriggers = ExtruderTriggers(None, None, None, None,
                                                    None, True, None, None,
                                                    None, None)
        state.IsPartiallyRetracted = True
        trigger.update(position)
        self.assertTrue(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # Reset the previous extruder state
        state = ExtruderState()
        position.Extruder.StateHistory[0] = state
        state.IsPrimed = False
        # try out on retracted
        trigger.ExtruderTriggers = ExtruderTriggers(None, None, None, None,
                                                    None, None, True, None,
                                                    None, None)
        state.IsRetracted = True
        trigger.update(position)
        self.assertTrue(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # Reset the previous extruder state
        state = ExtruderState()
        position.Extruder.StateHistory[0] = state
        state.IsPrimed = False
        # try out on detracting Start
        trigger.ExtruderTriggers = ExtruderTriggers(None, None, None, None,
                                                    None, None, None, True,
                                                    None, None)
        state.IsDetractingStart = True
        trigger.update(position)
        self.assertTrue(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # Reset the previous extruder state
        state = ExtruderState()
        position.Extruder.StateHistory[0] = state
        state.IsPrimed = False
        # try out on detracting Start
        trigger.ExtruderTriggers = ExtruderTriggers(None, None, None, None,
                                                    None, None, None, None,
                                                    True, None)
        state.IsDetracting = True
        trigger.update(position)
        self.assertTrue(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # Reset the previous extruder state
        state = ExtruderState()
        position.Extruder.StateHistory[0] = state
        state.IsPrimed = False
        trigger.ExtruderTriggers = ExtruderTriggers(None, None, None, None,
                                                    None, None, None, None,
                                                    None, True)
        state.IsDetracted = True
        trigger.update(position)
        self.assertTrue(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

    def test_LayerTrigger_ExtruderTriggerWait(self):
        """Test wait on extruder"""
        position = Position(self.Settings, self.OctoprintPrinterProfile, False)

        trigger = LayerTrigger(self.Settings)
        trigger.RequireZHop = False  # no zhop required
        trigger.HeightIncrement = 0  # Trigger on every layer change

        # home the axis
        position.update("G28")

        # add the current state
        pos = position.get_position(0)
        state = position.Extruder.get_state(0)
        state.IsPrimed = False
        # Use on extruding start for this test.
        trigger.ExtruderTriggers = ExtruderTriggers(True, None, None, None,
                                                    None, None, None, None,
                                                    None, None)
        state.IsExtrudingStart = False
        pos.IsLayerChange = True

        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertTrue(trigger.is_waiting(0))

        # update again with no change
        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertTrue(trigger.is_waiting(0))
        # set the trigger and try again
        state.IsExtrudingStart = True
        trigger.update(position)
        self.assertTrue(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

    def test_LayerTrigger_LayerChange_ZHop(self):
        """Test the layer trigger for layer changes triggers"""
        self.Settings.current_snapshot().layer_trigger_require_zhop = True
        self.Settings.current_printer().z_hop = .5
        position = Position(self.Settings, self.OctoprintPrinterProfile, False)
        trigger = LayerTrigger(self.Settings)
        trigger.ExtruderTriggers = ExtruderTriggers(None, None, None, None,
                                                    None, None, None, None,
                                                    None,
                                                    None)  # Ignore extruder
        trigger.HeightIncrement = 0  # Trigger on any height change
        # test initial state
        self.assertFalse(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # send commands that normally would trigger a layer change, but without all axis homed.
        position.update("g0 x0 y0 z.2 e1")
        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # Home all axis and try again, will not trigger or wait, previous axis not homed
        position.update("g28")
        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # Waiting on ZHop
        position.update("g0 x0 y0 z.2 e1")
        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertTrue(trigger.is_waiting(0))
        # try zhop
        position.update("g0 x0 y0 z.7 ")
        trigger.update(position)
        self.assertTrue(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # extrude on current layer, no trigger (wait on zhop)
        position.update("g0 x0 y0 z.7 e1")
        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertTrue(trigger.is_waiting(0))

        # do not extrude on current layer, still waiting
        position.update("g0 x0 y0 z.7 ")
        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertTrue(trigger.is_waiting(0))

        # partial hop, but close enough based on our printer measurement tolerance (0.005)
        position.update("g0 x0 y0 z1.1999")
        trigger.update(position)
        self.assertTrue(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # creat wait state
        position.update("g0 x0 y0 z1.3 e1")
        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertTrue(trigger.is_waiting(0))

        # move down (should never happen, should behave properly anyway)
        position.update("g0 x0 y0 z.8")
        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertTrue(trigger.is_waiting(0))

        # move back up to current layer (should NOT trigger zhop)
        position.update("g0 x0 y0 z1.3")
        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertTrue(trigger.is_waiting(0))

        # move up a bit, not enough to trigger zhop
        position.update("g0 x0 y0 z1.795")
        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertTrue(trigger.is_waiting(0))

        # move up a bit, just enough to trigger zhop
        position.update("g0 x0 y0 z1.7951")
        trigger.update(position)
        self.assertTrue(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))
Esempio n. 5
0
class TestTimerTrigger(unittest.TestCase):
    def setUp(self):
        self.Settings = OctolapseSettings(NamedTemporaryFile().name)
        self.Settings.current_printer().e_axis_default_mode = 'relative'
        self.Settings.current_printer().xyz_axes_default_mode = 'absolute'
        self.Settings.current_printer().auto_detect_position = False
        self.Settings.current_printer().origin_x = 0
        self.Settings.current_printer().origin_y = 0
        self.Settings.current_printer().origin_z = 0
        self.OctoprintPrinterProfile = self.create_octoprint_printer_profile()

    def tearDown(self):
        del self.Settings
        del self.OctoprintPrinterProfile

    @staticmethod
    def create_octoprint_printer_profile():
        return dict(
            volume=dict(
                width=250,
                depth=200,
                height=200,
                formFactor="Not A Circle",
                custom_box=False,
            )
        )

    def test_TimerTrigger(self):
        """Test the timer trigger"""
        # use a short trigger time so that the test doesn't take too long
        self.Settings.current_snapshot().timer_trigger_seconds = 2
        position = Position(self.Settings, self.OctoprintPrinterProfile, False)
        trigger = TimerTrigger(self.Settings)
        trigger.ExtruderTriggers = ExtruderTriggers(None, None, None, None, None, None, None, None, None,
                                                    None)  # Ignore extruder
        trigger.RequireZHop = False  # no zhop required
        trigger.HeightIncrement = 0  # Trigger on any height change
        # test initial state
        self.assertFalse(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # set interval time to 0, send another command and test again (should not trigger, no homed axis)
        trigger.IntervalSeconds = 0
        position.update("g0 x0 y0 z.2 e1")
        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # Home all axis and try again with interval seconds 1 - should not trigger since the timer will start after
        # the home command
        trigger.IntervalSeconds = 2
        position.update("g28")
        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # send another command and try again, should not trigger cause we haven't waited 2 seconds yet
        position.update("g0 x0 y0 z.2 e1")
        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # Set the last trigger time to 1 before the previous LastTrigger time(equal to interval seconds), should not
        # trigger
        trigger.get_state(0).TriggerStartTime = time.time() - 1.01
        position.update("g0 x0 y0 z.2 e1")
        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # Set the last trigger time to 1 before the previous LastTrigger time(equal to interval seconds), should trigger
        trigger.TriggerStartTime = time.time() - 2.01
        position.update("g0 x0 y0 z.2 e1")
        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

    def test_TimerTrigger_ExtruderTriggers(self):
        """Test All Extruder Triggers"""
        position = Position(self.Settings, self.OctoprintPrinterProfile, False)
        # home the axis
        position.update("G28")
        trigger = TimerTrigger(self.Settings)
        trigger.IntervalSeconds = 1
        trigger.RequireZHop = False  # no zhop required

        # Reset the extruder
        state = ExtruderState()
        position.Extruder.StateHistory[0] = state
        # Try on extruding start - previous position not homed, do not trigger
        trigger.ExtruderTriggers = ExtruderTriggers(
            True, None, None, None, None, None, None, None, None, None)
        trigger.get_state(0).TriggerStartTime = time.time() - 1.01
        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertTrue(trigger.is_waiting(0))

        # Reset the extruder
        state = ExtruderState()
        position.Extruder.StateHistory[0] = state
        # send another command, now the previous state has been homed, should trigger
        position.update("AnotherCommandNowPreviousHomed")
        # set is extruding start, wont be set by the above command!
        position.Extruder.StateHistory[0].IsExtrudingStart = True
        trigger.update(position)
        self.assertTrue(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # Reset the extruder
        state = ExtruderState()
        position.Extruder.StateHistory[0] = state
        # try out on extruding
        trigger.ExtruderTriggers = ExtruderTriggers(
            None, True, None, None, None, None, None, None, None, None)
        state.IsExtruding = True
        trigger.get_state(0).TriggerStartTime = time.time() - 1.01
        trigger.update(position)
        self.assertTrue(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # Reset the extruder
        state = ExtruderState()
        position.Extruder.StateHistory[0] = state
        # try out on primed
        trigger.ExtruderTriggers = ExtruderTriggers(
            None, None, True, None, None, None, None, None, None, None)
        state.IsPrimed = True
        trigger.get_state(0).TriggerStartTime = time.time() - 1.01
        trigger.update(position)
        self.assertTrue(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # Reset the extruder
        state = ExtruderState()
        position.Extruder.StateHistory[0] = state
        # try out on retracting start
        trigger.ExtruderTriggers = ExtruderTriggers(
            None, None, None, True, None, None, None, None, None, None)
        state.IsRetractingStart = True
        trigger.get_state(0).TriggerStartTime = time.time() - 1.01
        trigger.update(position)
        self.assertTrue(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # Reset the extruder
        state = ExtruderState()
        position.Extruder.StateHistory[0] = state
        # try out on retracting
        trigger.ExtruderTriggers = ExtruderTriggers(
            None, None, None, None, True, None, None, None, None, None)
        state.IsRetracting = True
        trigger.get_state(0).TriggerStartTime = time.time() - 1.01
        trigger.update(position)
        self.assertTrue(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # Reset the extruder
        state = ExtruderState()
        position.Extruder.StateHistory[0] = state
        # try out on partially retracted
        trigger.ExtruderTriggers = ExtruderTriggers(
            None, None, None, None, None, True, None, None, None, None)
        state.IsPartiallyRetracted = True
        trigger.get_state(0).TriggerStartTime = time.time() - 1.01
        trigger.update(position)
        self.assertTrue(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # Reset the extruder
        state = ExtruderState()
        position.Extruder.StateHistory[0] = state
        # try out on retracted
        trigger.ExtruderTriggers = ExtruderTriggers(
            None, None, None, None, None, None, True, None, None, None)
        state.IsRetracted = True
        trigger.get_state(0).TriggerStartTime = time.time() - 1.01
        trigger.update(position)
        self.assertTrue(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # Reset the extruder
        state = ExtruderState()
        position.Extruder.StateHistory[0] = state
        # try out on detracting Start
        trigger.ExtruderTriggers = ExtruderTriggers(
            None, None, None, None, None, None, None, True, None, None)
        state.IsDetractingStart = True
        trigger.get_state(0).TriggerStartTime = time.time() - 1.01
        trigger.update(position)
        self.assertTrue(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # Reset the extruder
        state = ExtruderState()
        position.Extruder.StateHistory[0] = state
        # try out on detracting Start
        trigger.ExtruderTriggers = ExtruderTriggers(
            None, None, None, None, None, None, None, None, True, None)
        state.IsDetracting = True
        trigger.get_state(0).TriggerStartTime = time.time() - 1.01
        trigger.update(position)
        self.assertTrue(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # Reset the extruder
        state = ExtruderState()
        position.Extruder.StateHistory[0] = state
        # try out on detracting Start
        trigger.ExtruderTriggers = ExtruderTriggers(
            None, None, None, None, None, None, None, None, None, True)
        state.IsDetracted = True
        trigger.get_state(0).TriggerStartTime = time.time() - 1.01
        trigger.update(position)
        self.assertTrue(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

    def test_TimerTrigger_ExtruderTriggerWait(self):
        """Test wait on extruder"""
        position = Position(self.Settings, self.OctoprintPrinterProfile, False)
        # home the axis
        position.update("G28")
        trigger = TimerTrigger(self.Settings)
        trigger.RequireZHop = False  # no zhop required
        trigger.IntervalSeconds = 1

        # Use on extruding start for this test.
        trigger.ExtruderTriggers = ExtruderTriggers(
            True, None, None, None, None, None, None, None, None, None)

        # set the extruder trigger
        position.Extruder.get_state(0).IsExtrudingStart = True
        # will not wait or trigger because not enough time has elapsed
        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # add 1 second to the state and try again
        trigger.get_state(0).TriggerStartTime = time.time() - 1.01

        # send another command and try again
        position.update("PreviousPositionIsNowHomed")
        # set the extruder trigger
        position.Extruder.get_state(0).IsExtrudingStart = True
        trigger.update(position)
        self.assertTrue(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

    def test_TimerTrigger_LayerChange_ZHop(self):
        """Test the layer trigger for layer changes triggers"""
        self.Settings.current_snapshot().timer_trigger_require_zhop = True
        self.Settings.current_printer().z_hop = .5
        position = Position(self.Settings, self.OctoprintPrinterProfile, False)
        trigger = TimerTrigger(self.Settings)
        trigger.ExtruderTriggers = ExtruderTriggers(None, None, None, None, None, None, None, None, None,
                                                    None)  # Ignore extruder
        trigger.IntervalSeconds = 1
        # test initial state
        self.assertFalse(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # send commands that normally would trigger a layer change, but without all axis homed.
        position.update("g0 x0 y0 z.2 e1")
        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # Home all axis and try again, wait on zhop
        position.update("g28")
        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))
        position.update("g0 x0 y0 z.2 e1")
        trigger.get_state(0).TriggerStartTime = time.time() - 1.01
        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertTrue(trigger.is_waiting(0))

        # try zhop
        position.update("g0 x0 y0 z.7 ")
        trigger.update(position)
        self.assertTrue(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # extrude on current layer, no trigger (wait on zhop)
        position.update("g0 x0 y0 z.7 e1")
        trigger.get_state(0).TriggerStartTime = time.time() - 1.01
        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertTrue(trigger.is_waiting(0))

        # do not extrude on current layer, still waiting
        position.update("g0 x0 y0 z.7 ")
        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertTrue(trigger.is_waiting(0))

        # partial hop, but close enough based on our printer measurement tolerance (0.005)
        position.update("g0 x0 y0 z1.1999")
        trigger.update(position)
        self.assertTrue(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))

        # creat wait state
        position.update("g0 x0 y0 z1.3 e1")
        trigger.get_state(0).TriggerStartTime = time.time() - 1.01
        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertTrue(trigger.is_waiting(0))

        # move down (should never happen, should behave properly anyway)
        position.update("g0 x0 y0 z.8")
        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertTrue(trigger.is_waiting(0))

        # move back up to current layer (should NOT trigger zhop)
        position.update("g0 x0 y0 z1.3")
        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertTrue(trigger.is_waiting(0))

        # move up a bit, not enough to trigger zhop
        position.update("g0 x0 y0 z1.795")
        trigger.update(position)
        self.assertFalse(trigger.is_triggered(0))
        self.assertTrue(trigger.is_waiting(0))

        # move up a bit, just enough to trigger zhop
        position.update("g0 x0 y0 z1.7951")
        trigger.update(position)
        self.assertTrue(trigger.is_triggered(0))
        self.assertFalse(trigger.is_waiting(0))