First implementation on x07mb-bec_001 after merging with x07mb-test

This commit is contained in:
gac-x07mb
2024-08-20 13:29:30 +02:00
parent 8c36484fec
commit 1b8aabccc1
45 changed files with 5169 additions and 0 deletions

View File

@ -34,3 +34,135 @@ to setup the prompts.
""" """
# pylint: disable=invalid-name, unused-import, import-error, undefined-variable, unused-variable, unused-argument, no-name-in-module # pylint: disable=invalid-name, unused-import, import-error, undefined-variable, unused-variable, unused-argument, no-name-in-module
import time as tt
import sys
from IPython.core.magic import register_line_magic
from bec_lib.logger import bec_logger
logger = bec_logger.logger
#logger = bec_logger.LOGLEVEL.TRACE
#pylint: disable=invald-name, unused-import, import-error, undefined-variable, unused-variable, unused-argument, no-name-in-module
_session_name = "session_phoenix"
# SETUP PROMPTS
bec._ip.prompts.username = "PHOENIX"
bec._ip.prompts.status = 1
# make sure that edited modules are reloaded when changed
print('phoenix_bec/bec_iphyon_client/startup/post_startup.py')
print('... set autoreload of modules')
bec._ip.run_line_magic("reload_ext","autoreload")
bec._ip.run_line_magic("autoreload", "2")
print('autoreload loaded ')
#############################################################################
#
#... register BL specific magic commands
#
##############################################################################
@register_line_magic
def ph_reload(line):
##########################################################################
#
# this reloads certain phoenix related script to the iphython shell.
# useful for debugging/development to be revised for production
# aim of this magic is to quickl reload BL related stuff for debugging
# Most likely there are better ways to do this (possibly bec.reload_user_script()
# but syntax not clear, error messages. Here we know what we do
#
###################################################################W#####
from phoenix_bec.scripts import phoenix as PH
print('reload phoenix_bec.scripts.phoenix to iphyhton console')
print('to update version server restart server ')
# need to use global statement here, as I like to reload into space on
# iphyton consoel
global PH,phoenix
print('from phoenix_bec.scripts import phoenix as PH')
print('phoenix = PH.PhoenixBL()')
phoenix = PH.PhoenixBL()
#ph_config=PH.PhoenixConfighelper()
#enddef
print('##################################################################')
print('register magic')
print('...... %ph_load_xmap ... to reload xmap configuration')
@register_line_magic
def ph_load_xmap(line):
###
#magic for loading xmap
###
t0=tt.time()
phoenix_server.add_xmap()
print('elapsed time:', tt.time()-t0)
#enddef
print('...... %ph_load_falcon ... to reload falcon configuration')
@register_line_magic
def ph_load_falcon(line):
# magic to load falcon
t0=tt.time()
phoenix_server.add_falcon()
print('elapsed time:', tt.time()-t0)
#enddef
print('...... %ph_load_config ... to reload phoenix default configuration')
@register_line_magic
def ph_load_config(line):
t0=tt.time()
phoenix_server.add_phoenix_config()
print('elapsed time:', tt.time()-t0)
#enddef
##@register_line_magic
#def ph_post_startup(line):
# print('import phoenix_bec.bec_ipython_client.startup.post_startup does not work caused loop ')
# #import phoenix_bec.bec_ipython_client.startup.post_startup
# does not work seems to build a infinite stack...
####################################################################################
#
# init phoenix.py from server version as
# .................. phoenix_server=PhoenixBL()
# and in ipython shell only as
# .............. phoenix = Ph.Pheonix(BL()
##
#####################################################################################
print('###############################################################')
print('init phoenix_bec/scripts/phoenix.py in two different ways')
print(' 1) phoenix_server = PhoenixBL() ... takes code from server version ')
print('SERBVR VERSION DOES NOT WORK ANYMORE ')
print('FOLDER SCRUIPT SEEMS TO BE NON_STANDARD!!!!!!! ')
#phoenix_server=PhoenixBL()
print(' 2) phoenix=PH.PhoenixBL() ... on inpython shell only! (for debugging)')
from phoenix_bec.scripts import phoenix as PH
phoenix = PH.PhoenixBL()
#from phoenix_bec.bec_ipython_client.plugins.phoenix import Phoenix
#from phoenix_bec.devices.falcon_phoenix_no_hdf5 import FalconHDF5Plugins

View File

@ -0,0 +1,182 @@
"""
FILE
phoenix_bec.bec_iphyton_client
Post startup script for the BEC client. This script is executed after the
IPython shell is started. It is used to load the beamline specific
information and to setup the prompts.
The script is executed in the global namespace of the IPython shell. This
means that all variables defined here are available in the shell.
While command-line arguments have to be set in the pre-startup script, the
post-startup script can be used to load beamline specific information and
to setup the prompts.
#################################################################
# OLD CODE FROM CSAXS ONLY AS EXAMPLE NEXT LINES CAN BE IGNORED
################################################################
from bec_lib.logger import bec_logger
logger = bec_logger.logger
# pylint: disable=import-error
_args = _main_dict["args"]
_session_name = "cSAXS"
if _args.session.lower() == "lamni":
from csaxs_bec.bec_ipython_client.plugins.cSAXS import *
from csaxs_bec.bec_ipython_client.plugins.LamNI import *
_session_name = "LamNI"
lamni = LamNI(bec)
logger.succ#ess("LamNI session loaded.")
elif _args.session.lower() == "csaxs":
print("Loading cSAXS session")
from csaxs_bec.bec_ipython_client.plugins.cSAXS import *
logger.success("cSAXS session loaded.")
#####################################################################
"""
import time as tt
import sys
from IPython.core.magic import register_line_magic
from bec_lib.logger import bec_logger
logger = bec_logger.logger
#logger = bec_logger.LOGLEVEL.TRACE
#pylint: disable=invald-name, unused-import, import-error, undefined-variable, unused-variable, unused-argument, no-name-in-module
_session_name = "session_phoenix"
# SETUP PROMPTS
bec._ip.prompts.username = "PHOENIX"
bec._ip.prompts.status = 1
# make sure that edited modules are reloaded when changed
print('phoenix_bec/bec_iphyon_client/startup/post_startup.py')
print('... set autoreload of modules')
bec._ip.run_line_magic("reload_ext","autoreload")
bec._ip.run_line_magic("autoreload", "2")
print('autoreload loaded ')
################################################################
#
# next lines are not ideal and likely need to be removed they are a temporary fix as
# we had trouble finding certain paths.
#
# we need to work in server_env, otherwise no server is found
# path to ophyd devices is only way to find default ophyd devices
# path to python packages not ideal but only way to add pyp5 and pandas packages
#
##############################################################
print('post_startup.py : set some paths as temp fix. This needs to be solved ')
ophyd_devices_path='/data/test/x07mb-test-bec/bec_deployment/ophyd_devices'
p_path='/data/test/x07mb-test-bec/bec_deployment/bec_server_venv/lib/python3.11/site-packages'
print('add ',ophyd_devices_path)
print('add ',p_path)
if ophyd_devices_path not in sys.path:
sys.path.insert(1, os.path.expandvars(ophyd_devices_path))
#endif
if p_path not in sys.path:
sys.path.insert(1, os.path.expandvars(p_path))
#endif
#############################################################################
#
#... register BL specific magic commands
#
##############################################################################
# not clear, error messages. Here we know what we do.
#
########################################################################
from phoenix_bec.scripts import phoenix as PH
print('reload phoenix_bec.scripts.phoenix to iphyhton console')
print('to update version server restart server ')
# need to use global statement here, as I like to reload into space on
# iphyton consoel
global PH,phoenix
print('from phoenix_bec.scripts import phoenix as PH')
print('phoenix = PH.PhoenixBL()')
phoenix = PH.PhoenixBL()
#ph_config=PH.PhoenixConfighelper()
#enddef
print('##################################################################')
print('register magic')
print('...... %ph_load_xmap ... to reload xmap configuration')
@register_line_magic
def ph_load_xmap(line):
###
#magic for loading xmap
###
t0=tt.time()
phoenix_server.add_xmap()
print('elapsed time:', tt.time()-t0)
#enddef
print('...... %ph_load_falcon ... to reload falcon configuration')
@register_line_magic
def ph_load_falcon(line):
# magic to load falcon
t0=tt.time()
phoenix_server.add_falcon()
print('elapsed time:', tt.time()-t0)
#enddef
print('...... %ph_load_config ... to reload phoenix default configuration')
@register_line_magic
def ph_load_config(line):
t0=tt.time()
phoenix_server.add_phoenix_config()
print('elapsed time:', tt.time()-t0)
#enddef
##@register_line_magic
#def ph_post_startup(line):
# print('import phoenix_bec.bec_ipython_client.startup.post_startup does not work caused loop ')
# #import phoenix_bec.bec_ipython_client.startup.post_startup
# does not work seems to build a infinite stack...
####################################################################################
#
# init phoenix.py from server version as
# .................. phoenix_server=PhoenixBL()
# and in ipython shell only as
# .............. phoenix = Ph.Pheonix(BL()
##
#####################################################################################
print('###############################################################')
print('init phoenix_bec/scripts/phoenix.py in two different ways')
print(' 1) phoenix_server = PhoenixBL() ... takes code from server version ')
phoenix_server=PhoenixBL()
print(' 2) phoenix=PH.PhoenixBL() ... on inpython shell only! (for debugging)')
from phoenix_bec.scripts import phoenix as PH
phoenix = PH.PhoenixBL()
#from phoenix_bec.bec_ipython_client.plugins.phoenix import Phoenix
#from phoenix_bec.devices.falcon_phoenix_no_hdf5 import FalconHDF5Plugins

View File

@ -0,0 +1,26 @@
"""
Pre-startup script for BEC client. This script is executed before the BEC client
is started. It can be used to add additional command line arguments.
"""
from bec_lib.service_config import ServiceConfig
def extend_command_line_args(parser):
"""
Extend the command line arguments of the BEC client.
"""
# parser.add_argument("--session", help="Session name", type=str, default="cSAXS")
return parser
# def get_config() -> ServiceConfig:
# """
# Create and return the service configuration.
# """
# return ServiceConfig(redis={"host": "localhost", "port": 6379})

View File

@ -0,0 +1,73 @@
###################################################
#
# phoenix standard devices (motors)
#
#
#####################################################
#
# MOTORS ES1
#
ScanX:
readoutPriority: baseline
description: 'Horizontal sample position'
deviceClass: ophyd.EpicsMotor
deviceConfig:
prefix: 'X07MB-ES-MA1:ScanX'
deviceTags:
- ES-MA1
- phoenix_bec/device_configs/phoenix_devices.yaml
onFailure: retry
enabled: true
readOnly: false
softwareTrigger: false
ScanY:
readoutPriority: baseline
description: 'Horizontal sample position'
deviceClass: ophyd.EpicsMotor
deviceConfig:
prefix: 'X07MB-ES-MA1:ScanY'
deviceTags:
- ES-MA1
- phoenix_bec/device_configs/phoenix_devices.yaml
onFailure: retry
enabled: true
readOnly: false
softwareTrigger: false
#
#
# DIODES from ES1 ADC
#
#
SAI_07_MEAN:
readoutPriority: monitored
description: DIODE
deviceClass: ophyd.EpicsSignalRO
deviceConfig:
auto_monitor: true
read_pv: 'X07MB-OP2-SAI_07:MEAN'
deviceTags:
- PHOENIX
- phoenix_bec/device_configs/phoenix_devices.yaml
onFailure: buffer
enabled: true
readOnly: true
softwareTrigger: false
SAI_08_MEAN:
readoutPriority: monitored
description: DIODE
deviceClass: ophyd.EpicsSignalRO
deviceConfig:
auto_monitor: true
read_pv: 'X07MB-OP2-SAI_08:MEAN'
deviceTags:
- PHOENIX
- phoenix_bec/device_configs/phoenix_devices.yaml
onFailure: buffer
enabled: true
readOnly: true
softwareTrigger: false

View File

@ -0,0 +1,14 @@
falcon_nohdf5:
description: Falcon detector x-ray fluoresence II
deviceClass: phoenix_bec.devices.falcon_phoenix_no_hdf5.FalconcSAXS
deviceConfig:
prefix: 'X07MB-SITORO:'
deviceTags:
- phoenix
- falcon
- no hdf5
- phoenix_devices.yaml
onFailure: buffer
enabled: true
readoutPriority: async
softwareTrigger: false

View File

@ -0,0 +1,14 @@
xmap_nohdf5:
description: XMAP detector x-ray fluoresence II
deviceClass: phoenix_bec.devices.xmap_phoenix_no_hdf5.XMAPphoenix
deviceConfig:
prefix: 'X07MB-XMAP:'
deviceTags:
- phoenix
- xmap
- no hdf5
- phoenix_bec/device_configs/phoenix_xmap.yaml
onFailure: buffer
enabled: true
readoutPriority: async
softwareTrigger: false

View File

