Skip to content

Commit 760b1c7

Browse files
authored
Merge pull request #194 from ISISComputingGroup/unfuck_polling_plan
remove run decorator from polling plan
2 parents 1925b72 + f32e5f8 commit 760b1c7

File tree

5 files changed

+75
-54
lines changed

5 files changed

+75
-54
lines changed

doc/plan_stubs/polling.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Continuous polling
2+
3+
[`polling_plan`](ibex_bluesky_core.plans.polling_plan) - is used for moving a motor and dropping updates from a "readable" if no motor updates are provided. An example of this is a laser reading which updates much more quickly than a motor might register it's moved, so the laser readings are not really useful information.
4+
5+
This in itself doesn't start a bluesky "run" so you will need to use a `run_decorator` on any outer plan which calls this plan stub.
6+

doc/plans/plans.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,6 @@ These take "devices" so you can pass your own DAE object and a movable/readable
4040

4141
[`adaptive_scan`](ibex_bluesky_core.plans.adaptive_scan) - this is for an adaptive/relative-adaptive scan.
4242

43-
[`polling_plan`](ibex_bluesky_core.plans.polling_plan) - this is used for moving a motor and dropping updates from a "readable" if no motor updates are provided. An example of this is a laser reading which updates much more quickly than a motor might register it's moved, so the laser readings are not really useful information.
44-
4543
An example of using one of these could be:
4644

