dls-dodal 1.68.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.68.0.dist-info → dls_dodal-2.0.0.dist-info}/METADATA +1 -31
- dls_dodal-2.0.0.dist-info/RECORD +354 -0
- {dls_dodal-1.68.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 +40 -33
- 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 +17 -45
- dodal/beamlines/i02_2.py +6 -12
- dodal/beamlines/i03.py +8 -5
- dodal/beamlines/i03_supervisor.py +19 -0
- dodal/beamlines/i04.py +87 -184
- dodal/beamlines/i05.py +9 -39
- dodal/beamlines/i05_1.py +4 -13
- dodal/beamlines/i05_shared.py +51 -0
- dodal/beamlines/i06_1.py +26 -0
- dodal/beamlines/{i06.py → i06_shared.py} +25 -14
- dodal/beamlines/i07.py +14 -16
- dodal/beamlines/i09.py +76 -29
- dodal/beamlines/i09_1.py +25 -56
- 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/i16.py +11 -0
- 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 +17 -11
- dodal/beamlines/i21.py +44 -29
- dodal/beamlines/i22.py +19 -4
- dodal/beamlines/i23.py +20 -27
- dodal/beamlines/i24.py +64 -113
- dodal/beamlines/k07.py +99 -5
- dodal/beamlines/p38.py +3 -3
- dodal/beamlines/p60.py +35 -14
- dodal/beamlines/p99.py +16 -15
- dodal/beamlines/training_rig.py +20 -12
- dodal/cli.py +36 -2
- dodal/common/__init__.py +2 -1
- 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 +87 -19
- 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/beamlines/i04/max_pixel.py +25 -0
- dodal/devices/{i04 → beamlines/i04}/murko_results.py +23 -8
- 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 +37 -51
- 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 +16 -22
- 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 +12 -12
- 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 +32 -17
- dodal/devices/eiger_odin.py +1 -1
- dodal/devices/electron_analyser/base/__init__.py +3 -3
- dodal/devices/electron_analyser/base/base_controller.py +32 -21
- 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_enums.py +0 -5
- dodal/devices/electron_analyser/base/base_region.py +29 -31
- dodal/devices/electron_analyser/base/base_util.py +18 -16
- dodal/devices/electron_analyser/base/energy_sources.py +35 -40
- dodal/devices/electron_analyser/specs/specs_detector.py +7 -6
- dodal/devices/electron_analyser/vgscienta/vgscienta_detector.py +7 -6
- dodal/devices/eurotherm.py +3 -2
- dodal/devices/fast_grid_scan.py +31 -34
- dodal/devices/fast_shutter.py +125 -39
- 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 +20 -8
- dodal/devices/insertion_device/apple2_controller.py +371 -0
- dodal/devices/insertion_device/apple2_undulator.py +184 -587
- dodal/devices/insertion_device/apple_knot_controller.py +222 -0
- dodal/devices/insertion_device/energy.py +161 -0
- dodal/devices/insertion_device/energy_motor_lookup.py +21 -28
- dodal/devices/insertion_device/lookup_table_models.py +47 -52
- dodal/devices/insertion_device/polarisation.py +36 -0
- 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 +92 -22
- 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 +20 -6
- 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 +45 -20
- dodal/devices/s4_slit_gaps.py +1 -1
- dodal/devices/selectable_source.py +41 -0
- 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 +21 -7
- dodal/devices/zebra/zebra_constants_mapping.py +12 -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/configure_arm_trigger_and_disarm_detector.py +0 -1
- 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/__init__.py +0 -0
- 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.68.0.dist-info/RECORD +0 -330
- dodal/beamline_specific_utils/i05_shared.py +0 -14
- dodal/devices/b07/__init__.py +0 -3
- dodal/devices/i04/max_pixel.py +0 -38
- dodal/devices/i05/__init__.py +0 -3
- dodal/devices/i09/__init__.py +0 -3
- dodal/devices/i21/__init__.py +0 -5
- {dls_dodal-1.68.0.dist-info → dls_dodal-2.0.0.dist-info}/entry_points.txt +0 -0
- {dls_dodal-1.68.0.dist-info → dls_dodal-2.0.0.dist-info}/licenses/LICENSE +0 -0
- {dls_dodal-1.68.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/{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/devices/insertion_device/{id_enum.py → enum.py} +0 -0
|
@@ -15,7 +15,7 @@ from .base_driver_io import (
|
|
|
15
15
|
GenericAnalyserDriverIO,
|
|
16
16
|
TAbstractAnalyserDriverIO,
|
|
17
17
|
)
|
|
18
|
-
from .base_enums import EnergyMode
|
|
18
|
+
from .base_enums import EnergyMode
|
|
19
19
|
from .base_region import (
|
|
20
20
|
AbstractBaseRegion,
|
|
21
21
|
AbstractBaseSequence,
|
|
@@ -27,7 +27,7 @@ from .base_region import (
|
|
|
27
27
|
TLensMode,
|
|
28
28
|
)
|
|
29
29
|
from .base_util import to_binding_energy, to_kinetic_energy
|
|
30
|
-
from .energy_sources import DualEnergySource, EnergySource
|
|
30
|
+
from .energy_sources import AbstractEnergySource, DualEnergySource, EnergySource
|
|
31
31
|
|
|
32
32
|
__all__ = [
|
|
33
33
|
"ElectronAnalyserController",
|
|
@@ -42,7 +42,6 @@ __all__ = [
|
|
|
42
42
|
"GenericAnalyserDriverIO",
|
|
43
43
|
"TAbstractAnalyserDriverIO",
|
|
44
44
|
"EnergyMode",
|
|
45
|
-
"SelectedSource",
|
|
46
45
|
"AbstractBaseRegion",
|
|
47
46
|
"AbstractBaseSequence",
|
|
48
47
|
"GenericRegion",
|
|
@@ -53,6 +52,7 @@ __all__ = [
|
|
|
53
52
|
"TLensMode",
|
|
54
53
|
"to_binding_energy",
|
|
55
54
|
"to_kinetic_energy",
|
|
55
|
+
"AbstractEnergySource",
|
|
56
56
|
"DualEnergySource",
|
|
57
57
|
"EnergySource",
|
|
58
58
|
]
|
|
@@ -12,46 +12,53 @@ from dodal.devices.electron_analyser.base.base_region import (
|
|
|
12
12
|
GenericRegion,
|
|
13
13
|
TAbstractBaseRegion,
|
|
14
14
|
)
|
|
15
|
-
from dodal.devices.electron_analyser.base.energy_sources import
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
)
|
|
15
|
+
from dodal.devices.electron_analyser.base.energy_sources import AbstractEnergySource
|
|
16
|
+
from dodal.devices.fast_shutter import FastShutter
|
|
17
|
+
from dodal.devices.selectable_source import SourceSelector
|
|
19
18
|
|
|
20
19
|
|
|
21
20
|
class ElectronAnalyserController(
|
|
22
21
|
ConstantDeadTimeController[TAbstractAnalyserDriverIO],
|
|
23
22
|
Generic[TAbstractAnalyserDriverIO, TAbstractBaseRegion],
|
|
24
23
|
):
|
|
25
|
-
"""
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
24
|
+
"""Specialised controller for the electron analysers to provide additional setup
|
|
25
|
+
logic such as selecting the energy source to use from requested region and giving
|
|
26
|
+
the driver the correct region parameters.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
driver (TAbstractAnalyserDriverIO): The electron analyser driver to wrap
|
|
30
|
+
around that holds the PV's.
|
|
31
|
+
energy_source (AbstractEnergySource): Device that holds the excitation
|
|
32
|
+
energy and ability to switch between sources.
|
|
33
|
+
deadtime (float, optional): For a given exposure, what is the safest minimum
|
|
34
|
+
time between exposures that can be determined without reading signals.
|
|
35
|
+
image_mode (ADImageMode, optional): The image mode to configure the driver
|
|
36
|
+
with before measuring.
|
|
29
37
|
"""
|
|
30
38
|
|
|
31
39
|
def __init__(
|
|
32
40
|
self,
|
|
33
41
|
driver: TAbstractAnalyserDriverIO,
|
|
34
42
|
energy_source: AbstractEnergySource,
|
|
35
|
-
|
|
43
|
+
shutter: FastShutter | None = None,
|
|
44
|
+
source_selector: SourceSelector | None = None,
|
|
45
|
+
deadtime: float = 0,
|
|
36
46
|
image_mode: ADImageMode = ADImageMode.SINGLE,
|
|
37
47
|
):
|
|
38
|
-
"""
|
|
39
|
-
Parameters:
|
|
40
|
-
driver: The electron analyser driver to wrap around that holds the PV's.
|
|
41
|
-
energy_source: Device that holds the excitation energy and ability to switch
|
|
42
|
-
between sources.
|
|
43
|
-
deadtime: For a given exposure, what is the safest minimum time between
|
|
44
|
-
exposures that can be determined without reading signals.
|
|
45
|
-
image_mode: The image mode to configure the driver with before measuring.
|
|
46
|
-
"""
|
|
47
48
|
self.energy_source = energy_source
|
|
49
|
+
self.shutter = shutter
|
|
50
|
+
self.source_selector = source_selector
|
|
48
51
|
super().__init__(driver, deadtime, image_mode)
|
|
49
52
|
|
|
50
|
-
async def setup_with_region(self, region: TAbstractBaseRegion):
|
|
53
|
+
async def setup_with_region(self, region: TAbstractBaseRegion) -> None:
|
|
51
54
|
"""Logic to set the driver with a region."""
|
|
55
|
+
if self.source_selector is not None:
|
|
56
|
+
await self.source_selector.set(region.excitation_energy_source)
|
|
57
|
+
|
|
58
|
+
# Should this be moved to a VGScientController only?
|
|
59
|
+
if self.shutter is not None:
|
|
60
|
+
await self.shutter.set(self.shutter.close_state)
|
|
52
61
|
|
|
53
|
-
if isinstance(self.energy_source, DualEnergySource):
|
|
54
|
-
self.energy_source.selected_source.set(region.excitation_energy_source)
|
|
55
62
|
excitation_energy = await self.energy_source.energy.get_value()
|
|
56
63
|
epics_region = region.prepare_for_epics(excitation_energy)
|
|
57
64
|
await self.driver.set(epics_region)
|
|
@@ -62,6 +69,10 @@ class ElectronAnalyserController(
|
|
|
62
69
|
# axis calculation.
|
|
63
70
|
excitation_energy = await self.energy_source.energy.get_value()
|
|
64
71
|
await self.driver.cached_excitation_energy.set(excitation_energy)
|
|
72
|
+
|
|
73
|
+
if self.shutter is not None:
|
|
74
|
+
await self.shutter.set(self.shutter.open_state)
|
|
75
|
+
|
|
65
76
|
await super().prepare(trigger_info)
|
|
66
77
|
|
|
67
78
|
|
|
@@ -33,9 +33,8 @@ class BaseElectronAnalyserDetector(
|
|
|
33
33
|
AsyncConfigurable,
|
|
34
34
|
Generic[TAbstractAnalyserDriverIO, TAbstractBaseRegion],
|
|
35
35
|
):
|
|
36
|
-
"""
|
|
37
|
-
|
|
38
|
-
already configured for the device.
|
|
36
|
+
"""Detector for data acquisition of electron analyser. Can only acquire using
|
|
37
|
+
settings already configured for the device.
|
|
39
38
|
|
|
40
39
|
If possible, this should be changed to inherit from a StandardDetector. Currently,
|
|
41
40
|
StandardDetector forces you to use a file writer which doesn't apply here.
|
|
@@ -90,9 +89,8 @@ class ElectronAnalyserRegionDetector(
|
|
|
90
89
|
BaseElectronAnalyserDetector[TAbstractAnalyserDriverIO, TAbstractBaseRegion],
|
|
91
90
|
Generic[TAbstractAnalyserDriverIO, TAbstractBaseRegion],
|
|
92
91
|
):
|
|
93
|
-
"""
|
|
94
|
-
|
|
95
|
-
acquisition. It is designed to only exist inside a plan.
|
|
92
|
+
"""Extends electron analyser detector to configure specific region settings before
|
|
93
|
+
data acquisition. It is designed to only exist inside a plan.
|
|
96
94
|
"""
|
|
97
95
|
|
|
98
96
|
def __init__(
|
|
@@ -127,10 +125,9 @@ class ElectronAnalyserDetector(
|
|
|
127
125
|
Stageable,
|
|
128
126
|
Generic[TAbstractBaseSequence, TAbstractAnalyserDriverIO, TAbstractBaseRegion],
|
|
129
127
|
):
|
|
130
|
-
"""
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
setup configured region settings before data acquisition.
|
|
128
|
+
"""Electron analyser detector with the additional functionality to load a sequence
|
|
129
|
+
file and create a list of temporary ElectronAnalyserRegionDetector objects. These
|
|
130
|
+
will setup configured region settings before data acquisition.
|
|
134
131
|
"""
|
|
135
132
|
|
|
136
133
|
def __init__(
|
|
@@ -146,8 +143,7 @@ class ElectronAnalyserDetector(
|
|
|
146
143
|
|
|
147
144
|
@AsyncStatus.wrap
|
|
148
145
|
async def stage(self) -> None:
|
|
149
|
-
"""
|
|
150
|
-
Prepare the detector for use by ensuring it is idle and ready.
|
|
146
|
+
"""Prepare the detector for use by ensuring it is idle and ready.
|
|
151
147
|
|
|
152
148
|
This method asynchronously stages the detector by first disarming the controller
|
|
153
149
|
to ensure the detector is not actively acquiring data, then invokes the driver's
|
|
@@ -165,11 +161,10 @@ class ElectronAnalyserDetector(
|
|
|
165
161
|
await self._controller.disarm()
|
|
166
162
|
|
|
167
163
|
def load_sequence(self, filename: str) -> TAbstractBaseSequence:
|
|
168
|
-
"""
|
|
169
|
-
Load the sequence data from a provided json file into a sequence class.
|
|
164
|
+
"""Load the sequence data from a provided json file into a sequence class.
|
|
170
165
|
|
|
171
166
|
Args:
|
|
172
|
-
filename: Path to the sequence file containing the region data.
|
|
167
|
+
filename (str): Path to the sequence file containing the region data.
|
|
173
168
|
|
|
174
169
|
Returns:
|
|
175
170
|
Pydantic model representing the sequence file.
|
|
@@ -181,17 +176,17 @@ class ElectronAnalyserDetector(
|
|
|
181
176
|
) -> list[
|
|
182
177
|
ElectronAnalyserRegionDetector[TAbstractAnalyserDriverIO, TAbstractBaseRegion]
|
|
183
178
|
]:
|
|
184
|
-
"""
|
|
185
|
-
Create a list of detectors equal to the number of regions in a sequence file.
|
|
179
|
+
"""Create a list of detectors equal to the number of regions in a sequence file.
|
|
186
180
|
Each detector is responsible for setting up a specific region.
|
|
187
181
|
|
|
188
182
|
Args:
|
|
189
|
-
filename:
|
|
190
|
-
enabled_only: If true, only include the region if enabled
|
|
183
|
+
filename (str): Path to the sequence file containing the region data.
|
|
184
|
+
enabled_only (bool, optional): If true, only include the region if enabled
|
|
185
|
+
is True.
|
|
191
186
|
|
|
192
187
|
Returns:
|
|
193
188
|
List of ElectronAnalyserRegionDetector, equal to the number of regions in
|
|
194
|
-
|
|
189
|
+
the sequence file.
|
|
195
190
|
"""
|
|
196
191
|
seq = self.load_sequence(filename)
|
|
197
192
|
regions: list[TAbstractBaseRegion] = (
|
|
@@ -41,10 +41,24 @@ class AbstractAnalyserDriverIO(
|
|
|
41
41
|
Movable[TAbstractBaseRegion],
|
|
42
42
|
Generic[TAbstractBaseRegion, TAcquisitionMode, TLensMode, TPsuMode, TPassEnergy],
|
|
43
43
|
):
|
|
44
|
-
"""
|
|
45
|
-
Driver device that defines signals and readables that should be common to all
|
|
44
|
+
"""Driver device that defines signals and readables that should be common to all
|
|
46
45
|
electron analysers. Implementations of electron analyser devices should inherit
|
|
47
46
|
from this class and define additional specialised signals and methods.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
prefix (str): Base PV to connect to EPICS for this device.
|
|
50
|
+
acquisition_mode_type (type[TAcquisitionMode]): Enum that determines the
|
|
51
|
+
available acquisition modes for this device.
|
|
52
|
+
lens_mode_type (type[TLensMode]): Enum that determines the available lens
|
|
53
|
+
mode for this device.
|
|
54
|
+
psu_mode_type (type[TPsuMode]): Enum that determines the available psu modes
|
|
55
|
+
for this device.
|
|
56
|
+
pass_energy_type (type[TPassEnergy]): Can be enum or float, depending on
|
|
57
|
+
electron analyser model. If enum, it determines the available pass
|
|
58
|
+
energies for this device.
|
|
59
|
+
energy_source: Device that can give us the correct excitation energy (in eV)
|
|
60
|
+
and switch sources if applicable.
|
|
61
|
+
name (str, optional): Name of the device.
|
|
48
62
|
"""
|
|
49
63
|
|
|
50
64
|
def __init__(
|
|
@@ -56,24 +70,6 @@ class AbstractAnalyserDriverIO(
|
|
|
56
70
|
pass_energy_type: type[TPassEnergy],
|
|
57
71
|
name: str = "",
|
|
58
72
|
) -> None:
|
|
59
|
-
"""
|
|
60
|
-
Constructor method for setting up electron analyser.
|
|
61
|
-
|
|
62
|
-
Args:
|
|
63
|
-
prefix: Base PV to connect to EPICS for this device.
|
|
64
|
-
acquisition_mode_type: Enum that determines the available acquisition modes
|
|
65
|
-
for this device.
|
|
66
|
-
lens_mode_type: Enum that determines the available lens mode for this
|
|
67
|
-
device.
|
|
68
|
-
psu_mode_type: Enum that determines the available psu modes for this device.
|
|
69
|
-
pass_energy_type: Can be enum or float, depends on electron analyser model.
|
|
70
|
-
If enum, it determines the available pass energies for
|
|
71
|
-
this device.
|
|
72
|
-
energy_source: Device that can give us the correct excitation energy and
|
|
73
|
-
switch sources if applicable.
|
|
74
|
-
(in eV).
|
|
75
|
-
name: Name of the device.
|
|
76
|
-
"""
|
|
77
73
|
self.acquisition_mode_type = acquisition_mode_type
|
|
78
74
|
self.lens_mode_type = lens_mode_type
|
|
79
75
|
self.psu_mode_type = psu_mode_type
|
|
@@ -140,33 +136,31 @@ class AbstractAnalyserDriverIO(
|
|
|
140
136
|
@abstractmethod
|
|
141
137
|
@AsyncStatus.wrap
|
|
142
138
|
async def set(self, epics_region: TAbstractBaseRegion):
|
|
143
|
-
"""
|
|
144
|
-
|
|
145
|
-
is responsible for implementing this method correctly.
|
|
139
|
+
"""Move a group of signals defined in a region. Each implementation of this
|
|
140
|
+
class is responsible for implementing this method correctly.
|
|
146
141
|
|
|
147
142
|
Args:
|
|
148
|
-
|
|
143
|
+
epics_region (TAbstractBaseRegion): Contains the parameters to setup the
|
|
144
|
+
driver for a scan.
|
|
149
145
|
"""
|
|
150
146
|
|
|
151
147
|
@abstractmethod
|
|
152
148
|
def _create_angle_axis_signal(self, prefix: str) -> SignalR[Array1D[np.float64]]:
|
|
153
|
-
"""
|
|
154
|
-
The signal that defines the angle axis. Depends on analyser model.
|
|
149
|
+
"""The signal that defines the angle axis. Depends on analyser model.
|
|
155
150
|
|
|
156
151
|
Args:
|
|
157
|
-
prefix: PV string used for connecting to angle axis.
|
|
152
|
+
prefix (str): PV string used for connecting to angle axis.
|
|
158
153
|
|
|
159
154
|
Returns:
|
|
160
|
-
|
|
155
|
+
SignalR that can give us angle axis array data.
|
|
161
156
|
"""
|
|
162
157
|
|
|
163
158
|
@abstractmethod
|
|
164
159
|
def _create_energy_axis_signal(self, prefix: str) -> SignalR[Array1D[np.float64]]:
|
|
165
|
-
"""
|
|
166
|
-
The signal that defines the energy axis. Depends on analyser model.
|
|
160
|
+
"""The signal that defines the energy axis. Depends on analyser model.
|
|
167
161
|
|
|
168
162
|
Args:
|
|
169
|
-
prefix: PV string used for connecting to energy axis.
|
|
163
|
+
prefix (str): PV string used for connecting to energy axis.
|
|
170
164
|
|
|
171
165
|
Returns:
|
|
172
166
|
Signal that can give us energy axis array data.
|
|
@@ -178,16 +172,16 @@ class AbstractAnalyserDriverIO(
|
|
|
178
172
|
excitation_energy: float,
|
|
179
173
|
energy_mode: EnergyMode,
|
|
180
174
|
) -> Array1D[np.float64]:
|
|
181
|
-
"""
|
|
182
|
-
|
|
183
|
-
derived signal.
|
|
175
|
+
"""Calculate the binding energy axis to calibrate the spectra data. Function for
|
|
176
|
+
a derived signal.
|
|
184
177
|
|
|
185
178
|
Args:
|
|
186
|
-
energy_axis:
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
179
|
+
energy_axis (Array1D[np.float64]): Array data of the original energy_axis
|
|
180
|
+
from epics.
|
|
181
|
+
excitation_energy (float): The excitation energy value used for the scan of
|
|
182
|
+
this region.
|
|
183
|
+
energy_mode (EnergyMode): The energy_mode of the region that was used for
|
|
184
|
+
the scan of this region.
|
|
191
185
|
|
|
192
186
|
Returns:
|
|
193
187
|
Array that is the correct axis for the spectra data.
|
|
@@ -205,17 +199,16 @@ class AbstractAnalyserDriverIO(
|
|
|
205
199
|
def _calculate_total_time(
|
|
206
200
|
self, total_steps: int, step_time: float, iterations: int
|
|
207
201
|
) -> float:
|
|
208
|
-
"""
|
|
209
|
-
|
|
210
|
-
signal.
|
|
202
|
+
"""Calulcate the total time the scan takes for this region. Function for a
|
|
203
|
+
derived signal.
|
|
211
204
|
|
|
212
205
|
Args:
|
|
213
|
-
total_steps: Number of steps for the region.
|
|
214
|
-
step_time: Time for each step for the region.
|
|
215
|
-
iterations: The number of iterations the region collected data for.
|
|
206
|
+
total_steps (int): Number of steps for the region.
|
|
207
|
+
step_time (float): Time for each step for the region.
|
|
208
|
+
iterations (int): The number of iterations the region collected data for.
|
|
216
209
|
|
|
217
210
|
Returns:
|
|
218
|
-
Calculated total time in seconds.
|
|
211
|
+
Float: Calculated total time in seconds.
|
|
219
212
|
"""
|
|
220
213
|
return total_steps * step_time * iterations
|
|
221
214
|
|
|
@@ -6,11 +6,12 @@ from typing import Generic, Self, TypeAlias, TypeVar
|
|
|
6
6
|
from ophyd_async.core import StrictEnum, SupersetEnum
|
|
7
7
|
from pydantic import BaseModel, Field, model_validator
|
|
8
8
|
|
|
9
|
-
from dodal.devices.electron_analyser.base.base_enums import EnergyMode
|
|
9
|
+
from dodal.devices.electron_analyser.base.base_enums import EnergyMode
|
|
10
10
|
from dodal.devices.electron_analyser.base.base_util import (
|
|
11
11
|
to_binding_energy,
|
|
12
12
|
to_kinetic_energy,
|
|
13
13
|
)
|
|
14
|
+
from dodal.devices.selectable_source import SelectedSource
|
|
14
15
|
|
|
15
16
|
AnyAcqMode: TypeAlias = StrictEnum
|
|
16
17
|
AnyLensMode: TypeAlias = SupersetEnum | StrictEnum
|
|
@@ -27,11 +28,13 @@ TPsuMode = TypeVar("TPsuMode", bound=AnyPsuMode)
|
|
|
27
28
|
|
|
28
29
|
|
|
29
30
|
def java_to_python_case(java_str: str) -> str:
|
|
30
|
-
"""
|
|
31
|
-
|
|
31
|
+
"""Convert a camelCase Java-style string to a snake_case Python-style string.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
java_str (str): The Java-style camelCase string.
|
|
32
35
|
|
|
33
|
-
:
|
|
34
|
-
|
|
36
|
+
Returns:
|
|
37
|
+
str: The Python-style snake_case string.
|
|
35
38
|
"""
|
|
36
39
|
new_value = re.sub("(.)([A-Z][a-z]+)", r"\1_\2", java_str)
|
|
37
40
|
new_value = re.sub("([a-z0-9])([A-Z])", r"\1_\2", new_value).lower()
|
|
@@ -66,9 +69,8 @@ class AbstractBaseRegion(
|
|
|
66
69
|
JavaToPythonModel,
|
|
67
70
|
Generic[TAcquisitionMode, TLensMode, TPassEnergy],
|
|
68
71
|
):
|
|
69
|
-
"""
|
|
70
|
-
|
|
71
|
-
this to extend functionality. All energy units are assumed to be in eV.
|
|
72
|
+
"""Generic region model that holds the data. Specialised region models should
|
|
73
|
+
inherit this to extend functionality. All energy units are assumed to be in eV.
|
|
72
74
|
"""
|
|
73
75
|
|
|
74
76
|
name: str = "New_region"
|
|
@@ -88,15 +90,11 @@ class AbstractBaseRegion(
|
|
|
88
90
|
energy_mode: EnergyMode = EnergyMode.KINETIC
|
|
89
91
|
|
|
90
92
|
def is_binding_energy(self) -> bool:
|
|
91
|
-
"""
|
|
92
|
-
Returns true if the energy_mode is binding.
|
|
93
|
-
"""
|
|
93
|
+
"""Returns true if the energy_mode is binding."""
|
|
94
94
|
return self.energy_mode == EnergyMode.BINDING
|
|
95
95
|
|
|
96
96
|
def is_kinetic_energy(self) -> bool:
|
|
97
|
-
"""
|
|
98
|
-
Returns true if the energy_mode is kinetic.
|
|
99
|
-
"""
|
|
97
|
+
"""Returns true if the energy_mode is kinetic."""
|
|
100
98
|
return self.energy_mode == EnergyMode.KINETIC
|
|
101
99
|
|
|
102
100
|
def switch_energy_mode(
|
|
@@ -105,19 +103,18 @@ class AbstractBaseRegion(
|
|
|
105
103
|
excitation_energy: float,
|
|
106
104
|
copy: bool = True,
|
|
107
105
|
) -> Self:
|
|
108
|
-
"""
|
|
109
|
-
Get a region with a new energy mode: Kinetic or Binding.
|
|
106
|
+
"""Get a region with a new energy mode: Kinetic or Binding.
|
|
110
107
|
It caculates new values for low_energy, centre_energy, high_energy, via the
|
|
111
108
|
excitation enerrgy. It doesn't calculate anything if the region is already of
|
|
112
109
|
the same energy mode.
|
|
113
110
|
|
|
114
|
-
|
|
115
|
-
energy_mode: Mode you want to switch the region to.
|
|
116
|
-
excitation_energy: Energy conversion for low_energy, centre_energy,
|
|
117
|
-
|
|
118
|
-
copy: Defaults to True. If true, create a copy of this
|
|
119
|
-
|
|
120
|
-
|
|
111
|
+
Args:
|
|
112
|
+
energy_mode (EnergyMode): Mode you want to switch the region to.
|
|
113
|
+
excitation_energy (float): Energy conversion for low_energy, centre_energy,
|
|
114
|
+
and high_energy for new energy mode.
|
|
115
|
+
copy (bool, optional): Defaults to True. If true, create a copy of this
|
|
116
|
+
region to alter for the new energy_mode and return it. If False, alter
|
|
117
|
+
this region for the energy_mode and return it self.
|
|
121
118
|
|
|
122
119
|
Returns:
|
|
123
120
|
Region with selected energy mode and new calculated energy values.
|
|
@@ -146,14 +143,16 @@ class AbstractBaseRegion(
|
|
|
146
143
|
new values for low_energy, centre_energy, and high_energy while also preserving
|
|
147
144
|
the original energy mode e.g mode BINDING will stay as BINDING.
|
|
148
145
|
|
|
149
|
-
|
|
150
|
-
excitation_energy: Energy conversion for low_energy, centre_energy,
|
|
151
|
-
|
|
152
|
-
copy: Defaults to True. If true, create a copy of this
|
|
153
|
-
|
|
146
|
+
Args:
|
|
147
|
+
excitation_energy (float): Energy conversion for low_energy, centre_energy,
|
|
148
|
+
and high_energy for new energy mode.
|
|
149
|
+
copy (bool, optional): Defaults to True. If true, create a copy of this
|
|
150
|
+
region to alter to calculate new energy values to return. If false,
|
|
151
|
+
alter this region.
|
|
152
|
+
|
|
154
153
|
Returns:
|
|
155
154
|
Region with selected original energy mode and new calculated KINETIC energy
|
|
156
|
-
|
|
155
|
+
values for epics.
|
|
157
156
|
"""
|
|
158
157
|
original_energy_mode = self.energy_mode
|
|
159
158
|
r = self.switch_energy_mode(EnergyMode.KINETIC, excitation_energy, copy)
|
|
@@ -176,8 +175,7 @@ class AbstractBaseSequence(
|
|
|
176
175
|
JavaToPythonModel,
|
|
177
176
|
Generic[TAbstractBaseRegion],
|
|
178
177
|
):
|
|
179
|
-
"""
|
|
180
|
-
Generic sequence model that holds the list of region data. Specialised sequence
|
|
178
|
+
"""Generic sequence model that holds the list of region data. Specialised sequence
|
|
181
179
|
models should inherit this to extend functionality and define type of region to
|
|
182
180
|
hold.
|
|
183
181
|
"""
|
|
@@ -4,15 +4,16 @@ from dodal.devices.electron_analyser.base.base_enums import EnergyMode
|
|
|
4
4
|
def to_kinetic_energy(
|
|
5
5
|
value: float, value_mode: EnergyMode, excitation_energy: float
|
|
6
6
|
) -> float:
|
|
7
|
-
"""
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
value: The value to convert.
|
|
11
|
-
value_mode: Energy mode of the value. If it is already kinetic,
|
|
12
|
-
|
|
13
|
-
excitation_energy: Value to calculate the conversion.
|
|
7
|
+
"""Convert a value that is binding energy to kinetic energy.
|
|
8
|
+
|
|
9
|
+
Args:
|
|
10
|
+
value (float): The value to convert.
|
|
11
|
+
value_mode (EnergyMode): Energy mode of the value. If it is already kinetic,
|
|
12
|
+
return the same value. If it is binding, convert to kinetic.
|
|
13
|
+
excitation_energy (float): Value to calculate the conversion.
|
|
14
|
+
|
|
14
15
|
Returns:
|
|
15
|
-
Caluclated kinetic energy value
|
|
16
|
+
float: Caluclated kinetic energy value.
|
|
16
17
|
"""
|
|
17
18
|
return value if value_mode == EnergyMode.KINETIC else excitation_energy - value
|
|
18
19
|
|
|
@@ -20,14 +21,15 @@ def to_kinetic_energy(
|
|
|
20
21
|
def to_binding_energy(
|
|
21
22
|
value: float, value_mode: EnergyMode, excitation_energy: float
|
|
22
23
|
) -> float:
|
|
23
|
-
"""
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
value: The value to convert.
|
|
27
|
-
value_mode: Energy mode of the value. If it is already binding,
|
|
28
|
-
|
|
29
|
-
excitation_energy: Value to calculate the conversion.
|
|
24
|
+
"""Convert a value that is kinetic energy to binding energy.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
value (float): The value to convert.
|
|
28
|
+
value_mode (EnergyMode): Energy mode of the value. If it is already binding,
|
|
29
|
+
return the same value. If it is kinetic, convert to binding.
|
|
30
|
+
excitation_energy (float): Value to calculate the conversion.
|
|
31
|
+
|
|
30
32
|
Returns:
|
|
31
|
-
Caluclated binding energy value
|
|
33
|
+
float: Caluclated binding energy value.
|
|
32
34
|
"""
|
|
33
35
|
return value if value_mode == EnergyMode.BINDING else excitation_energy - value
|
|
@@ -3,20 +3,19 @@ from abc import abstractmethod
|
|
|
3
3
|
from ophyd_async.core import (
|
|
4
4
|
Reference,
|
|
5
5
|
SignalR,
|
|
6
|
+
SignalRW,
|
|
6
7
|
StandardReadable,
|
|
7
8
|
StandardReadableFormat,
|
|
8
9
|
derived_signal_r,
|
|
9
10
|
soft_signal_r_and_setter,
|
|
10
|
-
soft_signal_rw,
|
|
11
11
|
)
|
|
12
12
|
|
|
13
|
-
from dodal.devices.
|
|
13
|
+
from dodal.devices.selectable_source import SelectedSource, get_obj_from_selected_source
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
class AbstractEnergySource(StandardReadable):
|
|
17
|
-
"""
|
|
18
|
-
|
|
19
|
-
a energy signal.
|
|
17
|
+
"""Abstract device that wraps an energy source signal and provides common interface
|
|
18
|
+
via a energy signal.
|
|
20
19
|
"""
|
|
21
20
|
|
|
22
21
|
def __init__(self, name: str = "") -> None:
|
|
@@ -25,14 +24,11 @@ class AbstractEnergySource(StandardReadable):
|
|
|
25
24
|
@property
|
|
26
25
|
@abstractmethod
|
|
27
26
|
def energy(self) -> SignalR[float]:
|
|
28
|
-
"""
|
|
29
|
-
Signal to provide the excitation energy value in eV.
|
|
30
|
-
"""
|
|
27
|
+
"""Signal to provide the excitation energy value in eV."""
|
|
31
28
|
|
|
32
29
|
|
|
33
30
|
class EnergySource(AbstractEnergySource):
|
|
34
|
-
"""
|
|
35
|
-
Wraps a signal that relates to energy and provides common interface via energy
|
|
31
|
+
"""Wraps a signal that relates to energy and provides common interface via energy
|
|
36
32
|
signal. It provides the name of the wrapped signal as a child signal in the
|
|
37
33
|
read_configuration via wrapped_device_name and adds the signal as a readable.
|
|
38
34
|
"""
|
|
@@ -51,51 +47,50 @@ class EnergySource(AbstractEnergySource):
|
|
|
51
47
|
return self._source_ref()
|
|
52
48
|
|
|
53
49
|
|
|
50
|
+
def get_float_from_selected_source(
|
|
51
|
+
selected: SelectedSource, s1: float, s2: float
|
|
52
|
+
) -> float:
|
|
53
|
+
"""Wrapper function to provide type hints for derived signal."""
|
|
54
|
+
return get_obj_from_selected_source(selected, s1, s2)
|
|
55
|
+
|
|
56
|
+
|
|
54
57
|
class DualEnergySource(AbstractEnergySource):
|
|
55
|
-
"""
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
"""Holds two EnergySource devices and provides a signal to read energy depending on
|
|
59
|
+
which source is selected. The energy is the one that corrosponds to the
|
|
60
|
+
selected_source signal. For example, selected_source is source1 if selected_source
|
|
61
|
+
is at SelectedSource.SOURCE1 and vise versa for source2 and
|
|
62
|
+
SelectedSource.SOURCE2.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
source1 (SignalR): Energy source that corrosponds to SelectedSource.SOURCE1.
|
|
66
|
+
source2 (SignalR): Energy source that corrosponds to SelectedSource.SOURCE2.
|
|
67
|
+
selected_source (SignalRW): Signal that decides the active energy source.
|
|
68
|
+
name (str, optional): Name of this device.
|
|
61
69
|
"""
|
|
62
70
|
|
|
63
71
|
def __init__(
|
|
64
|
-
self,
|
|
72
|
+
self,
|
|
73
|
+
source1: SignalR[float],
|
|
74
|
+
source2: SignalR[float],
|
|
75
|
+
selected_source: SignalRW[SelectedSource],
|
|
76
|
+
name: str = "",
|
|
65
77
|
):
|
|
66
|
-
|
|
67
|
-
Args:
|
|
68
|
-
source1: Default energy signal to select.
|
|
69
|
-
source2: Secondary energy signal to select.
|
|
70
|
-
name: name of this device.
|
|
71
|
-
"""
|
|
72
|
-
|
|
78
|
+
self.selected_source_ref = Reference(selected_source)
|
|
73
79
|
with self.add_children_as_readables():
|
|
74
|
-
self.selected_source = soft_signal_rw(
|
|
75
|
-
SelectedSource, initial_value=SelectedSource.SOURCE1
|
|
76
|
-
)
|
|
77
80
|
self.source1 = EnergySource(source1)
|
|
78
81
|
self.source2 = EnergySource(source2)
|
|
79
82
|
|
|
80
83
|
self._selected_energy = derived_signal_r(
|
|
81
|
-
|
|
84
|
+
get_float_from_selected_source,
|
|
82
85
|
"eV",
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
+
selected=self.selected_source_ref(),
|
|
87
|
+
s1=self.source1.energy,
|
|
88
|
+
s2=self.source2.energy,
|
|
86
89
|
)
|
|
90
|
+
self.add_readables([selected_source])
|
|
87
91
|
|
|
88
92
|
super().__init__(name)
|
|
89
93
|
|
|
90
|
-
def _get_excitation_energy(
|
|
91
|
-
self, selected_source: SelectedSource, source1: float, source2: float
|
|
92
|
-
) -> float:
|
|
93
|
-
match selected_source:
|
|
94
|
-
case SelectedSource.SOURCE1:
|
|
95
|
-
return source1
|
|
96
|
-
case SelectedSource.SOURCE2:
|
|
97
|
-
return source2
|
|
98
|
-
|
|
99
94
|
@property
|
|
100
95
|
def energy(self) -> SignalR[float]:
|
|
101
96
|
return self._selected_energy
|