def test_writeable_function_value(self): def nop(): return 0 def write(position): positions.append(position) positions = [] # Get 10 images. readables = [nop] expected_positions = [1, 2, 3, 4, 5] positioner = VectorPositioner(expected_positions) writables = write scan(positioner, readables=readables, writables=writables) self.assertEqual(positions, expected_positions, "Expected positions not equal.") def write2(position): positions2.append(position) positions2 = [] positions.clear() expected_positions = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]] writables = ["something1", write, "something2", write2] positioner = VectorPositioner(expected_positions) scan(positioner, readables=readables, writables=writables) self.assertEqual(positions, [x[1] for x in expected_positions], "Values not as expected.") self.assertEqual(positions2, [x[3] for x in expected_positions], "Values not as expected")
def test_VectorPositioner(self): expected_result = [[-2., -2], [-1., -1], [0., 0], [1., 1], [2., 2]] # # Test 1 pass. self.verify_result(VectorPositioner(expected_result), expected_result) # Test 3 passes. self.verify_result(VectorPositioner(expected_result, passes=3), expected_result * 3) expected_result = [[0.0, -1], [1.0, 0], [2.0, 1], [3.0, 2], [4.0, 3]] # Test offset. self.verify_result(VectorPositioner(expected_result, offsets=[2, 1]), expected_result)
def test_actions(self): positions = [[1, 1], [2, 2]] positioner = VectorPositioner(positions) writables = [ epics_pv("PYSCAN:TEST:MOTOR1:SET", "PYSCAN:TEST:MOTOR1:GET"), epics_pv("PYSCAN:TEST:MOTOR2:SET", "PYSCAN:TEST:MOTOR2:GET") ] readables = [epics_pv("PYSCAN:TEST:OBS1")] # MOTOR1 initial values should be -11, MOTOR2 -22. cached_initial_values["PYSCAN:TEST:MOTOR1:SET"] = -11 cached_initial_values["PYSCAN:TEST:MOTOR2:SET"] = -22 initialization = [action_set_epics_pv("PYSCAN:TEST:OBS1", -33)] finalization = [action_restore(writables)] result = scan(positioner=positioner, readables=readables, writables=writables, initialization=initialization, finalization=finalization, settings=scan_settings(measurement_interval=0.25, n_measurements=1)) self.assertEqual(pv_cache["PYSCAN:TEST:MOTOR1:SET"][0].value, -11, "Finalization did not restore original value.") self.assertEqual(pv_cache["PYSCAN:TEST:MOTOR2:SET"][0].value, -22, "Finalization did not restore original value.") self.assertEqual(result[0][0], -33, "Initialization action did not work.")
def test_progress_monitor(self): current_index = [] total_positions = 0 current_percentage = [] def progress(current_position, max_position): current_index.append(current_position) current_percentage.append(100 * (current_position / max_position)) nonlocal total_positions total_positions = max_position positions = [1, 2, 3, 4, 5] positioner = VectorPositioner(positions) writables = epics_pv("PYSCAN:TEST:MOTOR1:SET", "PYSCAN:TEST:MOTOR1:GET") readables = epics_pv("PYSCAN:TEST:OBS1") settings = scan_settings(progress_callback=progress) scan(positioner, readables, writables, settings=settings) self.assertEqual( len(positions) + 1, len(current_index), "The number of reported positions is wrong.") self.assertEqual(total_positions, 5, "The number of total positions is wrong.") self.assertEqual(current_index, [0, 1, 2, 3, 4, 5], "The reported percentage is wrong.") self.assertEqual(current_percentage, [0, 20, 40, 60, 80, 100], "The reported percentage is wrong.")
def test_after_measurement_executor(self): counter = -1 def void_read(): nonlocal counter counter += 1 return counter def void_write(position): positions.append(position) positions = [] def after_read_0(): self.assertTrue(len(positions) > 0) def after_read_1(position): self.assertEqual(position, positions[-1]) def after_read_2(position, position_data): self.assertEqual(position, positions[-1]) # Data value is equal to the position index. self.assertEqual(position_data[0], positions[-1]) positioner = VectorPositioner([0, 1, 2, 3, 4, 5]) after_read = [after_read_0, after_read_1, after_read_2] scan(readables=void_read, writables=void_write, positioner=positioner, after_read=after_read)
def test_mixed_sources(self): config.bs_connection_mode = "pull" config.bs_default_host = "localhost" config.bs_default_port = 9999 positions = [[1, 1], [2, 2], [3, 3], [4, 4]] positioner = VectorPositioner(positions) writables = [ epics_pv("PYSCAN:TEST:MOTOR1:SET", "PYSCAN:TEST:MOTOR1:GET"), epics_pv("PYSCAN:TEST:MOTOR2:SET", "PYSCAN:TEST:MOTOR2:GET") ] readables = [ bs_property("CAMERA1:X"), bs_property("CAMERA1:Y"), epics_pv("PYSCAN:TEST:OBS1") ] conditions = [ epics_condition("PYSCAN:TEST:VALID1", 10), bs_condition("CAMERA1:VALID", 10) ] initialization = [ action_set_epics_pv("PYSCAN:TEST:PRE1:SET", 1, "PYSCAN:TEST:PRE1:GET") ] finalization = [ action_set_epics_pv("PYSCAN:TEST:PRE1:SET", 0, "PYSCAN:TEST:PRE1:GET"), action_restore(writables) ] result = scan(positioner=positioner, readables=readables, writables=writables, conditions=conditions, initialization=initialization, finalization=finalization, settings=scan_settings(measurement_interval=0.25, n_measurements=1)) self.assertEqual(len(result), len(positions), "Not the expected number of results.") # The first 2 attributes are from bs_read, they should be equal to the pulse ID processed. self.assertTrue(all(x[0] == x[1] and x[2] == 1 for x in result), "The result is wrong.")
def get_positioner(self): """ Generate a positioner for the provided dimensions. :return: Positioner object. """ # Read all the initial positions - in case we need to do an additive scan. initial_positions = self.epics_dal.get_group(READ_GROUP).read() positioners = [] knob_readback_offset = 0 for dimension in self.dimensions: is_additive = bool(dimension.get("Additive", 0)) is_series = bool(dimension.get("Series", 0)) n_knob_readbacks = len(dimension["KnobReadback"]) # This dimension uses relative positions, read the PVs initial state. # We also need initial positions for the series scan. if is_additive or is_series: offsets = convert_to_list(initial_positions[ knob_readback_offset:knob_readback_offset + n_knob_readbacks]) else: offsets = None # Series scan in this dimension, use StepByStepVectorPositioner. if is_series: # In the StepByStep positioner, the initial values need to be added to the steps. positions = convert_to_list(dimension["ScanValues"]) positioners.append( SerialPositioner(positions, initial_positions=offsets, offsets=offsets if is_additive else None)) # Line scan in this dimension, use VectorPositioner. else: positions = convert_to_position_list( convert_to_list(dimension["KnobExpanded"])) positioners.append(VectorPositioner(positions, offsets=offsets)) # Increase the knob readback offset. knob_readback_offset += n_knob_readbacks # Assemble all individual positioners together. positioner = CompoundPositioner(positioners) return positioner
def test_convert_readables(self): config.bs_connection_mode = "pull" config.bs_default_host = "localhost" config.bs_default_port = 9999 positions = [[0, 10], [1, 11], [2, 12], [2, 13]] positioner = VectorPositioner(positions) readables = [ bs_property("CAMERA1:X"), "bs://CAMERA1:X", "BS://CAMERA1:X", epics_pv("PYSCAN:TEST:OBS1"), "PYSCAN:TEST:OBS1", "ca://PYSCAN:TEST:OBS1", "CA://PYSCAN:TEST:OBS1" ] writables = ["ca://PYSCAN:TEST:MOTOR1:SET", "PYSCAN:TEST:MOTOR2:SET"] def collect_positions(): actual_positions.append([ pv_cache["PYSCAN:TEST:MOTOR1:SET"][0].value, pv_cache["PYSCAN:TEST:MOTOR2:SET"][0].value ]) actual_positions = [] result = scan(positioner=positioner, readables=readables, writables=writables, after_read=collect_positions) for index in range(len(positions)): self.assertTrue( result[index][0] == result[index][1] == result[index][2], "Not all acquisitions are equal.") self.assertTrue( result[index][3] == result[index][4] == result[index][5], "Not all acquisitions are equal.") self.assertEqual(positions, actual_positions, "Does not work for writables.")
def test_before_after_move_executor(self): def void_read(): return 1 def void_write(position): positions.append(position) positions = [] def before_move(position): self.assertTrue(position not in positions, "Positions already visited.") def after_move(position): self.assertTrue(position in positions, "Position not yet visited.") positioner = VectorPositioner([1, 2, 3, 4, 5, 6]) scan(readables=void_read, writables=void_write, positioner=positioner, before_move=before_move, after_move=after_move)
def test_CompoundPositioner(self): expected_result = [[0], [1], [2], [3]] # The compound positioner should not modify the behaviour if only 1 positioner was supplied. self.verify_result( CompoundPositioner([SerialPositioner([0, 1, 2, 3], [-1])]), expected_result) expected_result = [[0.0, 0.0], [0.0, 3.0], [3.0, 3.0], [3.0, 0.0]] # Test with other positioners as well. self.verify_result( CompoundPositioner([ZigZagAreaPositioner([0, 0], [3, 3], [1, 1])]), expected_result) self.verify_result( CompoundPositioner([VectorPositioner(expected_result, passes=3)]), expected_result * 3) # Perform tests for PyScan. first_input = [[-3, -2, -1, 0], [-3, -2, -1, 0]] second_input = [[0, 1, 2], [0, 1, 2]] # Transform the list to be position by position, not axis by axis. first_input = convert_to_position_list(first_input) second_input = convert_to_position_list(second_input) # 2 ScanLine axes. expected_result = [[-3, -3, 0, 0], [-3, -3, 1, 1], [-3, -3, 2, 2], [-2, -2, 0, 0], [-2, -2, 1, 1], [-2, -2, 2, 2], [-1, -1, 0, 0], [-1, -1, 1, 1], [-1, -1, 2, 2], [-0, -0, 0, 0], [-0, -0, 1, 1], [-0, -0, 2, 2]] self.verify_result( CompoundPositioner([ VectorPositioner(first_input), VectorPositioner(second_input) ]), expected_result) # 2 ScanSeries axes. expected_result = [[-3, -12, 0, -22], [-3, -12, 1, -22], [-3, -12, 2, -22], [-3, -12, -21, 0], [-3, -12, -21, 1], [-3, -12, -21, 2], [-2, -12, 0, -22], [-2, -12, 1, -22], [-2, -12, 2, -22], [-2, -12, -21, 0], [-2, -12, -21, 1], [-2, -12, -21, 2], [-1, -12, 0, -22], [-1, -12, 1, -22], [-1, -12, 2, -22], [-1, -12, -21, 0], [-1, -12, -21, 1], [-1, -12, -21, 2], [-0, -12, 0, -22], [-0, -12, 1, -22], [-0, -12, 2, -22], [-0, -12, -21, 0], [-0, -12, -21, 1], [-0, -12, -21, 2], [-11, -3, 0, -22], [-11, -3, 1, -22], [-11, -3, 2, -22], [-11, -3, -21, 0], [-11, -3, -21, 1], [-11, -3, -21, 2], [-11, -2, 0, -22], [-11, -2, 1, -22], [-11, -2, 2, -22], [-11, -2, -21, 0], [-11, -2, -21, 1], [-11, -2, -21, 2], [-11, -1, 0, -22], [-11, -1, 1, -22], [-11, -1, 2, -22], [-11, -1, -21, 0], [-11, -1, -21, 1], [-11, -1, -21, 2], [-11, -0, 0, -22], [-11, -0, 1, -22], [-11, -0, 2, -22], [-11, -0, -21, 0], [-11, -0, -21, 1], [-11, -0, -21, 2]] # Initial positions to be used. first_input = [[-3, -2, -1, 0], [-3, -2, -1, 0]] second_input = [[0, 1, 2], [0, 1, 2]] first_initial = [-11, -12] second_initial = [-21, -22] self.verify_result( CompoundPositioner([ SerialPositioner(first_input, first_initial), SerialPositioner(second_input, second_initial) ]), expected_result) # First dimension LineScan, second dimension first change one, than another. expected_result = [[-3, -3, 0, -22], [-3, -3, 1, -22], [-3, -3, 2, -22], [-3, -3, -21, 0], [-3, -3, -21, 1], [-3, -3, -21, 2], [-2, -2, 0, -22], [-2, -2, 1, -22], [-2, -2, 2, -22], [-2, -2, -21, 0], [-2, -2, -21, 1], [-2, -2, -21, 2], [-1, -1, 0, -22], [-1, -1, 1, -22], [-1, -1, 2, -22], [-1, -1, -21, 0], [-1, -1, -21, 1], [-1, -1, -21, 2], [-0, -0, 0, -22], [-0, -0, 1, -22], [-0, -0, 2, -22], [-0, -0, -21, 0], [-0, -0, -21, 1], [-0, -0, -21, 2]] first_input = convert_to_position_list([[-3, -2, -1, 0], [-3, -2, -1, 0]]) self.verify_result( CompoundPositioner([ VectorPositioner(first_input), SerialPositioner(second_input, second_initial) ]), expected_result)