What You’ll Learn 🧩
Understand DewesoftX’s C++ Plugin framework and its ability to extend core functionality
Install the Visual Studio plugin template (VSIX) to enable DewesoftX C++ plugin development
Create your first plugin: the “latch math” example—use two input channels, apply conditions, and output values
Develop custom UIs: build GUI components, use DewesoftBridge for interactive control, and handle events (e.g., button clicks)
Advance to vector-based processing (“vector latch math”), including saving/loading settings, debugging, and testing with unit tests
Integrate with Dewesoft internals: read/write channels, process data in real time, and modify UI dynamically
Debug and unit test your plugin with Visual Studio tools
Export/import compiled plugin bundles for easy distribution across DewesoftX installations
Course overview
The Basic Custom Plugin Development in C++ course empowers developers to build real-time plugins that integrate deeply with DewesoftX. It begins with an introduction to the C++ Plugin architecture, highlighting its ability to access Dewesoft’s internals—channels, UI, settings, and more—through DCOM, enabling full-featured custom modules.
You’ll set up your development environment by installing Visual Studio and the DewesoftX plugin template via a VSIX installer. With that in place, you’ll work through the iconic latch math example: monitoring one channel and outputting values from another based on a threshold condition—useful for scenarios like sampling RPM when a speed threshold is crossed.
The course then covers UI integration: creating custom panels, interactive controls via DewesoftBridge, event handling, and seamless integration with DewesoftX’s visual interface. You’ll later enhance your plugin to support vector-based signal processing (“vector latch math”), along with features like saving/loading settings, debugging in Visual Studio, and writing unit tests to ensure reliability.
Finally, you’ll learn how to package your plugin into a bundled DLL and distribute it—allowing users to import it into any DewesoftX setup. This module lays the foundation for more advanced topics, such as advanced plugin development and custom DAQ driver creation.
Page 1 of 19
Introduction to basic custom plugin development in C++
Do you want to be able to extend Dewesoft on your own? With the help of C++ Plugin you can create your own plugins and integrate them into any Dewesoft. Since C++ Plugin uses Dewesoft internals, it can do anything that Dewesoft can. You can take arbitrarily many input channels, process the data using modern C++, output the results into output channels, change Dewesoft settings, and much much more!
C++ Plugin also allows you to create your very own user interface which is, together with your C++ code, compiled into an external library and automatically recognised and loaded by Dewesoft. This is why your plugin can be easily exported and imported for use on other computers.
All the examples of C++ Plugins we will create and the setups we will use in this tutorial are available on Dewesoft's webpage under Support > Downloads > Developers > C++ Plugin. Note that you have to be logged in to access the C++ Plugin section.
Page 1 of 19
How to install the Dewesoft plugin template?
In order to start using C++ Plugin, you must have Visual Studio IDE installed on your system. Some of the reasons we have chosen Visual Studio are its functionalities, powerful developer tooling, like IntelliSense code completion and debugging, fast code editor, easy modification/customization and many more.
Dewesoft plugin for Visual Studio development can be found on the Dewesoft webpage under Support -> Downloads -> Developers -> C++ Plugin. Note that you have to be logged in to access the C++ Plugin section. After downloading, just double-click the VSIX file and the installer will guide you through the installation process.

Once VSIX plugin is downloaded and installed you will be able to create the Dewesoft C++ plugin using the New project window and selecting the DewesoftX C++ Plugin.

