6. Capabilities and Interfaces

6.1. Programming Language Support

RTI Connext TSS supports the FACE TSS APIs in C++.

6.2. Operating System Environment

RTI Connext TSS has been implemented using the POSIX API calls prescribed for the applicable conformance profile. This usage has been validated using the FACE Conformance Test Suite (CTS).

6.3. TSS Capabilities

RTI Connext TSS supports the following capabilities:

  • Transport Service (TS) Capability

  • TSS Distribution Capability

  • TSS Configuration Capability

  • QoS Management Capability

RTI Connext TSS generates support code with the rtiddsgen tool to enable these capabilities.

6.3.1. FACE Profiles

RTI Connext TSS officially supports two FACE Compliance profiles, GeneralPurpose and SafetyBase. While RTI_TSS_ENABLE_FACE_COMPLIANCE can be set to any FACE TSS profile, this release of RTI Connext TSS supports up to the SafetyBase profile with Connext DDS Micro 2.4.13.4, and up to the GeneralPurpose profile with Connext DDS Professional 6.1.1.4.

6.3.1.1. GeneralPurpose Profile

RTI Connext TSS supports up to the GeneralPurpose profile with Connext DDS Professional 6.1.1.4. While RTI_TSS_ENABLE_FACE_COMPLIANCE can be set to either GeneralPurpose or None, both will be mapped to the GeneralPurpose profile.

Run the following CMake command to build RTI Connext TSS (Release) for the GeneralPurpose Profile with Connext DDS Professional:

Run the following CMake command to build RTI Connext TSS (Release) for the GeneralPurpose Profile with Connext DDS Micro:

Note

Refer to the (Build) section for a step-by-step building guide.

6.3.1.2. Safety Profile

RTI Connext TSS supports up to the SafetyBase profile with Connext DDS Micro 2.4.13.4. While RTI_TSS_ENABLE_FACE_COMPLIANCE can be set to either SafetyExtended, SafetyBase, or Security, all three will be mapped to the SafetyBase profile because Connext TSS does not specifically support SafetyExtended or Security.

Run the following CMake command to build RTI Connext TSS (Release) for the SafetyBase Profile:

Note

Connext DDS Micro is the only option for -DRTI_CONNEXT_TYPE when building for the SafetyBase profile.

RTI Connext TSS built for SafetyBase profile is different from TSS built for GeneralPurpose. RTI Connext TSS SafetyBase is limited to only using DPSE discovery. See the Connext DDS Micro DPSE Configuration subsection for more detail on DPSE discovery. RTI Connext TSS SafetyBase also does not support multicast, octect, or internal logging. Most importantly, RTI Connext TSS SafetyBase does not support deallocation or reallocation of allocated memory. This limitation extends to all RTI entities and resources.

6.3.2. Type Support

DDS is a strongly typed API that requires explicit definition of the data types to be used. The definition is provided in a platform and language neutral description such as IDL.

Once this definition is provided, the rtiddsgen tool is used to generate type support code for the given data type(s). The generated code (for a specific language) contains the definition of:

  • the type itself,

  • the DataWriter and DataReader code,

  • serialization and deserialization code, and

  • other utilities needed to use the data type in DDS systems.

In a DDS application, the generated type support is ‘registered’ with a DomainParticipant via an explicit call:

HelloWorldTypeSupport_register_type(participant, type_name);

Essentially, this function installs a type support plugin that registers the various type-specific utilities so that the DDS core can use the type.

The FACE TSS API is also strongly typed. The modified version of the rtiddsgen tool provided with Connext TSS will generate both the type support required by the underlying DDS and the type plugin required by Connext TSS.

The generated type plugin files (i.e., HelloGoodbye, HelloGoodbyePlugin, and HelloGoodbyeSupport) should not need any modification. The register_type function for an specific type is configured from the Configuration Interface capability described bellow.

6.3.3. Type-Specific Base and Typed Interfaces

While the FACE 3.0 TSS Type-Specific Base interface is defined and implemented by Connext TSS independently of any user data type, the TSS Type-Specific Typed interface must be defined for each type. Consequently, the rtiddsgen tool is used to generate the following headers and source files:

  • <MODULES/<DATATYPE_TYPE>/TypedTS.hpp defines the Type-Specific Typed interface for the user data type.

  • <MODULES/<DATATYPE_TYPE>/TypedTS_Impl.hpp defines a class implementating the interface defined in <MODULES/<DATATYPE_TYPE>/TypedTS.hpp.

  • <MODULES/<DATATYPE_TYPE>/TypedTS_Impl.cpp is the implementation of <MODULES/<DATATYPE_TYPE>/TypedTS_Impl.hpp.

6.3.4. Using the FACE 3.x Configuration Interface

Connext TSS implements the FACE 3.x Configuration Interface. Users configure Connext TSS by customizing configuration data within a Configuration interface instance and then setting its reference with the Connext TSS Base.

6.3.4.1. Generated Configured Implementation

