.. _pywrap:

Python Wrapper
==============

.. module:: PyWrtd

The WRTD Python wrapper provides a thin wrapper around the :ref:`clib`, using the `Python ctypes
package <https://pypi.org/project/ctypes/>`_.

The wrapper is provided as a Python package with a single class that encapsulates the complete WRTD
:ref:`clib`:

.. autoclass:: PyWrtd

All the provided class methods have exactly the same names (and function) as their C counterparts,
without the "wrtd\_" prefix.

To start using it, simply import the PyWrtd package and instantiate a :py:class:`PyWrtd` object,
passing to it the :ref:`identifier of the Node<node_id>` you wish to access.

If the identifier is wrong or if the user does not have the correct permissions to access it, WRTD
will return :cpp:enumerator:`WRTD_ERROR_RESOURCE_UNKNOWN`.

.. code-block:: python

   >>> from PyWrtd import PyWrtd
   >>> wrtd = PyWrtd("bad id")
   Error 0xbffa0060: WRTD_ERROR_RESOURCE_UNKNOWN
   >>> wrtd = PyWrtd("MT01")
   >>> wrtd
   <PyWrtd.PyWrtd object at 0x7fa9aeeaee48>

.. hint::

   The provided :ref:`tools <tools>` are built on top of this Python wrapper, so they also serve as
   a good example of how to use the wrapper.

Error Handling API
------------------

.. automethod:: PyWrtd.get_error
.. code-block:: python

   >>> wrtd.get_error()
   (0, 'WRTD_SUCCESS ')

.. automethod:: PyWrtd.error_message
.. code-block:: python

   >>> wrtd.error_message(PyWrtd.WRTD_SUCCESS)
   'WRTD_SUCCESS'

.. _pyapi_init:

Initialisation API
------------------

Compared to the :ref:`clib` :ref:`api_init`, the Python version lacks the :cpp:func:`wrtd_init` and
:cpp:func:`wrtd_close` functions, because the Python wrapper performs these tasks internally.

.. automethod:: PyWrtd.reset
.. code-block:: python

   >>> wrtd.reset()
   >>> wrtd.add_rule('rule1')
   >>> # Query number of declared rules
   >>> wrtd.get_attr_int32(PyWrtd.WRTD_GLOBAL_REP_CAP_ID, PyWrtd.WRTD_ATTR_RULE_COUNT)
   1
   >>> # After reset, 'rule1' should be gone
   >>> wrtd.reset()
   >>> wrtd.get_attr_int32(PyWrtd.WRTD_GLOBAL_REP_CAP_ID, PyWrtd.WRTD_ATTR_RULE_COUNT)
   0

.. _pyapi_attr:

Attribute Handling API
----------------------

Getting and setting boolean, integer and string :ref:`Attributes <attribute>` is straight-forward.

Boolean Attributes
++++++++++++++++++

.. automethod:: PyWrtd.set_attr_bool
.. automethod:: PyWrtd.get_attr_bool
.. code-block:: python

   >>> wrtd.add_alarm('alarm1')
   >>> wrtd.get_attr_bool('alarm1', PyWrtd.WRTD_ATTR_ALARM_ENABLED)
   False
   >>> wrtd.set_attr_bool('alarm1', PyWrtd.WRTD_ATTR_ALARM_ENABLED, True)
   >>> wrtd.get_attr_bool('alarm1', PyWrtd.WRTD_ATTR_ALARM_ENABLED)
   True

Integer Attributes
++++++++++++++++++

.. automethod:: PyWrtd.set_attr_int32
.. automethod:: PyWrtd.get_attr_int32
.. code-block:: python

   >>> wrtd.add_alarm('alarm1')
   >>> wrtd.get_attr_int32('alarm1', PyWrtd.WRTD_ATTR_ALARM_REPEAT_COUNT)
   0
   >>> wrtd.set_attr_int32('alarm1', PyWrtd.WRTD_ATTR_ALARM_REPEAT_COUNT, 5)
   >>> wrtd.get_attr_int32('alarm1', PyWrtd.WRTD_ATTR_ALARM_REPEAT_COUNT)
   5

String Attributes
+++++++++++++++++

.. automethod:: PyWrtd.set_attr_string
.. automethod:: PyWrtd.get_attr_string
.. code-block:: python

   >>> wrtd.add_rule('rule1')
   >>> wrtd.set_attr_string('rule1', PyWrtd.WRTD_ATTR_RULE_SOURCE, 'event0')
   >>> wrtd.get_attr_string('rule1', PyWrtd.WRTD_ATTR_RULE_SOURCE)
   'event0'

