ADPilatus Area Detector with default HDF5 File Name#
Objective
Demonstrate the setup of an ADPilatus camera driver (part of EPICS area detector) to acquire an image with bluesky and write it to an HDF5 file.
EPICS Area Detector IOC#
The Pilatus detector computer collects images to its own file system. These files are read by the ADPilatus driver in the IOC. There is a feature in the HDF5 plugin to select if the driver file file should be deleted after the HDF5 file is written by the IOC. This is a good idea to avoid filling up the Pilatus computer’s file system with old files.
Both the IOC and Bluesky should be able to see the HDF5 files. Whatever the
method to accomplish this goal (shared file system, such as NFS, or file
transfer from Pilatus computer to Bluesky computer), the file path on the IOC
(AD_IOC_MOUNT_PATH
) will likely be different than on the Bluesky (local)
workstation (BLUESKY_MOUNT_PATH
). Following the setup in the File-Directories
section of the basic example:
import pathlib
IOC = "pilatus:" # TODO: replace with your IOC's prefix
AD_IOC_MOUNT_PATH = pathlib.Path("/mnt/fileserver/data") # TODO: use yours
BLUESKY_MOUNT_PATH = pathlib.Path("/export/raid5/fileshare/data") # TODO: use yours
IMAGE_DIR = "example/%Y/%m/%d"
# MUST end with a `/`, pathlib will NOT provide it
WRITE_PATH_TEMPLATE = f"{AD_IOC_MOUNT_PATH / IMAGE_DIR}/"
READ_PATH_TEMPLATE = f"{BLUESKY_MOUNT_PATH / IMAGE_DIR}/"
ophyd#
The ADPilatus driver is supported by ophyd.areadetector
’s
PilatusDetectorCam
class. We’ll use that class here since we want to update that class for changes
in area detector’s ADCore
.
Note that ophyd makes a distinction (using the Pilatus here as an
example) between PilatusDetector
and PilatusDetectorCam
. The PilatusDetector
includes PilatusDetectorCam
as a component to build a useful detector. We must customize the PilatusDetectorCam
class with updates, so we can’t use the stock PilatusDetector
.
from apstools.devices import CamMixin_V34
from ophyd.areadetector import PilatusDetectorCam
class PilatusDetectorCam_V34(CamMixin_V34, PilatusDetectorCam):
"""Update PilatusDetectorCam to ADCore 3.4+."""
Build a class which configures the HDF5 plugin with the data acquisition methods we need:
from ophyd.areadetector.filestore_mixins import FileStoreHDF5IterativeWrite
from ophyd.areadetector.plugins import HDF5Plugin_V34 as HDF5Plugin
class MyHDF5Plugin(FileStoreHDF5IterativeWrite, HDF5Plugin):
"""
Add data acquisition methods to HDF5Plugin.
* ``stage()`` - prepare device PVs befor data acquisition
* ``unstage()`` - restore device PVs after data acquisition
* ``generate_datum()`` - coordinate image storage metadata
"""
def stage(self):
self.stage_sigs.move_to_end("capture", last=True)
super().stage()
NOTE: If you want to control the HDF5 file naming with EPICS PVs (in area
detector), then replace the MyHDF5Plugin
class with AD_EpicsFileNameHDF5Plugin
when creating the detector class below and follow the additional instructions
and cautions in the HDF5: AD_EpicsFileNameHDF5Plugin section of the
custom HDF5 image file names example. You’ll
need this import:
from apstools.devices import AD_EpicsFileNameHDF5Plugin
Then, build the detector class which puts it all together:
from apstools.devices import SingleTrigger_V34
from ophyd import ADComponent
from ophyd.areadetector import DetectorBase
class PilatusDetector_V34(SingleTrigger_V34, DetectorBase):
"""
ADSimDetector
SingleTrigger:
* stop any current acquisition
* sets image_mode to 'Multiple'
"""
cam = ADComponent(PilatusDetectorCam_V34, "cam1:")
hdf1 = ADComponent(
MyHDF5Plugin,
"HDF1:",
write_path_template=WRITE_PATH_TEMPLATE,
read_path_template=READ_PATH_TEMPLATE,
)
image = ADComponent(ImagePlugin, "image1:")
Create the Python object for the detector:
pilatus = PilatusDetector_V34(IOC, name="pilatus")
pilatus.wait_for_connection(timeout=15)
pilatus.missing_plugins() # confirm all plugins are defined
pilatus.read_attrs.append("hdf1") # include `hdf1` plugin with 'pilatus.read()'
# override default settings from ophyd
# Plugins will tell the camera driver when acquisition is finished.
# RunEngine will wait until `pilatus:cam1:AcquireBusy_RBV` PV goes to zero.
pilatus.cam.stage_sigs["wait_for_plugins"] = "Yes"
pilatus.hdf1.stage_sigs["blocking_callbacks"] = "No"
pilatus.image.stage_sigs["blocking_callbacks"] = "No"
Consider staging any customizations you wish for data acquisition:
# IOC may create up to 5 new subdirectories, as needed
pilatus.hdf1.create_directory.put(-5)
NUM_FRAMES = 5
pilatus.cam.stage_sigs["acquire_period"] = 0.1
pilatus.cam.stage_sigs["acquire_time"] = 0.01
pilatus.cam.stage_sigs["num_images"] = 5
pilatus.hdf1.stage_sigs["num_capture"] = 0 # capture ALL frames received
pilatus.hdf1.stage_sigs["compression"] = "zlib" # LZ4
# pilatus.hdf1.stage_sigs["queue_size"] = 20
bluesky#
Use with the bluesky RunEngine RE
like any other detector in a plan, such as:
RE(bp.count([pilatus]))
See the bluesky section of the basic example for more information.