Skip to content

ISIS standard callback collection #93

@Tom-Willemsen

Description

@Tom-Willemsen

In a lot of our plans we have callbacks that look very similar to:

def some_plan():
    ...
    x = some_device()
    y = some_device()

    ...

    _, ax = yield from call_qt_aware(plt.subplots)

    lf = LiveFit(
        Linear.fit(), y=y.name, x=x.name, yerr=y.stddev.name
    )

    @subs_decorator(
        [
            HumanReadableFileCallback(
                Path("C:\\") / "instrument" / "var" / "logs" / "bluesky" / "output_files",
                [
                    x.name,
                    y.name,
                    y.stddev.name,
                    y.foo.name,
                    y.bar.name,
                ],
            ),
            LiveFitPlot(livefit=lf, ax=ax),
            LivePlot(
                y=y.name,
                x=x.name,
                marker="x",
                linestyle="none",
                ax=ax,
                yerr=y.stddev.name,
            ),
            LiveTable(
                [
                    x.name,
                    y.name,
                    y.stddev.name,
                    y.foo.name,
                    y.bar.name,
                ]
            ),
        ]
    )
    def _inner() -> Generator[Msg, None, None]:
        yield from the_thing_i_want_to_do()

While this is very explicit and also very flexible, as a developer & user I'd like this to be more concise - since most of these callbacks will be wanted most of the time, I think we should have a standard "callback collection" that allows us to write something more like:

def some_plan():
    ...
    x = some_device()
    y = some_device()

    ...

    @isis_standard_callbacks(x=x, y=y, yerr=y.stddev, fit=Linear.fit(), extras=[y.foo, y.bar])
    def _inner() -> Generator[Msg, None, None]:
        yield from the_thing_i_want_to_do()

This is in the general spirit of the "best effort callback" from bluesky, I don't think we can use that directly - we still need to be able to specify e.g. fit types and extras.

The isis_standard_callbacks decorator is about a set of "typical" callbacks which will work for many plans. It's ok if it doesn't work for all cases - here we should use opinionated defaults (can always fall back to explicit approach if people need fine-grained control).

Acceptance criteria:

  • isis_standard_callbacks exists and works functionally similarly to subs_decorator but adds all of our "usual" callbacks and configures mpl plotting in the "usual" way for the user.
  • It is still possible to use full explicit callbacks if desired (i.e. first option above should still be possible)
  • If yerr is omitted, error bars do not appear on plot, fit is unweighted, yerr not present in file/table
  • If yerr is present, error bars appear on plot and fit is weighted, yerr present in file/table
  • It must still be possible to access the result of the LiveFit in some way (e.g. make return value of the decorated function (original_return value, live_fit_result))
  • Must be possible to add "extra" signals to put in files and on the livetable (e.g. y.foo and y.bar above), as well as x, y, yerr
  • If fit is not specified, don't add LiveFit and LiveFitPlot
  • Have a set of keyword toggles, defaulting to True, which include / exclude the underlying callbacks:
    • e.g. isis_standard_callbacks(..., table=False) would be good for LOQ's laser scan, we get so many points that the table is quite noisy.

Planning

10/01/25 - 00:36:00

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions