Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions src/blueapi/core/protocols.py

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Is there a better place for this to live?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

utils would make the most sense if there's a way of getting it working without import order issues

Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from dataclasses import dataclass, field
from typing import Any, Protocol, runtime_checkable


Expand Down Expand Up @@ -25,3 +26,19 @@ def build_and_connect(
timeout: float | None = None,
fixtures: dict[str, Any] | None = None,
) -> DeviceConnectResult: ...


@dataclass
class StaticDeviceManager:
devices: dict[str, Any] = field(default_factory=dict)
build_errors: dict[str, Exception] = field(default_factory=dict)
connection_errors: dict[str, Exception] = field(default_factory=dict)

def build_and_connect(
self,
*,
mock: bool = False,
timeout: float | None = None,
fixtures: dict[str, Any] | None = None,
) -> DeviceConnectResult:
return self
11 changes: 11 additions & 0 deletions src/blueapi/tutorial/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
env:
metadata:
instrument: tutorial-instrument
sources:
- kind: deviceManager
module: blueapi.tutorial.devices
- kind: planFunctions
module: blueapi.tutorial.plans
stomp:
enabled: true
url: tcp://localhost:61613/
11 changes: 11 additions & 0 deletions src/blueapi/tutorial/devices.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from ophyd_async.sim import SimMotor

from blueapi.core.protocols import StaticDeviceManager

devices = StaticDeviceManager()

x = SimMotor(name="x")
y = SimMotor(name="y")

devices.devices["x"] = x
devices.devices["y"] = y
43 changes: 43 additions & 0 deletions src/blueapi/tutorial/plans.py

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is just a direct copy of the wrapped count in dodal. I don't know if it is worth while paring this down to something more simple, or to add a move plan or something.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I don't think we should have a direct copy - paring it down to the basic would make it clearer, eg the validation checks are not useful for tutorial purposes.

Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from collections.abc import Sequence
from typing import Annotated, Any

import bluesky.plans as bp
from bluesky.protocols import Readable
from bluesky.utils import MsgGenerator
from ophyd_async.core import AsyncReadable
from pydantic import Field, NonNegativeFloat, validate_call


@validate_call(config={"arbitrary_types_allowed": True})
def count(
detectors: Annotated[
Sequence[Readable | AsyncReadable],
Field(
description="Set of readable devices, will take a reading at each point",
min_length=1,
),
],
num: Annotated[int, Field(description="Number of frames to collect", ge=1)] = 1,
delay: Annotated[
NonNegativeFloat | Sequence[NonNegativeFloat],
Field(
description="Delay between readings: if tuple, len(delay) == num - 1 and \
the delays are between each point, if value or None is the delay for every \
gap",
json_schema_extra={"units": "s"},
),
] = 0.0,
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.
"""
if isinstance(delay, Sequence):
assert len(delay) == num - 1, (
f"Number of delays given must be {num - 1}: was given {len(delay)}"
)
metadata = metadata or {}
metadata["shape"] = (num,)
yield from bp.count(tuple(detectors), num, delay=delay, md=metadata)
22 changes: 3 additions & 19 deletions tests/unit_tests/core/test_context.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
from __future__ import annotations

from dataclasses import dataclass, field
from dataclasses import dataclass
from pathlib import Path
from types import ModuleType, NoneType
from typing import Any, Generic, TypeVar, Union
from typing import Generic, TypeVar, Union
from unittest.mock import MagicMock, Mock, patch

import pytest
Expand Down Expand Up @@ -45,7 +45,7 @@
)
from blueapi.core import BlueskyContext, is_bluesky_compatible_device
from blueapi.core.context import DefaultFactory, generic_bounds, qualified_name
from blueapi.core.protocols import DeviceConnectResult, DeviceManager
from blueapi.core.protocols import DeviceManager, StaticDeviceManager
from blueapi.utils.invalid_config_error import InvalidConfigError

SIM_MOTOR_NAME = "sim"
Expand Down Expand Up @@ -669,22 +669,6 @@ def demo_plan(foo: int | None) -> MsgGenerator:
assert "foo" in schema.get("required", [])


@dataclass
class StaticDeviceManager:
devices: dict[str, Any] = field(default_factory=dict)
build_errors: dict[str, Exception] = field(default_factory=dict)
connection_errors: dict[str, Exception] = field(default_factory=dict)

def build_and_connect(
self,
*,
mock: bool = False,
timeout: float | None = None,
fixtures: dict[str, Any] | None = None,
) -> DeviceConnectResult:
return self


def test_empty_device_manager(empty_context: BlueskyContext):
sdm = StaticDeviceManager()
empty_context.with_device_manager(sdm)
Expand Down
Loading