The buildable source for a configuration interface is generated by Connext TSS. Given an IDL type definition file for a type Foo, running the rtiddsgen utility provided with Connext TSS will generate, among other files, a Foo_TSSConfigInterface.hpp header file. This file will define a class (RTI::<name of the IDL file>::Configuration) that implements the FACE::Configuration interface and its Configuration Services API.

Warning

The generated configuration is provided as a baseline configuration. Users are encouraged to modify the generated Configuration Interface Implementations and QoS functions.

6.3.4.2. Generated OSAPI Configuration for Connext Micro

When building Connext TSS with Connext Micro, the OSAPI is used to allocate memory during the initialization phase. OSAPI must be set up before calling any of the functions it provides. To do so the generated code, calls the _set_osapi_property followed by DDS_DomainParticipantFactory_get_instance functions in RTI::<name of the IDL file>::Configuration::initialize.

void Initialize(const FACE::Configuration::INITIALIZATION_TYPE &initialization_information,
                FACE::RETURN_CODE_TYPE::Value &return_code)
{
    UNUSED_ARG(initialization_information);

    if (_initialized)
    {
        return_code = FACE::RETURN_CODE_TYPE::NO_ERROR;
        return;
    }

    #ifdef RTI_CONNEXT_MICRO

    /* OSAPI needs to be set up before it can be used. */
    if (!_set_osapi_property())
    {
        return_code = FACE::RETURN_CODE_TYPE::INVALID_CONFIG;
        return;
    }

    DDS_DomainParticipantFactory *factory = NULL;
    factory = DDS_DomainParticipantFactory_get_instance();
    if (factory == NULL)
    {
        return_code = FACE::RETURN_CODE_TYPE::INVALID_CONFIG;
        return;
    }
    /* If and only if the set up above are successfully,
    *  OSAPI is ready to be used after this point.
    */

    #endif

    if (!_populate_system_config() ||
        !_populate_domain_config() ||
        !_populate_type_support_config() ||
        !_populate_connection_config())
    {
        return_code = FACE::RETURN_CODE_TYPE::INVALID_CONFIG;
        return;
    }

    _initialized = DDS_BOOLEAN_TRUE;
    return_code = FACE::RETURN_CODE_TYPE::NO_ERROR;
}

OSAPI properties can be modified by overriding the _set_osapi_property function.

FACE::Boolean _set_osapi_property()
{
/* User can override this function to set the osapi properties instead of
* creating a plugin function and pass it to the now-deprecated
* RTI_TSS_System_Configuration.configure_osapi_system_property_fn.
* This function does nothing by default.
*
* The overriding function should follow the following steps:
* 1. Create a varaible of struct OSAPI_SystemProperty and initializes it with
*    OSAPI_SystemProperty_INITIALIZER
* 2. Get the current osapi properties by passing the variable to
*    OSAPI_System_get_property function.
* 3. Modify the OSAPI_SystemProperty variable.
*    Notable properties:
*    - OSAPI_SystemProperty.timer_property.thread.port_property.parent.period
*    - OSAPI_SystemProperty.timer_property.thread.port_property.parent.time_capacity
*    - OSAPI_SystemProperty.timer_property.thread.port_property.parent.name
*    - OSAPI_SystemProperty.timer_property.thread.port_property.parent.deadline
*    - OSAPI_SystemProperty.timer_property.thread.stack_size
*    - OSAPI_SystemProperty.timer_property.thread.priority
* 4. Pass the modified OSAPI_SystemProperty variable to OSAPI_System_set_property.
*/

    return true;
}

6.3.4.3. Configuration Data Types

The configuration data types of RTI::Configuration are defined in RTI/TSS/Configuration.h. They are organized in these categories:

  • System Configuration (RTI_TSS_System_Configuration_T)

  • DomainParticipant Configuration (RTI_TSS_DDS_DomainParticipant_Configuration_T)

  • Type Configuration (RTI_TSS_TypeSupport_Configuration_T)

  • Connection Configuration (RTI_TSS_Connection_Configuration_t)

6.3.4.3.1. System Configuration

The System configuration configures system properties for Connext TSS and the underlying DDS factories. It is applied upon the very first call, idempotently, to Base::Initialize(). A System configuration is represented by RTI_TSS_System_Configuration_T.

/*RTI_TSS_System_Configuration */
typedef struct RTI_TSS_System_Configuration
{
    /*Name of the System Configuration */
    const char * config_name;

#if not in a Safety Base profile
    /*Logging verbosity */
    LogVerbosity verbosity;
#endif

    /*the configure_domain_participant_factory_qos_fn that configues the domain participant factory*/
    RTI_TSS_DomainParticipantFactoryQosCallback
            configure_domain_participant_factory_qos_fn;

    /*additional configuration data */
    void *user_data;

#if using RTI Connext Micro
    /*max number of connections that are allowed in the system */
    RTI_UINT32 max_connections;

    /*max number of topics that are allowed in the system */
    RTI_UINT32 max_topics;

    /*pointer to an array of the Micro components used for configuring the low level Micro Settings*/
    RTI_TSS_Micro_Component_T *components;

    /*number of components */
    RTI_UINT32 components_length;

#endif
#if using RTI Connext Pro
    /*pointer to the xml qos file */
    const char *xml_qos_file;

    /*pointer to the qos_library to be used by the system */
    const char* qos_library;

    /*pointer to the qos_profile to be used by the system*/
    const char* qos_profile;
#endif
} RTI_TSS_System_Configuration_T;

