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 71 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
2a43ada
example beamline with implementation
stan-dot Jul 24, 2024
a0f7516
small cleanup
stan-dot Jul 24, 2024
73717bb
added controller demonstration
stan-dot Jul 24, 2024
9ca8158
fix wrong extension
stan-dot Jul 26, 2024
8785072
Update src/dodal/beamlines/example_beamline.py
stan-dot Jul 26, 2024
6fd997a
Update src/dodal/beamlines/example_beamline.py
stan-dot Jul 26, 2024
d124d39
Update src/dodal/beamlines/example_beamline.py
stan-dot Jul 26, 2024
6d15da1
Update src/dodal/beamlines/example_beamline.py
stan-dot Jul 26, 2024
2a7b434
fix example beamline into a dataclass
stan-dot Jul 26, 2024
d4c1966
new-i22 as an example
stan-dot Jul 26, 2024
4b6b461
respond to feedback
stan-dot Jul 30, 2024
525ff16
respond to comments
stan-dot Aug 1, 2024
0abbd03
initial test
stan-dot Aug 1, 2024
a3158d3
comment out some tests to check coverage
stan-dot Aug 1, 2024
933950e
lint
stan-dot Aug 7, 2024
16901f6
use factory name for active devices dict key
stan-dot Aug 8, 2024
6ba5373
this cannot be active devices, globals harder to mock
stan-dot Aug 8, 2024
2339cd0
amend the test
stan-dot Aug 21, 2024
0742041
added a red test for caching by name
stan-dot Aug 21, 2024
81610a1
fix the tests and add the support for a name flag
stan-dot Aug 21, 2024
b39af24
add the API for blueapi
stan-dot Aug 21, 2024
fa5c9ef
remove todos
stan-dot Aug 22, 2024
5dd9536
unused utils deleted for coverage
stan-dot Aug 27, 2024
b00629a
Just one i22 now
stan-dot Aug 27, 2024
0b81dfa
respond to comments
stan-dot Aug 28, 2024
1429067
rename to device factory
stan-dot Aug 28, 2024
fc3e45a
is this enough to replace skip_device()
stan-dot Aug 28, 2024
7fa038e
ruff
stan-dot Aug 28, 2024
d00d131
remove the connect default timeout =10
stan-dot Aug 28, 2024
7126703
add support for the terminal use case and some tests
stan-dot Aug 29, 2024
32608cf
fix mock status and add the new decorator@
stan-dot Aug 29, 2024
e10c941
fix the tests
stan-dot Aug 29, 2024
9162925
delete bad comments, add good comments
stan-dot Aug 29, 2024
77082e4
ruff
stan-dot Aug 29, 2024
940cf3b
feedback response
stan-dot Aug 29, 2024
1a9e5fe
add return type annotation for testing coverage purposes
stan-dot Aug 29, 2024
1ffb221
respond to feedback
stan-dot Sep 9, 2024
84953f0
adapt tetramm
stan-dot Sep 10, 2024
fe4cfb8
respond to feedback
stan-dot Sep 10, 2024
00747a5
add repr that includes the config
stan-dot Sep 10, 2024
4ea0de7
lint
stan-dot Sep 10, 2024
733d623
remove old panda ref
stan-dot Sep 10, 2024
dab0263
fix import
stan-dot Sep 10, 2024
73ccdc3
fix the tests
stan-dot Sep 12, 2024
4b7feeb
pin pyright version due to is_device check failing
stan-dot Sep 12, 2024
9a982bc
cancel pyright pinning
stan-dot Sep 13, 2024
e610a46
renaming
stan-dot Sep 13, 2024
9ed7099
partial update to fix the test coverage for the factory
stan-dot Sep 13, 2024
63a11eb
maybe fixed
stan-dot Sep 13, 2024
1103792
imports fix
stan-dot Sep 13, 2024
ada96ba
fix imports
stan-dot Sep 13, 2024
67ec50b
forgot one
stan-dot Sep 13, 2024
7791803
added test to increase coverage, still broken a bit
stan-dot Sep 20, 2024
68a3261
towards a stable problem definitio a stable problme definition
stan-dot Sep 20, 2024
8bf345e
fix the test instance
stan-dot Sep 20, 2024
f9afc85
finally fixed tests
stan-dot Sep 20, 2024
c28359c
lint
stan-dot Sep 20, 2024
d8046b7
fix tests
stan-dot Sep 20, 2024
3083cec
exit some tests early
stan-dot Sep 20, 2024
b0711b2
remove pytest skip
stan-dot Sep 23, 2024
4d216d5
hardcode the i22 slit refs
stan-dot Sep 23, 2024
6b3f62a
i22 remove device_instantiation
stan-dot Sep 24, 2024
b6a9a34
respond to feedback
stan-dot Sep 24, 2024
77a8f12
more tests to raise coverage
stan-dot Sep 24, 2024
108d78e
respond to comment on the connect logic in controller call
stan-dot Sep 25, 2024
98414db
deprecation warning added
stan-dot Sep 27, 2024
e06e4ee
remove the v1 ophyd relevant piece of logic - fake_with_ophyd_sim
stan-dot Sep 27, 2024
5d43fa8
halfway through
stan-dot Sep 30, 2024
757aa98
identified the issue
stan-dot Oct 1, 2024
9138761
name param issues
stan-dot Oct 2, 2024
97f5450
add new appropriate mocks
stan-dot Oct 3, 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.
10 changes: 10 additions & 0 deletions src/dodal/aliases.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from collections.abc import Callable
from typing import TypeAlias

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(
f"Error while finding module spec for {__name__}: {e}"
) from e

if not spec or not spec.submodule_search_locations:
raise ModuleDiscoveryError(
Copy link
Contributor

Choose a reason for hiding this comment

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

Should: A unit test covering this would be good

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok that is one of the lines not covered in the test, I'll try to come up with something

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 @@ def all_beamline_names() -> Iterable[str]:
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