Components

Components represent all data acquisition objects that define the tree structure of an openDAQ™ system. Generally, they represent objects that represent physical device components (eg. Device, Channel), or data processing objects that form the Data path (eg. Function blocks, Signals, or Input Ports).

Each Component inherits all capabilities of a Property Object and uses its features to allow for the definition of user-customizable parameters such as a Device’s sample rate, or the number of averaged samples in an Averaging Function block.

The Component differentiates itself from base Property Objects in that it has a Local and Global ID in an openDAQ™ system and thus is a building block of the system’s structure. When viewed from the perspective of a user interface, Components would show up as a tree-view that represents the structure, while Properties (including Object-type properties that represent base Property Objects), appear as customizable properties to the user in a separate part of the user interface.

Component types

The following components are currently available in the openDAQ™ SDK. The table below outlines their purpose in a brief explanation, and provides links to articles describing each component in detail.

Component

Description

Component

A base Component that describes any custom Component of a Device.

Folder

A base Component that can have 0 or more child Components.

IO Folder

A base Component that can have 0 or more IO (input-output) Components. IO Folders and Channels are IO Components.

Device

Representation of a physical, or virtual data acquisition Device that acts as a container for Channels, Function Blocks, Signals, and other Devices

Channel

Representation of physical hardware input, or output Channel. Examples of Channels are analog or digital inputs or outputs of DAQ hardware.

Signal

Signals carry data streams in the form of packets through openDAQ™ systems. They are used to form data paths from data producers (eg. Channels) to consumers (eg. Function Blocks with Input Ports).

Function Block

Data processing Component of the SDK. Function blocks can have 0 or more Input Ports through which they acquire data and 0 or more Output Signals through which they send data to any data consumer. Note that a Function Block can have multiple modes of operation that do not require any Input Ports or Output Signals (for example, a File writer that writes acquired data from an Input Port to a file, but does not produce Output Signals).

Input Port

Port object to which a Signal is connected, forming a Connection between them. When a Signal is connected to an Input Port the Input Port acts as a consumer and will receive Packets sent by the signal.

Component IDs

Each Component in the openDAQ™ tree has a unique Global ID. The Global ID is formed using the hierarchical structure of Components, with it being formed with the Local IDs of the Component’s ancestors separated by forward slashes ("/"). A Device with a Local ID "DAQDevice" that has a Channel named "CH1" with Output Signals "Scaled" and "Raw" would have the following components:

Local ID

Global ID

DAQDevice

DAQDevice

CH1

DAQDevice/io/CH1

Scaled

DAQDevice/io/CH1/Scaled

Raw

DAQDevice/io/CH1/Raw

Of note are the io components that separate the Device and Channel Components. These represent the Device’s IO Folder that is automatically created by the openDAQ™ Device in addition to the fb (Function Blocks) and sig (Signals) folders.

Folders

Folders are standard Components that can also have child Components. A Device for example, is a Folder, as is a Function Block. Folders are used to define the structure of an openDAQ™ system. Organization of standardized Folders such as Function Blocks and Devices, as well as standard Device folders (io, sig, fb, dev) is done automatically when such Components are added in an openDAQ™ system.

Non-standardized Folder structures can be designed by Devices with custom Components/Folders, as well as Function Blocks with nested Function Block structures. These are explored in detail in their respective articles.

Traversing Folders

Folders such as Devices and Function Blocks offer methods to simply navigate through their child Components. Devices provide getters for their Channel/Function Block/Signal/Device child Components, while Function Block provide similar getter methods for their Output Signals and nested Function Blocks.

When working with a custom Folder implementation, however, a generic getter method is available that returns all child Components. Folders, unless specialized (eg. IO Folders), should only have base Folders or base Components as children. A base Folder should not have Devices, Function Blocks, Signals, or other DAQ Components as child Components.

Example of a recursive folder traversal method that prints out the Global IDs of all leaf Components:

  • Cpp

  • Python

void traverseFolder(const FolderPtr& folder)
{
    for (auto childComponent : folder.getItems())
    {
        if (auto childFolder = childComponent.asPtrOrNull<IFolder>(); childFolder.assigned())
            traverseFolder(childFolder);
        else
            std::cout << childComponent.getGlobalId() << std::endl;
    }
}
def traverseFolder(folder: opendaq.IFolder):
    for childComponent in folder.items:
        if isinstance(childComponent, opendaq.IFolder):
            traverseFolder(childComponent)
        else:
            print(childComponent.global_id)

IO Folders

Folders can have specializations where only specific child Components can be added as children. Currently openDAQ™ contains only the IO Folder specialization. IO Folders can only have other IO Folders or Channels as children. This allows Devices to organize their channels below the IO Folder in a tree structure but prevents them from placing non-Channel Components into the folder.

Each Device always has an io folder as its child, and all its Channels should be children of the IO Folder.
  • How to search components (not yet implemented/automated in the SDK)