docs: reviewed docstrings

This commit is contained in:
appel_c 2023-11-22 10:36:56 +01:00 committed by wakonig_k
parent 6780c523bd
commit d295741bd0

View File

@ -20,15 +20,20 @@ logger = bec_logger.logger
class DelayGeneratorError(Exception):
"""Exception raised for errors in the Delay Generator."""
"""Exception raised for errors."""
class DeviceInitError(DelayGeneratorError):
"""Error raised when init of device class fails due to missing device manager or not started in sim_mode."""
"""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 DelayStatic(Device):
"""Static axis for the T0 output channel
"""
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
@ -62,7 +67,7 @@ class DelayStatic(Device):
class DummyPositioner(PVPositioner):
"""Dummy positioner for the DG645"""
"""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)
@ -71,10 +76,11 @@ class DummyPositioner(PVPositioner):
class DelayPair(PseudoPositioner):
"""Delay pair interface for DG645
"""
Delay pair interface
Virtual motor interface to a pair of signals (on the frontpanel).
It offers a simple delay and pulse width interface for scanning.
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
@ -107,10 +113,15 @@ class DelayPair(PseudoPositioner):
class DDGCustomMixin:
"""
Mixin class for custom DelayGenerator logic
Mixin class for custom DelayGenerator logic within PSIDelayGeneratorBase.
This class is used to implement BL specific logic for the DDG.
It is used in the PSIDelayGeneratorBase class.
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:
@ -118,41 +129,48 @@ class DDGCustomMixin:
def initialize_default_parameter(self) -> None:
"""
Initialize default parameters for DDG
Method to initialize default parameters for DDG.
This method is called upon initiating the class.
It can be conveniently used to set default parameters for the DDG.
These may include, amplitudes, offsets, delays, etc.
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:
"""
Prepare the DDG for the upcoming scan
Method to prepare the DDG for the upcoming scan.
This methods hosts the full logic for the upcoming scan.
It is called by the stage method and needs to fully prepare the DDGs 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:
"""Define action executed on trigger methods"""
"""Method executed upon trigger call in parent class"""
def finished(self) -> None:
"""Checks if DDG finished acquisition"""
"""Method to check if DDG is finished with the scan"""
def on_pre_scan(self) -> None:
"""
Called by pre scan hook
Method executed upon pre_scan call in parent class.
These actions get executed just before the trigger method/start of scan
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_scanID(self) -> None:
"""
Check if BEC is running on a new scanID
"""
"""Method to check if there is a new scanID, called by stage."""
def is_ddg_okay(self, raise_on_error=False) -> None:
"""Check if DDG is okay, if not try to clear error"""
"""
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")
@ -160,7 +178,7 @@ class DDGCustomMixin:
time.sleep(1)
self.is_ddg_okay(raise_on_error=True)
elif status != "STATUS OK":
raise Exception(f"DDG failed to start with status: {status}")
raise DelayGeneratorNotOkay(f"DDG failed to start with status: {status}")
class PSIDelayGeneratorBase(Device):
@ -170,34 +188,35 @@ class PSIDelayGeneratorBase(Device):
This class implements a thin Ophyd wrapper around the Stanford Research DG645
digital delay generator.
Internally, 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 a combination of these signals.
Back panel outputs are directly routed signals. So signals are NOT INDEPENDENT.
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.
Front panel signals:
All signals go high after their defined delays and go low after the trigger
holdoff period, i.e. this is the trigger window. Front panel outputs provide
a combination of these events.
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
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
@ -215,6 +234,7 @@ class PSIDelayGeneratorBase(Device):
"reload_config",
]
# Assign PVs from DDG645
trigger_burst_readout = Component(
EpicsSignal, "EventStatusLI.PROC", name="trigger_burst_readout"
)
@ -230,7 +250,6 @@ class PSIDelayGeneratorBase(Device):
channelEF = Component(DelayPair, "", name="EF", channel="EF")
channelGH = Component(DelayPair, "", name="GH", channel="GH")
# Minimum time between triggers
holdoff = Component(
EpicsSignal,
"TriggerHoldoffAI",
@ -267,7 +286,6 @@ class PSIDelayGeneratorBase(Device):
kind=Kind.config,
)
trigger_shot = Component(EpicsSignal, "TriggerDelayBO", name="trigger_shot", kind="config")
# Burst mode
burstMode = Component(
EpicsSignal,
"BurstModeBI",
@ -359,27 +377,30 @@ class PSIDelayGeneratorBase(Device):
self.custom_prepare.is_ddg_okay()
def _update_scaninfo(self) -> None:
"""Update scaninfo from BecScaninfoMixing
This depends on device manager and operation/sim_mode
"""
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:
"""Initialize detector, filewriter and set default parameters"""
"""Method to initialize custom parameters of the DDG."""
self.custom_prepare.initialize_default_parameter()
def set_channels(self, signal: str, value: Any, channels: List = None) -> None:
"""
Sets value on signal in list all_channels
Method to set signals on DelayPair and DelayStatic channels.
Setting values works 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
value (Any): value to set
channels (List, optional): list of channels to set. Defaults to self.all_channels.
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
@ -395,17 +416,18 @@ class PSIDelayGeneratorBase(Device):
def stage(self) -> List[object]:
"""
Stage device in preparation for a scan
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
"""
# Method idempotent, should rais ;obj;'RedudantStaging' if staged twice
if self._staged != Staged.no:
return super().stage()
self.stopped = False
@ -418,25 +440,35 @@ class PSIDelayGeneratorBase(Device):
return super().stage()
def trigger(self) -> DeviceStatus:
"""Trigger the detector, called from BEC."""
"""
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:
"""Pre scan hook, called before the scan starts"""
"""
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]:
"""
Unstage device in preparation for a scan
Method unstage gets called at the end of a scan.
Returns directly if self.stopped,
otherwise checks with self._finished
if data acquisition on device finished (an was successful)
If scan (self.stopped is True) is stopped, returns directly.
Otherwise, checks if the DDG finished acquisition
Internal Calls:
- custom_prepare.check_scanID : check if scanID 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
@ -451,9 +483,12 @@ class PSIDelayGeneratorBase(Device):
def stop(self, *, success=False) -> None:
"""
Stop the DDG
Method to stop the DDG
#TODO check if the pulse sequence can be stopped, which PV should be called?
#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)