Table of Contents

C Client Library

V3DSClientLib provides a strict C function interface. Any language that supports C, such C++, Objective-C, Java, Matlab and Python, can use V3DSClientLib to access real-time data from Visual3DServer. The most important functions are:

v3ds_init() and v3ds_kill() v3ds_connect() and v3ds_disconnect() v3ds_setSystems(), v3ds_setPipeline() and v3ds_setLinkModel() v3ds_installCallback() for clients that are pushed data v3ds_getValue() or v3ds_getResults() for clients that poll for data The remaining functions are mostly for examining and adjusting the internal state of V3DSClientLib.

C and C++ clients have the option of polling for data or being pushed data through an installed C callback function. Matlab and Python clients must poll for data currently.

Example client code is provided in ANSI C, C++, Matlab and Python to help you get started using V3DSClientLib.


V3DSClientLib


bool v3ds_init(...)

Recommend default values are: - TCP timeout: 5000 ms - TCP buffer size: 16383 B - UDP buffer size: 32767 B - thread sleep: 1 ms - thread priority: “normal” Thread priority can be one of “Critical”, “Highest”, “High”, “Normal”, “Low”, “Lowest”, “Idle”. Thread priority is case insensitive.


bool v3ds_kill()


int v3ds_getTcpTimeout()


int v3ds_getTcpBufferSize()

Importnat: the TCP buffer must be large enough to hold the longest possible message. 16384 bytes is the recommended minimum size.


int v3ds_getUdpBufferSize()

Important: the UDP buffer must be large enough to hold the longest possible data results. 32676 bytes is the recommended minimum size. Note that the data results length is typically proportional to the length of the pipeline script set in the client.


const char* v3ds_getPriority()


int v3ds_getSleep()

A small sleep value makes the data acquisition loop run faster, minimizing the UDP packet size and the possibly of missing frames from Visaul3DServer. However, less CPU time will be available to other programs. A larger sleep value makes the data acquisition loop run slower, increasing the UDP packet size and the possibly of missing frames from Visual3DServer. However, more CPU time will be available to other programs. In general, a lower sleep value is appropriate, especially if Visual3DServer or the 3rd party mocap software is running on a different machine. Thread sleep is set when the client calls v3ds_init().


bool v3ds_connect(...)

The port number for Visual3DServer is 12099. Here are examples for the descriptive client strings: Client name: “MyClientApp”–any client name is allowed. Client version: “1.0.0”–any version number format is allowed. Client date: “2014-Aug-25”–any date format is allowed. Client environment: “Windows8Pro”–any environment description is allowed. Client protocol: “Biofeedback”–any protocol description is allowed.


bool v3ds_isConnected()


bool v3ds_disconnect()


const char* v3ds_getClientName()


const char* v3ds_getClientVersion()


const char* v3ds_getClientDate()


const char* v3ds_getClientEnvironment()


const char* v3ds_getClientProtocol()


const char* v3ds_getTCPIPAddress()


int v3ds_getUDPPort()


bool v3ds_setSystems(char*)

A client can select more than one system and also set the priority order of those systems.


bool v3ds_isSystemsSet()


const char* v3ds_getSystems()


bool v3ds_setPipeline(char*)


v3ds_isPipelineSet


const char* v3ds_getPipeline()


bool v3ds_setLinkModel(char*)


bool v3ds_isLinkModelSet()


const char* v3ds_getLinkModel()


bool v3ds_parseXML(char*, void*)

If the pdeqRes argument is NOT 0, then the results are stored in the pdeqRes argument and are accessible C++ clients immediately. If the pdeqRes argument is 0, then the results are stored internally and are accessed by the client using the v3ds_getResult..() functions. The client must ensure the mutex is locked before calling any v3ds_getResult..() functions. See ''%%v3ds_lockMutex()%%''.


bool v3ds_lockMutex()

