ophyd icon indicating copy to clipboard operation
ophyd copied to clipboard

Ask a device for the limits of its components

Open jklynch opened this issue 6 years ago • 5 comments

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.

jklynch avatar Apr 02 '20 14:04 jklynch

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.

prjemian avatar Apr 02 '20 14:04 prjemian

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.

prjemian avatar Apr 02 '20 14:04 prjemian

Signals have easily accessible limits: https://github.com/bluesky/ophyd/blob/c71bd8de58f2aa4f7e66d3475f51030f151f6931/ophyd/signal.py#L403

Devices do not, currently.

klauer avatar Apr 02 '20 16:04 klauer

@klauer, yes, that's the idea described in this issue to have the Device to report all known limits.

mrakitin avatar Apr 02 '20 17:04 mrakitin

A whole bunch of unstructured thoughts:

  • The name limits is 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 on Device with a "base case" in one of the Signal classes
  • Like read the return should be a dictionary, but what should the key be:
    • leaf.name -> matches read but 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 that obj.query_limits() is not a superset of obj.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 Msg and 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)

tacaswell avatar Apr 02 '20 17:04 tacaswell