Skip to content

Commit

Permalink
Merge branch 'main' into pilatus
Browse files Browse the repository at this point in the history
  • Loading branch information
DiamondJoseph committed Apr 29, 2024
2 parents 1125f9c + 295b6ff commit 916c6c4
Show file tree
Hide file tree
Showing 10 changed files with 521 additions and 27 deletions.
14 changes: 14 additions & 0 deletions src/dodal/beamlines/i22.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from dodal.beamlines.beamline_utils import set_beamline as set_utils_beamline
from dodal.common.visit import StaticVisitDirectoryProvider
from dodal.devices.areadetector.pilatus import DLSPilatus
from dodal.devices.i22.fswitch import FSwitch
from dodal.devices.slits import Slits
from dodal.devices.tetramm import TetrammDetector
from dodal.log import set_beamline as set_log_beamline
Expand Down Expand Up @@ -149,3 +150,16 @@ def slits_6(
wait_for_connection,
fake_with_ophyd_sim,
)


def fswitch(
wait_for_connection: bool = True,
fake_with_ophyd_sim: bool = False,
) -> FSwitch:
return device_instantiation(
FSwitch,
"fswitch",
"-MO-FSWT-01:",
wait_for_connection,
fake_with_ophyd_sim,
)
10 changes: 7 additions & 3 deletions src/dodal/common/visit.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ def __call__(self) -> DirectoryInfo:


def attach_metadata(
plan: MsgGenerator,
plan: MsgGenerator, provider: UpdatingDirectoryProvider | None
) -> MsgGenerator:
"""
Attach data session metadata to the runs within a plan and make it correlate
Expand All @@ -183,10 +183,14 @@ def attach_metadata(
Yields:
Iterator[Msg]: Plan messages
"""
provider = beamline_utils.get_directory_provider()
if provider is None:
provider = beamline_utils.get_directory_provider()
yield from bps.wait_for([provider.update])
directory_info: DirectoryInfo = provider()
yield from bpp.inject_md_wrapper(plan, md={DATA_SESSION: directory_info.prefix})
# https://github.com/DiamondLightSource/dodal/issues/452
# As part of 452, write each dataCollection into their own folder, then can use resource_dir directly
data_session = directory_info.prefix.removesuffix("-")
yield from bpp.inject_md_wrapper(plan, md={DATA_SESSION: data_session})


attach_metadata_decorator = make_decorator(attach_metadata)
56 changes: 56 additions & 0 deletions src/dodal/devices/i22/fswitch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import asyncio
import time
from enum import Enum
from typing import Dict

from bluesky.protocols import Reading
from ophyd_async.core import StandardReadable
from ophyd_async.core.device import DeviceVector
from ophyd_async.epics.signal import epics_signal_r


class FilterState(str, Enum):
"""
Note that the in/out here refers to the internal rocker
position so a PV value of IN implies a filter OUT of beam
"""

IN_BEAM = "OUT"
OUT_BEAM = "IN"


class FSwitch(StandardReadable):
"""
Device for i22's fswitch. A filter switch for manipulating
compound refractive lenses. Also referred to as a transfocator.
This currently only implements the minimum
functionality for retrieving the number of lenses inserted.
Eventually this should be combined with the transfocator device in the i04
module but is currently incompatible as the Epics interfaces are different.
See https://github.com/DiamondLightSource/dodal/issues/399
"""

NUM_FILTERS = 128

def __init__(self, prefix: str, name: str = "") -> None:
self.filters = DeviceVector(
{
i: epics_signal_r(FilterState, f"{prefix}FILTER-{i:03}:STATUS_RBV")
for i in range(FSwitch.NUM_FILTERS)
}
)
super().__init__(name)

async def read(self) -> Dict[str, Reading]:
result = await asyncio.gather(
*(filter.get_value() for filter in self.filters.values())
)
num_in = sum(r.value == FilterState.IN_BEAM for r in result)
default_reading = await super().read()
return {
"number_of_lenses": Reading(value=num_in, timestamp=time.time()),
**default_reading,
}
2 changes: 1 addition & 1 deletion src/dodal/devices/robot.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
from bluesky.protocols import Descriptor, Movable, Reading
from ophyd_async.core import AsyncStatus, StandardReadable, wait_for_value
from ophyd_async.epics.signal import epics_signal_r, epics_signal_x
from ophyd_async.epics.signal.signal import epics_signal_rw_rbv

from dodal.devices.util.epics_util import epics_signal_rw_rbv
from dodal.log import LOGGER


Expand Down
7 changes: 0 additions & 7 deletions src/dodal/devices/util/epics_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

from ophyd import Component, Device, EpicsSignal
from ophyd.status import Status, StatusBase
from ophyd_async.epics.signal import epics_signal_rw