The generated RTI configuration class defines a function, _populate_system_config(), that sets a static array of system configurations that are read by a Base instance when it is initialized with the configuration interface.

/* Example system configuration snippet from a Foo_TSSConfigInterface.hpp */

static const FACE::UnsignedLong _system_configurations_length = 2;
RTI_TSS_System_Configuration_T _system_configurations[_system_configurations_length];

FACE::Boolean _populate_system_config()
{
    /* Customize each element of _system_configurations[] with a system configuration
     * that may be used to configure your Connext TSS application.
     */
    if (!RTI_TSS_System_Configuration_initialize(&_system_configurations[0]))
    {
        return false;
    }
    _system_configurations[0].config_name = "HelloWorld";
    ...

    if (!RTI_TSS_System_Configuration_initialize(&_system_configurations[1]))
    {
        return false;
    }
    _system_configurations[1].config_name = "GoodbyeWorld";
    ...
}
6.3.4.3.1.1. Connext Micro RT Components

For Connext DDS Micro (or Micro Cert), run-time (RT) components must be configured and registered during system initialization. These components for Micro include transports (UDP, ARINC 653) and discovery (DPDE, DPSE). To support their configuration, the System configuration type contains an array of configurations for Micro RT components, all of which are registered with Micro upon initialization.

Because the property of each component is complicated to set, the generated RTI::Configuration provides functions for you to customize that return the component property.

The snippet below shows how components are configured for a system configuration. Note how the function _new_udp_property() is defined and configured to return a UDP_InterfaceFactoryProperty for the UDP component. This pattern is applied for other components as well.

/* Example Micro components snippet from a Foo_TSSConfigInterface.hpp */

FACE::Boolean _populate_system_config()
{
    ...
    /* Customize the Micro run-time components for _system_configurations[0] */
    _system_configurations[0].components_length = 4;
    _system_configurations[0].components =
        new RTI_TSS_Micro_Component_t[_system_configurations[0].components_length];

    // Writer History Component
    if (!RTI_TSS_Micro_Component_initialize(&_system_configurations[0].components[0]))
    {
        return false;
    }
    _system_configurations[0].components[0].name = DDSHST_WRITER_DEFAULT_HISTORY_NAME;
    _system_configurations[0].components[0].component = WHSM_HistoryFactory_get_interface();
    _system_configurations[0].components[0].listener = NULL;
    _system_configurations[0].components[0].property = NULL;

    // Reader History Component
    if (!RTI_TSS_Micro_Component_initialize(&_system_configurations[0].components[1]))
    {
        return false;
    }
    _system_configurations[0].components[1].name = DDSHST_READER_DEFAULT_HISTORY_NAME;
    _system_configurations[0].components[1].component = RHSM_HistoryFactory_get_interface();
    _system_configurations[0].components[1].listener = NULL;
    _system_configurations[0].components[1].property = NULL;

    // UDP Component
    if (!RTI_TSS_Micro_Component_initialize(&_system_configurations[0].components[2]))
    {
        return false;
    }
    _system_configurations[0].components[2].name = NETIO_DEFAULT_UDP_NAME;
    _system_configurations[0].components[2].component = UDP_InterfaceFactory_get_interface();
    _system_configurations[0].components[2].listener = NULL;
    _system_configurations[0].components[2].property =
            (struct RT_ComponentFactoryProperty*) _new_udp_property();

      // DPSE Component
      struct DPSE_DiscoveryPluginProperty *dpse_properties =
              (struct DPSE_DiscoveryPluginProperty*) malloc(
                      sizeof(struct DPSE_DiscoveryPluginProperty));

      if (DPSE_DiscoveryPluginProperty_initialize(dpse_properties) != DDS_RETCODE_OK)
      {
          printf("Failed to Initialize DPSE properties");
          return false;
      }

      if (!RTI_TSS_Micro_Component_initialize(&_system_configurations[0].components[3]))
      {
          return false;
      }
      _system_configurations[0].components[3].name = "dpse";
      _system_configurations[0].components[3].component = DPSE_DiscoveryFactory_get_interface();
      _system_configurations[0].components[3].listener = NULL;
      _system_configurations[0].components[3].property =
              (struct RT_ComponentFactoryProperty*) dpse_properties;

      ...
}

