Open danielballan opened 6 years ago
Perhaps even a method of a class? Such as a stage that has default settings when tuning?
class SomeStage(Device):
# ...
# motor, detector, min_step are set by other methods
def tune(start, stop):
yield from tune_centroid([self.detector], self.detector.name, self.motor, start, stop, self.min_step)
self.centroid = motor.read()[motor.name]["value"] # replace with Tom's suggested revision
Device methods that return plans are an interesting direction to explore. Currently there are zero examples of this.
I am :+1: on devices knowing how to create plans using them selves.
Some quibbles about how much state the device should store vs how much should be passed in and what the scope of what the SomeStage
device owns should be.
I have been thinking about this and have started to think we should stash the uids on an RE property and let the plan return what ever it wants.
It is a pretty big API change, but I have not seen the return from RE()
being used except in a few cases where it would be better handled by a callback on the RE or by capturing the uids returned by the start message.
Interesting. For what it's worth, I do make use of
uid = RE(...)
db[uid]
widely in tutorials and examples, but I agree that anecdotally that uptake by users does not seem to be high. Using db[-1]
some future db[RE.uids]
would be fine too.
It does seem like
result = RE(...)
and
def overnight():
result1 = yield from plan1(...)
print(result1)
result2 = yield from plan2(...)
print(result2)
RE(overnight())
would be popular and nice to have.
What about the following scenario?
def overnight():
result1 = yield from plan1(...)
result2 = yield from plan2(...)
return result1, result2
result1, result2 = RE(overnight())
# do some stuff with result1 and result2 interactively
Yep, I agree that is a better version of overnight
.
I think the take-away message is: by allowing plans to return anything they want, as opposed to standardizing on uid(s), as we currently do, plans can straightforwardly return results (1) for use by other sub-plans (2) through RE.__call__
back to the user.
Do we have any rules on what plans return right now? I think we can do @danielballan 's version of overnight right now. The RE just ignores the return value and returns the uids from __call__
instead.
I would be interested in feedback on this latest proposal from @prjemian @teddyrendahl @ZLLentz. To summarize:
RE(plan(...))
always returns the uid(s) of the RunStart documents emitted while executing plan
. If plan
happens to be a coroutine, it has a final return value, but that value is swallowed/ignored by the RunEngine. RE()
always return the RunStart uids, regardless of what the plan returns.def plan():
yield from close_shutter()
dark_uid = yield from count(...)
yield from open_shutter()
light_uid = yield from count(..., md={'dark_frame_run_uid': dark_uid})
RE(plan(...))
will return whatever the plan returns, if the plan is a coroutine. If the plan is a list or some other iterable, RE()
will return None
.result = RE(plan(...))
RE()
.abs_set
return a Status
object; open_run
returns a uid string; etc. The convention we propose relaxing here only ever applied to plans that created a run, and it was not enforced by any technical means.I'm 100% on-board with having the RE return the plan result.
To compensate, perhaps LiveTable
should be made to print the uids along with the tables? I imagine there are use cases for grabbing these uids interactively.
A edge we will have to deal with is that currently __call__
returns the uids collected so far, so if you pause it, it will return something (maybe an empty list). If we start returning the return value of the plan then we can not do this. I think this is ok and will mostly break people who are using RE inside of for-loops so I don't feel too bad.
And you get back the same information again anyway when you call RE.stop()
(or similar).
Actually, re-considering my comment from May, when you interrupt the RE we now raise so we can avoid having to return anything. The issue @danielballan points out is still a problem, when you call stop()
the plan may not gracefully exit, hence has no return value we can recover (but we should check to see if it did catch the exception and return instead. Probably just return None
otherwise?
876 prompted discussion about establishing a pattern for letting plans return some result for interactive use. This has come up only incidentally on the NSLS-II floor so far. We have employed various works arounds such as:
RE.md
(mixes abstractions badly, and requires the plan to hold a ref to the RunEngine instance)This might be the way to go: