Learn how to use Real-Time Tasks (RTTasks)
PREMIUM FEATURE BETA
- Recommended
- This guide will help you integrate RTTasks into your application. If you're new to RTTasks or want to better understand how they work, refer to the following resources:
- Read the Real-Time Tasks concept page for an overview
- Explore the RTTasks examples in the C++ Sample Apps, beginning with
HelloRTTasks
- Review the
RTTaskFunctions
folder included with the C++ examples
🔹 Quickstart
To get up and running quickly, follow these steps:
- Copy the
RTTaskFunctionsTemplate
(found in the examples folder) into your project
- Add any required global variables to
src/rttaskglobals.h
- Define your task functions in
src/rttaskfunctions.cpp
, using Increment
as a template
- Build the library using CMake
- In your application, create a RTTaskManager and use TaskSubmit to launch your tasks
For a more detailed walkthrough, continue below.
🔹 Set up your project
The examples
folder in your RMP installation includes a CMake project called RTTaskFunctionsTemplate
. This serves as a starting point for building a shared library containing RTTask functions. Copy this folder into your project directory and rename it appropriately.
Here is an overview of the key files:
Directory Structure
RTTaskFunctionsTemplate/
├── src/
│ ├── rttaskglobals.h # Defines Global variables
│ └── rttaskfunctions.cpp # Task function implementations
│
├── CMakeLists.txt # CMake setup for building the shared library
└── cmake/ # CMake helper modules
├── RapidSoftware.cmake # Paths for RapidCode/RMP firmware
└── INtime/ # INtime helper scripts
How it works
The template project takes all the source files (.h, .cpp) in the src/
directory and compiles them into a shared library for platform (Windows/INtime or Linux). It automatically includes the RMP headers, links the RMP libraries, and applies necessary configuration for use with RTTasks.
By default, the resulting library is named RTTaskFunctions
and is output to the default RMP install directory (e.g., /rsi
or C:/RSI/X.X.X
). If these paths or names are modified, you must specify them explicitly when submitting a task. Otherwise, they will be discovered automatically.
If you want to change any of the default behavior or are interested in learning more about how it works, then look at the CMakeLists.txt
located in the root of the project folder.
🔹 Create an RTTask functions library
In this guide, you’ll build a simple application that moves an axis based on the value of an analog input. It will use one global variable and two RTTasks. The first task, CalculateTarget
, reads the analog input and calculates a target position, storing it in the global variable targetPosition
. The second task, FollowTarget
, moves the axis to the specified target.
Create a global variable
Open src/rttaskglobals.h
. This file is where the global variables are defined and registered.
To add a new global of type double
called targetPosition
:
- Add
RSI_GLOBAL(double, targetPosition)
to the GlobalData
struct
- Add
REGISTER_GLOBAL(targetPosition)
to the GlobalMetaDataMap
The result should be:
struct GlobalData
{
GlobalData() { std::memset(this, 0, sizeof(*this)); }
GlobalData(GlobalData&& other) { std::memcpy(this, &other, sizeof(*this)); }
RSI_GLOBAL(int64_t, counter);
RSI_GLOBAL(double, average);
RSI_GLOBAL(double, targetPosition);
};
inline constexpr GlobalMetadataMap<RSI::RapidCode::RealTimeTasks::GlobalMaxSize> GlobalMetadata(
{
REGISTER_GLOBAL(counter),
REGISTER_GLOBAL(average),
REGISTER_GLOBAL(targetPosition),
});
Create a task function
Open src/rttaskfunctions.cpp
. This is the file where the task functions are defined. A task function must follow this template:
LIBRARY_EXPORT void FunctionName(GlobalData* data)
{
...
}
We will add a new task function called CalculateTarget
that will read the analog input, scale it to a value between 0 and 1, and store it in the global created in the previous step.
RSI_TASK(CalculateTarget)
{
constexpr int NODE_INDEX = 0;
constexpr int ANALOG_INDEX = 0;
constexpr int ANALOG_MAX = 65536;
constexpr int ANALOG_ORIGIN = 42800;
auto networkNode = RTNetworkNodeGet(NODE_INDEX);
int32_t analogInVal = networkNode->AnalogInGet(ANALOG_INDEX);
int32_t shiftedVal = analogInVal - ANALOG_ORIGIN;
int32_t modVal = (shiftedVal + ANALOG_MAX) % ANALOG_MAX;
data->targetPosition = double(modVal) / ANALOG_MAX;
}
Then we will add another task function called FollowTarget
that will move the axis towards the target position.
RSI_TASK(FollowTarget)
{
constexpr int AXIS_INDEX = 1;
constexpr double TOLERANCE = 0.02;
auto axis = RTAxisGet(AXIS_INDEX);
if (abs(axis->ActualPositionGet() - data->targetPosition) > TOLERANCE)
{
axis->MoveSCurve(data->targetPosition);
}
}
Once the task functions have been added, build the project to produce a new library.
🔹 Launch your tasks
In your application, after configuring your RapidCode objects, create an RTTaskManager instance and submit your tasks.
std::snprintf(parameters.RTTaskDirectory,
RTTaskManagerCreationParameters::DirectoryLengthMaximum,
RMP_DEFAULT_PATH);
#if defined(WIN32) && defined(NDEBUG)
parameters.Platform = PlatformType::INtime;
#endif
std::snprintf(parameters.NodeName,
RTTaskManagerCreationParameters::NameLengthMaximum,
"NodeA");
parameters.CpuCore = 3;
std::shared_ptr<RTTaskManager> manager(RTTaskManager::Create(parameters));
Next, run the Initialize
task one time, to get access to RapidCode objects and initialize your global variables.
initParams.Repeats = RTTaskCreationParameters::RepeatNone;
std::shared_ptr<RTTask> initTask(manager->TaskSubmit(initParams));
initTask->ExecutionCountAbsoluteWait(1, TIMEOUT_MS);
Then submit the tasks created in the previous section.
calcParams.Repeats = RTTaskCreationParameters::RepeatForever;
calcParams.Period = 10;
std::shared_ptr<RTTask> calcTask(manager->TaskSubmit(calcParams));
calcTask->ExecutionCountAbsoluteWait(1, TIMEOUT_MS);
followParams.Repeats = RTTaskCreationParameters::RepeatForever;
followParams.Period = 10;
std::shared_ptr<RTTask> followTask(manager->TaskSubmit(followParams));
While your tasks are running, you can use RTTask::StatusGet
and RTTaskManager::GlobalValueGet
to monitor your tasks.
FirmwareValue targetPosition = manager->GlobalValueGet(
"targetPosition");
std::cout <<
"Target Position: " << targetPosition.
Double << std::endl;
At the end of your program, stop the tasks, manager, and axis.
followTask->Stop();
calcTask->Stop();