refactor(pilatus): update config, add live mode
This commit is contained in:
@@ -1,9 +1,34 @@
|
||||
pilatus:
|
||||
pilatus:
|
||||
readoutPriority: async
|
||||
description: Pilatus
|
||||
deviceClass: debye_bec.devices.pilatus.PilatusDetector
|
||||
deviceClass: debye_bec.devices.pilatus.pilatus.Pilatus
|
||||
deviceTags:
|
||||
- detector
|
||||
deviceConfig:
|
||||
prefix: "X01DA-ES2-PIL:"
|
||||
onFailure: retry
|
||||
enabled: true
|
||||
softwareTrigger: false
|
||||
softwareTrigger: true
|
||||
samx:
|
||||
readoutPriority: baseline
|
||||
deviceClass: ophyd_devices.SimPositioner
|
||||
deviceConfig:
|
||||
delay: 1
|
||||
limits:
|
||||
- -50
|
||||
- 50
|
||||
tolerance: 0.01
|
||||
update_frequency: 400
|
||||
deviceTags:
|
||||
- user motors
|
||||
enabled: true
|
||||
readOnly: false
|
||||
bpm4i:
|
||||
readoutPriority: monitored
|
||||
deviceClass: ophyd_devices.SimMonitor
|
||||
deviceConfig:
|
||||
deviceTags:
|
||||
- beamline
|
||||
enabled: true
|
||||
readOnly: false
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ from ophyd import Component as Cpt
|
||||
from ophyd import EpicsSignal, Kind
|
||||
from ophyd.areadetector.cam import ADBase, PilatusDetectorCam
|
||||
from ophyd.areadetector.plugins import HDF5Plugin_V22 as HDF5Plugin
|
||||
from ophyd.areadetector.plugins import ImagePlugin_V22 as ImagePlugin
|
||||
from ophyd.status import WaitTimeoutError
|
||||
from ophyd_devices import (
|
||||
AndStatusWithList,
|
||||
@@ -23,9 +24,7 @@ from ophyd_devices import (
|
||||
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
|
||||
|
||||
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:
|
||||
@@ -191,14 +192,16 @@ class Pilatus(PSIDeviceBase, ADBase):
|
||||
# Geometry correction for the image
|
||||
data = np.reshape(value, (height, width))
|
||||
last_image: DevicePreviewMessage = self.preview.get()
|
||||
if last_image is None:
|
||||
return
|
||||
elif np.array_equal(data, last_image.data):
|
||||
# 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
|
||||
|
||||
if last_image is not None:
|
||||
if np.array_equal(data, last_image.data):
|
||||
# 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."
|
||||
)
|
||||
continue
|
||||
|
||||
logger.debug(f"Setting preview data for {self.name}")
|
||||
self.preview.put(data)
|
||||
except Exception: # pylint: disable=broad-except
|
||||
content = traceback.format_exc()
|
||||
@@ -243,13 +246,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 +331,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 +366,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 +389,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 +404,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()
|
||||
|
||||
Reference in New Issue
Block a user