{ "cells": [ { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Callback\n", "\n", "To demonstrate how the `NXWriter` is used as a callback, it is necessary to have a data acquisition setup.\n", "\n", "This example scans a `sensor` in response to a `motor` position. The `NXWriter` is subscribed to the `RunEngine` so that during data collection, the `NXWriter` receives data updates. Once the acquisition ends (when a `stop` document is received), the HDF5 file is written.\n", "\n", "The data acquisition is a prebuilt [synApps xxx IOC](https://github.com/epics-modules/xxx) driver, packaged in a [docker](https://www.docker.com/) image\n", "([prjemian/synapps](https://hub.docker.com/r/prjemian/prjemian/synapps/tags)). The [EPICS IOC](https://docs.epics-controls.org/projects/how-tos/en/latest/getting-started/creating-ioc.html) is started using prefix `gp:` by the [bash shell script](https://raw.githubusercontent.com/prjemian/epics-docker/main/resources/iocmgr.sh):\n", "\n", "
\n", "$ iocmgr.sh start GP gp\n", "\n", "\n", "For the purposes of demonstration, the sensor is a random number generator (new values at 10 Hz). The random number generator is provided by a [userCalc](https://htmlpreview.github.io/?https://raw.githubusercontent.com/epics-modules/calc/R3-6-1/documentation/swaitRecord.html). The motor is a [software simulator of a stepping motor](https://github.com/epics-motor/motorMotorSim). There is no particular correlation between the `sensor` and the `motor` in this example, they are used only for purposes of illustration.\n", "\n", "After connecting with the EPICS PVs, the `RunEngine` is constructed and connected with a temporary databroker catalog.\n", "\n", "**Note**\n", "\n", "If you use ``NXWriter`` (or a subclass), you must wait for all data processing to finish before proceeding with the next acquisition or processing. (The `writer()` method is launched in a background thread to complete once all readable assets are available, potentially even after the run ends.) See the `NXWriter` [documentation](https://bcda-aps.github.io/apstools/latest/api/_filewriters.html#apstools.callbacks.nexus_writer.NXWriter) for details." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "%matplotlib inline\n", "from apstools.synApps import setup_random_number_swait\n", "from apstools.synApps import SwaitRecord\n", "from bluesky import RunEngine\n", "from bluesky import SupplementalData\n", "from bluesky import plans as bp\n", "from bluesky.callbacks.best_effort import BestEffortCallback\n", "from matplotlib import pyplot as plt\n", "from ophyd import EpicsMotor\n", "from ophyd import EpicsSignalRO\n", "import databroker\n", "\n", "IOC = \"gp:\"\n", "\n", "# ophyd-level\n", "motor = EpicsMotor(f\"{IOC}m10\", name=\"motor\")\n", "calc10 = SwaitRecord(f\"{IOC}userCalc10\", name=\"calc10\")\n", "sensor = EpicsSignalRO(calc10.calculated_value.pvname, name=\"sensor\")\n", "motor.wait_for_connection()\n", "sensor.wait_for_connection()\n", "\n", "# calc10 sets up the RNG, updating at 10Hz\n", "calc10.wait_for_connection()\n", "setup_random_number_swait(calc10)\n", "\n", "# bluesky-level\n", "best_effort_callback = BestEffortCallback()\n", "cat = databroker.temp().v2\n", "plt.ion() # enables matplotlib graphics\n", "RE = RunEngine({})\n", "RE.subscribe(cat.v1.insert)\n", "RE.subscribe(best_effort_callback) # LivePlot & LiveTable\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Setup the `NXWriter` to create and write the scan data to an HDF5 file. We override the default HDF5 file name. The steps:\n", "\n", "1. import the Python structures\n", "2. Define the file name. (A pathlib object provides an easy way to test if the\n", " file exists.)\n", "3. Create the `NXWriter` instance\n", "4. Subscribe the writer's `receiver` to the RunEngine.\n", "5. Configure the writer for file name and to suppress extra warnings in the example." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "from apstools.callbacks import NXWriter\n", "import pathlib\n", "\n", "h5_file = pathlib.Path(\"/tmp/nxwriter.h5\")\n", "\n", "nxwriter = NXWriter()\n", "RE.subscribe(nxwriter.receiver)\n", "nxwriter.file_name = str(h5_file)\n", "nxwriter.warn_on_missing_content = False" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Collect data by scanning `sensor` *v*. ` motor`. A `LiveTable` and ` LivePlot` will be shown.\n", "\n", "The `sensor` updates automatically at 10 Hz. The `motor` moves slowly enough that the sensor updates before the next position is reached. The data itself is for the purpose of demonstrating the `NXWriter` callback.\n", "\n", "After the scan, show that the file exists." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "\n", "Transient Scan ID: 1 Time: 2022-08-12 17:27:38\n", "Persistent Unique Scan ID: 'c888d282-1094-403b-baff-3781057ff087'\n", "New stream: 'primary'\n", "+-----------+------------+------------+------------+\n", "| seq_num | time | motor | sensor |\n", "+-----------+------------+------------+------------+\n", "| 1 | 17:27:40.1 | -0.50000 | 0.69377 |\n", "| 2 | 17:27:40.5 | -0.25000 | 0.44727 |\n", "| 3 | 17:27:41.0 | 0.00000 | 0.07127 |\n", "| 4 | 17:27:41.5 | 0.25000 | 0.97433 |\n", "| 5 | 17:27:42.0 | 0.50000 | 0.57342 |\n", "+-----------+------------+------------+------------+\n", "generator scan ['c888d282'] (scan num: 1)\n", "\n", "\n", "\n", "h5_file.exists()=True h5_file=PosixPath('/tmp/nxwriter.h5')\n" ] }, { "data": { "image/png": "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", "text/plain": [ "