diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml new file mode 100644 index 0000000..3bda971 --- /dev/null +++ b/.github/workflows/documentation.yml @@ -0,0 +1,8 @@ +name: sphinx + +on: [push, pull_request, workflow_call] + +jobs: + call_sphinx_builder: + uses: ISISComputingGroup/reusable-workflows/.github/workflows/sphinx.yml@main + secrets: inherit diff --git a/.gitignore b/.gitignore index 61cd4f9..c9514d4 100644 --- a/.gitignore +++ b/.gitignore @@ -158,3 +158,7 @@ cython_debug/ .vscode/ coverage_html_report/ + +# Sphinx generated +doc/generated/* +_build/ diff --git a/doc/_api.rst b/doc/_api.rst new file mode 100644 index 0000000..d7e88d5 --- /dev/null +++ b/doc/_api.rst @@ -0,0 +1,12 @@ +:orphan: + +API +=== + +.. autosummary:: + :toctree: generated + :template: custom-module-template.rst + :recursive: + + ibex_bluesky_core + diff --git a/doc/_templates/custom-module-template.rst b/doc/_templates/custom-module-template.rst new file mode 100644 index 0000000..f483c41 --- /dev/null +++ b/doc/_templates/custom-module-template.rst @@ -0,0 +1,40 @@ + + +{{ ('``' + fullname + '``') | underline }} + +{%- set filtered_members = [] %} +{%- for item in members %} + {%- if item in functions + classes + exceptions + attributes %} + {% set _ = filtered_members.append(item) %} + {%- endif %} +{%- endfor %} + +.. automodule:: {{ fullname }} + :members: + :show-inheritance: + + {% block modules %} + {% if modules %} + .. rubric:: Submodules + + .. autosummary:: + :toctree: + :template: custom-module-template.rst + :recursive: + {% for item in modules %} + {{ item }} + {%- endfor %} + {% endif %} + {% endblock %} + + {% block members %} + {% if filtered_members %} + .. rubric:: Members + + .. autosummary:: + :nosignatures: + {% for item in filtered_members %} + {{ item }} + {%- endfor %} + {% endif %} + {% endblock %} \ No newline at end of file diff --git a/doc/architectural_decisions/001-repo-structure.md b/doc/architectural_decisions/001-repo-structure.md index bd816c3..0f6e1db 100644 --- a/doc/architectural_decisions/001-repo-structure.md +++ b/doc/architectural_decisions/001-repo-structure.md @@ -1,19 +1,19 @@ # Repository structure -### Status +## Status Current -### Context +## Context We need to decide how to structure our bluesky and scans code, in terms of technical repository layout. -### Present +## Present Tom & Kathryn -### Decision +## Decision We will create a `core` repository, and publish it on PyPI. @@ -31,7 +31,7 @@ depend on this repository. This `core` repository is analogous to a similar repo, `dodal`, being used at Diamond. -### Consequences +## Consequences - We will have some bluesky code across multiple repositories. - Other groups should be able to: diff --git a/doc/architectural_decisions/002-use-ophyd-async.md b/doc/architectural_decisions/002-use-ophyd-async.md index c85cce7..944c3cd 100644 --- a/doc/architectural_decisions/002-use-ophyd-async.md +++ b/doc/architectural_decisions/002-use-ophyd-async.md @@ -1,10 +1,10 @@ # Use `ophyd-async` -### Status +## Status Current -### Context +## Context We need to decide whether to use `ophyd` or `ophyd-async` as our bluesky device abstraction layer. @@ -20,15 +20,15 @@ The *primary* differences are: - `ophyd-async` has better support for non-channel-access backends (notably, PVA) - Reduction in boilerplate -### Present +## Present Tom & Kathryn -### Decision +## Decision We will use `ophyd-async`. -### Consequences +## Consequences - `ophyd-async` will allow us to use PVAccess easily. - `ophyd-async` will allow us to do fly scanning, if required in future, more easily than `ophyd` diff --git a/doc/architectural_decisions/003-run-in-process.md b/doc/architectural_decisions/003-run-in-process.md index baf0858..bd4cbe7 100644 --- a/doc/architectural_decisions/003-run-in-process.md +++ b/doc/architectural_decisions/003-run-in-process.md @@ -1,26 +1,26 @@ # Run in-process -### Status +## Status Current -### Context +## Context `bluesky` code can be run in several ways: - By the user at an interactive shell, directly calling the run engine in-process. - By a central worker process, to which the user would "submit" plans to run. - See DLS's `blueapi` for an example of a REST API for submitting plans to the run engine. -### Present +## Present Tom & Kathryn -### Decision +## Decision We will run bluesky plans in-process for now, while not _excluding_ the possibility that they could be run behind a worker process at some point in future. -### Consequences +## Consequences - We will not, at least initially, have to write or use an extra worker process. - Users will have the ability to call the run engine directly diff --git a/doc/docs_logging_callback.md b/doc/callbacks/docs_logging_callback.md similarity index 100% rename from doc/docs_logging_callback.md rename to doc/callbacks/docs_logging_callback.md diff --git a/doc/plotting.md b/doc/callbacks/plotting.md similarity index 100% rename from doc/plotting.md rename to doc/callbacks/plotting.md diff --git a/doc/conf.py b/doc/conf.py new file mode 100644 index 0000000..6358698 --- /dev/null +++ b/doc/conf.py @@ -0,0 +1,48 @@ +# Configuration file for the Sphinx documentation builder. +# +# For the full list of built-in configuration values, see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Project information ----------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information + +import os +import sys + +sys.path.insert(0, os.path.abspath("../src")) + +project = "ibex_bluesky_core" +copyright = "" +author = "ISIS Experiment Controls" +release = "0.1" + + +# -- General configuration --------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration + +extensions = [ + "myst_parser", + "sphinx.ext.autodoc", + # and making summary tables at the top of API docs + "sphinx.ext.autosummary", + # This can parse google style docstrings + "sphinx.ext.napoleon", + # For linking to external sphinx documentation + "sphinx.ext.intersphinx", + # Add links to source code in API docs + "sphinx.ext.viewcode", +] +napoleon_google_docstring = True +napoleon_numpy_docstring = False + +templates_path = ["_templates"] +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] + + +# -- Options for HTML output ------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output + +html_theme = "sphinx_rtd_theme" +html_static_path = ["_static"] + +autoclass_content = "both" diff --git a/doc/dae/base.md b/doc/dae/base.md deleted file mode 100644 index 0a73687..0000000 --- a/doc/dae/base.md +++ /dev/null @@ -1,39 +0,0 @@ -# DaeBase (base class) - -`Dae` is the principal class in ibex_bluesky_core which exposes configuration settings -and controls from the ISIS data acquisition electronics (DAE). - -> **_ℹ️_** -> The `Dae` class is not intended to be used directly in scans - it is a low-level class -> which directly exposes functionality from the DAE, but has no scanning functionality by -> itself. -> -> It is intended that this object is only used directly when: -> - Configuring DAE settings within a plan. -> - For advanced use-cases, if arbitrary DAE control is required from a plan - but note -> that it will usually be better to implement functionality at the device level rather -> than the plan level. -> -> For other use-cases, a user-facing DAE class is likely to be more appropriate to use -> as a detector in a scan - this class cannot be used by itself. - -# Top-level signals - -Some DAE parameters, particularly metadata parameters, are exposed as simple signals, -for example `dae.title` or `dae.good_uah`. - -These signals are directly readable and settable from plans: - -```python -import bluesky.plan_stubs as bps -from ibex_bluesky_core.devices.dae.dae import Dae - -def plan(dae: Dae): - current_title = yield from bps.rd(dae.title) - yield from bps.mv(dae.title, "new title") -``` - -# Period-specific signals - -For signals which apply to the current period, see `dae.period`, which contains signals -such as `dae.period.good_uah` (the number of good uamp-hours collected in the current period). diff --git a/doc/dae/controls.md b/doc/dae/controls.md deleted file mode 100644 index c06a369..0000000 --- a/doc/dae/controls.md +++ /dev/null @@ -1,16 +0,0 @@ -# Controlling the DAE directly - -It is possible to control the DAE directly using the signals provided by `dae.controls`. - -The intention is that these signals should be used by higher-level _devices_, rather than being -used by plans directly. - -For example, beginning a run is possible via `dae.controls.begin_run.trigger()`. - -## Advanced options - -Options on `begin` (for example, beginning a run in paused mode) can be specified -using the `dae.controls.begin_run_ex` signal. - -Unlike the standard `begin_run` signal, this needs to be `set()` rather than simply -`trigger()`ed, the value on set is a combination of flags from `BeginRunExBits`. diff --git a/doc/dae/settings.md b/doc/dae/settings.md deleted file mode 100644 index 206d493..0000000 --- a/doc/dae/settings.md +++ /dev/null @@ -1,33 +0,0 @@ -# DAE Settings - -Many signals on the DAE are only available as composite signals - this includes most DAE -configuration parameters which are available under the "experiment setup" tab in IBEX, for -example wiring/detector/spectra tables, tcb settings, or vetos. - -The classes implemented in this way are: -- `DaeTCBSettings` (`dae.tcb_settings`) - - Parameters which appear under the "time channels" tab in IBEX -- `DaeSettings` (`dae.dae_settings`) - - Parameters which appear under the "data acquisition" tab in IBEX -- `DaePeriodSettings` (`dae.period_settings`): - - Parameters which appear under the "periods" tab in IBEX - -To read or change these settings from plans, use the associated dataclasses, which are -suffixed with `Data` (e.g. `DaeSettingsData` is the dataclass corresponding to `DaeSettings`): - -```python -import bluesky.plan_stubs as bps -from ibex_bluesky_core.devices.dae.dae import Dae -from ibex_bluesky_core.devices.dae.dae_settings import DaeSettingsData - -def plan(dae: Dae): - # On read, settings are returned together as an instance of a dataclass. - current_settings: DaeSettingsData = yield from bps.rd(dae.dae_settings) - wiring_table: str = current_settings.wiring_filepath - - # On set, any unprovided settings are left unchanged. - yield from bps.mv(dae.dae_settings, DaeSettingsData( - wiring_filepath="a_new_wiring_table.dat", - spectra_filepath="a_new_spectra_table.dat" - )) -``` \ No newline at end of file diff --git a/doc/dae/spectra.md b/doc/dae/spectra.md deleted file mode 100644 index 89bbf9f..0000000 --- a/doc/dae/spectra.md +++ /dev/null @@ -1,17 +0,0 @@ -# DAE Spectra - -Raw spectra are provided by the `DaeSpectra` class. Not all spectra are automatically available -on the base DAE object - user classes will define the specific set of spectra which they are -interested in. - -A `DaeSpectrum` object provides 3 arrays: -- `tof` (x-axis): time of flight. -- `counts` (y-axis): number of counts - - Suitable for summing counts - - Will give a discontinuous plot if plotted directly and bin widths are non-uniform. -- `counts_per_time` (y-axis): number of counts normalized by bin width - - Not suitable for summing counts directly - - Gives a continuous plot when plotted against x directly. - -The `Dae` base class does not provide any spectra by default. User-level classes should specify -the set of spectra which they are interested in. diff --git a/doc/create_a_release.md b/doc/dev/create_a_release.md similarity index 97% rename from doc/create_a_release.md rename to doc/dev/create_a_release.md index 0f5d438..5ad0e03 100644 --- a/doc/create_a_release.md +++ b/doc/dev/create_a_release.md @@ -1,4 +1,4 @@ -### Release Process +# Release Process Releases are created automatically via a github action. diff --git a/doc/set_up_dev_environment.md b/doc/dev/set_up_dev_environment.md similarity index 66% rename from doc/set_up_dev_environment.md rename to doc/dev/set_up_dev_environment.md index 7146d4b..4de931c 100644 --- a/doc/set_up_dev_environment.md +++ b/doc/dev/set_up_dev_environment.md @@ -1,13 +1,13 @@ -## Local development +# Local development -### Checkout the repository locally +## Checkout the repository locally ``` cd c:\Instrument\Dev git clone https://github.com/ISISComputingGroup/ibex_bluesky_core.git ``` -### Create & activate a python virtual environment (windows): +## Create & activate a python virtual environment (windows): ``` cd c:\Instrument\Dev\ibex_bluesky_core @@ -15,26 +15,26 @@ python -m venv .venv .venv\Scripts\activate ``` -### Install the library & dev dependencies in editable mode: +## Install the library & dev dependencies in editable mode: ``` python -m pip install -e .[dev] ``` -### Run the unit tests: +## Run the unit tests: ``` python -m pytest ``` > [!TIP] > To debug the tests in pycharm, use `--no-cov` as an additional option to your run configuration. There is a conflict [issue](https://youtrack.jetbrains.com/issue/PY-20186/debugging-of-py.test-does-not-stop-on-breakpoints-if-coverage-plugin-enabled) with the pytest-cov module which breaks the debugger. -### Run lints: +## Run lints: ``` ruff format --check ruff check pyright ``` -### Run the 'demo' plan +## Run the 'demo' plan Option 1: from a terminal: @@ -64,3 +64,9 @@ set "EPICS_CA_AUTO_ADDR_LIST=NO" - You have an IBEX server running with a DAE in setup state, which can begin a simulated run - You have a readable & writable block named "mot" in the current configuration pointing at the type of block expected by `demo_plan` + +## Build docs locally + +To build the sphinx documentation locally run `sphinx-build doc _build` from the root of the repo. The generated output will be in the _build directory. + +If you want to preview changes live, you can run `sphinx-autobuild doc _build --watch src` from the root of the repo instead which will start a local, hot-reloadable web server. This should rebuild the documentation whenever you change anything in src, which in turn will rebuild the API reference pages. diff --git a/doc/dev/sphinx_notes.md b/doc/dev/sphinx_notes.md new file mode 100644 index 0000000..353de50 --- /dev/null +++ b/doc/dev/sphinx_notes.md @@ -0,0 +1,50 @@ +# Notes on Sphinx and addons + +This repository uses sphinx with some addons to build documentation and then deploy it to Github pages. The deployment only occurs when changes are made to main, and changes are published to the `gh-pages` branch which are then served via the page. + +We use the [MyST](https://myst-parser.readthedocs.io/en/latest/index.html) parser which lets us use a mixture of markdown and reStructuredText in documentation - though the latter is preferred by sphinx. + +## Using MyST admonitions +To use [MyST admonitions](https://myst-parser.readthedocs.io/en/latest/syntax/admonitions.html), you need to use backticks instead of triple colons, ie. + +\`\`\`{tip}\ +Let's give readers a helpful hint!\ +\`\`\` + +becomes + +```{tip} +Let's give readers a helpful hint! +``` + +## Code blocks in docstrings +To add code blocks within the docstrings of classes or functions, use the `::` marker along with a newline then the indented code. For example: + +``` + """... + + Basic usage: + + - Get the IBEX run engine:: + + RE = get_run_engine() + + - Run a plan:: + + from bluesky.plans import count # Or any other plan + det = ... # A "detector" object, for example a Block or Dae device. + RE(count([det])) + + - Control the state of the run engine:: + + RE.abort(reason="...") # Stop a plan, do cleanup, and mark as failed (e.g. bad data). + RE.stop() # Stop a plan, do cleanup, mark as success"(e.g. scan has moved past peak). + RE.halt() # Stop a plan, don't do any cleanup, just abort with no further action. + RE.resume() # Resume running a previously-paused plan. + + - Subscribe to data emitted by this run engine:: + + RE.subscribe(lambda name, document: ...) + ... + """ +``` diff --git a/doc/blocks.md b/doc/devices/blocks.md similarity index 79% rename from doc/blocks.md rename to doc/devices/blocks.md index fa62109..93fa40c 100644 --- a/doc/blocks.md +++ b/doc/devices/blocks.md @@ -9,17 +9,19 @@ scientifically interesting PV. - Read/write with setpoint readback - Motors -> **_ℹ️_** -> All signals, including blocks, in bluesky have a strong type. This must match -> the underlying EPICS type of the PV, which helps to catch problems up-front rather than -> the middle of a plan. Example error at the start of a plan, from trying to connect a `str` block to a `float` PV: -> ``` -> ophyd_async.core._utils.NotConnected: -> mot: NotConnected: -> setpoint_readback: TypeError: TE:NDW2922:CS:SB:mot:SP:RBV has type float not str -> setpoint: TypeError: TE:NDW2922:CS:SB:mot:SP has type float not str -> readback: TypeError: TE:NDW2922:CS:SB:mot has type float not str -> ``` +```{note} +All signals, including blocks, in bluesky have a strong type. This must match +the underlying EPICS type of the PV, which helps to catch problems up-front rather than + the middle of a plan. Example error at the start of a plan, from trying to connect a `str` block to a `float` PV: +```{code} +ophyd_async.core._utils.NotConnected: +mot: NotConnected: + setpoint_readback: TypeError: TE:NDW2922:CS:SB:mot:SP:RBV has type float not str + setpoint: TypeError: TE:NDW2922:CS:SB:mot:SP has type float not str + readback: TypeError: TE:NDW2922:CS:SB:mot has type float not str +``` + + ## Block types @@ -54,13 +56,14 @@ stubs such as `bluesky.plan_stubs.mv()` or `bluesky.plan_stubs.abs_set()`. It can also be used as the `Movable` in full plans like `bluesky.plans.scan()`. -> **_ℹ️_** -> In bluesky terminology, any object with a `set()` method is `Movable`. Therefore, a -> temperature controller is "moved" from one temperature to another, and a run title -> may equally be "moved" from one title to another. -> -> This is simply a matter of terminology - bluesky fully supports moving things which -> are not motors, even if the documentation tends to use motors as the examples. +```{note} + In bluesky terminology, any object with a `set()` method is `Movable`. Therefore, a + temperature controller is "moved" from one temperature to another, and a run title + may equally be "moved" from one title to another. + + This is simply a matter of terminology - bluesky fully supports moving things which + are not motors, even if the documentation tends to use motors as the examples. +``` Like `block_r`, a simple constructor is available: @@ -75,6 +78,7 @@ writable_block = block_rw( ) ``` + ### `block_rw_rbv` (read, write, setpoint readback) This is a block with full support for reading and writing as per `BlockRw`, but with @@ -141,5 +145,6 @@ Run control information is available via the `block.run_control` sub-device. Both configuring and reading the current status of run control are permitted. -> **_ℹ️_** -> Run control limits are always `float`, regardless of the datatype of the block. +```{note} + Run control limits are always `float`, regardless of the datatype of the block. +``` diff --git a/doc/devices/dae.md b/doc/devices/dae.md new file mode 100644 index 0000000..c562621 --- /dev/null +++ b/doc/devices/dae.md @@ -0,0 +1,114 @@ +# DAE + +## DaeBase (base class) + +`Dae` is the principal class in ibex_bluesky_core which exposes configuration settings +and controls from the ISIS data acquisition electronics (DAE). + +```{note} + The `Dae` class is not intended to be used directly in scans - it is a low-level class + which directly exposes functionality from the DAE, but has no scanning functionality by + itself. + + It is intended that this object is only used directly when: + - Configuring DAE settings within a plan. + - For advanced use-cases, if arbitrary DAE control is required from a plan - but note + that it will usually be better to implement functionality at the device level rather + than the plan level. + + For other use-cases, a user-facing DAE class is likely to be more appropriate to use + as a detector in a scan - this class cannot be used by itself. +``` + +## Top-level signals + +Some DAE parameters, particularly metadata parameters, are exposed as simple signals, +for example `dae.title` or `dae.good_uah`. + +These signals are directly readable and settable from plans: + +```python +import bluesky.plan_stubs as bps +from ibex_bluesky_core.devices.dae.dae import Dae + +def plan(dae: Dae): + current_title = yield from bps.rd(dae.title) + yield from bps.mv(dae.title, "new title") +``` + +## Period-specific signals + +For signals which apply to the current period, see `dae.period`, which contains signals +such as `dae.period.good_uah` (the number of good uamp-hours collected in the current period). + + +## Controlling the DAE directly + +It is possible to control the DAE directly using the signals provided by `dae.controls`. + +The intention is that these signals should be used by higher-level _devices_, rather than being +used by plans directly. + +For example, beginning a run is possible via `dae.controls.begin_run.trigger()`. + +### Advanced options + +Options on `begin` (for example, beginning a run in paused mode) can be specified +using the `dae.controls.begin_run_ex` signal. + +Unlike the standard `begin_run` signal, this needs to be `set()` rather than simply +`trigger()`ed, the value on set is a combination of flags from `BeginRunExBits`. + + +## DAE Settings + +Many signals on the DAE are only available as composite signals - this includes most DAE +configuration parameters which are available under the "experiment setup" tab in IBEX, for +example wiring/detector/spectra tables, tcb settings, or vetos. + +The classes implemented in this way are: +- `DaeTCBSettings` (`dae.tcb_settings`) + - Parameters which appear under the "time channels" tab in IBEX +- `DaeSettings` (`dae.dae_settings`) + - Parameters which appear under the "data acquisition" tab in IBEX +- `DaePeriodSettings` (`dae.period_settings`): + - Parameters which appear under the "periods" tab in IBEX + +To read or change these settings from plans, use the associated dataclasses, which are +suffixed with `Data` (e.g. `DaeSettingsData` is the dataclass corresponding to `DaeSettings`): + +```python +import bluesky.plan_stubs as bps +from ibex_bluesky_core.devices.dae.dae import Dae +from ibex_bluesky_core.devices.dae.dae_settings import DaeSettingsData + +def plan(dae: Dae): + # On read, settings are returned together as an instance of a dataclass. + current_settings: DaeSettingsData = yield from bps.rd(dae.dae_settings) + wiring_table: str = current_settings.wiring_filepath + + # On set, any unprovided settings are left unchanged. + yield from bps.mv(dae.dae_settings, DaeSettingsData( + wiring_filepath="a_new_wiring_table.dat", + spectra_filepath="a_new_spectra_table.dat" + )) +``` + + +## DAE Spectra + +Raw spectra are provided by the `DaeSpectra` class. Not all spectra are automatically available +on the base DAE object - user classes will define the specific set of spectra which they are +interested in. + +A `DaeSpectrum` object provides 3 arrays: +- `tof` (x-axis): time of flight. +- `counts` (y-axis): number of counts + - Suitable for summing counts + - Will give a discontinuous plot if plotted directly and bin widths are non-uniform. +- `counts_per_time` (y-axis): number of counts normalized by bin width + - Not suitable for summing counts directly + - Gives a continuous plot when plotted against x directly. + +The `Dae` base class does not provide any spectra by default. User-level classes should specify +the set of spectra which they are interested in. diff --git a/doc/index.rst b/doc/index.rst new file mode 100644 index 0000000..84cd58b --- /dev/null +++ b/doc/index.rst @@ -0,0 +1,41 @@ + + +Welcome to ibex_bluesky_core's documentation! +============================================= + +This documentation contains information for the bluesky plan stubs & devices for use at ISIS. + +.. toctree:: + :maxdepth: 2 + :caption: Devices + :glob: + + devices/* + + +.. toctree:: + :maxdepth: 2 + :caption: Callbacks + :glob: + + callbacks/* + +.. toctree:: + :maxdepth: 2 + :caption: Developer information + :glob: + + dev/* + +.. toctree:: + :titlesonly: + :caption: Architectural decisions + :glob: + + architectural_decisions/* + +.. toctree:: + :titlesonly: + :caption: API reference + + _api diff --git a/pyproject.toml b/pyproject.toml index fd8d9c5..326e424 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,7 +48,14 @@ dependencies = [ ] [project.optional-dependencies] +doc = [ + "sphinx", + "sphinx_rtd_theme", + "myst_parser", + "sphinx-autobuild", +] dev = [ + "ibex_bluesky_core[doc]", "ruff>=0.6", "pyright", "pytest", diff --git a/ruff.toml b/ruff.toml index b67ca88..09ae3d9 100644 --- a/ruff.toml +++ b/ruff.toml @@ -30,6 +30,9 @@ ignore = [ "D", # Don't require method documentation for test methods "ANN" # Don't require tests to use type annotations ] +"doc/conf.py" = [ + "D100" +] [lint.pep8-naming] extend-ignore-names = ["RE"] # Conventional name used for RunEngine diff --git a/src/ibex_bluesky_core/devices/block.py b/src/ibex_bluesky_core/devices/block.py index bf8ae10..cf77639 100644 --- a/src/ibex_bluesky_core/devices/block.py +++ b/src/ibex_bluesky_core/devices/block.py @@ -40,16 +40,19 @@ class BlockWriteConfig(Generic[T]): """Configuration settings for writing to blocks. - use_completion_callback: Whether to wait for an EPICS completion callback while setting + use_completion_callback: + Whether to wait for an EPICS completion callback while setting this block. Defaults to true, which is appropriate for most blocks. - set_success_func: An arbitrary function which is called to decide whether the block has + set_success_func: + An arbitrary function which is called to decide whether the block has set successfully yet or not. The function takes (setpoint, actual) as arguments and should return true if the value has successfully set and is "ready", or False otherwise. - This can be used to implement arbitrary tolerance behaviour. For example: - >>> def check(setpoint: T, actual: T) -> bool: - >>> return setpoint - 0.1 <= actual <= setpoint + 0.1 + This can be used to implement arbitrary tolerance behaviour. For example:: + + def check(setpoint: T, actual: T) -> bool: + return setpoint - 0.1 <= actual <= setpoint + 0.1 If use_completion_callback is True, the completion callback must complete before set_success_func is ever called. @@ -59,13 +62,15 @@ class BlockWriteConfig(Generic[T]): Defaults to None, which means no check is applied. - set_timeout_s: A timeout, in seconds, on the value being set successfully. The timeout + set_timeout_s: + A timeout, in seconds, on the value being set successfully. The timeout applies to the EPICS completion callback (if enabled) and the set success function (if provided), and excludes any configured settle time. Defaults to None, which means no timeout. - settle_time_s: A wait time, in seconds, which is unconditionally applied just before the set + settle_time_s: + A wait time, in seconds, which is unconditionally applied just before the set status is marked as complete. Defaults to zero. """ @@ -152,10 +157,11 @@ def __init__( The setpoint is not added to read() by default. For most cases where setpoint readback functionality is desired, BlockRwRbv is a more suitable type. - If you *explicitly* need to read the setpoint from a BlockRw, you can do so in a plan with: - >>> import bluesky.plan_stubs as bps - >>> block: BlockRw = ... - >>> bps.read(block.setpoint) + If you *explicitly* need to read the setpoint from a BlockRw, you can do so in a plan with:: + + import bluesky.plan_stubs as bps + block: BlockRw = ... + bps.read(block.setpoint) But note that this does not read back the setpoint from hardware, but rather the setpoint which was last sent by EPICS. diff --git a/src/ibex_bluesky_core/run_engine.py b/src/ibex_bluesky_core/run_engine.py index ef5db70..befc88d 100644 --- a/src/ibex_bluesky_core/run_engine.py +++ b/src/ibex_bluesky_core/run_engine.py @@ -39,22 +39,27 @@ def get_run_engine() -> RunEngine: bluesky directly. Basic usage: - - Get the IBEX run engine: - >>> RE = get_run_engine() - - - Run a plan: - >>> from bluesky.plans import count # Or any other plan - >>> det = ... # A "detector" object, for example a Block or Dae device. - >>> RE(count([det])) - - - Control the state of the run engine: - >>> RE.abort(reason="...") # Stop a plan, do cleanup, and mark as failed (e.g. bad data). - >>> RE.stop() # Stop a plan, do cleanup, mark as success"(e.g. scan has moved past peak). - >>> RE.halt() # Stop a plan, don't do any cleanup, just abort with no further action. - >>> RE.resume() # Resume running a previously-paused plan. - - - Subscribe to data emitted by this run engine: - >>> RE.subscribe(lambda name, document: ...) + + - Get the IBEX run engine:: + + RE = get_run_engine() + + - Run a plan:: + + from bluesky.plans import count # Or any other plan + det = ... # A "detector" object, for example a Block or Dae device. + RE(count([det])) + + - Control the state of the run engine:: + + RE.abort(reason="...") # Stop a plan, do cleanup, and mark as failed (e.g. bad data). + RE.stop() # Stop a plan, do cleanup, mark as success"(e.g. scan has moved past peak). + RE.halt() # Stop a plan, don't do any cleanup, just abort with no further action. + RE.resume() # Resume running a previously-paused plan. + + - Subscribe to data emitted by this run engine:: + + RE.subscribe(lambda name, document: ...) For full documentation about the run engine, see: - https://nsls-ii.github.io/bluesky/tutorial.html#the-runengine