Ask a device for the limits of its components
This a vague idea. Could there be a mechanism that reports for a device those components with limits - these would be mostly motors.
A feature like this was seen as being helpful in the beamline optimization project at TES.
More than just motors. Numerical values, in general, often (not always) have a limited range. In EPICS, most such records have HOPR and LOPR fields. The motor record is anomalous (reasons are beyond this scope), using HLM and LLM for ends of the user coordinate system.
But, I like the general interface idea of an object being to describe that it has limits and interfaces with how to get/set them.
To be complete, there are also DHLM & DLLM for the dial coordinate system of the EPICS motor record but no corresponding values for the raw coordinate system.
Signals have easily accessible limits: https://github.com/bluesky/ophyd/blob/c71bd8de58f2aa4f7e66d3475f51030f151f6931/ophyd/signal.py#L403
Devices do not, currently.
@klauer, yes, that's the idea described in this issue to have the Device to report all known limits.
A whole bunch of unstructured thoughts:
- The name
limitsis probably a non-starter given it is already used (as @klauer points out). - Probably better as a method (rather than a property) as I do not think we can guarantee that this will be instant (for example, some of the children may not be connected yet / we may want to force an update from the control system).
-
obj.query_limits()obj.fetch_limits()? - Like
read,describe, ... we should implement this via the recursion pattern onDevicewith a "base case" in one of theSignalclasses - Like
readthe return should be a dictionary, but what should the key be:-
leaf.name-> matchesreadbut is not guaranteed to be unique (!) and is basically useless for working at the device level -
leaf.attr_name-> more useful at the devices level, from this you can get to the actually leaf object. How ever this means thatobj.query_limits()is not a superset ofobj.child.query_limits() -
leaf-> the object its self. This cuts out a step in getting to the object, fixes the sub-set problem above, but if we were to wrap this in a msg and go the RE-as-a-service at a very granular level this would be a bit harder to deal with. - maybe add a kwarg to flip between the last two options?
-
- some complex devices (like pseudo positioners) may have different limits than their children / derived from their children. How do they inject them selves?
- simple case of stacked stages, the total devices would have limits that are the "sum" of the underlying limits
- complex case of goiniometer where we can not describe the limits as a bounding box
- do we need a
Msgand RE coro for this?limits = yield from bpp.get_the_limits(obj)? - this pushes towards the actual objects as keys because otherwise we have to invent a way to refer to "self" as a string.
I'm imagining usage like
def centering_plan(obj):
limits = obj.query_limits()
group_key = 'centering'
for leaf, lim in limits.items():
yield from bpp.abs_set(leaf, np.mean([lim['high'], lim['low']), group=group_key)
yield from bpp.wait(group=group_key)
or
limits = obj.query_limits()
args = list(itertools.chain((leaf, (lim['low'], lim['high']))) for leaf, lim in limits.items()))
yield from adaptive_in_volume(dets, *args)