While the mutex is locked, the client can call any of the v3ds_getResult..() functions. The client should spend as little time as possible between calls to v3ds_lockMutex() and v3ds_unlockMutex(). The mutex will ONLY be locked if there is new data available when this function is called. Otherwise the mutex will NOT be locked when this function returns.


void v3ds_unlockMutex()


int v3ds_getResultsCount()

The client must ensure the mutex is locked before calling this function. See ''%%v3ds_lockMutex()%%''.


const char* v3ds_getResultString(const int)

pchDest.

The client must ensure the mutex is locked before calling this function. See ''%%v3ds_lockMutex()%%''.


const char* v3ds_getResultType(const int)

This function might alter the status string. The client must ensure the mutex is locked before calling this function. See ''%%v3ds_lockMutex()%%''.


const char* v3ds_getResultFolder(const int)

This function might alter the status string. The client must ensure the mutex is locked before calling this function. See ''%%v3ds_lockMutex()%%''.


const char* v3ds_getResultName(const int)

This function might alter the status string. The client must ensure the mutex is locked before calling this function. See ''%%v3ds_lockMutex()%%''.


int v3ds_getResultFramesCount(const int)

The client must ensure the mutex is locked before calling this function. See ''%%v3ds_lockMutex()%%''.


int v3ds_getResultFrameValue(const int, const int)

This function might alter the status string. The client must ensure the mutex is locked before calling this function. See ''%%v3ds_lockMutex()%%''.


int v3ds_getResultFrameValuesCount(const int, const int)

This function might alter the status string. The client must ensure the mutex is locked before calling this function. See ''%%v3ds_lockMutex()%%''.


float v3ds_getResultValue(const int, const int, const int)

This function might alter the status string. The client must ensure the mutex is locked before calling this function. See ''%%v3ds_lockMutex()%%''.


void* v3ds_getResultsObject()

The client must ensure the mutex is locked before calling this function. See ''%%v3ds_lockMutex()%%''. .


void v3ds_installCallback(*pCallback, void*)


bool v3ds_isCallbackInstalled()


const char* v3ds_getStatus()

The memory for the status string is owned by the library. The client should never delete this memory.


Example Client Code

Example client code is provided in ANSI C, C++, Matlab and Python to help you get started using V3DSClientLib.

Note that the examples generally contain less error checking code and more global variables than a typical real-world program. This was done so that the important details of using V3DSClientLib would stand out. Ideally your clients would be more fully and robustly designed.


ANSI C Example Client Code

/*******************************************************************************
| ANSI C ConsoleClient Example
| Copyright (C) 2014 C-Motion, Inc.
|******************************************************************************/

/* Copy V3DSClientLib.dll to client application folder. */
/* Copy Qt5Core.dll and Qt5Network.dll to client application folder. */
/* Copy Qt platform folder to client application folder. */

/* Link to V3DSClientLib.lib to client application. */

/* Include V3DSClientLib header */
#include "../V3DSClientLib/V3DSClientLib.h"

/* Global Variables */
static int      g_nCount        = 0;        // How many results did we count?
static bool     bPollForData    = false;    // false means use callback instead.

/* Global functions */
static bool     dataCallback(void* pUserData, char* pXML, int nMsgCount);
static bool     printData();

/*******************************************************************************
| main
|******************************************************************************/

