add example to connect to Epics channels
This commit is contained in:
@ -1,5 +1,5 @@
|
|||||||
xmap_nohdf5:
|
xmap_nohdf5:
|
||||||
description: Falcon detector x-ray fluoresence II
|
description: XMAP detector x-ray fluoresence II
|
||||||
deviceClass: phoenix_bec.devices.xmap_phoenix_no_hdf5.XMAPphoenix
|
deviceClass: phoenix_bec.devices.xmap_phoenix_no_hdf5.XMAPphoenix
|
||||||
deviceConfig:
|
deviceConfig:
|
||||||
prefix: 'X07MB-XMAP:'
|
prefix: 'X07MB-XMAP:'
|
||||||
|
180
phoenix_bec/devices/Phoenix_trigger.py
Normal file
180
phoenix_bec/devices/Phoenix_trigger.py
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
from ophyd import (
|
||||||
|
ADComponent as ADCpt,
|
||||||
|
Device,
|
||||||
|
DeviceStatus,
|
||||||
|
)
|
||||||
|
|
||||||
|
from ophyd import Component as Cpt
|
||||||
|
from ophyd import Device, EpicsSignal, EpicsSignalRO
|
||||||
|
|
||||||
|
from ophyd_devices.interfaces.base_classes.psi_detector_base import PSIDetectorBase, CustomDetectorMixin
|
||||||
|
|
||||||
|
from bec_lib import bec_logger, messages
|
||||||
|
from bec_lib.endpoints import MessageEndpoints
|
||||||
|
|
||||||
|
logger = bec_logger.logger
|
||||||
|
|
||||||
|
DETECTOR_TIMEOUT = 5
|
||||||
|
|
||||||
|
class PhoenixTriggerError(Exception):
|
||||||
|
"""Base class for exceptions in this module."""
|
||||||
|
|
||||||
|
|
||||||
|
class PhoenixTriggerTimeoutError(XMAPError):
|
||||||
|
"""Raised when the PhoenixTrigger does not respond in time."""
|
||||||
|
|
||||||
|
|
||||||
|
class PhoenixTriggerDetectorState(enum.IntEnum):
|
||||||
|
"""Detector states for XMAP detector"""
|
||||||
|
|
||||||
|
DONE = 0
|
||||||
|
ACQUIRING = 1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class PhoenixTriggerSetup(CustomDetectorMixin):
|
||||||
|
|
||||||
|
def __init__(self, *args, parent:Device = None, **kwargs):
|
||||||
|
super().__init__(*args, parent=parent, **kwargs)
|
||||||
|
self._counter = 0
|
||||||
|
|
||||||
|
def on_stage(self):
|
||||||
|
exposure_time = self.parent.scaninfo.exp_time
|
||||||
|
num_points = self.parent.scaninfo.num_points
|
||||||
|
|
||||||
|
# camera acquisition parameters
|
||||||
|
self.parent.cam.array_counter.put(0)
|
||||||
|
if self.parent.scaninfo.scan_type == 'step':
|
||||||
|
self.parent.cam.acquire_time.put(exposure_time)
|
||||||
|
self.parent.cam.num_images.put(1)
|
||||||
|
self.parent.cam.image_mode.put(0) # Single
|
||||||
|
self.parent.cam.trigger_mode.put(0) # auto
|
||||||
|
else:
|
||||||
|
# In flyscan, the exp_time is the time between two triggers,
|
||||||
|
# which minus 15% is used as the acquisition time.
|
||||||
|
self.parent.cam.acquire_time.put(exposure_time * 0.85)
|
||||||
|
self.parent.cam.num_images.put(num_points)
|
||||||
|
self.parent.cam.image_mode.put(1) # Multiple
|
||||||
|
self.parent.cam.trigger_mode.put(1) # trigger
|
||||||
|
self.parent.cam.acquire.put(1, wait=False) # arm
|
||||||
|
|
||||||
|
# file writer
|
||||||
|
self.parent.hdf.lazy_open.put(1)
|
||||||
|
self.parent.hdf.num_capture.put(num_points)
|
||||||
|
self.parent.hdf.file_write_mode.put(2) # Stream
|
||||||
|
self.parent.hdf.capture.put(1, wait=False)
|
||||||
|
self.parent.hdf.enable.put(1) # enable plugin
|
||||||
|
|
||||||
|
# roi statistics to collect signal and background in a timeseries
|
||||||
|
self.parent.roistat.enable.put(1)
|
||||||
|
self.parent.roistat.ts_num_points.put(num_points)
|
||||||
|
self.parent.roistat.ts_control.put(0, wait=False) # Erase/Start
|
||||||
|
|
||||||
|
logger.success('XXXX stage XXXX')
|
||||||
|
|
||||||
|
def on_trigger(self):
|
||||||
|
self.parent.cam.acquire.put(1, wait=False)
|
||||||
|
logger.success('XXXX trigger XXXX')
|
||||||
|
|
||||||
|
return self.wait_with_status(
|
||||||
|
[(self.parent.cam.acquire.get, 0)],
|
||||||
|
self.parent.scaninfo.exp_time + DETECTOR_TIMEOUT,
|
||||||
|
all_signals=True
|
||||||
|
)
|
||||||
|
|
||||||
|
def on_complete(self):
|
||||||
|
status = DeviceStatus(self.parent)
|
||||||
|
|
||||||
|
if self.parent.scaninfo.scan_type == 'step':
|
||||||
|
timeout = DETECTOR_TIMEOUT
|
||||||
|
else:
|
||||||
|
timeout = self.parent.scaninfo.exp_time * self.parent.scaninfo.num_points + DETECTOR_TIMEOUT
|
||||||
|
logger.success('XXXX %s XXXX' % self.parent.roistat.ts_acquiring.get())
|
||||||
|
success = self.wait_for_signals(
|
||||||
|
[
|
||||||
|
(self.parent.cam.acquire.get, 0),
|
||||||
|
(self.parent.hdf.capture.get, 0),
|
||||||
|
(self.parent.roistat.ts_acquiring.get, 'Done')
|
||||||
|
],
|
||||||
|
timeout,
|
||||||
|
check_stopped=True,
|
||||||
|
all_signals=True
|
||||||
|
)
|
||||||
|
|
||||||
|
# publish file location
|
||||||
|
self.parent.filepath.put(self.parent.hdf.full_file_name.get())
|
||||||
|
self.publish_file_location(done=True, successful=success)
|
||||||
|
|
||||||
|
# publish timeseries data
|
||||||
|
metadata = self.parent.scaninfo.scan_msg.metadata
|
||||||
|
metadata.update({"async_update": "append", "num_lines": self.parent.roistat.ts_current_point.get()})
|
||||||
|
msg = messages.DeviceMessage(
|
||||||
|
signals={
|
||||||
|
self.parent.roistat.roi1.name_.get(): {
|
||||||
|
'value': self.parent.roistat.roi1.ts_total.get(),
|
||||||
|
},
|
||||||
|
self.parent.roistat.roi2.name_.get(): {
|
||||||
|
'value': self.parent.roistat.roi2.ts_total.get(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
metadata=self.parent.scaninfo.scan_msg.metadata
|
||||||
|
)
|
||||||
|
self.parent.connector.xadd(
|
||||||
|
topic=MessageEndpoints.device_async_readback(
|
||||||
|
scan_id=self.parent.scaninfo.scan_id, device=self.parent.name
|
||||||
|
),
|
||||||
|
msg_dict={"data": msg},
|
||||||
|
expire=1800,
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.success('XXXX complete %d XXXX' % success)
|
||||||
|
if success:
|
||||||
|
status.set_finished()
|
||||||
|
else:
|
||||||
|
status.set_exception(TimeoutError())
|
||||||
|
return status
|
||||||
|
|
||||||
|
def on_stop(self):
|
||||||
|
logger.success('XXXX stop XXXX')
|
||||||
|
self.parent.cam.acquire.put(0)
|
||||||
|
self.parent.hdf.capture.put(0)
|
||||||
|
self.parent.roistat.ts_control.put(2)
|
||||||
|
|
||||||
|
def on_unstage(self):
|
||||||
|
self.parent.cam.acquire.put(0)
|
||||||
|
self.parent.hdf.capture.put(0)
|
||||||
|
self.parent.roistat.ts_control.put(2)
|
||||||
|
logger.success('XXXX unstage XXXX')
|
||||||
|
|
||||||
|
|
||||||
|
class EigerROIStatPlugin(ROIStatPlugin):
|
||||||
|
roi1 = ADCpt(ROIStatNPlugin, '1:')
|
||||||
|
roi2 = ADCpt(ROIStatNPlugin, '2:')
|
||||||
|
|
||||||
|
class PhoenixTrigger(PSIDetectorBase):
|
||||||
|
"""
|
||||||
|
"""
|
||||||
|
custom_prepare_cls = PhoenixTriggerSetup
|
||||||
|
|
||||||
|
cam = ADCpt(SLSDetectorCam, 'cam1:')
|
||||||
|
|
||||||
|
|
||||||
|
X07MB-OP2:START-CSMPL.. cont on / off
|
||||||
|
|
||||||
|
X07MB-OP2:SMPL.. take single sample
|
||||||
|
X07MB-OP2:INTR-COUNT.. counter run up
|
||||||
|
X07MB-OP2:TOTAL-CYCLES .. cycles set
|
||||||
|
X07MB-OP2:SMPL-DONE
|
||||||
|
|
||||||
|
|
||||||
|
#image = ADCpt(ImagePlugin, 'image1:')
|
||||||
|
#roi1 = ADCpt(ROIPlugin, 'ROI1:')
|
||||||
|
#roi2 = ADCpt(ROIPlugin, 'ROI2:')
|
||||||
|
#stats1 = ADCpt(StatsPlugin, 'Stats1:')
|
||||||
|
#stats2 = ADCpt(StatsPlugin, 'Stats2:')
|
||||||
|
roistat = ADCpt(EigerROIStatPlugin, 'ROIStat1:')
|
||||||
|
#roistat1 = ADCpt(ROIStatNPlugin, 'ROIStat1:1:')
|
||||||
|
#roistat2 = ADCpt(ROIStatNPlugin, 'ROIStat1:2:')
|
||||||
|
hdf = ADCpt(HDF5Plugin, 'HDF1:')
|
||||||
|
)
|
@ -1,6 +1,6 @@
|
|||||||
#
|
#
|
||||||
#
|
#
|
||||||
# changes version for PHOENIX WITHOUT HDF5 plugin to be revised/renamed...
|
# changes version for PHOENIX WITHOUT HDF5 plugin to be revised/renamed...
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
@ -18,6 +18,9 @@ from ophyd_devices.interfaces.base_classes.psi_detector_base import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
logger = bec_logger.logger
|
logger = bec_logger.logger
|
||||||
|
#bec_logger.level = bec_logger.LOGLEVEL.TRACE
|
||||||
|
bec_logger.level = bec_logger.LOGLEVEL.INFO
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class XMAPError(Exception):
|
class XMAPError(Exception):
|
||||||
@ -56,12 +59,13 @@ class EpicsDXPXMAP(Device):
|
|||||||
|
|
||||||
Base class to map EPICS PVs from DXP parameters to ophyd signals.
|
Base class to map EPICS PVs from DXP parameters to ophyd signals.
|
||||||
"""
|
"""
|
||||||
|
#roi2 = ADCpt(ROIPlugin, 'ROI2:')
|
||||||
|
|
||||||
elapsed_live_time = Cpt(EpicsSignal, "ElapsedLiveTime")
|
elapsed_live_time = Cpt(EpicsSignal, "ElapsedLiveTime")
|
||||||
elapsed_real_time = Cpt(EpicsSignal, "ElapsedRealTime")
|
elapsed_real_time = Cpt(EpicsSignal, "ElapsedRealTime")
|
||||||
elapsed_trigger_live_time = Cpt(EpicsSignal, "ElapsedTriggerLiveTime")
|
elapsed_trigger_live_time = Cpt(EpicsSignal, "ElapsedTriggerLiveTime")
|
||||||
|
|
||||||
# Energy Filter PVs .... uncomment falcon stuff
|
# Energy Filter PVs .... uncomment falcon stuff
|
||||||
#energy_threshold = Cpt(EpicsSignalWithRBV, "DetectionThreshold")
|
#energy_threshold = Cpt(EpicsSignalWithRBV, "DetectionThreshold")
|
||||||
#min_pulse_separation = Cpt(EpicsSignalWithRBV, "MinPulsePairSeparation")
|
#min_pulse_separation = Cpt(EpicsSignalWithRBV, "MinPulsePairSeparation")
|
||||||
#detection_filter = Cpt(EpicsSignalWithRBV, "DetectionFilter", string=True)
|
#detection_filter = Cpt(EpicsSignalWithRBV, "DetectionFilter", string=True)
|
||||||
@ -82,7 +86,7 @@ class XMAPHDF5Plugins(Device):
|
|||||||
Base class to map EPICS PVs from HDF5 Plugin to ophyd signals.
|
Base class to map EPICS PVs from HDF5 Plugin to ophyd signals.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
""" ----------------------------------------------------------------------------
|
""" ----------------------------------------------------------------------------
|
||||||
capture = Cpt(EpicsSignalWithRBV, "Capture")
|
capture = Cpt(EpicsSignalWithRBV, "Capture")
|
||||||
enable = Cpt(EpicsSignalWithRBV, "EnableCallbacks", string=True, kind="config")
|
enable = Cpt(EpicsSignalWithRBV, "EnableCallbacks", string=True, kind="config")
|
||||||
xml_file_name = Cpt(EpicsSignalWithRBV, "XMLFileName", string=True, kind="config")
|
xml_file_name = Cpt(EpicsSignalWithRBV, "XMLFileName", string=True, kind="config")
|
||||||
@ -186,7 +190,7 @@ class XMAPSetup(CustomDetectorMixin):
|
|||||||
|
|
||||||
def prepare_data_backend(self) -> None:
|
def prepare_data_backend(self) -> None:
|
||||||
"""Prepare data backend for acquisition"""
|
"""Prepare data backend for acquisition"""
|
||||||
w=9
|
w=9
|
||||||
""" --------------------------------------------------------------
|
""" --------------------------------------------------------------
|
||||||
self.parent.filepath.set(
|
self.parent.filepath.set(
|
||||||
self.parent.filewriter.compile_full_filename(f"{self.parent.name}.h5")
|
self.parent.filewriter.compile_full_filename(f"{self.parent.name}.h5")
|
||||||
@ -247,10 +251,7 @@ class XMAPSetup(CustomDetectorMixin):
|
|||||||
#-------------------------------------------------------------------
|
#-------------------------------------------------------------------
|
||||||
#signal_conditions = [
|
#signal_conditions = [
|
||||||
# (lambda: self.parent.state.read()[self.parent.state.name]["value"], DetectorState.DONE)
|
# (lambda: self.parent.state.read()[self.parent.state.name]["value"], DetectorState.DONE)
|
||||||
#]
|
#]stage2 = StageXY(prefix='X07MB',name='-ES-MA1', name='stage2')
|
||||||
|
|
||||||
#if not self.wait_for_signals(
|
|
||||||
# signal_conditions=signal_conditions,
|
|
||||||
# timeout=self.parent.TIMEOUT_FOR_SIGNALS - self.parent.TIMEOUT_FOR_SIGNALS // 2,
|
# timeout=self.parent.TIMEOUT_FOR_SIGNALS - self.parent.TIMEOUT_FOR_SIGNALS // 2,
|
||||||
# all_signals=False,
|
# all_signals=False,
|
||||||
#):
|
#):
|
||||||
@ -312,7 +313,7 @@ class XMAPphoenix(PSIDetectorBase):
|
|||||||
XMAP detector for phoenix
|
XMAP detector for phoenix
|
||||||
|
|
||||||
Parent class: PSIDetectorBase
|
Parent class: PSIDetectorBase
|
||||||
|
|
||||||
class attributes:
|
class attributes:
|
||||||
custom_prepare_cls (XMAPSetup) : Custom detector setup class for cSAXS,
|
custom_prepare_cls (XMAPSetup) : Custom detector setup class for cSAXS,
|
||||||
inherits from CustomDetectorMixin
|
inherits from CustomDetectorMixin
|
||||||
@ -339,8 +340,8 @@ class XMAPphoenix(PSIDetectorBase):
|
|||||||
mca2 = Cpt(EpicsMCARecord, "mca2")
|
mca2 = Cpt(EpicsMCARecord, "mca2")
|
||||||
mca3 = Cpt(EpicsMCARecord, "mca3")
|
mca3 = Cpt(EpicsMCARecord, "mca3")
|
||||||
mca4 = Cpt(EpicsMCARecord, "mca4")
|
mca4 = Cpt(EpicsMCARecord, "mca4")
|
||||||
|
print('load hdf5')
|
||||||
hdf5 = Cpt(XMAPHDF5Plugins, "HDF1:")
|
#hdf5 = Cpt(XMAPHDF5Plugins, "HDF1:")
|
||||||
|
|
||||||
stop_all = Cpt(EpicsSignal, "StopAll")
|
stop_all = Cpt(EpicsSignal, "StopAll")
|
||||||
erase_all = Cpt(EpicsSignal, "EraseAll")
|
erase_all = Cpt(EpicsSignal, "EraseAll")
|
||||||
@ -350,8 +351,8 @@ class XMAPphoenix(PSIDetectorBase):
|
|||||||
preset_real = Cpt(EpicsSignal, "PresetReal")
|
preset_real = Cpt(EpicsSignal, "PresetReal")
|
||||||
preset_events = Cpt(EpicsSignal, "PresetEvents")
|
preset_events = Cpt(EpicsSignal, "PresetEvents")
|
||||||
preset_triggers = Cpt(EpicsSignal, "PresetTriggers")
|
preset_triggers = Cpt(EpicsSignal, "PresetTriggers")
|
||||||
#triggers = Cpt(EpicsSignalRO, "MaxTriggers", lazy=True) #=========== falcon only
|
#triggers = Cpt(EpicsSignalRO, "MaxTriggers", lazy=True) #=========== falcon only
|
||||||
# events = Cpt(EpicsSignalRO, "MaxEvents", lazy=True) #=========== falcon only
|
# events = Cpt(EpicsSignalRO, "MaxEvents", lazy=True) #=========== falcon only
|
||||||
#input_count_rate = Cpt(EpicsSignalRO, "MaxInputCountRate", lazy=True) #=========== falcon only
|
#input_count_rate = Cpt(EpicsSignalRO, "MaxInputCountRate", lazy=True) #=========== falcon only
|
||||||
#output_count_rate = Cpt(EpicsSignalRO, "MaxOutputCountRate", lazy=True) #=========== falcon only
|
#output_count_rate = Cpt(EpicsSignalRO, "MaxOutputCountRate", lazy=True) #=========== falcon only
|
||||||
collect_mode = Cpt(EpicsSignal, "CollectMode") # 0 MCA spectra, 1 MCA mapping
|
collect_mode = Cpt(EpicsSignal, "CollectMode") # 0 MCA spectra, 1 MCA mapping
|
||||||
@ -362,7 +363,7 @@ class XMAPphoenix(PSIDetectorBase):
|
|||||||
pixels_per_buffer = Cpt(EpicsSignal, "PixelsPerBuffer")
|
pixels_per_buffer = Cpt(EpicsSignal, "PixelsPerBuffer")
|
||||||
pixels_per_run = Cpt(EpicsSignal, "PixelsPerRun")
|
pixels_per_run = Cpt(EpicsSignal, "PixelsPerRun")
|
||||||
#nd_array_mode = Cpt(EpicsSignal, "NDArrayMode")
|
#nd_array_mode = Cpt(EpicsSignal, "NDArrayMode")
|
||||||
|
print('DONE connecton chanels in XMAPphoenix')
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
xmap = XMAPphoenix(name="xmap", prefix="X07MB-XMAP:", sim_mode=True)
|
xmap = XMAPphoenix(name="xmap", prefix="X07MB-XMAP:", sim_mode=True)
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
This file contains a selection of base classes to remember where to find them and how they look
|
This file contains a selection of base classes to remember where to find them and how they look
|
||||||
|
|
||||||
|
|
||||||
#########################################################################################
|
#########################################################################################
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# PART I device classes psi_detector_base.py
|
# PART I device classes psi_detector_base.py
|
||||||
# CONTAINS
|
# CONTAINS
|
||||||
# DetectorInitErroR(Exception)
|
# DetectorInitErroR(Exception)
|
||||||
# CustomDetectorMixing
|
# CustomDetectorMixing
|
||||||
# PSIDetectorBase(Device)
|
# PSIDetectorBase(Device)
|
||||||
@ -13,7 +13,7 @@ This file contains a selection of base classes to remember where to find them an
|
|||||||
#
|
#
|
||||||
#########################################################################################
|
#########################################################################################
|
||||||
|
|
||||||
psi_detector_base.py
|
psi_detector_base.py
|
||||||
https://gitlab.psi.ch/bec/ophyd_devices/-/blob/main/ophyd_devices/interfaces/base_classes/psi_detector_base.py
|
https://gitlab.psi.ch/bec/ophyd_devices/-/blob/main/ophyd_devices/interfaces/base_classes/psi_detector_base.py
|
||||||
""This module contains the base class for SLS detectors. We follow the approach to integrate
|
""This module contains the base class for SLS detectors. We follow the approach to integrate
|
||||||
PSI detectors into the BEC system based on this base class. The base class is used to implement
|
PSI detectors into the BEC system based on this base class. The base class is used to implement
|
||||||
@ -21,7 +21,9 @@ certain methods that are expected by BEC, such as stage, unstage, trigger, stop,
|
|||||||
We use composition with a custom prepare class to implement BL specific logic for the detector.
|
We use composition with a custom prepare class to implement BL specific logic for the detector.
|
||||||
The beamlines need to inherit from the CustomDetectorMixing for their mixin classes."""
|
The beamlines need to inherit from the CustomDetectorMixing for their mixin classes."""
|
||||||
|
|
||||||
file psi_detector_base.py
|
file psi_detector_base.py
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
@ -51,7 +53,7 @@ class CustomDetectorMixin:
|
|||||||
"""
|
"""
|
||||||
Init sequence for the detector
|
Init sequence for the detector
|
||||||
"""
|
"""
|
||||||
_base.py
|
_base.py
|
||||||
ready has all current parameters for the upcoming scan.
|
ready has all current parameters for the upcoming scan.
|
||||||
|
|
||||||
In case the backend service is writing data on disk, this step should include publishing
|
In case the backend service is writing data on disk, this step should include publishing
|
||||||
@ -286,6 +288,7 @@ class PSIDetectorBase(Device):
|
|||||||
self.service_cfg = None
|
self.service_cfg = None
|
||||||
self.scaninfo = None
|
self.scaninfo = None
|
||||||
self.filewriter = None
|
self.filewriter = None
|
||||||
|
self._update_filewriter()
|
||||||
|
|
||||||
if not issubclass(self.custom_prepare_cls, CustomDetectorMixin):
|
if not issubclass(self.custom_prepare_cls, CustomDetectorMixin):
|
||||||
raise DetectorInitError("Custom prepare class must be subclass of CustomDetectorMixin")
|
raise DetectorInitError("Custom prepare class must be subclass of CustomDetectorMixin")
|
@ -0,0 +1,159 @@
|
|||||||
|
from ophyd import (
|
||||||
|
ADComponent as ADCpt,
|
||||||
|
Device,
|
||||||
|
DeviceStatus,
|
||||||
|
)
|
||||||
|
|
||||||
|
from ophyd_devices.devices.areadetector.cam import SLSDetectorCam
|
||||||
|
from ophyd_devices.devices.areadetector.plugins import (
|
||||||
|
ImagePlugin_V35 as ImagePlugin,
|
||||||
|
StatsPlugin_V35 as StatsPlugin,
|
||||||
|
HDF5Plugin_V35 as HDF5Plugin,
|
||||||
|
ROIPlugin_V35 as ROIPlugin,
|
||||||
|
ROIStatPlugin_V35 as ROIStatPlugin,
|
||||||
|
ROIStatNPlugin_V35 as ROIStatNPlugin,
|
||||||
|
)
|
||||||
|
from ophyd_devices.interfaces.base_classes.psi_detector_base import PSIDetectorBase, CustomDetectorMixin
|
||||||
|
|
||||||
|
from bec_lib import bec_logger, messages
|
||||||
|
from bec_lib.endpoints import MessageEndpoints
|
||||||
|
|
||||||
|
logger = bec_logger.logger
|
||||||
|
|
||||||
|
DETECTOR_TIMEOUT = 5
|
||||||
|
|
||||||
|
class Eiger500KSetup(CustomDetectorMixin):
|
||||||
|
def __init__(self, *args, parent:Device = None, **kwargs):
|
||||||
|
super().__init__(*args, parent=parent, **kwargs)
|
||||||
|
self._counter = 0
|
||||||
|
|
||||||
|
def on_stage(self):
|
||||||
|
exposure_time = self.parent.scaninfo.exp_time
|
||||||
|
num_points = self.parent.scaninfo.num_points
|
||||||
|
|
||||||
|
# camera acquisition parameters
|
||||||
|
self.parent.cam.array_counter.put(0)
|
||||||
|
if self.parent.scaninfo.scan_type == 'step':
|
||||||
|
self.parent.cam.acquire_time.put(exposure_time)
|
||||||
|
self.parent.cam.num_images.put(1)
|
||||||
|
self.parent.cam.image_mode.put(0) # Single
|
||||||
|
self.parent.cam.trigger_mode.put(0) # auto
|
||||||
|
else:
|
||||||
|
# In flyscan, the exp_time is the time between two triggers,
|
||||||
|
# which minus 15% is used as the acquisition time.
|
||||||
|
self.parent.cam.acquire_time.put(exposure_time * 0.85)
|
||||||
|
self.parent.cam.num_images.put(num_points)
|
||||||
|
self.parent.cam.image_mode.put(1) # Multiple
|
||||||
|
self.parent.cam.trigger_mode.put(1) # trigger
|
||||||
|
self.parent.cam.acquire.put(1, wait=False) # arm
|
||||||
|
|
||||||
|
# file writer
|
||||||
|
self.parent.hdf.lazy_open.put(1)
|
||||||
|
self.parent.hdf.num_capture.put(num_points)
|
||||||
|
self.parent.hdf.file_write_mode.put(2) # Stream
|
||||||
|
self.parent.hdf.capture.put(1, wait=False)
|
||||||
|
self.parent.hdf.enable.put(1) # enable plugin
|
||||||
|
|
||||||
|
# roi statistics to collect signal and background in a timeseries
|
||||||
|
self.parent.roistat.enable.put(1)
|
||||||
|
self.parent.roistat.ts_num_points.put(num_points)
|
||||||
|
self.parent.roistat.ts_control.put(0, wait=False) # Erase/Start
|
||||||
|
|
||||||
|
logger.success('XXXX stage XXXX')
|
||||||
|
|
||||||
|
def on_trigger(self):
|
||||||
|
self.parent.cam.acquire.put(1, wait=False)
|
||||||
|
logger.success('XXXX trigger XXXX')
|
||||||
|
|
||||||
|
return self.wait_with_status(
|
||||||
|
[(self.parent.cam.acquire.get, 0)],
|
||||||
|
self.parent.scaninfo.exp_time + DETECTOR_TIMEOUT,
|
||||||
|
all_signals=True
|
||||||
|
)
|
||||||
|
|
||||||
|
def on_complete(self):
|
||||||
|
status = DeviceStatus(self.parent)
|
||||||
|
|
||||||
|
if self.parent.scaninfo.scan_type == 'step':
|
||||||
|
timeout = DETECTOR_TIMEOUT
|
||||||
|
else:
|
||||||
|
timeout = self.parent.scaninfo.exp_time * self.parent.scaninfo.num_points + DETECTOR_TIMEOUT
|
||||||
|
logger.success('XXXX %s XXXX' % self.parent.roistat.ts_acquiring.get())
|
||||||
|
success = self.wait_for_signals(
|
||||||
|
[
|
||||||
|
(self.parent.cam.acquire.get, 0),
|
||||||
|
(self.parent.hdf.capture.get, 0),
|
||||||
|
(self.parent.roistat.ts_acquiring.get, 'Done')
|
||||||
|
],
|
||||||
|
timeout,
|
||||||
|
check_stopped=True,
|
||||||
|
all_signals=True
|
||||||
|
)
|
||||||
|
|
||||||
|
# publish file location
|
||||||
|
self.parent.filepath.put(self.parent.hdf.full_file_name.get())
|
||||||
|
self.publish_file_location(done=True, successful=success)
|
||||||
|
|
||||||
|
# publish timeseries data
|
||||||
|
metadata = self.parent.scaninfo.scan_msg.metadata
|
||||||
|
metadata.update({"async_update": "append", "num_lines": self.parent.roistat.ts_current_point.get()})
|
||||||
|
msg = messages.DeviceMessage(
|
||||||
|
signals={
|
||||||
|
self.parent.roistat.roi1.name_.get(): {
|
||||||
|
'value': self.parent.roistat.roi1.ts_total.get(),
|
||||||
|
},
|
||||||
|
self.parent.roistat.roi2.name_.get(): {
|
||||||
|
'value': self.parent.roistat.roi2.ts_total.get(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
metadata=self.parent.scaninfo.scan_msg.metadata
|
||||||
|
)
|
||||||
|
self.parent.connector.xadd(
|
||||||
|
topic=MessageEndpoints.device_async_readback(
|
||||||
|
scan_id=self.parent.scaninfo.scan_id, device=self.parent.name
|
||||||
|
),
|
||||||
|
msg_dict={"data": msg},
|
||||||
|
expire=1800,
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.success('XXXX complete %d XXXX' % success)
|
||||||
|
if success:
|
||||||
|
status.set_finished()
|
||||||
|
else:
|
||||||
|
status.set_exception(TimeoutError())
|
||||||
|
return status
|
||||||
|
|
||||||
|
def on_stop(self):
|
||||||
|
logger.success('XXXX stop XXXX')
|
||||||
|
self.parent.cam.acquire.put(0)
|
||||||
|
self.parent.hdf.capture.put(0)
|
||||||
|
self.parent.roistat.ts_control.put(2)
|
||||||
|
|
||||||
|
def on_unstage(self):
|
||||||
|
self.parent.cam.acquire.put(0)
|
||||||
|
self.parent.hdf.capture.put(0)
|
||||||
|
self.parent.roistat.ts_control.put(2)
|
||||||
|
logger.success('XXXX unstage XXXX')
|
||||||
|
|
||||||
|
|
||||||
|
class EigerROIStatPlugin(ROIStatPlugin):
|
||||||
|
roi1 = ADCpt(ROIStatNPlugin, '1:')
|
||||||
|
roi2 = ADCpt(ROIStatNPlugin, '2:')
|
||||||
|
|
||||||
|
class Eiger500K(PSIDetectorBase):
|
||||||
|
"""
|
||||||
|
"""
|
||||||
|
custom_prepare_cls = Eiger500KSetup
|
||||||
|
|
||||||
|
cam = ADCpt(SLSDetectorCam, 'cam1:')
|
||||||
|
|
||||||
|
#image = ADCpt(ImagePlugin, 'image1:')
|
||||||
|
#roi1 = ADCpt(ROIPlugin, 'ROI1:')
|
||||||
|
#roi2 = ADCpt(ROIPlugin, 'ROI2:')
|
||||||
|
#stats1 = ADCpt(StatsPlugin, 'Stats1:')
|
||||||
|
#stats2 = ADCpt(StatsPlugin, 'Stats2:')
|
||||||
|
roistat = ADCpt(EigerROIStatPlugin, 'ROIStat1:')
|
||||||
|
#roistat1 = ADCpt(ROIStatNPlugin, 'ROIStat1:1:')
|
||||||
|
#roistat2 = ADCpt(ROIStatNPlugin, 'ROIStat1:2:')
|
||||||
|
hdf = ADCpt(HDF5Plugin, 'HDF1:')
|
||||||
|
)
|
@ -0,0 +1,219 @@
|
|||||||
|
"""
|
||||||
|
This was takem from Addam repository
|
||||||
|
and commented after discussion with Xiaquiang
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
#Strutur of software
|
||||||
|
#######################################
|
||||||
|
|
||||||
|
1) Eiger500KSetup(CustomDetectorMixin)---> inherits from CustomdetectorMixing
|
||||||
|
|
||||||
|
2) class Eiger500K(PSIDetectorBase) ---> inherits from PSIDetectorBase
|
||||||
|
|
||||||
|
|
||||||
|
These calsses are linkes to each other by the command
|
||||||
|
|
||||||
|
custom_prepare_cls = Eiger500KSetup
|
||||||
|
|
||||||
|
it references/activates the self.parent used in Eiger500Ksetup to attributes defined in Eiger500K.
|
||||||
|
for example in Eiger500K, we define cam = ADCpt(SLSDetectorCam, 'cam1:'), which is referenced in \
|
||||||
|
Eiger500Ksetup by the command
|
||||||
|
self.parent.cam.array_counter.put(0)
|
||||||
|
|
||||||
|
|
||||||
|
class Eiger500KSetup(CustomDetectorMixin):
|
||||||
|
def __init__(self, *args, parent:Device = None, **kwargs):
|
||||||
|
super().__init__(*args, parent=parent, **kwargs)
|
||||||
|
self._counter = 0
|
||||||
|
|
||||||
|
def on_stage(self):
|
||||||
|
exposure_time = self.parent.scaninfo.exp_time
|
||||||
|
num_points = self.parent.scaninfo.num_points
|
||||||
|
|
||||||
|
# camera acquisition parameters
|
||||||
|
self.parent.cam.array_counter.put(0)
|
||||||
|
if self.parent.scaninfo.scan_type == 'step':
|
||||||
|
self.parent.cam.acquire_time.put(exposure_time)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
... etc
|
||||||
|
|
||||||
|
|
||||||
|
class Eiger500K(PSIDetectorBase):
|
||||||
|
"""
|
||||||
|
"""
|
||||||
|
custom_prepare_cls = Eiger500KSetup
|
||||||
|
cam = ADCpt(SLSDetectorCam, 'cam1:')
|
||||||
|
|
||||||
|
.... etc
|
||||||
|
|
||||||
|
###################################################
|
||||||
|
#
|
||||||
|
# Using ROI in flyscans
|
||||||
|
#
|
||||||
|
###################################################
|
||||||
|
Here the roi plugin of the area detector is used
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from ophyd import (
|
||||||
|
ADComponent as ADCpt,
|
||||||
|
Device,
|
||||||
|
DeviceStatus,
|
||||||
|
)
|
||||||
|
|
||||||
|
from ophyd_devices.devices.areadetector.cam import SLSDetectorCam
|
||||||
|
from ophyd_devices.devices.areadetector.plugins import (
|
||||||
|
ImagePlugin_V35 as ImagePlugin,
|
||||||
|
StatsPlugin_V35 as StatsPlugin,
|
||||||
|
HDF5Plugin_V35 as HDF5Plugin,
|
||||||
|
ROIPlugin_V35 as ROIPlugin,
|
||||||
|
ROIStatPlugin_V35 as ROIStatPlugin,
|
||||||
|
ROIStatNPlugin_V35 as ROIStatNPlugin,
|
||||||
|
)
|
||||||
|
from ophyd_devices.interfaces.base_classes.psi_detector_base import PSIDetectorBase, CustomDetectorMixin
|
||||||
|
|
||||||
|
from bec_lib import bec_logger, messages
|
||||||
|
from bec_lib.endpoints import MessageEndpoints
|
||||||
|
|
||||||
|
logger = bec_logger.logger
|
||||||
|
|
||||||
|
DETECTOR_TIMEOUT = 5
|
||||||
|
|
||||||
|
class Eiger500KSetup(CustomDetectorMixin):
|
||||||
|
def __init__(self, *args, parent:Device = None, **kwargs):
|
||||||
|
super().__init__(*args, parent=parent, **kwargs)
|
||||||
|
self._counter = 0
|
||||||
|
|
||||||
|
def on_stage(self):
|
||||||
|
exposure_time = self.parent.scaninfo.exp_time
|
||||||
|
num_points = self.parent.scaninfo.num_points
|
||||||
|
|
||||||
|
# camera acquisition parameters
|
||||||
|
self.parent.cam.array_counter.put(0)
|
||||||
|
if self.parent.scaninfo.scan_type == 'step':
|
||||||
|
self.parent.cam.acquire_time.put(exposure_time)
|
||||||
|
self.parent.cam.num_images.put(1)
|
||||||
|
self.parent.cam.image_mode.put(0) # Single
|
||||||
|
self.parent.cam.trigger_mode.put(0) # auto
|
||||||
|
else:
|
||||||
|
# In flyscan, the exp_time is the time between two triggers,
|
||||||
|
# which minus 15% is used as the acquisition time.
|
||||||
|
self.parent.cam.acquire_time.put(exposure_time * 0.85)
|
||||||
|
self.parent.cam.num_images.put(num_points)
|
||||||
|
self.parent.cam.image_mode.put(1) # Multiple
|
||||||
|
self.parent.cam.trigger_mode.put(1) # trigger
|
||||||
|
self.parent.cam.acquire.put(1, wait=False) # arm
|
||||||
|
|
||||||
|
# file writer
|
||||||
|
self.parent.hdf.lazy_open.put(1)
|
||||||
|
self.parent.hdf.num_capture.put(num_points)
|
||||||
|
self.parent.hdf.file_write_mode.put(2) # Stream
|
||||||
|
self.parent.hdf.capture.put(1, wait=False)
|
||||||
|
self.parent.hdf.enable.put(1) # enable plugin
|
||||||
|
|
||||||
|
# roi statistics to collect signal and background in a timeseries
|
||||||
|
self.parent.roistat.enable.put(1)
|
||||||
|
self.parent.roistat.ts_num_points.put(num_points)
|
||||||
|
self.parent.roistat.ts_control.put(0, wait=False) # Erase/Start
|
||||||
|
|
||||||
|
logger.success('XXXX stage XXXX')
|
||||||
|
|
||||||
|
def on_trigger(self):
|
||||||
|
self.parent.cam.acquire.put(1, wait=False)
|
||||||
|
logger.success('XXXX trigger XXXX')
|
||||||
|
|
||||||
|
return self.wait_with_status(
|
||||||
|
[(self.parent.cam.acquire.get, 0)],
|
||||||
|
self.parent.scaninfo.exp_time + DETECTOR_TIMEOUT,
|
||||||
|
all_signals=True
|
||||||
|
)
|
||||||
|
|
||||||
|
def on_complete(self):
|
||||||
|
status = DeviceStatus(self.parent)
|
||||||
|
|
||||||
|
if self.parent.scaninfo.scan_type == 'step':
|
||||||
|
timeout = DETECTOR_TIMEOUT
|
||||||
|
else:
|
||||||
|
timeout = self.parent.scaninfo.exp_time * self.parent.scaninfo.num_points + DETECTOR_TIMEOUT
|
||||||
|
logger.success('XXXX %s XXXX' % self.parent.roistat.ts_acquiring.get())
|
||||||
|
success = self.wait_for_signals(
|
||||||
|
[
|
||||||
|
(self.parent.cam.acquire.get, 0),
|
||||||
|
(self.parent.hdf.capture.get, 0),
|
||||||
|
(self.parent.roistat.ts_acquiring.get, 'Done')
|
||||||
|
],
|
||||||
|
timeout,
|
||||||
|
check_stopped=True,
|
||||||
|
all_signals=True
|
||||||
|
)
|
||||||
|
|
||||||
|
# publish file location
|
||||||
|
self.parent.filepath.put(self.parent.hdf.full_file_name.get())
|
||||||
|
self.publish_file_location(done=True, successful=success)
|
||||||
|
|
||||||
|
# publish timeseries data
|
||||||
|
metadata = self.parent.scaninfo.scan_msg.metadata
|
||||||
|
metadata.update({"async_update": "append", "num_lines": self.parent.roistat.ts_current_point.get()})
|
||||||
|
msg = messages.DeviceMessage(
|
||||||
|
signals={
|
||||||
|
self.parent.roistat.roi1.name_.get(): {
|
||||||
|
'value': self.parent.roistat.roi1.ts_total.get(),
|
||||||
|
},
|
||||||
|
self.parent.roistat.roi2.name_.get(): {
|
||||||
|
'value': self.parent.roistat.roi2.ts_total.get(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
metadata=self.parent.scaninfo.scan_msg.metadata
|
||||||
|
)
|
||||||
|
self.parent.connector.xadd(
|
||||||
|
topic=MessageEndpoints.device_async_readback(
|
||||||
|
scan_id=self.parent.scaninfo.scan_id, device=self.parent.name
|
||||||
|
),
|
||||||
|
msg_dict={"data": msg},
|
||||||
|
expire=1800,
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.success('XXXX complete %d XXXX' % success)
|
||||||
|
if success:
|
||||||
|
status.set_finished()
|
||||||
|
else:
|
||||||
|
status.set_exception(TimeoutError())
|
||||||
|
return status
|
||||||
|
|
||||||
|
def on_stop(self):
|
||||||
|
logger.success('XXXX stop XXXX')
|
||||||
|
self.parent.cam.acquire.put(0)
|
||||||
|
self.parent.hdf.capture.put(0)
|
||||||
|
self.parent.roistat.ts_control.put(2)
|
||||||
|
|
||||||
|
def on_unstage(self):
|
||||||
|
self.parent.cam.acquire.put(0)
|
||||||
|
self.parent.hdf.capture.put(0)
|
||||||
|
self.parent.roistat.ts_control.put(2)
|
||||||
|
logger.success('XXXX unstage XXXX')
|
||||||
|
|
||||||
|
|
||||||
|
class EigerROIStatPlugin(ROIStatPlugin):
|
||||||
|
roi1 = ADCpt(ROIStatNPlugin, '1:')
|
||||||
|
roi2 = ADCpt(ROIStatNPlugin, '2:')
|
||||||
|
|
||||||
|
class Eiger500K(PSIDetectorBase):
|
||||||
|
"""
|
||||||
|
"""
|
||||||
|
custom_prepare_cls = Eiger500KSetup
|
||||||
|
|
||||||
|
cam = ADCpt(SLSDetectorCam, 'cam1:')
|
||||||
|
|
||||||
|
#image = ADCpt(ImagePlugin, 'image1:')
|
||||||
|
#roi1 = ADCpt(ROIPlugin, 'ROI1:')
|
||||||
|
#roi2 = ADCpt(ROIPlugin, 'ROI2:')
|
||||||
|
#stats1 = ADCpt(StatsPlugin, 'Stats1:')
|
||||||
|
#stats2 = ADCpt(StatsPlugin, 'Stats2:')
|
||||||
|
roistat = ADCpt(EigerROIStatPlugin, 'ROIStat1:')
|
||||||
|
#roistat1 = ADCpt(ROIStatNPlugin, 'ROIStat1:1:')
|
||||||
|
#roistat2 = ADCpt(ROIStatNPlugin, 'ROIStat1:2:')
|
||||||
|
hdf = ADCpt(HDF5Plugin, 'HDF1:')
|
@ -0,0 +1,226 @@
|
|||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
This was taken from Addam repository
|
||||||
|
and commented after discussion with Xiaquiang
|
||||||
|
As this also uses the are detector software, we might use is as well for teh flacon /xmap integration.
|
||||||
|
This is based on falcon integration at cSAXS. Advantage over integration from BEC team is that this integration is very slim, and does not contain channels which are nor needed for data acquisition.
|
||||||
|
One could consider to split integration into two classes, as slim one for data acquisition, and a more complete one for 'operation and monitoring'
|
||||||
|
The channels in the 2nd class would then the saved only before a scan, which the 'data acquisition class' would be read at each data point.
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# Strutur of software
|
||||||
|
#######################################
|
||||||
|
|
||||||
|
1) Eiger500KSetup(CustomDetectorMixin)---> inherits from CustomdetectorMixing
|
||||||
|
|
||||||
|
2) class Eiger500K(PSIDetectorBase) ---> inherits from PSIDetectorBase
|
||||||
|
|
||||||
|
|
||||||
|
These calsses are linkes to each other by the command
|
||||||
|
|
||||||
|
custom_prepare_cls = Eiger500KSetup
|
||||||
|
|
||||||
|
it references/activates the self.parent used in Eiger500Ksetup to attributes defined in Eiger500K.
|
||||||
|
for example in Eiger500K, we define cam = ADCpt(SLSDetectorCam, 'cam1:'), which is referenced in \
|
||||||
|
Eiger500Ksetup by the command
|
||||||
|
self.parent.cam.array_counter.put(0)
|
||||||
|
|
||||||
|
|
||||||
|
class Eiger500KSetup(CustomDetectorMixin):
|
||||||
|
def __init__(self, *args, parent:Device = None, **kwargs):
|
||||||
|
super().__init__(*args, parent=parent, **kwargs)
|
||||||
|
self._counter = 0
|
||||||
|
|
||||||
|
def on_stage(self):
|
||||||
|
exposure_time = self.parent.scaninfo.exp_time
|
||||||
|
num_points = self.parent.scaninfo.num_points
|
||||||
|
|
||||||
|
# camera acquisition parameters
|
||||||
|
self.parent.cam.array_counter.put(0)
|
||||||
|
if self.parent.scaninfo.scan_type == 'step':
|
||||||
|
self.parent.cam.acquire_time.put(exposure_time)
|
||||||
|
|
||||||
|
... etc
|
||||||
|
|
||||||
|
|
||||||
|
class Eiger500K(PSIDetectorBase):
|
||||||
|
"""
|
||||||
|
"""
|
||||||
|
custom_prepare_cls = Eiger500KSetup
|
||||||
|
cam = ADCpt(SLSDetectorCam, 'cam1:')
|
||||||
|
|
||||||
|
.... etc
|
||||||
|
|
||||||
|
###################################################
|
||||||
|
#
|
||||||
|
# flyscans
|
||||||
|
#
|
||||||
|
###################################################
|
||||||
|
|
||||||
|
Images seem to be saves via hdf5 plugin (no live view is possible()
|
||||||
|
|
||||||
|
ROI is used to store 0d Signal.
|
||||||
|
These data are stored continuously collected an array in the ROI plugin
|
||||||
|
this could be used to store ROI data of XMAP/FALCON
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from ophyd import (
|
||||||
|
ADComponent as ADCpt,
|
||||||
|
Device,
|
||||||
|
DeviceStatus,
|
||||||
|
)
|
||||||
|
|
||||||
|
from ophyd_devices.devices.areadetector.cam import SLSDetectorCam
|
||||||
|
from ophyd_devices.devices.areadetector.plugins import (
|
||||||
|
ImagePlugin_V35 as ImagePlugin,
|
||||||
|
StatsPlugin_V35 as StatsPlugin,
|
||||||
|
HDF5Plugin_V35 as HDF5Plugin,
|
||||||
|
ROIPlugin_V35 as ROIPlugin,
|
||||||
|
ROIStatPlugin_V35 as ROIStatPlugin,
|
||||||
|
ROIStatNPlugin_V35 as ROIStatNPlugin,
|
||||||
|
)
|
||||||
|
from ophyd_devices.interfaces.base_classes.psi_detector_base import PSIDetectorBase, CustomDetectorMixin
|
||||||
|
|
||||||
|
from bec_lib import bec_logger, messages
|
||||||
|
from bec_lib.endpoints import MessageEndpoints
|
||||||
|
|
||||||
|
logger = bec_logger.logger
|
||||||
|
|
||||||
|
DETECTOR_TIMEOUT = 5
|
||||||
|
|
||||||
|
class Eiger500KSetup(CustomDetectorMixin):
|
||||||
|
def __init__(self, *args, parent:Device = None, **kwargs):
|
||||||
|
super().__init__(*args, parent=parent, **kwargs)
|
||||||
|
self._counter = 0
|
||||||
|
|
||||||
|
def on_stage(self):
|
||||||
|
exposure_time = self.parent.scaninfo.exp_time
|
||||||
|
num_points = self.parent.scaninfo.num_points
|
||||||
|
|
||||||
|
# camera acquisition parameters
|
||||||
|
self.parent.cam.array_counter.put(0)
|
||||||
|
if self.parent.scaninfo.scan_type == 'step':
|
||||||
|
self.parent.cam.acquire_time.put(exposure_time)
|
||||||
|
self.parent.cam.num_images.put(1)
|
||||||
|
self.parent.cam.image_mode.put(0) # Single
|
||||||
|
self.parent.cam.trigger_mode.put(0) # auto
|
||||||
|
else:
|
||||||
|
# In flyscan, the exp_time is the time between two triggers,
|
||||||
|
# which minus 15% is used as the acquisition time.
|
||||||
|
self.parent.cam.acquire_time.put(exposure_time * 0.85)
|
||||||
|
self.parent.cam.num_images.put(num_points)
|
||||||
|
self.parent.cam.image_mode.put(1) # Multiple
|
||||||
|
self.parent.cam.trigger_mode.put(1) # trigger
|
||||||
|
self.parent.cam.acquire.put(1, wait=False) # arm
|
||||||
|
|
||||||
|
# file writer
|
||||||
|
self.parent.hdf.lazy_open.put(1)
|
||||||
|
self.parent.hdf.num_capture.put(num_points)
|
||||||
|
self.parent.hdf.file_write_mode.put(2) # Stream
|
||||||
|
self.parent.hdf.capture.put(1, wait=False)
|
||||||
|
self.parent.hdf.enable.put(1) # enable plugin
|
||||||
|
|
||||||
|
# roi statistics to collect signal and background in a timeseries
|
||||||
|
self.parent.roistat.enable.put(1)
|
||||||
|
self.parent.roistat.ts_num_points.put(num_points)
|
||||||
|
self.parent.roistat.ts_control.put(0, wait=False) # Erase/Start
|
||||||
|
|
||||||
|
logger.success('XXXX stage XXXX')
|
||||||
|
|
||||||
|
def on_trigger(self):
|
||||||
|
self.parent.cam.acquire.put(1, wait=False)
|
||||||
|
logger.success('XXXX trigger XXXX')
|
||||||
|
|
||||||
|
return self.wait_with_status(
|
||||||
|
[(self.parent.cam.acquire.get, 0)],
|
||||||
|
self.parent.scaninfo.exp_time + DETECTOR_TIMEOUT,
|
||||||
|
all_signals=True
|
||||||
|
)
|
||||||
|
|
||||||
|
def on_complete(self):
|
||||||
|
status = DeviceStatus(self.parent)
|
||||||
|
|
||||||
|
if self.parent.scaninfo.scan_type == 'step':
|
||||||
|
timeout = DETECTOR_TIMEOUT
|
||||||
|
else:
|
||||||
|
timeout = self.parent.scaninfo.exp_time * self.parent.scaninfo.num_points + DETECTOR_TIMEOUT
|
||||||
|
logger.success('XXXX %s XXXX' % self.parent.roistat.ts_acquiring.get())
|
||||||
|
success = self.wait_for_signals(
|
||||||
|
[
|
||||||
|
(self.parent.cam.acquire.get, 0),
|
||||||
|
(self.parent.hdf.capture.get, 0),
|
||||||
|
(self.parent.roistat.ts_acquiring.get, 'Done')
|
||||||
|
],
|
||||||
|
timeout,
|
||||||
|
check_stopped=True,
|
||||||
|
all_signals=True
|
||||||
|
)
|
||||||
|
|
||||||
|
# publish file location
|
||||||
|
self.parent.filepath.put(self.parent.hdf.full_file_name.get())
|
||||||
|
self.publish_file_location(done=True, successful=success)
|
||||||
|
|
||||||
|
# publish timeseries data
|
||||||
|
metadata = self.parent.scaninfo.scan_msg.metadata
|
||||||
|
metadata.update({"async_update": "append", "num_lines": self.parent.roistat.ts_current_point.get()})
|
||||||
|
msg = messages.DeviceMessage(
|
||||||
|
signals={
|
||||||
|
self.parent.roistat.roi1.name_.get(): {
|
||||||
|
'value': self.parent.roistat.roi1.ts_total.get(),
|
||||||
|
},
|
||||||
|
self.parent.roistat.roi2.name_.get(): {
|
||||||
|
'value': self.parent.roistat.roi2.ts_total.get(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
metadata=self.parent.scaninfo.scan_msg.metadata
|
||||||
|
)
|
||||||
|
self.parent.connector.xadd(
|
||||||
|
topic=MessageEndpoints.device_async_readback(
|
||||||
|
scan_id=self.parent.scaninfo.scan_id, device=self.parent.name
|
||||||
|
),
|
||||||
|
msg_dict={"data": msg},
|
||||||
|
expire=1800,
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.success('XXXX complete %d XXXX' % success)
|
||||||
|
if success:
|
||||||
|
status.set_finished()
|
||||||
|
else:
|
||||||
|
status.set_exception(TimeoutError())
|
||||||
|
return status
|
||||||
|
|
||||||
|
def on_stop(self):
|
||||||
|
logger.success('XXXX stop XXXX')
|
||||||
|
self.parent.cam.acquire.put(0)
|
||||||
|
self.parent.hdf.capture.put(0)
|
||||||
|
self.parent.roistat.ts_control.put(2)
|
||||||
|
|
||||||
|
def on_unstage(self):
|
||||||
|
self.parent.cam.acquire.put(0)
|
||||||
|
self.parent.hdf.capture.put(0)
|
||||||
|
self.parent.roistat.ts_control.put(2)
|
||||||
|
logger.success('XXXX unstage XXXX')
|
||||||
|
|
||||||
|
|
||||||
|
class EigerROIStatPlugin(ROIStatPlugin):
|
||||||
|
roi1 = ADCpt(ROIStatNPlugin, '1:')
|
||||||
|
roi2 = ADCpt(ROIStatNPlugin, '2:')
|
||||||
|
|
||||||
|
class Eiger500K(PSIDetectorBase):
|
||||||
|
"""
|
||||||
|
"""
|
||||||
|
custom_prepare_cls = Eiger500KSetup
|
||||||
|
|
||||||
|
cam = ADCpt(SLSDetectorCam, 'cam1:')
|
||||||
|
|
||||||
|
#image = ADCpt(ImagePlugin, 'image1:')
|
||||||
|
#roi1 = ADCpt(ROIPlugin, 'ROI1:')
|
||||||
|
#roi2 = ADCpt(ROIPlugin, 'ROI2:')
|
||||||
|
#stats1 = ADCpt(StatsPlugin, 'Stats1:')
|
||||||
|
#stats2 = ADCpt(StatsPlugin, 'Stats2:')
|
||||||
|
roistat = ADCpt(EigerROIStatPlugin, 'ROIStat1:')
|
||||||
|
#roistat1 = ADCpt(ROIStatNPlugin, 'ROIStat1:1:')
|
||||||
|
#roistat2 = ADCpt(ROIStatNPlugin, 'ROIStat1:2:')
|
||||||
|
hdf = ADCpt(HDF5Plugin, 'HDF1:')
|
@ -0,0 +1,227 @@
|
|||||||
|
"""
|
||||||
|
This was takem from Addam repository
|
||||||
|
and commented after discussion with Xiaquiang
|
||||||
|
|
||||||
|
As this also uses the aere detector software, we might use is as well for teh flacon /xmap integration.
|
||||||
|
This is based on falcon integration at cSAXS. Advantage over integration from BEC team is that this integration is very slem, and
|
||||||
|
does not contain channels which are nor needed for data aquisition.
|
||||||
|
One could consider to split integration into two classses, as slim one for data aquisition, and a more complete one for 'operation and monitoring'
|
||||||
|
The channekls in the 2nd calss would then the saved only before a scan, which the 'data aquisition class' would be read at each data point.
|
||||||
|
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# Strutur of software
|
||||||
|
#######################################
|
||||||
|
|
||||||
|
1) Eiger500KSetup(CustomDetectorMixin)---> inherits from CustomdetectorMixing
|
||||||
|
|
||||||
|
2) class Eiger500K(PSIDetectorBase) ---> inherits from PSIDetectorBase
|
||||||
|
|
||||||
|
|
||||||
|
These calsses are linkes to each other by the command
|
||||||
|
|
||||||
|
custom_prepare_cls = Eiger500KSetup
|
||||||
|
|
||||||
|
it references/activates the self.parent used in Eiger500Ksetup to attributes defined in Eiger500K.
|
||||||
|
for example in Eiger500K, we define cam = ADCpt(SLSDetectorCam, 'cam1:'), which is referenced in \
|
||||||
|
Eiger500Ksetup by the command
|
||||||
|
self.parent.cam.array_counter.put(0)
|
||||||
|
|
||||||
|
|
||||||
|
class Eiger500KSetup(CustomDetectorMixin):
|
||||||
|
def __init__(self, *args, parent:Device = None, **kwargs):
|
||||||
|
super().__init__(*args, parent=parent, **kwargs)
|
||||||
|
self._counter = 0
|
||||||
|
|
||||||
|
def on_stage(self):
|
||||||
|
exposure_time = self.parent.scaninfo.exp_time
|
||||||
|
num_points = self.parent.scaninfo.num_points
|
||||||
|
|
||||||
|
# camera acquisition parameters
|
||||||
|
self.parent.cam.array_counter.put(0)
|
||||||
|
if self.parent.scaninfo.scan_type == 'step':
|
||||||
|
self.parent.cam.acquire_time.put(exposure_time)
|
||||||
|
|
||||||
|
... etc
|
||||||
|
|
||||||
|
|
||||||
|
class Eiger500K(PSIDetectorBase):
|
||||||
|
"""
|
||||||
|
"""
|
||||||
|
custom_prepare_cls = Eiger500KSetup
|
||||||
|
cam = ADCpt(SLSDetectorCam, 'cam1:')
|
||||||
|
|
||||||
|
.... etc
|
||||||
|
|
||||||
|
###################################################
|
||||||
|
#
|
||||||
|
# flyscans
|
||||||
|
#
|
||||||
|
###################################################
|
||||||
|
|
||||||
|
Images seem to be saves via hdf5 plugin (no live vuiew is possible()
|
||||||
|
ROI
|
||||||
|
Here the roi plugin of the area detector is used.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from ophyd import (
|
||||||
|
ADComponent as ADCpt,
|
||||||
|
Device,
|
||||||
|
DeviceStatus,
|
||||||
|
)
|
||||||
|
|
||||||
|
from ophyd_devices.devices.areadetector.cam import SLSDetectorCam
|
||||||
|
from ophyd_devices.devices.areadetector.plugins import (
|
||||||
|
ImagePlugin_V35 as ImagePlugin,
|
||||||
|
StatsPlugin_V35 as StatsPlugin,
|
||||||
|
HDF5Plugin_V35 as HDF5Plugin,
|
||||||
|
ROIPlugin_V35 as ROIPlugin,
|
||||||
|
ROIStatPlugin_V35 as ROIStatPlugin,
|
||||||
|
ROIStatNPlugin_V35 as ROIStatNPlugin,
|
||||||
|
)
|
||||||
|
from ophyd_devices.interfaces.base_classes.psi_detector_base import PSIDetectorBase, CustomDetectorMixin
|
||||||
|
|
||||||
|
from bec_lib import bec_logger, messages
|
||||||
|
from bec_lib.endpoints import MessageEndpoints
|
||||||
|
|
||||||
|
logger = bec_logger.logger
|
||||||
|
|
||||||
|
DETECTOR_TIMEOUT = 5
|
||||||
|
|
||||||
|
class Eiger500KSetup(CustomDetectorMixin):
|
||||||
|
def __init__(self, *args, parent:Device = None, **kwargs):
|
||||||
|
super().__init__(*args, parent=parent, **kwargs)
|
||||||
|
self._counter = 0
|
||||||
|
|
||||||
|
def on_stage(self):
|
||||||
|
exposure_time = self.parent.scaninfo.exp_time
|
||||||
|
num_points = self.parent.scaninfo.num_points
|
||||||
|
|
||||||
|
# camera acquisition parameters
|
||||||
|
self.parent.cam.array_counter.put(0)
|
||||||
|
if self.parent.scaninfo.scan_type == 'step':
|
||||||
|
self.parent.cam.acquire_time.put(exposure_time)
|
||||||
|
self.parent.cam.num_images.put(1)
|
||||||
|
self.parent.cam.image_mode.put(0) # Single
|
||||||
|
self.parent.cam.trigger_mode.put(0) # auto
|
||||||
|
else:
|
||||||
|
# In flyscan, the exp_time is the time between two triggers,
|
||||||
|
# which minus 15% is used as the acquisition time.
|
||||||
|
self.parent.cam.acquire_time.put(exposure_time * 0.85)
|
||||||
|
self.parent.cam.num_images.put(num_points)
|
||||||
|
self.parent.cam.image_mode.put(1) # Multiple
|
||||||
|
self.parent.cam.trigger_mode.put(1) # trigger
|
||||||
|
self.parent.cam.acquire.put(1, wait=False) # arm
|
||||||
|
|
||||||
|
# file writer
|
||||||
|
self.parent.hdf.lazy_open.put(1)
|
||||||
|
self.parent.hdf.num_capture.put(num_points)
|
||||||
|
self.parent.hdf.file_write_mode.put(2) # Stream
|
||||||
|
self.parent.hdf.capture.put(1, wait=False)
|
||||||
|
self.parent.hdf.enable.put(1) # enable plugin
|
||||||
|
|
||||||
|
# roi statistics to collect signal and background in a timeseries
|
||||||
|
self.parent.roistat.enable.put(1)
|
||||||
|
self.parent.roistat.ts_num_points.put(num_points)
|
||||||
|
self.parent.roistat.ts_control.put(0, wait=False) # Erase/Start
|
||||||
|
|
||||||
|
logger.success('XXXX stage XXXX')
|
||||||
|
|
||||||
|
def on_trigger(self):
|
||||||
|
self.parent.cam.acquire.put(1, wait=False)
|
||||||
|
logger.success('XXXX trigger XXXX')
|
||||||
|
|
||||||
|
return self.wait_with_status(
|
||||||
|
[(self.parent.cam.acquire.get, 0)],
|
||||||
|
self.parent.scaninfo.exp_time + DETECTOR_TIMEOUT,
|
||||||
|
all_signals=True
|
||||||
|
)
|
||||||
|
|
||||||
|
def on_complete(self):
|
||||||
|
status = DeviceStatus(self.parent)
|
||||||
|
|
||||||
|
if self.parent.scaninfo.scan_type == 'step':
|
||||||
|
timeout = DETECTOR_TIMEOUT
|
||||||
|
else:
|
||||||
|
timeout = self.parent.scaninfo.exp_time * self.parent.scaninfo.num_points + DETECTOR_TIMEOUT
|
||||||
|
logger.success('XXXX %s XXXX' % self.parent.roistat.ts_acquiring.get())
|
||||||
|
success = self.wait_for_signals(
|
||||||
|
[
|
||||||
|
(self.parent.cam.acquire.get, 0),
|
||||||
|
(self.parent.hdf.capture.get, 0),
|
||||||
|
(self.parent.roistat.ts_acquiring.get, 'Done')
|
||||||
|
],
|
||||||
|
timeout,
|
||||||
|
check_stopped=True,
|
||||||
|
all_signals=True
|
||||||
|
)
|
||||||
|
|
||||||
|
# publish file location
|
||||||
|
self.parent.filepath.put(self.parent.hdf.full_file_name.get())
|
||||||
|
self.publish_file_location(done=True, successful=success)
|
||||||
|
|
||||||
|
# publish timeseries data
|
||||||
|
metadata = self.parent.scaninfo.scan_msg.metadata
|
||||||
|
metadata.update({"async_update": "append", "num_lines": self.parent.roistat.ts_current_point.get()})
|
||||||
|
msg = messages.DeviceMessage(
|
||||||
|
signals={
|
||||||
|
self.parent.roistat.roi1.name_.get(): {
|
||||||
|
'value': self.parent.roistat.roi1.ts_total.get(),
|
||||||
|
},
|
||||||
|
self.parent.roistat.roi2.name_.get(): {
|
||||||
|
'value': self.parent.roistat.roi2.ts_total.get(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
metadata=self.parent.scaninfo.scan_msg.metadata
|
||||||
|
)
|
||||||
|
self.parent.connector.xadd(
|
||||||
|
topic=MessageEndpoints.device_async_readback(
|
||||||
|
scan_id=self.parent.scaninfo.scan_id, device=self.parent.name
|
||||||
|
),
|
||||||
|
msg_dict={"data": msg},
|
||||||
|
expire=1800,
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.success('XXXX complete %d XXXX' % success)
|
||||||
|
if success:
|
||||||
|
status.set_finished()
|
||||||
|
else:
|
||||||
|
status.set_exception(TimeoutError())
|
||||||
|
return status
|
||||||
|
|
||||||
|
def on_stop(self):
|
||||||
|
logger.success('XXXX stop XXXX')
|
||||||
|
self.parent.cam.acquire.put(0)
|
||||||
|
self.parent.hdf.capture.put(0)
|
||||||
|
self.parent.roistat.ts_control.put(2)
|
||||||
|
|
||||||
|
def on_unstage(self):
|
||||||
|
self.parent.cam.acquire.put(0)
|
||||||
|
self.parent.hdf.capture.put(0)
|
||||||
|
self.parent.roistat.ts_control.put(2)
|
||||||
|
logger.success('XXXX unstage XXXX')
|
||||||
|
|
||||||
|
|
||||||
|
class EigerROIStatPlugin(ROIStatPlugin):
|
||||||
|
roi1 = ADCpt(ROIStatNPlugin, '1:')
|
||||||
|
roi2 = ADCpt(ROIStatNPlugin, '2:')
|
||||||
|
|
||||||
|
class Eiger500K(PSIDetectorBase):
|
||||||
|
"""
|
||||||
|
"""
|
||||||
|
custom_prepare_cls = Eiger500KSetup
|
||||||
|
|
||||||
|
cam = ADCpt(SLSDetectorCam, 'cam1:')
|
||||||
|
|
||||||
|
#image = ADCpt(ImagePlugin, 'image1:')
|
||||||
|
#roi1 = ADCpt(ROIPlugin, 'ROI1:')
|
||||||
|
#roi2 = ADCpt(ROIPlugin, 'ROI2:')
|
||||||
|
#stats1 = ADCpt(StatsPlugin, 'Stats1:')
|
||||||
|
#stats2 = ADCpt(StatsPlugin, 'Stats2:')
|
||||||
|
roistat = ADCpt(EigerROIStatPlugin, 'ROIStat1:')
|
||||||
|
#roistat1 = ADCpt(ROIStatNPlugin, 'ROIStat1:1:')
|
||||||
|
#roistat2 = ADCpt(ROIStatNPlugin, 'ROIStat1:2:')
|
||||||
|
hdf = ADCpt(HDF5Plugin, 'HDF1:')
|
40
phoenix_bec/local_scripts/Examples/DefiningEpics_Channels.py
Normal file
40
phoenix_bec/local_scripts/Examples/DefiningEpics_Channels.py
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
from ophyd import Device, EpicsMotor, EpicsSignal, EpicsSignalRO
|
||||||
|
from ophyd import Component as Cpt
|
||||||
|
|
||||||
|
#option I via direct acces to classes
|
||||||
|
|
||||||
|
ScanX = EpicsMotor(name='ScanX',prefix='X07MB-ES-MA1:ScanX')
|
||||||
|
ScanY = EpicsMotor(name='ScanY',prefix='X07MB-ES-MA1:ScanY')
|
||||||
|
DIODE = EpicsSignal(name='SI',read_pv='X07MB-OP2-SAI_07:MEAN')
|
||||||
|
SMPL = EpicsSignal(name='SMPL',read_pv='X07MB-OP2:SMPL')
|
||||||
|
CYCLES = EpicsSignal(name='SMPL',read_pv='X07MB-OP2:TOTAL-CYCLES',write_pv='X07MB-OP2:TOTAL-CYCLES')
|
||||||
|
|
||||||
|
# Option 2 using component
|
||||||
|
|
||||||
|
class StageXY(Device):
|
||||||
|
x = Cpt(EpicsMotor, 'ScanX')
|
||||||
|
y = Cpt(EpicsMotor, 'ScanY')
|
||||||
|
|
||||||
|
xy_stage = StageXY('X07MB-ES-MA1:', name='stage')
|
||||||
|
|
||||||
|
#############################################
|
||||||
|
# This is basic bluesky
|
||||||
|
# Epics motor def seems to use init params in device, whcih are
|
||||||
|
# __init__(
|
||||||
|
# self,
|
||||||
|
# prefix="",
|
||||||
|
# *,
|
||||||
|
# name,
|
||||||
|
# kind=None,
|
||||||
|
# read_attrs=None,
|
||||||
|
# configuration_attrs=None,
|
||||||
|
# parent=None,
|
||||||
|
# **kwargs,
|
||||||
|
# ):
|
||||||
|
#
|
||||||
|
#########################################################
|
||||||
|
|
||||||
|
# to move motor use
|
||||||
|
# stage.x.move(0)
|
||||||
|
# to see all dict
|
||||||
|
# stage.x.__dict__
|
@ -78,7 +78,8 @@ class PhoenixBL():
|
|||||||
def add_xmap(self):
|
def add_xmap(self):
|
||||||
print('add xmap ')
|
print('add xmap ')
|
||||||
print(self.path_devices+'phoenix_xmap.yaml')
|
print(self.path_devices+'phoenix_xmap.yaml')
|
||||||
bec.config.update_session_with_file(self.path_devices+'phoenix_xmap.yaml',timeout=50)
|
|
||||||
|
bec.config.update_session_with_file(self.path_devices+'phoenix_xmap.yaml',timeout=100)
|
||||||
|
|
||||||
def add_falcon(self):
|
def add_falcon(self):
|
||||||
print('add_xmap')
|
print('add_xmap')
|
||||||
|
Reference in New Issue
Block a user