Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

make device factory decorator #597

Open
wants to merge 52 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
b862249
example beamline with implementation
stan-dot Jul 24, 2024
371e49c
small cleanup
stan-dot Jul 24, 2024
c6183bb
added controller demonstration
stan-dot Jul 24, 2024
992ce0c
fix wrong extension
stan-dot Jul 26, 2024
cb02123
Update src/dodal/beamlines/example_beamline.py
stan-dot Jul 26, 2024
861c713
Update src/dodal/beamlines/example_beamline.py
stan-dot Jul 26, 2024
bd54d7a
Update src/dodal/beamlines/example_beamline.py
stan-dot Jul 26, 2024
faf7fc7
Update src/dodal/beamlines/example_beamline.py
stan-dot Jul 26, 2024
88833c7
fix example beamline into a dataclass
stan-dot Jul 26, 2024
4199909
new-i22 as an example
stan-dot Jul 26, 2024
64d9a65
respond to feedback
stan-dot Jul 30, 2024
2ded84e
respond to comments
stan-dot Aug 1, 2024
d0eabcf
initial test
stan-dot Aug 1, 2024
6c674a2
comment out some tests to check coverage
stan-dot Aug 1, 2024
1e2e12f
lint
stan-dot Aug 7, 2024
7a33b22
use factory name for active devices dict key
stan-dot Aug 8, 2024
443ca48
this cannot be active devices, globals harder to mock
stan-dot Aug 8, 2024
cd3a66f
amend the test
stan-dot Aug 21, 2024
a17af52
added a red test for caching by name
stan-dot Aug 21, 2024
d7cc09e
fix the tests and add the support for a name flag
stan-dot Aug 21, 2024
28ac863
add the API for blueapi
stan-dot Aug 21, 2024
a27a983
remove todos
stan-dot Aug 22, 2024
2589824
unused utils deleted for coverage
stan-dot Aug 27, 2024
03b54b9
Just one i22 now
stan-dot Aug 27, 2024
b47221d
respond to comments
stan-dot Aug 28, 2024
c158735
rename to device factory
stan-dot Aug 28, 2024
1bf2909
is this enough to replace skip_device()
stan-dot Aug 28, 2024
74c5093
ruff
stan-dot Aug 28, 2024
ae0dfe6
remove the connect default timeout =10
stan-dot Aug 28, 2024
926da27
add support for the terminal use case and some tests
stan-dot Aug 29, 2024
c6c2fad
fix mock status and add the new decorator@
stan-dot Aug 29, 2024
1550a83
fix the tests
stan-dot Aug 29, 2024
4e2d0c3
delete bad comments, add good comments
stan-dot Aug 29, 2024
7df2b64
ruff
stan-dot Aug 29, 2024
3990818
feedback response
stan-dot Aug 29, 2024
ff80ab7
add return type annotation for testing coverage purposes
stan-dot Aug 29, 2024
528016d
respond to feedback
stan-dot Sep 9, 2024
3e96bd9
adapt tetramm
stan-dot Sep 10, 2024
a1500e8
respond to feedback
stan-dot Sep 10, 2024
5050a3c
add repr that includes the config
stan-dot Sep 10, 2024
e7acd12
lint
stan-dot Sep 10, 2024
9d3016f
remove old panda ref
stan-dot Sep 10, 2024
83a6974
fix import
stan-dot Sep 10, 2024
a13b7ca
fix the tests
stan-dot Sep 12, 2024
d6fe8c6
pin pyright version due to is_device check failing
stan-dot Sep 12, 2024
97fdc29
cancel pyright pinning
stan-dot Sep 13, 2024
5a5a0c7
renaming
stan-dot Sep 13, 2024
21df367
partial update to fix the test coverage for the factory
stan-dot Sep 13, 2024
0779785
maybe fixed
stan-dot Sep 13, 2024
982c1a2
imports fix
stan-dot Sep 13, 2024
422a87b
fix imports
stan-dot Sep 13, 2024
67e9a76
forgot one
stan-dot Sep 13, 2024
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
28 changes: 28 additions & 0 deletions docs/explanations/decisions/0003-make-devices-factory.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# 3. Add device factory decorator with lazy connect support

Date: 2024-04-26

## Status

Accepted

## Context

We should add a decorator to support verified device connection.

## Decision

DAQ members led us to this proposal:

- ophyd-async: make Device.connect(mock, timeout, force=False) be idempotent
- ophyd-async: make ensure_connected(\*devices) plan stub
- dodal: make device_factory(eager=False) decorator that makes, names, caches and optionally connects a device
- dodal: make get_device_factories() that returns all device factories and whether they should be connected at startup
- blueapi: call get_device_factories(), make all the Devices, connect the ones that should be connected at startup in parallel and log those that fail
- blueapi: when plan is called, run ensure_connected on all plan args and defaults that are Devices

We can then iterate on this if the parallel connect causes a broadcast storm. We could also in future add a monitor to a heartbeat PV per device in Device.connect so that it would reconnect next time it was called.

## Consequences

The changes above.
13 changes: 13 additions & 0 deletions src/dodal/aliases.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from collections.abc import Callable

try:
from typing import TypeAlias
except ImportError:
from typing import TypeAlias

Check warning on line 6 in src/dodal/aliases.py

View check run for this annotation

Codecov / codecov/patch

src/dodal/aliases.py#L5-L6

Added lines #L5 - L6 were not covered by tests
from ophyd.device import Device as OphydV1Device
from ophyd_async.core import Device as OphydV2Device

