Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Working prototype of TiledWriter-based data access #5

Merged
merged 7 commits into from
Mar 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 21 additions & 4 deletions startup/00-startup.py
Original file line number Diff line number Diff line change
@@ -1,30 +1,47 @@
# Make ophyd listen to pyepics.
print(f"Loading file {__file__!r} ...")

import asyncio
import datetime
import logging
import os
import subprocess
import warnings

import epicscorelibs.path.pyepics
import nslsii
import ophyd.signal
from bluesky.callbacks.broker import post_run, verify_files_saved
from bluesky.run_engine import call_in_bluesky_event_loop
from bluesky.callbacks.tiled_writer import TiledWriter
from bluesky.run_engine import RunEngine, call_in_bluesky_event_loop
from databroker.v0 import Broker
from IPython import get_ipython
from tiled.client import from_uri

ophyd.signal.EpicsSignal.set_defaults(connection_timeout=5)
# See docstring for nslsii.configure_base() for more details
# this command takes away much of the boilerplate for settting up a profile
# (such as setting up best effort callbacks etc)
# this command takes away much of the boilerplate for setting up a profile
# (such as setting up best-effort callback, etc)


nslsii.configure_base(
get_ipython().user_ns,
"tst",
Broker.named("temp"),
pbar=True,
bec=True,
magics=True,
mpl=True,
epics_context=False,
)

event_loop = asyncio.get_event_loop()
RE = RunEngine(loop=event_loop)
RE.subscribe(bec)

tiled_client = from_uri("http://localhost:8000", api_key=os.getenv("TILED_API_KEY", ""))
tw = TiledWriter(tiled_client)
RE.subscribe(tw)

# This is needed for ophyd-async to enable 'await <>' instead of 'asyncio.run(<>)':
get_ipython().run_line_magic("autoawait", "call_in_bluesky_event_loop")

Expand Down
1 change: 1 addition & 0 deletions startup/05-motors.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

class EpicsMotorWithSPMG(EpicsMotor):
spmg = Cpt(EpicsSignal, ".SPMG")
velocity = Cpt(EpicsSignal, ".VELO")


rot_motor = EpicsMotorWithSPMG("XF:31ID1-OP:1{CMT:1-Ax:X}Mtr", name="rot_motor")
35 changes: 23 additions & 12 deletions startup/10-panda.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
print(f"Loading file {__file__!r} ...")

import asyncio
import datetime
import json
Expand Down Expand Up @@ -130,9 +132,6 @@ class BITS(Device):
D = Cpt(EpicsSignal, "D")


print(f"Loading file {__file__!r} ...")


class PandA_Ophyd1(Device):
pcap = Cpt(PCAP, "PCAP:")
data = Cpt(DATA, "DATA:")
Expand Down Expand Up @@ -174,24 +173,36 @@ async def print_children(device):
print(f"{name}: {await obj.read()}")


async def instantiate_panda_async():
async with DeviceCollector():
class TSTPandaHDFWriter(PandaHDFWriter):
async def open(self, *args, **kwargs):
desc = await super().open(*args, **kwargs)
# prefix = self._name_provider()
for key in desc:
if "-counter2-out-" in key:
desc[key]["dtype_str"] = "<i4"
else:
desc[key]["dtype_str"] = "<f8"
return desc


def instantiate_panda_async():
with DeviceCollector():
panda3_async = PandA("XF:31ID1-ES{PANDA:3}:", name="panda3_async")

