DataSource & Brokers¶
Components inheriting from DataSourceI provide a real-time interface for the interchange of input and output signals with the hardware.
Configuration¶
A DataSource is initialised just like any other MARTe Object.
bool FileReader::Initialise(StructuredDataI& data) {
//You must call DataSource::Initialise !
bool ok = DataSourceI::Initialise(data);
if (ok) {
ok = data.Read("FileFormat", fileFormatStr);
...
The properties related to the DataSource signals are available when the SetConfiguredDatabase
method is called.
At this stage any of the signal related methods, described in the DataSourceI API, can be used to query the available signal properties.
bool FileReader::SetConfiguredDatabase (StructuredDataI & data) {
//You must call DataSource::SetConfiguredDatabase !
bool ok = DataSource::SetConfiguredDatabase (data);
...
//Only one and one GAM allowed to interact with this DataSourceI
ok = (GetNumberOfFunctions() == 1u);
if (!ok) {
REPORT_ERROR(ErrorManagement::ParametersError, "Exactly one Function allowed to interact with this DataSourceI");
}
...
uint32 nOfFunctionSignals = 0u;
ok = GetFunctionNumberOfSignals(InputSignals, 0u, nOfFunctionSignals);
...
As in the case of the GAMs, the DataSources can be conceptually divided in two sets: one where the signals (number, type and dimensions) are fixed by design (e.g. LinuxTimer); and another where the behaviour of the hardware is adapted to the signal characteristics of a given real-time application (e.g. FileReader).
In the configuration stream the signals shall be placed inside a node named Signals
.
The DataSourceI can also use registered structured types as input/output signals (the strategy is identical to the one described for the GAMs).
Signal properties¶
The signal name (in the context of the DataSource) is the name of the node. Other properties that can be set for any signal are:
Property |
Meaning |
---|---|
Type |
The signal type as any of the supported Types or a structure type. |
NumberOfElements |
The number of elements (1 if the signal is a scalar). |
NumberOfDimensions |
The number of dimensions (0 if scalar, 1 if vector, 2 if matrix). |
Brokers¶
The BrokerI components provide the interface between the GAMs memory and the DataSource hardware data (typically memory).
The DataSource implementation will return the most appropriate Broker based only on the GAM signal properties (see DataSourceI::GetBrokerName
to read the available properties):
//This function will be called for every signal that interacts with this DataSource.
const char8* LinuxTimer::GetBrokerName(StructuredDataI& data, const SignalDirection direction) {
const char8 *brokerName = NULL_PTR(const char8 *);
if (direction == InputSignals) {
float32 frequency = 0.F;
//Read the signal property
if (!data.Read("Frequency", frequency)) {
frequency = -1.F;
}
//Depending on the property select the type of Broker
if (frequency > 0.F) {
brokerName = "MemoryMapSynchronisedInputBroker";
synchronising = true;
}
else {
brokerName = "MemoryMapInputBroker";
}
...
return brokerName;
Before the Execute method of the GAM is called, all the Brokers that provide input data to the GAM are executed, so that the GAM has all the required input data ready before executing its algorithm.
Conversely, after the GAM finishes its execution, all the Brokers that read data from the GAM (and write to a given DataSource) are executed.
Returning Input and Output Brokers¶
The DataSourceI will have to instantiate and return the appropriate brokers, depending on the signal type. The name of the broker returned in the GetBrokerName
method shall be consistent with the class type of the Brokers that are added in the GetInputBrokers
and GetOutputBrokers
.
bool LinuxTimer::GetInputBrokers(ReferenceContainer& inputBrokers, const char8* const functionName, void* const gamMemPtr) {
bool ok = GetFunctionIndex(functionIdx, functionName);
if ((synchronising) && (synchronisingFunctionIdx == functionIdx)) {
ReferenceT<MemoryMapSynchronisedInputBroker> brokerSync("MemoryMapSynchronisedInputBroker");
...
if (ok) {
//This call will only add the Synchronous signal (given that GetBrokerName returned MemoryMapSynchronisedInputBroker at most for one signal)
ok = brokerSync->Init(InputSignals, *this, functionName, gamMemPtr);
...
if (ok) {
ok = inputBrokers.Insert(brokerSync);
...
else {
ReferenceT<MemoryMapInputBroker> broker("MemoryMapInputBroker");
ok = broker.IsValid();
if (ok) {
ok = broker->Init(InputSignals, *this, functionName, gamMemPtr);
...
if (ok) {
ok = inputBrokers.Insert(broker);
...
A Broker may be specifically developed for a given DataSource (e.g. because the Broker needs to access an hardware dependent function) or one of the MARTe standard brokers may be used:
Name Description |
|
---|---|
Copies the signals from a memory area declared in the DataSource. |
|
Interpolates the signals from the DataSource. |
|
Copy from/to a DataSourceI multi-buffer memory address. A different buffer is allocated for each signal. |
|
Input version of the MemoryMapMultiBufferBroker. |
|
Output version of the MemoryMapMultiBufferBroker. |
|
Copies the signals to a memory area declared in the DataSource. |
|
Calls the |
|
Synchronised input implementation of the MemoryMapMultiBufferBroker. |
|
Synchronised output implementation of the MemoryMapMultiBufferBroker. |
|
Calls the |
|
Asynchronously (i.e. in the context of another, decoupled, thread) calls the |
|
Only stores data based on an event trigger (with pre and post windows). Asynchronously (i.e. in the context of another, decoupled, thread) calls the |
The memory map based brokers access the DataSource memory using the DataSourceI::GetSignalMemoryBuffer
.
All the functions which are related to data transformation should be implemented in a Broker. This allows to reuse the same Broker class in different DataSource implementations (e.g. the MemoryMapInperpolatedInputBroker can be reused on any DataSourceI which requires data interpolation).
In the example below, the interpolation and the time window brokers are reused in different hardware interfaces.
Broker synchronisation¶
All the Brokers implement the ExecutableI interface and are thus scheduled by the application scheduler.
As discussed above, their main function is to copy the data from/to the GAM memory, but they can also be used as a synchronisation point: either by blocking the execution until new data is available; or by triggering the output when data is written.
Examples of synchronisation brokers are the MemoryMapSynchronisedInputBroker and the MemoryMapSynchronisedOutputBroker.
//Function called by the MemoryMapSynchronisedInputBroker before copying the data from the data source memory.
bool LinuxTimer::Synchronise() {
...
//Block execution until an event occurs
err = synchSem.ResetWait(TTInfiniteWait);
...
Asynchronous brokers are expected to provide unblocking data (i.e. as soon as the broker asks for it). This data is typically made available by another thread.
Warning
If the synchronisation requires interfacing with the operating system, a decoupling thread shall be used. This allows to decouple the real-time thread performance and to handle possible timeouts. The MemoryMapAsyncOutputBroker is an example of such type of Broker.
GAMDataSource (DDB)¶
The GAMDataSource (also known as DDB) is a framework standard DataSource component for the real-time interchange of data between GAMs that belong to the same RealTimeThread.
The DDB is based on a non-blocking (i.e. without any synchronisation) scatter and gather mechanism.
First the data is copied (gather) to the GAM input signal memory…
…then is processed by the GAM …
…and finally the data is scattered back to the DDB:
MemoryDataSourceI¶
The MemoryDataSourceI can be used as a base class to develop data sources which require a memory map (eventually multi-buffer) implementation.
The RealTimeThreadAsyncBridge is an example of a MemoryDataSourceI implementation.
CircularBufferThreadInputDataSource¶
The CircularBufferThreadInputDataSource can be used as a base class for data sources which require a circular buffer interface to the underlying data. This abstract class already offers all the circular buffer implementation and the threading mechanisms that allows to decouple the access to the data from the real-time thread interface.
The TODO is an example of a CircularBufferThreadInputDataSource implementation.
Examples¶
The following is an example which uses 4 distinct DataSources. Note that the signals will be automatically added to the GAMDataSource and to the LoggerDataSource, based on the GAM requests.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | $TestApp = {
Class = RealTimeApplication
+Functions = {
Class = ReferenceContainer
+GAMTimer = {
Class = IOGAM
InputSignals = {
Counter = {
DataSource = Timer
Type = uint32
}
Time = {
Frequency = 1
DataSource = Timer
Type = uint32
}
}
OutputSignals = {
Counter = {
DataSource = DDB1
Type = uint32
}
Time = {
DataSource = DDB1
Type = uint32
}
}
}
+GAMFixed1 = {
Class = FixedGAMExample1
Gain = 2
InputSignals = {
Counter = {
DataSource = DDB1
Type = uint32
}
}
OutputSignals = {
GainCounter = {
DataSource = DDB1
Type = uint32
}
}
}
+GAMDisplay = {
Class = IOGAM
InputSignals = {
Counter = {
DataSource = DDB1
Type = uint32
}
GainCounter = {
DataSource = DDB1
Type = uint32
}
}
OutputSignals = {
Counter = {
DataSource = LoggerDataSource
Type = uint32
}
GainCounter = {
DataSource = LoggerDataSource
Type = uint32
}
}
}
}
+Data = {
Class = ReferenceContainer
DefaultDataSource = DDB1
+DDB1 = {
Class = GAMDataSource
}
+LoggerDataSource = {
Class = LoggerDataSource
}
+Timings = {
Class = TimingDataSource
}
+Timer = {
Class = LinuxTimer
SleepNature = "Default"
Signals = {
Counter = {
Type = uint32
}
Time = {
Type = uint32
}
}
}
}
+States = {
Class = ReferenceContainer
+State1 = {
Class = RealTimeState
+Threads = {
Class = ReferenceContainer
+Thread1 = {
Class = RealTimeThread
CPUs = 0x1
Functions = {GAMTimer GAMFixed1 GAMDisplay }
}
}
}
}
+Scheduler = {
Class = GAMScheduler
TimingDataSource = Timings
}
}
|
Instructions on how to compile and execute the examples can be found here.