Page 1 of 19
Example: latch math
To get a better understanding of how to work with C++ Plugin we will implement a latch math module. Latch math outputs the value of an input channel when some other input channel crosses some predefined criteria. An example use case of this module could be to monitor your car engine RPM as you pass 100 km/h: we just set the first input channel to engine RPM, the second input channel to the channel with our car's current speed, and set the criteria to 100 km/h.
Given this, at the end of the example, we should end up with a working module that allows you to select two input channels, one to look for latch criteria points and one from which the outputted values will be read, and produce an output channel with these values.
Signals for testing our module
Before we begin creating our C++ Plugin, let's first create the two input signals we will use to test it. Open Dewesoft, click on the New setup button and then click Math.
The first signal will be the one we will use to look for criteria limit crossing. For our testing purposes, this signal will be a sine with a frequency of 1 Hz. We can create it by clicking the Formula button next to the Add math button and add sine function from the Signal tab. Let's name it "sine(1)". In the formula area write the following line:
1sine(1)
The second signal will be the one from which we read the values to output at criteria limit. This signal will be the current time. We add it by clicking the Formula button next to the Add math button and add time function from the Signal tab, naming it "time".
1timeime
Don't forget to save the setup!
Page 1 of 19
Example: new C++ plugin
Now we go back to Visual Studio. To create a new C++ Plugin we click the Project button in File tab > New > Project. We select the Dewesoft X Plugin Template as our template and fill in the name of our project. After clicking the Ok button a wizard window will appear to guide us through the creation of the plugin.
Since your plugin will be integrated inside Dewesoft, it needs to know Dewesoft's location. We can define the location by ourselves and the installer will create DEWESOFT_EXE_X86 (for 32-bit Dewesoft) and DEWESOFT_EXE_X64 (for 64-bit Dewesoft) system variables for us. In that case, Visual Studio will have to be restarted so it will update its used environmental variables state.
Once those system variables are set, installer will automatically find those fields for you and you will not have to specify the location for further usages.
If we want to set those variables ourselves, we can do this using System properties window (it can be found pressing Windows key and searching for Edit the system environment variables), and under advanced tab clicking the Environment variables.
If you only have the 64-bit (or 32-bit) version of Dewesoft on your computer, you will only be able to create 64-bit (or 32-bit) plugins.
After clicking the Next button the following window appears which is used to set Plugin information such as plugin name, its ownership, and version.
Plugin name - The name that will be seen in Dewesoft.
Description - Short description of your plugin.
Vendor - Company that created the plugin.
Copyright - Owner of the plugin.
Major version - Sets the initial major version. The value should change when a breaking change occurs (it's incompatible with previous versions).
Minor version - Sets the initial minor version. The value should change when new features and bug fixes are added without breaking compatibility.
Release version - Sets the initial release version. The value should change if the new changes contain only bugfixes.
All fields are optional except for the Plugin name, and they can all be modified later from the code.
After clicking the Next button a final window appears. This window is used to set your Base class name. It is used as a prefix for class and project name. When the Base class name is set, we can click the Finish button and the wizard will generate the plugin template based on your choices.

Below the base class name, you can see a dropdown menu, where you can select the example which will be generated for you. There are two options, one is a sine wave example, which will set everything up for outputing a sine wave values to the output channel. The other option is Empty example, which will remove all example code from the project. We recommend using this if you already have some knowledge about Dewesoft plugin development.
When a new C++ Plugin project is created, the wizard will create the basic files and project structure needed for development. In the picture below you can see the structure of a project in a tree view with collapsed items. In our case, ProTutorial refers to text, which was used as the Base class name.

proTutorial_latchMath_scalar - Used for communication with Dewesoft through its DCOM interface and creating the Plugin UI.
proTutorial_latchMath_scalarLib - Your main plugin logic should be defined here. In Example I, we will write plugin logic inside the Plugin project to simplify things.
proTutorial_latchMath_scalarTest - Contains test cases that will be run during unit testing.
gtest - Simple library, required by ProTutorialPluginTest for unit testing your plugin. This project should not be modified.
Once the plugin is created and you stumble upon Error MSB8036: The Windows SDK version sdk_version was not found. You should retarget your solution to use the one you have installed. You can do this by right clicking on your solution node in Solution explorer and selecting Retarget solution.

Once the Retarget solution window is opened, you can select "Latest installed version" item.
When our project is successfully generated, we will be able to extend Dewesoft. But before implementing the logic behind our plugin, let's take a look at how our plugin is integrated into Dewesoft. In order to do that, we have to start our program using the shortcut F5 or pressing the Start button in the center of Visual Studio's main toolbar.

After Dewesoft loads, our plugin can be accessed in Dewesofts main toolbar in Measure mode under Ch. setup -> ProTutorial. As we can see, it already contains some example elements which were automatically added to the user interface.


Page 1 of 19
Example I: creating custom UI
We are now ready to start creating the plugin for Latch math. We will start by taking a look at the already existing UI for latch math that is integrated into Dewesoft. It can be found in Measure mode -> Math -> Add math -> Latch value math. Keep in mind that this is a basic tutorial so we will keep things simple. Our goal is to recreate the UI on the right side of the picture below. We will not use the channel preview (bottom left corner), we will only allow the user to pick one Input channel and one Criteria channel and the Output value will be set to Actual by default, so we will not allow the user to change it. We will also add Radio buttons where the user will choose if we are looking for the latch condition at rising or falling edge.

Therefore, we will need two ComboBoxes (control, which contains a drop-down menu where only one item can be selected), both displaying channel names. One will be used for selecting the Input channel and the other will be used for selecting the Criteria channel. We will also need a TextBox (control, which displays a user-editable text) so the user will be able to enter the Criteria limit and a Label (control, which displays read-only text) that will be added to the TextBox so it will be more descriptive. Finally, we will need two RadioButtons (control, whose Checked property can be changed by clicking on it) for determining whether we are calculating rising or falling edge.
We will visually group settings for Input channel and Criteria channel using CaptionPanel (layout control with a title) and a Grid (a type of layout, which arranges its child controls in an arbitrary number of rows and columns that can be spanned).
We are now ready to create our custom user interface for latch math. The user interface is defined in the setup_window.xml file by using simple, XAML-like syntax. The XML code for our UI will be presented in smaller pieces so it will be easier to explain and understand. For each code snippet, there will be a picture added so you can see what the code creates. Your user interface can be seen inside Dewesoft when the plugin is run.
1<?xml version="1.0" encoding="utf-8"?>
2<Window xmlns="https://mui.dewesoft.com/schema/1.1">
3 <Grid>
4 <Grid.ColumnDefinitions>
5 <ColumnDefinition Width ="170"/>
6 <ColumnDefinition Width ="100%"/>
7 </Grid.ColumnDefinitions>
8 <Grid.RowDefinitions>
9 <RowDefinition Height="100%"/>
10 </Grid.RowDefinitions>
This part of the code contains a Grid that will split your UI into two columns. The first column will have a width of 170 pixels and the second column will take the remaining window width (e.g. if the window width is 500 pixels the second column will have a width of 330px). If the window is resized, the width of the first column will remain fixed but the width of the second column will be adapted to fit the window. As you can see the greed also contains a row, which takes the entire window height.

1 <CaptionPanel Grid.Column="0" Title="Input">
2 <Grid>
3 <Grid.RowDefinitions>
4 <RowDefinition Height="20"/>
5 <RowDefinition Height="100%"/>
6 </Grid.RowDefinitions>
7 <Grid.ColumnDefinitions>
8 <ColumnDefinition Width="100%"/>
9 </Grid.ColumnDefinitions>
10
11 <Label Text="Input channel" Grid.Row="0" />
12 <ComboBox MarginRight="20" Grid.Row="1" Name="inputChannelName" />
13 </Grid>
14 </CaptionPanel>
With the code above we will add CaptionPanel to the first column of our main Grid. This is done by adding Grid.Column="0"
to the CaptionPanel. The Grid inside the CaptionPanel contains two rows, one for the Label and one for the ComboBox.

1 <CaptionPanel Grid.Column="1" Title="Latch criteria settings">
2 <Grid>
3 <Grid.ColumnDefinitions>
4 <ColumnDefinition Width="140"/>
5 <ColumnDefinition Width="30"/>
6 <ColumnDefinition Width="120"/>
7 <ColumnDefinition Width="100%"/>
8 </Grid.ColumnDefinitions>
9 <Grid.RowDefinitions>
10 <RowDefinition Height="20"/>
11 <RowDefinition Height="20"/>
12 <RowDefinition Height="10"/>
13 <RowDefinition Height="20"/>
14 <RowDefinition Height="20"/>
15 <RowDefinition Height="100%"/>
16 </Grid.RowDefinitions>
17
18 <Label Grid.Column="0" Text="Criteria channel"/>
19 <ComboBox Grid.Column="0" Grid.Row="1" Name="criteriaChannelName" />
20 <Label Grid.Column="2" Text="Criteria limit" />
21 <TextBox Grid.Column="2" Grid.Row="1" Name="latchCriteria" Text="0"></TextBox>
22 <RadioButton Grid.Column="0" Grid.Row="3" Name="risingEdge" Label="Rising edge" IsChecked="True" />
23 <RadioButton Grid.Column="0" Grid.Row="4" Name="fallingEdge" Label="Falling edge" />
24 </Grid>
25 </CaptionPanel>
26 </Grid>
27</Window>
And lastly, we add the code for filling out the second column of our main Grid. This one is a bit more complex since elements need to be aligned and visually grouped (items that are connected should be closer. e.g. Criteria limit caption Label and TextBox). That is why we have created another Grid element with six rows and four columns, where our components can be inserted.

MUI designer
So far we have tested our user interface inside Dewesoft by running our plugin. When editing the UI it is not necessary to run the plugin every time we want to see the changes we made. We can use MUI Designer, which live previews your design. It can be found in Visual Studio > View > Other windows > MUI Designer (Dewesoft).
In the picture below you can see our final user interface in MUI Designer. The preview is automatically refreshed when the code is changed and if we run the plugin in Dewesoft the UI will look and behave exactly like shown in the MUI Designer.

Page 1 of 19
Example I: accessing the UI component using C++
We have now created the user interface for our plugin but it is not really doing anything at the moment. We still have to add some functionality to the UI components.
It is important to keep in mind that C++ uses header files (you can recognize them by the .h extension) in addition to source files. Header files are designed to provide information about your class and are used for declaration of variables and methods, while their initialization is done in the source files with .cpp extension. Before writing our own code, we will first remove the sample code as it is not needed. Our setup_window.h should look like this:
1#pragma once
2#include <mui/ds_window.h>
3#include <mui/controls.h>
4#include <mui/layout.h>
5
6class DewesoftBridge;
7
8class SetupWindow : public Dewesoft::MUI::DSWindow
9{
10public:
11 SetupWindow(Dewesoft::MUI::WindowPtr ui, DewesoftBridge& bridge);
12
13private:
14 DewesoftBridge& bridge;
15};
and setup_window.cpp should look like this:
1#include "StdAfx.h"
2#include "setup_window.h"
3#include "dewesoft_bridge.h"
4#include <thread>
5#include <chrono>
6#include <regex>
7
8using namespace Dewesoft::MUI;
9using namespace Dewesoft::RT::Core;
10
11SetupWindow::SetupWindow(WindowPtr ui, DewesoftBridge& bridge)
12 : DSWindow(ui, "ui/setup_window.xml")
13 , bridge(bridge)
14{
15}
Our new components will be declared in setup_window.h file. We usually keep components in the private section of our class. We then forward all the information from the controls and their events to the bridge that will handle all the communication with Dewesoft.
1private:
2 DewesoftBridge& bridge;
3
4 Dewesoft::MUI::ComboBox criteriaChannelCBox;
5 Dewesoft::MUI::ComboBox inputChannelCBox;
6 Dewesoft::MUI::TextBox latchCriteriaTBox;
7 Dewesoft::MUI::RadioButton risingEdgeRButton;
8 Dewesoft::MUI::RadioButton fallingEdgeRButton;
Members are later initialized in setup_window.cpp file where we connect them with UI component by specifying the component name. In our case this is done in the class constructor by using the Connect
method.
1SetupWindow::SetupWindow(WindowPtr ui, DewesoftBridge& bridge)
2 : DSWindow(ui, "ui/setup_window.xml")
3 , bridge(bridge)
4{
5 criteriaChannelCBox = ComboBox::Connect(ui, "criteriaChannelName");
6 inputChannelCBox = ComboBox::Connect(ui, "inputChannelName");
7 latchCriteriaTBox = TextBox::Connect(ui, "latchCriteria");
8 risingEdgeRButton = RadioButton::Connect(ui, "risingEdge");
9 fallingEdgeRButton = RadioButton::Connect(ui, "fallingEdge");
10}
After successfully connecting the UI components we now need to get the available channels from Dewesoft and list their names in ComboBoxes so the user can see and choose them. To make our calculation easier, our input channels will be synchronous (time difference between two sequential samples is always the same). We first have to get all the available channels from Dewesoft and filter out all but the synchronous ones.
In order to do so, we have to access Dewesoft internals, which can be done inside the DewesoftBridge (you can find it in the dewesoft_bridge.h and dewesoft_bridge.cpp). The channel information is stored in the bridge member variable app
which represents the Dewesoft DCOM interface. We create a getter function called getSyncChannels()
that will read the channels from app
, filter them and return only the synchronous ones.
1// dewesoft_bridge.h
2// this method needs to be public
3std::vector<IChannelPtr> getSyncChannels();
4
5// dewesoft_bridge.cpp
6std::vector<IChannelPtr> DewesoftBridge::getSyncChannels()
7{
8 std::vector<IChannelPtr> allSyncChannels;
9
10 app->Data->BuildChannelList();
11 IChannelListPtr channels = app->Data->UsedChannels;
12
13 for (int i = 0; i < channels->Count; i++)
14 if (!channels->GetItem(i)->Async)
15 allSyncChannels.push_back(channels->GetItem(i));
16
17 return allSyncChannels;
18}
Once getSyncChannels()
function is written, we are ready to insert channel names into CheckBoxes. We will do this in a constructor in the file setup_window.cpp below the initialization of the controls. The criteriaChannelCBox
and inputChannelCBox
refer to CheckBoxes in which channel names will be inserted. To insert channel names to ComboBox, we will create a function called void addChannelsToCBox();
, which should be public because we will need it later inside of the bridge.
1// setup_window.h
2void addChannelsToCBox();
1// setup_window.cpp
2SetupWindow::SetupWindow(WindowPtr ui, DewesoftBridge& bridge)
3 : DSWindow(ui, "ui/setup_window.xml")
4 , bridge(bridge)
5{
6 // member initialization
7 // ...
8 addChannelsToCBox();
9}
10
11void SetupWindow::addChannelsToCBox()
12{
13 criteriaChannelCBox.clear();
14 inputChannelCBox.clear();
15 std::vector<IChannelPtr> allChannels = bridge.getSyncChannels();
16
17 for (int i = 0; i < allChannels.size(); i++)
18 {
19 std::string channelName = allChannels[i]->Name;
20 criteriaChannelCBox.addItem(channelName);
21 inputChannelCBox.addItem(channelName);
22 }
23}
If we now start Dewesoft, channel names should appear when we click on ComboBoxes.

Page 1 of 19
Example I: handling events
We have now initialized all UI components we will need in our example, but these components don't do anything yet. To bring them to life we need to introduce events to our code (e.g. Click, CheckedChanged, ...). To create an event all you have to do is provide a method with the same signature as the control's event handler and then bind it to the control.
Event handlers are declared in the header files. Here you can see the declaration of event handlers that will be executed when an event is triggered. This part of the code should be placed inside the private section in setup_window.h file.
1private
2 // ...
3 void onCriteriaChannelChanged(Dewesoft::MUI::ComboBox& cBox, Dewesoft::MUI::EventArgs& args);
4 void onInputChannelChanged(Dewesoft::MUI::ComboBox& cBox, Dewesoft::MUI::EventArgs& args);
5 void onEditTextChanged(Dewesoft::MUI::TextBox& editBox, Dewesoft::MUI::EventArgs& args);
6 void onRadioGroupChanged(Dewesoft::MUI::RadioButton& radioGroup, Dewesoft::MUI::EventArgs& args);
7}
Because a component can have multiple event handlers bound to the same event, we use +=
operator for adding and -=
operator for removing event handlers. This can only be done after the variable is connected. In our case, we bind events in the constructor immediately after connecting the variables. The code for how to bind events to our controls in setup_window.cpp can be seen below.
1SetupWindow::SetupWindow(WindowPtr ui, DewesoftBridge& bridge)
2 : DSWindow(ui, "ui/setup_window.xml")
3 , bridge(bridge)
4{
5 // member initialisation
6 // adding channel names to ComboBoxes
7 // ...
8 criteriaChannelCBox.OnChange += event(&SetupWindow::onCriteriaChannelChanged);
9 inputChannelCBox.OnChange += event(&SetupWindow::onInputChannelChanged);
10 latchCriteriaTBox.OnTextChanged += event(&SetupWindow::onEditTextChanged);
11 risingEdgeRButton.OnCheckedChanged += event(&SetupWindow::onRadioGroupChanged);
12 fallingEdgeRButton.OnCheckedChanged += event(&SetupWindow::onRadioGroupChanged);
13}
Event handlers are defined in setup_window.cpp file. The following pictures will show which event handler is called when a certain action is performed.

This part of the code is triggered if we click on an item (that is not already selected) inside ComboBox containing Criteria channel names.
1void SetupWindow::onCriteriaChannelChanged(Dewesoft::MUI::ComboBox& cBox, Dewesoft::MUI::EventArgs& args)
2{
3 bridge.setCriteriaChannelName(cBox.getSelectedItem());
4}

This part of the code is triggered if we click on any item (that is not already selected) inside the ComboBox component containing input channel names.
1void SetupWindow::onInputChannelChanged(Dewesoft::MUI::ComboBox& cBox, Dewesoft::MUI::EventArgs& args)
2{
3 bridge.setInputChannelName(cBox.getSelectedItem());
4}

This part of the code is triggered if we edit text in component marked with the red square in the picture above.
1void SetupWindow::onEditTextChanged(Dewesoft::MUI::TextBox& editBox, Dewesoft::MUI::EventArgs& args)
2{
3 bridge.setCriteriaLimit(stof(editBox.getText().toStdString()));
4}

This part of the code is triggered if we click on the component marked with the red square in the picture above.
1void SetupWindow::onRadioGroupChanged(RadioButton& radioGroup, EventArgs& args)
2{
3 if (SetupWindow::fallingEdgeRButton.getIsChecked())
4 bridge.setEdgeType(FallingEdge);
5 else
6 bridge.setEdgeType(RisingEdge);
7}
Page 1 of 19
Example I: DewesoftBridge
The main purpose of DewesoftBridge is to allow your plugin to communicate with Dewesoft. It contains functions and events, that are triggered at a certain time (e.g. when measuring is started, when measuring is stopped, when setup is saved,...). The list of all function and events can be found in dewesoft_bridge.h file. In this section we will only mention those used in order for our plugin to work as intended, others will remain unchanged. We will use this class for implementing the logic behind the latch math.
We will first make changes to the dewesoft_bridge.h file where we will declare methods and variables. For our plugin to work we need variables for holding Criteria limit value, channel names and for determining whether we are calculating the rising or falling edge. We will also create getter and setter functions for these variables. All of these variables and methods are only going to be accessed inside the DewesoftBridge, so they should be private.
1enum edgeTypes
2{
3 RisingEdge = 0,
4 FallingEdge = 1
5};
6
7class DewesoftBridge
8{
9public:
10 // ...
11
12 /* Declarations of methods we added*/
13 std::vector<IChannelPtr> getSyncChannels();
14
15 void setCriteriaChannelName(std::string name);
16 std::string getCriteriaChannelName();
17
18 void setInputChannelName(std::string name);
19 std::string getInputChannelName();
20
21 void setCriteriaChannel(std::string channelName);
22 IChannelPtr getCriteriaChannel();
23
24 void setInputChannel(std::string channelName);
25 IChannelPtr getInputChannel();
26
27 void setCriteriaLimit(float threshold);
28 float getCriteriaLimit();
29
30 void setEdgeType(edgeTypes type);
31 edgeTypes getEdgeType();
32
33private:
34 // ...
35
36 /* Declarations of methods and variables we added */
37 std::string inputChannelName = "";
38 std::string criteriaChannelName = "";
39 IChannelPtr inputChannel;
40 IChannelPtr criteriaChannel;
41
42 float criteriaLimit = 0;
43 edgeTypes edgeType = RisingEdge;
44
45 int64_t lastPosChecked;
46};
Definitions of setters and getters should be written in dewesoft_bridge.cpp.
1void DewesoftBridge::setCriteriaChannelName(std::string name)
2{
3 this->criteriaChannelName = name;
4}
5std::string DewesoftBridge::getCriteriaChannelName()
6{
7 return this->criteriaChannelName;
8}
9
10void DewesoftBridge::setInputChannelName(std::string name)
11{
12 this->inputChannelName = name;
13}
14std::string DewesoftBridge::getInputChannelName()
15{
16 return this->inputChannelName;
17}
18
19void DewesoftBridge::setCriteriaChannel(std::string channelName)
20{
21 criteriaChannel = app->Data->FindChannel(channelName.c_str());
22}
23IChannelPtr DewesoftBridge::getCriteriaChannel()
24{
25 return this->criteriaChannel;
26}
27
28void DewesoftBridge::setInputChannel(std::string channelName)
29{
30 this->inputChannel = app->Data->FindChannel(channelName.c_str());
31}
32IChannelPtr DewesoftBridge::getInputChannel()
33{
34 return this->inputChannel;
35}
36
37void DewesoftBridge::setCriteriaLimit(float threshold)
38{
39 this->criteriaLimit = threshold;
40}
41float DewesoftBridge::getCriteriaLimit()
42{
43 return this->criteriaLimit;
44}
45
46void DewesoftBridge::setEdgeType(edgeTypes type)
47{
48 this->edgeType = type;
49}
50edgeTypes DewesoftBridge::getEdgeType()
51{
52 return this->edgeType;
53}
In the rest of this section we mention methods which were modified so our plugin works as it should.
DewesoftBridge::mountChannels()
The void DewesoftBridge::mountChannels()
method is called when your plugin or setup is first loaded. This method gives you the option to create your own output channels inside Dewesoft. In our example, the output channel should be of an asynchronous type because we do not know exactly when a new sample will be added. We will set basic channel properties like channel name, base type (synchronous, asynchronous type) and underlying data type.
1void DewesoftBridge::mountChannels()
2{
3 const std::vector<int> chIndexVector = {1};
4
5 outputChannel = pluginGroup->MountChannelEx(pluginGuid, long(chIndexVector.size()), fromStdVec(chIndexVector));
6
7 outputChannel->SetDataType(long(ChannelDataType::Single));
8 outputChannel->Name = "Latch";
9 outputChannel->Unit_ = "";
10 outputChannel->SetAsync(true);
11 outputChannel->ExpectedAsyncRate = app->Data->GetSampleRateEx();
12 outputChannel->Used = true;
13}
Dewesoft::onStartData()
This method gets called before the start of each measurement. Here we should initialize all variables used during our measurement.
1STDMETHODIMP DewesoftBridge::onStartData()
2{
3 setInputChannel(inputChannelName.c_str());
4 setCriteriaChannel(criteriaChannelName.c_str());
5 lastPosChecked = 0;
6
7 return S_OK;
8}
DewesoftBridge::onGetData()
This method is called repeatedly during Measure mode. Here we can safely read from and write to the channels. We should not perform any time-consuming tasks here. The method itself is marked as STDMETHODIMP, this is required for Dewesoft to call this function over the DCOM interface. Keep in mind that the method gets a block of samples and not an individual sample every time it gets called, so if we want to access every sample in the channel we need to create a loop that will perform this action.
1STDMETHODIMP DewesoftBridge::onGetData()
2{
3 if (!inputChannel || !criteriaChannel)
4 return S_OK;
5
6 // calculate the size of blocks in both channels
7 int blockSizeCriteriaChannel =
8 (criteriaChannel->DBPos - (lastPosChecked % criteriaChannel->DBBufSize) + criteriaChannel->DBBufSize) % criteriaChannel->DBBufSize;
9 int blockSizeInputChannel =
10 (inputChannel->DBPos - (lastPosChecked % inputChannel->DBBufSize) + inputChannel->DBBufSize) % inputChannel->DBBufSize;
11
12 int minBlockSize = min(blockSizeCriteriaChannel, blockSizeInputChannel);
13
14 for (int i = 0; i < minBlockSize - 1; i++)
15 {
16 float currentSampleCriteriaChannel = criteriaChannel->DBValues[lastPosChecked % criteriaChannel->DBBufSize];
17 float nextSampleCriteriaChannel = criteriaChannel->DBValues[(lastPosChecked + 1) % criteriaChannel->DBBufSize];
18
19 // check if the two samples from Criteria channel are on different sides of Latch criteria
20 bool crossedRisingEdgeCriteria = currentSampleCriteriaChannel <= criteriaLimit && nextSampleCriteriaChannel >= criteriaLimit;
21 bool crossedFallingEdgeCriteria = currentSampleCriteriaChannel >= criteriaLimit && nextSampleCriteriaChannel <= criteriaLimit;
22
23 // if user set the type of edge to rising edge and the Criteria channel crossed it
24 // or user set the type of edge to falling edge and the Criteria channel crossed it
25 if ((crossedFallingEdgeCriteria && edgeType == FallingEdge) || (crossedRisingEdgeCriteria && edgeType == RisingEdge))
26 {
27 // add the value of the next sample from Input channel to the Latched channel
28 float value = inputChannel->DBValues[(lastPosChecked + 1) % inputChannel->DBBufSize];
29 outputChannel->AddAsyncSingleSample(value, (lastPosChecked + 1) / app->Data->SampleRateEx);
30 }
31 lastPosChecked++;
32 }
33 return S_OK;
34}
Page 1 of 19
Example I: output result
We are now ready to test our plugin. We can do this by running your plugin by pressing F5 on your keyboard and going to Ch. setup tab, then click the Latch math - Scalar button in the main toolbar in Dewesoft. A window like this should appear.