4745
```python

src/ibex_bluesky_core/plan_stubs.py

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,17 @@
22

33
import logging
44
from collections.abc import Callable, Generator
5-
from typing import ParamSpec, TypeVar, cast
5+
from typing import Any, ParamSpec, TypeVar, cast
66

7-
import bluesky.plan_stubs as bps
7+
from bluesky import plan_stubs as bps
8+
from bluesky.plan_stubs import trigger_and_read
89
from bluesky.preprocessors import finalize_wrapper
10+
from bluesky.protocols import Readable
911
from bluesky.utils import Msg
1012
from ophyd_async.epics.motor import Motor, UseSetMode
1113

1214
from ibex_bluesky_core.devices.reflectometry import ReflParameter
15+
from ibex_bluesky_core.utils import NamedReadableAndMovable
1316

1417
logger = logging.getLogger(__name__)
1518

@@ -24,6 +27,7 @@
2427
__all__ = [
2528
"call_qt_aware",
2629
"call_sync",
30+
"polling_plan",
2731
"prompt_user_for_choice",
2832
"redefine_motor",
2933
"redefine_refl_parameter",
@@ -142,3 +146,47 @@ def prompt_user_for_choice(*, prompt: str, choices: list[str]) -> Generator[Msg,
142146
choice = yield from call_sync(input, prompt)
143147

144148
return choice
149+
150+
151+
def polling_plan(
152+
motor: NamedReadableAndMovable, readable: Readable[Any], destination: float
153+
) -> Generator[Msg, None, None]:
154+
"""Move to a destination but drop updates from readable if motor position has not changed.
155+
156+
Note - this does not start a run, this should be done with a run_decorator or similar in an
157+
outer plan which calls this plan.
158+
159+
Args:
160+
motor: the motor to move.
161+
readable: the readable to read updates from, but drop if motor has not moved.
162+
destination: the destination position.
163+
164+
Returns:
165+
None
166+
167+
If we just used bp.scan() with a readable that updates more frequently than a motor can
168+
register that it has moved, we would have lots of updates with the same motor position,
169+
which may not be helpful.
170+
171+
"""
172+
yield from bps.checkpoint()
173+
yield from bps.create()
174+
reading = yield from bps.read(motor)
175+
yield from bps.read(readable)
176+
yield from bps.save()
177+
178+
# start the ramp
179+
status = yield from bps.abs_set(motor, destination, wait=False)
180+
while not status.done:
181+
yield from bps.create()
182+
new_reading = yield from bps.read(motor)
183+
yield from bps.read(readable)
184+
185+
if new_reading[motor.name]["value"] == reading[motor.name]["value"]:
186+
yield from bps.drop()
187+
else:
188+
reading = new_reading
189+
yield from bps.save()
190+
191+
# take a 'post' data point
192+
yield from trigger_and_read([motor, readable])

src/ibex_bluesky_core/plans/__init__.py

Lines changed: 10 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,7 @@
66
import bluesky.plans as bp
77
import matplotlib.pyplot as plt
88
from bluesky import plan_stubs as bps
9-
from bluesky.plan_stubs import trigger_and_read
10-
from bluesky.preprocessors import run_decorator
11-
from bluesky.protocols import NamedMovable, Readable
9+
from bluesky.protocols import NamedMovable
1210
from bluesky.utils import Msg
1311
from matplotlib.axes import Axes
1412
from ophyd_async.plan_stubs import ensure_connected
@@ -17,13 +15,20 @@
1715
from ibex_bluesky_core.devices.block import BlockWriteConfig, block_rw
1816
from ibex_bluesky_core.devices.simpledae import monitor_normalising_dae
1917
from ibex_bluesky_core.fitting import FitMethod
20-
from ibex_bluesky_core.plan_stubs import call_qt_aware
18+
from ibex_bluesky_core.plan_stubs import call_qt_aware, polling_plan
2119
from ibex_bluesky_core.utils import NamedReadableAndMovable, centred_pixel
2220

2321
if TYPE_CHECKING:
2422
from ibex_bluesky_core.devices.simpledae import SimpleDae
2523

26-
__all__ = ["adaptive_scan", "motor_adaptive_scan", "motor_scan", "polling_plan", "scan"]
24+
__all__ = [
25+
"NamedReadableAndMovable",
26+
"adaptive_scan",
27+
"motor_adaptive_scan",
28+
"motor_scan",
29+
"polling_plan",
30+
"scan",
31+
]
2732

2833

2934
def scan( # noqa: PLR0913
@@ -296,45 +301,3 @@ def motor_adaptive_scan( # noqa: PLR0913
296301
rel=rel,
297302
)
298303
)
299-
300-
301-
@run_decorator(md={})
302-
def polling_plan(
303-
motor: NamedReadableAndMovable, readable: Readable[Any], destination: float
304-
) -> Generator[Msg, None, None]:
305-
"""Move to a destination but drop updates from readable if motor position has not changed.
306-
307-
Args:
308-
motor: the motor to move.
309-
readable: the readable to read updates from, but drop if motor has not moved.
310-
destination: the destination position.
311-
312-
Returns:
313-
None
314-
315-
If we just used bp.scan() with a readable that updates more frequently than a motor can
316-
register that it has moved, we would have lots of updates with the same motor position,
317-
which may not be helpful.
318-
319-
"""
320-
yield from bps.checkpoint()
321-
yield from bps.create()
322-
reading = yield from bps.read(motor)
323-
yield from bps.read(readable)
324-
yield from bps.save()
325-
326-
# start the ramp
327-
status = yield from bps.abs_set(motor, destination, wait=False)
328-
while not status.done:
329-
yield from bps.create()
330-
new_reading = yield from bps.read(motor)
331-
yield from bps.read(readable)
332-
333-
if new_reading[motor.name]["value"] == reading[motor.name]["value"]:
334-
yield from bps.drop()
335-
else:
336-
reading = new_reading
337-
yield from bps.save()
338-
339-
# take a 'post' data point
340-
yield from trigger_and_read([motor, readable])

tests/plans/test_init.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# pyright: reportMissingParameterType=false
2-
32
from unittest.mock import patch
43

54
import pytest
5+
from bluesky.preprocessors import run_decorator
66
from ophyd_async.plan_stubs import ensure_connected
77
from ophyd_async.sim import SimMotor
88
from ophyd_async.testing import callback_on_mock_put, set_mock_value
@@ -17,11 +17,11 @@
1717
Waiter,
1818
)
1919
from ibex_bluesky_core.fitting import Gaussian
20+
from ibex_bluesky_core.plan_stubs import polling_plan
2021
from ibex_bluesky_core.plans import (
2122
adaptive_scan,
2223
motor_adaptive_scan,
2324
motor_scan,
24-
polling_plan,
2525
scan,
2626
)
2727

@@ -289,8 +289,14 @@ def lots_of_updates_between_set(*args, **kwargs):
289289
callback_on_mock_put(motor.user_readback, lots_of_updates_between_set)
290290
captured_events = []
291291

292+
@run_decorator(md={})
293+
def p():
294+
return (
295+
yield from polling_plan(motor=motor, readable=block_readable, destination=destination)
296+
) # pyright: ignore[reportArgumentType])
297+
292298
RE(
293-
polling_plan(motor=motor, readable=block_readable, destination=destination), # pyright: ignore[reportArgumentType]
299+
p(),
294300
{"event": lambda x, y: captured_events.append(y["data"])},
295301
)
296302

0 commit comments

Comments
 (0)