int main(int argc, char* argv[])
{
    /* Initialize V3DSClientLib. */
    bool bContinue = v3ds_init(5000, 16384, 32768, 1, "normal");

    if(bContinue)
    {
        /* Connect to Visual3DServer. */
        bContinue = v3ds_connect("127.0.0.1", 12099, "ConsoleClient", "1.0",
            "Today", "env", "Biofeedback");
    }

    /* Initialize count variables for counting results. */
    g_nCount = 0;
    int nMaxCount = 5;

    if(bContinue && !bPollForData)
    {
        /* Install callback if bPollForData is false. */
        v3ds_installCallback(dataCallback, 0);
    }

    if(bContinue)
    {
        /* Set selected system to C3D File. */
        bContinue = v3ds_setSystems("C3D");
    }

    if(bContinue)
    {
        /* Set pipeline script to look for LTOE marker. */
        char* pchPipelineScript =
            "Multiply_Signals_By_Constant"
            "/SIGNAL_TYPES=TARGET"
            "/SIGNAL_FOLDER=ORIGINAL"
            "/SIGNAL_NAMES=LTOE"
            "/RESULT_TYPES=TARGET"
            "/RESULT_FOLDER=ORIGINAL"
            "/RESULT_NAMES=LTOE"
            "/RESULT_SUFFIX=_RT"
            "/SIGNAL_COMPONENTS=0"
            "/CONSTANT=1;";
        bContinue = v3ds_setPipeline(pchPipelineScript);
    }

    /* Look for nMaxCount results in a fast loop. */
    while(g_nCount < nMaxCount)
    {
        /* Check if we are polling instead of the callback method. */
        if(bPollForData)
        {
            /* We are polling.  Print any new reuslts. */
            if(v3ds_lockMutex())
            {
                printData();
                v3ds_unlockMutex();
            }
        }
    }

    /* Tear down V3DSClientLib before quitting application. */
    v3ds_kill();

    return 0;
}

/*******************************************************************************
| dataCallback -- invoked when bPollForData is false
|******************************************************************************/

static bool dataCallback(void* pUserData, char* pXML, int nMsgCount)
{
    bool bHaveData = false;

    // mutex is automatically locked while callback is invoked.

    // 0 for 2nd arg means we are not a C++ client, so we need to use
    // v3ds_get..() functions to get data in printData().
    if(v3ds_parseXML(pXML, 0))
    {
        bHaveData = printData();
    }

    // mutex is automatically unlocked when callback returns.

    return bHaveData; 
}

/*******************************************************************************
| printData -- called either when polling or using the callback method
|******************************************************************************/

static bool printData()
{
    bool bHaveData = false;

    int nNumRes = v3ds_getResultsCount();
    if(0 < nNumRes)
    {
        const bool kbUseStringFunc = true;

        for(int r = 0; r < nNumRes; r++)
        {
            if(kbUseStringFunc)
            {
                const int knBufSize = 4096; // make it something big
                char chBuf[knBufSize];
                if(0 < v3ds_getResultString(r, chBuf, knBufSize))
                {
                    printf("%s", chBuf);
                }
                else
                {
                    printf("No data found for result %d.\n", r);
                }
            }
            else    // ask for each piece of data separately.
            {
                printf("Result name: %s\n", v3ds_getResultName(r));
                int nSubFrames = v3ds_getResultFramesCount(r);
                if(0 <  nSubFrames)
                {
                    int nFrameVal = v3ds_getResultFrameValue(r, 0);
                    printf("Subframes: %d, subframe %d value: %d\n",
                        nSubFrames,  1, nFrameVal);

                    int nNumCompPerFrame = v3ds_getResultFrameValuesCount(r);
                    printf("Components: %d\n", nNumCompPerFrame);

                    for(int c = 0; c < nNumCompPerFrame; c++)
                    {
                        printf("Component %d value: %f\n", c+1,
                            v3ds_getResultValue(r, 0, c));
                    }
                }
            }
        }

        g_nCount++; /* main() is examining this variable in a while loop. */

        bHaveData = true;
    }

    return bHaveData;
}

/******************************************************************************/

C++ Example Client Code

The C++ example is very similar to the C example.

The notable difference is the optional use of a std::deque<CResType> to hold the results coming from Visaul3DServer. C++ clients can directly access this deque of results instead of calling various v3ds_get..() functions.

When using a callback function, C++ clients can pass a deque of results to v3ds_parseXML() to be populated.

When polling, C++ clients can call getResults() to get a deque of results. The return from getResults() must be cast from void* to std::deque<CResType>*.

