From a78077139ac069f0ec102cf07d85beda9ac0b16a Mon Sep 17 00:00:00 2001 From: gac-x05la Date: Fri, 31 Jan 2025 12:24:01 +0100 Subject: [PATCH] Basic PCO functionality tested --- .../{helgecamera.py => pcoedgecamera.py} | 67 +++++++++---------- 1 file changed, 32 insertions(+), 35 deletions(-) rename tomcat_bec/devices/gigafrost/{helgecamera.py => pcoedgecamera.py} (90%) diff --git a/tomcat_bec/devices/gigafrost/helgecamera.py b/tomcat_bec/devices/gigafrost/pcoedgecamera.py similarity index 90% rename from tomcat_bec/devices/gigafrost/helgecamera.py rename to tomcat_bec/devices/gigafrost/pcoedgecamera.py index 3f9751a..8225111 100644 --- a/tomcat_bec/devices/gigafrost/helgecamera.py +++ b/tomcat_bec/devices/gigafrost/pcoedgecamera.py @@ -5,11 +5,8 @@ Created on Wed Dec 6 11:33:54 2023 @author: mohacsi_i """ -from ophyd import Device, Component, EpicsMotor, EpicsSignal, EpicsSignalRO, Kind -from ophyd.status import Status, SubscriptionStatus, StatusBase, DeviceStatus -from time import sleep -import warnings -import numpy as np +from ophyd import Component, EpicsSignal, EpicsSignalRO, Kind +from ophyd.status import SubscriptionStatus 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 CustomDetectorMixin as CustomDeviceMixin @@ -32,10 +29,10 @@ class PcoEdgeCameraMixin(CustomDeviceMixin): """ # Gigafrost can finish a run without explicit unstaging - if self.parent.infoBusyFlag.value: - logger.warning("Camera is already running, unstaging it first!") + if self.parent.state not in ("IDLE"): + logger.warning(f"Trying to stage the camera from state {self.parent.state}, unstaging it first!") self.parent.unstage() - sleep(0.5) + time.sleep(0.5) # Fish out our configuration from scaninfo (via explicit or generic addressing) scanparam = self.parent.scaninfo.scan_msg.info @@ -95,8 +92,6 @@ class HelgeCameraBase(PSIDeviceBase): 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 """ - # Specify Mixin class - custom_prepare_cls = HelgeCameraMixin # ######################################################################## # General hardware info (in AD nomenclature) @@ -166,14 +161,9 @@ class HelgeCameraBase(PSIDeviceBase): def configure(self, d: dict = {}) -> tuple: """ 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}") - # Stop acquisition - self.unstage() - if not self._initialized: - pass - # If Bluesky style configure if d is not None: # Commonly changed settings @@ -209,7 +199,7 @@ class HelgeCameraBase(PSIDeviceBase): # Subscribe and wait for update 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.wait() @@ -222,7 +212,7 @@ class HelgeCameraBase(PSIDeviceBase): # Subscribe and wait for update def isIdle(*args, old_value, value, timestamp, **kwargs): 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() @@ -265,8 +255,6 @@ class PcoEdgeBase(HelgeCameraBase): bufferRecMode = Component(EpicsSignalRO, "RECMODE", 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) - - 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 size and shape (that takes time). Starting the exposure with CAMERASTATUS will also 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: # Need to be smart how we set the ROI.... # Image sensor is 2560x2160 (X and Y) @@ -300,20 +308,9 @@ class PcoEdgeBase(HelgeCameraBase): super().configure(d) - - - - - # Automatically connect to test camera if directly invoked if __name__ == "__main__": # Drive data collection - cam = HelgeCameraBase("X02DA-CCDCAM2:", name="mcpcam") + cam = PcoEdgeBase("X02DA-CCDCAM2:", name="mcpcam") cam.wait_for_connection() - - - - - -