def test_configure_motion_controller_trigger(self):
        xs = LineGenerator("x", "mm", 0.0, 0.3, 4, alternate=True)
        ys = LineGenerator("y", "mm", 0.0, 0.1, 2)
        generator = CompoundGenerator([ys, xs], [], [], 1.0)
        self.set_motor_attributes()
        self.set_attributes(self.child, rowTrigger="Motion Controller")
        self.set_attributes(self.child_seq1, bita="TTLIN1.VAL")
        self.set_attributes(self.child_seq2, bita="TTLIN1.VAL")
        axes_to_move = ["x", "y"]

        seq_rows = self.get_sequencer_rows(generator, axes_to_move)
        # Triggers
        B0 = Trigger.BITA_0
        B1 = Trigger.BITA_1
        IT = Trigger.IMMEDIATE
        # Half a frame
        hf = 62500000
        expected = SequencerRows()
        expected.add_seq_entry(count=1,
                               trigger=B1,
                               position=0,
                               half_duration=hf,
                               live=1,
                               dead=0)
        expected.add_seq_entry(3, IT, 0, hf, 1, 0)
        expected.add_seq_entry(1, B0, 0, MIN_PULSE, 0, 1)
        expected.add_seq_entry(1, B1, 0, hf, 1, 0)
        expected.add_seq_entry(3, IT, 0, hf, 1, 0)
        expected.add_seq_entry(1, IT, 0, MIN_PULSE, 0, 1)
        expected.add_seq_entry(0, IT, 0, MIN_PULSE, 0, 0)

        assert seq_rows.as_tuples() == expected.as_tuples()
    def test_configure_continuous(self):
        xs = LineGenerator("x", "mm", 0.0, 0.3, 4, alternate=True)
        ys = LineGenerator("y", "mm", 0.0, 0.1, 2)
        generator = CompoundGenerator([ys, xs], [], [], 1.0)
        self.set_motor_attributes()
        axes_to_move = ["x", "y"]

        seq_rows = self.get_sequencer_rows(generator, axes_to_move)
        # Triggers
        GT = Trigger.POSA_GT
        IT = Trigger.IMMEDIATE
        LT = Trigger.POSA_LT
        # Half a frame
        hf = 62500000
        # Half how long to be blind for
        hb = 22500000
        expected = SequencerRows()
        expected.add_seq_entry(count=1,
                               trigger=LT,
                               position=50,
                               half_duration=hf,
                               live=1,
                               dead=0)
        expected.add_seq_entry(3, IT, 0, hf, 1, 0)
        expected.add_seq_entry(1, IT, 0, hb, 0, 1)
        expected.add_seq_entry(1, GT, -350, hf, 1, 0)
        expected.add_seq_entry(3, IT, 0, hf, 1, 0)
        expected.add_seq_entry(1, IT, 0, MIN_PULSE, 0, 1)
        expected.add_seq_entry(0, IT, 0, MIN_PULSE, 0, 0)

        assert seq_rows.as_tuples() == expected.as_tuples()
    def test_configure_stepped(self):
        xs = LineGenerator("x", "mm", 0.0, 0.3, 4)
        ys = LineGenerator("y", "mm", 0.0, 0.2, 3)
        generator = CompoundGenerator([ys, xs], [], [], 1.0, continuous=False)
        generator.prepare()
        self.set_motor_attributes()
        self.set_attributes(self.child, rowTrigger="Motion Controller")
        self.set_attributes(self.child_seq1, bita="TTLIN1.VAL")
        self.set_attributes(self.child_seq2, bita="TTLIN1.VAL")
        axes_to_move = ["x", "y"]

        seq_rows = self.get_sequencer_rows(generator, axes_to_move)
        # Triggers
        B0 = Trigger.BITA_0
        B1 = Trigger.BITA_1
        IT = Trigger.IMMEDIATE
        # Half a frame
        hf = 62500000
        expected = SequencerRows()
        for i in range(11):
            expected.add_seq_entry(1, B1, 0, hf, 1, 0)
            expected.add_seq_entry(1, B0, 0, MIN_PULSE, 0, 1)
        expected.add_seq_entry(1, B1, 0, hf, 1, 0)
        expected.add_seq_entry(1, IT, 0, MIN_PULSE, 0, 1)
        expected.add_seq_entry(0, IT, 0, MIN_PULSE, 0, 0)

        assert seq_rows.as_tuples() == expected.as_tuples()
    def test_configure_with_zero_points(self):
        xs = LineGenerator("x", "mm", 0.0, 0.3, 4, alternate=True)
        ys = LineGenerator("y", "mm", 0.0, 0.1, 2)
        generator = CompoundGenerator([ys, xs], [], [], 1.0)
        self.set_motor_attributes()
        axes_to_move = ["x", "y"]

        seq_rows = self.get_sequencer_rows(generator, axes_to_move, steps=0)
        # Triggers
        IT = Trigger.IMMEDIATE
        expected = SequencerRows()
        expected.add_seq_entry(1, IT, 0, MIN_PULSE, 0, 1)
        expected.add_seq_entry(0, IT, 0, MIN_PULSE, 0, 0)

        assert seq_rows.as_tuples() == expected.as_tuples()
    def test_acquire_scan(self):
        generator = CompoundGenerator([StaticPointGenerator(size=5)], [], [],
                                      1.0)
        generator.prepare()

        seq_rows = self.get_sequencer_rows(generator, [])
        # Triggers
        IT = Trigger.IMMEDIATE
        # Half a frame
        hf = 62500000
        expected = SequencerRows()
        expected.add_seq_entry(count=5,
                               trigger=IT,
                               position=0,
                               half_duration=hf,
                               live=1,
                               dead=0)
        expected.add_seq_entry(1, IT, 0, MIN_PULSE, 0, 1)
        expected.add_seq_entry(0, IT, 0, MIN_PULSE, 0, 0)

        assert seq_rows.as_tuples() == expected.as_tuples()
    def test_split_with_final_row_zero_repeat(self):
        seq_rows = SequencerRows()
        seq_rows.add_seq_entry(4, Trigger.POSB_LT, 400, 1000, 0, 1, 50)
        seq_rows.add_seq_entry(3, Trigger.BITA_0, 300, 2000, 1, 0, 100)
        seq_rows.add_seq_entry(0, Trigger.IMMEDIATE, 300, 2000, 0, 1, 100)

        expected = SequencerRows()  # End of scan - no switch delay
        expected.add_seq_entry(4, Trigger.POSB_LT, 400, 1000, 0, 1, 50)
        expected.add_seq_entry(3, Trigger.BITA_0, 300, 2000, 1, 0, 100)
        expected.add_seq_entry(0, Trigger.IMMEDIATE, 300, 2000, 0, 1, 100)

        remainder = seq_rows.split(3)

        assert seq_rows.as_tuples() == expected.as_tuples()
        assert remainder.as_tuples() == SequencerRows().as_tuples()
    def test_split_with_final_row_one_repeat(self):
        seq_rows = SequencerRows()
        seq_rows.add_seq_entry(4, Trigger.POSB_LT, 400, 1000, 0, 1, 50)
        seq_rows.add_seq_entry(3, Trigger.BITA_0, 300, 2000, 1, 0, 100)
        seq_rows.add_seq_entry(1, Trigger.POSB_LT, 300, 2000, 0, 1, 100)

        expected = SequencerRows()
        expected.add_seq_entry(4, Trigger.POSB_LT, 400, 1000, 0, 1, 50)
        expected.add_seq_entry(3, Trigger.BITA_0, 300, 2000, 1, 0, 100)
        expected.add_seq_entry(1, Trigger.POSB_LT, 300, 2000, 0, 1,
                               100 + SEQ_TABLE_SWITCH_DELAY)

        remainder = seq_rows.split(3)

        assert seq_rows.as_tuples() == expected.as_tuples()
        assert remainder.as_tuples() == SequencerRows().as_tuples()
    def test_extend(self):
        seq_rows = SequencerRows()
        seq_rows.add_seq_entry()
        seq_rows2 = SequencerRows()
        seq_rows2.add_seq_entry(4, Trigger.POSB_LT, 400, 1000, 0, 1, 50)
        seq_rows.extend(seq_rows2)

        total_ticks = (MIN_PULSE * 2) + 4 * (1000 + 950)
        assert isclose(seq_rows.duration, total_ticks * TICK)
        assert len(seq_rows) == 2

        table = seq_rows.get_table()
        assert table.repeats == [1, 4]
        assert table.trigger == [Trigger.IMMEDIATE, Trigger.POSB_LT]
        assert table.position == [0, 400]
        assert table.time1 == [MIN_PULSE, 1000]
        assert table.outa1 == [0, 0]  # Live
        assert table.outb1 == [0, 1]  # Dead
        assert table.outc1 == table.outd1 == table.oute1 == table.outf1 == [
            0, 0
        ]
        assert table.time2 == [MIN_PULSE, 950]
        assert (table.outa2 == table.outb2 == table.outc2 == table.outd2 ==
                table.oute2 == table.outf2 == [0, 0])
    def test_configure_with_one_point(self):
        xs = LineGenerator("x", "mm", 0.0, 0.3, 4, alternate=True)
        ys = LineGenerator("y", "mm", 0.0, 0.1, 2)
        generator = CompoundGenerator([ys, xs], [], [], 1.0)
        self.set_motor_attributes()
        axes_to_move = ["x", "y"]

        seq_rows = self.get_sequencer_rows(generator, axes_to_move, steps=1)
        # Triggers
        IT = Trigger.IMMEDIATE
        LT = Trigger.POSA_LT
        # Half a frame
        hf = 62500000
        expected = SequencerRows()
        expected.add_seq_entry(count=1,
                               trigger=LT,
                               position=50,
                               half_duration=hf,
                               live=1,
                               dead=0)
        expected.add_seq_entry(1, IT, 0, MIN_PULSE, 0, 1)
        expected.add_seq_entry(0, IT, 0, MIN_PULSE, 0, 0)

        assert seq_rows.as_tuples() == expected.as_tuples()
    def test_split_below_max_table_size(self):
        seq_rows = SequencerRows()
        seq_rows.add_seq_entry(4, Trigger.POSB_LT, 400, 1000, 0, 1, 50)
        seq_rows.add_seq_entry(3, Trigger.BITA_0, 300, 2000, 1, 0, 100)

        expected = SequencerRows()
        expected.add_seq_entry(4, Trigger.POSB_LT, 400, 1000, 0, 1, 50)
        expected.add_seq_entry(2, Trigger.BITA_0, 300, 2000, 1, 0, 100)
        expected.add_seq_entry(1, Trigger.BITA_0, 300, 2000, 1, 0,
                               100 + SEQ_TABLE_SWITCH_DELAY)

        remainder = seq_rows.split(100)

        assert seq_rows.as_tuples() == expected.as_tuples()
        assert remainder.as_tuples() == SequencerRows().as_tuples()
    def get_sequencer_rows(position=0):
        min_ticks = int(MIN_TABLE_DURATION / TICK)

        rows = SequencerRows()
        rows.add_seq_entry(count=2,
                           half_duration=min_ticks // 4 + 100,
                           position=position)

        expected = SequencerRows()
        expected.add_seq_entry(count=1,
                               half_duration=min_ticks // 4 + 100,
                               position=position)
        expected.add_seq_entry(
            count=1,
            half_duration=min_ticks // 4 + 100,
            position=position,
            trim=SEQ_TABLE_SWITCH_DELAY,
        )
        return rows, expected
    def test_tables_are_set_correctly_on_configure(self):
        min_ticks = int(MIN_TABLE_DURATION / TICK)

        rows1 = SequencerRows()  # Just over half min duration
        rows1.add_seq_entry(count=2, half_duration=min_ticks // 8 + 1000)

        rows2 = SequencerRows()  # Just over minimum duration
        rows2.add_seq_entry(count=1, half_duration=min_ticks // 8 + 1000)
        rows2.add_seq_entry(count=3, half_duration=min_ticks // 8 + 1000)

        extra = SequencerRows()  # Extra tables are ignored for configure()
        extra.add_seq_entry(count=2, half_duration=min_ticks // 8 + 1000)
        extra.add_seq_entry(count=2, half_duration=min_ticks // 8 + 1000)
        self.db.configure(self.rows_generator([rows1, rows1, rows2, extra]))

        # Check to ensure repeats is set correctly
        for table in self.db._table_map.values():
            assert table.repeats.value == 1

        self.seq_parts[1].table_set.assert_called_once()
        table1 = self.seq_parts[1].table_set.call_args[0][0]
        expected1 = SequencerRows()
        expected1.add_seq_entry(count=2, half_duration=min_ticks // 8 + 1000)
        expected1.add_seq_entry(count=1, half_duration=min_ticks // 8 + 1000)
        expected1.add_seq_entry(count=1,
                                half_duration=min_ticks // 8 + 1000,
                                trim=SEQ_TABLE_SWITCH_DELAY)
        self.assert_rows_equal_table(expected1, table1)

        self.seq_parts[2].table_set.assert_called_once()
        table2 = self.seq_parts[2].table_set.call_args[0][0]
        expected2 = SequencerRows()
        expected2.add_seq_entry(count=1, half_duration=min_ticks // 8 + 1000)
        expected2.add_seq_entry(count=2, half_duration=min_ticks // 8 + 1000)
        expected2.add_seq_entry(count=1,
                                half_duration=min_ticks // 8 + 1000,
                                trim=SEQ_TABLE_SWITCH_DELAY)
        self.assert_rows_equal_table(expected2, table2)
    def test_configure_with_delay_after(self):
        # a test to show that delay_after inserts a "loop_back" turnaround
        delay = 1.0
        x_steps, y_steps = 3, 2
        xs = LineGenerator("x", "mm", 0.0, 0.5, x_steps, alternate=True)
        ys = LineGenerator("y", "mm", 0.0, 0.1, y_steps)
        generator = CompoundGenerator([ys, xs], [], [], 1.0, delay_after=delay)
        self.set_motor_attributes()
        axes_to_move = ["x", "y"]

        seq_rows = self.get_sequencer_rows(generator, axes_to_move)
        # Triggers
        GT = Trigger.POSA_GT
        IT = Trigger.IMMEDIATE
        LT = Trigger.POSA_LT
        # Half a frame
        hf = 62500000
        # Half how long to be blind for a single point
        hfb = 55625000
        # Half how long to be blind for end of row
        hrb = 56500000
        expected = SequencerRows()
        expected.add_seq_entry(count=1,
                               trigger=LT,
                               position=125,
                               half_duration=hf,
                               live=1,
                               dead=0)
        expected.add_seq_entry(1, IT, 0, hfb, 0, 1)
        expected.add_seq_entry(1, LT, -125, hf, 1, 0)
        expected.add_seq_entry(1, IT, 0, hfb, 0, 1)
        expected.add_seq_entry(1, LT, -375, hf, 1, 0)
        expected.add_seq_entry(1, IT, 0, hrb, 0, 1)
        expected.add_seq_entry(1, GT, -625, hf, 1, 0)
        expected.add_seq_entry(1, IT, 0, hfb, 0, 1)
        expected.add_seq_entry(1, GT, -375, hf, 1, 0)
        expected.add_seq_entry(1, IT, 0, hfb, 0, 1)
        expected.add_seq_entry(1, GT, -125, hf, 1, 0)
        expected.add_seq_entry(1, IT, 0, MIN_PULSE, 0, 1)
        expected.add_seq_entry(0, IT, 0, MIN_PULSE, 0, 0)

        assert seq_rows.as_tuples() == expected.as_tuples()
    def test_configure_pcomp_row_trigger_with_single_point_rows(self):
        x_steps, y_steps = 1, 5
        xs = LineGenerator("x", "mm", 0.0, 0.5, x_steps, alternate=True)
        ys = LineGenerator("y", "mm", 0.0, 4, y_steps)
        generator = CompoundGenerator([ys, xs], [], [], 1.0)
        self.set_motor_attributes()
        axes_to_move = ["x", "y"]

        seq_rows = self.get_sequencer_rows(generator, axes_to_move)
        # Triggers
        GT = Trigger.POSA_GT
        LT = Trigger.POSA_LT
        IT = Trigger.IMMEDIATE
        # Half a frame
        hf = 62500000
        # Half blind
        hb = 75000000
        expected = SequencerRows()
        expected.add_seq_entry(count=1,
                               trigger=LT,
                               position=0,
                               half_duration=hf,
                               live=1,
                               dead=0)
        expected.add_seq_entry(1, IT, 0, hb, 0, 1)
        expected.add_seq_entry(1, GT, -500, hf, 1, 0)
        expected.add_seq_entry(1, IT, 0, hb, 0, 1)
        expected.add_seq_entry(1, LT, 0, hf, 1, 0)
        expected.add_seq_entry(1, IT, 0, hb, 0, 1)
        expected.add_seq_entry(1, GT, -500, hf, 1, 0)
        expected.add_seq_entry(1, IT, 0, hb, 0, 1)
        expected.add_seq_entry(1, LT, 0, hf, 1, 0)
        expected.add_seq_entry(1, IT, 0, MIN_PULSE, 0, 1)
        expected.add_seq_entry(0, IT, 0, MIN_PULSE, 0, 0)

        assert seq_rows.as_tuples() == expected.as_tuples()