call_sync
(calling external code)
API reference: ibex_bluesky_core.plan_stubs.call_sync
All interaction with the “outside world” should be via bluesky messages, and not directly called from within a plan. For example, the following is bad:
import bluesky.plan_stubs as bps
from genie_python import genie as g
def bad_plan():
yield from bps.open_run()
g.cset("foo", 123) # This is bad - must not do this
yield from bps.close_run()
Danger
External I/O - including most genie_python
or inst
functions - should never be done directly in a plan,
as it will break:
Rewindability (for example, the ability to interrupt a scan and then later seamlessly continue it)
Simulation (the
cset
above would be executed during a simulation)Error handling (including ctrl-c handling)
Ability to emit documents
Ability to use bluesky signals
…
In the above case, a good plan, which uses bluesky messages in a better way using
a bluesky-native Block
object, would be:
import bluesky.plan_stubs as bps
from ophyd_async.plan_stubs import ensure_connected
from ibex_bluesky_core.devices.block import block_rw
foo = block_rw(float, "foo")
def good_plan():
yield from ensure_connected(foo)
yield from bps.open_run()
yield from bps.mv(foo, 123)
yield from bps.close_run()
However, if the functionality you want to use is not yet natively available in bluesky, a fallback option
for synchronous functions is available using the call_sync
plan stub:
import bluesky.plan_stubs as bps
from ibex_bluesky_core.plan_stubs import call_sync
from genie_python import genie as g
def good_plan():
yield from bps.open_run()
# Note use of g.some_function, rather than g.some_function() - i.e. a function reference
# We can also access the returned value from the call.
return_value = yield from call_sync(g.some_function, 123, keyword_argument=456)
yield from bps.checkpoint()
yield from bps.close_run()
It is strongly recommended that any functions run in this way are “fast” (i.e. less than a few seconds). In particular, avoid doing arbitrarily-long waits - for example, waiting for detector data or sample environment. For these long-running tasks, seek to implement at least the long-running parts using native bluesky mechanisms.
Note
bps.checkpoint()
above instructs bluesky that this is a safe point from which to resume a plan.
call_sync
always clears an active checkpoint first, as the code it runs may have arbitrary external
side effects.
If a plan is interrupted with no checkpoint active, it cannot be resumed later (it effectively forces
the plan to abort rather than pause). You will see bluesky.utils.FailedPause
as part of the traceback
on ctrl-c, if this is the case.