diff --git a/debye_bec/devices/pilatus/pilatus.py b/debye_bec/devices/pilatus/pilatus.py index c2fbad9..ff23944 100644 --- a/debye_bec/devices/pilatus/pilatus.py +++ b/debye_bec/devices/pilatus/pilatus.py @@ -25,7 +25,6 @@ from ophyd_devices import ( ) 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 if TYPE_CHECKING: # pragma: no cover from bec_lib.devicemanager import ScanInfo @@ -171,17 +170,19 @@ class Pilatus(PSIDeviceBase, ADBase): self.device_manager = device_manager self._readout_time = PILATUS_READOUT_TIME self._full_path = "" + self._poll_thread = threading.Thread( + target=self._poll_array_data, daemon=True, name=f"{self.name}_poll_thread" + ) self._poll_thread_stop_event = threading.Event() - self._task_status: TaskStatus | None = None - self._poll_rate = 1 # 1Hz + self._poll_rate = 1 # Poll rate in Hz ######################################## # Custom Beamline Methods # ######################################## def _poll_array_data(self): + """Poll the array data for preview updates.""" while not self._poll_thread_stop_event.wait(1 / self._poll_rate): - logger.debug("Polling Pilatus array data for preview...") try: value = self.image1.array_data.get() if value is None: @@ -243,13 +244,14 @@ class Pilatus(PSIDeviceBase, ADBase): self.hdf.lazy_open.set(1).wait(5) self.hdf.compression.set(COMPRESSIONALGORITHM.NONE.value).wait(5) # To test which to use # Start polling thread... - self._task_status = self.task_handler.submit_task(task=self._poll_array_data, run=True) + self._poll_thread.start() def on_stage(self) -> DeviceStatus | None: """ Called while staging the device. - Information about the upcoming scan can be accessed from the scan_info (self.scan_info.msg) object. + Information about the upcoming scan can be accessed from the scan_info + (self.scan_info.msg) object. """ scan_msg: ScanStatusMessage = self.scan_info.msg if scan_msg.scan_name.startswith("xas"): @@ -327,32 +329,30 @@ class Pilatus(PSIDeviceBase, ADBase): if status.success: status.device.file_event.put( file_path=status.device._full_path, done=True, successful=True - ) + ) # pylint: disable:protected-access else: status.device.file_event.put( file_path=status.device._full_path, done=True, successful=False - ) + ) # pylint: disable:protected-access def on_complete(self) -> DeviceStatus | None: """Called to inquire if a device has completed a scans.""" if self.scan_info.msg.scan_name.startswith("xas"): # TODO implement logic for 'xas' scans return None - else: - status_hdf = CompareStatus(self.hdf.capture, ACQUIREMODE.DONE.value) - status_cam = CompareStatus(self.cam.acquire, ACQUIREMODE.DONE.value) - status_cam_server = CompareStatus(self.cam.armed, DETECTORSTATE.UNARMED.value) - num_images = self.scan_info.msg.num_points * self.scan_info.msg.scan_parameters.get( - "frames_per_trigger", 1 - ) - status_img_written = CompareStatus(self.hdf.num_captured, num_images) - status = AndStatusWithList( - device=self, - status_list=[status_hdf, status_cam, status_img_written, status_cam_server], - ) - status.add_callback(self._complete_callback) # Callback that writing was successful - self.cancel_on_stop(status) - return status + status_hdf = CompareStatus(self.hdf.capture, ACQUIREMODE.DONE.value) + status_cam = CompareStatus(self.cam.acquire, ACQUIREMODE.DONE.value) + status_cam_server = CompareStatus(self.cam.armed, DETECTORSTATE.UNARMED.value) + num_images = self.scan_info.msg.num_points * self.scan_info.msg.scan_parameters.get( + "frames_per_trigger", 1 + ) + status_img_written = CompareStatus(self.hdf.num_captured, num_images) + status = AndStatusWithList( + device=self, status_list=[status_hdf, status_cam, status_img_written, status_cam_server] + ) + status.add_callback(self._complete_callback) # Callback that writing was successful + self.cancel_on_stop(status) + return status def on_kickoff(self) -> None: """Called to kickoff a device for a fly scan. Has to be called explicitly.""" @@ -364,19 +364,20 @@ class Pilatus(PSIDeviceBase, ADBase): def on_destroy(self) -> None: """Called when the device is destroyed. Cleanup resources here.""" - self.on_stop() self._poll_thread_stop_event.set() + # TODO do we need to clean the poll thread ourselves? + self.on_stop() if __name__ == "__main__": try: pilatus = Pilatus(name="pilatus", prefix="X01DA-ES2-PIL:") - logger.info(f"Calling wait for connection") + logger.info("Calling wait for connection") # pilatus.wait_for_connection(all_signals=True, timeout=20) - logger.info(f"Connecting to pilatus...") + logger.info("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") + logger.info("Sleeping for 5s") time.sleep(5) pilatus.scan_info.msg.num_points = n_pnts pilatus.scan_info.msg.scan_parameters["exp_time"] = exp_time @@ -386,9 +387,9 @@ if __name__ == "__main__": "h5", ) pilatus.on_stage() - logger.info(f"Stage done") + logger.info("Stage done") pilatus.on_pre_scan().wait(timeout=5) - logger.info(f"Pre-scan done") + logger.info("Pre-scan done") for ii in range(pilatus.scan_info.msg.num_points): # if ii == 0: # time.sleep(1) @@ -401,8 +402,8 @@ if __name__ == "__main__": f"Preview shape: {p.data.shape}, max: {np.max(p.data)}, min: {np.min(p.data)}, mean: {np.mean(p.data)}" ) pilatus.on_complete().wait(timeout=5) - logger.info(f"Complete done") + logger.info("Complete done") pilatus.on_unstage() - logger.info(f"Unstage done") + logger.info("Unstage done") finally: pilatus.on_destroy()