@ -0,0 +1,243 @@
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):
"""
This defines the trigger setup.
"""
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):
"""
Parent class: PSIDetectorBase
class attributes:
custom_prepare_cls (XMAPSetup) : Custom detector setup class for cSAXS,
inherits from CustomDetectorMixin
in __init__ of PSIDetecor bases
class is initialized
self.custom_prepare = self.custom_prepare_cls(parent=self, **kwargs)
PSIDetectorBase.set_min_readout (float) : Minimum readout time for the detector
dxp (EpicsDXPXMAP) : DXP parameters for XMAP detector
mca (EpicsMCARecord) : MCA parameters for XMAP detector
hdf5 (XMAPHDF5Plugins) : HDF5 parameters for XMAP detector
MIN_READOUT (float) : Minimum readout time for the detector
The class PhoenixTrigger is the class to be called via yaml configuration file
the input arguments are defined by PSIDetectorBase,
and need to be given in the yaml configuration file.
To adress chanels such as 'X07MB-OP2:SMPL-DONE':
use prefix 'X07MB-OP2:' in the device definition in the yaml configuration file.
PSIDetectorBase(
prefix='',
*,Q
name,
kind=None,
parent=None,
device_manager=None,
**kwargs,
)
Docstring:
Abstract base class for SLS detectors
Class attributes:
custom_prepare_cls (object): class for custom prepare logic (BL specific)
Args:
prefix (str): EPICS PV prefix for component (optional)
name (str): name of the device, as will be reported via read()
kind (str): member of class 'ophydobj.Kind', defaults to Kind.normal
omitted -> readout ignored for read 'ophydobj.read()'
normal -> readout for read
config -> config parameter for 'ophydobj.read_configuration()'
hinted -> which attribute is readout for read
parent (object): instance of the parent device
device_manager (object): bec device manager
**kwargs: keyword arguments
File: /data/test/x07mb-test-bec/bec_deployment/ophyd_devices/ophyd_devices/interfaces/base_classes/psi_detector_base.py
Type: type
Subclasses: SimCamera, SimMonitorAsync
"""
#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
QUESTION HOW does ADCpt kno the EPICS prefix??????
#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,345 @@
from bec_lib import bec_logger
from ophyd import Component
from ophyd_devices.interfaces.base_classes.psi_delay_generator_base import (
DDGCustomMixin,
PSIDelayGeneratorBase,
TriggerSource,
)
from ophyd_devices.utils import bec_utils
logger = bec_logger.logger
class DelayGeneratorError(Exception):
"""Exception raised for errors."""
class DDGSetup(DDGCustomMixin):
"""
Mixin class for DelayGenerator logic at cSAXS.
At cSAXS, multiple DDGs were operated at the same time. There different behaviour is
implemented in the ddg_config signals that are passed via the device config.
"""
def initialize_default_parameter(self) -> None:
"""Method to initialize default parameters."""
for ii, channel in enumerate(self.parent.all_channels):
self.parent.set_channels("polarity", self.parent.polarity.get()[ii], [channel])
self.parent.set_channels("amplitude", self.parent.amplitude.get())
self.parent.set_channels("offset", self.parent.offset.get())
# Setup reference
self.parent.set_channels(
"reference", 0, [f"channel{pair}.ch1" for pair in self.parent.all_delay_pairs]
)
self.parent.set_channels(
"reference", 0, [f"channel{pair}.ch2" for pair in self.parent.all_delay_pairs]
)
self.parent.set_trigger(getattr(TriggerSource, self.parent.set_trigger_source.get()))
# Set threshold level for ext. pulses
self.parent.level.put(self.parent.thres_trig_level.get())
def prepare_ddg(self) -> None:
"""
Method to prepare scan logic of cSAXS
Two scantypes are supported: "step" and "fly":
- step: Scan is performed by stepping the motor and acquiring data at each step
- fly: Scan is performed by moving the motor with a constant velocity and acquiring data
Custom logic for different DDG behaviour during scans.
- set_high_on_exposure : If True, then TTL signal is high during
the full exposure time of the scan (all frames).
E.g. Keep shutter open for the full scan.
- fixed_ttl_width : fixed_ttl_width is a list of 5 values, one for each channel.
If the value is 0, then the width of the TTL pulse is determined,
no matter which parameters are passed from the scaninfo for exposure time
- set_trigger_source : Specifies the default trigger source for the DDG. For cSAXS, relevant ones
were: SINGLE_SHOT, EXT_RISING_EDGE
"""
self.parent.set_trigger(getattr(TriggerSource, self.parent.set_trigger_source.get()))
# scantype "step"
if self.parent.scaninfo.scan_type == "step":
# High on exposure means that the signal
if self.parent.set_high_on_exposure.get():
# caluculate parameters
num_burst_cycle = 1 + self.parent.additional_triggers.get()
exp_time = (
self.parent.delta_width.get()
+ self.parent.scaninfo.frames_per_trigger
* (self.parent.scaninfo.exp_time + self.parent.scaninfo.readout_time)
)
total_exposure = exp_time
delay_burst = self.parent.delay_burst.get()
# Set individual channel widths, if fixed_ttl_width and trigger_width are combined, this can be a common call too
if not self.parent.trigger_width.get():
self.parent.set_channels("width", exp_time)
else:
self.parent.set_channels("width", self.parent.trigger_width.get())
for value, channel in zip(
self.parent.fixed_ttl_width.get(), self.parent.all_channels
):
logger.debug(f"Trying to set DDG {channel} to {value}")
if value != 0:
self.parent.set_channels("width", value, channels=[channel])
else:
# caluculate parameters
exp_time = self.parent.delta_width.get() + self.parent.scaninfo.exp_time
total_exposure = exp_time + self.parent.scaninfo.readout_time
delay_burst = self.parent.delay_burst.get()
num_burst_cycle = (
self.parent.scaninfo.frames_per_trigger + self.parent.additional_triggers.get()
)
# Set individual channel widths, if fixed_ttl_width and trigger_width are combined, this can be a common call too
if not self.parent.trigger_width.get():
self.parent.set_channels("width", exp_time)
else:
self.parent.set_channels("width", self.parent.trigger_width.get())
# scantype "fly"
elif self.parent.scaninfo.scan_type == "fly":
if self.parent.set_high_on_exposure.get():
# caluculate parameters
exp_time = (
self.parent.delta_width.get()
+ self.parent.scaninfo.exp_time * self.parent.scaninfo.num_points
+ self.parent.scaninfo.readout_time * (self.parent.scaninfo.num_points - 1)
)
total_exposure = exp_time
delay_burst = self.parent.delay_burst.get()
num_burst_cycle = 1 + self.parent.additional_triggers.get()
# Set individual channel widths, if fixed_ttl_width and trigger_width are combined, this can be a common call too
if not self.parent.trigger_width.get():
self.parent.set_channels("width", exp_time)
else:
self.parent.set_channels("width", self.parent.trigger_width.get())
for value, channel in zip(
self.parent.fixed_ttl_width.get(), self.parent.all_channels
):
logger.debug(f"Trying to set DDG {channel} to {value}")
if value != 0:
self.parent.set_channels("width", value, channels=[channel])
else:
# caluculate parameters
exp_time = self.parent.delta_width.get() + self.parent.scaninfo.exp_time
total_exposure = exp_time + self.parent.scaninfo.readout_time
delay_burst = self.parent.delay_burst.get()
num_burst_cycle = (
self.parent.scaninfo.num_points + self.parent.additional_triggers.get()
)
# Set individual channel widths, if fixed_ttl_width and trigger_width are combined, this can be a common call too
if not self.parent.trigger_width.get():
self.parent.set_channels("width", exp_time)
else:
self.parent.set_channels("width", self.parent.trigger_width.get())
else:
raise Exception(f"Unknown scan type {self.parent.scaninfo.scan_type}")
# Set common DDG parameters
self.parent.burst_enable(num_burst_cycle, delay_burst, total_exposure, config="first")
self.parent.set_channels("delay", 0.0)
def on_trigger(self) -> None:
"""Method to be executed upon trigger"""
if self.parent.source.read()[self.parent.source.name]["value"] == TriggerSource.SINGLE_SHOT:
self.parent.trigger_shot.put(1)
def check_scan_id(self) -> None:
"""
Method to check if scan_id has changed.
If yes, then it changes parent.stopped to True, which will stop further actions.
"""
old_scan_id = self.parent.scaninfo.scan_id
self.parent.scaninfo.load_scan_metadata()
if self.parent.scaninfo.scan_id != old_scan_id:
self.parent.stopped = True
def finished(self) -> None:
"""Method checks if DDG finished acquisition"""
def on_pre_scan(self) -> None:
"""
Method called by pre_scan hook in parent class.
Executes trigger if premove_trigger is Trus.
"""
if self.parent.premove_trigger.get() is True:
self.parent.trigger_shot.put(1)
class DelayGeneratorcSAXS(PSIDelayGeneratorBase):
"""
DG645 delay generator at cSAXS (multiple can be in use depending on the setup)
Default values for setting up DDG.
Note: checks of set calues are not (only partially) included, check manual for details on possible settings.
https://www.thinksrs.com/downloads/pdfs/manuals/DG645m.pdf
- delay_burst : (float >=0) Delay between trigger and first pulse in burst mode
- delta_width : (float >= 0) Add width to fast shutter signal to make sure its open during acquisition
- additional_triggers : (int) add additional triggers to burst mode (mcs card needs +1 triggers per line)
- polarity : (list of 0/1) polarity for different channels
- amplitude : (float) amplitude voltage of TTLs
- offset : (float) offset for ampltitude
- thres_trig_level : (float) threshold of trigger amplitude
Custom signals for logic in different DDGs during scans (for custom_prepare.prepare_ddg):
- set_high_on_exposure : (bool): if True, then TTL signal should go high during the full acquisition time of a scan.
# TODO trigger_width and fixed_ttl could be combined into single list.
- fixed_ttl_width : (list of either 1 or 0), one for each channel.
- trigger_width : (float) if fixed_ttl_width is True, then the width of the TTL pulse is set to this value.
- set_trigger_source : (TriggerSource) specifies the default trigger source for the DDG.
- premove_trigger : (bool) if True, then a trigger should be executed before the scan starts (to be implemented in on_pre_scan).
- set_high_on_stage : (bool) if True, then TTL signal should go high already on stage.
"""
custom_prepare_cls = DDGSetup
delay_burst = Component(
bec_utils.ConfigSignal, name="delay_burst", kind="config", config_storage_name="ddg_config"
)
delta_width = Component(
bec_utils.ConfigSignal, name="delta_width", kind="config", config_storage_name="ddg_config"
)
additional_triggers = Component(
bec_utils.ConfigSignal,
name="additional_triggers",
kind="config",
config_storage_name="ddg_config",
)
polarity = Component(
bec_utils.ConfigSignal, name="polarity", kind="config", config_storage_name="ddg_config"
)
fixed_ttl_width = Component(
bec_utils.ConfigSignal,
name="fixed_ttl_width",
kind="config",
config_storage_name="ddg_config",
)
amplitude = Component(
bec_utils.ConfigSignal, name="amplitude", kind="config", config_storage_name="ddg_config"
)
offset = Component(
bec_utils.ConfigSignal, name="offset", kind="config", config_storage_name="ddg_config"
)
thres_trig_level = Component(
bec_utils.ConfigSignal,
name="thres_trig_level",
kind="config",
config_storage_name="ddg_config",
)
set_high_on_exposure = Component(
bec_utils.ConfigSignal,
name="set_high_on_exposure",
kind="config",
config_storage_name="ddg_config",
)
set_high_on_stage = Component(
bec_utils.ConfigSignal,
name="set_high_on_stage",
kind="config",
config_storage_name="ddg_config",
)
set_trigger_source = Component(
bec_utils.ConfigSignal,
name="set_trigger_source",
kind="config",
config_storage_name="ddg_config",
)
trigger_width = Component(
bec_utils.ConfigSignal,
name="trigger_width",
kind="config",
config_storage_name="ddg_config",
)
premove_trigger = Component(
bec_utils.ConfigSignal,
name="premove_trigger",
kind="config",
config_storage_name="ddg_config",
)
def __init__(
self,
prefix="",
*,
name,
kind=None,
read_attrs=None,
configuration_attrs=None,
parent=None,
device_manager=None,
sim_mode=False,
ddg_config=None,
**kwargs,
):
"""
Args:
prefix (str, optional): Prefix of the device. Defaults to "".
name (str): Name of the device.
kind (str, optional): Kind of the device. Defaults to None.
read_attrs (list, optional): List of attributes to read. Defaults to None.
configuration_attrs (list, optional): List of attributes to configure. Defaults to None.
parent (Device, optional): Parent device. Defaults to None.
device_manager (DeviceManagerBase, optional): DeviceManagerBase object. Defaults to None.
sim_mode (bool, optional): Simulation mode flag. Defaults to False.
ddg_config (dict, optional): Dictionary of ddg_config signals. Defaults to None.
"""
# Default values for ddg_config signals
self.ddg_config = {
# Setup default values
f"{name}_delay_burst": 0,
f"{name}_delta_width": 0,
f"{name}_additional_triggers": 0,
f"{name}_polarity": [1, 1, 1, 1, 1],
f"{name}_amplitude": 4.5,
f"{name}_offset": 0,
f"{name}_thres_trig_level": 2.5,
# Values for different behaviour during scans
f"{name}_fixed_ttl_width": [0, 0, 0, 0, 0],
f"{name}_trigger_width": None,
f"{name}_set_high_on_exposure": False,
f"{name}_set_high_on_stage": False,
f"{name}_set_trigger_source": "SINGLE_SHOT",
f"{name}_premove_trigger": False,
}
if ddg_config is not None:
# pylint: disable=expression-not-assigned
[self.ddg_config.update({f"{name}_{key}": value}) for key, value in ddg_config.items()]
super().__init__(
prefix=prefix,
name=name,
kind=kind,
read_attrs=read_attrs,
configuration_attrs=configuration_attrs,
parent=parent,
device_manager=device_manager,
sim_mode=sim_mode,
**kwargs,
)
if __name__ == "__main__":
# Start delay generator in simulation mode.
# Note: To run, access to Epics must be available.
dgen = DelayGeneratorcSAXS("delaygen:DG1:", name="dgen", sim_mode=True)

View File

@ -0,0 +1,363 @@
#
#
# changes version for PHOENIX WITHOUT HDF5 plugin to be revised/renamed...
#
#
import enum
import os
import threading
from bec_lib.logger import bec_logger
from ophyd import Component as Cpt
from ophyd import Device, EpicsSignal, EpicsSignalRO, EpicsSignalWithRBV
from ophyd.mca import EpicsMCARecord
from ophyd_devices.interfaces.base_classes.psi_detector_base import (
CustomDetectorMixin,
PSIDetectorBase,
)
logger = bec_logger.logger
class FalconError(Exception):
"""Base class for exceptions in this module."""
class FalconTimeoutError(FalconError):
"""Raised when the Falcon does not respond in time."""
class DetectorState(enum.IntEnum):
"""Detector states for Falcon detector"""
DONE = 0
ACQUIRING = 1
class TriggerSource(enum.IntEnum):
"""Trigger source for Falcon detector"""
USER = 0
GATE = 1
SYNC = 2
class MappingSource(enum.IntEnum):
"""Mapping source for Falcon detector"""
SPECTRUM = 0
MAPPING = 1
class EpicsDXPFalcon(Device):
"""
DXP parameters for Falcon detector
Base class to map EPICS PVs from DXP parameters to ophyd signals.
"""
elapsed_live_time = Cpt(EpicsSignal, "ElapsedLiveTime")
elapsed_real_time = Cpt(EpicsSignal, "ElapsedRealTime")
elapsed_trigger_live_time = Cpt(EpicsSignal, "ElapsedTriggerLiveTime")
# Energy Filter PVs
energy_threshold = Cpt(EpicsSignalWithRBV, "DetectionThreshold")
min_pulse_separation = Cpt(EpicsSignalWithRBV, "MinPulsePairSeparation")
detection_filter = Cpt(EpicsSignalWithRBV, "DetectionFilter", string=True)
scale_factor = Cpt(EpicsSignalWithRBV, "ScaleFactor")
risetime_optimisation = Cpt(EpicsSignalWithRBV, "RisetimeOptimization")
# Misc PVs
detector_polarity = Cpt(EpicsSignalWithRBV, "DetectorPolarity")
decay_time = Cpt(EpicsSignalWithRBV, "DecayTime")
current_pixel = Cpt(EpicsSignalRO, "CurrentPixel")
class FalconHDF5Plugins(Device):
"""
HDF5 parameters for Falcon detector
Base class to map EPICS PVs from HDF5 Plugin to ophyd signals.
"""
""" ----------------------------------------------------------------------------
capture = Cpt(EpicsSignalWithRBV, "Capture")
enable = Cpt(EpicsSignalWithRBV, "EnableCallbacks", string=True, kind="config")
xml_file_name = Cpt(EpicsSignalWithRBV, "XMLFileName", string=True, kind="config")
lazy_open = Cpt(EpicsSignalWithRBV, "LazyOpen", string=True, doc="0='No' 1='Yes'")
temp_suffix = Cpt(EpicsSignalWithRBV, "TempSuffix", string=True)
file_path = Cpt(EpicsSignalWithRBV, "FilePath", string=True, kind="config")
file_name = Cpt(EpicsSignalWithRBV, "FileName", string=True, kind="config")
file_template = Cpt(EpicsSignalWithRBV, "FileTemplate", string=True, kind="config")
num_capture = Cpt(EpicsSignalWithRBV, "NumCapture", kind="config")
file_write_mode = Cpt(EpicsSignalWithRBV, "FileWriteMode", kind="config")
queue_size = Cpt(EpicsSignalWithRBV, "QueueSize", kind="config")
array_counter = Cpt(EpicsSignalWithRBV, "ArrayCounter", kind="config")
"""
class FalconSetup(CustomDetectorMixin):
"""
Falcon setup class for cSAXS
Parent class: CustomDetectorMixin
"""
def __init__(self, *args, parent: Device = None, **kwargs) -> None:
super().__init__(*args, parent=parent, **kwargs)
self._lock = threading.RLock()
def on_init(self) -> None:
"""Initialize Falcon detector"""
self.initialize_default_parameter()
self.initialize_detector()
self.initialize_detector_backend()
def initialize_default_parameter(self) -> None:
"""
Set default parameters for Falcon
This will set:
- readout (float): readout time in seconds
- value_pixel_per_buffer (int): number of spectra in buffer of Falcon Sitoro
"""
self.parent.value_pixel_per_buffer = 20
self.update_readout_time()
def update_readout_time(self) -> None:
"""Set readout time for Eiger9M detector"""
readout_time = (
self.parent.scaninfo.readout_time
if hasattr(self.parent.scaninfo, "readout_time")
else self.parent.MIN_READOUT
)
self.parent.readout_time = max(readout_time, self.parent.MIN_READOUT)
def initialize_detector(self) -> None:
"""Initialize Falcon detector"""
self.stop_detector()
self.stop_detector_backend()
self.set_trigger(
mapping_mode=MappingSource.MAPPING, trigger_source=TriggerSource.GATE, ignore_gate=0
)
# 1 Realtime
self.parent.preset_mode.put(1)
# 0 Normal, 1 Inverted
self.parent.input_logic_polarity.put(0)
# 0 Manual 1 Auto
self.parent.auto_pixels_per_buffer.put(0)
# Sets the number of pixels/spectra in the buffer
self.parent.pixels_per_buffer.put(self.parent.value_pixel_per_buffer)
def initialize_detector_backend(self) -> None:
"""Initialize the detector backend for Falcon."""
w=0
#----------------------------------------------------------------------
#self.parent.hdf5.enable.put(1)
# file location of h5 layout for cSAXS
#self.parent.hdf5.xml_file_name.put("layout.xml")
# TODO Check if lazy open is needed and wanted!
#self.parent.hdf5.lazy_open.put(1)
#self.parent.hdf5.temp_suffix.put("")
# size of queue for number of spectra allowed in the buffer, if too small at high throughput, data is lost
#self.parent.hdf5.queue_size.put(2000)
# Segmentation into Spectra within EPICS, 1 is activate, 0 is deactivate
#self.parent.nd_array_mode.put(1)
def on_stage(self) -> None:
"""Prepare detector and backend for acquisition"""
self.prepare_detector()
self.prepare_data_backend()
self.publish_file_location(done=False, successful=False)
self.arm_acquisition()
def prepare_detector(self) -> None:
"""Prepare detector for acquisition"""
self.set_trigger(
mapping_mode=MappingSource.MAPPING, trigger_source=TriggerSource.GATE, ignore_gate=0
)
self.parent.preset_real.put(self.parent.scaninfo.exp_time)
self.parent.pixels_per_run.put(
int(self.parent.scaninfo.num_points * self.parent.scaninfo.frames_per_trigger)
)
def prepare_data_backend(self) -> None:
"""Prepare data backend for acquisition"""
w=9
""" --------------------------------------------------------------
self.parent.filepath.set(
self.parent.filewriter.compile_full_filename(f"{self.parent.name}.h5")
).wait()
file_path, file_name = os.path.split(self.parent.filepath.get())
self.parent.hdf5.file_path.put(file_path)
self.parent.hdf5.file_name.put(file_name)
self.parent.hdf5.file_template.put("%s%s")
self.parent.hdf5.num_capture.put(
int(self.parent.scaninfo.num_points * self.parent.scaninfo.frames_per_trigger)
)
self.parent.hdf5.file_write_mode.put(2)
# Reset spectrum counter in filewriter, used for indexing & identifying missing triggers
self.parent.hdf5.array_counter.put(0)
# Start file writing
self.parent.hdf5.capture.put(1)
"""
def arm_acquisition(self) -> None:
"""Arm detector for acquisition"""
self.parent.start_all.put(1)
signal_conditions = [
(
lambda: self.parent.state.read()[self.parent.state.name]["value"],
DetectorState.ACQUIRING,
)
]
if not self.wait_for_signals(
signal_conditions=signal_conditions,
timeout=self.parent.TIMEOUT_FOR_SIGNALS,
check_stopped=True,
all_signals=False,
):
raise FalconTimeoutError(
f"Failed to arm the acquisition. Detector state {signal_conditions[0][0]}"
)
def on_unstage(self) -> None:
"""Unstage detector and backend"""
pass
def on_complete(self) -> None:
"""Complete detector and backend"""
#------------------------------------------------------------------
#self.finished(timeout=self.parent.TIMEOUT_FOR_SIGNALS)
#self.publish_file_location(done=True, successful=True)
w=9
def on_stop(self) -> None:
"""Stop detector and backend"""
self.stop_detector()
#self.stop_detector_backend()
def stop_detector(self) -> None:
"""Stops detector"""
self.parent.stop_all.put(1)
self.parent.erase_all.put(1)
#-------------------------------------------------------------------
#signal_conditions = [
# (lambda: self.parent.state.read()[self.parent.state.name]["value"], DetectorState.DONE)
#]
#if not self.wait_for_signals(
# signal_conditions=signal_conditions,
# timeout=self.parent.TIMEOUT_FOR_SIGNALS - self.parent.TIMEOUT_FOR_SIGNALS // 2,
# all_signals=False,
#):
# # Retry stop detector and wait for remaining time
# raise FalconTimeoutError(
# f"Failed to stop detector, timeout with state {signal_conditions[0][0]}"
# )
def stop_detector_backend(self) -> None:
"""Stop the detector backend"""
#self.parent.hdf5.capture.put(0)
w=0
def finished(self, timeout: int = 5) -> None:
"""Check if scan finished succesfully"""
with self._lock:
total_frames = int(
self.parent.scaninfo.num_points * self.parent.scaninfo.frames_per_trigger
)
signal_conditions = [
(self.parent.dxp.current_pixel.get, total_frames),
# (self.parent.hdf5.array_counter.get, total_frames), ---------------------
]
if not self.wait_for_signals(
signal_conditions=signal_conditions,
timeout=timeout,
check_stopped=True,
all_signals=True,
):
logger.debug(
f"Falcon missed a trigger: received trigger {self.parent.dxp.current_pixel.get()},"
f" send data {self.parent.hdf5.array_counter.get()} from total_frames"
f" {total_frames}"
)
self.stop_detector()
self.stop_detector_backend()
def set_trigger(
self, mapping_mode: MappingSource, trigger_source: TriggerSource, ignore_gate: int = 0
) -> None:
"""
Set triggering mode for detector
Args:
mapping_mode (MappingSource): Mapping mode for the detector
trigger_source (TriggerSource): Trigger source for the detector, pixel_advance_signal
ignore_gate (int): Ignore gate from TTL signal; defaults to 0
"""
mapping = int(mapping_mode)
trigger = trigger_source
self.parent.collect_mode.put(mapping)
self.parent.pixel_advance_mode.put(trigger)
self.parent.ignore_gate.put(ignore_gate)
class FalconcSAXS(PSIDetectorBase):
"""
Falcon Sitoro detector for CSAXS
Parent class: PSIDetectorBase
class attributes:
custom_prepare_cls (FalconSetup) : Custom detector setup class for cSAXS,
inherits from CustomDetectorMixin
PSIDetectorBase.set_min_readout (float) : Minimum readout time for the detector
dxp (EpicsDXPFalcon) : DXP parameters for Falcon detector
mca (EpicsMCARecord) : MCA parameters for Falcon detector
hdf5 (FalconHDF5Plugins) : HDF5 parameters for Falcon detector
MIN_READOUT (float) : Minimum readout time for the detector
"""
# Specify which functions are revealed to the user in BEC client
USER_ACCESS = ["describe"]
# specify Setup class
custom_prepare_cls = FalconSetup
# specify minimum readout time for detector
MIN_READOUT = 3e-3
TIMEOUT_FOR_SIGNALS = 5
# specify class attributes
dxp = Cpt(EpicsDXPFalcon, "dxp1:")
mca = Cpt(EpicsMCARecord, "mca1")
hdf5 = Cpt(FalconHDF5Plugins, "HDF1:")
stop_all = Cpt(EpicsSignal, "StopAll")
erase_all = Cpt(EpicsSignal, "EraseAll")
start_all = Cpt(EpicsSignal, "StartAll")
state = Cpt(EpicsSignal, "Acquiring")
preset_mode = Cpt(EpicsSignal, "PresetMode") # 0 No preset 1 Real time 2 Events 3 Triggers
preset_real = Cpt(EpicsSignal, "PresetReal")
preset_events = Cpt(EpicsSignal, "PresetEvents")
preset_triggers = Cpt(EpicsSignal, "PresetTriggers")
triggers = Cpt(EpicsSignalRO, "MaxTriggers", lazy=True)
events = Cpt(EpicsSignalRO, "MaxEvents", lazy=True)
input_count_rate = Cpt(EpicsSignalRO, "MaxInputCountRate", lazy=True)
output_count_rate = Cpt(EpicsSignalRO, "MaxOutputCountRate", lazy=True)
collect_mode = Cpt(EpicsSignal, "CollectMode") # 0 MCA spectra, 1 MCA mapping
pixel_advance_mode = Cpt(EpicsSignal, "PixelAdvanceMode")
ignore_gate = Cpt(EpicsSignal, "IgnoreGate")
input_logic_polarity = Cpt(EpicsSignal, "InputLogicPolarity")
auto_pixels_per_buffer = Cpt(EpicsSignal, "AutoPixelsPerBuffer")
pixels_per_buffer = Cpt(EpicsSignal, "PixelsPerBuffer")
pixels_per_run = Cpt(EpicsSignal, "PixelsPerRun")
nd_array_mode = Cpt(EpicsSignal, "NDArrayMode")
if __name__ == "__main__":
falcon = FalconcSAXS(name="falcon", prefix="X12SA-SITORO:", sim_mode=True)

