DiamondLightSource / DiamondLightSource/dodal
Initial docs on best practices for writing plans, including references to the baseline_decorator for per-scan Monitor behaviour
- Dominant language
- Python
- Stars
- 5
- Forks
- 13
- Avg merge
- 3d 2h
- Merged PRs (30d)
- 10
Description
Bluesky has a standard preprocesser/plan decorator `baseline_decorator` that reads from a collection of Readable devices at the start and end of every plan. It would be nice to support this as a standard way to support per-plan metadata/per-scan Monitors as they were known in GDA.
These devices are usually not included in the scan logic (e.g. upstream optical components). They are likely to be consistent across multiple plans, although they may be overridden by explicitly passed arguments.
We should have tutorials in dodal (which may be moved later) on how to write a plan. It should include using this decorator and a description of a sensible way to use it. It may include a worked example.
In the following code snippet, the actual plan logic is contained in `inner_plan` which allows the use of the stage and run decorators to be used. [See ophyd-async docs](https://github.com/bluesky/ophyd-async/blob/c9d01fbf74a2e850b75c39ee819a4a84c56fecbd/docs/explanations/plan-stubs.md?plain=1#L21) on using those vs. `yield from stage(devices)`, [and here](https://github.com/bluesky/ophyd-async/blob/c9d01fbf74a2e850b75c39ee819a4a84c56fecbd/docs/explanations/plan-stubs.md?plain=1#L30). We should have a more complete tutorial in dodal for how to write plans that defers in places to these docs in ophyd-async.
Here is how we have defined BASELINE_DEVICES for `i22` where this is already being used: https://github.com/DiamondLightSource/i22-bluesky/blob/main/src/i22_bluesky/util/baseline.py
```python
from typing import Any
import bluesky.preprocessors as bpp
import bluesky.plans as bp
from bluesky.protocols import Readable
from bluesky.utils import MsgGenerator
from dodal.plan_stubs.data_session import attach_data_session_metadata_decorator
from techinque_bluesky.baseline import (
IXX_BASELINE_MEASUREMENTS,
)
_PLAN_NAME = "stopflow"
def experiment(
experiment_detector: Readable,
experiment_param: int,
baseline: set[Readable] = IXX_BASELINE_MEASUREMENTS,
metadata: dict[str, Any] | None = None,
) -> MsgGenerator:
# Collect metadata
plan_args = {
"experiment_device": experiment_detector.name,
"experiment_param": experiment_param,
"baseline": {device.name + ":" + repr(device) for device in baseline},
}
_md = {
"detectors": {experiment_detector.name},
"plan_args": plan_args,
"hints": {},
}
_md.update(metadata or {})
@bpp.baseline_decorator(baseline)
@attach_data_session_metadata_decorator() # TODO: Remove when NumTracker deployed widely
@bpp.stage_decorator([experiment_detector])
@bpp.run_decorator(md=_md)
def inner_plan():
yield from bp.count([experiment_detector], experiment_param)
yield from inner_plan()
```
Contributor guide
Assessment
This issue has not been assessed yet.