Source code for apstools.utils.device_info

"""
Device information
++++++++++++++++++

.. autosummary::

   ~listdevice
"""


from collections import defaultdict
from ophyd import Device
from ophyd import Signal
from ophyd.signal import EpicsSignalBase

import datetime
import logging
import pandas as pd


logger = logging.getLogger(__name__)
pd.set_option("display.max_rows", None)


def _all_signals(base):
    if isinstance(base, Signal):
        return [base]
    items = []
    if hasattr(base, "component_names"):
        for k in base.component_names:
            obj = getattr(base, k)
            if isinstance(obj, (Device, Signal)):
                items += _all_signals(obj)
    return items


def _get_named_child(obj, nm):
    """
    return named child of ``obj`` or None
    """
    try:
        child = getattr(obj, nm)
        return child
    except TimeoutError:
        logger.debug(f"timeout: {obj.name}_{nm}")
        return "TIMEOUT"


def _get_pv(obj):
    """
    Return PV name, prefix, or None from ophyd object.
    """
    if hasattr(obj, "pvname"):
        return obj.pvname
    elif hasattr(obj, "prefix"):
        return obj.prefix


def _list_epics_signals(obj):
    """
    Return a list of the EPICS signals in obj.

    RETURNS

    list of ophyd objects that are children of ``obj``
    """
    # import pdb; pdb.set_trace()
    if isinstance(obj, EpicsSignalBase):
        return [obj]
    elif isinstance(obj, Device):
        items = []
        for nm in obj.component_names:
            child = _get_named_child(obj, nm)
            if child in (None, "TIMEOUT"):
                continue
            result = _list_epics_signals(child)
            if result is not None:
                items.extend(result)
        return items


[docs]def listdevice( obj, scope=None, cname=False, dname=True, show_pv=False, use_datetime=True, show_ancient=True, ): """ Describe the signal information from device ``obj`` in a pandas DataFrame. Look through all subcomponents to find all the signals to be shown. PARAMETERS obj *object* : Instance of ophyd Signal or Device. scope *str* or None : Scope of content to be shown. - ``"full"`` (or ``None``) shows all Signal components - ``"epics"`` shows only EPICS-based Signals - ``"read"`` shows only the signals returned by ``obj.read()`` default: ``None`` cname *bool* : Show the _control_ (Python, dotted) name in column ``name``. default: ``False`` dname *bool* : Show the _data_ (databroker, with underlines) name in column ``data name``. default: ``True`` show_pv *bool* : Show the EPICS process variable (PV) name in column ``PV``. default: ``False`` use_datetime *bool* : Show the EPICS timestamp (time of last update) in column ``timestamp``. default: ``True`` show_ancient *bool* : Show uninitialized EPICS process variables. In EPICS, an uninitialized PV has a timestamp of 1990-01-01 UTC. This option enables or suppresses ancient values identified by timestamp from 1989. These are values only defined in the original ``.db`` file. default: ``True`` """ scope = (scope or "full").lower() signals = _all_signals(obj) if scope in ("full", "epics"): if scope == "epics": signals = [s for s in signals if isinstance(s, EpicsSignalBase)] elif scope == "read": reading = obj.read() signals = [s for s in signals if s.name in reading] else: raise KeyError( f"Unknown scope='{scope}'." " Must be one of None, 'full', 'epics', 'read'" ) # in EPICS, an uninitialized PV has a timestamp of 1990-01-01 UTC UNINITIALIZED = datetime.datetime.timestamp( datetime.datetime.fromisoformat("1990-06-01") ) if not cname and not dname: cname = True dd = defaultdict(list) for signal in signals: if scope != "epics" or isinstance(signal, EpicsSignalBase): ts = getattr(signal, "timestamp", 0) if show_ancient or (ts >= UNINITIALIZED): if cname: dd["name"].append(f"{obj.name}.{signal.dotted_name}") if dname: dd["data name"].append(signal.name) if show_pv: dd["PV"].append(_get_pv(signal) or "") dd["value"].append(signal.get()) if use_datetime: dd["timestamp"].append(datetime.datetime.fromtimestamp(ts)) return pd.DataFrame(dd)
# ----------------------------------------------------------------------------- # :author: Pete R. Jemian # :email: jemian@anl.gov # :copyright: (c) 2017-2022, UChicago Argonne, LLC # # Distributed under the terms of the Creative Commons Attribution 4.0 International Public License. # # The full license is in the file LICENSE.txt, distributed with this software. # -----------------------------------------------------------------------------