Basic PCO functionality tested
This commit is contained in:
+32
-35
@@ -5,11 +5,8 @@ Created on Wed Dec 6 11:33:54 2023
|
|||||||
@author: mohacsi_i
|
@author: mohacsi_i
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from ophyd import Device, Component, EpicsMotor, EpicsSignal, EpicsSignalRO, Kind
|
from ophyd import Component, EpicsSignal, EpicsSignalRO, Kind
|
||||||
from ophyd.status import Status, SubscriptionStatus, StatusBase, DeviceStatus
|
from ophyd.status import SubscriptionStatus
|
||||||
from time import sleep
|
|
||||||
import warnings
|
|
||||||
import numpy as np
|
|
||||||
import time
|
import time
|
||||||
from ophyd_devices.interfaces.base_classes.psi_detector_base import PSIDetectorBase as PSIDeviceBase
|
from ophyd_devices.interfaces.base_classes.psi_detector_base import PSIDetectorBase as PSIDeviceBase
|
||||||
from ophyd_devices.interfaces.base_classes.psi_detector_base import CustomDetectorMixin as CustomDeviceMixin
|
from ophyd_devices.interfaces.base_classes.psi_detector_base import CustomDetectorMixin as CustomDeviceMixin
|
||||||
@@ -32,10 +29,10 @@ class PcoEdgeCameraMixin(CustomDeviceMixin):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# Gigafrost can finish a run without explicit unstaging
|
# Gigafrost can finish a run without explicit unstaging
|
||||||
if self.parent.infoBusyFlag.value:
|
if self.parent.state not in ("IDLE"):
|
||||||
logger.warning("Camera is already running, unstaging it first!")
|
logger.warning(f"Trying to stage the camera from state {self.parent.state}, unstaging it first!")
|
||||||
self.parent.unstage()
|
self.parent.unstage()
|
||||||
sleep(0.5)
|
time.sleep(0.5)
|
||||||
|
|
||||||
# Fish out our configuration from scaninfo (via explicit or generic addressing)
|
# Fish out our configuration from scaninfo (via explicit or generic addressing)
|
||||||
scanparam = self.parent.scaninfo.scan_msg.info
|
scanparam = self.parent.scaninfo.scan_msg.info
|
||||||
@@ -95,8 +92,6 @@ class HelgeCameraBase(PSIDeviceBase):
|
|||||||
The status flag state machine during re-configuration is:
|
The status flag state machine during re-configuration is:
|
||||||
BUSY low, SET low -> BUSY high, SET low -> BUSY low, SET high -> BUSY low, SET low
|
BUSY low, SET low -> BUSY high, SET low -> BUSY low, SET high -> BUSY low, SET low
|
||||||
"""
|
"""
|
||||||
# Specify Mixin class
|
|
||||||
custom_prepare_cls = HelgeCameraMixin
|
|
||||||
|
|
||||||
# ########################################################################
|
# ########################################################################
|
||||||
# General hardware info (in AD nomenclature)
|
# General hardware info (in AD nomenclature)
|
||||||
@@ -166,14 +161,9 @@ class HelgeCameraBase(PSIDeviceBase):
|
|||||||
|
|
||||||
def configure(self, d: dict = {}) -> tuple:
|
def configure(self, d: dict = {}) -> tuple:
|
||||||
""" Configure the base Helge camera device"""
|
""" Configure the base Helge camera device"""
|
||||||
if self.state in ["OFFLINE", "REMOVED", "RUNNING"]:
|
if self.state not in ("IDLE"):
|
||||||
raise RuntimeError(f"Can't change configuration from state {self.state}")
|
raise RuntimeError(f"Can't change configuration from state {self.state}")
|
||||||
|
|
||||||
# Stop acquisition
|
|
||||||
self.unstage()
|
|
||||||
if not self._initialized:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# If Bluesky style configure
|
# If Bluesky style configure
|
||||||
if d is not None:
|
if d is not None:
|
||||||
# Commonly changed settings
|
# Commonly changed settings
|
||||||
@@ -209,7 +199,7 @@ class HelgeCameraBase(PSIDeviceBase):
|
|||||||
|
|
||||||
# Subscribe and wait for update
|
# Subscribe and wait for update
|
||||||
def isRunning(*args, old_value, value, timestamp, **kwargs):
|
def isRunning(*args, old_value, value, timestamp, **kwargs):
|
||||||
return bool(self.state=="RUNNING")
|
return bool(value==6)
|
||||||
status = SubscriptionStatus(self.camStatusCode, isRunning, timeout=5, settle_time=0.2)
|
status = SubscriptionStatus(self.camStatusCode, isRunning, timeout=5, settle_time=0.2)
|
||||||
status.wait()
|
status.wait()
|
||||||
|
|
||||||
@@ -222,7 +212,7 @@ class HelgeCameraBase(PSIDeviceBase):
|
|||||||
# Subscribe and wait for update
|
# Subscribe and wait for update
|
||||||
def isIdle(*args, old_value, value, timestamp, **kwargs):
|
def isIdle(*args, old_value, value, timestamp, **kwargs):
|
||||||
return bool(value==2)
|
return bool(value==2)
|
||||||
status = SubscriptionStatus(self.parent.camStatusCode, isIdle, timeout=5, settle_time=0.2)
|
status = SubscriptionStatus(self.camStatusCode, isIdle, timeout=5, settle_time=0.2)
|
||||||
status.wait()
|
status.wait()
|
||||||
|
|
||||||
|
|
||||||
@@ -265,8 +255,6 @@ class PcoEdgeBase(HelgeCameraBase):
|
|||||||
bufferRecMode = Component(EpicsSignalRO, "RECMODE", auto_monitor=True, kind=Kind.config)
|
bufferRecMode = Component(EpicsSignalRO, "RECMODE", auto_monitor=True, kind=Kind.config)
|
||||||
bufferStoreMode = Component(EpicsSignalRO, "STOREMODE", auto_monitor=True, kind=Kind.config)
|
bufferStoreMode = Component(EpicsSignalRO, "STOREMODE", auto_monitor=True, kind=Kind.config)
|
||||||
fileRecMode = Component(EpicsSignalRO, "RECMODE", auto_monitor=True, kind=Kind.config)
|
fileRecMode = Component(EpicsSignalRO, "RECMODE", auto_monitor=True, kind=Kind.config)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def configure(self, d: dict = {}) -> tuple:
|
def configure(self, d: dict = {}) -> tuple:
|
||||||
"""
|
"""
|
||||||
@@ -276,9 +264,29 @@ class PcoEdgeBase(HelgeCameraBase):
|
|||||||
both send the settings to the camera and allocate the necessary buffers in the correct
|
both send the settings to the camera and allocate the necessary buffers in the correct
|
||||||
size and shape (that takes time). Starting the exposure with CAMERASTATUS will also
|
size and shape (that takes time). Starting the exposure with CAMERASTATUS will also
|
||||||
call SET_PARAM, but it might take long.
|
call SET_PARAM, but it might take long.
|
||||||
"""
|
|
||||||
old = self.read_configuration()
|
NOTE:
|
||||||
|
The camera IOC will automatically round up RoiX coordinates to the
|
||||||
|
next multiple of 160. This means that configure can only change image
|
||||||
|
width in steps of 320 pixels (or manually of 160). Roi
|
||||||
|
|
||||||
|
Parameters as 'd' dictionary
|
||||||
|
----------------------------
|
||||||
|
exposure_time_ms : float, optional
|
||||||
|
Exposure time [ms].
|
||||||
|
exposure_period_ms : float, optional
|
||||||
|
Exposure period [ms], ignored in soft trigger mode.
|
||||||
|
image_width : int, optional
|
||||||
|
ROI size in the x-direction, multiple of 320 [pixels]
|
||||||
|
image_height : int, optional
|
||||||
|
ROI size in the y-direction, multiple of 2 [pixels]
|
||||||
|
image_binx : int optional
|
||||||
|
Binning along image width [pixels]
|
||||||
|
image_biny: int, optional
|
||||||
|
Binning along image height [pixels]
|
||||||
|
acq_mode : str, not yet implemented
|
||||||
|
Select one of the pre-configured trigger behavior
|
||||||
|
"""
|
||||||
if d is not None:
|
if d is not None:
|
||||||
# Need to be smart how we set the ROI....
|
# Need to be smart how we set the ROI....
|
||||||
# Image sensor is 2560x2160 (X and Y)
|
# Image sensor is 2560x2160 (X and Y)
|
||||||
@@ -300,20 +308,9 @@ class PcoEdgeBase(HelgeCameraBase):
|
|||||||
super().configure(d)
|
super().configure(d)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Automatically connect to test camera if directly invoked
|
# Automatically connect to test camera if directly invoked
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
||||||
# Drive data collection
|
# Drive data collection
|
||||||
cam = HelgeCameraBase("X02DA-CCDCAM2:", name="mcpcam")
|
cam = PcoEdgeBase("X02DA-CCDCAM2:", name="mcpcam")
|
||||||
cam.wait_for_connection()
|
cam.wait_for_connection()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user