/*******************************************************************************
| C++ ConsoleClient Example application
| Copyright (C) 2014 C-Motion, Inc.
|******************************************************************************/

// Copy V3DSClientLib.dll to client application folder.
// Copy Qt5Core.dll and Qt5Network.dll to client application folder.
// Copy Qt platform folder to client application folder.

// Link to V3DSClientLib.lib to client application.

// Include V3DSClientLib header
#include "../V3DSClientLib/V3DSClientLib.h"

// Global Variables
static int      g_nCount        = 0;        // How many results did we count?
static bool     bPollForData    = false;    // false means use callback instead.

// Global functions
static bool     dataCallback(void* pUserData, char* pXML, int nMsgCount);
static bool     printData(const std::deque<CResType>& deqRes);

static std::deque<CResType>   g_deqRes; // C++ Object to hold results

/*******************************************************************************
| main
|******************************************************************************/

int main(int argc, char* argv[])
{
    std::cout << "main()" << std::endl;

    // Initialize V3DSClientLib.
    bool bContinue = v3ds_init(5000, 16384, 32768, 1, "normal");

    if(bContinue)
    {
        // Connect to Visual3DServer.
        bContinue = v3ds_connect("127.0.0.1", 12099, "ConsoleClient", "1.0",
            "Today", "env", "Biofeedback");
    }

    // Initialize count variables for counting results.
    g_nCount = 0;
    int nMaxCount = 5;

    if(bContinue && !bPollForData)
    {
        // Install callback if bPollForData is false.
        v3ds_installCallback(dataCallback, 0);
    }

    if(bContinue)
    {
        // Set selected system to C3D File.
        bContinue = v3ds_setSystems("C3D");
    }

    if(bContinue)
    {
        // Set pipeline script to look for LTOE marker.
        char* pchPipelineScript =
            "Multiply_Signals_By_Constant"
            "/SIGNAL_TYPES=TARGET"
            "/SIGNAL_FOLDER=ORIGINAL"
            "/SIGNAL_NAMES=LTOE"
            "/RESULT_TYPES=TARGET"
            "/RESULT_FOLDER=ORIGINAL"
            "/RESULT_NAMES=LTOE"
            "/RESULT_SUFFIX=_RT"
            "/SIGNAL_COMPONENTS=0"
            "/CONSTANT=1;";
        bContinue = v3ds_setPipeline(pchPipelineScript);
    }

    std::deque<CResType>* pdeqRes = 0;

    // Look for nMaxCount results in a fast loop.
    while(g_nCount < nMaxCount)
    {
        // Check if we are polling instead of the callback method.
        if(bPollForData)
        {
            // We are polling.  Print any new reuslts.
            if(v3ds_lockMutex())
            {
                void* p = v3ds_getResultsObject();
                if(0 != p)
                {
                    // Magic cast to access the CResType memory objects.
                    pdeqRes = reinterpret_cast<std::deque<CResType>*>(p);
                    printData(*pdeqRes);

                    g_nCount++;
                }
                v3ds_unlockMutex();
            }
        }
    }

    // Tear down V3DSClientLib before quitting application.
    v3ds_kill();

    return 0;
}

/*******************************************************************************
| dataCallback -- invoked when bPollForData is false
|******************************************************************************/

static bool dataCallback(void* pUserData, char* pXML, int nMsgCount)
{
    std::cout << "dataCallback()" << std::endl;

    bool bHaveData = false;

    if(v3ds_parseXML(pXML, &g_deqRes))
    {
        bHaveData = printData(g_deqRes);

        g_nCount++; // main() is examining this variable in a while loop.
    }

    return bHaveData; 
}

/*******************************************************************************
| printData -- called either when polling or using the callback method
|******************************************************************************/

