Skip to content
Open
47 changes: 33 additions & 14 deletions src/dodal/devices/scintillator.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@


class InOut(StrictEnum):
"""Currently Hyperion only needs to move the scintillator out for data collection."""
"""Moves scintillator in and out of the beam."""

OUT = "Out"
OUT = "Out" # Out of beam
IN = "In" # In to beam
UNKNOWN = "Unknown"


Expand Down Expand Up @@ -45,38 +46,56 @@ def __init__(
self._scintillator_out_yz_mm = [
float(beamline_parameters[f"scin_{axis}_SCIN_OUT"]) for axis in ("y", "z")
]
self._scintillator_in_yz_mm = [
float(beamline_parameters[f"scin_{axis}_SCIN_IN"]) for axis in ("y", "z")
]
self._yz_tolerance_mm = [
float(beamline_parameters[f"scin_{axis}_tolerance"]) for axis in ("y", "z")
]

super().__init__(name)

def _get_selected_position(self, y: float, z: float) -> InOut:
current_pos = [y, z]
if all(
def _check_position(
self, current_pos: tuple[float, float], pos_to_check: tuple[float, float]
):
return all(
isclose(axis_pos, axis_in_beam, abs_tol=axis_tolerance)
for axis_pos, axis_in_beam, axis_tolerance in zip(
current_pos,
self._scintillator_out_yz_mm,
pos_to_check,
self._yz_tolerance_mm,
strict=False,
)
):
)

def _get_selected_position(self, y: float, z: float) -> InOut:
current_pos = (y, z)
if self._check_position(current_pos, self._scintillator_out_yz_mm):
return InOut.OUT

elif self._check_position(current_pos, self._scintillator_in_yz_mm):
return InOut.IN

else:
return InOut.UNKNOWN

def _check_aperture_parked(self):
if (
self._aperture_scatterguard().selected_aperture.get_value()
!= ApertureValue.PARKED
):
raise ValueError(
"Cannot move scintillator out if aperture/scatterguard is not parked"
)

async def _set_selected_position(self, position: InOut) -> None:
self._check_aperture_parked()
match position:
case InOut.OUT:
if (
self._aperture_scatterguard().selected_aperture.get_value()
!= ApertureValue.PARKED
):
raise ValueError(
"Cannot move scintillator out if aperture/scatterguard is not parked"
)
await self.y_mm.set(self._scintillator_out_yz_mm[0])
await self.z_mm.set(self._scintillator_out_yz_mm[1])
case InOut.IN:
await self.z_mm.set(self._scintillator_in_yz_mm[1])
await self.y_mm.set(self._scintillator_in_yz_mm[0])
case _:
raise ValueError(f"Cannot set scintillator to position {position}")
21 changes: 18 additions & 3 deletions tests/devices/test_scintillator.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import pytest
from ophyd_async.core import init_devices
from ophyd_async.testing import assert_value

from dodal.common.beamlines.beamline_parameters import GDABeamlineParameters
from dodal.devices.aperturescatterguard import ApertureScatterguard, ApertureValue
Expand Down Expand Up @@ -46,6 +47,7 @@ async def scintillator_and_ap_sg(
@pytest.mark.parametrize(
"y, z, expected_position",
[
(100.855, 101.5115, InOut.IN),
(-0.02, 0.1, InOut.OUT),
(0.1, 0.1, InOut.UNKNOWN),
(10.2, 15.6, InOut.UNKNOWN),
Expand Down Expand Up @@ -81,8 +83,20 @@ async def test_given_aperture_scatterguard_parked_when_set_to_out_position_then_

await scintillator.selected_pos.set(InOut.OUT)

assert await scintillator.y_mm.user_setpoint.get_value() == -0.02
assert await scintillator.z_mm.user_setpoint.get_value() == 0.1
await assert_value(scintillator.y_mm.user_setpoint, -0.02)
await assert_value(scintillator.z_mm.user_setpoint, 0.1)


async def test_given_aperture_scatterguard_parked_when_set_to_in_position_then_returns_expected(
scintillator_and_ap_sg: tuple[Scintillator, ApertureScatterguard],
):
scintillator, ap_sg = scintillator_and_ap_sg
ap_sg.return_value.selected_aperture.get_value.return_value = ApertureValue.PARKED # type: ignore

await scintillator.selected_pos.set(InOut.IN)

await assert_value(scintillator.y_mm.user_setpoint, 100.855)
await assert_value(scintillator.z_mm.user_setpoint, 101.5115)


async def test_given_aperture_scatterguard_not_parked_when_set_to_out_position_then_exception_raised(
Expand All @@ -92,6 +106,7 @@ async def test_given_aperture_scatterguard_not_parked_when_set_to_out_position_t
if position != ApertureValue.PARKED:
scintillator, ap_sg = scintillator_and_ap_sg
ap_sg.return_value.selected_aperture.get_value.return_value = position # type: ignore

with pytest.raises(ValueError):
await scintillator.selected_pos.set(InOut.OUT)
with pytest.raises(ValueError):
await scintillator.selected_pos.set(InOut.IN)
Loading