|
| 1 | +# `call_sync` (calling external code) |
| 2 | + |
| 3 | +API reference: {py:obj}`ibex_bluesky_core.plan_stubs.call_sync` |
| 4 | + |
| 5 | +All interaction with the "outside world" should be via bluesky messages, and **not** directly called from |
| 6 | +within a plan. For example, the following is **bad**: |
| 7 | + |
| 8 | +```python |
| 9 | +import bluesky.plan_stubs as bps |
| 10 | +from genie_python import genie as g |
| 11 | + |
| 12 | +def bad_plan(): |
| 13 | + yield from bps.open_run() |
| 14 | + g.cset("foo", 123) # This is bad - must not do this |
| 15 | + yield from bps.close_run() |
| 16 | +``` |
| 17 | + |
| 18 | +```{danger} |
| 19 | +External I/O - including most `genie_python` or `inst` functions - should never be done directly in a plan, |
| 20 | +as it will break: |
| 21 | +- Rewindability (for example, the ability to interrupt a scan and then later seamlessly continue it) |
| 22 | +- Simulation (the `cset` above would be executed during a simulation) |
| 23 | +- Error handling (including ctrl-c handling) |
| 24 | +- Ability to emit documents |
| 25 | +- Ability to use bluesky signals |
| 26 | +- ... |
| 27 | +``` |
| 28 | + |
| 29 | +In the above case, a good plan, which uses bluesky messages in a better way using |
| 30 | +a bluesky-native `Block` object, would be: |
| 31 | + |
| 32 | +```python |
| 33 | +import bluesky.plan_stubs as bps |
| 34 | +from ophyd_async.plan_stubs import ensure_connected |
| 35 | +from ibex_bluesky_core.devices.block import block_rw |
| 36 | + |
| 37 | +foo = block_rw(float, "foo") |
| 38 | + |
| 39 | +def good_plan(): |
| 40 | + yield from ensure_connected(foo) |
| 41 | + yield from bps.open_run() |
| 42 | + yield from bps.mv(foo, 123) |
| 43 | + yield from bps.close_run() |
| 44 | +``` |
| 45 | + |
| 46 | +However, if the functionality you want to use is not yet natively available in bluesky, a fallback option |
| 47 | +for synchronous functions is available using the `call_sync` plan stub: |
| 48 | + |
| 49 | +```python |
| 50 | +import bluesky.plan_stubs as bps |
| 51 | +from ibex_bluesky_core.plan_stubs import call_sync |
| 52 | +from genie_python import genie as g |
| 53 | + |
| 54 | +def good_plan(): |
| 55 | + yield from bps.open_run() |
| 56 | + |
| 57 | + # Note use of g.some_function, rather than g.some_function() - i.e. a function reference |
| 58 | + # We can also access the returned value from the call. |
| 59 | + return_value = yield from call_sync(g.some_function, 123, keyword_argument=456) |
| 60 | + yield from bps.checkpoint() |
| 61 | + yield from bps.close_run() |
| 62 | +``` |
| 63 | + |
| 64 | +It is strongly recommended that any functions run in this way are "fast" (i.e. less than a few seconds). |
| 65 | +In particular, avoid doing arbitrarily-long waits - for example, waiting for detector data |
| 66 | +or sample environment. For these long-running tasks, seek to implement at least the long-running parts using |
| 67 | +native bluesky mechanisms. |
| 68 | + |
| 69 | +```{note} |
| 70 | +`bps.checkpoint()` above instructs bluesky that this is a safe point from which to resume a plan. |
| 71 | +`call_sync` always clears an active checkpoint first, as the code it runs may have arbitrary external |
| 72 | +side effects. |
| 73 | +
|
| 74 | +If a plan is interrupted with no checkpoint active, it cannot be resumed later (it effectively forces |
| 75 | +the plan to abort rather than pause). You will see `bluesky.utils.FailedPause` as part of the traceback |
| 76 | +on ctrl-c, if this is the case. |
| 77 | +``` |
0 commit comments