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
dodal/common/visit.py
CHANGED
|
@@ -9,32 +9,29 @@ from pydantic import BaseModel, Field
|
|
|
9
9
|
from dodal.common.types import UpdatingPathProvider
|
|
10
10
|
from dodal.log import LOGGER
|
|
11
11
|
|
|
12
|
-
"""
|
|
13
|
-
|
|
14
|
-
"""
|
|
12
|
+
"""Functionality required for/from the API of a DirectoryService which exposes the
|
|
13
|
+
specifics of the Diamond filesystem."""
|
|
15
14
|
|
|
16
15
|
|
|
17
16
|
class DataCollectionIdentifier(BaseModel):
|
|
18
|
-
"""
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
"""Equivalent to a `Scan Number` or `scan_id`, non-globally unique scan identifier.
|
|
18
|
+
Should be always incrementing, unique per-visit, co-ordinated with any other scan
|
|
19
|
+
engines.
|
|
21
20
|
"""
|
|
22
21
|
|
|
23
22
|
collection_number: int = Field(alias="collectionNumber")
|
|
24
23
|
|
|
25
24
|
|
|
26
25
|
class DirectoryServiceClient(ABC):
|
|
27
|
-
"""
|
|
28
|
-
Object responsible for I/O in determining collection number
|
|
29
|
-
"""
|
|
26
|
+
"""Object responsible for I/O in determining collection number."""
|
|
30
27
|
|
|
31
28
|
@abstractmethod
|
|
32
29
|
async def create_new_collection(self) -> DataCollectionIdentifier:
|
|
33
|
-
"""Create new collection"""
|
|
30
|
+
"""Create new collection."""
|
|
34
31
|
|
|
35
32
|
@abstractmethod
|
|
36
33
|
async def get_current_collection(self) -> DataCollectionIdentifier:
|
|
37
|
-
"""Get current collection"""
|
|
34
|
+
"""Get current collection."""
|
|
38
35
|
|
|
39
36
|
|
|
40
37
|
class DiamondFilenameProvider(FilenameProvider):
|
|
@@ -50,9 +47,9 @@ class DiamondFilenameProvider(FilenameProvider):
|
|
|
50
47
|
|
|
51
48
|
|
|
52
49
|
class RemoteDirectoryServiceClient(DirectoryServiceClient):
|
|
53
|
-
"""Client for the VisitService REST API
|
|
50
|
+
"""Client for the VisitService REST API.
|
|
54
51
|
Currently exposed by the GDA Server to co-ordinate unique filenames.
|
|
55
|
-
While VisitService is embedded in GDA, url is likely to be `ixx-control:8088/api
|
|
52
|
+
While VisitService is embedded in GDA, url is likely to be `ixx-control:8088/api`.
|
|
56
53
|
"""
|
|
57
54
|
|
|
58
55
|
def __init__(self, url: str) -> None:
|
|
@@ -98,13 +95,15 @@ class LocalDirectoryServiceClient(DirectoryServiceClient):
|
|
|
98
95
|
|
|
99
96
|
|
|
100
97
|
class StaticVisitPathProvider(UpdatingPathProvider):
|
|
101
|
-
"""
|
|
102
|
-
|
|
98
|
+
"""Static (single visit) implementation of PathProvider whilst awaiting auth
|
|
99
|
+
infrastructure to generate necessary information per-scan.
|
|
103
100
|
Allows setting a singular visit into which all run files will be saved.
|
|
104
|
-
update() queries a visit service to get the next DataCollectionIdentifier to
|
|
101
|
+
update() queries a visit service to get the next DataCollectionIdentifier to
|
|
102
|
+
increment the suffix of all file writers' next files.
|
|
105
103
|
Requires that all detectors are running with a mutual view on the filesystem.
|
|
106
|
-
Supports a single Visit which should be passed as a Path relative to the root of
|
|
107
|
-
|
|
104
|
+
Supports a single Visit which should be passed as a Path relative to the root of
|
|
105
|
+
the Detector IOC mounting.
|
|
106
|
+
i.e. to write to visit `/dls/ixx/data/YYYY/cm12345-1`.
|
|
108
107
|
"""
|
|
109
108
|
|
|
110
109
|
def __init__(
|
|
@@ -123,9 +122,7 @@ class StaticVisitPathProvider(UpdatingPathProvider):
|
|
|
123
122
|
self._session: ClientSession | None
|
|
124
123
|
|
|
125
124
|
async def update(self, **kwargs) -> None:
|
|
126
|
-
"""
|
|
127
|
-
Creates a new data collection in the current visit.
|
|
128
|
-
"""
|
|
125
|
+
"""Creates a new data collection in the current visit."""
|
|
129
126
|
# https://github.com/DiamondLightSource/dodal/issues/452
|
|
130
127
|
# TODO: Allow selecting visit as part of a request
|
|
131
128
|
# TODO: DAQ-4827: Pass AuthN information as part of request
|
dodal/common/watcher_utils.py
CHANGED
|
@@ -55,22 +55,24 @@ def log_on_percentage_complete(
|
|
|
55
55
|
message_prefix: str,
|
|
56
56
|
percent_interval: int = 25,
|
|
57
57
|
):
|
|
58
|
-
"""
|
|
59
|
-
|
|
58
|
+
"""Add watcher to a WatchableAsyncStatus status which will periodically log a
|
|
59
|
+
message based on percentage completion.
|
|
60
60
|
|
|
61
61
|
Args:
|
|
62
|
-
status
|
|
63
|
-
|
|
64
|
-
message_prefix: The string at the start of each of the produced logging
|
|
65
|
-
|
|
66
|
-
percent_interval: How often to produce logging message, in
|
|
67
|
-
|
|
62
|
+
status (WatchableAsyncStatus): For example, Ophyd-async produces this status
|
|
63
|
+
from a Motor.set method.
|
|
64
|
+
message_prefix (str): The string at the start of each of the produced logging
|
|
65
|
+
messages
|
|
66
|
+
percent_interval (int, optional): How often to produce logging message, in
|
|
67
|
+
terms of percentage completion of the status.
|
|
68
68
|
|
|
69
69
|
Note that when using with Bluesky plan stubs you will need to cast the status (as of
|
|
70
|
-
Bluesky v1.14.2), since a Bluesky status doesn't use generics - see
|
|
70
|
+
Bluesky v1.14.2), since a Bluesky status doesn't use generics - see
|
|
71
|
+
https://github.com/bluesky/bluesky/issues/1948.
|
|
71
72
|
|
|
72
|
-
When running Bluesky plans using an interactive terminal, it is better to use the
|
|
73
|
-
bar instead of this function. See
|
|
73
|
+
When running Bluesky plans using an interactive terminal, it is better to use the
|
|
74
|
+
standard bluesky progress bar instead of this function. See
|
|
75
|
+
https://blueskyproject.io/bluesky/main/progress-bar.html#progress-bar.
|
|
74
76
|
|
|
75
77
|
Example usage within a Bluesky plan:
|
|
76
78
|
yield from bps.kickoff(my_detector)
|
|
@@ -78,6 +80,5 @@ def log_on_percentage_complete(
|
|
|
78
80
|
status = cast(WatchableAsyncStatus, status)
|
|
79
81
|
log_on_percentage_complete(status, "Data collection triggers received", 10)
|
|
80
82
|
yield from bps.wait("collection complete")
|
|
81
|
-
|
|
82
83
|
"""
|
|
83
84
|
_LogOnPercentageProgressWatcher(status, message_prefix, percent_interval)
|
dodal/device_manager.py
CHANGED
|
@@ -4,7 +4,7 @@ import typing
|
|
|
4
4
|
from collections import UserDict
|
|
5
5
|
from collections.abc import Callable, Iterable, Mapping, MutableMapping
|
|
6
6
|
from functools import cached_property, wraps
|
|
7
|
-
from inspect import Parameter
|
|
7
|
+
from inspect import Parameter, cleandoc
|
|
8
8
|
from types import NoneType
|
|
9
9
|
from typing import (
|
|
10
10
|
Annotated,
|
|
@@ -17,14 +17,10 @@ from typing import (
|
|
|
17
17
|
TypeVar,
|
|
18
18
|
)
|
|
19
19
|
|
|
20
|
-
from bluesky.run_engine import
|
|
21
|
-
get_bluesky_event_loop,
|
|
22
|
-
)
|
|
20
|
+
from bluesky.run_engine import get_bluesky_event_loop
|
|
23
21
|
from ophyd.sim import make_fake_device
|
|
24
22
|
|
|
25
|
-
from dodal.common.beamlines.beamline_utils import
|
|
26
|
-
wait_for_connection,
|
|
27
|
-
)
|
|
23
|
+
from dodal.common.beamlines.beamline_utils import wait_for_connection
|
|
28
24
|
from dodal.utils import (
|
|
29
25
|
AnyDevice,
|
|
30
26
|
OphydV1Device,
|
|
@@ -33,6 +29,7 @@ from dodal.utils import (
|
|
|
33
29
|
)
|
|
34
30
|
|
|
35
31
|
DEFAULT_TIMEOUT = 30
|
|
32
|
+
NO_DOCS = "No documentation available."
|
|
36
33
|
|
|
37
34
|
T = TypeVar("T")
|
|
38
35
|
Args = ParamSpec("Args")
|
|
@@ -44,12 +41,11 @@ DeviceFactoryDecorator = Callable[[Callable[Args, V2]], "DeviceFactory[Args, V2]
|
|
|
44
41
|
OphydInitialiser = Callable[Concatenate[V1, Args], V1 | None]
|
|
45
42
|
|
|
46
43
|
_EMPTY = object()
|
|
47
|
-
"""Sentinel value to distinguish between missing values and present but null values"""
|
|
44
|
+
"""Sentinel value to distinguish between missing values and present but null values."""
|
|
48
45
|
|
|
49
46
|
|
|
50
47
|
class LazyFixtures(UserDict[str, Any]):
|
|
51
|
-
"""
|
|
52
|
-
Wrapper around fixtures and fixture generators
|
|
48
|
+
"""Wrapper around fixtures and fixture generators.
|
|
53
49
|
|
|
54
50
|
If a fixture is provided at runtime, the generator function does not have to be called.
|
|
55
51
|
"""
|
|
@@ -71,11 +67,9 @@ class LazyFixtures(UserDict[str, Any]):
|
|
|
71
67
|
|
|
72
68
|
|
|
73
69
|
class DeviceFactory(Generic[Args, V2]):
|
|
74
|
-
"""
|
|
75
|
-
Wrapper around a device factory (any function returning a device) that holds
|
|
70
|
+
"""Wrapper around a device factory (any function returning a device) that holds
|
|
76
71
|
a reference to a device manager that can provide dependencies, along with
|
|
77
|
-
default connection information for how the created device should be
|
|
78
|
-
connected.
|
|
72
|
+
default connection information for how the created device should be connected.
|
|
79
73
|
"""
|
|
80
74
|
|
|
81
75
|
def __init__(
|
|
@@ -103,14 +97,17 @@ class DeviceFactory(Generic[Args, V2]):
|
|
|
103
97
|
self._manager = manager
|
|
104
98
|
wraps(factory)(self)
|
|
105
99
|
|
|
100
|
+
return_type = inspect.get_annotations(factory).get("return")
|
|
101
|
+
self.__doc__ = _format_doc(self, return_type)
|
|
102
|
+
|
|
106
103
|
@property
|
|
107
104
|
def name(self) -> str:
|
|
108
|
-
"""Name of the underlying factory function"""
|
|
105
|
+
"""Name of the underlying factory function."""
|
|
109
106
|
return self.factory.__name__
|
|
110
107
|
|
|
111
108
|
@cached_property
|
|
112
109
|
def dependencies(self) -> set[str]:
|
|
113
|
-
"""Names of all parameters"""
|
|
110
|
+
"""Names of all parameters."""
|
|
114
111
|
sig = inspect.signature(self.factory)
|
|
115
112
|
return {
|
|
116
113
|
para.name
|
|
@@ -120,7 +117,7 @@ class DeviceFactory(Generic[Args, V2]):
|
|
|
120
117
|
|
|
121
118
|
@cached_property
|
|
122
119
|
def optional_dependencies(self) -> set[str]:
|
|
123
|
-
"""Names of optional dependencies"""
|
|
120
|
+
"""Names of optional dependencies."""
|
|
124
121
|
sig = inspect.signature(self.factory)
|
|
125
122
|
return {
|
|
126
123
|
para.name
|
|
@@ -131,9 +128,8 @@ class DeviceFactory(Generic[Args, V2]):
|
|
|
131
128
|
|
|
132
129
|
@property
|
|
133
130
|
def skip(self) -> bool:
|
|
134
|
-
"""
|
|
135
|
-
|
|
136
|
-
still be built if a required device depends on it
|
|
131
|
+
"""Whether this device should be skipped as part of build_all - it will
|
|
132
|
+
still be built if a required device depends on it.
|
|
137
133
|
"""
|
|
138
134
|
return self._skip() if callable(self._skip) else self._skip
|
|
139
135
|
|
|
@@ -145,7 +141,7 @@ class DeviceFactory(Generic[Args, V2]):
|
|
|
145
141
|
timeout: float | None = None,
|
|
146
142
|
**fixtures,
|
|
147
143
|
) -> V2:
|
|
148
|
-
"""Build this device, building any dependencies first"""
|
|
144
|
+
"""Build this device, building any dependencies first."""
|
|
149
145
|
devices = self._manager.build_devices(
|
|
150
146
|
self,
|
|
151
147
|
fixtures=fixtures,
|
|
@@ -175,8 +171,7 @@ class DeviceFactory(Generic[Args, V2]):
|
|
|
175
171
|
|
|
176
172
|
# TODO: Remove when ophyd v1 support is no longer required - see #1718
|
|
177
173
|
class V1DeviceFactory(Generic[Args, V1]):
|
|
178
|
-
"""
|
|
179
|
-
Wrapper around an ophyd v1 device that holds a reference to a device
|
|
174
|
+
"""Wrapper around an ophyd v1 device that holds a reference to a device
|
|
180
175
|
manager that can provide dependencies, along with default connection
|
|
181
176
|
information for how the created device should be connected.
|
|
182
177
|
"""
|
|
@@ -202,15 +197,16 @@ class V1DeviceFactory(Generic[Args, V1]):
|
|
|
202
197
|
self.post_create = init or (lambda x: x)
|
|
203
198
|
self._manager = manager
|
|
204
199
|
wraps(init)(self)
|
|
200
|
+
self.__doc__ = _format_doc(self, factory)
|
|
205
201
|
|
|
206
202
|
@property
|
|
207
203
|
def name(self) -> str:
|
|
208
|
-
"""Name of the underlying factory function"""
|
|
204
|
+
"""Name of the underlying factory function."""
|
|
209
205
|
return self.post_create.__name__
|
|
210
206
|
|
|
211
207
|
@cached_property
|
|
212
208
|
def dependencies(self) -> set[str]:
|
|
213
|
-
"""Names of all parameters"""
|
|
209
|
+
"""Names of all parameters."""
|
|
214
210
|
sig = inspect.signature(self.post_create)
|
|
215
211
|
# first parameter should be the device we've just built
|
|
216
212
|
_, *params = sig.parameters.values()
|
|
@@ -218,16 +214,15 @@ class V1DeviceFactory(Generic[Args, V1]):
|
|
|
218
214
|
|
|
219
215
|
@cached_property
|
|
220
216
|
def optional_dependencies(self) -> set[str]:
|
|
221
|
-
"""Names of optional dependencies"""
|
|
217
|
+
"""Names of optional dependencies."""
|
|
222
218
|
sig = inspect.signature(self.post_create)
|
|
223
219
|
_, *params = sig.parameters.values()
|
|
224
220
|
return {para.name for para in params if para.default is not Parameter.empty}
|
|
225
221
|
|
|
226
222
|
@property
|
|
227
223
|
def skip(self) -> bool:
|
|
228
|
-
"""
|
|
229
|
-
|
|
230
|
-
still be built if a required device depends on it
|
|
224
|
+
"""Whether this device should be skipped as part of build_all - it will
|
|
225
|
+
still be built if a required device depends on it.
|
|
231
226
|
"""
|
|
232
227
|
return self._skip() if callable(self._skip) else self._skip
|
|
233
228
|
|
|
@@ -248,7 +243,7 @@ class V1DeviceFactory(Generic[Args, V1]):
|
|
|
248
243
|
)
|
|
249
244
|
|
|
250
245
|
def __call__(self, dev: V1, *args: Args.args, **kwargs: Args.kwargs):
|
|
251
|
-
"""Call the wrapped function to make decorator transparent"""
|
|
246
|
+
"""Call the wrapped function to make decorator transparent."""
|
|
252
247
|
return self.post_create(dev, *args, **kwargs)
|
|
253
248
|
|
|
254
249
|
def create(self, *args: Args.args, **kwargs: Args.kwargs) -> V1:
|
|
@@ -259,7 +254,7 @@ class V1DeviceFactory(Generic[Args, V1]):
|
|
|
259
254
|
return device
|
|
260
255
|
|
|
261
256
|
def build(self, mock: bool = False, fixtures: dict[str, Any] | None = None) -> V1:
|
|
262
|
-
"""Build this device, building any dependencies first"""
|
|
257
|
+
"""Build this device, building any dependencies first."""
|
|
263
258
|
devices = self._manager.build_devices(
|
|
264
259
|
self,
|
|
265
260
|
fixtures=fixtures,
|
|
@@ -274,21 +269,21 @@ class V1DeviceFactory(Generic[Args, V1]):
|
|
|
274
269
|
|
|
275
270
|
|
|
276
271
|
class ConnectionSpec(NamedTuple):
|
|
277
|
-
"""The options used to configure a device"""
|
|
272
|
+
"""The options used to configure a device."""
|
|
278
273
|
|
|
279
274
|
mock: bool
|
|
280
275
|
timeout: float
|
|
281
276
|
|
|
282
277
|
|
|
283
278
|
class ConnectionResult(NamedTuple):
|
|
284
|
-
"""Wrapper around results of building and connecting devices"""
|
|
279
|
+
"""Wrapper around results of building and connecting devices."""
|
|
285
280
|
|
|
286
281
|
devices: dict[str, AnyDevice]
|
|
287
282
|
build_errors: dict[str, Exception]
|
|
288
283
|
connection_errors: dict[str, Exception]
|
|
289
284
|
|
|
290
285
|
def or_raise(self) -> dict[str, Any]:
|
|
291
|
-
"""Re-raise any errors from build or connect stage or return devices"""
|
|
286
|
+
"""Re-raise any errors from build or connect stage or return devices."""
|
|
292
287
|
if self.build_errors or self.connection_errors:
|
|
293
288
|
all_exc = []
|
|
294
289
|
for name, exc in (self.build_errors | self.connection_errors).items():
|
|
@@ -299,14 +294,14 @@ class ConnectionResult(NamedTuple):
|
|
|
299
294
|
|
|
300
295
|
|
|
301
296
|
class DeviceBuildResult(NamedTuple):
|
|
302
|
-
"""Wrapper around the results of building devices"""
|
|
297
|
+
"""Wrapper around the results of building devices."""
|
|
303
298
|
|
|
304
299
|
devices: dict[str, AnyDevice]
|
|
305
300
|
errors: dict[str, Exception]
|
|
306
301
|
connection_specs: dict[str, ConnectionSpec]
|
|
307
302
|
|
|
308
303
|
def connect(self, timeout: float | None = None) -> ConnectionResult:
|
|
309
|
-
"""Connect all devices that didn't fail to build"""
|
|
304
|
+
"""Connect all devices that didn't fail to build."""
|
|
310
305
|
connections = {}
|
|
311
306
|
connected = {}
|
|
312
307
|
loop: asyncio.EventLoop = get_bluesky_event_loop() # type: ignore
|
|
@@ -335,7 +330,7 @@ class DeviceBuildResult(NamedTuple):
|
|
|
335
330
|
return ConnectionResult(connected, self.errors, connection_errors)
|
|
336
331
|
|
|
337
332
|
def or_raise(self) -> Self:
|
|
338
|
-
"""Re-raise any build errors"""
|
|
333
|
+
"""Re-raise any build errors."""
|
|
339
334
|
if self.errors:
|
|
340
335
|
for name, exc in self.errors.items():
|
|
341
336
|
exc.add_note(name)
|
|
@@ -344,7 +339,7 @@ class DeviceBuildResult(NamedTuple):
|
|
|
344
339
|
|
|
345
340
|
|
|
346
341
|
class DeviceManager:
|
|
347
|
-
"""Manager to handle building and connecting interdependent devices"""
|
|
342
|
+
"""Manager to handle building and connecting interdependent devices."""
|
|
348
343
|
|
|
349
344
|
_factories: dict[str, DeviceFactory]
|
|
350
345
|
_fixtures: dict[str, Callable[[], Any]]
|
|
@@ -355,11 +350,44 @@ class DeviceManager:
|
|
|
355
350
|
self._v1_factories = {}
|
|
356
351
|
self._fixtures = {}
|
|
357
352
|
|
|
353
|
+
def get_all_factories(self) -> dict[str, V1DeviceFactory | DeviceFactory]:
|
|
354
|
+
return self._factories | self._v1_factories
|
|
355
|
+
|
|
358
356
|
def fixture(self, func: Callable[[], T]) -> Callable[[], T]:
|
|
359
|
-
"""Add a function that can provide fixtures required by the factories"""
|
|
357
|
+
"""Add a function that can provide fixtures required by the factories."""
|
|
360
358
|
self._fixtures[func.__name__] = func
|
|
361
359
|
return func
|
|
362
360
|
|
|
361
|
+
def include(self, other: "DeviceManager"):
|
|
362
|
+
"""Merge an external DeviceManager into this one.
|
|
363
|
+
|
|
364
|
+
Registered devices from the included DeviceManager will be included
|
|
365
|
+
when all devices are built and will be available as dependencies when
|
|
366
|
+
building specific devices from this manager. Names of devices still
|
|
367
|
+
need to be unique and an error will be raised if an included device
|
|
368
|
+
shares a name with any existing device.
|
|
369
|
+
|
|
370
|
+
Fixtures are also included and will override any previously defined
|
|
371
|
+
fixtures in this manager. Fixtures defined after including the other
|
|
372
|
+
manager will take precedence over included ones.
|
|
373
|
+
"""
|
|
374
|
+
# Bug in pyright means type checking doesn't recognise 'DeviceManager'
|
|
375
|
+
# as this class and fails with private member access
|
|
376
|
+
common = self._factories.keys() & other._factories # noqa SLF001
|
|
377
|
+
common |= self._v1_factories.keys() & other._v1_factories # noqa SLF001
|
|
378
|
+
common |= self._factories.keys() & other._v1_factories # noqa SLF001
|
|
379
|
+
common |= self._v1_factories.keys() & other._factories # noqa SLF001
|
|
380
|
+
if common:
|
|
381
|
+
raise ValueError(
|
|
382
|
+
f"Duplicate factories in included device manager: {common}"
|
|
383
|
+
)
|
|
384
|
+
|
|
385
|
+
self._factories.update(other._factories) # noqa SLF001
|
|
386
|
+
self._v1_factories.update(other._v1_factories) # noqa SLF001
|
|
387
|
+
|
|
388
|
+
# duplicate fixtures are not checked as fixtures can be overridden
|
|
389
|
+
self._fixtures.update(other._fixtures) # noqa SLF001
|
|
390
|
+
|
|
363
391
|
def v1_init(
|
|
364
392
|
self,
|
|
365
393
|
factory: type[V1],
|
|
@@ -369,8 +397,7 @@ class DeviceManager:
|
|
|
369
397
|
wait: bool = True,
|
|
370
398
|
timeout: int = DEFAULT_TIMEOUT,
|
|
371
399
|
):
|
|
372
|
-
"""
|
|
373
|
-
Register an ophyd v1 device
|
|
400
|
+
"""Register an ophyd v1 device.
|
|
374
401
|
|
|
375
402
|
The function this decorates is an initialiser that takes a built device
|
|
376
403
|
and is not used to create the device.
|
|
@@ -429,6 +456,7 @@ class DeviceManager:
|
|
|
429
456
|
if func.__name__ in self:
|
|
430
457
|
raise ValueError(f"Duplicate factory name: {func.__name__}")
|
|
431
458
|
factory = DeviceFactory(func, use_factory_name, timeout, mock, skip, self)
|
|
459
|
+
|
|
432
460
|
self._factories[func.__name__] = factory
|
|
433
461
|
return factory
|
|
434
462
|
|
|
@@ -456,7 +484,7 @@ class DeviceManager:
|
|
|
456
484
|
return self.build_devices(
|
|
457
485
|
*(
|
|
458
486
|
f
|
|
459
|
-
for f in (self.
|
|
487
|
+
for f in (self.get_all_factories()).values()
|
|
460
488
|
# allow overriding skip but still allow fixtures to override devices
|
|
461
489
|
if (include_skipped or not f.skip)
|
|
462
490
|
),
|
|
@@ -470,11 +498,9 @@ class DeviceManager:
|
|
|
470
498
|
fixtures: Mapping[str, Any] | None = None,
|
|
471
499
|
mock: bool = False,
|
|
472
500
|
) -> DeviceBuildResult:
|
|
473
|
-
"""
|
|
474
|
-
Build the devices from the given factories, ensuring that any
|
|
501
|
+
"""Build the devices from the given factories, ensuring that any
|
|
475
502
|
dependencies are built first and passed to later factories as required.
|
|
476
503
|
"""
|
|
477
|
-
|
|
478
504
|
fixtures = LazyFixtures(provided=fixtures, factories=self._fixtures)
|
|
479
505
|
if common := fixtures.keys() & {f.name for f in factories}:
|
|
480
506
|
factories = tuple(f for f in factories if f.name not in common)
|
|
@@ -529,15 +555,14 @@ class DeviceManager:
|
|
|
529
555
|
factories: Iterable[DeviceFactory[..., V2] | V1DeviceFactory[..., V1]],
|
|
530
556
|
available_fixtures: Mapping[str, Any],
|
|
531
557
|
) -> set[str]:
|
|
532
|
-
"""
|
|
533
|
-
Determine full list of devices that are required to build the given devices.
|
|
558
|
+
"""Determine full list of devices that are required to build the given devices.
|
|
534
559
|
If a dependency is available via the fixtures, a matching device factory
|
|
535
560
|
will not be included unless explicitly requested allowing for devices to
|
|
536
561
|
be overridden.
|
|
537
562
|
|
|
538
|
-
|
|
539
|
-
If a required dependency is not available as either a device
|
|
540
|
-
|
|
563
|
+
Raises:
|
|
564
|
+
If a required dependency is not available as either a device factory or a
|
|
565
|
+
fixture, a ValueError is raised
|
|
541
566
|
"""
|
|
542
567
|
dependencies = set()
|
|
543
568
|
factories = set(factories)
|
|
@@ -561,14 +586,12 @@ class DeviceManager:
|
|
|
561
586
|
factories: dict[str, DeviceFactory[..., V2] | V1DeviceFactory[..., V1]],
|
|
562
587
|
fixtures: Mapping[str, Any],
|
|
563
588
|
) -> list[str]:
|
|
564
|
-
"""
|
|
565
|
-
|
|
566
|
-
that dependencies are built before the things that depend on them
|
|
589
|
+
"""Determine the order devices in which devices should be build to ensure
|
|
590
|
+
that dependencies are built before the things that depend on them.
|
|
567
591
|
|
|
568
592
|
Assumes that all required devices and fixtures are included in the
|
|
569
593
|
given factory list.
|
|
570
594
|
"""
|
|
571
|
-
|
|
572
595
|
# This is not an efficient way of doing this, however, for realistic use
|
|
573
596
|
# cases, it is fast enough for now
|
|
574
597
|
order = []
|
|
@@ -602,3 +625,20 @@ class DeviceManager:
|
|
|
602
625
|
|
|
603
626
|
def __repr__(self) -> str:
|
|
604
627
|
return f"<DeviceManager: {len(self)} devices>"
|
|
628
|
+
|
|
629
|
+
|
|
630
|
+
def _format_doc(
|
|
631
|
+
factory: DeviceFactory | V1DeviceFactory, return_type: type[V1 | V2] | None
|
|
632
|
+
) -> str | None:
|
|
633
|
+
"""Helper function to combine the doc strings of our factory instance and the
|
|
634
|
+
return type of the function we wrap.
|
|
635
|
+
"""
|
|
636
|
+
if not return_type:
|
|
637
|
+
return factory.__doc__
|
|
638
|
+
if existing := factory.__doc__:
|
|
639
|
+
return f"{existing}\n\n{_type_docs(return_type)}"
|
|
640
|
+
return _type_docs(return_type)
|
|
641
|
+
|
|
642
|
+
|
|
643
|
+
def _type_docs(target: type[V1 | V2]) -> str:
|
|
644
|
+
return f"{target.__name__}:\n\n{cleandoc(target.__doc__ or NO_DOCS)}"
|
|
@@ -24,7 +24,7 @@ class InvalidApertureMoveError(Exception):
|
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
class _GDAParamApertureValue(StrictEnum):
|
|
27
|
-
"""Maps from a short usable name to the value name in the GDA Beamline parameters"""
|
|
27
|
+
"""Maps from a short usable name to the value name in the GDA Beamline parameters."""
|
|
28
28
|
|
|
29
29
|
ROBOT_LOAD = "ROBOT_LOAD"
|
|
30
30
|
SMALL = "SMALL_APERTURE"
|
|
@@ -34,16 +34,16 @@ class _GDAParamApertureValue(StrictEnum):
|
|
|
34
34
|
|
|
35
35
|
|
|
36
36
|
class AperturePosition(BaseModel):
|
|
37
|
-
"""
|
|
38
|
-
|
|
37
|
+
"""Represents one of the available positions for the Aperture-Scatterguard.
|
|
38
|
+
|
|
39
39
|
Attributes:
|
|
40
|
-
aperture_x: The x position of the aperture component in mm
|
|
41
|
-
aperture_y: The y position of the aperture component in mm
|
|
42
|
-
aperture_z: The z position of the aperture component in mm
|
|
43
|
-
scatterguard_x: The x position of the scatterguard component in mm
|
|
44
|
-
scatterguard_y: The y position of the scatterguard component in mm
|
|
45
|
-
|
|
46
|
-
|
|
40
|
+
aperture_x (float): The x position of the aperture component in mm.
|
|
41
|
+
aperture_y (float): The y position of the aperture component in mm.
|
|
42
|
+
aperture_z (float): The z position of the aperture component in mm.
|
|
43
|
+
scatterguard_x (float): The x position of the scatterguard component in mm.
|
|
44
|
+
scatterguard_y (float): The y position of the scatterguard component in mm.
|
|
45
|
+
diameter (float, optional): Diameter of the selected aperture. When in the Robot
|
|
46
|
+
Load position, the diameter is defined to be 0.
|
|
47
47
|
"""
|
|
48
48
|
|
|
49
49
|
aperture_x: float
|
|
@@ -51,7 +51,7 @@ class AperturePosition(BaseModel):
|
|
|
51
51
|
aperture_z: float
|
|
52
52
|
scatterguard_x: float
|
|
53
53
|
scatterguard_y: float
|
|
54
|
-
|
|
54
|
+
diameter: float = Field(json_schema_extra={"units": "µm"}, default=0.0)
|
|
55
55
|
|
|
56
56
|
@property
|
|
57
57
|
def values(self) -> tuple[float, float, float, float, float]:
|
|
@@ -78,7 +78,7 @@ class AperturePosition(BaseModel):
|
|
|
78
78
|
@staticmethod
|
|
79
79
|
def from_gda_params(
|
|
80
80
|
name: _GDAParamApertureValue,
|
|
81
|
-
|
|
81
|
+
diameter: float,
|
|
82
82
|
params: GDABeamlineParameters,
|
|
83
83
|
) -> AperturePosition:
|
|
84
84
|
return AperturePosition(
|
|
@@ -87,7 +87,7 @@ class AperturePosition(BaseModel):
|
|
|
87
87
|
aperture_z=params[f"miniap_z_{name.value}"],
|
|
88
88
|
scatterguard_x=params[f"sg_x_{name.value}"],
|
|
89
89
|
scatterguard_y=params[f"sg_y_{name.value}"],
|
|
90
|
-
|
|
90
|
+
diameter=diameter,
|
|
91
91
|
)
|
|
92
92
|
|
|
93
93
|
|
|
@@ -95,7 +95,7 @@ class ApertureValue(StrictEnum):
|
|
|
95
95
|
"""The possible apertures that can be selected.
|
|
96
96
|
|
|
97
97
|
Changing these means changing the external parameter model of Hyperion.
|
|
98
|
-
See https://github.com/DiamondLightSource/mx-bluesky/issues/760
|
|
98
|
+
See https://github.com/DiamondLightSource/mx-bluesky/issues/760.
|
|
99
99
|
"""
|
|
100
100
|
|
|
101
101
|
SMALL = "SMALL_APERTURE"
|
|
@@ -139,8 +139,8 @@ class ApertureScatterguard(StandardReadable, Preparable):
|
|
|
139
139
|
|
|
140
140
|
await aperture_scatterguard.selected_aperture.set(ApertureValue.LARGE)
|
|
141
141
|
|
|
142
|
-
This will move the assembly so that the large aperture is in the beam,
|
|
143
|
-
of where the assembly currently is.
|
|
142
|
+
This will move the assembly so that the large aperture is in the beam,
|
|
143
|
+
regardless of where the assembly currently is.
|
|
144
144
|
|
|
145
145
|
We may also want to move the assembly out of the beam with::
|
|
146
146
|
|
|
@@ -149,8 +149,8 @@ class ApertureScatterguard(StandardReadable, Preparable):
|
|
|
149
149
|
Note, to make sure we do this as quickly as possible, the scatterguard will stay
|
|
150
150
|
in the same position relative to the aperture.
|
|
151
151
|
|
|
152
|
-
We may then want to keep the assembly out of the beam whilst asynchronously
|
|
153
|
-
the other axes for the aperture that's to follow::
|
|
152
|
+
We may then want to keep the assembly out of the beam whilst asynchronously
|
|
153
|
+
preparing the other axes for the aperture that's to follow::
|
|
154
154
|
|
|
155
155
|
await aperture_scatterguard.prepare(ApertureValue.LARGE)
|
|
156
156
|
|
|
@@ -185,8 +185,8 @@ class ApertureScatterguard(StandardReadable, Preparable):
|
|
|
185
185
|
current_ap_z=self.aperture.z.user_readback,
|
|
186
186
|
)
|
|
187
187
|
|
|
188
|
-
self.
|
|
189
|
-
self.
|
|
188
|
+
self.diameter = derived_signal_r(
|
|
189
|
+
self._get_current_diameter,
|
|
190
190
|
current_aperture=self.selected_aperture,
|
|
191
191
|
derived_units="µm",
|
|
192
192
|
)
|
|
@@ -198,7 +198,7 @@ class ApertureScatterguard(StandardReadable, Preparable):
|
|
|
198
198
|
self.aperture.z.user_readback,
|
|
199
199
|
self.scatterguard.x.user_readback,
|
|
200
200
|
self.scatterguard.y.user_readback,
|
|
201
|
-
self.
|
|
201
|
+
self.diameter,
|
|
202
202
|
],
|
|
203
203
|
)
|
|
204
204
|
|
|
@@ -262,8 +262,8 @@ class ApertureScatterguard(StandardReadable, Preparable):
|
|
|
262
262
|
"triggering another move."
|
|
263
263
|
)
|
|
264
264
|
|
|
265
|
-
def
|
|
266
|
-
return self._loaded_positions[current_aperture].
|
|
265
|
+
def _get_current_diameter(self, current_aperture: ApertureValue) -> float:
|
|
266
|
+
return self._loaded_positions[current_aperture].diameter
|
|
267
267
|
|
|
268
268
|
def _is_in_position(
|
|
269
269
|
self, position: ApertureValue, current_ap_y: float, current_ap_z: float
|
|
@@ -300,11 +300,10 @@ class ApertureScatterguard(StandardReadable, Preparable):
|
|
|
300
300
|
)
|
|
301
301
|
|
|
302
302
|
async def _safe_move_whilst_in_beam(self, position: AperturePosition):
|
|
303
|
-
"""
|
|
304
|
-
Move the aperture and scatterguard combo safely to a new position.
|
|
303
|
+
"""Move the aperture and scatterguard combo safely to a new position.
|
|
305
304
|
See https://github.com/DiamondLightSource/hyperion/wiki/Aperture-Scatterguard-Collisions
|
|
306
|
-
for why this is required. TLDR is that we have a collision at the top of y so we
|
|
307
|
-
to make sure we move the assembly down before we move the scatterguard up.
|
|
305
|
+
for why this is required. TLDR is that we have a collision at the top of y so we
|
|
306
|
+
need to make sure we move the assembly down before we move the scatterguard up.
|
|
308
307
|
"""
|
|
309
308
|
current_ap_y = await self.aperture.y.user_readback.get_value()
|
|
310
309
|
|
|
@@ -3,9 +3,7 @@ from ophyd_async.epics.core import epics_signal_r, epics_signal_rw
|
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
class ColorMode(StrictEnum):
|
|
6
|
-
"""
|
|
7
|
-
Enum to store the various color modes of the camera. We use RGB1.
|
|
8
|
-
"""
|
|
6
|
+
"""Enum to store the various color modes of the camera. We use RGB1."""
|
|
9
7
|
|
|
10
8
|
MONO = "Mono"
|
|
11
9
|
BAYER = "Bayer"
|