AnyDevice: TypeAlias = OphydV1Device | OphydV2Device
V1DeviceFactory: TypeAlias = Callable[..., OphydV1Device]
V2DeviceFactory: TypeAlias = Callable[..., OphydV2Device]
AnyDeviceFactory: TypeAlias = V1DeviceFactory | V2DeviceFactory
61 changes: 40 additions & 21 deletions src/dodal/beamlines/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,30 +20,49 @@
}


def all_beamline_modules() -> Iterable[str]:
"""
Get the names of all importable modules in beamlines
class ModuleDiscoveryError(Exception):
"""Custom exception for module discovery errors."""

pass


def get_all_beamline_modules() -> Iterable[str]:
"""
Get the names of all importable modules in the beamlines package by inspecting file paths.
Returns:
Iterable[str]: Generator of beamline module names
Iterable[str]: Generator of beamline module names (excluding __init__.py and __pycache__).
Raises:
ModuleDiscoveryError: If the beamlines module could not be found.
"""

# This is done by inspecting file names rather than modules to avoid
# premature importing
spec = importlib.util.find_spec(__name__)
if spec is not None:
assert spec.submodule_search_locations
search_paths = [Path(path) for path in spec.submodule_search_locations]
for path in search_paths:
for subpath in path.glob("**/*"):
if (
subpath.name.endswith(".py")
and subpath.name != "__init__.py"
and ("__pycache__" not in str(subpath))
):
yield subpath.with_suffix("").name
else:
raise KeyError(f"Unable to find {__name__} module")
try:
# Find the package spec for the current module
spec = importlib.util.find_spec(__name__)
except Exception as e:
raise ModuleDiscoveryError(

Check warning on line 42 in src/dodal/beamlines/__init__.py

View check run for this annotation

Codecov / codecov/patch

src/dodal/beamlines/__init__.py#L41-L42

Added lines #L41 - L42 were not covered by tests
f"Error while finding module spec for {__name__}: {e}"
) from e

if not spec or not spec.submodule_search_locations:
raise ModuleDiscoveryError(

Check warning on line 47 in src/dodal/beamlines/__init__.py

View check run for this annotation

Codecov / codecov/patch

src/dodal/beamlines/__init__.py#L47

Added line #L47 was not covered by tests
f"Unable to find module search locations for {__name__}"
)

search_paths = [Path(path) for path in spec.submodule_search_locations]

# Yield valid module names by filtering Python files
for path in search_paths:
for subpath in path.glob("**/*.py"):
if _is_valid_module(subpath):
yield subpath.stem # `.stem` gives the filename without the suffix


def _is_valid_module(subpath):
return (
subpath.name.endswith(".py")
and subpath.name != "__init__.py"
and ("__pycache__" not in str(subpath))
)


def all_beamline_names() -> Iterable[str]:
Expand All @@ -54,7 +73,7 @@
Iterable[str]: Generator of beamline names that dodal supports
"""
inverse_mapping = _module_name_overrides()
for module_name in all_beamline_modules():
for module_name in get_all_beamline_modules():
yield from inverse_mapping.get(module_name, set()).union({module_name})


Expand Down
4 changes: 3 additions & 1 deletion src/dodal/beamlines/i03.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

from dodal.common.beamlines.beamline_parameters import get_beamline_parameters
from dodal.common.beamlines.beamline_utils import (
BeamlinePrefix,
device_instantiation,
get_path_provider,
set_path_provider,
skip_device,
)
from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline
from dodal.common.udc_directory_provider import PandASubpathProvider
Expand Down Expand Up @@ -40,7 +42,7 @@
from dodal.devices.zebra_controlled_shutter import ZebraShutter
from dodal.devices.zocalo import ZocaloResults
from dodal.log import set_beamline as set_log_beamline
from dodal.utils import BeamlinePrefix, get_beamline_name, skip_device
from dodal.utils import get_beamline_name

ZOOM_PARAMS_FILE = (
"/dls_sw/i03/software/gda/configurations/i03-config/xml/jCameraManZoomLevels.xml"
Expand Down
8 changes: 6 additions & 2 deletions src/dodal/beamlines/i04.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
from dodal.common.beamlines.beamline_parameters import get_beamline_parameters
from dodal.common.beamlines.beamline_utils import device_instantiation
from dodal.common.beamlines.beamline_utils import (
BeamlinePrefix,
device_instantiation,
skip_device,
)
from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline
from dodal.devices.aperturescatterguard import (
AperturePosition,
Expand Down Expand Up @@ -29,7 +33,7 @@
from dodal.devices.zebra import Zebra
from dodal.devices.zebra_controlled_shutter import ZebraShutter
from dodal.log import set_beamline as set_log_beamline
from dodal.utils import BeamlinePrefix, get_beamline_name, skip_device
from dodal.utils import get_beamline_name

ZOOM_PARAMS_FILE = (
"/dls_sw/i04/software/gda/configurations/i04-config/xml/jCameraManZoomLevels.xml"
Expand Down
8 changes: 6 additions & 2 deletions src/dodal/beamlines/i04_1.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
from dodal.common.beamlines.beamline_utils import device_instantiation
from dodal.common.beamlines.beamline_utils import (
BeamlinePrefix,
device_instantiation,
skip_device,
)
from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline
from dodal.devices.backlight import Backlight
from dodal.devices.detector import DetectorParams
Expand All @@ -9,7 +13,7 @@
from dodal.devices.undulator import Undulator
from dodal.devices.zebra import Zebra
from dodal.log import set_beamline as set_log_beamline
from dodal.utils import BeamlinePrefix, get_beamline_name, skip_device
from dodal.utils import get_beamline_name

ZOOM_PARAMS_FILE = "/dls_sw/i04-1/software/gda/config/xml/jCameraManZoomLevels.xml"
DISPLAY_CONFIG = "/dls_sw/i04-1/software/gda_versions/var/display.configuration"
Expand Down
Loading
Loading