Compare commits
68 Commits
x07mb_test
...
x07mb_v3
Author | SHA1 | Date | |
---|---|---|---|
77403aea1e | |||
03789c34f4 | |||
c888c22da2 | |||
010fefd11d | |||
feb911ab68 | |||
cd81030430 | |||
c021972d97 | |||
3a45759f79 | |||
da1c7bb4cf | |||
d4512c0fb1 | |||
1654f7cfa4 | |||
3c618b431e | |||
a36a0a9189 | |||
c944d5da69 | |||
c86a5e3f4b | |||
61b1385976 | |||
42ec7d64f2 | |||
2da6af2b50 | |||
bde88ec5ce | |||
10c512604a | |||
4ef08b9b99 | |||
7a28dd3ca8 | |||
e583e25424 | |||
14da4a1ad5 | |||
cea11ff48f | |||
bae057995f | |||
b806cbc9c2 | |||
55e723ba24 | |||
e10be3e9bf | |||
2e93a0d811 | |||
956a3267c4 | |||
b704ba1095 | |||
08e2c73c2d | |||
86582fdebc | |||
2a445e3449 | |||
06ffca03ee | |||
e07a1fe66f | |||
f162e8585f | |||
d4b74302ef | |||
e5f004258a | |||
2f2b152ef1 | |||
ec61712803 | |||
08a63c54ce | |||
16b49fe670 | |||
20774ba2a4 | |||
3423a978b6 | |||
66eff8e5f0 | |||
5ba10cdff8 | |||
b752a79c41 | |||
bfa06fe519 | |||
6c8d5ee202 | |||
1f55dc8736 | |||
1c9c94f28f | |||
08d576dbc2 | |||
bd7dff99c5 | |||
f3f51d0a05 | |||
d815c24e27 | |||
dac949679d | |||
69097bc9aa | |||
9f34dc3b22 | |||
0a62c9aac6 | |||
9abbcd4d48 | |||
c77f359451 | |||
6572a71f3b | |||
943aa44abe | |||
20aaa069de | |||
7359d1b8f6 | |||
1b8aabccc1 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -7,6 +7,7 @@
|
||||
**/.vscode
|
||||
**/.pytest_cache
|
||||
**/*.egg*
|
||||
*.gz
|
||||
|
||||
# recovery_config files
|
||||
recovery_config_*
|
||||
|
28
LICENSE
Normal file
28
LICENSE
Normal file
@ -0,0 +1,28 @@
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2024, Paul Scherrer Institute
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
68
phoenix_bec/Documentation/2024_08_26_bookmarks.html
Normal file
68
phoenix_bec/Documentation/2024_08_26_bookmarks.html
Normal file
File diff suppressed because one or more lines are too long
BIN
phoenix_bec/Documentation/Software_structure.pdf
Normal file
BIN
phoenix_bec/Documentation/Software_structure.pdf
Normal file
Binary file not shown.
@ -34,3 +34,205 @@ to setup the prompts.
|
||||
"""
|
||||
|
||||
# pylint: disable=invalid-name, unused-import, import-error, undefined-variable, unused-variable, unused-argument, no-name-in-module
|
||||
import time as tt
|
||||
import sys
|
||||
import os
|
||||
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):
|
||||
"""reloads certain phoenix related script to the iphython shell"""
|
||||
|
||||
##########################################################################
|
||||
#
|
||||
# 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_add_xmap(line):
|
||||
"""
|
||||
magic for loading xmap
|
||||
"""
|
||||
t0 = tt.time()
|
||||
phoenix.add_xmap()
|
||||
print("elapsed time:", tt.time() - t0)
|
||||
|
||||
|
||||
# enddef
|
||||
|
||||
print("...... %ph_add_falcon ... to addd falcon to configuration")
|
||||
|
||||
|
||||
@register_line_magic
|
||||
def ph_add_falcon(line):
|
||||
"""
|
||||
magic to add falcon to existing configuration
|
||||
"""
|
||||
t0 = tt.time()
|
||||
phoenix.add_falcon()
|
||||
print("elapsed time:", tt.time() - t0)
|
||||
|
||||
|
||||
## enddef
|
||||
|
||||
print("...... %ph_load_falcon ... to load falcon ")
|
||||
|
||||
|
||||
@register_line_magic
|
||||
def ph_load_falcon(line):
|
||||
"""
|
||||
magic to load falcon as sole detector
|
||||
"""
|
||||
t0 = tt.time()
|
||||
phoenix.load_falcon()
|
||||
print("elapsed time:", tt.time() - t0)
|
||||
|
||||
|
||||
print("...... %ph_add_xmap ... to add xmap to default configuration")
|
||||
|
||||
|
||||
@register_line_magic
|
||||
def ph_add_xmap(line):
|
||||
"""
|
||||
magic to add falcon to existing configuration
|
||||
"""
|
||||
t0 = tt.time()
|
||||
phoenix.add_xmap()
|
||||
print("elapsed time:", tt.time() - t0)
|
||||
|
||||
|
||||
## enddef
|
||||
|
||||
print("...... %ph_load_xmap ... to load xmap ")
|
||||
|
||||
|
||||
@register_line_magic
|
||||
def ph_load_xmap(line):
|
||||
"""
|
||||
magic to load falcon as sole detector
|
||||
"""
|
||||
t0 = tt.time()
|
||||
phoenix.load_xmap()
|
||||
print("elapsed time:", tt.time() - t0)
|
||||
|
||||
|
||||
print("...... %ph_create_base_config ...load base devices ")
|
||||
|
||||
|
||||
@register_line_magic
|
||||
def ph_create_base_config(line):
|
||||
"""
|
||||
load base configuration for PHOENIX beamline
|
||||
using phoenix.create_base_config()
|
||||
phoenix_bec/device_configs/phoenix_devices.yaml
|
||||
"""
|
||||
t0 = tt.time()
|
||||
phoenix.create_base_config()
|
||||
print("elapsed time:", tt.time() - t0)
|
||||
|
||||
|
||||
### enddef
|
||||
|
||||
print("...... %ph_restart_bec_server restarts bec-server in new terminal ")
|
||||
|
||||
|
||||
@register_line_magic
|
||||
def ph_restart_bec_server(line):
|
||||
os.system("bec-server restart")
|
||||
os.system(
|
||||
'gnome-terminal --geometry 100X30 -- bash -c "source /data/test/x07mb-test-bec/production/bec_venv/bin/activate ; bec-server attach ; exec bash"'
|
||||
)
|
||||
# os.system(
|
||||
# "gnome-terminal --geometry 170X50 -- bash -c "source / tmux attach -t bec ; exec bash""
|
||||
# )
|
||||
|
||||
|
||||
# #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.Phoenix(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()
|
||||
pheonix = phoenix # take care os a frequent typo
|
||||
|
||||
print("")
|
||||
print("## HELP ########### HELP #### HELP ")
|
||||
print("")
|
||||
print(" for help type phoenix.help() ")
|
||||
|
@ -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
|
@ -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})
|
||||
|
||||
|
||||
|
282
phoenix_bec/device_configs/current_devices_tmp.yaml
Normal file
282
phoenix_bec/device_configs/current_devices_tmp.yaml
Normal file
@ -0,0 +1,282 @@
|
||||
|
||||
PH_TTL:
|
||||
description: PHOENIX TTL trigger
|
||||
deviceClass: phoenix_bec.devices.phoenix_trigger.PhoenixTrigger
|
||||
deviceConfig:
|
||||
prefix: 'X07MB-OP2:'
|
||||
deviceTags:
|
||||
- phoenix
|
||||
- TTL Trigger
|
||||
- phoenix_devices.yaml
|
||||
- class PhoenixTrigger
|
||||
onFailure: buffer
|
||||
enabled: true
|
||||
readoutPriority: monitored
|
||||
softwareTrigger: true
|
||||
|
||||
|
||||
###################################################
|
||||
#
|
||||
# phoenix standard devices (motors)
|
||||
#
|
||||
#
|
||||
####################################################:
|
||||
|
||||
PH_Dummy:
|
||||
description: Class to test functionality of PSI detector. reads/writes into X07MB-PC-PSCAN.P-P0D0"
|
||||
deviceClass: phoenix_bec.devices.dummy_devices.Dummy_PSIDetector
|
||||
deviceConfig:
|
||||
prefix: 'X07MB-PC-PSCAN:'
|
||||
name: 'Dummy_Detector_PSI_Detector'
|
||||
deviceTags:
|
||||
- phoenix
|
||||
- phoenix_devices.yaml
|
||||
- class Dummy_PSIDetector
|
||||
- reads channel X07MB-PC-PSCAN.P-P0D0
|
||||
- Dummy class to test PSI detector c from DAQ GUI
|
||||
onFailure: buffer
|
||||
enabled: true
|
||||
readoutPriority: monitored
|
||||
softwareTrigger: false
|
||||
|
||||
|
||||
|
||||
############################
|
||||
#
|
||||
# MOTORS ES1
|
||||
#
|
||||
############################
|
||||
|
||||
MA1_ScanX:
|
||||
readoutPriority: baseline
|
||||
description: 'Vertical sample position ES-MA1.ScanX'
|
||||
deviceClass: ophyd.EpicsMotor
|
||||
deviceConfig:
|
||||
prefix: 'X07MB-ES-MA1:ScanX'
|
||||
deviceTags:
|
||||
- ES-MA1.ScanX
|
||||
- phoenix_devices.yaml
|
||||
- class EpicsMotor
|
||||
onFailure: retry
|
||||
enabled: false
|
||||
readOnly: false
|
||||
|
||||
MA1_ScanY:
|
||||
readoutPriority: baseline
|
||||
description: 'Horizontal sample position ES-MA1.ScanY'
|
||||
deviceClass: ophyd.EpicsMotor
|
||||
deviceConfig:
|
||||
prefix: 'X07MB-ES-MA1:ScanY'
|
||||
deviceTags:
|
||||
- ES-MA1.ScanY
|
||||
- phoenix_devices.yaml
|
||||
- class EpicsMotor
|
||||
onFailure: retry
|
||||
enabled: false
|
||||
readOnly: false
|
||||
softwareTrigger: false
|
||||
|
||||
|
||||
MA1_ROT:
|
||||
readoutPriority: baseline
|
||||
description: 'Horizontal sample position ES-MA1.ROT'
|
||||
deviceClass: ophyd.EpicsMotor
|
||||
deviceConfig:
|
||||
prefix: 'X07MB-ES-MA1:ROT'
|
||||
deviceTags:
|
||||
- ES-MA1.ROT
|
||||
- phoenix_devices.yaml
|
||||
- class EpicsMotor
|
||||
onFailure: retry
|
||||
enabled: false
|
||||
readOnly: false
|
||||
softwareTrigger: false
|
||||
|
||||
|
||||
MA1_TRZ1:
|
||||
readoutPriority: baseline
|
||||
description: 'position ES-MA1.ROT'
|
||||
deviceClass: ophyd.EpicsMotor
|
||||
deviceConfig:
|
||||
prefix: 'X07MB-ES-MA1:TRZ1'
|
||||
deviceTags:
|
||||
- ES-MA1.TRZ1
|
||||
- phoenix_devices.yaml
|
||||
- class EpicsMotor
|
||||
onFailure: retry
|
||||
enabled: false
|
||||
readOnly: false
|
||||
softwareTrigger: false
|
||||
|
||||
|
||||
|
||||
MA1_TRX1:
|
||||
readoutPriority: baseline
|
||||
description: 'position ES-MA1.TRX1'
|
||||
deviceClass: ophyd.EpicsMotor
|
||||
deviceConfig:
|
||||
prefix: 'X07MB-ES-MA1:TRX1'
|
||||
deviceTags:
|
||||
- ES-MA1.TRX1
|
||||
- phoenix_devices.yaml
|
||||
- class EpicsMotor
|
||||
onFailure: retry
|
||||
enabled: false
|
||||
readOnly: false
|
||||
softwareTrigger: false
|
||||
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
# DIODES from ES1 ADC
|
||||
#
|
||||
#
|
||||
|
||||
SAI_01_MEAN:
|
||||
readoutPriority: monitored
|
||||
description: DIODE SAI01
|
||||
deviceClass: ophyd.EpicsSignalRO
|
||||
deviceConfig:
|
||||
auto_monitor: true
|
||||
read_pv: 'X07MB-OP2-SAI_01:MEAN'
|
||||
deviceTags:
|
||||
- PHOENIX
|
||||
- phoenix_devices.yaml
|
||||
- class EpicsSignalRO
|
||||
onFailure: buffer
|
||||
enabled: true
|
||||
readOnly: true
|
||||
softwareTrigger: false
|
||||
|
||||
SAI_02_MEAN:
|
||||
readoutPriority: monitored
|
||||
description: DIODE SAI02
|
||||
deviceClass: ophyd.EpicsSignalRO
|
||||
deviceConfig:
|
||||
auto_monitor: true
|
||||
read_pv: 'X07MB-OP2-SAI_02:MEAN'
|
||||
deviceTags:
|
||||
- PHOENIX
|
||||
- phoenix_devices.yaml
|
||||
- class EpicsSignalRO
|
||||
onFailure: buffer
|
||||
enabled: true
|
||||
readOnly: true
|
||||
softwareTrigger: false
|
||||
|
||||
SAI_03_MEAN:
|
||||
readoutPriority: monitored
|
||||
description: DIODE SAI03
|
||||
deviceClass: ophyd.EpicsSignalRO
|
||||
deviceConfig:
|
||||
auto_monitor: true
|
||||
read_pv: 'X07MB-OP2-SAI_03:MEAN'
|
||||
deviceTags:
|
||||
- PHOENIX
|
||||
- phoenix_devices.yaml
|
||||
- class EpicsSignalRO
|
||||
onFailure: buffer
|
||||
enabled: true
|
||||
readOnly: true
|
||||
softwareTrigger: false
|
||||
|
||||
SAI_04_MEAN:
|
||||
readoutPriority: monitored
|
||||
description: DIODE SAI04
|
||||
deviceClass: ophyd.EpicsSignalRO
|
||||
deviceConfig:
|
||||
auto_monitor: true
|
||||
read_pv: 'X07MB-OP2-SAI_04:MEAN'
|
||||
deviceTags:
|
||||
- PHOENIX
|
||||
- phoenix_devices.yaml
|
||||
- class EpicsSignalRO
|
||||
onFailure: buffer
|
||||
enabled: true
|
||||
readOnly: true
|
||||
softwareTrigger: false
|
||||
|
||||
SAI_05_MEAN:
|
||||
readoutPriority: monitored
|
||||
description: DIODE SAI05
|
||||
deviceClass: ophyd.EpicsSignalRO
|
||||
deviceConfig:
|
||||
auto_monitor: true
|
||||
read_pv: 'X07MB-OP2-SAI_05:MEAN'
|
||||
deviceTags:
|
||||
- PHOENIX
|
||||
- phoenix_devices.yaml
|
||||
- class EpicsSignalRO
|
||||
onFailure: buffer
|
||||
enabled: true
|
||||
readOnly: true
|
||||
softwareTrigger: false
|
||||
|
||||
SAI_06_MEAN:
|
||||
readoutPriority: monitored
|
||||
description: DIODE SAI06
|
||||
deviceClass: ophyd.EpicsSignalRO
|
||||
deviceConfig:
|
||||
auto_monitor: true
|
||||
read_pv: 'X07MB-OP2-SAI_06:MEAN'
|
||||
deviceTags:
|
||||
- PHOENIX
|
||||
- phoenix_devices.yaml
|
||||
- class EpicsSignalRO
|
||||
onFailure: buffer
|
||||
enabled: true
|
||||
readOnly: true
|
||||
softwareTrigger: false
|
||||
|
||||
SAI_07_MEAN:
|
||||
readoutPriority: monitored
|
||||
description: DIODE SAI07
|
||||
deviceClass: ophyd.EpicsSignalRO
|
||||
deviceConfig:
|
||||
auto_monitor: true
|
||||
read_pv: 'X07MB-OP2-SAI_07:MEAN'
|
||||
deviceTags:
|
||||
- PHOENIX
|
||||
- phoenix_devices.yaml
|
||||
- class EpicsSignalRO
|
||||
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_devices.yaml
|
||||
- class EpicsSignalRO
|
||||
onFailure: buffer
|
||||
enabled: true
|
||||
readOnly: true
|
||||
softwareTrigger: false
|
||||
|
||||
#
|
||||
# END OF STANDARD CONFIG
|
||||
#
|
||||
|
||||
MA1_TRZ1:
|
||||
readoutPriority: baseline
|
||||
description: 'Horizontal sample position ES-MA1.ROT'
|
||||
deviceClass: ophyd.EpicsMotor
|
||||
deviceConfig:
|
||||
prefix: 'X07MB-ES-MA1:TRZ1'
|
||||
deviceTags:
|
||||
- ES-MA1.TRZ1
|
||||
- phoenix_devices.yaml
|
||||
- class EpicsMotor
|
||||
onFailure: retry
|
||||
enabled: false
|
||||
readOnly: false
|
||||
softwareTrigger: false
|
||||
|
313
phoenix_bec/device_configs/phoenix_devices.yaml
Normal file
313
phoenix_bec/device_configs/phoenix_devices.yaml
Normal file
@ -0,0 +1,313 @@
|
||||
|
||||
PH_TTL:
|
||||
description: PHOENIX TTL TRIGGER
|
||||
deviceClass: phoenix_bec.devices.phoenix_trigger.PhoenixTrigger
|
||||
deviceConfig:
|
||||
prefix: 'X07MB-OP2:'
|
||||
deviceTags:
|
||||
- phoenix
|
||||
- TTL Trigger
|
||||
- phoenix_devices.yaml
|
||||
- class PhoenixTrigger
|
||||
onFailure: buffer
|
||||
enabled: false
|
||||
readoutPriority: monitored
|
||||
softwareTrigger: true
|
||||
|
||||
PH_SCAN_1:
|
||||
description: PHOENIX Scan_1 (Experimental)
|
||||
deviceClass: phoenix_bec.devices.phoenix_scan_1.PhoenixScan_1
|
||||
deviceConfig:
|
||||
prefix: 'X07MB-OP2:'
|
||||
deviceTags:
|
||||
- phoenix
|
||||
- TTL Trigger
|
||||
- phoenix_devices.yaml
|
||||
- class PhoenixTrigger
|
||||
onFailure: buffer
|
||||
enabled: false
|
||||
readoutPriority: monitored
|
||||
softwareTrigger: true
|
||||
###################################################
|
||||
#
|
||||
# phoenix standard devices (motors)
|
||||
#
|
||||
#
|
||||
####################################################:
|
||||
|
||||
PH_Dummy:
|
||||
description: Class to test functionality of PSI detector. reads/writes into X07MB-PC-PSCAN.P-P0D0"
|
||||
deviceClass: phoenix_bec.devices.dummy_devices.Dummy_PSIDetector
|
||||
deviceConfig:
|
||||
prefix: 'X07MB-PC-PSCAN:'
|
||||
name: 'Dummy_Detector_PSI_Detector'
|
||||
deviceTags:
|
||||
- phoenix
|
||||
- phoenix_devices.yaml
|
||||
- class Dummy_PSIDetector
|
||||
- reads channel X07MB-PC-PSCAN.P-P0D0
|
||||
- Dummy class to test PSI detector c from DAQ GUI
|
||||
onFailure: buffer
|
||||
enabled: true
|
||||
readoutPriority: monitored
|
||||
softwareTrigger: false
|
||||
|
||||
|
||||
|
||||
############################
|
||||
#
|
||||
# MOTORS ES1
|
||||
#
|
||||
############################
|
||||
|
||||
MA1_ScanX:
|
||||
readoutPriority: baseline
|
||||
description: 'Vertical sample position ES-MA1.ScanX'
|
||||
deviceClass: ophyd.EpicsMotor
|
||||
deviceConfig:
|
||||
prefix: 'X07MB-ES-MA1:ScanX'
|
||||
deviceTags:
|
||||
- ES-MA1.ScanX
|
||||
- phoenix_devices.yaml
|
||||
- class EpicsMotor
|
||||
onFailure: retry
|
||||
enabled: false
|
||||
readOnly: false
|
||||
|
||||
MA1_ScanY:
|
||||
readoutPriority: baseline
|
||||
description: 'Horizontal sample position ES-MA1.ScanY'
|
||||
deviceClass: ophyd.EpicsMotor
|
||||
deviceConfig:
|
||||
prefix: 'X07MB-ES-MA1:ScanY'
|
||||
deviceTags:
|
||||
- ES-MA1.ScanY
|
||||
- phoenix_devices.yaml
|
||||
- class EpicsMotor
|
||||
onFailure: retry
|
||||
enabled: false
|
||||
readOnly: false
|
||||
softwareTrigger: false
|
||||
|
||||
|
||||
MA1_ROT:
|
||||
readoutPriority: baseline
|
||||
description: 'Horizontal sample position ES-MA1.ROT'
|
||||
deviceClass: ophyd.EpicsMotor
|
||||
deviceConfig:
|
||||
prefix: 'X07MB-ES-MA1:ROT'
|
||||
deviceTags:
|
||||
- ES-MA1.ROT
|
||||
- phoenix_devices.yaml
|
||||
- class EpicsMotor
|
||||
onFailure: retry
|
||||
enabled: false
|
||||
readOnly: false
|
||||
softwareTrigger: false
|
||||
|
||||
|
||||
MA1_TRZ1:
|
||||
readoutPriority: baseline
|
||||
description: 'position ES-MA1.ROT'
|
||||
deviceClass: ophyd.EpicsMotor
|
||||
deviceConfig:
|
||||
prefix: 'X07MB-ES-MA1:TRZ1'
|
||||
deviceTags:
|
||||
- ES-MA1.TRZ1
|
||||
- phoenix_devices.yaml
|
||||
- class EpicsMotor
|
||||
onFailure: retry
|
||||
enabled: false
|
||||
readOnly: false
|
||||
softwareTrigger: false
|
||||
|
||||
|
||||
|
||||
MA1_TRX1:
|
||||
readoutPriority: baseline
|
||||
description: 'position ES-MA1.TRX1'
|
||||
deviceClass: ophyd.EpicsMotor
|
||||
deviceConfig:
|
||||
prefix: 'X07MB-ES-MA1:TRX1'
|
||||
deviceTags:
|
||||
- ES-MA1.TRX1
|
||||
- phoenix_devices.yaml
|
||||
- class EpicsMotor
|
||||
onFailure: retry
|
||||
enabled: false
|
||||
readOnly: false
|
||||
softwareTrigger: false
|
||||
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
# DIODES from ES1 ADC
|
||||
#
|
||||
#
|
||||
|
||||
PP2_VO5:
|
||||
readoutPriority: baseline
|
||||
description: Chamber light
|
||||
deviceClass: ophyd.EpicsSignal
|
||||
deviceConfig:
|
||||
auto_monitor: true
|
||||
read_pv: 'X07MB-ES1-PP2:VO5'
|
||||
deviceTags:
|
||||
- PHOENIX
|
||||
- phoenix_devices.yaml
|
||||
- class EpicsSignal
|
||||
onFailure: buffer
|
||||
enabled: true
|
||||
readOnly: false
|
||||
softwareTrigger: false
|
||||
|
||||
|
||||
SAI_01_MEAN:
|
||||
readoutPriority: baseline
|
||||
description: DIODE SAI01
|
||||
deviceClass: ophyd.EpicsSignalRO
|
||||
deviceConfig:
|
||||
auto_monitor: true
|
||||
read_pv: 'X07MB-OP2-SAI_01:MEAN'
|
||||
deviceTags:
|
||||
- PHOENIX
|
||||
- phoenix_devices.yaml
|
||||
- class EpicsSignalRO
|
||||
onFailure: buffer
|
||||
enabled: true
|
||||
readOnly: true
|
||||
softwareTrigger: false
|
||||
|
||||
SAI_02_MEAN:
|
||||
readoutPriority: baseline
|
||||
description: DIODE SAI02
|
||||
deviceClass: ophyd.EpicsSignalRO
|
||||
deviceConfig:
|
||||
auto_monitor: true
|
||||
read_pv: 'X07MB-OP2-SAI_02:MEAN'
|
||||
deviceTags:
|
||||
- PHOENIX
|
||||
- phoenix_devices.yaml
|
||||
- class EpicsSignalRO
|
||||
onFailure: buffer
|
||||
enabled: true
|
||||
readOnly: true
|
||||
softwareTrigger: false
|
||||
|
||||
|
||||
SAI_03_MEAN:
|
||||
readoutPriority: baseline
|
||||
description: DIODE SAI03
|
||||
deviceClass: ophyd.EpicsSignalRO
|
||||
deviceConfig:
|
||||
auto_monitor: true
|
||||
read_pv: 'X07MB-OP2-SAI_03:MEAN'
|
||||
deviceTags:
|
||||
- PHOENIX
|
||||
- phoenix_devices.yaml
|
||||
- class EpicsSignalRO
|
||||
onFailure: buffer
|
||||
enabled: true
|
||||
readOnly: true
|
||||
softwareTrigger: false
|
||||
|
||||
SAI_04_MEAN:
|
||||
readoutPriority: baseline
|
||||
description: DIODE SAI04
|
||||
deviceClass: ophyd.EpicsSignalRO
|
||||
deviceConfig:
|
||||
auto_monitor: true
|
||||
read_pv: 'X07MB-OP2-SAI_04:MEAN'
|
||||
deviceTags:
|
||||
- PHOENIX
|
||||
- phoenix_devices.yaml
|
||||
- class EpicsSignalRO
|
||||
onFailure: buffer
|
||||
enabled: true
|
||||
readOnly: true
|
||||
softwareTrigger: false
|
||||
|
||||
SAI_05_MEAN:
|
||||
readoutPriority: baseline
|
||||
description: DIODE SAI05
|
||||
deviceClass: ophyd.EpicsSignalRO
|
||||
deviceConfig:
|
||||
auto_monitor: true
|
||||
read_pv: 'X07MB-OP2-SAI_05:MEAN'
|
||||
deviceTags:
|
||||
- PHOENIX
|
||||
- phoenix_devices.yaml
|
||||
- class EpicsSignalRO
|
||||
onFailure: buffer
|
||||
enabled: true
|
||||
readOnly: true
|
||||
softwareTrigger: false
|
||||
|
||||
SAI_06_MEAN:
|
||||
readoutPriority: baseline
|
||||
description: DIODE SAI06
|
||||
deviceClass: ophyd.EpicsSignalRO
|
||||
deviceConfig:
|
||||
auto_monitor: true
|
||||
read_pv: 'X07MB-OP2-SAI_06:MEAN'
|
||||
deviceTags:
|
||||
- PHOENIX
|
||||
- phoenix_devices.yaml
|
||||
- class EpicsSignalRO
|
||||
onFailure: buffer
|
||||
enabled: true
|
||||
readOnly: true
|
||||
softwareTrigger: false
|
||||
|
||||
SAI_07_MEAN:
|
||||
readoutPriority: baseline
|
||||
description: DIODE SAI07
|
||||
deviceClass: ophyd.EpicsSignalRO
|
||||
deviceConfig:
|
||||
auto_monitor: true
|
||||
read_pv: 'X07MB-OP2-SAI_07:MEAN'
|
||||
deviceTags:
|
||||
- PHOENIX
|
||||
- phoenix_devices.yaml
|
||||
- class EpicsSignalRO
|
||||
onFailure: buffer
|
||||
enabled: true
|
||||
readOnly: true
|
||||
softwareTrigger: false
|
||||
|
||||
SAI_08_MEAN:
|
||||
readoutPriority: baseline
|
||||
description: DIODE
|
||||
deviceClass: ophyd.EpicsSignalRO
|
||||
deviceConfig:
|
||||
auto_monitor: true
|
||||
read_pv: 'X07MB-OP2-SAI_08:MEAN'
|
||||
deviceTags:
|
||||
- PHOENIX
|
||||
- phoenix_devices.yaml
|
||||
- class EpicsSignalRO
|
||||
onFailure: buffer
|
||||
enabled: true
|
||||
readOnly: true
|
||||
softwareTrigger: false
|
||||
|
||||
#
|
||||
# END OF STANDARD CONFIG
|
||||
#
|
||||
|
||||
MA1_TRZ1:
|
||||
readoutPriority: baseline
|
||||
description: 'Horizontal sample position ES-MA1.ROT'
|
||||
deviceClass: ophyd.EpicsMotor
|
||||
deviceConfig:
|
||||
prefix: 'X07MB-ES-MA1:TRZ1'
|
||||
deviceTags:
|
||||
- ES-MA1.TRZ1
|
||||
- phoenix_devices.yaml
|
||||
- class EpicsMotor
|
||||
onFailure: retry
|
||||
enabled: false
|
||||
readOnly: false
|
||||
softwareTrigger: false
|
||||
|
23
phoenix_bec/device_configs/phoenix_falcon.yaml
Normal file
23
phoenix_bec/device_configs/phoenix_falcon.yaml
Normal file
@ -0,0 +1,23 @@
|
||||
|
||||
#
|
||||
# falcon without hdf5
|
||||
#
|
||||
falcon:
|
||||
description: Falcon detector x-ray fluoresence with hdf5 plugin from device class phoenix_bec.devices. falcon_phoenix.FalconPhoenix
|
||||
deviceClass: phoenix_bec.devices.phoenix_falcon.FalconPhoenix
|
||||
deviceConfig:
|
||||
prefix: 'X07MB-SITORO:'
|
||||
deviceTags:
|
||||
- phoenix
|
||||
- falcon
|
||||
- with hdf5
|
||||
- phoenix_falcon.yaml
|
||||
- class FalconPhoenix
|
||||
- file:phoenix_falcon.py
|
||||
onFailure: buffer
|
||||
enabled: true
|
||||
readoutPriority: async
|
||||
softwareTrigger: false
|
||||
#
|
||||
# END FALCON with HDF5
|
||||
#
|
19
phoenix_bec/device_configs/phoenix_xmap.yaml
Normal file
19
phoenix_bec/device_configs/phoenix_xmap.yaml
Normal file
@ -0,0 +1,19 @@
|
||||
#
|
||||
# Configuration XMAP
|
||||
#
|
||||
xmap:
|
||||
description: XMAP detector x-ray fluoresence
|
||||
deviceClass: phoenix_bec.devices.phoenix_xmap.XMAPPhoenix
|
||||
deviceConfig:
|
||||
prefix: 'X07MB-XMAP:'
|
||||
deviceTags:
|
||||
- phoenix
|
||||
- xmap
|
||||
- phoenix_bec/device_configs/phoenix_xmap.yaml
|
||||
- class XMAPPhoenix
|
||||
- file phoenix_xmap
|
||||
onFailure: buffer
|
||||
enabled: true
|
||||
readoutPriority: async
|
||||
softwareTrigger: false
|
||||
|
@ -0,0 +1 @@
|
||||
from .phoenix_trigger import PhoenixTrigger
|
||||
|
@ -3,3 +3,30 @@
|
||||
### phoenix_bec
|
||||
| Device | Documentation | Module |
|
||||
| :----- | :------------- | :------ |
|
||||
| Dummy_PSIDetector | <br> Abstract base class for SLS detectors<br><br> Class attributes:<br> custom_prepare_cls (object): class for custom prepare logic (BL specific)<br><br> Args:<br> prefix (str): EPICS PV prefix for component (optional)<br> name (str): name of the device, as will be reported via read()<br> kind (str): member of class 'ophydobj.Kind', defaults to Kind.normal<br> omitted -> reado_PSIDetectorBase<br> | [phoenix_bec.devices.dummy_devices](https://gitlab.psi.ch/bec/phoenix_bec/-/blob/main/phoenix_bec/devices/dummy_devices.py) |
|
||||
| EpicsDXPFalcon | <br> DXP parameters for Falcon detector<br><br> Base class to map EPICS PVs from DXP parameters to ophyd signals.<br> | [phoenix_bec.devices.falcon_phoenix_no_hdf5](https://gitlab.psi.ch/bec/phoenix_bec/-/blob/main/phoenix_bec/devices/falcon_phoenix_no_hdf5.py) |
|
||||
| EpicsDXPXMAP | <br> DXP parameters for XMAP detector<br><br> Base class to map EPICS PVs from DXP parameters to ophyd signals.<br> | [phoenix_bec.devices.xmap_phoenix_no_hdf5](https://gitlab.psi.ch/bec/phoenix_bec/-/blob/main/phoenix_bec/devices/xmap_phoenix_no_hdf5.py) |
|
||||
| EpicsMCARecordExtended | | [phoenix_bec.devices.falcon_phoenix](https://gitlab.psi.ch/bec/phoenix_bec/-/blob/main/phoenix_bec/devices/falcon_phoenix.py) |
|
||||
| FalconcSAXS | <br> Falcon Sitoro detector for CSAXS<br><br> Parent class: PSIDetectorBase<br><br> class attributes:<br> custom_prepare_cls (FalconSetup) : Custom detector setup class for cSAXS,<br> inherits from CustomDetectorMixin<br> PSIDetectorBase.set_min_readout (float) : Minimum readout time for the detector<br> dxp (EpicsDXPFalcon) : DXP parameters for Falcon detector<br> mca (EpicsMCARecord) : MCA parameters for Falcon detector<br> hdf5 (FalconHDF5Plugins) : HDF5 parameters for Falcon detector<br> MIN_READOUT (float) : Minimum readout time for the detector<br> | [phoenix_bec.devices.falcon_csaxs_original](https://gitlab.psi.ch/bec/phoenix_bec/-/blob/main/phoenix_bec/devices/falcon_csaxs_original.py) |
|
||||
| FalconHDF5Plugins | <br> HDF5 parameters for Falcon detector<br><br> Base class to map EPICS PVs from HDF5 Plugin to ophyd signals.<br> | [phoenix_bec.devices.falcon_phoenix_no_hdf5](https://gitlab.psi.ch/bec/phoenix_bec/-/blob/main/phoenix_bec/devices/falcon_phoenix_no_hdf5.py) |
|
||||
| FalconPhoenix | <br> Falcon detector for phoenix<br> custom_prepare_cls (XMAPSetu<br> custom_prepare_cls (XMAPSetup) : Custom detector setup class for cSAXS,<br> inherits from CustomDetectorMixin<br> in __init__ of PSIDetecor base<br> PSIDetectorBase.set_min_readout (float) : Minimum readout time for the detector<br> dxp (EpicsDXPXMAP) : DXP parameters for XMAP detector<br> mca (EpicsMCARecord) : MCA parameters for XMAP detector<br> hdf5 (XMAPHDF5Plugins) : HDF5 parameters for XMAP detector<br> MIN_READOUT (float) : Minimum readout time for the detector<br> | [phoenix_bec.devices.falcon_phoenix_no_hdf5](https://gitlab.psi.ch/bec/phoenix_bec/-/blob/main/phoenix_bec/devices/falcon_phoenix_no_hdf5.py) |
|
||||
| PhoenixTrigger | <br> Class for PHOENIX TTL hardware trigger: 'X07MB-OP2:'<br><br> This device is used to trigger communicate with an ADC card<br> that creates TTL signals to trigger cameras and detectors at Phoenix.<br><br> | [phoenix_bec.devices.phoenix_trigger](https://gitlab.psi.ch/bec/phoenix_bec/-/blob/main/phoenix_bec/devices/phoenix_trigger.py) |
|
||||
| ROI | | [phoenix_bec.devices.sitoro](https://gitlab.psi.ch/bec/phoenix_bec/-/blob/main/phoenix_bec/devices/sitoro.py) |
|
||||
| SitoroEpicsDXP | All high-level DXP parameters for each channel | [phoenix_bec.devices.sitoro](https://gitlab.psi.ch/bec/phoenix_bec/-/blob/maiself.file_devices_tmn/phoenix_bec/devices/sitoro.py) |
|
||||
| SitoroEpicsDXP_OLD | <br> DXP parameters for Sitoro detector<br><br> Base class to map EPICS PVs from DXP parameters to ophyd signals.<br> | [phoenix_bec.devices.sitoro_phoenix](https://gitlab.psi.ch/bec/phoenix_bec/-/blob/main/phoenix_bec/devices/sitoro_phoenix.py) |
|
||||
| SitoroEpicsDXPBaseSystem | | [phoenix_bec.devices.sitoro](https://gitlab.psi.ch/bec/phoenix_bec/-/blob/main/phoenix_bec/devices/sitoro.py) |
|
||||
| SitoroEpicsDXPLowLevel | | [phoenix_bec.devices.sitoro](https://gitlab.psi.ch/bec/phoenix_bec/-/blob/main/phoenix_bec/devices/sitoro.py) |
|
||||
| SitoroEpicsDXPLowLevelParameter | | [phoenix_bec.devices.sitoro](https://gitlab.psi.ch/bec/phoenix_bec/-/blob/main/phoenix_bec/devices/sitoro.py) |
|
||||
| SitoroEpicsDXPMapping | | [phoenix_bec.devices.sitoro](https://gitlab.psi.ch/bec/phoenix_bec/-/blob/main/phoenix_bec/devices/sitoro.py) |
|
||||
| SitoroEpicsDXPMultiElementSystem | | [phoenix_bec.devices.sitoro](https://gitlab.psi.ch/bec/phoenix_bec/-/blob/main/phoenix_bec/devices/sitoro.py) |
|
||||
| SitoroEpicsMCA | mca records with extras from mca.db | [phoenix_bec.devices.sitoro](https://gitlab.psi.ch/bec/phoenix_bec/-/blob/main/phoenix_bec/devices/sitoro.py) |
|
||||
| SitoroEpicsMCACallback | Callback-related signals for MCA devices | [phoenix_bec.devices.sitoro](https://gitlab.psi.ch/bec/phoenix_bec/-/blob/main/phoenix_bec/devices/sitoro.py) |
|
||||
| SitoroEpicsMCAReadNotify | mca record with extras from mcaReadNotify.db | [phoenix_bec.devices.sitoro](https://gitlab.psi.ch/bec/phoenix_bec/-/blob/main/phoenix_bec/devices/sitoro.py) |
|
||||
| SitoroEpicsMCARecord | SynApps MCA Record interface | [phoenix_bec.devices.sitoro](https://gitlab.psi.ch/bec/phoenix_bec/-/blob/main/phoenix_bec/devices/sitoro.py) |
|
||||
| SitoroEpicsMCARecordExtended_OLD | | [phoenix_bec.devices.sitoro_phoenix](https://gitlab.psi.ch/bec/phoenix_bec/-/blob/main/phoenix_bec/devices/sitoro_phoenix.py) |
|
||||
| SitoroHDF5Plugins | <br> HDF5 parameters for Sitoro detector<br><br> Base class to map EPICS PVs from HDF5 Plugin to ophyd signals.<br> | [phoenix_bec.devices.sitoro_phoenix](https://gitlab.psi.ch/bec/phoenix_bec/-/blob/main/phoenix_bec/devices/sitoro_phoenix.py) |
|
||||
| SitoroPhoenix | <br> Sitoro Sitoro detector for Phoenix<br><br><br> Parent class: PSIDetectorBase<br><br> class attributes:<br> custom_prepare_cls (SitoroSetup) : Custom detector setup class,<br> inherits from CustomDetectorMixin<br><br> PSIDetectorBase.set_min_readout (float) : Minimum readout time for the detector<br> dxp1, .. dxpi, .. , dxpN (SitoroEpicsDXP) : DXP parameters for Sitoro detector Nr i<br> mca1, .. mcai, .. , mcaN (SitoroEpicsMCARecord) : MCA parameters for Sitoro detector Nr i<br><br> hdf5 (SitoroHDF5Plugins) : HDF5 parameters for Sitoro detector<br> MIN_READOUT (float) : Minimum readout time for the detector<br><br><br><br> | [phoenix_bec.devices.sitoro_phoenix](https://gitlab.psi.ch/bec/phoenix_bec/-/blob/main/phoenix_bec/devices/sitoro_phoenix.py) |
|
||||
| SitoroSoftDXPTrigger | Simple soft trigger for DXP devices<br><br> Parameters<br> ----------<br> count_signal : str, optional<br> Signal to set acquisition time (default: 'preset_real_time')<br> preset_mode : str, optional<br> Default preset mode for the stage signals (default: 'Real time')<br> mode_signal : str, optional<br> Preset mode signal attribute (default 'preset_mode')<br> stop_signal : str, optional<br> Stop signal attribute (default 'stop_all')<br> | [phoenix_bec.devices.sitoro](https://gitlab.psi.ch/bec/phoenix_bec/-/blob/main/phoenix_bec/devices/sitoro.py) |
|
||||
| SitoroTest | | [phoenix_bec.devices.sitoro](https://gitlab.psi.ch/bec/phoenix_bec/-/blob/main/phoenix_bec/devices/sitoro.py) |
|
||||
| XMAPHDF5Plugins | <br> HDF5 parameters for XMAP detector<br><br> Base class to map EPICS PVs from HDF5 Plugin to ophyd signals.<br> | [phoenix_bec.devices.xmap_phoenix_no_hdf5](https://gitlab.psi.ch/bec/phoenix_bec/-/blob/main/phoenix_bec/devices/xmap_phoenix_no_hdf5.py) |
|
||||
| XMAPPhoenix | MCA<br> XMAP detector for phoenix<br> custom_prepare_cls (XMAPSetu<br> custom_prepare_cls (XMAPSetup) : Custom detector setup class for cSAXS,<br> inherits from CustomDetectorMixin<br> in __init__ of PSIDetecor base<br> PSIDetectorBase.set_min_readout (float) : Minimum readout time for the detector<br> dxp (EpicsDXPXMAP) : DXP parameters for XMAP detector<br> mca (EpicsMCARecord) : MCA parameters for XMAP detector<br> hdf5 (XMAPHDF5Plugins) : HDF5 parameters for XMAP detector<br> MIN_READOUT (float) : Minimum readout time for the detector<br> | [phoenix_bec.devices.xmap_phoenix_no_hdf5](https://gitlab.psi.ch/bec/phoenix_bec/-/blob/main/phoenix_bec/devices/xmap_phoenix_no_hdf5.py) |
|
||||
|
499
phoenix_bec/devices/dummy_devices.py
Normal file
499
phoenix_bec/devices/dummy_devices.py
Normal file
@ -0,0 +1,499 @@
|
||||
"""
|
||||
This is a copy of psi_detecto_base.py
|
||||
with added print sigbnals to understand how it functions
|
||||
|
||||
"""
|
||||
|
||||
import os
|
||||
import threading
|
||||
import time
|
||||
import traceback
|
||||
|
||||
from bec_lib import messages
|
||||
from bec_lib.endpoints import MessageEndpoints
|
||||
from bec_lib.file_utils import FileWriter
|
||||
from bec_lib.logger import bec_logger
|
||||
from ophyd import Component
|
||||
from ophyd import Component as Cpt
|
||||
from ophyd import Device, DeviceStatus, EpicsSignal, EpicsSignalRO
|
||||
from ophyd import FormattedComponent as FCpt
|
||||
from ophyd import Kind
|
||||
from ophyd.device import Staged
|
||||
from ophyd_devices.interfaces.base_classes.psi_detector_base import (
|
||||
CustomDetectorMixin,
|
||||
PSIDetectorBase,
|
||||
)
|
||||
from ophyd_devices.sim.sim_signals import SetableSignal
|
||||
from ophyd_devices.utils import bec_utils
|
||||
from ophyd_devices.utils.bec_scaninfo_mixin import BecScaninfoMixin
|
||||
from ophyd_devices.utils.errors import DeviceStopError, DeviceTimeoutError
|
||||
|
||||
from phoenix_bec.scripts.phoenix import PhoenixBL
|
||||
|
||||
logger = bec_logger.logger
|
||||
|
||||
|
||||
# class LogTime():
|
||||
|
||||
# def __init__(self):
|
||||
# self.t0=time.time()
|
||||
|
||||
# def p_s(self,x):
|
||||
# now=time.time()
|
||||
# #delta=now-self.t0
|
||||
# m=str(now)+' sec '+x
|
||||
# logger.success(m)
|
||||
# #self.t0=now
|
||||
# file=open('MyLogfile.txt','a')
|
||||
# file.write(m+'\n')
|
||||
# file.close
|
||||
|
||||
|
||||
p_s = PhoenixBL.my_log
|
||||
|
||||
|
||||
class DetectorInitError(Exception):
|
||||
"""Raised when initiation of the device class fails,
|
||||
due to missing device manager or not started in sim_mode."""
|
||||
|
||||
|
||||
class SetupDummy(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:
|
||||
""" """
|
||||
|
||||
def on_stage(self) -> None:
|
||||
"""e 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 = None,
|
||||
) -> 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): T t_offset = 1724683600 # subtract some arbtrary offset from the time value
|
||||
rue 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
|
||||
"""
|
||||
if exception_on_timeout is None:
|
||||
exception_on_timeout = DeviceTimeoutError(
|
||||
f"Timeout error for {self.parent.name} while waiting for signals {signal_conditions}"
|
||||
)
|
||||
|
||||
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,
|
||||
):
|
||||
"""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:
|
||||
if self.parent.stopped:
|
||||
# INFO This will execute a callback to the parent device.stop() method
|
||||
status.set_exception(exc=DeviceStopError(f"{self.parent.name} was stopped"))
|
||||
else:
|
||||
# INFO This will execute a callback to the parent device.stop() method
|
||||
status.set_exception(exc=exception_on_timeout)
|
||||
# pylint: disable=broad-except
|
||||
except Exception as exc:
|
||||
content = traceback.format_exc()
|
||||
logger.warning(
|
||||
f"Error in wait_for_signals in {self.parent.name}; Traceback: {content}"
|
||||
)
|
||||
# INFO This will execute a callback to the parent device.stop() method
|
||||
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 Dummy_PSIDetector(PSIDetectorBase):
|
||||
"""
|
||||
Abstract base class for SLS detasyn_pipeline_configectors
|
||||
|
||||
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 -> reado_PSIDetectorBase
|
||||
"""
|
||||
|
||||
filepath = Component(SetableSignal, value="", kind=Kind.config)
|
||||
|
||||
custom_prepare_cls = SetupDummy
|
||||
|
||||
# prefix=X07MB-PC-PSCAN
|
||||
|
||||
D = Cpt(EpicsSignal, "P-P0D0") # cont on / off
|
||||
|
||||
def __init__(self, prefix="", *, name, kind=None, parent=None, device_manager=None, **kwargs):
|
||||
|
||||
self.p_s = PhoenixBL.my_log # must be before super!!!
|
||||
|
||||
self.p_s("Dummy_device Dummy_PSIDetector.__init__ ")
|
||||
|
||||
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
|
||||
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()
|
||||
# .. prepare my own log file
|
||||
|
||||
self.p_s("Dummy_device Dummy_PSIDetector.__init__ .. done ")
|
||||
|
||||
def _update_filewriter(self) -> None:
|
||||
"""Update filewriter with service config"""
|
||||
self.p_s("Dummy_device Dummy_PSIDetector._update_filewriter")
|
||||
self.filewriter = FileWriter(service_config=self.service_cfg, connector=self.connector)
|
||||
self.p_s("Dummy_device Dummy_PSIDetector._update_filewriter .. done ")
|
||||
|
||||
def _update_scaninfo(self) -> None:
|
||||
"""Update scaninfo from BecScaninfoMixing
|
||||
This depends on device manager and operation/sim_mode
|
||||
"""
|
||||
self.p_s("Dummy_device Dummy_PSIDetector._update_scaninfo")
|
||||
|
||||
self.scaninfo = BecScaninfoMixin(self.device_manager)
|
||||
self.scaninfo.load_scan_metadata()
|
||||
self.p_s("Dummy_device Dummy_PSIDetector._update_scaninfo .. done ")
|
||||
|
||||
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
|
||||
|
||||
self.p_s("Dummy_device Dummy_PSIDetector._update_service_config")
|
||||
|
||||
if SERVICE_CONFIG:
|
||||
self.service_cfg = SERVICE_CONFIG.config["service_config"]["file_writer"]
|
||||
return
|
||||
self.service_cfg = {"base_path": os.path.abspath(".")}
|
||||
self.p_s("Dummy_device Dummy_PSIDetector._update_service_config .. done")
|
||||
|
||||
def check_scan_id(self) -> None:
|
||||
"""Checks if scan_id has changed and set stopped flagged to True if it has."""
|
||||
self.p_s("Dummy_device Dummy_PSIDetector.check_scan_id")
|
||||
|
||||
old_scan_id = self.scaninfo.scan_id
|
||||
self.scaninfo.load_scan_metadata()
|
||||
if self.scaninfo.scan_id != old_scan_id:
|
||||
self.stopped = True
|
||||
self.p_s("Dummy_device Dummy_PSIDetector.check_scan_id .. done ")
|
||||
|
||||
def _init(self) -> None:
|
||||
"""Initialize detector, filewriter and set default parameters"""
|
||||
self.p_s("Dummy_device Dummy_PSIDetector._init")
|
||||
|
||||
self.custom_prepare.on_init()
|
||||
self.p_s("Dummy_device Dummy_PSIDetector._init ... done ")
|
||||
|
||||
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
|
||||
|
||||
"""
|
||||
self.p_s("Dummy_device Dummy_PSIDetector.stage")
|
||||
|
||||
if self._staged != Staged.no:
|
||||
return super().stage()
|
||||
self.stopped = False
|
||||
self.scaninfo.load_scan_metadata()
|
||||
self.custom_prepare.on_stage()
|
||||
self.p_s("Dummy_device Dummy_PSIDetector.stage done ")
|
||||
|
||||
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.p_s("Dummy_device Dummy_PSIDetector.pre_scan")
|
||||
|
||||
self.custom_prepare.on_pre_scan()
|
||||
self.p_s("Dummy_device Dummy_PSIDetector.pre_scan .. done ")
|
||||
|
||||
def trigger(self) -> DeviceStatus:
|
||||
"""Trigger the detector, called from BEC."""
|
||||
|
||||
# pylint: disable=assignment-from-no-return
|
||||
self.p_s("Dummy_device Dummy_PSIDetector.trigger")
|
||||
|
||||
status = self.custom_prepare.on_trigger()
|
||||
if isinstance(status, DeviceStatus):
|
||||
return status
|
||||
self.p_s("Dummy_device Dummy_PSIDetector.trigger.. done ")
|
||||
|
||||
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
|
||||
self.p_s("Dummy_device Dummy_PSIDetector.complete")
|
||||
|
||||
status = self.custom_prepare.on_complete()
|
||||
if isinstance(status, DeviceStatus):
|
||||
return status
|
||||
status = DeviceStatus(self)
|
||||
status.set_finished()
|
||||
self.p_s("Dummy_device Dummy_PSIDetector.complete ... done ")
|
||||
|
||||
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.p_s("Dummy_device Dummy_PSIDetector.unstage")
|
||||
self.check_scan_id()
|
||||
self.custom_prepare.on_unstage()
|
||||
self.stopped = False
|
||||
self.p_s("Dummy_device Dummy_PSIDetector.unstage .. done")
|
||||
|
||||
return super().unstage()
|
||||
|
||||
def stop(self, *, success=False) -> None:
|
||||
"""
|
||||
Stop the scan, with camera and file writer
|
||||
|
||||
"""
|
||||
self.p_s("Dummy_device Dummy_PSIDetector.stop")
|
||||
self.custom_prepare.on_stop()
|
||||
super().stop(success=success)
|
||||
self.stopped = True
|
||||
self.p_s("Dummy_device Dummy_PSIDetector.stop ... done")
|
207
phoenix_bec/devices/dxp_loc.py
Normal file
207
phoenix_bec/devices/dxp_loc.py
Normal file
@ -0,0 +1,207 @@
|
||||
"""
|
||||
This file dxp_loc.py is a local copy of dxp.py, as relased by Christian on Dec 5th 2024, as take from git
|
||||
https://gitlab.psi.ch/bec/ophyd_devices/-/tree/main/ophyd_devices/devices?ref_type=heads
|
||||
use local copy for now, as we likely need new deploymen... to complicated for me for now.
|
||||
|
||||
|
||||
|
||||
Base classes for XIA xMAP and FalconX dxp system.
|
||||
Falcon interfaces with the dxpSITORO epics driver, https://github.com/epics-modules/dxpSITORO.
|
||||
xMAP interfaces with the dxp epics driver, https://github.com/epics-modules/dxp.
|
||||
|
||||
An example usage for a 4-element FalconX system. ::
|
||||
|
||||
from ophyd import Component as Cpt
|
||||
from ophyd_devices.devices.dxp import Falcon, EpicsMCARecord, EpicsDXPFalcon
|
||||
from ophyd_devices.devices.areadetector.plugins import HDF5Plugin_V35 as HDF5Plugin
|
||||
|
||||
class FalconX4(Falcon):
|
||||
# DXP parameters
|
||||
dxp1 = Cpt(EpicsDXPFalcon, "dxp1:")
|
||||
dxp2 = Cpt(EpicsDXPFalcon, "dxp2:")
|
||||
dxp3 = Cpt(EpicsDXPFalcon, "dxp3:")
|
||||
dxp4 = Cpt(EpicsDXPFalcon, "dxp4:")
|
||||
|
||||
# MCA record with spectrum data
|
||||
mca1 = Cpt(EpicsMCARecord, "mca1")
|
||||
mca2 = Cpt(EpicsMCARecord, "mca2")
|
||||
mca3 = Cpt(EpicsMCARecord, "mca3")
|
||||
mca4 = Cpt(EpicsMCARecord, "mca4")
|
||||
|
||||
# optionally with a HDF5 writer plugin
|
||||
hdf = Cpt(HDF5Plugin, "HDF1:")
|
||||
|
||||
falcon = FalconX4("X07MB-SITORO:", name="falcon")
|
||||
falcon.collect_mode.put(0) # 0: MCA spectra, 1: MCA mapping
|
||||
falcon.preset_mode.put("Real time")
|
||||
falcon.preset_real_time.put(1)
|
||||
status = falcon.erase_start.set(1)
|
||||
status.wait()
|
||||
falcon.mca1.spectrum.get()
|
||||
|
||||
"""
|
||||
|
||||
from ophyd import Component as Cpt
|
||||
from ophyd import Device, EpicsSignal, EpicsSignalRO, Kind
|
||||
from ophyd.areadetector import ADBase
|
||||
from ophyd.areadetector import ADComponent as ADCpt
|
||||
from ophyd.areadetector import EpicsSignalWithRBV
|
||||
from ophyd.mca import EpicsDXP, EpicsDXPBaseSystem, EpicsDXPMapping
|
||||
from ophyd.mca import EpicsDXPMultiElementSystem as _EpicsDXPMultiElementSystem
|
||||
from ophyd.mca import EpicsMCARecord as _EpicsMCARecord
|
||||
|
||||
__all__ = ("EpicsMCARecord", "EpicsDXP", "EpicsDXPFalcon", "Falcon", "Mercury", "xMAP")
|
||||
|
||||
|
||||
class EpicsMCARecord(_EpicsMCARecord):
|
||||
"""EpicsMCARecord with addtional fields"""
|
||||
|
||||
# Calibration values
|
||||
calo = Cpt(EpicsSignal, ".CALO", kind=Kind.config)
|
||||
cals = Cpt(EpicsSignal, ".CALS", kind=Kind.config)
|
||||
calq = Cpt(EpicsSignal, ".CALQ", kind=Kind.config)
|
||||
tth = Cpt(EpicsSignal, ".TTH", kind=Kind.config)
|
||||
|
||||
|
||||
class EpicsDXPFalcon(Device):
|
||||
"""All high-level DXP parameters for each channel"""
|
||||
|
||||
# Detection
|
||||
detection_filter = Cpt(EpicsSignalWithRBV, "DetectionFilter")
|
||||
detection_threshold = Cpt(EpicsSignalWithRBV, "DetectionThreshold")
|
||||
min_pulse_pair_separation = Cpt(EpicsSignalWithRBV, "MinPulsePairSeparation")
|
||||
|
||||
# Pre-amp and energe range
|
||||
detector_polarity = Cpt(EpicsSignalWithRBV, "DetectorPolarity")
|
||||
decay_time = Cpt(EpicsSignalWithRBV, "DecayTime")
|
||||
risetime_optimization = Cpt(EpicsSignalWithRBV, "RisetimeOptimization")
|
||||
scale_factor = Cpt(EpicsSignalWithRBV, "ScaleFactor")
|
||||
|
||||
# Presets
|
||||
preset_events = Cpt(EpicsSignalWithRBV, "PresetEvents")
|
||||
preset_mode = Cpt(EpicsSignalWithRBV, "PresetMode", string=True)
|
||||
preset_triggers = Cpt(EpicsSignalWithRBV, "PresetTriggers")
|
||||
preset_real_time = Cpt(EpicsSignalWithRBV, "PresetReal")
|
||||
|
||||
# Couting statistics
|
||||
elapsed_live_time = Cpt(EpicsSignalRO, "ElapsedLiveTime", lazy=True)
|
||||
elapsed_real_time = Cpt(EpicsSignalRO, "ElapsedRealTime", lazy=True)
|
||||
elapsed_trigger_live = Cpt(EpicsSignalRO, "ElapsedTriggerLiveTime", lazy=True)
|
||||
triggers = Cpt(EpicsSignalRO, "Triggers", lazy=True)
|
||||
events = Cpt(EpicsSignalRO, "Events", lazy=True)
|
||||
input_count_rate = Cpt(EpicsSignalRO, "InputCountRate", lazy=True)
|
||||
output_count_rate = Cpt(EpicsSignalRO, "OutputCountRate", lazy=True)
|
||||
|
||||
# Mapping
|
||||
current_pixel = Cpt(EpicsSignal, "CurrentPixel")
|
||||
|
||||
# Diagnostic trace
|
||||
trace_data = Cpt(EpicsSignal, "TraceData")
|
||||
|
||||
|
||||
class EpicsDXPFalconMultiElementSystem(EpicsDXPBaseSystem):
|
||||
"""System-wide parameters as defined in dxpMED.template"""
|
||||
|
||||
# Preset control
|
||||
preset_events = Cpt(EpicsSignal, "PresetEvents")
|
||||
preset_real_time = Cpt(EpicsSignal, "PresetReal")
|
||||
preset_mode = Cpt(EpicsSignal, "PresetMode", string=True)
|
||||
preset_triggers = Cpt(EpicsSignal, "PresetTriggers")
|
||||
|
||||
# Acquisition control
|
||||
erase_all = Cpt(EpicsSignal, "EraseAll")
|
||||
erase_start = Cpt(EpicsSignal, "EraseStart", put_complete=True, trigger_value=1)
|
||||
start_all = Cpt(EpicsSignal, "StartAll", put_complete=True, trigger_value=1)
|
||||
stop_all = Cpt(EpicsSignal, "StopAll")
|
||||
|
||||
# Status
|
||||
set_acquire_busy = Cpt(EpicsSignal, "SetAcquireBusy")
|
||||
acquire_busy = Cpt(EpicsSignal, "AcquireBusy")
|
||||
status_all = Cpt(EpicsSignal, "StatusAll")
|
||||
status_all_once = Cpt(EpicsSignal, "StatusAllOnce")
|
||||
acquiring = Cpt(EpicsSignal, "Acquiring")
|
||||
|
||||
# Reading
|
||||
read_all = Cpt(EpicsSignal, "ReadAll", kind=Kind.omitted)
|
||||
read_all_once = Cpt(EpicsSignal, "ReadAllOnce", kind=Kind.omitted)
|
||||
|
||||
# As a debugging note, if snl_connected is not '1', your IOC is
|
||||
# misconfigured:
|
||||
snl_connected = Cpt(EpicsSignal, "SNL_Connected")
|
||||
|
||||
# High-level parameters
|
||||
copy_decay_time = Cpt(EpicsSignal, "CopyDecayTime", kind=Kind.omitted)
|
||||
copy_detection_filter = Cpt(EpicsSignal, "CopyDetectionFilter", kind=Kind.omitted)
|
||||
copy_detection_threshold = Cpt(EpicsSignal, "CopyDetectionThreshold", kind=Kind.omitted)
|
||||
copy_detector_polarity = Cpt(EpicsSignal, "CopyDetectorPolarity", kind=Kind.omitted)
|
||||
copy_min_pulse_pair_separation = Cpt(
|
||||
EpicsSignal, "CopyMinPulsePairSeparation", kind=Kind.omitted
|
||||
)
|
||||
copt_risetime_optimization = Cpt(EpicsSignal, "CopyRisetimeOptimization", kind=Kind.omitted)
|
||||
copy_scale_factor = Cpt(EpicsSignal, "CopyScaleFactor", kind=Kind.omitted)
|
||||
read_traces = Cpt(EpicsSignal, "ReadTraces", kind=Kind.omitted)
|
||||
|
||||
# ROI and SCA
|
||||
copy_roi_channel = Cpt(EpicsSignal, "CopyROIChannel", kind=Kind.omitted)
|
||||
copy_roi_energy = Cpt(EpicsSignal, "CopyROIEnergy", kind=Kind.omitted)
|
||||
copy_roi_sca = Cpt(EpicsSignal, "CopyROI_SCA", kind=Kind.omitted)
|
||||
|
||||
# do_* executes the process:
|
||||
do_read_all = Cpt(EpicsSignal, "DoReadAll", kind=Kind.omitted)
|
||||
do_status_all = Cpt(EpicsSignal, "DoStatusAll", kind=Kind.omitted)
|
||||
do_read_traces = Cpt(EpicsSignal, "DoReadTraces", kind=Kind.omitted)
|
||||
|
||||
# Statistics
|
||||
dead_time = Cpt(EpicsSignal, "DeadTime")
|
||||
idead_time = Cpt(EpicsSignal, "IDeadTime")
|
||||
max_elapsed_live = Cpt(EpicsSignal, "MaxElapsedLive")
|
||||
max_elapsed_real = Cpt(EpicsSignal, "MaxElapsedReal")
|
||||
max_elapsed_trigger_live = Cpt(EpicsSignal, "MaxElapsedTriggerLive")
|
||||
max_triggers = Cpt(EpicsSignal, "MaxTriggers")
|
||||
max_events = Cpt(EpicsSignal, "MaxEvents")
|
||||
max_input_count_rate = Cpt(EpicsSignal, "MaxInputCountRate")
|
||||
max_output_count_rate = Cpt(EpicsSignal, "MaxOutputCountRate")
|
||||
|
||||
|
||||
class EpicsDxpFalconMapping(EpicsDXPMapping):
|
||||
"""Mapping mode parameters as defined in dxpMapping.template"""
|
||||
|
||||
auto_apply = None
|
||||
apply = None
|
||||
nd_array_mode = Cpt(EpicsSignalWithRBV, "NDArrayMode")
|
||||
|
||||
|
||||
class Falcon(EpicsDXPFalconMultiElementSystem, EpicsDxpFalconMapping, ADBase):
|
||||
"""Falcon base device"""
|
||||
|
||||
# attribute required by ADBase
|
||||
port_name = ADCpt(EpicsSignalRO, "Asyn.PORT", string=True)
|
||||
|
||||
|
||||
class EpicsDXPMultiElementSystem(_EpicsDXPMultiElementSystem):
|
||||
"""System-wide parameters as defined in dxpMED.template"""
|
||||
|
||||
# Override some action signals, so calling `set`` method
|
||||
# returns a waitable Status object. Otherwise the Status object is immediately done.
|
||||
erase_start = Cpt(EpicsSignal, "EraseStart", put_complete=True, trigger_value=1)
|
||||
start_all = Cpt(EpicsSignal, "StartAll", put_complete=True, trigger_value=1)
|
||||
|
||||
# mca.EpicsDXPMultiElementSystem maps the EPICS records under wrong names, i.e.
|
||||
# copy_adcp_ercent_rule, copy_roic_hannel and copy_roie_nergy
|
||||
copy_adc_percent_rule = Cpt(EpicsSignal, "CopyADCPercentRule")
|
||||
copy_roi_channel = Cpt(EpicsSignal, "CopyROIChannel")
|
||||
copy_roi_energy = Cpt(EpicsSignal, "CopyROIEnergy")
|
||||
|
||||
|
||||
class Mercury(EpicsDXPMultiElementSystem, ADBase):
|
||||
"""Mercury base device"""
|
||||
|
||||
# attribute required by ADBase
|
||||
port_name = ADCpt(EpicsSignalRO, "Asyn.PORT", string=True)
|
||||
|
||||
|
||||
class xMAP(EpicsDXPMultiElementSystem, EpicsDXPMapping, ADBase):
|
||||
"""xMAP base device"""
|
||||
|
||||
# attribute required by ADBase
|
||||
port_name = ADCpt(EpicsSignalRO, "Asyn.PORT", string=True)
|
171
phoenix_bec/devices/dxp_old.py
Normal file
171
phoenix_bec/devices/dxp_old.py
Normal file
@ -0,0 +1,171 @@
|
||||
"""
|
||||
Base classes for XIA xMAP and FalconX dxp system.
|
||||
Falcon interfaces with the dxpSITORO epics driver, https://github.com/epics-modules/dxpSITORO.
|
||||
xMAP interfaces with the dxp epics driver, https://github.com/epics-modules/dxp.
|
||||
|
||||
An example usage for a 4-element FalconX system. ::
|
||||
|
||||
from ophyd import Component as Cpt
|
||||
from ophyd_devices.devices.dxp import Falcon, EpicsMCARecord, EpicsDXPFalcon
|
||||
from ophyd_devices.devices.areadetector.plugins import HDF5Plugin_V35 as HDF5Plugin
|
||||
|
||||
class FalconX4(Falcon):
|
||||
# DXP parameters
|
||||
dxp1 = Cpt(EpicsDXPFalcon, "dxp1:")
|
||||
dxp2 = Cpt(EpicsDXPFalcon, "dxp2:")
|
||||
dxp3 = Cpt(EpicsDXPFalcon, "dxp3:")
|
||||
dxp4 = Cpt(EpicsDXPFalcon, "dxp4:")
|
||||
|
||||
# MCA record with spectrum data
|
||||
mca1 = Cpt(EpicsMCARecord, "mca1")
|
||||
mca2 = Cpt(EpicsMCARecord, "mca2")
|
||||
mca3 = Cpt(EpicsMCARecord, "mca3")
|
||||
mca4 = Cpt(EpicsMCARecord, "mca4")
|
||||
|
||||
# optionally with a HDF5 writer plugin
|
||||
hdf = Cpt(HDF5Plugin, "HDF1:")
|
||||
|
||||
falcon = FalconX4("X07MB-SITORO:", name="falcon")
|
||||
falcon.collect_mode.put(0) # 0: MCA spectra, 1: MCA mapping
|
||||
falcon.preset_mode.put("Real time")
|
||||
falcon.preset_real_time.put(1)
|
||||
status = falcon.erase_start.set(1)
|
||||
status.wait()
|
||||
falcon.mca1.spectrum.get()
|
||||
|
||||
"""
|
||||
|
||||
from ophyd import Component as Cpt
|
||||
from ophyd import Kind, EpicsSignal, EpicsSignalRO, Device
|
||||
from ophyd.mca import (
|
||||
EpicsMCARecord as _EpicsMCARecord,
|
||||
EpicsDXPBaseSystem,
|
||||
EpicsDXPMultiElementSystem,
|
||||
EpicsDXPMapping,
|
||||
)
|
||||
from ophyd.areadetector import EpicsSignalWithRBV
|
||||
|
||||
__all__ = ("EpicsMCARecord", "EpicsDXPFalcon", "Falcon", "xMAP")
|
||||
|
||||
|
||||
class EpicsMCARecord(_EpicsMCARecord):
|
||||
"""EpicsMCARecord with addtional fields"""
|
||||
|
||||
calo = Cpt(EpicsSignal, ".CALO")
|
||||
cals = Cpt(EpicsSignal, ".CALS")
|
||||
calq = Cpt(EpicsSignal, ".CALQ")
|
||||
tth = Cpt(EpicsSignal, ".TTH")
|
||||
|
||||
|
||||
class EpicsDXPFalcon(Device):
|
||||
"""All high-level DXP parameters for each channel"""
|
||||
|
||||
# Detection
|
||||
detection_filter = Cpt(EpicsSignalWithRBV, "DetectionFilter")
|
||||
detection_threshold = Cpt(EpicsSignalWithRBV, "DetectionThreshold")
|
||||
min_pulse_pair_separation = Cpt(EpicsSignalWithRBV, "MinPulsePairSeparation")
|
||||
|
||||
# Pre-amp and energe range
|
||||
detector_polarity = Cpt(EpicsSignalWithRBV, "DetectorPolarity")
|
||||
decay_time = Cpt(EpicsSignalWithRBV, "DecayTime")
|
||||
risetime_optimization = Cpt(EpicsSignalWithRBV, "RisetimeOptimization")
|
||||
scale_factor = Cpt(EpicsSignalWithRBV, "ScaleFactor")
|
||||
|
||||
# Presets
|
||||
preset_events = Cpt(EpicsSignalWithRBV, "PresetEvents")
|
||||
preset_mode = Cpt(EpicsSignalWithRBV, "PresetMode", string=True)
|
||||
preset_triggers = Cpt(EpicsSignalWithRBV, "PresetTriggers")
|
||||
preset_real_time = Cpt(EpicsSignalWithRBV, "PresetReal")
|
||||
|
||||
# Couting statistics
|
||||
elapsed_live_time = Cpt(EpicsSignalRO, "ElapsedLiveTime", lazy=True)
|
||||
elapsed_real_time = Cpt(EpicsSignalRO, "ElapsedRealTime", lazy=True)
|
||||
elapsed_trigger_live = Cpt(EpicsSignalRO, "ElapsedTriggerLiveTime", lazy=True)
|
||||
triggers = Cpt(EpicsSignalRO, "Triggers", lazy=True)
|
||||
events = Cpt(EpicsSignalRO, "Events", lazy=True)
|
||||
input_count_rate = Cpt(EpicsSignalRO, "InputCountRate", lazy=True)
|
||||
output_count_rate = Cpt(EpicsSignalRO, "OutputCountRate", lazy=True)
|
||||
|
||||
# Mapping
|
||||
current_pixel = Cpt(EpicsSignal, "CurrentPixel")
|
||||
|
||||
# Diagnostic trace
|
||||
trace_data = Cpt(EpicsSignal, "TraceData")
|
||||
|
||||
|
||||
class EpicsDXPFalconMultiElementSystem(EpicsDXPBaseSystem):
|
||||
# Preset control
|
||||
preset_events = Cpt(EpicsSignal, "PresetEvents")
|
||||
preset_real_time = Cpt(EpicsSignal, "PresetReal")
|
||||
preset_mode = Cpt(EpicsSignal, "PresetMode", string=True)
|
||||
preset_triggers = Cpt(EpicsSignal, "PresetTriggers")
|
||||
|
||||
# Acquisition control
|
||||
erase_all = Cpt(EpicsSignal, "EraseAll")
|
||||
erase_start = Cpt(EpicsSignal, "EraseStart", put_complete=True, trigger_value=1)
|
||||
start_all = Cpt(EpicsSignal, "StartAll", put_complete=True, trigger_value=1)
|
||||
stop_all = Cpt(EpicsSignal, "StopAll")
|
||||
|
||||
# Status
|
||||
set_acquire_busy = Cpt(EpicsSignal, "SetAcquireBusy")
|
||||
acquire_busy = Cpt(EpicsSignal, "AcquireBusy")
|
||||
status_all = Cpt(EpicsSignal, "StatusAll")
|
||||
status_all_once = Cpt(EpicsSignal, "StatusAllOnce")
|
||||
acquiring = Cpt(EpicsSignal, "Acquiring")
|
||||
|
||||
# Reading
|
||||
read_all = Cpt(EpicsSignal, "ReadAll", kind=Kind.omitted)
|
||||
read_all_once = Cpt(EpicsSignal, "ReadAllOnce", kind=Kind.omitted)
|
||||
|
||||
# As a debugging note, if snl_connected is not '1', your IOC is
|
||||
# misconfigured:
|
||||
snl_connected = Cpt(EpicsSignal, "SNL_Connected")
|
||||
|
||||
# High-level parameters
|
||||
copy_decay_time = Cpt(EpicsSignal, "CopyDecayTime", kind=Kind.omitted)
|
||||
copy_detection_filter = Cpt(EpicsSignal, "CopyDetectionFilter", kind=Kind.omitted)
|
||||
copy_detection_threshold = Cpt(EpicsSignal, "CopyDetectionThreshold", kind=Kind.omitted)
|
||||
copy_detector_polarity = Cpt(EpicsSignal, "CopyDetectorPolarity", kind=Kind.omitted)
|
||||
copy_min_pulse_pair_separation = Cpt(
|
||||
EpicsSignal, "CopyMinPulsePairSeparation", kind=Kind.omitted
|
||||
)
|
||||
copt_risetime_optimization = Cpt(EpicsSignal, "CopyRisetimeOptimization", kind=Kind.omitted)
|
||||
copy_scale_factor = Cpt(EpicsSignal, "CopyScaleFactor", kind=Kind.omitted)
|
||||
read_traces = Cpt(EpicsSignal, "ReadTraces", kind=Kind.omitted)
|
||||
|
||||
# ROI and SCA
|
||||
copy_roic_hannel = Cpt(EpicsSignal, "CopyROIChannel", kind=Kind.omitted)
|
||||
copy_roie_nergy = Cpt(EpicsSignal, "CopyROIEnergy", kind=Kind.omitted)
|
||||
copy_roi_sca = Cpt(EpicsSignal, "CopyROI_SCA", kind=Kind.omitted)
|
||||
|
||||
# do_* executes the process:
|
||||
do_read_all = Cpt(EpicsSignal, "DoReadAll", kind=Kind.omitted)
|
||||
do_status_all = Cpt(EpicsSignal, "DoStatusAll", kind=Kind.omitted)
|
||||
do_read_traces = Cpt(EpicsSignal, "DoReadTraces", kind=Kind.omitted)
|
||||
|
||||
# Statistics
|
||||
dead_time = Cpt(EpicsSignal, "DeadTime")
|
||||
idead_time = Cpt(EpicsSignal, "IDeadTime")
|
||||
max_elapsed_live = Cpt(EpicsSignal, "MaxElapsedLive")
|
||||
max_elapsed_real = Cpt(EpicsSignal, "MaxElapsedReal")
|
||||
max_elapsed_trigger_live = Cpt(EpicsSignal, "MaxElapsedTriggerLive")
|
||||
max_triggers = Cpt(EpicsSignal, "MaxTriggers")
|
||||
max_events = Cpt(EpicsSignal, "MaxEvents")
|
||||
max_input_count_rate = Cpt(EpicsSignal, "MaxInputCountRate")
|
||||
max_output_count_rate = Cpt(EpicsSignal, "MaxOutputCountRate")
|
||||
|
||||
|
||||
class EpicsDxpFalconMapping(EpicsDXPMapping):
|
||||
auto_apply = None
|
||||
apply = None
|
||||
nd_array_mode = Cpt(EpicsSignalWithRBV, "NDArrayMode")
|
||||
|
||||
|
||||
class Falcon(EpicsDXPFalconMultiElementSystem, EpicsDxpFalconMapping): ...
|
||||
|
||||
|
||||
class xMAP(EpicsDXPMultiElementSystem, EpicsDXPMapping):
|
||||
# Override signals from EpicsDXPMultiElementSystem, so calling `set`` method
|
||||
# returns a waitable Status object. Otherwise the Status object is immediately done.
|
||||
erase_start = Cpt(EpicsSignal, "EraseStart", put_complete=True, trigger_value=1)
|
||||
start_all = Cpt(EpicsSignal, "StartAll", put_complete=True, trigger_value=1)
|
349
phoenix_bec/devices/obsolete/falcon_csaxs_original.py
Normal file
349
phoenix_bec/devices/obsolete/falcon_csaxs_original.py
Normal 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)
|
468
phoenix_bec/devices/obsolete/falcon_phoenix_backup.py
Normal file
468
phoenix_bec/devices/obsolete/falcon_phoenix_backup.py
Normal file
@ -0,0 +1,468 @@
|
||||
"""
|
||||
Implementation for falcon at PHOENIX, derived from
|
||||
implementation on csaxs (file falcon_csaxs.py)
|
||||
|
||||
17.10.2024 try to streamline implementation with mca record
|
||||
|
||||
|
||||
Differences to implement
|
||||
|
||||
1) we consider EPICS initialization as standard implementaion,
|
||||
so no reinitialization when bec device is initrialized ... DONE ...
|
||||
|
||||
2) in EpicsDXPFalcon(Device) add ICR and OCR for individual detectors
|
||||
|
||||
3) can we make this generic to make it suited for both falcon and XMAP ?
|
||||
|
||||
3) make easy switching between mca spectra an mca mapping
|
||||
|
||||
fix defiend relation bwetween variables and names used here for example DONE
|
||||
|
||||
aquiring is currently called 'state' --> should be renamed to aquiring
|
||||
Currently state = Cpt(EpicsSignal, "Acquiring")
|
||||
should be acquiring = Cpt(EpicsSignal, "Acquiring")
|
||||
|
||||
|
||||
hdf5 = Cpt(FalconHDF5Plugins, "HDF1:")
|
||||
|
||||
def arm_aquisition
|
||||
|
||||
raise FalconTimeoutError(
|
||||
f"Failed to arm the acquisition. Detector state {signal_conditions[0][0]}"
|
||||
)
|
||||
|
||||
|
||||
|
||||
CHANGES LOG and
|
||||
|
||||
System as taken from cSAXS some times works for one element need about 7 second
|
||||
There seem to be still serious timout issues
|
||||
|
||||
changes log
|
||||
TIMEOUT_FOR_SIGNALs from 5 to 10
|
||||
|
||||
|
||||
|
||||
|
||||
"""
|
||||
|
||||
import enum
|
||||
import threading
|
||||
import time
|
||||
|
||||
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 # old import
|
||||
# now import ophyd.mca completely
|
||||
|
||||
import ophyd.mca as Mca
|
||||
|
||||
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 EpicsMCARecordExtended(Mca.EpicsMCARecord):
|
||||
|
||||
# add parameters for detector energy calibration
|
||||
# which are missing in mca.py
|
||||
|
||||
calo = Cpt(EpicsSignal, ".CALO")
|
||||
cals = Cpt(EpicsSignal, ".CALS")
|
||||
calq = Cpt(EpicsSignal, ".CALQ")
|
||||
tth = Cpt(EpicsSignal, ".TTH")
|
||||
|
||||
|
||||
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")
|
||||
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 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 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"""
|
||||
|
||||
pass
|
||||
|
||||
"""
|
||||
THIS IS THE OLD CSACS CODE. uncomment for now, as we consider EPICS as the boss
|
||||
for initialization
|
||||
|
||||
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.acquiring.read()[self.parent.acquiring.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)
|
||||
time.sleep(0.5)
|
||||
self.parent.erase_all.put(1)
|
||||
time.sleep(0.5)
|
||||
|
||||
signal_conditions = [
|
||||
(
|
||||
lambda: self.parent.acquiring.read()[self.parent.acquiring.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 FalconPhoenix(PSIDetectorBase):
|
||||
"""
|
||||
Falcon Sitoro detector for Phoenix
|
||||
|
||||
|
||||
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 = 1
|
||||
|
||||
# specify class attributes
|
||||
|
||||
# Parameters for individual detector elements
|
||||
# Note: need to wrote 'dxp: here, but not dxp'
|
||||
|
||||
# dxp1 = Cpt(Mca.EpicsDXP, "dxp1:")
|
||||
dxp1 = Cpt(EpicsDXPFalcon, "dxp1:")
|
||||
# dxp2 = Cpt(EpicsDXPFalcon, "dxp2:")
|
||||
# dxp3 = Cpt(EpicsDXPFalcon, "dxp3:")
|
||||
# dxp4 = Cpt(EpicsDXPFalcon, "dxp4:")
|
||||
|
||||
#
|
||||
# THIS IS NOT WELL-DONE as it take out one part of mca.py from ophy.py
|
||||
#
|
||||
#
|
||||
mca1 = Cpt(EpicsMCARecordExtended, "mca1")
|
||||
# mca2 = Cpt(EpicsMCARecordExtended, "mca2")
|
||||
# mca3 = Cpt(EpicsMCARecordExtended, "mca3")
|
||||
# mca4 = Cpt(EpicsMCARecordExtended, "mca4")
|
||||
|
||||
# need to write 'mca1', but not 'mca1:'
|
||||
# mca1 = Cpt(EpicsMCARecord, "mca1")
|
||||
# mca2 = Cpt(EpicsMCARecord, "mca2")
|
||||
# mca3 = Cpt(EpicsMCARecord, "mca3")
|
||||
# mca4 = Cpt(EpicsMCARecord, "mca4")
|
||||
|
||||
# other general parameters
|
||||
hdf5 = Cpt(FalconHDF5Plugins, "HDF1:")
|
||||
|
||||
stop_all = Cpt(EpicsSignal, "StopAll")
|
||||
erase_all = Cpt(EpicsSignal, "EraseAll")
|
||||
start_all = Cpt(EpicsSignal, "StartAll")
|
||||
# state = Cpt(EpicsSignal, "Acquiring") # <-- This is from cSAX implementation
|
||||
acquiring = 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")
|
||||
|
||||
# _________________ General Epic parameters
|
||||
|
||||
# changes Oct 2024
|
||||
# triggers--> max_triggers,
|
||||
# events-->max_events
|
||||
# input_count_rate--> max_input_count_rate
|
||||
# output_count_rate--> max_output_count_rate
|
||||
|
||||
max_triggers = Cpt(EpicsSignalRO, "MaxTriggers", lazy=True)
|
||||
max_events = Cpt(EpicsSignalRO, "MaxEvents", lazy=True)
|
||||
|
||||
max_input_count_rate = Cpt(EpicsSignalRO, "MaxInputCountRate", lazy=True)
|
||||
max_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")
|
||||
# print(pixel_per_run
|
||||
# if "SITORO" in prefix:
|
||||
nd_array_mode = Cpt(EpicsSignal, "NDArrayMode")
|
||||
# endif
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
falcon = FalconPhoenix(name="falcon_hdf5", prefix="X07MB-SITORO:", sim_mode=True)
|
367
phoenix_bec/devices/obsolete/falcon_phoenix_no_hdf5.py
Normal file
367
phoenix_bec/devices/obsolete/falcon_phoenix_no_hdf5.py
Normal file
@ -0,0 +1,367 @@
|
||||
#
|
||||
#
|
||||
# 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.SPECTRUM, trigger_source=TriggerSource.GATE, ignore_gate=0
|
||||
)
|
||||
# 1 Realtime
|
||||
self.parent.preset_mode.put(0)
|
||||
# 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 on_trigger(self) -> None:
|
||||
"""Actions on pre_scan. This is performed AFTER stage, just before scan_core"""
|
||||
self.arm_acquisition()
|
||||
|
||||
def prepare_detector(self) -> None:
|
||||
"""Prepare detector for acquisition"""
|
||||
self.set_trigger(
|
||||
mapping_mode=MappingSource.SPECTRUM, 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 FalconPhoenix(PSIDetectorBase):
|
||||
"""
|
||||
Falcon detector for phoenix
|
||||
custom_prepare_cls (XMAPSetu
|
||||
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 = 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 = FalconPhoenix(name="falcon", prefix="X07MB-SITORO:", sim_mode=True)
|
431
phoenix_bec/devices/obsolete/sitoro.py
Normal file
431
phoenix_bec/devices/obsolete/sitoro.py
Normal file
@ -0,0 +1,431 @@
|
||||
"""
|
||||
Base implementation for Sitoro Falcon
|
||||
|
||||
|
||||
This is based on ophyd.mca.py
|
||||
All relevant classes are renames by putting Sitoro ahead of the class name
|
||||
eg. EpicsMCARecord(Device): --> SitoroEpicsMCARecord(Device)
|
||||
|
||||
fundamentally on could use
|
||||
class SitoroEpicsMCARecord(Device):
|
||||
class SitoroEpicsMCA(SitoroEpicsMCARecord):
|
||||
class SitoroEpicsMCAReadNotify(SitoroEpicsMCARecord):
|
||||
class SitoroEpicsMCAReadNotify(SitoroEpicsMCARecord):
|
||||
class SitoroEpicsMCACallback(Device):
|
||||
|
||||
|
||||
class SitoroEpicsDXP(Device):
|
||||
class SitoroEpicsDXPLowLevelParameter(Device):
|
||||
class SitoroEpicsDXPLowLevel(Device):
|
||||
class SitoroEpicsDXPMapping(Device):
|
||||
class SitoroEpicsDXPBaseSystem(Device):
|
||||
class SitoroEpicsDXPMultiElementSystem(SitoroEpicsDXPBaseSystem):
|
||||
class SitoroSoftDXPTrigger(Device):
|
||||
|
||||
|
||||
|
||||
"""
|
||||
|
||||
import logging
|
||||
from collections import OrderedDict
|
||||
|
||||
|
||||
from ophyd.areadetector import EpicsSignalWithRBV as SignalWithRBV
|
||||
from ophyd.device import Component as Cpt
|
||||
from ophyd.device import Device
|
||||
from ophyd.device import DynamicDeviceComponent as DDC
|
||||
from ophyd.device import Kind
|
||||
from ophyd.signal import EpicsSignal, EpicsSignalRO, Signal
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ROI(Device): # must keep name
|
||||
|
||||
# 'name' is not an allowed attribute
|
||||
label = Cpt(EpicsSignal, "NM", lazy=True)
|
||||
count = Cpt(EpicsSignalRO, "", lazy=True)
|
||||
net_count = Cpt(EpicsSignalRO, "N", lazy=True)
|
||||
preset_count = Cpt(EpicsSignal, "P", lazy=True)
|
||||
is_preset = Cpt(EpicsSignal, "IP", lazy=True)
|
||||
bkgnd_chans = Cpt(EpicsSignal, "BG", lazy=True)
|
||||
hi_chan = Cpt(EpicsSignal, "HI", lazy=True)
|
||||
lo_chan = Cpt(EpicsSignal, "LO", lazy=True)
|
||||
|
||||
def __init__(
|
||||
self, prefix, *, read_attrs=None, configuration_attrs=None, name=None, parent=None, **kwargs
|
||||
):
|
||||
|
||||
super().__init__(
|
||||
prefix,
|
||||
read_attrs=read_attrs,
|
||||
configuration_attrs=configuration_attrs,
|
||||
name=name,
|
||||
parent=parent,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
|
||||
def add_rois(range_, **kwargs): # must keep name
|
||||
"""Add one or more ROIs to an MCA instance
|
||||
|
||||
Parameters
|
||||
----------
|
||||
range_ : sequence of ints
|
||||
Must be be in the set [0,31]
|
||||
|
||||
By default, an EpicsMCA is initialized with all 32 rois.
|
||||
These provide the following Components as EpicsSignals (N=[0,31]):
|
||||
EpicsMCA.rois.roiN.(label,count,net_count,preset_cnt, is_preset,
|
||||
bkgnd_chans, hi_chan, lo_chan)
|
||||
"""
|
||||
defn = OrderedDict()
|
||||
|
||||
for roi in range_:
|
||||
if not (0 <= roi < 32):
|
||||
raise ValueError("roi must be in the set [0,31]")
|
||||
|
||||
attr = "roi{}".format(roi)
|
||||
defn[attr] = (ROI, ".R{}".format(roi), kwargs)
|
||||
|
||||
return defn
|
||||
|
||||
|
||||
class SitoroEpicsMCARecord(Device):
|
||||
"""SynApps MCA Record interface"""
|
||||
|
||||
stop_signal = Cpt(EpicsSignal, ".STOP", kind="omitted")
|
||||
preset_real_time = Cpt(EpicsSignal, ".PRTM", kind=Kind.config | Kind.normal)
|
||||
preset_live_time = Cpt(EpicsSignal, ".PLTM", kind="omitted")
|
||||
elapsed_real_time = Cpt(EpicsSignalRO, ".ERTM")
|
||||
elapsed_live_time = Cpt(EpicsSignalRO, ".ELTM", kind="omitted")
|
||||
|
||||
spectrum = Cpt(EpicsSignalRO, ".VAL")
|
||||
background = Cpt(EpicsSignalRO, ".BG", kind="omitted")
|
||||
mode = Cpt(EpicsSignal, ".MODE", string=True, kind="omitted")
|
||||
|
||||
rois = DDC(add_rois(range(0, 32), kind="omitted"), kind="omitted")
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
# could arguably be made a configuration_attr instead...
|
||||
self.stage_sigs["mode"] = "PHA"
|
||||
|
||||
def stop(self, *, success=False):
|
||||
self.stop_signal.put(1)
|
||||
|
||||
|
||||
class SitoroEpicsMCA(SitoroEpicsMCARecord):
|
||||
"""mca records with extras from mca.db"""
|
||||
|
||||
start = Cpt(EpicsSignal, "Start", kind="omitted")
|
||||
stop_signal = Cpt(EpicsSignal, "Stop", kind="omitted")
|
||||
erase = Cpt(EpicsSignal, "Erase", kind="omitted")
|
||||
erase_start = Cpt(EpicsSignal, "EraseStart", trigger_value=1, kind="omitted")
|
||||
|
||||
check_acquiring = Cpt(EpicsSignal, "CheckACQG", kind="omitted")
|
||||
client_wait = Cpt(EpicsSignal, "ClientWait", kind="omitted")
|
||||
enable_wait = Cpt(EpicsSignal, "EnableWait", kind="omitted")
|
||||
force_read = Cpt(EpicsSignal, "Read", kind="omitted")
|
||||
set_client_wait = Cpt(EpicsSignal, "SetClientWait", kind="omitted")
|
||||
status = Cpt(EpicsSignal, "Status", kind="omitted")
|
||||
when_acq_stops = Cpt(EpicsSignal, "WhenAcqStops", kind="omitted")
|
||||
why1 = Cpt(EpicsSignal, "Why1", kind="omitted")
|
||||
why2 = Cpt(EpicsSignal, "Why2", kind="omitted")
|
||||
why3 = Cpt(EpicsSignal, "Why3", kind="omitted")
|
||||
why4 = Cpt(EpicsSignal, "Why4", kind="omitted")
|
||||
|
||||
|
||||
class SitoroEpicsMCAReadNotify(SitoroEpicsMCARecord):
|
||||
"""mca record with extras from mcaReadNotify.db"""
|
||||
|
||||
start = Cpt(EpicsSignal, "Start", kind="omitted")
|
||||
stop_signal = Cpt(EpicsSignal, "Stop", kind="omitted")
|
||||
erase = Cpt(EpicsSignal, "Erase", kind="omitted")
|
||||
erase_start = Cpt(EpicsSignal, "EraseStart", trigger_value=1, kind="omitted")
|
||||
|
||||
check_acquiring = Cpt(EpicsSignal, "CheckACQG", kind="omitted")
|
||||
client_wait = Cpt(EpicsSignal, "ClientWait", kind="omitted")
|
||||
enable_wait = Cpt(EpicsSignal, "EnableWait", kind="omitted")
|
||||
force_read = Cpt(EpicsSignal, "Read", kind="omitted")
|
||||
set_client_wait = Cpt(EpicsSignal, "SetClientWait", kind="omitted")
|
||||
status = Cpt(EpicsSignal, "Status", kind="omitted")
|
||||
|
||||
|
||||
class SitoroEpicsMCACallback(Device):
|
||||
"""Callback-related signals for MCA devices"""
|
||||
|
||||
read_callback = Cpt(EpicsSignal, "ReadCallback")
|
||||
read_data_once = Cpt(EpicsSignal, "ReadDataOnce")
|
||||
read_status_once = Cpt(EpicsSignal, "ReadStatusOnce")
|
||||
collect_data = Cpt(EpicsSignal, "CollectData")
|
||||
|
||||
|
||||
class SitoroEpicsDXP(Device):
|
||||
"""All high-level DXP parameters for each channel"""
|
||||
|
||||
preset_mode = Cpt(EpicsSignal, "PresetMode", string=True)
|
||||
|
||||
live_time_output = Cpt(SignalWithRBV, "LiveTimeOutput", string=True)
|
||||
# elapsed_live_time = Cpt(EpicsSignal, "ElapsedLiveTime")
|
||||
# elapsed_real_time = Cpt(EpicsSignal, "ElapsedRealTime")
|
||||
# elapsed_trigger_live_time = Cpt(EpicsSignal, "ElapsedTriggerLiveTime")
|
||||
|
||||
nd_array_mode = Cpt(EpicsSignal, "NDArrayMode")
|
||||
|
||||
# Trigger Filter PVs
|
||||
trigger_peaking_time = Cpt(SignalWithRBV, "TriggerPeakingTime")
|
||||
trigger_threshold = Cpt(SignalWithRBV, "TriggerThreshold")
|
||||
trigger_gap_time = Cpt(SignalWithRBV, "TriggerGapTime")
|
||||
trigger_output = Cpt(SignalWithRBV, "TriggerOutput", string=True)
|
||||
max_width = Cpt(SignalWithRBV, "MaxWidth")
|
||||
|
||||
# Energy Filter PVs
|
||||
peaking_time = Cpt(SignalWithRBV, "PeakingTime")
|
||||
energy_threshold = Cpt(SignalWithRBV, "EnergyThreshold")
|
||||
gap_time = Cpt(SignalWithRBV, "GapTime")
|
||||
|
||||
# Baseline PVs
|
||||
# baseline_cut_percent = Cpt(SignalWithRBV, "BaselineCutPercent")
|
||||
# baseline_cut_enable = Cpt(SignalWithRBV, "BaselineCutEnable")
|
||||
# baseline_filter_length = Cpt(SignalWithRBV, "BaselineFilterLength")
|
||||
# baseline_threshold = Cpt(SignalWithRBV, "BaselineThreshold")
|
||||
# baseline_energy_array = Cpt(EpicsSignal, "BaselineEnergyArray")
|
||||
# baseline_histogram = Cpt(EpicsSignal, "BaselineHistogram")
|
||||
# baseline_threshold = Cpt(SignalWithRBV, "BaselineThreshold")
|
||||
|
||||
# Misc PVs
|
||||
preamp_gain = Cpt(SignalWithRBV, "PreampGain")
|
||||
detector_polarity = Cpt(SignalWithRBV, "DetectorPolarity")
|
||||
reset_delay = Cpt(SignalWithRBV, "ResetDelay")
|
||||
decay_time = Cpt(SignalWithRBV, "DecayTime")
|
||||
max_energy = Cpt(SignalWithRBV, "MaxEnergy")
|
||||
adc_percent_rule = Cpt(SignalWithRBV, "ADCPercentRule")
|
||||
max_width = Cpt(SignalWithRBV, "MaxWidth")
|
||||
|
||||
# read-only diagnostics
|
||||
triggers = Cpt(EpicsSignalRO, "Triggers", lazy=True)
|
||||
events = Cpt(EpicsSignalRO, "Events", lazy=True)
|
||||
overflows = Cpt(EpicsSignalRO, "Overflows", lazy=True)
|
||||
underflows = Cpt(EpicsSignalRO, "Underflows", lazy=True)
|
||||
input_count_rate = Cpt(EpicsSignalRO, "InputCountRate", lazy=True)
|
||||
output_count_rate = Cpt(EpicsSignalRO, "OutputCountRate", lazy=True)
|
||||
|
||||
mca_bin_width = Cpt(EpicsSignalRO, "MCABinWidth_RBV")
|
||||
calibration_energy = Cpt(EpicsSignalRO, "CalibrationEnergy_RBV")
|
||||
current_pixel = Cpt(EpicsSignal, "CurrentPixel")
|
||||
dynamic_range = Cpt(EpicsSignalRO, "DynamicRange_RBV")
|
||||
|
||||
# Preset options
|
||||
preset_events = Cpt(SignalWithRBV, "PresetEvents")
|
||||
preset_mode = Cpt(SignalWithRBV, "PresetMode", string=True)
|
||||
preset_triggers = Cpt(SignalWithRBV, "PresetTriggers")
|
||||
|
||||
# Trace options
|
||||
trace_data = Cpt(EpicsSignal, "TraceData")
|
||||
trace_mode = Cpt(SignalWithRBV, "TraceMode", string=True)
|
||||
trace_time_array = Cpt(EpicsSignal, "TraceTimeArray")
|
||||
trace_time = Cpt(SignalWithRBV, "TraceTime")
|
||||
|
||||
|
||||
class SitoroEpicsDXPLowLevelParameter(Device):
|
||||
param_name = Cpt(EpicsSignal, "Name")
|
||||
value = Cpt(SignalWithRBV, "Val")
|
||||
|
||||
|
||||
class SitoroEpicsDXPLowLevel(Device):
|
||||
num_low_level_params = Cpt(EpicsSignal, "NumLLParams")
|
||||
read_low_level_params = Cpt(EpicsSignal, "ReadLLParams")
|
||||
|
||||
parameter_prefix = "LL{}"
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self._parameter_cache = {}
|
||||
|
||||
def get_low_level_parameter(self, index):
|
||||
"""Get a DXP low level parameter
|
||||
|
||||
Parameters
|
||||
----------
|
||||
index : int
|
||||
In the range of [0, 229]
|
||||
|
||||
Returns
|
||||
-------
|
||||
param : EpicsDXPLowLevelParameter
|
||||
"""
|
||||
try:
|
||||
return self._parameter_cache[index]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
prefix = "{}{}".format(self.prefix, self.parameter_prefix)
|
||||
name = "{}_param{}".format(self.name, index)
|
||||
param = EpicsDXPLowLevelParameter(prefix, name=name)
|
||||
self._parameter_cache[index] = param
|
||||
return param
|
||||
|
||||
|
||||
class SitoroEpicsDXPMapping(Device):
|
||||
apply = Cpt(EpicsSignal, "Apply")
|
||||
auto_apply = Cpt(SignalWithRBV, "AutoApply")
|
||||
auto_pixels_per_buffer = Cpt(SignalWithRBV, "AutoPixelsPerBuffer")
|
||||
buffer_size = Cpt(EpicsSignalRO, "BufferSize_RBV")
|
||||
collect_mode = Cpt(SignalWithRBV, "CollectMode")
|
||||
ignore_gate = Cpt(SignalWithRBV, "IgnoreGate")
|
||||
input_logic_polarity = Cpt(SignalWithRBV, "InputLogicPolarity")
|
||||
list_mode = Cpt(SignalWithRBV, "ListMode")
|
||||
mbytes_read = Cpt(EpicsSignalRO, "MBytesRead_RBV")
|
||||
next_pixel = Cpt(EpicsSignal, "NextPixel")
|
||||
pixel_advance_mode = Cpt(SignalWithRBV, "PixelAdvanceMode")
|
||||
pixels_per_buffer = Cpt(SignalWithRBV, "PixelsPerBuffer")
|
||||
pixels_per_run = Cpt(SignalWithRBV, "PixelsPerRun")
|
||||
read_rate = Cpt(EpicsSignalRO, "ReadRate_RBV")
|
||||
sync_count = Cpt(SignalWithRBV, "SyncCount")
|
||||
|
||||
|
||||
class SitoroEpicsDXPBaseSystem(Device):
|
||||
channel_advance = Cpt(EpicsSignal, "ChannelAdvance")
|
||||
client_wait = Cpt(EpicsSignal, "ClientWait")
|
||||
dwell = Cpt(EpicsSignal, "Dwell")
|
||||
max_scas = Cpt(EpicsSignal, "MaxSCAs")
|
||||
num_scas = Cpt(SignalWithRBV, "NumSCAs")
|
||||
poll_time = Cpt(SignalWithRBV, "PollTime")
|
||||
prescale = Cpt(EpicsSignal, "Prescale")
|
||||
save_system = Cpt(SignalWithRBV, "SaveSystem")
|
||||
save_system_file = Cpt(EpicsSignal, "SaveSystemFile")
|
||||
set_client_wait = Cpt(EpicsSignal, "SetClientWait")
|
||||
|
||||
|
||||
class SitoroTest(Device):
|
||||
preset_mode = Cpt(EpicsSignal, "PresetMode", string=True)
|
||||
|
||||
|
||||
class SitoroEpicsDXPMultiElementSystem(SitoroEpicsDXPBaseSystem):
|
||||
|
||||
# Preset info
|
||||
preset_mode = Cpt(EpicsSignal, "PresetMode", string=True)
|
||||
preset_real_time = Cpt(EpicsSignal, "PresetReal")
|
||||
preset_events = Cpt(EpicsSignal, "PresetEvents")
|
||||
preset_triggers = Cpt(EpicsSignal, "PresetTriggers")
|
||||
mca_refresh_period = Cpt(EpicsSignal, "MCARefreshPeriod")
|
||||
|
||||
# preset_live_time = Cpt(EpicsSignal, "PresetLive")
|
||||
# Acquisition
|
||||
erase_all = Cpt(EpicsSignal, "EraseAll")
|
||||
erase_start = Cpt(EpicsSignal, "EraseStart", trigger_value=1)
|
||||
start_all = Cpt(EpicsSignal, "StartAll")
|
||||
stop_all = Cpt(EpicsSignal, "StopAll")
|
||||
|
||||
# Status
|
||||
|
||||
set_acquire_busy = Cpt(EpicsSignal, "SetAcquireBusy") # -- not working
|
||||
acquire_busy = Cpt(EpicsSignal, "AcquireBusy") # -- not working
|
||||
status_all = Cpt(EpicsSignal, "StatusAll") # -- not working
|
||||
status_all_once = Cpt(EpicsSignal, "StatusAllOnce") # -- not working
|
||||
acquiring = Cpt(EpicsSignalRO, "Acquiring") # -- not working
|
||||
|
||||
# Reading
|
||||
# read_baseline_histograms = Cpt(EpicsSignal, "ReadBaselineHistograms")
|
||||
read_all = Cpt(EpicsSignal, "ReadAll") # -- not working
|
||||
read_all_once = Cpt(EpicsSignal, "ReadAllOnce") # -- not working
|
||||
|
||||
# As a debugging note, if snl_connected is not '1', your IOC is
|
||||
# misconfigured:
|
||||
snl_connected = Cpt(EpicsSignal, "SNL_Connected")
|
||||
|
||||
"""
|
||||
|
||||
# Copying to individual elements
|
||||
copy_adcp_ercent_rule = Cpt(EpicsSignal, "CopyADCPercentRule")
|
||||
#copy_baseline_cut_enable = Cpt(EpicsSignal, "CopyBaselineCutEnable")
|
||||
#copy_baseline_cut_percent = Cpt(EpicsSignal, "CopyBaselineCutPercent")
|
||||
#copy_baseline_filter_length = Cpt(EpicsSignal, "CopyBaselineFilterLength")
|
||||
#copy_baseline_threshold = Cpt(EpicsSignal, "CopyBaselineThreshold")
|
||||
copy_decay_time = Cpt(EpicsSignal, "CopyDecayTime")
|
||||
copy_detector_polarity = Cpt(EpicsSignal, "CopyDetectorPolarity")
|
||||
copy_energy_threshold = Cpt(EpicsSignal, "CopyEnergyThreshold")
|
||||
copy_gap_time = Cpt(EpicsSignal, "CopyGapTime")
|
||||
copy_max_energy = Cpt(EpicsSignal, "CopyMaxEnergy")
|
||||
copy_max_width = Cpt(EpicsSignal, "CopyMaxWidth")
|
||||
copy_peaking_time = Cpt(EpicsSignal, "CopyPeakingTime")
|
||||
copy_preamp_gain = Cpt(EpicsSignal, "CopyPreampGain")
|
||||
copy_roic_hannel = Cpt(EpicsSignal, "CopyROIChannel")
|
||||
copy_roie_nergy = Cpt(EpicsSignal, "CopyROIEnergy")
|
||||
copy_roi_sca = Cpt(EpicsSignal, "CopyROI_SCA")
|
||||
copy_reset_delay = Cpt(EpicsSignal, "CopyResetDelay")
|
||||
copy_trigger_gap_time = Cpt(EpicsSignal, "CopyTriggerGapTime")
|
||||
copy_trigger_peaking_time = Cpt(EpicsSignal, "CopyTriggerPeakingTime")
|
||||
copy_trigger_threshold = Cpt(EpicsSignal, "CopyTriggerThreshold")
|
||||
|
||||
# do_* executes the process:
|
||||
do_read_all = Cpt(EpicsSignal, "DoReadAll")
|
||||
#do_read_baseline_histograms = Cpt(EpicsSignal, "DoReadBaselineHistograms")
|
||||
do_read_traces = Cpt(EpicsSignal, "DoReadTraces")
|
||||
do_status_all = Cpt(EpicsSignal, "DoStatusAll")
|
||||
"""
|
||||
|
||||
# Time
|
||||
# dead_time = Cpt(EpicsSignal, "DeadTime")
|
||||
# elapsed_live = Cpt(EpicsSignal, "ElapsedLive")
|
||||
# elapsed_real = Cpt(EpicsSignal, "ElapsedReal")
|
||||
|
||||
# low-level
|
||||
# read_low_level_params = Cpt(EpicsSignal, "ReadLLParams")
|
||||
|
||||
# Traces
|
||||
# read_traces = Cpt(EpicsSignal, "ReadTraces")
|
||||
# trace_modes = Cpt(EpicsSignal, "TraceModes", string=True)
|
||||
# trace_times = Cpt(EpicsSignal, "TraceTimes")
|
||||
|
||||
|
||||
class SitoroSoftDXPTrigger(Device):
|
||||
"""Simple soft trigger for DXP devices
|
||||
|
||||
Parameters
|
||||
----------
|
||||
count_signal : str, optional
|
||||
Signal to set acquisition time (default: 'preset_real_time')
|
||||
preset_mode : str, optional
|
||||
Default preset mode for the stage signals (default: 'Real time')
|
||||
mode_signal : str, optional
|
||||
Preset mode signal attribute (default 'preset_mode')
|
||||
stop_signal : str, optional
|
||||
Stop signal attribute (default 'stop_all')
|
||||
"""
|
||||
|
||||
count_time = Cpt(Signal, value=None, doc="bluesky count time")
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*args,
|
||||
count_signal="preset_real_time",
|
||||
stop_signal="stop_all",
|
||||
mode_signal="preset_mode",
|
||||
preset_mode="Real time",
|
||||
**kwargs,
|
||||
):
|
||||
super().__init__(*args, **kwargs)
|
||||
self._status = None
|
||||
self._count_signal = getattr(self, count_signal)
|
||||
|
||||
stop_signal = getattr(self, stop_signal)
|
||||
self.stage_sigs[stop_signal] = 1
|
||||
|
||||
mode_signal = getattr(self, mode_signal)
|
||||
self.stage_sigs[mode_signal] = preset_mode
|
||||
|
||||
def stage(self):
|
||||
if self.count_time.get() is None:
|
||||
# remove count_time from the stage signals if count_time unset
|
||||
try:
|
||||
del self.stage_sigs[self._count_signal]
|
||||
except KeyError:
|
||||
pass
|
||||
else:
|
||||
self.stage_sigs[self._count_signal] = self.count_time.get()
|
||||
|
||||
super().stage()
|
485
phoenix_bec/devices/obsolete/sitoro_phoenix.py
Normal file
485
phoenix_bec/devices/obsolete/sitoro_phoenix.py
Normal file
@ -0,0 +1,485 @@
|
||||
"""
|
||||
Implementation for falcon at PHOENIX, derived from
|
||||
implementation on csaxs (file falcon_csaxs.py)
|
||||
|
||||
18.10.2024 further development of falcon_phoenix.y to phoenix to sitoro_phoenix.py
|
||||
Now we use the definition of all EPICS channels for falcon as defined in the classes in sitoro.py
|
||||
|
||||
WIP......
|
||||
|
||||
17.10.2024 try to streamline implementation with mca record
|
||||
|
||||
|
||||
Differences to implement
|
||||
|
||||
1) we consider EPICS initialization as standard implementaion,
|
||||
so no reinitialization when bec device is initrialized ... DONE ...
|
||||
|
||||
2) in EpicsDXPFalcon(Device) add ICR and OCR for individual detectors
|
||||
|
||||
3) can we make this generic to make it suited for both falcon and XMAP ?
|
||||
|
||||
3) make easy switching between mca spectra an mca mapping
|
||||
|
||||
fix defiend relation bwetween variables and names used here for example DONE
|
||||
|
||||
aquiring is currently called 'state' --> should be renamed to aquiring
|
||||
Currently state = Cpt(EpicsSignal, "Acquiring")
|
||||
should be acquiring = Cpt(EpicsSignal, "Acquiring")
|
||||
|
||||
|
||||
hdf5 = Cpt(FalconHDF5Plugins, "HDF1:")
|
||||
|
||||
def arm_aquisition
|
||||
|
||||
raise FalconTimeoutError(
|
||||
f"Failed to arm the acquisition. Detector state {signal_conditions[0][0]}"
|
||||
)
|
||||
|
||||
|
||||
|
||||
CHANGES LOG and
|
||||
|
||||
System as taken from cSAXS some times works for one element need about 7 second
|
||||
There seem to be still serious timout issues
|
||||
|
||||
changes log
|
||||
TIMEOUT_FOR_SIGNALs from 5 to 10
|
||||
|
||||
|
||||
|
||||
|
||||
"""
|
||||
|
||||
import enum
|
||||
import threading
|
||||
import time
|
||||
|
||||
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 # old import
|
||||
# now import ophyd.mca completely
|
||||
|
||||
# import ophyd.mca as Mca
|
||||
|
||||
from ..sitoro import (
|
||||
SitoroEpicsMCARecord,
|
||||
SitoroEpicsMCA,
|
||||
SitoroEpicsMCAReadNotify,
|
||||
SitoroEpicsDXP,
|
||||
SitoroEpicsDXPBaseSystem,
|
||||
SitoroEpicsDXPMultiElementSystem,
|
||||
SitoroTest,
|
||||
)
|
||||
|
||||
from ophyd_devices.interfaces.base_classes.psi_detector_base import (
|
||||
CustomDetectorMixin,
|
||||
PSIDetectorBase,
|
||||
)
|
||||
|
||||
logger = bec_logger.logger
|
||||
|
||||
|
||||
class SitoroError(Exception):
|
||||
"""Base class for exceptions in this module."""
|
||||
|
||||
|
||||
class SitoroTimeoutError(SitoroError):
|
||||
"""Raised when the Sitoro does not respond in time."""
|
||||
|
||||
|
||||
class DetectorState(enum.IntEnum):
|
||||
"""Detector states for Sitoro detector"""
|
||||
|
||||
DONE = 0
|
||||
ACQUIRING = 1
|
||||
|
||||
|
||||
class TriggerSource(enum.IntEnum):
|
||||
"""Trigger source for Sitoro detector"""
|
||||
|
||||
USER = 0
|
||||
GATE = 1
|
||||
SYNC = 2
|
||||
|
||||
|
||||
class MappingSource(enum.IntEnum):
|
||||
"""Mapping source for Sitoro detector"""
|
||||
|
||||
SPECTRUM = 0
|
||||
MAPPING = 1
|
||||
|
||||
|
||||
class SitoroEpicsMCARecordExtended_OLD(SitoroEpicsMCARecord):
|
||||
|
||||
# add parameters for detector energy calibration
|
||||
# which are missing in mca.py
|
||||
|
||||
calo = Cpt(EpicsSignal, ".CALO")
|
||||
cals = Cpt(EpicsSignal, ".CALS")
|
||||
calq = Cpt(EpicsSignal, ".CALQ")
|
||||
tth = Cpt(EpicsSignal, ".TTH")
|
||||
|
||||
|
||||
class SitoroEpicsDXP_OLD(Device):
|
||||
"""
|
||||
DXP parameters for Sitoro 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")
|
||||
decay_time = Cpt(EpicsSignalWithRBV, "DecayTime")
|
||||
|
||||
current_pixel = Cpt(EpicsSignalRO, "CurrentPixel")
|
||||
|
||||
|
||||
class SitoroHDF5Plugins(Device):
|
||||
"""
|
||||
HDF5 parameters for Sitoro 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 SitoroSetup(CustomDetectorMixin):
|
||||
"""
|
||||
Sitoro 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 Sitoro detector"""
|
||||
self.initialize_default_parameter()
|
||||
self.initialize_detector()
|
||||
self.initialize_detector_backend()
|
||||
|
||||
def initialize_default_parameter(self) -> None:
|
||||
"""
|
||||
Set default parameters for Sitoro
|
||||
|
||||
This will set:
|
||||
- readout (float): readout time in seconds
|
||||
- value_pixel_per_buffer (int): number of spectra in buffer of Sitoro 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 Sitoro detector"""
|
||||
|
||||
pass
|
||||
|
||||
"""
|
||||
THIS IS THE OLD CSACS CODE. uncomment for now, as we consider EPICS as the boss
|
||||
for initialization
|
||||
|
||||
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 Sitoro."""
|
||||
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.acquiring.read()[self.parent.acquiring.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 SitoroTimeoutError(
|
||||
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)
|
||||
time.sleep(0.5)
|
||||
self.parent.erase_all.put(1)
|
||||
time.sleep(0.5)
|
||||
|
||||
signal_conditions = [
|
||||
(
|
||||
lambda: self.parent.acquiring.read()[self.parent.acquiring.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 SitoroTimeoutError(
|
||||
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"Sitoro 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 SitoroPhoenix(PSIDetectorBase, SitoroEpicsDXPMultiElementSystem):
|
||||
# class SitoroPhoenix(PSIDetectorBase):
|
||||
"""
|
||||
Sitoro Sitoro detector for Phoenix
|
||||
|
||||
|
||||
Parent class: PSIDetectorBase
|
||||
|
||||
class attributes:
|
||||
custom_prepare_cls (SitoroSetup) : Custom detector setup class,
|
||||
inherits from CustomDetectorMixin
|
||||
|
||||
PSIDetectorBase.set_min_readout (float) : Minimum readout time for the detector
|
||||
dxp1, .. dxpi, .. , dxpN (SitoroEpicsDXP) : DXP parameters for Sitoro detector Nr i
|
||||
mca1, .. mcai, .. , mcaN (SitoroEpicsMCARecord) : MCA parameters for Sitoro detector Nr i
|
||||
|
||||
hdf5 (SitoroHDF5Plugins) : HDF5 parameters for Sitoro 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 = SitoroSetup
|
||||
# specify minimum readout time for detector
|
||||
MIN_READOUT = 3e-3
|
||||
TIMEOUT_FOR_SIGNALS = 1
|
||||
|
||||
# specify class attributes
|
||||
|
||||
# Parameters for individual detector elements
|
||||
# Note: need to wrote 'dxp: here, but not dxp'
|
||||
|
||||
# dxp1 = Cpt(SitoroEpicsDXP, "dxp1:")
|
||||
# dxp2 = Cpt(SitoroEpicsDXP, "dxp2:")
|
||||
# dxp3 = Cpt(SitoroEpicsDXP, "dxp3:")
|
||||
# dxp4 = Cpt(SitoroEpicsDXP, "dxp4:")
|
||||
|
||||
#
|
||||
# THIS IS NOT WELL-DONE as it take out one part of mca.py from ophy.py
|
||||
#
|
||||
#
|
||||
# mca1 = Cpt(SitoroEpicsMCARecordExtended, "mca1")
|
||||
# mca2 = Cpt(SitoroEpicsMCARecordExtended, "mca2")
|
||||
# mca3 = Cpt(SitoroEpicsMCARecordExtended, "mca3")
|
||||
# mca4 = Cpt(SitoroEpicsMCARecordExtended, "mca4")
|
||||
|
||||
# need to write 'mca1', but not 'mca1:'
|
||||
# mca1 = Cpt(EpicsMCARecord, "mca1")
|
||||
# mca2 = Cpt(EpicsMCARecord, "mca2")
|
||||
# mca3 = Cpt(EpicsMCARecord, "mca3")
|
||||
# mca4 = Cpt(EpicsMCARecord, "mca4")
|
||||
|
||||
# other general parameters
|
||||
hdf5 = Cpt(SitoroHDF5Plugins, "HDF1:")
|
||||
|
||||
# stop_all = Cpt(EpicsSignal, "StopAll")
|
||||
# erase_all = Cpt(EpicsSignal, "EraseAll")
|
||||
# start_all = Cpt(EpicsSignal, "StartAll")
|
||||
# state = Cpt(EpicsSignal, "Acquiring") # <-- This is from cSAX implementation
|
||||
# acquiring = 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")
|
||||
|
||||
# _________________ General Epic parameters
|
||||
|
||||
# changes Oct 2024
|
||||
# triggers--> max_triggers,
|
||||
# events-->max_events
|
||||
# input_count_rate--> max_input_count_rate
|
||||
# output_count_rate--> max_output_count_rate
|
||||
|
||||
# max_triggers = Cpt(EpicsSignalRO, "MaxTriggers", lazy=True)
|
||||
# max_events = Cpt(EpicsSignalRO, "MaxEvents", lazy=True)
|
||||
|
||||
# max_input_count_rate = Cpt(EpicsSignalRO, "MaxInputCountRate", lazy=True)
|
||||
# max_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")
|
||||
# print(pixel_per_run
|
||||
# if "SITORO" in prefix:
|
||||
nd_array_mode = Cpt(EpicsSignal, "NDArrayMode")
|
||||
# endif
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sitoro = SitoroPhoenix(name="sitoro", prefix="X07MB-SITORO:", sim_mode=True)
|
369
phoenix_bec/devices/obsolete/xmap_phoenix_no_hdf5.py
Normal file
369
phoenix_bec/devices/obsolete/xmap_phoenix_no_hdf5.py
Normal file
@ -0,0 +1,369 @@
|
||||
#
|
||||
#
|
||||
# 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
|
||||
custom_prepare_cls (XMAPSetu
|
||||
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')
|
||||
|
||||
|
||||
def aaaa(self):
|
||||
print('aaaa')
|
380
phoenix_bec/devices/phoenix_falcon.py
Normal file
380
phoenix_bec/devices/phoenix_falcon.py
Normal file
@ -0,0 +1,380 @@
|
||||
"""
|
||||
Implementation for falcon at PHOENIX, derived from
|
||||
implementation on csaxs (file falcon_csaxs.py)
|
||||
|
||||
17.10.2024 try to streamline implementation with mca record
|
||||
|
||||
|
||||
Differences to implement
|
||||
|
||||
1) we consider EPICS initialization as standard implementaion,
|
||||
so no reinitialization when bec device is initrialized ... DONE ...
|
||||
|
||||
2) in EpicsDXPFalcon(Device) add ICR and OCR for individual detectors
|
||||
|
||||
3) can we make this generic to make it suited for both falcon and XMAP ?
|
||||
|
||||
3) make easy switching between mca spectra an mca mapping
|
||||
|
||||
fix defiend relation bwetween variables and names used here for example DONE
|
||||
|
||||
aquiring is currently called 'state' --> should be renamed to aquiring
|
||||
Currently state = Cpt(EpicsSignal, "Acquiring")
|
||||
should be acquiring = Cpt(EpicsSignal, "Acquiring")
|
||||
|
||||
|
||||
hdf5 = Cpt(FalconHDF5Plugins, "HDF1:")
|
||||
|
||||
def arm_aquisition
|
||||
|
||||
raise FalconTimeoutError(
|
||||
f"Failed to arm the acquisition. Detector state {signal_conditions[0][0]}"
|
||||
)
|
||||
|
||||
|
||||
|
||||
CHANGES LOG and
|
||||
|
||||
System as taken from cSAXS some times works for one element need about 7 second
|
||||
There seem to be still serious timout issues
|
||||
|
||||
changes log
|
||||
TIMEOUT_FOR_SIGNALs from 5 to 10
|
||||
|
||||
|
||||
|
||||
|
||||
"""
|
||||
|
||||
import enum
|
||||
import os
|
||||
import threading
|
||||
import time
|
||||
|
||||
|
||||
from bec_lib.logger import bec_logger
|
||||
|
||||
from ophyd import Component as Cpt
|
||||
from ophyd import Device, EpicsSignal, EpicsSignalRO, EpicsSignalWithRBV
|
||||
|
||||
# from ophyd_devices.devices.dxp import Falcon, EpicsMCARecord, EpicsDXPFalcon
|
||||
from phoenix_bec.devices.dxp_loc import Falcon, EpicsMCARecord, EpicsDXPFalcon
|
||||
|
||||
from ophyd_devices.devices.areadetector.plugins import HDF5Plugin_V35 as HDF5Plugin #
|
||||
|
||||
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 FalconSetup(CustomDetectorMixin):
|
||||
"""
|
||||
Falcon setup class for Phoenix
|
||||
|
||||
Parent class: CustomDetectorMixin
|
||||
|
||||
"""
|
||||
|
||||
# Specify which functions are revealed to the user in BEC client
|
||||
USER_ACCESS = ["My_routine"]
|
||||
|
||||
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"""
|
||||
|
||||
pass
|
||||
|
||||
"""
|
||||
THIS IS THE OLD CSACS CODE. uncomment for now, as we consider EPICS as the boss
|
||||
for initialization
|
||||
|
||||
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.acquiring.read()[self.parent.acquiring.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)
|
||||
time.sleep(0.5)
|
||||
self.parent.erase_all.put(1)
|
||||
time.sleep(0.5)
|
||||
|
||||
signal_conditions = [
|
||||
(
|
||||
lambda: self.parent.acquiring.read()[self.parent.acquiring.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)
|
||||
|
||||
def My_routine(self):
|
||||
print("My routine in Falcon mixing class ")
|
||||
|
||||
|
||||
class FalconPhoenix(PSIDetectorBase, Falcon):
|
||||
"""
|
||||
FalconX4 Sitoro detector for Phoenixdef My_routine(self):
|
||||
print('My_routine')
|
||||
|
||||
|
||||
Parent class: PSIDetectorBase, Falcon
|
||||
|
||||
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<n> (EpicsDXPFalcon) : DXP parameters for Falcon detector
|
||||
mca<n> (EpicsMCARecord) : MCA parameters for Falcon detector
|
||||
hdf5 (FalconHDF5Plugins) : HDF5 parameters for Falcon detector
|
||||
MIN_READOUT (float)"""
|
||||
|
||||
# Specify which functions are revealed to the user in BEC client
|
||||
USER_ACCESS = ["describe", "help"]
|
||||
|
||||
# specify Setup class
|
||||
custom_prepare_cls = FalconSetup
|
||||
# specify minimum readout time for detector
|
||||
MIN_READOUT = 3e-3
|
||||
TIMEOUT_FOR_SIGNALS = 1
|
||||
|
||||
# specify class attributes
|
||||
|
||||
# DXP parameters
|
||||
dxp1 = Cpt(EpicsDXPFalcon, "dxp1:")
|
||||
# dxp2 = Cpt(EpicsDXPFalcon, "dxp2:")
|
||||
# dxp3 = Cpt(EpicsDXPFalcon, "dxp3:")
|
||||
# dxp4 = Cpt(EpicsDXPFalcon, "dxp4:")
|
||||
|
||||
# MCA record with spectrum data
|
||||
mca1 = Cpt(EpicsMCARecord, "mca1")
|
||||
# mca2 = Cpt(EpicsMCARecord, "mca2")
|
||||
# mca3 = Cpt(EpicsMCARecord, "mca3")
|
||||
# mca4 = Cpt(EpicsMCARecord, "mca4")
|
||||
|
||||
# optionally with a HDF5 writer plugin
|
||||
# hdf5 = Cpt(HDF5Plugin, "HDF1:")
|
||||
|
||||
def help(self):
|
||||
|
||||
help_text = """
|
||||
PHOENIX FALCON
|
||||
"""
|
||||
print(help_text)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
falcon = FalconPhoenix(name="falcon_hdf5", prefix="X07MB-SITORO:", sim_mode=True)
|
433
phoenix_bec/devices/phoenix_scan_1.py
Normal file
433
phoenix_bec/devices/phoenix_scan_1.py
Normal file
@ -0,0 +1,433 @@
|
||||
"""
|
||||
This module serve to test whether we can abuse the device concept to
|
||||
Define the scan logic in a device which collects EPICS Channels whic operate the
|
||||
dat aaquisition from several devices, such as
|
||||
such as trigger channel, xmap falcon, etc.
|
||||
|
||||
Aim is to define the data aquisition logic in the on_trigger() routine in this file,
|
||||
but leave data reading in the device definitions.
|
||||
|
||||
Hops is to create an easy to maintain/ edit setup.
|
||||
|
||||
The module is bases on the PhoenixTrigger class to connect to the ADC card
|
||||
that creates TTL signals to trigger cameras and detectors at Phoenix.
|
||||
|
||||
|
||||
TO Do
|
||||
|
||||
-- allow for variable dwell times
|
||||
-- add erase/Start for XMAP and FALCON
|
||||
-- check values for time.sleep()
|
||||
-- check in on_triggerthe status check for Falcon
|
||||
-- rework togther with Xiaoquiang the functionality of involved EPICS channels
|
||||
(Callbacks etc.) to minimize the need of sleeping times.
|
||||
|
||||
"""
|
||||
|
||||
import enum
|
||||
import time
|
||||
|
||||
import numpy as np
|
||||
from bec_lib import bec_logger
|
||||
from ophyd import Component as Cpt
|
||||
from ophyd import DeviceStatus, EpicsSignal, EpicsSignalRO, Kind, Device
|
||||
from ophyd_devices.interfaces.base_classes.psi_detector_base import (
|
||||
CustomDetectorMixin,
|
||||
PSIDetectorBase,
|
||||
)
|
||||
from ophyd_devices.devices.dxp import xMAP
|
||||
#, EpicsMCARecord
|
||||
|
||||
#from phoenix_bec.scripts.phoenix import PhoenixBL
|
||||
|
||||
logger = bec_logger.logger
|
||||
|
||||
DETECTOR_TIMEOUT = 5
|
||||
|
||||
"""
|
||||
#class ph_Logger():
|
||||
# PHOENIX Logger to my Logfile #
|
||||
# p_s = PhoenixBL.my_log
|
||||
"""
|
||||
|
||||
|
||||
class PhoenixTriggerError(Exception):
|
||||
"""PhoenixTrigger specific error"""
|
||||
|
||||
|
||||
class SAMPLING(int, enum.Enum):
|
||||
"""Sampling Done PV
|
||||
|
||||
This class serves redabilty of missinx class and ensure correct setting of
|
||||
certain conditions.
|
||||
defiend preset values for certain states to be called in the
|
||||
mixing class, where we for example check whether the sampling is done of not
|
||||
by comparing with SAMPLING.DONE
|
||||
xxx==SAMPLING.DONE
|
||||
|
||||
"""
|
||||
|
||||
RUNNING = 0
|
||||
DONE = 1
|
||||
|
||||
|
||||
class PhoenixScan_1Setup(CustomDetectorMixin):
|
||||
"""
|
||||
Mixin Class to setup the PhoenixTrigger device
|
||||
|
||||
"""
|
||||
|
||||
|
||||
def on_stage(self) -> None:
|
||||
"""
|
||||
|
||||
On stage actions which are executed upon staging the device
|
||||
|
||||
|
||||
"""
|
||||
if self.parent.scaninfo.scan_type == "step": # check whether we use step scanning
|
||||
###############
|
||||
# next lines ensure that TTL trigger is on single sampling mode (posible )
|
||||
##############
|
||||
self.parent.start_csmpl.set(0)
|
||||
time.sleep(0.1)
|
||||
self.parent.total_cycles.set(1)
|
||||
self.parent.smpl.put(1)
|
||||
time.sleep(0.5)
|
||||
#####
|
||||
# set sampling to dwell time of scan
|
||||
######
|
||||
self.parent.total_cycles.set(np.ceil(self.parent.scaninfo.exp_time * 5))
|
||||
|
||||
logger.info(f"Device {self.parent.name} was staged for step scan")
|
||||
|
||||
def on_unstage(self) -> None:
|
||||
"""On unstage actions which are executed upon unstaging the device"""
|
||||
|
||||
self.on_stop()
|
||||
|
||||
def on_trigger(self) -> DeviceStatus:
|
||||
"""On trigger actions which are executed upon triggering the device"""
|
||||
|
||||
# TODO Test the proper check for the falcon state
|
||||
# Check first that falcon is set to acquiring
|
||||
|
||||
# check for falcon
|
||||
print('on_trigger')
|
||||
falcon = self.parent.device_manager.devices.get("falcon", None) # get device
|
||||
timeout = 20
|
||||
print('ss')
|
||||
|
||||
|
||||
print('Check for falcon')
|
||||
if falcon is not None:
|
||||
# TODO Check that falcon.aquiring.get() == 1 is the correct check.
|
||||
# --> When is the falcon acquiring, this assumes 1?
|
||||
# self.wait_for_signals is defined in PSI_detector_base.CustomDetectorMixin
|
||||
# ALSO check for enabled or not here!!!
|
||||
#
|
||||
|
||||
if not self.wait_for_signals([(falcon.acquiring.get, 1)], timeout=timeout):
|
||||
raise PhoenixTriggerError(
|
||||
f"Device {self.parent.name} is not ready to take trigger, timeout due to waiting for Falcon to get ready. Timeout after {timeout}s"
|
||||
)
|
||||
#endif
|
||||
else:
|
||||
print('Falcon is None')
|
||||
#endif
|
||||
|
||||
|
||||
print(self.parent.simulate_step_logic)
|
||||
|
||||
|
||||
if ( self.parent.scaninfo.scan_type == "step") or self.parent.simulate_step_logic==True:
|
||||
print('stepscan')
|
||||
time.sleep(0.2)
|
||||
|
||||
|
||||
self.parent.smpl.put(1)
|
||||
|
||||
self.parent.xmap_erase_start.put(1)
|
||||
|
||||
# Minimum of 1 cycle has to be waited. Cycle == 0.2s
|
||||
|
||||
time.sleep(0.2)
|
||||
|
||||
# Trigger function from ophyd.Device returns a DeviceStatus. This function
|
||||
# starts a process that creates a DeviceStatus, and waits for the signal_conditions
|
||||
# self.parent.smpl_done.get to change to the value SAMPLING.DONE
|
||||
# Once this takes place, the DeviceStatus.done flag will be set to True.
|
||||
# When BEC calls trigger() on the devices, this method will be called assuming that
|
||||
# the devices config softwareTrigger=True is set.
|
||||
# In ScanBase, the _at_each_point function calls
|
||||
# self.stubs.wait(wait_type="trigger", group="trigger", wait_time=self.exp_time)
|
||||
# which ensures that the DeviceStatus object resolves before continuing,
|
||||
# i.e. DeviceStatus.done = True
|
||||
|
||||
#calling this does not seem to wait, but seem sto call on_stop
|
||||
|
||||
#status = self.wait_with_status(
|
||||
# signal_conditions=[(self.parent.smpl_done.get, SAMPLING.DONE)],
|
||||
# timeout=5 * self.parent.scaninfo.exp_time, # Check if timeout is appropriate
|
||||
# check_stopped=True,
|
||||
#)
|
||||
|
||||
print('dd',self.parent.smpl_done.get())
|
||||
|
||||
|
||||
# explanation of last line (self.parent.smpl_done.get, SAMPLINGDONE.DONE)
|
||||
# creates a tuple which defines a
|
||||
# condition, which is tested in self.wait_with_status, here it tests for :
|
||||
# (self.parent.smpl_done.get() == SAMPLINGDONE.DONE),
|
||||
# where SAMPLINGDONE.DONE =1, as set in code above
|
||||
# As this is in mixing class (PhoenixtriggerSetup), parent.sample_done is defined in
|
||||
# main class as smpl_done = Cpt(EpicsSignalRO, "SMPL-DONE", kind=Kind.config)
|
||||
|
||||
|
||||
# the wait for status did not work so explicit, make own routine later
|
||||
|
||||
#ii=0
|
||||
#while (self.parent.smpl_done.get() == 0 and ii< 10000):
|
||||
# time.sleep(0.05)
|
||||
# ii=ii+1
|
||||
# print(ii,self.parent.smpl_done.get())
|
||||
##endwhile
|
||||
|
||||
self.parent.xmap_stop_all.put(1)
|
||||
print('leave on trigger')
|
||||
time.sleep(4)
|
||||
|
||||
return
|
||||
#return status # should this be in if clause level or outside?
|
||||
# endif
|
||||
|
||||
def on_stop(self) -> None:
|
||||
"""
|
||||
Actions to stop the Device
|
||||
Here the device is switched back to continuous sampling.
|
||||
|
||||
"""
|
||||
print('phoenix_scan_1 on_stop')
|
||||
|
||||
self.parent.total_cycles.set(5)
|
||||
|
||||
self.parent.start_csmpl.set(1)
|
||||
time.sleep(0.5)
|
||||
self.parent.smpl.put(1)
|
||||
time.sleep(0.5)
|
||||
self.parent.smpl.put(1)
|
||||
time.sleep(0.2)
|
||||
if self.parent.smpl_done.get() == SAMPLING.RUNNING:
|
||||
return
|
||||
self.parent.smpl.put(1)
|
||||
|
||||
|
||||
class PhoenixScan_1(PSIDetectorBase):
|
||||
"""
|
||||
Class for PHOENIX TTL hardware trigger: 'X07MB-OP2:'
|
||||
|
||||
This device is used to trigger communicate with an ADC card
|
||||
that creates TTL signals to trigger cameras and detectors at Phoenix.
|
||||
|
||||
"""
|
||||
|
||||
##################################################################
|
||||
#
|
||||
# The Variable USER_ACCESS contains an ascii list of functions which will be
|
||||
# visible in dev.TTL. Here, this list is empty.
|
||||
# note that components are alway public
|
||||
#
|
||||
##################################################################
|
||||
|
||||
USER_ACCESS = ["describe", "help"]
|
||||
|
||||
####################################################################
|
||||
#
|
||||
# # specify Setup class into variable custom_prepare_cls
|
||||
#
|
||||
####################################################################
|
||||
|
||||
custom_prepare_cls = PhoenixScan_1Setup
|
||||
|
||||
###################################################################
|
||||
# in __init__ of PSIDetectorBase will the initialzed by
|
||||
# self.custom_prepare = self.custom_prepare_cls(parent=self, **kwargs)
|
||||
# making the instance of PSIDetectorBase availble to functions
|
||||
# in PhoenixTriggerSetup.
|
||||
#
|
||||
# This inherits, among other things, the input parameters, such as
|
||||
# the notable prefix, which is here 'X07MB-OP2:'
|
||||
#
|
||||
# The input of Component=Cpt is Cpt(deviceClass,suffix)
|
||||
# if Cpt is used in a class, which has interited Device, here via:
|
||||
#
|
||||
# (Here PhoenixTrigger <-- PSIDetectorBase <- Device
|
||||
#
|
||||
# intr_count = Cpt(
|
||||
# EpicsSignal, "INTR-COUN
|
||||
# then Cpt will construct - magically- the
|
||||
# Epics channel name = prefix+suffix,
|
||||
#
|
||||
# for example
|
||||
# 'X07MB-OP2:' + 'START-CSMPL' -> 'X07MB-OP2:' + 'START-CSMPL'
|
||||
#
|
||||
# to construct names, for now we keep the convention to derive
|
||||
# them from the EPICS NAMES (such as ) X07MB-OP2:SMPL-DONE --> smpl_done
|
||||
#
|
||||
# this mean access to channel using dev.PH_TTL.smpl_done.get()
|
||||
#
|
||||
#
|
||||
###################################################################
|
||||
|
||||
simulate_step_logic=True # set to true for debugging should be false in operation
|
||||
|
||||
# SIGNALS FOR TRIGGER
|
||||
|
||||
start_csmpl = Cpt(
|
||||
EpicsSignal, "START-CSMPL", kind=Kind.config, put_complete=True
|
||||
) # cont on / off
|
||||
|
||||
# intr_count = Cpt(
|
||||
# EpicsSignal, "INTR-COUNT", kind=Kind.config, put_complete=True
|
||||
# ) # conter run up
|
||||
|
||||
|
||||
intr_count = Cpt(EpicsSignal, "INTR-COUNT") # counter run up NEEDS TO BE READ OUT !!!!
|
||||
|
||||
total_cycles = Cpt(
|
||||
EpicsSignal, "TOTAL-CYCLES", kind=Kind.config, put_complete=True
|
||||
) # cycles set
|
||||
smpl = Cpt(
|
||||
EpicsSignal, "SMPL", kind=Kind.config, put_complete=True
|
||||
) # start sampling --> aquire
|
||||
|
||||
csmpl = Cpt(
|
||||
EpicsSignal, "START-CSMPL", kind=Kind.config, put_complete=True
|
||||
) # start sampling --> aquire
|
||||
smpl_done = Cpt(
|
||||
EpicsSignalRO, "SMPL-DONE", kind=Kind.config
|
||||
) # show trigger is done, consider using string=True
|
||||
|
||||
|
||||
# create subset in name spacs
|
||||
|
||||
ph_start_csmpl = start_csmpl
|
||||
ph_intr_count = intr_count
|
||||
ph_total_cycles = total_cycles
|
||||
ph_smpl = smpl
|
||||
ph_csmpl = csmpl
|
||||
ph_smpl_done = smpl_done
|
||||
|
||||
##################################################################
|
||||
# channels for xmap
|
||||
##################################################################
|
||||
|
||||
|
||||
class xmap_local(Device):
|
||||
"""
|
||||
subclass to PhoenixScan_1
|
||||
local class for xmap controll
|
||||
|
||||
consider importing general xmap setup ??
|
||||
less channels might be less risk free and faster!
|
||||
cannot import on higher level as it takes wron prefix,
|
||||
possibly import here and set prefic in def __init__
|
||||
for now we keep it simple
|
||||
|
||||
"""
|
||||
|
||||
erase_start = Cpt(EpicsSignal,'EraseStart',kind=Kind.config)
|
||||
start_all = Cpt(EpicsSignal,'StartAll',kind=Kind.config)
|
||||
stop_all = Cpt(EpicsSignal,'StopAll',kind=Kind.config)
|
||||
erase_all = Cpt(EpicsSignal,'EraseAll',kind=Kind.config)
|
||||
acquiring = Cpt(EpicsSignal,'Acquiring',kind=Kind.config)
|
||||
|
||||
#endclass
|
||||
|
||||
xmap_control=xmap_local(name='ddd',prefix='X07MB-XMAP:')
|
||||
|
||||
xmap_erase_start = xmap_control.erase_start
|
||||
xmap_start_all = xmap_control.start_all
|
||||
xmap_stop_all = xmap_control.stop_all
|
||||
xmap_erase_all = xmap_control.erase_all
|
||||
xmap_acquiring = xmap_control.acquiring
|
||||
|
||||
|
||||
ph_xmap_erase_start = xmap_erase_start
|
||||
ph_xmap_start_all = xmap_start_all
|
||||
ph_xmap_stop_all = xmap_stop_all
|
||||
ph_xmap_erase_all = xmap_erase_all
|
||||
ph_xmap_aquiring = xmap_acquiring
|
||||
|
||||
|
||||
def help(self):
|
||||
"""
|
||||
Help function for phoenix_trigger
|
||||
"""
|
||||
|
||||
help_text = """
|
||||
PHOENIXScan_1
|
||||
|
||||
HELP NT CORRECT NEEDSA to be updated
|
||||
|
||||
signal attributes vs Epics Channels
|
||||
|
||||
description device Epics Channel
|
||||
attribute
|
||||
|
||||
Cont sampling on/off .start_csmpl ~ X07MB-OP2:SMPL
|
||||
1: continous
|
||||
0: continous off
|
||||
|
||||
Counter run up .intr_count ~ " :INTR-COUNT
|
||||
|
||||
Cycles set .total_cycles ~ " :TOTAL-CYCLES
|
||||
1 cycle = 200 ms
|
||||
|
||||
Start sampling (aquiring) .smpl ~ " :SMPL
|
||||
|
||||
Show trigger is done .smpl_done ~ " :SMPL-DONE
|
||||
1: aquiring
|
||||
0: not aquiring
|
||||
|
||||
Assuming device is called dev.PH_TTL
|
||||
|
||||
Read Channel dev.PH_TTL.start_csmpl.get()
|
||||
|
||||
Write into Channel dev.PH_TTL.total_cycles.put(1)
|
||||
dev.PH_TTL.total_cycles.set(1)
|
||||
|
||||
For further general help try the attributes
|
||||
.describe() considered as stable dict
|
||||
.describe_confguration()
|
||||
.summary()
|
||||
._config ._info
|
||||
|
||||
Important channel are collected with prefix ph, to ease finding of the channels
|
||||
|
||||
for example
|
||||
|
||||
dev.PH_TTl.ph_total_cycles = dev.PH_TTl.total_cycles
|
||||
|
||||
.. etc
|
||||
"""
|
||||
|
||||
print("Name of device is", self.name)
|
||||
print("")
|
||||
print(help_text)
|
||||
|
||||
# end def help
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
# Test the PhoenixTrigger class
|
||||
trigger = PhoenixScan_1(name="Scan_1", prefix="X07MB-OP2:")
|
||||
trigger.wait_for_connection(all_signals=True)
|
||||
trigger.read()
|
||||
trigger.read_configuration()
|
||||
|
||||
trigger.stage()
|
||||
device_status = trigger.trigger()
|
||||
device_status.wait()
|
||||
trigger.unstage()
|
357
phoenix_bec/devices/phoenix_trigger.py
Normal file
357
phoenix_bec/devices/phoenix_trigger.py
Normal file
@ -0,0 +1,357 @@
|
||||
"""
|
||||
Module for the PhoenixTrigger class to connect to the ADC card
|
||||
that creates TTL signals to trigger cameras and detectors at Phoenix.
|
||||
|
||||
TO Do
|
||||
|
||||
-- allow for variable dwell times
|
||||
-- add erase/Start for XMAP and FALCON
|
||||
-- check values for time.sleep()
|
||||
-- check in on_triggerthe status check for Falcon
|
||||
-- rework togther with Xiaoquiang the functionality of involved EPICS channels
|
||||
(Callbacks etc.) to minimize the need of sleeping times.
|
||||
|
||||
"""
|
||||
|
||||
import enum
|
||||
import time
|
||||
|
||||
import numpy as np
|
||||
from bec_lib import bec_logger
|
||||
from ophyd import Component as Cpt
|
||||
from ophyd import DeviceStatus, EpicsSignal, EpicsSignalRO, Kind
|
||||
from ophyd_devices.interfaces.base_classes.psi_detector_base import (
|
||||
CustomDetectorMixin,
|
||||
PSIDetectorBase,
|
||||
)
|
||||
#from phoenix_bec.scripts.phoenix import PhoenixBL
|
||||
|
||||
logger = bec_logger.logger
|
||||
|
||||
DETECTOR_TIMEOUT = 5
|
||||
|
||||
"""
|
||||
#class ph_Logger():
|
||||
# PHOENIX Logger to my Logfile #
|
||||
# p_s = PhoenixBL.my_log
|
||||
"""
|
||||
|
||||
|
||||
class PhoenixTriggerError(Exception):
|
||||
"""PhoenixTrigger specific error"""
|
||||
|
||||
|
||||
class SAMPLING(int, enum.Enum):
|
||||
"""Sampling Done PV
|
||||
|
||||
This class serves redabilty of missinx class and ensure correct setting of
|
||||
certain conditions.
|
||||
defiend preset values for certain states to be called in the
|
||||
mixing class, where we for example check whether the sampling is done of not
|
||||
by comparing with SAMPLING.DONE
|
||||
xxx==SAMPLING.DONE
|
||||
|
||||
"""
|
||||
|
||||
RUNNING = 0
|
||||
DONE = 1
|
||||
|
||||
|
||||
class PhoenixTriggerSetup(CustomDetectorMixin):
|
||||
"""
|
||||
Mixin Class to setup the PhoenixTrigger device
|
||||
|
||||
"""
|
||||
|
||||
|
||||
def on_stage(self) -> None:
|
||||
"""
|
||||
|
||||
On stage actions which are executed upon staging the device
|
||||
|
||||
|
||||
"""
|
||||
if self.parent.scaninfo.scan_type == "step": # check whether we use step scanning
|
||||
###############
|
||||
# next lines ensure that TTL trigger is on single sampling mode (posible )
|
||||
##############
|
||||
self.parent.start_csmpl.set(0)
|
||||
time.sleep(0.1)
|
||||
self.parent.total_cycles.set(1)
|
||||
self.parent.smpl.put(1)
|
||||
time.sleep(0.5)
|
||||
#####
|
||||
# set sampling to dwell time of scan
|
||||
######
|
||||
self.parent.total_cycles.set(np.ceil(self.parent.scaninfo.exp_time * 5))
|
||||
|
||||
logger.info(f"Device {self.parent.name} was staged for step scan")
|
||||
|
||||
def on_unstage(self) -> None:
|
||||
"""On unstage actions which are executed upon unstaging the device"""
|
||||
|
||||
self.on_stop()
|
||||
|
||||
def on_trigger(self) -> DeviceStatus:
|
||||
"""On trigger actions which are executed upon triggering the device"""
|
||||
|
||||
# TODO Test the proper check for the falcon state
|
||||
# Check first that falcon is set to acquiring
|
||||
|
||||
# check for falcon
|
||||
print('on_trigger')
|
||||
falcon = self.parent.device_manager.devices.get("falcon", None) # get device
|
||||
timeout = 20
|
||||
print('ss')
|
||||
|
||||
if falcon is not None:
|
||||
# TODO Check that falcon.aquiring.get() == 1 is the correct check.
|
||||
# --> When is the falcon acquiring, this assumes 1?
|
||||
# self.wait_for_signals is defined in PSI_detector_base.CustomDetectorMixin
|
||||
# ALSO check for enabled or not here!!!
|
||||
#
|
||||
|
||||
if not self.wait_for_signals([(falcon.acquiring.get, 1)], timeout=timeout):
|
||||
raise PhoenixTriggerError(
|
||||
f"Device {self.parent.name} is not ready to take trigger, timeout due to waiting for Falcon to get ready. Timeout after {timeout}s"
|
||||
)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
xmap = self.parent.device_manager.devices.get("xmap", None) # get device
|
||||
|
||||
timeout = 20 # 21.17 change to 20 sec
|
||||
|
||||
print('ss2')
|
||||
|
||||
|
||||
if xmap is not None:
|
||||
|
||||
print('xmap erase start')
|
||||
xmap.erase_start.put(1) # .. should be in phoenix_xmap?
|
||||
|
||||
|
||||
print('xmap erase start')
|
||||
ww=xmap.acquiring.get()
|
||||
print(ww)
|
||||
time.sleep(0.1)
|
||||
w2=xmap.acquiring.get()
|
||||
print(w2)
|
||||
|
||||
|
||||
if not self.wait_for_signals([(xmap.acquiring.get, 1)], timeout=timeout):
|
||||
raise PhoenixTriggerError(
|
||||
f"Device {self.parent.name} is not ready to take trigger, timeout due to waiting for xmap to get ready. Timeout after {timeout}s"
|
||||
)
|
||||
|
||||
if self.parent.scaninfo.scan_type == "step":
|
||||
time.sleep(0.2)
|
||||
self.parent.smpl.put(1)
|
||||
# Minimum of 1 cycle has to be waited. Cycle == 0.2s
|
||||
|
||||
time.sleep(0.2)
|
||||
|
||||
# Trigger function from ophyd.Device returns a DeviceStatus. This function
|
||||
# starts a process that creates a DeviceStatus, and waits for the signal_conditions
|
||||
# self.parent.smpl_done.get to change to the value SAMPLING.DONE
|
||||
# Once this takes place, the DeviceStatus.done flag will be set to True.
|
||||
# When BEC calls trigger() on the devices, this method will be called assuming that
|
||||
# the devices config softwareTrigger=True is set.
|
||||
# In ScanBase, the _at_each_point function calls
|
||||
# self.stubs.wait(wait_type="trigger", group="trigger", wait_time=self.exp_time)
|
||||
# which ensures that the DeviceStatus object resolves before continuing,
|
||||
# i.e. DeviceStatus.done = True
|
||||
|
||||
status = self.wait_with_status(
|
||||
signal_conditions=[(self.parent.smpl_done.get, SAMPLING.DONE)],
|
||||
timeout=5 * self.parent.scaninfo.exp_time, # Check if timeout is appropriate
|
||||
check_stopped=True,
|
||||
)
|
||||
|
||||
# explanation of last line (self.parent.smpl_done.get, SAMPLINGDONE.DONE)
|
||||
# creates a tuple which defines a
|
||||
# condition, which is tested in self.wait_with_status, here it tests for :
|
||||
# (self.parent.smpl_done.get() == SAMPLINGDONE.DONE),
|
||||
# where SAMPLINGDONE.DONE =1, as set in code above
|
||||
# As this is in mixing class (PhoenixtriggerSetup), parent.sample_done is defined in
|
||||
# main class as smpl_done = Cpt(EpicsSignalRO, "SMPL-DONE", kind=Kind.config)
|
||||
|
||||
return status # should this be in if clause level or outside?
|
||||
# endif
|
||||
|
||||
def on_stop(self) -> None:
|
||||
"""
|
||||
Actions to stop the Device
|
||||
Here the device is switched back to continuous sampling.
|
||||
|
||||
"""
|
||||
|
||||
self.parent.total_cycles.set(5)
|
||||
|
||||
self.parent.start_csmpl.set(1)
|
||||
time.sleep(0.5)
|
||||
self.parent.smpl.put(1)
|
||||
time.sleep(0.5)
|
||||
self.parent.smpl.put(1)
|
||||
time.sleep(0.2)
|
||||
if self.parent.smpl_done.get() == SAMPLING.RUNNING:
|
||||
return
|
||||
self.parent.smpl.put(1)
|
||||
|
||||
|
||||
class PhoenixTrigger(PSIDetectorBase):
|
||||
"""
|
||||
Class for PHOENIX TTL hardware trigger: 'X07MB-OP2:'
|
||||
|
||||
This device is used to trigger communicate with an ADC card
|
||||
that creates TTL signals to trigger cameras and detectors at Phoenix.
|
||||
|
||||
"""
|
||||
|
||||
##################################################################
|
||||
#
|
||||
# The Variable USER_ACCESS contains an ascii list of functions which will be
|
||||
# visible in dev.TTL. Here, this list is empty.
|
||||
# note that components are alway public
|
||||
#
|
||||
##################################################################
|
||||
|
||||
USER_ACCESS = ["describe", "help"]
|
||||
|
||||
####################################################################
|
||||
#
|
||||
# # specify Setup class into variable custom_prepare_cls
|
||||
#
|
||||
####################################################################
|
||||
|
||||
custom_prepare_cls = PhoenixTriggerSetup
|
||||
|
||||
###################################################################
|
||||
# in __init__ of PSIDetectorBase will the initialzed by
|
||||
# self.custom_prepare = self.custom_prepare_cls(parent=self, **kwargs)
|
||||
# making the instance of PSIDetectorBase availble to functions
|
||||
# in PhoenixTriggerSetup.
|
||||
#
|
||||
# This inherits, among other things, the input parameters, such as
|
||||
# the notable prefix, which is here 'X07MB-OP2:'
|
||||
#
|
||||
# The input of Component=Cpt is Cpt(deviceClass,suffix)
|
||||
# if Cpt is used in a class, which has interited Device, here via:
|
||||
#
|
||||
# (Here PhoenixTrigger <-- PSIDetectorBase <- Device
|
||||
#
|
||||
# intr_count = Cpt(
|
||||
# EpicsSignal, "INTR-COUN
|
||||
# then Cpt will construct - magically- the
|
||||
# Epics channel name = prefix+suffix,
|
||||
#
|
||||
# for example
|
||||
# 'X07MB-OP2:' + 'START-CSMPL' -> 'X07MB-OP2:' + 'START-CSMPL'
|
||||
#
|
||||
# to construct names, for now we keep the convention to derive
|
||||
# them from the EPICS NAMES (such as ) X07MB-OP2:SMPL-DONE --> smpl_done
|
||||
#
|
||||
# this mean access to channel using dev.PH_TTL.smpl_done.get()
|
||||
#
|
||||
#
|
||||
###################################################################
|
||||
|
||||
start_csmpl = Cpt(
|
||||
EpicsSignal, "START-CSMPL", kind=Kind.config, put_complete=True
|
||||
) # cont on / off
|
||||
|
||||
# intr_count = Cpt(
|
||||
# EpicsSignal, "INTR-COUNT", kind=Kind.config, put_complete=True
|
||||
# ) # conter run up
|
||||
|
||||
intr_count = Cpt(EpicsSignal, "INTR-COUNT") # counter run up NEEDS TO BE READ OUT !!!!
|
||||
|
||||
total_cycles = Cpt(
|
||||
EpicsSignal, "TOTAL-CYCLES", kind=Kind.config, put_complete=True
|
||||
) # cycles set
|
||||
smpl = Cpt(
|
||||
EpicsSignal, "SMPL", kind=Kind.config, put_complete=True
|
||||
) # start sampling --> aquire
|
||||
smpl_done = Cpt(
|
||||
EpicsSignalRO, "SMPL-DONE", kind=Kind.config
|
||||
) # show trigger is done, consider using string=True
|
||||
|
||||
|
||||
ph_start_csmpl = start_csmpl
|
||||
ph_intr_count = intr_count
|
||||
ph_total_cycles = total_cycles
|
||||
ph_smpl = smpl
|
||||
ph_smpl_done = smpl_done
|
||||
|
||||
# create subset in name spacs
|
||||
|
||||
def help(self):
|
||||
"""
|
||||
Help function for phoenix_trigger
|
||||
"""
|
||||
|
||||
help_text = """
|
||||
PHOENIX TRIGGER
|
||||
|
||||
signal attributes vs Epics Channels
|
||||
|
||||
description device Epics Channel
|
||||
attribute
|
||||
|
||||
Cont sampling on/off .start_csmpl ~ X07MB-OP2:SMPL
|
||||
1: continous
|
||||
0: continous off
|
||||
|
||||
Counter run up .intr_count ~ " :INTR-COUNT
|
||||
|
||||
Cycles set .total_cycles ~ " :TOTAL-CYCLES
|
||||
1 cycle = 200 ms
|
||||
|
||||
Start sampling (aquiring) .smpl ~ " :SMPL
|
||||
|
||||
Show trigger is done .smpl_done ~ " :SMPL-DONE
|
||||
1: aquiring
|
||||
0: not aquiring
|
||||
|
||||
Assuming device is called dev.PH_TTL
|
||||
|
||||
Read Channel dev.PH_TTL.start_csmpl.get()
|
||||
|
||||
Write into Channel dev.PH_TTL.total_cycles.put(1)
|
||||
dev.PH_TTL.total_cycles.set(1)
|
||||
|
||||
For further general help try the attributes
|
||||
.describe() considered as stable dict
|
||||
.describe_confguration()
|
||||
.summary()
|
||||
._config ._info
|
||||
|
||||
Important channel are collected with prefix ph, to ease finding of the channels
|
||||
|
||||
for example
|
||||
|
||||
dev.PH_TTl.ph_total_cycles = dev.PH_TTl.total_cycles
|
||||
|
||||
.. etc
|
||||
"""
|
||||
|
||||
print("Name of device is", self.name)
|
||||
print("")
|
||||
print(help_text)
|
||||
|
||||
# end def help
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
# Test the PhoenixTrigger class
|
||||
trigger = PhoenixTrigger(name="trigger", prefix="X07MB-OP2:")
|
||||
trigger.wait_for_connection(all_signals=True)
|
||||
trigger.read()
|
||||
trigger.read_configuration()
|
||||
|
||||
trigger.stage()
|
||||
device_status = trigger.trigger()
|
||||
device_status.wait()
|
||||
trigger.unstage()
|
366
phoenix_bec/devices/phoenix_xmap.py
Normal file
366
phoenix_bec/devices/phoenix_xmap.py
Normal file
@ -0,0 +1,366 @@
|
||||
#
|
||||
#
|
||||
# changes version for PHOENIX WITHOUT HDF5 plugin to be revised/renamed...
|
||||
#
|
||||
#
|
||||
|
||||
import enum
|
||||
import os
|
||||
import threading
|
||||
import time
|
||||
|
||||
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,
|
||||
)
|
||||
|
||||
|
||||
from ophyd.mca import EpicsDXP
|
||||
|
||||
from ophyd_devices.devices.dxp import xMAP, EpicsMCARecord
|
||||
|
||||
#from phoenix_bec.devices.dxp_loc import xMAP, EpicsMCARecord
|
||||
|
||||
from ophyd_devices.devices.areadetector.plugins import HDF5Plugin_V35 as HDF5Plugin
|
||||
|
||||
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 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()
|
||||
pass
|
||||
|
||||
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)
|
||||
"""
|
||||
pass
|
||||
|
||||
def initialize_detector_backend(self) -> None:
|
||||
"""
|
||||
Initialize the detector backend for XMAP.
|
||||
currently no function
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# 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 a time.sleep(0.05)llowed 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
|
||||
"""
|
||||
|
||||
# staging for XMAP as used in FDA this is for step scan
|
||||
# make sure that detector is ready to measure
|
||||
|
||||
self.parent.stop_all.set(1)
|
||||
time.sleep(0.05)
|
||||
self.parent.collect_mode.set(0)
|
||||
time.sleep(0.05)
|
||||
self.parent.apply.set(0)
|
||||
time.sleep(0.05)
|
||||
self.parent.preset_mode.set(0)
|
||||
|
||||
time.sleep(0.05)
|
||||
|
||||
# self.prepare_detector()
|
||||
# self.prepare_data_backend()
|
||||
# self.publish_file_location(done=False, successful=False)
|
||||
# self.arm_acquisition() time.sleep(0.05)
|
||||
|
||||
def prepare_detector(self) -> None:
|
||||
"""Prepare detector for acquisition.. originally called from on stage """
|
||||
pass
|
||||
"""
|
||||
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.. originally called from on_stage """
|
||||
pass
|
||||
""" --------------------------------------------------------------
|
||||
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 called fron on stage """
|
||||
pass
|
||||
"""
|
||||
self.parent.start_all.put(1)
|
||||
signal_conditions = [
|
||||
(
|
||||
lambda: self.parent.acquiring.read()[self.parent.acquiring.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 called from on _sttop"""
|
||||
self.parent.stop_all.put(1)
|
||||
self.parent.erase_all.put(1)
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# signal_conditions = [
|
||||
# (lambda: self.parent.acquiring.read()[self.parent.acquiring.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)
|
||||
|
||||
|
||||
def finished(self, timeout: int = 5) -> None:
|
||||
"""Check if scan finished succesfully called fon on _complete """
|
||||
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 finisheddetector
|
||||
|
||||
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
|
||||
|
||||
"""
|
||||
pass
|
||||
|
||||
"""
|
||||
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, xMAP):
|
||||
"""
|
||||
XMAP 4-element detector for phoenix
|
||||
custom_prepare_cls (XMAPSetup) : Custom detector setup class for PHOENIX,
|
||||
inherits from CustomDetectorMixin
|
||||
in __init__ of PSIDetecor base
|
||||
PSIDetectorBase.set_min_readout (float) : Minimum readout time for the detector
|
||||
dxp<n> (EpicsDXPXMAP) : DXP parameters for XMAP detector
|
||||
mca<n> (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", "ep"]
|
||||
|
||||
# specify Setup class
|
||||
custom_prepare_cls = XMAPSetup
|
||||
# specify minimum readout time for detector
|
||||
MIN_READOUT = 3e-3
|
||||
TIMEOUT_FOR_SIGNALS = 5
|
||||
|
||||
# for fast loading uncomment dxp and mca for now
|
||||
# dxp1 = Cpt(EpicsDXP, "dxp1:")
|
||||
# dxp2 = Cpt(EpicsDXP, "dxp2:")
|
||||
# dxp3 = Cpt(EpicsDXP, "dxp3:")
|
||||
# dxp4 = Cpt(EpicsDXP, "dxp4:")
|
||||
|
||||
# mca1 = Cpt(EpicsMCARecord, "mca1")
|
||||
# mca2 = Cpt(EpicsMCARecord, "mca2")
|
||||
# mca3 = Cpt(EpicsMCARecord, "mca3")
|
||||
# mca4 = Cpt(EpicsMCARecord, "mca4")
|
||||
|
||||
hdf5 = Cpt(HDF5Plugin, "HDF1:")
|
||||
|
||||
|
||||
# for compatibility and mapping ruke EPICS --> bec
|
||||
|
||||
preset_real = xMAP.preset_real_time
|
||||
preset_live = xMAP.preset_live_time
|
||||
|
||||
# create some easy to find names for frequently used channels
|
||||
|
||||
ph_erase_start = xMAP.erase_start
|
||||
ph_start_all = xMAP.start_all
|
||||
ph_stop_all = xMAP.stop_all
|
||||
ph_erase_all = xMAP.erase_all
|
||||
ph_collect_mode = xMAP.collect_mode
|
||||
ph_preset_mode = xMAP.preset_mode
|
||||
ph_preset_real = xMAP.preset_real_time
|
||||
ph_preset_live = xMAP.preset_live_time
|
||||
ph_elapsed_real = xMAP.elapsed_real
|
||||
ph_elapsed_live = xMAP.elapsed_live
|
||||
|
||||
|
63
phoenix_bec/local_scripts/Code_to_test_devices/test.py
Normal file
63
phoenix_bec/local_scripts/Code_to_test_devices/test.py
Normal file
@ -0,0 +1,63 @@
|
||||
|
||||
class X:
|
||||
|
||||
def print_dict(self,dict,level=4,_level=0):
|
||||
"""
|
||||
Utility to print structure of a dictionary
|
||||
"""
|
||||
_level=_level+1
|
||||
if _level==3:
|
||||
return
|
||||
#print('............... LEVEL'+str(level))
|
||||
for key in dict:
|
||||
print(_level,'...'*(_level-1),key)
|
||||
#print(dict[key])
|
||||
if 'dict' in str(type(dict[key])):
|
||||
print(' ')
|
||||
self.print_dict(dict[key],level=level,_level=_level)
|
||||
#endfor
|
||||
if _level==1:
|
||||
print('...... main level keys....')
|
||||
print(dict.keys())
|
||||
#endif
|
||||
|
||||
def print_signale(self,dict,level=1,_level=0):
|
||||
"""
|
||||
Utility to print structure of a dictionary
|
||||
"""
|
||||
_level=_level+1
|
||||
if _level==3:
|
||||
return
|
||||
#print('............... LEVEL'+str(level))
|
||||
for key in dict:
|
||||
print(_level,'...'*(_level-1),key)
|
||||
#print(dict[key])
|
||||
if 'dict' in str(type(dict[key])):
|
||||
print(' ')
|
||||
self.print_dict(dict[key],level=level,_level=_level)
|
||||
#endfor
|
||||
if _level==1:
|
||||
print('...... main level keys....')
|
||||
print(dict.keys())
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
this_dev_ttl=dev.PH_TTL
|
||||
this_dev_fa=dev.falcon
|
||||
this_dev=this_dev_fa
|
||||
w_dict=this_dev.__dict__
|
||||
w_dir=this_dev.__dir__()
|
||||
w_info=this_dev._info
|
||||
print('Structure of dict')
|
||||
print(w_dict.keys())
|
||||
keys=w_dict.keys()
|
||||
x=X()
|
||||
x.print_dict(w_dict,level=1)
|
@ -0,0 +1,48 @@
|
||||
# against all rues, make sure ff and falcon are really
|
||||
# creates newly
|
||||
|
||||
|
||||
ff = 0
|
||||
falcon = 0
|
||||
|
||||
from ophyd import Component as Cpt
|
||||
import phoenix_bec.devices.phoenix_falcon as ff
|
||||
|
||||
falcon = ff.FalconPhoenix(name="falcon", prefix="X07MB-SITORO:")
|
||||
# xmap = ff.FalconPhoenix(name="falcon_hdf5", prefix="X07MB-XMAP:")
|
||||
# make a 'get to read all epics channels
|
||||
# there will be an error message, if device contains a channel whcih does not exist
|
||||
w = falcon.get()
|
||||
|
||||
#print(w)
|
||||
# for attr in falcon.hdf5.component_names:
|
||||
# if not attr.startswith("_"):
|
||||
# print(attr)
|
||||
# signal = getattr(falcon, attr)
|
||||
# signal.get()
|
||||
|
||||
|
||||
"""
|
||||
print(this_scan.scan.data.keys())
|
||||
for outer_key in this_scan.scan.data.keys():
|
||||
print("outer_key", outer_key)
|
||||
n_outer = len(this_scan.scan.data.keys())
|
||||
for inner_key in this_scan.scan.data[outer_key].keys():
|
||||
print("inner_key", inner_key)
|
||||
# calculate nunber of points
|
||||
n_inner = len(this_scan.scan.data[outer_key][inner_key].keys())
|
||||
value = np.zeros(n_inner)
|
||||
timestamp = np.zeros(n_inner)
|
||||
for i in range(n_inner):
|
||||
try:
|
||||
value[i] = this_scan.scan.data[outer_key][inner_key][i]["value"]
|
||||
except:
|
||||
value = None
|
||||
try:
|
||||
timestamp[i] = this_scan.scan.data[outer_key][inner_key][i]["timestamp"]
|
||||
except:
|
||||
timestamp[i] = None
|
||||
# endfor
|
||||
self.add(inner_key + "_" + outer_key + "_val", value)
|
||||
self.add(innerprint("test")
|
||||
"""
|
@ -0,0 +1,17 @@
|
||||
import time
|
||||
from phoenix_bec.scripts.phoenix import PhoenixBL
|
||||
|
||||
print("imported")
|
||||
time.sleep(2)
|
||||
ph = PhoenixBL()
|
||||
# ph.read_phoenix_config()
|
||||
|
||||
#
|
||||
# how do we get this to iphython command line ?
|
||||
# print('........... falkon.mca1 '
|
||||
##
|
||||
|
||||
# ph.list_signals_falcon()
|
||||
# ph.list_signals_xmap()
|
||||
ph.run_shell("ls")
|
||||
ph.run_shell(["ls -altr ", "pwd"])
|
@ -0,0 +1,9 @@
|
||||
import time
|
||||
from phoenix_bec.scripts.phoenix import PhoenixBL
|
||||
|
||||
from ophyd import Component as Cpt
|
||||
import phoenix_bec.devices.phoenix_trigger as pt
|
||||
|
||||
trig = pt.PhoenixTrigger(name="phoenixTrigger", prefix="X07MB-OP2:")
|
||||
trig.describe()
|
||||
trig.custom_prepare.on_trigger
|
@ -0,0 +1,19 @@
|
||||
import time
|
||||
from phoenix_bec.scripts.phoenix import PhoenixBL
|
||||
|
||||
from ophyd import Component as Cpt
|
||||
import phoenix_bec.devices.phoenix_scan_1 as sc_1
|
||||
|
||||
ss = sc_1.PhoenixScan_1(name="phoenixTrigger", prefix="X07MB-OP2:")
|
||||
|
||||
print('s')
|
||||
ss.describe()
|
||||
print('o')
|
||||
|
||||
ss.ph_xmap_stop_all.put(1)
|
||||
|
||||
ss.csmpl.set(0)
|
||||
time.sleep(1.4)
|
||||
ss.custom_prepare.on_trigger()
|
||||
|
||||
|
15
phoenix_bec/local_scripts/Code_to_test_devices/test_xmap.py
Normal file
15
phoenix_bec/local_scripts/Code_to_test_devices/test_xmap.py
Normal file
@ -0,0 +1,15 @@
|
||||
# against all rues, make sure ff and falcon are really
|
||||
# creates newly
|
||||
|
||||
|
||||
ff = 0
|
||||
falcon = 0
|
||||
|
||||
from ophyd import Component as Cpt
|
||||
import phoenix_bec.devices.phoenix_xmap as ff
|
||||
|
||||
xmap = ff.XMAPPhoenix(name="xmap", prefix="X07MB-XMAP:")
|
||||
|
||||
#HOw to test function in mixing layer
|
||||
|
||||
xmap.custom_prepare.on_stage()
|
@ -0,0 +1,9 @@
|
||||
from phoenix_bec.devices.obsolete.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 ?
|
||||
#
|
45
phoenix_bec/local_scripts/Documentation/.bashrc
Normal file
45
phoenix_bec/local_scripts/Documentation/.bashrc
Normal file
@ -0,0 +1,45 @@
|
||||
# .bashrc
|
||||
|
||||
# Source global definitions
|
||||
if [ -f /etc/bashrc ]; then
|
||||
. /etc/bashrc
|
||||
fi
|
||||
|
||||
# User specific environment
|
||||
if ! [[ "$PATH" =~ "$HOME/.local/bin:$HOME/bin:" ]]
|
||||
then
|
||||
PATH="$HOME/.local/bin:$HOME/bin:$PATH"
|
||||
fi
|
||||
export PATH
|
||||
|
||||
# Uncomment the following line if you don't like systemctl's auto-paging feature:
|
||||
# export SYSTEMD_PAGER=
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# User specific aliases and functions
|
||||
|
||||
|
||||
|
||||
|
||||
BEC_PATH='/data/test/x07mb-test-bec/production'
|
||||
alias cd_bec='cd '$BEC_PATH
|
||||
alias bec_env='source 'BEC_PATH'/bec_venv/bin/activate'
|
||||
alias cd_bec_ph='cd '$BEC_PATH'/phoenix_bec/phoenix_bec'
|
||||
alias cd_bec_local_scripts='cd '$BEC_PATH'/phoenix_bec/phoenix_bec/local_scripts'
|
||||
|
||||
alias cd_bec_devices='cd '$BEC_PATH'/phoenix_bec/phoenix_bec/devices'
|
||||
alias cd_bec_devices='cd '$BEC_PATH'/phoenix_bec/phoenix_bec/device_configs'
|
||||
|
||||
alias cd_bec_device_configs='cd /data/test/x07mb-test-bec/production/phoenix_bec/phoenix_bec/device_configs'
|
||||
alias cd_bec_scripts='cd /data/test/x07mb-test-bec/production/phoenix_bec/phoenix_bec/scripts'
|
||||
alias cd_bec_venv_bin='cd /data/test/x07mb-test-bec/production/bec_venv/bin'
|
||||
|
||||
|
||||
#alias activate_bec_venv='source /data/test/x07mb-test-bec/bec_deployment/bec_venv/bin/activate'
|
||||
#alias activate_bec_venv='source /data/test/x07mb-test-bec/production/bec_venv/bin/activate'
|
||||
alias bec_env='source /data/test/x07mb-test-bec/production/bec_venv/bin/activate'
|
||||
|
@ -0,0 +1,435 @@
|
||||
FILE ophyd_devices/ophy_devices/devices/interfaces/base_classes
|
||||
|
||||
|
||||
"""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 Custoon_
|
||||
import threading
|
||||
import time
|
||||
import traceback
|
||||
|
||||
from bec_lib import messages
|
||||
from bec_lib.endpoints import MessageEndpoints
|
||||
from bec_lib.file_utils import FileWriter
|
||||
from bec_lib.logger import bec_logger
|
||||
from ophyd import Component, Device, DeviceStatus, Kind
|
||||
from ophyd.device import Staged
|
||||
|
||||
from ophyd_devices.sim.sim_signals import SetableSignal
|
||||
from ophyd_devices.utils import bec_utils
|
||||
from ophyd_devices.utils.bec_scaninfo_mixin import BecScaninfoMixin
|
||||
from ophyd_devices.utils.errors import DeviceStopError, DeviceTimeoutError
|
||||
|
||||
logger = bec_logger.logger
|
||||
|
||||
|
||||
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
|
||||
"""
|
||||
|
||||
def on_stage(self) -> None:
|
||||
"""
|
||||
Specify actions to be executed during stage in preparation for a scan.
|
||||
self.parent.scaninfo already 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 = None,
|
||||
) -> 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
|
||||
"""
|
||||
if exception_on_timeout is None:
|
||||
exception_on_timeout = DeviceTimeoutError(
|
||||
f"Timeout error for {self.parent.name} while waiting for signals {signal_conditions}"
|
||||
)
|
||||
|
||||
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,
|
||||
):
|
||||
"""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:
|
||||
if self.parent.stopped:
|
||||
# INFO This will execute a callback to the parent device.stop() method
|
||||
status.set_exception(exc=DeviceStopError(f"{self.parent.name} was stopped"))
|
||||
else:
|
||||
# INFO This will execute a callback to the parent device.stop() method
|
||||
status.set_exception(exc=exception_on_timeout)
|
||||
# pylint: disable=broad-except
|
||||
except Exception as exc:
|
||||
content = traceback.format_exc()
|
||||
logger.warning(
|
||||
f"Error in wait_for_signals in {self.parent.name}; Traceback: {content}"
|
||||
)
|
||||
# INFO This will execute a callback to the parent device.stop() method
|
||||
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
|
||||
|
||||
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()
|
||||
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
|
@ -0,0 +1,512 @@
|
||||
"""
|
||||
This module contains the Scans class and related classes for defining and running scans in BEC
|
||||
from the client side.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import builtins
|
||||
import uuid
|
||||
from collections.abc import Callable
|
||||
from contextlib import ContextDecorator
|
||||
from copy import deepcopy
|
||||
from typing import TYPE_CHECKING, Dict, Literal
|
||||
|
||||
from toolz import partition
|
||||
from typeguard import typecheck
|
||||
from bec_lib import messages
|
||||
from bec_lib.bec_errors import ScanAbortion
|
||||
from bec_lib.client import SystemConfig
|
||||
from bec_lib.device import DeviceBase
|
||||
from bec_lib.endpoints import MessageEndpoints
|
||||
from bec_lib.logger import bec_logger
|
||||
from bec_lib.scan_report import ScanReport
|
||||
from bec_lib.signature_serializer import dict_to_signature
|
||||
from bec_lib.utils import scan_to_csv
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from bec_lib.client import BECClient
|
||||
from bec_lib.connector import ConsumerConnector
|
||||
|
||||
logger = bec_logger.logger
|
||||
|
||||
|
||||
class ScanObject:
|
||||
"""ScanObject is a class for scans"""
|
||||
|
||||
def __init__(self, scan_name: str, scan_info: dict, client: BECClient = None) -> None:
|
||||
self.scan_name = scan_name
|
||||
self.scan_info = scan_info
|
||||
self.client = client
|
||||
|
||||
# run must be an anonymous function to allow for multiple doc strings
|
||||
# pylint: disable=unnecessary-lambda
|
||||
self.run = lambda *args, **kwargs: self._run(*args, **kwargs)
|
||||
|
||||
def _run(
|
||||
self,
|
||||
*args,
|
||||
callback: Callable = None,
|
||||
async_callback: Callable = None,
|
||||
hide_report: bool = False,
|
||||
metadata: dict = None,
|
||||
monitored: list[str | DeviceBase] = None,
|
||||
file_suffix: str = None,
|
||||
file_directory: str = None,
|
||||
**kwargs,
|
||||
) -> ScanReport:
|
||||
"""
|
||||
Run the request with the given arguments.
|
||||
|
||||
Args:
|
||||
*args: Arguments for the scan
|
||||
callback: Callback function
|
||||
async_callback: Asynchronous callback function
|
||||
hide_report: Hide the report
|
||||
metadata: Metadata dictionary
|
||||
monitored: List of monitored devices
|
||||
**kwargs: Keyword arguments
|
||||
|
||||
Returns:
|
||||
ScanReport
|
||||
"""
|
||||
if self.client.alarm_handler.alarms_stack:
|
||||
logger.info("The alarm stack is not empty but will be cleared now.")
|
||||
self.client.clear_all_alarms()
|
||||
scans = self.client.scans
|
||||
|
||||
# pylint: disable=protected-access
|
||||
hide_report = hide_report or scans._hide_report
|
||||
|
||||
user_metadata = deepcopy(self.client.metadata)
|
||||
|
||||
sys_config = self.client.system_config.model_copy(deep=True)
|
||||
if file_suffix:
|
||||
sys_config.file_suffix = file_suffix
|
||||
if file_directory:
|
||||
sys_config.file_directory = file_directory
|
||||
|
||||
if "sample_name" not in user_metadata:
|
||||
var = self.client.get_globa file_suffix: str = None,
|
||||
l_var("sample_name")
|
||||
if var is not None:
|
||||
user_metadata["sample_name"] = var
|
||||
|
||||
if metadata is not None:
|
||||
user_metadata.update(metadata)
|
||||
|
||||
if monitored is not None:
|
||||
if not isinstance(monitored, list):
|
||||
monitored = [monitored]
|
||||
for mon_device in monitored:
|
||||
if isinstance(mon_device, str):
|
||||
mon_device = self.client.device_manager.devices.get(mon_device)
|
||||
if not mon_device:
|
||||
raise RuntimeError(
|
||||
f"Specified monitored device {mon_device} does not exist in the current device configuration."
|
||||
)
|
||||
kwargs["monitored"] = monitored
|
||||
|
||||
sys_config = sys_config.model_dump()
|
||||
# pylint: disable=protected-access
|
||||
if scans._scan_group:
|
||||
sys_config["queue_group"] = scans._scan_group
|
||||
if scans._scan_def_id:
|
||||
sys_config["scan_def_id"] = scans._scan_def_id
|
||||
if scans._dataset_id_on_hold:
|
||||
sys_config["dataset_id_on_hold"] = scans._dataset_id_on_hold
|
||||
|
||||
kwargs["user_metadata"] = user_metadata
|
||||
kwargs["system_config"] = sys_config
|
||||
|
||||
request = Scans.prepare_scan_request(self.scan_name, self.scan_info, *args, **kwargs)
|
||||
request_id = str(uuid.uuid4())
|
||||
|
||||
# pylint: disable=unsupported-assignment-operation
|
||||
request.metadata["RID"] = request_id
|
||||
|
||||
self._send_scan_request(request)
|
||||
|
||||
report = ScanReport.from_request(request, client=self.client)
|
||||
report.request.callbacks.register_many("scan_segment", callback, sync=True)
|
||||
report.request.callbacks.register_many("scan_segment", async_callback, sync=False)
|
||||
|
||||
if scans._scan_export and scans._scan_export.scans is not None:
|
||||
scans._scan_export.scans.append(report)
|
||||
|
||||
if not hide_report and self.client.live_updates:
|
||||
self.client.live_updates.process_request(request, callback)
|
||||
|
||||
self.client.callbacks.poll()
|
||||
|
||||
return report
|
||||
|
||||
def _start_register(self, request: messages.ScanQueueMessage) -> ConsumerConnector:
|
||||
"""Start a register for the given request"""
|
||||
register = self.client.device_manager.connector.register(
|
||||
[
|
||||
MessageEndpoints.device_readback(dev)
|
||||
for dev in request.content["parameter"]["args"].keys()
|
||||
],
|
||||
threaded=False,
|
||||
cb=(lambda msg: msg),
|
||||
)
|
||||
return register
|
||||
|
||||
def _send_scan_request(self, request: messages.ScanQueueMessage) -> None:
|
||||
"""Send a scan request to the scan server"""
|
||||
self.client.device_manager.connector.send(MessageEndpoints.scan_queue_request(), request)
|
||||
|
||||
|
||||
class Scans:
|
||||
"""Scans is a class for available scans in BEC"""
|
||||
|
||||
def __init__(self, parent):
|
||||
self.parent = parent
|
||||
self._available_scans = {}
|
||||
self._import_scans()
|
||||
self._scan_group = None
|
||||
self._scan_def_id = None
|
||||
self._scan_group_ctx = ScanGroup(parent=self)
|
||||
self._scan_def_ctx = ScanDef(parent=self)
|
||||
self._hide_report = None
|
||||
self._hide_report_ctx = HideReport(parent=self)
|
||||
self._dataset_id_on_hold = None
|
||||
self._dataset_id_on_hold_ctx = DatasetIdOnHold(parent=self)
|
||||
self._scan_export = None
|
||||
|
||||
def _import_scans(self):
|
||||
"""Import scans from the scan server"""
|
||||
available_scans = self.parent.connector.get(MessageEndpoints.available_scans())
|
||||
if available_scans is None:
|
||||
logger.warning("No scans available. Are redis and the BEC server running?")
|
||||
return
|
||||
for scan_name, scan_info in available_scans.resource.items():
|
||||
self._available_scans[scan_name] = ScanObject(scan_name, scan_info, client=self.parent)
|
||||
setattr(self, scan_name, self._available_scans[scan_name].run)
|
||||
setattr(getattr(self, scan_name), "__doc__", scan_info.get("doc"))
|
||||
setattr(
|
||||
getattr(self, scan_name),
|
||||
"__signature__",
|
||||
dict_to_signature(scan_info.get("signature")),
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def get_arg_type(in_type: str):
|
||||
"""translate type string into python type"""
|
||||
# pylint: disable=too-many-return-statements
|
||||
if in_type == "float":
|
||||
return (float, int)
|
||||
if in_type == "int":
|
||||
return int
|
||||
if in_type == "list":
|
||||
return list
|
||||
if in_type == "boolean":
|
||||
return bool
|
||||
if in_type == "str":
|
||||
return str
|
||||
if in_type == "dict":
|
||||
return dict
|
||||
if in_type == "device":
|
||||
return DeviceBase
|
||||
raise TypeError(f"Unknown type {in_type}")
|
||||
|
||||
@staticmethod
|
||||
def prepare_scan_request(
|
||||
scan_name: str, scan_info: dict, *args, **kwargs
|
||||
) -> messages.ScanQueueMessage:
|
||||
"""Prepare scan request message with given scan arguments
|
||||
|
||||
Args:
|
||||
scan_name (str): scan name (matching a scan name on the scan server)
|
||||
scan_info (dict): dictionary describing the scan (e.g. doc string, required kwargs etc.)
|
||||
|
||||
Raises:
|
||||
TypeError: Raised if not all required keyword arguments have been specified.
|
||||
TypeError: Raised if the number of args do fit into the required bundling pattern.
|
||||
TypeError: Raised if an argument is not of the required type as specified in scan_info.
|
||||
|
||||
Returns:
|
||||
messages.ScanQueueMessage: scan request message
|
||||
"""
|
||||
arg_input = list(scan_info.get("arg_input", {}).values())
|
||||
|
||||
arg_bundle_size = scan_info.get("arg_bundle_size", {})
|
||||
bundle_size = arg_bundle_size.get("bundle")
|
||||
if len(arg_input) > 0:
|
||||
if len(args) % len(arg_input) != 0:
|
||||
raise TypeError(
|
||||
f"{scan_info.get('doc')}\n {scan_name} takes multiples of"
|
||||
f" {len(arg_input)} arguments ({len(args)} given)."
|
||||
)
|
||||
if not all(req_kwarg in kwargs for req_kwarg in scan_info.get("required_kwargs")):
|
||||
raise TypeError(
|
||||
f"{scan_info.get('doc')}\n Not all required keyword arguments have been"
|
||||
f" specified. The required arguments are: {scan_info.get('required_kwargs')}"
|
||||
)
|
||||
# check that all specified devices in args are different objects
|
||||
for arg in args:
|
||||
if not isinstance(arg, DeviceBase):
|
||||
continue
|
||||
if args.count(arg) > 1:
|
||||
raise TypeError(
|
||||
f"{scan_info.get('doc')}\n All specified devices must be different"
|
||||
f" objects."
|
||||
)
|
||||
|
||||
# check that all arguments are of the correct type
|
||||
for ii, arg in enumerate(args):
|
||||
if not isinstance(arg, Scans.get_arg_type(arg_input[ii % len(arg_input)])):
|
||||
raise TypeError(
|
||||
f"{scan_info.get('doc')}\n Argument {ii} must be of type"
|
||||
f" {arg_input[ii%len(arg_input)]}, not {type(arg).__name__}."
|
||||
)
|
||||
|
||||
metadata = {}
|
||||
metadata.update(kwargs["system_config"])
|
||||
metadata["user_metadata"] = kwargs.pop("user_metadata", {})
|
||||
|
||||
params = {"args": Scans._parameter_bundler(args, bundle_size), "kwargs": kwargs}
|
||||
# check the number of arg bundles against the number of required bundles
|
||||
if bundle_size:
|
||||
num_bundles = len(params["args"])
|
||||
min_bundles = arg_bundle_size.get("min")
|
||||
max_bundles = arg_bundle_size.get("max")
|
||||
if min_bundles and num_bundles < min_bundles:
|
||||
raise TypeError(
|
||||
f"{scan_info.get('doc')}\n {scan_name} requires at least {min_bundles} bundles"
|
||||
f" of arguments ({num_bundles} given)."
|
||||
)
|
||||
if max_bundles and num_bundles > max_bundles:
|
||||
raise TypeError(
|
||||
f"{scan_info.get('doc')}\n {scan_name} requires at most {max_bundles} bundles"
|
||||
f" of arguments ({num_bundles} given)."
|
||||
)
|
||||
return messages.ScanQueueMessage(
|
||||
scan_type=scan_name, parameter=params, queue="primary", metadata=metadata
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _parameter_bundler(args, bundle_size):
|
||||
"""
|
||||
|
||||
Args:
|
||||
args:
|
||||
bundle_size: number of parameters per bundle
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
if not bundle_size:
|
||||
return tuple(cmd.name if hasattr(cmd, "name") else cmd for cmd in args)
|
||||
params = {}
|
||||
for cmds in partition(bundle_size, args):
|
||||
cmds_serialized = [cmd.name if hasattr(cmd, "name") else cmd for cmd in cmds]
|
||||
params[cmds_serialized[0]] = cmds_serialized[1:]
|
||||
return params
|
||||
|
||||
@property
|
||||
def scan_group(self):
|
||||
"""Context manager / decorator for defining scan groups"""
|
||||
return self._scan_group_ctx
|
||||
|
||||
@property
|
||||
def scan_def(self):
|
||||
"""Context manager / decorator for defining new scans"""
|
||||
return self._scan_def_ctx
|
||||
|
||||
@property
|
||||
def hide_report(self):
|
||||
"""Context manager / decorator for hiding the report"""
|
||||
return self._hide_report_ctx
|
||||
|
||||
@property
|
||||
def dataset_id_on_hold(self):
|
||||
"""Context manager / decorator for setting the dataset id on hold"""
|
||||
return self._dataset_id_on_hold_ctx
|
||||
|
||||
def scan_export(self, output_file: str):
|
||||
"""Context manager / decorator for exporting scans"""
|
||||
return ScanExport(output_file)
|
||||
|
||||
|
||||
class ScanGroup(ContextDecorator):
|
||||
"""ScanGroup is a ContextDecorator for defining a scan group"""
|
||||
|
||||
def __init__(self, parent: Scans = None) -> None:
|
||||
super().__init__()
|
||||
self.parent = parent
|
||||
|
||||
def __enter__(self):
|
||||
group_id = str(uuid.uuid4())
|
||||
self.parent._scan_group = group_id
|
||||
return self
|
||||
|
||||
def __exit__(self, *exc):
|
||||
self.parent.close_scan_group()
|
||||
self.parent._scan_group = None
|
||||
|
||||
|
||||
class ScanDef(ContextDecorator):
|
||||
"""ScanDef is a ContextDecorator for defining a new scan"""
|
||||
|
||||
def __init__(self, parent: Scans = None) -> None:
|
||||
super().__init__()
|
||||
self.parent = parent
|
||||
|
||||
def __enter__(self):
|
||||
if self.parent._scan_def_id is not None:
|
||||
raise ScanAbortion("Nested scan definitions currently not supported.")
|
||||
scan_def_id = str(uuid.uuid4())
|
||||
self.parent._scan_def_id = scan_def_id
|
||||
self.parent.open_scan_def()
|
||||
return self
|
||||
|
||||
def __exit__(self, *exc):
|
||||
if exc[0] is None:
|
||||
self.parent.close_scan_def()
|
||||
self.parent._scan_def_id = None
|
||||
|
||||
|
||||
class HideReport(ContextDecorator):
|
||||
"""HideReport is a ContextDecorator for hiding the report"""
|
||||
|
||||
def __init__(self, parent: Scans = None) -> None:
|
||||
super().__init__()
|
||||
self.parent = parent
|
||||
|
||||
def __enter__(self):
|
||||
if self.parent._hide_report is None:
|
||||
self.parent._hide_report = True
|
||||
return self
|
||||
|
||||
def __exit__(self, *exc):
|
||||
self.parent._hide_report = None
|
||||
|
||||
|
||||
class DatasetIdOnHold(ContextDecorator):
|
||||
"""DatasetIdOnHold is a ContextDecorator for setting the dataset id on hold"""
|
||||
|
||||
def __init__(self, parent: Scans = None) -> None:
|
||||
super().__init__()
|
||||
self.parent = parent
|
||||
self._call_count = 0
|
||||
|
||||
def __enter__(self):
|
||||
self._call_count += 1
|
||||
if self.parent._dataset_id_on_hold is None:
|
||||
self.parent._dataset_id_on_hold = True
|
||||
return self
|
||||
|
||||
def __exit__(self, *exc):
|
||||
self._call_count -= 1
|
||||
if self._call_count:
|
||||
return
|
||||
self.parent._dataset_id_on_hold = None
|
||||
queue = self.parent.parent.queue
|
||||
queue.next_dataset_number += 1
|
||||
|
||||
|
||||
class FileWriter:
|
||||
@typechecked
|
||||
def __init__(self, file_suffix: str = None, file_directory: str = None) -> None:
|
||||
"""Context manager for updating metadata
|
||||
|
||||
Args:
|
||||
fw_config (dict): Dictionary with metadata for the filewriter, can only have keys "file_suffix" and "file_directory"
|
||||
"""
|
||||
self.client = self._get_client()
|
||||
self.system_config = self.client.system_config
|
||||
self._orig_system_config = None
|
||||
self._orig_metadata = None
|
||||
self.file_suffix = file_suffix
|
||||
self.file_directory = file_directory
|
||||
|
||||
def _get_client(self):
|
||||
"""Get BEC client"""
|
||||
return builtins.__dict__["bec"]
|
||||
|
||||
def __enter__(self):
|
||||
"""Enter the context manager"""
|
||||
self._orig_metadata = deepcopy(self.client.metadata)
|
||||
self._orig_system_config = self.system_config.model_copy(deep=True)
|
||||
self.system_config.file_suffix = self.file_suffix
|
||||
self.system_config.file_directory = self.file_directory
|
||||
return self
|
||||
|
||||
def __exit__(self, *exc):
|
||||
"""Exit the context manager"""
|
||||
self.client.metadata = self._orig_metadata
|
||||
self.system_config.file_suffix = self._orig_system_config.file_suffix
|
||||
self.system_config.file_directory = self._orig_system_config.file_directory
|
||||
|
||||
|
||||
class Metadata:
|
||||
@typechecked
|
||||
def __init__(self, metadata: dict) -> None:
|
||||
"""Context manager for updating metadata
|
||||
|
||||
Args:
|
||||
metadata (dict): Metadata dictionary
|
||||
"""
|
||||
self.client = self._get_client()
|
||||
self._metadata = metadata
|
||||
self._orig_metadata = None
|
||||
|
||||
def _get_client(self):
|
||||
"""Get BEC client"""
|
||||
return builtins.__dict__["bec"]
|
||||
|
||||
def __enter__(self):
|
||||
"""Enter the context manager"""
|
||||
self._orig_metadata = deepcopy(self.client.metadata)
|
||||
self.client.metadata.update(self._metadata)
|
||||
return self
|
||||
|
||||
def __exit__(self, *exc):
|
||||
"""Exit the context manager"""
|
||||
self.client.metadata = self._orig_metadata
|
||||
|
||||
|
||||
class ScanExport:
|
||||
def __init__(self, output_file: str) -> None:
|
||||
"""Context manager for exporting scans
|
||||
|
||||
Args:
|
||||
output_file (str): Output file name
|
||||
"""
|
||||
self.output_file = output_file
|
||||
self.client = None
|
||||
self.scans = None
|
||||
|
||||
def _check_abort_on_ctrl_c(self):
|
||||
"""Check if scan should be aborted on Ctrl-C"""
|
||||
# pylint: disable=protected-access
|
||||
if not self.client._service_config.abort_on_ctrl_c:
|
||||
raise RuntimeError(
|
||||
"ScanExport context manager can only be used if abort_on_ctrl_c is set to True"
|
||||
)
|
||||
|
||||
def _get_client(self):
|
||||
return builtins.__dict__["bec"]
|
||||
|
||||
def __enter__(self):
|
||||
self.scans = []
|
||||
self.client = self._get_client()
|
||||
self.client.scans._scan_export = self
|
||||
self._check_abort_on_ctrl_c()
|
||||
return self
|
||||
|
||||
def _export_to_csv(self):
|
||||
scan_to_csv(self.scans, self.output_file)
|
||||
|
||||
def __exit__(self, *exc):
|
||||
try:
|
||||
for scan in self.scans:
|
||||
scan.wait()
|
||||
finally:
|
||||
try:
|
||||
self._export_to_csv()
|
||||
self.scans = None
|
||||
except Exception as exc:
|
||||
logger.warning(f"Could not export scans to csv file, due to exception {exc}")
|
@ -0,0 +1,510 @@
|
||||
import enum
|
||||
import time
|
||||
from typing import Any
|
||||
|
||||
from bec_lib import bec_logger
|
||||
from ophyd import (
|
||||
Component,
|
||||
Device,
|
||||
DeviceStatus,
|
||||
EpicsSignal,
|
||||
EpicsSignalRO,
|
||||
Kind,
|
||||
PVPositioner,
|
||||
Signal,
|
||||
)
|
||||
from ophyd.device import Staged
|
||||
from ophyd.pseudopos import (
|
||||
PseudoPositioner,
|
||||
PseudoSingle,
|
||||
pseudo_position_argument,
|
||||
real_position_argument,
|
||||
)
|
||||
|
||||
from ophyd_devices.utils import bec_utils
|
||||
from ophyd_devices.utils.bec_scaninfo_mixin import BecScaninfoMixin
|
||||
|
||||
logger = bec_logger.logger
|
||||
|
||||
|
||||
class DelayGeneratorError(Exception):
|
||||
"""Exception raised for errors."""
|
||||
|
||||
|
||||
class DeviceInitError(DelayGeneratorError):
|
||||
"""Error upon failed initialization, invoked by missing device manager or device not started in sim_mode."""
|
||||
|
||||
|
||||
class DelayGeneratorNotOkay(DelayGeneratorError):
|
||||
"""Error when DDG is not okay"""
|
||||
|
||||
|
||||
class TriggerSource(enum.IntEnum):
|
||||
"""
|
||||
Class for trigger options of DG645
|
||||
|
||||
Used to set the trigger source of the DG645 by setting the value
|
||||
e.g. source.put(TriggerSource.Internal)
|
||||
Exp:
|
||||
TriggerSource.Internal
|
||||
"""
|
||||
|
||||
INTERNAL = 0
|
||||
EXT_RISING_EDGE = 1
|
||||
EXT_FALLING_EDGE = 2
|
||||
SS_EXT_RISING_EDGE = 3
|
||||
SS_EXT_FALLING_EDGE = 4
|
||||
SINGLE_SHOT = 5
|
||||
LINE = 6
|
||||
|
||||
|
||||
class DelayStatic(Device):
|
||||
"""
|
||||
Static axis for the T0 output channel
|
||||
|
||||
It allows setting the logic levels, but the timing is fixed.
|
||||
The signal is high after receiving the trigger until the end
|
||||
of the holdoff period.
|
||||
"""
|
||||
|
||||
# Other channel stuff
|
||||
ttl_mode = Component(EpicsSignal, "OutputModeTtlSS.PROC", kind=Kind.config)
|
||||
nim_mode = Component(EpicsSignal, "OutputModeNimSS.PROC", kind=Kind.config)
|
||||
polarity = Component(
|
||||
EpicsSignal,
|
||||
"OutputPolarityBI",
|
||||
write_pv="OutputPolarityBO",
|
||||
name="polarity",
|
||||
kind=Kind.config,
|
||||
)
|
||||
amplitude = Component(
|
||||
EpicsSignal, "OutputAmpAI", write_pv="OutputAmpAO", name="amplitude", kind=Kind.config
|
||||
)
|
||||
offset = Component(
|
||||
EpicsSignal, "OutputOffsetAI", write_pv="OutputOffsetAO", name="offset", kind=Kind.config
|
||||
)
|
||||
|
||||
|
||||
class DummyPositioner(PVPositioner):
|
||||
"""Dummy Positioner to set AO, AI and ReferenceMO."""
|
||||
|
||||
setpoint = Component(EpicsSignal, "DelayAO", put_complete=True, kind=Kind.config)
|
||||
readback = Component(EpicsSignalRO, "DelayAI", kind=Kind.config)
|
||||
done = Component(Signal, value=1)
|
||||
reference = Component(EpicsSignal, "ReferenceMO", put_complete=True, kind=Kind.config)
|
||||
|
||||
|
||||
class DelayPair(PseudoPositioner):
|
||||
"""
|
||||
Delay pair interface
|
||||
|
||||
Virtual motor interface to a pair of signals (on the frontpanel - AB/CD/EF/GH).
|
||||
It offers a simple delay and pulse width interface.
|
||||
"""
|
||||
|
||||
# The pseudo positioner axes
|
||||
delay = Component(PseudoSingle, limits=(0, 2000.0), name="delay")
|
||||
width = Component(PseudoSingle, limits=(0, 2000.0), name="pulsewidth")
|
||||
ch1 = Component(DummyPositioner, name="ch1")
|
||||
ch2 = Component(DummyPositioner, name="ch2")
|
||||
io = Component(DelayStatic, name="io")
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
# Change suffix names before connecting (a bit of dynamic connections)
|
||||
self.__class__.__dict__["ch1"].suffix = kwargs["channel"][0]
|
||||
self.__class__.__dict__["ch2"].suffix = kwargs["channel"][1]
|
||||
self.__class__.__dict__["io"].suffix = kwargs["channel"]
|
||||
|
||||
del kwargs["channel"]
|
||||
# Call parent to start the connections
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
@pseudo_position_argument
|
||||
def forward(self, pseudo_pos):
|
||||
"""Run a forward (pseudo -> real) calculation"""
|
||||
return self.RealPosition(ch1=pseudo_pos.delay, ch2=pseudo_pos.delay + pseudo_pos.width)
|
||||
|
||||
@real_position_argument
|
||||
def inverse(self, real_pos):
|
||||
"""Run an inverse (real -> pseudo) calculation"""
|
||||
return self.PseudoPosition(delay=real_pos.ch1, width=real_pos.ch2 - real_pos.ch1)
|
||||
|
||||
|
||||
class DDGCustomMixin:
|
||||
"""
|
||||
Mixin class for custom DelayGenerator logic within PSIDelayGeneratorBase.
|
||||
|
||||
This class provides a parent class for implementation of BL specific logic of the device.
|
||||
It is also possible to pass implementing certain methods, e.g. finished or on_trigger,
|
||||
based on the setup and desired operation mode at the beamline.
|
||||
|
||||
Args:
|
||||
parent (object): instance of PSIDelayGeneratorBase
|
||||
**kwargs: keyword arguments
|
||||
"""
|
||||
|
||||
def __init__(self, *_args, parent: Device = None, **_kwargs) -> None:
|
||||
self.parent = parent
|
||||
|
||||
def initialize_default_parameter(self) -> None:
|
||||
"""
|
||||
Method to initialize default parameters for DDG.
|
||||
|
||||
Called upon initiating the base class.
|
||||
It should be used to set the DDG default parameters.
|
||||
These may include: amplitude, offsets, delays, etc.
|
||||
"""
|
||||
|
||||
def prepare_ddg(self) -> None:
|
||||
"""
|
||||
Method to prepare the DDG for the upcoming scan.
|
||||
|
||||
Called by the stage method of the base class.
|
||||
It should be used to set the DDG parameters for the upcoming scan.
|
||||
"""
|
||||
|
||||
def on_trigger(self) -> None:
|
||||
"""Method executed upon trigger call in parent class"""
|
||||
|
||||
def finished(self) -> None:
|
||||
"""Method to check if DDG is finished with the scan"""
|
||||
|
||||
def on_pre_scan(self) -> None:
|
||||
"""
|
||||
Method executed upon pre_scan call in parent class.
|
||||
|
||||
Covenient to implement time sensitive actions to be executed right before start of the scan.
|
||||
Example could be to open the shutter by triggering a pulse via pre_scan.
|
||||
"""
|
||||
|
||||
def check_scan_id(self) -> None:
|
||||
"""Method to check if there is a new scan_id, called by stage."""
|
||||
|
||||
def is_ddg_okay(self, raise_on_error=False) -> None:
|
||||
"""
|
||||
Method to check if DDG is okay
|
||||
|
||||
It checks the status PV of the DDG and tries to clear the error if it is not okay.
|
||||
It will rerun itself and raise DelayGeneratorNotOkay if DDG is still not okay.
|
||||
|
||||
Args:
|
||||
raise_on_error (bool, optional): raise exception if DDG is not okay. Defaults to False.
|
||||
"""
|
||||
status = self.parent.status.read()[self.parent.status.name]["value"]
|
||||
if status != "STATUS OK" and not raise_on_error:
|
||||
logger.warning(f"DDG returns {status}, trying to clear ERROR")
|
||||
self.parent.clear_error()
|
||||
time.sleep(1)
|
||||
self.is_ddg_okay(raise_on_error=True)
|
||||
elif status != "STATUS OK":
|
||||
raise DelayGeneratorNotOkay(f"DDG failed to start with status: {status}")
|
||||
|
||||
|
||||
class PSIDelayGeneratorBase(Device):
|
||||
"""
|
||||
Abstract base class for DelayGenerator DG645
|
||||
|
||||
This class implements a thin Ophyd wrapper around the Stanford Research DG645
|
||||
digital delay generator.
|
||||
|
||||
The DG645 generates 8+1 signals: A, B, C, D, E, F, G, H and T0. Front panel outputs
|
||||
T0, AB, CD, EF and GH are combinations of these signals. Back panel outputs are
|
||||
directly routed signals. Signals are not independent.
|
||||
|
||||
Signal pairs, e.g. AB, CD, EF, GH, are implemented as DelayPair objects. They
|
||||
have a TTL pulse width, delay and a reference signal to which they are being triggered.
|
||||
In addition, the io layer allows setting amplitude, offset and polarity for each pair.
|
||||
|
||||
Detailed information can be found in the manual:
|
||||
https://www.thinksrs.com/downloads/pdfs/manuals/DG645m.pdf
|
||||
|
||||
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
|
||||
read_attrs (list) : sequence of attribute names to read
|
||||
configuration_attrs (list) : sequence of attribute names via config_parameters
|
||||
parent (object) : instance of the parent device
|
||||
device_manager (object) : bec device manager
|
||||
sim_mode (bool) : simulation mode, if True, no device manager is required
|
||||
**kwargs : keyword arguments
|
||||
attributes : lazy_wait_for_connection : bool
|
||||
"""
|
||||
|
||||
# Custom_prepare_cls
|
||||
custom_prepare_cls = DDGCustomMixin
|
||||
|
||||
SUB_PROGRESS = "progress"
|
||||
SUB_VALUE = "value"
|
||||
_default_sub = SUB_VALUE
|
||||
|
||||
USER_ACCESS = ["set_channels", "_set_trigger", "burst_enable", "burst_disable", "reload_config"]
|
||||
|
||||
# Assign PVs from DDG645
|
||||
trigger_burst_readout = Component(
|
||||
EpicsSignal, "EventStatusLI.PROC", name="trigger_burst_readout"
|
||||
)
|
||||
burst_cycle_finished = Component(EpicsSignalRO, "EventStatusMBBID.B3", name="read_burst_state")
|
||||
delay_finished = Component(EpicsSignalRO, "EventStatusMBBID.B2", name="delay_finished")
|
||||
status = Component(EpicsSignalRO, "StatusSI", name="status")
|
||||
clear_error = Component(EpicsSignal, "StatusClearBO", name="clear_error")
|
||||
|
||||
# Front Panel
|
||||
channelT0 = Component(DelayStatic, "T0", name="T0")
|
||||
channelAB = Component(DelayPair, "", name="AB", channel="AB")
|
||||
channelCD = Component(DelayPair, "", name="CD", channel="CD")
|
||||
channelEF = Component(DelayPair, "", name="EF", channel="EF")
|
||||
channelGH = Component(DelayPair, "", name="GH", channel="GH")
|
||||
|
||||
holdoff = Component(
|
||||
EpicsSignal,
|
||||
"TriggerHoldoffAI",
|
||||
write_pv="TriggerHoldoffAO",
|
||||
name="trigger_holdoff",
|
||||
kind=Kind.config,
|
||||
)
|
||||
inhibit = Component(
|
||||
EpicsSignal,
|
||||
"TriggerInhibitMI",
|
||||
write_pv="TriggerInhibitMO",
|
||||
name="trigger_inhibit",
|
||||
kind=Kind.config,
|
||||
)
|
||||
source = Component(
|
||||
EpicsSignal,
|
||||
"TriggerSourceMI",
|
||||
write_pv="TriggerSourceMO",
|
||||
name="trigger_source",
|
||||
kind=Kind.config,
|
||||
)
|
||||
level = Component(
|
||||
EpicsSignal,
|
||||
"TriggerLevelAI",
|
||||
write_pv="TriggerLevelAO",
|
||||
name="trigger_level",
|
||||
kind=Kind.config,
|
||||
)
|
||||
rate = Component(
|
||||
EpicsSignal,
|
||||
"TriggerRateAI",
|
||||
write_pv="TriggerRateAO",
|
||||
name="trigger_rate",
|
||||
kind=Kind.config,
|
||||
)
|
||||
trigger_shot = Component(EpicsSignal, "TriggerDelayBO", name="trigger_shot", kind="config")
|
||||
burstMode = Component(
|
||||
EpicsSignal, "BurstModeBI", write_pv="BurstModeBO", name="burstmode", kind=Kind.config
|
||||
)
|
||||
burstConfig = Component(
|
||||
EpicsSignal, "BurstConfigBI", write_pv="BurstConfigBO", name="burstconfig", kind=Kind.config
|
||||
)
|
||||
burstCount = Component(
|
||||
EpicsSignal, "BurstCountLI", write_pv="BurstCountLO", name="burstcount", kind=Kind.config
|
||||
)
|
||||
burstDelay = Component(
|
||||
EpicsSignal, "BurstDelayAI", write_pv="BurstDelayAO", name="burstdelay", kind=Kind.config
|
||||
)
|
||||
burstPeriod = Component(
|
||||
EpicsSignal, "BurstPeriodAI", write_pv="BurstPeriodAO", name="burstperiod", kind=Kind.config
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
prefix="",
|
||||
*,
|
||||
name,
|
||||
kind=None,
|
||||
read_attrs=None,
|
||||
configuration_attrs=None,
|
||||
parent=None,
|
||||
device_manager=None,
|
||||
sim_mode=False,
|
||||
**kwargs,
|
||||
):
|
||||
super().__init__(
|
||||
prefix=prefix,
|
||||
name=name,
|
||||
kind=kind,
|
||||
read_attrs=read_attrs,
|
||||
configuration_attrs=configuration_attrs,
|
||||
parent=parent,
|
||||
**kwargs,
|
||||
)
|
||||
if device_manager is None and not sim_mode:
|
||||
raise DeviceInitError(
|
||||
f"No device manager for device: {name}, and not started sim_mode: {sim_mode}. Add"
|
||||
" DeviceManager to initialization or init with sim_mode=True"
|
||||
)
|
||||
# Init variables
|
||||
self.sim_mode = sim_mode
|
||||
self.stopped = False
|
||||
self.name = name
|
||||
self.scaninfo = None
|
||||
self.timeout = 5
|
||||
self.all_channels = ["channelT0", "channelAB", "channelCD", "channelEF", "channelGH"]
|
||||
self.all_delay_pairs = ["AB", "CD", "EF", "GH"]
|
||||
self.wait_for_connection(all_signals=True)
|
||||
|
||||
# Init custom prepare class with BL specific logic
|
||||
self.custom_prepare = self.custom_prepare_cls(parent=self, **kwargs)
|
||||
if not sim_mode:
|
||||
self.device_manager = device_manager
|
||||
else:
|
||||
self.device_manager = bec_utils.DMMock()
|
||||
self.connector = self.device_manager.connector
|
||||
self._update_scaninfo()
|
||||
self._init()
|
||||
|
||||
def _update_scaninfo(self) -> None:
|
||||
"""
|
||||
Method to updated scaninfo from BEC.
|
||||
|
||||
In sim_mode, scaninfo output is mocked - see bec_scaninfo_mixin.py
|
||||
"""
|
||||
self.scaninfo = BecScaninfoMixin(self.device_manager, self.sim_mode)
|
||||
self.scaninfo.load_scan_metadata()
|
||||
|
||||
def _init(self) -> None:
|
||||
"""Method to initialize custom parameters of the DDG."""
|
||||
self.custom_prepare.initialize_default_parameter()
|
||||
self.custom_prepare.is_ddg_okay()
|
||||
|
||||
def set_channels(self, signal: str, value: Any, channels: list = None) -> None:
|
||||
"""
|
||||
Method to set signals on DelayPair and DelayStatic channels.
|
||||
|
||||
Signals can be set on the DelayPair and DelayStatic channels. The method checks
|
||||
if the signal is available on the channel and sets it. It works for both, DelayPair
|
||||
and Delay Static although signals are hosted in different layers.
|
||||
|
||||
Args:
|
||||
signal (str) : signal to set (width, delay, amplitude, offset, polarity)
|
||||
value (Any) : value to set
|
||||
channels (list, optional) : list of channels to set. Defaults to self.all_channels (T0,AB,CD,EF,GH)
|
||||
"""
|
||||
if not channels:
|
||||
channels = self.all_channels
|
||||
for chname in channels:
|
||||
channel = getattr(self, chname, None)
|
||||
if not channel:
|
||||
continue
|
||||
if signal in channel.component_names:
|
||||
getattr(channel, signal).set(value)
|
||||
continue
|
||||
if "io" in channel.component_names and signal in channel.io.component_names:
|
||||
getattr(channel.io, signal).set(value)
|
||||
|
||||
def set_trigger(self, trigger_source: TriggerSource) -> None:
|
||||
"""Set trigger source on DDG - possible values defined in TriggerSource enum"""
|
||||
value = int(trigger_source)
|
||||
self.source.put(value)
|
||||
|
||||
def burst_enable(self, count, delay, period, config="all"):
|
||||
"""Enable the burst mode"""
|
||||
# Validate inputs
|
||||
count = int(count)
|
||||
assert count > 0, "Number of bursts must be positive"
|
||||
assert delay >= 0, "Burst delay must be larger than 0"
|
||||
assert period > 0, "Burst period must be positive"
|
||||
assert config in ["all", "first"], "Supported burst configs are 'all' and 'first'"
|
||||
|
||||
self.burstMode.put(1)
|
||||
self.burstCount.put(count)
|
||||
self.burstDelay.put(delay)
|
||||
self.burstPeriod.put(period)
|
||||
|
||||
if config == "all":
|
||||
self.burstConfig.put(0)
|
||||
elif config == "first":
|
||||
self.burstConfig.put(1)
|
||||
|
||||
def burst_disable(self):
|
||||
"""Disable burst mode"""
|
||||
self.burstMode.put(0)
|
||||
|
||||
def stage(self) -> list[object]:
|
||||
"""
|
||||
Method to stage the device.
|
||||
|
||||
Called in preparation for a scan.
|
||||
|
||||
Internal Calls:
|
||||
- scaninfo.load_scan_metadata : load scan metadata
|
||||
- custom_prepare.prepare_ddg : prepare DDG for measurement
|
||||
- is_ddg_okay : check if DDG is okay
|
||||
|
||||
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.prepare_ddg()
|
||||
self.custom_prepare.is_ddg_okay()
|
||||
# At the moment needed bc signal might not be reliable, BEC too fast.
|
||||
# Consider removing this overhead in future!
|
||||
time.sleep(0.05)
|
||||
return super().stage()
|
||||
|
||||
def trigger(self) -> DeviceStatus:
|
||||
"""
|
||||
Method to trigger the acquisition.
|
||||
|
||||
Internal Call:
|
||||
- custom_prepare.on_trigger : execute BL specific action
|
||||
"""
|
||||
self.custom_prepare.on_trigger()
|
||||
return super().trigger()
|
||||
|
||||
def pre_scan(self) -> None:
|
||||
"""
|
||||
Method pre_scan gets executed directly before the scan
|
||||
|
||||
Internal Call:
|
||||
- custom_prepare.on_pre_scan : execute BL specific action
|
||||
"""
|
||||
self.custom_prepare.on_pre_scan()
|
||||
|
||||
def unstage(self) -> list[object]:
|
||||
"""
|
||||
Method unstage gets called at the end of a scan.
|
||||
|
||||
If scan (self.stopped is True) is stopped, returns directly.
|
||||
Otherwise, checks if the DDG finished acquisition
|
||||
|
||||
Internal Calls:
|
||||
- custom_prepare.check_scan_id : check if scan_id changed or detector stopped
|
||||
- custom_prepare.finished : check if device finished acquisition (succesfully)
|
||||
- is_ddg_okay : check if DDG is okay
|
||||
|
||||
Returns:
|
||||
list(object): list of objects that were unstaged
|
||||
"""
|
||||
self.custom_prepare.check_scan_id()
|
||||
if self.stopped is True:
|
||||
return super().unstage()
|
||||
self.custom_prepare.finished()
|
||||
self.custom_prepare.is_ddg_okay()
|
||||
self.stopped = False
|
||||
return super().unstage()
|
||||
|
||||
def stop(self, *, success=False) -> None:
|
||||
"""
|
||||
Method to stop the DDG
|
||||
|
||||
#TODO Check if the pulse generation can be interruppted
|
||||
|
||||
Internal Call:
|
||||
- custom_prepare.is_ddg_okay : check if DDG is okay
|
||||
"""
|
||||
self.custom_prepare.is_ddg_okay()
|
||||
super().stop(success=success)
|
||||
self.stopped = True
|
@ -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
|
@ -0,0 +1,570 @@
|
||||
"""
|
||||
Scan stubs are commands that can be used to control devices during a scan. They typically yield device messages that are
|
||||
consumed by the scan worker and potentially forwarded to the device server.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import threading
|
||||
import time
|
||||
import uuid
|
||||
from collections.abc import Callable
|
||||
from typing import Generator, Literal
|
||||
|
||||
import numpy as np
|
||||
|
||||
from bec_lib import messages
|
||||
from bec_lib.connector import ConnectorBase
|
||||
from bec_lib.device import Status
|
||||
from bec_lib.endpoints import MessageEndpoints
|
||||
from bec_lib.logger import bec_logger
|
||||
|
||||
from .errors import DeviceMessageError, ScanAbortion
|
||||
|
||||
logger = bec_logger.logger
|
||||
|
||||
|
||||
class ScanStubs:
|
||||
"""
|
||||
Scan stubs are commands that can be used to control devices during a scan. They typically yield device messages that are
|
||||
consumed by the scan worker and potentially forwarded to the device server.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
connector: ConnectorBase,
|
||||
device_msg_callback: Callable = None,
|
||||
shutdown_event: threading.Event = None,
|
||||
) -> None:
|
||||
self.connector = connector
|
||||
self.device_msg_metadata = (
|
||||
device_msg_callback if device_msg_callback is not None else lambda: {}
|
||||
)
|
||||
self.shutdown_event = shutdown_event
|
||||
|
||||
@staticmethod
|
||||
def _exclude_nones(input_dict: dict):
|
||||
for key in list(input_dict.keys()):
|
||||
if input_dict[key] is None:
|
||||
input_dict.pop(key)
|
||||
|
||||
def _device_msg(self, **kwargs):
|
||||
""""""
|
||||
msg = messages.DeviceInstructionMessage(**kwargs)
|
||||
msg.metadata = {**self.device_msg_metadata(), **msg.metadata}
|
||||
return msg
|
||||
|
||||
def send_rpc_and_wait(self, device: str, func_name: str, *args, **kwargs) -> any:
|
||||
"""Perform an RPC (remote procedure call) on a device and wait for its return value.
|
||||
|
||||
Args:
|
||||
device (str): Name of the device
|
||||
func_name (str): Function name. The function name will be appended to the device.
|
||||
args (tuple): Arguments to pass on to the RPC function
|
||||
kwargs (dict): Keyword arguments to pass on to the RPC function
|
||||
|
||||
Raises:
|
||||
ScanAbortion: Raised if the RPC's success is False
|
||||
|
||||
Returns:
|
||||
any: Return value of the executed rpc function
|
||||
|
||||
Examples:
|
||||
>>> send_rpc_and_wait("samx", "controller.my_custom_function")
|
||||
"""
|
||||
rpc_id = str(uuid.uuid4())
|
||||
parameter = {
|
||||
"device": device,
|
||||
"func": func_name,
|
||||
"rpc_id": rpc_id,
|
||||
"args": args,
|
||||
"kwargs": kwargs,
|
||||
}
|
||||
yield from self.rpc(
|
||||
device=device, parameter=parameter, metadata={"response": True, "RID": rpc_id}
|
||||
)
|
||||
return self._get_from_rpc(rpc_id)
|
||||
|
||||
def _get_from_rpc(self, rpc_id) -> any:
|
||||
"""
|
||||
Get the return value from an RPC call.
|
||||
|
||||
Args:
|
||||
rpc_id (str): RPC ID
|
||||
|
||||
Raises:
|
||||
ScanAbortion: Raised if the RPC's success flag is False
|
||||
|
||||
Returns:
|
||||
any: Return value of the RPC call
|
||||
"""
|
||||
|
||||
while not self.shutdown_event.is_set():
|
||||
msg = self.connector.get(MessageEndpoints.device_rpc(rpc_id))
|
||||
if msg:
|
||||
break
|
||||
time.sleep(0.001)
|
||||
if self.shutdown_event.is_set():
|
||||
raise ScanAbortion("The scan was aborted.")
|
||||
if not msg.content["success"]:
|
||||
error = msg.content["out"]
|
||||
if isinstance(error, dict) and {"error", "msg", "traceback"}.issubset(
|
||||
set(error.keys())
|
||||
):
|
||||
error_msg = f"During an RPC, the following error occured:\n{error['error']}: {error['msg']}.\nTraceback: {error['traceback']}\n The scan will be aborted."
|
||||
else:
|
||||
error_msg = "During an RPC, an error occured"
|
||||
raise ScanAbortion(error_msg)
|
||||
|
||||
logger.debug(msg.content.get("out"))
|
||||
return_val = msg.content.get("return_val")
|
||||
if not isinstance(return_val, dict):
|
||||
return return_val
|
||||
if return_val.get("type") == "status" and return_val.get("RID"):
|
||||
return Status(self.connector, return_val.get("RID"))
|
||||
return return_val
|
||||
|
||||
def set_with_response(
|
||||
self, *, device: str, value: float, request_id: str = None, metadata=None
|
||||
) -> Generator[None, None, None]:
|
||||
"""Set a device to a specific value and return the request ID. Use :func:`request_is_completed` to later check if the request is completed.
|
||||
|
||||
Args:
|
||||
device (str): Device name.
|
||||
value (float): Target value.
|
||||
|
||||
Returns:
|
||||
Generator[None, None, None]: Generator that yields a device message.
|
||||
|
||||
see also: :func:`request_is_completed`
|
||||
|
||||
"""
|
||||
request_id = str(uuid.uuid4()) if request_id is None else request_id
|
||||
metadata = metadata if metadata is not None else {}
|
||||
metadata.update({"response": True, "RID": request_id})
|
||||
yield from self.set(device=device, value=value, wait_group="set", metadata=metadata)
|
||||
return request_id
|
||||
|
||||
def request_is_completed(self, RID: str) -> bool:
|
||||
"""Check if a request that was initiated with :func:`set_with_response` is completed.
|
||||
|
||||
Args:
|
||||
RID (str): Request ID.
|
||||
|
||||
Returns:
|
||||
bool: True if the request is completed, False otherwise.
|
||||
|
||||
"""
|
||||
msg = self.connector.lrange(MessageEndpoints.device_req_status_container(RID), 0, -1)
|
||||
if not msg:
|
||||
return False
|
||||
return True
|
||||
|
||||
def set_and_wait(
|
||||
self, *, device: list[str], positions: list | np.ndarray
|
||||
) -> Generator[None, None, None]:
|
||||
"""Set devices to a specific position and wait completion.
|
||||
|
||||
Args:
|
||||
device (list[str]): List of device names.
|
||||
positions (list | np.ndarray): Target position.
|
||||
|
||||
Returns:
|
||||
Generator[None, None, None]: Generator that yields a device message.
|
||||
|
||||
see also: :func:`set`, :func:`wait`, :func:`set_with_response`
|
||||
|
||||
"""
|
||||
if not isinstance(positions, list) and not isinstance(positions, np.ndarray):
|
||||
positions = [positions]
|
||||
if len(positions) == 0:
|
||||
return
|
||||
for ind, val in enumerate(device):
|
||||
yield from self.set(device=val, value=positions[ind], wait_group="scan_motor")
|
||||
yield from self.wait(device=device, wait_type="move", wait_group="scan_motor")
|
||||
|
||||
def read_and_wait(
|
||||
self, *, wait_group: str, device: list = None, group: str = None, point_id: int = None
|
||||
) -> Generator[None, None, None]:
|
||||
"""Trigger a reading and wait for completion.
|
||||
|
||||
Args:
|
||||
wait_group (str): wait group
|
||||
device (list, optional): List of device names. Can be specified instead of group. Defaults to None.
|
||||
group (str, optional): Group name of devices. Can be specified instead of device. Defaults to None.
|
||||
point_id (int, optional): _description_. Defaults to None.
|
||||
|
||||
Returns:
|
||||
Generator[None, None, None]: Generator that yields a device message.
|
||||
|
||||
"""
|
||||
self._check_device_and_groups(device, group)
|
||||
yield from self.read(device=device, group=group, wait_group=wait_group, point_id=point_id)
|
||||
yield from self.wait(device=device, wait_type="read", group=group, wait_group=wait_group)
|
||||
|
||||
def open_scan(
|
||||
self,
|
||||
*,
|
||||
scan_motors: list,
|
||||
readout_priority: dict,
|
||||
num_pos: int,
|
||||
scan_name: str,
|
||||
scan_type: Literal["step", "fly"],
|
||||
positions=None,
|
||||
metadata=None,
|
||||
) -> Generator[None, None, None]:
|
||||
"""Open a new scan.
|
||||
|
||||
Args:
|
||||
scan_motors (list): List of scan motors.
|
||||
readout_priority (dict): Modification of the readout priority.
|
||||
num_pos (int): Number of positions within the scope of this scan.
|
||||
positions (list): List of positions for this scan.
|
||||
scan_name (str): Scan name.
|
||||
scan_type (str): Scan type (e.g. 'step' or 'fly')
|
||||
|
||||
Returns:
|
||||
Generator[None, None, None]: Generator that yields a device message.
|
||||
|
||||
"""
|
||||
yield self._device_msg(
|
||||
device=None,
|
||||
action="open_scan",
|
||||
parameter={
|
||||
"scan_motors": scan_motors,
|
||||
"readout_priority": readout_priority,
|
||||
"num_points": num_pos,
|
||||
"positions": positions,
|
||||
"scan_name": scan_name,
|
||||
"scan_type": scan_type,
|
||||
},
|
||||
metadata=metadata,
|
||||
)
|
||||
|
||||
def kickoff(
|
||||
self, *, device: str, parameter: dict = None, wait_group="kickoff", metadata=None
|
||||
) -> Generator[None, None, None]:
|
||||
"""Kickoff a fly scan device.
|
||||
|
||||
Args:
|
||||
device (str): Device name of flyer.
|
||||
parameter (dict, optional): Additional parameters that should be forwarded to the device. Defaults to {}.
|
||||
|
||||
Returns:
|
||||
Generator[None, None, None]: Generator that yields a device message.
|
||||
"""
|
||||
parameter = parameter if parameter is not None else {}
|
||||
parameter = {"configure": parameter, "wait_group": wait_group}
|
||||
yield self._device_msg(
|
||||
device=device, action="kickoff", parameter=parameter, metadata=metadata
|
||||
)
|
||||
|
||||
def complete(self, *, device: str, metadata=None) -> Generator[None, None, None]:
|
||||
"""Complete a fly scan device.
|
||||
|
||||
Args:
|
||||
device (str): Device name of flyer.
|
||||
|
||||
Returns:
|
||||
Generator[None, None, None]: Generator that yields a device message.
|
||||
"""
|
||||
yield self._device_msg(device=device, action="complete", parameter={}, metadata=metadata)
|
||||
|
||||
def get_req_status(self, device: str, RID: str, DIID: int) -> int:
|
||||
"""Check if a device request status matches the given RID and DIID
|
||||
|
||||
Args:
|
||||
device (str): device under inspection
|
||||
RID (str): request ID
|
||||
DIID (int): device instruction ID
|
||||
|
||||
Returns:
|
||||
int: 1 if the request status matches the RID and DIID, 0 otherwise
|
||||
"""
|
||||
msg = self.connector.get(MessageEndpoints.device_req_status(device))
|
||||
if not msg:
|
||||
return 0
|
||||
matching_RID = msg.metadata.get("RID") == RID
|
||||
matching_DIID = msg.metadata.get("DIID") == DIID
|
||||
if matching_DIID and matching_RID:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
def get_device_progress(self, device: str, RID: str) -> float | None:
|
||||
"""Get reported device progress
|
||||
|
||||
Args:
|
||||
device (str): Name of the device
|
||||
RID (str): request ID
|
||||
|
||||
Returns:
|
||||
float: reported progress value
|
||||
|
||||
"""
|
||||
msg = self.connector.get(MessageEndpoints.device_progress(device))
|
||||
if not msg:
|
||||
return None
|
||||
matching_RID = msg.metadata.get("RID") == RID
|
||||
if not matching_RID:
|
||||
return None
|
||||
if not isinstance(msg, messages.ProgressMessage):
|
||||
raise DeviceMessageError(
|
||||
f"Expected to receive a Progressmessage for device {device} but instead received {msg}."
|
||||
)
|
||||
return msg.content["value"]
|
||||
|
||||
def close_scan(self) -> Generator[None, None, None]:
|
||||
"""
|
||||
Close the scan.
|
||||
|
||||
Returns:
|
||||
Generator[None, None, None]: Generator that yields a device message.
|
||||
|
||||
see also: :func:`open_scan`
|
||||
"""
|
||||
|
||||
yield self._device_msg(device=None, action="close_scan", parameter={})
|
||||
|
||||
def stage(self) -> Generator[None, None, None]:
|
||||
"""
|
||||
Stage all devices
|
||||
|
||||
Returns:
|
||||
Generator[None, None, None]: Generator that yields a device message.
|
||||
|
||||
see also: :func:`unstage`
|
||||
"""
|
||||
yield self._device_msg(device=None, action="stage", parameter={})
|
||||
|
||||
def unstage(self) -> Generator[None, None, None]:
|
||||
"""
|
||||
Unstage all devices
|
||||
|
||||
Returns:
|
||||
Generator[None, None, None]: Generator that yields a device message.
|
||||
|
||||
see also: :func:`stage`
|
||||
"""
|
||||
yield self._device_msg(device=None, action="unstage", parameter={})
|
||||
|
||||
def pre_scan(self) -> Generator[None, None, None]:
|
||||
"""
|
||||
Trigger pre-scan actions on all devices. Typically, pre-scan actions are called directly before the scan core starts and
|
||||
are used to perform time-critical actions.
|
||||
The event will be sent to all devices that have a pre_scan method implemented.
|
||||
|
||||
Returns:
|
||||
Generator[None, None, None]: Generator that yields a device message.
|
||||
"""
|
||||
yield self._device_msg(device=None, action="pre_scan", parameter={})
|
||||
|
||||
def baseline_reading(self) -> Generator[None, None, None]:
|
||||
"""
|
||||
Run the baseline readings. This will readout all devices that are marked with the readout_priority "baseline".
|
||||
|
||||
Returns:
|
||||
Generator[None, None, None]: Generator that yields a device message.
|
||||
|
||||
"""
|
||||
yield self._device_msg(
|
||||
device=None,
|
||||
action="baseline_reading",
|
||||
parameter={},
|
||||
metadata={"readout_priority": "baseline"},
|
||||
)
|
||||
|
||||
def wait(
|
||||
self,
|
||||
*,
|
||||
wait_type: Literal["move", "read", "trigger"],
|
||||
device: list[str] | str | None = None,
|
||||
group: Literal["scan_motor", "primary", None] = None,
|
||||
wait_group: str = None,
|
||||
wait_time: float = None,
|
||||
):
|
||||
"""Wait for an event.
|
||||
|
||||
Args:
|
||||
wait_type (Literal["move", "read", "trigger"]): Type of wait event. Can be "move", "read" or "trigger".
|
||||
device (list[str] | str, optional): List of device names. Defaults to None.
|
||||
group (Literal["scan_motor", "primary", None]): Device group that can be used instead of device. Defaults to None.
|
||||
wait_group (str, optional): Wait group for this event. Defaults to None.
|
||||
wait_time (float, optional): Wait time (for wait_type="trigger"). Defaults to None.
|
||||
|
||||
Returns:
|
||||
Generator[None, None, None]: Generator that yields a device message.
|
||||
|
||||
Example:
|
||||
>>> yield from self.stubs.wait(wait_type="move", group="scan_motor", wait_group="scan_motor")
|
||||
>>> yield from self.stubs.wait(wait_type="read", group="scan_motor", wait_group="my_readout_motors")
|
||||
|
||||
"""
|
||||
self._check_device_and_groups(device, group)
|
||||
parameter = {"type": wait_type, "time": wait_time, "group": group, "wait_group": wait_group}
|
||||
self._exclude_nones(parameter)
|
||||
yield self._device_msg(device=device, action="wait", parameter=parameter)
|
||||
|
||||
def read(
|
||||
self,
|
||||
*,
|
||||
wait_group: str,
|
||||
device: list[str] | str | None = None,
|
||||
point_id: int | None = None,
|
||||
group: Literal["scan_motor", "primary", None] = None,
|
||||
) -> Generator[None, None, None]:
|
||||
"""
|
||||
Trigger a reading on a device or device group.
|
||||
|
||||
Args:
|
||||
wait_group (str): Wait group for this event. The specified wait group can later be used
|
||||
to wait for the completion of this event. Please note that the wait group has to be
|
||||
unique. within the scope of the read / wait event.
|
||||
device (list, optional): Device name. Can be used instead of group. Defaults to None.
|
||||
point_id (int, optional): point_id to assign this reading to point within the scan. Defaults to None.
|
||||
group (Literal["scan_motor", "primary", None], optional): Device group. Can be used instead of device. Defaults to None.
|
||||
|
||||
Returns:
|
||||
Generator[None, None, None]: Generator that yields a device message.
|
||||
|
||||
Example:
|
||||
>>> yield from self.stubs.read(wait_group="readout_primary", group="primary", point_id=self.point_id)
|
||||
>>> yield from self.stubs.read(wait_group="sample_stage", device="samx", point_id=self.point_id)
|
||||
|
||||
"""
|
||||
self._check_device_and_groups(device, group)
|
||||
parameter = {"group": group, "wait_group": wait_group}
|
||||
metadata = {"point_id": point_id}
|
||||
self._exclude_nones(parameter)
|
||||
self._exclude_nones(metadata)
|
||||
yield self._device_msg(device=device, action="read", parameter=parameter, metadata=metadata)
|
||||
|
||||
def publish_data_as_read(
|
||||
self, *, device: str, data: dict, point_id: int
|
||||
) -> Generator[None, None, None]:
|
||||
"""
|
||||
Publish the given data as a read event and assign it to the given point_id.
|
||||
This method can be used to customize the assignment of data to a specific point within a scan.
|
||||
|
||||
Args:
|
||||
device (str): Device name.
|
||||
data (dict): Data that should be published.
|
||||
point_id (int): point_id that should be attached to this data.
|
||||
|
||||
Returns:
|
||||
Generator[None, None, None]: Generator that yields a device message.
|
||||
"""
|
||||
metadata = {"point_id": point_id}
|
||||
yield self._device_msg(
|
||||
device=device,
|
||||
action="publish_data_as_read",
|
||||
parameter={"data": data},
|
||||
metadata=metadata,
|
||||
)
|
||||
|
||||
def trigger(self, *, group: str, point_id: int) -> Generator[None, None, None]:
|
||||
"""Trigger a device group. Note that the trigger event is not blocking and does not wait for the completion of the trigger event.
|
||||
To wait for the completion of the trigger event, use the :func:`wait` command, specifying the wait_type as "trigger".
|
||||
|
||||
Args:
|
||||
group (str): Device group that should receive the trigger.
|
||||
point_id (int): point_id that should be attached to this trigger event.
|
||||
|
||||
Returns:
|
||||
Generator[None, None, None]: Generator that yields a device message.
|
||||
|
||||
see also: :func:`wait`
|
||||
"""
|
||||
yield self._device_msg(
|
||||
device=None,
|
||||
action="trigger",
|
||||
parameter={"group": group},
|
||||
metadata={"point_id": point_id},
|
||||
)
|
||||
|
||||
def set(self, *, device: str, value: float, wait_group: str, metadata=None):
|
||||
"""Set the device to a specific value. This is similar to the direct set command
|
||||
in the command-line interface. The wait_group can be used to wait for the completion of this event.
|
||||
For a set operation, this simply means that the device has acknowledged the set command and does not
|
||||
necessarily mean that the device has reached the target value.
|
||||
|
||||
Args:
|
||||
device (str): Device name
|
||||
value (float): Target value.
|
||||
wait_group (str): wait group for this event.
|
||||
|
||||
Returns:
|
||||
Generator[None, None, None]: Generator that yields a device message.
|
||||
|
||||
.. warning::
|
||||
|
||||
Do not use this command to kickoff a long running operation. Use :func:`kickoff` instead or, if the
|
||||
device does not support the kickoff command, use :func:`set_with_response` instead.
|
||||
|
||||
see also: :func:`wait`, :func:`set_and_wait`, :func:`set_with_response`
|
||||
|
||||
"""
|
||||
yield self._device_msg(
|
||||
device=device,
|
||||
action="set",
|
||||
parameter={"value": value, "wait_group": wait_group},
|
||||
metadata=metadata,
|
||||
)
|
||||
|
||||
def open_scan_def(self) -> Generator[None, None, None]:
|
||||
"""
|
||||
Open a new scan definition
|
||||
|
||||
Returns:
|
||||
Generator[None, None, None]: Generator that yields a device message.
|
||||
"""
|
||||
yield self._device_msg(device=None, action="open_scan_def", parameter={})
|
||||
|
||||
def close_scan_def(self) -> Generator[None, None, None]:
|
||||
"""
|
||||
Close a scan definition
|
||||
|
||||
Returns:
|
||||
Generator[None, None, None]: Generator that yields a device message.
|
||||
"""
|
||||
yield self._device_msg(device=None, action="close_scan_def", parameter={})
|
||||
|
||||
def close_scan_group(self) -> Generator[None, None, None]:
|
||||
"""
|
||||
Close a scan group
|
||||
|
||||
Returns:
|
||||
Generator[None, None, None]: Generator that yields a device message.
|
||||
"""
|
||||
yield self._device_msg(device=None, action="close_scan_group", parameter={})
|
||||
|
||||
def rpc(self, *, device: str, parameter: dict, metadata=None) -> Generator[None, None, None]:
|
||||
"""Perfrom an RPC (remote procedure call) on a device.
|
||||
|
||||
Args:
|
||||
device (str): Device name.
|
||||
parameter (dict): parameters used for this rpc instructions.
|
||||
|
||||
Returns:
|
||||
Generator[None, None, None]: Generator that yields a device message.
|
||||
|
||||
"""
|
||||
yield self._device_msg(device=device, action="rpc", parameter=parameter, metadata=metadata)
|
||||
|
||||
def scan_report_instruction(self, instructions: dict) -> Generator[None, None, None]:
|
||||
"""Scan report instructions
|
||||
|
||||
Args:
|
||||
instructions (dict): Dict containing the scan report instructions
|
||||
|
||||
Returns:
|
||||
Generator[None, None, None]: Generator that yields a device message.
|
||||
"""
|
||||
yield self._device_msg(
|
||||
device=None, action="scan_report_instruction", parameter=instructions
|
||||
)
|
||||
|
||||
def _check_device_and_groups(self, device, group) -> None:
|
||||
if device and group:
|
||||
raise DeviceMessageError("Device and device group was specified. Pick one.")
|
||||
if device is None and group is None:
|
||||
raise DeviceMessageError("Either devices or device groups have to be specified.")
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,159 @@
|
||||
from ophyd import (
|
||||
ADComponent as ADCpt,
|
||||
Device,
|
||||
DeviceStatus,
|
||||
)
|
||||
|
||||
from ophyd_devices.devices.areadetector.cam import SLSDetectorCam
|
||||
from ophyd_devices.devices.areadetector.plugins import (
|
||||
ImagePlugin_V35 as ImagePlugin,
|
||||
StatsPlugin_V35 as StatsPlugin,
|
||||
HDF5Plugin_V35 as HDF5Plugin,
|
||||
ROIPlugin_V35 as ROIPlugin,
|
||||
ROIStatPlugin_V35 as ROIStatPlugin,
|
||||
ROIStatNPlugin_V35 as ROIStatNPlugin,
|
||||
)
|
||||
from ophyd_devices.interfaces.base_classes.psi_detector_base import PSIDetectorBase, CustomDetectorMixin
|
||||
|
||||
from bec_lib import bec_logger, messages
|
||||
from bec_lib.endpoints import MessageEndpoints
|
||||
|
||||
logger = bec_logger.logger
|
||||
|
||||
DETECTOR_TIMEOUT = 5
|
||||
|
||||
class Eiger500KSetup(CustomDetectorMixin):
|
||||
def __init__(self, *args, parent:Device = None, **kwargs):
|
||||
super().__init__(*args, parent=parent, **kwargs)
|
||||
self._counter = 0
|
||||
|
||||
def on_stage(self):
|
||||
exposure_time = self.parent.scaninfo.exp_time
|
||||
num_points = self.parent.scaninfo.num_points
|
||||
|
||||
# camera acquisition parameters
|
||||
self.parent.cam.array_counter.put(0)
|
||||
if self.parent.scaninfo.scan_type == 'step':
|
||||
self.parent.cam.acquire_time.put(exposure_time)
|
||||
self.parent.cam.num_images.put(1)
|
||||
self.parent.cam.image_mode.put(0) # Single
|
||||
self.parent.cam.trigger_mode.put(0) # auto
|
||||
else:
|
||||
# In flyscan, the exp_time is the time between two triggers,
|
||||
# which minus 15% is used as the acquisition time.
|
||||
self.parent.cam.acquire_time.put(exposure_time * 0.85)
|
||||
self.parent.cam.num_images.put(num_points)
|
||||
self.parent.cam.image_mode.put(1) # Multiple
|
||||
self.parent.cam.trigger_mode.put(1) # trigger
|
||||
self.parent.cam.acquire.put(1, wait=False) # arm
|
||||
|
||||
# file writer
|
||||
self.parent.hdf.lazy_open.put(1)
|
||||
self.parent.hdf.num_capture.put(num_points)
|
||||
self.parent.hdf.file_write_mode.put(2) # Stream
|
||||
self.parent.hdf.capture.put(1, wait=False)
|
||||
self.parent.hdf.enable.put(1) # enable plugin
|
||||
|
||||
# roi statistics to collect signal and background in a timeseries
|
||||
self.parent.roistat.enable.put(1)
|
||||
self.parent.roistat.ts_num_points.put(num_points)
|
||||
self.parent.roistat.ts_control.put(0, wait=False) # Erase/Start
|
||||
|
||||
logger.success('XXXX stage XXXX')
|
||||
|
||||
def on_trigger(self):
|
||||
self.parent.cam.acquire.put(1, wait=False)
|
||||
logger.success('XXXX trigger XXXX')
|
||||
|
||||
return self.wait_with_status(
|
||||
[(self.parent.cam.acquire.get, 0)],
|
||||
self.parent.scaninfo.exp_time + DETECTOR_TIMEOUT,
|
||||
all_signals=True
|
||||
)
|
||||
|
||||
def on_complete(self):
|
||||
status = DeviceStatus(self.parent)
|
||||
|
||||
if self.parent.scaninfo.scan_type == 'step':
|
||||
timeout = DETECTOR_TIMEOUT
|
||||
else:
|
||||
timeout = self.parent.scaninfo.exp_time * self.parent.scaninfo.num_points + DETECTOR_TIMEOUT
|
||||
logger.success('XXXX %s XXXX' % self.parent.roistat.ts_acquiring.get())
|
||||
success = self.wait_for_signals(
|
||||
[
|
||||
(self.parent.cam.acquire.get, 0),
|
||||
(self.parent.hdf.capture.get, 0),
|
||||
(self.parent.roistat.ts_acquiring.get, 'Done')
|
||||
],
|
||||
timeout,
|
||||
check_stopped=True,
|
||||
all_signals=True
|
||||
)
|
||||
|
||||
# publish file location
|
||||
self.parent.filepath.put(self.parent.hdf.full_file_name.get())
|
||||
self.publish_file_location(done=True, successful=success)
|
||||
|
||||
# publish timeseries data
|
||||
metadata = self.parent.scaninfo.scan_msg.metadata
|
||||
metadata.update({"async_update": "append", "num_lines": self.parent.roistat.ts_current_point.get()})
|
||||
msg = messages.DeviceMessage(
|
||||
signals={
|
||||
self.parent.roistat.roi1.name_.get(): {
|
||||
'value': self.parent.roistat.roi1.ts_total.get(),
|
||||
},
|
||||
self.parent.roistat.roi2.name_.get(): {
|
||||
'value': self.parent.roistat.roi2.ts_total.get(),
|
||||
},
|
||||
},
|
||||
metadata=self.parent.scaninfo.scan_msg.metadata
|
||||
)
|
||||
self.parent.connector.xadd(
|
||||
topic=MessageEndpoints.device_async_readback(
|
||||
scan_id=self.parent.scaninfo.scan_id, device=self.parent.name
|
||||
),
|
||||
msg_dict={"data": msg},
|
||||
expire=1800,
|
||||
)
|
||||
|
||||
logger.success('XXXX complete %d XXXX' % success)
|
||||
if success:
|
||||
status.set_finished()
|
||||
else:
|
||||
status.set_exception(TimeoutError())
|
||||
return status
|
||||
|
||||
def on_stop(self):
|
||||
logger.success('XXXX stop XXXX')
|
||||
self.parent.cam.acquire.put(0)
|
||||
self.parent.hdf.capture.put(0)
|
||||
self.parent.roistat.ts_control.put(2)
|
||||
|
||||
def on_unstage(self):
|
||||
self.parent.cam.acquire.put(0)
|
||||
self.parent.hdf.capture.put(0)
|
||||
self.parent.roistat.ts_control.put(2)
|
||||
logger.success('XXXX unstage XXXX')
|
||||
|
||||
|
||||
class EigerROIStatPlugin(ROIStatPlugin):
|
||||
roi1 = ADCpt(ROIStatNPlugin, '1:')
|
||||
roi2 = ADCpt(ROIStatNPlugin, '2:')
|
||||
|
||||
class Eiger500K(PSIDetectorBase):
|
||||
"""
|
||||
"""
|
||||
custom_prepare_cls = Eiger500KSetup
|
||||
|
||||
cam = ADCpt(SLSDetectorCam, 'cam1:')
|
||||
|
||||
#image = ADCpt(ImagePlugin, 'image1:')
|
||||
#roi1 = ADCpt(ROIPlugin, 'ROI1:')
|
||||
#roi2 = ADCpt(ROIPlugin, 'ROI2:')
|
||||
#stats1 = ADCpt(StatsPlugin, 'Stats1:')
|
||||
#stats2 = ADCpt(StatsPlugin, 'Stats2:')
|
||||
roistat = ADCpt(EigerROIStatPlugin, 'ROIStat1:')
|
||||
#roistat1 = ADCpt(ROIStatNPlugin, 'ROIStat1:1:')
|
||||
#roistat2 = ADCpt(ROIStatNPlugin, 'ROIStat1:2:')
|
||||
hdf = ADCpt(HDF5Plugin, 'HDF1:')
|
||||
)
|
@ -0,0 +1,219 @@
|
||||
"""
|
||||
This was takem from Addam repository
|
||||
and commented after discussion with Xiaquiang
|
||||
|
||||
#######################################
|
||||
#Strutur of software
|
||||
#######################################
|
||||
|
||||
1) Eiger500KSetup(CustomDetectorMixin)---> inherits from CustomdetectorMixing
|
||||
|
||||
2) class Eiger500K(PSIDetectorBase) ---> inherits from PSIDetectorBase
|
||||
|
||||
|
||||
These calsses are linkes to each other by the command
|
||||
|
||||
custom_prepare_cls = Eiger500KSetup
|
||||
|
||||
it references/activates the self.parent used in Eiger500Ksetup to attributes defined in Eiger500K.
|
||||
for example in Eiger500K, we define cam = ADCpt(SLSDetectorCam, 'cam1:'), which is referenced in \
|
||||
Eiger500Ksetup by the command
|
||||
self.parent.cam.array_counter.put(0)
|
||||
|
||||
|
||||
class Eiger500KSetup(CustomDetectorMixin):
|
||||
def __init__(self, *args, parent:Device = None, **kwargs):
|
||||
super().__init__(*args, parent=parent, **kwargs)
|
||||
self._counter = 0
|
||||
|
||||
def on_stage(self):
|
||||
exposure_time = self.parent.scaninfo.exp_time
|
||||
num_points = self.parent.scaninfo.num_points
|
||||
|
||||
# camera acquisition parameters
|
||||
self.parent.cam.array_counter.put(0)
|
||||
if self.parent.scaninfo.scan_type == 'step':
|
||||
self.parent.cam.acquire_time.put(exposure_time)
|
||||
|
||||
|
||||
|
||||
|
||||
... etc
|
||||
|
||||
|
||||
class Eiger500K(PSIDetectorBase):
|
||||
"""
|
||||
"""
|
||||
custom_prepare_cls = Eiger500KSetup
|
||||
cam = ADCpt(SLSDetectorCam, 'cam1:')
|
||||
|
||||
.... etc
|
||||
|
||||
###################################################
|
||||
#
|
||||
# Using ROI in flyscans
|
||||
#
|
||||
###################################################
|
||||
Here the roi plugin of the area detector is used
|
||||
|
||||
|
||||
"""
|
||||
|
||||
from ophyd import (
|
||||
ADComponent as ADCpt,
|
||||
Device,
|
||||
DeviceStatus,
|
||||
)
|
||||
|
||||
from ophyd_devices.devices.areadetector.cam import SLSDetectorCam
|
||||
from ophyd_devices.devices.areadetector.plugins import (
|
||||
ImagePlugin_V35 as ImagePlugin,
|
||||
StatsPlugin_V35 as StatsPlugin,
|
||||
HDF5Plugin_V35 as HDF5Plugin,
|
||||
ROIPlugin_V35 as ROIPlugin,
|
||||
ROIStatPlugin_V35 as ROIStatPlugin,
|
||||
ROIStatNPlugin_V35 as ROIStatNPlugin,
|
||||
)
|
||||
from ophyd_devices.interfaces.base_classes.psi_detector_base import PSIDetectorBase, CustomDetectorMixin
|
||||
|
||||
from bec_lib import bec_logger, messages
|
||||
from bec_lib.endpoints import MessageEndpoints
|
||||
|
||||
logger = bec_logger.logger
|
||||
|
||||
DETECTOR_TIMEOUT = 5
|
||||
|
||||
class Eiger500KSetup(CustomDetectorMixin):
|
||||
def __init__(self, *args, parent:Device = None, **kwargs):
|
||||
super().__init__(*args, parent=parent, **kwargs)
|
||||
self._counter = 0
|
||||
|
||||
def on_stage(self):
|
||||
exposure_time = self.parent.scaninfo.exp_time
|
||||
num_points = self.parent.scaninfo.num_points
|
||||
|
||||
# camera acquisition parameters
|
||||
self.parent.cam.array_counter.put(0)
|
||||
if self.parent.scaninfo.scan_type == 'step':
|
||||
self.parent.cam.acquire_time.put(exposure_time)
|
||||
self.parent.cam.num_images.put(1)
|
||||
self.parent.cam.image_mode.put(0) # Single
|
||||
self.parent.cam.trigger_mode.put(0) # auto
|
||||
else:
|
||||
# In flyscan, the exp_time is the time between two triggers,
|
||||
# which minus 15% is used as the acquisition time.
|
||||
self.parent.cam.acquire_time.put(exposure_time * 0.85)
|
||||
self.parent.cam.num_images.put(num_points)
|
||||
self.parent.cam.image_mode.put(1) # Multiple
|
||||
self.parent.cam.trigger_mode.put(1) # trigger
|
||||
self.parent.cam.acquire.put(1, wait=False) # arm
|
||||
|
||||
# file writer
|
||||
self.parent.hdf.lazy_open.put(1)
|
||||
self.parent.hdf.num_capture.put(num_points)
|
||||
self.parent.hdf.file_write_mode.put(2) # Stream
|
||||
self.parent.hdf.capture.put(1, wait=False)
|
||||
self.parent.hdf.enable.put(1) # enable plugin
|
||||
|
||||
# roi statistics to collect signal and background in a timeseries
|
||||
self.parent.roistat.enable.put(1)
|
||||
self.parent.roistat.ts_num_points.put(num_points)
|
||||
self.parent.roistat.ts_control.put(0, wait=False) # Erase/Start
|
||||
|
||||
logger.success('XXXX stage XXXX')
|
||||
|
||||
def on_trigger(self):
|
||||
self.parent.cam.acquire.put(1, wait=False)
|
||||
logger.success('XXXX trigger XXXX')
|
||||
|
||||
return self.wait_with_status(
|
||||
[(self.parent.cam.acquire.get, 0)],
|
||||
self.parent.scaninfo.exp_time + DETECTOR_TIMEOUT,
|
||||
all_signals=True
|
||||
)
|
||||
|
||||
def on_complete(self):
|
||||
status = DeviceStatus(self.parent)
|
||||
|
||||
if self.parent.scaninfo.scan_type == 'step':
|
||||
timeout = DETECTOR_TIMEOUT
|
||||
else:
|
||||
timeout = self.parent.scaninfo.exp_time * self.parent.scaninfo.num_points + DETECTOR_TIMEOUT
|
||||
logger.success('XXXX %s XXXX' % self.parent.roistat.ts_acquiring.get())
|
||||
success = self.wait_for_signals(
|
||||
[
|
||||
(self.parent.cam.acquire.get, 0),
|
||||
(self.parent.hdf.capture.get, 0),
|
||||
(self.parent.roistat.ts_acquiring.get, 'Done')
|
||||
],
|
||||
timeout,
|
||||
check_stopped=True,
|
||||
all_signals=True
|
||||
)
|
||||
|
||||
# publish file location
|
||||
self.parent.filepath.put(self.parent.hdf.full_file_name.get())
|
||||
self.publish_file_location(done=True, successful=success)
|
||||
|
||||
# publish timeseries data
|
||||
metadata = self.parent.scaninfo.scan_msg.metadata
|
||||
metadata.update({"async_update": "append", "num_lines": self.parent.roistat.ts_current_point.get()})
|
||||
msg = messages.DeviceMessage(
|
||||
signals={
|
||||
self.parent.roistat.roi1.name_.get(): {
|
||||
'value': self.parent.roistat.roi1.ts_total.get(),
|
||||
},
|
||||
self.parent.roistat.roi2.name_.get(): {
|
||||
'value': self.parent.roistat.roi2.ts_total.get(),
|
||||
},
|
||||
},
|
||||
metadata=self.parent.scaninfo.scan_msg.metadata
|
||||
)
|
||||
self.parent.connector.xadd(
|
||||
topic=MessageEndpoints.device_async_readback(
|
||||
scan_id=self.parent.scaninfo.scan_id, device=self.parent.name
|
||||
),
|
||||
msg_dict={"data": msg},
|
||||
expire=1800,
|
||||
)
|
||||
|
||||
logger.success('XXXX complete %d XXXX' % success)
|
||||
if success:
|
||||
status.set_finished()
|
||||
else:
|
||||
status.set_exception(TimeoutError())
|
||||
return status
|
||||
|
||||
def on_stop(self):
|
||||
logger.success('XXXX stop XXXX')
|
||||
self.parent.cam.acquire.put(0)
|
||||
self.parent.hdf.capture.put(0)
|
||||
self.parent.roistat.ts_control.put(2)
|
||||
|
||||
def on_unstage(self):
|
||||
self.parent.cam.acquire.put(0)
|
||||
self.parent.hdf.capture.put(0)
|
||||
self.parent.roistat.ts_control.put(2)
|
||||
logger.success('XXXX unstage XXXX')
|
||||
|
||||
|
||||
class EigerROIStatPlugin(ROIStatPlugin):
|
||||
roi1 = ADCpt(ROIStatNPlugin, '1:')
|
||||
roi2 = ADCpt(ROIStatNPlugin, '2:')
|
||||
|
||||
class Eiger500K(PSIDetectorBase):
|
||||
"""
|
||||
"""
|
||||
custom_prepare_cls = Eiger500KSetup
|
||||
|
||||
cam = ADCpt(SLSDetectorCam, 'cam1:')
|
||||
|
||||
#image = ADCpt(ImagePlugin, 'image1:')
|
||||
#roi1 = ADCpt(ROIPlugin, 'ROI1:')
|
||||
#roi2 = ADCpt(ROIPlugin, 'ROI2:')
|
||||
#stats1 = ADCpt(StatsPlugin, 'Stats1:')
|
||||
#stats2 = ADCpt(StatsPlugin, 'Stats2:')
|
||||
roistat = ADCpt(EigerROIStatPlugin, 'ROIStat1:')
|
||||
#roistat1 = ADCpt(ROIStatNPlugin, 'ROIStat1:1:')
|
||||
#roistat2 = ADCpt(ROIStatNPlugin, 'ROIStat1:2:')
|
||||
hdf = ADCpt(HDF5Plugin, 'HDF1:')
|
@ -0,0 +1,226 @@
|
||||
"""
|
||||
|
||||
|
||||
This was taken from Addam repository
|
||||
and commented after discussion with Xiaquiang
|
||||
As this also uses the are detector software, we might use is as well for teh flacon /xmap integration.
|
||||
This is based on falcon integration at cSAXS. Advantage over integration from BEC team is that this integration is very slim, and does not contain channels which are nor needed for data acquisition.
|
||||
One could consider to split integration into two classes, as slim one for data acquisition, and a more complete one for 'operation and monitoring'
|
||||
The channels in the 2nd class would then the saved only before a scan, which the 'data acquisition class' would be read at each data point.
|
||||
|
||||
#######################################
|
||||
# Strutur of software
|
||||
#######################################
|
||||
|
||||
1) Eiger500KSetup(CustomDetectorMixin)---> inherits from CustomdetectorMixing
|
||||
|
||||
2) class Eiger500K(PSIDetectorBase) ---> inherits from PSIDetectorBase
|
||||
|
||||
|
||||
These calsses are linkes to each other by the command
|
||||
|
||||
custom_prepare_cls = Eiger500KSetup
|
||||
|
||||
it references/activates the self.parent used in Eiger500Ksetup to attributes defined in Eiger500K.
|
||||
for example in Eiger500K, we define cam = ADCpt(SLSDetectorCam, 'cam1:'), which is referenced in \
|
||||
Eiger500Ksetup by the command
|
||||
self.parent.cam.array_counter.put(0)
|
||||
|
||||
|
||||
class Eiger500KSetup(CustomDetectorMixin):
|
||||
def __init__(self, *args, parent:Device = None, **kwargs):
|
||||
super().__init__(*args, parent=parent, **kwargs)
|
||||
self._counter = 0
|
||||
|
||||
def on_stage(self):
|
||||
exposure_time = self.parent.scaninfo.exp_time
|
||||
num_points = self.parent.scaninfo.num_points
|
||||
|
||||
# camera acquisition parameters
|
||||
self.parent.cam.array_counter.put(0)
|
||||
if self.parent.scaninfo.scan_type == 'step':
|
||||
self.parent.cam.acquire_time.put(exposure_time)
|
||||
|
||||
... etc
|
||||
|
||||
|
||||
class Eiger500K(PSIDetectorBase):
|
||||
"""
|
||||
"""
|
||||
custom_prepare_cls = Eiger500KSetup
|
||||
cam = ADCpt(SLSDetectorCam, 'cam1:')
|
||||
|
||||
.... etc
|
||||
|
||||
###################################################
|
||||
#
|
||||
# flyscans
|
||||
#
|
||||
###################################################
|
||||
|
||||
Images seem to be saves via hdf5 plugin (no live view is possible()
|
||||
|
||||
ROI is used to store 0d Signal.
|
||||
These data are stored continuously collected an array in the ROI plugin
|
||||
this could be used to store ROI data of XMAP/FALCON
|
||||
|
||||
"""
|
||||
|
||||
from ophyd import (
|
||||
ADComponent as ADCpt,
|
||||
Device,
|
||||
DeviceStatus,
|
||||
)
|
||||
|
||||
from ophyd_devices.devices.areadetector.cam import SLSDetectorCam
|
||||
from ophyd_devices.devices.areadetector.plugins import (
|
||||
ImagePlugin_V35 as ImagePlugin,
|
||||
StatsPlugin_V35 as StatsPlugin,
|
||||
HDF5Plugin_V35 as HDF5Plugin,
|
||||
ROIPlugin_V35 as ROIPlugin,
|
||||
ROIStatPlugin_V35 as ROIStatPlugin,
|
||||
ROIStatNPlugin_V35 as ROIStatNPlugin,
|
||||
)
|
||||
from ophyd_devices.interfaces.base_classes.psi_detector_base import PSIDetectorBase, CustomDetectorMixin
|
||||
|
||||
from bec_lib import bec_logger, messages
|
||||
from bec_lib.endpoints import MessageEndpoints
|
||||
|
||||
logger = bec_logger.logger
|
||||
|
||||
DETECTOR_TIMEOUT = 5
|
||||
|
||||
class Eiger500KSetup(CustomDetectorMixin):
|
||||
def __init__(self, *args, parent:Device = None, **kwargs):
|
||||
super().__init__(*args, parent=parent, **kwargs)
|
||||
self._counter = 0
|
||||
|
||||
def on_stage(self):
|
||||
exposure_time = self.parent.scaninfo.exp_time
|
||||
num_points = self.parent.scaninfo.num_points
|
||||
|
||||
# camera acquisition parameters
|
||||
self.parent.cam.array_counter.put(0)
|
||||
if self.parent.scaninfo.scan_type == 'step':
|
||||
self.parent.cam.acquire_time.put(exposure_time)
|
||||
self.parent.cam.num_images.put(1)
|
||||
self.parent.cam.image_mode.put(0) # Single
|
||||
self.parent.cam.trigger_mode.put(0) # auto
|
||||
else:
|
||||
# In flyscan, the exp_time is the time between two triggers,
|
||||
# which minus 15% is used as the acquisition time.
|
||||
self.parent.cam.acquire_time.put(exposure_time * 0.85)
|
||||
self.parent.cam.num_images.put(num_points)
|
||||
self.parent.cam.image_mode.put(1) # Multiple
|
||||
self.parent.cam.trigger_mode.put(1) # trigger
|
||||
self.parent.cam.acquire.put(1, wait=False) # arm
|
||||
|
||||
# file writer
|
||||
self.parent.hdf.lazy_open.put(1)
|
||||
self.parent.hdf.num_capture.put(num_points)
|
||||
self.parent.hdf.file_write_mode.put(2) # Stream
|
||||
self.parent.hdf.capture.put(1, wait=False)
|
||||
self.parent.hdf.enable.put(1) # enable plugin
|
||||
|
||||
# roi statistics to collect signal and background in a timeseries
|
||||
self.parent.roistat.enable.put(1)
|
||||
self.parent.roistat.ts_num_points.put(num_points)
|
||||
self.parent.roistat.ts_control.put(0, wait=False) # Erase/Start
|
||||
|
||||
logger.success('XXXX stage XXXX')
|
||||
|
||||
def on_trigger(self):
|
||||
self.parent.cam.acquire.put(1, wait=False)
|
||||
logger.success('XXXX trigger XXXX')
|
||||
|
||||
return self.wait_with_status(
|
||||
[(self.parent.cam.acquire.get, 0)],
|
||||
self.parent.scaninfo.exp_time + DETECTOR_TIMEOUT,
|
||||
all_signals=True
|
||||
)
|
||||
|
||||
def on_complete(self):
|
||||
status = DeviceStatus(self.parent)
|
||||
|
||||
if self.parent.scaninfo.scan_type == 'step':
|
||||
timeout = DETECTOR_TIMEOUT
|
||||
else:
|
||||
timeout = self.parent.scaninfo.exp_time * self.parent.scaninfo.num_points + DETECTOR_TIMEOUT
|
||||
logger.success('XXXX %s XXXX' % self.parent.roistat.ts_acquiring.get())
|
||||
success = self.wait_for_signals(
|
||||
[
|
||||
(self.parent.cam.acquire.get, 0),
|
||||
(self.parent.hdf.capture.get, 0),
|
||||
(self.parent.roistat.ts_acquiring.get, 'Done')
|
||||
],
|
||||
timeout,
|
||||
check_stopped=True,
|
||||
all_signals=True
|
||||
)
|
||||
|
||||
# publish file location
|
||||
self.parent.filepath.put(self.parent.hdf.full_file_name.get())
|
||||
self.publish_file_location(done=True, successful=success)
|
||||
|
||||
# publish timeseries data
|
||||
metadata = self.parent.scaninfo.scan_msg.metadata
|
||||
metadata.update({"async_update": "append", "num_lines": self.parent.roistat.ts_current_point.get()})
|
||||
msg = messages.DeviceMessage(
|
||||
signals={
|
||||
self.parent.roistat.roi1.name_.get(): {
|
||||
'value': self.parent.roistat.roi1.ts_total.get(),
|
||||
},
|
||||
self.parent.roistat.roi2.name_.get(): {
|
||||
'value': self.parent.roistat.roi2.ts_total.get(),
|
||||
},
|
||||
},
|
||||
metadata=self.parent.scaninfo.scan_msg.metadata
|
||||
)
|
||||
self.parent.connector.xadd(
|
||||
topic=MessageEndpoints.device_async_readback(
|
||||
scan_id=self.parent.scaninfo.scan_id, device=self.parent.name
|
||||
),
|
||||
msg_dict={"data": msg},
|
||||
expire=1800,
|
||||
)
|
||||
|
||||
logger.success('XXXX complete %d XXXX' % success)
|
||||
if success:
|
||||
status.set_finished()
|
||||
else:
|
||||
status.set_exception(TimeoutError())
|
||||
return status
|
||||
|
||||
def on_stop(self):
|
||||
logger.success('XXXX stop XXXX')
|
||||
self.parent.cam.acquire.put(0)
|
||||
self.parent.hdf.capture.put(0)
|
||||
self.parent.roistat.ts_control.put(2)
|
||||
|
||||
def on_unstage(self):
|
||||
self.parent.cam.acquire.put(0)
|
||||
self.parent.hdf.capture.put(0)
|
||||
self.parent.roistat.ts_control.put(2)
|
||||
logger.success('XXXX unstage XXXX')
|
||||
|
||||
|
||||
class EigerROIStatPlugin(ROIStatPlugin):
|
||||
roi1 = ADCpt(ROIStatNPlugin, '1:')
|
||||
roi2 = ADCpt(ROIStatNPlugin, '2:')
|
||||
|
||||
class Eiger500K(PSIDetectorBase):
|
||||
"""
|
||||
"""
|
||||
custom_prepare_cls = Eiger500KSetup
|
||||
|
||||
cam = ADCpt(SLSDetectorCam, 'cam1:')
|
||||
|
||||
#image = ADCpt(ImagePlugin, 'image1:')
|
||||
#roi1 = ADCpt(ROIPlugin, 'ROI1:')
|
||||
#roi2 = ADCpt(ROIPlugin, 'ROI2:')
|
||||
#stats1 = ADCpt(StatsPlugin, 'Stats1:')
|
||||
#stats2 = ADCpt(StatsPlugin, 'Stats2:')
|
||||
roistat = ADCpt(EigerROIStatPlugin, 'ROIStat1:')
|
||||
#roistat1 = ADCpt(ROIStatNPlugin, 'ROIStat1:1:')
|
||||
#roistat2 = ADCpt(ROIStatNPlugin, 'ROIStat1:2:')
|
||||
hdf = ADCpt(HDF5Plugin, 'HDF1:')
|
@ -0,0 +1,227 @@
|
||||
"""
|
||||
This was takem from Addam repository
|
||||
and commented after discussion with Xiaquiang
|
||||
|
||||
As this also uses the aere detector software, we might use is as well for teh flacon /xmap integration.
|
||||
This is based on falcon integration at cSAXS. Advantage over integration from BEC team is that this integration is very slem, and
|
||||
does not contain channels which are nor needed for data aquisition.
|
||||
One could consider to split integration into two classses, as slim one for data aquisition, and a more complete one for 'operation and monitoring'
|
||||
The channekls in the 2nd calss would then the saved only before a scan, which the 'data aquisition class' would be read at each data point.
|
||||
|
||||
|
||||
#######################################
|
||||
# Strutur of software
|
||||
#######################################
|
||||
|
||||
1) Eiger500KSetup(CustomDetectorMixin)---> inherits from CustomdetectorMixing
|
||||
|
||||
2) class Eiger500K(PSIDetectorBase) ---> inherits from PSIDetectorBase
|
||||
|
||||
|
||||
These calsses are linkes to each other by the command
|
||||
|
||||
custom_prepare_cls = Eiger500KSetup
|
||||
|
||||
it references/activates the self.parent used in Eiger500Ksetup to attributes defined in Eiger500K.
|
||||
for example in Eiger500K, we define cam = ADCpt(SLSDetectorCam, 'cam1:'), which is referenced in \
|
||||
Eiger500Ksetup by the command
|
||||
self.parent.cam.array_counter.put(0)
|
||||
|
||||
|
||||
class Eiger500KSetup(CustomDetectorMixin):
|
||||
def __init__(self, *args, parent:Device = None, **kwargs):
|
||||
super().__init__(*args, parent=parent, **kwargs)
|
||||
self._counter = 0
|
||||
|
||||
def on_stage(self):
|
||||
exposure_time = self.parent.scaninfo.exp_time
|
||||
num_points = self.parent.scaninfo.num_points
|
||||
|
||||
# camera acquisition parameters
|
||||
self.parent.cam.array_counter.put(0)
|
||||
if self.parent.scaninfo.scan_type == 'step':
|
||||
self.parent.cam.acquire_time.put(exposure_time)
|
||||
|
||||
... etc
|
||||
|
||||
|
||||
class Eiger500K(PSIDetectorBase):
|
||||
"""
|
||||
"""
|
||||
custom_prepare_cls = Eiger500KSetup
|
||||
cam = ADCpt(SLSDetectorCam, 'cam1:')
|
||||
|
||||
.... etc
|
||||
|
||||
###################################################
|
||||
#
|
||||
# flyscans
|
||||
#
|
||||
###################################################
|
||||
|
||||
Images seem to be saves via hdf5 plugin (no live vuiew is possible()
|
||||
ROI
|
||||
Here the roi plugin of the area detector is used.
|
||||
|
||||
|
||||
|
||||
"""
|
||||
|
||||
from ophyd import (
|
||||
ADComponent as ADCpt,
|
||||
Device,
|
||||
DeviceStatus,
|
||||
)
|
||||
|
||||
from ophyd_devices.devices.areadetector.cam import SLSDetectorCam
|
||||
from ophyd_devices.devices.areadetector.plugins import (
|
||||
ImagePlugin_V35 as ImagePlugin,
|
||||
StatsPlugin_V35 as StatsPlugin,
|
||||
HDF5Plugin_V35 as HDF5Plugin,
|
||||
ROIPlugin_V35 as ROIPlugin,
|
||||
ROIStatPlugin_V35 as ROIStatPlugin,
|
||||
ROIStatNPlugin_V35 as ROIStatNPlugin,
|
||||
)
|
||||
from ophyd_devices.interfaces.base_classes.psi_detector_base import PSIDetectorBase, CustomDetectorMixin
|
||||
|
||||
from bec_lib import bec_logger, messages
|
||||
from bec_lib.endpoints import MessageEndpoints
|
||||
|
||||
logger = bec_logger.logger
|
||||
|
||||
DETECTOR_TIMEOUT = 5
|
||||
|
||||
class Eiger500KSetup(CustomDetectorMixin):
|
||||
def __init__(self, *args, parent:Device = None, **kwargs):
|
||||
super().__init__(*args, parent=parent, **kwargs)
|
||||
self._counter = 0
|
||||
|
||||
def on_stage(self):
|
||||
exposure_time = self.parent.scaninfo.exp_time
|
||||
num_points = self.parent.scaninfo.num_points
|
||||
|
||||
# camera acquisition parameters
|
||||
self.parent.cam.array_counter.put(0)
|
||||
if self.parent.scaninfo.scan_type == 'step':
|
||||
self.parent.cam.acquire_time.put(exposure_time)
|
||||
self.parent.cam.num_images.put(1)
|
||||
self.parent.cam.image_mode.put(0) # Single
|
||||
self.parent.cam.trigger_mode.put(0) # auto
|
||||
else:
|
||||
# In flyscan, the exp_time is the time between two triggers,
|
||||
# which minus 15% is used as the acquisition time.
|
||||
self.parent.cam.acquire_time.put(exposure_time * 0.85)
|
||||
self.parent.cam.num_images.put(num_points)
|
||||
self.parent.cam.image_mode.put(1) # Multiple
|
||||
self.parent.cam.trigger_mode.put(1) # trigger
|
||||
self.parent.cam.acquire.put(1, wait=False) # arm
|
||||
|
||||
# file writer
|
||||
self.parent.hdf.lazy_open.put(1)
|
||||
self.parent.hdf.num_capture.put(num_points)
|
||||
self.parent.hdf.file_write_mode.put(2) # Stream
|
||||
self.parent.hdf.capture.put(1, wait=False)
|
||||
self.parent.hdf.enable.put(1) # enable plugin
|
||||
|
||||
# roi statistics to collect signal and background in a timeseries
|
||||
self.parent.roistat.enable.put(1)
|
||||
self.parent.roistat.ts_num_points.put(num_points)
|
||||
self.parent.roistat.ts_control.put(0, wait=False) # Erase/Start
|
||||
|
||||
logger.success('XXXX stage XXXX')
|
||||
|
||||
def on_trigger(self):
|
||||
self.parent.cam.acquire.put(1, wait=False)
|
||||
logger.success('XXXX trigger XXXX')
|
||||
|
||||
return self.wait_with_status(
|
||||
[(self.parent.cam.acquire.get, 0)],
|
||||
self.parent.scaninfo.exp_time + DETECTOR_TIMEOUT,
|
||||
all_signals=True
|
||||
)
|
||||
|
||||
def on_complete(self):
|
||||
status = DeviceStatus(self.parent)
|
||||
|
||||
if self.parent.scaninfo.scan_type == 'step':
|
||||
timeout = DETECTOR_TIMEOUT
|
||||
else:
|
||||
timeout = self.parent.scaninfo.exp_time * self.parent.scaninfo.num_points + DETECTOR_TIMEOUT
|
||||
logger.success('XXXX %s XXXX' % self.parent.roistat.ts_acquiring.get())
|
||||
success = self.wait_for_signals(
|
||||
[
|
||||
(self.parent.cam.acquire.get, 0),
|
||||
(self.parent.hdf.capture.get, 0),
|
||||
(self.parent.roistat.ts_acquiring.get, 'Done')
|
||||
],
|
||||
timeout,
|
||||
check_stopped=True,
|
||||
all_signals=True
|
||||
)
|
||||
|
||||
# publish file location
|
||||
self.parent.filepath.put(self.parent.hdf.full_file_name.get())
|
||||
self.publish_file_location(done=True, successful=success)
|
||||
|
||||
# publish timeseries data
|
||||
metadata = self.parent.scaninfo.scan_msg.metadata
|
||||
metadata.update({"async_update": "append", "num_lines": self.parent.roistat.ts_current_point.get()})
|
||||
msg = messages.DeviceMessage(
|
||||
signals={
|
||||
self.parent.roistat.roi1.name_.get(): {
|
||||
'value': self.parent.roistat.roi1.ts_total.get(),
|
||||
},
|
||||
self.parent.roistat.roi2.name_.get(): {
|
||||
'value': self.parent.roistat.roi2.ts_total.get(),
|
||||
},
|
||||
},
|
||||
metadata=self.parent.scaninfo.scan_msg.metadata
|
||||
)
|
||||
self.parent.connector.xadd(
|
||||
topic=MessageEndpoints.device_async_readback(
|
||||
scan_id=self.parent.scaninfo.scan_id, device=self.parent.name
|
||||
),
|
||||
msg_dict={"data": msg},
|
||||
expire=1800,
|
||||
)
|
||||
|
||||
logger.success('XXXX complete %d XXXX' % success)
|
||||
if success:
|
||||
status.set_finished()
|
||||
else:
|
||||
status.set_exception(TimeoutError())
|
||||
return status
|
||||
|
||||
def on_stop(self):
|
||||
logger.success('XXXX stop XXXX')
|
||||
self.parent.cam.acquire.put(0)
|
||||
self.parent.hdf.capture.put(0)
|
||||
self.parent.roistat.ts_control.put(2)
|
||||
|
||||
def on_unstage(self):
|
||||
self.parent.cam.acquire.put(0)
|
||||
self.parent.hdf.capture.put(0)
|
||||
self.parent.roistat.ts_control.put(2)
|
||||
logger.success('XXXX unstage XXXX')
|
||||
|
||||
|
||||
class EigerROIStatPlugin(ROIStatPlugin):
|
||||
roi1 = ADCpt(ROIStatNPlugin, '1:')
|
||||
roi2 = ADCpt(ROIStatNPlugin, '2:')
|
||||
|
||||
class Eiger500K(PSIDetectorBase):
|
||||
"""
|
||||
"""
|
||||
custom_prepare_cls = Eiger500KSetup
|
||||
|
||||
cam = ADCpt(SLSDetectorCam, 'cam1:')
|
||||
|
||||
#image = ADCpt(ImagePlugin, 'image1:')
|
||||
#roi1 = ADCpt(ROIPlugin, 'ROI1:')
|
||||
#roi2 = ADCpt(ROIPlugin, 'ROI2:')
|
||||
#stats1 = ADCpt(StatsPlugin, 'Stats1:')
|
||||
#stats2 = ADCpt(StatsPlugin, 'Stats2:')
|
||||
roistat = ADCpt(EigerROIStatPlugin, 'ROIStat1:')
|
||||
#roistat1 = ADCpt(ROIStatNPlugin, 'ROIStat1:1:')
|
||||
#roistat2 = ADCpt(ROIStatNPlugin, 'ROIStat1:2:')
|
||||
hdf = ADCpt(HDF5Plugin, 'HDF1:')
|
@ -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()
|
@ -0,0 +1,2 @@
|
||||
print('Work with unix shell comands :')
|
||||
print('use %ls %mkdir etc ')
|
@ -0,0 +1,93 @@
|
||||
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):
|
||||
# Here the whole namespace and finctionality
|
||||
# of Device(Blueskyinterface,Pphydobject) is inherited
|
||||
# into class StageXY using Device
|
||||
# device requires as input parameters the prefix
|
||||
# in the initialization of Cpt there is some magic
|
||||
# hard to understand, moist likely using calss methods..
|
||||
#
|
||||
|
||||
x = Cpt(EpicsMotor, "ScanX")
|
||||
y = Cpt(EpicsMotor, "ScanY")
|
||||
|
||||
|
||||
# end class
|
||||
|
||||
|
||||
print()
|
||||
print("init xy_stage, use input parameter from Device and prefix is defined in call ")
|
||||
xy_stage = StageXY("X07MB-ES-MA1:", name="stageXXX")
|
||||
|
||||
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__
|
@ -0,0 +1,120 @@
|
||||
from ophyd import Device, EpicsMotor, EpicsSignal, EpicsSignalRO
|
||||
from ophyd import Component as Cpt
|
||||
|
||||
|
||||
from phoenix_bec.local_scripts.Examples.my_ophyd import Device,EpicsMotor, EpicsSignal, EpicsSignalRO
|
||||
from phoenix_bec.local_scripts.Examples.my_ophyd import Component as Cpt
|
||||
|
||||
############################################
|
||||
#
|
||||
# KEEP my_ophyd zipped to avoid chaos local version does not run
|
||||
# so this is rather useless
|
||||
#
|
||||
##########################################
|
||||
|
||||
|
||||
|
||||
#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 ')
|
||||
|
||||
EpicsMotor, 'ScanY'
|
||||
"""
|
||||
class MyCpt(typing.Generic[K]):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
cls: Type[K],
|
||||
suffix: Optional[str] = None,
|
||||
*,
|
||||
lazy: Optional[bool] = None,
|
||||
trigger_value: Optional[Any] = None,
|
||||
add_prefix: Optional[Sequence[str]] = None,
|
||||
doc: Optional[str] = None,
|
||||
kind: Union[str, Kind] = Kind.normal,
|
||||
**kwargs,
|
||||
):
|
||||
self.attr = None # attr is set later by the device when known
|
||||
self.cls = cls
|
||||
self.kwargs = kwargs
|
||||
#self.lazy = lazy if lazy is not None else self.lazy_default
|
||||
self.suffix = suffix
|
||||
self.doc = doc
|
||||
self.trigger_value = trigger_value # TODO discuss
|
||||
self.kind = Kind[kind.lower()] if isinstance(kind, str) else Kind(kind)
|
||||
if add_prefix is None:
|
||||
add_prefix = ("suffix", "write_pv")
|
||||
self.add_prefix = tuple(add_prefix)
|
||||
self._subscriptions = collections.defaultdict(list)
|
||||
print(' ')
|
||||
"""
|
||||
|
||||
|
||||
in Device we have this class method
|
||||
Class device(..):
|
||||
....
|
||||
@classmethod
|
||||
def _initialize_device(cls):
|
||||
....
|
||||
|
||||
|
||||
class StageXY(Device):
|
||||
# Here the whole namespace and finctionality
|
||||
# of Device(Blueskyinterface,Pphydobject) is inherited
|
||||
# into class StageXY
|
||||
|
||||
x = Cpt(EpicsMotor, 'ScanX')
|
||||
y = Cpt(EpicsMotor, 'ScanY')
|
||||
|
||||
# end class
|
||||
|
||||
|
||||
|
||||
|
||||
print()
|
||||
print('init xy_stage, use input parameter from Device and prefix is defined in call ')
|
||||
xy = StageXY('X07MB-ES-MA1:', name='xy_name')
|
||||
|
||||
print_dic('class StageXY',StageXY)
|
||||
print_dic('instance of StageXY',xy)
|
||||
|
||||
|
||||
|
||||
|
||||
#print('xy.x.prefix')
|
||||
#print(xy.x.prefix)
|
||||
xy.__dict__
|
@ -0,0 +1,26 @@
|
||||
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")
|
||||
ScanX_RBV = EpicsMotor(name="ScanX", prefix="X07MB-ES-MA1:ScanX.RBV")
|
||||
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"
|
||||
)
|
@ -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)
|
0
phoenix_bec/local_scripts/Native_Python_script/5
Normal file
0
phoenix_bec/local_scripts/Native_Python_script/5
Normal file
522
phoenix_bec/local_scripts/Native_Python_script/MyLib.py
Normal file
522
phoenix_bec/local_scripts/Native_Python_script/MyLib.py
Normal file
@ -0,0 +1,522 @@
|
||||
#! /usr/bin/env python
|
||||
#
|
||||
#
|
||||
# old python library for pure python aplicatoins
|
||||
#
|
||||
# ===========================================================================
|
||||
import os
|
||||
import sys
|
||||
import numpy as np
|
||||
#from CaChannel import *
|
||||
import epics as ep
|
||||
#from EpicsMotor import *
|
||||
#from epicsPV import *
|
||||
import time
|
||||
import string
|
||||
|
||||
|
||||
|
||||
|
||||
def init_PV():
|
||||
|
||||
|
||||
#
|
||||
# routine initialzed all EPIC channels used in the Phoenix beamline
|
||||
#
|
||||
|
||||
global PV
|
||||
PV = {} # define EPICS CHANNEL (PV) directory
|
||||
|
||||
# define one channel
|
||||
|
||||
try:
|
||||
PV['X07MB_MB_ScanX_VAL'] = ep.PV('X07MB-MA1_ScanX.VAL')
|
||||
except:
|
||||
print( "*** Error finding EPICS channel: ")
|
||||
sys.exit("*** Program STOP ***")
|
||||
print ('init PV done')
|
||||
|
||||
|
||||
def get_epicsPV(channel):
|
||||
|
||||
|
||||
b=-1
|
||||
# first check fro channel existence
|
||||
#print '.................................. enter get_epicsPV from X_X07MB_lib_dev.py' ,channel
|
||||
#print('try ',channel)
|
||||
try:
|
||||
#print ' try reading channel X_X07MB_lib_dev.py' ,channel
|
||||
b=PV[channel].getw()
|
||||
except:
|
||||
# channel not defined / found now created in PV list this channel into PV LIST
|
||||
print ('trouble reading epics PV..',channel,'likely PV undefined')
|
||||
n_tries=0
|
||||
tt=0
|
||||
while tt==0:
|
||||
print ('in while loop ')
|
||||
try:
|
||||
print ('try============= ',channel)
|
||||
PV[channel]=epicsPV(channel)
|
||||
#print PV
|
||||
tt=1
|
||||
print (PV[channel].getw())
|
||||
except:
|
||||
#print 'set tt back to zero as something went wrong here'
|
||||
tt=0
|
||||
#print 'except somthing is wrong here, likely time out, now retry channels'
|
||||
#print b
|
||||
n_tries=n_tries+1
|
||||
time.sleep(.3)
|
||||
print( 'next try',n_tries)
|
||||
if n_tries==25:
|
||||
tt=1
|
||||
print( 'too many tries for channel acces')
|
||||
print( 'Channel',channel)
|
||||
sys.exit("*** Program STOP ***")
|
||||
# endif
|
||||
|
||||
# endtry
|
||||
# now try again and set monitor to reduce chanel accesscalls
|
||||
|
||||
PV[channel].setMonitor()
|
||||
b=PV[channel].getw()
|
||||
# end while
|
||||
# endtry
|
||||
|
||||
return b
|
||||
|
||||
|
||||
|
||||
def put_epicsPV(channel,val,delay={}):
|
||||
|
||||
print(channel,val,delay)
|
||||
|
||||
#try:
|
||||
# from CaChannel import *
|
||||
# from epicsMotor import *
|
||||
# from epicsPV import *
|
||||
# import time
|
||||
# import string
|
||||
#except:
|
||||
# try:
|
||||
# sys.path.insert(0, os.path.expandvars("$SLSBASE/sls/lib/python22/CaChannel"))
|
||||
# sys.path.insert(0, os.path.expandvars("/exchange/share/mXAS/pyth/mod"))
|
||||
# from CaChannel import *
|
||||
# from epicsPV import *
|
||||
# except:
|
||||
# os.system ("xkbbell")
|
||||
# os.system ("xmessage -nearmouse -timeout 30 \
|
||||
# -buttons \"\" \"epicsPV or CaChannel module cannot be found\"")
|
||||
# sys.exit(1)
|
||||
# # endtry
|
||||
|
||||
# endtry
|
||||
|
||||
#print '------------------------------------------' , val
|
||||
#print ' *delay) ', delay
|
||||
#print ' *delay) '
|
||||
|
||||
#print ' locals',locals()
|
||||
if delay == {}:
|
||||
#print 'in if use predefined delay of 0 '
|
||||
set_delay=0.0
|
||||
else:
|
||||
#print ' set delay to input value',delay
|
||||
set_delay = delay
|
||||
#endelse
|
||||
|
||||
|
||||
|
||||
n_tries=0
|
||||
tt=0
|
||||
b=-1
|
||||
while tt==0:
|
||||
try:
|
||||
#print ' try writing into ' ,channel
|
||||
epicsPV(channel).putw(val)
|
||||
tt=1
|
||||
except:
|
||||
print( 'trouble reading epics PV..')
|
||||
print( b)
|
||||
|
||||
n_tries=n_tries+1
|
||||
tt=0
|
||||
time.sleep(.5)
|
||||
if n_tries==50:
|
||||
tt=1
|
||||
print ('too many tries for channel acces')
|
||||
#sys.exit("*** Program STOP ***")
|
||||
# endtry
|
||||
# finally perform delay
|
||||
time.sleep(set_delay)
|
||||
return b
|
||||
|
||||
# ======================================================
|
||||
|
||||
|
||||
|
||||
def move_and_wait_bk(Ch_motor,value,backlash):
|
||||
global phoenix_no_move
|
||||
global phoenix_no_scan
|
||||
# print ch_motor
|
||||
if phoenix_no_move==1:
|
||||
print (' ')
|
||||
print ('in move_and_wait_bk ')
|
||||
print ('should move motor ',Ch_motor,' to ', value)
|
||||
print ('BL in no move debugging state. DO NOT MOVE MOTOR')
|
||||
print (' ')
|
||||
return
|
||||
|
||||
# move to new position with backlash correction
|
||||
move_and_wait(Ch_motor,value-backlash)
|
||||
move_and_wait(Ch_motor,value)
|
||||
|
||||
# ======================================================
|
||||
|
||||
def move_and_wait_old(Ch_motor,value,check_moving):
|
||||
global phoenix_no_move
|
||||
global phoenix_no_scan
|
||||
# print 'in move and waqit',phoenix_no_move
|
||||
# print Ch_motor
|
||||
|
||||
if phoenix_no_move==1:
|
||||
print (' ')
|
||||
print ('in move_and_wait ')
|
||||
print ('should move motor ',Ch_motor,' to ', value)
|
||||
print ('BL in no move debugging state. DO NOT MOVE MOTOR')
|
||||
print (' ')
|
||||
return
|
||||
|
||||
Ch_motor_use=Ch_motor
|
||||
|
||||
if (Ch_motor[len(Ch_motor)-4:len(Ch_motor)] == '.VAL'):
|
||||
Ch_motor_use=Ch_motor[0:len(Ch_motor)-4]
|
||||
# print Ch_motor_use
|
||||
|
||||
if Ch_motor_use[0:5] == 'X07MA':
|
||||
stop_if_phoenix_is_offline()
|
||||
|
||||
if Ch_motor_use[0:11] == 'X07MB-OP-MI1':
|
||||
stop_if_phoenix_is_offline()
|
||||
|
||||
|
||||
Ch_motor_val = Ch_motor_use +'.VAL'
|
||||
Ch_motor_done = Ch_motor_use +'.DMOV'
|
||||
#print Ch_motor_val
|
||||
|
||||
epicsPV(Ch_motor_val).putw(value)
|
||||
|
||||
print( 'wait for movement of', Ch_motor_val)
|
||||
while not epicsPV(Ch_motor_done).getw() :
|
||||
time.sleep(0.1)
|
||||
get_epicsPV(Ch_motor_val)
|
||||
|
||||
#print 'Motor ',Ch_motor_val,' still moving, wait '
|
||||
print ( 'movement finished')
|
||||
|
||||
# ===========================================================
|
||||
|
||||
def move_and_wait(motor,value,check_moving=0):
|
||||
global phoenix_no_move
|
||||
global phoenix_no_scan
|
||||
# print 'in move and waqit',phoenix_no_move
|
||||
# print Ch_motor
|
||||
|
||||
if phoenix_no_move==1:
|
||||
print( ' ')
|
||||
print( 'in move_and_wait ')
|
||||
print( 'should move motor ',Ch_motor,' to ', value)
|
||||
print( 'BL in no move debugging state. DO NOT MOVE MOTOR')
|
||||
print( ' ')
|
||||
return
|
||||
# some safety checks...
|
||||
|
||||
if motor.val[0:5] == 'X07MA':
|
||||
stop_if_phoenix_is_offline()
|
||||
|
||||
if motor.val[0:11] == 'X07MB-OP-MI1':
|
||||
stop_if_phoenix_is_offline()
|
||||
|
||||
put_epicsPV(motor.val,value,delay=0.2)
|
||||
|
||||
print ('wait for movement of', motor.val)
|
||||
|
||||
if check_moving == 0:
|
||||
while not get_epicsPV(motor.dmov):
|
||||
time.sleep(0.1)
|
||||
print( get_epicsPV(motor.val))
|
||||
print( 'Motor ',motor.val,' still moving, wait ')
|
||||
#endwhile
|
||||
else: # case with check of movement of encoders
|
||||
print ('else....')
|
||||
while not get_epicsPV(motor.dmov):
|
||||
|
||||
enc1= get_epicsPV(motor.rep)
|
||||
time.sleep(0.25)
|
||||
enc2= get_epicsPV(motor.rep)
|
||||
diff=enc2-enc1
|
||||
print( get_epicsPV(motor.val))
|
||||
if (abs(diff) < 5 and (get_epicsPV(motor.dmov) == 0)) :
|
||||
print ( 'suspect hanging of motor. monitor')
|
||||
enc1= get_epicsPV(motor.rep)
|
||||
time.sleep(0.5)
|
||||
enc2= get_epicsPV(motor.rep)
|
||||
diff=enc2-enc1
|
||||
time.sleep(0.5)
|
||||
if ((abs(diff) < 3) and (get_epicsPV(motor.dmov) == 0)):
|
||||
print (' ===================================== diff',diff )
|
||||
print ('difference encoder to motor too large')
|
||||
print (' STOP movement')
|
||||
put_epicsPV(motor.spmg,0,delay=0.1)
|
||||
stop
|
||||
# endif
|
||||
#endif
|
||||
print( 'Motor ',motor.val,' still moving, wait ')
|
||||
print( 'difference subsequent encoder positions ',diff)
|
||||
# endwhile
|
||||
# endif
|
||||
print( 'movement finished')
|
||||
|
||||
# =======================================================================
|
||||
|
||||
def Wait_for_Value(Channel,value):
|
||||
|
||||
while get_epicsPV(Channel) != value:
|
||||
#print(Channel)
|
||||
#print get_epicsPV(Channel)
|
||||
time.sleep(.02)
|
||||
#endwhile
|
||||
|
||||
def move_and_wait_2motors(mot1,mot2,val_1,val_2,check_moving=0):
|
||||
global phoenix_no_move
|
||||
global phoenix_no_scan
|
||||
# print 'in move and waqit',phoenix_no_move
|
||||
# print Ch_motor
|
||||
|
||||
if phoenix_no_move==1:
|
||||
print( ' ')
|
||||
print( 'in move_and_wait ')
|
||||
print( 'should move motor ',Ch_motor,' to ', value)
|
||||
print( 'BL in no move debugging state. DO NOT MOVE MOTOR')
|
||||
print( ' ')
|
||||
return
|
||||
|
||||
# some safety checks...
|
||||
|
||||
if mot1.val[0:5] == 'X07MA':
|
||||
stop_if_phoenix_is_offline()
|
||||
|
||||
if mot2.val[0:5] == 'X07MA':
|
||||
stop_if_phoenix_is_offline()
|
||||
|
||||
if mot1.val[0:11] == 'X07MB-OP-MI1':
|
||||
stop_if_phoenix_is_offline()
|
||||
|
||||
if mot2.val[0:11] == 'X07MB-OP-MI1':
|
||||
stop_if_phoenix_is_offline()
|
||||
|
||||
put_epicsPV(mot1.val,val_1,delay=0.2)
|
||||
put_epicsPV(mot2.val,val_2,delay=0.2)
|
||||
|
||||
print( 'wait for movement of', mot1.val,'and ', mot2.val)
|
||||
|
||||
if check_moving == 0:
|
||||
print (not get_epicsPV(mot1.dmov))
|
||||
print (not get_epicsPV(mot2.dmov))
|
||||
|
||||
while ((not get_epicsPV(mot1.dmov)) or (not get_epicsPV(mot2.dmov))):
|
||||
time.sleep(0.25)
|
||||
#endwhile
|
||||
print( get_epicsPV(mot1.val),get_epicsPV(mot2.val))
|
||||
print( 'Motor ',mot1.val,mot2.val,' still moving, wait ')
|
||||
|
||||
else: # case with check of movement of encoders
|
||||
print ( 'else....')
|
||||
while (not get_epicsPV(mot1.dmov)) or (not get_epicsPV(mot2.dmov)):
|
||||
#print ' in while '
|
||||
# for motor 1
|
||||
enc_1_a= get_epicsPV(mot1.rep)
|
||||
enc_2_a= get_epicsPV(mot2.rep)
|
||||
time.sleep(0.25)
|
||||
enc_1_b= get_epicsPV(mot1.rep)
|
||||
enc_2_b= get_epicsPV(mot2.rep)
|
||||
|
||||
diff_1=enc_1_b-enc_1_a
|
||||
diff_2=enc_2_b-enc_2_a
|
||||
#print ' encoder diffs',diff_1,diff_2
|
||||
|
||||
# check encoder movement for motor 1:
|
||||
if (abs(diff_1) < 5 and (get_epicsPV(mot1.dmov) == 0)) :
|
||||
print( 'suspect hanging of motor 1 monitor')
|
||||
enc_1_a= get_epicsPV(mot1.rep)
|
||||
time.sleep(0.25)
|
||||
enc_1_b= get_epicsPV(mot1.rep)
|
||||
diff_1=enc_1_b-enc_1_a
|
||||
time.sleep(0.5)
|
||||
if ((abs(diff_1) < 3) and (get_epicsPV(mot1.dmov) == 0)):
|
||||
print( ' ===================================== diff',diff_1 )
|
||||
print( 'difference subsequent encoder steps too small')
|
||||
print( 'Motor 1 hanging???? STOP movement for safety ')
|
||||
put_epicsPV(mot_1.spmg,0,delay=0.1)
|
||||
stop
|
||||
# endif
|
||||
#endif
|
||||
|
||||
# Now checlk motor 2
|
||||
if (abs(diff_2) < 5 and (get_epicsPV(mot2.dmov) == 0)) :
|
||||
print ('suspect hanging of motor 2: monitor')
|
||||
enc_2_a= get_epicsPV(mot2.rep)
|
||||
time.sleep(0.25)
|
||||
enc_2_b= get_epicsPV(mot2.rep)
|
||||
diff_2=enc_2_b-enc_2_a
|
||||
time.sleep(0.5)
|
||||
if ((abs(diff_2) < 3) and (get_epicsPV(mot2.dmov) == 0)):
|
||||
print( ' ===================================== diff',diff_2 )
|
||||
print( 'difference subsequent encoder steps too small')
|
||||
print( 'Motor 2 hanging???? STOP movement for safety ')
|
||||
put_epicsPV(mot2.spmg,0,delay=0.1)
|
||||
stop
|
||||
# endif
|
||||
#endif
|
||||
print( 'Motors ',mot1.val, 'and',mot2.val,' still moving, wait ')
|
||||
print( 'difference subsequent encoder positions ',diff_1, diff_2)
|
||||
# endwhile
|
||||
# endif
|
||||
print( 'movement finished')
|
||||
|
||||
# =======================================================================
|
||||
|
||||
# Generate all names
|
||||
class Motor():
|
||||
#define extensions for all channels
|
||||
e_val = '.VAL'
|
||||
e_dval = '.DVAL'
|
||||
e_rbv = '.RBV'
|
||||
e_drbv = '.DRBV'
|
||||
e_off ='.OFF'
|
||||
e_twv = '.TWV'
|
||||
e_twf = '.TWF'
|
||||
e_twr = '.TWR'
|
||||
|
||||
e_jogr ='.JOGR' # jog rev
|
||||
e_jogf ='.JOGF' # jog forw
|
||||
e_jomf ='.HOMF' # home forward
|
||||
e_jomr ='.HOMR' # home reverse
|
||||
e_hlm ='.HLM' #High limit
|
||||
e_llm ='.LLM' #lower limit
|
||||
|
||||
e_hls ='.HLS' #High limit
|
||||
e_lls ='.LLS' #lower limit
|
||||
|
||||
e_dmov ='.DMOV' # moving done
|
||||
e_movn ='.MOVN' # moving
|
||||
e_spmg ='.SPMG' # stop pause move go (0,1,2,3)
|
||||
e_ueip ='.UEIP' # encoder on / off
|
||||
e_diff ='.DIFF'
|
||||
e_rep = '.REP'
|
||||
e_foff ='.FOFF' #FOFF (1 = Frozen / 0 = variable)
|
||||
e_set = '.SET' # 0 = use 1 = set
|
||||
e_mres = '.MRES'
|
||||
e_eres = '.ERES'
|
||||
|
||||
def create_Ch(self,ch):
|
||||
# this routine creates all channel names
|
||||
self.val=ch+self.e_val
|
||||
self.dval=ch+self.e_dval
|
||||
self.rbv=ch+self.e_rbv
|
||||
self.drbv=ch+self.e_drbv
|
||||
self.off=ch+self.e_off
|
||||
self.twv=ch+self.e_twv
|
||||
self.twf=ch+self.e_twf
|
||||
self.twr=ch+self.e_twr
|
||||
self.jogr=ch+self.e_jogr
|
||||
self.jogf=ch+self.e_jogf
|
||||
self.hlm=ch+self.e_hlm
|
||||
self.llm=ch+self.e_llm
|
||||
self.hls=ch+self.e_hls
|
||||
self.lls=ch+self.e_lls
|
||||
|
||||
self.dmov = ch+self.e_dmov
|
||||
self.movn = ch+self.e_movn
|
||||
self.spmg = ch+self.e_spmg
|
||||
self.ueip = ch+self.e_ueip
|
||||
self.rep = ch+self.e_rep
|
||||
self.mres = ch+self.e_mres
|
||||
self.eres = ch+self.e_eres
|
||||
self.diff = ch+self.e_diff
|
||||
self.foff = ch+self.e_foff
|
||||
self.set = ch+self.e_set
|
||||
# end create_Chnames
|
||||
|
||||
|
||||
# endclass
|
||||
def define_motor(ch):
|
||||
g=Motor()
|
||||
g.create_Ch(ch)
|
||||
return g
|
||||
# end define_motor
|
||||
|
||||
def define_motor_kb(ch):
|
||||
g=Motor()
|
||||
g.create_Ch(ch)
|
||||
|
||||
# set offset to variable as default setting
|
||||
# this keeps the dval always constant
|
||||
|
||||
put_epicsPV(g.foff,1,delay=0.2) # offset to frozen as we do not initialize
|
||||
|
||||
put_epicsPV(g.set,0,delay=0.2) # make sure that coos are on use
|
||||
return g
|
||||
|
||||
# end define_motor
|
||||
|
||||
|
||||
def Next_Point():
|
||||
sp1=np.zeros(2048)
|
||||
sp2=np.zeros(2048)
|
||||
sp3=np.zeros(2048)
|
||||
sp4=np.zeros(2048)
|
||||
|
||||
d1 = 0
|
||||
d2 = 0
|
||||
d3 = 0
|
||||
d4 = 0
|
||||
|
||||
#print('erasestart')
|
||||
put_epicsPV('X07MB-XMAP:EraseStart',1,delay=.03)
|
||||
|
||||
#print('erasestart')
|
||||
#time.sleep(.1)
|
||||
|
||||
put_epicsPV('X07MB-OP2:SMPL',1,delay=.03)
|
||||
|
||||
Wait_for_Value("X07MB-OP2:SMPL-DONE",1)
|
||||
|
||||
put_epicsPV('X07MB-XMAP:StopAll',1,delay=.03)
|
||||
|
||||
sp1_old=sp1
|
||||
sp2_old=sp2
|
||||
sp3_old=sp3
|
||||
sp4_old=sp4
|
||||
|
||||
d1_old=d1
|
||||
d2_old=d2
|
||||
d3_old=d3
|
||||
d4_old=d4
|
||||
|
||||
sp1=get_epicsPV('X07MB-XMAP:mca1.VAL')
|
||||
sp2=get_epicsPV('X07MB-XMAP:mca2.VAL')
|
||||
sp3=get_epicsPV('X07MB-XMAP:mca3.VAL')
|
||||
sp4=get_epicsPV('X07MB-XMAP:mca4.VAL')
|
||||
|
||||
d1=get_epicsPV('X07MB-XMAP:mca1.DTIM')
|
||||
d2=get_epicsPV('X07MB-XMAP:mca2.DTIM')
|
||||
d3=get_epicsPV('X07MB-XMAP:mca3.DTIM')
|
||||
d4=get_epicsPV('X07MB-XMAP:mca4.DTIM')
|
||||
|
||||
print('differences',max(sp1-sp1_old),max(sp2-sp2_old),max(sp3-sp3_old),max(sp4-sp4_old))
|
||||
print('differences',d1-d1_old,d2-d2_old,d3-d3_old,d4-d4_old)
|
||||
|
||||
time.sleep(.5)
|
||||
# end
|
||||
init_PV()
|
789
phoenix_bec/local_scripts/Native_Python_script/MyLib.py~
Normal file
789
phoenix_bec/local_scripts/Native_Python_script/MyLib.py~
Normal file
@ -0,0 +1,789 @@
|
||||
#! /usr/bin/env python
|
||||
#
|
||||
#
|
||||
# old python library for pure python aplicatoins
|
||||
#
|
||||
# ===========================================================================
|
||||
|
||||
# for now still use the old python installation .....
|
||||
|
||||
try:
|
||||
from CaChannel import *
|
||||
from epicsMotor import *
|
||||
from epicsPV import *
|
||||
import time
|
||||
import string
|
||||
except:
|
||||
try:
|
||||
sys.path.insert(0, os.path.expandvars("$SLSBASE/sls/lib/python22/CaChannel"))
|
||||
sys.path.insert(0, os.path.expandvars("/exchange/share/mXAS/pyth/mod"))
|
||||
from CaChannel import *
|
||||
from epicsPV import *
|
||||
except:
|
||||
os.system ("xkbbell")
|
||||
os.system ("xmessage -nearmouse -timeout 30 \
|
||||
-buttons \"\" \"epicsPV or CaChannel module cannot be found\"")
|
||||
sys.exit(1)
|
||||
# endtry
|
||||
#endtry
|
||||
import numpy as np
|
||||
|
||||
|
||||
def stop_if_phoenix_is_offline():
|
||||
if (epicsPV('X07MB-OP-MI1:ONLINE').getw() <> 4) or ( epicsPV('X07MA-OP-CMU:ONLINE').getw() <>6 ):
|
||||
print ' '
|
||||
print 'PHOENIX NOT ONLINE ACCESS DENIED'
|
||||
print 'Status PHOENIX ',epicsPV("X07MB-OP-MI1:ONLINE.VAL").getw()
|
||||
print 'Status XTREME ',epicsPV("X07MA-OP-CMU:ONLINE.VAL").getw()
|
||||
print ' '
|
||||
print ' =========================================================== '
|
||||
print ' '
|
||||
print ' SCRIPT STOPPED '
|
||||
print ' '
|
||||
print ' ========================================================== '
|
||||
print ' '
|
||||
|
||||
stop
|
||||
|
||||
# ======================================================
|
||||
|
||||
def init_PV():
|
||||
|
||||
|
||||
#
|
||||
# routine initialzed all EPIC channels used in the Phoenix beamline
|
||||
#
|
||||
|
||||
global PV
|
||||
PV = {} # define EPICS CHANNEL (PV) directory
|
||||
|
||||
# define one channel
|
||||
|
||||
try:
|
||||
PV['X07MB-OP-MO:TC1'] = epicsPV('X07MB-OP-MO:TC1')
|
||||
except:
|
||||
print "*** Error finding EPICS channel: "
|
||||
sys.exit("*** Program STOP ***")
|
||||
print 'init PV done'
|
||||
|
||||
|
||||
def get_epicsPV(channel):
|
||||
|
||||
|
||||
b=-1
|
||||
# first check fro channel existence
|
||||
#print '.................................. enter get_epicsPV from X_X07MB_lib_dev.py' ,channel
|
||||
#print('try ',channel)
|
||||
try:
|
||||
#print ' try reading channel X_X07MB_lib_dev.py' ,channel
|
||||
b=PV[channel].getw()
|
||||
except:
|
||||
# channel not defined / found now created in PV list this channel into PV LIST
|
||||
print 'trouble reading epics PV..',channel,'likely PV undefined'
|
||||
n_tries=0
|
||||
tt=0
|
||||
while tt==0:
|
||||
print 'in while loop '
|
||||
try:
|
||||
print 'try============= ',channel
|
||||
PV[channel]=epicsPV(channel)
|
||||
#print PV
|
||||
tt=1
|
||||
print PV[channel].getw()
|
||||
except:
|
||||
#print 'set tt back to zero as something went wrong here'
|
||||
tt=0
|
||||
#print 'except somthing is wrong here, likely time out, now retry channels'
|
||||
#print b
|
||||
n_tries=n_tries+1
|
||||
time.sleep(.3)
|
||||
print 'next try',n_tries
|
||||
if n_tries==25:
|
||||
tt=1
|
||||
print 'too many tries for channel acces'
|
||||
print 'Channel',channel
|
||||
sys.exit("*** Program STOP ***")
|
||||
# endif
|
||||
|
||||
# endtry
|
||||
# now try again and set monitor to reduce chanel accesscalls
|
||||
|
||||
PV[channel].setMonitor()
|
||||
b=PV[channel].getw()
|
||||
# end while
|
||||
# endtry
|
||||
|
||||
return b
|
||||
|
||||
|
||||
|
||||
def put_epicsPV(channel,val,delay={}):
|
||||
|
||||
print(channel,val,delay)
|
||||
|
||||
#try:
|
||||
# from CaChannel import *
|
||||
# from epicsMotor import *
|
||||
# from epicsPV import *
|
||||
# import time
|
||||
# import string
|
||||
#except:
|
||||
# try:
|
||||
# sys.path.insert(0, os.path.expandvars("$SLSBASE/sls/lib/python22/CaChannel"))
|
||||
# sys.path.insert(0, os.path.expandvars("/exchange/share/mXAS/pyth/mod"))
|
||||
# from CaChannel import *
|
||||
# from epicsPV import *
|
||||
# except:
|
||||
# os.system ("xkbbell")
|
||||
# os.system ("xmessage -nearmouse -timeout 30 \
|
||||
# -buttons \"\" \"epicsPV or CaChannel module cannot be found\"")
|
||||
# sys.exit(1)
|
||||
# # endtry
|
||||
|
||||
# endtry
|
||||
|
||||
#print '------------------------------------------' , val
|
||||
#print ' *delay) ', delay
|
||||
#print ' *delay) '
|
||||
|
||||
#print ' locals',locals()
|
||||
if delay == {}:
|
||||
#print 'in if use predefined delay of 0 '
|
||||
set_delay=0.0
|
||||
else:
|
||||
#print ' set delay to input value',delay
|
||||
set_delay = delay
|
||||
#endelse
|
||||
|
||||
|
||||
|
||||
n_tries=0
|
||||
tt=0
|
||||
b=-1
|
||||
while tt==0:
|
||||
try:
|
||||
#print ' try writing into ' ,channel
|
||||
epicsPV(channel).putw(val)
|
||||
tt=1
|
||||
except:
|
||||
print 'trouble reading epics PV..'
|
||||
print b
|
||||
print b
|
||||
n_tries=n_tries+1
|
||||
tt=0
|
||||
time.sleep(.5)
|
||||
if n_tries==50:
|
||||
tt=1
|
||||
print 'too many tries for channel acces'
|
||||
#sys.exit("*** Program STOP ***")
|
||||
# endtry
|
||||
# finally perform delay
|
||||
time.sleep(set_delay)
|
||||
return b
|
||||
|
||||
# ======================================================
|
||||
|
||||
|
||||
|
||||
def move_and_wait_bk(Ch_motor,value,backlash):
|
||||
global phoenix_no_move
|
||||
global phoenix_no_scan
|
||||
# print ch_motor
|
||||
if phoenix_no_move==1:
|
||||
print ' '
|
||||
print 'in move_and_wait_bk '
|
||||
print 'should move motor ',Ch_motor,' to ', value
|
||||
print 'BL in no move debugging state. DO NOT MOVE MOTOR'
|
||||
print ' '
|
||||
return
|
||||
|
||||
# move to new position with backlash correction
|
||||
move_and_wait(Ch_motor,value-backlash)
|
||||
move_and_wait(Ch_motor,value)
|
||||
|
||||
# ======================================================
|
||||
|
||||
def move_and_wait_old(Ch_motor,value,check_moving):
|
||||
global phoenix_no_move
|
||||
global phoenix_no_scan
|
||||
# print 'in move and waqit',phoenix_no_move
|
||||
# print Ch_motor
|
||||
|
||||
if phoenix_no_move==1:
|
||||
print ' '
|
||||
print 'in move_and_wait '
|
||||
print 'should move motor ',Ch_motor,' to ', value
|
||||
print 'BL in no move debugging state. DO NOT MOVE MOTOR'
|
||||
print ' '
|
||||
return
|
||||
|
||||
Ch_motor_use=Ch_motor
|
||||
|
||||
if (Ch_motor[len(Ch_motor)-4:len(Ch_motor)] == '.VAL'):
|
||||
Ch_motor_use=Ch_motor[0:len(Ch_motor)-4]
|
||||
# print Ch_motor_use
|
||||
|
||||
if Ch_motor_use[0:5] == 'X07MA':
|
||||
stop_if_phoenix_is_offline()
|
||||
|
||||
if Ch_motor_use[0:11] == 'X07MB-OP-MI1':
|
||||
stop_if_phoenix_is_offline()
|
||||
|
||||
|
||||
Ch_motor_val = Ch_motor_use +'.VAL'
|
||||
Ch_motor_done = Ch_motor_use +'.DMOV'
|
||||
#print Ch_motor_val
|
||||
|
||||
epicsPV(Ch_motor_val).putw(value)
|
||||
|
||||
print 'wait for movement of', Ch_motor_val
|
||||
while not epicsPV(Ch_motor_done).getw() :
|
||||
time.sleep(0.1)
|
||||
get_epicsPV(Ch_motor_val)
|
||||
|
||||
#print 'Motor ',Ch_motor_val,' still moving, wait '
|
||||
print 'movement finished'
|
||||
|
||||
# ===========================================================
|
||||
|
||||
def move_and_wait(motor,value,check_moving=0):
|
||||
global phoenix_no_move
|
||||
global phoenix_no_scan
|
||||
# print 'in move and waqit',phoenix_no_move
|
||||
# print Ch_motor
|
||||
|
||||
if phoenix_no_move==1:
|
||||
print ' '
|
||||
print 'in move_and_wait '
|
||||
print 'should move motor ',Ch_motor,' to ', value
|
||||
print 'BL in no move debugging state. DO NOT MOVE MOTOR'
|
||||
print ' '
|
||||
return
|
||||
# some safety checks...
|
||||
|
||||
if motor.val[0:5] == 'X07MA':
|
||||
stop_if_phoenix_is_offline()
|
||||
|
||||
if motor.val[0:11] == 'X07MB-OP-MI1':
|
||||
stop_if_phoenix_is_offline()
|
||||
|
||||
put_epicsPV(motor.val,value,delay=0.2)
|
||||
|
||||
print 'wait for movement of', motor.val
|
||||
|
||||
if check_moving == 0:
|
||||
while not get_epicsPV(motor.dmov):
|
||||
time.sleep(0.1)
|
||||
print get_epicsPV(motor.val)
|
||||
print 'Motor ',motor.val,' still moving, wait '
|
||||
#endwhile
|
||||
else: # case with check of movement of encoders
|
||||
print 'else....'
|
||||
while not get_epicsPV(motor.dmov):
|
||||
print ' in while '
|
||||
enc1= get_epicsPV(motor.rep)
|
||||
time.sleep(0.25)
|
||||
enc2= get_epicsPV(motor.rep)
|
||||
diff=enc2-enc1
|
||||
print get_epicsPV(motor.val)
|
||||
if (abs(diff) < 5 and (get_epicsPV(motor.dmov) == 0)) :
|
||||
print 'suspect hanging of motor. monitor'
|
||||
enc1= get_epicsPV(motor.rep)
|
||||
time.sleep(0.5)
|
||||
enc2= get_epicsPV(motor.rep)
|
||||
diff=enc2-enc1
|
||||
time.sleep(0.5)
|
||||
if ((abs(diff) < 3) and (get_epicsPV(motor.dmov) == 0)):
|
||||
print ' ===================================== diff',diff
|
||||
print 'difference encoder to motor too large'
|
||||
print ' STOP movement'
|
||||
put_epicsPV(motor.spmg,0,delay=0.1)
|
||||
stop
|
||||
# endif
|
||||
#endif
|
||||
print 'Motor ',motor.val,' still moving, wait '
|
||||
print 'difference subsequent encoder positions ',diff
|
||||
# endwhile
|
||||
# endif
|
||||
print 'movement finished'
|
||||
|
||||
# =======================================================================
|
||||
|
||||
def Wait_for_Value(Channel,value):
|
||||
|
||||
while get_epicsPV(Channel) <> value:
|
||||
#print(Channel)
|
||||
#print get_epicsPV(Channel)
|
||||
time.sleep(.02)
|
||||
#endwhile
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def move_and_wait_2motors(mot1,mot2,val_1,val_2,check_moving=0):
|
||||
global phoenix_no_move
|
||||
global phoenix_no_scan
|
||||
# print 'in move and waqit',phoenix_no_move
|
||||
# print Ch_motor
|
||||
|
||||
if phoenix_no_move==1:
|
||||
print ' '
|
||||
print 'in move_and_wait '
|
||||
print 'should move motor ',Ch_motor,' to ', value
|
||||
print 'BL in no move debugging state. DO NOT MOVE MOTOR'
|
||||
print ' '
|
||||
return
|
||||
|
||||
# some safety checks...
|
||||
|
||||
if mot1.val[0:5] == 'X07MA':
|
||||
stop_if_phoenix_is_offline()
|
||||
|
||||
if mot2.val[0:5] == 'X07MA':
|
||||
stop_if_phoenix_is_offline()
|
||||
|
||||
if mot1.val[0:11] == 'X07MB-OP-MI1':
|
||||
stop_if_phoenix_is_offline()
|
||||
|
||||
if mot2.val[0:11] == 'X07MB-OP-MI1':
|
||||
stop_if_phoenix_is_offline()
|
||||
|
||||
put_epicsPV(mot1.val,val_1,delay=0.2)
|
||||
put_epicsPV(mot2.val,val_2,delay=0.2)
|
||||
|
||||
print 'wait for movement of', mot1.val,'and ', mot2.val
|
||||
|
||||
if check_moving == 0:
|
||||
print (not get_epicsPV(mot1.dmov))
|
||||
print (not get_epicsPV(mot2.dmov))
|
||||
|
||||
while ((not get_epicsPV(mot1.dmov)) or (not get_epicsPV(mot2.dmov))):
|
||||
time.sleep(0.25)
|
||||
#endwhile
|
||||
print get_epicsPV(mot1.val),get_epicsPV(mot2.val)
|
||||
print 'Motor ',mot1.val,mot2.val,' still moving, wait '
|
||||
|
||||
else: # case with check of movement of encoders
|
||||
print 'else....'
|
||||
while (not get_epicsPV(mot1.dmov)) or (not get_epicsPV(mot2.dmov)):
|
||||
#print ' in while '
|
||||
# for motor 1
|
||||
enc_1_a= get_epicsPV(mot1.rep)
|
||||
enc_2_a= get_epicsPV(mot2.rep)
|
||||
time.sleep(0.25)
|
||||
enc_1_b= get_epicsPV(mot1.rep)
|
||||
enc_2_b= get_epicsPV(mot2.rep)
|
||||
|
||||
diff_1=enc_1_b-enc_1_a
|
||||
diff_2=enc_2_b-enc_2_a
|
||||
#print ' encoder diffs',diff_1,diff_2
|
||||
|
||||
# check encoder movement for motor 1:
|
||||
if (abs(diff_1) < 5 and (get_epicsPV(mot1.dmov) == 0)) :
|
||||
print 'suspect hanging of motor 1 monitor'
|
||||
enc_1_a= get_epicsPV(mot1.rep)
|
||||
time.sleep(0.25)
|
||||
enc_1_b= get_epicsPV(mot1.rep)
|
||||
diff_1=enc_1_b-enc_1_a
|
||||
time.sleep(0.5)
|
||||
if ((abs(diff_1) < 3) and (get_epicsPV(mot1.dmov) == 0)):
|
||||
print ' ===================================== diff',diff_1
|
||||
print 'difference subsequent encoder steps too small'
|
||||
print 'Motor 1 hanging???? STOP movement for safety '
|
||||
put_epicsPV(mot_1.spmg,0,delay=0.1)
|
||||
stop
|
||||
# endif
|
||||
#endif
|
||||
|
||||
# Now checlk motor 2
|
||||
if (abs(diff_2) < 5 and (get_epicsPV(mot2.dmov) == 0)) :
|
||||
print 'suspect hanging of motor 2: monitor'
|
||||
enc_2_a= get_epicsPV(mot2.rep)
|
||||
time.sleep(0.25)
|
||||
enc_2_b= get_epicsPV(mot2.rep)
|
||||
diff_2=enc_2_b-enc_2_a
|
||||
time.sleep(0.5)
|
||||
if ((abs(diff_2) < 3) and (get_epicsPV(mot2.dmov) == 0)):
|
||||
print ' ===================================== diff',diff_2
|
||||
print 'difference subsequent encoder steps too small'
|
||||
print 'Motor 2 hanging???? STOP movement for safety '
|
||||
put_epicsPV(mot2.spmg,0,delay=0.1)
|
||||
stop
|
||||
# endif
|
||||
#endif
|
||||
print 'Motors ',mot1.val, 'and',mot2.val,' still moving, wait '
|
||||
print 'difference subsequent encoder positions ',diff_1, diff_2
|
||||
# endwhile
|
||||
# endif
|
||||
print 'movement finished'
|
||||
|
||||
# =======================================================================
|
||||
|
||||
# Generate all names
|
||||
class Motor():
|
||||
#define extensions for all channels
|
||||
e_val = '.VAL'
|
||||
e_dval = '.DVAL'
|
||||
e_rbv = '.RBV'
|
||||
e_drbv = '.DRBV'
|
||||
e_off ='.OFF'
|
||||
e_twv = '.TWV'
|
||||
e_twf = '.TWF'
|
||||
e_twr = '.TWR'
|
||||
|
||||
e_jogr ='.JOGR' # jog rev
|
||||
e_jogf ='.JOGF' # jog forw
|
||||
e_jomf ='.HOMF' # home forward
|
||||
e_jomr ='.HOMR' # home reverse
|
||||
e_hlm ='.HLM' #High limit
|
||||
e_llm ='.LLM' #lower limit
|
||||
|
||||
e_hls ='.HLS' #High limit
|
||||
e_lls ='.LLS' #lower limit
|
||||
|
||||
e_dmov ='.DMOV' # moving done
|
||||
e_movn ='.MOVN' # moving
|
||||
e_spmg ='.SPMG' # stop pause move go (0,1,2,3)
|
||||
e_ueip ='.UEIP' # encoder on / off
|
||||
e_diff ='.DIFF'
|
||||
e_rep = '.REP'
|
||||
e_foff ='.FOFF' #FOFF (1 = Frozen / 0 = variable)
|
||||
e_set = '.SET' # 0 = use 1 = set
|
||||
e_mres = '.MRES'
|
||||
e_eres = '.ERES'
|
||||
|
||||
def create_Ch(self,ch):
|
||||
# this routine creates all channel names
|
||||
self.val=ch+self.e_val
|
||||
self.dval=ch+self.e_dval
|
||||
self.rbv=ch+self.e_rbv
|
||||
self.drbv=ch+self.e_drbv
|
||||
self.off=ch+self.e_off
|
||||
self.twv=ch+self.e_twv
|
||||
self.twf=ch+self.e_twf
|
||||
self.twr=ch+self.e_twr
|
||||
self.jogr=ch+self.e_jogr
|
||||
self.jogf=ch+self.e_jogf
|
||||
self.hlm=ch+self.e_hlm
|
||||
self.llm=ch+self.e_llm
|
||||
self.hls=ch+self.e_hls
|
||||
self.lls=ch+self.e_lls
|
||||
|
||||
self.dmov = ch+self.e_dmov
|
||||
self.movn = ch+self.e_movn
|
||||
self.spmg = ch+self.e_spmg
|
||||
self.ueip = ch+self.e_ueip
|
||||
self.rep = ch+self.e_rep
|
||||
self.mres = ch+self.e_mres
|
||||
self.eres = ch+self.e_eres
|
||||
self.diff = ch+self.e_diff
|
||||
self.foff = ch+self.e_foff
|
||||
self.set = ch+self.e_set
|
||||
# end create_Chnames
|
||||
|
||||
|
||||
# endclass
|
||||
def define_motor(ch):
|
||||
g=Motor()
|
||||
g.create_Ch(ch)
|
||||
return g
|
||||
# end define_motor
|
||||
|
||||
def define_motor_kb(ch):
|
||||
g=Motor()
|
||||
g.create_Ch(ch)
|
||||
|
||||
# set offset to variable as default setting
|
||||
# this keeps the dval always constant
|
||||
|
||||
put_epicsPV(g.foff,1,delay=0.2) # offset to frozen as we do not initialize
|
||||
|
||||
put_epicsPV(g.set,0,delay=0.2) # make sure that coos are on use
|
||||
return g
|
||||
|
||||
# end define_motor
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def init_one_motor(init_motor,HHL=0,LHL=0,LIM='low',set_offset='variable'):
|
||||
# initialization for givrn location of limit switch (1 motor)
|
||||
motor=define_motor_kb(init_motor)
|
||||
total_range = abs(HHL-LHL)
|
||||
|
||||
# general settings
|
||||
if set_offset == 'frozen':
|
||||
put_epicsPV(motor.foff,1,delay=0.2) # offset to frozen as we do not initialize
|
||||
else:
|
||||
put_epicsPV(motor.foff,0,delay=0.2) # offset to variable if needed
|
||||
#endif
|
||||
put_epicsPV(motor.twv,0.1,delay=0.2) # set tweak value to 100 mum as default
|
||||
|
||||
put_epicsPV(motor.ueip , 0 ,delay=.5) # switch encoder off
|
||||
|
||||
# first remember start value
|
||||
x_ini=get_epicsPV(motor.val)
|
||||
print x_ini
|
||||
|
||||
# now remove soft limits allow start value plus total range as maximum
|
||||
put_epicsPV(motor.hlm ,x_ini+ total_range*1.15 ,delay=.5) # switch encoder off
|
||||
put_epicsPV(motor.llm ,x_ini-total_range*1.15 ,delay=.5) # switch encoder off
|
||||
|
||||
# now move to lower limit
|
||||
|
||||
if LIM == 'low':
|
||||
move_and_wait(motor ,x_ini-total_range*1.07,check_moving=1)
|
||||
# now check whether we are in the limit
|
||||
if get_epicsPV(motor.lls) == 0:
|
||||
print(' hard limit not reached',motor.val)
|
||||
stop
|
||||
else:
|
||||
print(' hard limit reached',motor.val)
|
||||
#endelse
|
||||
# now set coordinate system according to limits
|
||||
|
||||
# endcoder on / off twice to account for bug in motor record
|
||||
put_epicsPV(motor.ueip ,1,delay=.5) # encoder on
|
||||
put_epicsPV(motor.ueip ,0,delay=.5) # encoder on
|
||||
put_epicsPV(motor.ueip ,1,delay=.5) # encoder on
|
||||
|
||||
# widen soft limits temporally around aimed position
|
||||
# in case the correct value is outside the current soft limits
|
||||
|
||||
put_epicsPV(motor.llm , LHL-10000 , delay=1) # st lower soft limit according to table
|
||||
put_epicsPV(motor.hlm , LHL+10000 , delay=1) # st higher soft limit according to table
|
||||
|
||||
put_epicsPV(motor.set ,1,delay=1) # now allow change of coo syste cal to set m
|
||||
put_epicsPV(motor.val ,LHL,delay=1) # set current position to lower limit
|
||||
|
||||
put_epicsPV(motor.set ,0,delay=0.5) # set co system to use
|
||||
|
||||
# finally drive motor out of hard limits and set the correct soft limits
|
||||
|
||||
put_epicsPV(motor.twv,0.1,delay=1.) # set tweak value to 100 mum as default
|
||||
put_epicsPV(motor.twf ,1,delay=2.) # tweak out of hard limit
|
||||
put_epicsPV(motor.llm , LHL+0.05 , delay=0.5) # st lower soft limit according to table
|
||||
put_epicsPV(motor.hlm , HHL-0.05 , delay=0.5) # st higher soft limit according to table
|
||||
|
||||
#endif
|
||||
|
||||
if LIM == 'high':
|
||||
move_and_wait(motor ,x_ini+total_range*1.07,check_moving=1) # move to high limits
|
||||
# now check whether we are in the limit
|
||||
if get_epicsPV(motor.hls) == 0:
|
||||
print(' hard limit not reached',motor.val)
|
||||
stop
|
||||
else:
|
||||
print(' hard limit reached',motor.val)
|
||||
#endelse
|
||||
# now set coordinate system according to limits
|
||||
|
||||
put_epicsPV(motor.ueip ,1,delay=.5) # encoder on
|
||||
|
||||
# widen soft limits temporally around aimed position
|
||||
# in case the correct value is outside the current soft limits
|
||||
|
||||
put_epicsPV(motor.llm , LHL-10000 , delay=1) # st lower soft limit according to table
|
||||
put_epicsPV(motor.hlm , LHL+10000 , delay=1) # st higher soft limit according to table
|
||||
|
||||
|
||||
put_epicsPV(motor.set ,1,delay=0.5) # now allow change of coo syste cal to set m
|
||||
put_epicsPV(motor.val ,HHL,delay=0.5) # set current position to lower limit
|
||||
put_epicsPV(motor.set ,0,delay=0.5) # set coo system to use
|
||||
|
||||
# finally set the soft limits
|
||||
put_epicsPV(motor.twv,0.1,delay=0.5) # set tweak value to 100 mum as default
|
||||
put_epicsPV(motor.twr ,1,delay=2.) # tweak out of hard limit to lower values
|
||||
put_epicsPV(motor.llm , LHL+0.05 , delay=0.2) # st lower soft limit according to table
|
||||
put_epicsPV(motor.hlm , HHL-0.05 , delay=0.2) # st higher soft limit according to table
|
||||
#endif
|
||||
#end
|
||||
|
||||
|
||||
def init_two_motors(mot1=' ',mot2=' ',HHL_1=0,LHL_1=0,HHL_2=0,LHL_2=0,LIM='low',set_offset='variable'):
|
||||
# initialization for givrn location of limit switch
|
||||
# initializes 2 motors with simultaneous movemnets into the same direction
|
||||
#
|
||||
motor_1=define_motor_kb(mot1)
|
||||
motor_2=define_motor_kb(mot2)
|
||||
total_range_1 = abs(HHL_1-LHL_1)
|
||||
total_range_2 = abs(HHL_2-LHL_2)
|
||||
|
||||
# general settings
|
||||
if set_offset == 'frozen':
|
||||
put_epicsPV(motor_1.foff,1,delay=0.2) # offset to frozen as we do not initialize
|
||||
put_epicsPV(motor_2.foff,1,delay=0.2) # offset to frozen as we do not initialize
|
||||
else:
|
||||
put_epicsPV(motor_1.foff,0,delay=0.2) # offset to variable if needed
|
||||
put_epicsPV(motor_2.foff,0,delay=0.2) # offset to variable if needed
|
||||
#endif
|
||||
put_epicsPV(motor_1.twv,0.1,delay=0.2) # set tweak value to 100 mum as default
|
||||
put_epicsPV(motor_2.twv,0.1,delay=0.2) # set tweak value to 100 mum as default
|
||||
put_epicsPV(motor_1.ueip , 0 ,delay=.5) # switch encoder off
|
||||
put_epicsPV(motor_2.ueip , 0 ,delay=.5) # switch encoder off
|
||||
|
||||
# first remember start values
|
||||
x_ini_1=get_epicsPV(motor_1.val)
|
||||
x_ini_2=get_epicsPV(motor_2.val)
|
||||
|
||||
print x_ini_1,x_ini_2
|
||||
|
||||
# now remove soft limits allow start value plus total range as maximum
|
||||
|
||||
put_epicsPV(motor_1.hlm ,x_ini_1 + total_range_1*1.15 ,delay=.5) # switch encoder off
|
||||
put_epicsPV(motor_1.llm ,x_ini_1 - total_range_1*1.15 ,delay=.5) # switch encoder off
|
||||
put_epicsPV(motor_2.hlm ,x_ini_2 + total_range_2*1.15 ,delay=.5) # switch encoder off
|
||||
put_epicsPV(motor_2.llm ,x_ini_2 - total_range_2*1.15 ,delay=.5) # switch encoder off
|
||||
|
||||
# now move to lower limit
|
||||
|
||||
if LIM == 'low':
|
||||
move_and_wait_2motors(motor_1,motor_2 ,x_ini_1-total_range_1*1.07,x_ini_2-total_range_2*1.07,check_moving=1)
|
||||
|
||||
# now check whether we are in the limit
|
||||
if ((get_epicsPV(motor_1.lls) == 0 ) or (get_epicsPV(motor_2.lls) == 0 )) :
|
||||
print(' hard limit not reached',motor_1.val)
|
||||
print(' hard limit not reached',motor_2.val)
|
||||
stop
|
||||
else:
|
||||
print(' hard limit reached',motor_1.val)
|
||||
print(' hard limit reached',motor_2.val)
|
||||
#endelse
|
||||
|
||||
# Now both motors are in the hard limit
|
||||
# now set coordinate system according to limits
|
||||
|
||||
# endcoder on / off twice to account for bug in motor record
|
||||
put_epicsPV(motor_1.ueip ,1,delay=.5) # encoder on
|
||||
put_epicsPV(motor_1.ueip ,0,delay=.5) # encoder on
|
||||
put_epicsPV(motor_1.ueip ,1,delay=.5) # encoder on
|
||||
|
||||
put_epicsPV(motor_2.ueip ,1,delay=.5) # encoder on
|
||||
put_epicsPV(motor_2.ueip ,0,delay=.5) # encoder on
|
||||
put_epicsPV(motor_2.ueip ,1,delay=.5) # encoder on
|
||||
|
||||
# widen soft limits temporally around aimed position
|
||||
# in case the correct value is outside the current soft limits
|
||||
|
||||
put_epicsPV(motor_1.llm , LHL_1-10000 , delay=1) # st lower soft limit according to table
|
||||
put_epicsPV(motor_1.hlm , LHL_1+10000 , delay=1) # st higher soft limit according to table
|
||||
|
||||
|
||||
put_epicsPV(motor_2.llm , LHL_2-10000 , delay=1) # st lower soft limit according to table
|
||||
put_epicsPV(motor_2.hlm , LHL_2+10000 , delay=1) # st higher soft limit according to table
|
||||
|
||||
|
||||
|
||||
put_epicsPV(motor_1.set ,1,delay=1) # now allow change of coo syste cal to set m
|
||||
put_epicsPV(motor_2.set ,1,delay=1) # now allow change of coo syste cal to set m
|
||||
|
||||
put_epicsPV(motor_1.val ,LHL_1,delay=1) # set current position to lower limit
|
||||
put_epicsPV(motor_2.val ,LHL_2,delay=1) # set current position to lower limit
|
||||
|
||||
put_epicsPV(motor_1.set ,0,delay=0.5) # set co system to use
|
||||
put_epicsPV(motor_2.set ,0,delay=0.5) # set co system to use
|
||||
|
||||
# finally drive motor out of hard limits and set the correct soft limits
|
||||
|
||||
put_epicsPV(motor_1.twv,0.1,delay=1.) # set tweak value to 100 mum as default
|
||||
put_epicsPV(motor_1.twf ,1,delay=2.) # tweak out of hard limit
|
||||
put_epicsPV(motor_1.llm , LHL_1+0.05 , delay=0.5) # st lower soft limit according to table
|
||||
put_epicsPV(motor_1.hlm , HHL_1-0.05 , delay=0.5) # st higher soft limit according to table
|
||||
|
||||
put_epicsPV(motor_2.twv,0.1,delay=1.) # set tweak value to 100 mum as default
|
||||
put_epicsPV(motor_2.twf ,1,delay=2.) # tweak out of hard limit
|
||||
put_epicsPV(motor_2.llm , LHL_2+0.05 , delay=0.5) # st lower soft limit according to table
|
||||
put_epicsPV(motor_2.hlm , HHL_2-0.05 , delay=0.5) # st higher soft limit according to table
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
if LIM == 'high':
|
||||
print ' not implemented yet'
|
||||
stop
|
||||
move_and_wait(motor ,x_ini+total_range*1.07,check_moving=1) # move to high limits
|
||||
# now check whether we are in the limit
|
||||
if get_epicsPV(motor.hls) == 0:
|
||||
print(' hard limit not reached',motor.val)
|
||||
stop
|
||||
else:
|
||||
print(' hard limit reached',motor.val)
|
||||
#endelse
|
||||
# now set coordinate system according to limits
|
||||
|
||||
put_epicsPV(motor.ueip ,1,delay=.5) # encoder on
|
||||
|
||||
# widen soft limits temporally around aimed position
|
||||
# in case the correct value is outside the current soft limits
|
||||
|
||||
put_epicsPV(motor.llm , LHL-10000 , delay=1) # st lower soft limit according to table
|
||||
put_epicsPV(motor.hlm , LHL+10000 , delay=1) # st higher soft limit according to table
|
||||
|
||||
|
||||
put_epicsPV(motor.set ,1,delay=0.5) # now allow change of coo syste cal to set m
|
||||
put_epicsPV(motor.val ,HHL,delay=0.5) # set current position to lower limit
|
||||
put_epicsPV(motor.set ,0,delay=0.5) # set coo system to use
|
||||
|
||||
# finally set the soft limits
|
||||
put_epicsPV(motor.twv,0.1,delay=0.5) # set tweak value to 100 mum as default
|
||||
put_epicsPV(motor.twr ,1,delay=2.) # tweak out of hard limit to lower values
|
||||
put_epicsPV(motor.llm , LHL+0.05 , delay=0.2) # st lower soft limit according to table
|
||||
put_epicsPV(motor.hlm , HHL-0.05 , delay=0.2) # st higher soft limit according to table
|
||||
#endif
|
||||
#end
|
||||
|
||||
def Next_Point():
|
||||
sp1=np.zeros(2048)
|
||||
sp2=np.zeros(2048)
|
||||
sp3=np.zeros(2048)
|
||||
sp4=np.zeros(2048)
|
||||
|
||||
d1 = 0
|
||||
d2 = 0
|
||||
d3 = 0
|
||||
d4 = 0
|
||||
|
||||
#print('erasestart')
|
||||
put_epicsPV('X07MB-XMAP:EraseStart',1,delay=.03)
|
||||
|
||||
#print('erasestart')
|
||||
#time.sleep(.1)
|
||||
|
||||
put_epicsPV('X07MB-OP2:SMPL',1,delay=.03)
|
||||
|
||||
Wait_for_Value("X07MB-OP2:SMPL-DONE",1)
|
||||
|
||||
put_epicsPV('X07MB-XMAP:StopAll',1,delay=.03)
|
||||
|
||||
sp1_old=sp1
|
||||
sp2_old=sp2
|
||||
sp3_old=sp3
|
||||
sp4_old=sp4
|
||||
|
||||
d1_old=d1
|
||||
d2_old=d2
|
||||
d3_old=d3
|
||||
d4_old=d4
|
||||
|
||||
sp1=get_epicsPV('X07MB-XMAP:mca1.VAL')
|
||||
sp2=get_epicsPV('X07MB-XMAP:mca2.VAL')
|
||||
sp3=get_epicsPV('X07MB-XMAP:mca3.VAL')
|
||||
sp4=get_epicsPV('X07MB-XMAP:mca4.VAL')
|
||||
|
||||
d1=get_epicsPV('X07MB-XMAP:mca1.DTIM')
|
||||
d2=get_epicsPV('X07MB-XMAP:mca2.DTIM')
|
||||
d3=get_epicsPV('X07MB-XMAP:mca3.DTIM')
|
||||
d4=get_epicsPV('X07MB-XMAP:mca4.DTIM')
|
||||
|
||||
print('differences',max(sp1-sp1_old),max(sp2-sp2_old),max(sp3-sp3_old),max(sp4-sp4_old))
|
||||
print('differences',d1-d1_old,d2-d2_old,d3-d3_old,d4-d4_old)
|
||||
|
||||
time.sleep(.5)
|
||||
# end
|
||||
init_PV()
|
||||
N=10000000
|
||||
for i in range(N):
|
||||
Next_Point()
|
||||
#endwhile
|
@ -0,0 +1,33 @@
|
||||
import time
|
||||
import MyLib as ML
|
||||
channels=['X07MB-MA:ScanX.RBV']
|
||||
import epics as ep
|
||||
|
||||
def init_PV(channels):
|
||||
PV = []
|
||||
|
||||
for i in range(len(channels)):
|
||||
print('init',i,)
|
||||
PV.append(ep.camonitor(channels[i]))
|
||||
#endfor
|
||||
return PV
|
||||
#end
|
||||
|
||||
def read_all(channels):
|
||||
res=[]
|
||||
for i in range(len(channels)):
|
||||
print('read',i)
|
||||
res.append(PV[i].get())
|
||||
#endfor
|
||||
#end
|
||||
|
||||
PV=init_PV(channels)
|
||||
n_ch=len(channels)
|
||||
print(PV)
|
||||
asdasd
|
||||
|
||||
for i in range(n_ch):
|
||||
print(i)
|
||||
w=read_all(channels)
|
||||
print(w)
|
||||
#endfor
|
@ -0,0 +1 @@
|
||||
camon X07MB-ES-MA1:ScanX.RBV
|
@ -0,0 +1 @@
|
||||
camon X07MB-ES1-MA1:ScanX.RBV
|
85
phoenix_bec/local_scripts/PhoenixTemplate.py
Normal file
85
phoenix_bec/local_scripts/PhoenixTemplate.py
Normal file
@ -0,0 +1,85 @@
|
||||
"""
|
||||
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
|
||||
|
||||
|
||||
#
|
||||
phoenix.add_phoenix_config()
|
||||
# bec.config.update_session_with_file('./ConfigPHOENIX/device_config/phoenix_devices.yaml')
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
s1 = scans.line_scan(dev.ScanX, 0, 0.1, steps=4, exp_time=0.2, relative=False, delay=2)
|
||||
|
||||
s2 = scans.phoenix_line_scan(dev.ScanX, 0, 0.1, steps=4, exp_time=0.2, relative=False, delay=2)
|
||||
|
||||
res1 = s1.scan.to_pandas()
|
||||
re1 = res1.to_numpy()
|
||||
w1 = PH.PhGroup("Bec Linescan")
|
||||
w1.linescan2group(s1)
|
||||
|
||||
print("res1")
|
||||
print(res1)
|
||||
print("as numpy")
|
||||
print("re1")
|
||||
|
||||
|
||||
res2 = s2.scan.to_pandas()
|
||||
re2 = res2.to_numpy()
|
||||
w2 = PH.PhGroup("PHOENIX Linescan")
|
||||
w2.linescan2group(s2)
|
||||
|
||||
print("res2")
|
||||
print(res2)
|
||||
print("as numpy")
|
||||
print("re2")
|
||||
|
||||
|
||||
print(s1)
|
||||
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)
|
||||
|
||||
"""
|
9
phoenix_bec/local_scripts/README.md
Normal file
9
phoenix_bec/local_scripts/README.md
Normal file
@ -0,0 +1,9 @@
|
||||
# purpose of directory
|
||||
|
||||
This diretory is for scripts, test etc. which are not loaded into the bec-server.
|
||||
|
||||
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.
|
||||
|
||||
|
||||
TO avoid poading of these files, there should be no files called __init__.py anywhere in the directory tree
|
8
phoenix_bec/local_scripts/README.md~
Normal file
8
phoenix_bec/local_scripts/README.md~
Normal 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.
|
1
phoenix_bec/local_scripts/Settings/thomas_2.code-profile
Normal file
1
phoenix_bec/local_scripts/Settings/thomas_2.code-profile
Normal file
File diff suppressed because one or more lines are too long
@ -0,0 +1 @@
|
||||
from .phoenix import PhoenixBL
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
@ -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
|
@ -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)
|
@ -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)
|
@ -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)
|
73
phoenix_bec/local_scripts/TEST_scanning/Linescan_1.py
Normal file
73
phoenix_bec/local_scripts/TEST_scanning/Linescan_1.py
Normal file
@ -0,0 +1,73 @@
|
||||
"""
|
||||
test script for linescans
|
||||
"""
|
||||
|
||||
# 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")
|
||||
# create PHOENIX base configuration
|
||||
load_data=True
|
||||
if load_data == True:
|
||||
phoenix.create_base_config()
|
||||
phoenix.add_xmap()
|
||||
#endif
|
||||
bec.queue.request_queue_reset()
|
||||
dev.MA1_ScanX.enabled = True
|
||||
dev.xmap.enabled=True
|
||||
dev.xmap.unstage() # needed in case scan went wrong
|
||||
print("---------------------------------")
|
||||
|
||||
|
||||
# scan will not diode
|
||||
print(" SCAN DO NOT READ DIODE ")
|
||||
dev.SAI_01_MEAN.readout_priority = "monitored" # do not read detector
|
||||
ti = tt.time_ns()
|
||||
|
||||
|
||||
s1 = scans.line_scan(
|
||||
dev.PP2_VO5, 0, 1, steps=4, exp_time=1, relative=False, delay=2)
|
||||
|
||||
|
||||
|
||||
#s1 = scans.line_scan(dev.MA1_ScanX, 0, 0.1, steps=4, exp_time=1, relative=False, delay=2)
|
||||
|
||||
tf = tt.time_ns()
|
||||
dev.PH_TTL.start_csmpl.put(0)
|
||||
print("elapsed time", (tf - ti) / 1e9)
|
||||
# scan will read diode
|
||||
print(" SCAN READ DIODE ")
|
||||
tt.sleep(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("")
|
@ -0,0 +1,72 @@
|
||||
"""
|
||||
test script for linescans
|
||||
"""
|
||||
|
||||
# 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")
|
||||
# create PHOENIX base configuration
|
||||
|
||||
phoenix.create_base_config()
|
||||
dev.MA1_ScanX.enabled = True
|
||||
|
||||
print("---------------------------------")
|
||||
|
||||
# scan will not diode
|
||||
print(" SCAN DO NOT READ DIODE ")
|
||||
dev.SAI_01_MEAN.readout_priority = "baseline" # do not read detector
|
||||
ti = tt.time_ns()
|
||||
|
||||
|
||||
phoenix.run_shell("sh monitor.sh > monitor.out & ")
|
||||
asdhjk
|
||||
s1 = scans.line_scan(dev.MA1_ScanX, 0, 0.002, steps=4, exp_time=1, 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.SAI_01_MEAN.readout_priority = "monitored" # read detector
|
||||
|
||||
s2 = scans.line_scan(dev.MA1_ScanX, 0, 0.002, steps=11, exp_time=0.01, relative=False, delay=2)
|
||||
dev.MA1_ScanX.enabled = False
|
||||
|
||||
|
||||
# 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)
|
513
phoenix_bec/local_scripts/TEST_scanning/MyLogfile.txt
Normal file
513
phoenix_bec/local_scripts/TEST_scanning/MyLogfile.txt
Normal file
@ -0,0 +1,513 @@
|
||||
258.3017885684967 sec Dummy_device Dummy_PSIDetector.__init__
|
||||
258.3085923194885 sec Dummy_device Dummy_PSIDetector._update_scaninfo
|
||||
258.315456867218 sec Dummy_device Dummy_PSIDetector._update_scaninfo .. done
|
||||
258.3181173801422 sec Dummy_device Dummy_PSIDetector._update_filewriter
|
||||
258.3209879398346 sec Dummy_device Dummy_PSIDetector._update_filewriter .. done
|
||||
258.32397651672363 sec Dummy_device Dummy_PSIDetector._init
|
||||
258.3270974159241 sec Dummy_device Dummy_PSIDetector._init ... done
|
||||
258.3299674987793 sec Dummy_device Dummy_PSIDetector._update_service_config
|
||||
258.33247351646423 sec Dummy_device Dummy_PSIDetector._update_scaninfo
|
||||
258.33590173721313 sec Dummy_device Dummy_PSIDetector._update_scaninfo .. done
|
||||
258.3385784626007 sec Dummy_device Dummy_PSIDetector._update_filewriter
|
||||
258.34114718437195 sec Dummy_device Dummy_PSIDetector._update_filewriter .. done
|
||||
258.34365034103394 sec Dummy_device Dummy_PSIDetector._init
|
||||
258.3460626602173 sec Dummy_device Dummy_PSIDetector._init ... done
|
||||
258.348486661911 sec Dummy_device Dummy_PSIDetector.__init__ .. done
|
||||
273.28097581863403 sec Dummy_device Dummy_PSIDetector.__init__
|
||||
273.2839708328247 sec Dummy_device Dummy_PSIDetector._update_scaninfo
|
||||
273.28703570365906 sec Dummy_device Dummy_PSIDetector._update_scaninfo .. done
|
||||
273.28923439979553 sec Dummy_device Dummy_PSIDetector._update_filewriter
|
||||
273.29163694381714 sec Dummy_device Dummy_PSIDetector._update_filewriter .. done
|
||||
273.2945909500122 sec Dummy_device Dummy_PSIDetector._init
|
||||
273.29863691329956 sec Dummy_device Dummy_PSIDetector._init ... done
|
||||
273.3010427951813 sec Dummy_device Dummy_PSIDetector._update_service_config
|
||||
273.3033182621002 sec Dummy_device Dummy_PSIDetector._update_scaninfo
|
||||
273.3065254688263 sec Dummy_device Dummy_PSIDetector._update_scaninfo .. done
|
||||
273.3088366985321 sec Dummy_device Dummy_PSIDetector._update_filewriter
|
||||
273.31172704696655 sec Dummy_device Dummy_PSIDetector._update_filewriter .. done
|
||||
273.31443309783936 sec Dummy_device Dummy_PSIDetector._init
|
||||
273.3175792694092 sec Dummy_device Dummy_PSIDetector._init ... done
|
||||
273.31978011131287 sec Dummy_device Dummy_PSIDetector.__init__ .. done
|
||||
318.5410006046295 sec Dummy_device Dummy_PSIDetector.__init__
|
||||
318.5440480709076 sec Dummy_device Dummy_PSIDetector._update_scaninfo
|
||||
318.5478036403656 sec Dummy_device Dummy_PSIDetector._update_scaninfo .. done
|
||||
318.5504558086395 sec Dummy_device Dummy_PSIDetector._update_filewriter
|
||||
318.5526340007782 sec Dummy_device Dummy_PSIDetector._update_filewriter .. done
|
||||
318.5549247264862 sec Dummy_device Dummy_PSIDetector._init
|
||||
318.5572078227997 sec Dummy_device Dummy_PSIDetector._init ... done
|
||||
318.55954241752625 sec Dummy_device Dummy_PSIDetector._update_service_config
|
||||
318.5614619255066 sec Dummy_device Dummy_PSIDetector._update_scaninfo
|
||||
318.56455636024475 sec Dummy_device Dummy_PSIDetector._update_scaninfo .. done
|
||||
318.56680154800415 sec Dummy_device Dummy_PSIDetector._update_filewriter
|
||||
318.56896591186523 sec Dummy_device Dummy_PSIDetector._update_filewriter .. done
|
||||
318.5713827610016 sec Dummy_device Dummy_PSIDetector._init
|
||||
318.57353615760803 sec Dummy_device Dummy_PSIDetector._init ... done
|
||||
318.5840895175934 sec Dummy_device Dummy_PSIDetector.__init__ .. done
|
||||
319.48665595054626 sec Dummy_device Dummy_PSIDetector.stage
|
||||
319.4943518638611 sec Dummy_device Dummy_PSIDetector.stage done
|
||||
320.14649772644043 sec Dummy_device Dummy_PSIDetector.pre_scan
|
||||
320.1493499279022 sec Dummy_device Dummy_PSIDetector.pre_scan .. done
|
||||
322.0216338634491 sec Dummy_device Dummy_PSIDetector.complete
|
||||
322.02455163002014 sec Dummy_device Dummy_PSIDetector.complete ... done
|
||||
322.0301237106323 sec Dummy_device Dummy_PSIDetector.unstage
|
||||
322.0323655605316 sec Dummy_device Dummy_PSIDetector.check_scan_id
|
||||
322.03556275367737 sec Dummy_device Dummy_PSIDetector.check_scan_id .. done
|
||||
322.0434696674347 sec Dummy_device Dummy_PSIDetector.unstage .. done
|
||||
322.0462124347687 sec Dummy_device Dummy_PSIDetector.check_scan_id
|
||||
322.049613237381 sec Dummy_device Dummy_PSIDetector.check_scan_id .. done
|
||||
325.49065828323364 sec Dummy_device Dummy_PSIDetector.stage
|
||||
325.49778604507446 sec Dummy_device Dummy_PSIDetector.stage done
|
||||
326.2169315814972 sec Dummy_device Dummy_PSIDetector.pre_scan
|
||||
326.2199287414551 sec Dummy_device Dummy_PSIDetector.pre_scan .. done
|
||||
333.2339792251587 sec Dummy_device Dummy_PSIDetector.complete
|
||||
333.2387444972992 sec Dummy_device Dummy_PSIDetector.complete ... done
|
||||
333.2450532913208 sec Dummy_device Dummy_PSIDetector.unstage
|
||||
333.2477023601532 sec Dummy_device Dummy_PSIDetector.check_scan_id
|
||||
333.252215385437 sec Dummy_device Dummy_PSIDetector.check_scan_id .. done
|
||||
333.2547912597656 sec Dummy_device Dummy_PSIDetector.unstage .. done
|
||||
333.25732684135437 sec Dummy_device Dummy_PSIDetector.check_scan_id
|
||||
333.2605242729187 sec Dummy_device Dummy_PSIDetector.check_scan_id .. done
|
||||
383.52448415756226 sec Dummy_device Dummy_PSIDetector.__init__
|
||||
383.5282344818115 sec Dummy_device Dummy_PSIDetector._update_scaninfo
|
||||
383.53171586990356 sec Dummy_device Dummy_PSIDetector._update_scaninfo .. done
|
||||
383.5346601009369 sec Dummy_device Dummy_PSIDetector._update_filewriter
|
||||
383.53747296333313 sec Dummy_device Dummy_PSIDetector._update_filewriter .. done
|
||||
383.5405652523041 sec Dummy_device Dummy_PSIDetector._init
|
||||
383.5436053276062 sec Dummy_device Dummy_PSIDetector._init ... done
|
||||
383.5470070838928 sec Dummy_device Dummy_PSIDetector._update_service_config
|
||||
383.5498764514923 sec Dummy_device Dummy_PSIDetector._update_scaninfo
|
||||
383.553973197937 sec Dummy_device Dummy_PSIDetector._update_scaninfo .. done
|
||||
383.5567262172699 sec Dummy_device Dummy_PSIDetector._update_filewriter
|
||||
383.5595018863678 sec Dummy_device Dummy_PSIDetector._update_filewriter .. done
|
||||
383.5622727870941 sec Dummy_device Dummy_PSIDetector._init
|
||||
383.56509137153625 sec Dummy_device Dummy_PSIDetector._init ... done
|
||||
383.56781673431396 sec Dummy_device Dummy_PSIDetector.__init__ .. done
|
||||
384.4197027683258 sec Dummy_device Dummy_PSIDetector.stage
|
||||
384.4258131980896 sec Dummy_device Dummy_PSIDetector.stage done
|
||||
385.1663706302643 sec Dummy_device Dummy_PSIDetector.pre_scan
|
||||
385.17092657089233 sec Dummy_device Dummy_PSIDetector.pre_scan .. done
|
||||
387.17647194862366 sec Dummy_device Dummy_PSIDetector.complete
|
||||
387.1801919937134 sec Dummy_device Dummy_PSIDetector.complete ... done
|
||||
387.18778562545776 sec Dummy_device Dummy_PSIDetector.unstage
|
||||
387.19075655937195 sec Dummy_device Dummy_PSIDetector.check_scan_id
|
||||
387.1947159767151 sec Dummy_device Dummy_PSIDetector.check_scan_id .. done
|
||||
387.19729018211365 sec Dummy_device Dummy_PSIDetector.unstage .. done
|
||||
387.2002501487732 sec Dummy_device Dummy_PSIDetector.check_scan_id
|
||||
387.2042922973633 sec Dummy_device Dummy_PSIDetector.check_scan_id .. done
|
||||
390.5967915058136 sec Dummy_device Dummy_PSIDetector.stage
|
||||
390.6016550064087 sec Dummy_device Dummy_PSIDetector.stage done
|
||||
391.31412744522095 sec Dummy_device Dummy_PSIDetector.pre_scan
|
||||
391.317245721817 sec Dummy_device Dummy_PSIDetector.pre_scan .. done
|
||||
398.2885682582855 sec Dummy_device Dummy_PSIDetector.complete
|
||||
398.2922840118408 sec Dummy_device Dummy_PSIDetector.complete ... done
|
||||
398.2986834049225 sec Dummy_device Dummy_PSIDetector.unstage
|
||||
398.3014578819275 sec Dummy_device Dummy_PSIDetector.check_scan_id
|
||||
398.305011510849 sec Dummy_device Dummy_PSIDetector.check_scan_id .. done
|
||||
398.30738377571106 sec Dummy_device Dummy_PSIDetector.unstage .. done
|
||||
398.30989623069763 sec Dummy_device Dummy_PSIDetector.check_scan_id
|
||||
398.313679933548 sec Dummy_device Dummy_PSIDetector.check_scan_id .. done
|
||||
55.712119579315186 sec Dummy_device Dummy_PSIDetector.__init__
|
||||
55.71538472175598 sec Dummy_device Dummy_PSIDetector._update_scaninfo
|
||||
55.72820711135864 sec Dummy_device Dummy_PSIDetector._update_scaninfo .. done
|
||||
55.73171591758728 sec Dummy_device Dummy_PSIDetector._update_filewriter
|
||||
55.734644651412964 sec Dummy_device Dummy_PSIDetector._update_filewriter .. done
|
||||
55.73781394958496 sec Dummy_device Dummy_PSIDetector._init
|
||||
55.74082565307617 sec Dummy_device Dummy_PSIDetector._init ... done
|
||||
55.743837118148804 sec Dummy_device Dummy_PSIDetector._update_service_config
|
||||
55.74670958518982 sec Dummy_device Dummy_PSIDetector._update_scaninfo
|
||||
55.75890302658081 sec Dummy_device Dummy_PSIDetector._update_scaninfo .. done
|
||||
55.76187348365784 sec Dummy_device Dummy_PSIDetector._update_filewriter
|
||||
55.76482844352722 sec Dummy_device Dummy_PSIDetector._update_filewriter .. done
|
||||
55.767723083496094 sec Dummy_device Dummy_PSIDetector._init
|
||||
55.77075743675232 sec Dummy_device Dummy_PSIDetector._init ... done
|
||||
55.77365469932556 sec Dummy_device Dummy_PSIDetector.__init__ .. done
|
||||
56.55250263214111 sec Dummy_device Dummy_PSIDetector.stage
|
||||
56.56144142150879 sec Dummy_device Dummy_PSIDetector.stage done
|
||||
57.29970669746399 sec Dummy_device Dummy_PSIDetector.pre_scan
|
||||
57.30306386947632 sec Dummy_device Dummy_PSIDetector.pre_scan .. done
|
||||
59.22956824302673 sec Dummy_device Dummy_PSIDetector.complete
|
||||
59.23271870613098 sec Dummy_device Dummy_PSIDetector.complete ... done
|
||||
59.2375168800354 sec Dummy_device Dummy_PSIDetector.unstage
|
||||
59.23973989486694 sec Dummy_device Dummy_PSIDetector.check_scan_id
|
||||
59.24262094497681 sec Dummy_device Dummy_PSIDetector.check_scan_id .. done
|
||||
59.244933128356934 sec Dummy_device Dummy_PSIDetector.unstage .. done
|
||||
59.24763607978821 sec Dummy_device Dummy_PSIDetector.check_scan_id
|
||||
59.25109386444092 sec Dummy_device Dummy_PSIDetector.check_scan_id .. done
|
||||
62.65221810340881 sec Dummy_device Dummy_PSIDetector.stage
|
||||
62.656172037124634 sec Dummy_device Dummy_PSIDetector.stage done
|
||||
63.31900715827942 sec Dummy_device Dummy_PSIDetector.pre_scan
|
||||
63.32232117652893 sec Dummy_device Dummy_PSIDetector.pre_scan .. done
|
||||
70.31378293037415 sec Dummy_device Dummy_PSIDetector.complete
|
||||
70.31722259521484 sec Dummy_device Dummy_PSIDetector.complete ... done
|
||||
70.32310819625854 sec Dummy_device Dummy_PSIDetector.unstage
|
||||
70.32593488693237 sec Dummy_device Dummy_PSIDetector.check_scan_id
|
||||
70.32954263687134 sec Dummy_device Dummy_PSIDetector.check_scan_id .. done
|
||||
70.3320255279541 sec Dummy_device Dummy_PSIDetector.unstage .. done
|
||||
70.33445334434509 sec Dummy_device Dummy_PSIDetector.check_scan_id
|
||||
70.33776235580444 sec Dummy_device Dummy_PSIDetector.check_scan_id .. done
|
||||
230.2806589603424 sec Dummy_device Dummy_PSIDetector.__init__
|
||||
230.2841832637787 sec Dummy_device Dummy_PSIDetector._update_scaninfo
|
||||
230.28762674331665 sec Dummy_device Dummy_PSIDetector._update_scaninfo .. done
|
||||
230.2903938293457 sec Dummy_device Dummy_PSIDetector._update_filewriter
|
||||
230.29354524612427 sec Dummy_device Dummy_PSIDetector._update_filewriter .. done
|
||||
230.29628920555115 sec Dummy_device Dummy_PSIDetector._init
|
||||
230.2988178730011 sec Dummy_device Dummy_PSIDetector._init ... done
|
||||
230.30137276649475 sec Dummy_device Dummy_PSIDetector._update_service_config
|
||||
230.30426454544067 sec Dummy_device Dummy_PSIDetector._update_scaninfo
|
||||
230.30788803100586 sec Dummy_device Dummy_PSIDetector._update_scaninfo .. done
|
||||
230.3148958683014 sec Dummy_device Dummy_PSIDetector._update_filewriter
|
||||
230.31744003295898 sec Dummy_device Dummy_PSIDetector._update_filewriter .. done
|
||||
230.3200912475586 sec Dummy_device Dummy_PSIDetector._init
|
||||
230.32251811027527 sec Dummy_device Dummy_PSIDetector._init ... done
|
||||
230.3249213695526 sec Dummy_device Dummy_PSIDetector.__init__ .. done
|
||||
95.54364109039307 sec Dummy_device Dummy_PSIDetector.__init__
|
||||
95.55088138580322 sec Dummy_device Dummy_PSIDetector._update_scaninfo
|
||||
95.55566167831421 sec Dummy_device Dummy_PSIDetector._update_scaninfo .. done
|
||||
95.55822157859802 sec Dummy_device Dummy_PSIDetector._update_filewriter
|
||||
95.56095170974731 sec Dummy_device Dummy_PSIDetector._update_filewriter .. done
|
||||
95.56354069709778 sec Dummy_device Dummy_PSIDetector._init
|
||||
95.56620073318481 sec Dummy_device Dummy_PSIDetector._init ... done
|
||||
95.56886649131775 sec Dummy_device Dummy_PSIDetector._update_service_config
|
||||
95.57119488716125 sec Dummy_device Dummy_PSIDetector._update_scaninfo
|
||||
95.57833242416382 sec Dummy_device Dummy_PSIDetector._update_scaninfo .. done
|
||||
95.58116269111633 sec Dummy_device Dummy_PSIDetector._update_filewriter
|
||||
95.58369874954224 sec Dummy_device Dummy_PSIDetector._update_filewriter .. done
|
||||
95.58608078956604 sec Dummy_device Dummy_PSIDetector._init
|
||||
95.58928394317627 sec Dummy_device Dummy_PSIDetector._init ... done
|
||||
95.59169387817383 sec Dummy_device Dummy_PSIDetector.__init__ .. done
|
||||
122.57217597961426 sec Dummy_device Dummy_PSIDetector.__init__
|
||||
122.57641291618347 sec Dummy_device Dummy_PSIDetector._update_scaninfo
|
||||
122.57983541488647 sec Dummy_device Dummy_PSIDetector._update_scaninfo .. done
|
||||
122.58276462554932 sec Dummy_device Dummy_PSIDetector._update_filewriter
|
||||
122.58589124679565 sec Dummy_device Dummy_PSIDetector._update_filewriter .. done
|
||||
122.5887725353241 sec Dummy_device Dummy_PSIDetector._init
|
||||
122.59162330627441 sec Dummy_device Dummy_PSIDetector._init ... done
|
||||
122.59505033493042 sec Dummy_device Dummy_PSIDetector._update_service_config
|
||||
122.60520505905151 sec Dummy_device Dummy_PSIDetector._update_scaninfo
|
||||
122.60943651199341 sec Dummy_device Dummy_PSIDetector._update_scaninfo .. done
|
||||
122.61239290237427 sec Dummy_device Dummy_PSIDetector._update_filewriter
|
||||
122.61533832550049 sec Dummy_device Dummy_PSIDetector._update_filewriter .. done
|
||||
122.61793851852417 sec Dummy_device Dummy_PSIDetector._init
|
||||
122.62070679664612 sec Dummy_device Dummy_PSIDetector._init ... done
|
||||
122.62477135658264 sec Dummy_device Dummy_PSIDetector.__init__ .. done
|
||||
107.0432550907135 sec Dummy_device Dummy_PSIDetector.__init__
|
||||
107.04688167572021 sec Dummy_device Dummy_PSIDetector._update_scaninfo
|
||||
107.05029559135437 sec Dummy_device Dummy_PSIDetector._update_scaninfo .. done
|
||||
107.05297303199768 sec Dummy_device Dummy_PSIDetector._update_filewriter
|
||||
107.05624318122864 sec Dummy_device Dummy_PSIDetector._update_filewriter .. done
|
||||
107.0593614578247 sec Dummy_device Dummy_PSIDetector._init
|
||||
107.06229066848755 sec Dummy_device Dummy_PSIDetector._init ... done
|
||||
107.06521582603455 sec Dummy_device Dummy_PSIDetector._update_service_config
|
||||
107.06809163093567 sec Dummy_device Dummy_PSIDetector._update_scaninfo
|
||||
107.07408380508423 sec Dummy_device Dummy_PSIDetector._update_scaninfo .. done
|
||||
107.07719945907593 sec Dummy_device Dummy_PSIDetector._update_filewriter
|
||||
107.08207726478577 sec Dummy_device Dummy_PSIDetector._update_filewriter .. done
|
||||
107.08525776863098 sec Dummy_device Dummy_PSIDetector._init
|
||||
107.08839154243469 sec Dummy_device Dummy_PSIDetector._init ... done
|
||||
107.091153383255 sec Dummy_device Dummy_PSIDetector.__init__ .. done
|
||||
125.38539528846741 sec Dummy_device Dummy_PSIDetector.__init__
|
||||
125.38865613937378 sec Dummy_device Dummy_PSIDetector._update_scaninfo
|
||||
125.39200568199158 sec Dummy_device Dummy_PSIDetector._update_scaninfo .. done
|
||||
125.39495873451233 sec Dummy_device Dummy_PSIDetector._update_filewriter
|
||||
125.39762306213379 sec Dummy_device Dummy_PSIDetector._update_filewriter .. done
|
||||
125.40058827400208 sec Dummy_device Dummy_PSIDetector._init
|
||||
125.40388655662537 sec Dummy_device Dummy_PSIDetector._init ... done
|
||||
125.40676546096802 sec Dummy_device Dummy_PSIDetector._update_service_config
|
||||
125.40919518470764 sec Dummy_device Dummy_PSIDetector._update_scaninfo
|
||||
125.41260886192322 sec Dummy_device Dummy_PSIDetector._update_scaninfo .. done
|
||||
125.4149432182312 sec Dummy_device Dummy_PSIDetector._update_filewriter
|
||||
125.41725778579712 sec Dummy_device Dummy_PSIDetector._update_filewriter .. done
|
||||
125.41977047920227 sec Dummy_device Dummy_PSIDetector._init
|
||||
125.4225537776947 sec Dummy_device Dummy_PSIDetector._init ... done
|
||||
125.42497134208679 sec Dummy_device Dummy_PSIDetector.__init__ .. done
|
||||
240.22587060928345 sec Dummy_device Dummy_PSIDetector.__init__
|
||||
240.22946453094482 sec Dummy_device Dummy_PSIDetector._update_scaninfo
|
||||
240.23306703567505 sec Dummy_device Dummy_PSIDetector._update_scaninfo .. done
|
||||
240.23666286468506 sec Dummy_device Dummy_PSIDetector._update_filewriter
|
||||
240.24055981636047 sec Dummy_device Dummy_PSIDetector._update_filewriter .. done
|
||||
240.2433557510376 sec Dummy_device Dummy_PSIDetector._init
|
||||
240.24579644203186 sec Dummy_device Dummy_PSIDetector._init ... done
|
||||
240.2483983039856 sec Dummy_device Dummy_PSIDetector._update_service_config
|
||||
240.2509183883667 sec Dummy_device Dummy_PSIDetector._update_scaninfo
|
||||
240.254554271698 sec Dummy_device Dummy_PSIDetector._update_scaninfo .. done
|
||||
240.25772190093994 sec Dummy_device Dummy_PSIDetector._update_filewriter
|
||||
240.26073122024536 sec Dummy_device Dummy_PSIDetector._update_filewriter .. done
|
||||
240.2632761001587 sec Dummy_device Dummy_PSIDetector._init
|
||||
240.26823139190674 sec Dummy_device Dummy_PSIDetector._init ... done
|
||||
240.27151918411255 sec Dummy_device Dummy_PSIDetector.__init__ .. done
|
||||
336.71218276023865 sec Dummy_device Dummy_PSIDetector.__init__
|
||||
336.7164421081543 sec Dummy_device Dummy_PSIDetector._update_scaninfo
|
||||
336.7255961894989 sec Dummy_device Dummy_PSIDetector._update_scaninfo .. done
|
||||
336.7289445400238 sec Dummy_device Dummy_PSIDetector._update_filewriter
|
||||
336.7329602241516 sec Dummy_device Dummy_PSIDetector._update_filewriter .. done
|
||||
336.7363522052765 sec Dummy_device Dummy_PSIDetector._init
|
||||
336.73995661735535 sec Dummy_device Dummy_PSIDetector._init ... done
|
||||
336.7464373111725 sec Dummy_device Dummy_PSIDetector._update_service_config
|
||||
336.75028443336487 sec Dummy_device Dummy_PSIDetector._update_scaninfo
|
||||
336.7545552253723 sec Dummy_device Dummy_PSIDetector._update_scaninfo .. done
|
||||
336.7576735019684 sec Dummy_device Dummy_PSIDetector._update_filewriter
|
||||
336.76090717315674 sec Dummy_device Dummy_PSIDetector._update_filewriter .. done
|
||||
336.7639853954315 sec Dummy_device Dummy_PSIDetector._init
|
||||
336.7671637535095 sec Dummy_device Dummy_PSIDetector._init ... done
|
||||
336.77041029930115 sec Dummy_device Dummy_PSIDetector.__init__ .. done
|
||||
440.15757870674133 sec Dummy_device Dummy_PSIDetector.__init__
|
||||
440.16116285324097 sec Dummy_device Dummy_PSIDetector._update_scaninfo
|
||||
440.16435194015503 sec Dummy_device Dummy_PSIDetector._update_scaninfo .. done
|
||||
440.16683554649353 sec Dummy_device Dummy_PSIDetector._update_filewriter
|
||||
440.1695673465729 sec Dummy_device Dummy_PSIDetector._update_filewriter .. done
|
||||
440.1722664833069 sec Dummy_device Dummy_PSIDetector._init
|
||||
440.1750702857971 sec Dummy_device Dummy_PSIDetector._init ... done
|
||||
440.1782214641571 sec Dummy_device Dummy_PSIDetector._update_service_config
|
||||
440.1812493801117 sec Dummy_device Dummy_PSIDetector._update_scaninfo
|
||||
440.19190526008606 sec Dummy_device Dummy_PSIDetector._update_scaninfo .. done
|
||||
440.19455003738403 sec Dummy_device Dummy_PSIDetector._update_filewriter
|
||||
440.1971185207367 sec Dummy_device Dummy_PSIDetector._update_filewriter .. done
|
||||
440.1997950077057 sec Dummy_device Dummy_PSIDetector._init
|
||||
440.2028238773346 sec Dummy_device Dummy_PSIDetector._init ... done
|
||||
440.2053735256195 sec Dummy_device Dummy_PSIDetector.__init__ .. done
|
||||
448.7176239490509 sec Dummy_device Dummy_PSIDetector.__init__
|
||||
448.7208425998688 sec Dummy_device Dummy_PSIDetector._update_scaninfo
|
||||
448.724422454834 sec Dummy_device Dummy_PSIDetector._update_scaninfo .. done
|
||||
448.72714829444885 sec Dummy_device Dummy_PSIDetector._update_filewriter
|
||||
448.72995138168335 sec Dummy_device Dummy_PSIDetector._update_filewriter .. done
|
||||
448.73266077041626 sec Dummy_device Dummy_PSIDetector._init
|
||||
448.73561096191406 sec Dummy_device Dummy_PSIDetector._init ... done
|
||||
448.73833537101746 sec Dummy_device Dummy_PSIDetector._update_service_config
|
||||
448.7411322593689 sec Dummy_device Dummy_PSIDetector._update_scaninfo
|
||||
448.744580745697 sec Dummy_device Dummy_PSIDetector._update_scaninfo .. done
|
||||
448.7474892139435 sec Dummy_device Dummy_PSIDetector._update_filewriter
|
||||
448.7504150867462 sec Dummy_device Dummy_PSIDetector._update_filewriter .. done
|
||||
448.75304079055786 sec Dummy_device Dummy_PSIDetector._init
|
||||
448.7556526660919 sec Dummy_device Dummy_PSIDetector._init ... done
|
||||
448.75805735588074 sec Dummy_device Dummy_PSIDetector.__init__ .. done
|
||||
449.67220664024353 sec Dummy_device Dummy_PSIDetector.stage
|
||||
449.67666006088257 sec Dummy_device Dummy_PSIDetector.stage done
|
||||
450.35275387763977 sec Dummy_device Dummy_PSIDetector.pre_scan
|
||||
450.35644245147705 sec Dummy_device Dummy_PSIDetector.pre_scan .. done
|
||||
452.2964699268341 sec Dummy_device Dummy_PSIDetector.complete
|
||||
452.2998161315918 sec Dummy_device Dummy_PSIDetector.complete ... done
|
||||
452.3060998916626 sec Dummy_device Dummy_PSIDetector.unstage
|
||||
452.30941367149353 sec Dummy_device Dummy_PSIDetector.check_scan_id
|
||||
452.31272315979004 sec Dummy_device Dummy_PSIDetector.check_scan_id .. done
|
||||
452.31540155410767 sec Dummy_device Dummy_PSIDetector.unstage .. done
|
||||
452.3178186416626 sec Dummy_device Dummy_PSIDetector.check_scan_id
|
||||
452.3210325241089 sec Dummy_device Dummy_PSIDetector.check_scan_id .. done
|
||||
455.73230934143066 sec Dummy_device Dummy_PSIDetector.stage
|
||||
455.73638582229614 sec Dummy_device Dummy_PSIDetector.stage done
|
||||
456.4665324687958 sec Dummy_device Dummy_PSIDetector.pre_scan
|
||||
456.4696660041809 sec Dummy_device Dummy_PSIDetector.pre_scan .. done
|
||||
463.4272403717041 sec Dummy_device Dummy_PSIDetector.complete
|
||||
463.4405252933502 sec Dummy_device Dummy_PSIDetector.complete ... done
|
||||
463.46015453338623 sec Dummy_device Dummy_PSIDetector.unstage
|
||||
463.4632029533386 sec Dummy_device Dummy_PSIDetector.check_scan_id
|
||||
463.4672119617462 sec Dummy_device Dummy_PSIDetector.check_scan_id .. done
|
||||
463.4705789089203 sec Dummy_device Dummy_PSIDetector.unstage .. done
|
||||
463.47444796562195 sec Dummy_device Dummy_PSIDetector.check_scan_id
|
||||
463.4784984588623 sec Dummy_device Dummy_PSIDetector.check_scan_id .. done
|
||||
534.1574292182922 sec Dummy_device Dummy_PSIDetector.__init__
|
||||
534.161062002182 sec Dummy_device Dummy_PSIDetector._update_scaninfo
|
||||
534.167254447937 sec Dummy_device Dummy_PSIDetector._update_scaninfo .. done
|
||||
534.1703021526337 sec Dummy_device Dummy_PSIDetector._update_filewriter
|
||||
534.1736783981323 sec Dummy_device Dummy_PSIDetector._update_filewriter .. done
|
||||
534.1766662597656 sec Dummy_device Dummy_PSIDetector._init
|
||||
534.1794040203094 sec Dummy_device Dummy_PSIDetector._init ... done
|
||||
534.1823811531067 sec Dummy_device Dummy_PSIDetector._update_service_config
|
||||
534.2018411159515 sec Dummy_device Dummy_PSIDetector._update_scaninfo
|
||||
534.2123966217041 sec Dummy_device Dummy_PSIDetector._update_scaninfo .. done
|
||||
534.2163343429565 sec Dummy_device Dummy_PSIDetector._update_filewriter
|
||||
534.2226631641388 sec Dummy_device Dummy_PSIDetector._update_filewriter .. done
|
||||
534.2264795303345 sec Dummy_device Dummy_PSIDetector._init
|
||||
534.2294256687164 sec Dummy_device Dummy_PSIDetector._init ... done
|
||||
534.2324883937836 sec Dummy_device Dummy_PSIDetector.__init__ .. done
|
||||
598.7346422672272 sec Dummy_device Dummy_PSIDetector.__init__
|
||||
598.7380158901215 sec Dummy_device Dummy_PSIDetector._update_scaninfo
|
||||
598.7417516708374 sec Dummy_device Dummy_PSIDetector._update_scaninfo .. done
|
||||
598.7443566322327 sec Dummy_device Dummy_PSIDetector._update_filewriter
|
||||
598.7471566200256 sec Dummy_device Dummy_PSIDetector._update_filewriter .. done
|
||||
598.7500152587891 sec Dummy_device Dummy_PSIDetector._init
|
||||
598.7526416778564 sec Dummy_device Dummy_PSIDetector._init ... done
|
||||
598.7552354335785 sec Dummy_device Dummy_PSIDetector._update_service_config
|
||||
598.7579991817474 sec Dummy_device Dummy_PSIDetector._update_scaninfo
|
||||
598.7619400024414 sec Dummy_device Dummy_PSIDetector._update_scaninfo .. done
|
||||
598.7644789218903 sec Dummy_device Dummy_PSIDetector._update_filewriter
|
||||
598.7724177837372 sec Dummy_device Dummy_PSIDetector._update_filewriter .. done
|
||||
598.7756130695343 sec Dummy_device Dummy_PSIDetector._init
|
||||
598.7785928249359 sec Dummy_device Dummy_PSIDetector._init ... done
|
||||
598.7815330028534 sec Dummy_device Dummy_PSIDetector.__init__ .. done
|
||||
7.837775468826294 sec Dummy_device Dummy_PSIDetector.__init__
|
||||
7.84122633934021 sec Dummy_device Dummy_PSIDetector._update_scaninfo
|
||||
7.845379829406738 sec Dummy_device Dummy_PSIDetector._update_scaninfo .. done
|
||||
7.8486504554748535 sec Dummy_device Dummy_PSIDetector._update_filewriter
|
||||
7.851677179336548 sec Dummy_device Dummy_PSIDetector._update_filewriter .. done
|
||||
7.862502336502075 sec Dummy_device Dummy_PSIDetector._init
|
||||
7.865923643112183 sec Dummy_device Dummy_PSIDetector._init ... done
|
||||
7.868553876876831 sec Dummy_device Dummy_PSIDetector._update_service_config
|
||||
7.8712639808654785 sec Dummy_device Dummy_PSIDetector._update_scaninfo
|
||||
7.8751060962677 sec Dummy_device Dummy_PSIDetector._update_scaninfo .. done
|
||||
7.878670930862427 sec Dummy_device Dummy_PSIDetector._update_filewriter
|
||||
7.881458520889282 sec Dummy_device Dummy_PSIDetector._update_filewriter .. done
|
||||
7.88470196723938 sec Dummy_device Dummy_PSIDetector._init
|
||||
7.887582302093506 sec Dummy_device Dummy_PSIDetector._init ... done
|
||||
7.8904688358306885 sec Dummy_device Dummy_PSIDetector.__init__ .. done
|
||||
8.856863498687744 sec Dummy_device Dummy_PSIDetector.stage
|
||||
8.876151084899902 sec Dummy_device Dummy_PSIDetector.stage done
|
||||
9.585510969161987 sec Dummy_device Dummy_PSIDetector.pre_scan
|
||||
9.589120864868164 sec Dummy_device Dummy_PSIDetector.pre_scan .. done
|
||||
11.556761264801025 sec Dummy_device Dummy_PSIDetector.complete
|
||||
11.560120582580566 sec Dummy_device Dummy_PSIDetector.complete ... done
|
||||
11.566796779632568 sec Dummy_device Dummy_PSIDetector.unstage
|
||||
11.570103883743286 sec Dummy_device Dummy_PSIDetector.check_scan_id
|
||||
11.574872493743896 sec Dummy_device Dummy_PSIDetector.check_scan_id .. done
|
||||
11.577794790267944 sec Dummy_device Dummy_PSIDetector.unstage .. done
|
||||
11.580487966537476 sec Dummy_device Dummy_PSIDetector.check_scan_id
|
||||
11.584205150604248 sec Dummy_device Dummy_PSIDetector.check_scan_id .. done
|
||||
14.98130464553833 sec Dummy_device Dummy_PSIDetector.stage
|
||||
14.98550009727478 sec Dummy_device Dummy_PSIDetector.stage done
|
||||
15.68580937385559 sec Dummy_device Dummy_PSIDetector.pre_scan
|
||||
15.689151048660278 sec Dummy_device Dummy_PSIDetector.pre_scan .. done
|
||||
22.657074689865112 sec Dummy_device Dummy_PSIDetector.complete
|
||||
22.662108659744263 sec Dummy_device Dummy_PSIDetector.complete ... done
|
||||
22.669034719467163 sec Dummy_device Dummy_PSIDetector.unstage
|
||||
22.671940326690674 sec Dummy_device Dummy_PSIDetector.check_scan_id
|
||||
22.675806283950806 sec Dummy_device Dummy_PSIDetector.check_scan_id .. done
|
||||
22.678316593170166 sec Dummy_device Dummy_PSIDetector.unstage .. done
|
||||
22.68111252784729 sec Dummy_device Dummy_PSIDetector.check_scan_id
|
||||
22.684999227523804 sec Dummy_device Dummy_PSIDetector.check_scan_id .. done
|
||||
198.9285409450531 sec Dummy_device Dummy_PSIDetector.__init__
|
||||
198.9362165927887 sec Dummy_device Dummy_PSIDetector._update_scaninfo
|
||||
198.94056749343872 sec Dummy_device Dummy_PSIDetector._update_scaninfo .. done
|
||||
198.94360828399658 sec Dummy_device Dummy_PSIDetector._update_filewriter
|
||||
198.94663405418396 sec Dummy_device Dummy_PSIDetector._update_filewriter .. done
|
||||
198.94988799095154 sec Dummy_device Dummy_PSIDetector._init
|
||||
198.95326805114746 sec Dummy_device Dummy_PSIDetector._init ... done
|
||||
198.95595622062683 sec Dummy_device Dummy_PSIDetector._update_service_config
|
||||
198.95901942253113 sec Dummy_device Dummy_PSIDetector._update_scaninfo
|
||||
198.96562147140503 sec Dummy_device Dummy_PSIDetector._update_scaninfo .. done
|
||||
198.96872448921204 sec Dummy_device Dummy_PSIDetector._update_filewriter
|
||||
198.97177743911743 sec Dummy_device Dummy_PSIDetector._update_filewriter .. done
|
||||
198.97474718093872 sec Dummy_device Dummy_PSIDetector._init
|
||||
198.97741293907166 sec Dummy_device Dummy_PSIDetector._init ... done
|
||||
198.979900598526 sec Dummy_device Dummy_PSIDetector.__init__ .. done
|
||||
273.58063983917236 sec Dummy_device Dummy_PSIDetector.__init__
|
||||
273.5842516422272 sec Dummy_device Dummy_PSIDetector._update_scaninfo
|
||||
273.587562084198 sec Dummy_device Dummy_PSIDetector._update_scaninfo .. done
|
||||
273.5902290344238 sec Dummy_device Dummy_PSIDetector._update_filewriter
|
||||
273.5933790206909 sec Dummy_device Dummy_PSIDetector._update_filewriter .. done
|
||||
273.5958044528961 sec Dummy_device Dummy_PSIDetector._init
|
||||
273.598260641098 sec Dummy_device Dummy_PSIDetector._init ... done
|
||||
273.60068702697754 sec Dummy_device Dummy_PSIDetector._update_service_config
|
||||
273.60293459892273 sec Dummy_device Dummy_PSIDetector._update_scaninfo
|
||||
273.6064066886902 sec Dummy_device Dummy_PSIDetector._update_scaninfo .. done
|
||||
273.60881304740906 sec Dummy_device Dummy_PSIDetector._update_filewriter
|
||||
273.61149501800537 sec Dummy_device Dummy_PSIDetector._update_filewriter .. done
|
||||
273.61411118507385 sec Dummy_device Dummy_PSIDetector._init
|
||||
273.6163446903229 sec Dummy_device Dummy_PSIDetector._init ... done
|
||||
273.6186339855194 sec Dummy_device Dummy_PSIDetector.__init__ .. done
|
||||
289.1456503868103 sec Dummy_device Dummy_PSIDetector.__init__
|
||||
289.14894795417786 sec Dummy_device Dummy_PSIDetector._update_scaninfo
|
||||
289.15220499038696 sec Dummy_device Dummy_PSIDetector._update_scaninfo .. done
|
||||
289.15479493141174 sec Dummy_device Dummy_PSIDetector._update_filewriter
|
||||
289.1575071811676 sec Dummy_device Dummy_PSIDetector._update_filewriter .. done
|
||||
289.1601548194885 sec Dummy_device Dummy_PSIDetector._init
|
||||
289.1627006530762 sec Dummy_device Dummy_PSIDetector._init ... done
|
||||
289.16531777381897 sec Dummy_device Dummy_PSIDetector._update_service_config
|
||||
289.16803646087646 sec Dummy_device Dummy_PSIDetector._update_scaninfo
|
||||
289.17167234420776 sec Dummy_device Dummy_PSIDetector._update_scaninfo .. done
|
||||
289.17424416542053 sec Dummy_device Dummy_PSIDetector._update_filewriter
|
||||
289.1768672466278 sec Dummy_device Dummy_PSIDetector._update_filewriter .. done
|
||||
289.19212794303894 sec Dummy_device Dummy_PSIDetector._init
|
||||
289.1952097415924 sec Dummy_device Dummy_PSIDetector._init ... done
|
||||
289.1978967189789 sec Dummy_device Dummy_PSIDetector.__init__ .. done
|
||||
293.3917553424835 sec Dummy_device Dummy_PSIDetector.__init__
|
||||
293.3952159881592 sec Dummy_device Dummy_PSIDetector._update_scaninfo
|
||||
293.398410320282 sec Dummy_device Dummy_PSIDetector._update_scaninfo .. done
|
||||
293.4014220237732 sec Dummy_device Dummy_PSIDetector._update_filewriter
|
||||
293.40486550331116 sec Dummy_device Dummy_PSIDetector._update_filewriter .. done
|
||||
293.40760946273804 sec Dummy_device Dummy_PSIDetector._init
|
||||
293.4102578163147 sec Dummy_device Dummy_PSIDetector._init ... done
|
||||
293.4129559993744 sec Dummy_device Dummy_PSIDetector._update_service_config
|
||||
293.416291475296 sec Dummy_device Dummy_PSIDetector._update_scaninfo
|
||||
293.4226896762848 sec Dummy_device Dummy_PSIDetector._update_scaninfo .. done
|
||||
293.43413949012756 sec Dummy_device Dummy_PSIDetector._update_filewriter
|
||||
293.4404549598694 sec Dummy_device Dummy_PSIDetector._update_filewriter .. done
|
||||
293.4433434009552 sec Dummy_device Dummy_PSIDetector._init
|
||||
293.44669795036316 sec Dummy_device Dummy_PSIDetector._init ... done
|
||||
293.450825214386 sec Dummy_device Dummy_PSIDetector.__init__ .. done
|
||||
294.4045298099518 sec Dummy_device Dummy_PSIDetector.stage
|
||||
294.41221737861633 sec Dummy_device Dummy_PSIDetector.stage done
|
||||
295.0753116607666 sec Dummy_device Dummy_PSIDetector.pre_scan
|
||||
295.0786409378052 sec Dummy_device Dummy_PSIDetector.pre_scan .. done
|
||||
296.9744246006012 sec Dummy_device Dummy_PSIDetector.complete
|
||||
296.9783310890198 sec Dummy_device Dummy_PSIDetector.complete ... done
|
||||
296.9849274158478 sec Dummy_device Dummy_PSIDetector.unstage
|
||||
296.9879596233368 sec Dummy_device Dummy_PSIDetector.check_scan_id
|
||||
296.9918463230133 sec Dummy_device Dummy_PSIDetector.check_scan_id .. done
|
||||
296.9946928024292 sec Dummy_device Dummy_PSIDetector.unstage .. done
|
||||
296.9977705478668 sec Dummy_device Dummy_PSIDetector.check_scan_id
|
||||
297.00152254104614 sec Dummy_device Dummy_PSIDetector.check_scan_id .. done
|
||||
300.39787578582764 sec Dummy_device Dummy_PSIDetector.stage
|
||||
300.40195393562317 sec Dummy_device Dummy_PSIDetector.stage done
|
||||
301.1010916233063 sec Dummy_device Dummy_PSIDetector.pre_scan
|
||||
301.10465002059937 sec Dummy_device Dummy_PSIDetector.pre_scan .. done
|
||||
308.1270532608032 sec Dummy_device Dummy_PSIDetector.complete
|
||||
308.1302571296692 sec Dummy_device Dummy_PSIDetector.complete ... done
|
||||
308.13682293891907 sec Dummy_device Dummy_PSIDetector.unstage
|
||||
308.13955307006836 sec Dummy_device Dummy_PSIDetector.check_scan_id
|
||||
308.14320278167725 sec Dummy_device Dummy_PSIDetector.check_scan_id .. done
|
||||
308.14579582214355 sec Dummy_device Dummy_PSIDetector.unstage .. done
|
||||
308.1484956741333 sec Dummy_device Dummy_PSIDetector.check_scan_id
|
||||
308.1524248123169 sec Dummy_device Dummy_PSIDetector.check_scan_id .. done
|
||||
406.1815185546875 sec Dummy_device Dummy_PSIDetector.__init__
|
||||
406.18433952331543 sec Dummy_device Dummy_PSIDetector._update_scaninfo
|
||||
406.1875720024109 sec Dummy_device Dummy_PSIDetector._update_scaninfo .. done
|
||||
406.1899950504303 sec Dummy_device Dummy_PSIDetector._update_filewriter
|
||||
406.19225120544434 sec Dummy_device Dummy_PSIDetector._update_filewriter .. done
|
||||
406.1947364807129 sec Dummy_device Dummy_PSIDetector._init
|
||||
406.1971571445465 sec Dummy_device Dummy_PSIDetector._init ... done
|
||||
406.1996533870697 sec Dummy_device Dummy_PSIDetector._update_service_config
|
||||
406.2021658420563 sec Dummy_device Dummy_PSIDetector._update_scaninfo
|
||||
406.20517230033875 sec Dummy_device Dummy_PSIDetector._update_scaninfo .. done
|
||||
406.2075276374817 sec Dummy_device Dummy_PSIDetector._update_filewriter
|
||||
406.2099573612213 sec Dummy_device Dummy_PSIDetector._update_filewriter .. done
|
||||
406.2122435569763 sec Dummy_device Dummy_PSIDetector._init
|
||||
406.21466541290283 sec Dummy_device Dummy_PSIDetector._init ... done
|
||||
406.2169442176819 sec Dummy_device Dummy_PSIDetector.__init__ .. done
|
||||
408.5058219432831 sec Dummy_device Dummy_PSIDetector.__init__
|
||||
408.5092177391052 sec Dummy_device Dummy_PSIDetector._update_scaninfo
|
||||
408.51229882240295 sec Dummy_device Dummy_PSIDetector._update_scaninfo .. done
|
||||
408.5166611671448 sec Dummy_device Dummy_PSIDetector._update_filewriter
|
||||
408.51968693733215 sec Dummy_device Dummy_PSIDetector._update_filewriter .. done
|
||||
408.52243971824646 sec Dummy_device Dummy_PSIDetector._init
|
||||
408.5258412361145 sec Dummy_device Dummy_PSIDetector._init ... done
|
||||
408.5286478996277 sec Dummy_device Dummy_PSIDetector._update_service_config
|
||||
408.53117513656616 sec Dummy_device Dummy_PSIDetector._update_scaninfo
|
||||
408.5346622467041 sec Dummy_device Dummy_PSIDetector._update_scaninfo .. done
|
||||
408.5387032032013 sec Dummy_device Dummy_PSIDetector._update_filewriter
|
||||
408.5484755039215 sec Dummy_device Dummy_PSIDetector._update_filewriter .. done
|
||||
408.5515501499176 sec Dummy_device Dummy_PSIDetector._init
|
||||
408.55432891845703 sec Dummy_device Dummy_PSIDetector._init ... done
|
||||
408.55744099617004 sec Dummy_device Dummy_PSIDetector.__init__ .. done
|
||||
409.36813139915466 sec Dummy_device Dummy_PSIDetector.stage
|
||||
409.37291145324707 sec Dummy_device Dummy_PSIDetector.stage done
|
||||
410.0452582836151 sec Dummy_device Dummy_PSIDetector.pre_scan
|
||||
410.04850935935974 sec Dummy_device Dummy_PSIDetector.pre_scan .. done
|
||||
411.9517059326172 sec Dummy_device Dummy_PSIDetector.complete
|
||||
411.9550929069519 sec Dummy_device Dummy_PSIDetector.complete ... done
|
||||
411.9607892036438 sec Dummy_device Dummy_PSIDetector.unstage
|
||||
411.9635720252991 sec Dummy_device Dummy_PSIDetector.check_scan_id
|
||||
411.96782517433167 sec Dummy_device Dummy_PSIDetector.check_scan_id .. done
|
||||
411.9705128669739 sec Dummy_device Dummy_PSIDetector.unstage .. done
|
||||
411.97303891181946 sec Dummy_device Dummy_PSIDetector.check_scan_id
|
||||
411.97616386413574 sec Dummy_device Dummy_PSIDetector.check_scan_id .. done
|
||||
415.3653302192688 sec Dummy_device Dummy_PSIDetector.stage
|
||||
415.36913657188416 sec Dummy_device Dummy_PSIDetector.stage done
|
||||
416.0821409225464 sec Dummy_device Dummy_PSIDetector.pre_scan
|
||||
416.0855324268341 sec Dummy_device Dummy_PSIDetector.pre_scan .. done
|
||||
422.92456674575806 sec Dummy_device Dummy_PSIDetector.complete
|
||||
422.92839455604553 sec Dummy_device Dummy_PSIDetector.complete ... done
|
||||
422.93567180633545 sec Dummy_device Dummy_PSIDetector.unstage
|
||||
422.9386281967163 sec Dummy_device Dummy_PSIDetector.check_scan_id
|
||||
422.9420807361603 sec Dummy_device Dummy_PSIDetector.check_scan_id .. done
|
||||
422.94424176216125 sec Dummy_device Dummy_PSIDetector.unstage .. done
|
||||
422.9465448856354 sec Dummy_device Dummy_PSIDetector.check_scan_id
|
||||
422.94983959198 sec Dummy_device Dummy_PSIDetector.check_scan_id .. done
|
1
phoenix_bec/local_scripts/TEST_scanning/monitor.sh
Normal file
1
phoenix_bec/local_scripts/TEST_scanning/monitor.sh
Normal file
@ -0,0 +1 @@
|
||||
camonitor X07MB-ES-MA1:ScanX.VAL X07MB-ES-MA1:ScanX.RBV X07MB-OP2:START-CSMPL X07MB-OP2:SMPL X07MB-OP2:INTR-COUNT X07MB-XMAP:StartAll X07MB-XMAP:EraseStart X07MB-XMAP:StopAll
|
@ -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)
|
||||
"""
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
104
phoenix_bec/local_scripts/TOBEDELETED/phoenix.py_old2
Normal file
104
phoenix_bec/local_scripts/TOBEDELETED/phoenix.py_old2
Normal 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')
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
104
phoenix_bec/local_scripts/TOBEDELETED/phoenix.py_old_wrongTAB
Normal file
104
phoenix_bec/local_scripts/TOBEDELETED/phoenix.py_old_wrongTAB
Normal 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')
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
5
phoenix_bec/local_scripts/p_test.py
Normal file
5
phoenix_bec/local_scripts/p_test.py
Normal file
@ -0,0 +1,5 @@
|
||||
print('test')
|
||||
base='/data/test/x07mb-test-bec/bec_deployment/phoenix_bec/phoenix_bec'
|
||||
bec.config.update_session_with_file(base+'/device_configs/phoenix_falcon.yaml')
|
||||
#bec.config.update_session_with_file(base+'/device_configs/phoenix_devices.yaml')
|
||||
|
3
phoenix_bec/local_scripts/test.py
Normal file
3
phoenix_bec/local_scripts/test.py
Normal file
@ -0,0 +1,3 @@
|
||||
import phoenix_bec.scripts.phoenix as PH
|
||||
w=PH.PhGroup('labelName')
|
||||
w.linescan2group(s1)
|
@ -0,0 +1 @@
|
||||
from .phoenix_scans import PhoenixLineScan
|
||||
|
174
phoenix_bec/scans/phoenix_scans.py
Normal file
174
phoenix_bec/scans/phoenix_scans.py
Normal file
@ -0,0 +1,174 @@
|
||||
"""
|
||||
|
||||
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
|
||||
"""
|
||||
|
||||
# imports in ScanBase
|
||||
# from __future__ import annotations
|
||||
|
||||
# import ast
|
||||
# import enum
|
||||
# import threading
|
||||
# import time
|
||||
# import uuid
|
||||
# from abc import ABC, abstractmethod
|
||||
# from typing import Any, Literal
|
||||
|
||||
# import numpy as np
|
||||
|
||||
# from bec_lib.device import DeviceBase
|
||||
# from bec_lib.devicemanager import DeviceManagerBase
|
||||
# from bec_lib.endpoints import MessageEndpoints
|
||||
# from bec_lib.logger import bec_logger
|
||||
|
||||
# from .errors import LimitError, ScanAbortion
|
||||
# from .path_optimization import PathOptimizerMixin
|
||||
# from .scan_stubs import ScanStubs
|
||||
# end imports in ScanBase
|
||||
|
||||
# import time
|
||||
|
||||
# import numpy as np
|
||||
|
||||
import time
|
||||
|
||||
import numpy as np
|
||||
|
||||
# from bec_lib.endpoints import MessageEndpoints
|
||||
from bec_lib.logger import bec_logger
|
||||
from bec_server.scan_server.scans import ScanArgType, ScanBase
|
||||
|
||||
from phoenix_bec.scripts.phoenix import PhoenixBL
|
||||
|
||||
# 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
|
||||
|
||||
|
||||
logger = bec_logger.logger
|
||||
|
||||
|
||||
class LogTime:
|
||||
|
||||
def __init__(self):
|
||||
logger.success("init LogTime")
|
||||
self.t0 = time.time()
|
||||
|
||||
def p_s(self, x):
|
||||
now = time.time()
|
||||
# delta=now-self.t0
|
||||
m = str(now) + " sec " + x
|
||||
logger.success(m)
|
||||
# making the instance of PSID
|
||||
# self.t0=now
|
||||
file = open("MyLogfile.txt", "a")
|
||||
file.write(m + "\n")
|
||||
file.close
|
||||
|
||||
|
||||
class PhoenixScanBaseTTL(ScanBase):
|
||||
"""
|
||||
Base scan cl p_s('init scrips.phoenix.scans.PhoenixLineScan')
|
||||
"""
|
||||
|
||||
def scan_core(self):
|
||||
"""perform the scan core procedure"""
|
||||
self.p_s("PhoenixScanBaseTT.scan_core")
|
||||
for ind, pos in self._get_position():
|
||||
for self.burst_index in range(self.burst_at_each_point):
|
||||
self.p_s("PhoenixScanBaseTT.scan_core in loop ")
|
||||
|
||||
yield from self._at_each_point(ind, pos)
|
||||
self.burst_index = 0
|
||||
|
||||
def _at_each_point(self, ind=None, pos=None):
|
||||
self.p_s("PhoenixScanBaseTT._at_each_point")
|
||||
yield from self._move_scan_motors_and_wait(pos)
|
||||
time.sleep(self.settling_time)
|
||||
yield from self.stubs.trigger(min_wait=self.exp_time)
|
||||
yield from self.stubs.read(group="monitored", point_id=self.point_id)
|
||||
|
||||
self.point_id += 1
|
||||
self.p_s("done")
|
||||
|
||||
|
||||
class PhoenixLineScan(PhoenixScanBaseTTL):
|
||||
|
||||
scan_name = "phoenix_line_scan"
|
||||
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)
|
||||
|
||||
"""
|
||||
# from phoenix_bec.scripts.phoenix import PhoenixBL
|
||||
self.p_s = PhoenixBL.my_log
|
||||
|
||||
self.p_s("init scripts.phoenix.scans.PhoenixLineScan")
|
||||
|
||||
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
|
||||
|
||||
time.sleep(1)
|
||||
self.p_s("done")
|
||||
|
||||
def _calculate_positions(self) -> None:
|
||||
self.p_s("PhoenixLineScan._calculate_positions")
|
||||
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)
|
||||
self.p_s("done")
|
33
phoenix_bec/scripts/Current_setup.txt
Normal file
33
phoenix_bec/scripts/Current_setup.txt
Normal 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
|
11
phoenix_bec/scripts/README.md
Normal file
11
phoenix_bec/scripts/README.md
Normal file
@ -0,0 +1,11 @@
|
||||
## scripts
|
||||
|
||||
Directory for general phoenix specific python code
|
||||
|
||||
|
||||
to autoload register in __init__.py
|
||||
|
||||
|
||||
## FILES
|
||||
|
||||
phoenix.py Base file with general definitions
|
1
phoenix_bec/scripts/__init__.py
Normal file
1
phoenix_bec/scripts/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
|
415
phoenix_bec/scripts/phoenix.py
Normal file
415
phoenix_bec/scripts/phoenix.py
Normal file
@ -0,0 +1,415 @@
|
||||
"""
|
||||
|
||||
General collection of calsses for PHEONIX beamline
|
||||
|
||||
"""
|
||||
|
||||
# from unittest import mock
|
||||
import os
|
||||
|
||||
# import sys
|
||||
import time
|
||||
|
||||
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
|
||||
|
||||
from phoenix_bec.scripts.phoenix_help import PhoenixHelp
|
||||
|
||||
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 Utilities:
|
||||
"""
|
||||
Utiliy class for PHOENIX
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
print("init phoenix.Utilities")
|
||||
|
||||
# setattr(self, "description", description)
|
||||
# atribute 'label' for compatibility woith La groups...
|
||||
# setattr(self, "label", description)
|
||||
# if type(NameTag)==list:
|
||||
# for i in NameTag:
|
||||
# setattr(self,i,None)
|
||||
# #endfor
|
||||
# else:
|
||||
# setattr(self,NameTag,None)
|
||||
# endif
|
||||
|
||||
def list_signals_(self, cls=None, check=False):
|
||||
"""
|
||||
|
||||
Create a list of all signals in a device
|
||||
including name of Epics channel
|
||||
|
||||
"""
|
||||
print("List of all Signals")
|
||||
print("......try also attributes")
|
||||
print(" .describe_configuration()")
|
||||
print(" ._info")
|
||||
try:
|
||||
name = cls.name
|
||||
n_name = len(name)
|
||||
except:
|
||||
name = " "
|
||||
try:
|
||||
config = cls._info["describe"]
|
||||
except:
|
||||
config = " "
|
||||
# endexcept
|
||||
|
||||
# print(config)
|
||||
try:
|
||||
for kk in config.keys():
|
||||
print(kk[n_name + 1 : 40], config[kk]["source"])
|
||||
# print('dev.'+def __init__():
|
||||
# kk.replace('_','.'),' ',config[kk]['source'])
|
||||
|
||||
# endexcept
|
||||
except:
|
||||
print(config)
|
||||
print("no key")
|
||||
|
||||
def list_signals_falcon(self):
|
||||
"""
|
||||
List signals for falcon
|
||||
"""
|
||||
|
||||
print("........... dev.falcon ")
|
||||
self.list_signals_(dev.falcon)
|
||||
print("........... falcon.mca1 ")
|
||||
self.list_signals_(dev.falcon.mca1)
|
||||
print("........... falcon.dxp1 ")
|
||||
self.list_signals_(dev.falcon.dxp1)
|
||||
print("........... falcon.roi0")
|
||||
self.list_signals_(dev.falcon.roi0)
|
||||
|
||||
def list_signals_xmap(self):
|
||||
"""
|
||||
List signals for XMAP
|
||||
"""
|
||||
|
||||
print("........... dev.xmap ")
|
||||
self.list_signals_(dev.xmap)
|
||||
print("........... xmap.mca1 ")
|
||||
self.list_signals_(dev.xmap.mca1)
|
||||
print("........... xmap.dxp1 ")
|
||||
self.list_signals_(dev.xmap.dxp1)
|
||||
print("........... xmap.roi0")
|
||||
self.list_signals_(dev.xmap.roi0)
|
||||
|
||||
|
||||
class PhoenixBL(Utilities, PhoenixHelp):
|
||||
"""
|
||||
#
|
||||
# General class for PHOENIX beamline located in phoenix_bec/phoenic_bec/scripts
|
||||
#
|
||||
"""
|
||||
|
||||
t0 = time.time()
|
||||
|
||||
def __init__(self):
|
||||
"""
|
||||
init PhoenixBL() in phoenix_bec/scripts
|
||||
|
||||
"""
|
||||
# import os
|
||||
|
||||
# self.silent = False
|
||||
# self.help()
|
||||
# self.help_phoenix()
|
||||
# self.help_attributes()
|
||||
|
||||
self.silent = False
|
||||
print("..... init PhoenixBL from phoenix_bec/scripts/phoenix.py")
|
||||
|
||||
# Define important paths
|
||||
|
||||
self.base_path = "/data/test/x07mb-test-bec/production/phoenix_bec/"
|
||||
self.path_phoenix_bec = self.base_path + "phoenix_bec/"
|
||||
self.path_devices = self.path_phoenix_bec + "device_configs/"
|
||||
|
||||
self.path_scripts = self.path_phoenix_bec + "scripts/"
|
||||
self.path_scans = self.path_phoenix_bec + "scans/"
|
||||
|
||||
self.path_scripts_local = self.path_phoenix_bec + "local_scripts/"
|
||||
self.path_config_local = self.path_scripts_local + "TEST_ConfigPhoenix/"
|
||||
|
||||
# yamal file for default configuration
|
||||
|
||||
self.file_devices_file = self.path_devices + "phoenix_devices.yaml"
|
||||
|
||||
self.file_devices_xmap = self.path_devices + "phoenix_xmap.yaml"
|
||||
self.file_devices_falcon = self.path_devices + "phoenix_falcon.yaml"
|
||||
|
||||
# temporary yaml file to allow adding devices with one single comamnd
|
||||
self.file_devices_tmp = self.path_devices + "current_devices_tmp.yaml"
|
||||
|
||||
self.t0 = time.time()
|
||||
|
||||
# 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 create_base_config(self):
|
||||
""" "
|
||||
create a yaml file from standard configuration
|
||||
"""
|
||||
os.system("cat " + self.file_devices_file + " > " + self.file_devices_tmp)
|
||||
# os.system("ls -altr" + self.path_phoenix_bec + "phoenix_bec/devices")
|
||||
bec.config.update_session_with_file(self.file_devices_tmp)
|
||||
|
||||
def add_phoenix_config(self):
|
||||
"""
|
||||
Add phoenix config
|
||||
"""
|
||||
print("add_phoenix_config ")
|
||||
print("self.file_devices_file")
|
||||
os.system("cat " + self.file_devices_file + " >> " + self.file_devices_tmp)
|
||||
|
||||
bec.config.update_session_with_file(self.file_devices_tmp)
|
||||
|
||||
def add_xmap(self):
|
||||
"""Add XMAP to config"""
|
||||
print("add xmap ")
|
||||
|
||||
os.system("cat " + self.file_devices_xmap + " >> " + self.file_devices_tmp)
|
||||
bec.config.update_session_with_file(self.file_devices_tmp)
|
||||
|
||||
def load_xmap(self):
|
||||
"""Load XMAP"""
|
||||
print("load_xmap")
|
||||
os.system("cat " + self.file_devices_xmap + " > " + self.file_devices_tmp)
|
||||
|
||||
bec.config.update_session_with_file(self.file_devices_xmap)
|
||||
|
||||
def add_falcon(self):
|
||||
"""Add Falcon to config"""
|
||||
print("add_falcon to existing configuration ")
|
||||
|
||||
os.system("cat " + self.file_devices_falcon + " >> " + self.file_devices_tmp)
|
||||
bec.config.update_session_with_file(self.file_devices_tmp)
|
||||
|
||||
def load_falcon(self):
|
||||
"""Load FALCON"""
|
||||
|
||||
print("load_falcon")
|
||||
os.system("cat " + self.file_devices_falcon + " > " + self.file_devices_tmp)
|
||||
bec.config.update_session_with_file(self.file_devices_falcon)
|
||||
|
||||
# def show_phoenix_setup(self):
|
||||
# print(self.path_phoenix_bec)
|
||||
# os.system("cat " + self.path_phoenix_bec + "phoenix_bec/scripts/Current_setup.txt")
|
||||
|
||||
def run_shell(self, commands):
|
||||
"""
|
||||
run one or sever shell comands in a new terminal
|
||||
|
||||
Example:
|
||||
|
||||
run_shell('ls')
|
||||
runt_shell(['sh script.sh','pwd','ls'])
|
||||
"""
|
||||
cmd = 'gnome-terminal --geometry 100X30 -- bash -c "'
|
||||
print(cmd)
|
||||
if type(commands) == list:
|
||||
for i in commands:
|
||||
cmd = cmd + i + " ; "
|
||||
# endfor
|
||||
else:
|
||||
cmd = cmd + commands + " ; "
|
||||
# endelse
|
||||
cmd = cmd + ' exec bash " '
|
||||
print(cmd)
|
||||
|
||||
os.system(cmd)
|
||||
# os.system(
|
||||
# 'gnome-terminal --geometry 100X30 -- bash -c "source /data/test/x07mb-test-bec/production/bec_venv/bin/activate ; bec-server attach ; exec bash"'
|
||||
# )
|
||||
|
||||
@classmethod
|
||||
def my_log(cls, x):
|
||||
"""
|
||||
class method allows to write a user defined log file
|
||||
time is seconds relative to some point max 10 minutes ago
|
||||
|
||||
"""
|
||||
|
||||
print(time.time())
|
||||
now = time.time() - (86400 * (time.time() // 86400))
|
||||
now = now - 3600.0 * (now // 3600.0)
|
||||
now = now - 600.0 * (now // 600.0)
|
||||
m = str(now) + " sec " + x
|
||||
|
||||
logger.success(m)
|
||||
|
||||
file = open("MyLogfile.txt", "a")
|
||||
file.write(m + "\n")
|
||||
file.close()
|
||||
|
||||
|
||||
class PhGroup:
|
||||
"""
|
||||
Class to create data groups
|
||||
compatible with larch groups
|
||||
|
||||
initialize by
|
||||
|
||||
ww=PhGroup('YourLabel')
|
||||
|
||||
it creates a group
|
||||
with default attributes
|
||||
|
||||
ww.label = 'YourLabel' --- for compatibility with larch groups
|
||||
ww.description =YourLabel'
|
||||
|
||||
Further data can be added with new tags by
|
||||
|
||||
ww.newtag=67
|
||||
(bec_venv) [gac-x07mb@x07mb-bec-001 phoenix_bec]$
|
||||
|
||||
ww.keys() -- list all keys
|
||||
ww.linescan2group -- converts bec linescan data to group format
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, description):
|
||||
|
||||
setattr(self, "description", description)
|
||||
# atribute 'label' for compatibility woith La groups...
|
||||
setattr(self, "label", description)
|
||||
# if type(NameTag)==list:
|
||||
# for i in NameTag:
|
||||
# setattr(self,i,None)
|
||||
# #endfor
|
||||
# else:
|
||||
# setattr(self,NameTag,None)
|
||||
# endif
|
||||
|
||||
def add(self, NameTag, content):
|
||||
"""
|
||||
Add tags to group...
|
||||
|
||||
Parameters
|
||||
----------
|
||||
NameTag : TYPE
|
||||
DESCRIPTION.
|
||||
content : TYPE
|
||||
DESCRIPTION.
|
||||
|
||||
Returns
|
||||
-------
|
||||
None.
|
||||
|
||||
"""
|
||||
|
||||
setattr(self, NameTag, content)
|
||||
|
||||
def keys(self):
|
||||
"""
|
||||
Method gets all atributes, which are not methods
|
||||
and which do not start with __
|
||||
|
||||
|
||||
Returns
|
||||
-------
|
||||
box : TYPE
|
||||
DESCRIPTION.
|
||||
|
||||
"""
|
||||
box = []
|
||||
|
||||
for i in self.__dir__():
|
||||
if "__" not in i:
|
||||
# print(i)
|
||||
if str(type(self.__getattribute__(i))) != "<class 'method'>":
|
||||
box.append(i)
|
||||
# endif
|
||||
# endfor
|
||||
return box
|
||||
|
||||
def linescan2group(self, this_scan):
|
||||
"""
|
||||
|
||||
method merges results of linescan into group and
|
||||
creates for each data a numpy variable constructed as
|
||||
|
||||
group_name.{device_name}_{variable_name}_val (for value )
|
||||
group_name.{device_name}_{variable_name}_ts (for timestamp )
|
||||
|
||||
|
||||
"""
|
||||
|
||||
print(this_scan.scan.data.keys())
|
||||
for outer_key in this_scan.scan.data.keys():
|
||||
print("outer_key", outer_key)
|
||||
# n_outer = len(this_scan.scan.data.keys())
|
||||
for inner_key in this_scan.scan.data[outer_key].keys():
|
||||
print("inner_key", inner_key)
|
||||
# calculate nunber of points
|
||||
n_inner = len(this_scan.scan.data[outer_key][inner_key].keys())
|
||||
value = np.zeros(n_inner)
|
||||
timestamp = np.zeros(n_inner)
|
||||
for i in range(n_inner):
|
||||
try:
|
||||
value[i] = this_scan.scan.data[outer_key][inner_key][i]["value"]
|
||||
except:
|
||||
value = None
|
||||
try:
|
||||
timestamp[i] = this_scan.scan.data[outer_key][inner_key][i]["timestamp"]
|
||||
except:
|
||||
timestamp[i] = None
|
||||
# endfor
|
||||
self.add(inner_key + "_" + outer_key + "_val", value)
|
||||
self.add(inner_key + "_" + outer_key + "_ts", timestamp)
|
||||
# endfor
|
||||
# endfor
|
||||
# endfor
|
||||
print(time.time())
|
||||
|
||||
# enddef
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
PH = PhoenixBL()
|
||||
|
||||
for x in PH.__dir__():
|
||||
if "path" in x:
|
||||
# print(x)
|
||||
pa = getattr(PH, x)
|
||||
if os.path.isdir(pa):
|
||||
print(pa, " exists")
|
||||
else:
|
||||
print("!!- ", pa, " does not exist")
|
||||
if "file" in x:
|
||||
pa = getattr(PH, x)
|
||||
if os.path.isfile(pa):
|
||||
print(pa, " exists")
|
||||
else:
|
||||
print("!!- ", pa, " does not exist")
|
180
phoenix_bec/scripts/phoenix_help.py
Normal file
180
phoenix_bec/scripts/phoenix_help.py
Normal file
@ -0,0 +1,180 @@
|
||||
from IPython import get_ipython
|
||||
|
||||
|
||||
class PhoenixHelp:
|
||||
|
||||
def pm(self, x):
|
||||
if self.silent == False:
|
||||
print(x)
|
||||
|
||||
# endif
|
||||
|
||||
def help(self):
|
||||
|
||||
self.__on_help = """HelpPhoenix
|
||||
HELP ON BEC
|
||||
|
||||
General online documentation of BEC
|
||||
https://beamline-experiment-control.readthedocs.io/en/latest/
|
||||
|
||||
code in git
|
||||
https://gitlab.psi.ch/bec
|
||||
|
||||
For help on BL specific topics see
|
||||
|
||||
phoenix.help()
|
||||
phoenix.help_phoenix() ... etc...
|
||||
|
||||
find attributes using tab extension on command line
|
||||
|
||||
BL related MAGIC COMMANDS on iphython shell start with ph:
|
||||
Example:
|
||||
%ph_create_base_config
|
||||
.. etc .. find all commands by tab extension, description in phoenix.help_magic
|
||||
|
||||
|
||||
"""
|
||||
self.pm(self.__on_help)
|
||||
# self.pm(self.on_attributes)
|
||||
|
||||
def help_phoenix_file_structure(self):
|
||||
self.__on_phoenix_file_structure = """
|
||||
|
||||
|
||||
phoenix_bec/scripts -- directory for general BL scripts
|
||||
(auto read to server)
|
||||
....... phoenix.py -- base classes for BL
|
||||
....... phoenix_help.py -- contains classes for help as relevant
|
||||
for BEC @ PHOENIX operation
|
||||
|
||||
phoenix_bec/scans -- location of dedicated scan types
|
||||
|
||||
phoenix_bec/bec_iphython_client -- directory for startup files
|
||||
....... post_startup.py -- BL specific definitions, read when bec starts
|
||||
Definiton of BL related magic commands
|
||||
|
||||
phoenix_bec/devcices -- Location for specific devices
|
||||
phoenix_bec/device_configs -- location of yaml configuration files
|
||||
|
||||
phoenix_bec/local_scripts -- space for local and test script
|
||||
(NOT auto read to server)
|
||||
|
||||
|
||||
#######################################################################
|
||||
|
||||
IT IS ABSOLUTELY FORBIDDEN FOR USERS TO MAKE ANY CHANGES TO ANY OF THE FILES
|
||||
|
||||
#######################################################################
|
||||
|
||||
|
||||
|
||||
"""
|
||||
|
||||
def help_phoenix(self):
|
||||
|
||||
self.__on_phoenix = """
|
||||
General setup of bec @ PHOENIX
|
||||
important files
|
||||
|
||||
phoenix_bec/scripts/phoenix.py
|
||||
|
||||
phoenix_bec/scripts/phoenix.py
|
||||
|
||||
"""
|
||||
self.pm(self.__on_phoenix)
|
||||
|
||||
def help_devices(self):
|
||||
"""
|
||||
|
||||
help text on using devices in bec command line
|
||||
|
||||
"""
|
||||
|
||||
self.__on_devices = """
|
||||
|
||||
===== THE BASICS =========
|
||||
|
||||
All devices are collected in dev.{name}, for example
|
||||
dev.falcon : falcon 4 element detector
|
||||
dev.MA_SCANX : motor ScanX of manipulator MA1
|
||||
dev.SAI_MA1 : diode detector etc.
|
||||
|
||||
For safety reasond, after initialization in bec,if possible,
|
||||
all devices are DISABLED by default.
|
||||
|
||||
i.e dev.{name}.enabled is set to False for safety
|
||||
to enable : dev.{name}.enabled = True
|
||||
to disable : dev.{name}.enabled = False
|
||||
|
||||
To see all devices use
|
||||
|
||||
dev.show_all()
|
||||
|
||||
===== READING DEVICES IN SCANS ====
|
||||
|
||||
dev.{name}.readout_priority = "monitored" : reads device in scan on each point
|
||||
dev.{name}.readout_priority = "baseline" : does not read device in scan
|
||||
|
||||
====== initialize devices =========
|
||||
|
||||
from bec comamnd line in python script
|
||||
%ph_create_base_config phoenx.create_base_config() -- created main devices
|
||||
%add_falcon phoenix.add_falcon -- add falcon to existing devices
|
||||
... etc..
|
||||
|
||||
====== WORK ON EPICS CHANNELS ====
|
||||
|
||||
.get() : read signal: value only
|
||||
.read() : read signal, into dictionary including timestamp
|
||||
.put() : write signal --- seems to have a callback ????
|
||||
.set() : write signal --- seems to have no callback ??
|
||||
|
||||
|
||||
OTHER;
|
||||
.name : name of device
|
||||
|
||||
|
||||
GET INFORMATION ABOUT DEVICES
|
||||
|
||||
._info --- seems to be most complete information dictionarty
|
||||
.describe() --- = ._info['describe']
|
||||
--- info about Signals/Epics channels (dictionary)
|
||||
.describe_configuration
|
||||
--- = ._info['describe_configuration']
|
||||
--- similar to .describe
|
||||
|
||||
.summary -- lists attributes/names and classes of signals (on screen)
|
||||
|
||||
.info --
|
||||
|
||||
._config ._info
|
||||
"""
|
||||
|
||||
self.pm(self.__on_devices)
|
||||
|
||||
def help_scans(self):
|
||||
self.__on_scans = """
|
||||
HELP ON SCANS --- missing
|
||||
"""
|
||||
self.pm(self.__on_scans)
|
||||
|
||||
def help_magic(self):
|
||||
self.__on_magic = """
|
||||
HELP ON MAGIC COMMANDS
|
||||
|
||||
Magic comamnd are called from the iphython shell.
|
||||
BL related magic comamnd are defined in post_startup.py
|
||||
|
||||
All magic command start with %, BL related ones use the name convention
|
||||
%ph_{name}
|
||||
|
||||
|
||||
Example:
|
||||
PHOENIX [8/225] ❯❯ %ph_create_base_config??
|
||||
will initialzes certain %ph_load_xmap
|
||||
%ph_add_xmap
|
||||
|
||||
%ph_restart_bec_server -- restart the bec server and opens a new terminal with tmux
|
||||
|
||||
"""
|
||||
self.pm(self.__on_magic)
|
@ -12,7 +12,7 @@ classifiers = [
|
||||
"Programming Language :: Python :: 3",
|
||||
"Topic :: Scientific/Engineering",
|
||||
]
|
||||
dependencies = []
|
||||
dependencies = ["pandas"]
|
||||
|
||||
[project.optional-dependencies]
|
||||
dev = [
|
||||
|
104
tests/tests_devices/test_phoenix_trigger.py
Normal file
104
tests/tests_devices/test_phoenix_trigger.py
Normal file
@ -0,0 +1,104 @@
|
||||
import threading
|
||||
import time
|
||||
from unittest import mock
|
||||
|
||||
import numpy as np
|
||||
import ophyd
|
||||
import pytest
|
||||
from bec_server.device_server.tests.utils import DMMock
|
||||
from ophyd_devices.interfaces.base_classes.psi_detector_base import DeviceTimeoutError
|
||||
from ophyd_devices.tests.utils import MockPV, patch_dual_pvs
|
||||
|
||||
from phoenix_bec.devices.phoenix_trigger import SAMPLING, PhoenixTrigger
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def mock_trigger():
|
||||
name = "phoenix_trigger"
|
||||
prefix = "X07MB-OP2:"
|
||||
dm = DMMock()
|
||||
with mock.patch.object(dm, "connector"):
|
||||
with (
|
||||
mock.patch(
|
||||
"ophyd_devices.interfaces.base_classes.psi_detector_base.FileWriter"
|
||||
) as filemixin,
|
||||
mock.patch(
|
||||
"ophyd_devices.interfaces.base_classes.psi_detector_base.PSIDetectorBase._update_service_config"
|
||||
) as mock_service_config,
|
||||
):
|
||||
with mock.patch.object(ophyd, "cl") as mock_cl:
|
||||
mock_cl.get_pv = MockPV
|
||||
mock_cl.thread_class = threading.Thread
|
||||
with mock.patch.object(PhoenixTrigger, "_init"):
|
||||
det = PhoenixTrigger(name=name, prefix=prefix, device_manager=dm)
|
||||
patch_dual_pvs(det)
|
||||
det.TIMEOUT_FOR_SIGNALS = 0.1
|
||||
yield det
|
||||
|
||||
|
||||
def test_phoenix_trigger_init(mock_trigger):
|
||||
"""Test PhoenixTrigger init"""
|
||||
assert mock_trigger.name == "phoenix_trigger"
|
||||
assert mock_trigger.prefix == "X07MB-OP2:"
|
||||
|
||||
|
||||
def test_phoenix_trigger_stage(mock_trigger):
|
||||
"""Test PhoenixTrigger on_stage"""
|
||||
with mock.patch.object(mock_trigger.scaninfo, "load_scan_metadata") as mock_load_scan_metadata:
|
||||
mock_trigger.scaninfo.scan_type = "step"
|
||||
mock_trigger.scaninfo.exp_time = exp_time = 1
|
||||
mock_trigger.stage()
|
||||
assert mock_load_scan_metadata.call_count == 1
|
||||
assert mock_trigger.start_csmpl.get() == 0
|
||||
assert mock_trigger.total_cycles.get() == np.ceil(exp_time * 5)
|
||||
assert mock_trigger.smpl.get() == 1
|
||||
|
||||
|
||||
def test_phoenix_trigger_unstage(mock_trigger):
|
||||
"""Test PhoenixTrigger on_unstage"""
|
||||
with mock.patch.object(mock_trigger.custom_prepare, "on_stop") as mock_on_stop:
|
||||
mock_trigger.unstage()
|
||||
assert mock_on_stop.call_count == 1
|
||||
|
||||
|
||||
def test_phoenix_trigger_stop(mock_trigger):
|
||||
"""Test PhoenixTrigger on_stop"""
|
||||
with mock.patch.object(mock_trigger.smpl, "put") as mock_smpl_put:
|
||||
mock_trigger.smpl_done._read_pv.mock_data = SAMPLING.RUNNING
|
||||
mock_trigger.stop()
|
||||
assert mock_trigger.stopped is True
|
||||
# assert mock_trigger.total_cycles.get() == 5
|
||||
# 5 cycles is too tight during development
|
||||
assert mock_trigger.start_csmpl.get() == 1
|
||||
assert mock_smpl_put.call_args_list == [mock.call(1), mock.call(1)]
|
||||
|
||||
|
||||
"""
|
||||
|
||||
uncomment this test, as device names etc will change
|
||||
and as other devices will bee added
|
||||
|
||||
def test_phoenix_trigger_trigger(mock_trigger):
|
||||
#Test PhoenixTrigger on_trigger
|
||||
#
|
||||
#irst test that the trigger timeouts due to readback from smpl_done not being done.
|
||||
#Afterwards, check that status object resolved correctly if smpl_done is done.
|
||||
#
|
||||
exp_time = 0.05
|
||||
mock_trigger.device_manager.add_device("falcon_nohdf5")
|
||||
falcon_state = mock_trigger.device_manager.devices.falcon_nohdf5.state = mock.MagicMock()
|
||||
falcon_state.get = mock.MagicMock(return_value=1)
|
||||
mock_trigger.scaninfo.scan_type = "step"
|
||||
mock_trigger.scaninfo.exp_time = exp_time
|
||||
|
||||
with mock.patch.object(
|
||||
mock_trigger.custom_prepare, "wait_with_status", return_value=mock.MagicMock()
|
||||
) as mock_wait_with_status:
|
||||
status = mock_trigger.trigger()
|
||||
assert mock_wait_with_status.call_count == 1
|
||||
assert mock_wait_with_status.call_args[1]["signal_conditions"] == [
|
||||
(mock_trigger.smpl_done.get, SAMPLING.DONE)
|
||||
]
|
||||
assert mock_wait_with_status.call_args[1]["timeout"] == 5 * exp_time
|
||||
assert mock_wait_with_status.call_args[1]["check_stopped"] is True
|
||||
"""
|
Reference in New Issue
Block a user