Skip to content

Commit

Permalink
Merge branch 'main' into 1032-apertures
Browse files Browse the repository at this point in the history
  • Loading branch information
stan-dot committed Feb 15, 2024
2 parents d7765a8 + 420aa05 commit b7bafab
Show file tree
Hide file tree
Showing 41 changed files with 736 additions and 154 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/code.yml
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ jobs:
pipx run build
- name: Upload sdist and wheel as artifacts
uses: actions/upload-artifact@v4.1.0
uses: actions/upload-artifact@v4.3.1
with:
name: ${{ env.DIST_WHEEL_PATH }}
path: dist
Expand Down Expand Up @@ -136,7 +136,7 @@ jobs:
echo "DIST_LOCKFILE_PATH=lockfiles-${{ env.CONTAINER_PYTHON }}-dist-${{ github.sha }}" >> $GITHUB_ENV
- name: Download wheel and lockfiles
uses: actions/[email protected].1
uses: actions/[email protected].2
with:
path: artifacts/
pattern: "*dist*"
Expand Down Expand Up @@ -216,7 +216,7 @@ jobs:

steps:
- name: Download wheel and lockfiles
uses: actions/[email protected].1
uses: actions/[email protected].2
with:
pattern: "*dist*"

Expand Down
67 changes: 67 additions & 0 deletions docs/user/how-to/create-beamline.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
Creating a new beamline
-----------------------

A beamline is a collection of devices that can be used together to run experiments, they may be read-only or capable of being set.
They include motors in the experiment hutch, optical components in the optics hutch, the synchrotron "machine" and more.

The following example creates a fictitious beamline ``w41``, with a simulated twin ``s41``.
``w41`` needs to monitor the status of the Synchrotron and has an AdAravisDetector.
``s41`` has a simulated clone of the AdAravisDetector, but not of the Synchrotron machine.

.. code-block:: python
from dodal.beamlines.beamline_utils import device_instantiation
from dodal.beamlines.beamline_utils import set_beamline as set_utils_beamline
from dodal.devices.areadetector.adaravis import AdAravisDetector
from dodal.devices.synchrotron import Synchrotron
from dodal.log import set_beamline as set_log_beamline
from dodal.utils import get_beamline_name, skip_device
BL = get_beamline_name("s41") # Default used when not on a live beamline
set_log_beamline(BL) # Configure logging and util functions
set_utils_beamline(BL)
"""
Define device factory functions below this point.
A device factory function is any function that has a return type which conforms
to one or more Bluesky Protocols.
"""
"""
A valid factory function which is:
- instantiated only on the live beamline
- a maximum of once
- can optionally be faked with ophyd simulated axes
- can optionally be connected concurrently by not waiting for connect to complete
- if constructor took a prefix, could optionally exclude the BLIXX prefix
""""
@skip_device(lambda: BL == "s41") # Conditionally do not instantiate this device
def synchrotron(
wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False
) -> Synchrotron:
"""Calls the Synchrotron class's constructor with name="synchrotron", prefix=""
If this is called when already instantiated, it will return the existing object.
"""
return device_instantiation(
Synchrotron,
"synchrotron",
"",
wait_for_connection,
fake_with_ophyd_sim,
bl_prefix=False,
)
def d11(name: str = "D11") -> AdAravisDetector:
"""
Also a valid Device factory function, but as multiple calls would instantiate
multiple copies of a device, discouraged.
"""
return AdAravisDetector(name=name, prefix=f"{BL}-DI-DCAM-01:")
``w41`` should also be added to the list of ``ALL_BEAMLINES`` in ``tests/beamlines/test_device_instantiation``.
This test checks that the function returns a type that conforms to Bluesky protocols,
that it always returns the same instance of the device and that the arguments passed
into the Device class constructor are valid.

1 change: 1 addition & 0 deletions docs/user/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ side-bar.
:maxdepth: 1

how-to/run-container
how-to/create-beamline.rst

