Hello World
Important
Two implicit rules:
All commands are assumed to be run from the
$MARTE_TRAINING_PARENT_DIR/MARTe2-training-proj/Startupdirectory, unless otherwise specified.When more than one application must be executed in parallel, a new terminal needs to be opened for each application.
Run the application
Start the application with:
cd $MARTE_TRAINING_PARENT_DIR/MARTe2-training-proj/Startup
./MARTeApp.sh -f ../Configurations/HelloWorld/RTApp-HelloWorld-1.cfg -l RealTimeLoader -s State1
Once the application is running, inspect the screen output and verify that the log shows the counter increasing:
$ [Information - LoggerBroker.cpp:152]: Counter [0:0]:1
$ [Information - LoggerBroker.cpp:152]: Counter [0:0]:2
...
If these messages appear, the Hello World MARTe2 application has started successfully.
To stop the application, press Ctrl+C in the terminal where it is running.
Notes on common warning messages
Warning
While running the application, you may observe messages such as:
$ [Warning - Threads.cpp:169]: Requested a thread priority that is higher ...
$ [Warning - Threads.cpp:181]: Failed to change the thread priority ...
These warnings are caused by insufficient user permissions to set real-time thread priorities.
For development purposes, they can be safely ignored and do not affect the functional behaviour of the application.
In a production environment, however, running without the appropriate permissions may impact real-time performance.
Understanding what just ran
The HelloWorld application that was launched in the previous step is fully described by the file:
../Configurations/HelloWorld/RTApp-HelloWorld-1.cfg
Rather than hard-coding the application structure in C++, MARTe2 uses a configuration-based approach. This means that the execution states, scheduling behaviour, functional blocks, and signal connections are all described in the configuration.
When the command below was executed:
./MARTeApp.sh -f ../Configurations/HelloWorld/RTApp-HelloWorld-1.cfg -l RealTimeLoader -s State1
MARTe2 loaded this configuration, created the RealTimeApplication, selected the State1 state, and started executing it.
RealTimeApplication
At the top level (but not necessarily at the beginning of the file) of every MARTe2 configuration there is a RealTimeApplication. This component defines the overall structure and behaviour of the application.
Note
A MARTe configuration file may contain multiple RealTimeApplication instances.
The RealTimeApplication is always composed of the following sections:
+Functions: defines the processing components (GAMs)
+Data: defines the data sources
+States: defines the execution states (e.g.
State1) and theirRealTimeThreadinstances+Scheduler: controls how the application is executed
1$HelloWorldApp = {
2 Class = RealTimeApplication
3 +Functions = {
4 Class = ReferenceContainer
5 +Data = {
6 Class = ReferenceContainer
7 +States = {
8 Class = ReferenceContainer
9 +Scheduler = {
10 Class = GAMScheduler
Functions
Two functional blocks (GAMs) are executed in the thread: GAMTimer and GAMDisplay.
The first is an IOGAM responsible for getting a counter and a timer signal from a DataSource named Timer and copying these signals to a GAMDataSource.
1 +GAMTimer = {
2 Class = IOGAM
3 InputSignals = {
4 Counter = {
5 DataSource = Timer
6 Type = uint32
7 }
8 Time = {
9 Frequency = 1
10 DataSource = Timer
11 Type = uint32
12 }
13 }
14 OutputSignals = {
15 Counter = {
16 DataSource = DDB1
17 Type = uint32
18 }
19 Time = {
20 DataSource = DDB1
21 Type = uint32
22 }
23 }
24 }
The Frequency is a special signal property that defines the rate at which the thread executes. The actual pacing of the thread is determined by the DataSource that provides the signal with the Frequency property. In this case, the Timer DataSource is configured to execute at a frequency of 1 Hz.
The second is an IOGAM that takes care of copying the signals from the GAMDataSource and offering them to a DataSource named Display.
1 +GAMDisplay = {
2 Class = IOGAM
3 InputSignals = {
4 Counter = {
5 DataSource = DDB1
6 Type = uint32
7 }
8 }
9 OutputSignals = {
10 Counter = {
11 DataSource = Display
12 Type = uint32
13 }
14 }
15 }
DataSources
The MARTe +Data provide an interface between the functional blocks (GAMs) and the outside world. They are responsible for acquiring data from external sources (e.g. hardware, files, network) and making it available to the GAMs, as well as for taking data from the GAMs and sending it to external sinks.
Two DataSources are defined in the configuration: Timer (of type LinuxTimer) and Display (of type LoggerDataSource).
1 +Timer = {
2 Class = LinuxTimer
3 SleepNature = "Default"
4 Signals = {
5 Counter = {
6 Type = uint32
7 }
8 Time = {
9 Type = uint32
10 }
11 }
12 }
1 +Display = {
2 Class = LoggerDataSource
3 }
States and Threads
In the HelloWorld example, a single state (State1), with a single thread (Thread), is defined. The thread executes the two GAMs described in the previous section.
1 +States = {
2 Class = ReferenceContainer
3 +State1 = {
4 Class = RealTimeState
5 +Threads = {
6 Class = ReferenceContainer
7 +Thread1 = {
8 Class = RealTimeThread
9 CPUs = 0x1
10 Functions = {GAMTimer GAMDisplay}
11 }
12 }
13 }
14 }
Scheduler
MARTe allows the default GAMScheduler component to be modified, but this is meant only for special applications (e.g. in embedded systems). For most applications, the default scheduler is sufficient.