/* Customize the UDP property for the Micro UDP transport component */
struct UDP_InterfaceFactoryProperty *_new_udp_property()
{
    struct UDP_InterfaceFactoryProperty *udp_property =
        (struct UDP_InterfaceFactoryProperty *) malloc(
                      sizeof(struct UDP_InterfaceFactoryProperty));
    memset(udp_property, 0, sizeof(struct UDP_InterfaceFactoryProperty));
    *udp_property = UDP_INTERFACE_FACTORY_PROPERTY_DEFAULT;

    udp_property->disable_auto_interface_config = RTI_TRUE;

    REDA_StringSeq_set_maximum(&udp_property->allow_interface,1);
    REDA_StringSeq_set_length(&udp_property->allow_interface,1);

    *DDS_StringSeq_get_reference(&udp_property->allow_interface,0) =
         DDS_String_dup("loopback");

    if (!UDP_InterfaceTable_add_entry(&udp_property->if_table,
                                      0x7f000001,0xff000000,"loopback",
                                      UDP_INTERFACE_INTERFACE_UP_FLAG))
    {
        printf("Failed UDP table add entry!\n");
        return NULL;
    }

    ...
    return udp_property;
}
6.3.4.3.1.2. Connext Micro ARINC 653 Transport

Configuration of the ARINC 653 transport of Micro requires registration of the RT components for the Port Manager and the ARINC Interface(s).

Micro requires one Port Manager instance for each ARINC 653 partition in order to create APEX ports and manage their sending and receiving of messages.

Also, Micro also requires one ARINC 653 interface instance for each DomainParticipant.

The snippet below configures a Port Manager with one APEX send port and one APEX receive port and an ARINC Interface that uses the aforementioned Port Manager.

/* Example ARINC 653 snippet from a Foo_TSSConfigInterface.hpp */

FACE::Boolean _populate_system_config()
{
    ...
    // ARINC 653 Port Manager
    if (!RTI_TSS_Micro_Component_initialize(&_system_configurations[0].components[5]))
    {
        return false;
    }
    _system_configurations[0].components[5].name = NETIO_DEFAULT_PORTMANAGER_NAME;
    _system_configurations[0].components[5].component = ARINC_PortManagerFactory_get_interface();
    _system_configurations[0].components[5].listener = NULL;
    _system_configurations[0].components[5].property =
          (struct RT_ComponentFactoryProperty*) _new_prtmgr_property();

    // ARINC 653 Transport
    if (!RTI_TSS_Micro_Component_initialize(&_system_configurations[0].components[6]))
    {
      return false;
    }
    _system_configurations[0].components[6].name = NETIO_DEFAULT_ARINC_NAME;
    _system_configurations[0].components[6].component = ARINC_InterfaceFactory_get_interface();
    _system_configurations[0].components[6].listener = NULL;
    _system_configurations[0].components[6].property =
          (struct RT_ComponentFactoryProperty*) _new_arinc_property();

     ...
}

struct ARINC_PortManagerFactoryProperty *_new_prtmgr_property()
{
     struct ARINC_RouteProperty *route_property = NULL;
     struct ARINC_ReceivePortProperty *receive_port_prop = NULL;
     struct ARINC_PortManagerFactoryProperty *port_mgr_prop =
    (struct ARINC_PortManagerFactoryProperty *) malloc(
        sizeof(struct ARINC_PortManagerFactoryProperty));
    memset(port_mgr_prop, 0, sizeof(struct ARINC_PortManagerFactoryProperty));
    *port_mgr_prop = ARINC_PORTMANAGER_FACTORY_PROPERTY_DEFAULT;

    /* Configure synchronous or asynchronous processing.
     * Synchronous (TRUE) will use this Port Manager's receive thread to pass
     * a received message all the way up to the receiving application.
     * Asynchronous (FALSE) will enqueue the message with the ARINC Interface, which
     * then uses its receive thread to pass each message up to the receiving application.
     */
     port_mgr_prop->sync_processing = RTI_TRUE;

    /* Configure APEX process attributes of the Port Manager's receive thread */
    port_mgr_prop->thread_property.port_property.parent.deadline = SOFT;
    port_mgr_prop->thread_property.port_property.parent.period = 100000000;
    port_mgr_prop->thread_property.port_property.parent.time_capacity = 100000000;
    port_mgr_prop->thread_property.stack_size = 32000;
    port_mgr_prop->thread_property.priority = 99;

    #define RTI_PORTMGR_RECV_THREAD_NAME "rti.ARINC.pm.rcv_thrd"
    OSAPI_Memory_zero(&port_mgr_prop->thread_property.port_property.parent.name, MAX_NAME_LENGTH);
    OSAPI_Memory_copy(&port_mgr_prop->thread_property.port_property.parent.name,
        RTI_PORTMGR_RECV_THREAD_NAME,
        OSAPI_String_length(RTI_PORTMGR_RECV_THREAD_NAME));


