DataSource & Brokers

Components inheriting from DataSourceI provide a real-time interface for the interchange of input and output signals with the hardware.

../../_images/DataSource-1.png

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).

../../_images/Brokers-1.png

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

MemoryMapInputBroker

Copies the signals from a memory area declared in the DataSource.

MemoryMapInperpolatedInputBroker

Interpolates the signals from the DataSource.

MemoryMapMultiBufferBroker

Copy from/to a DataSourceI multi-buffer memory address. A different buffer is allocated for each signal.

MemoryMapMultiBufferInputBroker

Input version of the MemoryMapMultiBufferBroker.

MemoryMapMultiBufferOutputBroker

Output version of the MemoryMapMultiBufferBroker.

MemoryMapOutputBroker

Copies the signals to a memory area declared in the DataSource.

MemoryMapSynchronisedInputBroker

Calls the Synchronise method on the DataSource before copying the signals.

MemoryMapSynchronisedMultiBufferInputBroker

Synchronised input implementation of the MemoryMapMultiBufferBroker.

MemoryMapSynchronisedMultiBufferOutputBroker

Synchronised output implementation of the MemoryMapMultiBufferBroker.

MemoryMapSynchronisedOutputBroker

Calls the Synchronise method on the DataSource after copying the signals.

MemoryMapAsyncOutputBroker

Asynchronously (i.e. in the context of another, decoupled, thread) calls the Synchronise method on the DataSource after copying the signals into a circular buffer.

MemoryMapAsyncTriggerOutputBroker

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 Synchronise method on the DataSource after copying the signals into a circular buffer.

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.

../../_images/Brokers-2.png

A GAM can be fed with data from different sources.

../../_images/Brokers-3.png

The same interpolation Broker is reused by the File and the Database Datasources.

../../_images/Brokers-4.png

The same time window Broker is reused by the File and the Database Datasources.

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…

../../_images/GAMDataSource-1.png

…then is processed by the GAM …

../../_images/GAMDataSource-2.png

…and finally the data is scattered back to the DDB:

../../_images/GAMDataSource-3.png

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.

Fixed signals configuration (Run with NAME_OF_THE_STATE=State1 and NAME_OF_THE_CONFIGURATION_FILE=GAMs-1.cfg)
  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.