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
softwareTrigger: false
pco_stream0:
description: Raw camera stream from PCO.edge
deviceClass: tomcat_bec.devices.StdDaqPreviewDetector
pcocam:
description: PCO.edge camera client
deviceClass: tomcat_bec.devices.PcoEdge5M
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:
- std-daq
enabled: true
onFailure: buffer
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

View File

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

View File

@@ -28,7 +28,7 @@ class PcoEdgeCameraMixin(CustomDeviceMixin):
"""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"):
logger.warning(f"Trying to stage the camera from state {self.parent.state}, unstaging it first!")
self.parent.unstage()
@@ -36,25 +36,30 @@ class PcoEdgeCameraMixin(CustomDeviceMixin):
# Fish out our configuration from scaninfo (via explicit or generic addressing)
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 = {}
if 'kwargs' in scanparam:
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']
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']
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']
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']
# 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']
# 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']
# elif self.parent.scaninfo.scan_type == "step":
# 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
if len(d) > 0:
@@ -136,6 +141,24 @@ class HelgeCameraBase(PSIDeviceBase):
camError = Component(EpicsSignalRO, "ERRCODE", 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
camStatusCode = Component(EpicsSignalRO, "STATUSCODE", auto_monitor=True, kind=Kind.config)
@@ -176,19 +199,43 @@ class HelgeCameraBase(PSIDeviceBase):
raise ReadOnlyError("State is a ReadOnly property")
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"):
raise RuntimeError(f"Can't change configuration from state {self.state}")
# If Bluesky style configure
if d is not None:
# Commonly changed settings
if 'num_images' in d:
self.file_savestop.set(d['num_images']).wait()
if 'exposure_time_ms' in d:
self.acquire_time.set(d['exposure_time_ms']).wait()
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()
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
# Initial: BUSY and SET both low
# 0. Write 1 to SET_PARAM
@@ -231,17 +278,29 @@ class HelgeCameraBase(PSIDeviceBase):
status = SubscriptionStatus(self.camStatusCode, isIdle, timeout=5, settle_time=0.2)
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
This class provides wrappers for Helge's camera IOCs around SwissFEL and
for high performance SLS 2.0 cameras. Theese are mostly PCO cameras running
on a special Windows IOC host with lots of RAM and CPU power.
"""
"""
custom_prepare_cls = PcoEdgeCameraMixin
USER_ACCESS = ["bluestage", "blueunstage"]
USER_ACCESS = ["bluestage", "blueunstage", "bluekickoff"]
# ########################################################################
# 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_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:
"""
Camera configuration instructions:
@@ -302,7 +355,7 @@ class PcoEdgeBase(HelgeCameraBase):
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)