Instrumentation
===============

The ``Instrument`` API provide a consistent way of collecting measurements from
a target. Measurements are collected via an instance of a class derived from
:class:`Instrument`. An ``Instrument`` allows collection of measurement from one
or more channels. An ``Instrument`` may support ``INSTANTANEOUS`` or
``CONTINUOUS`` collection, or both.

Example
-------

The following example shows how to use an instrument to read temperature from an
Android target.

.. code-block:: ipython

    # import and instantiate the Target and the instrument
    # (note: this assumes exactly one android target connected
    #  to the host machine).
    In [1]: from devlib import AndroidTarget, HwmonInstrument

    In [2]: t = AndroidTarget()

    In [3]: i = HwmonInstrument(t)

    # Set up the instrument on the Target. In case of HWMON, this is
    # a no-op, but is included here for completeness.
    In [4]: i.setup()

    # Find out what the instrument is capable collecting from the
    # target.
    In [5]: i.list_channels()
    Out[5]:
    [CHAN(battery/temp1, battery_temperature),
     CHAN(exynos-therm/temp1, exynos-therm_temperature)]

    # Set up a new measurement session, and specify what is to be
    # collected.
    In [6]: i.reset(sites=['exynos-therm'])

    # HWMON instrument supports INSTANTANEOUS collection, so invoking
    # take_measurement() will return a list of measurements take from
    # each of the channels configured during reset()
    In [7]: i.take_measurement()
    Out[7]: [exynos-therm_temperature: 36.0 degrees]

API
---

Instrument
~~~~~~~~~~

.. class:: Instrument(target, **kwargs)

   An ``Instrument`` allows collection of measurement from one or more
   channels. An ``Instrument`` may support ``INSTANTANEOUS`` or ``CONTINUOUS``
   collection, or both.

.. attribute:: Instrument.mode

   A bit mask that indicates collection modes that are supported by this
   instrument. Possible values are:

   :INSTANTANEOUS: The instrument supports taking a single sample via
                   ``take_measurement()``.
   :CONTINUOUS: The instrument supports collecting measurements over a
                period of time via ``start()``, ``stop()``, and
                ``get_data()`` methods.

   .. note:: It's possible for one instrument to support more than a single
             mode.

.. attribute:: Instrument.active_channels

   Channels that have been activated via ``reset()``. Measurements will only be
   collected for these channels.

.. method:: Instrument.list_channels()

   Returns a list of :class:`InstrumentChannel` instances that describe what
   this instrument can measure on the current target. A channel is a combination
   of a ``kind`` of measurement (power, temperature, etc) and a ``site`` that
   indicates where on the target the measurement will be collected from.

.. method:: Instrument.get_channels(measure)

   Returns channels for a particular ``measure`` type. A ``measure`` can be
   either a string (e.g. ``"power"``) or a :class:`MeasurmentType` instance.

.. method::  Instrument.setup(*args, **kwargs)

   This will set up the instrument on the target. Parameters this method takes
   are particular to subclasses (see documentation for specific instruments
   below).  What actions are performed by this method are also
   instrument-specific.  Usually these will be things like  installing
   executables, starting services, deploying assets, etc. Typically, this method
   needs to be invoked at most once per reboot of the target (unless
   ``teardown()`` has been called), but see documentation for the instrument
   you're interested in.

.. method:: Instrument.reset([sites, [kinds]])

   This is used to configure an instrument for collection. This must be invoked
   before ``start()`` is called to begin collection. ``sites`` and ``kinds``
   parameters may be used to specify which channels measurements should be
   collected from (if omitted, then measurements will be collected for all
   available sites/kinds). This methods sets the ``active_channels`` attribute
   of the ``Instrument``.

.. method:: Instrument.take_measurment()

   Take a single measurement from ``active_channels``. Returns a list of
   :class:`Measurement` objects (one for each active channel).

   .. note:: This method is only implemented by :class:`Instrument`\ s that
             support ``INSTANTANEOUS`` measurment.