static bool printData(const std::deque<CResType>& deqRes)
{
    std::cout << "printData()" << std::endl;

    bool bHaveData = false;

    const size_t knNumRes = deqRes.size();
    if(0 < knNumRes)
    {
        const bool kbUseStringFunc = true;

        std::cout << "Results: " << knNumRes << std::endl;
        for(size_t r = 0; r < knNumRes; r++)
        {
            if(kbUseStringFunc)
            {
                std::cout << deqRes[r];
            }
            else    // ask for each piece of data separately.
            {
                std::cout <<
                    "Name: " << deqRes[r].m_strName.c_str() <<
                    ", Folder: " << deqRes[r].m_strFolder.c_str() <<
                    ", Type: " << deqRes[r].m_strType.c_str() << std::endl;

                const size_t knSubframes = deqRes[r].m_deqFrame.size();
                std::cout << "Subframes: " << knSubframes << std::endl;
                for(size_t f = 0; f <  knSubframes; f++)
                {
                    std::cout << "FrameId: " << deqRes[r].m_deqFrame[f].m_nFrameId << std::endl;
                    std::cout << "Timestamp: " << deqRes[r].m_deqFrame[f].m_fTime << std::endl;
                    const size_t knNumComp = deqRes[r].m_deqFrame[f].m_deqfVal.size();
                    std::cout << "Components: " << knNumComp << std::endl;
                    if(0 < knNumComp)
                    {
                        std::cout << "(";
                        for(size_t c = 0; c < knNumComp; c++)
                        {
                            std::cout << deqRes[r].m_deqFrame[f].m_deqfVal[c];
                            if(c < knNumComp-1)
                            {
                                std::cout << ",";
                            }
                        }
                        std::cout << ")" << std::endl;
                    }
                }
            }
        }

        bHaveData = true;
    }

    return bHaveData;
}

/******************************************************************************/

Matlab Example Client Code

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%   client.m
%
%   Demonstrates how to use the V3DSClientLib matlab package to communicate
%   with the Visual3DServer application.
%
%   This matlab file works with data/run.c3d streaming in Visual3DServer.
%
%   Copyright (C) 2013-2014 C-Motion, Inc.  All rights reserved.
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% Load Visual3DServer client library, if necessary.
addpath(strcat(getenv('CM_V3DS_DIR'), '\SDK'));
addpath(strcat(getenv('CM_V3DS_DIR'), '\SDK\ClientCode\Matlab'));
addpath(strcat(getenv('CM_V3DS_DIR'), '\SDK\Release'));

if not(libisloaded('V3DSClientLib'))
    loadlibrary('V3DSClientLib', 'V3DSClientLib.h');
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Script
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

if libisloaded('V3DSClientLib')
    disp('Initializing V3DSClientLib...');
    % Initial object to represent Visual3DServer (V3DS).
    bContinue = calllib('V3DSClientLib', 'v3ds_init', 5000, 4096, 8192, ...
        5, 'normal');
    disp(calllib('V3DSClientLib', 'v3ds_getStatus'));

    if bContinue
        disp('Connecting to V3DSClientLib...');
        % Connect to Visual3DServer with IP address, etc.
        bContinue = calllib('V3DSClientLib', 'v3ds_connect', ...
            '127.0.0.1', 12099, 'MatlabClient', '1.0', '01-Jan-2014', ...
            'env', 'Biofeedback');
        disp(calllib('V3DSClientLib', 'v3ds_getStatus'));
    else
        bContinue = calllib('V3DSClientLib', 'v3ds_kill');
        unloadlibrary('V3DSClientLib');
        return
    end

    if bContinue
        disp('Setting systems in V3DSClientLib...');
        % Tell V3DS that we want data from the C3D File data source.
        bContinue = calllib('V3DSClientLib', 'v3ds_setSystems', 'C3D');
        disp(calllib('V3DSClientLib', 'v3ds_getStatus'));
    else
        bContinue = calllib('V3DSClientLib', 'v3ds_kill');
        unloadlibrary('V3DSClientLib');
        return
    end

    if bContinue
        disp('Setting pipeline script in V3DSClientLib...');
        % Tell V3DS what pipeline script to execute on each frame of data.
        % Pipeline script can be inline, as done here, or loaded from a file.
        pipelineScript = ['Multiply_Signals_By_Constant' ...
            '/SIGNAL_TYPES=TARGET' ...
            '/SIGNAL_FOLDER=ORIGINAL' ...
            '/SIGNAL_NAMES=LTOE' ...
            '/RESULT_FOLDER=ORIGINAL' ...
            '/RESULT_TYPE=TARGET' ...
            '/RESULT_NAME=LTOE' ...
            '/RESULT_SUFFIX=_RT' ...
            '/CONSTANT=1.0;' ...
            'Multiply_Signals_By_Constant' ...
            '/SIGNAL_TYPES=ANALOG' ...
            '/SIGNAL_FOLDER=ORIGINAL' ...
            '/SIGNAL_NAMES=FZ1' ...
            '/RESULT_FOLDER=ORIGINAL' ...
            '/RESULT_TYPE=ANALOG' ...
            '/RESULT_NAME=FZ1' ...
            '/RESULT_SUFFIX=_RT' ...
            '/CONSTANT=1.0;'];
        bContinue = calllib('V3DSClientLib', 'v3ds_setPipeline', ...
            pipelineScript(1:end));
        disp(calllib('V3DSClientLib', 'v3ds_getStatus'));
    else
        bContinue = calllib('V3DSClientLib', 'v3ds_kill');
        unloadlibrary('V3DSClientLib');
        return
    end

    if bContinue
        disp('Looking for 5 frames of data...');
        startTime = tic;     % Note the start time.

        % Get pipeline command results now over and over.
        nNumFramesRecv = 0;
        nTotalNumFrames = 5;
        while nNumFramesRecv < nTotalNumFrames
            calllib('V3DSClientLib', 'v3ds_lockMutex');
            bClientResults = calllib('V3DSClientLib', 'v3ds_hasNewResults');
            if bClientResults
                nNumFramesRecv = nNumFramesRecv + 1;
                nNumRes = calllib('V3DSClientLib', 'v3ds_getNumResults');
                fprintf('Found %d result(s)\n', nNumRes);
                for r = 0:nNumRes-1
                    strName = calllib('V3DSClientLib', 'v3ds_getName', r);
                    fprintf('Result %d: %s\n', r+1, strName);
                    nSubFrames = calllib('V3DSClientLib', 'v3ds_getNumFrames', r);
                    if 0 < nSubFrames
                        nFrameVal = calllib('V3DSClientLib', 'v3ds_getFrameValue', r, 0);
                        fprintf('Subframes: %d, subframe %d value: %d\n', ...
                            nSubFrames,  0, nFrameVal);
                        nNumComps = calllib('V3DSClientLib', 'v3ds_getNumCompsPerFrame', r);
                        fprintf('Components: %d\n', nNumComps);
                        for c = 0:nNumComps-1
                            fVal = calllib('V3DSClientLib', 'v3ds_getValue', r, 0, c);
                            fprintf('Component %d value: %f\n', c+1, fVal);
                        end
                    end
                end
            end
            calllib('V3DSClientLib', 'v3ds_unlockMutex');
        end

        % Note how long it took to request frames
        fprintf('Elapsed time: %0.6f\n', toc(startTime));

        % Disconnect from Visual3DServer.
        calllib('V3DSClientLib', 'v3ds_disconnect');
    else
        disp(calllib('V3DSClientLib', 'v3ds_getStatus'));
        bContinue = calllib('V3DSClientLib', 'v3ds_kill');
        unloadlibrary('V3DSClientLib');
        return
    end

    % Tear down object that represents Visual3DServer (V3DS).
    bContinue = calllib('V3DSClientLib', 'v3ds_kill');

    if libisloaded('V3DSClientLib')
        unloadlibrary('V3DSClientLib');
    end
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

