Add magic to post_startup.py to restart bec server from command line, first version of TTL Trigger device
This commit is contained in:
@ -36,6 +36,7 @@ to setup the prompts.
|
|||||||
# pylint: disable=invalid-name, unused-import, import-error, undefined-variable, unused-variable, unused-argument, no-name-in-module
|
# pylint: disable=invalid-name, unused-import, import-error, undefined-variable, unused-variable, unused-argument, no-name-in-module
|
||||||
import time as tt
|
import time as tt
|
||||||
import sys
|
import sys
|
||||||
|
import os
|
||||||
from IPython.core.magic import register_line_magic
|
from IPython.core.magic import register_line_magic
|
||||||
|
|
||||||
|
|
||||||
@ -132,6 +133,10 @@ def ph_load_config(line):
|
|||||||
print('elapsed time:', tt.time()-t0)
|
print('elapsed time:', tt.time()-t0)
|
||||||
#enddef
|
#enddef
|
||||||
|
|
||||||
|
@register_line_magic
|
||||||
|
def ph_restart_bec_server(line):
|
||||||
|
os.system('bec-server restart')
|
||||||
|
os.system('gnome-terminal --geometry 120X50 -- bash -c "bec-server attach; exec bash"')
|
||||||
|
|
||||||
##@register_line_magic
|
##@register_line_magic
|
||||||
#def ph_post_startup(line):
|
#def ph_post_startup(line):
|
||||||
|
@ -3,23 +3,15 @@
|
|||||||
# phoenix standard devices (motors)
|
# phoenix standard devices (motors)
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
#####################################################
|
####################################################:
|
||||||
|
TTL:
|
||||||
|
description: PHOENIX TTL trigger
|
||||||
####################
|
|
||||||
#
|
|
||||||
# TRIGGER/Delay
|
|
||||||
#
|
|
||||||
###################
|
|
||||||
|
|
||||||
phoenix_trigger:
|
|
||||||
description: Trigger
|
|
||||||
deviceClass: phoenix_bec.devices.phoenix_trigger.PhoenixTrigger
|
deviceClass: phoenix_bec.devices.phoenix_trigger.PhoenixTrigger
|
||||||
deviceConfig:
|
deviceConfig:
|
||||||
prefix: 'X07MB-OP2:'
|
prefix: 'X07MB-OP2:'
|
||||||
deviceTags:
|
deviceTags:
|
||||||
- phoenix
|
- phoenix
|
||||||
- trigger
|
- TTL Trigger
|
||||||
- phoenix_devices.yaml
|
- phoenix_devices.yaml
|
||||||
onFailure: buffer
|
onFailure: buffer
|
||||||
enabled: true
|
enabled: true
|
||||||
|
@ -1 +1 @@
|
|||||||
from PhoenixTrigger import PhoenixTrigger
|
from .phoenix_trigger import PhoenixTrigger
|
@ -256,7 +256,7 @@ class FalconSetup(CustomDetectorMixin):
|
|||||||
#):
|
#):
|
||||||
# # Retry stop detector and wait for remaining time
|
# # Retry stop detector and wait for remaining time
|
||||||
# raise FalconTimeoutError(
|
# raise FalconTimeoutError(
|
||||||
# f"Failed to stop detector, timeout with state {signal_conditions[0][0]}"
|
# f"Failed to stop detector, timeou t with state {signal_conditions[0][0]}"
|
||||||
# )
|
# )
|
||||||
|
|
||||||
def stop_detector_backend(self) -> None:
|
def stop_detector_backend(self) -> None:
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import time
|
||||||
|
|
||||||
from ophyd import (
|
from ophyd import (
|
||||||
ADComponent as ADCpt,
|
ADComponent as ADCpt,
|
||||||
Device,
|
Device,
|
||||||
@ -18,10 +20,6 @@ DETECTOR_TIMEOUT = 5
|
|||||||
|
|
||||||
#class PhoenixTriggerError(Exce start_csmpl=Cpt(EPicsSignal,'START-CSMPL') # cont on / off
|
#class PhoenixTriggerError(Exce start_csmpl=Cpt(EPicsSignal,'START-CSMPL') # cont on / off
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class PhoenixTriggerSetup(CustomDetectorMixin):
|
class PhoenixTriggerSetup(CustomDetectorMixin):
|
||||||
"""
|
"""
|
||||||
This defines the PHOENIX trigger setup.
|
This defines the PHOENIX trigger setup.
|
||||||
@ -29,11 +27,38 @@ class PhoenixTriggerSetup(CustomDetectorMixin):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
#self.acquire = self.parent.smpl.put(1)
|
||||||
|
#self.continuous_sampling_on = parent.start_cmpl.put(1)
|
||||||
|
#self.continuous_sampling_off = self.parent.start_cmpl.put(0)
|
||||||
|
|
||||||
def __init__(self, *args, parent:Device = None, **kwargs):
|
def __init__(self, *args, parent:Device = None, **kwargs):
|
||||||
super().__init__(*args, parent=parent, **kwargs)
|
super().__init__(*args, parent=parent, **kwargs)
|
||||||
self._counter = 0
|
self._counter = 0
|
||||||
|
|
||||||
WW
|
|
||||||
|
def on_acquire(self):
|
||||||
|
self.parent.smpl.put(1)
|
||||||
|
print('on_aquire')
|
||||||
|
|
||||||
|
|
||||||
|
def on_cont_sample_on(self):
|
||||||
|
self.parent.start_csmpl.put(1)
|
||||||
|
print('on_cont_sample_on')
|
||||||
|
|
||||||
|
def on_cont_sample_off(self):
|
||||||
|
self.parent.start_csmpl.put(0)
|
||||||
|
print('on_cont_sample_off')
|
||||||
|
|
||||||
|
|
||||||
|
def on_done(self):
|
||||||
|
done = self.parent.smpl_done.get()
|
||||||
|
return done
|
||||||
|
|
||||||
|
|
||||||
|
def on_dwell(self,t):
|
||||||
|
" calculate cycles from time in sec "
|
||||||
|
cycles=self.parent.total_cycles.put(0)*5
|
||||||
|
|
||||||
def on_stage(self):
|
def on_stage(self):
|
||||||
# is this called on each point in scan or just before scan ???
|
# is this called on each point in scan or just before scan ???
|
||||||
print('on stage')
|
print('on stage')
|
||||||
@ -43,19 +68,22 @@ class PhoenixTriggerSetup(CustomDetectorMixin):
|
|||||||
time.sleep(0.05)
|
time.sleep(0.05)
|
||||||
cycles=self.parent.total_cycles.put(0)
|
cycles=self.parent.total_cycles.put(0)
|
||||||
time.sleep(0.05)
|
time.sleep(0.05)
|
||||||
cycles=self.parent.smpl.put(2)
|
cycles=self.parent.smpl.put(1)
|
||||||
time.sleep(0.5)
|
time.sleep(0.5)
|
||||||
cycles=self.parent.total_cycles.put(cycles)
|
cycles=self.parent.total_cycles.put(cycles)
|
||||||
|
|
||||||
logger.success('PhoenixTrigger on stage')
|
logger.success('PhoenixTrigger on stage')
|
||||||
|
|
||||||
def on_trigger(self):
|
|
||||||
print('on_trigger')
|
|
||||||
self.parent.start_smpl.put(1)
|
|
||||||
logger.success('PhoenixTrigger on_trigger')
|
|
||||||
|
|
||||||
return self.wait_with_status(
|
|
||||||
[(self.parent.smpl_done.get, 1)])
|
|
||||||
|
|
||||||
|
#def on_trigger(self):
|
||||||
|
# print('on_trigger')
|
||||||
|
# self.parent.start_smpl.put(1)
|
||||||
|
# logger.success('PhoenixTrigger on_trigger')
|
||||||
|
#
|
||||||
|
# return self.wait_with_status(
|
||||||
|
# [(self.parent.smpl_done.get, 1)])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -70,67 +98,57 @@ class PhoenixTriggerSetup(CustomDetectorMixin):
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
def on_complete(self):
|
#def on_complete(self):
|
||||||
print('on_complete')
|
# print('on_complete')
|
||||||
timeout =10
|
# timeout =10
|
||||||
|
|
||||||
|
|
||||||
logger.success('XXXX complete %d XXXX' % success)
|
# logger.success('XXXX complete %d XXXX' % success)
|
||||||
|
|
||||||
success = self.wait_for_signals(
|
# success = self.wait_for_signals(
|
||||||
[
|
# [
|
||||||
(self.parent.smpl_done.get, 0))
|
# (self.parent.smpl_done.get, 0)
|
||||||
],
|
# ],
|
||||||
timeout,
|
# timeout,
|
||||||
check_stopped=True,
|
# check_stopped=True,
|
||||||
all_signals=True
|
# all_signals=True
|
||||||
)
|
# )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if success:
|
# if success:
|
||||||
status.set_finished()
|
# status.set_finished()
|
||||||
else:
|
# else:
|
||||||
status.set_exception(TimeoutError())
|
# status.set_exception(TimeoutError())
|
||||||
return status
|
# return status
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def on_stop(self):
|
# hoenixTrigger on_unstage ')
|
||||||
logger.success(' PhoenixTrigger on_stop ')
|
# self.parent.csmpl.put(1)
|
||||||
|
# self.parent.smpl.put(1)
|
||||||
self.parent.csmpl.put(1)
|
# logger.success(' PhoenixTrigger on_unstage finished ')
|
||||||
logger.success(' PhoenixTrigger on_stop finished ')
|
|
||||||
|
|
||||||
def on_unstage(self):
|
|
||||||
logger.success(' PhoenixTrigger on_unstage ')
|
|
||||||
self.parent.csmpl.put(1)
|
|
||||||
self.parent.smpl.put(1)
|
|
||||||
logger.success(' PhoenixTrigger on_unstage finished ')
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#def on_trigger():
|
||||||
|
# print('on_trigger')
|
||||||
|
|
||||||
|
|
||||||
class PhoenixTrigger(PSIDetectorBase):
|
class PhoenixTrigger(PSIDetectorBase):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
Docstring:
|
||||||
|
|
||||||
|
Class for PHOENIX TTL hardware trigger
|
||||||
|
|
||||||
Parent class: PSIDetectorBase
|
Parent class: PSIDetectorBase
|
||||||
|
|
||||||
class attributes:
|
class attributes:
|
||||||
custom_prepare_cls (XMAPSetup) : Custom detector setup class for cSAXS,
|
custom_prepare_cls (PhoenixTriggerSetup) : Custom setup for TTL trigger at PHOENIX
|
||||||
inherits from CustomDetectorMixin
|
inherits from CustomDetectorMixin
|
||||||
in __init__ of PSIDetecor bases
|
in __init__ of PSIDetecor bases
|
||||||
class is initialized
|
class is initialized
|
||||||
self.custom_prepare = self.custom_prepare_cls(parent=self, **kwargs)
|
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 class PhoenixTrigger is the class to be called via yaml configuration file
|
||||||
the input arguments are defined by PSIDetectorBase,
|
the input arguments are defined by PSIDetectorBase,
|
||||||
and need to be given in the yaml configuration file.
|
and need to be given in the yaml configuration file.
|
||||||
@ -138,6 +156,8 @@ class PhoenixTrigger(PSIDetectorBase):
|
|||||||
|
|
||||||
use prefix 'X07MB-OP2:' in the device definition in the yaml configuration file.
|
use prefix 'X07MB-OP2:' in the device definition in the yaml configuration file.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
PSIDetectorBase(
|
PSIDetectorBase(
|
||||||
prefix='',
|
prefix='',
|
||||||
*,Q
|
*,Q
|
||||||
@ -147,12 +167,6 @@ class PhoenixTrigger(PSIDetectorBase):
|
|||||||
device_manager=None,
|
device_manager=None,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
)
|
)
|
||||||
Docstring:
|
|
||||||
Abstract base class for SLS detectors
|
|
||||||
|
|
||||||
Class attributes:
|
|
||||||
custom_prepare_cls (object): class for custom prepare logic (BL specific)
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
prefix (str): EPICS PV prefix for component (optional)
|
prefix (str): EPICS PV prefix for component (optional)
|
||||||
name (str): name of the device, as will be reported via read()
|
name (str): name of the device, as will be reported via read()
|
||||||
@ -167,13 +181,66 @@ class PhoenixTrigger(PSIDetectorBase):
|
|||||||
File: /data/test/x07mb-test-bec/bec_deployment/ophyd_devices/ophyd_devices/interfaces/base_classes/psi_detector_base.py
|
File: /data/test/x07mb-test-bec/bec_deployment/ophyd_devices/ophyd_devices/interfaces/base_classes/psi_detector_base.py
|
||||||
Type: type
|
Type: type
|
||||||
Subclasses: EpicsSignal
|
Subclasses: EpicsSignal
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
##################################################################
|
||||||
|
# Specify which functions are revealed to the user in BEC client
|
||||||
|
# only a set of predefined functions will be visible in dev.TTL
|
||||||
|
# The Variable USER_ACCESS contains an ascii list of functions which will be
|
||||||
|
# visible in dev.TTL as well
|
||||||
|
# Alternatively one couls also create 2nd instance of PhoenixTrigger,
|
||||||
|
# which is probably not ideal
|
||||||
|
|
||||||
|
USER_ACCESS = ["a_acquire"
|
||||||
|
,"a_cont_sample_on"
|
||||||
|
,"a_cont_sample_off"
|
||||||
|
,"prefix"
|
||||||
|
,"a_done"]
|
||||||
|
|
||||||
|
#####################################################################
|
||||||
|
# specify Setup class into variable custom_prepare_cls
|
||||||
|
# in __init__ of PSIDetectorBase will the initialzed by
|
||||||
|
# self.custom_prepare = self.custom_prepare_cls(parent=self, **kwargs)
|
||||||
|
# making the instance of PSIDetectorBase availble in functions
|
||||||
|
|
||||||
custom_prepare_cls = PhoenixTriggerSetup
|
custom_prepare_cls = PhoenixTriggerSetup
|
||||||
|
|
||||||
|
#############################################################3
|
||||||
start_csmpl = Cpt(EpicsSignal,'START-CSMPL') # cont on / off
|
# Now use component to provide channel access
|
||||||
|
# when PhoenixTrigger is initialized, the parameters of the base class are
|
||||||
|
# inherided, most 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
|
||||||
|
# the Cpt will construct - magically- the Epics channel name
|
||||||
|
# EpicsPV = prefix+suffix,
|
||||||
|
# for example
|
||||||
|
# 'X07MB-OP2:' + 'START-CSMPL' -> 'X07MB-OP2:' + 'START-CSMPL'
|
||||||
|
#
|
||||||
|
start_csmpl = Cpt(EpicsSignal, 'START-CSMPL') # cont on / off
|
||||||
intr_count = Cpt(EpicsSignal,'INTR-COUNT') # conter run up
|
intr_count = Cpt(EpicsSignal,'INTR-COUNT') # conter run up
|
||||||
total_cycles = Cpt(EpicsSignal,'TOTAL-CYCLES') # cycles set
|
total_cycles = Cpt(EpicsSignal,'TOTAL-CYCLES') # cycles set
|
||||||
|
smpl = Cpt(EpicsSignal,'SMPL') # start sampling --> aquire
|
||||||
smpl_done = Cpt(EpicsSignal,'SMPL-DONE') # show trigger is done
|
smpl_done = Cpt(EpicsSignal,'SMPL-DONE') # show trigger is done
|
||||||
|
|
||||||
|
|
||||||
|
# link to reasonable names
|
||||||
|
# start with a_ to see functions quicklz in listing
|
||||||
|
#
|
||||||
|
#
|
||||||
|
def a_acquire(self):
|
||||||
|
self.custom_prepare.on_acquire()
|
||||||
|
|
||||||
|
def a_cont_sample_on(self):
|
||||||
|
self.custom_prepare.on_cont_sample_on()
|
||||||
|
|
||||||
|
def a_cont_sample_off(self):
|
||||||
|
self.custom_prepare.on_cont_sample_off()
|
||||||
|
|
||||||
|
def a_done(self):
|
||||||
|
done=self.custom_prepare.on_done()
|
||||||
|
return done
|
||||||
|
|
||||||
|
def a_dwell(self):
|
||||||
|
self.custom_prepare.on_dwell()
|
182
phoenix_bec/devices/phoenix_trigger.py~
Normal file
182
phoenix_bec/devices/phoenix_trigger.py~
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
import time
|
||||||
|
|
||||||
|
logger = bec_logger.logger
|
||||||
|
|
||||||
|
DETECTOR_TIMEOUT = 5
|
||||||
|
|
||||||
|
#class PhoenixTriggerError(Exce start_csmpl=Cpt(EPicsSignal,'START-CSMPL') # cont on / off
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class PhoenixTriggerSetup(CustomDetectorMixin):
|
||||||
|
"""
|
||||||
|
This defines the PHOENIX trigger setup.
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, *args, parent:Device = None, **kwargs):
|
||||||
|
super().__init__(*args, parent=parent, **kwargs)
|
||||||
|
self._counter = 0
|
||||||
|
|
||||||
|
WW
|
||||||
|
def on_stage(self):
|
||||||
|
# is this called on each point in scan or just before scan ???
|
||||||
|
print('on stage')
|
||||||
|
self.parent.start_csmpl.put(0)
|
||||||
|
time.sleep(0.05)
|
||||||
|
cycles=self.parent.total_cycles.get()
|
||||||
|
time.sleep(0.05)
|
||||||
|
cycles=self.parent.total_cycles.put(0)
|
||||||
|
time.sleep(0.05)
|
||||||
|
cycles=self.parent.smpl.put(2)
|
||||||
|
time.sleep(0.5)
|
||||||
|
cycles=self.parent.total_cycles.put(cycles)
|
||||||
|
|
||||||
|
logger.success('PhoenixTrigger on stage')
|
||||||
|
|
||||||
|
def on_trigger(self):
|
||||||
|
|
||||||
|
self.parent.start_smpl.put(1)
|
||||||
|
time.sleep(0.05) # use blocking
|
||||||
|
logger.success('PhoenixTrigger on_trigger')
|
||||||
|
|
||||||
|
return self.wait_with_status(
|
||||||
|
[(self.parent.smpl_done.get, 1)])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# logger.success(' PhoenixTrigger on_trigger complete ')
|
||||||
|
|
||||||
|
# if success:
|
||||||
|
# status.set_finished()
|
||||||
|
# else:
|
||||||
|
# status.set_exception(TimeoutError())
|
||||||
|
# return status
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def on_complete(self):
|
||||||
|
|
||||||
|
timeout =10
|
||||||
|
|
||||||
|
|
||||||
|
logger.success('XXXX complete %d XXXX' % success)
|
||||||
|
|
||||||
|
success = self.wait_for_signals(
|
||||||
|
[
|
||||||
|
(self.parent.smpl_done.get, 0))
|
||||||
|
],
|
||||||
|
timeout,
|
||||||
|
check_stopped=True,
|
||||||
|
all_signals=True
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if success:
|
||||||
|
status.set_finished()
|
||||||
|
else:
|
||||||
|
status.set_exception(TimeoutError())
|
||||||
|
return status
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def on_stop(self):
|
||||||
|
logger.success(' PhoenixTrigger on_stop ')
|
||||||
|
|
||||||
|
self.parent.csmpl.put(1)
|
||||||
|
logger.success(' PhoenixTrigger on_stop finished ')
|
||||||
|
|
||||||
|
def on_unstage(self):
|
||||||
|
logger.success(' PhoenixTrigger on_unstage ')
|
||||||
|
self.parent.csmpl.put(1)
|
||||||
|
self.parent.smpl.put(1)
|
||||||
|
logger.success(' PhoenixTrigger on_unstage finished ')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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: EpicsSignal
|
||||||
|
"""
|
||||||
|
|
||||||
|
custom_prepare_cls = PhoenixTriggerSetup
|
||||||
|
|
||||||
|
|
||||||
|
start_csmpl = Cpt(EpicsSignal,'START-CSMPL') # cont on / off
|
||||||
|
intr_count = Cpt(EpicsSignal,'INTR-COUNT') # conter run up
|
||||||
|
total_cycles = Cpt(EpicsSignal,'TOTAL-CYCLES') # cycles set
|
||||||
|
smpl_done = Cpt(EpicsSignal,'SMPL-DONE') # show trigger is done
|
||||||
|
|
@ -310,10 +310,7 @@ class XMAPSetup(CustomDetectorMixin):
|
|||||||
class XMAPphoenix(PSIDetectorBase):
|
class XMAPphoenix(PSIDetectorBase):
|
||||||
"""MCA
|
"""MCA
|
||||||
XMAP detector for phoenix
|
XMAP detector for phoenix
|
||||||
|
custom_prepare_cls (XMAPSetu
|
||||||
Parent class: PSIDetectorBase
|
|
||||||
|
|
||||||
class attributes:
|
|
||||||
custom_prepare_cls (XMAPSetup) : Custom detector setup class for cSAXS,
|
custom_prepare_cls (XMAPSetup) : Custom detector setup class for cSAXS,
|
||||||
inherits from CustomDetectorMixin
|
inherits from CustomDetectorMixin
|
||||||
in __init__ of PSIDetecor base
|
in __init__ of PSIDetecor base
|
||||||
@ -337,9 +334,9 @@ class XMAPphoenix(PSIDetectorBase):
|
|||||||
dxp = Cpt(EpicsDXPXMAP, "dxp1:")
|
dxp = Cpt(EpicsDXPXMAP, "dxp1:")
|
||||||
|
|
||||||
mca1 = Cpt(EpicsMCARecord, "mca1")
|
mca1 = Cpt(EpicsMCARecord, "mca1")
|
||||||
mca2 = Cpt(EpicsMCARecord, "mca2")
|
#mca2 = Cpt(EpicsMCARecord, "mca2")
|
||||||
mca3 = Cpt(EpicsMCARecord, "mca3")
|
#mca3 = Cpt(EpicsMCARecord, "mca3")
|
||||||
mca4 = Cpt(EpicsMCARecord, "mca4")
|
#mca4 = Cpt(EpicsMCARecord, "mca4")
|
||||||
print('load hdf5')
|
print('load hdf5')
|
||||||
#hdf5 = Cpt(XMAPHDF5Plugins, "HDF1:")
|
#hdf5 = Cpt(XMAPHDF5Plugins, "HDF1:")
|
||||||
|
|
||||||
@ -362,5 +359,11 @@ class XMAPphoenix(PSIDetectorBase):
|
|||||||
auto_pixels_per_buffer = Cpt(EpicsSignal, "AutoPixelsPerBuffer")
|
auto_pixels_per_buffer = Cpt(EpicsSignal, "AutoPixelsPerBuffer")
|
||||||
pixels_per_buffer = Cpt(EpicsSignal, "PixelsPerBuffer")
|
pixels_per_buffer = Cpt(EpicsSignal, "PixelsPerBuffer")
|
||||||
pixels_per_run = Cpt(EpicsSignal, "PixelsPerRun")
|
pixels_per_run = Cpt(EpicsSignal, "PixelsPerRun")
|
||||||
|
|
||||||
|
|
||||||
#nd_array_mode = Cpt(EpicsSignal, "NDArrayMode")
|
#nd_array_mode = Cpt(EpicsSignal, "NDArrayMode")
|
||||||
print('DONE connecton chanels in XMAPphoenix')
|
print('DONE connecton chanels in XMAPphoenix')
|
||||||
|
|
||||||
|
|
||||||
|
def aaaa(self):
|
||||||
|
print('aaaa')
|
||||||
|
@ -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
|
@ -13,8 +13,7 @@ from copy import deepcopy
|
|||||||
from typing import TYPE_CHECKING, Dict, Literal
|
from typing import TYPE_CHECKING, Dict, Literal
|
||||||
|
|
||||||
from toolz import partition
|
from toolz import partition
|
||||||
from typeguard import typechecked
|
from typeguard import typecheck
|
||||||
|
|
||||||
from bec_lib import messages
|
from bec_lib import messages
|
||||||
from bec_lib.bec_errors import ScanAbortion
|
from bec_lib.bec_errors import ScanAbortion
|
||||||
from bec_lib.client import SystemConfig
|
from bec_lib.client import SystemConfig
|
File diff suppressed because it is too large
Load Diff
@ -19,25 +19,15 @@ import importlib
|
|||||||
import ophyd
|
import ophyd
|
||||||
|
|
||||||
|
|
||||||
#logger = bec_logger.logger
|
#
|
||||||
# load local configuration
|
phoenix.add_phoenix_config()
|
||||||
#bec.config.load_demo_config()
|
#bec.config.update_session_with_file('./ConfigPHOENIX/device_config/phoenix_devices.yaml')
|
||||||
|
|
||||||
# .. define base path for directory with scripts
|
|
||||||
|
|
||||||
PhoenixBL=0
|
|
||||||
from ConfigPHOENIX.config.phoenix import PhoenixBL
|
|
||||||
#from ConfigPHOENIX.devices.falcon_csaxs import FalconSetup
|
|
||||||
# initialize general parameter
|
|
||||||
ph=PhoenixBL()
|
|
||||||
|
|
||||||
bec.config.update_session_with_file('./ConfigPHOENIX/device_config/phoenix_devices.yaml')
|
|
||||||
|
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
s1=scans.line_scan(dev.ScanX,0,0.002,steps=4,exp_time=1,relative=False,delay=2)
|
#s1=scans.line_scan(dev.ScanX,0,0.1,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)
|
s2=scans.phoenix_line_scan(dev.ScanX,0,0.002,steps=4,exp_time=.01,relative=False,delay=2)
|
||||||
|
|
||||||
|
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 +1 @@
|
|||||||
from .phoenix_line_scan import PhoenixLineScan
|
from .phoenix_scans import PhoenixLineScan
|
@ -22,19 +22,41 @@ but they are executed in a specific order:
|
|||||||
- self.cleanup # send a close scan message and perform additional cleanups if needed
|
- 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 time
|
||||||
|
|
||||||
# import numpy as np
|
# import numpy as np
|
||||||
|
|
||||||
# from bec_lib.endpoints import MessageEndpoints
|
# from bec_lib.endpoints import MessageEndpoints
|
||||||
# from bec_lib.logger import bec_logger
|
from bec_lib.logger import bec_logger
|
||||||
# from bec_lib import messages
|
# from bec_lib import messages
|
||||||
# from bec_server.scan_server.errors import ScanAbortion
|
# from bec_server.scan_server.errors import ScanAbortion
|
||||||
# from bec_server.scan_server.scans import FlyScanBase, RequestBase, ScanArgType, ScanBase
|
# from bec_server.scan_server.scans import FlyScanBase, RequestBase, ScanArgType, ScanBase
|
||||||
|
|
||||||
# logger = bec_logger.logger
|
# logger = bec_logger.logger
|
||||||
|
|
||||||
|
|
||||||
from bec_server.scan_server.scans import ScanBase, ScanArgType
|
from bec_server.scan_server.scans import ScanBase, ScanArgType
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import time
|
import time
|
||||||
@ -42,8 +64,63 @@ from bec_lib.logger import bec_logger
|
|||||||
|
|
||||||
logger = bec_logger.logger
|
logger = bec_logger.logger
|
||||||
|
|
||||||
class PhoenixLineScan(ScanBase):
|
|
||||||
scan_name = "phoenix_line_scanZZZ"
|
class LogTime():
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.t0=time.process_time()
|
||||||
|
|
||||||
|
def p_s(self,x):
|
||||||
|
now=time.process_time()
|
||||||
|
delta=now-self.t0
|
||||||
|
m=str(delta)+' sec '+x
|
||||||
|
logger.success(m)
|
||||||
|
self.t0=now
|
||||||
|
|
||||||
|
ll=LogTime()
|
||||||
|
|
||||||
|
|
||||||
|
class PhoenixScanBaseTTL(ScanBase):
|
||||||
|
"""
|
||||||
|
Base scan cl p_s('init scrips.phoenix.scans.PhoenixLineScan')
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
ll.p_s('enter scripts.phoenix.scans.PhoenixScanBaseTTL')
|
||||||
|
def scan_core(self):
|
||||||
|
"""perform the scan core procedure"""
|
||||||
|
ll.p_s('PhoenixScanBaseTT.scan_core')
|
||||||
|
for ind, pos in self._get_position():
|
||||||
|
for self.burst_index in range(self.burst_at_each_point):
|
||||||
|
ll.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):
|
||||||
|
ll.p_s('PhoenixScanBaseTT._at_each_point')
|
||||||
|
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)
|
||||||
|
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
|
||||||
|
ll.p_s('done')
|
||||||
|
|
||||||
|
class PhoenixLineScan(PhoenixScanBaseTTL):
|
||||||
|
|
||||||
|
ll.p_s('enter scripts.phoenix.scans.PhoenixLineScan')
|
||||||
|
scan_name = "phoenix_line_scan"
|
||||||
required_kwargs = ["steps", "relative"]
|
required_kwargs = ["steps", "relative"]
|
||||||
arg_input = {
|
arg_input = {
|
||||||
"device": ScanArgType.DEVICE,
|
"device": ScanArgType.DEVICE,
|
||||||
@ -78,38 +155,22 @@ class PhoenixLineScan(ScanBase):
|
|||||||
ans.line_scan(dev.motor1, -5, 5, dev.motor2, -5, 5, steps=10, exp_time=0.1, relative=True)
|
ans.line_scan(dev.motor1, -5, 5, dev.motor2, -5, 5, steps=10, exp_time=0.1, relative=True)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
ll.p_s('init scripts.phoenix.scans.PhoenixLineScan')
|
||||||
super().__init__(
|
super().__init__(
|
||||||
exp_time=exp_time, relative=relative, burst_at_each_point=burst_at_each_point, **kwargs
|
exp_time=exp_time, relative=relative, burst_at_each_point=burst_at_each_point, **kwargs
|
||||||
)
|
)
|
||||||
self.steps = steps
|
self.steps = steps
|
||||||
self.setup_device = setup_device
|
self.setup_device = setup_device
|
||||||
print('INIT CLASS PhoenixLineScan')
|
|
||||||
time.sleep(1)
|
|
||||||
|
|
||||||
|
time.sleep(1)
|
||||||
|
ll.p_s('done')
|
||||||
|
|
||||||
def _calculate_positions(self) -> None:
|
def _calculate_positions(self) -> None:
|
||||||
|
ll.p_s('PhoenixLineScan._calculate_positions')
|
||||||
axis = []
|
axis = []
|
||||||
for _, val in self.caller_args.items():
|
for _, val in self.caller_args.items():
|
||||||
ax_pos = np.linspace(val[0], val[1], self.steps, dtype=float)
|
ax_pos = np.linspace(val[0], val[1], self.steps, dtype=float)
|
||||||
axis.append(ax_pos)
|
axis.append(ax_pos)
|
||||||
self.positions = np.array(list(zip(*axis)), dtype=float)
|
self.positions = np.array(list(zip(*axis)), dtype=float)
|
||||||
|
ll.p_s('done')
|
||||||
|
|
||||||
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
|
|
@ -28,6 +28,9 @@ logger = bec_logger.logger
|
|||||||
|
|
||||||
# .. define base path for directory with scripts
|
# .. define base path for directory with scripts
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class PhoenixBL():
|
class PhoenixBL():
|
||||||
"""
|
"""
|
||||||
#
|
#
|
||||||
@ -68,7 +71,7 @@ class PhoenixBL():
|
|||||||
print('add xmap ')
|
print('add xmap ')
|
||||||
print(self.path_devices+'phoenix_xmap.yaml')
|
print(self.path_devices+'phoenix_xmap.yaml')
|
||||||
|
|
||||||
bec.config.update_session_with_file(self.path_devices+'phoenix_xmap.yaml',timeout=100)
|
bec.config.update_session_with_file(self.path_devices+'phoenix_xmap.yaml')#,timeout=100)
|
||||||
|
|
||||||
def add_falcon(self):
|
def add_falcon(self):
|
||||||
print('add_xmap')
|
print('add_xmap')
|
||||||
|
Reference in New Issue
Block a user