Mainpage - PIPython
Author: Physik Instrumente (PI) GmbH & Co. KG

PIPython is a collection of Python modules to access a PI device and process GCS data. It can be used with Python 2.7+ and 3.4+ on Windows, Linux and OSX and without the use of the GCS DLL also on any other platform.

## Quickstart

Communicate to a PI device via `GCSDevice` which wraps the GCS DLL functions and provides methods to connect to the device. Usually you call `GCSDevice` without an argument.

    from pipython import GCSDevice
    gcs = GCSDevice()
    gcs.ConnectTCPIP('192.168.90.112')  # you may change the IP address
    # gcs.ConnectUSB('1234567890')      # you may change the serial number
    # gcs.ConnectRS232(1, 115200)       # you may change the COM port and baudrate
    print(gcs.qIDN())
    gcs.CloseConnection()

Only for some older controllers that do not use the PI_GCS2_DLL you have to pass the controller name as argument.

    from pipython import GCSDevice
    gcs = GCSDevice('C-843')
    gcs.ConnectPciBoard(1)              # you may change the PCI board number
    print(gcs.qIDN())
    gcs.CloseConnection()

It's recommended to use `GCSDevice` as a context manager which closes the connection if an exception raises inside the `with` statement.

    from pipython import GCSDevice
    with GCSDevice() as gcs:
        gcs.InterfaceSetupDlg()         # interactive dialog to choose the interface
        print(gcs.qIDN())

See also the provided samples in the `samples` subdirectory. Start with `quickstart.py`.

## Requirements

There are no dependencies to additional packages.

With pipython.interfaces.piusb you can connect a USB device without using the GCS DLL. This works only on Linux systems and requires `LibUSB` which usually is provided by the OS.

## Arguments

From now on `gcs` refers to a connected `GCSDevice` instance.

Usually you can call a setter function with - a dictionary of axes/channels and values - a list for axes/channels and a list of the values - a single item for axis/channel and a single value

    gcs.MOV({'X': 1.23, 'Y': 2.34})
    gcs.MOV(['X', 'Y'], [1.23, 2.34])
    gcs.MOV('X', 1.23)

For channels and numeric axis names you can omit the quotes.

    gcs.MOV({1: 1.23, 2: 2.34})
    gcs.MOV([1, 2], [1.23, 2.34])
    gcs.MOV(1, 1.23)

Usually getter commands can be called with - a list of axes/channels - a single item for axis/channel, without quotes if numeric - without any arguments which will return the answer for all available axes/channels

    gcs.qPOS(['X', 'Y'])
    gcs.qPOS('X')
    gcs.qPOS(1)
    gcs.qPOS()

## Return values

Axes or channel related answers are returned as (ordered) dictionary.

    gcs.qPOS()
    >>>{'X': 1.23, 'Y': 2.34}

If you provide arguments their types are preserved and you can use these as keys.

    pos = gcs.qPOS([1, 2, 3])
    print(pos[1])

If you do not provide arguments you always have to use strings as keys.

    pos = gcs.qPOS()
    print(pos['1'])

The following sample will move all `axes` to `targets` and waits until the motion has finished. It shows how to use only the values from the returned dictionary.

    from time import sleep
    ...
    gcs.MOV(axes, targets)
    while not all(gcs.qONT(axes).values()):
        sleep(0.1)

## Some hints...

### Helpers

In `pipython.pitools` you will find some helper funtions for your convenience. See the docstrings and the provided samples for how to use them. The sample above can then be written as:

    import pipython.pitools as pi
    ...
    gcs.MOV(axes, targets)
    pi.waitontarget(gcs, axes)

### Enable debug logging

To log debug messages on the console just enter these lines prior to calling `GCSDevice`.

    from logging import basicConfig, DEBUG
    basicConfig(level=DEBUG)

There is also a property to log the communication data to a text file.

    gcs.logfile = 'commandlog.txt'

### GCSError and error check

By default an "ERR?" command is sent after each command to query if an error occurred on the device which then will be raised as `GCSError` exception. If communication speed is an issue you can disable error checking.

    gcs.errcheck = False

To handle a catched `GCSError` exception you can use the defines provided by `gcserror` instead of pure numeric values. Remember the difference between `GCSError` which is the exception class and `gcserror` which is the according module.

    from pipython import GCSDevice, GCSError, gcserror
    with GCSDevice() as gcs:
        try:
            gcs.MOV('X', 1.23)
        except GCSError as exc:
            if exc == gcserror.E_1024_PI_MOTION_ERROR:
                print 'There was a motion error, please check the mechanics.'
            else:
                raise

The exception class `GCSError` will translate the error code into a readable message.

    from pipython import GCSError, gcserror
    raise GCSError(gcserror.E_1024_PI_MOTION_ERROR)
    >>>GCSError: Motion error: position error too large, servo is switched off automatically (-1024)

With the property `GCSDevice.embederr` you can embed the error querying command into a GCS set command. Hence only one package needs to be sent to the device which also can improve communication performance.

### Big data

Commands like `qDRR()` which read a large amount of GCS data return immediately with the header dictionary containing information about the data. Then they will start a background task that carries on reading data from the device into an internal buffer. The `bufstate` property returns the progress of the reading as floating point number in the range 0 to 1 and turns to `True` when reading has finished. Hence, when using it in a loop check for `is not True`. (Remember, this is not the same as `!= True`.)

    header = gcs.qDRR(tables=1, offset=1, numvalues=8192)
    while gcs.bufstate is not True:
        print('read data {:.1f}%...'.format(gcs.bufstate * 100))
        sleep(0.1)
    data = gcs.bufdata

### Textual interface

Besides the functions implemented in GCSCommands you can send GCS commands as strings to the controller. Use `read()` for commands returning an answer, `read_gcsdata()` for commands returning GCS data and `send()` for non-answering commands.

    print gcs.read('POS?')
    print gcs.read_gcsdata('DRR? 1 100 1')
    gcs.send('MOV X 1.23')

They return the raw string or GCS data from the controller. If `errorcheck` is enabled the error state is queried from the device automatically. It's recommended to use the provided functions instead of sending raw strings.

In line with the C++ GCS DLL the functions `ReadGCSCommand()` and `GcsCommandset()` are also available. They will never query an error from the device.

    print gcs.ReadGCSCommand('POS?')
    gcs.GcsCommandset('MOV X 1.23')