add example to connect to Epics channels

This commit is contained in:
gac-x07mb
2024-08-13 17:54:08 +02:00
parent 76b30127d5
commit e72a00c13e
10 changed files with 1078 additions and 22 deletions

View File

@ -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:'

View 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:')
)

View File

@ -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,6 +59,7 @@ 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")
@ -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,
#): #):
@ -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")
@ -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)

View File

@ -23,6 +23,8 @@ The beamlines need to inherit from the CustomDetectorMixing for their mixin clas
file psi_detector_base.py file psi_detector_base.py
import os import os
class DetectorInitError(Exception): class DetectorInitError(Exception):
@ -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")

View File

@ -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:')
)

View File

@ -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:')

View File

@ -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:')

View File

@ -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:')

View 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__

View File

@ -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')