    /* Configure send routes */
    ARINC_RoutePropertySeq_set_maximum(&port_mgr_prop->send_routes, 1);
    ARINC_RoutePropertySeq_set_length(&port_mgr_prop->send_routes, 1);
    route_property = ARINC_RoutePropertySeq_get_reference(
        &port_mgr_prop->send_routes, 0);

    /* Configure a route from an APEX queuing port sending via a channel
     * with a unique channel identifier.
     */
    #define RTI_APP_SEND_PORT_NAME "Part1_send"
    route_property->channel_identifier = 1;
    route_property->send_port_property.max_message_size = 1024;
    route_property->send_port_property.max_queue_size = 32;
    OSAPI_Memory_copy(route_property->send_port_property.port_name,
        RTI_APP_SEND_PORT_NAME,
        OSAPI_String_length(RTI_APP_SEND_PORT_NAME));

    /* Configure APEX queuing ports to receive */
    ARINC_ReceivePortPropertySeq_set_maximum(&port_mgr_prop->receive_ports,1);
    ARINC_ReceivePortPropertySeq_set_length(&port_mgr_prop->receive_ports,1);
    receive_port_prop = ARINC_ReceivePortPropertySeq_get_reference(
        &port_mgr_prop->receive_ports,0);

    /* Configure an APEX queuing port receiving via a channel with
     * a unique channel identifier. */
    #define RTI_APP_RECV_PORT_NAME "Part1_recv"
    receive_port_prop->channel_identifier = 2;
    receive_port_prop->receive_port_property.max_message_size = 1024;
    receive_port_prop->receive_port_property.max_queue_size = 32;
    receive_port_prop->max_receive_queue_size = 32;
    OSAPI_Memory_copy(receive_port_prop->receive_port_property.port_name,
        RTI_APP_RECV_PORT_NAME,
        OSAPI_String_length(RTI_APP_RECV_PORT_NAME));

    return port_mgr_prop;
}

struct ARINC_InterfaceFactoryProperty *_new_arinc_property()
{
    struct ARINC_InterfaceFactoryProperty *arinc_property =
        (struct ARINC_InterfaceFactoryProperty *)
            malloc(sizeof(struct ARINC_InterfaceFactoryProperty));
    *arinc_property = ARINC_INTERFACE_FACTORY_PROPERTY_DEFAULT;

    /* Configure APEX process attributes of the ARINC Interface's receive thread.
     * Used only when this interface's Port Manager's sync_processing property is FALSE.
     */
    arinc_property->recv_thread.port_property.parent.deadline = SOFT;
    arinc_property->recv_thread.port_property.parent.period = 100000000;
    arinc_property->recv_thread.port_property.parent.time_capacity = 100000000;
    arinc_property->recv_thread.stack_size = 16000;
    arinc_property->recv_thread.priority = 97;

    #define RTI_RECV_THREAD_NAME "rti.ARINC.p2.rcv_thrd"
    OSAPI_Memory_zero(&arinc_property->recv_thread.port_property.parent.name, MAX_NAME_LENGTH);
    OSAPI_Memory_copy(&arinc_property->recv_thread.port_property.parent.name,
                      RTI_RECV_THREAD_NAME,
    OSAPI_String_length(RTI_RECV_THREAD_NAME));

    return arinc_property;
}
6.3.4.3.2. DDS DomainParticipant Configuration

The DomainParticipant configuration configures the properties and QoS of a DDS DomainParticipant. Each Connext TSS Connection relies on DDS entities created within a DomainParticipant, so each Connection configuration itself will use a DomainParticipant configuration (RTI_TSS_DDS_DomainParticipant_Configuration_T.config_name).

Different DomainParticipant configurations can be created to account for any combination of different domain IDs or participant QoS used by your TSS.

A DomainParticipant configuration is represented by RTI_TSS_DDS_DomainParticipant_Configuration_T.

typedef struct RTI_TSS_DDS_DomainParticipant_Configuration
{
    /*name of the Domain Participant Configuration*/
    const char *config_name;

    /*domain_id that the Domain Participant belongs to*/
    DDS_DomainId_t domain_id;

    /*the configure_domain_participant_qos_fn that configures the domain participant qos settings*/
    RTI_TSS_DomainParticipantQosCallback configure_domain_participant_qos_fn;

    /*additional configuration data */
    void *user_data;

    /*pointer to the domain participant listener */
    struct DDS_DomainParticipantListener dp_listener;

    /*listener status mask*/
    DDS_StatusMask dp_listener_status;

#if using Micro
    /*a structure describing the discovery configuration */
    struct RTI_TSS_DiscoveryConfigPlugin discovery_config;
#endif
#if using Pro
    /*a pointer to the qos_library for the Domain Participant*/
    const char *qos_library;

    /*a pointer to the qos_profile for the Domain Participant */
    const char *qos_profile;
#endif
} RTI_TSS_DDS_DomainParticipant_Configuration_T;

