{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# APS fly scans with taxi & fly `busy` records\n", "\n", "Some EPICS fly scans at APS are triggered by a pair of EPICS\n", "[*busy*](https://epics-modules.github.io/busy/) records. \n", "Each *busy* record initates a sequence of EPICS processing\n", "steps as defined by other EPICS records. The first *busy*\n", "record is called `taxi` and is responsible for preparing the hardware to fly.\n", "Once *taxi* is complete, the second *busy* record, called `fly`, performs the\n", "actual fly scan.\n", "\n", "The next figure shows a control screen (from an APS beam line). The screen has\n", "buttons to initiate taxi & fly sequences. Controls for some other scan\n", "parameters are also shown.\n", "\n", "![taxi/fly control screen](../_static/1ide-taxi-fly-screen.png)\n", "\n", "In a third (optional) phase, data is collected from hardware\n", "and written somewhere (in this example, to the databroker catalog). \n", "\n", "This document shows how to operate such a scan with two examples. \n", "We'll refer to *taxi* and *fly* as phases.\n", "\n", "- simplified processing sequence for each phase\n", " - shows the basic flow of control\n", " - sequence: delay a short time, then return\n", " - no data collection\n", "- step scan of scaler *v*. motor\n", " - includes data collection\n", " - additional PVs recorded\n", " - plot saved data\n", " - typical use case for APS beamlines\n", "\n", "## Overview\n", "\n", "Compare the taxi & fly scan algorithm to an airplane flight:\n", "\n", "phase | airplane flight | taxi & fly scan\n", "--- | --- | ---\n", "preparation | ticketing, boarding, baggage handling | configuration of software\n", "taxi | move the aircraft to the start of the runway | move the hardware to pre-scan positions\n", "fly | start moving, liftoff at flight velocity | start moving, begin collecting data at first position\n", "data | baggage claim | retrieve the fly scan data arrays" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Bluesky (Python) setup\n", "\n", "These packages are needed to begin. The first block contains Python standard\n", "packages, then come the various bluesky packages. Just the parts we plan on\n", "using here.\n", "\n", "* Create a logger instance in case we want to investigate internal details as our code runs.\n", "* Create an instance of the bluesky `RunEngine`.\n", "* Create a temporary databroker catalog to save collected data.\n", "* Subscribe the catalog to receive all data published by the RunEngine." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "cat=\n" ] } ], "source": [ "import logging\n", "import time\n", "\n", "from apstools.synApps import BusyRecord\n", "from apstools.plans import run_blocking_function\n", "import bluesky\n", "import bluesky.plan_stubs as bps\n", "import databroker\n", "from ophyd import Component, Device, Signal\n", "\n", "logger = logging.getLogger()\n", "logger.setLevel(logging.INFO)\n", "\n", "RE = bluesky.RunEngine()\n", "cat = databroker.temp().v2\n", "RE.subscribe(cat.v1.insert)\n", "print(f\"{cat=}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## EPICS IOC\n", "\n", "We'll start with an EPICS IOC that provides two instances of the\n", "[*busy*](https://epics-modules.github.io/busy/) record.\n", "\n", "In the `gp:` IOC, we can use these general purpose PVs for this example:\n", "\n", "PV | record | purpose\n", "--- | --- | ---\n", "`gp:mybusy1` | *busy* | taxi (preparation) phase\n", "`gp:mybusy2` | *busy* | fly (fly scan) phase\n", "\n", "Here, an ophyd `Device` subclass coordinates both *busy* records.\n", "\n", "The `BusyRecord` class from\n", "[apstools.devices](https://bcda-aps.github.io/apstools/latest/api/synApps/index.html#records)\n", "provides a standard interface to the synApps `busy` record. We subclass `BusyRecord`\n", "here as `MyBusyRecord` and redefine the `.trigger()` method, as advised by this\n", "[bluesky tutorial](https://blueskyproject.io/tutorials/Ophyd/02%20-%20Complex%20Behaviors%20%28Set%20and%20Multiple%20PVs%29.html).\n", "A `DeviceStatus` object is returned to monitor the progress of the busy\n", "record.\n", "\n", "Handling of the `taxi` and `fly` phases is identical. A complete taxi/fly scan\n", "is performed by the `taxi_fly_plan()` method. Note this method is a bluesky\n", "plan. It should be run by the bluesky RunEngine.\n", "\n", "Also note that, as written, the `taxi_fly_plan()` method does not collect any\n", "data. As such, it should be considered as a part of a bluesky\n", "[plan](https://blueskyproject.io/bluesky/plans.html#plans) which [opens a\n", "run](https://blueskyproject.io/bluesky/generated/bluesky.plan_stubs.open_run.html#bluesky.plan_stubs.open_run)\n", "and ([triggers\n", "and](https://blueskyproject.io/bluesky/generated/bluesky.plan_stubs.trigger_and_read.html#bluesky.plan_stubs.trigger_and_read))\n", "[reads](https://blueskyproject.io/bluesky/generated/bluesky.plan_stubs.read.html)\n", "data from one or more\n", "[Signals](https://blueskyproject.io/ophyd/user/reference/signals.html) or\n", "[Devices](https://blueskyproject.io/ophyd/user/tutorials/device.html)." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "from ophyd import DeviceStatus\n", "\n", "class MyBusyRecord(BusyRecord):\n", " timeout = Component(Signal, value=10, kind=\"config\")\n", "\n", " def trigger(self):\n", " \"\"\"\n", " Start this busy record and return status to monitor completion.\n", "\n", " This method is called from 'bps.trigger(busy, wait=True)'.\n", " \"\"\"\n", " status = DeviceStatus(self, timeout=self.timeout.get())\n", " executing_values = (1, self.state.enum_strs[1])\n", "\n", " def watch_state(old_value, value, **kwargs):\n", " if old_value in executing_values and value not in executing_values:\n", " # When busy finishes, state changes from 1 to 0.\n", " status.set_finished()\n", " self.state.clear_sub(watch_state)\n", "\n", " # Push the Busy button...\n", " self.state.put(1) # Write number in case text is different.\n", " # Start a CA monitor on self.state, call watch_state() with updates.\n", " self.state.subscribe(watch_state)\n", "\n", " # And return the DeviceStatus object.\n", " # The caller can use it to tell when the action is complete.\n", " return status\n", "\n", "class TaxiFlyScanDevice(Device):\n", " taxi = Component(MyBusyRecord, \"mybusy1\", kind=\"config\")\n", " fly = Component(MyBusyRecord, \"mybusy2\", kind=\"config\")\n", "\n", " def taxi_fly_plan(self):\n", " yield from bps.trigger(self.taxi, wait=True)\n", " yield from bps.trigger(self.fly, wait=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## The `busy` record\n", "\n", "Summary: The *busy* record tells the *sseq* record to do all its processing\n", "steps. The *sseq* record waits its assigned time, then turns the *busy* record\n", "off.\n", "\n", "The *busy* record has a very limited task. It signals the procedure should start\n", "and reports if the procedure is either `Busy` or `Done`. \n", "\n", "*The details of the procedure should be of no concern to the busy record.*\n", "\n", "
\n", "\n", "The EPICS *busy* record is quite simple. It is a boolean that is used to\n", "indicate if a procedure is still active (busy). The caller is responsible for\n", "setting it to `Busy` (value of 1) to start the procedure. The procedure (and\n", "**not** the caller) is responsible for setting it back to `Done` (value of 0)\n", "when the procedure is finished.\n", "\n", "![example of busy record](../_static/bf1-busy-record.png)\n", "\n", "A *userCalc* (the *swait* record) starts the *sseq* record when the *busy* record changes to `Busy`.\n", "\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Procedure -- Delay a short time\n", "\n", "A very simple procedure for the `taxi` phase might be to delay for a programmable time (seconds), then set `busy` to `Done`. The `fly` phase could use the same procedure, with a different programmable time.\n", "\n", "A *preparation* function is needed to configure the EPICS subsystem. In addition to the `busy` record, each phase of this example will use these EPICS records. The ophyd Device classes are from [apstools.synApps](https://bcda-aps.github.io/apstools/latest/api/synApps/index.html#records):\n", "\n", "EPICS record | ophyd class | purpose\n", "--- | --- | ---\n", "sseq | SseqRecord | runs the procedure: delay for _n_ seconds, then set busy to `Done`\n", "swait | SwaitRecord | Starts sseq when the busy record transitions to `Busy`.\n", "\n", "Both phases use the same procedure steps. A separate chain of busy/swait/sseq records is necessary for each phase.\n", "\n", "Later, we'll demonstrate an EPICS step scan using the motor, scaler, and sscan\n", "records.\n", "\n", "### SseqRecord\n", "\n", "The sseq record runs the procedure, then sets busy to `Done`.\n", "\n", "
\n", "\n", "![sseq record example](../_static/bf1-sseq-record.png)\n", "\n", "Setting `.SCAN=\"Passive\"` allows this record to process on command (from the\n", "swait record, below). Only the last step, step 10, is needed for this simple\n", "_delay_ procedure. Other procedures may use steps 1-9 for additional tasks.\n", "For more than 10 steps, use an additional sseq record(s), called from a step in\n", "this sseq record.\n", "\n", "Write the delay time to `.DLYA`, the busy record value to write\n", "(`.STRA=\"Done\"`), and the busy record PV to be written (`LNKA`). Note the use\n", "of the `CA` modifier to the PV name, which is required for the `.WAITA=\"Wait\"`\n", "setting.\n", "\n", "
\n", "\n", "### SwaitRecord\n", "\n", "The swait record acts like a trigger to start the sseq record. It senses when busy changes value.\n", "\n", "
\n", "\n", "![swait record example](../_static/bf1-swait-record.png)\n", "\n", "For both phases, the swait record watches its busy record (the PV name in channel A). It reacts (via its `.SCAN=\"I/O Intr\"` setting) when the busy record changes value. When busy is 1 (via `.CALC=\"A>0\"` and setting `.OOPT=\"When Non-zero\"`), it tells sseq to process (by sending a 1 to the `.PROC` field of the sseq record configured in the `.OUTN` field).\n", "\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Connect with EPICS\n", "\n", "Create local (ophyd-style) objects to connect with the EPICS IOC records." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "from apstools.synApps import SseqRecord, SwaitRecord\n", "from ophyd import EpicsSignal\n", "\n", "IOC = \"gp:\"\n", "\n", "flyscan = TaxiFlyScanDevice(IOC, name=\"flyscan\")\n", "taxi_sseq = SseqRecord(f\"{IOC}userStringSeq1\", name=\"taxi_sseq\")\n", "taxi_swait = SwaitRecord(f\"{IOC}userCalc11\", name=\"taxi_swait\")\n", "fly_sseq = SseqRecord(f\"{IOC}userStringSeq2\", name=\"fly_sseq\")\n", "fly_swait = SwaitRecord(f\"{IOC}userCalc12\", name=\"fly_swait\")\n", "\n", "for obj in (flyscan, taxi_sseq, taxi_swait, fly_sseq, fly_swait):\n", " obj.wait_for_connection()\n", "\n", "# just in case these are not already enabled\n", "sseq_enable = EpicsSignal(f\"{IOC}userStringSeqEnable\", name=\"sseq_enable\")\n", "swait_enable = EpicsSignal(f\"{IOC}userCalcEnable\", name=\"swait_enable\")\n", "for obj in (sseq_enable, swait_enable):\n", " obj.wait_for_connection()\n", " obj.put(\"Enable\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Write the plan that prepares EPICS\n", "\n", "The *busy*, *swait*, & *sseq* records for the *taxi* & *fly* phases are\n", "configured by the following bluesky plan.\n", "\n", "The plan uses predefined names for the ophyd objects, a pattern typical for\n", "beamline plans." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### The `reset()` method\n", "\n", "The *SseqRecord* support in *apstools* has a `reset()` method to clear any\n", "previous settings of the EPICS PVs and ophyd object and return them to default\n", "settings. Note: some of the other record support classes in *apstools.synApps*,\n", "including *SwaitRecord* and *SscanRecord*, have such `reset()` methods.\n", "\n", "The `reset()` method is written as *ophyd* code, intended to be called from a\n", "command-line session. The commands it contains that may take some time to\n", "complete and possibly block the normal execution of the RunEngine's callback\n", "thread. The\n", "[run_blocking_function()](https://bcda-aps.github.io/apstools/latest/api/_plans.html#module-apstools.plans.run_blocking_function_plan)\n", "plan from *apstools.plans* allows us to run `reset()` in a thread so that it\n", "does not block the `RunEngine`." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "def prep_taxi_fly_simple_delay(taxi_delay_s, fly_delay_s):\n", " \"\"\"Delay before returning from both taxi & fly phases.\"\"\"\n", " logger.debug(\"taxi time: %.2f s\", taxi_delay_s)\n", " logger.debug(\"fly time: %.2f s\", fly_delay_s)\n", " # stop any action in progress\n", " yield from bps.mv(\n", " flyscan.fly.state, \"Done\",\n", " flyscan.taxi.state, \"Done\",\n", " )\n", " # clear the taxi & fly busy records\n", " yield from bps.mv(\n", " flyscan.fly.forward_link, \"\",\n", " flyscan.fly.output_link, \"\",\n", " flyscan.taxi.forward_link, \"\",\n", " flyscan.taxi.output_link, \"\",\n", " )\n", "\n", " # clear the records to be used: swait and sseq\n", " for obj in (fly_sseq, fly_swait, taxi_sseq, taxi_swait):\n", " yield from run_blocking_function(obj.reset)\n", " yield from bps.sleep(0.2) # arbitrary wait for EPICS record processing\n", "\n", " # busy record (via swait record) triggers sseq record\n", " yield from bps.mv(\n", " taxi_swait.scanning_rate, \"I/O Intr\",\n", " taxi_swait.channels.A.input_pv, flyscan.taxi.prefix,\n", " taxi_swait.calculation, \"A>0\",\n", " taxi_swait.output_execute_option, \"When Non-zero\",\n", " taxi_swait.output_link_pv, taxi_sseq.process_record.pvname,\n", " )\n", " yield from bps.mv(\n", " fly_swait.scanning_rate, \"I/O Intr\",\n", " fly_swait.channels.A.input_pv, flyscan.fly.prefix,\n", " fly_swait.calculation, \"A>0\",\n", " fly_swait.output_execute_option, \"When Non-zero\",\n", " fly_swait.output_link_pv, fly_sseq.process_record.pvname,\n", " )\n", "\n", " # taxi & fly will each wait the selected time, then return\n", " yield from bps.mv(\n", " taxi_sseq.steps.step10.string_value, \"Done\",\n", " taxi_sseq.steps.step10.wait_completion, \"Wait\",\n", " taxi_sseq.steps.step10.delay, taxi_delay_s,\n", " taxi_sseq.steps.step10.output_pv, f\"{flyscan.taxi.prefix} CA NMS\",\n", " )\n", " yield from bps.mv(\n", " fly_sseq.steps.step10.string_value, \"Done\",\n", " fly_sseq.steps.step10.wait_completion, \"Wait\",\n", " fly_sseq.steps.step10.delay, fly_delay_s,\n", " fly_sseq.steps.step10.output_pv, f\"{flyscan.fly.prefix} CA NMS\",\n", " )" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Run the preparation plan\n", "\n", "Call the `prep_taxi_fly_simple_delay()` plan (with the bluesky RunEngine, `RE`)\n", "with delay times for each phase." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "()" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "RE(prep_taxi_fly_simple_delay(2, 4))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Run taxi & fly scan plan\n", "\n", "Call the `taxi_fly_plan()` method with the bluesky RunEngine. Note this plan completes in the ~6s interval, as configured in the preparation step." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "()" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "flyscan.fly.timeout.put(10)\n", "RE(flyscan.taxi_fly_plan())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Procedure -- step scan scaler & motor\n", "\n", "We'll need to connect with the EPICS scaler and motor PVs. Also we want to record\n", "other PVs in our step scan. And we want to record timestamps at each point to we can post the scan results as bluesky data.\n", "\n", "### scaler record\n", "![scaler](../_static/bf1-scaler-record.png)\n", "\n", "### motor record\n", "![motor](../_static/bf1-motor-record.png)" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "from ophyd import EpicsMotor\n", "from ophyd.scaler import ScalerCH\n", "\n", "m1 = EpicsMotor(f\"{IOC}m1\", name=\"m1\")\n", "scaler1 = ScalerCH(f\"{IOC}scaler1\", name=\"scaler1\")\n", "lorentzian = EpicsSignal(f\"{IOC}userCalc1\", name=\"lorentzian\")\n", "temperature = EpicsSignal(f\"{IOC}userCalc8\", name=\"temperature\")\n", "\n", "for obj in (m1, scaler1, lorentzian, temperature):\n", " obj.wait_for_connection()\n", "\n", "# convenience: pick out the individual detector signals from the scaler\n", "I0 = scaler1.channels.chan02.s\n", "scint = scaler1.channels.chan03.s\n", "diode = scaler1.channels.chan04.s\n", "I000 = scaler1.channels.chan05.s\n", "I00 = scaler1.channels.chan06.s" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### sscan record\n", "\n", "![sscan scaler and motor](../_static/bf1-sscan-record.png)" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "from apstools.synApps import SscanRecord\n", "\n", "scan1 = SscanRecord(f\"{IOC}scan1\", name=\"scan1\")\n", "scan1.wait_for_connection()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Write a preparation plan for the step scan\n", "\n", "The preparation plan configures these actions:\n", "\n", "- preparation\n", " - setup *sseq* records\n", " - for both *taxi* and *fly* phases\n", " - set counting time in *scaler* record\n", " - set scan parameters in *sscan* record\n", " - start, finish, number of points\n", " - motor PVs\n", " - detector PVs\n", " - detector trigger PVs\n", " - set *swait* records to start (process) *sseq* records\n", " - for both *taxi* and *fly* phases\n", " - only when *busy* record goes to `Busy`\n", "\n", "- *taxi* phase\n", " - move the motor (EPICS) to the start of the *fly* scan\n", " - wait for the move to finish\n", " - set its *busy* record to `Done`\n", "\n", "- *fly* phase \n", " - execute the scan (process the (EPICS) *sscan* record)\n", " - wait for the scan to finish\n", " - set its *busy* record to `Done`\n", "\n", "**Note**: The preparation plan does not actually move the motor or start the scan.\n", "It configures the *sseq* records to do these actions when commanded by the\n", "*busy* records.\n", "\n", "The *busy* records start the *taxi* and *fly* phases." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "def prep_taxi_fly_step_scan(start, finish, npts, ct):\n", " \"\"\"Setup EPICS for step scan directed by taxi & fly.\"\"\"\n", " logger.debug(\"start: %g s\", start)\n", " logger.debug(\"finish: %g s\", finish)\n", " logger.debug(\"number of points: %g s\", npts)\n", " logger.debug(\"count time: %g s\", ct)\n", " # stop any action in progress\n", " yield from bps.mv(\n", " flyscan.fly.state, \"Done\",\n", " flyscan.taxi.state, \"Done\",\n", " )\n", " # clear the taxi & fly busy records\n", " yield from bps.mv(\n", " flyscan.fly.forward_link, \"\",\n", " flyscan.fly.output_link, \"\",\n", " flyscan.taxi.forward_link, \"\",\n", " flyscan.taxi.output_link, \"\",\n", " )\n", "\n", " # clear the records to be used: swait, sscan, and sseq\n", " for obj in (fly_sseq, fly_swait, taxi_sseq, taxi_swait, scan1):\n", " yield from run_blocking_function(obj.reset)\n", " yield from bps.sleep(0.5) # arbitrary wait for EPICS record processing\n", "\n", " yield from bps.mv(\n", " taxi_sseq.description, \"taxi procedure\",\n", " fly_sseq.description, \"fly procedure\",\n", " )\n", "\n", " # Move the motor to the start position.\n", " step = taxi_sseq.steps.step1\n", " yield from bps.mv(\n", " step.numeric_value, start,\n", " step.output_pv, f\"{m1.prefix} CA NMS\",\n", " step.wait_completion, \"Wait\",\n", " )\n", "\n", " # Start the sscan.\n", " step = fly_sseq.steps.step1\n", " yield from bps.mv(\n", " step.numeric_value, 1,\n", " step.output_pv, f\"{scan1.execute_scan.pvname} CA NMS\",\n", " step.wait_completion, \"Wait\",\n", " )\n", "\n", " # Configure scaler count time.\n", " yield from bps.mv(scaler1.preset_time, ct)\n", "\n", " # Configure sscan.\n", " yield from bps.mv(\n", " scan1.positioners.p1.start, start,\n", " scan1.positioners.p1.end, finish,\n", " scan1.number_points, npts,\n", " )\n", " # Remember this mapping in scan1 of positioners and detectors.\n", " # We'll use that later to get the data arrays.\n", " # positioners\n", " yield from bps.mv(\n", " scan1.positioners.p1.readback_pv, m1.user_readback.pvname,\n", " scan1.positioners.p1.setpoint_pv, m1.user_setpoint.pvname,\n", " scan1.positioners.p4.readback_pv, \"time\", # timestamp at each point\n", " )\n", " # triggers\n", " yield from bps.mv(\n", " scan1.triggers.t1.trigger_pv, scaler1.count.pvname,\n", " )\n", " # detectors\n", " yield from bps.mv(\n", " scan1.detectors.d01.input_pv, scint.pvname,\n", " scan1.detectors.d02.input_pv, diode.pvname,\n", " scan1.detectors.d03.input_pv, I0.pvname,\n", " scan1.detectors.d04.input_pv, I00.pvname,\n", " scan1.detectors.d05.input_pv, I000.pvname,\n", " scan1.detectors.d06.input_pv, lorentzian.pvname,\n", " scan1.detectors.d07.input_pv, temperature.pvname,\n", " )\n", "\n", " # Trigger taxi & fly sseq records (via swait record) from their busy records.\n", " yield from bps.mv(\n", " taxi_swait.scanning_rate, \"I/O Intr\",\n", " taxi_swait.channels.A.input_pv, flyscan.taxi.prefix,\n", " taxi_swait.calculation, \"A>0\",\n", " taxi_swait.output_execute_option, \"When Non-zero\",\n", " taxi_swait.output_link_pv, taxi_sseq.process_record.pvname,\n", " )\n", " yield from bps.mv(\n", " fly_swait.scanning_rate, \"I/O Intr\",\n", " fly_swait.channels.A.input_pv, flyscan.fly.prefix,\n", " fly_swait.calculation, \"A>0\",\n", " fly_swait.output_execute_option, \"When Non-zero\",\n", " fly_swait.output_link_pv, fly_sseq.process_record.pvname,\n", " )\n", "\n", " # taxi & fly: set busy record to `Done`\n", " step = taxi_sseq.steps.step10\n", " yield from bps.mv(\n", " step.string_value, \"Done\",\n", " step.output_pv, f\"{flyscan.taxi.prefix} CA NMS\",\n", " step.wait_completion, \"Wait\",\n", " )\n", " step = fly_sseq.steps.step10\n", " yield from bps.mv(\n", " step.string_value, \"Done\",\n", " step.output_pv, f\"{flyscan.fly.prefix} CA NMS\",\n", " step.wait_completion, \"Wait\",\n", " )" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Run the preparation plan" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "()" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "RE(prep_taxi_fly_step_scan(-1.1, 1.2, 11, 0.5))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Run the taxi & fly scan" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "()" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "flyscan.fly.timeout.put(60) # might take longer than usual\n", "RE(flyscan.taxi_fly_plan())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Collect the data\n", "\n", "Get the data (arrays) from `scan1`." ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [], "source": [ "def get_sscan_data(t0=None):\n", " # t0: timestamp when sscan started\n", " npts = scan1.current_point.get()\n", " data = {\n", " # use the same mapping as configured above\n", " \"__dt__\": scan1.positioners.p4.array.get()[:npts],\n", " \"m1\": scan1.positioners.p1.array.get()[:npts],\n", " \"scint\": scan1.detectors.d01.array.get()[:npts],\n", " \"diode\": scan1.detectors.d02.array.get()[:npts],\n", " \"I0\": scan1.detectors.d03.array.get()[:npts],\n", " \"I00\": scan1.detectors.d04.array.get()[:npts],\n", " \"I000\": scan1.detectors.d05.array.get()[:npts],\n", " \"lorentzian\": scan1.detectors.d06.array.get()[:npts],\n", " \"temperature\": scan1.detectors.d07.array.get()[:npts],\n", " }\n", " # get timestamps for each step from sscan p4\n", " t0 = t0 or time.time() - data[\"__dt__\"][-1]\n", " data[\"__timestamps__\"] = t0 + data[\"__dt__\"]\n", " return data\n", "\n", "# get_sscan_data()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Write a bluesky plan that puts it all together:\n", "\n", "- metadata\n", "- bluesky run\n", "- prepare EPICS for the taxi & fly scan\n", "- taxi\n", "- fly\n", "- get the data\n", "- publish data to primary stream" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [], "source": [ "def taxi_fly_sscan_plan(start, finish, npts, ct, md={}):\n", " md[\"plan_name\"] = \"taxi_fly_sscan_plan\"\n", " flyscan.fly.timeout.put(60) # might take longer than usual\n", "\n", " yield from bps.open_run(md)\n", " \n", " yield from prep_taxi_fly_step_scan(start, finish, npts, ct)\n", "\n", " timestamps = Signal(name=\"timestamps\", value=[]) # collect by observing 'scan1'\n", " t0 = time.time()\n", "\n", " def callback(**kwargs):\n", " # print(f\"{len(timestamps.get())=} {kwargs=}\")\n", " if kwargs[\"value\"] == 0:\n", " timestamps.put([])\n", " else:\n", " timestamps.put(timestamps.get() + [time.time() - t0])\n", " logger.debug(f\"callback: {kwargs['value']} {time.time()-t0:.4f} {m1.position=}\")\n", " \n", " scan1.current_point.subscribe(callback)\n", "\n", " yield from bps.trigger(flyscan.taxi, wait=True)\n", " t0_fly = time.time() # Timestamp start of fly scan.\n", " yield from bps.trigger(flyscan.fly, wait=True)\n", "\n", " t1 = time.time() - t0\n", " logger.info(\"Fly time: %.3f s\", t1)\n", " scan1.current_point.clear_sub(callback)\n", "\n", " class SscanDataArrays(Device):\n", " __dt__ = Component(Signal)\n", " m1 = Component(Signal)\n", " I0 = Component(Signal)\n", " I00 = Component(Signal)\n", " I000 = Component(Signal)\n", " scint = Component(Signal)\n", " diode = Component(Signal)\n", " lorentzian = Component(Signal)\n", " temperature = Component(Signal)\n", "\n", " scan_data_arrays = SscanDataArrays(\"\", name=\"scan1\")\n", "\n", " # Get the data arrays from the sscan record.\n", " data = get_sscan_data(t0_fly)\n", "\n", " # Post the data as discrete bluesky events.\n", " timestamps = data.pop(\"__timestamps__\")\n", " for i, ts in enumerate(timestamps):\n", " yield from bps.create(name=\"primary\")\n", " for k in data.keys():\n", " obj = getattr(scan_data_arrays, k)\n", " obj.put(data[k][i]) # to Python memory, will not block RE\n", " obj._metadata[\"timestamp\"] = ts\n", " yield from bps.read(scan_data_arrays)\n", " yield from bps.save()\n", " yield from bps.close_run()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Run the complete taxi/fly scan\n", "\n", "Note: Includes data collection. Plotting will follow.\n", "\n", "parameter | value | description\n", "--- | --- | ---\n", "start | -1.2 | first motor position for the step scan\n", "finish | 1.2 | last motor position for the step scan\n", "npts | 21 | number of data points to be collected\n", "ct | 0.2 | scaler counting time per point\n", "\n", "The `m1` motor will be moved in constant size steps between `start` and\n", "`finish`. At each step of the scan, the scaler will be triggered to accumulate\n", "counts for `ct` seconds in each of its detector channels." ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "BlueskyRun\n", " uid='ddc2c053-1e25-4d76-ac56-0ebacdc2008f'\n", " exit_status='success'\n", " 2024-04-03 13:27:59.117 -- 2024-04-03 13:28:17.454\n", " Streams:\n", " * primary\n" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "uids = RE(taxi_fly_sscan_plan(-1.2, 1.2, 21, 0.2))\n", "run = cat[uids[0]]\n", "run" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Get and show the dataset from the `run`." ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
<xarray.Dataset>\n",
       "Dimensions:            (time: 21)\n",
       "Coordinates:\n",
       "  * time               (time) float64 1.712e+09 1.712e+09 ... 1.712e+09\n",
       "Data variables:\n",
       "    scan1___dt__       (time) float64 0.6779 1.38 2.081 ... 16.61 17.31 18.01\n",
       "    scan1_m1           (time) float64 -1.2 -1.08 -0.96 -0.84 ... 0.96 1.08 1.2\n",
       "    scan1_I0           (time) float32 1.0 2.0 1.0 1.0 0.0 ... 1.0 2.0 0.0 1.0\n",
       "    scan1_I00          (time) float32 1.0 0.0 1.0 0.0 2.0 ... 2.0 1.0 1.0 0.0\n",
       "    scan1_I000         (time) float32 0.0 1.0 1.0 2.0 1.0 ... 0.0 1.0 2.0 2.0\n",
       "    scan1_scint        (time) float32 1.0 0.0 1.0 2.0 1.0 ... 2.0 2.0 1.0 1.0\n",
       "    scan1_diode        (time) float32 1.0 1.0 2.0 1.0 1.0 ... 1.0 2.0 2.0 0.0\n",
       "    scan1_lorentzian   (time) float32 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0\n",
       "    scan1_temperature  (time) float32 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0
" ], "text/plain": [ "\n", "Dimensions: (time: 21)\n", "Coordinates:\n", " * time (time) float64 1.712e+09 1.712e+09 ... 1.712e+09\n", "Data variables:\n", " scan1___dt__ (time) float64 0.6779 1.38 2.081 ... 16.61 17.31 18.01\n", " scan1_m1 (time) float64 -1.2 -1.08 -0.96 -0.84 ... 0.96 1.08 1.2\n", " scan1_I0 (time) float32 1.0 2.0 1.0 1.0 0.0 ... 1.0 2.0 0.0 1.0\n", " scan1_I00 (time) float32 1.0 0.0 1.0 0.0 2.0 ... 2.0 1.0 1.0 0.0\n", " scan1_I000 (time) float32 0.0 1.0 1.0 2.0 1.0 ... 0.0 1.0 2.0 2.0\n", " scan1_scint (time) float32 1.0 0.0 1.0 2.0 1.0 ... 2.0 2.0 1.0 1.0\n", " scan1_diode (time) float32 1.0 1.0 2.0 1.0 1.0 ... 1.0 2.0 2.0 0.0\n", " scan1_lorentzian (time) float32 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0\n", " scan1_temperature (time) float32 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "dataset = run.primary.read()\n", "dataset" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Plot the data from the last scan\n", "\n", "Following the steps from the [plotting howto](https://bcda-aps.github.io/bluesky_training/howto/_plot_x_y_databroker.html#3.-Show-the-(primary)-data)..." ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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DRsmfvGLX9itY3vca2trxf/8z/+Ir776ShQVFYmcnByRlpYmevfuzePehg8//FAEBQWJtWvXisLCQvHII4+I8PBwUVxcLIQQ4vHHHxczZ860rv/TTz+JsLAw8eijj4rCwkKxdu1aERQUJDZs2KDXV/BKao/7yy+/LDZt2iR++OEHceDAAfH4448LACIrK0uvr+B1amtrrdduAGLFihWioKBAlJSUCCF4rreHwYhBzJo1SwBoNeXk5FjXASDWrVtnfd3c3Cz+8Ic/iNjYWGE2m8WECRPE/v37PZ94L3bq1Clx5513iq5du4quXbuKO++8s1XTRtvjfu7cOZGZmSl69eolgoKCRJ8+fcSsWbNEaWmp5xPvRd544w3Rt29fERwcLEaNGiXy8vKs782aNUtMnDjRbv3c3FwxcuRIERwcLJKSksSqVas8nGLfoOa4/+lPfxL9+/cXISEhonv37mL8+PHi448/1iHV3ktpHn3pNGvWLCEEz/X2mIT4T20ZIiIiIh2waS8RERHpisEIERER6YrBCBEREemKwQgRERHpisEIERER6YrBCBEREemKwQgRERHpisEIERER6YrBCBHp4uDBg7j55puRlJQEk8mElStXemS/Dz/8MEaPHg2z2YwrrrjCI/skovYxGCEiXZw7dw79+vXD888/bx2F2hOEELj77rvx61//2mP7JKL2MRgh8mMbNmzAsGHDEBoaiqioKFx77bWoq6sDALz11lsYMmQIzGYz4uLiMHfuXOvnVqxYgWHDhiE8PByJiYl44IEHcPbsWev769evR7du3fCvf/0LgwYNQkREBK6//npUVFRY1xkzZgxefPFF3HbbbTCbzarTPmnSJMybNw+PPPIIunfvjpiYGKxZswZ1dXX47W9/i65du6J///749NNP7T736quv4sEHH0S/fv1U75OItMFghMhPVVRU4Pbbb8fdd9+NQ4cOITc3FzNmzIAQAqtWrcKDDz6I3/3ud9i/fz82b96Myy67zPrZLl264NVXX8WBAwfw9ttvY+vWrVi4cKHd9s+dO4fly5fj3XffxbZt21BaWooFCxa49Tu8/fbb6NmzJ7755hvMmzcP999/P2699VaMGzcOe/fuxZQpUzBz5kycO3fOrfslIjfTd5w+ItLLnj17BADrkPK24uPjxZNPPun0tv72t7+JqKgo6+t169YJAOLHH3+0LnvjjTdETEyMw8/37dtXvPzyy84nXggxceJEMX78eOvrxsZGER4ebjdEe0VFhQAg8vPzW33+D3/4gxgxYoSqfRKRNgJ1joWISCcjRoxARkYGhg0bhilTpiAzMxO33HILLl68iGPHjiEjI6PNz+bk5OC5555DYWEhampq0NjYiAsXLqCurg7h4eEAgLCwMPTv39/6mbi4OFRVVbn1OwwfPtw6HxAQgKioKAwbNsy6LCYmBgDcvl8ici8W0xD5qYCAAGRnZ+PTTz/F4MGD8dprryElJQXHjx9v93MlJSWYNm0ahg4diqysLOzZswdvvPEGAODixYvW9YKCguw+ZzKZIIRw63dwtA/bZSaTCQDQ3Nzs1v0SkXsxGCHyYyaTCVdffTWWLFmCgoICBAcHIzs7G0lJSdiyZYvDz+zevRuNjY146aWXMHbsWAwYMADHjh3zcMqJyJewmIbIT+3cuRNbtmxBZmYmoqOjsXPnTpw4cQKDBg3C4sWLMWfOHERHR2Pq1Kmora3Fjh07MG/ePPTv3x+NjY147bXXcOONN2LHjh1YvXq16v03NDSgsLDQOl9eXo59+/YhIiLCrrKsu/344484e/YsKisrcf78eezbtw8AMHjwYAQHB2u2XyJqG4MRIj8VGRmJbdu2YeXKlaipqUHfvn3x0ksvYerUqQCACxcu4OWXX8aCBQvQs2dP3HLLLQCAK664AitWrMCf/vQnLFq0CBMmTMCyZctw1113qdr/sWPHMHLkSOvr5cuXY/ny5Zg4cSJyc3Pd9j0vde+99yIvL8/6WklDUVERkpKSNNsvEbXNJNxdiEtERESkAuuMEBERka4YjBCR4ZSWliIiIqLNqbS0VO8kEpEbsZiGiAynsbERxcXFbb6flJSEwEBWeSPyFQxGiIiISFcspiEiIiJdMRghIiIiXTEYISIiIl0xGCEiIiJdMRghIiIiXTEYISIiIl0xGCEiIiJdMRghIiIiXf3/jdJS2kqTaK0AAAAASUVORK5CYII=", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%matplotlib inline\n", "import matplotlib.pyplot as plt\n", "plt.ion()\n", "\n", "x = dataset[\"scan1_m1\"]\n", "y1 = dataset[\"scan1_lorentzian\"]\n", "y2 = dataset[\"scan1_I0\"]\n", "\n", "plt.plot(x.values, y1.values, \"bx-\", label=y1.name)\n", "plt.plot(x.values, y2.values, \"ro-\", label=y2.name)\n", "plt.xlabel(x.name)\n", "plt.title(f\"scan_id={run.metadata['start']['scan_id']}\")\n", "plt.legend()" ] } ], "metadata": { "kernelspec": { "display_name": "base", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.6" } }, "nbformat": 4, "nbformat_minor": 2 }