Python Example Client Code

################################################################################
#
#   client.py
#
#   Demonstrates how to use the V3DSClientLib python package to communicate
#   with the Visual3DServer application.
#
#   Copyright (C) 2013-2014 C-Motion, Inc.  All rights reserved.
#
################################################################################

################################################################################
# Imports
################################################################################

# Standard package imports
from ctypes import *
import time

v3dsClientLibPath = 'C:/Program Files (x86)/C-Motion/Visual3DServer/SDK/Release/V3DSClientLib.dll'
v3dsClientLib = cdll.LoadLibrary(v3dsClientLibPath)

v3dsClientLib.v3ds_init.restype = c_bool
v3dsClientLib.v3ds_kill.restype = c_bool

v3dsClientLib.v3ds_getTcpTimeout.restype = c_int
v3dsClientLib.v3ds_getTcpBufferSize.restype = c_int
v3dsClientLib.v3ds_getUdpBufferSize.restype = c_int

v3dsClientLib.v3ds_getPriority.restype = c_char_p
v3dsClientLib.v3ds_getSleep.restype = c_int

v3dsClientLib.v3ds_connect.restype = c_bool
v3dsClientLib.v3ds_isConnected.restype = c_bool
v3dsClientLib.v3ds_disconnect.restype = c_bool

v3dsClientLib.v3ds_getClientName.restype = c_char_p
v3dsClientLib.v3ds_getClientVersion.restype = c_char_p
v3dsClientLib.v3ds_getClientDate.restype = c_char_p
v3dsClientLib.v3ds_getClientEnvironment.restype = c_char_p
v3dsClientLib.v3ds_getClientProtocol.restype = c_char_p

v3dsClientLib.v3ds_setSystems.restype = c_bool
v3dsClientLib.v3ds_isSystemsSet.restype = c_bool
v3dsClientLib.v3ds_getSystems.restype = c_char_p

v3dsClientLib.v3ds_setPipeline.restype = c_bool
v3dsClientLib.v3ds_isPipelineSet.restype = c_bool
v3dsClientLib.v3ds_getPipeline.restype = c_char_p

v3dsClientLib.v3ds_setLinkModel.restype = c_bool
v3dsClientLib.v3ds_isLinkModelSet.restype = c_bool
v3dsClientLib.v3ds_getLinkModel.restype = c_char_p

v3dsClientLib.v3ds_hasNewResults.restype = c_bool
v3dsClientLib.v3ds_getNumResults.restype = c_int
v3dsClientLib.v3ds_getResultString.restype = c_char_p
v3dsClientLib.v3ds_getName.restype = c_char_p
v3dsClientLib.v3ds_getNumFrames.restype = c_int
v3dsClientLib.v3ds_getFrameValue.restype = c_int
v3dsClientLib.v3ds_getNumCompsPerFrame.restype = c_int
v3dsClientLib.v3ds_getValue.restype = c_float

v3dsClientLib.v3ds_getStatus.restype = c_char_p

################################################################################
# Script
################################################################################

# Create object to represent Visual3DServer (V3DS).
print('Initializing V3DSClientLib...')
bContinue = v3dsClientLib.v3ds_init(5000, 4096, 8192, 5, b"normal")
print('Status: ' + v3dsClientLib.v3ds_getStatus())

# Connect to Visual3DServer with IP address, etc.
if bContinue:
    print('Connecting to V3DSClientLib...')
    bContinue = v3dsClientLib.v3ds_connect(b"127.0.0.1", 12099, b"Python27Client", \
        b"1.0", b'20-Dec-2013', b'env', b'Biofeedback')
    print('Status: ' + v3dsClientLib.v3ds_getStatus())
else:
    exit()

