Skip to content

Commit d74dc0c

Browse files
Merge pull request #267 from ISISComputingGroup/ticket164wrapper
Plan wrapper functions to allow for easier cleanup of DAE setup changes
2 parents 7977b2b + 1883899 commit d74dc0c

File tree

10 files changed

+619
-12
lines changed

10 files changed

+619
-12
lines changed

doc/plan_stubs/plan_wrappers.md

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# Plan wrappers
2+
3+
Plan wrappers that temporarily modify [DAE (Data Acquisition Electronics)](/devices/dae.md) settings during a plan, automatically restoring the original values afterwards. This ensures that your experiments don't permanently change instrument configuration.
4+
5+
## Available Wrappers
6+
7+
### DAE Table
8+
9+
A function that wraps a plan to temporarily modify the DAE table.
10+
11+
API reference: {py:obj}`dae_table_wrapper<ibex_bluesky_core.plan_stubs.with_dae_tables>`
12+
13+
```python
14+
RE(
15+
with_dae_tables(
16+
bps.null(),
17+
dae=dae,
18+
new_settings=modified_settings
19+
)
20+
)
21+
```
22+
```python
23+
def plan():
24+
yield from with_dae_tables(scan(...), dae=dae, new_settings=modified_settings)
25+
```
26+
27+
(where `modified_settings` is a dataset in the form {py:obj}`DaeSettingsData <ibex_bluesky_core.devices.dae.DaeSettingsData>`)
28+
29+
30+
31+
### Num Periods
32+
33+
A function that wraps a plan to temporarily modify the number of periods.
34+
35+
API reference: {py:obj}`num_periods_wrapper<ibex_bluesky_core.plan_stubs.with_num_periods>`
36+
37+
```python
38+
RE(
39+
with_num_periods(
40+
bps.null(),
41+
dae=dae,
42+
number_of_periods=1000
43+
)
44+
)
45+
```
46+
```python
47+
def plan():
48+
yield from with_num_periods(scan(...), dae=dae, number_of_periods=1000)
49+
```
50+
51+
52+
### Time Channels
53+
A function that wraps a plan to temporarily modify the time channels boundaries.
54+
55+
API reference: {py:obj}`time_channels_wrapper<ibex_bluesky_core.plan_stubs.with_time_channels>`
56+
57+
```python
58+
RE(
59+
with_time_channels(
60+
bps.null(),
61+
dae=dae,
62+
new_settings=modified_settings
63+
)
64+
)
65+
```
66+
```python
67+
def plan():
68+
yield from with_time_channels(scan(...), dae=dae, new_settings=modified_settings)
69+
```
70+
(where `modified_settings` is a dataset in the form {py:obj}`DaeTCBSettingsData<ibex_bluesky_core.devices.dae.DaeTCBSettingsData>`)
71+
72+
## Usage
73+
74+
To use these wrappers, the plan written by the user must be wrapped by the function within the RunEngine:
75+
76+
``` python
77+
from ibex_bluesky_core.plan_stubs import with_num_periods
78+
from ibex_bluesky_core.devices.simpledae import SimpleDae
79+
80+
dae = SimpleDae() # Give your DAE options here
81+
82+
def plan():
83+
yield from with_num_periods(scan(...), dae=dae, number_of_periods=1000)
84+
```
85+
86+
the plan with the modified DAE settings, restoring the original settings afterwards.
87+

src/ibex_bluesky_core/devices/dae/_period_settings.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from xml.etree.ElementTree import tostring
88

99
from bluesky.protocols import Locatable, Location, Movable
10-
from ophyd_async.core import AsyncStatus, Device, SignalRW
10+
from ophyd_async.core import AsyncStatus, SignalRW, StandardReadable
1111