Timestamp Attributes
++++++++++++++++++++

Getting and setting :ref:`timestamp` :ref:`Attributes <attribute>` is slightly different than in the
:ref:`clib`.

The *set* method takes the ``seconds``, ``ns`` and ``frac`` fields of the :ref:`timestamp` as separate
parameters.

.. automethod:: PyWrtd.set_attr_tstamp

The *get* method returns a Python dictionary with the ``seconds``, ``ns`` and ``frac`` fields of the
:ref:`timestamp`.

.. automethod:: PyWrtd.get_attr_tstamp
.. code-block:: python

   >>> wrtd.add_alarm('alarm1')
   >>> wrtd.get_attr_tstamp('alarm1', PyWrtd.WRTD_ATTR_ALARM_TIME)
   {'frac': 0, 'ns': 0, 'seconds': 0}
   >>> wrtd.set_attr_tstamp('alarm1', PyWrtd.WRTD_ATTR_ALARM_TIME, seconds = 5, ns = 40e3)
   >>> wrtd.get_attr_tstamp('alarm1', PyWrtd.WRTD_ATTR_ALARM_TIME)
   {'frac': 0, 'seconds': 5, 'ns': 40000}

.. _pyapi_log:

Event Logging API
-----------------

.. automethod:: PyWrtd.clear_event_log_entries
.. automethod:: PyWrtd.get_next_event_log_entry
.. code-block:: python

   >>> # First, enable logging
   >>> wrtd.set_attr_bool(PyWrtd.WRTD_GLOBAL_REP_CAP_ID, PyWrtd.WRTD_ATTR_EVENT_LOG_ENABLED, True)
   >>> wrtd.get_next_event_log_entry()
   'Id:LC-I1           |Seq:0000|2019-07-05,12:08:03.491.231.296+000|2019-07-05,12:08:03.491.226.827+412|GENERATED|DEVICE_0'

.. _pyapi_alarm:

Alarms API
----------

.. automethod:: PyWrtd.add_alarm
.. code-block:: python

   >>> wrtd.get_attr_int32(PyWrtd.WRTD_GLOBAL_REP_CAP_ID, PyWrtd.WRTD_ATTR_ALARM_COUNT)
   0
   >>> wrtd.add_alarm('alarm1')
   >>> wrtd.get_attr_int32(PyWrtd.WRTD_GLOBAL_REP_CAP_ID, PyWrtd.WRTD_ATTR_ALARM_COUNT)
   1

.. automethod:: PyWrtd.disable_all_alarms
.. code-block:: python

   >>> wrtd.add_alarm('alarm1')
   >>> wrtd.get_attr_bool('alarm1', PyWrtd.WRTD_ATTR_ALARM_ENABLED)
   False
   >>> wrtd.set_attr_bool('alarm1', PyWrtd.WRTD_ATTR_ALARM_ENABLED, True)
   >>> wrtd.get_attr_bool('alarm1', PyWrtd.WRTD_ATTR_ALARM_ENABLED)
   True
   >>> wrtd.disable_all_rules()
   >>> wrtd.get_attr_bool('alarm1', PyWrtd.WRTD_ATTR_ALARM_ENABLED)
   False

.. automethod:: PyWrtd.remove_alarm
.. code-block:: python

   >>> wrtd.add_alarm('alarm1')
   >>> wrtd.get_attr_int32(PyWrtd.WRTD_GLOBAL_REP_CAP_ID, PyWrtd.WRTD_ATTR_ALARM_COUNT)
   1
   >>> wrtd.remove_alarm('alarm1')
   >>> wrtd.get_attr_int32(PyWrtd.WRTD_GLOBAL_REP_CAP_ID, PyWrtd.WRTD_ATTR_ALARM_COUNT)
   0

.. automethod:: PyWrtd.remove_all_alarms
.. code-block:: python

   >>> wrtd.add_alarm('alarm1')
   >>> wrtd.get_attr_int32(PyWrtd.WRTD_GLOBAL_REP_CAP_ID, PyWrtd.WRTD_ATTR_ALARM_COUNT)
   1
   >>> wrtd.remove_all_alarms()
   >>> wrtd.get_attr_int32(PyWrtd.WRTD_GLOBAL_REP_CAP_ID, PyWrtd.WRTD_ATTR_ALARM_COUNT)
   0

.. automethod:: PyWrtd.get_alarm_name
.. code-block:: python

   >>> wrtd.add_rule('alarm1')
   >>> count = wrtd.get_attr_int32(PyWrtd.WRTD_GLOBAL_REP_CAP_ID, PyWrtd.WRTD_ATTR_ALARM_COUNT)
   >>> print(count)
   1
   >>> for i in range(count):
   ...     wrtd.get_alarm_name(i)
   'alarm1'

.. _pyapi_rule:

Rules API
---------

.. automethod:: PyWrtd.add_rule
.. code-block:: python

   >>> wrtd.get_attr_int32(PyWrtd.WRTD_GLOBAL_REP_CAP_ID, PyWrtd.WRTD_ATTR_RULE_COUNT)
   0
   >>> wrtd.add_rule('rule1')
   >>> wrtd.get_attr_int32(PyWrtd.WRTD_GLOBAL_REP_CAP_ID, PyWrtd.WRTD_ATTR_RULE_COUNT)
   1

.. automethod:: PyWrtd.disable_all_rules
.. code-block:: python

   >>> wrtd.add_rule('rule1')
   >>> wrtd.get_attr_bool('rule1', PyWrtd.WRTD_ATTR_RULE_ENABLED)
   False
   >>> wrtd.set_attr_bool('rule1', PyWrtd.WRTD_ATTR_RULE_ENABLED, True)
   >>> wrtd.get_attr_bool('rule1', PyWrtd.WRTD_ATTR_RULE_ENABLED)
   True
   >>> wrtd.disable_all_rules()
   >>> wrtd.get_attr_bool('rule1', PyWrtd.WRTD_ATTR_RULE_ENABLED)
   False

.. automethod:: PyWrtd.remove_rule
.. code-block:: python

   >>> wrtd.add_rule('rule1')
   >>> wrtd.get_attr_int32(PyWrtd.WRTD_GLOBAL_REP_CAP_ID, PyWrtd.WRTD_ATTR_RULE_COUNT)
   1
   >>> wrtd.remove_rule('rule1')
   >>> wrtd.get_attr_int32(PyWrtd.WRTD_GLOBAL_REP_CAP_ID, PyWrtd.WRTD_ATTR_RULE_COUNT)
   0

.. automethod:: PyWrtd.remove_all_rules
.. code-block:: python

   >>> wrtd.add_rule('rule1')
   >>> wrtd.get_attr_int32(PyWrtd.WRTD_GLOBAL_REP_CAP_ID, PyWrtd.WRTD_ATTR_RULE_COUNT)
   1
   >>> wrtd.remove_all_rules()
   >>> wrtd.get_attr_int32(PyWrtd.WRTD_GLOBAL_REP_CAP_ID, PyWrtd.WRTD_ATTR_RULE_COUNT)
   0

.. automethod:: PyWrtd.get_rule_name
.. code-block:: python

   >>> wrtd.add_rule('rule1')
   >>> wrtd.add_rule('rule2')
   >>> count = wrtd.get_attr_int32(PyWrtd.WRTD_GLOBAL_REP_CAP_ID, PyWrtd.WRTD_ATTR_RULE_COUNT)
   >>> print(count)
   2
   >>> for i in range(count):
   ...     wrtd.get_rule_name(i)
   'rule1'
   'rule2'

.. automethod:: PyWrtd.reset_rule_stats
.. code-block:: python

   >>> # Assuming a rule 'rule1' exists and already has some statistics
   >>> wrtd.get_attr_int32('rule1', PyWrtd.WRTD_ATTR_STAT_RULE_RX_EVENTS)
   532
   >>> # First disable the rule
   >>> wrtd.set_attr_bool('rule1', PyWrtd.WRTD_ATTR_RULE_ENABLED, False)
   >>> wrtd.reset_rule_stats('rule1')
   >>> wrtd.get_attr_int32('rule1', PyWrtd.WRTD_ATTR_STAT_RULE_RX_EVENTS)
   0

.. _pyapi_app:

Applications API
----------------

.. automethod:: PyWrtd.get_fw_name
.. code-block:: python

   # From the SVEC-based TDC+FDELAY reference Node
   >>> count = wrtd.get_attr_int32(PyWrtd.WRTD_GLOBAL_REP_CAP_ID, PyWrtd.WRTD_ATTR_FW_COUNT)
   >>> print(count)
   2
   >>> for i in range(count):
   ...     wrtd.get_fw_name(i)
   'wrtd-tdc'
   'wrtd-fd'
