Skip to content

Commit

Permalink
DOC #940
Browse files Browse the repository at this point in the history
  • Loading branch information
prjemian committed Mar 20, 2024
1 parent 48f4a14 commit 5e73032
Showing 1 changed file with 26 additions and 30 deletions.
56 changes: 26 additions & 30 deletions docs/source/examples/de_sscan.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -144,15 +144,16 @@
"\n",
"Polling loops are discouraged because:\n",
"\n",
"- they are not efficient (involving waiting periods of arbitrary duration)\n",
"- they do not handle timeouts or settling times\n",
"- they do not handle other Python exceptions\n",
"- they introduce additional loops into the existing `RE` main loop\n",
"- they are not efficient (involving waiting periods of empirical duration)\n",
"- they do not handle timeouts, settling times, Python exceptions\n",
"- the `RE` already has a main event loop\n",
"- we often want to watch *multiple* \n",
" [signals](https://blueskyproject.io/ophyd/user/tutorials/single-PV.html#set)\n",
" with different update rates or complex logic\n",
"\n",
"Instead of polling the value of an EpicsSignal, it is more efficient to set up\n",
"an EPICS CA monitor on the EpicsSignal.\n",
"When new values of the signal are reported by EPICS, a designated function is\n",
"called to respond.\n",
"Instead of polling the value of an EpicsSignal, it is more efficient to start an\n",
"EPICS CA monitor on the EpicsSignal. When new values of the signal are reported\n",
"by EPICS as CA monitor events, a designated *callback* function is called to respond.\n",
"\n",
"Ophyd\n",
"[Status](https://blueskyproject.io/ophyd/ser/generated/ophyd.status.Status.html#ophyd.status.Status)\n",
Expand All @@ -163,23 +164,23 @@
"See the ophyd\n",
"[tutorial](https://blueskyproject.io/tutorials/Ophyd/02%20-%20Complex%20Behaviors%20%28Set%20and%20Multiple%20PVs%29.html#adding-a-set-method-to-device)\n",
"for use of a status object with a `.set()` method (which is the method called by\n",
"`bps.mv()`). It is not intuitive to use something like `bps.mv(scan_button, 1)`\n",
"`bps.mv()`). It is not intuitive to use `bps.mv(scan_button, 1)`\n",
"here. That would only trigger the scan to *start* but would not wait for the\n",
"scan button value to return to `0`. We also want to wait until the scan is\n",
"complete.\n",
"\n",
"There is a different bluesky plan stub to use in this case: `bps.trigger()`.\n",
"This triggers an ophyd object (by calling the object's `.trigger()` method) and\n",
"(optionally) waits for that trigger to report it is done. It waits using the\n",
"ophyd Status object returned by the object's `.trigger()` method.\n",
"Instead, `bps.trigger(ophyd_object)` tells the ophyd object (such as a scaler or\n",
"area detector) to acquire its data. This triggers the ophyd object (by calling\n",
"the object's `.trigger()` method which returns an ophyd Status object) and\n",
"(optionally) waits for that status object to report it is done.\n",
"\n",
"We can add such a `.trigger()` method if we create a subclass of `EpicsSignal`.\n",
"The `.trigger()` method is called from a bluesky plan using\n",
"`bps.trigger(scan_button)`.\n",
"\n",
"We use the\n",
"In our `.trigger()` method, our status object is built from the\n",
"[SubscriptionStatus](https://blueskyproject.io/ophyd/user/generated/ophyd.status.SubscriptionStatus.html#ophyd-status-subscriptionstatus)\n",
"class which manages the subscription to CA monitor events. The designated\n",
"class, which manages the subscription to CA monitor events. The designated\n",
"function receives `old_value` and `value` from a CA monitor event and returns a\n",
"boolean value. Once the scan ends, the status object is set to `done=True` and\n",
"`success=True` and the CA monitor subscription is removed.\n",
Expand Down Expand Up @@ -269,7 +270,9 @@
"[SscanRecord](https://bcda-aps.github.io/apstools/latest/api/synApps/_sscan.html#apstools.synApps.sscan.SscanRecord)\n",
"class from `apstools.synApps` provides access to most fields of the `sscan`\n",
"record. Use `SscanRecord` to connect with `gp:scan1`. Repeat the above example.\n",
"In the `SscanRecord` class, the scan button is called `execute_scan`."
"In the `SscanRecord` class, the scan button is called `execute_scan`.\n",
"\n",
"*Again, it is recommended to use an ophyd Status object instead of a polling loop.*"
]
},
{
Expand All @@ -284,21 +287,12 @@
"scan1.wait_for_connection()\n",
"\n",
"def run_sscan():\n",
" # Create an instance of the Status object.\n",
" # If it is not marked as finished within 20s, it will raise a timeout exception.\n",
" st = Status(timeout=20)\n",
"\n",
" def watch_execute_scan(old_value, value, **kwargs):\n",
" # Watch for scan1.EXSC to change from 1 to 0 (when the scan ends).\n",
" if old_value == 1 and value == 0:\n",
" # mark as finished (successfully).\n",
" st.set_finished()\n",
" # Remove the subscription.\n",
" scan1.execute_scan.clear_sub(watch_execute_scan)\n",
"\n",
" yield from bps.mv(scan1.execute_scan, 1)\n",
" scan1.execute_scan.subscribe(watch_execute_scan)\n",
" yield from run_blocking_function(st.wait)"
"\n",
" # Again, it is advised to use a Status object instead of a polling loop.\n",
" # Wait for the scan to end with a polling loop.\n",
" while scan1.execute_scan.get() != 0:\n",
" yield from bps.sleep(0.1)"
]
},
{
Expand Down Expand Up @@ -422,6 +416,8 @@
"metadata": {},
"outputs": [],
"source": [
"from apstools.plans import run_blocking_function\n",
"\n",
"def setup_scan1(start, finish, npts, ct=1):\n",
" yield from bps.mv(scaler1.preset_time, ct) # counting time/point\n",
" yield from run_blocking_function(scan1.reset)\n",
Expand Down

0 comments on commit 5e73032

Please sign in to comment.