from dodal.devices.status import await_value
from dodal.log import LOGGER
Expand Down Expand Up @@ -126,9 +125,3 @@ def set(self, proc: int) -> Status:
lambda: self.proc.set(proc),
]
)


def epics_signal_rw_rbv(
T, write_pv: str
): # Remove when https://github.com/bluesky/ophyd-async/issues/139 is done
return epics_signal_rw(T, write_pv + "_RBV", write_pv)
28 changes: 28 additions & 0 deletions tests/devices/i22/test_fswitch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from unittest import mock

import pytest
from ophyd_async.core import DeviceCollector, set_sim_value

from dodal.devices.i22.fswitch import FilterState, FSwitch


@pytest.fixture
async def fswitch() -> FSwitch:
async with DeviceCollector(sim=True):
fswitch = FSwitch("DEMO-FSWT-01:")

return fswitch


async def test_reading_fswitch(fswitch: FSwitch):
set_sim_value(fswitch.filters.get(0), FilterState.OUT_BEAM)
set_sim_value(fswitch.filters.get(1), FilterState.OUT_BEAM)
set_sim_value(fswitch.filters.get(2), FilterState.OUT_BEAM)

reading = await fswitch.read()
assert reading == {
"number_of_lenses": {
"timestamp": mock.ANY,
"value": 125, # three filters out
}
}
6 changes: 3 additions & 3 deletions tests/devices/unit_tests/test_slits.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ async def slits() -> Slits:


async def test_reading_slits_reads_gaps_and_centres(slits: Slits):
set_sim_value(slits.x_gap.readback, 0.5)
set_sim_value(slits.y_centre.readback, 1.0)
set_sim_value(slits.y_gap.readback, 1.5)
set_sim_value(slits.x_gap.user_readback, 0.5)
set_sim_value(slits.y_centre.user_readback, 1.0)
set_sim_value(slits.y_gap.user_readback, 1.5)

await assert_reading(
slits,
Expand Down
14 changes: 6 additions & 8 deletions tests/devices/unit_tests/test_synchrotron.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@
from ophyd_async.core import DeviceCollector, StandardReadable, set_sim_value

from dodal.devices.synchrotron import (
Prefix,
Suffix,
Synchrotron,
SynchrotronMode,
)
Expand All @@ -29,15 +27,15 @@
READING_FIELDS = ["value", "alarm_severity"]
DESCRIPTION_FIELDS = ["source", "dtype", "shape"]
READING_ADDRESSES = [
f"sim://{Prefix.SIGNAL + Suffix.SIGNAL}",
f"sim://{Prefix.STATUS + Suffix.USER_COUNTDOWN}",
f"sim://{Prefix.TOP_UP + Suffix.COUNTDOWN}",
f"sim://{Prefix.TOP_UP + Suffix.END_COUNTDOWN}",
"soft://synchrotron-ring_current",
"soft://synchrotron-machine_user_countdown",
"soft://synchrotron-topup_start_countdown",
"soft://synchrotron-top_up_end_countdown",
]

CONFIG_ADDRESSES = [
f"sim://{Prefix.STATUS + Suffix.BEAM_ENERGY}",
f"sim://{Prefix.STATUS + Suffix.MODE}",
"soft://synchrotron-beam_energy",
"soft://synchrotron-synchrotron_mode",
]

READ_SIGNALS = [
Expand Down
10 changes: 5 additions & 5 deletions tests/devices/unit_tests/test_tetramm.py
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ async def test_prepare_arms_tetramm(
async def test_stage_sets_up_writer(
tetramm: TetrammDetector,
):
set_sim_value(tetramm.hdf.file_path_exists, 1)
set_sim_value(tetramm.hdf.file_path_exists, True)
await tetramm.stage()

assert (await tetramm.hdf.num_capture.get_value()) == 0
Expand All @@ -257,14 +257,14 @@ async def test_stage_sets_up_writer(
async def test_stage_sets_up_accurate_describe_output(
tetramm: TetrammDetector,
):
assert tetramm.describe() == {}
assert await tetramm.describe() == {}

set_sim_value(tetramm.hdf.file_path_exists, 1)
set_sim_value(tetramm.hdf.file_path_exists, True)
await tetramm.stage()

assert tetramm.describe() == {
assert await tetramm.describe() == {
TEST_TETRAMM_NAME: {
"source": "sim://MY-TETRAMM:HDF5:FullFileName_RBV",
"source": "soft://foobar-hdf-full_file_name",
"shape": (11, 1000),
"dtype": "array",
"external": "STREAM:",
Expand Down
Loading

0 comments on commit 916c6c4

Please sign in to comment.