Skip to content

Rewrite the detector core around DetectorLogic with bounded and unbounded data logics - #24

Draft
coretl wants to merge 1 commit into
split/G2from
split/G3
Draft

coretl wants to merge 1 commit into
split/G2from
split/G3

Conversation

@coretl

@coretl coretl commented Nov 10, 2026 •

Copy link
Copy Markdown
Owner

Fifth of six stacked draft PRs, based on #23. It rewrites the detector core around a DetectorLogic and splits data logics into bounded and unbounded tiers. Next: #25

Supersedes bluesky#1367

Fixes bluesky#1309

Fixes bluesky#1364

For Jakub Wlodek's attention: switches decide. Whether a plugin is used for a scan is decided by the switches on the plugin itself, not by Python arguments:

  • An ADDataFactory follows the plugin's EnableCallbacks PV. If it is off at the first prepare() after stage(), the plugin sits out the scans of that stage so another data logic can serve them. This is a behaviour change: the HDF writer used to be switched on whatever the PV said.
  • always_enable=True forces the plugin on, and restores the old behaviour. The default is False.
  • Where two things publish the same datakey, the highest serving provider wins and a registered signal with that name is left out of read(), describe() and hints.
  • Publish areaDetector statistics and ROI statistics as a bounded data logic #25 extends this to the statistics plugins, using their compute_* PVs.

Please check that those defaults match how you would configure a real IOC.

What changes

StandardDetector is now a StandardFlyable whose logic is a DetectorLogic holding the trigger, acquire and data logics (ADR 0026). Data logics are BoundedDataLogic (told the number of collections) or UnboundedDataLogic, and implement one start_provider() that sets up and arms the writer and returns a StreamableDataProvider, a PageableDataProvider or None (ADR 0025).

  • A detector builds its logics in a logic cached property; StandardDetector.__init__() takes no arguments and add_detector_logics() is removed. DetectorLogic.with_device() makes an ad-hoc detector.
  • DetectorAcquireLogic.start_acquiring() and wait_for_idle() are replaced by one acquire() async generator that starts, yields once armed, then waits until idle, and is closed if the scan is cancelled.
  • A detector follows the stage(), prepare(), kickoff(), complete() ordering of any flyer. unstage() cancels a running trigger(), and closes an acquisition that an externally triggered prepare() armed and nothing ran.
  • Which logics serve is decided at the first prepare() after stage() and holds until unstage(). No two serving providers may make the same datakey.
  • StandardDetector exposes collect_asset_docs() or collect_pages(), whichever the serving providers produce, as an instance attribute bound by prepare(), because bluesky picks the collect verb from the static type.
  • make_stream_docs(index) replaces make_stream_docs(collections_written, collections_per_event); StreamResourceDataProvider.last_emitted is removed.
  • EventPageDataProvider is a concrete provider in core for any device that fills fixed-length arrays as it acquires. It waits up to ARRAY_WAIT_TIMEOUT for each array to hold the page's collections, and takes an optional refresh.
  • DetectorSuppressLogic returns the names of the readables the detector's data hides; TriggerInfo.number_of_events must be positive.
  • TriggerInfo.flush_period is new, and ADHDFDataLogic sizes its HDF chunks from it (Discuss chunk size configuration bluesky/ophyd-async#1309).
  • adcore.ADWriterFactory is renamed ADDataFactory (the old name warns).
  • ADR 0012 and ADR 0021 are amended; the how-tos and tutorial are updated.

Breaking changes

Out-of-tree detectors must move their logics into a logic cached property, and data logics must implement start_provider(); acquire logics must implement acquire(); ADDataFactory's always_enable default changes behaviour as above. The full list is in the CHANGELOG diff.

How to review

Read ADR 0025 and ADR 0026 first, then core/_detector.py and core/_data_providers.py, then the areaDetector data logics. Tests drive one happy path through stage(), prepare(), kickoff() and complete() and then check the error cases.

Position in the stack

Previous: #23. Next: #25

AI Disclaimer

The code and this description were generated with AI and reviewed/tweaked by the author.

Split cut G3 (bluesky#1408) of detector-rederive (d0872e185): every file at
its tip content (merged with origin/main), including the readable
suppression hook moved here from bluesky#1400, minus the AD stats time series,
which is the next PR. Deletes the unreleased ADWriterFactory.stats and
ADWriterFactory.hdf(enable_callbacks=) shims rather than splitting them.
ADRs 0025 and 0026.

Assisted-by: Claude Opus 5.5
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant