.. _programming:

******************
Programming Basics
******************

The topics in this section explain how to perform basic programming tasks:

-  `Importing the Driver Module`_
-  `Creating a Driver Instance`_
-  `Using the Driver Hierarchy`_
-  `Repeated Capability Collections`_


Note: See :doc:`Installing the Driver Module <./installation>` for Python 
and driver module installation requirements.

Importing the Driver Module
===============================
    To make use of the functions in the ``keysight_kt34400`` driver module, you’ll need to import
    the module with an `import <https://docs.python.org/3/reference/simple_stmts.html#import>`_ statement.
    You may also need to import the `numpy <https://numpy.org/>`_ module which is used by the driver module to process numeric array data.
    The import statement will load the module and define the module name in the local namespace.  In a Python application,
    this must be declared at the top of the code.  Your application can then use the module name to call the driver module constructor.
    For example:
    
       .. code-block:: python

        import keysight_kt34400
        import numpy as np # For keysight_kt34400 arrays

    Note: Your Python application may need to import additional `Python Standard Library <https://docs.python.org/3/library/>`_ or other modules.

Creating a Driver Instance
===============================
    This topic describes the constructor and its options in detail.

    **The Python Driver Constructor**

    .. container:: section
       :name: section1Section

       To create an instance of the imported ``keysight_kt34400`` Python driver, call one of the
       driver's constructors.  The constructor returns a reference to the driver's root interface 
       that may be used to access the driver's child interfaces, methods, and properties.  For example:

       .. code-block:: python

            resource_name = "MyVisaAlias"
            id_query = True
            reset = True
            options = ""
            driver = keysight_kt34400.Kt34400(resource_name, id_query, reset, options)


    .. container:: section
       :name: section2Section

       The constructors for the Python driver offers a variety of
       options and parameters that control fundamental aspects of the
       driver's behavior.  It is critical to understand what options are
       available and how they impact driver operation.


       .. rubric:: ResourceName
          :name: resourcename
          :class: subHeading

       .. container:: subsection

          The **ResourceName** parameter is one of the following:

          -  **VISA resource descriptor** – VISA resource descriptors
             include all of the information needed to specify the location
             of the instrument on the specified I/O interface. The table
             below documents the format of resource descriptors for various
             I/O interfaces. VISA aliases resolve to
             VISA resource descriptors.
          -  **VISA alias** – A friendly name for an instrument, defined for
             use directly in VISA and VISA.NET. In the driver, aliases are
             passed directly to VISA, which resolves the alias to the VISA
             resource descriptor. This help file uses the string
             **"MyVisaAlias"** whenever a VISA alias is intended. Refer to
             IO Libraries Connection Expert documentation for instructions
             on creating an alias. Keysight recommends using VISA aliases
             for readability and flexibility.

             **VISA Resource Descriptor Syntax** -
             Note, your instrument may not support all listed I/O interfaces.
             
             +-----------------------------------+-----------------------------------+
             | Interface                         | Physical Resource Descriptor      |
             |                                   | Syntax                            |
             +===================================+===================================+
             | GPIB                              | GPIB[board]::primary              |
             |                                   | address[::secondary               |
             |                                   | address][::INSTR]                 |
             |                                   | Example: GPIB0::18::INSTR         |
             +-----------------------------------+-----------------------------------+
             | ASRL                              | ASRL conections are not           |
             |                                   | supported.                        |
             +-----------------------------------+-----------------------------------+
             | TCPIP                             | TCPIP[board]::host address[::LAN  |
             |                                   | device name]::INSTR               |
             |                                   | Example: TCPIP0::127.0.0.1::INSTR |
             +-----------------------------------+-----------------------------------+
             | TCPIP                             | TCPIP[board]::host                |
             |                                   | address::port::SOCKET             |
             |                                   | Example:                          |
             |                                   | TCPIP0::127.0.0.1::5025::SOCKET   |
             +-----------------------------------+-----------------------------------+
             | USB                               | USB[board]::manufacturer          |
             |                                   | ID::model code::serial            |
             |                                   | number[::USB interface            |
             |                                   | number][::INSTR]                  |
             |                                   | Example:                          |
             |                                   | USB0::2391::291::SN_001001::INSTR |
             +-----------------------------------+-----------------------------------+

             The following table describes the format of resource descriptors
             for PXI I/O interfaces.

             +-----------------------------------+-----------------------------------+
             | PXI Interface Physical Resource   | Example                           |
             | Descriptor Syntax                 |                                   |
             +===================================+===================================+
             | ``PXI[interface]:                 | **PXI0::3-4.5**                   |
             | :bus-device[.function][::INSTR]`` |                                   |
             | **    Note:** Interface is        |                                   |
             | optional; default is 0\ *.*       |                                   |
             +-----------------------------------+-----------------------------------+
             | ``PXI[bu                          | **PXI1::5**                       |
             | s]::device[::function][::INSTR]`` |                                   |
             | **    Note:** Bus is optional;    |                                   |
             | default is 0.                     |                                   |
             +-----------------------------------+-----------------------------------+
             | ``P                               | **PXI0::CHASSIS1::SLOT3**         |
             | XI[interface]::CHASSISchassis::SL |                                   |
             | OTslot[::FUNCfunction][::INSTR]`` |                                   |
             | **    Note:** Interface is        |                                   |
             | optional; default is 0.           |                                   |
             +-----------------------------------+-----------------------------------+

       .. rubric:: IdQuery
          :name: idquery
          :class: subHeading

       .. container:: subsection

          When enabled, the driver queries the instrument model and compares
          it with a list of instrument models supported by the driver. If
          the model is not supported, initialization throws:
          **IdQueryFailedException**

       .. rubric:: Reset
          :name: reset
          :class: subHeading

       .. container:: subsection

          When enabled, the instrument is reset when the driver is
          initialized. If the reset fails, initialization throws:
          **ResetFailedException**

       .. rubric:: OptionString
          :name: optionstring
          :class: subHeading

       .. container:: subsection

          Option string allows you to pass optional settings to the driver.

          The string is composed of comma-separated assignment operations in
          the form **"Option=value"**.  Spaces are ignored except as separators. 
          Default setting is used for unspecified options.  For example:

          .. code-block:: python

            options = "QueryInstrStatus=true, Simulate=true, Model=Kt1234, Trace=true"


          The following table describes the operations and values that may
          be used.

             +-------------------+-----------------------------------------+---------+
             |  Option Name      | Description                             | Default |
             +===================+=========================================+=========+
             | QueryInstrStatus  | Specifies whether the driver queries    | false   |
             |                   | the instrument status at the end of     |         |
             |                   | each user operation. Querying the       |         |
             |                   | instrument status is very useful for    |         |
             |                   | debugging. After validating the         |         |
             |                   | program, the user can set this          |         |
             |                   | attribute to **False** to disable       |         |
             |                   | status checking and maximize            |         |
             |                   | performance. The user specifies this    |         |
             |                   | value for the entire driver session.    |         |
             +-------------------+-----------------------------------------+---------+
             | Simulate          | The driver can operate without          | false   |
             |                   | connecting to an actual instrument by   |         |
             |                   | using simulation mode. If simulation is |         |
             |                   | enabled, the driver functions do not    |         |
             |                   | perform instrument I/O. For output      |         |
             |                   | parameters that represent instrument    |         |
             |                   | data, the driver functions return       |         |
             |                   | simulated values.  When true, the       |         |
             |                   | resource_name parameter is ignored.     |         |
             +-------------------+-----------------------------------------+---------+
             | Model             | Instrument model to use during          | 34450A  |
             |                   | simulation. Must be one of the          |         |
             |                   | supported model strings listed on the   |         |
             |                   | :doc:`home page <./index>`              |         |
             +-------------------+-----------------------------------------+---------+
             | Trace             | If true, append a trace log of all      | false   |
             |                   | driver calls to a   text file in the    |         |
             |                   | same directory as the application       |         |
             |                   | executable accessing the driver.        |         |
             |                   | Additional tracing parameters may be    |         |
             |                   | set using the SystemTracing             |         |
             |                   | interface methods and properties.       |         |
             +-------------------+-----------------------------------------+---------+
             | TraceArraySizeMax | Specifies the maximum number of         | 100     |
             |                   | elements to log for array parameters.   |         |
             +-------------------+-----------------------------------------+---------+

             **Simulation Limitations**
             
             Simulation is designed to allow the driver to be used without an instrument or any I/O software
             or hardware. However, simulation is not intended to fully emulate the instrument. Be aware of
             the following differences:

             -  Many instruments have status systems, and these are not live. For example, you can set Enable
                and Transition registers. However, queries of Condition and Event registers always return a
                fixed value (typically zero). 
             -  Most drivers depend on the instrument to perform range checking and coercion of parameter values.
                In simulation mode this does not occur, unless done in the driver.  For example, setting a frequency
                parameter to 1E99 will be accepted without error in simulation, while an instrument would return a
                "parameter out of range" error.
             -  The IIviDriverUtility.ErrorQuery method typically returns no error.
             -  Measurement data is not live. Most data queries return a fixed value or a small array of values.


       .. rubric:: Return Value
          :name: return-value
          :class: subHeading

       .. container:: subsection

          Driver constructors return a reference to the main driver class.
          This reference exposes the root level of the 
          driver hierarchy. 

       .. rubric:: Example:  Driver Instantiation (with Simulation)
          :name: example-driver-instantiation-with-simulation
          :class: subHeading

       .. container:: subsection

          The following example code instantiates the driver in simulation mode, simulating the default model.

          .. container::

             .. code-block:: python

                import keysight_kt34400
                import numpy as np # For keysight_kt34400 arrays


                def main():
                   # Edit resource_name and options as needed.  resource_name is ignored if option Simulate=True
                   resource_name = "MyVisaAlias"
                   #resource_name = "TCPIP0::127.0.0.1::INSTR"
                   id_query = False
                   reset = False
                   options = "Simulate=True, QueryInstrStatus=True"

                   try:
                      # Call driver constructor with options
                      driver = None
                      driver = keysight_kt34400.Kt34400(resource_name, id_query, reset, options)
                      print("Driver Initialized")



                      print('  description:', driver.identity.description)


                   except Exception as e:
                      print("\n  Exception:", e.__class__.__name__, e.args)

                   finally:
                      if driver is not None: # Skip close() if constructor failed
                         driver.close()
                      input("\nDone - Press Enter to Exit")


                if __name__ == "__main__":
                   main()

:doc:`Top <programming>`


Using the Driver Hierarchy
===============================

    The driver is organized hierarchically. For example, after `Creating a Driver Instance`_
    called *driver*, a reference to the driver's root interface, you can navigate to the
    **identity** interface child node of *driver* to get the **vendor** property and store
    it in the variable “vendor” by typing the following code:


    .. code-block:: python

        vendor = driver.identity.vendor


    As you navigate the driver hierarchy, each time you enter a dot (**.**),
    you may access the parent interface's properties, methods, and
    events that are supported at that level of the hierarchy. The hierarchy
    is constructed with a special type of interface reference property that points to a lower
    level child interface in the hierarchy. In the above line of code:

    -  **identity** is one of these special properties,
    -  **vendor** is a member of the Identity interface

    The driver hierarchy can contain multiple levels, and each level can
    contain multiple child levels. The hierarchy is used to group related
    methods, properties, and events. By exploring the hierarchy
    you can easily see the structure of the driver and can
    quickly navigate to the desired functionality.

    .. Note::  See the :doc:`API References <./reference>` page for details on the interfaces,
        methods, and properties provided by this driver. The **API Interfaces Hierarchy** section provides
        a hierarchical view of the interfaces where indentation indicates a parent / child 
        interface relationship. 

:doc:`Top <programming>`


Repeated Capability Collections
===============================

    In Keysight driver Python APIs, *Repeated Capability Collections* are a kind of class that 
    can contain multiple instances of the same type of functionality.  For example, oscilloscopes 
    often have multiple channels, each with independent settings for such things as vertical scale 
    and input attenuation. Functionality that is duplicated in an instrument as a repeated capability. 
    In Python they are implemented as `mapping objects <https://docs.python.org/3/glossary.html#term-mapping>`_. 
    This means that they inherit some special methods. These methods are named using the standard naming convention. 
    
    We use the repeated capability *Instance Name* string as the key and the repeated capability instance as its values.
    Valid instance name strings are listed on the Repeated Capability Collection interface help page.  Some drivers 
    support additional *Dynamic Instance* names that are determined at runtime based on connected instrument model 
    capabilities or other criteria.  For example, a driver that supports both 2 channel and 4 channel instrument models
    may always create 2 instance names and dynamically add 2 additional instance names when connected to a 4 channel instrument.

    .. Note:: Not all drivers use repeated capability collections.  To tell if this driver does, look for the word "Collection" 
         used in an interface name and a matching instance interface.  For example: KtDrvrChannelCollection and KtDrvrChannel

    In the rest of this page, we use this example to explain the supported operations: 
    suppose a driver class has a property *channels* which is a
    repeated capability collection interface of type *KtDrvrChannelCollection*, it's instance interface type is
    *KtDrvrChannel*. 

    **Instance Access by Name**

       **[name: str] operator**
         By using the [] operator, provided by the `__getitem__ <https://docs.python.org/3/reference/datamodel.html#object.__getitem__>`_ 
         special method, items can be indexed using their instance name, 
         normally a name which is defined by the driver collection.  Valid instance name strings are listed on the Repeated Capability Collection interface help page.
        
         :Returns: An interface reference to the desired repeated capability instance. All method and property calls on the returned 
            interface implicitly apply to the repeated capability instance chosen with the name parameter. 

         :Raises:
            RuntimeError: If the instance with the specified name doesn't exit in this collection, the 
            RuntimeError exception is raised.

         :Example:

           .. code-block:: python

             >>> channel = instrument.channels['CH1']
             >>> print(channel.name)
             CH1
             >>> print('type = ', type(channel))    
             type = <class 'keysight_ktdrvr.KtDrvrChannel'>


    **Instance Access by Numerical Index**

       **[index: int] operator**
         By using the [] operator, provided by the `__getitem__ <https://docs.python.org/3/reference/datamodel.html#object.__getitem__>`_ special method, 
         items can be indexed using their zero based position inside the collection, unless stated otherwise the contents of the collection retrieved in order of addition.

         :Returns: An interface reference to the desired repeated capability instance. All method and property calls on the returned 
            interface implicitly apply to the repeated capability instance chosen with the index parameter.

         :Raises:
           RuntimeError: If the index is out of range in this collection, the RuntimeError exception is raised.

         :Example:

           .. code-block:: python

             channel = instrument.channels[0]


    **Number of Items**

       To find the number of elements in a collection: you can use the *count* or use the built-in 
       `len() <https://docs.python.org/3/library/functions.html#len>`_ function.

         :Example:

           .. code-block:: python

             >>> num = len(instrument.channels)
             >>> print(num)
             >>> num = instrument.channels.count
             >>> print(num)
             5
             5


    **Iterate Through Instance Names**

       Like dictionaries, the `__iter__() <https://docs.python.org/3/reference/datamodel.html#object.__iter__>`_ 
       method is implemented to iterating over the keys.
       This means that if you put the collection into a for loop, you can get an iterator over its keys:

           .. code-block:: python

             >>> for name in instrument.channels:
             >>>    print(name)
             CH1 
             CH2
             CH3


    **Iterate Through items()**

       When working with collections, it is likely you may want to work with both the instance key and the values.
       One of the most useful ways to do it in Python is by using .items(), 
       which is a method that returns a new view of all the items:

           .. code-block:: python

             >>> for name, instance in instrument.channels.items():
             >>>    print(name, instance)
             CH1 <keysight_ktdrvr.KtDrvrChannel object at 0x00000299BA3D1880>
             CH2 <keysight_ktdrvr.KtDrvrChannel object at 0x00000299BA3790D8>
             CH3 <keysight_ktdrvr.KtDrvrChannel object at 0x00000299BA3D1880>


    **Get Name by Index**

       get_name_by_index(index: int) -> str

         Returns the item name by its index.             

         :Param int index: Zero-based index of the repeated capability name
         :Return: the item name by its index
         :Type: str

:doc:`Top <programming>`