A note about DomainParticipants and Topics:

  • Connext TSS will only create a single DomainParticipant for each unique domain. If more than one connection uses the same domain, the DomainParticipant will be reused. A shared DomainParticipant will not be deleted until the last connection using the DomainParticipant is deleted.

  • Similarly, Connext TSS will create a single Topic for each unique domain. Much like the DomainParticipant, the Topic will be reused if more than one connection uses the same topic (in the same domain). A shared Topic will not be deleted until the last connection using the Topic is deleted (as a side effect of destroying the connection).

Note

It is important to know and configure the participant QoS of the first created connection. Because only one DomainParticipant is created for each unique DDS domain, the corresponding participant QoS function of the first created connection will be applied.

6.3.4.3.2.1. Connext Micro DPSE Configuration

Configuration of Dynamic-Participant, Static-Endpoint (DPSE) discovery for Micro requires functions to be called to assert discoverable remote DDS endpoints. To support this, the DomainParticipant configuration has a discovery plugin field (RTI_TSS_DDS_DomainParticipant_Configuration_T.discovery_plugin) to configure the discoverable remote entities for that DomainParticipant.

The snippet below configures DPSE to discover two remote participants, one with a remote publisher and the other with a remote subscriber.

/* Example DPSE snippet from a Foo_TSSConfigInterface.hpp */

FACE::Boolean _populate_domain_config()
{
    if (!RTI_TSS_DDS_DomainParticipant_Configuration_initialize(&_domain_configurations[0]))
    {
        return false;
    }
    _domain_configurations[0].config_name = "publisher_domain";
    ...

#ifdef RTI_CONNEXT_MICRO
    _set_discovery_config(_domain_configurations[0].discovery_config);
    ...
#endif
    ...
}


#ifdef RTI_CONNEXT_MICRO
void _set_discovery_config(struct RTI_TSS_DiscoveryConfigPlugin &cfg)
{
    /* This configuration will be used by those participants using DPSE */
    FACE::UnsignedLong i;
    RTI_TSS_RemoteParticipantEntry_T *current_par_entry = NULL;
    RTI_TSS_RemoteSubscriptionEntry_T *current_sub_entry = NULL;
    RTI_TSS_RemotePublicationEntry_T *current_pub_entry = NULL;
    struct DDS_SubscriptionBuiltinTopicData rem_subscription_data =
        DDS_SubscriptionBuiltinTopicData_INITIALIZER;
    struct DDS_PublicationBuiltinTopicData rem_publication_data =
        DDS_PublicationBuiltinTopicData_INITIALIZER;

    RTI_TSS_DiscoveryConfigPlugin_initialize(&cfg);

    cfg.dpse_component_name = "dpse";

    /* The amount of remote participant_entries that will be asserted */
    cfg.length = 2;
    cfg.participant_entries = (RTI_TSS_RemoteParticipantEntry_T *)
        malloc(sizeof(RTI_TSS_RemoteParticipantEntry_T) * cfg.length);
    memset(cfg.participant_entries, 0, sizeof(RTI_TSS_RemoteParticipantEntry_T));

    /* Configure the remote publisher participant */
    current_par_entry = &cfg.participant_entries[1];
    current_par_entry->participant_name = "publisher";

    current_par_entry->sub_length = 0;

    /* Configure remote publisher */
    current_par_entry->pub_length = 1;
    current_par_entry->publication_entries = (RTI_TSS_RemotePublicationEntry_T *)
        malloc( sizeof(RTI_TSS_RemotePublicationEntry_T) * current_par_entry->pub_length);
    memset(current_par_entry->publication_entries, 0,
        sizeof(RTI_TSS_RemotePublicationEntry_T) * current_par_entry->pub_length);

    rem_publication_data.key.value[DDS_BUILTIN_TOPIC_KEY_OBJECT_ID] = 100;
    rem_publication_data.topic_name = DDS_String_dup("Example FACE_DM_RTI_InteropExample");
    rem_publication_data.type_name = DDS_String_dup("FACE::DM::RTI::InteropExample");
    rem_publication_data.reliability.kind = DDS_RELIABLE_RELIABILITY_QOS;

    current_pub_entry = &current_par_entry->publication_entries[0];
    current_pub_entry->data = rem_publication_data;
    current_pub_entry->key_kind = FACE_DM_RTI_InteropExample_get_key_kind(
        FACE_DM_RTI_InteropExampleTypePlugin_get(), NULL);

    /* Configure the remote subscriber participant */
    current_par_entry = &cfg.participant_entries[0];
    current_par_entry->participant_name = "subscriber";

    current_par_entry->pub_length = 0;

    /* Configure remote subscriber */
    current_par_entry->sub_length = 1;
    current_par_entry->subscription_entries = (RTI_TSS_RemoteSubscriptionEntry_T *)
        malloc(sizeof(RTI_TSS_RemoteSubscriptionEntry_T) * current_par_entry->sub_length);
    memset(current_par_entry->subscription_entries, 0,
        sizeof(RTI_TSS_RemoteSubscriptionEntry_T) * current_par_entry->sub_length);

    rem_subscription_data.key.value[DDS_BUILTIN_TOPIC_KEY_OBJECT_ID] = 200;
    rem_subscription_data.topic_name = DDS_String_dup("Example FACE_DM_RTI_InteropExample");
    rem_subscription_data.type_name = DDS_String_dup("FACE::DM::RTI::InteropExample");
    rem_subscription_data.reliability.kind = DDS_RELIABLE_RELIABILITY_QOS;

    current_sub_entry = &current_par_entry->subscription_entries[0];
    current_sub_entry->data = rem_subscription_data;
    current_sub_entry->key_kind = FACE_DM_RTI_InteropExample_get_key_kind(
        FACE_DM_RTI_InteropExampleTypePlugin_get(), NULL);

}
#endif //RTI_CONNEXT_MICRO
6.3.4.3.3. Type Support Configuration