View File

@ -0,0 +1,366 @@
#
#
# changes version for PHOENIX WITHOUT HDF5 plugin to be revised/renamed...
#
#
import enum
import os
import threading
from bec_lib.logger import bec_logger
from ophyd import Component as Cpt
from ophyd import Device, EpicsSignal, EpicsSignalRO, EpicsSignalWithRBV
from ophyd.mca import EpicsMCARecord
from ophyd_devices.interfaces.base_classes.psi_detector_base import (
CustomDetectorMixin,
PSIDetectorBase,
)
logger = bec_logger.logger
#bec_logger.level = bec_logger.LOGLEVEL.TRACE
bec_logger.level = bec_logger.LOGLEVEL.INFO
class XMAPError(Exception):
"""Base class for exceptions in this module."""
class XMAPTimeoutError(XMAPError):
"""Raised when the XMAP does not respond in time."""
class DetectorState(enum.IntEnum):
"""Detector states for XMAP detector"""
DONE = 0
ACQUIRING = 1
class TriggerSource(enum.IntEnum):
"""Trigger source for XMAP detector"""
USER = 0
GATE = 1
SYNC = 2
class MappingSource(enum.IntEnum):
"""Mapping source for XMAP detector"""
SPECTRUM = 0
MAPPING = 1
class EpicsDXPXMAP(Device):
"""
DXP parameters for XMAP detector
Base class to map EPICS PVs from DXP parameters to ophyd signals.
"""
#roi2 = ADCpt(ROIPlugin, 'ROI2:')
elapsed_live_time = Cpt(EpicsSignal, "ElapsedLiveTime")
elapsed_real_time = Cpt(EpicsSignal, "ElapsedRealTime")
elapsed_trigger_live_time = Cpt(EpicsSignal, "ElapsedTriggerLiveTime")
# Energy Filter PVs .... uncomment falcon stuff
#energy_threshold = Cpt(EpicsSignalWithRBV, "DetectionThreshold")
#min_pulse_separation = Cpt(EpicsSignalWithRBV, "MinPulsePairSeparation")
#detection_filter = Cpt(EpicsSignalWithRBV, "DetectionFilter", string=True)
#scale_factor = Cpt(EpicsSignalWithRBV, "ScaleFactor")
#risetime_optimisation = Cpt(EpicsSignalWithRBV, "RisetimeOptimization")
# Misc PVs
detector_polarity = Cpt(EpicsSignalWithRBV, "DetectorPolarity")
decay_time = Cpt(EpicsSignalWithRBV, "DecayTime")
current_pixel = Cpt(EpicsSignalRO, "CurrentPixel")
class XMAPHDF5Plugins(Device):
"""
HDF5 parameters for XMAP detector
Base class to map EPICS PVs from HDF5 Plugin to ophyd signals.
"""
""" ----------------------------------------------------------------------------
capture = Cpt(EpicsSignalWithRBV, "Capture")
enable = Cpt(EpicsSignalWithRBV, "EnableCallbacks", string=True, kind="config")
xml_file_name = Cpt(EpicsSignalWithRBV, "XMLFileName", string=True, kind="config")
lazy_open = Cpt(EpicsSignalWithRBV, "LazyOpen", string=True, doc="0='No' 1='Yes'")
temp_suffix = Cpt(EpicsSignalWithRBV, "TempSuffix", string=True)
file_path = Cpt(EpicsSignalWithRBV, "FilePath", string=True, kind="config")
file_name = Cpt(EpicsSignalWithRBV, "FileName", string=True, kind="config")
file_template = Cpt(EpicsSignalWithRBV, "FileTemplate", string=True, kind="config")
num_capture = Cpt(EpicsSignalWithRBV, "NumCapture", kind="config")
file_write_mode = Cpt(EpicsSignalWithRBV, "FileWriteMode", kind="config")
queue_size = Cpt(EpicsSignalWithRBV, "QueueSize", kind="config")
array_counter = Cpt(EpicsSignalWithRBV, "ArrayCounter", kind="config")
"""
class XMAPSetup(CustomDetectorMixin):
"""
XMAP setup class for phoenix
Parent class: CustomDetectorMixin
"""
def __init__(self, *args, parent: Device = None, **kwargs) -> None:
super().__init__(*args, parent=parent, **kwargs)
self._lock = threading.RLock()
def on_init(self) -> None:
"""Initialize XMAP detector"""
self.initialize_default_parameter()
self.initialize_detector()
self.initialize_detector_backend()
def initialize_default_parameter(self) -> None:
"""
Set default parameters for XMAP
This will set:
- readout (float): readout time in seconds
- value_pixel_per_buffer (int): number of spectra in buffer of XMAP
"""
self.parent.value_pixel_per_buffer = 20
self.update_readout_time()
def update_readout_time(self) -> None:
"""Set readout time for Eiger9M detector"""
readout_time = (
self.parent.scaninfo.readout_time
if hasattr(self.parent.scaninfo, "readout_time")
else self.parent.MIN_READOUT
)
self.parent.readout_time = max(readout_time, self.parent.MIN_READOUT)
def initialize_detector(self) -> None:
"""Initialize XMAP detector"""
self.stop_detector()
self.stop_detector_backend()
self.set_trigger(
mapping_mode=MappingSource.MAPPING, trigger_source=TriggerSource.GATE, ignore_gate=0
)
# 1 Realtime
self.parent.preset_mode.put(1)
# 0 Normal, 1 Inverted
self.parent.input_logic_polarity.put(0)
# 0 Manual 1 Auto
self.parent.auto_pixels_per_buffer.put(0)
# Sets the number of pixels/spectra in the buffer
self.parent.pixels_per_buffer.put(self.parent.value_pixel_per_buffer)
def initialize_detector_backend(self) -> None:
"""Initialize the detector backend for XMAP."""
w=0
#----------------------------------------------------------------------
#self.parent.hdf5.enable.put(1)
# file location of h5 layout for cSAXS
#self.parent.hdf5.xml_file_name.put("layout.xml")
# TODO Check if lazy open is needed and wanted!
#self.parent.hdf5.lazy_open.put(1)
#self.parent.hdf5.temp_suffix.put("")
# size of queue for number of spectra allowed in the buffer, if too small at high throughput, data is lost
#self.parent.hdf5.queue_size.put(2000)
# Segmentation into Spectra within EPICS, 1 is activate, 0 is deactivate
#self.parent.nd_array_mode.put(1)
def on_stage(self) -> None:
"""Prepare detector and backend for acquisition"""
self.prepare_detector()
self.prepare_data_backend()
self.publish_file_location(done=False, successful=False)
self.arm_acquisition()
def prepare_detector(self) -> None:
"""Prepare detector for acquisition"""
self.set_trigger(
mapping_mode=MappingSource.MAPPING, trigger_source=TriggerSource.GATE, ignore_gate=0
)
self.parent.preset_real.put(self.parent.scaninfo.exp_time)
self.parent.pixels_per_run.put(
int(self.parent.scaninfo.num_points * self.parent.scaninfo.frames_per_trigger)
)
def prepare_data_backend(self) -> None:
"""Prepare data backend for acquisition"""
w=9
""" --------------------------------------------------------------
self.parent.filepath.set(
self.parent.filewriter.compile_full_filename(f"{self.parent.name}.h5")
).wait()
file_path, file_name = os.path.split(self.parent.filepath.get())
self.parent.hdf5.file_path.put(file_path)
self.parent.hdf5.file_name.put(file_name)
self.parent.hdf5.file_template.put("%s%s")
self.parent.hdf5.num_capture.put(
int(self.parent.scaninfo.num_points * self.parent.scaninfo.frames_per_trigger)
)
self.parent.hdf5.file_write_mode.put(2)
# Reset spectrum counter in filewriter, used for indexing & identifying missing triggers
self.parent.hdf5.array_counter.put(0)
# Start file writing
self.parent.hdf5.capture.put(1)
"""
def arm_acquisition(self) -> None:
"""Arm detector for acquisition"""
self.parent.start_all.put(1)
signal_conditions = [
(
lambda: self.parent.state.read()[self.parent.state.name]["value"],
DetectorState.ACQUIRING,
)
]
if not self.wait_for_signals(
signal_conditions=signal_conditions,
timeout=self.parent.TIMEOUT_FOR_SIGNALS,
check_stopped=True,
all_signals=False,
):
raise XMAPTimeoutError(
f"Failed to arm the acquisition. Detector state {signal_conditions[0][0]}"
)
def on_unstage(self) -> None:
"""Unstage detector and backend"""
pass
def on_complete(self) -> None:
"""Complete detector and backend"""
#------------------------------------------------------------------
#self.finished(timeout=self.parent.TIMEOUT_FOR_SIGNALS)
#self.publish_file_location(done=True, successful=True)
w=9
def on_stop(self) -> None:
"""Stop detector and backend"""
self.stop_detector()
#self.stop_detector_backend()
def stop_detector(self) -> None:
"""Stops detector"""
self.parent.stop_all.put(1)
self.parent.erase_all.put(1)
#-------------------------------------------------------------------
#signal_conditions = [
# (lambda: self.parent.state.read()[self.parent.state.name]["value"], DetectorState.DONE)
#]stage2 = StageXY(prefix='X07MB',name='-ES-MA1', name='stage2')
# timeout=self.parent.TIMEOUT_FOR_SIGNALS - self.parent.TIMEOUT_FOR_SIGNALS // 2,
# all_signals=False,
#):
# # Retry stop detector and wait for remaining time
# raise XMAPTimeoutError(
# f"Failed to stop detector, timeout with state {signal_conditions[0][0]}"
# )
def stop_detector_backend(self) -> None:
"""Stop the detector backend"""
#self.parent.hdf5.capture.put(0)
w=0
def finished(self, timeout: int = 5) -> None:
"""Check if scan finished succesfully"""
with self._lock:
total_frames = int(
self.parent.scaninfo.num_points * self.parent.scaninfo.frames_per_trigger
)
signal_conditions = [
(self.parent.dxp.current_pixel.get, total_frames),
# (self.parent.hdf5.array_counter.get, total_frames), ---------------------
]
if not self.wait_for_signals(
signal_conditions=signal_conditions,
timeout=timeout,
check_stopped=True,
all_signals=True,
):
logger.debug(
f"XMAP missed a trigger: received trigger {self.parent.dxp.current_pixel.get()},"
f" send data {self.parent.hdf5.array_counter.get()} from total_frames"
f" {total_frames}"
)
self.stop_detector()
self.stop_detector_backend()
def set_trigger(
self, mapping_mode: MappingSource, trigger_source: TriggerSource, ignore_gate: int = 0
) -> None:
"""
Set triggering mode for detector
Args:
mapping_mode (MappingSource): Mapping mode for the detector
trigger_source (TriggerSource): Trigger source for the detector, pixel_advance_signal
ignore_gate (int): Ignore gate from TTL signal; defaults to 0
"""
mapping = int(mapping_mode)
trigger = trigger_source
self.parent.collect_mode.put(mapping)
self.parent.pixel_advance_mode.put(trigger)
self.parent.ignore_gate.put(ignore_gate)
class XMAPphoenix(PSIDetectorBase):
"""MCA
XMAP detector for phoenix
Parent class: PSIDetectorBase
class attributes:
custom_prepare_cls (XMAPSetup) : Custom detector setup class for cSAXS,
inherits from CustomDetectorMixin
in __init__ of PSIDetecor base
PSIDetectorBase.set_min_readout (float) : Minimum readout time for the detector
dxp (EpicsDXPXMAP) : DXP parameters for XMAP detector
mca (EpicsMCARecord) : MCA parameters for XMAP detector
hdf5 (XMAPHDF5Plugins) : HDF5 parameters for XMAP detector
MIN_READOUT (float) : Minimum readout time for the detector
"""
# Specify which functions are revealed to the user in BEC client
USER_ACCESS = ["describe"]
# specify Setup class
custom_prepare_cls = XMAPSetup
# specify minimum readout time for detector
MIN_READOUT = 3e-3
TIMEOUT_FOR_SIGNALS = 5
# specify class attributes
dxp = Cpt(EpicsDXPXMAP, "dxp1:")
mca1 = Cpt(EpicsMCARecord, "mca1")
mca2 = Cpt(EpicsMCARecord, "mca2")
mca3 = Cpt(EpicsMCARecord, "mca3")
mca4 = Cpt(EpicsMCARecord, "mca4")
print('load hdf5')
#hdf5 = Cpt(XMAPHDF5Plugins, "HDF1:")
stop_all = Cpt(EpicsSignal, "StopAll")
erase_all = Cpt(EpicsSignal, "EraseAll")
start_all = Cpt(EpicsSignal, "StartAll")
state = Cpt(EpicsSignal, "Acquiring")
preset_mode = Cpt(EpicsSignal, "PresetMode") # 0 No preset 1 Real time 2 Events 3 Triggers
preset_real = Cpt(EpicsSignal, "PresetReal")
preset_events = Cpt(EpicsSignal, "PresetEvents")
preset_triggers = Cpt(EpicsSignal, "PresetTriggers")
#triggers = Cpt(EpicsSignalRO, "MaxTriggers", lazy=True) #=========== falcon only
# events = Cpt(EpicsSignalRO, "MaxEvents", lazy=True) #=========== falcon only
#input_count_rate = Cpt(EpicsSignalRO, "MaxInputCountRate", 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
pixel_advance_mode = Cpt(EpicsSignal, "PixelAdvanceMode")
ignore_gate = Cpt(EpicsSignal, "IgnoreGate")
input_logic_polarity = Cpt(EpicsSignal, "InputLogicPolarity")
auto_pixels_per_buffer = Cpt(EpicsSignal, "AutoPixelsPerBuffer")
pixels_per_buffer = Cpt(EpicsSignal, "PixelsPerBuffer")
pixels_per_run = Cpt(EpicsSignal, "PixelsPerRun")
#nd_array_mode = Cpt(EpicsSignal, "NDArrayMode")
print('DONE connecton chanels in XMAPphoenix')

