# ADPilatus Area Detector with default HDF5 File Name **Objective** Demonstrate the setup of an [ADPilatus](https://github.com/areaDetector/ADPilatus) camera driver (part of [EPICS area detector](https://areadetector.github.io/master/index.html)) to acquire an image with [bluesky](https://blueskyproject.io/) and write it to an [HDF5](https://www.hdfgroup.org/solutions/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](de_0_adsim_hdf5_basic.ipynb): ```py 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](https://blueskyproject.io/ophyd/generated/ophyd.areadetector.cam.PilatusDetectorCam.html) 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`. ```py 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: ```py 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](de_1_adsim_hdf5_custom_names.ipynb). You'll need this import: ```py from apstools.devices import AD_EpicsFileNameHDF5Plugin ``` Then, build the detector class which puts it all together: ```py 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: ```py 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: ```py # 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: ```py RE(bp.count([pilatus])) ``` See the *bluesky* section of the [basic example](de_0_adsim_hdf5_basic.ipynb) for more information.