Skip to content

Commit

Permalink
Remove dls
Browse files Browse the repository at this point in the history
  • Loading branch information
DiamondJoseph committed Oct 21, 2024
1 parent fae3e9f commit 27c8fe0
Show file tree
Hide file tree
Showing 8 changed files with 256 additions and 69 deletions.
24 changes: 24 additions & 0 deletions src/dodal/plan_stubs/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
"""Plan stubs may be operations required to construct larger plans or contained
functionality that does not operate a complete Run and collect data but that may wish
to be run alone.
Functions that are the latter should be added to __export__ to allow them to be picked
up by blueapi. Functions in __export__ are checked to ensure that their arguments are
non-variadic (to enable well-typed forms to be generated).
"""

from .check_topup import check_topup_and_wait_if_necessary, wait_for_topup_complete
from .data_session import (
DATA_SESSION,
Expand All @@ -11,6 +19,7 @@
home_and_reset_wrapper,
move_and_reset_wrapper,
)
from .wrapped import move, move_relative, set_absolute, set_relative, sleep, wait

__all__ = [
"DATA_SESSION",
Expand All @@ -23,4 +32,19 @@
"home_and_reset_decorator",
"home_and_reset_wrapper",
"move_and_reset_wrapper",
"wait",
"set_absolute",
"set_relative",
"move",
"move_relative",
"sleep",
]

__export__ = [ # Stubs that should be exposed by blueapi
"wait",
"set_absolute",
"set_relative",
"move",
"move_relative",
"sleep",
]
4 changes: 2 additions & 2 deletions src/dodal/plan_stubs/data_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from bluesky import preprocessors as bpp
from bluesky.utils import MsgGenerator, make_decorator

from dodal.common.beamlines import beamline_utils
from dodal.common.beamlines.beamline_utils import get_path_provider
from dodal.common.types import UpdatingPathProvider