View File

@ -0,0 +1,9 @@
from phoenix_bec.devices.xmap_phoenix_no_hdf5 import XMAPphoenix
from phoenix_bec.scripts.phoenix import PhoenixBL
phoenix=PhoenixBL()
phoenix.read_phoenix_config()
#
# how do we get this to iphython command line ?
#

View File

@ -0,0 +1,422 @@
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
# CONTAINS
# DetectorInitErroR(Exception)
# CustomDetectorMixing
# PSIDetectorBase(Device)
#
#
#########################################################################################
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
PSI detectors into the BEC system based on this base class. The base class is used to implement
certain methods that are expected by BEC, such as stage, unstage, trigger, stop, etc...
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."""
file psi_detector_base.py
import os
class DetectorInitError(Exception):
"""Raised when initiation of the device class fails,
due to missing device manager or not started in sim_mode."""
class CustomDetectorMixin:
"""
Mixin class for custom detector logic
This class is used to implement BL specific logic for the detector.
It is used in the PSIDetectorBase class.
For the integration of a new detector, the following functions should
help with integrating functionality, but additional ones can be added.
Check PSIDetectorBase for the functions that are called during relevant function calls of
stage, unstage, trigger, stop and _init.
"""
def __init__(self, *_args, parent: Device = None, **_kwargs) -> None:
self.parent = parent
def on_init(self) -> None:
"""
Init sequence for the detector
"""
_base.py
ready has all current parameters for the upcoming scan.
In case the backend service is writing data on disk, this step should include publishing
a file_event and file_message to BEC to inform the system where the data is written to.
IMPORTANT:
It must be safe to assume that the device is ready for the scan
to start immediately once this function is finished.
"""
def on_unstage(self) -> None:
"""
Specify actions to be executed during unstage.
This step should include checking if the acqusition was successful,
and publishing the file location and file event message,
with flagged done to BEC.
"""
def on_stop(self) -> None:
"""
Specify actions to be executed during stop.
This must also set self.parent.stopped to True.
This step should include stopping the detector and backend service.
"""
def on_trigger(self) -> None | DeviceStatus:
"""
Specify actions to be executed upon receiving trigger signal.
Return a DeviceStatus object or None
"""
def on_pre_scan(self) -> None:
"""
Specify actions to be executed right before a scan starts.
Only use if needed, and it is recommended to keep this function as short/fast as possible.
"""
def on_complete(self) -> None | DeviceStatus:
"""
Specify actions to be executed when the scan is complete.
This can for instance be to check with the detector and backend if all data is written succsessfully.
"""
def publish_file_location(self, done: bool, successful: bool, metadata: dict = None) -> None:
"""
Publish the filepath to REDIS.
We publish two events here:
- file_event: event for the filewriter
- public_file: event for any secondary service (e.g. radial integ code)
Args:
done (bool): True if scan is finished
successful (bool): True if scan was successful
metadata (dict): additional metadata to publish
"""
if metadata is None:
metadata = {}
msg = messages.FileMessage(
file_path=self.parent.filepath.get(),
done=done,
successful=successful,
metadata=metadata,
)
pipe = self.parent.connector.pipeline()
self.parent.connector.set_and_publish(
MessageEndpoints.public_file(self.parent.scaninfo.scan_id, self.parent.name),
msg,
pipe=pipe,
)
self.parent.connector.set_and_publish(
MessageEndpoints.file_event(self.parent.name), msg, pipe=pipe
)
pipe.execute()
def wait_for_signals(
self,
signal_conditions: list[tuple],
timeout: float,
check_stopped: bool = False,
interval: float = 0.05,
all_signals: bool = False,
) -> bool:
"""
Convenience wrapper to allow waiting for signals to reach a certain condition.
For EPICs PVs, an example usage is pasted at the bottom.
Args:
signal_conditions (list[tuple]): tuple of executable calls for conditions (get_current_state, condition) to check
timeout (float): timeout in seconds
interval (float): interval in seconds
all_signals (bool): True if all signals should be True, False if any signal should be True
Returns:
bool: True if all signals are in the desired state, False if timeout is reached
>>> Example usage for EPICS PVs:
>>> self.wait_for_signals(signal_conditions=[(self.acquiring.get, False)], timeout=5, interval=0.05, check_stopped=True, all_signals=True)
"""
timer = 0
while True:
checks = [
get_current_state() == condition
for get_current_state, condition in signal_conditions
]
if check_stopped is True and self.parent.stopped is True:
return False
if (all_signals and all(checks)) or (not all_signals and any(checks)):
return True
if timer > timeout:
return False
time.sleep(interval)
timer += interval
def wait_with_status(
self,
signal_conditions: list[tuple],
timeout: float,
check_stopped: bool = False,
interval: float = 0.05,
all_signals: bool = False,
exception_on_timeout: Exception = TimeoutError("Timeout while waiting for signals"),
) -> DeviceStatus:
"""Utility function to wait for signals in a thread.
Returns a DevicesStatus object that resolves either to set_finished or set_exception.
The DeviceStatus is attached to the parent device, i.e. the detector object inheriting from PSIDetectorBase.
Usage:
This function should be used to wait for signals to reach a certain condition, especially in the context of
on_trigger and on_complete. If it is not used, functions may block and slow down the performance of BEC.
It will return a DeviceStatus object that is to be returned from the function. Once the conditions are met,
the DeviceStatus will be set to set_finished in case of success or set_exception in case of a timeout or exception.
The exception can be specified with the exception_on_timeout argument. The default exception is a TimeoutError.
Args:
signal_conditions (list[tuple]): tuple of executable calls for conditions (get_current_state, condition) to check
timeout (float): timeout in seconds
check_stopped (bool): True if stopped flag should be checked
interval (float): interval in seconds
all_signals (bool): True if all signals should be True, False if any signal should be True
exception_on_timeout (Exception): Exception to raise on timeout
Returns:
DeviceStatus: DeviceStatus object that resolves either to set_finished or set_exception
"""
status = DeviceStatus(self.parent)
# utility function to wrap the wait_for_signals function
def wait_for_signals_wrapper(
status: DeviceStatus,
signal_conditions: list[tuple],
timeout: float,
check_stopped: bool,
interval: float,
all_signals: bool,
exception_on_timeout: Exception = TimeoutError("Timeout while waiting for signals"),
):
"""Convenient wrapper around wait_for_signals to set status based on the result.
Args:
status (DeviceStatus): DeviceStatus object to be set
signal_conditions (list[tuple]): tuple of executable calls for conditions (get_current_state, condition) to check
timeout (float): timeout in seconds
check_stopped (bool): True if stopped flag should be checked
interval (float): interval in seconds
all_signals (bool): True if all signals should be True, False if any signal should be True
exception_on_timeout (Exception): Exception to raise on timeout
"""
try:
result = self.wait_for_signals(
signal_conditions, timeout, check_stopped, interval, all_signals
)
if result:
status.set_finished()
else:
status.set_exception(exception_on_timeout)
except Exception as exc:
status.set_exception(exc=exc)
thread = threading.Thread(
target=wait_for_signals_wrapper,
args=(
status,
signal_conditions,
timeout,
check_stopped,
interval,
all_signals,
exception_on_timeout,
),
daemon=True,
)
thread.start()
return status
class PSIDetectorBase(Device):
"""
Abstract base class for SLS detectors
Class attributes:
custom_prepare_cls (object): class for custom prepare logic (BL specific)
Args:
prefix (str): EPICS PV prefix for component (optional)
name (str): name of the device, as will be reported via read()
kind (str): member of class 'ophydobj.Kind', defaults to Kind.normal
omitted -> readout ignored for read 'ophydobj.read()'
normal -> readout for read
config -> config parameter for 'ophydobj.read_configuration()'
hinted -> which attribute is readout for read
parent (object): instance of the parent device
device_manager (object): bec device manager
**kwargs: keyword arguments
"""
filepath = Component(SetableSignal, value="", kind=Kind.config)
custom_prepare_cls = CustomDetectorMixin
def __init__(self, prefix="", *, name, kind=None, parent=None, device_manager=None, **kwargs):
super().__init__(prefix=prefix, name=name, kind=kind, parent=parent, **kwargs)
self.stopped = False
self.name = name
self.service_cfg = None
self.scaninfo = None
self.filewriter = None
self._update_filewriter()
if not issubclass(self.custom_prepare_cls, CustomDetectorMixin):
raise DetectorInitError("Custom prepare class must be subclass of CustomDetectorMixin")
self.custom_prepare = self.custom_prepare_cls(parent=self, **kwargs)
if device_manager:
self._update_service_config()
self.device_manager = device_manager
else:
self.device_manager = bec_utils.DMMock()
base_path = kwargs["basepath"] if "basepath" in kwargs else "."
self.service_cfg = {"base_path": os.path.abspath(base_path)}
self.connector = self.device_manager.connector
self._update_scaninfo()
self._update_filewriter()
self._init()
def _update_filewriter(self) -> None:
"""Update filewriter with service config"""
self.filewriter = FileWriter(service_config=self.service_cfg, connector=self.connector)
def _update_scaninfo(self) -> None:
"""Update scaninfo from BecScaninfoMixing
This depends on device manager and operation/sim_mode
"""
self.scaninfo = BecScaninfoMixin(self.device_manager)
self.scaninfo.load_scan_metadata()
def _update_service_config(self) -> None:
"""Update service config from BEC service config
If bec services are not running and SERVICE_CONFIG is NONE, we fall back to the current directory.
"""
# pylint: disable=import-outside-toplevel
from bec_lib.bec_service import SERVICE_CONFIG
if SERVICE_CONFIG:
self.service_cfg = SERVICE_CONFIG.config["service_config"]["file_writer"]
return
self.service_cfg = {"base_path": os.path.abspath(".")}
def check_scan_id(self) -> None:
"""Checks if scan_id has changed and set stopped flagged to True if it has."""
old_scan_id = self.scaninfo.scan_id
self.scaninfo.load_scan_metadata()
if self.scaninfo.scan_id != old_scan_id:
self.stopped = True
def _init(self) -> None:
"""Initialize detector, filewriter and set default parameters"""
self.custom_prepare.on_init()
def stage(self) -> list[object]:
"""
Stage device in preparation for a scan.
First we check if the device is already staged. Stage is idempotent,
if staged twice it should raise (we let ophyd.Device handle the raise here).
We reset the stopped flag and get the scaninfo from BEC, before calling custom_prepare.on_stage.
Returns:
list(object): list of objects that were staged
"""
if self._staged != Staged.no:
return super().stage()
self.stopped = False
self.scaninfo.load_scan_metadata()
self.custom_prepare.on_stage()
return super().stage()
def pre_scan(self) -> None:
"""Pre-scan logic.
This function will be called from BEC directly before the scan core starts, and should only implement
time-critical actions. Therefore, it should also be kept as short/fast as possible.
I.e. Arming a detector in case there is a risk of timing out.
"""
self.custom_prepare.on_pre_scan()
def trigger(self) -> DeviceStatus:
"""Trigger the detector, called from BEC."""
# pylint: disable=assignment-from-no-return
status = self.custom_prepare.on_trigger()
if isinstance(status, DeviceStatus):
return status
return super().trigger()
def complete(self) -> None:
"""Complete the acquisition, called from BEC.
This function is called after the scan is complete, just before unstage.
We can check here with the data backend and detector if the acquisition successfully finished.
Actions are implemented in custom_prepare.on_complete since they are beamline specific.
"""
# pylint: disable=assignment-from-no-return
status = self.custom_prepare.on_complete()
if isinstance(status, DeviceStatus):
return status
status = DeviceStatus(self)
status.set_finished()
return status
def unstage(self) -> list[object]:
"""
Unstage device after a scan.
We first check if the scanID has changed, thus, the scan was unexpectedly interrupted but the device was not stopped.
If that is the case, the stopped flag is set to True, which will immediately unstage the device.
Custom_prepare.on_unstage is called to allow for BL specific logic to be executed.
Returns:
list(object): list of objects that were unstaged
"""
self.check_scan_id()
if self.stopped is True:
return super().unstage()
self.custom_prepare.on_unstage()
self.stopped = False
return super().unstage()
def stop(self, *, success=False) -> None:
"""
Stop the scan, with camera and file writer
"""
self.custom_prepare.on_stop()
super().stop(success=success)
self.stopped = True

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,17 @@
print('#######################################')
print('bec.show_global_vars()')
bec.show_global_vars()
print('#######################################')
print('bec.show_all_commands()')
bec.show_all_commands()
print('#######################################')
print('bec.list_user_scripts()')
bec.list_user_scripts()

View File

@ -0,0 +1,2 @@
print('Work with unix shell comands :')
print('use %ls %mkdir etc ')

View File

