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.
@ -1,2 +0,0 @@
|
||||
#to load plugins in this directory upon bec startup, make import in
|
||||
# bec_iphyton_client/startup.py
|
@ -1,7 +1,4 @@
|
||||
"""
|
||||
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.
|
||||
@ -13,11 +10,6 @@ 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
|
||||
@ -32,19 +24,19 @@ to setup the prompts.
|
||||
|
||||
_session_name = "LamNI"
|
||||
lamni = LamNI(bec)
|
||||
logger.succ#ess("LamNI session loaded.")
|
||||
logger.success("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.")
|
||||
#####################################################################
|
||||
"""
|
||||
|
||||
|
||||
# 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
|
||||
|
||||
|
||||
@ -61,40 +53,13 @@ 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')
|
||||
|
||||
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
|
||||
print("autoreload loaded ")
|
||||
|
||||
|
||||
#############################################################################
|
||||
@ -102,60 +67,144 @@ if p_path not in sys.path:
|
||||
# ... register BL specific magic commands
|
||||
#
|
||||
##############################################################################
|
||||
# not clear, error messages. Here we know what we do.
|
||||
|
||||
|
||||
@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 ')
|
||||
|
||||
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()')
|
||||
|
||||
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')
|
||||
print("##################################################################")
|
||||
print("register magic")
|
||||
print("...... %ph_load_xmap ... to reload xmap configuration")
|
||||
|
||||
|
||||
@register_line_magic
|
||||
def ph_load_xmap(line):
|
||||
|
||||
###
|
||||
#magic for loading xmap
|
||||
###
|
||||
|
||||
def ph_add_xmap(line):
|
||||
"""
|
||||
magic for loading xmap
|
||||
"""
|
||||
t0 = tt.time()
|
||||
phoenix_server.add_xmap()
|
||||
print('elapsed time:', tt.time()-t0)
|
||||
phoenix.add_xmap()
|
||||
print("elapsed time:", tt.time() - t0)
|
||||
|
||||
|
||||
# enddef
|
||||
|
||||
print('...... %ph_load_falcon ... to reload falcon configuration')
|
||||
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
|
||||
|
||||
"""
|
||||
magic to load falcon as sole detector
|
||||
"""
|
||||
t0 = tt.time()
|
||||
phoenix_server.add_falcon()
|
||||
print('elapsed time:', tt.time()-t0)
|
||||
#enddef
|
||||
phoenix.load_falcon()
|
||||
print("elapsed time:", tt.time() - t0)
|
||||
|
||||
|
||||
print("...... %ph_add_xmap ... to add xmap to default configuration")
|
||||
|
||||
|
||||
print('...... %ph_load_config ... to reload phoenix default configuration')
|
||||
@register_line_magic
|
||||
def ph_load_config(line):
|
||||
def ph_add_xmap(line):
|
||||
"""
|
||||
magic to add falcon to existing configuration
|
||||
"""
|
||||
t0 = tt.time()
|
||||
phoenix_server.add_phoenix_config()
|
||||
print('elapsed time:', tt.time()-t0)
|
||||
#enddef
|
||||
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""
|
||||
# )
|
||||
|
||||
|
||||
##@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...
|
||||
|
||||
@ -164,19 +213,26 @@ def ph_load_config(line):
|
||||
# init phoenix.py from server version as
|
||||
# .................. phoenix_server=PhoenixBL()
|
||||
# and in ipython shell only as
|
||||
# .............. phoenix = Ph.Pheonix(BL()
|
||||
# .............. 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 ')
|
||||
|
||||
#############################################")
|
||||
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)')
|
||||
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
|
||||
|
||||
|
||||
#from phoenix_bec.bec_ipython_client.plugins.phoenix import Phoenix
|
||||
#from phoenix_bec.devices.falcon_phoenix_no_hdf5 import FalconHDF5Plugins
|
||||
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
|
@ -21,6 +21,3 @@ def extend_command_line_args(parser):
|
||||
# Create and return the service configuration.
|
||||
# """
|
||||
# return ServiceConfig(redis={"host": "localhost", "port": 6379})
|
||||
|
||||
|
||||
|
||||
|
@ -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})
|
||||
|
||||
|
||||
|
@ -1 +0,0 @@
|
||||
|
||||
|
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
|
||||
|
@ -1,64 +1,283 @@
|
||||
|
||||
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
|
||||
#
|
||||
############################
|
||||
|
||||
ScanX:
|
||||
MA1_ScanX:
|
||||
readoutPriority: baseline
|
||||
description: 'Horizontal sample position'
|
||||
description: 'Vertical sample position ES-MA1.ScanX'
|
||||
deviceClass: ophyd.EpicsMotor
|
||||
deviceConfig:
|
||||
prefix: 'X07MB-ES-MA1:ScanX'
|
||||
deviceTags:
|
||||
- ES-MA1
|
||||
- phoenix_bec/device_configs/phoenix_devices.yaml
|
||||
- ES-MA1.ScanX
|
||||
- phoenix_devices.yaml
|
||||
- class EpicsMotor
|
||||
onFailure: retry
|
||||
enabled: true
|
||||
enabled: false
|
||||
readOnly: false
|
||||
softwareTrigger: false
|
||||
|
||||
ScanY:
|
||||
MA1_ScanY:
|
||||
readoutPriority: baseline
|
||||
description: 'Horizontal sample position'
|
||||
description: 'Horizontal sample position ES-MA1.ScanY'
|
||||
deviceClass: ophyd.EpicsMotor
|
||||
deviceConfig:
|
||||
prefix: 'X07MB-ES-MA1:ScanY'
|
||||
deviceTags:
|
||||
- ES-MA1
|
||||
- phoenix_bec/device_configs/phoenix_devices.yaml
|
||||
- ES-MA1.ScanY
|
||||
- phoenix_devices.yaml
|
||||
- class EpicsMotor
|
||||
onFailure: retry
|
||||
enabled: true
|
||||
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: monitored
|
||||
description: DIODE
|
||||
readoutPriority: baseline
|
||||
description: DIODE SAI07
|
||||
deviceClass: ophyd.EpicsSignalRO
|
||||
deviceConfig:
|
||||
auto_monitor: true
|
||||
read_pv: 'X07MB-OP2-SAI_07:MEAN'
|
||||
deviceTags:
|
||||
- PHOENIX
|
||||
- phoenix_bec/device_configs/phoenix_devices.yaml
|
||||
- phoenix_devices.yaml
|
||||
- class EpicsSignalRO
|
||||
onFailure: buffer
|
||||
enabled: true
|
||||
readOnly: true
|
||||
softwareTrigger: false
|
||||
|
||||
SAI_08_MEAN:
|
||||
readoutPriority: monitored
|
||||
readoutPriority: baseline
|
||||
description: DIODE
|
||||
deviceClass: ophyd.EpicsSignalRO
|
||||
deviceConfig:
|
||||
@ -66,8 +285,29 @@ SAI_08_MEAN:
|
||||
read_pv: 'X07MB-OP2-SAI_08:MEAN'
|
||||
deviceTags:
|
||||
- PHOENIX
|
||||
- phoenix_bec/device_configs/phoenix_devices.yaml
|
||||
- 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
|
||||
|
||||
|
@ -1,14 +1,23 @@
|
||||
falcon_nohdf5:
|
||||
description: Falcon detector x-ray fluoresence II
|
||||
deviceClass: phoenix_bec.devices.falcon_phoenix_no_hdf5.FalconcSAXS
|
||||
|
||||
#
|
||||
# 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
|
||||
- no hdf5
|
||||
- phoenix_devices.yaml
|
||||
- with hdf5
|
||||
- phoenix_falcon.yaml
|
||||
- class FalconPhoenix
|
||||
- file:phoenix_falcon.py
|
||||
onFailure: buffer
|
||||
enabled: true
|
||||
readoutPriority: async
|
||||
softwareTrigger: false
|
||||
#
|
||||
# END FALCON with HDF5
|
||||
#
|
@ -1,14 +1,19 @@
|
||||
xmap_nohdf5:
|
||||
description: XMAP detector x-ray fluoresence II
|
||||
deviceClass: phoenix_bec.devices.xmap_phoenix_no_hdf5.XMAPphoenix
|
||||
#
|
||||
# Configuration XMAP
|
||||
#
|
||||
xmap:
|
||||
description: XMAP detector x-ray fluoresence
|
||||
deviceClass: phoenix_bec.devices.phoenix_xmap.XMAPPhoenix
|
||||
deviceConfig:
|
||||
prefix: 'X07MB-XMAP:'
|
||||
deviceTags:
|
||||
- phoenix
|
||||
- xmap
|
||||
- no hdf5
|
||||
- phoenix_bec/device_configs/phoenix_xmap.yaml
|
||||
- class XMAPPhoenix
|
||||
- file phoenix_xmap
|
||||
onFailure: buffer
|
||||
enabled: true
|
||||
readoutPriority: async
|
||||
softwareTrigger: false
|
||||
|
||||
|
@ -1,243 +0,0 @@
|
||||
from ophyd import (
|
||||
ADComponent as ADCpt,
|
||||
Device,
|
||||
DeviceStatus,
|
||||
)
|
||||
|
||||
from ophyd import Component as Cpt
|
||||
from ophyd import Device, EpicsSignal, EpicsSignalRO
|
||||
|
||||
from ophyd_devices.interfaces.base_classes.psi_detector_base import PSIDetectorBase, CustomDetectorMixin
|
||||
|
||||
from bec_lib import bec_logger, messages
|
||||
from bec_lib.endpoints import MessageEndpoints
|
||||
|
||||
logger = bec_logger.logger
|
||||
|
||||
DETECTOR_TIMEOUT = 5
|
||||
|
||||
class PhoenixTriggerError(Exception):
|
||||
"""Base class for exceptions in this module."""
|
||||
|
||||
|
||||
class PhoenixTriggerTimeoutError(XMAPError):
|
||||
"""Raised when the PhoenixTrigger does not respond in time."""
|
||||
|
||||
|
||||
class PhoenixTriggerDetectorState(enum.IntEnum):
|
||||
"""Detector states for XMAP detector"""
|
||||
|
||||
DONE = 0
|
||||
ACQUIRING = 1
|
||||
|
||||
|
||||
|
||||
|
||||
class PhoenixTriggerSetup(CustomDetectorMixin):
|
||||
"""
|
||||
This defines the trigger setup.
|
||||
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, *args, parent:Device = None, **kwargs):
|
||||
super().__init__(*args, parent=parent, **kwargs)
|
||||
self._counter = 0
|
||||
|
||||
def on_stage(self):
|
||||
exposure_time = self.parent.scaninfo.exp_time
|
||||
num_points = self.parent.scaninfo.num_points
|
||||
|
||||
# camera acquisition parameters
|
||||
self.parent.cam.array_counter.put(0)
|
||||
if self.parent.scaninfo.scan_type == 'step':
|
||||
self.parent.cam.acquire_time.put(exposure_time)
|
||||
self.parent.cam.num_images.put(1)
|
||||
self.parent.cam.image_mode.put(0) # Single
|
||||
self.parent.cam.trigger_mode.put(0) # auto
|
||||
else:
|
||||
# In flyscan, the exp_time is the time between two triggers,
|
||||
# which minus 15% is used as the acquisition time.
|
||||
self.parent.cam.acquire_time.put(exposure_time * 0.85)
|
||||
self.parent.cam.num_images.put(num_points)
|
||||
self.parent.cam.image_mode.put(1) # Multiple
|
||||
self.parent.cam.trigger_mode.put(1) # trigger
|
||||
self.parent.cam.acquire.put(1, wait=False) # arm
|
||||
|
||||
# file writer
|
||||
self.parent.hdf.lazy_open.put(1)
|
||||
self.parent.hdf.num_capture.put(num_points)
|
||||
self.parent.hdf.file_write_mode.put(2) # Stream
|
||||
self.parent.hdf.capture.put(1, wait=False)
|
||||
self.parent.hdf.enable.put(1) # enable plugin
|
||||
|
||||
# roi statistics to collect signal and background in a timeseries
|
||||
self.parent.roistat.enable.put(1)
|
||||
self.parent.roistat.ts_num_points.put(num_points)
|
||||
self.parent.roistat.ts_control.put(0, wait=False) # Erase/Start
|
||||
|
||||
logger.success('XXXX stage XXXX')
|
||||
|
||||
def on_trigger(self):
|
||||
self.parent.cam.acquire.put(1, wait=False)
|
||||
logger.success('XXXX trigger XXXX')
|
||||
|
||||
return self.wait_with_status(
|
||||
[(self.parent.cam.acquire.get, 0)],
|
||||
self.parent.scaninfo.exp_time + DETECTOR_TIMEOUT,
|
||||
all_signals=True
|
||||
)
|
||||
|
||||
def on_complete(self):
|
||||
status = DeviceStatus(self.parent)
|
||||
|
||||
if self.parent.scaninfo.scan_type == 'step':
|
||||
timeout = DETECTOR_TIMEOUT
|
||||
else:
|
||||
timeout = self.parent.scaninfo.exp_time * self.parent.scaninfo.num_points + DETECTOR_TIMEOUT
|
||||
logger.success('XXXX %s XXXX' % self.parent.roistat.ts_acquiring.get())
|
||||
success = self.wait_for_signals(
|
||||
[
|
||||
(self.parent.cam.acquire.get, 0),
|
||||
(self.parent.hdf.capture.get, 0),
|
||||
(self.parent.roistat.ts_acquiring.get, 'Done')
|
||||
],
|
||||
timeout,
|
||||
check_stopped=True,
|
||||
all_signals=True
|
||||
)
|
||||
|
||||
# publish file location
|
||||
self.parent.filepath.put(self.parent.hdf.full_file_name.get())
|
||||
self.publish_file_location(done=True, successful=success)
|
||||
|
||||
# publish timeseries data
|
||||
metadata = self.parent.scaninfo.scan_msg.metadata
|
||||
metadata.update({"async_update": "append", "num_lines": self.parent.roistat.ts_current_point.get()})
|
||||
msg = messages.DeviceMessage(
|
||||
signals={
|
||||
self.parent.roistat.roi1.name_.get(): {
|
||||
'value': self.parent.roistat.roi1.ts_total.get(),
|
||||
},
|
||||
self.parent.roistat.roi2.name_.get(): {
|
||||
'value': self.parent.roistat.roi2.ts_total.get(),
|
||||
},
|
||||
},
|
||||
metadata=self.parent.scaninfo.scan_msg.metadata
|
||||
)
|
||||
self.parent.connector.xadd(
|
||||
topic=MessageEndpoints.device_async_readback(
|
||||
scan_id=self.parent.scaninfo.scan_id, device=self.parent.name
|
||||
),
|
||||
msg_dict={"data": msg},
|
||||
expire=1800,
|
||||
)
|
||||
|
||||
logger.success('XXXX complete %d XXXX' % success)
|
||||
if success:
|
||||
status.set_finished()
|
||||
else:
|
||||
status.set_exception(TimeoutError())
|
||||
return status
|
||||
|
||||
def on_stop(self):
|
||||
logger.success('XXXX stop XXXX')
|
||||
self.parent.cam.acquire.put(0)
|
||||
self.parent.hdf.capture.put(0)
|
||||
self.parent.roistat.ts_control.put(2)
|
||||
|
||||
def on_unstage(self):
|
||||
self.parent.cam.acquire.put(0)
|
||||
self.parent.hdf.capture.put(0)
|
||||
self.parent.roistat.ts_control.put(2)
|
||||
logger.success('XXXX unstage XXXX')
|
||||
|
||||
|
||||
class EigerROIStatPlugin(ROIStatPlugin):
|
||||
roi1 = ADCpt(ROIStatNPlugin, '1:')
|
||||
roi2 = ADCpt(ROIStatNPlugin, '2:')
|
||||
|
||||
class PhoenixTrigger(PSIDetectorBase):
|
||||
|
||||
"""
|
||||
Parent class: PSIDetectorBase
|
||||
|
||||
class attributes:
|
||||
custom_prepare_cls (XMAPSetup) : Custom detector setup class for cSAXS,
|
||||
inherits from CustomDetectorMixin
|
||||
in __init__ of PSIDetecor bases
|
||||
class is initialized
|
||||
self.custom_prepare = self.custom_prepare_cls(parent=self, **kwargs)
|
||||
PSIDetectorBase.set_min_readout (float) : Minimum readout time for the detector
|
||||
dxp (EpicsDXPXMAP) : DXP parameters for XMAP detector
|
||||
mca (EpicsMCARecord) : MCA parameters for XMAP detector
|
||||
hdf5 (XMAPHDF5Plugins) : HDF5 parameters for XMAP detector
|
||||
MIN_READOUT (float) : Minimum readout time for the detector
|
||||
|
||||
|
||||
The class PhoenixTrigger is the class to be called via yaml configuration file
|
||||
the input arguments are defined by PSIDetectorBase,
|
||||
and need to be given in the yaml configuration file.
|
||||
To adress chanels such as 'X07MB-OP2:SMPL-DONE':
|
||||
|
||||
use prefix 'X07MB-OP2:' in the device definition in the yaml configuration file.
|
||||
|
||||
PSIDetectorBase(
|
||||
prefix='',
|
||||
*,Q
|
||||
name,
|
||||
kind=None,
|
||||
parent=None,
|
||||
device_manager=None,
|
||||
**kwargs,
|
||||
)
|
||||
Docstring:
|
||||
Abstract base class for SLS detectors
|
||||
|
||||
Class attributes:
|
||||
custom_prepare_cls (object): class for custom prepare logic (BL specific)
|
||||
|
||||
Args:
|
||||
prefix (str): EPICS PV prefix for component (optional)
|
||||
name (str): name of the device, as will be reported via read()
|
||||
kind (str): member of class 'ophydobj.Kind', defaults to Kind.normal
|
||||
omitted -> readout ignored for read 'ophydobj.read()'
|
||||
normal -> readout for read
|
||||
config -> config parameter for 'ophydobj.read_configuration()'
|
||||
hinted -> which attribute is readout for read
|
||||
parent (object): instance of the parent device
|
||||
device_manager (object): bec device manager
|
||||
**kwargs: keyword arguments
|
||||
File: /data/test/x07mb-test-bec/bec_deployment/ophyd_devices/ophyd_devices/interfaces/base_classes/psi_detector_base.py
|
||||
Type: type
|
||||
Subclasses: SimCamera, SimMonitorAsync
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
"""
|
||||
#custom_prepare_cls = PhoenixTriggerSetup
|
||||
|
||||
#cam = ADCpt(SLSDetectorCam, 'cam1:')
|
||||
|
||||
|
||||
#X07MB-OP2:START-CSMPL.. cont on / off
|
||||
|
||||
# X07MB-OP2:SMPL.. take single sample
|
||||
#X07MB-OP2:INTR-COUNT.. counter run up
|
||||
#X07MB-OP2:TOTAL-CYCLES .. cycles set
|
||||
#X07MB-OP2:SMPL-DONE
|
||||
|
||||
QUESTION HOW does ADCpt kno the EPICS prefix??????
|
||||
|
||||
#image = ADCpt(ImagePlugin, 'image1:')
|
||||
#roi1 = ADCpt(ROIPlugin, 'ROI1:')
|
||||
#roi2 = ADCpt(ROIPlugin, 'ROI2:')
|
||||
#stats1 = ADCpt(StatsPlugin, 'Stats1:')
|
||||
#stats2 = ADCpt(StatsPlugin, 'Stats2:')
|
||||
roistat = ADCpt(EigerROIStatPlugin, 'ROIStat1:')
|
||||
#roistat1 = ADCpt(ROIStatNPlugin, 'ROIStat1:1:')
|
||||
#roistat2 = ADCpt(ROIStatNPlugin, 'ROIStat1:2:')
|
||||
hdf = ADCpt(HDF5Plugin, 'HDF1:')
|
||||
)
|
@ -1 +1 @@
|
||||
from .falcon_phoenix_no_hdf5 import FalconcSAXS
|
||||
from .phoenix_trigger import PhoenixTrigger
|
||||
|
@ -1,345 +0,0 @@
|
||||
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)
|
@ -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)
|
@ -97,6 +97,7 @@ class FalconHDF5Plugins(Device):
|
||||
array_counter = Cpt(EpicsSignalWithRBV, "ArrayCounter", kind="config")
|
||||
"""
|
||||
|
||||
|
||||
class FalconSetup(CustomDetectorMixin):
|
||||
"""
|
||||
Falcon setup class for cSAXS
|
||||
@ -113,7 +114,7 @@ class FalconSetup(CustomDetectorMixin):
|
||||
"""Initialize Falcon detector"""
|
||||
self.initialize_default_parameter()
|
||||
self.initialize_detector()
|
||||
self.initialize_detector_backend()
|
||||
# self.initialize_detector_backend()
|
||||
|
||||
def initialize_default_parameter(self) -> None:
|
||||
"""
|
||||
@ -139,12 +140,12 @@ class FalconSetup(CustomDetectorMixin):
|
||||
def initialize_detector(self) -> None:
|
||||
"""Initialize Falcon detector"""
|
||||
self.stop_detector()
|
||||
self.stop_detector_backend()
|
||||
# self.stop_detector_backend()
|
||||
self.set_trigger(
|
||||
mapping_mode=MappingSource.MAPPING, trigger_source=TriggerSource.GATE, ignore_gate=0
|
||||
mapping_mode=MappingSource.SPECTRUM, trigger_source=TriggerSource.GATE, ignore_gate=0
|
||||
)
|
||||
# 1 Realtime
|
||||
self.parent.preset_mode.put(1)
|
||||
self.parent.preset_mode.put(0)
|
||||
# 0 Normal, 1 Inverted
|
||||
self.parent.input_logic_polarity.put(0)
|
||||
# 0 Manual 1 Auto
|
||||
@ -170,14 +171,18 @@ class FalconSetup(CustomDetectorMixin):
|
||||
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.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.MAPPING, trigger_source=TriggerSource.GATE, ignore_gate=0
|
||||
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(
|
||||
@ -234,6 +239,7 @@ class FalconSetup(CustomDetectorMixin):
|
||||
# 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()
|
||||
@ -307,19 +313,17 @@ class FalconSetup(CustomDetectorMixin):
|
||||
self.parent.ignore_gate.put(ignore_gate)
|
||||
|
||||
|
||||
class FalconcSAXS(PSIDetectorBase):
|
||||
class FalconPhoenix(PSIDetectorBase):
|
||||
"""
|
||||
Falcon Sitoro detector for CSAXS
|
||||
|
||||
Parent class: PSIDetectorBase
|
||||
|
||||
class attributes:
|
||||
custom_prepare_cls (FalconSetup) : Custom detector setup class for cSAXS,
|
||||
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 (EpicsDXPFalcon) : DXP parameters for Falcon detector
|
||||
mca (EpicsMCARecord) : MCA parameters for Falcon detector
|
||||
hdf5 (FalconHDF5Plugins) : HDF5 parameters for Falcon 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
|
||||
"""
|
||||
|
||||
@ -360,4 +364,4 @@ class FalconcSAXS(PSIDetectorBase):
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
falcon = FalconcSAXS(name="falcon", prefix="X12SA-SITORO:", sim_mode=True)
|
||||
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)
|
@ -78,7 +78,6 @@ class EpicsDXPXMAP(Device):
|
||||
|
||||
current_pixel = Cpt(EpicsSignalRO, "CurrentPixel")
|
||||
|
||||
Q
|
||||
class XMAPHDF5Plugins(Device):
|
||||
"""
|
||||
HDF5 parameters for XMAP detector
|
||||
@ -308,13 +307,10 @@ class XMAPSetup(CustomDetectorMixin):
|
||||
self.parent.ignore_gate.put(ignore_gate)
|
||||
|
||||
|
||||
class XMAPphoenix(PSIDetectorBase):
|
||||
class XMAPPhoenix(PSIDetectorBase):
|
||||
"""MCA
|
||||
XMAP detector for phoenix
|
||||
|
||||
Parent class: PSIDetectorBase
|
||||
|
||||
class attributes:
|
||||
custom_prepare_cls (XMAPSetu
|
||||
custom_prepare_cls (XMAPSetup) : Custom detector setup class for cSAXS,
|
||||
inherits from CustomDetectorMixin
|
||||
in __init__ of PSIDetecor base
|
||||
@ -338,9 +334,9 @@ class XMAPphoenix(PSIDetectorBase):
|
||||
dxp = Cpt(EpicsDXPXMAP, "dxp1:")
|
||||
|
||||
mca1 = Cpt(EpicsMCARecord, "mca1")
|
||||
mca2 = Cpt(EpicsMCARecord, "mca2")
|
||||
mca3 = Cpt(EpicsMCARecord, "mca3")
|
||||
mca4 = Cpt(EpicsMCARecord, "mca4")
|
||||
#mca2 = Cpt(EpicsMCARecord, "mca2")
|
||||
#mca3 = Cpt(EpicsMCARecord, "mca3")
|
||||
#mca4 = Cpt(EpicsMCARecord, "mca4")
|
||||
print('load hdf5')
|
||||
#hdf5 = Cpt(XMAPHDF5Plugins, "HDF1:")
|
||||
|
||||
@ -363,5 +359,11 @@ class XMAPphoenix(PSIDetectorBase):
|
||||
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()
|
@ -1,4 +1,4 @@
|
||||
from phoenix_bec.devices.xmap_phoenix_no_hdf5 import XMAPphoenix
|
||||
from phoenix_bec.devices.obsolete.xmap_phoenix_no_hdf5 import XMAPphoenix
|
||||
from phoenix_bec.scripts.phoenix import PhoenixBL
|
||||
|
||||
phoenix=PhoenixBL()
|
||||
|
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,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
@ -1,51 +0,0 @@
|
||||
from ophyd import Device, EpicsMotor, EpicsSignal, EpicsSignalRO
|
||||
from ophyd import Component as Cpt
|
||||
|
||||
#option I via direct acces to classes
|
||||
|
||||
ScanX = EpicsMotor(name='ScanX',prefix='X07MB-ES-MA1:ScanX')
|
||||
ScanY = EpicsMotor(name='ScanY',prefix='X07MB-ES-MA1:ScanY')
|
||||
DIODE = EpicsSignal(name='SI',read_pv='X07MB-OP2-SAI_07:MEAN')
|
||||
SMPL = EpicsSignal(name='SMPL',read_pv='X07MB-OP2:SMPL')
|
||||
CYCLES = EpicsSignal(name='SMPL',read_pv='X07MB-OP2:TOTAL-CYCLES',write_pv='X07MB-OP2:TOTAL-CYCLES')
|
||||
|
||||
|
||||
prefix='XXXX:'
|
||||
y_cpt = Cpt(EpicsMotor, 'ScanX')
|
||||
# Option 2 using component
|
||||
prefix='X07MB-ES-MA1:'
|
||||
dd=Device('X07MB-ES-MA1:',name=('device'))
|
||||
y_cpt_prefix = Cpt(EpicsMotor,'ScanX',parent=Device)
|
||||
|
||||
class StageXY(Device):
|
||||
|
||||
x = Cpt(EpicsMotor, 'ScanX')
|
||||
y = Cpt(EpicsMotor, 'ScanY')
|
||||
|
||||
xy_stage = StageXY('X07MB-ES-MA1:', name='stage')
|
||||
|
||||
#############################################
|
||||
# This is basic bluesky
|
||||
# Epics motor def seems to use init params in device, whcih are
|
||||
# __init__(
|
||||
# self,
|
||||
# prefix="",
|
||||
# *,
|
||||
# name,
|
||||
# kind=None,
|
||||
# read_attrs=None,
|
||||
# configuration_attrs=None,
|
||||
# parent=None,
|
||||
# **kwargs,
|
||||
# ):
|
||||
#
|
||||
#########################################################
|
||||
|
||||
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,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"
|
||||
)
|
@ -1,86 +0,0 @@
|
||||
#from unittest import mock
|
||||
import numpy as np
|
||||
#import pandas
|
||||
#import pytest
|
||||
#from bec_lib import messages
|
||||
#import device_server
|
||||
#from ophyd import Component as Cpt
|
||||
from ophyd import Device, EpicsMotor, EpicsSignal, EpicsSignalRO
|
||||
#from ophyd import FormattedComponent as FCpt
|
||||
#from ophyd import Kind, PVPositioner, Signal
|
||||
#from ophyd.flyers import FlyerInterface
|
||||
#from ophyd.pv_positioner import PVPositionerComparator
|
||||
#from ophyd.status import DeviceStatus, SubscriptionStatus
|
||||
|
||||
import time as tt
|
||||
|
||||
#import ophyd
|
||||
import os
|
||||
import sys
|
||||
|
||||
#logger = bec_logger.logger
|
||||
# load simulation
|
||||
|
||||
#bec.config.load_demo_config()
|
||||
|
||||
bec.config.update_session_with_file("config/config_1.yaml")
|
||||
|
||||
os.system('mv *.yaml tmp')
|
||||
|
||||
|
||||
|
||||
class PhoenixBL:
|
||||
|
||||
#define some epics channels
|
||||
|
||||
def __init__(self):
|
||||
from ophyd import Device, EpicsMotor, EpicsSignal, EpicsSignalRO
|
||||
from ophyd import Component as Cpt
|
||||
self.ScanX = EpicsMotor(name='ScanX',prefix='X07MB-ES-MA1:ScanX')
|
||||
self.ScanY = EpicsMotor(name='ScanY',prefix='X07MB-ES-MA1:ScanY')
|
||||
self.DIODE = EpicsSignal(name='SI',read_pv='X07MB-OP2-SAI_07:MEAN')
|
||||
self.SIG = Cpt(EpicsSignal,name='we',read_pv="X07MB-OP2-SAI_07:MEAN")
|
||||
self.SMPL = EpicsSignal(name='SMPL',read_pv='X07MB-OP2:SMPL')
|
||||
self.CYCLES = EpicsSignal(name='SMPL',read_pv='X07MB-OP2:TOTAL-CYCLES',write_pv='X07MB-OP2:TOTAL-CYCLES')
|
||||
self.fielda =EpicsSignal(name='SMPL',read_pv='X07MB-SCAN:scan1.P1SP',write_pv='X07MB-SCAN:scan1.P1SP')
|
||||
#end class
|
||||
|
||||
ph=PhoenixBL()
|
||||
|
||||
print('---------------------------------')
|
||||
|
||||
# scan will not diode
|
||||
print(' SCAN DO NOT READ DIODE ')
|
||||
dev.PH_curr_conf.readout_priority='baseline' # do not read detector
|
||||
ti=tt.time_ns()
|
||||
s1=scans.line_scan(dev.PH_ScanX_conf,0,0.002,steps=4,exp_time=.01,relative=False,delay=2)
|
||||
tf=tt.time_ns()
|
||||
|
||||
print('elapsed time',(tf-ti)/1e9)
|
||||
# scan will read diode
|
||||
print(' SCAN READ DIODE ')
|
||||
tt.sleep(2)
|
||||
dev.PH_curr_conf.readout_priority='monitored' # read detector
|
||||
|
||||
s2=scans.line_scan(dev.PH_ScanX_conf,0,0.002,steps=11,exp_time=.3,relative=False,delay=2)
|
||||
|
||||
|
||||
"""
|
||||
next lines do not work as pandas is not installed on test system
|
||||
|
||||
res1 = s1.scan.to_pandas()
|
||||
re1 = res1.to_numpy()
|
||||
print('Scana')
|
||||
print(res1)
|
||||
print('')
|
||||
print('Scan2 at pandas ')
|
||||
print(res2)
|
||||
print('Scan2 as numpy ')
|
||||
print(res2)
|
||||
"""
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
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
|
@ -1,8 +1,10 @@
|
||||
"""
|
||||
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
|
||||
@ -19,34 +21,42 @@ import importlib
|
||||
import ophyd
|
||||
|
||||
|
||||
#logger = bec_logger.logger
|
||||
# load local configuration
|
||||
#bec.config.load_demo_config()
|
||||
|
||||
# .. define base path for directory with scripts
|
||||
|
||||
PhoenixBL=0
|
||||
from ConfigPHOENIX.config.phoenix import PhoenixBL
|
||||
#from ConfigPHOENIX.devices.falcon_csaxs import FalconSetup
|
||||
# initialize general parameter
|
||||
ph=PhoenixBL()
|
||||
|
||||
bec.config.update_session_with_file('./ConfigPHOENIX/device_config/phoenix_devices.yaml')
|
||||
#
|
||||
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")
|
||||
|
||||
|
||||
s1=scans.line_scan(dev.ScanX,0,0.002,steps=4,exp_time=1,relative=False,delay=2)
|
||||
|
||||
s2=scans.phoenix_line_scan(dev.ScanX,0,0.002,steps=4,exp_time=.01,relative=False,delay=2)
|
||||
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("---------------------------------")
|
||||
|
||||
"""
|
||||
print('---------------------------------')
|
||||
|
||||
# scan will not diode
|
||||
print(' SCAN DO NOT READ DIODE ')
|
||||
dev.PH_curr_conf.readout_priority='baseline' # do not read detector
|
||||
@ -59,9 +69,8 @@ print('elapsed time',(tf-ti)/1e9)
|
||||
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
|
||||
|
||||
#next lines do not work as pandas is not installed on test system
|
||||
|
||||
res1 = s1.scan.to_pandas()
|
||||
re1 = res1.to_numpy()
|
||||
@ -72,4 +81,5 @@ 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
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
|
@ -1,2 +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)
|
@ -1,2 +1 @@
|
||||
from .phoenix_line_scan import PhoenixLineScan
|
||||
|
||||
from .phoenix_scans import PhoenixLineScan
|
||||
|
@ -22,12 +22,43 @@ but they are executed in a specific order:
|
||||
- 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_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
|
||||
@ -35,15 +66,56 @@ but they are executed in a specific order:
|
||||
# logger = bec_logger.logger
|
||||
|
||||
|
||||
from bec_server.scan_server.scans import ScanBase, ScanArgType
|
||||
import numpy as np
|
||||
import time
|
||||
from bec_lib.logger import bec_logger
|
||||
|
||||
logger = bec_logger.logger
|
||||
|
||||
class PhoenixLineScan(ScanBase):
|
||||
scan_name = "phoenix_line_scanZZZ"
|
||||
|
||||
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,
|
||||
@ -78,38 +150,25 @@ class PhoenixLineScan(ScanBase):
|
||||
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
|
||||
print('INIT CLASS PhoenixLineScan')
|
||||
time.sleep(1)
|
||||
|
||||
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)
|
||||
|
||||
def _at_each_point(self, ind=None, pos=None):
|
||||
yield from self._move_scan_motors_and_wait(pos)
|
||||
if ind > 0:
|
||||
yield from self.stubs.wait(
|
||||
wait_type="read", group="primary", wait_group="readout_primary"
|
||||
)
|
||||
time.sleep(self.settling_time)
|
||||
if self.setup_device:
|
||||
yield from self.stubs.send_rpc_and_wait(self.setup_device, "velocity.set", 1)
|
||||
yield from self.stubs.trigger(group="trigger", point_id=self.point_id)
|
||||
yield from self.stubs.wait(wait_type="trigger", group="trigger", wait_time=self.exp_time)
|
||||
yield from self.stubs.read(
|
||||
group="primary", wait_group="readout_primary", point_id=self.point_id
|
||||
)
|
||||
yield from self.stubs.wait(
|
||||
wait_type="read", group="scan_motor", wait_group="readout_primary"
|
||||
)
|
||||
|
||||
self.point_id += 1
|
||||
self.p_s("done")
|
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,15 +1,24 @@
|
||||
"""
|
||||
|
||||
General collection of calsses for PHEONIX beamline
|
||||
|
||||
"""
|
||||
|
||||
# from unittest import mock
|
||||
import os
|
||||
import sys
|
||||
import time as tt
|
||||
|
||||
# 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 Device, EpicsMotor, EpicsSignal, EpicsSignalRO
|
||||
|
||||
# from ophyd import FormattedComponent as FCpt
|
||||
# from ophyd import Kind, PVPositioner, Signal
|
||||
# from ophyd.flyers import FlyerInterface
|
||||
@ -18,6 +27,9 @@ from ophyd import Device, EpicsMotor, EpicsSignal, EpicsSignalRO
|
||||
|
||||
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
|
||||
|
||||
|
||||
@ -28,65 +40,376 @@ logger = bec_logger.logger
|
||||
|
||||
# .. define base path for directory with scripts
|
||||
|
||||
class PhoenixBL():
|
||||
|
||||
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()
|
||||
|
||||
import os
|
||||
self.silent = False
|
||||
print("..... init PhoenixBL from phoenix_bec/scripts/phoenix.py")
|
||||
|
||||
print('..... init PhoenixBL from phoenix_bec/scripts/phoenix.py')
|
||||
# Define important paths
|
||||
|
||||
#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.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/"
|
||||
|
||||
# load local configuration
|
||||
self.path_scripts = self.path_phoenix_bec + "scripts/"
|
||||
self.path_scans = self.path_phoenix_bec + "scans/"
|
||||
|
||||
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_scripts_local = self.path_phoenix_bec + "local_scripts/"
|
||||
self.path_config_local = self.path_scripts_local + "TEST_ConfigPhoenix/"
|
||||
|
||||
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
|
||||
# yamal file for default configuration
|
||||
|
||||
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)
|
||||
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):
|
||||
print('add_phoenix_config ')
|
||||
print('self.file_devices_file')
|
||||
bec.config.update_session_with_file(self.file_devices_file)
|
||||
"""
|
||||
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):
|
||||
print('add xmap ')
|
||||
print(self.path_devices+'phoenix_xmap.yaml')
|
||||
"""Add XMAP to config"""
|
||||
print("add xmap ")
|
||||
|
||||
bec.config.update_session_with_file(self.path_devices+'phoenix_xmap.yaml',timeout=100)
|
||||
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):
|
||||
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')
|
||||
"""Add Falcon to config"""
|
||||
print("add_falcon to existing configuration ")
|
||||
|
||||
def show_phoenix_setup(self):
|
||||
print(self.path_phoenix_bec)
|
||||
os.system('cat '+self.path_phoenix_bec+'phoenix_bec/scripts/Current_setup.txt')
|
||||
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