Skip to content

Getting Started with LabOne Modules

In this Tutorial you will learn the basic usage of the LabOne Modules. The Tutorial uses the Device Settings Module to store the settings of a connected device to a file.

Introduction

LabOne Modules are small software components that help with common tasks. An instance of a LabOne Module is created based on existing session to a data server.

sequenceDiagram
    actor User
    User ->>+ Client: connect to Data Server
    participant LabOne Module
    Client->>+Data Server: create new session
    Data Server-->>-Client: 
    Client-->>-User: 
    User ->>+ Client: create a LabOne Module
    Client->>+LabOne Module: instanciate(Data Server)
    LabOne Module->>+Data Server: create new session
    Data Server-->>-LabOne Module: 
    LabOne Module-->>-Client: 
    Client-->>-User: 

LabOne Data Server Session

The first step is therefore to create a connection to a LabOne Data Server and connect to a device.

from zhinst.core import ziDAQServer
daq = ziDAQServer("localhost", 8004, 6)
daq.connectDevice("DEV2345", "1GbE")
from zhinst.core import ziDAQServer
daq = ziDAQServer("localhost", 8005, 1)
daq.connectDevice("DEV2345", "USB")
from zhinst.toolkit import Session
session = Session("localhost")
device = session.connect_device("DEV2345")
from zhinst.toolkit import Session
session = Session("localhost", hf2=True)
device = session.connect_device("DEV2345")
#include <iostream>
#include <unistd.h>
#include "ziAPI.h"

/**
* Error handling for the ziAPI.
* Translate C API result codes into C++ exceptions
*
* @param[in] conn        Pointer to the ziConnection for which the value(s) will be set.
* @param[in] resultCode  Result code from the ziAPI.
*/
void handleError(ZIConnection conn, ZIResult_enum resultCode) {
    if (resultCode == ZI_INFO_SUCCESS) {
        return;
    }
    char *errorDescription;
    ziAPIGetError(resultCode, &errorDescription, nullptr);
    constexpr size_t maxErrorLength = 1000;
    char errorDetails[maxErrorLength];
    errorDetails[0] = 0;
    ziAPIGetLastError(conn, errorDetails, maxErrorLength);
    auto message = std::string{"LabOne C API error "} +
                    std::to_string(resultCode) + ": " + errorDescription +
                    "\nDetails: " + errorDetails;
    throw std::runtime_error(message);
}

int main(int argc, char** argv)
{
    ZIConnection conn = nullptr;
    handleError(conn, ziAPIInit(&conn));
    handleError(
        conn, ziAPIConnectEx(conn, "localhost", 8004, ZI_API_VERSION_6, nullptr)
    );
    handleError(conn, ziAPIConnectDevice(conn, "DEV2345", "1GbE", nullptr));
}
#include <iostream>
#include <unistd.h>
#include "ziAPI.h"

/**
* Error handling for the ziAPI.
* Translate C API result codes into C++ exceptions
*
* @param[in] conn        Pointer to the ziConnection for which the value(s) will be set.
* @param[in] resultCode  Result code from the ziAPI.
*/
void handleError(ZIConnection conn, ZIResult_enum resultCode) {
    if (resultCode == ZI_INFO_SUCCESS) {
        return;
    }
    char *errorDescription;
    ziAPIGetError(resultCode, &errorDescription, nullptr);
    constexpr size_t maxErrorLength = 1000;
    char errorDetails[maxErrorLength];
    errorDetails[0] = 0;
    ziAPIGetLastError(conn, errorDetails, maxErrorLength);
    auto message = std::string{"LabOne C API error "} +
                    std::to_string(resultCode) + ": " + errorDescription +
                    "\nDetails: " + errorDetails;
    throw std::runtime_error(message);
}

int main(int argc, char** argv)
{
    ZIConnection conn = nullptr;
    handleError(conn, ziAPIInit(&conn));
    handleError(
        conn, ziAPIConnectEx(conn, "localhost", 8005, ZI_API_VERSION_1, nullptr)
    );
    handleError(conn, ziAPIConnectDevice(conn, "DEV2345", "USB", nullptr));
}
ziDAQ('connect', 'localhost', 8004, 6);
ziDAQ('connectDevice', 'DEV2345', '1GbE')
ziDAQ('connect', 'localhost', 8005, 1);
ziDAQ('connectDevice', 'DEV2345', 'USB')
using zhinst;

namespace LabOneTutorial
{
    class Program
    {
        static void Main(string[] args)
        {
            ziDotNET daq = new ziDotNET();
            daq.init("localhost", 8004, (ZIAPIVersion_enum)6);
            daq.connectDevice("DEV2345", "1GbE", "");
        }
    }
}
using zhinst;

namespace LabOneTutorial
{
    class Program
    {
        static void Main(string[] args)
        {
            ziDotNET daq = new ziDotNET();
            daq.init("localhost", 8005, (ZIAPIVersion_enum)1);
            daq.connectDevice("DEV2345", "USB", "");
        }
    }
}

TODO

Module Instantiation

A new instance of a LabOne Module can be created at any time, and there is no limitation how many instances can be created. However, since every module creates a new session to the Data Server, it consumes resources both inside the client and the server. LabOne Modules should therefore be created with care.

For the purpose of this tutorial, we instantiate a Device Settings Module. However, the process is for all LabOne Modules the same.

module = daq.deviceSettings()
module = session.modules.device_settings

Note

Toolkit caches the instance of every LabOne Module. To create a new instance of a LabOne Module one can use the create_... method

module = session.modules.create_device_settings_module()
ZIModuleHandle module;
handleError(conn, ziAPIModCreate(conn, &module, "deviceSettings"));
module = ziDAQ('deviceSettings');
ziModule module = daq.deviceSettings();

TODO

The Module already created a separate session to the Data Server and is ready to use.

Module Configuration

Modules are controlled, similar like the devices and Data Server, through nodes. The commands in the API are identical.

module.setString("/DEVICE", "DEV2345")
module.setString("/COMMAND", "save")
module.setString("/FILENAME", "module_tutorial")
output_dir = module.getString("/PATH")
print(f"Config file module_tutorial.xml will be written to {output_dir}.")
module.device(device)
module.command("save")
module.filename("module_tutorial")
output_dir = module.path()
print(f"Config file module_tutorial.xml will be written to {output_dir}.")
handleError(conn, ziAPIModSetString(conn, module, "/DEVICE", "DEV2345"));
handleError(conn, ziAPIModSetString(conn, module, "/COMMAND", "save"));
handleError(conn, ziAPIModSetString(conn, module, "/FILENAME", "module_tutorial"));
char outputDir[1024];
unsigned int length = 0;
handleError(conn, ziAPIModGetString(conn, module, "/PATH", outputDir, &length, 1024));
std::cout << "Config file module_tutorial.xml will be written to " << outputDir << "." << std::endl;
ziDAQ('setString', module, '/DEVICE', 'DEV2345')
ziDAQ('setString', module, '/COMMAND', 'save')
ziDAQ('setString', module, '/FILENAME', 'module_tutorial')
output_dir = ziDAQ('getString', module, '/PATH');
fprintf('Config file module_tutorial.xml will be written to %s.', output_dir);
module.setString("/DEVICE", "DEV2345");
module.setString("/COMMAND", "save");
module.setString("/FILENAME", "module_tutorial");
String outputDir = daq.getString("/PATH");
System.Diagnostics.Trace.WriteLine(
    String.Format("Config file module_tutorial.xml will be written to {0}.", outputDir));

TODO

The above commands configure the Device Settings Module to store the settings for the connected Device to the default directory in a file called module_tutorial.xml. The output of the above command should be the default directory path where the Device Settings Module stores and loads the settings from.

It becomes clear now the LabOne Modules can be controlled by nodes in the same way as the Devices and the Data Server. A list of available nodes can be found on the respective page.

Module Execution

The additional functions described in the overview page are available on all LabOne Modules and are used for general tasks such as starting, reading and checking the state of the Module.

Info

A lot of modules are in an idle state by default. After the configuration the must be started with the execute explicitly.

module.execute()
module.execute()
handleError(conn, ziAPIModExecute(conn, module));
ziDAQ('execute', module)

``` vbnet module.execute();

TODO

The execute commands starts the execution. Since the modules run in a separate thread the module execution is asynchronous and the user can either continuously read the data or do something else in the meantime.

import time
while not module.finished():
    time.sleep(0.01)
import time
while not module.finished():
    time.sleep(0.01)
int64_t finished = 0;
handleError(conn, ziAPIModFinished(conn, module, &finished));
while(!finished)
{
    handleError(conn, ziAPIModFinished(conn, module, &finished));
    usleep(10);
}
while not ziDAQ('finished', module)
    pause(0.01);
end

``` vbnet while (!module.finished()) { System.Threading.Thread.Sleep(50); }

TODO

The above lines block until the LabOne Module is finished.

Important

It is Important to wait before the module execution is finished before closing the program/session. Since the execution is asynchronous and the lifetime of the module thread is bound to the session object, the module would otherwise not be able to finish its task.

You have successfully used the LabOne Device Settings Module to store the settings from a connected device to a file called module_tutorial. The usage of the Module applies similarly to all other LabOne modules.