@ -0,0 +1,95 @@
from ophyd import Device, EpicsMotor, EpicsSignal, EpicsSignalRO
from ophyd import Component as Cpt
#option I via direct acces to classes
def print_dic(clname,cl):
print('')
print('-------- ',clname)
for ii in cl.__dict__:
if '_' not in ii:
try:
print(ii,' ---- ',cl.__getattribute__(ii))
except:
print(ii)
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')
#prefix='XXXX:'
y_cpt = Cpt(EpicsMotor, 'ScanX')
# Option 2 using component
device_ins=Device('X07MB-ES-MA1:',name=('device_name'))
print(' initialzation of device_in=Device(X07MB-ES-MA1:,name=(device_name)')
print('device_ins.__init__')
print(device_ins.__init__)
print_dic('class Device',Device)
print_dic('instance of device device_ins',device_ins)
print(' ')
print('DEFINE class StageXY... prefix variable not defined ')
class StageXY(Device):
print('in StageXY')
try:
print('prefix',prefix)
except:
print('prefix not defined')
x = Cpt(EpicsMotor, 'ScanX')
y = Cpt(EpicsMotor, 'ScanY')
class StageXY2(Device):
#def __init__(self,prefix,name=''):
# print('in StageXY')
# print(prefix)
##print('prefix',prefix)
x = Cpt(EpicsMotor, 'ScanX')
y = Cpt(EpicsMotor, 'ScanY')
print()
print('init xy_stage, use input parameter from Device and prefix is defined')
xy_stage = StageXY('X07MB-ES-MA1:', name='stage')
print_dic('class StageXY',StageXY)
print_dic('instance of StageXY',xy_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,
# ):
#
#########################################################
print('xy_stage.x.prefix')
print(xy_stage.x.prefix)
xy_stage.__dict__
# to move motor use
# stage.x.move(0)
# to see all dict
# stage.x.__dict__

View File

@ -0,0 +1,25 @@
import epics as ep
################
# Testing base class epics
# The raw code is located in the file
# bec_client_venv/lib64/python3.11/site-packages/epics/__init__.py
# in bec start script by run -i
# option -i ensures taht iphyjon shell and scritp are in same namespace
# run -i BaseClass_Epics.py
pvname='X07MB-OP2-SAI_07:INP-OFS'
print('ep.cainfo(pvname)')
ep.cainfo(pvname)
print('caget(pvname)')
ep.caput(pvname,0.5,wait=.1)
res=ep.caget(pvname)
print('1:',res)
ep.caput(pvname,0.01,wait=.1)
res=ep.caget(pvname)
print('2',res)
print('Start camon to see effect , change value of pv ')
ep.camonitor(pvname)

View File

@ -0,0 +1,86 @@
#from unittest import mock
import numpy as np
#import pandas
#import pytest
#from bec_lib import messages
#import device_server
#from ophyd import Component as Cpt
from ophyd import Device, EpicsMotor, EpicsSignal, EpicsSignalRO
#from ophyd import FormattedComponent as FCpt
#from ophyd import Kind, PVPositioner, Signal
#from ophyd.flyers import FlyerInterface
#from ophyd.pv_positioner import PVPositionerComparator
#from ophyd.status import DeviceStatus, SubscriptionStatus
import time as tt
#import ophyd
import os
import sys
#logger = bec_logger.logger
# load simulation
#bec.config.load_demo_config()
bec.config.update_session_with_file("config/config_1.yaml")
os.system('mv *.yaml tmp')
class PhoenixBL:
#define some epics channels
def __init__(self):
from ophyd import Device, EpicsMotor, EpicsSignal, EpicsSignalRO
from ophyd import Component as Cpt
self.ScanX = EpicsMotor(name='ScanX',prefix='X07MB-ES-MA1:ScanX')
self.ScanY = EpicsMotor(name='ScanY',prefix='X07MB-ES-MA1:ScanY')
self.DIODE = EpicsSignal(name='SI',read_pv='X07MB-OP2-SAI_07:MEAN')
self.SIG = Cpt(EpicsSignal,name='we',read_pv="X07MB-OP2-SAI_07:MEAN")
self.SMPL = EpicsSignal(name='SMPL',read_pv='X07MB-OP2:SMPL')
self.CYCLES = EpicsSignal(name='SMPL',read_pv='X07MB-OP2:TOTAL-CYCLES',write_pv='X07MB-OP2:TOTAL-CYCLES')
self.fielda =EpicsSignal(name='SMPL',read_pv='X07MB-SCAN:scan1.P1SP',write_pv='X07MB-SCAN:scan1.P1SP')
#end class
ph=PhoenixBL()
print('---------------------------------')
# scan will not diode
print(' SCAN DO NOT READ DIODE ')
dev.PH_curr_conf.readout_priority='baseline' # do not read detector
ti=tt.time_ns()
s1=scans.line_scan(dev.PH_ScanX_conf,0,0.002,steps=4,exp_time=.01,relative=False,delay=2)
tf=tt.time_ns()
print('elapsed time',(tf-ti)/1e9)
# scan will read diode
print(' SCAN READ DIODE ')
tt.sleep(2)
dev.PH_curr_conf.readout_priority='monitored' # read detector
s2=scans.line_scan(dev.PH_ScanX_conf,0,0.002,steps=11,exp_time=.3,relative=False,delay=2)
"""
next lines do not work as pandas is not installed on test system
res1 = s1.scan.to_pandas()
re1 = res1.to_numpy()
print('Scana')
print(res1)
print('')
print('Scan2 at pandas ')
print(res2)
print('Scan2 as numpy ')
print(res2)
"""

View File

@ -0,0 +1,75 @@
"""
Scritpt to be developed as template for phoenic scritps
"""
#from unittest import mock
import numpy as np
#import pandas
#import pytest
#from bec_lib import messages
#import device_server
#from ophyd importPhoenixTemplate.pyitioner import PVPositionerComparator
#from ophyd.status import DeviceStatus, SubscriptionStatus
import time
#import ophyd
import os
import sys
import importlib
import ophyd
#logger = bec_logger.logger
# load local configuration
#bec.config.load_demo_config()
# .. define base path for directory with scripts
PhoenixBL=0
from ConfigPHOENIX.config.phoenix import PhoenixBL
#from ConfigPHOENIX.devices.falcon_csaxs import FalconSetup
# initialize general parameter
ph=PhoenixBL()
bec.config.update_session_with_file('./ConfigPHOENIX/device_config/phoenix_devices.yaml')
time.sleep(1)
s1=scans.line_scan(dev.ScanX,0,0.002,steps=4,exp_time=1,relative=False,delay=2)
s2=scans.phoenix_line_scan(dev.ScanX,0,0.002,steps=4,exp_time=.01,relative=False,delay=2)
"""
print('---------------------------------')
# scan will not diode
print(' SCAN DO NOT READ DIODE ')
dev.PH_curr_conf.readout_priority='baseline' # do not read detector
ti=tt.time_ns()
s1=scans.line_scan(dev.PH_ScanX_conf,0,0.002,steps=4,exp_time=.01,relative=False,delay=2)
tf=tt.time_ns()
print('elapsed time',(tf-ti)/1e9)
# scan will read diode
print(' SCAN READ DIODE ')s is not installed on test system
ScanX_conf,0,0.002,steps=11,exp_time=.3,relative=False,delay=2)
"""
"""
next lines do not work as pandas is not installed on test system
res1 = s1.scan.to_pandas()
re1 = res1.to_numpy()
print('Scana')
print(res1)
print('')
print('Scan2 at pandas ')
print(res2)
print('Scan2 as numpy ')
print(res2)
"""

View File

@ -0,0 +1,8 @@
This diretory is for scripts, test etc. which are not loaded into the server.
Hence no directory should contain a file named
__init__.py
For now we keep it in the phoenix_bec structure, but for operation, such files should be located out side of the
bec_phoenix plugin.

View File

@ -0,0 +1 @@
from .phoenix import PhoenixBL

View File

@ -0,0 +1,79 @@
#from unittest import mock
import numpy as np
#import pandas
#import pytest
#from bec_lib import messages
#import device_server
#from ophyd import Component as Cpt
from ophyd import Device, EpicsMotor, EpicsSignal, EpicsSignalRO
#from ophyd import FormattedComponent as FCpt
#from ophyd import Kind, PVPositioner, Signal
#from ophyd.flyers import FlyerInterface
#from ophyd.pv_positioner import PVPositionerComparator
#from ophyd.status import DeviceStatus, SubscriptionStatus
from bec_lib.logger import bec_logger
logger = bec_logger.logger
import time as tt
#import ophyd
import os
import sys
#logger = bec_logger.logger
# load simulation
#bec.config.load_demo_config()
# .. define base path for directory with scripts
class PhoenixBL():
"""
General class for PHOENIX beamline
"""
#define some epics channels
#scan_name = "phoenix_base"
def __init__(self):
"""
init PhoenixBL() in ConfigPHOENIX.config.phoenix
"""
import os
#from ophyd import Device, EpicsMotor, EpicsSignal, EpicsSignalRO
#from ophyd import Component as Cpt
#self.ScanX = EpicsMotor(name='ScanX',prefix='X07MB-ES-MA1:ScanX')
#self.ScanY = EpicsMotor(name='ScanY',prefix='X07MB-ES-MA1:ScanY')
#self.DIODE = EpicsSignal(name='SI',read_pv='X07MB-OP2-SAI_07:MEAN')
#self.SIG = Cpt(EpicsSignal,name='we',read_pv="X07MB-OP2-SAI_07:MEAN")
#self.SMPL = EpicsSignal(name='SMPL',read_pv='X07MB-OP2:SMPL')
#self.CYCLES = EpicsSignal(name='SMPL',read_pv='X07MB-OP2:TOTAL-CYCLES',write_pv='X07MB-OP2:TOTAL-CYCLES')
# load local configuration
print('init PhoenixBL')
self.path_scripts_local = '/data/test/x07mb-test-bec/bec_deployment/LocalScripts/'
self.path_config_local = self.path_scripts_local + 'ConfigPHOENIX/' # base dir for local configurations
self.path_devices_local = self.path_config_local + 'device_config/' # local yamal file
self.file_device_conf = self.path_devices_local + 'phoenix_devices.yaml'
#bec.config.update_session_with_file(self.file_device_conf)
# last command created yaml backup, for now just move it away
#os.system('mv *.yaml '+Devices_local+'/recovery_configs')
#os.system('mv *.yaml tmp')
def read_def_config():
bec.config.update_session_with_file(self.file_device_conf)
def print_setup(self):
"""
docstring print_setup
"""
print(self.path_scripts_local)

View File

@ -0,0 +1,24 @@
PH_ScanX_conf:
readoutPriority: baseline
description: 'Horizontal sample position'
deviceClass: ophyd.EpicsMotor
deviceConfig:
prefix: 'X07MB-ES-MA1:ScanX'
onFailure: retry
enabled: true
readOnly: false
softwareTrigger: false
PH_curr_conf:
readoutPriority: monitored
description: DIODE
deviceClass: ophyd.EpicsSignalRO
deviceConfig:
auto_monitor: true
read_pv: 'X07MB-OP2-SAI_07:MEAN'
deviceTags:
- PHOENIX
onFailure: buffer
enabled: true
readOnly: true
softwareTrigger: false

View File

@ -0,0 +1,13 @@
Falcon:
readoutPriority: baseline
description: 'Falcon'
deviceClass: .ConfigPHOENIX.devices.falcon_phoenix_no_hdf5
deviceConfig:
prefix: 'X07MB-ES-MA1:ScanX'
onFailure: retry
enabled: true
readOnly: false
softwareTrigger: false

View File

@ -0,0 +1,64 @@
falcon:
description: Falcon detector x-ray fluoresence
deviceClass: phoenix_bec.devices._csaxs.FalconcSAXS
deviceConfig:
prefix: 'X07MB-SITORO:'
deviceTags:
- cSAXS
- falcon
onFailure: buffer
enabled: true
readoutPriority: async
softwareTrigger: false
# MOTORS ES1
#
ScanX:
readoutPriority: baseline
description: 'Horizontal sample position'
deviceClass: ophyd.EpicsMotor
deviceConfig:
prefix: 'X07MB-ES-MA1:ScanX'
onFailure: retry
enabled: true
readOnly: false
softwareTrigger: false
ScanY:
readoutPriority: baseline
description: 'Horizontal sample position'
deviceClass: ophyd.EpicsMotor
deviceConfig:
prefix: 'X07MB-ES-MA1:ScanY'
onFailure: retry
enabled: true
readOnly: false
softwareTrigger: false
#
#
# DIODES from ES1 ADC
#
#
#SAI_07_MEAN:
# readoutPriority: monitored
# description: DIODE
# deviceClass: ophyd.EpicsSignalRO
# deviceConfig:
# auto_monitor: true
# read_pv: 'X07MB-OP2-SAI_07:MEAN'
# onFailure: buffer
# enabled: true
# readOnly: true
# softwareTrigger: false
#SAI_08_MEAN:
# readoutPriority: monitored
# description: DIODE
# deviceClass: ophyd.EpicsSignalRO
# deviceConfig:
# auto_monitor: true
# read_pv: 'X07MB-OP2-SAI_08:MEAN'
# onFailure: buffer
# enabled: true
# readOnly: true
# softwareTrigger: false

View File

@ -0,0 +1,57 @@
#
# MOTORS ES1
#
ScanX:
readoutPriority: baseline
description: 'Horizontal sample position'
deviceClass: ophyd.EpicsMotor
deviceConfig:
prefix: 'X07MB-ES-MA1:ScanX'
onFailure: retry
enabled: true
readOnly: false
softwareTrigger: false
ScanY:
readoutPriority: baseline
description: 'Horizontal sample position'
deviceClass: ophyd.EpicsMotor
deviceConfig:
prefix: 'X07MB-ES-MA1:ScanY'
onFailure: retry
enabled: true
readOnly: false
softwareTrigger: false
#
#
# DIODES from ES1 ADC
#
#
SAI_07_MEAN:
readoutPriority: monitored
description: DIODE
deviceClass: ophyd.EpicsSignalRO
deviceConfig:
auto_monitor: true
read_pv: 'X07MB-OP2-SAI_07:MEAN'
deviceTags:
- PHOENIX
onFailure: buffer
enabled: true
readOnly: true
softwareTrigger: false
SAI_08_MEAN:
readoutPriority: monitored
description: DIODE
deviceClass: ophyd.EpicsSignalRO
deviceConfig:
auto_monitor: true
read_pv: 'X07MB-OP2-SAI_08:MEAN'
deviceTags:
- PHOENIX
onFailure: buffer
enabled: true
readOnly: true
softwareTrigger: false

View File

@ -0,0 +1,345 @@
from bec_lib import bec_logger
from ophyd import Component
from ophyd_devices.interfaces.base_classes.psi_delay_generator_base import (
DDGCustomMixin,
PSIDelayGeneratorBase,
TriggerSource,
)
from ophyd_devices.utils import bec_utils
logger = bec_logger.logger
class DelayGeneratorError(Exception):
"""Exception raised for errors."""
class DDGSetup(DDGCustomMixin):
"""
Mixin class for DelayGenerator logic at cSAXS.
At cSAXS, multiple DDGs were operated at the same time. There different behaviour is
implemented in the ddg_config signals that are passed via the device config.
"""
def initialize_default_parameter(self) -> None:
"""Method to initialize default parameters."""
for ii, channel in enumerate(self.parent.all_channels):
self.parent.set_channels("polarity", self.parent.polarity.get()[ii], [channel])
self.parent.set_channels("amplitude", self.parent.amplitude.get())
self.parent.set_channels("offset", self.parent.offset.get())
# Setup reference
self.parent.set_channels(
"reference", 0, [f"channel{pair}.ch1" for pair in self.parent.all_delay_pairs]
)
self.parent.set_channels(
"reference", 0, [f"channel{pair}.ch2" for pair in self.parent.all_delay_pairs]
)
self.parent.set_trigger(getattr(TriggerSource, self.parent.set_trigger_source.get()))
# Set threshold level for ext. pulses
self.parent.level.put(self.parent.thres_trig_level.get())
def prepare_ddg(self) -> None:
"""
Method to prepare scan logic of cSAXS
Two scantypes are supported: "step" and "fly":
- step: Scan is performed by stepping the motor and acquiring data at each step
- fly: Scan is performed by moving the motor with a constant velocity and acquiring data
Custom logic for different DDG behaviour during scans.
- set_high_on_exposure : If True, then TTL signal is high during
the full exposure time of the scan (all frames).
E.g. Keep shutter open for the full scan.
- fixed_ttl_width : fixed_ttl_width is a list of 5 values, one for each channel.
If the value is 0, then the width of the TTL pulse is determined,
no matter which parameters are passed from the scaninfo for exposure time
- set_trigger_source : Specifies the default trigger source for the DDG. For cSAXS, relevant ones
were: SINGLE_SHOT, EXT_RISING_EDGE
"""
self.parent.set_trigger(getattr(TriggerSource, self.parent.set_trigger_source.get()))
# scantype "step"
if self.parent.scaninfo.scan_type == "step":
# High on exposure means that the signal
if self.parent.set_high_on_exposure.get():
# caluculate parameters
num_burst_cycle = 1 + self.parent.additional_triggers.get()
exp_time = (
self.parent.delta_width.get()
+ self.parent.scaninfo.frames_per_trigger
* (self.parent.scaninfo.exp_time + self.parent.scaninfo.readout_time)
)
total_exposure = exp_time
delay_burst = self.parent.delay_burst.get()
# Set individual channel widths, if fixed_ttl_width and trigger_width are combined, this can be a common call too
if not self.parent.trigger_width.get():
self.parent.set_channels("width", exp_time)
else:
self.parent.set_channels("width", self.parent.trigger_width.get())
for value, channel in zip(
self.parent.fixed_ttl_width.get(), self.parent.all_channels
):
logger.debug(f"Trying to set DDG {channel} to {value}")
if value != 0:
self.parent.set_channels("width", value, channels=[channel])
else:
# caluculate parameters
exp_time = self.parent.delta_width.get() + self.parent.scaninfo.exp_time
total_exposure = exp_time + self.parent.scaninfo.readout_time
delay_burst = self.parent.delay_burst.get()
num_burst_cycle = (
self.parent.scaninfo.frames_per_trigger + self.parent.additional_triggers.get()
)
# Set individual channel widths, if fixed_ttl_width and trigger_width are combined, this can be a common call too
if not self.parent.trigger_width.get():
self.parent.set_channels("width", exp_time)
else:
self.parent.set_channels("width", self.parent.trigger_width.get())
# scantype "fly"
elif self.parent.scaninfo.scan_type == "fly":
if self.parent.set_high_on_exposure.get():
# caluculate parameters
exp_time = (
self.parent.delta_width.get()
+ self.parent.scaninfo.exp_time * self.parent.scaninfo.num_points
+ self.parent.scaninfo.readout_time * (self.parent.scaninfo.num_points - 1)
)
total_exposure = exp_time
delay_burst = self.parent.delay_burst.get()
num_burst_cycle = 1 + self.parent.additional_triggers.get()
# Set individual channel widths, if fixed_ttl_width and trigger_width are combined, this can be a common call too
if not self.parent.trigger_width.get():
self.parent.set_channels("width", exp_time)
else:
self.parent.set_channels("width", self.parent.trigger_width.get())
for value, channel in zip(
self.parent.fixed_ttl_width.get(), self.parent.all_channels
):
logger.debug(f"Trying to set DDG {channel} to {value}")
if value != 0:
self.parent.set_channels("width", value, channels=[channel])
else:
# caluculate parameters
exp_time = self.parent.delta_width.get() + self.parent.scaninfo.exp_time
total_exposure = exp_time + self.parent.scaninfo.readout_time
delay_burst = self.parent.delay_burst.get()
num_burst_cycle = (
self.parent.scaninfo.num_points + self.parent.additional_triggers.get()
)
# Set individual channel widths, if fixed_ttl_width and trigger_width are combined, this can be a common call too
if not self.parent.trigger_width.get():
self.parent.set_channels("width", exp_time)
else:
self.parent.set_channels("width", self.parent.trigger_width.get())
else:
raise Exception(f"Unknown scan type {self.parent.scaninfo.scan_type}")
# Set common DDG parameters
self.parent.burst_enable(num_burst_cycle, delay_burst, total_exposure, config="first")
self.parent.set_channels("delay", 0.0)
def on_trigger(self) -> None:
"""Method to be executed upon trigger"""
if self.parent.source.read()[self.parent.source.name]["value"] == TriggerSource.SINGLE_SHOT:
self.parent.trigger_shot.put(1)
def check_scan_id(self) -> None:
"""
Method to check if scan_id has changed.
If yes, then it changes parent.stopped to True, which will stop further actions.
"""
old_scan_id = self.parent.scaninfo.scan_id
self.parent.scaninfo.load_scan_metadata()
if self.parent.scaninfo.scan_id != old_scan_id:
self.parent.stopped = True
def finished(self) -> None:
"""Method checks if DDG finished acquisition"""
def on_pre_scan(self) -> None:
"""
Method called by pre_scan hook in parent class.
Executes trigger if premove_trigger is Trus.
"""
if self.parent.premove_trigger.get() is True:
self.parent.trigger_shot.put(1)
class DelayGeneratorcSAXS(PSIDelayGeneratorBase):
"""
DG645 delay generator at cSAXS (multiple can be in use depending on the setup)
Default values for setting up DDG.
Note: checks of set calues are not (only partially) included, check manual for details on possible settings.
https://www.thinksrs.com/downloads/pdfs/manuals/DG645m.pdf
- delay_burst : (float >=0) Delay between trigger and first pulse in burst mode
- delta_width : (float >= 0) Add width to fast shutter signal to make sure its open during acquisition
- additional_triggers : (int) add additional triggers to burst mode (mcs card needs +1 triggers per line)
- polarity : (list of 0/1) polarity for different channels
- amplitude : (float) amplitude voltage of TTLs
- offset : (float) offset for ampltitude
- thres_trig_level : (float) threshold of trigger amplitude
Custom signals for logic in different DDGs during scans (for custom_prepare.prepare_ddg):
- set_high_on_exposure : (bool): if True, then TTL signal should go high during the full acquisition time of a scan.
# TODO trigger_width and fixed_ttl could be combined into single list.
- fixed_ttl_width : (list of either 1 or 0), one for each channel.
- trigger_width : (float) if fixed_ttl_width is True, then the width of the TTL pulse is set to this value.
- set_trigger_source : (TriggerSource) specifies the default trigger source for the DDG.
- premove_trigger : (bool) if True, then a trigger should be executed before the scan starts (to be implemented in on_pre_scan).
- set_high_on_stage : (bool) if True, then TTL signal should go high already on stage.
"""
custom_prepare_cls = DDGSetup
delay_burst = Component(
bec_utils.ConfigSignal, name="delay_burst", kind="config", config_storage_name="ddg_config"
)
delta_width = Component(
bec_utils.ConfigSignal, name="delta_width", kind="config", config_storage_name="ddg_config"
)
additional_triggers = Component(
bec_utils.ConfigSignal,
name="additional_triggers",
kind="config",
config_storage_name="ddg_config",
)
polarity = Component(
bec_utils.ConfigSignal, name="polarity", kind="config", config_storage_name="ddg_config"
)
fixed_ttl_width = Component(
bec_utils.ConfigSignal,
name="fixed_ttl_width",
kind="config",
config_storage_name="ddg_config",
)
amplitude = Component(
bec_utils.ConfigSignal, name="amplitude", kind="config", config_storage_name="ddg_config"
)
offset = Component(
bec_utils.ConfigSignal, name="offset", kind="config", config_storage_name="ddg_config"
)
thres_trig_level = Component(
bec_utils.ConfigSignal,
name="thres_trig_level",
kind="config",
config_storage_name="ddg_config",
)
set_high_on_exposure = Component(
bec_utils.ConfigSignal,
name="set_high_on_exposure",
kind="config",
config_storage_name="ddg_config",
)
set_high_on_stage = Component(
bec_utils.ConfigSignal,
name="set_high_on_stage",
kind="config",
config_storage_name="ddg_config",
)
set_trigger_source = Component(
bec_utils.ConfigSignal,
name="set_trigger_source",
kind="config",
config_storage_name="ddg_config",
)
trigger_width = Component(
bec_utils.ConfigSignal,
name="trigger_width",
kind="config",
config_storage_name="ddg_config",
)
premove_trigger = Component(
bec_utils.ConfigSignal,
name="premove_trigger",
kind="config",
config_storage_name="ddg_config",
)
def __init__(
self,
prefix="",
*,
name,
kind=None,
read_attrs=None,
configuration_attrs=None,
parent=None,
device_manager=None,
sim_mode=False,
ddg_config=None,
**kwargs,
):
"""
Args:
prefix (str, optional): Prefix of the device. Defaults to "".
name (str): Name of the device.
kind (str, optional): Kind of the device. Defaults to None.
read_attrs (list, optional): List of attributes to read. Defaults to None.
configuration_attrs (list, optional): List of attributes to configure. Defaults to None.
parent (Device, optional): Parent device. Defaults to None.
device_manager (DeviceManagerBase, optional): DeviceManagerBase object. Defaults to None.
sim_mode (bool, optional): Simulation mode flag. Defaults to False.
ddg_config (dict, optional): Dictionary of ddg_config signals. Defaults to None.
"""
# Default values for ddg_config signals
self.ddg_config = {
# Setup default values
f"{name}_delay_burst": 0,
f"{name}_delta_width": 0,
f"{name}_additional_triggers": 0,
f"{name}_polarity": [1, 1, 1, 1, 1],
f"{name}_amplitude": 4.5,
f"{name}_offset": 0,
f"{name}_thres_trig_level": 2.5,
# Values for different behaviour during scans
f"{name}_fixed_ttl_width": [0, 0, 0, 0, 0],
f"{name}_trigger_width": None,
f"{name}_set_high_on_exposure": False,
f"{name}_set_high_on_stage": False,
f"{name}_set_trigger_source": "SINGLE_SHOT",
f"{name}_premove_trigger": False,
}
if ddg_config is not None:
# pylint: disable=expression-not-assigned
[self.ddg_config.update({f"{name}_{key}": value}) for key, value in ddg_config.items()]
super().__init__(
prefix=prefix,
name=name,
kind=kind,
read_attrs=read_attrs,
configuration_attrs=configuration_attrs,
parent=parent,
device_manager=device_manager,
sim_mode=sim_mode,
**kwargs,
)
if __name__ == "__main__":
# Start delay generator in simulation mode.
# Note: To run, access to Epics must be available.
dgen = DelayGeneratorcSAXS("delaygen:DG1:", name="dgen", sim_mode=True)

View File

@ -0,0 +1,349 @@
import enum
import os
import threading
from bec_lib.logger import bec_logger
from ophyd import Component as Cpt
from ophyd import Device, EpicsSignal, EpicsSignalRO, EpicsSignalWithRBV
from ophyd.mca import EpicsMCARecord
from ophyd_devices.interfaces.base_classes.psi_detector_base import (
CustomDetectorMixin,
PSIDetectorBase,
)
logger = bec_logger.logger
class FalconError(Exception):
"""Base class for exceptions in this module."""
class FalconTimeoutError(FalconError):
"""Raised when the Falcon does not respond in time."""
class DetectorState(enum.IntEnum):
"""Detector states for Falcon detector"""
DONE = 0
ACQUIRING = 1
class TriggerSource(enum.IntEnum):
"""Trigger source for Falcon detector"""
USER = 0
GATE = 1
SYNC = 2
class MappingSource(enum.IntEnum):
"""Mapping source for Falcon detector"""
SPECTRUM = 0
MAPPING = 1
class EpicsDXPFalcon(Device):
"""
DXP parameters for Falcon detector
Base class to map EPICS PVs from DXP parameters to ophyd signals.
"""
elapsed_live_time = Cpt(EpicsSignal, "ElapsedLiveTime")
elapsed_real_time = Cpt(EpicsSignal, "ElapsedRealTime")
elapsed_trigger_live_time = Cpt(EpicsSignal, "ElapsedTriggerLiveTime")
# Energy Filter PVs
energy_threshold = Cpt(EpicsSignalWithRBV, "DetectionThreshold")
min_pulse_separation = Cpt(EpicsSignalWithRBV, "MinPulsePairSeparation")
detection_filter = Cpt(EpicsSignalWithRBV, "DetectionFilter", string=True)
scale_factor = Cpt(EpicsSignalWithRBV, "ScaleFactor")
risetime_optimisation = Cpt(EpicsSignalWithRBV, "RisetimeOptimization")
# Misc PVs
detector_polarity = Cpt(EpicsSignalWithRBV, "DetectorPolarity")
decay_time = Cpt(EpicsSignalWithRBV, "DecayTime")
current_pixel = Cpt(EpicsSignalRO, "CurrentPixel")
class FalconHDF5Plugins(Device):
"""
HDF5 parameters for Falcon detector
Base class to map EPICS PVs from HDF5 Plugin to ophyd signals.
"""
capture = Cpt(EpicsSignalWithRBV, "Capture")
enable = Cpt(EpicsSignalWithRBV, "EnableCallbacks", string=True, kind="config")
xml_file_name = Cpt(EpicsSignalWithRBV, "XMLFileName", string=True, kind="config")
lazy_open = Cpt(EpicsSignalWithRBV, "LazyOpen", string=True, doc="0='No' 1='Yes'")
temp_suffix = Cpt(EpicsSignalWithRBV, "TempSuffix", string=True)
file_path = Cpt(EpicsSignalWithRBV, "FilePath", string=True, kind="config")
file_name = Cpt(EpicsSignalWithRBV, "FileName", string=True, kind="config")
file_template = Cpt(EpicsSignalWithRBV, "FileTemplate", string=True, kind="config")
num_capture = Cpt(EpicsSignalWithRBV, "NumCapture", kind="config")
file_write_mode = Cpt(EpicsSignalWithRBV, "FileWriteMode", kind="config")
queue_size = Cpt(EpicsSignalWithRBV, "QueueSize", kind="config")
array_counter = Cpt(EpicsSignalWithRBV, "ArrayCounter", kind="config")
class FalconSetup(CustomDetectorMixin):
"""
Falcon setup class for cSAXS
Parent class: CustomDetectorMixin
"""
def __init__(self, *args, parent: Device = None, **kwargs) -> None:
super().__init__(*args, parent=parent, **kwargs)
self._lock = threading.RLock()
def on_init(self) -> None:
"""Initialize Falcon detector"""
self.initialize_default_parameter()
self.initialize_detector()
self.initialize_detector_backend()
def initialize_default_parameter(self) -> None:
"""
Set default parameters for Falcon
This will set:
- readout (float): readout time in seconds
- value_pixel_per_buffer (int): number of spectra in buffer of Falcon Sitoro
"""
self.parent.value_pixel_per_buffer = 20
self.update_readout_time()
def update_readout_time(self) -> None:
"""Set readout time for Eiger9M detector"""
readout_time = (
self.parent.scaninfo.readout_time
if hasattr(self.parent.scaninfo, "readout_time")
else self.parent.MIN_READOUT
)
self.parent.readout_time = max(readout_time, self.parent.MIN_READOUT)
def initialize_detector(self) -> None:
"""Initialize Falcon detector"""
self.stop_detector()
self.stop_detector_backend()
self.set_trigger(
mapping_mode=MappingSource.MAPPING, trigger_source=TriggerSource.GATE, ignore_gate=0
)
# 1 Realtime
self.parent.preset_mode.put(1)
# 0 Normal, 1 Inverted
self.parent.input_logic_polarity.put(0)
# 0 Manual 1 Auto
self.parent.auto_pixels_per_buffer.put(0)
# Sets the number of pixels/spectra in the buffer
self.parent.pixels_per_buffer.put(self.parent.value_pixel_per_buffer)
def initialize_detector_backend(self) -> None:
"""Initialize the detector backend for Falcon."""
self.parent.hdf5.enable.put(1)
# file location of h5 layout for cSAXS
self.parent.hdf5.xml_file_name.put("layout.xml")
# TODO Check if lazy open is needed and wanted!
self.parent.hdf5.lazy_open.put(1)
self.parent.hdf5.temp_suffix.put("")
# size of queue for number of spectra allowed in the buffer, if too small at high throughput, data is lost
self.parent.hdf5.queue_size.put(2000)
# Segmentation into Spectra within EPICS, 1 is activate, 0 is deactivate
self.parent.nd_array_mode.put(1)
def on_stage(self) -> None:
"""Prepare detector and backend for acquisition"""
self.prepare_detector()
self.prepare_data_backend()
self.publish_file_location(done=False, successful=False)
self.arm_acquisition()
def prepare_detector(self) -> None:
"""Prepare detector for acquisition"""
self.set_trigger(
mapping_mode=MappingSource.MAPPING, trigger_source=TriggerSource.GATE, ignore_gate=0
)
self.parent.preset_real.put(self.parent.scaninfo.exp_time)
self.parent.pixels_per_run.put(
int(self.parent.scaninfo.num_points * self.parent.scaninfo.frames_per_trigger)
)
def prepare_data_backend(self) -> None:
"""Prepare data backend for acquisition"""
self.parent.filepath.set(
self.parent.filewriter.compile_full_filename(f"{self.parent.name}.h5")
).wait()
file_path, file_name = os.path.split(self.parent.filepath.get())
self.parent.hdf5.file_path.put(file_path)
self.parent.hdf5.file_name.put(file_name)
self.parent.hdf5.file_template.put("%s%s")
self.parent.hdf5.num_capture.put(
int(self.parent.scaninfo.num_points * self.parent.scaninfo.frames_per_trigger)
)
self.parent.hdf5.file_write_mode.put(2)
# Reset spectrum counter in filewriter, used for indexing & identifying missing triggers
self.parent.hdf5.array_counter.put(0)
# Start file writing
self.parent.hdf5.capture.put(1)
def arm_acquisition(self) -> None:
"""Arm detector for acquisition"""
self.parent.start_all.put(1)
signal_conditions = [
(
lambda: self.parent.state.read()[self.parent.state.name]["value"],
DetectorState.ACQUIRING,
)
]
if not self.wait_for_signals(
signal_conditions=signal_conditions,
timeout=self.parent.TIMEOUT_FOR_SIGNALS,
check_stopped=True,
all_signals=False,
):
raise FalconTimeoutError(
f"Failed to arm the acquisition. Detector state {signal_conditions[0][0]}"
)
def on_unstage(self) -> None:
"""Unstage detector and backend"""
pass
def on_complete(self) -> None:
"""Complete detector and backend"""
self.finished(timeout=self.parent.TIMEOUT_FOR_SIGNALS)
self.publish_file_location(done=True, successful=True)
def on_stop(self) -> None:
"""Stop detector and backend"""
self.stop_detector()
self.stop_detector_backend()
def stop_detector(self) -> None:
"""Stops detector"""
self.parent.stop_all.put(1)
self.parent.erase_all.put(1)
signal_conditions = [
(lambda: self.parent.state.read()[self.parent.state.name]["value"], DetectorState.DONE)
]
if not self.wait_for_signals(
signal_conditions=signal_conditions,
timeout=self.parent.TIMEOUT_FOR_SIGNALS - self.parent.TIMEOUT_FOR_SIGNALS // 2,
all_signals=False,
):
# Retry stop detector and wait for remaining time
raise FalconTimeoutError(
f"Failed to stop detector, timeout with state {signal_conditions[0][0]}"
)
def stop_detector_backend(self) -> None:
"""Stop the detector backend"""
self.parent.hdf5.capture.put(0)
def finished(self, timeout: int = 5) -> None:
"""Check if scan finished succesfully"""
with self._lock:
total_frames = int(
self.parent.scaninfo.num_points * self.parent.scaninfo.frames_per_trigger
)
signal_conditions = [
(self.parent.dxp.current_pixel.get, total_frames),
(self.parent.hdf5.array_counter.get, total_frames),
]
if not self.wait_for_signals(
signal_conditions=signal_conditions,
timeout=timeout,
check_stopped=True,
all_signals=True,
):
logger.debug(
f"Falcon missed a trigger: received trigger {self.parent.dxp.current_pixel.get()},"
f" send data {self.parent.hdf5.array_counter.get()} from total_frames"
f" {total_frames}"
)
self.stop_detector()
self.stop_detector_backend()
def set_trigger(
self, mapping_mode: MappingSource, trigger_source: TriggerSource, ignore_gate: int = 0
) -> None:
"""
Set triggering mode for detector
Args:
mapping_mode (MappingSource): Mapping mode for the detector
trigger_source (TriggerSource): Trigger source for the detector, pixel_advance_signal
ignore_gate (int): Ignore gate from TTL signal; defaults to 0
"""
mapping = int(mapping_mode)
trigger = trigger_source
self.parent.collect_mode.put(mapping)
self.parent.pixel_advance_mode.put(trigger)
self.parent.ignore_gate.put(ignore_gate)
class FalconcSAXS(PSIDetectorBase):
"""
Falcon Sitoro detector for CSAXS
Parent class: PSIDetectorBase
class attributes:
custom_prepare_cls (FalconSetup) : Custom detector setup class for cSAXS,
inherits from CustomDetectorMixin
PSIDetectorBase.set_min_readout (float) : Minimum readout time for the detector
dxp (EpicsDXPFalcon) : DXP parameters for Falcon detector
mca (EpicsMCARecord) : MCA parameters for Falcon detector
hdf5 (FalconHDF5Plugins) : HDF5 parameters for Falcon detector
MIN_READOUT (float) : Minimum readout time for the detector
"""
# Specify which functions are revealed to the user in BEC client
USER_ACCESS = ["describe"]
# specify Setup class
custom_prepare_cls = FalconSetup
# specify minimum readout time for detector
MIN_READOUT = 3e-3
TIMEOUT_FOR_SIGNALS = 5
# specify class attributes
dxp = Cpt(EpicsDXPFalcon, "dxp1:")
mca = Cpt(EpicsMCARecord, "mca1")
hdf5 = Cpt(FalconHDF5Plugins, "HDF1:")
stop_all = Cpt(EpicsSignal, "StopAll")
erase_all = Cpt(EpicsSignal, "EraseAll")
start_all = Cpt(EpicsSignal, "StartAll")
state = Cpt(EpicsSignal, "Acquiring")
preset_mode = Cpt(EpicsSignal, "PresetMode") # 0 No preset 1 Real time 2 Events 3 Triggers
preset_real = Cpt(EpicsSignal, "PresetReal")
preset_events = Cpt(EpicsSignal, "PresetEvents")
preset_triggers = Cpt(EpicsSignal, "PresetTriggers")
triggers = Cpt(EpicsSignalRO, "MaxTriggers", lazy=True)
events = Cpt(EpicsSignalRO, "MaxEvents", lazy=True)
input_count_rate = Cpt(EpicsSignalRO, "MaxInputCountRate", lazy=True)
output_count_rate = Cpt(EpicsSignalRO, "MaxOutputCountRate", lazy=True)
collect_mode = Cpt(EpicsSignal, "CollectMode") # 0 MCA spectra, 1 MCA mapping
pixel_advance_mode = Cpt(EpicsSignal, "PixelAdvanceMode")
ignore_gate = Cpt(EpicsSignal, "IgnoreGate")
input_logic_polarity = Cpt(EpicsSignal, "InputLogicPolarity")
auto_pixels_per_buffer = Cpt(EpicsSignal, "AutoPixelsPerBuffer")
pixels_per_buffer = Cpt(EpicsSignal, "PixelsPerBuffer")
pixels_per_run = Cpt(EpicsSignal, "PixelsPerRun")
nd_array_mode = Cpt(EpicsSignal, "NDArrayMode")
if __name__ == "__main__":
falcon = FalconcSAXS(name="falcon", prefix="X12SA-SITORO:", sim_mode=True)

View File

@ -0,0 +1,362 @@
#
# #
# #
# # copied file from csaxs, but with all hdf5 commentred out.. (lazy for quit testing )
# # file needs to be renamed
# #
# #
#
#
import enum
import os
import threading
from bec_lib.logger import bec_logger
from ophyd import Component as Cpt
from ophyd import Device, EpicsSignal, EpicsSignalRO, EpicsSignalWithRBV
from ophyd.mca import EpicsMCARecord
from ophyd_devices.interfaces.base_classes.psi_detector_base import (
CustomDetectorMixin,
PSIDetectorBase,
)
logger = bec_logger.logger
class FalconError(Exception):
"""Base class for exceptions in this module."""
class FalconTimeoutError(FalconError):
"""Raised when the Falcon does not respond in time."""
class DetectorState(enum.IntEnum):
"""Detector states for Falcon detector"""
DONE = 0
ACQUIRING = 1
class TriggerSource(enum.IntEnum):
"""Trigger source for Falcon detector"""
USER = 0
GATE = 1
SYNC = 2
class MappingSource(enum.IntEnum):
"""Mapping source for Falcon detector"""
SPECTRUM = 0
MAPPING = 1
class EpicsDXPFalcon(Device):
"""
DXP parameters for Falcon detector
Base class to map EPICS PVs from DXP parameters to ophyd signals.
"""
elapsed_live_time = Cpt(EpicsSignal, "ElapsedLiveTime")
elapsed_real_time = Cpt(EpicsSignal, "ElapsedRealTime")
elapsed_trigger_live_time = Cpt(EpicsSignal, "ElapsedTriggerLiveTime")
# Energy Filter PVs
energy_threshold = Cpt(EpicsSignalWithRBV, "DetectionThreshold")
min_pulse_separation = Cpt(EpicsSignalWithRBV, "MinPulsePairSeparation")
detection_filter = Cpt(EpicsSignalWithRBV, "DetectionFilter", string=True)
scale_factor = Cpt(EpicsSignalWithRBV, "ScaleFactor")
risetime_optimisation = Cpt(EpicsSignalWithRBV, "RisetimeOptimization")
# Misc PVs
detector_polarity = Cpt(EpicsSignalWithRBV, "DetectorPolarity")
decay_time = Cpt(EpicsSignalWithRBV, "DecayTime")
current_pixel = Cpt(EpicsSignalRO, "CurrentPixel")
class FalconHDF5Plugins(Device):
"""
HDF5 parameters for Falcon detector
Base class to map EPICS PVs from HDF5 Plugin to ophyd signals.
"""
capture = Cpt(EpicsSignalWithRBV, "Capture")
enable = Cpt(EpicsSignalWithRBV, "EnableCallbacks", string=True, kind="config")
xml_file_name = Cpt(EpicsSignalWithRBV, "XMLFileName", string=True, kind="config")
lazy_open = Cpt(EpicsSignalWithRBV, "LazyOpen", string=True, doc="0='No' 1='Yes'")
temp_suffix = Cpt(EpicsSignalWithRBV, "TempSuffix", string=True)
file_path = Cpt(EpicsSignalWithRBV, "FilePath", string=True, kind="config")
file_name = Cpt(EpicsSignalWithRBV, "FileName", string=True, kind="config")
file_template = Cpt(EpicsSignalWithRBV, "FileTemplate", string=True, kind="config")
num_capture = Cpt(EpicsSignalWithRBV, "NumCapture", kind="config")
file_write_mode = Cpt(EpicsSignalWithRBV, "FileWriteMode", kind="config")
queue_size = Cpt(EpicsSignalWithRBV, "QueueSize", kind="config")
array_counter = Cpt(EpicsSignalWithRBV, "ArrayCounter", kind="config")
class FalconSetup(CustomDetectorMixin):
"""
Falcon setup class for cSAXS
Parent class: CustomDetectorMixin
"""
def __init__(self, *args, parent: Device = None, **kwargs) -> None:
super().__init__(*args, parent=parent, **kwargs)
self._lock = threading.RLock()
def on_init(self) -> None:
"""Initialize Falcon detector"""
self.initialize_default_parameter()
self.initialize_detector()
self.initialize_detector_backend()
def initialize_default_parameter(self) -> None:
"""
Set default parameters for Falcon
This will set:
- readout (float): readout time in seconds
- value_pixel_per_buffer (int): number of spectra in buffer of Falcon Sitoro
"""
#self.parent.value_pixel_per_buffer = 20 -------------
#self.update_readout_time()
w=2 --------------
def update_readout_time(self) -> None:
"""Set readout time for Eiger9M detector"""
readout_time = (
self.parent.scaninfo.readout_time
if hasattr(self.parent.scaninfo, "readout_time")
else self.parent.MIN_READOUT
)
self.parent.readout_time = max(readout_time, self.parent.MIN_READOUT)
def initialize_detector(self) -> None:
"""Initialize Falcon detector"""
self.stop_detector()
self.stop_detector_backend()
self.set_trigger(
mapping_mode=MappingSource.MAPPING, trigger_source=TriggerSource.GATE, ignore_gate=0
)
# 1 Realtime
self.parent.preset_mode.put(1)
# 0 Normal, 1 Inverted
self.parent.input_logic_polarity.put(0)
# 0 Manual 1 Auto
self.parent.auto_pixels_per_buffer.put(0)
# Sets the number of pixels/spectra in the buffer
self.parent.pixels_per_buffer.put(self.parent.value_pixel_per_buffer)
def initialize_detector_backend(self) -> None:
"""Initialize the detector backend for Falcon."""
self.parent.hdf5.enable.put(1)
# file location of h5 layout for cSAXS
self.parent.hdf5.xml_file_name.put("layout.xml")
# TODO Check if lazy open is needed and wanted!
self.parent.hdf5.lazy_open.put(1)
self.parent.hdf5.temp_suffix.put("")
# size of queue for number of spectra allowed in the buffer, if too small at high throughput, data is lost
self.parent.hdf5.queue_size.put(2000)
# Segmentation into Spectra within EPICS, 1 is activate, 0 is deactivate
self.parent.nd_array_mode.put(1)
def on_stage(self) -> None:
"""Prepare detector and backend for acquisition"""
self.prepare_detector()
self.prepare_data_backend()
self.publish_file_location(done=False, successful=False)
self.arm_acquisition()
def prepare_detector(self) -> None:
"""Prepare detector for acquisition"""
self.set_trigger(
mapping_mode=MappingSource.MAPPING, trigger_source=TriggerSource.GATE, ignore_gate=0
)
self.parent.preset_real.put(self.parent.scaninfo.exp_time)
self.parent.pixels_per_run.put(
int(self.parent.scaninfo.num_points * self.parent.scaninfo.frames_per_trigger)
)
def prepare_data_backend(self) -> None:
"""Prepare data backend for acquisition"""
self.parent.filepath.set(
self.parent.filewriter.compile_full_filename(f"{self.parent.name}.h5")
).wait()
file_path, file_name = os.path.split(self.parent.filepath.get())
self.parent.hdf5.file_path.put(file_path)
self.parent.hdf5.file_name.put(file_name)
self.parent.hdf5.file_template.put("%s%s")
self.parent.hdf5.num_capture.put(
int(self.parent.scaninfo.num_points * self.parent.scaninfo.frames_per_trigger)
)
self.parent.hdf5.file_write_mode.put(2)
# Reset spectrum counter in filewriter, used for indexing & identifying missing triggers
self.parent.hdf5.array_counter.put(0)
# Start file writing
self.parent.hdf5.capture.put(1)
def arm_acquisition(self) -> None:
"""Arm detector for acquisition"""
self.parent.start_all.put(1)
signal_conditions = [
(
lambda: self.parent.state.read()[self.parent.state.name]["value"],
DetectorState.ACQUIRING,
)
]
if not self.wait_for_signals(
signal_conditions=signal_conditions,
timeout=self.parent.TIMEOUT_FOR_SIGNALS,
check_stopped=True,
all_signals=False,
):
raise FalconTimeoutError(
f"Failed to arm the acquisition. Detector state {signal_conditions[0][0]}"
)
def on_unstage(self) -> None:
"""Unstage detector and backend"""
pass
def on_complete(self) -> None:
"""Complete detector and backend"""
self.finished(timeout=self.parent.TIMEOUT_FOR_SIGNALS)
self.publish_file_location(done=True, successful=True)
def on_stop(self) -> None:
"""Stop detector and backend"""
self.stop_detector()
self.stop_detector_backend()
def stop_detector(self) -> None:
"""Stops detector"""
self.parent.stop_all.put(1)
self.parent.erase_all.put(1)
#signal_conditions = [
# (lambda: self.parent.state.read()[self.parent.state.name]["value"], DetectorState.DONE)
#]
signal_condition = []
## --------- next commented out wg hdf 5 --------------------------------------------
#if not self.wait_for_signals(
# signal_conditions=signal_conditions,
# timeout=self.parent.TIMEOUT_FOR_SIGNALS - self.parent.TIMEOUT_FOR_SIGNALS // 2,
# all_signals=False,
#):
# # Retry stop detector and wait for remaining time
# raise FalconTimeoutError(
# f"Failed to stop detector, timeout with state {signal_conditions[0][0]}"
# )
def stop_detector_backend(self) -> None:
"""Stop the detector backend"""
#self.parent.hdf5.capture.put(0) ---------------------------
w=3
def finished(self, timeout: int = 5) -> None:
"""Check if scan finished succesfully"""
with self._lock:
total_frames = int(
self.parent.scaninfo.num_points * self.parent.scaninfo.frames_per_trigger
)
signal_conditions = [
(self.parent.dxp.current_pixel.get, total_frames),
(self.parent.hdf5.array_counter.get, total_frames),
]
if not self.wait_for_signals(
signal_conditions=signal_conditions,
timeout=timeout,
check_stopped=True,
all_signals=True,
):
logger.debug(
f"Falcon missed a trigger: received trigger {self.parent.dxp.current_pixel.get()},"
f" send data {self.parent.hdf5.array_counter.get()} from total_frames"
f" {total_frames}"
)
self.stop_detector()
self.stop_detector_backend()
def set_trigger(
self, mapping_mode: MappingSource, trigger_source: TriggerSource, ignore_gate: int = 0
) -> None:
"""
Set triggering mode for detector
Args:
mapping_mode (MappingSource): Mapping mode for the detector
trigger_source (TriggerSource): Trigger source for the detector, pixel_advance_signal
ignore_gate (int): Ignore gate from TTL signal; defaults to 0
"""
mapping = int(mapping_mode)
trigger = trigger_source
self.parent.collect_mode.put(mapping)
self.parent.pixel_advance_mode.put(trigger)
self.parent.ignore_gate.put(ignore_gate)
class FalconcSAXS(PSIDetectorBase):
"""
Falcon Sitoro detector for CSAXS
Parent class: PSIDetectorBase
class attributes:
custom_prepare_cls (FalconSetup) : Custom detector setup class for cSAXS,
inherits from CustomDetectorMixin
PSIDetectorBase.set_min_readout (float) : Minimum readout time for the detector
dxp (EpicsDXPFalcon) : DXP parameters for Falcon detector
mca (EpicsMCARecord) : MCA parameters for Falcon detector
hdf5 (FalconHDF5Plugins) : HDF5 parameters for Falcon detector
MIN_READOUT (float) : Minimum readout time for the detector
"""
# Specify which functions are revealed to the user in BEC client
USER_ACCESS = ["describe"]
# specify Setup class
custom_prepare_cls = FalconSetup
# specify minimum readout time for detector
MIN_READOUT = 3e-3
TIMEOUT_FOR_SIGNALS = 5
# specify class attributes
dxp = Cpt(EpicsDXPFalcon, "dxp1:")
mca = Cpt(EpicsMCARecord, "mca1")
# hdf5 = Cpt(FalconHDF5Plugins, "HDF1:") ------------------
stop_all = Cpt(EpicsSignal, "StopAll")
erase_all = Cpt(EpicsSignal, "EraseAll")
start_all = Cpt(EpicsSignal, "StartAll")
state = Cpt(EpicsSignal, "Acquiring")
preset_mode = Cpt(EpicsSignal, "PresetMode") # 0 No preset 1 Real time 2 Events 3 Triggers
preset_real = Cpt(EpicsSignal, "PresetReal")
preset_events = Cpt(EpicsSignal, "PresetEvents")
preset_triggers = Cpt(EpicsSignal, "PresetTriggers")
triggers = Cpt(EpicsSignalRO, "MaxTriggers", lazy=True)
events = Cpt(EpicsSignalRO, "MaxEvents", lazy=True)
input_count_rate = Cpt(EpicsSignalRO, "MaxInputCountRate", lazy=True)
output_count_rate = Cpt(EpicsSignalRO, "MaxOutputCountRate", lazy=True)
collect_mode = Cpt(EpicsSignal, "CollectMode") # 0 MCA spectra, 1 MCA mapping
pixel_advance_mode = Cpt(EpicsSignal, "PixelAdvanceMode")
ignore_gate = Cpt(EpicsSignal, "IgnoreGate")
input_logic_polarity = Cpt(EpicsSignal, "InputLogicPolarity")
auto_pixels_per_buffer = Cpt(EpicsSignal, "AutoPixelsPerBuffer")
pixels_per_buffer = Cpt(EpicsSignal, "PixelsPerBuffer")
pixels_per_run = Cpt(EpicsSignal, "PixelsPerRun")
nd_array_mode = Cpt(EpicsSignal, "NDArrayMode")
if __name__ == "__main__":
falcon = FalconcSAXS(name="falcon", prefix="X07MB-SITORO:", sim_mode=True)

View File

@ -0,0 +1,104 @@
s#from unittest import mock
import numpy as np
#import pandas
#import pytest
#from bec_lib import messages
#import device_server
#from ophyd import Component as Cpt
from ophyd import Device, EpicsMotor, EpicsSignal, EpicsSignalRO
#from ophyd import FormattedComponent as FCpt
#from ophyd import Kind, PVPositioner, Signal
#from ophyd.flyers import FlyerInterface
#from ophyd.pv_positioner import PVPositionerComparator
#from ophyd.status import DeviceStatus, SubscriptionStatus
from bec_lib.config_helper import ConfigHelper
from bec_lib.logger import bec_logger
logger = bec_logger.logger
import time as tt
#import ophyd
import os
import sys
#logger = bec_logger.logger
# load simulation
#bec.config.load_demo_config()
# .. define base path for directory with scripts
class PhoenixBL():
"""
General class for PHOENIX beamline from phoenix_bec/phoenic_bec/scripts
"""
def __init__(self):
"""
init PhoenixBL() in phoenix_bec/scripts
"""
import os
print('..... init PhoenixBL from phoenix_bec/scripts/phoenix.py')
#from ophyd import Device, EpicsMotor, EpicsSignal, EpicsSignalRO
#from ophyd import Component as Cpt
#self.ScanX = EpicsMotor(name='ScanX',prefix='X07MB-ES-MA1:ScanX')
#self.ScanY = EpicsMotor(name='ScanY',prefix='X07MB-ES-MA1:ScanY')
#self.DIODE = EpicsSignal(name='SI',read_pv='X07MB-OP2-SAI_07:MEAN')
#self.SIG = Cpt(EpicsSignal,name='we',read_pv="X07MB-OP2-SAI_07:MEAN")
#self.SMPL = EpicsSignal(name='SMPL',read_pv='X07MB-OP2:SMPL')
#self.CYCLES = EpicsSignal(name='SMPL',read_pv='X07MB-OP2:TOTAL-CYCLES',write_pv='X07MB-OP2:TOTAL-CYCLES')
# load local configuration
self.path_scripts_local = '/data/test/x07mb-test-bec/bec_deployment/phoenix_bec/phoenix_bec/local_scripts/'
self.path_config_local = self.path_scripts_local + 'TEST_ConfigPhoenix/' # base dir for local configurations
self.path_devices_local = self.path_config_local + 'Local_device_config/' # local yamal file
self.file_devices_file_local = self.path_devices_local + 'phoenix_devices.yaml'
self.path_phoenix_bec ='/data/test/x07mb-test-bec/bec_deployment/phoenix_bec/'
self.path_devices = self.path_phoenix_bec + 'phoenix_bec/device_configs/' # local yamal file
self.file_devices_file = self.path_phoenix_bec + 'phoenix_bec/device_configs/phoenix_devices.yaml' # local yamal file
def read_local_phoenix_config(self):
print('read file ')
print(self.file_phoenix_devices_file)
bec.config.update_session_with_file(self.file_devices_file_local)
def add_phoenix_config(self):
print('add_phoenix_config ')
print('self.file_devices_file')
bec.config.update_session_with_file(self.file_devices_file)
def add_xmap(self):
print('add xmap ')
print(self.path_devices+'phoenix_xmap.yaml')
bec.config.update_session_with_file(self.path_devices+'phoenix_xmap.yaml',timeout=50)
def add_falcon(self):
print('add_xmap')
print(self.path_devices+'/phoenix_falcon.yaml')
bec.config.wait_for_config_reply()
bec.config.update_session_with_file(self.path_devices+'/phoenix_falcon.yaml')
def show_phoenix_setup(self):
print(self.path_phoenix_bec)
os.system('cat '+self.path_phoenix_bec+'phoenix_bec/scripts/Current_setup.txt')

View File

@ -0,0 +1,104 @@
#from unittest import mock
import numpy as np
#import pandas
#import pytest
#from bec_lib import messages
#import device_server
#from ophyd import Component as Cpt
from ophyd import Device, EpicsMotor, EpicsSignal, EpicsSignalRO
#from ophyd import FormattedComponent as FCpt
#from ophyd import Kind, PVPositioner, Signal
#from ophyd.flyers import FlyerInterface
#from ophyd.pv_positioner import PVPositionerComparator
#from ophyd.status import DeviceStatus, SubscriptionStatus
from bec_lib.config_helper import ConfigHelper
from bec_lib.logger import bec_logger
logger = bec_logger.logger
import time as tt
#import ophyd
import os
import sys
#logger = bec_logger.logger
# load simulation
#bec.config.load_demo_config()
# .. define base path for directory with scripts
class PhoenixBL():
"""
General class for PHOENIX beamline from phoenix_bec/phoenic_bec/scripts
"""
def __init__(self):
"""
init PhoenixBL() in phoenix_bec/scripts
"""
import os
print('..... init PhoenixBL from phoenix_bec/scripts/phoenix.py')
#from ophyd import Device, EpicsMotor, EpicsSignal, EpicsSignalRO
#from ophyd import Component as Cpt
#self.ScanX = EpicsMotor(name='ScanX',prefix='X07MB-ES-MA1:ScanX')
#self.ScanY = EpicsMotor(name='ScanY',prefix='X07MB-ES-MA1:ScanY')
#self.DIODE = EpicsSignal(name='SI',read_pv='X07MB-OP2-SAI_07:MEAN')
#self.SIG = Cpt(EpicsSignal,name='we',read_pv="X07MB-OP2-SAI_07:MEAN")
#self.SMPL = EpicsSignal(name='SMPL',read_pv='X07MB-OP2:SMPL')
#self.CYCLES = EpicsSignal(name='SMPL',read_pv='X07MB-OP2:TOTAL-CYCLES',write_pv='X07MB-OP2:TOTAL-CYCLES')
# load local configuration
self.path_scripts_local = '/data/test/x07mb-test-bec/bec_deployment/phoenix_bec/phoenix_bec/local_scripts/'
self.path_config_local = self.path_scripts_local + 'TEST_ConfigPhoenix/' # base dir for local configurations
self.path_devices_local = self.path_config_local + 'Local_device_config/' # local yamal file
self.file_devices_file_local = self.path_devices_local + 'phoenix_devices.yaml'
self.path_phoenix_bec ='/data/test/x07mb-test-bec/bec_deployment/phoenix_bec/'
self.path_devices = self.path_phoenix_bec + 'phoenix_bec/device_configs/' # local yamal file
self.file_devices_file = self.path_phoenix_bec + 'phoenix_bec/device_configs/phoenix_devices.yaml' # local yamal file
def read_local_phoenix_config(self):
print('read file ')
print(self.file_phoenix_devices_file)
bec.config.update_session_with_file(self.file_devices_file_local)
def add_phoenix_config(self):
print('add_phoenix_config ')
print('self.file_devices_file')
bec.config.update_session_with_file(self.file_devices_file)
def add_xmap(self):
print('add xmap ')
print(self.path_devices+'phoenix_xmap.yaml')
bec.config.update_session_with_file(self.path_devices+'phoenix_xmap.yaml',timeout=50)
def add_falcon(self):
print('add_xmap')
print(self.path_devices+'/phoenix_falcon.yaml')
bec.config.wait_for_config_reply()
bec.config.update_session_with_file(self.path_devices+'/phoenix_falcon.yaml')
def show_phoenix_setup(self):
print(self.path_phoenix_bec)
os.system('cat '+self.path_phoenix_bec+'phoenix_bec/scripts/Current_setup.txt')

View File

@ -0,0 +1,2 @@
print('test')

View File

@ -0,0 +1,93 @@
#from unittest import mock
import numpy as np
#import pandas
#import pytest
#from bec_lib import messages
#import device_server
#from ophyd import Component as Cpt
from ophyd import Device, EpicsMotor, EpicsSignal, EpicsSignalRO
#from ophyd import FormattedComponent as FCpt
#from ophyd import Kind, PVPositioner, Signal
#from ophyd.flyers import FlyerInterface
#from ophyd.pv_positioner import PVPositionerComparator
#from ophyd.status import DeviceStatus, SubscriptionStatus
import time as tt
#import ophyd
import os
import sys
#logger = bec_logger.logger
# load simulation
#bec.config.load_demo_config()
bec.config.update_session_with_file("config/config_1.yaml")
os.system('mv *.yaml tmp')
class PhoenixBL:
#define some epics channels
def __init__(self):
from ophyd import Device, EpicsMotor, EpicsSignal, EpicsSignalRO
from ophyd import Component as Cpt
self.ScanX = EpicsMotor(name='ScanX',prefix='X07MB-ES-MA1:ScanX')
self.ScanY = EpicsMotor(name='ScanY',prefix='X07MB-ES-MA1:ScanY')
self.DIODE = EpicsSignal(name='SI',read_pv='X07MB-OP2-SAI_07:MEAN')
self.SIG = Cpt(EpicsSignal,name='we',read_pv="X07MB-OP2-SAI_07:MEAN")
self.SMPL = EpicsSignal(name='SMPL',read_pv='X07MB-OP2:SMPL')
self.CYCLES = EpicsSignal(name='SMPL',read_pv='X07MB-OP2:TOTAL-CYCLES',write_pv='X07MB-OP2:TOTAL-CYCLES')
self.fielda =EpicsSignal(name='SMPL',read_pv='X07MB-SCAN:scan1.P1SP',write_pv='X07MB-SCAN:scan1.P1SP')
#end class
ph=PhoenixBL()
print('---------------------------------')
# scan will not diode
print(' SCAN ')
dev.PH_curr_conf.readout_priority='baseline' # do not read detector
dev.PH_curr_conf.readout_priority='monitored' # read detector
ti=tt.time_ns()
print('start scan ')
tt.sleep(.2)
s1=scans.line_scan(dev.PH_ScanX_conf,0,0.002,steps=2,exp_time=1,relative=False,delay=2)
tf=tt.time_ns()
print('elapsed time',(tf-ti)/1e9)
s1.scan.data
for thiskey in s1.scan.data.keys():
print(thiskey)
print(s1.scan.data[thiskey])
#ww=s1.scan.data['Ph_ScanX_conf']
#print(ww)
"""
next lines do not work as pandas is not installed on test system
res1 = s1.scan.to_pandas()
re1 = res1.to_numpy()
print('Scana')
print(res1)
print('')
print('Scan2 at pandas ')
print(res2)
print('Scan2 as numpy ')
print(res2)
"""

View File

@ -0,0 +1 @@
from .phoenix_line_scan import PhoenixLineScan

View File

@ -0,0 +1,115 @@
"""
SCAN PLUGINS for PHOENIX
All new scans should be derived from ScanBase. ScanBase provides various methods that can be customized and overriden
but they are executed in a specific order:
- self.initialize # initialize the class if needed
- self.read_scan_motors # used to retrieve the start position (and the relative position shift if needed)
- self.prepare_positions # prepare the positions for the scan. The preparation is split into multiple sub fuctions:
- self._calculate_positions # calculate the positions
- self._set_positions_offset # apply the previously retrieved scan position shift (if needed)
- self._check_limits # tests to ensure the limits won't be reached
- self.open_scan # send an open_scan message including the scan name, the number of points and the scan motor names
- self.stage # stage all devices for the upcoming acquisiton
- self.run_baseline_readings # read all devices to get a baseline for the upcoming scan
- self.pre_scan # perform additional actions before the scan starts
- self.scan_core # run a loop over all position
- self._at_each_point(ind, pos) # called at each position with the current index and the target positions as arguments
- self.finalize # clean up the scan, e.g. move back to the start position; wait everything to finish
- self.unstage # unstage all devices that have been staged before
- self.cleanup # send a close scan message and perform additional cleanups if needed
"""
# import time
# import numpy as np
# from bec_lib.endpoints import MessageEndpoints
# from bec_lib.logger import bec_logger
# from bec_lib import messages
# from bec_server.scan_server.errors import ScanAbortion
# from bec_server.scan_server.scans import FlyScanBase, RequestBase, ScanArgType, ScanBase
# logger = bec_logger.logger
from bec_server.scan_server.scans import ScanBase, ScanArgType
import numpy as np
import time
from bec_lib.logger import bec_logger
logger = bec_logger.logger
class PhoenixLineScan(ScanBase):
scan_name = "phoenix_line_scanZZZ"
required_kwargs = ["steps", "relative"]
arg_input = {
"device": ScanArgType.DEVICE,
"start": ScanArgType.FLOAT,
"stop": ScanArgType.FLOAT,
}
arg_bundle_size = {"bundle": len(arg_input), "min": 1, "max": None}
gui_config = {
"Movement Parameters": ["steps", "relative"],
"Acquisition Parameters": ["exp_time", "burst_at_each_point"],
}
def __init__(
self,
*args,
exp_time: float = 0,
steps: int = None,
relative: bool = False,
burst_at_each_point: int = 1,
setup_device:str= None,
**kwargs,
):
"""
A phoenix line scan for one or more motors.
Args:
*args (Device, float, float): pairs of device / start position / end position
exp_time (float): exposure time in s. Default: 0
steps (int): number of steps. Default: 10
relative (bool): if True, the start and end positions are relative to the current position. Default: False
burst_a Specifies the level of type checking analysis to perform.
ans.line_scan(dev.motor1, -5, 5, dev.motor2, -5, 5, steps=10, exp_time=0.1, relative=True)
"""
super().__init__(
exp_time=exp_time, relative=relative, burst_at_each_point=burst_at_each_point, **kwargs
)
self.steps = steps
self.setup_device = setup_device
print('INIT CLASS PhoenixLineScan')
time.sleep(1)
def _calculate_positions(self) -> None:
axis = []
for _, val in self.caller_args.items():
ax_pos = np.linspace(val[0], val[1], self.steps, dtype=float)
axis.append(ax_pos)
self.positions = np.array(list(zip(*axis)), dtype=float)
def _at_each_point(self, ind=None, pos=None):
yield from self._move_scan_motors_and_wait(pos)
if ind > 0:
yield from self.stubs.wait(
wait_type="read", group="primary", wait_group="readout_primary"
)
time.sleep(self.settling_time)
if self.setup_device:
yield from self.stubs.send_rpc_and_wait(self.setup_device, "velocity.set", 1)
yield from self.stubs.trigger(group="trigger", point_id=self.point_id)
yield from self.stubs.wait(wait_type="trigger", group="trigger", wait_time=self.exp_time)
yield from self.stubs.read(
group="primary", wait_group="readout_primary", point_id=self.point_id
)
yield from self.stubs.wait(
wait_type="read", group="scan_motor", wait_group="readout_primary"
)
self.point_id += 1

View File

@ -0,0 +1,33 @@
#######################################################
Definiton from file local_scripts/Documentation/Current_Setup.txt
#######################################################
Current setup for bec --- to be professionanlized
Description of current setup local_scripts/Documentation/Current_Setup.txt
/phoenix_bec/phoenix_bec/bec_ipython_client/startup/post_startup.py
.. for commands to start/init bec iphython shell
.. here we init phoenix=PhoenixBL()
/bec_deployment/phoenix_bec/phoenix_bec/scripts
.. autoloaded scripts directory
.. for solidified scritps
.. file PhoenixBL in phoenix.py defines BL core functions
/bec_deployment/phoenix_bec/phoenix_bec/devices
.. yamal files for device
/bec_deployment/phoenix_bec/phoenix_bec/local_scripts
.. collection of local scripts for testing purposes
.. all local configurations start name with LOCAL to minimize confusion
to run startup file:
phoenix_bec/bec_ipython_client/startup/post_startup.py
Magic commands defiend in post_startup.py (should all start with ph_)
%ph_reload : reloads module phoenix.py to ipython shell BUT not to server

View File

@ -0,0 +1,2 @@
Directory for general code specific fro PHOENIX
Seems to be loaded outomatically

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,92 @@
#from unittest import mock
import os
import sys
import time as tt
import numpy as np
#import pandas
#import pytest
#from bec_lib import messages
#import device_server
#from ophyd import Component as Cpt
from ophyd import Device, EpicsMotor, EpicsSignal, EpicsSignalRO
#from ophyd import FormattedComponent as FCpt
#from ophyd import Kind, PVPositioner, Signal
#from ophyd.flyers import FlyerInterface
#from ophyd.pv_positioner import PVPositionerComparator
#from ophyd.status import DeviceStatus, SubscriptionStatus
from bec_lib.config_helper import ConfigHelper
from bec_lib.logger import bec_logger
logger = bec_logger.logger
#import ophyd
#logger = bec_logger.logger
# load simulation
#bec.config.load_demo_config()
# .. define base path for directory with scripts
class PhoenixBL():
"""
#
# General class for PHOENIX beamline located in phoenix_bec/phoenic_bec/scripts
#
"""
def __init__(self):
"""
init PhoenixBL() in phoenix_bec/scripts
"""
import os
print('..... init PhoenixBL from phoenix_bec/scripts/phoenix.py')
#from ophyd import Device, EpicsMotor, EpicsSignal, EpicsSignalRO
#from ophyd import Component as Cpt
#self.ScanX = EpicsMotor(name='ScanX',prefix='X07MB-ES-MA1:ScanX')
#self.ScanY = EpicsMotor(name='ScanY',prefix='X07MB-ES-MA1:ScanY')
#self.DIODE = EpicsSignal(name='SI',read_pv='X07MB-OP2-SAI_07:MEAN')
#self.SIG = Cpt(EpicsSignal,name='we',read_pv="X07MB-OP2-SAI_07:MEAN")
#self.SMPL = EpicsSignal(name='SMPL',read_pv='X07MB-OP2:SMPL')
#self.CYCLES = EpicsSignal(name='SMPL',read_pv='X07MB-OP2:TOTAL-CYCLES',write_pv='X07MB-OP2:TOTAL-CYCLES')
# load local configuration
self.path_scripts_local = '/data/test/x07mb-test-bec/bec_deployment/phoenix_bec/phoenix_bec/local_scripts/'
self.path_config_local = self.path_scripts_local + 'TEST_ConfigPhoenix/' # base dir for local configurations
self.path_devices_local = self.path_config_local + 'Local_device_config/' # local yamal file
self.file_devices_file_local = self.path_devices_local + 'phoenix_devices.yaml'
self.path_phoenix_bec ='/data/test/x07mb-test-bec/bec_deployment/phoenix_bec/'
self.path_devices = self.path_phoenix_bec + 'phoenix_bec/device_configs/' # local yamal file
self.file_devices_file = self.path_phoenix_bec + 'phoenix_bec/device_configs/phoenix_devices.yaml' # local yamal file
def read_local_phoenix_config(self):
print('read file ')
print(self.file_phoenix_devices_file)
bec.config.update_session_with_file(self.file_devices_file_local)
def add_phoenix_config(self):
print('add_phoenix_config ')
print('self.file_devices_file')
bec.config.update_session_with_file(self.file_devices_file)
def add_xmap(self):
print('add xmap ')
print(self.path_devices+'phoenix_xmap.yaml')
bec.config.update_session_with_file(self.path_devices+'phoenix_xmap.yaml',timeout=100)
def add_falcon(self):
print('add_xmap')
print(self.path_devices+'/phoenix_falcon.yaml')
bec.config.wait_for_config_reply()
bec.config.update_session_with_file(self.path_devices+'/phoenix_falcon.yaml')
def show_phoenix_setup(self):
print(self.path_phoenix_bec)
os.system('cat '+self.path_phoenix_bec+'phoenix_bec/scripts/Current_setup.txt')