The Type Support configuration configures the DDS type-specific functions to be used for a specific type identified by a unique name (RTI_TSS_TypeSupport_Configuration.type_name).

Each Type Support configuration holds a pointer to the DDS register_type function for that type (RTI_TSS_TypeSupport_Configuration.register_type_fn).

A Type Support configuration is represented by RTI_TSS_TypeSupport_Configuration_T.

typedef struct RTI_TSS_TypeSupport_Configuration
{
    /*name of the type*/
    const char* type_name;

    /*the register_type_fn that registers the type*/
    RTI_TSS_RegisterTypeFunction register_type_fn;
} RTI_TSS_TypeSupport_Configuration_T;

The snippet below sets up a Type support configuration.

/***** FACE::DM::RTI::HelloWorld *****/
if (!RTI_TSS_TypeSupport_Configuration_initialize(&_type_support_configurations[0]))
{
    return false;
}
_type_support_configurations[0].type_name = "FACE::DM::RTI::HelloWorld";
_type_support_configurations[0].register_type_fn =
        FACE_DM_RTI_HelloWorldTypeSupport_register_type;
6.3.4.3.4. Connection Configuration

The Connection configuration configures a named connection and its underlying DDS entities. The RTI_TSS_Connection_Configuration_t.config_name must match the connection_name parameter of RTI::TSS::Base::Create_Connection().

The connection configuration will use the Type Support configuration identified by the RTI_TSS_Connection_Configuration_t.type_name field.

Each connection’s DDS entities are managed by the DomainParticipant corresponding to the named DomainParticipant configuration (RTI_TSS_Connection_Configuration_t.domain_config_name). The DDS QoS for each DDS entity type (Publisher, Subscriber, DataWriter, DataReader, Topic) are set by separate functions (configure_*_qos_fnc()). The generated files Foo_TSSQosSupport.c and Foo_TSSConfigInterface.hpp together define and assign these functions to the default connection configurations of one publisher and one subscriber per top-level type defined in IDL.

A Connection configuration is represented by RTI_TSS_Connection_Configuration_T.

typedef struct RTI_TSS_Connection_Configuration
{
    /*name of the connection configuration */
    const char *config_name;

    /*name of the domain configuration*/
    const char *domain_config_name;

    /*direction indicates if the connection is a send or receive*/
    FACE_CONNECTION_DIRECTION_TYPE direction;

    /*name of the topic for the connection */
    const char *topic_name;

    /*name of the type for the connection */
    const char *type_name;

    /*the configure_publisher_qos_fn that configures the publisher qos settings */
    RTI_TSS_PublisherQosCallback configure_publisher_qos_fn;

    /*the publisher listener for this connection*/
    struct DDS_PublisherListener publisher_listener;

    /*the statusmask for the publisher listener */
    DDS_StatusMask publisher_listener_status;

    /*the configure_subscriber_qos_fn that configures the subscriber qos settings  */
    RTI_TSS_SubscriberQosCallback configure_subscriber_qos_fn;

    /*the subscriber listener for this connection*/
    struct DDS_SubscriberListener subscriber_listener;

    /*the statusmask for the subscriber listener  */
    DDS_StatusMask subscriber_listener_status;

    /*the configure_topic_qos_fn that configures the topic qos settings  */
    RTI_TSS_TopicQosCallback configure_topic_qos_fn;

    /*the topic listener for this connection */
    struct DDS_TopicListener topic_listener;

    /*the statusmask for the topic listener */
    DDS_StatusMask topic_listener_status;

#if using Pro
    /*name of the content filtered topic*/
    const char *content_filter_topic_name;
    /*the content filter expressionson*/
    const char *content_filter_topic_expr;
    /*the content filter parameters*/
    struct DDS_StringSeq content_filter_topic_params;
#endif

    /*the configure_datawriter_qos_fn that configures the datawriter qos settings  */
    RTI_TSS_DataWriterQosCallback configure_datawriter_qos_fn;

    /*the writer listener for this connection */
    struct DDS_DataWriterListener writer_listener;

    /*the statusmask for the topic listener */
    DDS_StatusMask writer_listener_status;

    /*the configure_datareader_qos_fn that configures the datareader qos settings  */
    RTI_TSS_DataReaderQosCallback configure_datareader_qos_fn;

    /*the reader listener for this connection */
    struct DDS_DataReaderListener reader_listener;

    /*the statusmask for the topic listener */
    DDS_StatusMask reader_listener_status;

    /*additional configuration data */
    void *user_data;

#if using Pro
    /*set the connection to ignore itself */
    DDS_Boolean bi_dir_ignore_self;

    /*pointer to the qos_library for the connection*/
    const char* qos_library;

    /*pointer to the qos_profile for the connection*/
    const char* qos_profile;
#endif
} RTI_TSS_Connection_Configuration_T;