.. method:: Instrument.start()

   Starts collecting measurements from ``active_channels``.

   .. note:: This method is only implemented by :class:`Instrument`\ s that
             support ``CONTINUOUS`` measurment.

.. method:: Instrument.stop()

   Stops collecting measurements from ``active_channels``. Must be called after
   :func:`start()`.

   .. note:: This method is only implemented by :class:`Instrument`\ s that
             support ``CONTINUOUS`` measurment.

.. method:: Instrument.get_data(outfile)

   Write collected data into ``outfile``. Must be called after :func:`stop()`.
   Data will be written in CSV format with a column for each channel and a row
   for each sample. Column heading will be channel, labels in the form
   ``<site>_<kind>`` (see :class:`InstrumentChannel`). The order of the coluns
   will be the same as the order of channels in ``Instrument.active_channels``.

   This returns a :class:`MeasurementCsv` instance associated with the outfile
   that can be used to stream :class:`Measurement`\ s lists (similar to what is
   returned by ``take_measurement()``.

   .. note:: This method is only implemented by :class:`Instrument`\ s that
             support ``CONTINUOUS`` measurment.

.. attribute:: Instrument.sample_rate_hz

   Sample rate of the instrument in Hz. Assumed to be the same for all channels.

   .. note:: This attribute is only provided by :class:`Instrument`\ s that
             support ``CONTINUOUS`` measurment.

Instrument Channel
~~~~~~~~~~~~~~~~~~

.. class:: InstrumentChannel(name, site, measurement_type, **attrs)

   An :class:`InstrumentChannel` describes a single type of measurement that may
   be collected by an :class:`Instrument`. A channel is primarily defined by a
   ``site`` and a ``measurement_type``.

   A ``site`` indicates where  on the target a measurement is collected from
   (e.g. a volage rail or location of a sensor).

   A ``measurement_type`` is an instance of :class:`MeasurmentType` that
   describes what sort of measurment this is (power, temperature, etc). Each
   mesurement type has a standard unit it is reported in, regardless of an
   instrument used to collect it.

   A channel (i.e. site/measurement_type combination) is unique per instrument,
   however there may be more than one channel associated with one site (e.g. for
   both volatage and power).

   It should not be assumed that any site/measurement_type combination is valid.
   The list of available channels can queried with
   :func:`Instrument.list_channels()`.

.. attribute:: InstrumentChannel.site

   The name of the "site" from which the measurments are collected (e.g. voltage
   rail, sensor, etc).

.. attribute:: InstrumentChannel.kind

   A string indingcating the type of measrument that will be collted. This is
   the ``name`` of the :class:`MeasurmentType` associated with this channel.

.. attribute:: InstrumentChannel.units

   Units in which measurment will be reported. this is determined by the
   underlying :class:`MeasurmentType`.

.. attribute:: InstrumentChannel.label

   A label that can be attached to measurments associated with with channel.
   This is constructed with ::

       '{}_{}'.format(self.site, self.kind)


Measurement Types
~~~~~~~~~~~~~~~~~

In order to make instruments easer to use, and to make it easier to swap them
out when necessary (e.g. change method of collecting power), a number of
standard measurement types are defined. This way, for example, power will always
be reported as "power" in Watts, and never as "pwr" in milliWatts. Currently
defined measurement types are


+-------------+---------+---------------+
| name        | units   | category      |
+=============+=========+===============+
| time        | seconds |               |
+-------------+---------+---------------+
| temperature | degrees |               |
+-------------+---------+---------------+
| power       | watts   | power/energy  |
+-------------+---------+---------------+
| voltage     | volts   | power/energy  |
+-------------+---------+---------------+
| current     | amps    | power/energy  |
+-------------+---------+---------------+
| energy      | joules  | power/energy  |
+-------------+---------+---------------+
| tx          | bytes   | data transfer |
+-------------+---------+---------------+
| rx          | bytes   | data transfer |
+-------------+---------+---------------+
| tx/rx       | bytes   | data transfer |
+-------------+---------+---------------+


.. instruments:

Available Instruments
---------------------

This section lists instruments that are currently part of devlib.

TODO