Remember the setup we created and saved in Dewesoft before? We are going to use it here. We go to the Setup files tab and double-click on the setup we want to load. The setup we loaded should include the two signals we created earlier and a blank setup Latch Math - Scalar setup window.
We assign the signals to the Input and Criteria channel, set the Criteria limit and decide whether we are looking for a latch conditions at rising or falling edge. Our setup should now look like this:

Now we can go to the Measure tab, where we can see a visual representation of the Latch.
Note that to get the exact picture below we turned off the Interpolate asynchronous channels option in the Drawing option of the recorder setup, to better demonstrate that our values are "latched".

In the picture above we can see that every time the sine signal passes 0 at the rising edge threshold, the output channel outputs the current value of the time signal. The outputted value represents the time at which the sine signal passed 0.
Now change the edge type to falling edge and the criteria limit to 0,5. The results will now look like this:

Page 1 of 19
Example II: vector latch math explanation
Our Latch math example can only output scalar sample values. But what if we wanted to latch a vector channel at some condition? We can not just assign this channel as our input because our plugin does not know how to behave in the case of a vector input. This problem can also be solved using C++ Plugin and we will do this by making some changes to our current plugin.
To understand which changes we need, let's first make a slight detour and explain the different types of channels in Dewesoft.
Channel types
We can think of a channel in Dewesoft as a structure holding our signals. Channels can be split in two ways: the first split is based on the value type of the channel, and the second one on its time base.
Based on their value type, channels can be split into:
scalar, vector, and matrix channels; where each of these could be
real, or complex channels.
This means that if you have e.g. a real vector channel all the samples in the channel are vectors (of same dimensions) with real values.
Based on their time base, channels can be split into:
synchronous;
asynchronous; and
single value.
Synchronous channels have equidistant time between consecutive samples. The time difference is defined by the acquisition sample rate and channel's sample rate divider. Asynchronous channels, on the other hand, don't have this restriction: the time between two consecutive samples can be arbitrary. Finally, single value channels don't care about time at all, they only contain one single sample per entire measurement with no timestamp - meaning each new sample in the single value channel simply overwrites the current value.
For a more visual example, the following image shows a sine curve in channels with different time bases.