+++

Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ dev = [
"mockito",
"pipdeptree",
"pre-commit",
"psutil",
"pydata-sphinx-theme>=0.12",
"pytest",
"pytest-asyncio",
Expand Down
19 changes: 11 additions & 8 deletions src/dodal/beamlines/beamline_parameters.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Any, Tuple, cast
from typing import Any, Optional, Tuple, cast

from dodal.log import LOGGER
from dodal.utils import get_beamline_name
Expand Down Expand Up @@ -87,11 +87,14 @@ def parse_list(cls, value: str):
return list_output


def get_beamline_parameters():
beamline_name = get_beamline_name("s03")
beamline_param_path = BEAMLINE_PARAMETER_PATHS.get(beamline_name)
if beamline_param_path is None:
raise KeyError(
"No beamline parameter path found, maybe 'BEAMLINE' environment variable is not set!"
)
def get_beamline_parameters(beamline_param_path: Optional[str] = None):
"""Loads the beamline parameters from the specified path, or according to the
environment variable if none is given"""
if not beamline_param_path:
beamline_name = get_beamline_name("s03")
beamline_param_path = BEAMLINE_PARAMETER_PATHS.get(beamline_name)
if beamline_param_path is None:
raise KeyError(
"No beamline parameter path found, maybe 'BEAMLINE' environment variable is not set!"
)
return GDABeamlineParameters.from_file(beamline_param_path)
17 changes: 17 additions & 0 deletions src/dodal/beamlines/beamline_utils.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import inspect
import tempfile
from typing import Callable, Dict, Final, List, Optional, TypeVar, cast

from bluesky.run_engine import call_in_bluesky_event_loop
from ophyd import Device as OphydV1Device
from ophyd.sim import make_fake_device
from ophyd_async.core import Device as OphydV2Device
from ophyd_async.core import DirectoryProvider, StaticDirectoryProvider
from ophyd_async.core import wait_for_connection as v2_device_wait_for_connection

from dodal.utils import AnyDevice, BeamlinePrefix, skip_device
Expand All @@ -13,6 +15,7 @@

ACTIVE_DEVICES: Dict[str, AnyDevice] = {}
BL = ""
DIRECTORY_PROVIDER: Optional[DirectoryProvider] = None


def set_beamline(beamline: str):
Expand Down Expand Up @@ -117,3 +120,17 @@ def device_instantiation(
if post_create:
post_create(device_instance)
return device_instance


def set_directory_provider(provider: DirectoryProvider):
global DIRECTORY_PROVIDER

DIRECTORY_PROVIDER = provider


def get_directory_provider() -> DirectoryProvider:
if DIRECTORY_PROVIDER is None:
set_directory_provider(
StaticDirectoryProvider(tempfile.NamedTemporaryFile().name, "")
)
return DIRECTORY_PROVIDER
16 changes: 16 additions & 0 deletions src/dodal/beamlines/i03.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from dodal.devices.oav.pin_image_recognition import PinTipDetection
from dodal.devices.panda_fast_grid_scan import PandAFastGridScan
from dodal.devices.qbpm1 import QBPM1
from dodal.devices.robot import BartRobot
from dodal.devices.s4_slit_gaps import S4SlitGaps
from dodal.devices.sample_shutter import SampleShutter
from dodal.devices.smargon import Smargon
Expand Down Expand Up @@ -433,3 +434,18 @@ def zocalo(
wait_for_connection,
fake_with_ophyd_sim,
)


def robot(
wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False
) -> BartRobot:
"""Get the i03 robot device, instantiate it if it hasn't already been.
If this is called when already instantiated in i03, it will return the existing object.
"""
return device_instantiation(
BartRobot,
"robot",
"-MO-ROBOT-01:",
wait_for_connection,
fake_with_ophyd_sim,
)
8 changes: 5 additions & 3 deletions src/dodal/beamlines/i24.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@
from dodal.devices.i24.pmac import PMAC
from dodal.devices.oav.oav_detector import OAV, OAVConfigParams
from dodal.devices.zebra import Zebra
from dodal.log import set_beamline
from dodal.log import set_beamline as set_log_beamline
from dodal.utils import get_beamline_name, skip_device

ZOOM_PARAMS_FILE = "/dls_sw/i24/software/gda/config/xml/jCameraManZoomLevels.xml"
DISPLAY_CONFIG = "/dls_sw/i24/software/gda_versions/var/display.configuration"

BL = get_beamline_name("s24")
set_beamline(BL)
set_log_beamline(BL)
set_utils_beamline(BL)


Expand Down Expand Up @@ -108,7 +108,9 @@ def oav(wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False) ->


@skip_device(lambda: BL == "s24")
def vgonio(wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False) -> OAV:
def vgonio(
wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False
) -> VGonio:
"""Get the i24 vgonio device, instantiate it if it hasn't already been.
If this is called when already instantiated, it will return the existing object.
"""
Expand Down
33 changes: 27 additions & 6 deletions src/dodal/beamlines/p38.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,33 @@
from dodal.beamlines.beamline_utils import device_instantiation
from dodal.beamlines.beamline_utils import set_beamline as set_utils_beamline
from dodal.devices.areadetector import AdAravisDetector
from dodal.utils import BeamlinePrefix
from dodal.log import set_beamline as set_log_beamline
from dodal.utils import get_beamline_name

PREFIX: str = BeamlinePrefix("p38").beamline_prefix
BL = get_beamline_name("p38")
set_log_beamline(BL)
set_utils_beamline(BL)


def d11(name: str = "D11") -> AdAravisDetector:
return AdAravisDetector(name=name, prefix=f"{PREFIX}-DI-DCAM-03:")
def d11(
wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False
) -> AdAravisDetector:
return device_instantiation(
AdAravisDetector,
"D11",
"-DI-DCAM-03:",
wait_for_connection,
fake_with_ophyd_sim,
)


def d12(name: str = "D12") -> AdAravisDetector:
return AdAravisDetector(name=name, prefix=f"{PREFIX}-DI-DCAM-04:")
def d12(
wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False
) -> AdAravisDetector:
return device_instantiation(
AdAravisDetector,
"D12",
"-DI-DCAM-04:",
wait_for_connection,
fake_with_ophyd_sim,
)
75 changes: 56 additions & 19 deletions src/dodal/beamlines/p45.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,58 @@
from dodal.beamlines.beamline_utils import device_instantiation
from dodal.beamlines.beamline_utils import set_beamline as set_utils_beamline
from dodal.devices.areadetector import AdAravisDetector
from dodal.devices.p45 import Choppers, TomoStageWithStretchAndSkew
from dodal.utils import BeamlinePrefix

PREFIX: str = BeamlinePrefix("p45").beamline_prefix


def sample(name: str = "sample_stage") -> TomoStageWithStretchAndSkew:
return TomoStageWithStretchAndSkew(name=name, prefix=f"{PREFIX}-MO-STAGE-01:")


def choppers(name: str = "chopper") -> Choppers:
return Choppers(name=name, prefix=f"{PREFIX}-MO-CHOP-01:")


def det(name: str = "det") -> AdAravisDetector:
return AdAravisDetector(name=name, prefix=f"{PREFIX}-EA-MAP-01:")


def diff(name: str = "diff") -> AdAravisDetector:
return AdAravisDetector(name=name, prefix=f"{PREFIX}-EA-DIFF-01:")
from dodal.log import set_beamline as set_log_beamline
from dodal.utils import get_beamline_name

BL = get_beamline_name("p45")
set_log_beamline(BL)
set_utils_beamline(BL)


def sample(
wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False
) -> TomoStageWithStretchAndSkew:
return device_instantiation(
TomoStageWithStretchAndSkew,
"sample_stage",
"-MO-STAGE-01:",
wait_for_connection,
fake_with_ophyd_sim,
)


def choppers(
wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False
) -> Choppers:
return device_instantiation(
Choppers,
"chopper",
"-MO-CHOP-01:",
wait_for_connection,
fake_with_ophyd_sim,
)


def det(
wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False
) -> AdAravisDetector:
return device_instantiation(
AdAravisDetector,
"det",
"-EA-MAP-01:",
wait_for_connection,
fake_with_ophyd_sim,
)


def diff(
wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False
) -> AdAravisDetector:
return device_instantiation(
AdAravisDetector,
"diff",
"-EA-DIFF-01:",
wait_for_connection,
fake_with_ophyd_sim,
)
4 changes: 3 additions & 1 deletion src/dodal/devices/DCM.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ def __init__(self, *args, daq_configuration_path: str, **kwargs):
)
# I03 configures the DCM Perp as a side effect of applying this fixed value to the DCM Offset after an energy change
# Nb this parameter is misleadingly named to confuse you
self.fixed_offset_mm = get_beamline_parameters()["DCM_Perp_Offset_FIXED"]
self.fixed_offset_mm = get_beamline_parameters(
daq_configuration_path + "/domain/beamlineParameters"
)["DCM_Perp_Offset_FIXED"]

"""
A double crystal monochromator (DCM), used to select the energy of the beam.
Expand Down
4 changes: 2 additions & 2 deletions src/dodal/devices/oav/pin_image_recognition/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,8 @@ async def _get_tip_position(
)

array_reading: dict[str, Reading] = await self.array_data.read()
array_data: NDArray[np.uint8] = array_reading[""]["value"]
timestamp: float = array_reading[""]["timestamp"]
array_data: NDArray[np.uint8] = array_reading[self.array_data.name]["value"]
timestamp: float = array_reading[self.array_data.name]["timestamp"]

try:
start_time = time.time()
Expand Down
Loading

0 comments on commit b7bafab

Please sign in to comment.