Rework StandardDetector to extend from StandardReadable, to follow a more ophyd-v1-like pattern for read signals for step scanning and config signals. - #1395
Conversation
… registered child readables
…ng read/config/describe from children automatically
…onents for config/read
| def __init__(self, prefix: str = "", with_pvi: bool = False, name: str = ""): | ||
| super().__init__(prefix, with_pvi, name) | ||
| self.add_readables( | ||
| [self.min_x, self.min_y, self.size_x, self.size_y], Format.CONFIG_SIGNAL | ||
| ) |
There was a problem hiding this comment.
Why add the readables in __init__ for these driver IO classes? Can't you do it the type annotation way like in ADBaseIO?
There was a problem hiding this comment.
Originally I had more signals with formats set, and there were a lot of Format.* repeated, so this was more concise. I removed most and I think I can go back to using the declarative style.
| async def _update_prepare_context(self, trigger_info: TriggerInfo) -> None: | ||
| # Step scans (trigger+read) need plugins to finish before reading scalars | ||
| if trigger_info.number_of_events == 1 and self.has_child_readables( | ||
| ignore_filter=[self.driver] | ||
| ): | ||
| await self.driver.wait_for_plugins.set(True) | ||
| await super()._update_prepare_context(trigger_info) |
There was a problem hiding this comment.
This makes the assumption that every AreaDetector wants WaitForPlugins = true if they have child readables. I don't really want to have to subclass AreaDetector to turn this off. I have step scans where I want more custom waiting than a generic wait for all plugins.
For example, a very long plugin chain that is only for display purposes, I don't want displays to slow down my scans. WaitForPlugins is too coarse for this case.
The data logic abstraction allows you to add this without subclassing. Is the same thing possible to achieve with a data logic? Where the IO classes are still StandardReadable (that part I like a lot).
StandardDetectornow inheritsStandardReadable, and areaDetector IO classes(
ADBaseIO,NDStatsIO,NDROIStatNIO,NDROIIO) declare their own signalformats using
StandardReadableFormatannotations. This means detector pluginsand drivers follow the same pattern as any other
StandardReadabledevice —they self-describe which signals are configuration, which are hinted reads, and
which are uncached.
Motivation
Readables and detectors now follow the same pattern for signal data. Before this change,
StandardDetectorhad its own separate mechanism for configuration and reading,while
StandardReadablehadadd_readables()with format annotations. Nowboth use the same
StandardReadablemachinery. This makes adding step-scan signal reading functionalityto a file-writing detector identical to how it is handled for just StandardReadable. A plugin like
NDStatsIOisjust a
StandardReadablethat declares its signals, and adding it to a detectorwith
Format.CHILDis identical to composing any twoStandardReadabledevices.Data logics are still the required approach for any file-based data.
Adding a data logic that is readable only is also still supported.
IO classes own their signal roles. Previously, the detector or a separate
PluginSignalDataLogicclass had to know which plugin signals wereconfiguration vs. read vs. hinted. Now
ADBaseIOannotatesacquire_time,model, etc. asFormat.CONFIG_SIGNAL, andNDStatsIOannotatestotal,mean_valueasFormat.HINTED_UNCACHED_SIGNAL— the knowledge lives wherethe signals are defined, not in an external wiring class. Signals or other readables
can also be added outside of the constructor via
det.add_readables, just as withStandardReadable.Closer to ophyd v1. In ophyd v1, a detector's
Componentdeclarationsdetermined what appeared in
read()andread_configuration(), and pluginscomposed naturally as sub-devices.
StandardReadablealready followed this pattern,but
StandardDetectordid not, which caused confusion in some cases during transitions.This change restores that composability:
adding a plugin to a detector automatically pulls in its read and config
signals, just as adding a component did in v1. Users migrating from ophyd v1
will find the mental model familiar.
Automatic
wait_for_pluginsmanagement.AreaDetector._update_prepare_contextnow uses
has_child_readables(ignore_filter=[self.driver])to detect whetherany plugin has read signals that need to complete before the detector reads.
During step scans (
number_of_events == 1), it setswait_for_plugins = Trueautomatically. Fly scans skip this since they stream data asynchronously. An explicit
PluginSignalDataLogic(which was a somewhat verbose solution) is no longer required.