dds-fmu 0.5.1
DDS-FMU communication integration
|
OMG DDS [8] is a standard for a data-centric publish and subscribe scheme, which is devised by the Object Management Group (OMG). It defines a set of quality of services (QoS) for data distribution service (DDS). This includes APIs and communication semantics, where real-time publish subscribe (RTPS) [11] is one such protocol that aims to provide interoperability between DDS implementations from different vendors.
Functional mock-up interface (FMI) [7] is a widely used standard that defines containers and interfaces for exchanging dynamical simulation models. As defined by the FMI standard, an FMU (Functional Mock-up Unit) is essentially a zip archive that implements the FMI standard for a specific 'system' in form of binaries, code, configuration files, et cetera.
DDS can be used as a communication middleware for components in a software system. These components can consist of graphical user interfaces, hardware instruments, software components, for instance control systems or business logic units. Suppose one such component is a control system module or sensor acquisition module. If the purpose is to use the DDS component together with other FMUs, one needs to somehow translate between the different APIs. One option is to integrate the DDS component directly in an FMU, but this requires access to the software code for proper integration. An alternative, less invasive, approach is to use an adaptation layer that translates between DDS and FMI.
dds-fmu
implements the latter approach and is inspired by eProsima's Integration Service [4]. dds-fmu
is extensible: the user can create a customised FMU by providing new IDL files and mappings between DDS pub/sub types and FMI inputs/outputs. dds-fmu
is configurable: it comes bundled with all necessary tools to generate modelDescription.xml
, which is consistent with the custom configuration. dds-fmu
customisation has no need for a code compiler and no new binary files are created.
dds-fmu
can be downloaded from dds-fmu package repository. The FMU comes with linux64
and win64
binaries. Your system will need: Linux: GLibc >= 2.27
, e.g. Ubuntu 18.04 or newer; Windows: Microsoft Visual C++ Runtime libraries 2019. These are typically already installed on a development computer. If you prefer to build the binaries yourself, the source code is available at SINTEF's hosted GitLab co-simulation/dds-fmu. The repository README provides build instructions and requirements.
A typical workflow can be summarised with these main steps:
cd /to/path/with/dds-fmu.fmu
unzip dds-fmu.fmu
resources/config/
, see details in Configuration.chmod +x resources/tools/linux64/repacker
resources/tools/linux64/repacker create -v -o dds-fmu.fmu /to/path/with/
For repacker
command line options, see repacker --help
.
Preparing the FMU for a customised setup is done by editing configuration files, which are located in the resources
folder. The layout of this folder can be seen in the figure below. Typically, a customisation consists of editing three files, which will be elaborated below:
dds-fmu.idl
Data type specification with IDL files (Data type specification with IDL).ddsfmu_mapping.xml
DDS-to-FMU mapping (DDS-to-FMU mapping).dds_profile.xml
Fast-DDS XML profiles (Fast-DDS XML profiles).The IDL standard [9] is a descriptive way to define data types and more. We make use of it to define data structures on DDS that we want to integrate with FMU. The basic notation is not unlike regular C++ and in the listing below we provide an example, which cover many use cases.
Suppose the contents above are added to dds-fmu.idl
, the file will be parsed by the FMU, and idl::Klass
becomes available as a type.
The file ddsfmu_mapping.xml
contains elements that specify which DDS topics to map to FMU inputs and outputs, see figure below. The topic attribute is a name identifier for a DDS Topic entity. Each topic is associated with a data type, which in our case is defined by our IDL file. Note that FMU outputs are subscribed DDS signals, and FMU inputs are published DDS signals. DDS input = FMU output and DDS output = FMU inputs. The user defines the necessary of FMU inputs and outputs using <fmu_in>
and <fmu_out>
elements, respectively. See the listing below for an example. For each element of <fmu_in>
a DDS DataWriter is created, and likewise, for each <fmu_out>
a DDS DataReader. The attribute key_filter of the <fmu_out>
node indicates whether the FMU should perform key filtering on the output signals. This is disabled by default, which would result in all data on the topic being processed by the DDS DataReader. If it is enabled, on the other hand, corresponding FMU parameters will be generated in the modelDescription.xml
.
The repacker
tool generates the modelDescription.xml
based on this mapping. Suppose the unzipped contents with modified configuration files is located in /my/custom/fmu
. By running the commands below, the user can inspect the generated /my/custom/fmu/modelDescription.xml
.
A user can configure the Fast-DDS to a great extent by means of XML profiles. Central concepts such as domain id, QoS (like durability and reliability), and much more are configured using configuration profiles for various DDS entities. These profiles are loaded by purposefully specifying the profile_name
attribute for an element type, see the figure below. The profiles for participant, publisher, and subscriber are attempted loaded by profile_name="dds-fmu-default"
, with fallback to builtin default QoS. Profiles for topic
, data_writer
, and data_reader
elements are attempted loaded by profile_name="[topic]"
, where topic is as defined in <fig:ddsfmu>, with fallback to default QoS. This means that the user can specify custom profiles for specific topic
, data_reader
, and data_writer
entities. XML profile documentation for each DDS entity type can be found on Fast-DDS online documentation [3]. The FMU comes with an example dds_profile.xml
, which can be edited as needed.
Continuing the example from previous sections, it could be necessary to add custom QoS for the data_writer
. Then, the dds_profile.xml
would contain an element as indicated below.
DDS supports data exchange of user-defined data structures. These are often defined using an interface definition language (IDL), whose grammar is specified by the OMG IDL [9]. What the IDL files defines, can be represented as dynamic types through the XTypes API specification [10]. dds-fmu
makes use of this standard through a vendor implementation, namely eProsima xtypes
[5]. Moreover, dds-fmu
uses eProsima Fast-DDS
[2], which implements DDS RTPS. dds-fmu
parses IDL files into xtypes DynamicData and, with the help of code taken from [4], converts between xtypes DynamicData and Fast-DDS DynamicData. As a result, dds-fmu
supports DDS communication with data types defined in IDL files without the need for code compilation. The xTypes API facilitates access to members of DynamicData in a way that infers the type kind of each member. dds-fmu
makes use of this feature to ensure that each member is read or write accessed as the appropriate primitive type, as supported from the FMU side. Since dds-fmu
is a co-simulation FMU, the implementation of the API is achieved with the help of cppfmu
[6]. Currently, dds-fmu
supports FMI 2.0, which means that there are some limitations in terms of mapping from DynamicData member types to FMI types, see table below for an overview of supported data type mapping.
Type kind | FMI 2.0 type | Comment | Type kind | Comment | |
---|---|---|---|---|---|
boolean | fmiBoolean | long double | N/A | ||
int8 | fmiInteger | char16 | N/A | ||
uint8 | fmiInteger | wide char | N/A | ||
int16 | fmiInteger | bitset | N/A | ||
uint16 | fmiInteger | sequence type | N/A | ||
int32 | fmiInteger | wstring | N/A | ||
uint32 | fmiReal | map type | N/A | ||
int64 | fmiReal | Lossy | |||
uint64 | fmiReal | Lossy | |||
float | fmiReal | ||||
double | fmiReal | ||||
string | fmiString | ||||
char8 | fmiString | ||||
enumeration | fmiInteger |
An IDL data structure can be complex, with non-primitive types and nested data structures. These members needs to be demultiplexed in a way that allows the scalar variable access interface of FMI 2.0 to read or write member variables. This must be done in a manner that correctly casts to their primitive type. While parsing a requested DynamicData variable, dds-fmu
instantiates visitor functions for read and write, with appropriate reference to the DynamicData's primitive type, as well as casting for input and output types. These visitor functions are stored in vectors in such a way that with so-called value references, they can be directly accessed by FMU setters and getters.
dds-fmu
comes bundled with an executable command line tool for generating modelDescription.xml
. In short: given IDL
files, Fast-DDS configuration files, and a DDS-to-FMU mapping specification, the tool automatically generates modelDescription.xml
. The output model description creates <ModelVariables>
elements with <ScalarVariable>
entries, and <ModelStructure>
element with <Outputs>
. All the <ScalarVariables>
entries have attribute variability=discrete
when they consist solely of inputs and outputs: causality=input|output
. If there are any @key
variables, additional entries with causality=parameter
and variability=fixed
will be created. The generated <ScalarVariable>
entries have name
attribute based on the FMI standard's structured
variable naming convention. The variable name is constructed as name=[pubsub].[topic name].[structured name]
, where topic name
is as prescribed in the DDS-to-FMU mapping specification file, and pubsub
is pub
for input and sub
for output. For @key
parameters, they will have naming name=key.sub.[topic name].[structured name]
.
Each instance of dds-fmu
only creates a single DDS Participant, DDS Publisher, and DDS Subscriber. As a consequence, the QoS settings for these entities will be the same for all DDS DataReaders and DDS DataWriters in the current FMU instance. However, it is possible to specify QoS for each DataReader and DataWriter. The QoS settings for all mentioned entities are set through Fast-DDS XML profiles. These profiles are documented in the Fast-DDS documentation [3]. This setup may not suit complex use cases. Then, one approach would entail splitting the DDS mapping into multiple FMUs. For details on how to do profile configuration, see Usage.
The interaction with DDS reader and writer entities are done in each call to DoStep() on the FMI side. Writing DDS data is done before reading. If the reader QoS is configured to have history greater than one, all data is fetched, but only the latest sample is kept. Effectively, this approach is a sample and hold. See the figure below for a sequence diagram of DoStep().
There are some things the user should be aware of to avoid unnecessary troubleshooting. Below we list several points and in some cases suggest workarounds.
key_filter
With key filtering this can become particularly evident.dds-fmu
instances in the simulator instance if they are on the same DDS Domain ID. There are workarounds for some simulators. In the case of cosim
[12] you can use proxyfmu
[1] on additional dds-fmu
instances.#include "file.idl"
in the IDL).std::vector<TYPE>
@key
partially supported: primitive types, string and enumerations. This excludes directly on structs, or array-like members.@optional
and @id
is supported by the IDL parser via a patch, but ignored by our implementationThe FMU is licensed under MPL-2.0. Licenses for transitive dependencies are located in the documentation/licenses
folder of the FMU. An overview is found at Licenses.