The DataIO class defined in C++ provides a flexible input/output object for interfacing between models, tasks, events, and more. It is templated to work with nearly any data type, provided that type can be passed and returned via function. This guide provides an overview of how to interact with the DataIO class.

The DataIO Class

The SIGNAL Macro

The SIGNAL macro which defines parameters, inputs, and outputs for models uses is simply a wrapper for the DataIO object. It is used to simplify DataIO creation for new users, but is not mandatory. Examples using SIGNAL and DataIO are provided here.

The PARAMS, INPUTS, and OUTPUTS sections in each model define signals for interfacing with it.

Use

Because SIGNALs are defined on the interfaces of models, the most common way of constructing large sims is to connect the inputs of one model to the outputs of another. In this way, complex calculations are changed in much the same way as Simulink blocks are tied together.

The most basic use cases

The most basic use cases for signals (and, by extension, the DataIO object) that a user would typically need involve writing, reading, and connecting signals to one another. Writing to a signal involves setting its value, typically from some internal calculation in a model. Reading a signal involves getting its value, often for use inside a model, and connecting signals involves tying a downstream (input) to an upstream (output) to facilitate data flow in the simulation.

WRITE: Writing to a DataIO object involves passing a value to it in parenthesis (using the assignment operator won’t work). Example:

DataIO<double> data(parent, "data", 1.0); // Define a DataIO to hold a double value named data with default 1.0
SIGNAL(data, double, 1.0);                // This is the exact equivalent of the above using the SIGNAL macro
data(2.0);                                // Write value 2.0 to the data signal

READ: Reading from a DataIO object involves passing a value to it in parenthesis (using the assignment operator won’t work). Example:

DataIO<double> data(parent, "data", 1.0); // Define a DataIO to hold a double value named data with default 1.0
SIGNAL(data, double, 1.0);                // This is the exact equivalent of the above using the SIGNAL macro
double read_val = data();                 // read_val will receive 1.0

CONNECT: Connecting two DataIO object involves pointing a downstream signal (such as a model input) to an upstream signal (such as a model output) such that the downstream signal always returns the exact same value as held by the upstream signal. Example:

DataIO<double> upstream(parent, "upstream", 1.0);   // Define a DataIO to hold a double value named upstream with default 1.0
SIGNAL(upstream, double, 1.0);                      // This is the exact equivalent of the above using the SIGNAL macro
DataIO<double> downstream(parent, "downstream", 3.0);   // Define a DataIO to hold a double value named upstream with default 3.0
SIGNAL(downstream, double, 3.0);                        // This is the exact equivalent of the above using the SIGNAL macro
double read_val = downstream();                     // read_val will receive 3.0

// Now connect our downstream signal to our upstream signal
connectSignals(upstream, downstream);           // This connects downstream to upstream so downstream returns the value of upstream
downstream.mapTo(upstream);                     // This does the same as the above -- this is more low level. The above is recommended.
read_val = downstream();                        // Now downstream will return 1.0, which is the value held by upstream

// Alternatively, you can connect a DataIO to a pointer for easy integration with non-ModelSpace objects, such as integrated FSW
double x = 4.0;                 // Create a value x and set to 4.0
double* x_ptr = &x;             // Pointer x_ptr points to x
downstream.mapTo(x_ptr);        // Map downstream to x_ptr
read_val = downstream();        // Now downstream will return 4.0, which is the value x_ptr points to

Description and Naming

The DataIO class is a templated class with one template argument - T. DataIO<T> produces an input/output object for data of type T. The argument is:

  • T - The type of the data. This should be a type that can be passed and returned via function.

DataIO operates by abstracting data using a pointer. It holds an internal value of type T and a pointer to the data source to use. By default, that data source is itself, but it may point to another DataIO object if commanded to do so via the mapToPointer function. In that case, DataIO will recursively call DataIO objects until it finds the root object, then return the value held by the root object.

For a complete guide on interacting with the DataIO class, refer to the Doxygen documentation: DataIO Doxygen

Creating a DataIO

Description C++ Example Python Example

Constructor with Parent, Name, and Initial Value - Creates a DataIO object with a specified parent, name, and initial value. Equivalent SIGNAL example provided

DataIO<double> data1(parent, "data1", 1.0);
SIGNAL("data1", double, 1.0);
# Not used this way in Python

Getting and Setting Values

Description C++ Example Python Example

Getting the Value - Returns the value of the DataIO object.

double value = data1();
value = data1()

Setting a Value - Sets the value of the DataIO object.

data1(10.0);
data1(10.0)

Mapping DataIO objects to one another

Description C++ Example Python Example

Mapping DataIO to another DataIO — This is used to connect inputs and outputs on models. This example maps data1 to return the value held by data2. As data2 is changed, data1 will change as well.

data1.mapTo(data2);
data1.mapTo(data2)

Mapping to a Pointer - Maps the DataIO object to a pointer, rather than another DataIO object.

double externalData = 5.0;
data1.mapToPointer(&externalData);
# Not used in Python

Additional Operations

Description C++ Example Python Example

Resetting the Map - Resets the mapping of the DataIO object to its own internal data.

data1.resetMap();
data1.resetMap()