Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use dodal devices more widely for I24 #116

Merged
merged 12 commits into from
Jul 19, 2024
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ dependencies = [
"requests",
"opencv-python",
"pydantic",
"dls-dodal @ git+https://github.com/DiamondLightSource/dodal.git@a4adbadfaae60b5158cf19647dba42b79280e103",
"dls-dodal @ git+https://github.com/DiamondLightSource/dodal.git@523e47764173bc46f284e7e8279f30757c9bde87",
"fastapi[all]<0.99",
"blueapi>=0.4.3-a3",
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from dodal.devices.hutch_shutter import HutchShutter, ShutterDemand
from dodal.devices.i24.aperture import Aperture
from dodal.devices.i24.beamstop import Beamstop
from dodal.devices.i24.dcm import DCM
from dodal.devices.i24.dual_backlight import DualBacklight
from dodal.devices.i24.I24_detector_motion import DetectorMotion
from dodal.devices.zebra import DISCONNECT, SOFT_IN3, Zebra
Expand Down Expand Up @@ -185,6 +186,7 @@ def run_extruder_plan(
beamstop: Beamstop = inject("beamstop"),
detector_stage: DetectorMotion = inject("detector_motion"),
shutter: HutchShutter = inject("shutter"),
dcm: DCM = inject("dcm"),
) -> MsgGenerator:
setup_logging()
start_time = datetime.now()
Expand Down Expand Up @@ -358,8 +360,9 @@ def run_extruder_plan(
dcid.notify_start()

if parameters.detector_name == "eiger":
wavelength = yield from bps.rd(dcm.wavelength_in_a)
logger.debug("Call nexgen server for nexus writing.")
call_nexgen(None, start_time, parameters, "extruder")
call_nexgen(None, start_time, parameters, wavelength, "extruder")

aborted = False
timeout_time = time.time() + parameters.num_images * parameters.exposure_time_s + 10
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from dodal.devices.hutch_shutter import HutchShutter, ShutterDemand
from dodal.devices.i24.aperture import Aperture
from dodal.devices.i24.beamstop import Beamstop
from dodal.devices.i24.dcm import DCM
from dodal.devices.i24.dual_backlight import DualBacklight
from dodal.devices.i24.I24_detector_motion import DetectorMotion
from dodal.devices.i24.pmac import PMAC
Expand Down Expand Up @@ -502,14 +503,15 @@ def start_i24(
# Open the hutch shutter
yield from bps.abs_set(shutter, ShutterDemand.OPEN, wait=True)

return start_time.ctime(), dcid
return start_time, dcid


@log.log_on_entry
def finish_i24(
zebra: Zebra,
pmac: PMAC,
shutter: HutchShutter,
dcm: DCM,
parameters: FixedTargetParameters,
):
logger.info(f"Finish I24 data collection with {parameters.detector_name} detector.")
Expand All @@ -520,7 +522,7 @@ def finish_i24(
filepath = parameters.visit + parameters.directory
filename = parameters.filename
transmission = (float(caget(pv.pilat_filtertrasm)),)
wavelength = float(caget(pv.dcm_lambda))
wavelength = yield from bps.rd(dcm.wavelength_in_a)

if parameters.detector_name == "pilatus":
logger.debug("Finish I24 Pilatus")
Expand Down Expand Up @@ -591,6 +593,7 @@ def run_fixed_target_plan(
beamstop: Beamstop = inject("beamstop"),
detector_stage: DetectorMotion = inject("detector_motion"),
shutter: HutchShutter = inject("shutter"),
dcm: DCM = inject("dcm"),
) -> MsgGenerator:
setup_logging()
# ABORT BUTTON
Expand Down Expand Up @@ -665,12 +668,14 @@ def run_fixed_target_plan(
tot_num_imgs = datasetsizei24(
parameters.num_exposures, parameters.chip_type, parameters.map_type
)
wavelength = yield from bps.rd(dcm.wavelength_in_a)
if parameters.detector_name == "eiger":
logger.debug("Start nexus writing service.")
call_nexgen(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could: It would be nice to have a test that covered that call_nexgen has been called with the expected params

chip_prog_dict,
start_time,
parameters,
wavelength,
total_numb_imgs=tot_num_imgs,
)

Expand Down Expand Up @@ -728,13 +733,13 @@ def run_fixed_target_plan(
caput(pv.eiger_acquire, 0)
caput(pv.eiger_ODcapture, "Done")

end_time = yield from finish_i24(zebra, pmac, shutter, parameters)
end_time = yield from finish_i24(zebra, pmac, shutter, dcm, parameters)

dcid.collection_complete(end_time, aborted=aborted)
logger.debug("Notify DCID of end of collection.")
dcid.notify_end()

logger.debug("Quick summary of settings")
logger.debug(f"Chip name = {parameters.filename} sub_dir = {parameters.directory}")
logger.debug(f"Start Time = {start_time}")
logger.debug(f"Start Time = {start_time.ctime()}")
logger.debug(f"End Time = {end_time}")
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
from blueapi.core import MsgGenerator
from dodal.beamlines import i24
from dodal.common import inject
from dodal.devices.i24.beamstop import Beamstop, BeamstopPositions
from dodal.devices.i24.dual_backlight import BacklightPositions, DualBacklight
from dodal.devices.i24.I24_detector_motion import DetectorMotion
from dodal.devices.i24.pmac import PMAC, EncReset, LaserSettings

Expand Down Expand Up @@ -610,7 +612,13 @@ def moveto(place: str = "origin", pmac: PMAC = inject("pmac")) -> MsgGenerator:


@log.log_on_entry
def moveto_preset(place: str, pmac: PMAC = inject("pmac")) -> MsgGenerator:
def moveto_preset(
place: str,
pmac: PMAC = inject("pmac"),
beamstop: Beamstop = inject("beamstop"),
backlight: DualBacklight = inject("backlight"),
det_stage: DetectorMotion = inject("detector_motion"),
) -> MsgGenerator:
setup_logging()

# Non Chip Specific Move
Expand All @@ -620,16 +628,22 @@ def moveto_preset(place: str, pmac: PMAC = inject("pmac")) -> MsgGenerator:

elif place == "load_position":
logger.info("load position")
caput(pv.bs_mp_select, "Robot")
caput(pv.bl_mp_select, "Out")
caput(pv.det_z, 1300)
yield from bps.abs_set(
beamstop.pos_select, BeamstopPositions.ROBOT, group=place
)
yield from bps.abs_set(backlight, BacklightPositions.OUT, group=place)
yield from bps.mv(det_stage.z, 1300)
yield from bps.wait(group=place)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: mixing abs_set with group and mv like this seems a bit odd it would be a bit better to do one of:

        yield from bps.mv(
            beamstop.pos_select, BeamstopPositions.ROBOT,
            backlight, BacklightPositions.OUT,
            det_stage.z, 1300
        )

or

        yield from bps.abs_set(
            beamstop.pos_select, BeamstopPositions.ROBOT, group=place
        )
        yield from bps.abs_set(backlight, BacklightPositions.OUT, group=place)
        yield from bps.abs_set(det_stage.z, 1300, group=place)
        yield from bps.wait(group=place)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd go with the second one because for the backlight I've overloaded set as it needs to turn it on and off depending on position. Not sure move would achieve that in this case...


elif place == "collect_position":
logger.info("collect position")
caput(pv.me14e_filter, 20)
yield from bps.mv(pmac.x, 0.0, pmac.y, 0.0, pmac.z, 0.0)
caput(pv.bs_mp_select, "Data Collection")
caput(pv.bl_mp_select, "In")
yield from bps.abs_set(
beamstop.pos_select, BeamstopPositions.DATA_COLLECTION, group=place
)
yield from bps.abs_set(backlight, BacklightPositions.IN, group=place)
yield from bps.wait(group=place)
Comment on lines +658 to +662
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: As above, a mv might be nicer?


elif place == "microdrop_position":
logger.info("microdrop align position")
Expand Down
7 changes: 0 additions & 7 deletions src/mx_bluesky/I24/serial/setup_beamline/pv.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,13 +155,6 @@ def __which__():
dcm_lambda = "BL24I-MO-DCM-01:LAMBDA"
dcm_energy = "BL24I-MO-DCM-01:ENERGY"

# OLD Mono. Left for short term reference only 10Nov21
# dcm_bragg = 'BL24I-OP-DCM-01:BRAGG'
# dcm_t2 = 'BL24I-OP-DCM-01:T2'
# dcm_roll1 = 'BL24I-OP-DCM-01:ROLL1'
# dcm_pitch2 = 'BL24I-OP-DCM-01:PITCH2'
# dcm_lambda = 'BL24I-OP-DCM-01:LAMBDA'
# dcm_energy = 'BL24I-OP-DCM-01:ENERGY'
# S2
s2_x_plus = "BL24I-AL-SLITS-02:X:PLUS"
s2_x_minus = "BL24I-AL-SLITS-02:X:MINUS"
Expand Down
2 changes: 2 additions & 0 deletions src/mx_bluesky/I24/serial/setup_beamline/pv_abstract.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
Takes the PV tables from I24's setup_beamline and wraps a slightly more
abstract wrapper around them.
"""

from typing import Union

from mx_bluesky.I24.serial.setup_beamline import pv
Expand Down Expand Up @@ -56,6 +57,7 @@ class pv:
detector_distance = pv.eiger_detdist
wavelength = pv.eiger_wavelength
transmission = "BL24I-EA-PILAT-01:cam1:FilterTransm"
filenameRBV = pv.eiger_ODfilenameRBV
file_name = pv.eiger_ODfilename
file_path = pv.eiger_ODfilepath
file_template = None
Expand Down
12 changes: 6 additions & 6 deletions src/mx_bluesky/I24/serial/write_nexus.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,16 @@

from mx_bluesky.I24.serial.fixed_target.ft_utils import ChipType, MappingType
from mx_bluesky.I24.serial.parameters import ExtruderParameters, FixedTargetParameters
from mx_bluesky.I24.serial.setup_beamline import Eiger, caget, cagetstring, pv
from mx_bluesky.I24.serial.setup_beamline import Eiger, caget, cagetstring

logger = logging.getLogger("I24ssx.nexus_writer")


def call_nexgen(
chip_prog_dict: Dict,
chip_prog_dict: Dict | None,
start_time: datetime,
parameters: ExtruderParameters | FixedTargetParameters,
wavelength: float,
expt_type: Literal["fixed-target", "extruder"] = "fixed-target",
total_numb_imgs: Optional[int] = None,
):
Expand All @@ -42,7 +43,7 @@ def call_nexgen(
currentchipmap = None
pump_status = parameters.pump_status

filename_prefix = cagetstring(pv.eiger_ODfilenameRBV)
filename_prefix = cagetstring(Eiger.pv.filenameRBV)
meta_h5 = (
pathlib.Path(parameters.visit)
/ parameters.directory
Expand All @@ -62,8 +63,7 @@ def call_nexgen(
logger.warning(f"Giving up waiting for {meta_h5} after {max_wait} seconds")
return False

transmission = (float(caget(pv.pilat_filtertrasm)),)
wavelength = float(caget(pv.dcm_lambda))
transmission = (float(caget(Eiger.pv.transmission)),)

if det_type == Eiger.name:
logger.debug(
Expand All @@ -76,7 +76,7 @@ def call_nexgen(

payload = {
"beamline": "i24",
"beam_center": [caget(pv.eiger_beamx), caget(pv.eiger_beamy)],
"beam_center": [caget(Eiger.pv.beamx), caget(Eiger.pv.beamy)],
"chipmap": currentchipmap,
"chip_info": chip_prog_dict,
"det_dist": parameters.detector_distance_mm,
Expand Down
7 changes: 7 additions & 0 deletions tests/I24/serial/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
)
from dodal.devices.i24.aperture import Aperture
from dodal.devices.i24.beamstop import Beamstop
from dodal.devices.i24.dcm import DCM
from dodal.devices.i24.dual_backlight import DualBacklight
from dodal.devices.i24.pmac import PMAC
from dodal.devices.zebra import Zebra
Expand Down Expand Up @@ -118,3 +119,9 @@ def pmac(RE):
patch_motor(pmac.z),
):
yield pmac


@pytest.fixture
def dcm(RE) -> DCM:
dcm = i24.dcm(fake_with_ophyd_sim=True)
return dcm
14 changes: 12 additions & 2 deletions tests/I24/serial/extruder/test_extruder_collect.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,11 +149,16 @@ def test_run_extruder_quickshot_with_eiger(
backlight,
beamstop,
detector_stage,
dcm,
dummy_params,
):
mock_params.from_file.return_value = dummy_params
fake_det.return_value = Eiger()
RE(run_extruder_plan(zebra, aperture, backlight, beamstop, detector_stage, shutter))
RE(
run_extruder_plan(
zebra, aperture, backlight, beamstop, detector_stage, shutter, dcm
)
)
assert fake_nexgen.call_count == 1
assert fake_dcid.call_count == 1
assert fake_sup.setup_beamline_for_collection_plan.call_count == 1
Expand Down Expand Up @@ -202,11 +207,16 @@ def test_run_extruder_pump_probe_with_pilatus(
backlight,
beamstop,
detector_stage,
dcm,
dummy_params_pp,
):
mock_params.from_file.return_value = dummy_params_pp
fake_det.return_value = Pilatus()
RE(run_extruder_plan(zebra, aperture, backlight, beamstop, detector_stage, shutter))
RE(
run_extruder_plan(
zebra, aperture, backlight, beamstop, detector_stage, shutter, dcm
)
)
assert fake_dcid.call_count == 1
assert fake_sup.move_detector_stage_to_position_plan.call_count == 1
mock_pp_plan.assert_called_once()
Expand Down
35 changes: 27 additions & 8 deletions tests/I24/serial/fixed_target/test_chip_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
from unittest.mock import ANY, MagicMock, call, mock_open, patch

import pytest
from dodal.devices.i24.beamstop import Beamstop
from dodal.devices.i24.dual_backlight import DualBacklight
from dodal.devices.i24.I24_detector_motion import DetectorMotion
from dodal.devices.i24.pmac import PMAC
from ophyd_async.core import get_mock_put

Expand Down Expand Up @@ -96,20 +99,28 @@ async def test_moveto_chip_aspecific(fake_log: MagicMock, pmac: PMAC, RE):
@patch("mx_bluesky.I24.serial.fixed_target.i24ssx_Chip_Manager_py3v1.setup_logging")
@patch("mx_bluesky.I24.serial.fixed_target.i24ssx_Chip_Manager_py3v1.caput")
async def test_moveto_preset(
fake_caput: MagicMock, fake_log: MagicMock, pmac: PMAC, RE
fake_caput: MagicMock,
fake_log: MagicMock,
pmac: PMAC,
beamstop: Beamstop,
backlight: DualBacklight,
detector_stage: DetectorMotion,
RE,
):
RE(moveto_preset("zero", pmac))
RE(moveto_preset("zero", pmac, beamstop, backlight, detector_stage))
assert await pmac.pmac_string.get_value() == "!x0y0z0"

RE(moveto_preset("load_position", pmac))
assert fake_caput.call_count == 3
RE(moveto_preset("load_position", pmac, beamstop, backlight, detector_stage))
assert await beamstop.pos_select.get_value() == "Robot"
assert await backlight.backlight_position.pos_level.get_value() == "Out"
assert await detector_stage.z.user_readback.get_value() == 1300


@pytest.mark.parametrize(
"pos_request, expected_num_caput, expected_pmac_move",
"pos_request, expected_num_caput, expected_pmac_move, other_devices",
[
("collect_position", 3, [0.0, 0.0, 0.0]),
("microdrop_position", 0, [6.0, -7.8, 0.0]),
("collect_position", 1, [0.0, 0.0, 0.0], True),
("microdrop_position", 0, [6.0, -7.8, 0.0], False),
],
)
@patch("mx_bluesky.I24.serial.fixed_target.i24ssx_Chip_Manager_py3v1.setup_logging")
Expand All @@ -120,16 +131,24 @@ async def test_moveto_preset_with_pmac_move(
pos_request: str,
expected_num_caput: int,
expected_pmac_move: List,
other_devices: bool,
pmac: PMAC,
beamstop: Beamstop,
backlight: DualBacklight,
detector_stage: DetectorMotion,
RE,
):
RE(moveto_preset(pos_request, pmac))
RE(moveto_preset(pos_request, pmac, beamstop, backlight, detector_stage))
assert fake_caput.call_count == expected_num_caput

assert await pmac.x.user_readback.get_value() == expected_pmac_move[0]
assert await pmac.y.user_readback.get_value() == expected_pmac_move[1]
assert await pmac.z.user_readback.get_value() == expected_pmac_move[2]

if other_devices:
assert await beamstop.pos_select.get_value() == "Data Collection"
assert await backlight.backlight_position.pos_level.get_value() == "In"


@pytest.mark.parametrize(
"laser_setting, expected_pmac_string",
Expand Down
Loading