PCO Edge in regular beamline config

This commit is contained in:
gac-x05la
2025-02-10 18:18:08 +01:00
parent c729d67763
commit a36c8237da
3 changed files with 110 additions and 27 deletions

View File

@@ -176,15 +176,43 @@ daq_stream1:
readoutPriority: monitored readoutPriority: monitored
softwareTrigger: false softwareTrigger: false
pco_stream0:
description: Raw camera stream from PCO.edge pcocam:
deviceClass: tomcat_bec.devices.StdDaqPreviewDetector description: PCO.edge camera client
deviceClass: tomcat_bec.devices.PcoEdge5M
deviceConfig: deviceConfig:
url: 'tcp://129.129.106.124:8080' prefix: 'X02DA-CCDCAM2:'
deviceTags:
- camera
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: monitored
softwareTrigger: false
pcodaq:
description: GigaFrost stdDAQ client
deviceClass: tomcat_bec.devices.StdDaqClient
deviceConfig:
ws_url: 'ws://129.129.95.111:8081'
rest_url: 'http://129.129.95.111:5010'
deviceTags: deviceTags:
- std-daq - std-daq
enabled: true enabled: true
onFailure: buffer onFailure: buffer
readOnly: false readOnly: false
readoutPriority: async readoutPriority: monitored
softwareTrigger: false
pco_stream0:
description: stdDAQ preview (2 every 555)
deviceClass: tomcat_bec.devices.StdDaqPreviewDetector
deviceConfig:
url: 'tcp://129.129.95.111:20010'
deviceTags:
- std-daq
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: monitored
softwareTrigger: false softwareTrigger: false

View File

@@ -11,5 +11,7 @@ from .grashopper_tomcat import GrashopperTOMCAT
from .psimotor import EpicsMotorMR, EpicsMotorEC from .psimotor import EpicsMotorMR, EpicsMotorEC
from .gigafrost.gigafrostcamera import GigaFrostCamera from .gigafrost.gigafrostcamera import GigaFrostCamera
from .gigafrost.pcoedgecamera import PcoEdge5M
from .gigafrost.stddaq_client import StdDaqClient from .gigafrost.stddaq_client import StdDaqClient
from .gigafrost.stddaq_preview import StdDaqPreviewDetector from .gigafrost.stddaq_preview import StdDaqPreviewDetector

View File

