PCO step scanning works
This commit is contained in:
@@ -188,12 +188,13 @@ pcocam:
|
|||||||
prefix: 'X02DA-CCDCAM2:'
|
prefix: 'X02DA-CCDCAM2:'
|
||||||
deviceTags:
|
deviceTags:
|
||||||
- camera
|
- camera
|
||||||
|
- trigger
|
||||||
- pcocam
|
- pcocam
|
||||||
enabled: true
|
enabled: true
|
||||||
onFailure: buffer
|
onFailure: buffer
|
||||||
readOnly: false
|
readOnly: false
|
||||||
readoutPriority: monitored
|
readoutPriority: monitored
|
||||||
softwareTrigger: false
|
softwareTrigger: true
|
||||||
|
|
||||||
pcodaq:
|
pcodaq:
|
||||||
description: GigaFrost stdDAQ client
|
description: GigaFrost stdDAQ client
|
||||||
|
|||||||
@@ -6,16 +6,20 @@ Created on Wed Dec 6 11:33:54 2023
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from ophyd import Component, EpicsSignal, EpicsSignalRO, Kind
|
from ophyd import Component, EpicsSignal, EpicsSignalRO, Kind
|
||||||
from ophyd.status import SubscriptionStatus
|
from ophyd.status import SubscriptionStatus, DeviceStatus
|
||||||
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,
|
||||||
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from bec_lib import bec_logger
|
from bec_lib import bec_logger
|
||||||
|
|
||||||
logger = bec_logger.logger
|
logger = bec_logger.logger
|
||||||
except ModuleNotFoundError:
|
except ModuleNotFoundError:
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
logger = logging.getLogger("PcoEdgeCam")
|
logger = logging.getLogger("PcoEdgeCam")
|
||||||
|
|
||||||
|
|
||||||
@@ -24,42 +28,43 @@ class PcoEdgeCameraMixin(CustomDeviceMixin):
|
|||||||
|
|
||||||
This class will be called by the custom_prepare_cls attribute of the detector class.
|
This class will be called by the custom_prepare_cls attribute of the detector class.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def on_stage(self) -> None:
|
def on_stage(self) -> None:
|
||||||
"""Configure and arm PCO.Edge camera for acquisition
|
"""Configure and arm PCO.Edge camera for acquisition"""
|
||||||
"""
|
|
||||||
|
|
||||||
# PCO 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()
|
||||||
time.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
|
||||||
d = {}
|
d = {}
|
||||||
if 'kwargs' in scanparam:
|
if "kwargs" in scanparam:
|
||||||
scanargs = scanparam['kwargs']
|
scanargs = scanparam["kwargs"]
|
||||||
if 'num_images_total' in scanargs and scanargs['num_images_total'] is not None:
|
if "exp_burst" in scanargs and scanargs["exp_burst"] is not None:
|
||||||
d['images_total'] = scanargs['num_images_total']
|
d["exposure_num_burst"] = scanargs["exp_burst"]
|
||||||
if 'image_width' in scanargs and scanargs['image_width'] is not None:
|
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'] is not 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'] is not 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'] is not 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'] is not 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'] is not 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:
|
if "pco_store_mode" in scanargs and scanargs["pco_store_mode"] is not None:
|
||||||
d['store_mode'] = scanargs['pco_store_mode']
|
d["store_mode"] = scanargs["pco_store_mode"]
|
||||||
if 'pco_data_format' in scanargs and scanargs['pco_data_format'] is not None:
|
if "pco_data_format" in scanargs and scanargs["pco_data_format"] is not None:
|
||||||
d['data_format'] = scanargs['pco_data_format']
|
d["data_format"] = scanargs["pco_data_format"]
|
||||||
|
|
||||||
|
|
||||||
# Perform bluesky-style configuration
|
# Perform bluesky-style configuration
|
||||||
if len(d) > 0:
|
if len(d) > 0:
|
||||||
@@ -70,29 +75,86 @@ class PcoEdgeCameraMixin(CustomDeviceMixin):
|
|||||||
self.parent.bluestage()
|
self.parent.bluestage()
|
||||||
|
|
||||||
def on_unstage(self) -> None:
|
def on_unstage(self) -> None:
|
||||||
"""Disarm the PCO.Edge camera
|
"""Disarm the PCO.Edge camera"""
|
||||||
"""
|
|
||||||
self.parent.blueunstage()
|
self.parent.blueunstage()
|
||||||
|
|
||||||
def on_stop(self) -> None:
|
def on_stop(self) -> None:
|
||||||
"""Stop the PCO.Edge camera
|
"""Stop the PCO.Edge camera"""
|
||||||
"""
|
|
||||||
self.parent.blueunstage()
|
self.parent.blueunstage()
|
||||||
|
|
||||||
|
def on_trigger(self) -> None | DeviceStatus:
|
||||||
|
"""Trigger mode operation
|
||||||
|
|
||||||
|
Use it to repeatedly record a fixed number of frames and send it to stdDAQ. The method waits
|
||||||
|
for the acquisition and data transfer to complete.
|
||||||
|
|
||||||
|
NOTE: Maciej confirmed that sparse data is no problem to the stdDAQ.
|
||||||
|
TODO: Optimize data transfer to launch at end and check completion at the beginning.
|
||||||
|
"""
|
||||||
|
logger.warning(f"triggering PCO")
|
||||||
|
# Ensure that previous data transfer finished
|
||||||
|
# def sentIt(*args, value, timestamp, **kwargs):
|
||||||
|
# return value==0
|
||||||
|
# status = SubscriptionStatus(self.parent.file_savebusy, sentIt, timeout=120)
|
||||||
|
# status.wait()
|
||||||
|
|
||||||
|
# Not sure if it always sends the first batch of images or the newest
|
||||||
|
def didWeReset(*args, old_value, value, timestamp, **kwargs):
|
||||||
|
return (value < old_value) or (value == 0)
|
||||||
|
|
||||||
|
self.parent.buffer_clear.set(1).wait()
|
||||||
|
status = SubscriptionStatus(self.parent.buffer_used, didWeReset, timeout=5)
|
||||||
|
status.wait()
|
||||||
|
|
||||||
|
t_expected = (
|
||||||
|
self.parent.acquire_time.get() + self.parent.acquire_delay.get()
|
||||||
|
) * self.parent.file_savestop.get()
|
||||||
|
|
||||||
|
# Wait until the buffer fills up with enough images
|
||||||
|
def areWeDoneYet(*args, old_value, value, timestamp, **kwargs):
|
||||||
|
num_target = self.parent.file_savestop.get()
|
||||||
|
# logger.warning(f"{value} of {num_target}")
|
||||||
|
return bool(value >= num_target)
|
||||||
|
|
||||||
|
status = SubscriptionStatus(
|
||||||
|
self.parent.buffer_used, areWeDoneYet, timeout=max(5, 5 * t_expected), settle_time=0.2
|
||||||
|
)
|
||||||
|
status.wait()
|
||||||
|
|
||||||
|
# Then start file transfer (need to get the save busy flag update)
|
||||||
|
# self.parent.file_transfer.set(1, settle_time=0.2).wait()
|
||||||
|
self.parent.file_transfer.set(1).wait()
|
||||||
|
|
||||||
|
# And wait until the images have been sent
|
||||||
|
# NOTE: this does not wait for new value, the first check will be
|
||||||
|
# against values from the previous cycle, i.e. pass automatically.
|
||||||
|
t_start = time.time()
|
||||||
|
|
||||||
|
def haveWeSentIt(*args, old_value, value, timestamp, **kwargs):
|
||||||
|
t_elapsed = timestamp - t_start
|
||||||
|
# logger.warning(f"{old_value}\t{value}\t{t_elapsed}")
|
||||||
|
return old_value == 1 and value == 0 and t_elapsed > 0
|
||||||
|
|
||||||
|
status = SubscriptionStatus(
|
||||||
|
self.parent.file_savebusy, haveWeSentIt, timeout=120, settle_time=0.2
|
||||||
|
)
|
||||||
|
status.wait()
|
||||||
|
logger.warning(f"done PCO")
|
||||||
|
|
||||||
|
|
||||||
class HelgeCameraBase(PSIDeviceBase):
|
class HelgeCameraBase(PSIDeviceBase):
|
||||||
"""Ophyd baseclass for Helge camera IOCs
|
"""Ophyd baseclass for Helge camera IOCs
|
||||||
|
|
||||||
This class provides wrappers for Helge's camera IOCs around SwissFEL and
|
This class provides wrappers for Helge's camera IOCs around SwissFEL and
|
||||||
for high performance SLS 2.0 cameras. The IOC's operation is a bit arcane
|
for high performance SLS 2.0 cameras. The IOC's operation is a bit arcane
|
||||||
and there are different versions and cameras all around. So this device
|
and there are different versions and cameras all around. So this device
|
||||||
only covers the absolute basics.
|
only covers the absolute basics.
|
||||||
|
|
||||||
Probably the most important part is the configuration state machine. As
|
Probably the most important part is the configuration state machine. As
|
||||||
the SET_PARAMS takes care of buffer allocations it might take some time,
|
the SET_PARAMS takes care of buffer allocations it might take some time,
|
||||||
as well as a full re-configuration is required every time we change the
|
as well as a full re-configuration is required every time we change the
|
||||||
binning, roi, etc... This is automatically performed upon starting an
|
binning, roi, etc... This is automatically performed upon starting an
|
||||||
exposure (if it heven't been done before).
|
exposure (if it heven't been done before).
|
||||||
|
|
||||||
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
|
||||||
@@ -112,12 +174,12 @@ class HelgeCameraBase(PSIDeviceBase):
|
|||||||
Note that in FIFO mode buffer reads are destructive, to prevent this, we don't have EPICS preview
|
Note that in FIFO mode buffer reads are destructive, to prevent this, we don't have EPICS preview
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# ########################################################################
|
# ########################################################################
|
||||||
# General hardware info (in AD nomenclature)
|
# General hardware info (in AD nomenclature)
|
||||||
manufacturer = Component(EpicsSignalRO, "QUERY", kind=Kind.config, doc="Camera model info")
|
manufacturer = Component(EpicsSignalRO, "QUERY", kind=Kind.config, doc="Camera model info")
|
||||||
model = Component(EpicsSignalRO, "BOARD", kind=Kind.omitted, doc="Camera board info")
|
model = Component(EpicsSignalRO, "BOARD", kind=Kind.omitted, doc="Camera board info")
|
||||||
|
|
||||||
# ########################################################################
|
# ########################################################################
|
||||||
# Acquisition commands
|
# Acquisition commands
|
||||||
@@ -125,30 +187,41 @@ class HelgeCameraBase(PSIDeviceBase):
|
|||||||
|
|
||||||
# ########################################################################
|
# ########################################################################
|
||||||
# Acquisition configuration (in AD nomenclature)
|
# Acquisition configuration (in AD nomenclature)
|
||||||
acquire_time = Component(EpicsSignal, "EXPOSURE", put_complete=True, auto_monitor=True, kind=Kind.config)
|
acquire_time = Component(
|
||||||
acquire_delay = Component(EpicsSignal, "DELAY", put_complete=True, auto_monitor=True, kind=Kind.config)
|
EpicsSignal, "EXPOSURE", put_complete=True, auto_monitor=True, kind=Kind.config
|
||||||
trigger_mode = Component(EpicsSignal, "TRIGGER", put_complete=True, auto_monitor=True, kind=Kind.config)
|
)
|
||||||
|
acquire_delay = Component(
|
||||||
|
EpicsSignal, "DELAY", put_complete=True, auto_monitor=True, kind=Kind.config
|
||||||
|
)
|
||||||
|
trigger_mode = Component(
|
||||||
|
EpicsSignal, "TRIGGER", put_complete=True, auto_monitor=True, kind=Kind.config
|
||||||
|
)
|
||||||
|
|
||||||
# ########################################################################
|
# ########################################################################
|
||||||
# Image size configuration (in AD nomenclature)
|
# Image size configuration (in AD nomenclature)
|
||||||
bin_x = Component(EpicsSignal, "BINX", put_complete=True, auto_monitor=True, kind=Kind.config)
|
bin_x = Component(EpicsSignal, "BINX", put_complete=True, auto_monitor=True, kind=Kind.config)
|
||||||
bin_y = Component(EpicsSignal, "BINY", put_complete=True, auto_monitor=True, kind=Kind.config)
|
bin_y = Component(EpicsSignal, "BINY", put_complete=True, auto_monitor=True, kind=Kind.config)
|
||||||
array_size_x = Component(EpicsSignalRO, "WIDTH", auto_monitor=True, kind=Kind.config, doc="Final image width")
|
array_size_x = Component(
|
||||||
array_size_y = Component(EpicsSignalRO, "HEIGHT", auto_monitor=True, kind=Kind.config, doc="Final image height")
|
EpicsSignalRO, "WIDTH", auto_monitor=True, kind=Kind.config, doc="Final image width"
|
||||||
|
)
|
||||||
|
array_size_y = Component(
|
||||||
|
EpicsSignalRO, "HEIGHT", auto_monitor=True, kind=Kind.config, doc="Final image height"
|
||||||
|
)
|
||||||
|
|
||||||
# ########################################################################
|
# ########################################################################
|
||||||
# General hardware info
|
# General hardware info
|
||||||
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
|
# Buffer configuration
|
||||||
bufferRecMode = Component(EpicsSignalRO, "RECMODE", auto_monitor=True, kind=Kind.config)
|
bufferRecMode = Component(EpicsSignalRO, "RECMODE", auto_monitor=True, kind=Kind.config)
|
||||||
bufferStoreMode = Component(EpicsSignal, "STOREMODE", 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)
|
fileRecMode = Component(EpicsSignalRO, "RECMODE", auto_monitor=True, kind=Kind.config)
|
||||||
|
|
||||||
buffer_used = Component(EpicsSignalRO, "PIC_BUFFER", auto_monitor=True, kind=Kind.normal)
|
buffer_used = Component(EpicsSignalRO, "PIC_BUFFER", auto_monitor=True, kind=Kind.normal)
|
||||||
buffer_size = Component(EpicsSignalRO, "PIC_MAX", auto_monitor=True, kind=Kind.normal)
|
buffer_size = Component(EpicsSignalRO, "PIC_MAX", auto_monitor=True, kind=Kind.normal)
|
||||||
|
buffer_clear = Component(EpicsSignal, "CLEARMEM", put_complete=True, kind=Kind.omitted)
|
||||||
|
|
||||||
# ########################################################################
|
# ########################################################################
|
||||||
# File saving interface
|
# File saving interface
|
||||||
@@ -158,15 +231,18 @@ class HelgeCameraBase(PSIDeviceBase):
|
|||||||
file_savestop = Component(EpicsSignal, "SAVESTOP", 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_format = Component(EpicsSignal, "FILEFORMAT", put_complete=True, kind=Kind.config)
|
||||||
file_transfer = Component(EpicsSignal, "FTRANSFER", put_complete=True, kind=Kind.config)
|
file_transfer = Component(EpicsSignal, "FTRANSFER", put_complete=True, kind=Kind.config)
|
||||||
|
file_savebusy = Component(EpicsSignalRO, "FILESAVEBUSY", auto_monitor=True, kind=Kind.normal)
|
||||||
|
|
||||||
# ########################################################################
|
# ########################################################################
|
||||||
# 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)
|
||||||
camSetParam = Component(EpicsSignal, "SET_PARAM", auto_monitor=True, kind=Kind.config)
|
camSetParam = Component(EpicsSignal, "SET_PARAM", auto_monitor=True, kind=Kind.config)
|
||||||
camSetParamBusy = Component(EpicsSignalRO, "BUSY_SET_PARAM", auto_monitor=True, kind=Kind.config)
|
camSetParamBusy = Component(
|
||||||
|
EpicsSignalRO, "BUSY_SET_PARAM", auto_monitor=True, kind=Kind.config
|
||||||
|
)
|
||||||
camCamera = Component(EpicsSignalRO, "CAMERA", auto_monitor=True, kind=Kind.config)
|
camCamera = Component(EpicsSignalRO, "CAMERA", auto_monitor=True, kind=Kind.config)
|
||||||
camCameraBusy = Component(EpicsSignalRO, "BUSY_CAMERA", auto_monitor=True, kind=Kind.config)
|
camCameraBusy = Component(EpicsSignalRO, "BUSY_CAMERA", auto_monitor=True, kind=Kind.config)
|
||||||
camInit= Component(EpicsSignalRO, "INIT", auto_monitor=True, kind=Kind.config)
|
camInit = Component(EpicsSignalRO, "INIT", auto_monitor=True, kind=Kind.config)
|
||||||
camInitBusy = Component(EpicsSignalRO, "BUSY_INIT", auto_monitor=True, kind=Kind.config)
|
camInitBusy = Component(EpicsSignalRO, "BUSY_INIT", auto_monitor=True, kind=Kind.config)
|
||||||
|
|
||||||
# ########################################################################
|
# ########################################################################
|
||||||
@@ -175,22 +251,24 @@ class HelgeCameraBase(PSIDeviceBase):
|
|||||||
|
|
||||||
# ########################################################################
|
# ########################################################################
|
||||||
# Misc PVs
|
# Misc PVs
|
||||||
#camRemoval = Component(EpicsSignalRO, "REMOVAL", auto_monitor=True, kind=Kind.config)
|
# camRemoval = Component(EpicsSignalRO, "REMOVAL", auto_monitor=True, kind=Kind.config)
|
||||||
camStateString = Component(EpicsSignalRO, "SS_CAMERA", string=True, auto_monitor=True, kind=Kind.config)
|
camStateString = Component(
|
||||||
|
EpicsSignalRO, "SS_CAMERA", string=True, auto_monitor=True, kind=Kind.config
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def state(self) -> str:
|
def state(self) -> str:
|
||||||
""" Single word camera state"""
|
"""Single word camera state"""
|
||||||
if self.camSetParamBusy.value:
|
if self.camSetParamBusy.value:
|
||||||
return "BUSY"
|
return "BUSY"
|
||||||
if self.camStatusCode.value==2 and self.camInit.value==1:
|
if self.camStatusCode.value == 2 and self.camInit.value == 1:
|
||||||
return "IDLE"
|
return "IDLE"
|
||||||
if self.camStatusCode.value==6 and self.camInit.value==1:
|
if self.camStatusCode.value == 6 and self.camInit.value == 1:
|
||||||
return "RUNNING"
|
return "RUNNING"
|
||||||
#if self.camRemoval.value==0 and self.camInit.value==0:
|
# if self.camRemoval.value==0 and self.camInit.value==0:
|
||||||
if self.camInit.value==0:
|
if self.camInit.value == 0:
|
||||||
return "OFFLINE"
|
return "OFFLINE"
|
||||||
#if self.camRemoval.value:
|
# if self.camRemoval.value:
|
||||||
# return "REMOVED"
|
# return "REMOVED"
|
||||||
return "UNKNOWN"
|
return "UNKNOWN"
|
||||||
|
|
||||||
@@ -199,103 +277,113 @@ 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
|
Parameters as 'd' dictionary
|
||||||
----------------------------
|
----------------------------
|
||||||
num_images : int
|
num_images : int
|
||||||
Number of images to be taken during each scan. Meaning depends on
|
Number of images to be taken during each scan. Meaning depends on
|
||||||
store mode.
|
store mode.
|
||||||
exposure_time_ms : float
|
exposure_time_ms : float
|
||||||
Exposure time [ms], usually gets set back to 20 ms
|
Exposure time [ms], usually gets set back to 20 ms
|
||||||
exposure_period_ms : float
|
exposure_period_ms : float
|
||||||
Exposure period [ms], up to 200 ms.
|
Exposure period [ms], up to 200 ms.
|
||||||
store_mode : str
|
store_mode : str
|
||||||
Buffer operation mode
|
Buffer operation mode
|
||||||
*'Recorder' to record in buffer
|
*'Recorder' to record in buffer
|
||||||
*'FIFO buffer' for continous streaming
|
*'FIFO buffer' for continous streaming
|
||||||
data_format : str
|
data_format : str
|
||||||
Usually set to 'ZEROMQ'
|
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:
|
if "exposure_num_burst" in d:
|
||||||
self.file_savestop.set(d['num_images']).wait()
|
self.file_savestop.set(d["exposure_num_burst"]).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:
|
||||||
self.acquire_delay.set(d['exposure_period_ms']).wait()
|
self.acquire_delay.set(d["exposure_period_ms"]).wait()
|
||||||
if 'exposure_period_ms' in d:
|
if "exposure_period_ms" in d:
|
||||||
self.acquire_delay.set(d['exposure_period_ms']).wait()
|
self.acquire_delay.set(d["exposure_period_ms"]).wait()
|
||||||
if 'store_mode' in d:
|
if "store_mode" in d:
|
||||||
self.bufferStoreMode.set(d['store_mode']).wait()
|
self.bufferStoreMode.set(d["store_mode"]).wait()
|
||||||
if 'data_format' in d:
|
if "data_format" in d:
|
||||||
self.file_format.set(d['data_format']).wait()
|
self.file_format.set(d["data_format"]).wait()
|
||||||
|
|
||||||
# State machine
|
# State machine
|
||||||
# Initial: BUSY and SET both low
|
# Initial: BUSY and SET both low
|
||||||
# 0. Write 1 to SET_PARAM
|
# 0. Write 1 to SET_PARAM
|
||||||
# 1. BUSY goes high, SET stays low
|
# 1. BUSY goes high, SET stays low
|
||||||
# 2. BUSY goes low, SET goes high
|
# 2. BUSY goes low, SET goes high
|
||||||
# 3. BUSY stays low, SET goes low
|
# 3. BUSY stays low, SET goes low
|
||||||
# So we need a 'negedge' on SET_PARAM
|
# So we need a 'negedge' on SET_PARAM
|
||||||
self.camSetParam.set(1).wait()
|
self.camSetParam.set(1).wait()
|
||||||
|
|
||||||
def fallingEdge(*args, old_value, value, timestamp, **kwargs):
|
def fallingEdge(*args, old_value, value, timestamp, **kwargs):
|
||||||
return bool(old_value and not value)
|
return bool(old_value and not value)
|
||||||
|
|
||||||
# Subscribe and wait for update
|
# Subscribe and wait for update
|
||||||
status = SubscriptionStatus(self.camSetParam, fallingEdge, timeout=5, settle_time=0.5)
|
status = SubscriptionStatus(self.camSetParam, fallingEdge, timeout=5, settle_time=0.5)
|
||||||
status.wait()
|
status.wait()
|
||||||
|
|
||||||
def bluestage(self):
|
def bluestage(self):
|
||||||
"""Bluesky style stage: arm the detector
|
"""Bluesky style stage: arm the detector"""
|
||||||
"""
|
logger.warning("Staging PCO")
|
||||||
# Acquisition is only allowed when the IOC is not busy
|
# Acquisition is only allowed when the IOC is not busy
|
||||||
if self.state in ("OFFLINE", "BUSY", "REMOVED", "RUNNING"):
|
if self.state in ("OFFLINE", "BUSY", "REMOVED", "RUNNING"):
|
||||||
raise RuntimeError(f"Camera in in state: {self.state}")
|
raise RuntimeError(f"Camera in in state: {self.state}")
|
||||||
|
|
||||||
|
if (
|
||||||
|
self.bufferStoreMode.get() in ("Recorder", 0)
|
||||||
|
and self.file_savestop.get() > self.buffer_size.get()
|
||||||
|
):
|
||||||
|
self.logger.warning(
|
||||||
|
"You're about to send some empty images, {self.file_savestop.get()} is above buffer size"
|
||||||
|
)
|
||||||
|
|
||||||
# Start the acquisition (this sets parameers and starts acquisition)
|
# Start the acquisition (this sets parameers and starts acquisition)
|
||||||
self.camStatusCmd.set("Running").wait()
|
self.camStatusCmd.set("Running").wait()
|
||||||
|
|
||||||
# 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(value==6)
|
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()
|
||||||
|
|
||||||
def blueunstage(self):
|
def blueunstage(self):
|
||||||
"""Bluesky style unstage: stop the detector
|
"""Bluesky style unstage: stop the detector"""
|
||||||
"""
|
|
||||||
self.camStatusCmd.set("Idle").wait()
|
self.camStatusCmd.set("Idle").wait()
|
||||||
self.custom_prepare.stop_monitor = True
|
self.custom_prepare.stop_monitor = True
|
||||||
|
|
||||||
# Subscribe and wait for update
|
|
||||||
def isIdle(*args, old_value, value, timestamp, **kwargs):
|
|
||||||
return bool(value==2)
|
|
||||||
status = SubscriptionStatus(self.camStatusCode, isIdle, timeout=5, settle_time=0.2)
|
|
||||||
status.wait()
|
|
||||||
|
|
||||||
# Data streaming is stopped by setting the max index to 0
|
# Data streaming is stopped by setting the max index to 0
|
||||||
|
# FIXME: This might interrupt data transfer
|
||||||
self.file_savestop.set(0).wait()
|
self.file_savestop.set(0).wait()
|
||||||
|
|
||||||
|
|
||||||
def bluekickoff(self):
|
def bluekickoff(self):
|
||||||
""" Start data transfer
|
"""Start data transfer
|
||||||
|
|
||||||
TODO: Need to revisit this once triggering is complete
|
TODO: Need to revisit this once triggering is complete
|
||||||
"""
|
"""
|
||||||
self.file_transfer.set(1).wait()
|
self.file_transfer.set(1).wait()
|
||||||
|
|
||||||
|
# def complete(self):
|
||||||
|
# """ Wait until the images have been sent"""
|
||||||
|
# def areWeSending(*args, value, timestamp, **kwargs):
|
||||||
|
# return not bool(value)
|
||||||
|
# status = SubscriptionStatus(self.file_savebusy, haveWeSentIt, timeout=None, settle_time=0.2)
|
||||||
|
# return status
|
||||||
|
|
||||||
|
|
||||||
class PcoEdge5M(HelgeCameraBase):
|
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
|
||||||
for high performance SLS 2.0 cameras. Theese are mostly PCO cameras running
|
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.
|
on a special Windows IOC host with lots of RAM and CPU power.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -310,28 +398,36 @@ class PcoEdge5M(HelgeCameraBase):
|
|||||||
camRate = Component(EpicsSignalRO, "CAMRATE", auto_monitor=True, kind=Kind.config)
|
camRate = Component(EpicsSignalRO, "CAMRATE", auto_monitor=True, kind=Kind.config)
|
||||||
|
|
||||||
# ########################################################################
|
# ########################################################################
|
||||||
# Acquisition configuration
|
# Acquisition configuration
|
||||||
acqMode = Component(EpicsSignalRO, "ACQMODE", auto_monitor=True, kind=Kind.config)
|
acqMode = Component(EpicsSignalRO, "ACQMODE", auto_monitor=True, kind=Kind.config)
|
||||||
acqDelay = Component(EpicsSignalRO, "DELAY", auto_monitor=True, kind=Kind.config)
|
acqDelay = Component(EpicsSignalRO, "DELAY", auto_monitor=True, kind=Kind.config)
|
||||||
acqTriggerEna = Component(EpicsSignalRO, "TRIGGER", auto_monitor=True, kind=Kind.config)
|
acqTriggerEna = Component(EpicsSignalRO, "TRIGGER", auto_monitor=True, kind=Kind.config)
|
||||||
#acqTriggerSource = Component(EpicsSignalRO, "TRIGGERSOURCE", auto_monitor=True, kind=Kind.config)
|
# acqTriggerSource = Component(EpicsSignalRO, "TRIGGERSOURCE", auto_monitor=True, kind=Kind.config)
|
||||||
#acqTriggerEdge = Component(EpicsSignalRO, "TRIGGEREDGE", auto_monitor=True, kind=Kind.config)
|
# acqTriggerEdge = Component(EpicsSignalRO, "TRIGGEREDGE", auto_monitor=True, kind=Kind.config)
|
||||||
|
|
||||||
# ########################################################################
|
# ########################################################################
|
||||||
# Image size settings
|
# Image size settings
|
||||||
# Priority is: binning -> roi -> final size
|
# Priority is: binning -> roi -> final size
|
||||||
pxRoiX_lo = Component(EpicsSignal, "REGIONX_START", put_complete=True, auto_monitor=True, kind=Kind.config)
|
pxRoiX_lo = Component(
|
||||||
pxRoiX_hi = Component(EpicsSignal, "REGIONX_END", put_complete=True, auto_monitor=True, kind=Kind.config)
|
EpicsSignal, "REGIONX_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)
|
pxRoiX_hi = Component(
|
||||||
|
EpicsSignal, "REGIONX_END", 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
|
||||||
|
)
|
||||||
|
|
||||||
def configure(self, d: dict = {}) -> tuple:
|
def configure(self, d: dict = {}) -> tuple:
|
||||||
"""
|
"""
|
||||||
Camera configuration instructions:
|
Camera configuration instructions:
|
||||||
After setting the corresponding PVs, one needs to process SET_PARAM and wait until
|
After setting the corresponding PVs, one needs to process SET_PARAM and wait until
|
||||||
BUSY_SET_PARAM goes high and low, followed by SET_PARAM goes high and low. This will
|
BUSY_SET_PARAM goes high and low, followed by SET_PARAM goes high and low. This will
|
||||||
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.
|
||||||
|
|
||||||
NOTE:
|
NOTE:
|
||||||
@@ -360,18 +456,18 @@ class PcoEdge5M(HelgeCameraBase):
|
|||||||
# 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)
|
||||||
# Values are rounded to multiples of 16
|
# Values are rounded to multiples of 16
|
||||||
if 'image_width' in d and d['image_width'] is not None:
|
if "image_width" in d and d["image_width"] is not None:
|
||||||
width = d['image_width']
|
width = d["image_width"]
|
||||||
self.pxRoiX_lo.set(2560/2-width/2).wait()
|
self.pxRoiX_lo.set(2560 / 2 - width / 2).wait()
|
||||||
self.pxRoiX_hi.set(2560/2+width/2).wait()
|
self.pxRoiX_hi.set(2560 / 2 + width / 2).wait()
|
||||||
if 'image_height' in d and d['image_height'] is not None:
|
if "image_height" in d and d["image_height"] is not None:
|
||||||
height = d['image_height']
|
height = d["image_height"]
|
||||||
self.pxRoiY_lo.set(2160/2-height/2).wait()
|
self.pxRoiY_lo.set(2160 / 2 - height / 2).wait()
|
||||||
self.pxRoiY_hi.set(2160/2+height/2).wait()
|
self.pxRoiY_hi.set(2160 / 2 + height / 2).wait()
|
||||||
if 'image_binx' in d and d['image_binx'] is not None:
|
if "image_binx" in d and d["image_binx"] is not None:
|
||||||
self.pxBinX.set(d['image_binx']).wait()
|
self.pxBinX.set(d["image_binx"]).wait()
|
||||||
if 'image_biny' in d and d['image_biny'] is not None:
|
if "image_biny" in d and d["image_biny"] is not None:
|
||||||
self.pxBinY.set(d['image_biny']).wait()
|
self.pxBinY.set(d["image_biny"]).wait()
|
||||||
|
|
||||||
# Call super() to commit the changes
|
# Call super() to commit the changes
|
||||||
super().configure(d)
|
super().configure(d)
|
||||||
@@ -379,7 +475,7 @@ class PcoEdge5M(HelgeCameraBase):
|
|||||||
|
|
||||||
# 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 = PcoEdgeBase("X02DA-CCDCAM2:", name="mcpcam")
|
cam = PcoEdgeBase("X02DA-CCDCAM2:", name="mcpcam")
|
||||||
cam.wait_for_connection()
|
cam.wait_for_connection()
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ class StdDaqMixin(CustomDeviceMixin):
|
|||||||
for msg in self.parent._wsclient:
|
for msg in self.parent._wsclient:
|
||||||
message = json.loads(msg)
|
message = json.loads(msg)
|
||||||
self.parent.runstatus.put(message["status"], force=True)
|
self.parent.runstatus.put(message["status"], force=True)
|
||||||
logger.info(f"[{self.parent.name}] Pushed status: {message['status']}")
|
# logger.info(f"[{self.parent.name}] Pushed status: {message['status']}")
|
||||||
except (ConnectionClosedError, ConnectionClosedOK, AssertionError):
|
except (ConnectionClosedError, ConnectionClosedOK, AssertionError):
|
||||||
# Libraty throws theese after connection is closed
|
# Libraty throws theese after connection is closed
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ class TomcatStepScan(ScanBase):
|
|||||||
yield from self._move_scan_motors_and_wait(pos)
|
yield from self._move_scan_motors_and_wait(pos)
|
||||||
time.sleep(self.settling_time)
|
time.sleep(self.settling_time)
|
||||||
|
|
||||||
trigger_time = self.exp_time * self.burst_at_each_point
|
trigger_time = 0.001*self.exp_time * self.burst_at_each_point
|
||||||
yield from self.stubs.trigger(min_wait=trigger_time)
|
yield from self.stubs.trigger(min_wait=trigger_time)
|
||||||
# yield from self.stubs.trigger(group='trigger', point_id=self.point_id)
|
# yield from self.stubs.trigger(group='trigger', point_id=self.point_id)
|
||||||
# time.sleep(trigger_time)
|
# time.sleep(trigger_time)
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ class AcquireDark(Acquire):
|
|||||||
|
|
||||||
def scan_core(self):
|
def scan_core(self):
|
||||||
# close the shutter
|
# close the shutter
|
||||||
# yield from self._move_scan_motors_and_wait(self.dark_shutter_pos)
|
yield from self._move_scan_motors_and_wait(self.dark_shutter_pos)
|
||||||
#yield from self.stubs.set_and_wait(device=[self.shutter], positions=[0])
|
#yield from self.stubs.set_and_wait(device=[self.shutter], positions=[0])
|
||||||
yield from super().scan_core()
|
yield from super().scan_core()
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,25 @@ def dev_disable_all():
|
|||||||
dev[k].enabled = False
|
dev[k].enabled = False
|
||||||
|
|
||||||
|
|
||||||
|
def cam_select(camera: str):
|
||||||
|
"""Select the active camera pipeline"""
|
||||||
|
if isinstance(camera, str):
|
||||||
|
if camera in ("gf", "gf2", "gigafrost"):
|
||||||
|
dev.gfcam.enabled = True
|
||||||
|
dev.gfdaq.enabled = True
|
||||||
|
dev.daq_stream0.enabled = True
|
||||||
|
dev.pcocam.enabled = False
|
||||||
|
dev.pcodaq.enabled = False
|
||||||
|
dev.pco_stream0.enabled = False
|
||||||
|
if camera in ("pco", "pco.edge", "pcoedge"):
|
||||||
|
dev.gfcam.enabled = False
|
||||||
|
dev.gfdaq.enabled = False
|
||||||
|
dev.daq_stream0.enabled = False
|
||||||
|
dev.pcocam.enabled = True
|
||||||
|
dev.pcodaq.enabled = True
|
||||||
|
dev.pco_stream0.enabled = True
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def anotherstepscan(
|
def anotherstepscan(
|
||||||
scan_start,
|
scan_start,
|
||||||
@@ -26,8 +45,9 @@ def anotherstepscan(
|
|||||||
exp_time=0.005,
|
exp_time=0.005,
|
||||||
exp_burst=5,
|
exp_burst=5,
|
||||||
settling_time=0,
|
settling_time=0,
|
||||||
image_width=2016,
|
camera=None,
|
||||||
image_height=2016,
|
image_width=None,
|
||||||
|
image_height=None,
|
||||||
sync="inp1",
|
sync="inp1",
|
||||||
**kwargs
|
**kwargs
|
||||||
):
|
):
|
||||||
@@ -41,31 +61,49 @@ def anotherstepscan(
|
|||||||
--------
|
--------
|
||||||
demostepscan(scan_start=-32, scan_end=148, steps=180, exp_time=0.005, exp_burst=5)
|
demostepscan(scan_start=-32, scan_end=148, steps=180, exp_time=0.005, exp_burst=5)
|
||||||
"""
|
"""
|
||||||
# if not bl_check_beam():
|
# Check beamline status before scan
|
||||||
# raise RuntimeError("Beamline is not in ready state")
|
if not bl_check_beam():
|
||||||
|
raise RuntimeError("Beamline is not in ready state")
|
||||||
|
# Enable the correct camera before scan
|
||||||
|
cam_select(camera)
|
||||||
|
# This scan uses software triggering
|
||||||
|
if isinstance(camera, str):
|
||||||
|
if camera in ("gf", "gf2", "gigafrost"):
|
||||||
|
dev.gfcam.software_trigger = True
|
||||||
|
if camera in ("pco", "pco.edge", "pcoedge"):
|
||||||
|
dev.pcocam.software_trigger = True
|
||||||
|
|
||||||
dev_disable_all()
|
# Disable aerotech controller
|
||||||
|
# dev.es1_tasks.enabled = False
|
||||||
|
# dev.es1_psod.enabled = False
|
||||||
|
# dev.es1_ddaq.enabled = True
|
||||||
|
# Enable scan motor
|
||||||
dev.es1_roty.enabled = True
|
dev.es1_roty.enabled = True
|
||||||
#dev.es1_ddaq.enabled = True
|
|
||||||
dev.gfcam.enabled = True
|
|
||||||
dev.gfdaq.enabled = True
|
|
||||||
dev.daq_stream0.enabled = True
|
|
||||||
dev.daq_stream1.enabled = False
|
|
||||||
|
|
||||||
print("Handing over to 'scans.tomcatstepscan'")
|
print("Handing over to 'scans.tomcatstepscan'")
|
||||||
scans.tomcatstepscan(
|
try:
|
||||||
scan_start=scan_start,
|
scans.tomcatstepscan(
|
||||||
scan_end=scan_end,
|
scan_start=scan_start,
|
||||||
steps=steps,
|
scan_end=scan_end,
|
||||||
exp_time=exp_time,
|
steps=steps,
|
||||||
exp_burst=exp_burst,
|
exp_time=exp_time,
|
||||||
relative=False,
|
exp_burst=exp_burst,
|
||||||
image_width=image_width,
|
relative=False,
|
||||||
image_height=image_height,
|
image_width=image_width,
|
||||||
settling_time=settling_time,
|
image_height=image_height,
|
||||||
sync=sync,
|
settling_time=settling_time,
|
||||||
**kwargs
|
sync=sync,
|
||||||
)
|
**kwargs
|
||||||
|
)
|
||||||
|
finally:
|
||||||
|
# if isinstance(camera, str):
|
||||||
|
# if camera in ("gf", "gf2", "gigafrost"):
|
||||||
|
# dev.gfcam.software_trigger = False
|
||||||
|
# if camera in ("pco", "pco.edge", "pcoedge"):
|
||||||
|
# dev.pcocam.software_trigger = False
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -78,8 +116,9 @@ def anothersequencescan(
|
|||||||
repmode="PosNeg",
|
repmode="PosNeg",
|
||||||
exp_time=0.005,
|
exp_time=0.005,
|
||||||
exp_burst=180,
|
exp_burst=180,
|
||||||
image_width=2016,
|
camera=None,
|
||||||
image_height=2016,
|
image_width=None,
|
||||||
|
image_height=None,
|
||||||
sync="pso",
|
sync="pso",
|
||||||
**kwargs
|
**kwargs
|
||||||
):
|
):
|
||||||
@@ -96,16 +135,15 @@ def anothersequencescan(
|
|||||||
--------
|
--------
|
||||||
>>> anothersequencescan(33, 180, 180, exp_time=0.005, exp_frames=1800, repeats=10)
|
>>> anothersequencescan(33, 180, 180, exp_time=0.005, exp_frames=1800, repeats=10)
|
||||||
"""
|
"""
|
||||||
|
# Check beamline status before scan
|
||||||
if not bl_check_beam():
|
if not bl_check_beam():
|
||||||
raise RuntimeError("Beamline is not in ready state")
|
raise RuntimeError("Beamline is not in ready state")
|
||||||
|
# Enable the correct camera before scan
|
||||||
|
cam_select(camera)
|
||||||
|
# Enabling aerotech controller
|
||||||
dev.es1_tasks.enabled = True
|
dev.es1_tasks.enabled = True
|
||||||
dev.es1_psod.enabled = False
|
dev.es1_psod.enabled = False
|
||||||
dev.es1_ddaq.enabled = True
|
dev.es1_ddaq.enabled = True
|
||||||
dev.gfcam.enabled = True
|
|
||||||
dev.gfdaq.enabled = True
|
|
||||||
dev.daq_stream0.enabled = True
|
|
||||||
dev.daq_stream1.enabled = False
|
|
||||||
|
|
||||||
print("Handing over to 'scans.sequencescan'")
|
print("Handing over to 'scans.sequencescan'")
|
||||||
scans.tomcatsimplesequencescan(
|
scans.tomcatsimplesequencescan(
|
||||||
@@ -131,8 +169,9 @@ def anothersnapnstepscan(
|
|||||||
steps,
|
steps,
|
||||||
exp_time=0.005,
|
exp_time=0.005,
|
||||||
exp_burst=180,
|
exp_burst=180,
|
||||||
image_width=2016,
|
camera=None,
|
||||||
image_height=2016,
|
image_width=None,
|
||||||
|
image_height=None,
|
||||||
settling_time=0.1,
|
settling_time=0.1,
|
||||||
sync="pso",
|
sync="pso",
|
||||||
**kwargs
|
**kwargs
|
||||||
@@ -148,17 +187,15 @@ def anothersnapnstepscan(
|
|||||||
--------
|
--------
|
||||||
>>> anothersnapnstepscan(33, 180, 180, exp_time=0.005, exp_frames=1800, repeats=10)
|
>>> anothersnapnstepscan(33, 180, 180, exp_time=0.005, exp_frames=1800, repeats=10)
|
||||||
"""
|
"""
|
||||||
|
# Check beamline status before scan
|
||||||
if not bl_check_beam():
|
if not bl_check_beam():
|
||||||
raise RuntimeError("Beamline is not in ready state")
|
raise RuntimeError("Beamline is not in ready state")
|
||||||
|
# Enable the correct camera before scan
|
||||||
|
cam_select(camera)
|
||||||
|
# Enabling aerotech controller
|
||||||
dev.es1_tasks.enabled = True
|
dev.es1_tasks.enabled = True
|
||||||
dev.es1_psod.enabled = False
|
dev.es1_psod.enabled = False
|
||||||
dev.es1_ddaq.enabled = True
|
dev.es1_ddaq.enabled = True
|
||||||
dev.gfcam.enabled = True
|
|
||||||
dev.gfdaq.enabled = True
|
|
||||||
dev.daq_stream0.enabled = True
|
|
||||||
dev.daq_stream1.enabled = False
|
|
||||||
|
|
||||||
|
|
||||||
print("Handing over to 'scans.tomcatsnapnstepscan'")
|
print("Handing over to 'scans.tomcatsnapnstepscan'")
|
||||||
scans.tomcatsnapnstepscan(
|
scans.tomcatsnapnstepscan(
|
||||||
@@ -174,3 +211,58 @@ def anothersnapnstepscan(
|
|||||||
**kwargs
|
**kwargs
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def ascan(motor, scan_start, scan_end, steps, exp_time, datasource, visual=True, **kwargs):
|
||||||
|
"""Demo step scan with plotting
|
||||||
|
|
||||||
|
This is a simple user-space demo step scan with automatic plorring and fitting via the BEC.
|
||||||
|
It's mostly a standard BEC scan, just adds GUI setup and fits a hardcoded LlinearModel at
|
||||||
|
the end (other models are more picky on the initial parameters).
|
||||||
|
|
||||||
|
Example:
|
||||||
|
--------
|
||||||
|
ascan(dev.dccm_energy, 12,13, steps=21, exp_time=0.1, datasource=dev.dccm_xbpm)
|
||||||
|
"""
|
||||||
|
# Dummy method to check beamline status
|
||||||
|
if not bl_check_beam():
|
||||||
|
raise RuntimeError("Beamline is not in ready state")
|
||||||
|
|
||||||
|
if visual:
|
||||||
|
# Get or create scan specific window
|
||||||
|
window = None
|
||||||
|
for _, val in bec.gui.windows.items():
|
||||||
|
if val.title == "CurrentScan":
|
||||||
|
window = val.widget
|
||||||
|
window.clear_all()
|
||||||
|
if window is None:
|
||||||
|
window = bec.gui.new("CurrentScan")
|
||||||
|
|
||||||
|
# Draw a simploe plot in the window
|
||||||
|
dock = window.add_dock(f"ScanDisplay {motor}")
|
||||||
|
plt1 = dock.add_widget("BECWaveformWidget")
|
||||||
|
plt1.plot(x_name=motor, y_name=datasource)
|
||||||
|
plt1.set_x_label(motor)
|
||||||
|
plt1.set_y_label(datasource)
|
||||||
|
plt1.add_dap(motor, datasource, dap="LinearModel")
|
||||||
|
window.show()
|
||||||
|
|
||||||
|
print("Handing over to 'scans.line_scan'")
|
||||||
|
if "relative" in kwargs:
|
||||||
|
del kwargs["relative"]
|
||||||
|
s = scans.line_scan(
|
||||||
|
motor, scan_start, scan_end, steps=steps, exp_time=exp_time, relative=False, **kwargs
|
||||||
|
)
|
||||||
|
|
||||||
|
if visual:
|
||||||
|
fit = plt1.get_dap_params()
|
||||||
|
else:
|
||||||
|
fit = bec.dap.LinearModel.fit(s, motor.name, motor.name, datasource.name, datasource.name)
|
||||||
|
|
||||||
|
# TODO: Move to fitted value... like center, peak, edge, etc...
|
||||||
|
|
||||||
|
return s, fit
|
||||||
@@ -4,9 +4,7 @@
|
|||||||
|
|
||||||
def bl_check_beam():
|
def bl_check_beam():
|
||||||
"""Checks beamline status"""
|
"""Checks beamline status"""
|
||||||
motor_enabled = bool(dev.es1_roty.motor_enable.get())
|
return True
|
||||||
result = motor_enabled
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
def demostepscan(
|
def demostepscan(
|
||||||
|
|||||||
Reference in New Issue
Block a user