What You’ll Learn 💻
Understand what C++ Script is and how it enables you to write custom math modules within DewesoftX
Install and enable the DSMinGW compiler for C++ Script support in DewesoftX (SP6 and later)
Create your first C++ Script module: configure project metadata, inputs/outputs, and calculation modes
Write and deploy basic algorithms such as “latch math,” and learn how to test with synthetic inputs like sine and time signals
Choose between Sample-based vs. Block-based processing and tune block sizes for performance
Use the code editor, configure GUI in the Published Setup tab, and validate module operation via output channels
Import and export C++ Script bundles for sharing and reuse across setups
Learn when to extend Dewesoft with C++ Script vs. using formula modules or full plugins
Course overview
The Extending DewesoftX with C++ Script training course dives into how to seamlessly integrate custom C++ logic into DewesoftX as math modules. You’ll start with an introduction to C++ Script—Dewesoft’s scripting mechanism that allows writing modern C++ code to create custom data processing blocks without leaving the Dewesoft environment . You’ll be guided through installing and enabling DSMinGW (available in DewesoftX from SP6 onward), preparing your development environment, and ensuring you’re ready to compile and run C++ Script modules .
Next, the course walks you through setting up your first script: creating a “Latch math” example. You’ll configure project details (name, version, description), define input and output channels, and choose the processing mode. Through interactive examples—like latching a value when a signal crosses a threshold—you’ll learn how to code in the built-in editor, publish GUI elements, and monitor runtime outputs.
In later sections, you’ll export your module as a reusable bundle, import it into new setups, and share it with others. The course also helps you decide when to use C++ Script versus simpler formula modules or more advanced C++ plugins—highlighting appropriate use cases and limitations.
By the end, you’ll be capable of designing, building, and deploying your own C++ Script modules—broadening the DewesoftX ecosystem with powerful, customized data processing components tailored to your unique analysis needs.
What is C++ script?
Have you ever wanted to perform calculations on a signal inside Dewesoft but couldn’t find the right module or function? Ever struggled with implementing a non-trivial algorithm, only to end up with a mess of formula modules—just to find out it doesn't work due to the limitations of the formula module?
With the help of C++ Script, you can write your own math module that does exactly what you need and import it into any setup where it's required. You can take nearly any number of input channels, process the data using modern C++ with simple abstractions, and output the results into as many output channels as necessary.
Behind the scenes, C++ Script works by taking your C++ code and compiling it into an external library that Dewesoft can automatically load and communicate with. It continuously feeds data from the input channels into your script, and—based on the logic you implement—outputs the processed data to the output channels, handling the complex integration with Dewesoft on your behalf.
All examples of C++ Scripts created in this tutorial, along with the setups used, are available on Dewesoft’s website under:
Support > Developer Downloads > Example C++ Scripts
(Note: You must be logged in to access the C++ Script section.)
While you're highly encouraged to follow along and create the scripts yourself, you can refer to the provided examples if you get stuck.
How to install the C++ script?
C++ Script is available to Dewesoft users by default starting with Dewesoft X3 SP6 and later versions. Since we want to write our own custom C++ Scripts, you need to ensure that the DSMinGW option is enabled under External Dependencies during the installation of Dewesoft using the online installer. (Offline installers do not include this option, but you can always install DSMinGW manually, as described below.)
DSMinGW is a package of C++ compilers that enables you to write and compile your own C++ Scripts in Dewesoft.
If you didn’t enable DSMinGW during the initial installation of Dewesoft, you can still install it afterward. Go to Apps & Features (or Add or Remove Programs in older Windows versions), find and click on Dewesoft, then click the Modify button. This will allow you to modify your installation and include DSMinGW.
Alternatively, you can install DSMinGW manually by downloading the installer from the Dewesoft website under: Support > Downloads > Developers > C++ Script
Older versions of Dewesoft (before X3 SP6)
To write custom C++ Scripts in Dewesoft versions prior to X3 SP6, you need to manually install DSMinGW on your system. You can download the package from the Dewesoft website under:
Support > Downloads > Developers > C++ Script
Starting from Dewesoft X3 SP4, C++ Script is available as an experimental feature, which must be manually enabled. To do this: Navigate to Options in Dewesoft and click on Settings. Select the Advanced tab, then go to the Experimental section. Under Features, find the checkbox labeled C++ Script (Math). Check the box to enable it, click OK, and then restart Dewesoft.
C++ example: latch math
To better understand both the use cases and the simplicity of C++ Script, we will implement a Latch Math module. This example is intended to demonstrate several different capabilities of C++ Script in a (hopefully) easily understandable way.
Latch Math outputs the value of one input channel when another input channel crosses a predefined threshold. A typical use case for this module could be monitoring your car's engine RPM as you pass 100 km/h. For example, we would set the first input channel to engine RPM, the second input channel to the vehicle's current speed, and define the latch criterion at 100 km/h.
Given this setup, by the end of the example, we should have a working module that allows you to: Select two input channels: one for detecting the latch condition and one for reading output values. Generate an output channel that contains the latched values.
Signals for testing our module
Before we begin writing our C++ Script, let’s first create the two input signals that we’ll use for testing.
This signal will be used to detect when the threshold is crossed. For testing purposes, we’ll use a 1 Hz sine wave. Click the Formula button next to the Add math button. In the Signal tab, choose the sine function and name the signal "sine(1)".
This signal provides the values to be latched when the condition is met. For this, we’ll use the current time. Click the Formula button again. From the Signal tab, choose the time function and name it "time".
Sample rate
Another thing we should take care of before we begin development is to lower the Dynamic acquisition rate.
We do this by clicking the Analog in button and in the drop-down list set Dynamic acquisition rate to e.g. 500 Hz.
C++ example: latch math - create C++ script
To create a new C++ Script, click the Math button and then the Add Math button that appears beneath it. In the menu that opens, locate the section called Formula and Scripting and click on C++ Script.
A new C++ Script setup window will appear, as shown in Image 8. This setup window includes four tabs: Project, Configure, Code Editor, and Published Setup. Our example will be divided into steps corresponding to these tabs.
Published setup
But before we get started, let’s click on the Published Setup tab. Here, we can see that a user interface—similar to other Math modules in Dewesoft—was automatically generated. It includes a section for input channels, an empty space for output channels below that, and a section for defining and modifying module settings. Since our module is currently empty, the form is also empty.
Project
In the Project tab, we will specify the module name, a brief description of its functionality, and the module version. Start by filling in the Project Name field with Latch Math, the Description field with Module performing latch math, and leave the Version fields as they are. Also fill in your details in the Developer First and Last Name, Email, and Company fields.
Once that’s complete, click the Configure tab at the top of the setup window.
Configure
After clicking on the Configure tab, the first thing we can adjust is the general behavior of our module. We can choose the calculation method and define the block size for our calculations. C++ Script can operate in two modes: Sample-based, where each new sample in the input channels triggers a calculation step, or Block-based, where Dewesoft waits for a specified block size of new samples before executing the module.
Let’s assume we want users of our module to configure two settings:
Whether to latch the output on a rising or falling edge (i.e., when the car is accelerating or decelerating),A criteria limit — the value the criteria channel must exceed before the output is generated (e.g., the target speed).
The Configure tab is the right place to define such user settings.
Published variables
Inside the Configure tab, under Published Variables, we can define various types of variables. The key idea is that these variables are exposed to the Published Setup tab, and modifying their values does not require recompiling the code.
Click the Add Variable button to bring up the variable setup form.
To begin, let’s create a variable to determine whether the latch condition should respond to a rising or falling edge on the signal: In the Variable Name section: Set the C++ Variable Name to "edgeType" — this is how it will appear in the code. Set the Published Name to "Type of edge"— this is how it will appear in the user interface.
Next, in the Variable Type section, choose Enumeration from the drop-down list. A new section will appear where you can define the selectable values.
Add two values: "risingEdge" and "fallingEdge". To add each value, click the Add Value button.
You can also define a Default Value from the drop-down list and provide a Published Description.
When finished, click the Apply button to save the variable.
Now, let’s create another variable to allow users to set the criteria limit value. Set the type to Float. A Numerical Settings section will appear, where you can define the minimum and maximum values the user may enter in the Published Setup tab.
The two variables we just created are now visible in the Published Setup tab of the C++ Script setup window. C++ Script automatically generates a drop-down menu for selecting the edge type, and a numeric input field for entering the latch criteria. This setup provides a simple, user-friendly interface for configuring your custom module parameters.
C++ example: latch math - input and output channels
Input channels
Our latch math module will require two input channels: one for detecting the trigger points at the latch condition and one from which the output values will be read. Let's go ahead and add them.
Under the Input Channels tab, click the Add button. This opens the Variable Setup form for configuring your input channel. Set the C++ variable name to "criteriaChannelIn" and Set the Published name to "Criteria channel" Leave the Value type as Scalar and Real and check the Synchronous checkbox under Time base. Don’t worry if you’re not familiar with all these settings yet — we’ll explain the different channel types later in this training.
After clicking Apply, you should see a new row in the table showing "criteriaChannelIn" under the C++ variable name column and "(I) Sync Real Scalar" under Channel type.
Now, let’s create the second input channel: Set the C++ variable name to "inputChannelIn".
Set the Published name to "Input channel".
Keep the Value type as Scalar and Real, and again check the Synchronous box under Time base.
Click Apply again. The table should now display two rows, one for "criteriaChannelIn" and one of "inputChannelIn" both marked as "(I) Sync Real Scalar" under Channel type.
This indicates your module will use two synchronous input channels. We’ll explore the meaning of “synchronous” in an upcoming section.
Output channels
The output of our latch math module will be a channel that holds the appropriate value of the second input signal whenever the first input signal crosses the latch threshold. Let’s define that now.
Navigate to the Output Channels tab. You’ll see a debug channel listed — ignore it for now. Click the Add button to create a new output channel. A setup form will appear with options similar to the input channel configuration. Set the C++ variable name to "latchedChannelOut",
set the Published name to "Latch",
leave the Value type as Scalar and Real. Change the Time base to Asynchronous. Čeave the Expected async rate per second at 10, again, don’t worry about the exact meaning of the expected async rate — we’ll cover that later in this training.
Click Apply. A new row will appear in the table containing the channel you just created.
Switch to the Published Setup tab, and you will now see all the variables defined in the Configure tab — including the two unassigned input channels and the published variables with their default values.
This confirms that the configuration of inputs, outputs, and settings for the latch math module has been successfully completed.
C++ example: latch math - code editor tab
Now it's time to implement the logic of our latch module. We do this in the Code Editor tab.
One way our module can operate is by examining every two consecutive samples in the criteria channel. As long as both samples are either below or above the criteria limit, we do nothing. But when the first sample is below the limit and the second sample is above it (in the case of a rising edge — swap the conditions for a falling edge), it means the criteria threshold has been crossed. At that point, we output the value from the input channel to the output channel.
Let’s implement this logic.
Code
At the very top of the template in a new C++ Script module, you’ll see the following line:
1namespace bsc = Dewesoft::Math::Api::Basic;
All of C++ Script’s types and many of its structures reside in the Dewesoft::Math::Api::Basic
namespace. The line above allows us to reference these types using the alias bsc
, making the code cleaner and easier to read.
The next part of the template defines a class called Module
. This is the class where we’ll implement the core logic of our module.
C++ Script interacts with Dewesoft by allowing it to call specific methods of the Module
class at predefined events during execution. These methods are pre-populated in the C++ Script code editor template, and comments next to them describe when Dewesoft will invoke each one.
For our example, we only need to modify the Module::start()
and Module::calculate()
methods. Since Module::calculate()
will be called every time a new sample is received from the input channels, we’ll need a global variable to store the previous sample of the criteria channel. This will allow us to determine whether we’ve crossed the criteria threshold (rising or falling edge). We define this variable inside the Module class so its value persists between calls to Module::calculate()
:
1class Module: public bsc::MathModule
2{
3 public:
4 Module();
5 ~Module();
6 void configure() override;
7 void start() override;
8 void calculate() override;
9 void clear() override;
10 void stop() override;
11
12 bsc::Scalar prevCriteriaChannelValue = 0;
13};
void Module::Start()
Module::start()
function is called at the beginning of the measurement. It’s used for lightweight setup tasks such as initializing variables. Here, we will initialize the global variable mentioned above.
1inline void Module::start()
2{
3 prevCriteriaChannelValue = 0;
4}
void Module::calculate()
Module::calculate()
function is called repeatedly during measurement mode. Recall that in the Configure tab, we set the calculation call type to “Sample based.” This tells Dewesoft to call our Module::calculate()
method every time a new sample is available on the input channels.
The logic inside Module::calculate()
might look like this (example pseudocode):
1inline void Module::calculate()
2{
3 // read new sample from Criteria channel
4 bsc::Scalar currCriteriaChannelValue = criteriaChannelIn.getScalar(0);
5
6 // check if the two samples from Criteria channel are on different sides of Latch criteria
7 bool crossedRisingEdgeCriteria = prevCriteriaChannelValue <= published.latchCriteria
8 && currCriteriaChannelValue >= published.latchCriteria;
9
10 bool crossedFallingEdgeCriteria = prevCriteriaChannelValue >= published.latchCriteria
11 && currCriteriaChannelValue <= published.latchCriteria;
12
13 // if user set the type of edge to rising edge and the Criteria channel crossed it
14 // or user set the type of edge to falling edge and the Criteria channel crossed it
15 if ((published.edgeType == risingEdge && crossedRisingEdgeCriteria)
16 || (published.edgeType == fallingEdge && crossedFallingEdgeCriteria))
17 {
18 // add the value of the current sample from Input channel to the Latched channel
19 bsc::Scalar currInputChannelValue = inputChannelIn.getScalar(0);
20 latchedChannelOut.addScalar(currInputChannelValue, callInfo.endBlockTime);
21 }
22
23 // in any case remember the value of current sample from the Input channel for the next calculation call
24 prevCriteriaChannelValue = currCriteriaChannelValue;
25}
From the code above notice a couple of things:
to access the channels from our code we use the same names as we defined them under C++ variable name in Configure tab;
we can access and add the samples in the channels by invoking
.getScalar()
and.addScalar()
methods;we can access the values of published variables from Published setup tab through a special
published
struct by usingpublished.X
; where X is again the name as defined under C++ variable name in Configure tab; andwe used a
callInfo
structure which contains four read-only variables that get updated before each call toModule::calculate()
with the latest values. In our case, we use thecallInfo.endBlockTime
which tells us the time in seconds of the last new sample in input channels -- which in our case, since we only receive one new sample per calculate call, is always the time that this sample arrived at.
You can find a Compiler Output panel at the bottom of the Code Editor tab. This panel displays whether your code compiled successfully. If there are any compilation errors or warnings, they will be shown here with helpful messages for debugging.
C++ example: latch math - published setup tab
We can now assign the signals we created earlier to the correct input channels. In the Input section of the window, you will see a table with our two input channels.
To assign a signal to the Criteria channel, click on the white cell under column 1, then click the [...] button. From the drop-down list, select the "sine(1)" channel. Repeat the same process for the Input channel, but this time select the "time" channel from the drop-down list.
Click the OK button at the bottom of the setup form, and don’t forget to save your setup in Dewesoft.
In Image 20, you can see how the setup should look inside the Math section.
Finally, switch to Measure mode by clicking on the Measure tab and observe your output channel displaying the values at the latch condition.
Example: latch math - output
From the image below, we can observe that each time the sine signal crosses zero at a rising edge, the output channel displays the corresponding value of the time signal. This output value represents the moment at which the sine signal crosses zero.
To produce the exact visual result shown, we disabled the Interpolate asynchronous channels option in the Drawing options of the recorder setup. This helps clearly demonstrate that the values are "latched."
Next, we can modify the variables in the Published setup tab of the C++ Script to detect the latch criteria on the falling edge at a value of 0.5.
The results will now appear as follows:
C++ example: latch math - code simplification
The code presented in the previous section works, but one part stands out: the way we access the previous sample from the criteria channel to check if our signal has crossed the latch criteria. Currently, we do this by saving the current sample to a temporary variable called prevCriteriaChannelValue
at the end of the Module::calculate()
method.
However, since accessing multiple samples from input channels is generally a useful feature, C++ Script offers a simpler approach. Instead of manually creating a variable, we can simply set the predefined module variable pastSamplesRequiredForCalculation
in the Module::configure()
method to a value greater than 0—in our case, 1, since we need just one past sample.
Module::configure()
is a special method where you can override any settings from the Configure tab and define additional properties. This is useful if you want the behavior to adapt based on values provided by the user in the Published setup tab. If no changes are made in Module::configure()
,the default settings from the Configure tab are used.
By setting pastSamplesRequiredForCalculation
to a value such as 1, our input channels will include one additional past sample each time Module::calculate()
is called. We can then access this sample using .getScalar(-i)
where i
is the index of the sample relative to the current one.
This means that the code in our Module::configure()
method should look like this:
1inline void Module::configure()
2{
3 pastSamplesRequiredForCalculation = 1;
4}
The logic in Module::calculate()
can then directly access the previous sample without needing a custom variable.
1inline void Module::calculate()
2{
3 bsc::Scalar prevCriteriaChannelValue = criteriaChannelIn.getScalar(-1);
4 bsc::Scalar currCriteriaChannelValue = criteriaChannelIn.getScalar(0);
5
6 bool crossedRisingEdgeCriteria = prevCriteriaChannelValue <= published.latchCriteria
7 && currCriteriaChannelValue >= published.latchCriteria;
8
9 bool crossedFallingEdgeCriteria = prevCriteriaChannelValue >= published.latchCriteria
10 && currCriteriaChannelValue <= published.latchCriteria;
11
12 if ((published.edgeType == risingEdge && crossedRisingEdgeCriteria)
13 || (published.edgeType == fallingEdge && crossedFallingEdgeCriteria))
14 {
15 bsc::Scalar currInputChannelValue = inputChannelIn.getScalar(0);
16 latchedChannelOut.addScalar(currInputChannelValue, callInfo.endBlockTime);
17 }
18}
Consequently, we can remove the global variable prevCriteriaChannelValue
from both the Module::start()
from both the Module class and the Module: :start() method.
Another way we could approach this
Previously, we mentioned the block-based calculation call. You might be tempted to use it here by:Changing the calculation call in the Configure tab to Block based,
Setting the Block size to 2,
And comparing each pair of samples within Module::calculate()
.
You might rewrite your code in the Code editor tab accordingly.
1inline void Module::calculate()
2{
3 bsc::Scalar prevCriteriaChannelValue = criteriaChannelIn.getScalar(0);
4 bsc::Scalar currCriteriaChannelValue = criteriaChannelIn.getScalar(1);
5
6 bool crossedRisingEdgeCriteria = prevCriteriaChannelValue <= published.latchCriteria
7 && currCriteriaChannelValue >= published.latchCriteria;
8
9 bool crossedFallingEdgeCriteria = prevCriteriaChannelValue >= published.latchCriteria
10 && currCriteriaChannelValue <= published.latchCriteria;
11
12 if ((published.edgeType == risingEdge && crossedRisingEdgeCriteria)
13 || (published.edgeType == fallingEdge && crossedFallingEdgeCriteria))
14 {
15 bsc::Scalar currInputChannelValue = inputChannelIn.getScalar(1);
16 latchedChannelOut.addScalar(currInputChannelValue, callInfo.endBlockTime);
17 }
18}
This might seem like a good solution to achieve the same result, but if we think about it, it is not going to work correctly. We said that Dewesoft calls Module::calculate()
only when there are exactly two new samples in the input channel. The problem is that our logic compares only these two new samples. If the latch criteria is crossed between the last sample of the previous block and the first sample of the current block, we won't detect it. These two samples never get compared, so we miss the latch event entirely.
C++ example II: vector latch math
Our Latch math module currently only accepts scalar channels as input and outputs scalar samples.
But what if we wanted to latch a vector channel based on a specific condition? We cannot simply assign a vector channel as our input because the module won't accept it. Fortunately, quick prototyping is one of C++ Script’s strengths, and we don’t have to write the entire module from scratch—we only need to make some minor adjustments to the existing one.
To understand what changes are necessary, let’s first take a brief detour to explain the different types of channels in Dewesoft.
Channel types
We can think of a channel in Dewesoft as a structure that holds signal data. Channels can be classified in two ways: based on their value type, based on their 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.
For example, if you have a real vector channel, each sample in the channel will be a vector (of consistent dimensions) containing real-number values. The table below (not shown here) outlines the methods used to read from and write to different types of channels.
Channel type | Reading | Writing | |
---|---|---|---|
Real | Scalar | .getScalar() | .addScalar() |
Real | Vector | .getVector() | .addVector() |
Real | Matrix | .getMatrix() | .addMatrix() |
Complex | Scalar | .getComplexScalar() | .addComplexScalar() |
Complex | Vector | .getComplexVector() | .addComplexVector() |
Complex | Matrix | .getComplexMatrix() | .addComplexMatrix() |
Based on their time base, channels can be split into:
synchronous;
asynchronous; and
single value.
Synchronous channels have equidistant time between samples. The interval is defined by the acquisition sample rate and the channel’s sample rate divider. Asynchronous channels have variable intervals between samples—time between any two samples can vary. Single value channels do not track time. They contain only one sample per measurement, which gets overwritten with each new sample. These channels are not timestamped.
For a more visual illustration, consider a sine wave plotted across channels with different time bases—the behavior and sampling differ significantly depending on the type.
Signals for testing our module
Just like we previously created scalar channels for our initial Latch math module, we can now create a dummy vector channel to test our modified version. One commonly used math module in Dewesoft that outputs vectors is the Fourier Transform. So let’s use that. Add a new Fourier Transform setup from the Add math drop-down menu. In the Calculation parameters section, change the Resolution to 256 lines. This means the output vectors will contain 256 elements—enough for testing, but not so many as to overwhelm development. Click OK to finalize.
Example II: latch math - changes in configure tab
In the Configure tab, we leave the Published variables unchanged and make modifications only in the Input channels and Output channels tabs.
In the Input channels tab, we change the Time base of the Criteria channel to Asynchronous. This adjustment is necessary because the first input channel determines the sampling rate for the entire module. If our first input channel were synchronous and the second one a vector, we would end up with synchronous vector channels, which are not supported by Dewesoft.
To make this change, click the Setup button for the Criteria channel to open its configuration form. Then, set the Time base to Asynchronous and Single Value, and change the Value type to Vector.
In the Output channels tab, we change the Value type of the Latch channel to Vector and leave the vector size at its default value. We’ll override this setting in the code anyway, as it depends on the size of the input vector from the Input channel.
Note that this setup also includes an additional field: Expected Async Rate. Don’t worry about this for now—we’ll explain it in the next section.
C++ example II: latch math - changes in code editor tab
Starting from the correctly "simplified" version of our code, we need to make just a few changes in the Code editor tab. These modifications will take place in the Module::configure()
and Module::calculate()
methods.
Updated void Module::configure()
We’ll begin by updating the Module::configure()
function. Since our output channel will now be a vector channel, we must configure the latchedChannelOut channel’s axis to match the vectors we want to output. In this case, we are simply copying values from the input channel to the output channel, so the vector will have the same size as the input vector. This means we can directly copy the dimensions. Similarly, the name and unit of the output vector will match those of the input channel, and we can copy those values as well.
1inline void Module::configure()
2{
3 latchedChannelOut.expectedAsyncRate = inputChannelIn.expectedAsyncRate;
4 latchedChannelOut.axes[0].values = inputChannelIn.axes[0].values;
5 latchedChannelOut.axes[0].name = inputChannelIn.axes[0].name;
6 latchedChannelOut.axes[0].unit = inputChannelIn.axes[0].unit;
7
8 pastSamplesRequiredForCalculation = 1;
9}
Because our output channel is asynchronous, we must also configure one more setting: expectedAsyncRate
.
Expected async rate per second
If a module includes asynchronous output channels, it is mandatory to define their expected rate per second. You can think of this as: "How many samples at most will I be adding to this channel per second?" We set this value in Module::configure()
by modifying the channel’sexpectedAsyncRate
. Tproperty. This helps Dewesoft allocate the necessary memory for the channel. While this rate can be calculated independently, it’s often convenient to use the rate of an existing input channel—especially if the output rate is related. In our case, we simply set outputChannel.expectedAsyncRate
equal to inputChannel.expectedAsyncRate
, as shown in the code.
Updated void Module::calculate()
In the Module::calculate()
function we have to updatethe getScalar()
and addScalar()
calls to getVector()
and addVector()
respectively, and also change the variable type from bsc::Scalar
type of currInputChannelValue
to bsc::Vector
. You can easily make these replacements by using the Replace function, substituting "Scalar" with "Vector" where needed. Use the Find Next and Replace buttons to apply the changes efficiently.
The updated code in the Module::calculate()
function should now look like this:
1inline void Module::calculate()
2{
3 bsc::Scalar prevCriteriaChannelValue = criteriaChannelIn.getScalar(-1);
4 bsc::Scalar currCriteriaChannelValue = criteriaChannelIn.getScalar(0);
5
6 bool crossedRisingEdgeCriteria = prevCriteriaChannelValue <= published.latchCriteria
7 && currCriteriaChannelValue >= published.latchCriteria;
8
9 bool crossedFallingEdgeCriteria = prevCriteriaChannelValue >= published.latchCriteria
10 && currCriteriaChannelValue <= published.latchCriteria;
11
12 if ((published.edgeType == risingEdge && crossedRisingEdgeCriteria)
13 || (published.edgeType == fallingEdge && crossedFallingEdgeCriteria))
14 {
15 bsc::Vector currInputChannelValue = inputChannelIn.getVector(0);
16 latchedChannelOut.addVector(currInputChannelValue, callInfo.endBlockTime);
17 }
18}
These small adjustments are all that’s needed to enable our module to accept vector channels as input and output a vector whenever the latch criteria is met.
C++ example II: latch math - changes in published setup tab
Before we proceed, it’s important to note that we still need one more component to test our new C++ Script: an asynchronous criteria channel. To create one, we can simply convert a synchronous channel used in the previous example into an asynchronous one.
The easiest way to do this in Dewesoft is by using the Basic Statistics module. First, close the C++ Script setup by clicking the OK button at the bottom of the page. Then add a Basic Statistics module to the setup by clicking the Add math button and selecting Basic statistics from the Statistics section.
In the input window, check the box next to the sine(1) signal, and in the output channels, check the RMS box. Set the Calculation type to Sample based and Block based with a Block size of 1 sample. Click OK to add the statistic to the setup.
Your setup should now look like this:
Now return to the Published setup tab in the C++ Script setup to reassign the input channels. Assign the "sine(1)/RMS signal to the Criteria channel, and assign the "AI 1/AmplFFT" signal to the Input channel, then click OK.
You can now switch to Measurement mode in Dewesoft to observe the results.
C++ example II: latch math - output
In Image 33, we see the output vectors displayed on a 3D graph. A new vector is output every time the sine(1)/RMS signal crosses the value 0.5. We can also view the output vector in a 2D/3D table to inspect its individual values.
If we now decide that the latch condition should occur on the falling edge instead, we can simply change the corresponding variables in the Published setup tab of the C++ Script setup.
The output will then appear as follows:
C++ example II: latch math - debug channel
To aid in the development of your C++ Script, a special debug output channel (of type asynchronous string) is enabled by default whenever you create a new script. With this debug channel, you can use the function: void outputDebugString(const std::string& message, bsc::Time timestamp)
to output custom string messages from within the Module::calculate()
method. Additionally, the debug channel will automatically capture and display any exceptions thrown from inside your Module::calculate()
function.
To view these messages during measurement mode, add a Digital meter visual control to your display and bind the debug channel to it. Note that because the debug channel is asynchronous, you must properly specify the expected async rate per second—either in the channel setup or programmatically by setting: debug.expectedAsyncRate
to an appropriate value within your Module::configure()
method.
An example of a message output by the debug channel is shown below. It demonstrates the kind of warning that appears if you forget to set the
1pastSamplesRequiredForCalculation
value in the
1Module::configure()
method but still attempt to access the previous sample of the criteria channel using the
1criteriaChannel.getScalar(-1)
call.
Once you are finished developing your module, you can easily remove the debug channel by unchecking the corresponding option in the Extra tab of the Configure section.
How to import/export C++ bundle?
In this Pro Training, we created a highly useful and general-purpose Math module. One thing we might want to do next is use our Latch module in other setups—possibly on other computers. The best way to achieve this is by using C++ Script's bundle functionality, which allows you to package all settings along with the precompiled module. This means you won’t need to recompile the module if you just want to use it on a different machine.
To export the script, go to the Project tab of the setup and click the Export bundle... button. A dialog will appear asking you how you want to bundle the module:
Open source: bundle is exported with the source code included. When such a bundle is imported via the Import bundle... button, the user will have access to all 4 tabs in C++ Script.
Freeware: bundle is exported without the source code. When such a bundle is imported via the Import bundle... button, the user will only have access to the Published tab. Be careful, as there is no way to recover the source code from a Freeware bundle.
Select your preferred bundling option and click OK. A new window will then prompt you to choose a destination on your computer to save the bundle. After selecting the destination, click Save. Your module will be recompiled to support all available architectures and saved to the specified location with a ".cbu" file extension.
To import the bundle on another computer or into a different setup, create a new C++ Script and click the Import bundle... button in the Project tab. Locate the "*.cbu" file you want to load and click Open. The bundle will then be imported into your current setup.
Stand-alone math
Exported ".cbu" bundles can also be added to Dewesoft’s bin\addons\
folder, just like regular plugins. Dewesoft will recognize these bundles and display them in the Add math menu, alongside built-in Dewesoft math modules. Because bundles contain all the required precompiled binaries, the end-user does not need DSMinGW installed to use the module.
In Image 40, we have exported our Latch Math bundle into
1bin\addons\
the designated folder. After restarting Dewesoft, the module appears under the Custom C++ Scripts group, using the name specified in the Project name field and the description from the first line of the Description field in the Project tab. We can now use the Latch Math module in any setup, with only the Published tab visible—keeping other tabs hidden from the end-user.
In which ways is it possible to extend Dewesoft?
At this point, you likely have a solid understanding of how to use C++ Script. However, C++ Script is just one of many ways to extend Dewesoft to fit your specific needs. It can be somewhat confusing to determine whether it is truly the best solution for your particular task.
In this section, we’ll briefly compare different approaches and outline a few pros and cons of each. This should help you choose the most appropriate tool for the job.
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. | / |
Where to go from here?
A great way to learn anything new is by studying examples. For this very reason, we’ve created several C++ Script bundles for you, which are available on Dewesoft’s website under Support > Downloads > Developers > C++ Script. Note that you must be logged in to access the C++ Script section. These bundles demonstrate the versatility of C++ Script and may give you a clearer understanding of how and when to use it for your own tasks.
For a more in-depth explanation of C++ Script, you can also refer to the C++ Script Manual, available on the same page as the example bundles. It includes another step-by-step tutorial for creating a useful math module, along with detailed descriptions of all available structures and features.
The Support > Developers section of the Dewesoft website is also a valuable resource for getting help and additional clarification—whether from Dewesoft staff or the broader user community.
Page 1 of 19