mx-bluesky 0.3.1__py3-none-any.whl → 1.1.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.
- mx_bluesky/_version.py +2 -2
- mx_bluesky/beamlines/i04/__init__.py +3 -0
- mx_bluesky/{i04 → beamlines/i04}/thawing_plan.py +5 -4
- mx_bluesky/{i24 → beamlines/i24}/serial/blueapi_config.yaml +1 -1
- mx_bluesky/{i24 → beamlines/i24}/serial/dcid.py +2 -2
- mx_bluesky/{i24 → beamlines/i24}/serial/extruder/EX-gui-edm/DetStage.edl +3 -3
- mx_bluesky/{i24 → beamlines/i24}/serial/extruder/EX-gui-edm/DiamondExtruder-I24-py3v1.edl +7 -7
- mx_bluesky/{i24 → beamlines/i24}/serial/extruder/i24ssx_Extruder_Collect_py3v2.py +12 -9
- mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/CustomChip_py3v1.edl +3 -3
- mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/DetStage.edl +3 -3
- mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/DiamondChipI24-py3v1.edl +245 -200
- mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/MappingLite-oxford_py3v1.edl +4 -4
- mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/pumpprobe-py3v1.edl +8 -8
- mx_bluesky/beamlines/i24/serial/fixed_target/__init__.py +0 -0
- mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/i24ssx_Chip_Collect_py3v1.py +80 -70
- mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/i24ssx_Chip_Manager_py3v1.py +20 -21
- mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/i24ssx_Chip_Mapping_py3v1.py +5 -5
- mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/i24ssx_Chip_StartUp_py3v1.py +7 -4
- mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/i24ssx_moveonclick.py +59 -39
- mx_bluesky/{i24 → beamlines/i24}/serial/log.py +1 -9
- mx_bluesky/beamlines/i24/serial/parameters/__init__.py +15 -0
- mx_bluesky/{i24 → beamlines/i24}/serial/parameters/constants.py +1 -1
- mx_bluesky/{i24 → beamlines/i24}/serial/parameters/experiment_parameters.py +4 -25
- mx_bluesky/{i24 → beamlines/i24}/serial/parameters/utils.py +5 -3
- mx_bluesky/{i24 → beamlines/i24}/serial/run_serial.py +1 -1
- mx_bluesky/{i24 → beamlines/i24}/serial/setup_beamline/pv_abstract.py +1 -1
- mx_bluesky/{i24 → beamlines/i24}/serial/setup_beamline/setup_beamline.py +2 -2
- mx_bluesky/{i24 → beamlines/i24}/serial/setup_beamline/setup_detector.py +5 -5
- mx_bluesky/{i24 → beamlines/i24}/serial/write_nexus.py +6 -3
- mx_bluesky/hyperion/__init__.py +1 -0
- mx_bluesky/hyperion/__main__.py +374 -0
- mx_bluesky/hyperion/device_setup_plans/__init__.py +0 -0
- mx_bluesky/hyperion/device_setup_plans/dcm_pitch_roll_mirror_adjuster.py +134 -0
- mx_bluesky/hyperion/device_setup_plans/manipulate_sample.py +110 -0
- mx_bluesky/hyperion/device_setup_plans/position_detector.py +16 -0
- mx_bluesky/hyperion/device_setup_plans/read_hardware_for_setup.py +60 -0
- mx_bluesky/hyperion/device_setup_plans/setup_oav.py +87 -0
- mx_bluesky/hyperion/device_setup_plans/setup_panda.py +210 -0
- mx_bluesky/hyperion/device_setup_plans/setup_zebra.py +214 -0
- mx_bluesky/hyperion/device_setup_plans/smargon.py +25 -0
- mx_bluesky/hyperion/device_setup_plans/utils.py +44 -0
- mx_bluesky/hyperion/device_setup_plans/xbpm_feedback.py +93 -0
- mx_bluesky/hyperion/exceptions.py +47 -0
- mx_bluesky/hyperion/experiment_plans/__init__.py +30 -0
- mx_bluesky/hyperion/experiment_plans/experiment_registry.py +84 -0
- mx_bluesky/hyperion/experiment_plans/flyscan_xray_centre_plan.py +528 -0
- mx_bluesky/hyperion/experiment_plans/grid_detect_then_xray_centre_plan.py +209 -0
- mx_bluesky/hyperion/experiment_plans/oav_grid_detection_plan.py +173 -0
- mx_bluesky/hyperion/experiment_plans/oav_snapshot_plan.py +81 -0
- mx_bluesky/hyperion/experiment_plans/optimise_attenuation_plan.py +463 -0
- mx_bluesky/hyperion/experiment_plans/pin_centre_then_xray_centre_plan.py +119 -0
- mx_bluesky/hyperion/experiment_plans/pin_tip_centring_plan.py +164 -0
- mx_bluesky/hyperion/experiment_plans/robot_load_then_centre_plan.py +322 -0
- mx_bluesky/hyperion/experiment_plans/rotation_scan_plan.py +436 -0
- mx_bluesky/hyperion/experiment_plans/set_energy_plan.py +68 -0
- mx_bluesky/hyperion/external_interaction/__init__.py +9 -0
- mx_bluesky/hyperion/external_interaction/callbacks/__init__.py +10 -0
- mx_bluesky/hyperion/external_interaction/callbacks/__main__.py +148 -0
- mx_bluesky/hyperion/external_interaction/callbacks/aperture_change_callback.py +22 -0
- mx_bluesky/hyperion/external_interaction/callbacks/common/__init__.py +0 -0
- mx_bluesky/hyperion/external_interaction/callbacks/common/callback_util.py +46 -0
- mx_bluesky/hyperion/external_interaction/callbacks/common/ispyb_mapping.py +70 -0
- mx_bluesky/hyperion/external_interaction/callbacks/grid_detection_callback.py +88 -0
- mx_bluesky/hyperion/external_interaction/callbacks/ispyb_callback_base.py +203 -0
- mx_bluesky/hyperion/external_interaction/callbacks/log_uid_tag_callback.py +20 -0
- mx_bluesky/hyperion/external_interaction/callbacks/logging_callback.py +29 -0
- mx_bluesky/hyperion/external_interaction/callbacks/plan_reactive_callback.py +101 -0
- mx_bluesky/hyperion/external_interaction/callbacks/robot_load/ispyb_callback.py +88 -0
- mx_bluesky/hyperion/external_interaction/callbacks/rotation/__init__.py +0 -0
- mx_bluesky/hyperion/external_interaction/callbacks/rotation/ispyb_callback.py +174 -0
- mx_bluesky/hyperion/external_interaction/callbacks/rotation/ispyb_mapping.py +17 -0
- mx_bluesky/hyperion/external_interaction/callbacks/rotation/nexus_callback.py +102 -0
- mx_bluesky/hyperion/external_interaction/callbacks/xray_centre/__init__.py +0 -0
- mx_bluesky/hyperion/external_interaction/callbacks/xray_centre/ispyb_callback.py +269 -0
- mx_bluesky/hyperion/external_interaction/callbacks/xray_centre/ispyb_mapping.py +53 -0
- mx_bluesky/hyperion/external_interaction/callbacks/xray_centre/nexus_callback.py +95 -0
- mx_bluesky/hyperion/external_interaction/callbacks/zocalo_callback.py +92 -0
- mx_bluesky/hyperion/external_interaction/config_server.py +35 -0
- mx_bluesky/hyperion/external_interaction/exceptions.py +13 -0
- mx_bluesky/hyperion/external_interaction/ispyb/__init__.py +0 -0
- mx_bluesky/hyperion/external_interaction/ispyb/data_model.py +95 -0
- mx_bluesky/hyperion/external_interaction/ispyb/exp_eye_store.py +125 -0
- mx_bluesky/hyperion/external_interaction/ispyb/ispyb_store.py +276 -0
- mx_bluesky/hyperion/external_interaction/ispyb/ispyb_utils.py +29 -0
- mx_bluesky/hyperion/external_interaction/nexus/__init__.py +0 -0
- mx_bluesky/hyperion/external_interaction/nexus/nexus_utils.py +148 -0
- mx_bluesky/hyperion/external_interaction/nexus/write_nexus.py +114 -0
- mx_bluesky/hyperion/log.py +99 -0
- mx_bluesky/hyperion/parameters/__init__.py +2 -0
- mx_bluesky/hyperion/parameters/cli.py +68 -0
- mx_bluesky/{parameters → hyperion/parameters}/components.py +77 -24
- mx_bluesky/hyperion/parameters/constants.py +158 -0
- mx_bluesky/hyperion/parameters/gridscan.py +216 -0
- mx_bluesky/hyperion/parameters/rotation.py +160 -0
- mx_bluesky/hyperion/resources/panda/panda-gridscan.yaml +964 -0
- mx_bluesky/hyperion/tracing.py +28 -0
- mx_bluesky/hyperion/utils/context.py +84 -0
- mx_bluesky/hyperion/utils/utils.py +25 -0
- mx_bluesky/hyperion/utils/validation.py +196 -0
- mx_bluesky/jupyter_example.ipynb +3 -2
- {mx_bluesky-0.3.1.dist-info → mx_bluesky-1.1.0.dist-info}/METADATA +26 -11
- mx_bluesky-1.1.0.dist-info/RECORD +136 -0
- {mx_bluesky-0.3.1.dist-info → mx_bluesky-1.1.0.dist-info}/WHEEL +1 -1
- mx_bluesky-1.1.0.dist-info/entry_points.txt +8 -0
- mx_bluesky/i04/__init__.py +0 -3
- mx_bluesky/i24/serial/parameters/__init__.py +0 -15
- mx_bluesky/parameters/__init__.py +0 -31
- mx_bluesky-0.3.1.dist-info/RECORD +0 -67
- mx_bluesky-0.3.1.dist-info/entry_points.txt +0 -4
- /mx_bluesky/{i24 → beamlines}/__init__.py +0 -0
- /mx_bluesky/{i04 → beamlines/i04}/callbacks/murko_callback.py +0 -0
- /mx_bluesky/{i24/serial/extruder → beamlines/i24}/__init__.py +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/__init__.py +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/extruder/EX-gui-edm/microdrop_alignment.edl +0 -0
- /mx_bluesky/{i24/serial/fixed_target → beamlines/i24/serial/extruder}/__init__.py +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/ME14E-GeneralPurpose.edl +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/PMAC_Command.edl +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/Shutter_Control.edl +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/microdrop_alignment.edl +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/nudgechip.edl +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/short1-laser.png +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/short2-laser.png +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/ft_utils.py +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/parameters/fixed_target/cs/cs_maker.json +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/parameters/fixed_target/cs/motor_direction.txt +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/parameters/fixed_target/pvar_files/minichip-oxford.pvar +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/parameters/fixed_target/pvar_files/oxford.pvar +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/run_extruder.sh +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/run_fixed_target.sh +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/run_ssx.sh +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/set_visit_directory.sh +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/setup_beamline/__init__.py +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/setup_beamline/ca.py +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/setup_beamline/pv.py +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/setup_beamline/setup_zebra_plans.py +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/start_blueapi.sh +0 -0
- {mx_bluesky-0.3.1.dist-info → mx_bluesky-1.1.0.dist-info}/LICENSE +0 -0
- {mx_bluesky-0.3.1.dist-info → mx_bluesky-1.1.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import bluesky.plan_stubs as bps
|
|
4
|
+
from dodal.devices.aperturescatterguard import (
|
|
5
|
+
ApertureScatterguard,
|
|
6
|
+
ApertureValue,
|
|
7
|
+
)
|
|
8
|
+
from dodal.devices.backlight import Backlight, BacklightPosition
|
|
9
|
+
from dodal.devices.detector.detector_motion import DetectorMotion
|
|
10
|
+
from dodal.devices.smargon import Smargon
|
|
11
|
+
|
|
12
|
+
from mx_bluesky.hyperion.log import LOGGER
|
|
13
|
+
|
|
14
|
+
LOWER_DETECTOR_SHUTTER_AFTER_SCAN = True
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def setup_sample_environment(
|
|
18
|
+
aperture_scatterguard: ApertureScatterguard,
|
|
19
|
+
aperture_position_gda_name: str | None,
|
|
20
|
+
backlight: Backlight,
|
|
21
|
+
group="setup_senv",
|
|
22
|
+
):
|
|
23
|
+
"""Move the aperture into required position, move out the backlight."""
|
|
24
|
+
aperture_value = (
|
|
25
|
+
None
|
|
26
|
+
if not aperture_position_gda_name
|
|
27
|
+
else ApertureValue(aperture_position_gda_name)
|
|
28
|
+
)
|
|
29
|
+
yield from move_aperture_if_required(
|
|
30
|
+
aperture_scatterguard, aperture_value, group=group
|
|
31
|
+
)
|
|
32
|
+
yield from bps.abs_set(backlight, BacklightPosition.OUT, group=group)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def move_aperture_if_required(
|
|
36
|
+
aperture_scatterguard: ApertureScatterguard,
|
|
37
|
+
aperture_value: ApertureValue | None,
|
|
38
|
+
group="move_aperture",
|
|
39
|
+
):
|
|
40
|
+
if not aperture_value:
|
|
41
|
+
previous_aperture_position = yield from bps.rd(aperture_scatterguard)
|
|
42
|
+
assert isinstance(previous_aperture_position, ApertureValue)
|
|
43
|
+
LOGGER.info(
|
|
44
|
+
f"Using previously set aperture position {previous_aperture_position}"
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
else:
|
|
48
|
+
LOGGER.info(f"Setting aperture position to {aperture_value}")
|
|
49
|
+
yield from bps.abs_set(
|
|
50
|
+
aperture_scatterguard,
|
|
51
|
+
aperture_value,
|
|
52
|
+
group=group,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def cleanup_sample_environment(
|
|
57
|
+
detector_motion: DetectorMotion,
|
|
58
|
+
group="cleanup_senv",
|
|
59
|
+
):
|
|
60
|
+
"""Put the detector shutter back down"""
|
|
61
|
+
|
|
62
|
+
yield from bps.abs_set(
|
|
63
|
+
detector_motion.shutter,
|
|
64
|
+
int(not LOWER_DETECTOR_SHUTTER_AFTER_SCAN),
|
|
65
|
+
group=group,
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def move_x_y_z(
|
|
70
|
+
smargon: Smargon,
|
|
71
|
+
x_mm: float | None = None,
|
|
72
|
+
y_mm: float | None = None,
|
|
73
|
+
z_mm: float | None = None,
|
|
74
|
+
wait=False,
|
|
75
|
+
group="move_x_y_z",
|
|
76
|
+
):
|
|
77
|
+
"""Move the x, y, and z axes of the given smargon to the specified position. All
|
|
78
|
+
axes are optional."""
|
|
79
|
+
|
|
80
|
+
LOGGER.info(f"Moving smargon to x, y, z: {(x_mm, y_mm, z_mm)}")
|
|
81
|
+
if x_mm:
|
|
82
|
+
yield from bps.abs_set(smargon.x, x_mm, group=group)
|
|
83
|
+
if y_mm:
|
|
84
|
+
yield from bps.abs_set(smargon.y, y_mm, group=group)
|
|
85
|
+
if z_mm:
|
|
86
|
+
yield from bps.abs_set(smargon.z, z_mm, group=group)
|
|
87
|
+
if wait:
|
|
88
|
+
yield from bps.wait(group)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def move_phi_chi_omega(
|
|
92
|
+
smargon: Smargon,
|
|
93
|
+
phi: float | None = None,
|
|
94
|
+
chi: float | None = None,
|
|
95
|
+
omega: float | None = None,
|
|
96
|
+
wait=False,
|
|
97
|
+
group="move_phi_chi_omega",
|
|
98
|
+
):
|
|
99
|
+
"""Move the x, y, and z axes of the given smargon to the specified position. All
|
|
100
|
+
axes are optional."""
|
|
101
|
+
|
|
102
|
+
LOGGER.info(f"Moving smargon to phi, chi, omega: {(phi, chi, omega)}")
|
|
103
|
+
if phi:
|
|
104
|
+
yield from bps.abs_set(smargon.phi, phi, group=group)
|
|
105
|
+
if chi:
|
|
106
|
+
yield from bps.abs_set(smargon.chi, chi, group=group)
|
|
107
|
+
if omega:
|
|
108
|
+
yield from bps.abs_set(smargon.omega, omega, group=group)
|
|
109
|
+
if wait:
|
|
110
|
+
yield from bps.wait(group)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from bluesky import plan_stubs as bps
|
|
2
|
+
from dodal.devices.detector.detector_motion import DetectorMotion, ShutterState
|
|
3
|
+
|
|
4
|
+
from mx_bluesky.hyperion.log import LOGGER
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def set_detector_z_position(
|
|
8
|
+
detector_motion: DetectorMotion, detector_position: float, group=None
|
|
9
|
+
):
|
|
10
|
+
LOGGER.info(f"Moving detector to {detector_position} ({group})")
|
|
11
|
+
yield from bps.abs_set(detector_motion.z, detector_position, group=group)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def set_shutter(detector_motion: DetectorMotion, state: ShutterState, group=None):
|
|
15
|
+
LOGGER.info(f"Setting shutter to {state} ({group})")
|
|
16
|
+
yield from bps.abs_set(detector_motion.shutter, state, group=group)
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import bluesky.plan_stubs as bps
|
|
4
|
+
from dodal.devices.aperturescatterguard import ApertureScatterguard
|
|
5
|
+
from dodal.devices.attenuator import Attenuator
|
|
6
|
+
from dodal.devices.dcm import DCM
|
|
7
|
+
from dodal.devices.eiger import EigerDetector
|
|
8
|
+
from dodal.devices.flux import Flux
|
|
9
|
+
from dodal.devices.robot import BartRobot
|
|
10
|
+
from dodal.devices.s4_slit_gaps import S4SlitGaps
|
|
11
|
+
from dodal.devices.smargon import Smargon
|
|
12
|
+
from dodal.devices.synchrotron import Synchrotron
|
|
13
|
+
from dodal.devices.undulator import Undulator
|
|
14
|
+
|
|
15
|
+
from mx_bluesky.hyperion.log import LOGGER
|
|
16
|
+
from mx_bluesky.hyperion.parameters.constants import CONST
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def read_hardware_pre_collection(
|
|
20
|
+
undulator: Undulator,
|
|
21
|
+
synchrotron: Synchrotron,
|
|
22
|
+
s4_slit_gaps: S4SlitGaps,
|
|
23
|
+
robot: BartRobot,
|
|
24
|
+
smargon: Smargon,
|
|
25
|
+
):
|
|
26
|
+
LOGGER.info("Reading status of beamline for callbacks, pre collection.")
|
|
27
|
+
yield from bps.create(
|
|
28
|
+
name=CONST.DESCRIPTORS.HARDWARE_READ_PRE
|
|
29
|
+
) # gives name to event *descriptor* document
|
|
30
|
+
yield from bps.read(undulator.current_gap)
|
|
31
|
+
yield from bps.read(synchrotron.synchrotron_mode)
|
|
32
|
+
yield from bps.read(s4_slit_gaps.xgap)
|
|
33
|
+
yield from bps.read(s4_slit_gaps.ygap)
|
|
34
|
+
yield from bps.read(smargon.x)
|
|
35
|
+
yield from bps.read(smargon.y)
|
|
36
|
+
yield from bps.read(smargon.z)
|
|
37
|
+
yield from bps.save()
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def read_hardware_during_collection(
|
|
41
|
+
aperture_scatterguard: ApertureScatterguard,
|
|
42
|
+
attenuator: Attenuator,
|
|
43
|
+
flux: Flux,
|
|
44
|
+
dcm: DCM,
|
|
45
|
+
detector: EigerDetector,
|
|
46
|
+
):
|
|
47
|
+
LOGGER.info("Reading status of beamline for callbacks, during collection.")
|
|
48
|
+
yield from bps.create(name=CONST.DESCRIPTORS.HARDWARE_READ_DURING)
|
|
49
|
+
yield from bps.read(aperture_scatterguard)
|
|
50
|
+
yield from bps.read(attenuator.actual_transmission)
|
|
51
|
+
yield from bps.read(flux.flux_reading)
|
|
52
|
+
yield from bps.read(dcm.energy_in_kev)
|
|
53
|
+
yield from bps.read(detector.bit_depth)
|
|
54
|
+
yield from bps.save()
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def read_hardware_for_zocalo(detector: EigerDetector):
|
|
58
|
+
yield from bps.create(name=CONST.DESCRIPTORS.ZOCALO_HW_READ)
|
|
59
|
+
yield from bps.read(detector.odin.file_writer.id)
|
|
60
|
+
yield from bps.save()
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
from functools import partial
|
|
2
|
+
|
|
3
|
+
import bluesky.plan_stubs as bps
|
|
4
|
+
from dodal.devices.oav.oav_detector import OAV
|
|
5
|
+
from dodal.devices.oav.oav_errors import OAVError_ZoomLevelNotFound
|
|
6
|
+
from dodal.devices.oav.oav_parameters import OAVParameters
|
|
7
|
+
from dodal.devices.oav.pin_image_recognition import PinTipDetection
|
|
8
|
+
from dodal.devices.oav.utils import ColorMode
|
|
9
|
+
|
|
10
|
+
from mx_bluesky.hyperion.parameters.constants import CONST
|
|
11
|
+
|
|
12
|
+
# Helper function to make sure we set the waiting groups correctly
|
|
13
|
+
set_using_group = partial(bps.abs_set, group=CONST.WAIT.READY_FOR_OAV)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def setup_pin_tip_detection_params(
|
|
17
|
+
pin_tip_detect_device: PinTipDetection, parameters: OAVParameters
|
|
18
|
+
):
|
|
19
|
+
# select which blur to apply to image
|
|
20
|
+
yield from set_using_group(
|
|
21
|
+
pin_tip_detect_device.preprocess_operation, parameters.preprocess
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
# sets length scale for blurring
|
|
25
|
+
yield from set_using_group(
|
|
26
|
+
pin_tip_detect_device.preprocess_ksize, parameters.preprocess_K_size
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
# Canny edge detect - lower
|
|
30
|
+
yield from set_using_group(
|
|
31
|
+
pin_tip_detect_device.canny_lower_threshold,
|
|
32
|
+
parameters.canny_edge_lower_threshold,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
# Canny edge detect - upper
|
|
36
|
+
yield from set_using_group(
|
|
37
|
+
pin_tip_detect_device.canny_upper_threshold,
|
|
38
|
+
parameters.canny_edge_upper_threshold,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
# "Close" morphological operation
|
|
42
|
+
yield from set_using_group(
|
|
43
|
+
pin_tip_detect_device.close_ksize, parameters.close_ksize
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
# Sample detection direction
|
|
47
|
+
yield from set_using_group(
|
|
48
|
+
pin_tip_detect_device.scan_direction, parameters.direction
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
# Minimum height
|
|
52
|
+
yield from set_using_group(
|
|
53
|
+
pin_tip_detect_device.min_tip_height,
|
|
54
|
+
parameters.minimum_height,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def setup_general_oav_params(oav: OAV, parameters: OAVParameters):
|
|
59
|
+
yield from set_using_group(oav.cam.color_mode, ColorMode.RGB1)
|
|
60
|
+
yield from set_using_group(oav.cam.acquire_period, parameters.acquire_period)
|
|
61
|
+
yield from set_using_group(oav.cam.acquire_time, parameters.exposure)
|
|
62
|
+
yield from set_using_group(oav.cam.gain, parameters.gain)
|
|
63
|
+
|
|
64
|
+
zoom_level_str = f"{float(parameters.zoom)}x"
|
|
65
|
+
if zoom_level_str not in oav.zoom_controller.allowed_zoom_levels:
|
|
66
|
+
raise OAVError_ZoomLevelNotFound(
|
|
67
|
+
f"Found {zoom_level_str} as a zoom level but expected one of {oav.zoom_controller.allowed_zoom_levels}"
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
yield from bps.abs_set(
|
|
71
|
+
oav.zoom_controller,
|
|
72
|
+
zoom_level_str,
|
|
73
|
+
wait=True,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def pre_centring_setup_oav(
|
|
78
|
+
oav: OAV,
|
|
79
|
+
parameters: OAVParameters,
|
|
80
|
+
pin_tip_detection_device: PinTipDetection,
|
|
81
|
+
):
|
|
82
|
+
"""
|
|
83
|
+
Setup OAV PVs with required values.
|
|
84
|
+
"""
|
|
85
|
+
yield from setup_general_oav_params(oav, parameters)
|
|
86
|
+
yield from setup_pin_tip_detection_params(pin_tip_detection_device, parameters)
|
|
87
|
+
yield from bps.wait(CONST.WAIT.READY_FOR_OAV)
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from enum import Enum
|
|
3
|
+
from importlib import resources
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
import bluesky.plan_stubs as bps
|
|
7
|
+
from blueapi.core import MsgGenerator
|
|
8
|
+
from dodal.common.beamlines.beamline_utils import get_path_provider
|
|
9
|
+
from dodal.devices.fast_grid_scan import PandAGridScanParams
|
|
10
|
+
from ophyd_async.core import load_device
|
|
11
|
+
from ophyd_async.fastcs.panda import (
|
|
12
|
+
HDFPanda,
|
|
13
|
+
SeqTable,
|
|
14
|
+
SeqTrigger,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
import mx_bluesky.hyperion.resources.panda as panda_resource
|
|
18
|
+
from mx_bluesky.hyperion.log import LOGGER
|
|
19
|
+
|
|
20
|
+
MM_TO_ENCODER_COUNTS = 200000
|
|
21
|
+
GENERAL_TIMEOUT = 60
|
|
22
|
+
TICKS_PER_MS = 1000 # Panda sequencer prescaler will be set to us
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class Enabled(Enum):
|
|
26
|
+
ENABLED = "ONE"
|
|
27
|
+
DISABLED = "ZERO"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class PcapArm(Enum):
|
|
31
|
+
ARMED = "Arm"
|
|
32
|
+
DISARMED = "Disarm"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _get_seq_table(
|
|
36
|
+
parameters: PandAGridScanParams, exposure_distance_mm, time_between_steps_ms
|
|
37
|
+
) -> SeqTable:
|
|
38
|
+
"""
|
|
39
|
+
Generate the sequencer table for the panda.
|
|
40
|
+
|
|
41
|
+
- Sending a 'trigger' means trigger PCAP internally and send signal to Eiger via physical panda output
|
|
42
|
+
|
|
43
|
+
SEQUENCER TABLE:
|
|
44
|
+
|
|
45
|
+
1. Wait for physical trigger from motion script to mark start of scan / change of direction
|
|
46
|
+
2. Wait for POSA (X2) to be greater than X_START and send x_steps triggers every time_between_steps_ms
|
|
47
|
+
3. Wait for physical trigger from motion script to mark change of direction
|
|
48
|
+
4. Wait for POSA (X2) to be less than X_START + X_STEP_SIZE * x_steps + exposure distance, then
|
|
49
|
+
send x_steps triggers every time_between_steps_ms
|
|
50
|
+
5. Go back to step one.
|
|
51
|
+
|
|
52
|
+
For a more detailed explanation and a diagram, see https://github.com/DiamondLightSource/hyperion/wiki/PandA-constant%E2%80%90motion-scanning
|
|
53
|
+
|
|
54
|
+
For documentation on Panda itself, see https://pandablocks.github.io/PandABlocks-FPGA/master/index.html
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
exposure_distance_mm: The distance travelled by the sample each time the detector is exposed: exposure time * sample velocity
|
|
58
|
+
time_between_steps_ms: The time taken to traverse between each grid step.
|
|
59
|
+
parameters: Parameters for the panda gridscan
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
An instance of SeqTable describing the panda sequencer table
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
start_of_grid_x_counts = int(parameters.x_start * MM_TO_ENCODER_COUNTS)
|
|
66
|
+
|
|
67
|
+
# x_start is the first trigger point, so we need to travel to x_steps-1 for the final trigger point
|
|
68
|
+
end_of_grid_x_counts = int(
|
|
69
|
+
start_of_grid_x_counts
|
|
70
|
+
+ (parameters.x_step_size * (parameters.x_steps - 1) * MM_TO_ENCODER_COUNTS)
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
exposure_distance_x_counts = int(exposure_distance_mm * MM_TO_ENCODER_COUNTS)
|
|
74
|
+
|
|
75
|
+
num_pulses = parameters.x_steps
|
|
76
|
+
|
|
77
|
+
delay_between_pulses = time_between_steps_ms * TICKS_PER_MS
|
|
78
|
+
|
|
79
|
+
PULSE_WIDTH_US = 1
|
|
80
|
+
|
|
81
|
+
assert delay_between_pulses > PULSE_WIDTH_US
|
|
82
|
+
|
|
83
|
+
# BITA_1 trigger wired from TTLIN1, this is the trigger input
|
|
84
|
+
|
|
85
|
+
# +ve direction scan
|
|
86
|
+
|
|
87
|
+
table = (
|
|
88
|
+
SeqTable.row(trigger=SeqTrigger.BITA_1, time2=1)
|
|
89
|
+
+ SeqTable.row(
|
|
90
|
+
repeats=num_pulses,
|
|
91
|
+
trigger=SeqTrigger.POSA_GT,
|
|
92
|
+
position=start_of_grid_x_counts,
|
|
93
|
+
time1=PULSE_WIDTH_US,
|
|
94
|
+
outa1=True,
|
|
95
|
+
time2=delay_between_pulses - PULSE_WIDTH_US,
|
|
96
|
+
outa2=False,
|
|
97
|
+
)
|
|
98
|
+
+
|
|
99
|
+
# -ve direction scan
|
|
100
|
+
SeqTable.row(trigger=SeqTrigger.BITA_1, time2=1)
|
|
101
|
+
+ SeqTable.row(
|
|
102
|
+
repeats=num_pulses,
|
|
103
|
+
trigger=SeqTrigger.POSA_LT,
|
|
104
|
+
position=end_of_grid_x_counts + exposure_distance_x_counts,
|
|
105
|
+
time1=PULSE_WIDTH_US,
|
|
106
|
+
outa1=True,
|
|
107
|
+
time2=delay_between_pulses - PULSE_WIDTH_US,
|
|
108
|
+
outa2=False,
|
|
109
|
+
)
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
return table
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def setup_panda_for_flyscan(
|
|
116
|
+
panda: HDFPanda,
|
|
117
|
+
parameters: PandAGridScanParams,
|
|
118
|
+
initial_x: float,
|
|
119
|
+
exposure_time_s: float,
|
|
120
|
+
time_between_x_steps_ms: float,
|
|
121
|
+
sample_velocity_mm_per_s: float,
|
|
122
|
+
) -> MsgGenerator:
|
|
123
|
+
"""Configures the PandA device for a flyscan.
|
|
124
|
+
Sets PVs from a yaml file, calibrates the encoder, and
|
|
125
|
+
adjusts the sequencer table based off the grid parameters. Yaml file can be
|
|
126
|
+
created using ophyd_async.core.save_device()
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
panda (HDFPanda): The PandA Ophyd device
|
|
130
|
+
parameters (PandAGridScanParams): Grid parameters
|
|
131
|
+
initial_x (float): Motor positions at time of PandA setup
|
|
132
|
+
exposure_time_s (float): Detector exposure time per trigger
|
|
133
|
+
time_between_x_steps_ms (float): Time, in ms, between each trigger. Equal to deadtime + exposure time
|
|
134
|
+
sample_velocity_mm_per_s (float): Velocity of the sample in mm/s = x_step_size_mm * 1000 /
|
|
135
|
+
time_between_x_steps_ms
|
|
136
|
+
Returns:
|
|
137
|
+
MsgGenerator
|
|
138
|
+
|
|
139
|
+
Yields:
|
|
140
|
+
Iterator[MsgGenerator]
|
|
141
|
+
"""
|
|
142
|
+
assert parameters.x_steps > 0
|
|
143
|
+
assert time_between_x_steps_ms * 1000 >= exposure_time_s
|
|
144
|
+
assert sample_velocity_mm_per_s * exposure_time_s < parameters.x_step_size
|
|
145
|
+
|
|
146
|
+
yield from bps.stage(panda, group="panda-config")
|
|
147
|
+
|
|
148
|
+
with resources.as_file(
|
|
149
|
+
resources.files(panda_resource) / "panda-gridscan.yaml"
|
|
150
|
+
) as config_yaml_path:
|
|
151
|
+
yield from load_device(panda, str(config_yaml_path))
|
|
152
|
+
|
|
153
|
+
# Home the PandA X encoder using current motor position
|
|
154
|
+
yield from bps.abs_set(
|
|
155
|
+
panda.inenc[1].setp, # type: ignore
|
|
156
|
+
initial_x * MM_TO_ENCODER_COUNTS,
|
|
157
|
+
wait=True,
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
yield from bps.abs_set(panda.pulse[1].width, exposure_time_s, group="panda-config")
|
|
161
|
+
|
|
162
|
+
exposure_distance_mm = sample_velocity_mm_per_s * exposure_time_s
|
|
163
|
+
|
|
164
|
+
table = _get_seq_table(parameters, exposure_distance_mm, time_between_x_steps_ms)
|
|
165
|
+
|
|
166
|
+
yield from bps.abs_set(panda.seq[1].table, table, group="panda-config")
|
|
167
|
+
|
|
168
|
+
yield from bps.abs_set(
|
|
169
|
+
panda.pcap.enable, # type: ignore
|
|
170
|
+
Enabled.ENABLED.value,
|
|
171
|
+
group="panda-config",
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
# Values need to be set before blocks are enabled, so wait here
|
|
175
|
+
yield from bps.wait(group="panda-config", timeout=GENERAL_TIMEOUT)
|
|
176
|
+
|
|
177
|
+
LOGGER.info(f"PandA sequencer table has been set to: {str(table)}")
|
|
178
|
+
table_readback = yield from bps.rd(panda.seq[1].table)
|
|
179
|
+
LOGGER.debug(f"PandA sequencer table readback is: {str(table_readback)}")
|
|
180
|
+
|
|
181
|
+
yield from arm_panda_for_gridscan(panda)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def arm_panda_for_gridscan(panda: HDFPanda, group="arm_panda_gridscan"):
|
|
185
|
+
yield from bps.abs_set(panda.seq[1].enable, Enabled.ENABLED.value, group=group) # type: ignore
|
|
186
|
+
yield from bps.abs_set(panda.pulse[1].enable, Enabled.ENABLED.value, group=group) # type: ignore
|
|
187
|
+
yield from bps.abs_set(panda.counter[1].enable, Enabled.ENABLED.value, group=group) # type: ignore
|
|
188
|
+
yield from bps.abs_set(panda.pcap.arm, PcapArm.ARMED.value, group=group) # type: ignore
|
|
189
|
+
yield from bps.wait(group=group, timeout=GENERAL_TIMEOUT)
|
|
190
|
+
LOGGER.info("PandA has been armed")
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def disarm_panda_for_gridscan(panda, group="disarm_panda_gridscan") -> MsgGenerator:
|
|
194
|
+
yield from bps.abs_set(panda.pcap.arm, PcapArm.DISARMED.value, group=group) # type: ignore
|
|
195
|
+
yield from bps.abs_set(panda.counter[1].enable, Enabled.DISABLED.value, group=group) # type: ignore
|
|
196
|
+
yield from bps.abs_set(panda.seq[1].enable, Enabled.DISABLED.value, group=group)
|
|
197
|
+
yield from bps.abs_set(panda.pulse[1].enable, Enabled.DISABLED.value, group=group)
|
|
198
|
+
yield from bps.abs_set(panda.pcap.enable, Enabled.DISABLED.value, group=group) # type: ignore
|
|
199
|
+
yield from bps.wait(group=group, timeout=GENERAL_TIMEOUT)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def set_panda_directory(panda_directory: Path) -> MsgGenerator:
|
|
203
|
+
"""Updates the root folder which is used by the PandA's PCAP."""
|
|
204
|
+
|
|
205
|
+
suffix = datetime.now().strftime("_%Y%m%d%H%M%S")
|
|
206
|
+
|
|
207
|
+
async def set_panda_dir():
|
|
208
|
+
await get_path_provider().update(directory=panda_directory, suffix=suffix)
|
|
209
|
+
|
|
210
|
+
yield from bps.wait_for([set_panda_dir])
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
from collections.abc import Callable
|
|
2
|
+
from functools import wraps
|
|
3
|
+
|
|
4
|
+
import bluesky.plan_stubs as bps
|
|
5
|
+
import bluesky.preprocessors as bpp
|
|
6
|
+
from blueapi.core import MsgGenerator
|
|
7
|
+
from dodal.devices.zebra import (
|
|
8
|
+
AUTO_SHUTTER_GATE,
|
|
9
|
+
AUTO_SHUTTER_INPUT,
|
|
10
|
+
DISCONNECT,
|
|
11
|
+
IN1_TTL,
|
|
12
|
+
IN3_TTL,
|
|
13
|
+
IN4_TTL,
|
|
14
|
+
PC_GATE,
|
|
15
|
+
PC_PULSE,
|
|
16
|
+
TTL_DETECTOR,
|
|
17
|
+
TTL_PANDA,
|
|
18
|
+
TTL_XSPRESS3,
|
|
19
|
+
ArmDemand,
|
|
20
|
+
EncEnum,
|
|
21
|
+
I03Axes,
|
|
22
|
+
RotationDirection,
|
|
23
|
+
Zebra,
|
|
24
|
+
)
|
|
25
|
+
from dodal.devices.zebra_controlled_shutter import ZebraShutter, ZebraShutterControl
|
|
26
|
+
|
|
27
|
+
from mx_bluesky.hyperion.log import LOGGER
|
|
28
|
+
|
|
29
|
+
ZEBRA_STATUS_TIMEOUT = 30
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def bluesky_retry(func: Callable):
|
|
33
|
+
"""Decorator that will retry the decorated plan if it fails.
|
|
34
|
+
|
|
35
|
+
Use this with care as it knows nothing about the state of the world when things fail.
|
|
36
|
+
If it is possible that your plan fails when the beamline is in a transient state that
|
|
37
|
+
the plan could not act on do not use this decorator without doing some more intelligent
|
|
38
|
+
clean up.
|
|
39
|
+
|
|
40
|
+
You should avoid using this decorator often in general production as it hides errors,
|
|
41
|
+
instead it should be used only for debugging these underlying errors.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
@wraps(func)
|
|
45
|
+
def newfunc(*args, **kwargs):
|
|
46
|
+
def log_and_retry(exception):
|
|
47
|
+
LOGGER.error(f"Function {func.__name__} failed with {exception}, retrying")
|
|
48
|
+
yield from func(*args, **kwargs)
|
|
49
|
+
|
|
50
|
+
yield from bpp.contingency_wrapper(
|
|
51
|
+
func(*args, **kwargs), except_plan=log_and_retry, auto_raise=False
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
return newfunc
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def arm_zebra(zebra: Zebra):
|
|
58
|
+
yield from bps.abs_set(zebra.pc.arm, ArmDemand.ARM, wait=True)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def tidy_up_zebra_after_rotation_scan(
|
|
62
|
+
zebra: Zebra,
|
|
63
|
+
zebra_shutter: ZebraShutter,
|
|
64
|
+
group="tidy_up_zebra_after_rotation",
|
|
65
|
+
wait=True,
|
|
66
|
+
):
|
|
67
|
+
yield from bps.abs_set(zebra.pc.arm, ArmDemand.DISARM, group=group)
|
|
68
|
+
yield from bps.abs_set(
|
|
69
|
+
zebra_shutter.control_mode, ZebraShutterControl.MANUAL, group=group
|
|
70
|
+
)
|
|
71
|
+
if wait:
|
|
72
|
+
yield from bps.wait(group, timeout=ZEBRA_STATUS_TIMEOUT)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def set_shutter_auto_input(zebra: Zebra, input: int, group="set_shutter_trigger"):
|
|
76
|
+
"""Set the input that the shutter uses when set to auto.
|
|
77
|
+
|
|
78
|
+
For more details see the ZebraShutter device."""
|
|
79
|
+
auto_shutter_control = zebra.logic_gates.and_gates[AUTO_SHUTTER_GATE]
|
|
80
|
+
yield from bps.abs_set(
|
|
81
|
+
auto_shutter_control.sources[AUTO_SHUTTER_INPUT], input, group
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@bluesky_retry
|
|
86
|
+
def setup_zebra_for_rotation(
|
|
87
|
+
zebra: Zebra,
|
|
88
|
+
zebra_shutter: ZebraShutter,
|
|
89
|
+
axis: EncEnum = I03Axes.OMEGA,
|
|
90
|
+
start_angle: float = 0,
|
|
91
|
+
scan_width: float = 360,
|
|
92
|
+
shutter_opening_deg: float = 2.5,
|
|
93
|
+
shutter_opening_s: float = 0.04,
|
|
94
|
+
direction: RotationDirection = RotationDirection.POSITIVE,
|
|
95
|
+
group: str = "setup_zebra_for_rotation",
|
|
96
|
+
wait: bool = True,
|
|
97
|
+
):
|
|
98
|
+
"""Set up the Zebra to collect a rotation dataset. Any plan using this is
|
|
99
|
+
responsible for setting the smargon velocity appropriately so that the desired
|
|
100
|
+
image width is achieved with the exposure time given here.
|
|
101
|
+
|
|
102
|
+
Parameters:
|
|
103
|
+
zebra: The zebra device to use
|
|
104
|
+
axis: I03 axes enum representing which axis to use for position
|
|
105
|
+
compare. Currently always omega.
|
|
106
|
+
start_angle: Position at which the scan should begin, in degrees.
|
|
107
|
+
scan_width: Total angle through which to collect, in degrees.
|
|
108
|
+
shutter_opening_deg:How many degrees of rotation it takes for the fast shutter
|
|
109
|
+
to open. Increases the gate width.
|
|
110
|
+
shutter_opening_s: How many seconds it takes for the fast shutter to open. The
|
|
111
|
+
detector pulse is delayed after the shutter signal by this
|
|
112
|
+
amount.
|
|
113
|
+
direction: RotationDirection enum for positive or negative.
|
|
114
|
+
Defaults to Positive.
|
|
115
|
+
group: A name for the group of statuses generated
|
|
116
|
+
wait: Block until all the settings have completed
|
|
117
|
+
"""
|
|
118
|
+
if not isinstance(direction, RotationDirection):
|
|
119
|
+
raise ValueError(
|
|
120
|
+
"Disallowed rotation direction provided to Zebra setup plan. "
|
|
121
|
+
"Use RotationDirection.POSITIVE or RotationDirection.NEGATIVE."
|
|
122
|
+
)
|
|
123
|
+
yield from bps.abs_set(zebra.pc.dir, direction.value, group=group)
|
|
124
|
+
LOGGER.info("ZEBRA SETUP: START")
|
|
125
|
+
# Set gate start, adjust for shutter opening time if necessary
|
|
126
|
+
LOGGER.info(f"ZEBRA SETUP: degrees to adjust for shutter = {shutter_opening_deg}")
|
|
127
|
+
LOGGER.info(f"ZEBRA SETUP: start angle start: {start_angle}")
|
|
128
|
+
LOGGER.info(f"ZEBRA SETUP: start angle adjusted, gate start set to: {start_angle}")
|
|
129
|
+
yield from bps.abs_set(zebra.pc.gate_start, start_angle, group=group)
|
|
130
|
+
# set gate width to total width
|
|
131
|
+
yield from bps.abs_set(
|
|
132
|
+
zebra.pc.gate_width, scan_width + shutter_opening_deg, group=group
|
|
133
|
+
)
|
|
134
|
+
LOGGER.info(
|
|
135
|
+
f"Pulse start set to shutter open time, set to: {abs(shutter_opening_s)}"
|
|
136
|
+
)
|
|
137
|
+
yield from bps.abs_set(zebra.pc.pulse_start, abs(shutter_opening_s), group=group)
|
|
138
|
+
# Set gate position to be angle of interest
|
|
139
|
+
yield from bps.abs_set(zebra.pc.gate_trigger, axis.value, group=group)
|
|
140
|
+
# Trigger the shutter with the gate
|
|
141
|
+
yield from bps.abs_set(
|
|
142
|
+
zebra_shutter.control_mode, ZebraShutterControl.AUTO, group=group
|
|
143
|
+
)
|
|
144
|
+
yield from set_shutter_auto_input(zebra, PC_GATE, group=group)
|
|
145
|
+
# Trigger the detector with a pulse
|
|
146
|
+
yield from bps.abs_set(zebra.output.out_pvs[TTL_DETECTOR], PC_PULSE, group=group)
|
|
147
|
+
# Don't use the fluorescence detector
|
|
148
|
+
yield from bps.abs_set(zebra.output.out_pvs[TTL_XSPRESS3], DISCONNECT, group=group)
|
|
149
|
+
yield from bps.abs_set(zebra.output.pulse_1.input, DISCONNECT, group=group)
|
|
150
|
+
LOGGER.info(f"ZEBRA SETUP: END - {'' if wait else 'not'} waiting for completion")
|
|
151
|
+
if wait:
|
|
152
|
+
yield from bps.wait(group, timeout=ZEBRA_STATUS_TIMEOUT)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
@bluesky_retry
|
|
156
|
+
def setup_zebra_for_gridscan(
|
|
157
|
+
zebra: Zebra,
|
|
158
|
+
zebra_shutter: ZebraShutter,
|
|
159
|
+
group="setup_zebra_for_gridscan",
|
|
160
|
+
wait=True,
|
|
161
|
+
):
|
|
162
|
+
yield from bps.abs_set(zebra.output.out_pvs[TTL_DETECTOR], IN3_TTL, group=group)
|
|
163
|
+
yield from bps.abs_set(
|
|
164
|
+
zebra_shutter.control_mode, ZebraShutterControl.AUTO, group=group
|
|
165
|
+
)
|
|
166
|
+
yield from set_shutter_auto_input(zebra, IN4_TTL, group=group)
|
|
167
|
+
yield from bps.abs_set(zebra.output.out_pvs[TTL_XSPRESS3], DISCONNECT, group=group)
|
|
168
|
+
yield from bps.abs_set(zebra.output.pulse_1.input, DISCONNECT, group=group)
|
|
169
|
+
|
|
170
|
+
if wait:
|
|
171
|
+
yield from bps.wait(group, timeout=ZEBRA_STATUS_TIMEOUT)
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
@bluesky_retry
|
|
175
|
+
def tidy_up_zebra_after_gridscan(
|
|
176
|
+
zebra: Zebra,
|
|
177
|
+
zebra_shutter: ZebraShutter,
|
|
178
|
+
group="tidy_up_zebra_after_gridscan",
|
|
179
|
+
wait=True,
|
|
180
|
+
) -> MsgGenerator:
|
|
181
|
+
yield from bps.abs_set(zebra.output.out_pvs[TTL_DETECTOR], PC_PULSE, group=group)
|
|
182
|
+
yield from bps.abs_set(
|
|
183
|
+
zebra_shutter.control_mode, ZebraShutterControl.MANUAL, group=group
|
|
184
|
+
)
|
|
185
|
+
yield from set_shutter_auto_input(zebra, PC_GATE, group=group)
|
|
186
|
+
|
|
187
|
+
if wait:
|
|
188
|
+
yield from bps.wait(group, timeout=ZEBRA_STATUS_TIMEOUT)
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
@bluesky_retry
|
|
192
|
+
def setup_zebra_for_panda_flyscan(
|
|
193
|
+
zebra: Zebra,
|
|
194
|
+
zebra_shutter: ZebraShutter,
|
|
195
|
+
group="setup_zebra_for_panda_flyscan",
|
|
196
|
+
wait=True,
|
|
197
|
+
):
|
|
198
|
+
# Forwards eiger trigger signal from panda
|
|
199
|
+
yield from bps.abs_set(zebra.output.out_pvs[TTL_DETECTOR], IN1_TTL, group=group)
|
|
200
|
+
|
|
201
|
+
# Forwards signal from PPMAC to fast shutter. High while panda PLC is running
|
|
202
|
+
yield from bps.abs_set(
|
|
203
|
+
zebra_shutter.control_mode, ZebraShutterControl.AUTO, group=group
|
|
204
|
+
)
|
|
205
|
+
yield from set_shutter_auto_input(zebra, IN4_TTL, group=group)
|
|
206
|
+
|
|
207
|
+
yield from bps.abs_set(zebra.output.out_pvs[TTL_XSPRESS3], DISCONNECT, group=group)
|
|
208
|
+
|
|
209
|
+
yield from bps.abs_set(
|
|
210
|
+
zebra.output.out_pvs[TTL_PANDA], IN3_TTL, group=group
|
|
211
|
+
) # Tells panda that motion is beginning/changing direction
|
|
212
|
+
|
|
213
|
+
if wait:
|
|
214
|
+
yield from bps.wait(group, timeout=ZEBRA_STATUS_TIMEOUT)
|