@@ -28,7 +28,7 @@ class PcoEdgeCameraMixin(CustomDeviceMixin):
"""Configure and arm PCO.Edge camera for acquisition """Configure and arm PCO.Edge camera for acquisition
""" """
# Gigafrost can finish a run without explicit unstaging # PCO can finish a run without explicit unstaging
if self.parent.state not in ("IDLE"): if self.parent.state not in ("IDLE"):
logger.warning(f"Trying to stage the camera from state {self.parent.state}, 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()
@@ -36,25 +36,30 @@ class PcoEdgeCameraMixin(CustomDeviceMixin):
# 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
alias = self.parent.parent.name if self.parent.parent is not None else self.parent.name
# logger.warning(f"[{alias}] Scan parameters:\n{scanparam}")
d = {} d = {}
if 'kwargs' in scanparam: if 'kwargs' in scanparam:
scanargs = scanparam['kwargs'] scanargs = scanparam['kwargs']
if 'image_width' in scanargs and scanargs['image_width']!=None: if 'num_images_total' in scanargs and scanargs['num_images_total'] is not None:
d['images_total'] = scanargs['num_images_total']
if 'image_width' in scanargs and scanargs['image_width'] is not None:
d['image_width'] = scanargs['image_width'] d['image_width'] = scanargs['image_width']
if 'image_height' in scanargs and scanargs['image_height']!=None: if 'image_height' in scanargs and scanargs['image_height'] is not None:
d['image_height'] = scanargs['image_height'] d['image_height'] = scanargs['image_height']
if 'exp_time' in scanargs and scanargs['exp_time']!=None: if 'exp_time' in scanargs and scanargs['exp_time'] is not None:
d['exposure_time_ms'] = scanargs['exp_time'] d['exposure_time_ms'] = scanargs['exp_time']
if 'exp_period' in scanargs and scanargs['exp_period']!=None: if 'exp_period' in scanargs and scanargs['exp_period'] is not None:
d['exposure_period_ms'] = scanargs['exp_period'] d['exposure_period_ms'] = scanargs['exp_period']
# if 'exp_burst' in scanargs and scanargs['exp_burst']!=None: # if 'exp_burst' in scanargs and scanargs['exp_burst'] is not None:
# d['exposure_num_burst'] = scanargs['exp_burst'] # d['exposure_num_burst'] = scanargs['exp_burst']
# if 'acq_mode' in scanargs and scanargs['acq_mode']!=None: # if 'acq_mode' in scanargs and scanargs['acq_mode'] is not None:
# d['acq_mode'] = scanargs['acq_mode'] # d['acq_mode'] = scanargs['acq_mode']
# elif self.parent.scaninfo.scan_type == "step": # elif self.parent.scaninfo.scan_type == "step":
# d['acq_mode'] = "default" # d['acq_mode'] = "default"
if 'pco_store_mode' in scanargs and scanargs['pco_store_mode'] is not None:
d['store_mode'] = scanargs['pco_store_mode']
if 'pco_data_format' in scanargs and scanargs['pco_data_format'] is not None:
d['data_format'] = scanargs['pco_data_format']
# Perform bluesky-style configuration # Perform bluesky-style configuration
if len(d) > 0: if len(d) > 0:
@@ -136,6 +141,24 @@ class HelgeCameraBase(PSIDeviceBase):
camError = Component(EpicsSignalRO, "ERRCODE", auto_monitor=True, kind=Kind.config) camError = Component(EpicsSignalRO, "ERRCODE", auto_monitor=True, kind=Kind.config)
camWarning = Component(EpicsSignalRO, "WARNCODE", auto_monitor=True, kind=Kind.config) camWarning = Component(EpicsSignalRO, "WARNCODE", auto_monitor=True, kind=Kind.config)
# ########################################################################
# Buffer configuration
bufferRecMode = Component(EpicsSignalRO, "RECMODE", auto_monitor=True, kind=Kind.config)
bufferStoreMode = Component(EpicsSignal, "STOREMODE", auto_monitor=True, kind=Kind.config)
fileRecMode = Component(EpicsSignalRO, "RECMODE", auto_monitor=True, kind=Kind.config)
buffer_used = Component(EpicsSignalRO, "PIC_BUFFER", auto_monitor=True, kind=Kind.normal)
buffer_size = Component(EpicsSignalRO, "PIC_MAX", auto_monitor=True, kind=Kind.normal)
# ########################################################################
# File saving interface
cam_data_rate = Component(EpicsSignalRO, "CAMRATE", auto_monitor=True, kind=Kind.normal)
file_data_rate = Component(EpicsSignalRO, "FILERATE", auto_monitor=True, kind=Kind.normal)
file_savestart = Component(EpicsSignal, "SAVESTART", put_complete=True, kind=Kind.config)
file_savestop = Component(EpicsSignal, "SAVESTOP", put_complete=True, kind=Kind.config)
file_format = Component(EpicsSignal, "FILEFORMAT", put_complete=True, kind=Kind.config)
file_transfer = Component(EpicsSignal, "FTRANSFER", put_complete=True, kind=Kind.config)
# ######################################################################## # ########################################################################
# Configuration state maschine with separate transition states # Configuration state maschine with separate transition states
camStatusCode = Component(EpicsSignalRO, "STATUSCODE", auto_monitor=True, kind=Kind.config) camStatusCode = Component(EpicsSignalRO, "STATUSCODE", auto_monitor=True, kind=Kind.config)
@@ -176,18 +199,42 @@ class HelgeCameraBase(PSIDeviceBase):
raise ReadOnlyError("State is a ReadOnly property") raise ReadOnlyError("State is a ReadOnly property")
def configure(self, d: dict = {}) -> tuple: def configure(self, d: dict = {}) -> tuple:
""" Configure the base Helge camera device""" """ Configure the base Helge camera device
Parameters as 'd' dictionary
----------------------------
num_images : int
Number of images to be taken during each scan. Meaning depends on
store mode.
exposure_time_ms : float
Exposure time [ms], usually gets set back to 20 ms
exposure_period_ms : float
Exposure period [ms], up to 200 ms.
store_mode : str
Buffer operation mode
*'Recorder' to record in buffer
*'FIFO buffer' for continous streaming
data_format : str
Usually set to 'ZEROMQ'
"""
if self.state not in ("IDLE"): 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}")
# If Bluesky style configure # If Bluesky style configure
if d is not None: if d is not None:
# Commonly changed settings # Commonly changed settings
if 'num_images' in d:
self.file_savestop.set(d['num_images']).wait()
if 'exposure_time_ms' in d: if 'exposure_time_ms' in d:
self.acquire_time.set(d['exposure_time_ms']).wait() self.acquire_time.set(d['exposure_time_ms']).wait()
if 'exposure_period_ms' in d: if 'exposure_period_ms' in d:
# acquire_time = d['exposure_time_ms'] if 'exposure_time_ms' in d else self.acquire_time.get()
self.acquire_delay.set(d['exposure_period_ms']).wait() self.acquire_delay.set(d['exposure_period_ms']).wait()
if 'exposure_period_ms' in d:
self.acquire_delay.set(d['exposure_period_ms']).wait()
if 'store_mode' in d:
self.bufferStoreMode.set(d['store_mode']).wait()
if 'data_format' in d:
self.file_format.set(d['data_format']).wait()
# State machine # State machine
# Initial: BUSY and SET both low # Initial: BUSY and SET both low
@@ -231,8 +278,20 @@ class HelgeCameraBase(PSIDeviceBase):
status = SubscriptionStatus(self.camStatusCode, isIdle, timeout=5, settle_time=0.2) status = SubscriptionStatus(self.camStatusCode, isIdle, timeout=5, settle_time=0.2)
status.wait() status.wait()
# Data streaming is stopped by setting the max index to 0
self.file_savestop.set(0).wait()
class PcoEdgeBase(HelgeCameraBase):
def bluekickoff(self):
""" Start data transfer
TODO: Need to revisit this once triggering is complete
"""
self.file_transfer.set(1).wait()
class PcoEdge5M(HelgeCameraBase):
"""Ophyd baseclass for PCO.Edge cameras """Ophyd baseclass for PCO.Edge cameras
This class provides wrappers for Helge's camera IOCs around SwissFEL and This class provides wrappers for Helge's camera IOCs around SwissFEL and
@@ -241,7 +300,7 @@ class PcoEdgeBase(HelgeCameraBase):
""" """
custom_prepare_cls = PcoEdgeCameraMixin custom_prepare_cls = PcoEdgeCameraMixin
USER_ACCESS = ["bluestage", "blueunstage"] USER_ACCESS = ["bluestage", "blueunstage", "bluekickoff"]
# ######################################################################## # ########################################################################
# Additional status info # Additional status info
@@ -266,12 +325,6 @@ class PcoEdgeBase(HelgeCameraBase):
pxRoiY_lo = Component(EpicsSignal, "REGIONY_START", put_complete=True, auto_monitor=True, kind=Kind.config) pxRoiY_lo = Component(EpicsSignal, "REGIONY_START", put_complete=True, auto_monitor=True, kind=Kind.config)
pxRoiY_hi = Component(EpicsSignal, "REGIONY_END", put_complete=True, auto_monitor=True, kind=Kind.config) pxRoiY_hi = Component(EpicsSignal, "REGIONY_END", put_complete=True, auto_monitor=True, kind=Kind.config)
# ########################################################################
# Buffer configuration
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: def configure(self, d: dict = {}) -> tuple:
""" """
Camera configuration instructions: Camera configuration instructions: