Function Block
Function Blocks are data processing entities in openDAQ that can act as both consumers and producers of data. They specify a list of zero or more Input Ports and a list of zero or more output Signals. A Function Block is most often used to gather and process data and output the data to a sink (Signal, file, etc.).
Some examples of Function Blocks include:
-
An FFT Function Block, which performs the Fourier transform on input data obtained through its Input Port, and outputs a spectrum Signal.
-
A File Writer, which writes all input data received through its Input Port into a file.
-
A Signal Generator, which generates sine waves, and outputs them as Signals.
-
A Scaling Function Block, which scales input data received through its Input Port and produces a scaled Signal as output.
Combining Function Blocks to form extended Data Path chains is the core of Signal processing in openDAQ. It allows users to create sequences of Function Blocks to perform data analysis.
Function Blocks also provide a set of properties allowing users to modify their behavior. For example, an FFT Function Block might provide a set of available windowing functions, as well as allow the number of samples per block to be configured.
Input Ports
An Input Port is used to connect a Signal to the Function Block.
-
Cpp
-
Python
-
C#
inputPort.connect(signal);
// signal is now connected to the inputPort
daq::SignalPtr signal1 = inputPort.getSignal();
assert(signal1 == signal)
input_port.connect(signal)
# signal is now connected to the inputPort
signal1 = input_port.signal
assert signal1 == signal
inputPort.Connect(signal);
// signal is now connected to the inputPort
Signal signal1 = inputPort.Signal;
Debug.Assert(signal1 == signal);
The connected Input Port will receive Packets via the Connection object. Function Block uses an Input Port to dequeue the Packets for processing. Some Function Blocks may create optional Input Ports. In this case, the Function Block can process the data (from other connected Input Ports) even if the optional Input Port is not connected.
As an example, a file writer Function Block could create an optional Input Port for gate input. If the gate Input Port is not connected to a Signal, it will store always, otherwise, it will store only when the gate Signal has a value different than zero.
Function Block instantiation
Function Blocks are dynamic and are created/removed using Device addFunctionBlock/removeFunctionBlock methods by the application. They can be created on the Device (if it runs openDAQ in firmware) or on the host computer.
-
Cpp
-
Python
-
C#
daq::FunctionBlockPtr fb = instance.addFunctionBlock("fft_fb");
// Function Block appears under FunctionBlocks of the instance
daq::ListPtr<IFunctionBlock> fbs = instance.getFunctionBlocks();
daq::FunctionBlockPtr fb1 = fbs[fbs.getCount() - 1];
assert(fb == fb1);
fb = instance.add_function_block("fft_fb")
# Function Block appears under FunctionBlocks of the instance
fbs = instance.function_blocks
fb1 = fbs[-1]
assert fb == fb1
FunctionBlock fb = instance.AddFunctionBlock("fft_fb");
// Function Block appears under FunctionBlocks of the instance
IListObject<FunctionBlock> fbs = instance.GetFunctionBlocks();
FunctionBlock fb1 = fbs[fbs.Count - 1];
Debug.Assert(fb == fb1);
Function Blocks created on the Device appear also in the openDAQ host PC instance. These are mirrored or proxy function Function Blocks that can be configured on the host PC, but they do the processing on the Device.
The current version of openDAQ does not support the instantiation of Function Blocks on the Device from the host computer, but this should be possible in future versions.
A function block is implemented in a openDAQ™ Module. The SDK will properly route addFunctionBlock/removeFunctionBlock calls to the module that implements the Function Block.
Processing data
A typical role of the Function Block is to process an incoming stream of samples, manipulate them, and output results to an output Signal. The processing of samples implemented in a Function Block can be done in:
-
same thread that Packet was added to the Input Port,
-
task scheduled on a worker thread,
-
custom thread created by the Function Block implementation.
Which method is used depends on the implementation of the Function Block and cannot be changed through the application unless the Function Block implementation provides vendor-defined properties to set or change the processing method.
Nested Function Blocks
A Function Block can also contain other Function Blocks called nested or child Function Blocks. These function blocks can be some standard Function Blocks that can also be instantiated as standalone Function Blocks. In some other cases, nested Function Blocks can contain nested ones that are specific to the parent Function Block.
For example, a file writer Function Block that stores Signal data may contain a trigger Function Block as a child Function Block. Trigger Function Block can also be used as a standalone Function Block. Another example is the power Function Block which may contain filters and statistics as child Function Blocks.
Additionally, the parent Function Block can publish a standard nested Function Block with a limited set of options. It can also publish or hide Signals, Input Ports, and properties of child function blocks. For example, a power calculation Function Block could contain a standard IIR filter Function Block. However, power FB can hide many of its properties like cutoff frequency.
Channels
openDAQ Channels are specializations of Function Blocks that represent a Channel on physical hardware. They usually have physical inputs and/or outputs. Within openDAQ, they behave in the same way as a standard function block, but are used to identify Function Blocks that correspond to hardware components such as analog inputs, CAN busses, digital outputs, and others. Channels are mounted under the InputsOutputs folder of the Device. They are organized hierarchically, but a linear list can also be obtained.
-
Cpp
-
Python
-
C#
// get a flat list of channels
daq::ListPtr<IChannel> channels = device.getChannels()
# get a flat list of channels
channels = device.channels
// get a flat list of channels
IListObject<Channel> channels = device.GetChannels();
Channels are always instantiated by the owning Device. Channel creation and destruction can only be manipulated indirectly via Device properties (if supported by the Device).