1212
from ibex_bluesky_core.devices import (
1313
isis_epics_signal_rw,
@@ -108,7 +108,9 @@ def _convert_period_settings_to_xml(current_xml: str, value: DaePeriodSettingsDa
108108
return tostring(root, encoding="unicode")
109109

110110

111-
class DaePeriodSettings(Device, Locatable[DaePeriodSettingsData], Movable[DaePeriodSettingsData]):
111+
class DaePeriodSettings(
112+
StandardReadable, Locatable[DaePeriodSettingsData], Movable[DaePeriodSettingsData]
113+
):
112114
"""Subdevice for the DAE hardware period settings."""
113115

114116
def __init__(self, dae_prefix: str, name: str = "") -> None:

src/ibex_bluesky_core/devices/dae/_tcb_settings.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from xml.etree.ElementTree import tostring
88

99
from bluesky.protocols import Locatable, Location, Movable
10-
from ophyd_async.core import AsyncStatus, Device, SignalRW
10+
from ophyd_async.core import AsyncStatus, SignalRW, StandardReadable
1111

1212
from ibex_bluesky_core.devices import (
1313
compress_and_hex,
@@ -120,7 +120,7 @@ def _convert_tcb_settings_to_xml(current_xml: str, settings: DaeTCBSettingsData)
120120
return tostring(root, encoding="unicode")
121121

122122

123-
class DaeTCBSettings(Device, Locatable[DaeTCBSettingsData], Movable[DaeTCBSettingsData]):
123+
class DaeTCBSettings(StandardReadable, Locatable[DaeTCBSettingsData], Movable[DaeTCBSettingsData]):
124124
"""Subdevice for the DAE time channel settings."""
125125

126126
def __init__(self, dae_prefix: str, name: str = "") -> None:

src/ibex_bluesky_core/plan_stubs.py renamed to src/ibex_bluesky_core/plan_stubs/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
from ophyd_async.epics.motor import Motor, UseSetMode
1313

1414
from ibex_bluesky_core.devices.reflectometry import ReflParameter
15+
from ibex_bluesky_core.plan_stubs._dae_table_wrapper import with_dae_tables
16+
from ibex_bluesky_core.plan_stubs._num_periods_wrapper import with_num_periods
17+
from ibex_bluesky_core.plan_stubs._time_channels_wrapper import with_time_channels
1518
from ibex_bluesky_core.utils import NamedReadableAndMovable
1619

1720
logger = logging.getLogger(__name__)
@@ -31,6 +34,9 @@
3134
"prompt_user_for_choice",
3235
"redefine_motor",
3336
"redefine_refl_parameter",
37+
"with_dae_tables",
38+
"with_num_periods",
39+
"with_time_channels",
3440
]
3541

3642

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
"""Wrap a plan with temporary modification to DAE Settings."""
2+
3+
from collections.abc import Generator
4+
5+
import bluesky.plan_stubs as bps
6+
import bluesky.preprocessors as bpp
7+
from bluesky.utils import Msg
8+
from ophyd_async.plan_stubs import ensure_connected
9+
10+
from ibex_bluesky_core.devices.dae import Dae, DaeSettingsData
11+
12+
13+
def with_dae_tables(
14+
plan: Generator[Msg, None, None], dae: Dae, new_settings: DaeSettingsData
15+
) -> Generator[Msg, None, None]:
16+
"""Wrap a plan with temporary modification to DAE Settings.
17+
18+
Args:
19+
plan: The plan to wrap.
20+
dae: The Dae instance.
21+
new_settings: The new DAE Settings to apply temporarily.
22+
23+
Returns:
24+
A generator which runs the plan with the modified DAE settings, restoring the original
25+
settings afterwards.
26+
27+
"""
28+
yield from ensure_connected(dae)
29+
30+
original_dae_setting = None
31+
32+
def _inner() -> Generator[Msg, None, None]:
33+
nonlocal original_dae_setting
34+
original_dae_setting = yield from bps.rd(dae.dae_settings)
35+
36+
yield from bps.mv(dae.dae_settings, new_settings)
37+
38+
return (yield from plan)
39+
40+
def _cleanup() -> Generator[Msg, None, None]:
41+
yield from bps.mv(dae.dae_settings, original_dae_setting)
42+
43+
return (yield from bpp.finalize_wrapper(_inner(), _cleanup()))
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
"""Wrap a plan with temporary modification to Periods Settings."""
2+
3+
from collections.abc import Generator
4+
5+
import bluesky.plan_stubs as bps
6+
import bluesky.preprocessors as bpp
7+
from bluesky.utils import Msg
8+
from ophyd_async.plan_stubs import ensure_connected
9+
10+
from ibex_bluesky_core.devices.dae import Dae
11+
12+
13+
def with_num_periods(
14+
plan: Generator[Msg, None, None], dae: Dae, number_of_periods: int
15+
) -> Generator[Msg, None, None]:
16+
"""Wrap a plan with temporary modification to Periods Settings.
17+
18+
Args:
19+
plan: The plan to wrap.
20+
dae: The Dae instance.
21+
number_of_periods: The number of periods to set to temporarily.
22+
23+
Returns:
24+
A generator which runs the plan with the modified number of periods, restoring the original
25+
number of periods afterwards.
26+
27+
"""
28+
original_num_periods = None
29+
30+
def _inner() -> Generator[Msg, None, None]:
31+
yield from ensure_connected(dae)
32+
nonlocal original_num_periods
33+
original_num_periods = yield from bps.rd(dae.number_of_periods)
34+
35+
yield from bps.mv(dae.number_of_periods, number_of_periods)
36+
37+
return (yield from plan)
38+
39+
def _cleanup() -> Generator[Msg, None, None]:
40+
yield from bps.mv(dae.number_of_periods, original_num_periods)
41+
42+
return (yield from bpp.finalize_wrapper(_inner(), _cleanup()))
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
"""Wrap a plan with temporary modification to Time Channel Settings."""
2+
3+
from collections.abc import Generator
4+
5+
import bluesky.plan_stubs as bps
6+
import bluesky.preprocessors as bpp
7+
from bluesky.utils import Msg
8+
from ophyd_async.plan_stubs import ensure_connected
9+
10+
from ibex_bluesky_core.devices.dae import Dae, DaeTCBSettingsData
11+
12+
13+
def with_time_channels(
14+
plan: Generator[Msg, None, None], dae: Dae, new_tcb_settings: DaeTCBSettingsData
15+
) -> Generator[Msg, None, None]:
16+
"""Wrap a plan with temporary modification to Time Channel Settings.
17+
18+
Args:
19+
plan: The plan to wrap.
20+
dae: The Dae instance.
21+
new_tcb_settings: The time channel settings to apply temporarily.
22+
23+
Returns:
24+
A generator which runs the plan with the modified TCB settings, restoring the original
25+
settings afterwards.
26+
27+
"""
28+
yield from ensure_connected(dae)
29+
30+
original_time_channels = None
31+
32+
def _inner() -> Generator[Msg, None, None]:
33+
nonlocal original_time_channels
34+
original_time_channels = yield from bps.rd(dae.tcb_settings)
35+
36+
yield from bps.mv(dae.tcb_settings, new_tcb_settings)
37+
38+
return (yield from plan)
39+
40+
def _cleanup() -> Generator[Msg, None, None]:
41+
yield from bps.mv(dae.tcb_settings, original_time_channels)
42+
43+
return (yield from bpp.finalize_wrapper(_inner(), _cleanup()))

tests/conftest.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import pytest
44
from bluesky.run_engine import RunEngine
55

6+
from ibex_bluesky_core.devices.dae import Dae
67
from ibex_bluesky_core.devices.simpledae import Controller, Reducer, SimpleDae, Waiter
78
from ibex_bluesky_core.run_engine import get_run_engine
89

@@ -36,3 +37,10 @@ async def simpledae() -> SimpleDae:
3637
)
3738
await dae.connect(mock=True)
3839
return dae
40+
41+
42+
@pytest.fixture
43+
async def dae() -> Dae:
44+
dae = Dae("UNITTEST:MOCK:")
45+
await dae.connect(mock=True)
46+
return dae

tests/devices/test_dae.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,6 @@
5050
)
5151

5252

53-
@pytest.fixture
54-
async def dae() -> Dae:
55-
dae = Dae("UNITTEST:MOCK:")
56-
await dae.connect(mock=True)
57-
return dae
58-
59-
6053
@pytest.fixture
6154
async def spectrum() -> DaeSpectra:
6255
spectrum = DaeSpectra(dae_prefix="UNITTEST:MOCK:", spectra=1, period=1)

0 commit comments

Comments
 (0)