dls-dodal 1.69.0__py3-none-any.whl → 2.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {dls_dodal-1.69.0.dist-info → dls_dodal-2.0.0.dist-info}/METADATA +1 -1
- dls_dodal-2.0.0.dist-info/RECORD +354 -0
- {dls_dodal-1.69.0.dist-info → dls_dodal-2.0.0.dist-info}/WHEEL +1 -1
- dodal/_version.py +2 -2
- dodal/beamlines/__init__.py +10 -17
- dodal/beamlines/adsim.py +17 -17
- dodal/beamlines/b01_1.py +11 -0
- dodal/beamlines/b07.py +17 -21
- dodal/beamlines/b07_1.py +20 -22
- dodal/beamlines/b07_shared.py +12 -0
- dodal/beamlines/b16.py +1 -1
- dodal/beamlines/b21.py +15 -6
- dodal/beamlines/i02_1.py +3 -3
- dodal/beamlines/i02_2.py +1 -1
- dodal/beamlines/i03.py +4 -4
- dodal/beamlines/i04.py +16 -8
- dodal/beamlines/i05.py +7 -45
- dodal/beamlines/i05_1.py +4 -13
- dodal/beamlines/i05_shared.py +51 -0
- dodal/beamlines/i06_1.py +7 -5
- dodal/beamlines/{i06.py → i06_shared.py} +25 -14
- dodal/beamlines/i07.py +14 -16
- dodal/beamlines/i09.py +54 -51
- dodal/beamlines/i09_1.py +25 -64
- dodal/beamlines/i09_1_shared.py +61 -0
- dodal/beamlines/i09_2.py +6 -100
- dodal/beamlines/i09_2_shared.py +110 -0
- dodal/beamlines/i10.py +60 -54
- dodal/beamlines/i10_1.py +99 -10
- dodal/beamlines/{i10_optics.py → i10_shared.py} +80 -66
- dodal/beamlines/i11.py +31 -18
- dodal/beamlines/i13_1.py +1 -1
- dodal/beamlines/i15.py +6 -6
- dodal/beamlines/i15_1.py +6 -6
- dodal/beamlines/i17.py +37 -28
- dodal/beamlines/i18.py +3 -4
- dodal/beamlines/i19_1.py +95 -34
- dodal/beamlines/i19_2.py +68 -52
- dodal/beamlines/i19_optics.py +26 -13
- dodal/beamlines/i20_1.py +6 -14
- dodal/beamlines/i21.py +35 -28
- dodal/beamlines/i22.py +19 -4
- dodal/beamlines/i23.py +1 -2
- dodal/beamlines/i24.py +11 -10
- dodal/beamlines/k07.py +99 -5
- dodal/beamlines/p38.py +3 -3
- dodal/beamlines/p60.py +28 -17
- dodal/beamlines/p99.py +16 -15
- dodal/beamlines/training_rig.py +20 -12
- dodal/cli.py +36 -2
- dodal/common/beamlines/beamline_parameters.py +2 -1
- dodal/common/beamlines/beamline_utils.py +11 -9
- dodal/common/beamlines/commissioning_mode.py +6 -3
- dodal/common/coordination.py +12 -14
- dodal/common/crystal_metadata.py +5 -8
- dodal/common/device_utils.py +4 -3
- dodal/common/maths.py +28 -40
- dodal/common/udc_directory_provider.py +13 -8
- dodal/common/visit.py +18 -21
- dodal/common/watcher_utils.py +13 -12
- dodal/device_manager.py +94 -54
- dodal/devices/aperturescatterguard.py +26 -27
- dodal/devices/areadetector/plugins/cam.py +1 -3
- dodal/devices/areadetector/plugins/mjpg.py +6 -5
- dodal/devices/attenuator/attenuator.py +12 -11
- dodal/devices/beamlines/b07/__init__.py +3 -0
- dodal/devices/{b07_1 → beamlines/b07_1}/__init__.py +2 -2
- dodal/devices/{b07_1 → beamlines/b07_1}/ccmc.py +5 -10
- dodal/devices/{b16 → beamlines/b16}/detector.py +2 -3
- dodal/devices/{i02_1 → beamlines/i02_1}/fast_grid_scan.py +2 -3
- dodal/devices/{i02_1 → beamlines/i02_1}/sample_motors.py +1 -1
- dodal/devices/{i03 → beamlines/i03}/beamsize.py +11 -7
- dodal/devices/{i03 → beamlines/i03}/dcm.py +1 -2
- dodal/devices/{i03 → beamlines/i03}/undulator_dcm.py +4 -5
- dodal/devices/beamlines/i04/beam_centre.py +151 -0
- dodal/devices/{i04 → beamlines/i04}/beamsize.py +11 -7
- dodal/devices/{i04 → beamlines/i04}/murko_results.py +5 -5
- dodal/devices/{i04 → beamlines/i04}/transfocator.py +10 -15
- dodal/devices/beamlines/i05/__init__.py +3 -0
- dodal/devices/beamlines/i06_shared/__init__.py +3 -0
- dodal/devices/beamlines/i06_shared/i06_enum.py +7 -0
- dodal/devices/{i07 → beamlines/i07}/dcm.py +2 -3
- dodal/devices/{i07 → beamlines/i07}/id.py +8 -9
- dodal/devices/beamlines/i09/__init__.py +3 -0
- dodal/devices/{i09_1_shared → beamlines/i09_1_shared}/hard_energy.py +5 -6
- dodal/devices/{i09_1_shared → beamlines/i09_1_shared}/hard_undulator_functions.py +19 -16
- dodal/devices/{i10 → beamlines/i10}/diagnostics.py +4 -3
- dodal/devices/{i10 → beamlines/i10}/i10_apple2.py +31 -45
- dodal/devices/{i10 → beamlines/i10}/rasor/rasor_current_amp.py +1 -24
- dodal/devices/{i10 → beamlines/i10}/rasor/rasor_motors.py +2 -2
- dodal/devices/{i10 → beamlines/i10}/slits.py +5 -3
- dodal/devices/beamlines/i10_1/__init__.py +9 -0
- dodal/devices/beamlines/i10_1/electromagnet/magnet.py +16 -0
- dodal/devices/beamlines/i10_1/electromagnet/stages.py +14 -0
- dodal/devices/beamlines/i10_1/scaler_cards.py +13 -0
- dodal/devices/{i11 → beamlines/i11}/cyberstar_blower.py +1 -1
- dodal/devices/{i11 → beamlines/i11}/diff_stages.py +4 -6
- dodal/devices/{i11 → beamlines/i11}/mythen.py +3 -4
- dodal/devices/{i11 → beamlines/i11}/nx100robot.py +6 -6
- dodal/devices/{i11 → beamlines/i11}/spinner.py +1 -1
- dodal/devices/{i13_1 → beamlines/i13_1}/merlin.py +1 -1
- dodal/devices/{i15 → beamlines/i15}/dcm.py +1 -2
- dodal/devices/{i15 → beamlines/i15}/focussing_mirror.py +5 -5
- dodal/devices/{i15 → beamlines/i15}/jack.py +2 -2
- dodal/devices/{i15 → beamlines/i15}/multilayer_mirror.py +1 -1
- dodal/devices/{i17 → beamlines/i17}/i17_apple2.py +10 -16
- dodal/devices/{i18 → beamlines/i18}/diode.py +1 -1
- dodal/devices/{i19 → beamlines/i19}/access_controlled/attenuator_motor_squad.py +12 -8
- dodal/devices/{i19 → beamlines/i19}/access_controlled/blueapi_device.py +16 -15
- dodal/devices/beamlines/i19/access_controlled/piezo_control.py +72 -0
- dodal/devices/{i19 → beamlines/i19}/access_controlled/shutter.py +11 -9
- dodal/devices/{i19 → beamlines/i19}/backlight.py +3 -1
- dodal/devices/{i19 → beamlines/i19}/mapt_configuration.py +2 -1
- dodal/devices/{i19 → beamlines/i19}/pin_col_stages.py +11 -8
- dodal/devices/beamlines/i19/pin_tip.py +32 -0
- dodal/devices/beamlines/i21/__init__.py +3 -0
- dodal/devices/{i22 → beamlines/i22}/dcm.py +1 -2
- dodal/devices/{i22 → beamlines/i22}/fswitch.py +1 -3
- dodal/devices/{i22 → beamlines/i22}/nxsas.py +5 -4
- dodal/devices/{i24 → beamlines/i24}/beam_center.py +1 -1
- dodal/devices/{i24 → beamlines/i24}/beamstop.py +2 -2
- dodal/devices/{i24 → beamlines/i24}/commissioning_jungfrau.py +3 -2
- dodal/devices/{i24 → beamlines/i24}/dcm.py +1 -3
- dodal/devices/{i24 → beamlines/i24}/dual_backlight.py +3 -3
- dodal/devices/{i24 → beamlines/i24}/pmac.py +9 -7
- dodal/devices/{p60 → beamlines/p60}/lab_xray_source.py +1 -1
- dodal/devices/beamlines/p99/__init__.py +0 -0
- dodal/devices/{p99 → beamlines/p99}/andor2_point.py +11 -15
- dodal/devices/bimorph_mirror.py +22 -20
- dodal/devices/collimation_table.py +3 -2
- dodal/devices/common_dcm.py +30 -20
- dodal/devices/controllers.py +2 -2
- dodal/devices/cryostream.py +8 -0
- dodal/devices/current_amplifiers/current_amplifier.py +16 -18
- dodal/devices/current_amplifiers/current_amplifier_detector.py +9 -10
- dodal/devices/current_amplifiers/femto.py +8 -9
- dodal/devices/current_amplifiers/sr570.py +16 -16
- dodal/devices/current_amplifiers/struck_scaler_counter.py +5 -5
- dodal/devices/detector/det_resolution.py +9 -8
- dodal/devices/detector/detector.py +4 -2
- dodal/devices/diamond_filter.py +3 -4
- dodal/devices/eiger.py +3 -3
- dodal/devices/eiger_odin.py +1 -1
- dodal/devices/electron_analyser/base/base_controller.py +13 -13
- dodal/devices/electron_analyser/base/base_detector.py +15 -20
- dodal/devices/electron_analyser/base/base_driver_io.py +39 -46
- dodal/devices/electron_analyser/base/base_region.py +27 -30
- dodal/devices/electron_analyser/base/base_util.py +18 -16
- dodal/devices/electron_analyser/base/energy_sources.py +13 -19
- dodal/devices/eurotherm.py +3 -2
- dodal/devices/fast_grid_scan.py +31 -34
- dodal/devices/fast_shutter.py +24 -21
- dodal/devices/flux.py +1 -1
- dodal/devices/focusing_mirror.py +29 -11
- dodal/devices/hutch_shutter.py +6 -6
- dodal/devices/insertion_device/__init__.py +8 -0
- dodal/devices/insertion_device/apple2_controller.py +51 -60
- dodal/devices/insertion_device/apple2_undulator.py +40 -64
- dodal/devices/insertion_device/apple_knot_controller.py +222 -0
- dodal/devices/insertion_device/energy.py +100 -27
- dodal/devices/insertion_device/energy_motor_lookup.py +20 -27
- dodal/devices/insertion_device/lookup_table_models.py +45 -50
- dodal/devices/insertion_device/polarisation.py +1 -1
- dodal/devices/ipin.py +1 -1
- dodal/devices/linkam3.py +7 -5
- dodal/devices/motors.py +107 -19
- dodal/devices/mx_phase1/beamstop.py +2 -4
- dodal/devices/oav/oav_calculations.py +20 -13
- dodal/devices/oav/oav_detector.py +28 -23
- dodal/devices/oav/oav_parameters.py +4 -9
- dodal/devices/oav/oav_to_redis_forwarder.py +22 -18
- dodal/devices/oav/pin_image_recognition/__init__.py +4 -6
- dodal/devices/oav/pin_image_recognition/manual_test.py +1 -2
- dodal/devices/oav/pin_image_recognition/utils.py +30 -32
- dodal/devices/oav/snapshots/grid_overlay.py +10 -9
- dodal/devices/oav/snapshots/snapshot_image_processing.py +15 -13
- dodal/devices/oav/utils.py +9 -12
- dodal/devices/p45.py +3 -9
- dodal/devices/pgm.py +8 -14
- dodal/devices/pressure_jump_cell.py +93 -32
- dodal/devices/qbpm.py +1 -3
- dodal/devices/robot.py +12 -4
- dodal/devices/s4_slit_gaps.py +1 -1
- dodal/devices/selectable_source.py +5 -2
- dodal/devices/slits.py +2 -5
- dodal/devices/smargon.py +2 -3
- dodal/devices/temperture_controller/lakeshore/lakeshore.py +38 -64
- dodal/devices/temperture_controller/lakeshore/lakeshore_io.py +21 -35
- dodal/devices/tetramm.py +7 -7
- dodal/devices/turbo_slit.py +8 -7
- dodal/devices/undulator.py +42 -56
- dodal/devices/util/adjuster_plans.py +2 -3
- dodal/devices/util/epics_util.py +10 -10
- dodal/devices/util/lookup_tables.py +17 -18
- dodal/devices/v2f.py +2 -3
- dodal/devices/watsonmarlow323_pump.py +1 -1
- dodal/devices/xbpm_feedback.py +3 -2
- dodal/devices/xspress3/xspress3.py +8 -11
- dodal/devices/xspress3/xspress3_channel.py +3 -6
- dodal/devices/zebra/zebra.py +6 -7
- dodal/devices/zebra/zebra_constants_mapping.py +11 -7
- dodal/devices/zebra/zebra_controlled_shutter.py +2 -1
- dodal/devices/zocalo/zocalo_interaction.py +14 -14
- dodal/devices/zocalo/zocalo_results.py +33 -33
- dodal/log.py +23 -20
- dodal/plan_stubs/check_topup.py +15 -15
- dodal/plan_stubs/data_session.py +6 -6
- dodal/plan_stubs/motor_utils.py +22 -18
- dodal/plan_stubs/pressure_jump_cell.py +18 -0
- dodal/plan_stubs/wrapped.py +40 -55
- dodal/plans/bimorph.py +63 -52
- dodal/plans/device_setup_plans/__init__.py +5 -0
- dodal/plans/device_setup_plans/setup_pin_tip_params.py +63 -0
- dodal/plans/preprocessors/verify_undulator_gap.py +10 -8
- dodal/plans/spec_path.py +3 -5
- dodal/plans/verify_undulator_gap.py +1 -2
- dodal/plans/wrapped.py +4 -3
- dodal/testing/electron_analyser/device_factory.py +5 -7
- dodal/testing/fixtures/devices/apple2.py +38 -0
- dodal/testing/fixtures/run_engine.py +3 -7
- dodal/testing/fixtures/utils.py +1 -2
- dodal/utils.py +60 -58
- dls_dodal-1.69.0.dist-info/RECORD +0 -338
- dodal/beamline_specific_utils/i05_shared.py +0 -14
- dodal/devices/b07/__init__.py +0 -3
- dodal/devices/i04/beam_centre.py +0 -84
- dodal/devices/i05/__init__.py +0 -3
- dodal/devices/i09/__init__.py +0 -3
- dodal/devices/i21/__init__.py +0 -5
- {dls_dodal-1.69.0.dist-info → dls_dodal-2.0.0.dist-info}/entry_points.txt +0 -0
- {dls_dodal-1.69.0.dist-info → dls_dodal-2.0.0.dist-info}/licenses/LICENSE +0 -0
- {dls_dodal-1.69.0.dist-info → dls_dodal-2.0.0.dist-info}/top_level.txt +0 -0
- /dodal/{beamline_specific_utils → devices/beamlines}/__init__.py +0 -0
- /dodal/devices/{b07 → beamlines/b07}/enums.py +0 -0
- /dodal/devices/{b07_1 → beamlines/b07_1}/enums.py +0 -0
- /dodal/devices/{b16 → beamlines/b16}/__init__.py +0 -0
- /dodal/devices/{i02_1 → beamlines/i02_1}/__init__.py +0 -0
- /dodal/devices/{i02_2 → beamlines/i02_2}/__init__.py +0 -0
- /dodal/devices/{i03 → beamlines/i03}/__init__.py +0 -0
- /dodal/devices/{i03 → beamlines/i03}/constants.py +0 -0
- /dodal/devices/{i04 → beamlines/i04}/__init__.py +0 -0
- /dodal/devices/{i04 → beamlines/i04}/constants.py +0 -0
- /dodal/devices/{i04 → beamlines/i04}/max_pixel.py +0 -0
- /dodal/devices/{i05 → beamlines/i05}/enums.py +0 -0
- /dodal/devices/{i07 → beamlines/i07}/__init__.py +0 -0
- /dodal/devices/{i09 → beamlines/i09}/enums.py +0 -0
- /dodal/devices/{i09_1 → beamlines/i09_1}/__init__.py +0 -0
- /dodal/devices/{i09_1 → beamlines/i09_1}/enums.py +0 -0
- /dodal/devices/{i09_1_shared → beamlines/i09_1_shared}/__init__.py +0 -0
- /dodal/devices/{i09_2_shared → beamlines/i09_2_shared}/__init__.py +0 -0
- /dodal/devices/{i09_2_shared → beamlines/i09_2_shared}/i09_apple2.py +0 -0
- /dodal/devices/{i10 → beamlines/i10}/__init__.py +0 -0
- /dodal/devices/{i10 → beamlines/i10}/i10_setting_data.py +0 -0
- /dodal/devices/{i10 → beamlines/i10}/mirrors.py +0 -0
- /dodal/devices/{i10 → beamlines/i10}/rasor/__init__.py +0 -0
- /dodal/devices/{i10 → beamlines/i10}/rasor/rasor_scaler_cards.py +0 -0
- /dodal/devices/{i11 → beamlines/i10_1/electromagnet}/__init__.py +0 -0
- /dodal/devices/{i13_1 → beamlines/i11}/__init__.py +0 -0
- /dodal/devices/{i15 → beamlines/i13_1}/__init__.py +0 -0
- /dodal/devices/{i13_1 → beamlines/i13_1}/merlin_controller.py +0 -0
- /dodal/devices/{i17 → beamlines/i15}/__init__.py +0 -0
- /dodal/devices/{i15 → beamlines/i15}/laue.py +0 -0
- /dodal/devices/{i15 → beamlines/i15}/motors.py +0 -0
- /dodal/devices/{i15 → beamlines/i15}/rail.py +0 -0
- /dodal/devices/{i18 → beamlines/i17}/__init__.py +0 -0
- /dodal/devices/{i19 → beamlines/i18}/__init__.py +0 -0
- /dodal/devices/{i18 → beamlines/i18}/kb_mirror.py +0 -0
- /dodal/devices/{i19/access_controlled → beamlines/i19}/__init__.py +0 -0
- /dodal/devices/{i20_1 → beamlines/i19/access_controlled}/__init__.py +0 -0
- /dodal/devices/{i19 → beamlines/i19}/access_controlled/hutch_access.py +0 -0
- /dodal/devices/{i19 → beamlines/i19}/beamstop.py +0 -0
- /dodal/devices/{i19 → beamlines/i19}/diffractometer.py +0 -0
- /dodal/devices/{i22 → beamlines/i20_1}/__init__.py +0 -0
- /dodal/devices/{i21 → beamlines/i21}/enums.py +0 -0
- /dodal/devices/{i24 → beamlines/i22}/__init__.py +0 -0
- /dodal/devices/{p99 → beamlines/i24}/__init__.py +0 -0
- /dodal/devices/{i24 → beamlines/i24}/aperture.py +0 -0
- /dodal/devices/{i24 → beamlines/i24}/focus_mirrors.py +0 -0
- /dodal/devices/{i24 → beamlines/i24}/vgonio.py +0 -0
- /dodal/devices/{p60 → beamlines/p60}/__init__.py +0 -0
- /dodal/devices/{p60 → beamlines/p60}/enums.py +0 -0
- /dodal/devices/{p99 → beamlines/p99}/sample_stage.py +0 -0
|
@@ -25,7 +25,8 @@ class MJPG(StandardReadable, Triggerable, ABC):
|
|
|
25
25
|
"""The MJPG areadetector plugin creates an MJPG video stream of the camera's output.
|
|
26
26
|
|
|
27
27
|
This devices uses that stream to grab images. When it is triggered it will send the
|
|
28
|
-
latest image from the stream to the `post_processing` method for child classes to
|
|
28
|
+
latest image from the stream to the `post_processing` method for child classes to
|
|
29
|
+
handle.
|
|
29
30
|
"""
|
|
30
31
|
|
|
31
32
|
def __init__(
|
|
@@ -51,9 +52,9 @@ class MJPG(StandardReadable, Triggerable, ABC):
|
|
|
51
52
|
super().__init__(name)
|
|
52
53
|
|
|
53
54
|
async def _save_image(self, image: Image.Image):
|
|
54
|
-
"""A helper function to save a given image to the path supplied by the
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
"""A helper function to save a given image to the path supplied by the
|
|
56
|
+
directory and filename signals. The full resultant path is put on the
|
|
57
|
+
last_saved_path signal.
|
|
57
58
|
"""
|
|
58
59
|
filename_str = await self.filename.get_value()
|
|
59
60
|
directory_str = await self.directory.get_value()
|
|
@@ -74,7 +75,7 @@ class MJPG(StandardReadable, Triggerable, ABC):
|
|
|
74
75
|
"""This takes a snapshot image from the MJPG stream and send it to the
|
|
75
76
|
post_processing method, expected to be implemented by a child of this class.
|
|
76
77
|
|
|
77
|
-
It is the responsibility of the child class to save any resulting images by
|
|
78
|
+
It is the responsibility of the child class to save any resulting images by
|
|
78
79
|
calling _save_image.
|
|
79
80
|
"""
|
|
80
81
|
url_str = await self.url.get_value()
|
|
@@ -37,13 +37,15 @@ class ReadOnlyAttenuator(StandardReadable):
|
|
|
37
37
|
|
|
38
38
|
class BinaryFilterAttenuator(ReadOnlyAttenuator, Movable[float]):
|
|
39
39
|
"""The attenuator will insert filters into the beam to reduce its transmission.
|
|
40
|
-
In this attenuator, each filter can be in one of two states: IN or OUT
|
|
40
|
+
In this attenuator, each filter can be in one of two states: IN or OUT.
|
|
41
41
|
|
|
42
42
|
This device should be set with:
|
|
43
|
+
|
|
43
44
|
yield from bps.set(attenuator, desired_transmission)
|
|
44
45
|
|
|
45
46
|
Where desired_transmission is fraction e.g. 0-1. When the actual_transmission is
|
|
46
|
-
read from the device it is also fractional
|
|
47
|
+
read from the device it is also fractional
|
|
48
|
+
"""
|
|
47
49
|
|
|
48
50
|
def __init__(self, prefix: str, num_filters: int, name: str = ""):
|
|
49
51
|
self._calculated_filter_states: DeviceVector[SignalR[int]] = DeviceVector(
|
|
@@ -72,11 +74,10 @@ class BinaryFilterAttenuator(ReadOnlyAttenuator, Movable[float]):
|
|
|
72
74
|
async def set(self, value: float):
|
|
73
75
|
"""Set the transmission to the fractional (0-1) value given.
|
|
74
76
|
|
|
75
|
-
The attenuator IOC will then insert filters to reach the desired transmission
|
|
76
|
-
the current beamline energy, the set will only complete when they have all
|
|
77
|
-
applied.
|
|
77
|
+
The attenuator IOC will then insert filters to reach the desired transmission
|
|
78
|
+
for the current beamline energy, the set will only complete when they have all
|
|
79
|
+
been applied.
|
|
78
80
|
"""
|
|
79
|
-
|
|
80
81
|
LOGGER.debug("Using current energy ")
|
|
81
82
|
await self._use_current_energy.trigger()
|
|
82
83
|
LOGGER.info(f"Setting desired transmission to {value}")
|
|
@@ -110,7 +111,8 @@ ENUM_ATTENUATOR_SETTLE_TIME_S = 0.15
|
|
|
110
111
|
class EnumFilterAttenuator(ReadOnlyAttenuator, Movable[float]):
|
|
111
112
|
"""The attenuator will insert filters into the beam to reduce its transmission.
|
|
112
113
|
|
|
113
|
-
This device is currently working, but feature incomplete. See
|
|
114
|
+
This device is currently working, but feature incomplete. See
|
|
115
|
+
https://github.com/DiamondLightSource/dodal/issues/972
|
|
114
116
|
|
|
115
117
|
In this attenuator, the state of a filter corresponds to the selected material,
|
|
116
118
|
e.g Ag50, in contrast to being either 'IN' or 'OUT'; see BinaryFilterAttenuator.
|
|
@@ -141,11 +143,10 @@ class EnumFilterAttenuator(ReadOnlyAttenuator, Movable[float]):
|
|
|
141
143
|
async def set(self, value: float):
|
|
142
144
|
"""Set the transmission to the fractional (0-1) value given.
|
|
143
145
|
|
|
144
|
-
The attenuator IOC will then insert filters to reach the desired transmission
|
|
145
|
-
the current beamline energy, the set will only complete when they have all
|
|
146
|
-
applied.
|
|
146
|
+
The attenuator IOC will then insert filters to reach the desired transmission
|
|
147
|
+
for the current beamline energy, the set will only complete when they have all
|
|
148
|
+
been applied.
|
|
147
149
|
"""
|
|
148
|
-
|
|
149
150
|
# auto move should normally be on, but check here incase it was manually turned off
|
|
150
151
|
await self._auto_move_on_desired_transmission_set.set(YesNo.YES)
|
|
151
152
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
from dodal.devices.b07_1.ccmc import (
|
|
1
|
+
from dodal.devices.beamlines.b07_1.ccmc import (
|
|
2
2
|
ChannelCutMonochromator,
|
|
3
3
|
ChannelCutMonochromatorPositions,
|
|
4
4
|
)
|
|
5
|
-
from dodal.devices.b07_1.enums import Grating, LensMode
|
|
5
|
+
from dodal.devices.beamlines.b07_1.enums import Grating, LensMode
|
|
6
6
|
|
|
7
7
|
__all__ = [
|
|
8
8
|
"Grating",
|
|
@@ -25,13 +25,16 @@ error_message = "Can not get energy value in eV from ccmc position: "
|
|
|
25
25
|
class ChannelCutMonochromator(
|
|
26
26
|
StandardReadable, Movable[ChannelCutMonochromatorPositions]
|
|
27
27
|
):
|
|
28
|
-
"""
|
|
29
|
-
Device to move the channel cut monochromator (ccmc). CCMC has three
|
|
28
|
+
"""Device to move the channel cut monochromator (ccmc). CCMC has three
|
|
30
29
|
choices of crystal (Xtal for short). Setting energy is by means of a
|
|
31
30
|
multi-positioner. The positions are named after the nominal energies of the
|
|
32
31
|
crystals. To change energy select one of the crystals from the list.
|
|
33
32
|
This causes the Y motor to move that crystal into the beam and other
|
|
34
33
|
motors have to align the angles correctly.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
prefix (str): Beamline specific part of the PV.
|
|
37
|
+
name (str, optional): Name of the device.
|
|
35
38
|
"""
|
|
36
39
|
|
|
37
40
|
def __init__(
|
|
@@ -39,14 +42,6 @@ class ChannelCutMonochromator(
|
|
|
39
42
|
prefix: str,
|
|
40
43
|
name: str = "",
|
|
41
44
|
) -> None:
|
|
42
|
-
"""
|
|
43
|
-
Parameters
|
|
44
|
-
----------
|
|
45
|
-
prefix:
|
|
46
|
-
Beamline specific part of the PV
|
|
47
|
-
name:
|
|
48
|
-
Name of the device
|
|
49
|
-
"""
|
|
50
45
|
with self.add_children_as_readables():
|
|
51
46
|
# crystal motors
|
|
52
47
|
self._xyz = XYZStage(prefix)
|
|
@@ -10,10 +10,9 @@ from dodal.devices.controllers import ConstantDeadTimeController
|
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
def software_triggered_tiff_area_detector(prefix: str, deadtime: float = 0.0):
|
|
13
|
-
"""
|
|
14
|
-
Wrapper for AreaDetector with fixed dead time (defaulted to 0)
|
|
13
|
+
"""Wrapper for AreaDetector with fixed dead time (defaulted to 0)
|
|
15
14
|
and a TIFF file writer.
|
|
16
|
-
Most detectors in B16 could be configured like this
|
|
15
|
+
Most detectors in B16 could be configured like this.
|
|
17
16
|
"""
|
|
18
17
|
return AreaDetector(
|
|
19
18
|
writer=ADTIFFWriter.with_io(
|
|
@@ -11,8 +11,7 @@ from dodal.log import LOGGER
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
class ZebraGridScanParamsTwoD(GridScanParamsCommon, WithDwellTime):
|
|
14
|
-
"""
|
|
15
|
-
Params for 2D Zebra FGS. Adds on the dwell time, which is really the time
|
|
14
|
+
"""Params for 2D Zebra FGS. Adds on the dwell time, which is really the time
|
|
16
15
|
between trigger positions.
|
|
17
16
|
"""
|
|
18
17
|
|
|
@@ -23,7 +22,7 @@ class ZebraFastGridScanTwoD(FastGridScanCommon[ZebraGridScanParamsTwoD]):
|
|
|
23
22
|
- No Z steps, Z step sizes, or Y2 start positions, or Z2 start
|
|
24
23
|
- No scan valid PV - see https://github.com/DiamondLightSource/mx-bluesky/issues/1203
|
|
25
24
|
- No program_number - see https://github.com/DiamondLightSource/mx-bluesky/issues/1203
|
|
26
|
-
"""
|
|
25
|
+
""" # noqa D415
|
|
27
26
|
|
|
28
27
|
def __init__(
|
|
29
28
|
self, prefix: str, motion_controller_prefix: str, name: str = ""
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
from ophyd_async.core import Reference, derived_signal_r
|
|
2
2
|
|
|
3
3
|
from dodal.devices.aperturescatterguard import ApertureScatterguard
|
|
4
|
+
from dodal.devices.beamlines.i03.constants import BeamsizeConstants
|
|
4
5
|
from dodal.devices.beamsize.beamsize import BeamsizeBase
|
|
5
|
-
from dodal.devices.i03.constants import BeamsizeConstants
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class Beamsize(BeamsizeBase):
|
|
9
|
+
"""Device that calculates the size of the beam by taking the minimum of the beam
|
|
10
|
+
dimensions and the aperture scatterguard diameter.
|
|
11
|
+
"""
|
|
12
|
+
|
|
9
13
|
def __init__(self, aperture_scatterguard: ApertureScatterguard, name=""):
|
|
10
14
|
super().__init__(name=name)
|
|
11
15
|
self._aperture_scatterguard_ref = Reference(aperture_scatterguard)
|
|
@@ -13,23 +17,23 @@ class Beamsize(BeamsizeBase):
|
|
|
13
17
|
with self.add_children_as_readables():
|
|
14
18
|
self.x_um = derived_signal_r(
|
|
15
19
|
self._get_beamsize_x,
|
|
16
|
-
|
|
20
|
+
aperture_diameter=self._aperture_scatterguard_ref().diameter,
|
|
17
21
|
derived_units="µm",
|
|
18
22
|
)
|
|
19
23
|
self.y_um = derived_signal_r(
|
|
20
24
|
self._get_beamsize_y,
|
|
21
|
-
|
|
25
|
+
aperture_diameter=self._aperture_scatterguard_ref().diameter,
|
|
22
26
|
derived_units="µm",
|
|
23
27
|
)
|
|
24
28
|
|
|
25
29
|
def _get_beamsize_x(
|
|
26
30
|
self,
|
|
27
|
-
|
|
31
|
+
aperture_diameter: float,
|
|
28
32
|
) -> float:
|
|
29
|
-
return min(
|
|
33
|
+
return min(aperture_diameter, BeamsizeConstants.BEAM_WIDTH_UM)
|
|
30
34
|
|
|
31
35
|
def _get_beamsize_y(
|
|
32
36
|
self,
|
|
33
|
-
|
|
37
|
+
aperture_diameter: float,
|
|
34
38
|
) -> float:
|
|
35
|
-
return min(
|
|
39
|
+
return min(aperture_diameter, BeamsizeConstants.BEAM_HEIGHT_UM)
|
|
@@ -18,8 +18,7 @@ from dodal.devices.common_dcm import (
|
|
|
18
18
|
class DCM(
|
|
19
19
|
DoubleCrystalMonochromatorWithDSpacing[PitchAndRollCrystal, StationaryCrystal]
|
|
20
20
|
):
|
|
21
|
-
"""
|
|
22
|
-
A double crystal monochromator (DCM), used to select the energy of the beam.
|
|
21
|
+
"""A double crystal monochromator (DCM), used to select the energy of the beam.
|
|
23
22
|
|
|
24
23
|
perp describes the gap between the 2 DCM crystals which has to change as you alter
|
|
25
24
|
the angle to select the requested energy.
|
|
@@ -4,7 +4,7 @@ from bluesky.protocols import Movable
|
|
|
4
4
|
from ophyd_async.core import AsyncStatus, Reference, StandardReadable
|
|
5
5
|
|
|
6
6
|
from dodal.common.beamlines.beamline_parameters import get_beamline_parameters
|
|
7
|
-
from dodal.devices.i03.dcm import DCM
|
|
7
|
+
from dodal.devices.beamlines.i03.dcm import DCM
|
|
8
8
|
from dodal.devices.undulator import UndulatorInKeV
|
|
9
9
|
from dodal.log import LOGGER
|
|
10
10
|
|
|
@@ -12,9 +12,8 @@ ENERGY_TIMEOUT_S: float = 30.0
|
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
class UndulatorDCM(StandardReadable, Movable[float]):
|
|
15
|
-
"""
|
|
16
|
-
|
|
17
|
-
DCM. The DCM has a motor which controls the beam energy, when it moves, the
|
|
15
|
+
"""Composite device to handle changing beamline energies, wraps the Undulator and
|
|
16
|
+
the DCM. The DCM has a motor which controls the beam energy, when it moves, the
|
|
18
17
|
Undulator gap may also have to change to enable emission at the new energy.
|
|
19
18
|
The relationship between the two motor motor positions is provided via a lookup
|
|
20
19
|
table.
|
|
@@ -24,7 +23,7 @@ class UndulatorDCM(StandardReadable, Movable[float]):
|
|
|
24
23
|
a comprehensive way to set beam energy.
|
|
25
24
|
|
|
26
25
|
This class will be removed in the future. Use the separate Undulator and DCM devices
|
|
27
|
-
instead. See https://github.com/DiamondLightSource/dodal/issues/1092
|
|
26
|
+
instead. See https://github.com/DiamondLightSource/dodal/issues/1092.
|
|
28
27
|
"""
|
|
29
28
|
|
|
30
29
|
DCM_PERP_TOLERANCE = 0.01
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import math
|
|
2
|
+
|
|
3
|
+
import cv2
|
|
4
|
+
import numpy as np
|
|
5
|
+
from bluesky.protocols import Triggerable
|
|
6
|
+
from ophyd_async.core import (
|
|
7
|
+
AsyncStatus,
|
|
8
|
+
StandardReadable,
|
|
9
|
+
soft_signal_r_and_setter,
|
|
10
|
+
soft_signal_rw,
|
|
11
|
+
)
|
|
12
|
+
from ophyd_async.epics.core import (
|
|
13
|
+
epics_signal_r,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
from dodal.devices.oav.utils import convert_to_gray_and_blur
|
|
17
|
+
from dodal.log import LOGGER
|
|
18
|
+
|
|
19
|
+
# Constant was chosen from trial and error with test images
|
|
20
|
+
ADDITIONAL_BINARY_THRESH = 20
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def convert_image_to_binary(image: np.ndarray):
|
|
24
|
+
"""Creates a binary image from OAV image array data.
|
|
25
|
+
|
|
26
|
+
Pixels of the input image are converted to one of two values (a high and a low value).
|
|
27
|
+
Otsu's method is used for automatic thresholding.
|
|
28
|
+
See https://docs.opencv.org/4.x/d7/d4d/tutorial_py_thresholding.html.
|
|
29
|
+
The threshold is increased by ADDITIONAL_BINARY_THRESH in order to get more of
|
|
30
|
+
the centre of the beam.
|
|
31
|
+
"""
|
|
32
|
+
max_pixel_value = 255
|
|
33
|
+
|
|
34
|
+
blurred_image = convert_to_gray_and_blur(image)
|
|
35
|
+
|
|
36
|
+
threshold_value, _ = cv2.threshold(
|
|
37
|
+
blurred_image, 0, max_pixel_value, cv2.THRESH_BINARY + cv2.THRESH_OTSU
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
# Adjusting because the inner beam is less noisy compared to the outer
|
|
41
|
+
threshold_value += ADDITIONAL_BINARY_THRESH
|
|
42
|
+
|
|
43
|
+
_, thresholded_image = cv2.threshold(
|
|
44
|
+
blurred_image, threshold_value, max_pixel_value, cv2.THRESH_BINARY
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
LOGGER.info(f"Image binarised with threshold of {threshold_value}")
|
|
48
|
+
return thresholded_image
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def round_half_up(x):
|
|
52
|
+
return int(math.floor(x + 0.5))
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def get_roi(
|
|
56
|
+
image_arr: np.ndarray,
|
|
57
|
+
centre_x: int,
|
|
58
|
+
centre_y: int,
|
|
59
|
+
box_width: int = 200,
|
|
60
|
+
box_height: int = 200,
|
|
61
|
+
) -> tuple[np.ndarray, tuple[int, int], tuple[int, int]]:
|
|
62
|
+
"""Creates an ROI image array from a full screen image array, given a centre for the
|
|
63
|
+
ROI image and a width and height of the ROI box. Note that if the centre of the ROI
|
|
64
|
+
box is close to the edge of the full screen image, the box may be smaller than the
|
|
65
|
+
width and height provided, as the ROI will be trimmed to fit inside the full screen
|
|
66
|
+
image.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
image_arr (np.ndarray): The full screen image array.
|
|
70
|
+
centre_x (int): The x coordinate of the centre of the ROI box.
|
|
71
|
+
centre_y (int): The y coordinate of the centre of the ROI box.
|
|
72
|
+
box_width (int, optional): The width of the ROI box. Defaults to 200.
|
|
73
|
+
box_height (int, optional): The height of the ROI box. Defaults to 200.
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
tuple[np.ndarray, tuple[int, int], tuple[int, int]]: The ROI array, and (x, y)
|
|
77
|
+
coordinates of the top left and bottom right corners of the ROI box.
|
|
78
|
+
"""
|
|
79
|
+
height, width = image_arr.shape[:2]
|
|
80
|
+
x_dist = (box_width) / 2
|
|
81
|
+
y_dist = (box_height) / 2
|
|
82
|
+
|
|
83
|
+
# Clip coordinates to stay within bounds
|
|
84
|
+
x_min = max(round_half_up(centre_x - x_dist), 0)
|
|
85
|
+
x_max = min(round_half_up(centre_x + x_dist), width) - 1
|
|
86
|
+
y_min = max(round_half_up(centre_y - y_dist), 0)
|
|
87
|
+
y_max = min(round_half_up(centre_y + y_dist), height) - 1
|
|
88
|
+
|
|
89
|
+
roi_arr = image_arr[y_min : y_max + 1, x_min : x_max + 1]
|
|
90
|
+
|
|
91
|
+
return roi_arr, (x_min, y_min), (x_max, y_max)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class CentreEllipseMethod(StandardReadable, Triggerable):
|
|
95
|
+
"""Upon triggering, fits an ellipse to a binary image from the area detector defined
|
|
96
|
+
by the prefix.
|
|
97
|
+
|
|
98
|
+
This is used, in conjunction with a scintillator, to determine the centre of the
|
|
99
|
+
beam on the image.
|
|
100
|
+
"""
|
|
101
|
+
|
|
102
|
+
def __init__(self, prefix: str, overlay_channel: int = 1, name: str = ""):
|
|
103
|
+
self.oav_array_signal = epics_signal_r(np.ndarray, f"pva://{prefix}PVA:ARRAY")
|
|
104
|
+
|
|
105
|
+
self.center_x_val, self._center_x_val_setter = soft_signal_r_and_setter(float)
|
|
106
|
+
self.center_y_val, self._center_y_val_setter = soft_signal_r_and_setter(float)
|
|
107
|
+
|
|
108
|
+
self.current_centre_x = epics_signal_r(
|
|
109
|
+
int, f"{prefix}OVER:{overlay_channel}:CenterX"
|
|
110
|
+
)
|
|
111
|
+
self.current_centre_y = epics_signal_r(
|
|
112
|
+
int, f"{prefix}OVER:{overlay_channel}:CenterY"
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
self.roi_box_size = soft_signal_rw(int, 300)
|
|
116
|
+
|
|
117
|
+
super().__init__(name)
|
|
118
|
+
|
|
119
|
+
def _fit_ellipse(self, binary_img: cv2.typing.MatLike) -> cv2.typing.RotatedRect:
|
|
120
|
+
contours, _ = cv2.findContours(
|
|
121
|
+
binary_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE
|
|
122
|
+
)
|
|
123
|
+
if not contours:
|
|
124
|
+
raise ValueError("No contours found in image.")
|
|
125
|
+
|
|
126
|
+
largest_contour = max(contours, key=cv2.contourArea)
|
|
127
|
+
if len(largest_contour) < 5:
|
|
128
|
+
raise ValueError(
|
|
129
|
+
f"Not enough points to fit an ellipse. Found {largest_contour} points and need at least 5."
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
return cv2.fitEllipse(largest_contour)
|
|
133
|
+
|
|
134
|
+
@AsyncStatus.wrap
|
|
135
|
+
async def trigger(self):
|
|
136
|
+
array_data = await self.oav_array_signal.get_value()
|
|
137
|
+
current_x = await self.current_centre_x.get_value()
|
|
138
|
+
current_y = await self.current_centre_y.get_value()
|
|
139
|
+
roi_box_size = await self.roi_box_size.get_value()
|
|
140
|
+
|
|
141
|
+
roi_data, top_left_corner, _ = get_roi(
|
|
142
|
+
array_data, current_x, current_y, roi_box_size, roi_box_size
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
roi_binary = convert_image_to_binary(roi_data)
|
|
146
|
+
ellipse_fit = self._fit_ellipse(roi_binary)
|
|
147
|
+
roi_centre_x = ellipse_fit[0][0]
|
|
148
|
+
roi_centre_y = ellipse_fit[0][1]
|
|
149
|
+
# convert back to full screen image coords and set beam centre
|
|
150
|
+
self._center_x_val_setter(roi_centre_x + top_left_corner[0])
|
|
151
|
+
self._center_y_val_setter(roi_centre_y + top_left_corner[1])
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
from ophyd_async.core import Reference, derived_signal_r
|
|
2
2
|
|
|
3
3
|
from dodal.devices.aperturescatterguard import ApertureScatterguard
|
|
4
|
+
from dodal.devices.beamlines.i04.transfocator import Transfocator
|
|
4
5
|
from dodal.devices.beamsize.beamsize import BeamsizeBase
|
|
5
|
-
from dodal.devices.i04.transfocator import Transfocator
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class Beamsize(BeamsizeBase):
|
|
9
|
+
"""Device that calculates the size of the beam by taking the minimum of the
|
|
10
|
+
transfocator size and the aperture scatterguard diameter.
|
|
11
|
+
"""
|
|
12
|
+
|
|
9
13
|
def __init__(
|
|
10
14
|
self,
|
|
11
15
|
transfocator: Transfocator,
|
|
@@ -20,26 +24,26 @@ class Beamsize(BeamsizeBase):
|
|
|
20
24
|
self.x_um = derived_signal_r(
|
|
21
25
|
self._get_beamsize_x,
|
|
22
26
|
transfocator_size_x=self._transfocator_ref().current_horizontal_size_rbv,
|
|
23
|
-
|
|
27
|
+
aperture_diameter=self._aperture_scatterguard_ref().diameter,
|
|
24
28
|
derived_units="µm",
|
|
25
29
|
)
|
|
26
30
|
self.y_um = derived_signal_r(
|
|
27
31
|
self._get_beamsize_y,
|
|
28
32
|
transfocator_size_y=self._transfocator_ref().current_vertical_size_rbv,
|
|
29
|
-
|
|
33
|
+
aperture_diameter=self._aperture_scatterguard_ref().diameter,
|
|
30
34
|
derived_units="µm",
|
|
31
35
|
)
|
|
32
36
|
|
|
33
37
|
def _get_beamsize_x(
|
|
34
38
|
self,
|
|
35
39
|
transfocator_size_x: float,
|
|
36
|
-
|
|
40
|
+
aperture_diameter: float,
|
|
37
41
|
) -> float:
|
|
38
|
-
return min(transfocator_size_x,
|
|
42
|
+
return min(transfocator_size_x, aperture_diameter)
|
|
39
43
|
|
|
40
44
|
def _get_beamsize_y(
|
|
41
45
|
self,
|
|
42
46
|
transfocator_size_y: float,
|
|
43
|
-
|
|
47
|
+
aperture_diameter: float,
|
|
44
48
|
) -> float:
|
|
45
|
-
return min(transfocator_size_y,
|
|
49
|
+
return min(transfocator_size_y, aperture_diameter)
|
|
@@ -15,7 +15,7 @@ from ophyd_async.core import (
|
|
|
15
15
|
)
|
|
16
16
|
from redis.asyncio import ConnectionError, StrictRedis
|
|
17
17
|
|
|
18
|
-
from dodal.devices.i04.constants import RedisConstants
|
|
18
|
+
from dodal.devices.beamlines.i04.constants import RedisConstants
|
|
19
19
|
from dodal.devices.oav.oav_calculations import (
|
|
20
20
|
calculate_beam_distance,
|
|
21
21
|
)
|
|
@@ -238,7 +238,6 @@ class MurkoResultsDevice(StandardReadable, Triggerable, Stageable):
|
|
|
238
238
|
remove many of the outliers. Murko also occasionally picks a point in the bottom
|
|
239
239
|
left corner, which can be removed by filtering results with a small x pixel.
|
|
240
240
|
"""
|
|
241
|
-
|
|
242
241
|
LOGGER.info(f"Number of results before filtering: {len(self._results)}")
|
|
243
242
|
sorted_results = sorted(self._results, key=lambda item: item.chosen_point_px[0])
|
|
244
243
|
|
|
@@ -278,15 +277,16 @@ class MurkoResultsDevice(StandardReadable, Triggerable, Stageable):
|
|
|
278
277
|
|
|
279
278
|
|
|
280
279
|
def get_yz_least_squares(vertical_dists: list, omegas: list) -> tuple[float, float]:
|
|
281
|
-
"""Get the least squares solution for y and z from the vertical distances and omega
|
|
280
|
+
"""Get the least squares solution for y and z from the vertical distances and omega
|
|
281
|
+
angles.
|
|
282
282
|
|
|
283
283
|
Args:
|
|
284
|
-
|
|
284
|
+
vertical_dists (list): List of vertical distances from beam centre. Any units.
|
|
285
285
|
omegas (list): List of omega angles in degrees.
|
|
286
286
|
|
|
287
287
|
Returns:
|
|
288
288
|
tuple[float, float]: y, z distances from centre, in whichever units
|
|
289
|
-
|
|
289
|
+
v_dists came as.
|
|
290
290
|
"""
|
|
291
291
|
thetas = np.radians(omegas)
|
|
292
292
|
matrix = np.column_stack([np.cos(thetas), -np.sin(thetas)])
|
|
@@ -3,7 +3,6 @@ import asyncio
|
|
|
3
3
|
from ophyd_async.core import (
|
|
4
4
|
AsyncStatus,
|
|
5
5
|
StandardReadable,
|
|
6
|
-
observe_value,
|
|
7
6
|
wait_for_value,
|
|
8
7
|
)
|
|
9
8
|
from ophyd_async.epics.core import epics_signal_r, epics_signal_rw
|
|
@@ -49,24 +48,20 @@ class Transfocator(StandardReadable):
|
|
|
49
48
|
@AsyncStatus.wrap
|
|
50
49
|
async def set(self, value: float):
|
|
51
50
|
"""To set the beamsize on the transfocator we must:
|
|
52
|
-
1. Set the beamsize in the calculator part of the transfocator
|
|
53
|
-
2. Get the predicted number of lenses needed from this calculator
|
|
54
|
-
3. Enter this back into the device
|
|
55
|
-
4. Start the device moving
|
|
56
|
-
5. Wait for the start_rbv goes high and low again
|
|
51
|
+
1. Set the beamsize in the calculator part of the transfocator.
|
|
52
|
+
2. Get the predicted number of lenses needed from this calculator.
|
|
53
|
+
3. Enter this back into the device.
|
|
54
|
+
4. Start the device moving.
|
|
55
|
+
5. Wait for the start_rbv goes high and low again.
|
|
57
56
|
"""
|
|
58
57
|
LOGGER.info(f"Transfocator setting {value} beamsize")
|
|
59
58
|
|
|
60
|
-
# Logic in the IOC calculates _num_lenses_calc_rbv when _vert_size_calc_sp changes
|
|
61
|
-
|
|
62
|
-
# Register an observer before setting _vert_size_calc_sp to ensure we don't miss changes
|
|
63
|
-
num_lenses_calc_iterator = observe_value(
|
|
64
|
-
self._num_lenses_calc_rbv, timeout=self.TIMEOUT
|
|
65
|
-
)
|
|
66
|
-
|
|
67
|
-
await anext(num_lenses_calc_iterator)
|
|
68
59
|
await self._vert_size_calc_sp.set(value)
|
|
69
|
-
|
|
60
|
+
# Logic in the IOC calculates _num_lenses_calc_rbv when _vert_size_calc_sp changes,
|
|
61
|
+
# but this isn't instant so we need a short sleep until
|
|
62
|
+
# https://jira.diamond.ac.uk/browse/I04-1100
|
|
63
|
+
await asyncio.sleep(0.1)
|
|
64
|
+
calc_lenses = await self._num_lenses_calc_rbv.get_value()
|
|
70
65
|
|
|
71
66
|
async with periodic_reminder(
|
|
72
67
|
f"Waiting for transfocator to insert {calc_lenses} into beam"
|
|
@@ -9,9 +9,8 @@ from dodal.devices.common_dcm import (
|
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class DCM(DoubleCrystalMonochromator[PitchAndRollCrystal, StationaryCrystal]):
|
|
12
|
-
"""
|
|
13
|
-
|
|
14
|
-
included in GDA.
|
|
12
|
+
"""Device for i07's DCM, including temperature monitors and vertical motor which
|
|
13
|
+
were included in GDA.
|
|
15
14
|
"""
|
|
16
15
|
|
|
17
16
|
def __init__(
|
|
@@ -5,34 +5,33 @@ from dodal.devices.util.lookup_tables import energy_distance_table
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
class InsertionDevice(UndulatorInKeV):
|
|
8
|
-
"""
|
|
9
|
-
|
|
8
|
+
"""Insertion device for i07 including beamline-specific energy-gap lookup
|
|
9
|
+
behaviour.
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
12
|
def __init__(
|
|
13
13
|
self,
|
|
14
|
-
name: str,
|
|
15
14
|
prefix: str,
|
|
16
15
|
harmonic: UndulatorOrder,
|
|
17
16
|
id_gap_lookup_table_path: str = "/dls_sw/i07/software/gda/config/lookupTables/"
|
|
18
17
|
+ "IIDCalibrationTable.txt",
|
|
19
|
-
|
|
18
|
+
name: str = "",
|
|
19
|
+
):
|
|
20
20
|
super().__init__(prefix, id_gap_lookup_table_path, name=name)
|
|
21
21
|
self.harmonic = harmonic
|
|
22
22
|
|
|
23
23
|
async def _get_gap_to_match_energy(self, energy_kev: float) -> float:
|
|
24
|
-
"""
|
|
25
|
-
i07's energy scans remain on a particular harmonic while changing energy. The
|
|
24
|
+
"""i07's energy scans remain on a particular harmonic while changing energy. The
|
|
26
25
|
calibration table has one row for each harmonic, row contains max and min
|
|
27
26
|
energies and their corresponding ID gaps. The requested energy is used to
|
|
28
27
|
interpolate between these values, assuming a linear relationship on the relevant
|
|
29
28
|
scale.
|
|
30
29
|
"""
|
|
31
|
-
energy_to_distance_table
|
|
30
|
+
energy_to_distance_table = await energy_distance_table(
|
|
32
31
|
self.id_gap_lookup_table_path, comments="#", skiprows=2
|
|
33
32
|
)
|
|
34
|
-
harmonic_value
|
|
33
|
+
harmonic_value = await self.harmonic.value.get_value()
|
|
35
34
|
|
|
36
|
-
row
|
|
35
|
+
row = energy_to_distance_table[harmonic_value - 1, :]
|
|
37
36
|
gap = np.interp(energy_kev, [row[1], row[2]], [row[3], row[4]])
|
|
38
37
|
return gap
|