From 576f9a6a5b2e427d37f40c996bd7cd6735b8aab9 Mon Sep 17 00:00:00 2001 From: appel_c Date: Wed, 3 Sep 2025 20:00:25 +0200 Subject: [PATCH] w --- debye_bec/devices/pilatus/pilatus.py | 43 +++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/debye_bec/devices/pilatus/pilatus.py b/debye_bec/devices/pilatus/pilatus.py index cbaed1e..a24ab35 100644 --- a/debye_bec/devices/pilatus/pilatus.py +++ b/debye_bec/devices/pilatus/pilatus.py @@ -15,7 +15,13 @@ from ophyd import EpicsSignal, Kind from ophyd.areadetector.cam import ADBase, PilatusDetectorCam from ophyd.areadetector.plugins import HDF5Plugin_V22 as HDF5Plugin from ophyd.status import WaitTimeoutError -from ophyd_devices import AndStatus, CompareStatus, DeviceStatus, FileEventSignal, PreviewSignal +from ophyd_devices import ( + AndStatusWithList, + CompareStatus, + DeviceStatus, + FileEventSignal, + PreviewSignal, +) from ophyd_devices.devices.areadetector.plugins import ImagePlugin_V35 as ImagePlugin from ophyd_devices.interfaces.base_classes.psi_device_base import PSIDeviceBase from ophyd_devices.utils.psi_device_base_utils import TaskStatus @@ -178,6 +184,9 @@ class Pilatus(PSIDeviceBase, ADBase): last_image = self.preview.get() if np.array_equal(data, last_image): # No update if image is the same, ~2.5ms on 2400x2400 image (6M) + logger.debug( + f"Pilatus preview image for {self.name} is the same as last one, not updating." + ) return self.preview.put(data) @@ -245,7 +254,7 @@ class Pilatus(PSIDeviceBase, ADBase): self.cam.array_callbacks.set(1).wait(5) # Enable array callbacks self.hdf.enable.set(1).wait(5) # Enable HDF5 plugin # Camera settings - self.cam.num_exposures.set(1).wait(5) # why + self.cam.num_exposures.set(1).wait(5) self.cam.num_images.set(n_images).wait(5) self.cam.acquire_time.set(detector_exp_time).wait(5) self.cam.acquire_period.set(PILATUS_ACQUIRE_TIME).wait(5) @@ -272,9 +281,11 @@ class Pilatus(PSIDeviceBase, ADBase): else: status_hdf = CompareStatus(self.hdf.capture, ACQUIREMODE.ACQUIRING.value) status_cam = CompareStatus(self.cam.acquire, ACQUIREMODE.ACQUIRING.value) - status = AndStatus(status_hdf, status_cam) # , name=f"{self.name}_on_pre_scan") + status = AndStatusWithList(device=self, status_list=[status_hdf, status_cam]) self.cam.acquire.put(1) self.hdf.capture.put(1) + status.add_callback(self._on_failure_callback) # Callback on failure + self.cancel_on_stop(status) return status def on_trigger(self) -> DeviceStatus | None: @@ -289,6 +300,7 @@ class Pilatus(PSIDeviceBase, ADBase): status = CompareStatus(self.hdf.num_captured, img_counter + 1) logger.warning(f"Triggering took image {time.time() - start_time:.3f} seconds") self.trigger_shot.put(1) + status.add_callback(self._on_failure_callback) # Callback on failure self.cancel_on_stop(status) return status @@ -315,12 +327,27 @@ class Pilatus(PSIDeviceBase, ADBase): "frames_per_trigger", 1 ) status_img_written = CompareStatus(self.hdf.num_captured, num_images) - # TODO change to new ANDSTATUS - status_cam = AndStatus(status_hdf, status_cam) - status = AndStatus(status_cam, status_img_written) # , name=f"{self.name}_on_complete") - # status.add_callback(self._complete_callback) # Callback that writing was successful + status = AndStatusWithList( + device=self, status_list=[status_hdf, status_cam, status_img_written] + ) + status.add_callback(self._complete_callback) # Callback that writing was successful + status.add_callback(self._on_failure_callback) # Callback on failure + self.cancel_on_stop(status) return status + def _on_failure_callback(self, status: DeviceStatus | AndStatusWithList): + """ + This is a best effort cleanup procedure for the Pilatus backend. + It seems as if the CamServer is likely to crash, and needs a restart + if an acquisition is interrupted pre-dominantly and not properly stopped. + So we attach it to any status object during the acquisition. + + Args: + status (DeviceStatus | AndStatusWithList): The status object to check. + """ + if not status.success: + status.device.on_stop() + def on_kickoff(self) -> None: """Called to kickoff a device for a fly scan. Has to be called explicitly.""" @@ -343,6 +370,8 @@ if __name__ == "__main__": logger.info(f"Connecting to pilatus...") pilatus.on_connected() for exp_time, scan_number, n_pnts in zip([0.5, 1.0, 2.0], [1, 2, 3], [30, 20, 10]): + logger.info(f"Sleeping for 5s") + time.sleep(5) pilatus.scan_info.msg.num_points = n_pnts pilatus.scan_info.msg.scan_parameters["exposure_time"] = exp_time pilatus.scan_info.msg.scan_parameters["frames_per_trigger"] = 1