if bContinue:
    print('Getting systems in V3DSClientLib...')
    # Ask V3DS the systems.
    strSystems = v3dsClientLib.v3ds_getSystems()
    print('Systems: ' + strSystems)
    print('Status: ' + v3dsClientLib.v3ds_getStatus())
else:
    exit()

if bContinue:
    print('Setting systems in V3DSClientLib...')
    # Tell V3DS the systems, in priority order, in which to receive data.
    bContinue = v3dsClientLib.v3ds_setSystems(b'C3D')
    print('Status: ' + v3dsClientLib.v3ds_getStatus())
else:
    exit()

if bContinue:
    print('Setting pipeline script in V3DSClientLib...')
    # Tell V3DS what pipeline script to execute on each frame of data.
    # Pipeline script can be inline, as done here, or loaded from a file.
    pipelineScript = \
        b'Multiply_Signals_By_Constant' \
        b'/SIGNAL_TYPES=TARGET' \
        b'/SIGNAL_FOLDER=ORIGINAL' \
        b'/SIGNAL_NAMES=LTOE' \
        b'/RESULT_FOLDER=ORIGINAL' \
        b'/RESULT_TYPE=TARGET' \
        b'/RESULT_NAME=LTOE' \
        b'/RESULT_SUFFIX=_RT' \
        b'/CONSTANT=1.0;' \
        b'Multiply_Signals_By_Constant' \
        b'/SIGNAL_TYPES=ANALOG' \
        b'/SIGNAL_FOLDER=ORIGINAL' \
        b'/SIGNAL_NAMES=FZ1' \
        b'/RESULT_FOLDER=ORIGINAL' \
        b'/RESULT_TYPE=ANALOG' \
        b'/RESULT_NAME=FZ1' \
        b'/RESULT_SUFFIX=_RT' \
        b'/CONSTANT=1.0;'
        
    bContinue = v3dsClientLib.v3ds_setPipeline(pipelineScript)
    print('Status: ' + v3dsClientLib.v3ds_getStatus())
else:
    exit()

if bContinue:
    print('Looking for 5 frames of data...')

    startTime = time.clock()   # Note the start time.

    nNumFramesRecv = 0
    nTotalNumFrames = 5
    while nNumFramesRecv < nTotalNumFrames:
        v3dsClientLib.v3ds_lockMutex()
        clientResults = v3dsClientLib.v3ds_hasNewResults()
        if clientResults:
            nNumFramesRecv += 1
            nNumRes = v3dsClientLib.v3ds_getNumResults()
            print('Found ' + str(nNumRes) + ' result(s)')
            for r in range(0, nNumRes):
                strName = str(v3dsClientLib.v3ds_getName(r))
                print('Result ' + str(r+1) + ': ' + strName)
                nSubFrames = v3dsClientLib.v3ds_getNumFrames(r)
                if 0 < nSubFrames:
                    nFrameVal = v3dsClientLib.v3ds_getFrameValue(r, 0)
                    print('Subframes: ' + str(nSubFrames) + \
                        ', subframe 1 value: ' + str(nFrameVal))
                    nNumComps = v3dsClientLib.v3ds_getNumCompsPerFrame(r)
                    print('Components: ' + str(nNumComps))
                    for c in range(0, nNumComps):
                        fVal = v3dsClientLib.v3ds_getValue(r, 0, c)
                        print('Component ' + str(c+1) + ' value: ' + str(fVal))


        v3dsClientLib.v3ds_unlockMutex()

    # Note how long it took to request frames
    print('Elapsed time: ' + str(round(time.clock() - startTime, 6)))

    # Disconnect from Visual3DServer.
    v3dsClientLib.v3ds_disconnect()
else:
    print(v3dsClientLib.v3ds_getStatus())
    exit()

print('Status: ' + v3dsClientLib.v3ds_getStatus())

# Tear down object that represents Visual3DServer (V3DS).
bContinue = v3dsClientLib.v3ds_kill()

################################################################################