async with DeviceCollector():
with DeviceCollector():
dir_prov = UUIDDirectoryProvider(PROPOSAL_DIR)
writer3 = PandaHDFWriter(
writer3 = TSTPandaHDFWriter(
"XF:31ID1-ES{PANDA:3}",
dir_prov,
lambda: "test-panda",
lambda: "lab3-panda3",
panda_device=panda3_async,
)
print_children(panda3_async)

return panda3_async, writer3


panda3_async, writer3 = asyncio.run(instantiate_panda_async())
panda3_async, writer3 = instantiate_panda_async()


@AsyncStatus.wrap
Expand Down Expand Up @@ -232,9 +243,9 @@ def _count_async_panda_run(panda_device, writer):
An exception has occurred, use '%tb verbose' to see the full traceback.
RuntimeError: asyncio.run() cannot be called from a running event loop
"""
asyncio.run(writer.open())
# asyncio.run(writer.open())
yield from arm(panda_device)
asyncio.run(writer.close())
# asyncio.run(writer.close())


class TriggerState(str, Enum):
Expand All @@ -250,7 +261,7 @@ def __init__(self):

def trigger_info(self, value: int) -> TriggerInfo:
return TriggerInfo(
num=value, trigger=DetectorTrigger.constant_gate, deadtime=2, livetime=2
num=value, trigger=DetectorTrigger.constant_gate, deadtime=0.1, livetime=0.1
)

async def prepare(self, value: int):
Expand Down
159 changes: 159 additions & 0 deletions startup/15-manta.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
print(f"Loading file {__file__!r} ...")


import asyncio
from dataclasses import dataclass
from enum import Enum

from ophyd_async.core import (
DEFAULT_TIMEOUT,
DetectorControl,
DetectorTrigger,
DetectorWriter,
DeviceCollector,
HardwareTriggeredFlyable,
ShapeProvider,
SignalRW,
SimSignalBackend,
StaticDirectoryProvider,
TriggerInfo,
TriggerLogic,
UUIDDirectoryProvider,
set_sim_value,
)
from ophyd_async.core.async_status import AsyncStatus
from ophyd_async.core.detector import StandardDetector
from ophyd_async.core.device import DeviceCollector
from ophyd_async.epics.areadetector.controllers.vimba_controller import VimbaController
from ophyd_async.epics.areadetector.drivers.vimba_driver import VimbaDriver
from ophyd_async.epics.areadetector.writers.hdf_writer import HDFWriter
from ophyd_async.epics.areadetector.writers.nd_file_hdf import NDFileHDF

MANTA_PV_PREFIX = "XF:31ID1-ES{GigE-Cam:1}"


class TriggerState(str, Enum):
null = "null"
preparing = "preparing"
starting = "starting"
stopping = "stopping"


@dataclass
class MantaTriggerSetup:
num_images: int
exposure_time: float


class MantaTriggerLogic(TriggerLogic[int]):
def __init__(self):
self.state = TriggerState.null

def trigger_info(self, setup) -> TriggerInfo:
return TriggerInfo(
num=setup.num_images,
trigger=DetectorTrigger.edge_trigger,
deadtime=0.1,
livetime=setup.exposure_time,
)

async def prepare(self, value: int):
self.state = TriggerState.preparing
return value

async def start(self):
self.state = TriggerState.starting

async def stop(self):
self.state = TriggerState.stopping


manta_trigger_logic = MantaTriggerLogic()


class MantaShapeProvider(ShapeProvider):
def __init__(self) -> None:
pass

async def __call__(self):
return (544, 728) # y, x


def instantiate_panda_async():
with DeviceCollector():
manta_async = VimbaDriver(MANTA_PV_PREFIX + "cam1:")
hdf_plugin_manta = NDFileHDF(MANTA_PV_PREFIX + "HDF1:", name="manta_hdf_plugin")

with DeviceCollector():
dir_prov = UUIDDirectoryProvider(PROPOSAL_DIR)
manta_writer = HDFWriter(
hdf_plugin_manta,
dir_prov,
lambda: "lab3-manta",
MantaShapeProvider(),
)
print_children(manta_async)

return manta_async, manta_writer


manta_async, manta_writer = instantiate_panda_async()
manta_controller = VimbaController(manta_async)

manta_standard_det = StandardDetector(
manta_controller, manta_writer, name="manta_standard_det"
)


manta_flyer = HardwareTriggeredFlyable(manta_trigger_logic, [], name="manta_flyer")


def manta_stage():
yield from bps.stage(manta_standard_det)
yield from bps.sleep(5)


def manta_fly(
num=10,
): # Note: 724 points are specific for the "rotation_sim_04" panda config!
yield from bps.stage_all(manta_standard_det, manta_flyer)
assert manta_flyer._trigger_logic.state == TriggerState.stopping
yield from bps.prepare(manta_flyer, num, wait=True)
yield from bps.prepare(manta_standard_det, manta_flyer.trigger_info, wait=True)

detector = manta_standard_det
# detector.controller.disarm.assert_called_once # type: ignore

yield from bps.open_run()

yield from bps.kickoff(manta_flyer)
yield from bps.kickoff(detector)

yield from bps.complete(manta_flyer, wait=True, group="complete")
yield from bps.complete(detector, wait=True, group="complete")

# Manually incremenet the index as if a frame was taken
# detector.writer.index += 1

done = False
while not done:
try:
yield from bps.wait(group="complete", timeout=0.5)
except TimeoutError:
pass
else:
done = True
yield from bps.collect(
manta_standard_det,
stream=True,
return_payload=False,
name="main_stream",
)
yield from bps.sleep(0.01)

yield from bps.wait(group="complete")
val = yield from bps.rd(manta_writer.hdf.num_captured)
print(f"{val = }")
yield from bps.close_run()

yield from bps.unstage_all(manta_flyer, manta_standard_det)
3 changes: 2 additions & 1 deletion startup/30-handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ def __call__(self, field):
return entry[:]


db.reg.register_handler("PANDA", PandAHandlerHDF5, overwrite=True)
# TODO: remove completely when Tiled is used:
# db.reg.register_handler("PANDA", PandAHandlerHDF5, overwrite=True)
Loading