Source code for apstools.devices.axis_tuner
"""
Axis Tuner
+++++++++++++++++++++++++++++++++++++++
.. autosummary::
~AxisTunerException
~AxisTunerMixin
"""
import datetime
import logging
from collections import OrderedDict
from bluesky import plan_stubs as bps
from .mixin_base import DeviceMixinBase
logger = logging.getLogger(__name__)
[docs]class AxisTunerException(ValueError):
"""
Exception during execution of `AxisTunerBase` subclass
.. index:: Ophyd Exception; AxisTunerException
"""
[docs]class AxisTunerMixin(DeviceMixinBase):
"""
Mixin class to provide tuning capabilities for an axis
.. index:: Ophyd Device Mixin; AxisTunerMixin
See the `TuneAxis()` example in this jupyter notebook:
https://github.com/BCDA-APS/apstools/blob/master/docs/source/resources/demo_tuneaxis.ipynb
HOOK METHODS
There are two hook methods (`pre_tune_method()`, and `post_tune_method()`)
for callers to add additional plan parts, such as opening or closing shutters,
setting detector parameters, or other actions.
Each hook method must accept a single argument:
an axis object such as `EpicsMotor` or `SynAxis`,
such as::
def my_pre_tune_hook(axis):
yield from bps.mv(shutter, "open")
def my_post_tune_hook(axis):
yield from bps.mv(shutter, "close")
class TunableSynAxis(AxisTunerMixin, SynAxis): pass
myaxis = TunableSynAxis(name="myaxis")
mydet = SynGauss('mydet', myaxis, 'myaxis', center=0.21, Imax=0.98e5, sigma=0.127)
myaxis.tuner = TuneAxis([mydet], myaxis)
myaxis.pre_tune_method = my_pre_tune_hook
myaxis.post_tune_method = my_post_tune_hook
def tune_myaxis():
yield from myaxis.tune(md={"plan_name": "tune_myaxis"})
RE(tune_myaxis())
"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.tuner = None # such as: apstools.plans.TuneAxis
# Hook functions for callers to add additional plan parts
# Each must accept one argument: axis object such as `EpicsMotor` or `SynAxis`
self.pre_tune_method = self._default_pre_tune_method
self.post_tune_method = self._default_post_tune_method
[docs] def _default_pre_tune_method(self):
"""called before `tune()`"""
logger.info("{} position before tuning: {}".format(self.name, self.position))
yield from bps.null()
[docs] def _default_post_tune_method(self):
"""called after `tune()`"""
logger.info("{} position after tuning: {}".format(self.name, self.position))
yield from bps.null()
def tune(self, md=None, **kwargs):
if self.tuner is None:
msg = "Must define an axis tuner, none specified."
msg += " Consider using apstools.plans.TuneAxis()"
raise AxisTunerException(msg)
if self.tuner.axis is None:
msg = "Must define an axis, none specified."
raise AxisTunerException(msg)
if md is None:
md = OrderedDict()
md["purpose"] = "tuner"
md["datetime"] = datetime.datetime.now().isoformat(sep=" ")
if self.tuner is not None:
if self.pre_tune_method is not None:
yield from self.pre_tune_method()
yield from self.tuner.tune(md=md, **kwargs)
if self.post_tune_method is not None:
yield from self.post_tune_method()
# -----------------------------------------------------------------------------
# :author: Pete R. Jemian
# :email: jemian@anl.gov
# :copyright: (c) 2017-2024, UChicago Argonne, LLC
#
# Distributed under the terms of the Argonne National Laboratory Open Source License.
#
# The full license is in the file LICENSE.txt, distributed with this software.
# -----------------------------------------------------------------------------