The snippet below configures one connection.

 /* Example Connection snippet from a Foo_TSSConfigInterface.hpp */

static const FACE::UnsignedLong _connection_configurations_length = 2 * 3;
RTI_TSS_Connection_Configuration_T _connection_configurations[_connection_configurations_length];

FACE::Boolean _populate_connection_config()
{
    /***** FACE::DM::RTI::HelloWorld *****/
    if (!RTI_TSS_Connection_Configuration_initialize(&_connection_configurations[0]))
    {
        return false;
    }
    _connection_configurations[0].config_name = "FACE::DM::RTI::HelloWorld publisher";
    _connection_configurations[0].domain_config_name = "domain_0";
    _connection_configurations[0].direction = FACE_SOURCE;
    _connection_configurations[0].topic_name = "Example FACE::DM::RTI::HelloWorld";
    _connection_configurations[0].type_name = "FACE::DM::RTI::HelloWorld";
    _connection_configurations[0].configure_publisher_qos_fn = FACE_DM_RTI_HelloWorld_publisher_qos;
    _connection_configurations[0].configure_topic_qos_fn = FACE_DM_RTI_HelloWorld_topic_qos;
    _connection_configurations[0].configure_datawriter_qos_fn = FACE_DM_RTI_HelloWorld_datawriter_qos;
    _connection_configurations[0].configure_subscriber_qos_fn = NULL;
    _connection_configurations[0].configure_datareader_qos_fn = NULL;
    _connection_configurations[0].user_data = NULL;

#ifndef RTI_CONNEXT_MICRO
    _connection_configurations[0].bi_dir_ignore_self = DDS_BOOLEAN_TRUE;
    _connection_configurations[0].qos_library = "FACE_DM_RTI_HelloWorld_Library";
    _connection_configurations[0].qos_profile = "FACE_DM_RTI_HelloWorld_Profile";
#endif
    ...
}

Note

Per the FACE TSS specification, the QoS for a connection’s entities is established at creation time for that connection and cannot be dynamically modified during communication.

Note

If a bi-directional connection is created (with bi_dir_ignore_self set to TRUE), it will not be able to communicate with other connections in the same instance with the same DomainParticipant configuration. This is because DDS_DomainParticipant_ignore_publication() is called internally when creating a new connection with bi_dir_ignore_self set to TRUE. For more information, See the Connext API Reference.

6.3.4.4. How Configuration Data Is Used

A RTI::Configuration instance is set within a RTI::TSS::Base instance by calling RTI::TSS::Base::Set_Reference(). A subsequent call to RTI::TSS::Base::Initialize() will initialize the Base instance with the configuration data in RTI::Configuration. If this is the first call to RTI::TSS::Base::Initialize(), the system property will be used to allocate the resources for Connext TSS and DDS.

When creating a connection, the connection configuration name (RTI_TSS_Connection_Configuration_t.config_name) matching the connection_name parameter to RTI::TSS::Base::Create_Connection() is used to configure the created connection. Create_Connection() will fail if no matching config_name is found.

6.3.4.5. Extending the Configuration Interface

Users are free to implement the configuration interface as it better suits their need. The only hard requisites are:

  • The Open method should return a handle for each one of the well known container names defined at:

    • RTI_TSS_SYSTEM_CONFIGURATION_CONTAINER_NAME

    • RTI_TSS_DOMAIN_PARTICIPANT_CONFIGURATION_CONTAINER_NAME

    • RTI_TSS_TYPE_SUPPORT_CONFIGURATION_CONTAINER_NAME

    • RTI_TSS_CONNECTION_CONFIGURATION_CONTAINER_NAME

  • The Read method should return, based on the handle returned by Open, a reference to an object of type:

    • RTI_TSS_System_Configuration_T

    • RTI_TSS_DDS_DomainParticipant_Configuration_T

    • RTI_TSS_TypeSupport_Configuration_T

    • RTI_TSS_Connection_Configuration_t

6.3.5. Dynamic Memory Allocation

Connext TSS does not free dynamically allocated memory when built for FACE Safety Base or Safety Extended profiles. Dynamically allocated memory in the generated TSS plugins is also not returned.