Signals for testing our module
With these terms explained, let's go back to our problem. Like we created scalar channels at the very start of the training, let's now create some dummy vector channel for testing our modified module. One math module in Dewesoft that has vectors as output is Fourier transform, so let's use that. We add a new Fourier transform setup from the Add math drop-down list. We change the Resolution in Calculation parameters section to 256 lines (meaning the output vectors will have 256 elements) and click Ok.

Page 1 of 19
From example I to example II
Now that we have created a working plugin, we are ready to move to more complex concepts. We will use the project, which was created in Example I and modify it to support vector channel. Example II will be more complex, so we will have to modify things, which were simplified in Example I. We have already mentioned that all plugin logic, which does not need Dewesoft internals, should be defined inside the proTutorial_LatchMath_vectorPluginLib project, although we did not take that into account in Example I. So moving non-Dewesoft logic from the DewesoftBridge to the proTutorial_LatchMath_vectorPluginLib will be the first thing we will do.
DewesoftBridge is used for communicating with Dewesoft internals, that is why it should only contain functions which are meant to do that. This is why we will create a function for checking if we crossed the Criteria limit in the proTutorial_LatchMath_vectorPluginLib. In the protutorial_latchmath_vector.h file we declare the method:
1class proTutorial_LatchMath_vector
2{
3public
4 bool checkCrossedEdgeCriteria(float currentSampleCriteriaChannel, float nextSampleCriteriaChannel, float criteriaLimit, int edgeType);
5};
And in the protutorial_latchmath_vector.cpp file we define it.
1bool proTutorial_LatchMath_vector::checkCrossedEdgeCriteria(float currentSampleCriteriaChannel,
2 float nextSampleCriteriaChannel,
3 float criteriaLimit,
4 int edgeType)
5{
6 bool crossedRisingEdgeCriteria = currentSampleCriteriaChannel <= criteriaLimit && nextSampleCriteriaChannel >= criteriaLimit;
7 bool crossedFallingEdgeCriteria = currentSampleCriteriaChannel >= criteriaLimit && nextSampleCriteriaChannel <= criteriaLimit;
8
9 if ((crossedFallingEdgeCriteria && edgeType == 1) || (crossedRisingEdgeCriteria && edgeType == 0))
10 return true;
11 else
12 return false;
13}
Now, we have to change the method DewesoftBridge::onGetData()
in dewesoft_bridge.cpp file so it will use this new method.
1STDMETHODIMP DewesoftBridge::onGetData()
2{
3 if (!inputChannel || !criteriaChannel)
4 return S_OK;
5
6 int blockSizeCriteriaChannel =
7 (criteriaChannel->DBPos - (lastPosChecked % criteriaChannel->DBBufSize) + criteriaChannel->DBBufSize) % criteriaChannel->DBBufSize;
8 int blockSizeInputChannel =
9 (inputChannel->DBPos - (lastPosChecked % inputChannel->DBBufSize) + inputChannel->DBBufSize) % inputChannel->DBBufSize;
10
11 int minBlockSize = min(blockSizeCriteriaChannel, blockSizeInputChannel);
12
13 for (int i = 0; i < minBlockSize - 1; i++)
14 {
15 float currentSampleCriteriaChannel = criteriaChannel->DBValues[lastPosChecked % criteriaChannel->DBBufSize];
16 float nextSampleCriteriaChannel = criteriaChannel->DBValues[(lastPosChecked + 1) % criteriaChannel->DBBufSize];
17
18 bool crossedEdgeCriteria = protutorial_latchmath_vector.checkCrossedEdgeCriteria(currentSampleCriteriaChannel,
19 nextSampleCriteriaChannel,
20 criteriaLimit,
21 edgeType);
22 if (crossedEdgeCriteria)
23 {
24 float value = inputChannel->DBValues[(lastPosChecked + 1) % inputChannel->DBBufSize];
25 outputChannel->AddAsyncSingleSample(value, (lastPosChecked + 1) / app->Data->SampleRateEx);
26 }
27 lastPosChecked++;
28 }
29 return S_OK;
30}
Because vector channels are always of asynchronous type, we also have to change the getSyncChannels() method to getAsyncChannels() which will return all asynchronous channels. Do not forget to change the name of the method in dewesoft_bridge.h file and in setup_window.cpp where we call this method.
1std::vector<IChannelPtr> DewesoftBridge::getAsyncChannels()
2{
3 std::vector<IChannelPtr> allChannels;
4
5 app->Data->BuildChannelList();
6 IChannelListPtr channels = app->Data->UsedChannels;
7
8 for (int i = 0; i < channels->Count; i++)
9 if (channels->GetItem(i) != outputChannel && channels->GetItem(i)->Async)
10 allChannels.push_back(channels->GetItem(i));
11
12 return allChannels;
13}
Since our Input channel will now be a vector channel, we also have to make changes to addChannelsToCBox()
method. We will change this method so the inputChannelCBox
will only accept vector inputs and the criteriaChannelCBox
will only accept scalar inputs.
1void SetupWindow::addChannelsToCBox()
2{
3 criteriaChannelCBox.clear();
4 inputChannelCBox.clear();
5
6 std::vector<IChannelPtr> allChannels = bridge.getAsyncChannels();
7 for (int i = 0; i < allChannels.size(); i++)
8 {
9 std::string channelName = allChannels[i]->Name;
10 if (allChannels[i]->ArrayChannel)
11 inputChannelCBox.addItem(channelName);
12 else
13 criteriaChannelCBox.addItem(channelName);
14 }
15}
Page 1 of 19
Example II: saving and loading settings
Let's now start modifying our project. The reason for removing the non-Dewesoft logic from dewesoft_bridge.cpp file will be seen soon, but not just yet.
It may be annoying for you to select channels, enter criteria limit and determine rising or falling edge every time you start your plugin in Dewesoft. There is no need to do that anymore because C++ Plugin allows you to store and save your plugin settings in an XML file. In our case, we should save information about Criteria limit, Input channel name and Criteria channel name. To save enum settings we will change it to an integer and save it using WriteInteger
function.
Storing is done with methods that contain Write prefix (e.g. WriteInteger) and reading is done with methods that contain Read prefix (e.g. ReadInteger). We will use class member doc which is automatically set. Parameters for Write and Read methods depend on data type which should be saved, but we can generalize it for every data type.
The first parameter is the pointer to the node which Dewesoft uses. This parameter is the same for every type.
The second parameter is the name of XML element under which your setting is saved. This parameter should be unique for every setting.
The third parameter is the actual value to be stored.
The fourth parameter is the default value to be stored if the actual value is not yet initialized.
We will save plugin settings when setup is saved and load them when a new setup is loaded.
Storing our settings inside dewesoft_bridge.cpp file is done in onSaveSetup as seen in the code below.
1void DewesoftBridge::onSaveSetup(const Setup& setup)
2{
3 auto node = setup.getNode();
4 node->write("criteriaLimit_vector", getCriteriaLimit(), 0);
5 node->write("inputChannelName_vector", getInputChannelName().c_str(), "");
6 node->write("criteriaChannelName_vector", getCriteriaChannelName().c_str(), "");
7 node->write("edgeType_vector", getEdgeType(), 0);
8}
Loading our settings inside dewesoft_bridge.cpp file is done as seen in the code below. We need to set the default values in Read methods because onLoadSetup method is also called when a new plugin is created and XML elements (in node variable) do not even exist yet. They are first added when the plugin is saved. Therefore names for our Input and Criteria channel will be "" until the user chooses a channel name in ComboBox.
1void DewesoftBridge::onLoadSetup(const Setup& setup)
2{
3 const auto node = setup.getNode();
4
5 float criteriaValue = 0;
6 node->read("criteriaLimit_vector", criteriaValue, 0);
7 setCriteriaLimit(criteriaValue);
8
9 node->read("inputChannelName_vector", inputChannelName, "");
10 node->read("criteriaChannelName_vector", criteriaChannelName, "");
11
12 long edgeTypeVectorIndex;
13 node->read("edgeType_vector", edgeTypeVectorIndex, 0);
14 edgeType = edgeTypeVectorIndex == 0 ? RisingEdge : FallingEdge;
15
16 mountChannels();
17}
We will also create a public method which will be used to fill input fields in SetupWindow class.
1//setup_window.h
2void setSavedValues();
1// setup_window.cpp
2
3void SetupWindow::setSavedValues()
4{
5 inputChannelCBox.setSelectedIndex(inputChannelCBox.getIndexOf(bridge.getInputChannelName()));
6 criteriaChannelCBox.setSelectedIndex(criteriaChannelCBox.getIndexOf(bridge.getCriteriaChannelName()));
7 latchCriteriaTBox.setText(std::to_string(bridge.getCriteriaLimit()));
8
9 edgeTypes savedEdgeType = bridge.getEdgeType();
10 if (savedEdgeType == RisingEdge)
11 risingEdgeRButton->setIsChecked(risingEdgeRButton.getIsEnabled());
12 else
13 fallingEdgeRButton->setIsChecked(fallingEdgeRButton.getIsEnabled());
14}
Input fields should also be filled every time when the plugin user interface is entered so the user will not have to set channel names and Criteria limit again.
1void DewesoftBridge::onSetupEnter()
2{
3 setupWindow->addChannelsToCBox();
4 setupWindow->setSavedValues();
5}
When Dewesoft is now opened and saved setup is loaded, saved values will load automatically.
Page 1 of 19
Example II: vector latch math
Now that we know what Example II is about, we are ready to start with the main code: the code for handling vector channels.
In order to create our Output channel to support vectors, we will have to modify its mounting inside Dewesoft. It will accept an array containing values of the type Single. That is why we have to set the channel's dimension (property DimCount
) to 1. If we would need our Output channel to support matrix samples, we would have set DimCount
property to 2. We also set the size of the vector we want to output. In this method, we will set it to 1 and we will change this property later to fit the size of the input vectors.
1void DewesoftBridge::mountChannels()
2{
3 const std::vector<int> chIndexVector = {1};
4 outputChannel = pluginGroup->MountChannelEx(pluginGuid, long(chIndexVector.size()), fromStdVec(chIndexVector));
5
6 outputChannel->SetDataType(long(ChannelDataType::Single));
7 outputChannel->Name = "Latch";
8 outputChannel->Unit_ = "";
9 outputChannel->SetAsync(true);
10 outputChannel->ExpectedAsyncRate = 5;
11 outputChannel->Used = true;
12 outputChannel->ArrayChannel = true;
13
14 outputChannel->ArrayInfo->DimCount = 1;
15 outputChannel->ArrayInfo->DimSizes[0] = 1;
16 outputChannel->ArrayInfo->Init();
17}
There is one thing we need to keep an eye on: ExpectedAsyncRate
property of our output channel. This warrants a slight detour:
Expected async rate per second
If our module contains asynchronous output channels, we have to set their expected rate per second. You can think of this as "approximately how many samples will I be adding to this channel per second". We can change the value of this setting in DewesoftBridge::mountChannels()
by modifying the channel's ExpectedAsyncRate
. This setting is required because we need to help Dewesoft figure out much memory it needs to reserve for our channel. While we can calculate this value in any way we want, it can be useful if we know the rate of our output channel is somehow going to be connected to the rate of some other input channel, in which case we can simply set the outputChannel->ExpectedAsyncRate
to inputChannel->ExpectedAsyncRate
.
Method DewesoftBridge::onEstablishConnections()
is called when the acquisition is started (this occurs when ch. setup is entered). In this method, we will retrieve the Input and Criteria channels by their names. If names are not yet set (this will happen every time when no channel had been set in ComboBoxes containing channel names), we do not set any channels.
1STDMETHODIMP DewesoftBridge::onEstablishConnections()
2{
3 setInputChannel(inputChannelName.c_str());
4 setCriteriaChannel(criteriaChannelName.c_str());
5
6 return S_OK;
7}
As mentioned before, we still need to set the size of the vectors we want to output. This needs to be done before Dewesoft reserves space to run our plugin, so we have to add an event that gets triggered before this happens. This event is called evPreInitiate
and we need to add it to plugin_impl.cpp file, where we also define the method that gets triggered when this event happens. The method needs to be declared in the plugin_impl.h file.
1// plugin_impl.h
2//IPlugin4
3STDMETHODIMP eventBeforeReserveMemory();
4
5// plugin_impl.cpp
6STDMETHODIMP Plugin::raw_OnEvent(enum EventIDs eventID, VARIANT inParam, VARIANT* outParam)
7{
8 // ...
9 switch (eventID)
10 {
11 // ...
12 case evPreInitiate:
13 returnValue = eventBeforeReserveMemory();
14 break;
15 // ...
16 return returnValue;
17}
18
19STDMETHODIMP Plugin::eventBeforeReserveMemory()
20{
21 return bridge.onBeforeReserveMemory();
22}
As we see in the code above the method that gets triggered when evPreInitiate happens triggers a method defined in DewesoftBridge. We will add the declaration and definition of this method to the bridge.
1// dewesoft_bridge.h
2STDMETHODIMP onBeforeReserveMemory();
3
4// dewesoft_bridge.cpp
5STDMETHODIMP DewesoftBridge::onBeforeReserveMemory()
6{
7 if (!inputChannel)
8 return S_OK;
9
10 outputChannel->ArrayInfo->DimCount = 1;
11 outputChannel->ArrayInfo->DimSizes[0] = inputChannel->ArraySize;
12 outputChannel->ArrayInfo->Init();
13 outputChannel->ArrayInfo->AxisDef[0]->Name = inputChannel->ArrayInfo->AxisDef[0]->Name;
14 outputChannel->ArrayInfo->AxisDef[0]->_Unit = inputChannel->ArrayInfo->AxisDef[0]->_Unit;
15
16 outputChannel->ArrayInfo->AxisDef[0]->AxisType = atFloatLinearFunc;
17 outputChannel->ArrayInfo->AxisDef[0]->StartValue = inputChannel->ArrayInfo->AxisDef[0]->StartValue;
18 outputChannel->ArrayInfo->AxisDef[0]->StepValue = inputChannel->ArrayInfo->AxisDef[0]->StepValue;
19
20 return S_OK;
21}
We have now successfully mounted an output channel to Dewesoft, set its axis values, and updated the Output channel to support outputting vector values from the Input channel. All there is left to do is to actually output vector values to the Output channel.
This will be done inside onGetData()
method. For precautionary reasons, we also check if the Input channel or Criteria Channel is nullptr, otherwise, the Input channel could be uninitialized when new setup was created. To insert data to the Output channel we will use the method AddAsyncData(...)
which allows insertion of a vector to the Output channel.
1STDMETHODIMP DewesoftBridge::onGetData()
2{
3 if (!inputChannel || !criteriaChannel)
4 return S_OK;
5
6 if (inputChannel->DBDataSize == 0 || criteriaChannel->DBDataSize == 0)
7 return S_OK;
8
9 int blockSizeCriteriaChannel =
10 (criteriaChannel->DBPos - (lastPosChecked % criteriaChannel->DBBufSize) + criteriaChannel->DBBufSize) % criteriaChannel->DBBufSize;
11
12 for (int i = 0; i < blockSizeCriteriaChannel - 1; i++)
13 {
14 float currentSampleCriteriaChannel = criteriaChannel->DBValues[lastPosChecked % criteriaChannel->DBBufSize];
15 float nextSampleCriteriaChannel = criteriaChannel->DBValues[(lastPosChecked + 1) % criteriaChannel->DBBufSize];
16
17 bool crossedEdgeCriteria = protutorial_latchmath_vector.checkCrossedEdgeCriteria(currentSampleCriteriaChannel,
18 nextSampleCriteriaChannel,
19 criteriaLimit,
20 edgeType);
21 if (crossedEdgeCriteria)
22 {
23 double time = criteriaChannel->DBTimeStamp[(lastPosChecked + 1) % criteriaChannel->DBBufSize];
24
25 int posInputChannel = (inputChannel->DBPos - 1);
26
27 // reading vector from channel
28 std::vector<float> results;
29 for (int j = 0; j < outputChannel->ArraySize; j++)
30 {
31 float value = inputChannel->DBValues[posInputChannel * inputChannel->ArraySize + j];
32 results.push_back(value);
33 }
34
35 if (outputChannel)
36 outputChannel->AddAsyncData(fromStdVec(results), time);
37 }
38 lastPosChecked++;
39 }
40 return S_OK;
41}
Page 1 of 19
Example II: debugger
Because C++ Plugin uses Visual Studio IDE, it supports really efficient debugging. It helps you find semantic errors, see variable values in real-time, set breakpoints, add watches on variables to see values changing, and much more.
If we now run our plugin by pressing the F5 on the keyboard, load the setup we created for testing the plugin and assign the channels as seen in image 29.

Then enter the Measure mode in Dewesoft we quickly get an error message.

We can see that the error is thrown in the OnGetData()
method. We will now set a breakpoint inside of this method, start our plugin with F5 keystroke again and go to Measure tab. When the program execution reaches our breakpoint we will be able to look at every line of code to find out, which one is causing our plugin to crash. To move to the next line use the F10 keystroke (this will step over function calls).
We will try to find the error using Visual Studio Debugger.

After some iterations, we can see that
1posInputChannel
has a value of -1 when the last position of the Input channel is 0. This happens because the data in channels is stored in circular buffers and when we try to access the channel values using the
1inputChannel->
1DBValues[...]
we get an error.

Now we know what is the source of the problem, so we are able to fix it. We change the posInputChannels to first check if the position of the last value received in the Input channels is 0, and if it is, the value we want to output is not -1 but it is the last element of the buffer.
1int posInputChannel = inputChannel->DBPos != 0 ? inputChannel->DBPos - 1 : inputChannel->DBBufSize - 1;
Page 1 of 19
Example II: unit testing
One of the important stages of plugin development is testing. Luckily, C++ Plugin allows you to run automated unit tests, which will shorten your testing time. Unit testing is a software testing method that tests your plugin by running predefined test cases and checking if the result is correct.
Unit testing is one of the main reasons for separating the Dewesoft logic (defined inside DewesoftBridge) and non-Dewesoft logic (defined inside proTutorial_LatchMath_vectorPluginLib).
We can now test each segment without having to start Dewesoft. In order to perform unit tests, you have to set proTutorial_LatchMath_vectorPluginTest as your startUp project (right clicking it and clicking on set as startUp project). We will get rid of sample code so we will only test functions which we need. Every test is written inside protutorial_latchmath_vector_item_test.cpp file.
We will create two simple unit tests, to test our method checkCrossedEdgeCriteria(...)
. If the returned value is the same as the one set, the unit test will output "PASSED".
Here you can see two examples of a unit test (both should successfully pass).
1TEST_F(proTutorial_LatchMath_vectorItemTest, CheckCriteriaLimitRisingEdge)
2{
3 proTutorial_LatchMath_vector protutorial_latchmath_vector;
4
5 float currentSampleCriteriaChannel = 0.49;
6 float nextSampleCriteriaChannel = 0.51;
7 float criteriaLimit = 0.5;
8 int edgeType = 0; // RisingEdge
9
10 bool crossedEdgeCriteria = protutorial_latchmath_vector.checkCrossedEdgeCriteria(currentSampleCriteriaChannel,
11 nextSampleCriteriaChannel,
12 criteriaLimit,
13 edgeType);
14 ASSERT_TRUE(crossedEdgeCriteria);
15}
16
17TEST_F(proTutorial_LatchMath_vectorItemTest, CheckCriteriaLimitFallingEdge)
18{
19 proTutorial_LatchMath_vector protutorial_latchmath_vector;
20
21 float currentSampleCriteriaChannel = -0.12;
22 float nextSampleCriteriaChannel = 0.01;
23 float criteriaLimit = 0;
24 int edgeType = 1; // FallingEdge
25
26 bool crossedEdgeCriteria = protutorial_latchmath_vector.checkCrossedEdgeCriteria(currentSampleCriteriaChannel,
27 nextSampleCriteriaChannel,
28 criteriaLimit,
29 edgeType);
30 ASSERT_FALSE(crossedEdgeCriteria);
31}
To see the result of unit tests, we have to start your project. The result is outputted in the command prompt, which terminates after the testing is completed. In order for the command prompt to stay visible, we should place a breakpoint right before return res;
line of code in main.cpp file. In the picture below you can see an example of setting a breakpoint.

The output in the command prompt should look like that.

Page 1 of 19
Example II: output
If you now start your plugin and set your Input channel to AI 1/AmplFFT and Criteria channel to sine(1), as seen in the image 35.

You will be able to see the outputted vectors on a 3D graph. A new vector is outputted every time the sine(1) signal passes the value 0.5 on the rising edge.

As you can see, whenever sine(1) crosses the Criteria limit, we output the last vector sample from our input channel AI1/AmplFFT to our Output channel.
Page 1 of 19
How to import/export the C++ plugin?
In this Pro Training, we have created a pretty useful plugin, which inserts samples into our Output channel. We might want to use it in other setups or on other computers. C++ Plugin packs your plugin into an external library, which can be inserted into any Dewesoft around the world.
Your C++ Plugin is found inside a file with .dll extension (it contains instructions that Dewesoft can call upon to do certain things, based on the purpose of your plugin). To export it, you need to locate the .dll file first. It can be found inside DEWESoftX\DEWEsoft\Bin\Addons folder.

To import your plugin you have to copy and paste file with .dll extension into any Dewesoft that requires your plugin. You need to paste it inside Addons folder so Dewesoft will be able to automatically recognize and load it.
Page 1 of 19
Comparison with the other ways of extending Dewesoft
At this point, you might have a pretty good grasp on how to use C++ Script. But C++ Script is just one of many ways of extending Dewesoft to suit your needs, and it might be slightly confusing to try and figure out if it actually is the best solution for your task. So in this section, we briefly compare different approaches and list a couple of pros and cons which can hopefully help you pick the right tool.
Extention | Description |
---|---|
Formula | If you want to manipulate channels in a simple way, the Formula module is usually the best one to start experimenting with. Because of its ease of use, it can serve as a great starting point for quick prototyping, and it is usually good enough for most typical problems (signal generation, simple manipulation of data in channels, etc.). |
C++ Script | During its development, we mainly envisioned C++ Script as a tool to create custom math modules that you could export and use just like standard Dewesoft modules. C++ Script is probably a good second step after your approach with Formula modules gets too complicated, too cluttered, or, in the worst case, you cannot figure out how to solve the problem with them. |
Plugins | If you want to develop anything other than math modules, or if you tried creating a module with C++ Script and it proved to not be fast or powerful enough, or if you want to create a completely custom GUI for your module, Plugins are the right way to go. With Dewesoft Plugins you get access to entire Dewesoft from your code, including direct access to buffers behind channels, making Plugins incredibly fast compared to C++ Script. |
Sequencer / DCOM | Sequencer and DCOM are slightly different than the other 3 approaches mentioned in this section. Regardless, they serve a very useful purpose and deserve to be mentioned here: they are used to automate a person clicking on different parts of the Dewesoft UI. The difference among them is that with Sequencer you can create sequences by dragging and dropping graphical blocks (requiring little to no experience with programming) while with DCOM you need to use a programming language. Sequencer is easier to use, but you get much more control with DCOM. |
Extention | PROS | CONS |
---|---|---|
Formula | - The most intuitive of all the approaches, very simple to use.- Integrated fully into Dewesoft meaning no set up required to get running. | - Input channels are fixed in the formula, making reusability a lot of work.- While it supports combining arbitrarily many input channels, it always produces just one output channel.- Poor support for non-scalar channels. |
C++ script | - Dewesoft setups look much nicer as you (usually) only need one C++ Script to solve a problem that would require a bunch of Formula modules- Reusability and generality of your module: you can hide the code from the end-user and only expose the Published setup tab.- It can work with an arbitrary amount of input and output channels. | - Requires familiarity with at least basics of programming in C++.- Difficult to test and debug. |
Plugins | - Much easier to write nice code with proper unit tests.- Full control over the creation of GUI, access to Dewesoft internals, and blazing fast.- It can be used to create custom export formats, custom visual controls, add support for additional acquisition devices, ...- Made to work with Visual Studio, giving you access to a great debugger, code completion, and other static analysis tools. | - Requires Visual Studio.- Much harder to learn to use than C++ Script. |
Sequencer / DCOM | - It can be used to create an automated sequence of events in Dewesoft.- Creator of the sequence can hide the details from the end-user, exposing only a simple user interface to control Dewesoft. | / |
Page 1 of 19