Getting Started with LabOne Modules¶
In this tutorial you will learn how to use the LabOne modules. Here, we run the Device Settings module to store the configuration of an instrument to a file for future use.
Introduction¶
The LabOne modules are software components that help with common tasks. An instance of a LabOne module is created based on an 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: Instantiate (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 to create a connection to a LabOne data server and then to establish the connection between the data server and an instrument.
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")
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")
#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", "");
}
}
}
Module Instantiation¶
A new instance of a LabOne module can be created at any time, and there is no limitation on the number of module instances. However, since every module creates a new session to the data server, it consumes resources in the client and the server. Therefore, LabOne modules should 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 = 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()
module = daq.deviceSettings()
ZIModuleHandle module;
handleError(conn, ziAPIModCreate(conn, &module, "deviceSettings"));
module = ziDAQ('deviceSettings');
ziModule module = daq.deviceSettings();
The module has already created a separate session to the data server and is now ready to use.
Module Configuration¶
Similar to the instrument and data server, the modules are controlled through nodes. The commands to modify the module nodes in the API are identical.
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}.")
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}.")
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));
The above commands configure the Device Settings module to store the settings
of the connected instrument to the default directory in a file called module_tutorial.xml.
The output of the above commands should be the default directory path where the
Device Settings module stores and loads the settings from.
It becomes clear now that the LabOne modules can be controlled by nodes in the same way as the Devices and the data server. A list of available nodes in a module can be found on its corresponding documentation 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
By default, most of the modules are in an idle state. After configuration, they must be started with the execute command explicitly.
module.execute()
module.execute()
handleError(conn, ziAPIModExecute(conn, module));
ziDAQ('execute', module)
``` vbnet module.execute();
The execute command starts the module's task. 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); }
The above lines block until the LabOne module is finished.
Important
It is important to wait until the module execution is finished before closing the program or session. Since the execution is asynchronous and the lifetime of the module thread is bound to the session object, the module is not otherwise 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. A similar approach can be applied to all other LabOne modules.