Files
marte_dev_tools/docs/TUTORIAL.md
Martino Ferrari 7ae701e8c1 Auto doc...
2026-02-02 18:22:52 +01:00

4.8 KiB

Creating a MARTe Application with mdt

This tutorial will guide you through creating, building, and validating a complete MARTe application using the mdt toolset.

Prerequisites

  • mdt installed and available in your PATH.
  • make (optional but recommended).

Step 1: Initialize the Project

Start by creating a new project named MyControlApp.

mdt init MyControlApp
cd MyControlApp

This command creates a standard project structure:

  • Makefile: For building and checking the project.
  • .marte_schema.cue: For defining custom schemas (if needed).
  • src/app.marte: The main application definition.
  • src/components.marte: A placeholder for defining components (DataSources).

Step 2: Define Components

Open src/components.marte. This file uses the #package App.Data namespace, meaning all definitions here will be children of App.Data.

Let's define a Timer (input source) and a Logger (output destination).

#package MyContollApp.App.Data

+DDB = {
    Class = GAMDataSource
}
+TimingDataSource = {
    Class = TimingDataSource
}
+Timer = {
    Class = LinuxTimer
    Signals = {
        Counter = {
            Type = uint32
        }
        Time = {
            Type = uint32
        }
    }
}

+Logger = {
    Class = LoggerDataSource
    Signals = {
        LogValue = {
            Type = float32
        }
    }
}

Step 3: Implement Logic (GAM)

Open src/app.marte. This file defines the App node.

We will add a GAM that takes the time from the Timer, converts it, and logs it.

Add the GAM definition inside the +Main object (or as a separate object if you prefer modularity). Let's modify src/app.marte:

#package MyContollApp
+App = {
    Class = RealTimeApplication
    +Functions = {
        Class = RefenceContainer
        // Define the GAM
        +Converter = {
            Class = IOGAM
            InputSignals = {
                TimeIn = {
                    DataSource = Timer
                    Type = uint32
                    Frequency = 100 //Hz
                    Alias = Time // Refers to 'Time' signal in Timer
                }
            }
            OutputSignals = {
                LogOut = {
                    DataSource = Logger
                    Type = float32
                    Alias = LogValue
                }
            }
        }
    }
    +States = {
        Class = ReferenceContainer
        +Run = {
            Class = RealTimeState
            +MainThread = {
                Class = RealTimeThread
                Functions = { Converter } // Run our GAM
            }
        }
    }

    +Data = {
        Class = ReferenceContainer
        DefaultDataSource = DDB
    }
    +Scheduler = {
        Class = GAMScheduler
        TimingDataSource = TimingDataSource
    }
}

Step 4: Validate

Run the validation check to ensure everything is correct (types match, references are valid).

mdt check src/*.marte

Or using Make:

make check

If you made a mistake (e.g., mismatched types), mdt will report an error.

Step 5: Build

Merge all files into a single configuration file.

mdt build -o final_app.marte src/*.marte

Or using Make:

make build

This produces app.marte (or final_app.marte), which contains the flattened, merged configuration ready for the MARTe framework.

Step 6: Using Variables and Expressions

You can parameterize your application using variables. Let's define a constant for the sampling frequency.

Modify src/app.marte:

#package MyContollApp

//# Sampling frequency in Hz
#let SamplingFreq: uint32 = 100

+App = {
    // ...
    +Functions = {
        +Converter = {
            Class = IOGAM
            InputSignals = {
                TimeIn = {
                    DataSource = Timer
                    Type = uint32
                    Frequency = $SamplingFreq
                    Alias = Time
                }
            }
            // ...
        }
    }
}

You can also use expressions for calculations:

#let CycleTime: float64 = 1.0 / $SamplingFreq

LSP will show you the evaluated values directly in the code via Inlay Hints (e.g., CycleTime: 0.01) and in the hover documentation.

Step 7: Advanced - Custom Schema

Suppose you want to enforce that your DataSources support multithreading. You can modify .marte_schema.cue.

package schema

#Classes: {
    // Enforce that LinuxTimer must be multithreaded (example)
    LinuxTimer: {
        #meta: {
            multithreaded: true
        }
        ...
    }
}

Now, if you use LinuxTimer in multiple threads, mdt check will allow it (because of #meta.multithreaded: true). By default, it would disallow it.

Conclusion

You have successfully initialized, implemented, validated, and built a MARTe application using mdt.