DATA_SESSION = "data_session"
Expand Down Expand Up @@ -31,7 +31,7 @@ def attach_data_session_metadata_wrapper(
Iterator[Msg]: Plan messages
"""
if provider is None:
provider = beamline_utils.get_path_provider()
provider = get_path_provider()
yield from bps.wait_for([provider.update])
ress = yield from bps.wait_for([provider.data_session])
data_session = ress[0].result()
Expand Down
142 changes: 142 additions & 0 deletions src/dodal/plan_stubs/wrapped.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import itertools
from collections.abc import Mapping
from typing import Annotated, Any, TypeVar

import bluesky.plan_stubs as bps
from bluesky.protocols import Movable
from bluesky.utils import MsgGenerator

"""
Wrappers for Bluesky built-in plan stubs with type hinting
"""

Group = Annotated[str, "String identifier used by 'wait' or stubs that await"]


def set_absolute(
movable: Movable, value: Any, group: Group | None = None, wait: bool = False
) -> MsgGenerator:
"""
Set a device, wrapper for `bp.abs_set`.
Args:
movable (Movable): The device to set
value (T): The new value
group (Group | None, optional): The message group to associate with the
setting, for sequencing. Defaults to None.
wait (bool, optional): The group should wait until all setting is complete
(e.g. a motor has finished moving). Defaults to False.
Returns:
MsgGenerator: Plan
Yields:
Iterator[MsgGenerator]: Bluesky messages
"""

return (yield from bps.abs_set(movable, value, group=group, wait=wait))


def set_relative(
movable: Movable, value: Any, group: Group | None = None, wait: bool = False
) -> MsgGenerator:
"""
Change a device, wrapper for `bp.rel_set`.
Args:
movable (Movable): The device to set
value (T): The new value
group (Group | None, optional): The message group to associate with the
setting, for sequencing. Defaults to None.
wait (bool, optional): The group should wait until all setting is complete
(e.g. a motor has finished moving). Defaults to False.
Returns:
MsgGenerator: Plan
Yields:
Iterator[MsgGenerator]: Bluesky messages
"""

return (yield from bps.rel_set(movable, value, group=group, wait=wait))


def move(moves: Mapping[Movable, Any], group: Group | None = None) -> MsgGenerator:
"""
Move a device, wrapper for `bp.mv`.
Args:
moves (Mapping[Movable, Any]): Mapping of Movables to target positions
group (Group | None, optional): The message group to associate with the
setting, for sequencing. Defaults to None.
Returns:
MsgGenerator: Plan
Yields:
Iterator[MsgGenerator]: Bluesky messages
"""

return (
# https://github.com/bluesky/bluesky/issues/1809
yield from bps.mv(*itertools.chain.from_iterable(moves.items()), group=group) # type: ignore
)


def move_relative(
moves: Mapping[Movable, Any], group: Group | None = None
) -> MsgGenerator:
"""
Move a device relative to its current position, wrapper for `bp.mvr`.
Args:
moves (Mapping[Movable, Any]): Mapping of Movables to target deltas
group (Group | None, optional): The message group to associate with the
setting, for sequencing. Defaults to None.
Returns:
MsgGenerator: Plan
Yields:
Iterator[MsgGenerator]: Bluesky messages
"""

return (
# https://github.com/bluesky/bluesky/issues/1809
yield from bps.mvr(*itertools.chain.from_iterable(moves.items()), group=group) # type: ignore
)


def sleep(time: float) -> MsgGenerator:
"""
Suspend all action for a given time, wrapper for `bp.sleep`
Args:
time (float): Time to wait in seconds
Returns:
MsgGenerator: Plan
Yields:
Iterator[MsgGenerator]: Bluesky messages
"""

return (yield from bps.sleep(time))


def wait(group: Group | None = None) -> MsgGenerator:
"""
Wait for a group status to complete, wrapper for `bp.wait`
Args:
group (Group | None, optional): The name of the group to wait for, defaults
to None.
Returns:
MsgGenerator: Plan
Yields:
Iterator[MsgGenerator]: Bluesky messages
"""

return (yield from bps.wait(group))
6 changes: 6 additions & 0 deletions src/dodal/plans/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
"""Plans should be full experimental proceedures that start and end runs and collect
data. To aid in making plans adaptable and to enable them to be exposed in blueapi,
functions in this module are checked to ensure that: metadata may be passed into them,
and that their arguments are non-variadic (to enable well-typed forms to be generated).
"""

from .scanspec import spec_scan
from .wrapped import count

Expand Down
3 changes: 1 addition & 2 deletions src/dodal/plans/scanspec.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import operator
from collections.abc import Mapping
from functools import reduce
from typing import Annotated, Any

Expand Down Expand Up @@ -27,7 +26,7 @@ def spec_scan(
Spec[Movable],
Field(description="ScanSpec modelling the path of the scan"),
],
metadata: Mapping[str, Any] | None = None,
metadata: dict[str, Any] | None = None,
) -> MsgGenerator:
"""Generic plan for reading `detectors` at every point of a ScanSpec `spec`."""
_md = {
Expand Down
3 changes: 3 additions & 0 deletions src/dodal/plans/wrapped.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ def count(
],
metadata: dict[str, Any] | None = None,
) -> MsgGenerator:
"""Reads from a number of devices.
Wraps bluesky.plans.count(det, num, delay, md=metadata) exposing only serializable
parameters and metadata to be consumed by JSONForms or similar."""
if isinstance(delay, list):
assert (
delays := len(delay)
Expand Down
21 changes: 11 additions & 10 deletions tests/plans/test_compliance.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
from types import ModuleType
from typing import Any, get_type_hints

from dls_bluesky_core.core import MsgGenerator, PlanGenerator
from bluesky.utils import MsgGenerator

from dodal import plan_stubs, plans
from dodal.common.types import PlanGenerator


def is_bluesky_plan_generator(func: Any) -> bool:
Expand All @@ -20,10 +21,10 @@ def get_named_subset(names: list[str]):
for name in names:
yield getattr(mod, name)

if "__export__" in mod.__dict__:
yield from get_named_subset(mod.get("__export__"))
elif "__all__" in mod.__dict__:
yield from get_named_subset(mod.get("__all__"))
if explicit_exports := mod.__dict__.get("__export__"):
yield from get_named_subset(explicit_exports)
elif implicit_exports := mod.__dict__.get("__all__"):
yield from get_named_subset(implicit_exports)
else:
for name, value in mod.__dict__.items():
if not name.startswith("_"):
Expand Down Expand Up @@ -60,9 +61,9 @@ def test_plans_comply():


def test_stubs_comply():
for plan in get_all_available_generators(plan_stubs):
if is_bluesky_plan_generator(plan):
signature = inspect.Signature.from_callable(plan)
assert_hard_requirements(plan, signature)
for stub in get_all_available_generators(plan_stubs):
if is_bluesky_plan_generator(stub):
signature = inspect.Signature.from_callable(stub)
assert_hard_requirements(stub, signature)
if "metadata" in signature.parameters:
assert_metadata_requirements(plan, signature)
assert_metadata_requirements(stub, signature)
Loading

0 comments on commit 27c8fe0

Please sign in to comment.