6. Capabilities and Interfaces

6.1. Programming Language Support

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

6.2. Operating System Environment

Connext TSS has been implemented using the POSIX API calls prescribed for the applicable conformance profile.

6.3. TSS Capabilities

Connext TSS supports the following capabilities:

  • Transport Service (TS)

  • TSS Distribution

  • TSS Configuration

  • Type Abstraction

  • Transport Protocol Module

Connext TSS generates support code with the included Code Generator (rtiddsgen) tool to enable these capabilities.

6.4. FACE Profiles

Connext TSS officially supports two FACE Compliance profiles, GeneralPurpose and SafetyBase. The RTI_TSS_ENABLE_FACE_COMPLIANCE CMake definition can be set to any FACE TSS profile. However, check the Supported Platforms section for information on which profiles are supported per platform.

6.4.1. GeneralPurpose profile

Connext TSS supports the GeneralPurpose Profile with Connext Micro and Connext Professional. While RTI_TSS_ENABLE_FACE_COMPLIANCE can be set to either GeneralPurpose or None, both are mapped to the GeneralPurpose Profile.

Run the following CMake command to build Connext TSS for the GeneralPurpose Profile with Connext Micro:

cmake -DRTI_CONNEXT_TYPE=micro -DRTI_TSS_ENABLE_FACE_COMPLIANCE=GeneralPurpose -DCMAKE_BUILD_TYPE=Release ../

To build Connext TSS with the GeneralPurpose profile for Connext Professional instead, run the following command:

cmake -DRTI_CONNEXT_TYPE=pro -DRTI_TSS_ENABLE_FACE_COMPLIANCE=GeneralPurpose -DCMAKE_BUILD_TYPE=Release ../

Note

Refer to the Building Connext TSS chapter for a step-by-step guide to building Connext TSS libraries.

6.4.2. SafetyBase profile

Connext TSS also supports the SafetyBase Profile with Connext Micro. While RTI_TSS_ENABLE_FACE_COMPLIANCE can be set to either SafetyExtended, SafetyBase, or Security, all three are mapped to the SafetyBase Profile because Connext TSS does not specifically support SafetyExtended or Security.

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

cmake -DRTI_CONNEXT_TYPE=micro -DRTI_TSS_ENABLE_FACE_COMPLIANCE=SafetyBase -DCMAKE_BUILD_TYPE=Release ../

Note

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

6.4.3. Limitations

Connext TSS built for the SafetyBase profile is different from TSS built for GeneralPurpose. Connext TSS SafetyBase is limited to only using DPSE discovery. For details on DPSE discovery, see the Discovery section of the Connext Micro documentation, located in the doc folder of your Connext Micro installation directory (<RTIMEHOME>).

Connext TSS SafetyBase also does not support multicast, octet, or internal logging. Most importantly, Connext TSS SafetyBase does not support deallocation or reallocation of allocated memory. This limitation extends to all RTI entities and resources.

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

Note

Connext TSS supports user data types in IDL files only. FACE requires that the user data types are in the FACE::DM namespace. In IDL, the module keyword is used to create namespaces for the declaration of types defined within the file. For example:

module FACE {
    module DM {
        struct <user data type> {};
    };
};

Warning

Declaring structs in IDL files outside of the FACE::DM module is not permitted by the FACE technical standard. However, rtiddsgen will not raise an error in this case; it is up to you to ensure that no structs are declared elsewhere.

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 following definitions:

  • the type itself

  • the DataWriter and DataReader code

  • serialization and deserialization code

  • 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 generates both the type support and the type plugin required by Connext TSS.

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

6.6. Type Abstraction Base and Typed interfaces

While the FACE 3.x TSS Type-Specific 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.7. Configuring FACE 3.x for Connext Micro

Connext TSS utilizes the FACE 3.x Configuration Interface. Configure Connext TSS by customizing configuration data within a Configuration Interface instance and then setting its reference with the Type Abstraction or Transport Protocol Module. The Type Abstraction configuration data is XML-based and uses name:value string pairs to handle its configuration elements. The Transport Protocol Module configuration data is used by the lower level DDS product to configure its DDS settings. Transport Protocol Module utilizes configuration names set in the DDS data to handle its configuration.

6.7.1. Type Abstraction configuration implementation

Connext TSS provides example source code for a Configuration Interface. You need to replicate the XML configuration layout in your implementation of the Configuration Interface. Connext TSS provides the tss_configuration_schema.xsd file to validate the configuration XML, as well as an example TSS.xml configuration file.

Warning

The example configuration is provided as a baseline configuration. Modify the example configuration to adjust for your connections and Transport Protocol Modules.

If you already have a configuration UoC implemented, the following section describes the expected string format for the byte_array returned by Read_Configuration(). For example, guid:5; could be used as a configuration string for registering a Transport Protocol Module with Type Abstraction. A configuration string for a connection could be, for example, tpm:5;tpm:5;tpm:7;.

For each Transport Protocol Module to be registered with the Type Abstraction, the Type Abstraction expects to receive a configuration string in the returned buffer in the format guid:<TPM GUID> when Read_Configuration() is called to load a list of Transport Protocol Modules to be injected. For example, guid:5;. For each connection to be created with the Type Abstraction, the Type Abstraction expects a configuration string in the returned buffer in the format tpm:<First TPM GUID>;tpm:<Second TPM GUID>;...tpm:<Nth TPM GUID>;, where each Transport Protocol Module that is going to participate on the connection needs to be identified by its GUID. For example, tpm:5;tpm:5;tpm:7;.

6.7.1.1. Type Abstraction XML configuration elements

Connext TSS includes a set of XML elements that are used to configure the Type Abstraction. The xsd for those elements can be found in <RTITSSHOME>/resource/configuration/schema/tss_configuration_schema.xsd. The elements are:

  • configs

    This is the root element of the configuration file. It contains all the configuration elements for a system.

    • config

      This element contains all the configuration data for a specific configuration. It has a required name attribute that is used to identify the configuration. This attribute must match what is passed into the Configuration_Open() call in the code. It contains the following elements:

      • connections

        This element contains a list of the connections for a specific configuration.

        • connection

          This element contains the connection configuration details for a specific connection in the Type Abstraction. It has a required name attribute that is used to identify the connection. The name attribute must match what is passed into the Create_Connection call in the code. It contains the following elements:

          • tpms

            This element contains a list of the Transport Protocol Modules for a specific connection.

            • tpm

              This element contains Transport Protocol Module information for the specific connection. It has a required guid attribute that is used to identify the Transport Protocol Module. The guid attribute must match a Transport Protocol Module guid in the list of Transport Protocol Modules under the tpms element under the config element

      • tpms

        This element contains the Transport Protocol Modules for a specific configuration.

        • tpm

          This element contains the configuration data for a specific Transport Protocol Module that’s needed by the Type Abstraction. It has required guid and name attributes that are used to identify the Transport Protocol Module. Each name and guid must be unique per configuration.

      • types

        This element contains a list of the types for a specific configuration.

        Note

        The types element is not used in the current implementation of Connext TSS.

        • type

          This element contains all the type elements for a specific type. It has a required name attribute that is used to identify the type.

6.7.2. Transport Protocol Module configuration implementation

Transport Protocol Module provides example source code for a Configuration Interface. You need to copy the HelloWorld example configuration for your language and implement the FACE::Configuration interface (if using C++) or the FACE_Configuration interface (if using C) and the interface’s Configuration Services API. Examples and descriptions of what is required are in the following sections.

Warning

The example configuration is provided as a baseline configuration. You will need to modify the example Configuration Interface implementations and QoS functions.

6.7.3. OSAPI configuration for Connext Micro

When building Transport Protocol Module 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 example code calls the _set_osapi_property function, followed by the DDS_DomainParticipantFactory_get_instance function.

Example OSAPI configuration in C++:

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;
}

Example OSAPI configuration in C:

FACE_interface_return FACE_Configuration_Initialize(
                struct FACE_Configuration* this_obj,
                const FACE_Configuration_INITIALIZATION_TYPE *initialization_information,
                FACE_RETURN_CODE_TYPE *return_code)
 {
      if(this_obj == NULL)
      {
          return FACE_INTERFACE_NULL_PARAM;
      }

      UNUSED_ARG(initialization_information);

      if (_initialized)
      {
          *return_code = FACE_RETURN_CODE_TYPE_NO_ERROR;
          return FACE_INTERFACE_NO_ERROR;
      }

      #ifdef RTI_CONNEXT_MICRO

          /* OSAPI needs to be set up before it can be used. OSAPI
          should only be called once by the program otherwise undefined
          behavior may occur.*/
          if (!_set_osapi_property())
          {
              *return_code = FACE_RETURN_CODE_TYPE_INVALID_CONFIG;
              return FACE_INTERFACE_NO_ERROR;
          }

          DDS_DomainParticipantFactory *factory = NULL;
          factory = DDS_DomainParticipantFactory_get_instance();
          if (factory == NULL)
          {
              *return_code = FACE_RETURN_CODE_TYPE_INVALID_CONFIG;
              return FACE_INTERFACE_NO_ERROR;
          }
          /* If and only if the set up above is successful,
          *  OSAPI can 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 FACE_INTERFACE_NO_ERROR;
      }

      _initialized = DDS_BOOLEAN_TRUE;
      *return_code = FACE_RETURN_CODE_TYPE_NO_ERROR;
          return FACE_INTERFACE_NO_ERROR;
  }

To modify the OSAPI properties, you can override the _set_osapi_property function instead of creating a plugin function, then pass it to the now-deprecated RTI_TSS_System_Configuration.configure_osapi_system_property_fn Connext Micro function (this function does nothing by default).

Use the following steps to override the _set_osapi_property function:

  1. Create a variable of struct OSAPI_SystemProperty and initialize it with OSAPI_SystemProperty_INITIALIZER.

  2. Get the current OSAPI properties by passing the variable to the OSAPI_System_get_property function.

  3. Modify the OSAPI_SystemProperty variable. Notable properties include:

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

6.7.4. Configuration data types

The configuration data types of the RTI::Configuration class are defined in the file code/C/include/RTI/TSS/Configuration.h. They are organized in the following 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_Channel_Configuration_t)

6.7.4.1. System configuration

The system configuration sets system properties for Transport Protocol Module and the underlying DDS factories. It is applied upon the very first call, idempotently, to FACE::TSS::TPM_TPMTS::Initialize() (if using C++) or FACE_TSS_TPM_TPMTS_Initialize() (if using C). A system configuration is represented by RTI_TSS_System_Configuration_T.

Example system configuration in C/C++:

/*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 configures 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_channels;

    /*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 example 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 in C++:

/* 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";
    ...
}

Example system configuration snippet in C:

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

static const FACE_unsigned_long _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.7.4.1.1. Connext Micro RT components

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

Because the property of each component is complicated to set, the example 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 in C++:

/* 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;
}

Example in C:

/* 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.7.4.1.2. Connext Micro ARINC 653 transport

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

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

Connext 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 in C++:

/* 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;
}

Example ARINC 653 snippet in C:

/* 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.7.4.2. DDS DomainParticipant configuration

The DomainParticipant configuration sets the properties and QoS of a DDS DomainParticipant. Each Transport Protocol Module 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 DomainParticipant QoS used by your TSS installation.

A DomainParticipant configuration is represented by RTI_TSS_DDS_DomainParticipant_Configuration_T.

Example configuration in C:

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:

  • Transport Protocol Module 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, Transport Protocol Module 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 result of destroying the connection).

Note

It is important to know and configure the DomainParticipant 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.7.4.2.1. Connext Micro DPSE configuration

Configuration of Dynamic-Participant, Static-Endpoint (DPSE) discovery for Connext 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 following snippets configure DPSE to discover two remote DomainParticipants, one with a remote publisher and the other with a remote subscriber.

Example DPSE configuration snippet in C++:

/* 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

Example DPSE configuration snippet in C:

/* 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_unsigned_long 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.7.4.3. Type Support configuration

The Type Support configuration sets 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 following snippets set up a Type Support configuration.

Example in C++:

/***** 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;

Example in C:

/***** 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.7.4.4. Connection configuration

The Connection configuration sets a named connection and its underlying DDS Entities. The RTI_TSS_Channel_Configuration_t.config_name must match the connection_name parameter of FACE::TSS::TPM::TPMTS::Open_Channel (if using C++) or FACE_TSS_TPM_TPMTS_Open_Channel (if using C).

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

Each connection’s DDS Entities are managed by the DomainParticipant corresponding to the named DomainParticipant configuration (RTI_TSS_Channel_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()).

A Connection configuration is represented by RTI_TSS_Channel_Configuration_T.

Example Connection configuration in C:

typedef struct RTI_TSS_Channel_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_Channel_Configuration_T;

The following snippets configure one connection.

Example configuration in C++:

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

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

FACE::Boolean _populate_connection_config()
{
    /***** FACE::DM::RTI::HelloWorld *****/
    if (!RTI_TSS_Channel_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
    ...
}

Example configuration in C:

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

static const FACE_unsigned_long _connection_configurations_length = 2 * 3;
RTI_TSS_Channel_Configuration_T _connection_configurations[_connection_configurations_length];

FACE_boolean _populate_connection_config()
{
    /***** FACE_DM_RTI_HelloWorld *****/
    if (!RTI_TSS_Channel_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, refer to the API Reference in the Connext Micro User’s Manual located in your Connext Micro installation directory (<RTIMEHOME>\doc).

6.7.5. How configuration data is used in C++

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

When creating a connection, the connection configuration name (RTI_TSS_Channel_Configuration_t.config_name) matching the connection_name parameter to FACE::TSS::TPM::TPMTS_impl::Open_Channel() is used to configure the created connection. Open_Channel() will fail and return a FACE::RETURN_CODE_TYPE::Value::INVALID_CONFIG``if no matching ``config_name is found.

6.7.6. How configuration data is used in C

An RTI_Configuration instance is set within an RTI_TPM instance by calling Configuration_Injectable_Set_Reference() with an RTI_TPM cast as a FACE_Configuration_Injectable *. A subsequent call to FACE_TSS_TPM_TPMTS_Initialize() initializes the TPM instance with the configuration data in RTI_Configuration. If this is the first call to FACE_TSS_TPM_TPMTS_Initialize(), the system property will be used to allocate the resources for Transport Protocol Module and DDS.

When creating a connection, the connection configuration name (RTI_TSS_Channel_Configuration_t.config_name) matching the connection_name parameter to FACE_TSS_TPM_TPMTS_Open_Channel() is used to configure the created connection. Open_Channel() will fail and return a FACE_RETURN_CODE_TYPE_INVALID_CONFIG error if no matching config_name is found.

6.7.7. Extending the configuration interface

You are required to implement the configuration to suit your needs. You can copy the HelloWorld example configuration for your language as a starting point and working reference. The only hard requisites are:

  • All FACE Configuration functions must be implemented.

  • In a C FACE Configuration instance, the following variables must be defined in the Configuration Implementation provided to FACE_TSS_Base:

    /*ce \dref_RTI_TSS_SYSTEM_CONFIGURATION_CONTAINER_NAME */
    FACE_string RTI_TSS_SYSTEM_CONFIGURATION_CONTAINER_NAME;
    
    /*ce \dref_RTI_TSS_DOMAIN_PARTICIPANT_CONFIGURATION_CONTAINER_NAME */
    FACE_string RTI_TSS_DOMAIN_PARTICIPANT_CONFIGURATION_CONTAINER_NAME;
    
    /*ce \dref_RTI_TSS_TYPE_SUPPORT_CONFIGURATION_CONTAINER_NAME */
    FACE_string RTI_TSS_TYPE_SUPPORT_CONFIGURATION_CONTAINER_NAME;
    
    /*ce \dref_RTI_TSS_CONNECTION_CONFIGURATION_CONTAINER_NAME */
    FACE_string RTI_TSS_CHANNEL_CONFIGURATION_CONTAINER_NAME;
    

    The above variables must also be initialized as follows:

    FACE_string_init_managed_cstring(&RTI_TSS_SYSTEM_CONFIGURATION_CONTAINER_NAME, "RTI_TSS_SYSTEM_CONFIGURATION");
    FACE_string_init_managed_cstring(&RTI_TSS_DOMAIN_PARTICIPANT_CONFIGURATION_CONTAINER_NAME, "RTI_TSS_DOMAIN_PARTICIPANT_CONFIGURATION");
    FACE_string_init_managed_cstring(&RTI_TSS_TYPE_SUPPORT_CONFIGURATION_CONTAINER_NAME, "RTI_TSS_TYPE_SUPPORT_CONFIGURATION");
    FACE_string_init_managed_cstring(&RTI_TSS_CHANNEL_CONFIGURATION_CONTAINER_NAME, "RTI_TSS_CONNECTION_CONFIGURATION");
    
  • 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_CHANNEL_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_Channel_Configuration_t

6.8. Configuring FACE 3.x for Connext Professional

6.8.1. Connext Professional data types

The Connext Professional Transport Protocol Module uses XML-based application creation to handle configuration. Refer to the XML-Based Application Creation Getting Started Guide and Configuring QoS with XML for more information on this process.

There is an external hash table in rti_connext_tss_<version>/code/C/include/pro/Configuration_Pro.h that contains the list of types and type sizes that Connext Professional will use during the lifetime of the application.

6.8.2. Registering data types

In a C FACE Configuration instance, you must register all types to be transmitted in a message with the DomainParticipantFactory in the configuration_initialization call, as shown in the following code snippet:

DDS_ReturnCode_t retCode = DDS_DomainParticipantFactory_register_type_support(
        DDS_TheParticipantFactory,
        FACE_DM_HelloWorldTypeSupport_register_type,
        "FACE::DM::RTI::HelloWorld");
    if (retCode != DDS_RETCODE_OK) {
        // Handle error
    }

You must also register all types and their sizes with the types hash table in the configuration_initialization call, as shown below:

ht_result = types_hash_table_add_entry("FACE::DM::RTI::HelloWorld", sizeof(FACE_DM_HelloWorld)); /* Replace with actual size */
if (ht_result != HT_NO_ERROR)
{
    *return_code = FACE_RETURN_CODE_TYPE_RESOURCE_LIMIT_REACHED;
    return FACE_INTERFACE_INSUFFICIENT_MEMORY;
}

When reading a configuration, the string passed in must be matched to a DataReader/DataWriter configuration (found in XML app creation) passed into the system as a reference.

The configuration_read call acts as a lookup table that returns the fully-qualified DDS entity name (from XML QoS) for a given connection name. The FACE TSS uses these names to locate and create DDS DomainParticipants, Publishers, Subscribers, DataReaders, and DataWriters.

For Connext Pro integrations using XML Application Creation, these entities must be auto-created from the XML configuration before channel I/O starts.

6.8.3. Adding new participants

  1. Add a new if() block checking for your DomainParticipant name:

    if(0 == strcmp(FACE_string_buffer(set_name), "your_participant_name"))
    
  2. Clear the buffer and copy the DomainParticipant’s fully-qualified name from your XML:

    *bytes_read = 0;
    memset(buffer, '\0', buffer_size);
    strcat(buffer, "YourLibraryName::YourParticipantName\0");
    *bytes_read += strlen(buffer);
    
  3. Add goto done; to complete the lookup.

6.8.4. Adding new DataReaders and DataWriters

  1. Add a new if() block checking for your connection name:

    if(0 == strcmp(FACE_string_buffer(set_name), "your_connection_name"))
    
  2. Copy the full path to the DataReader or DataWriter from your XML QoS:

    *bytes_read = 0;
    memset(buffer, '\0', buffer_size);
    strcat(buffer, "YourParticipant::YourSubscriber::YourDataReader\0");
    *bytes_read += strlen(buffer);
    
  3. Add goto done;.

6.8.5. Example

To add a new “Temperature” data type with Publish/Subscribe DomainParticipants and a DataReader:

if(0 == strcmp(FACE_string_buffer(set_name), "temp_pub_participant"))
{
    *bytes_read = 0;
    memset(buffer, '\0', buffer_size);
    strcat(buffer, "TempLibrary::TempPublisher\0");
    *bytes_read += strlen(buffer);

    goto done;
}

if(0 == strcmp(FACE_string_buffer(set_name), "temp_receive"))
{
    *bytes_read = 0;
    memset(buffer, '\0', buffer_size);
    strcat(buffer, "TempLibrary::TempSubscriber::TempReader\0");
    *bytes_read += strlen(buffer);
    goto done;
}

Note

Connection names (set_name in the code snippets above) must match what your application passes to FACE_TSS_Create_Connection(). Entity names must match your XML QoS profile exactly.

Note

Currently, the Connext Professional Transport Protocol Module does not accept explicitly-defined XML app creation files (see Loading XML Configuration Files) and instead looks for a USER_QOS_PROFILES.xml in the application runtime area.

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

6.10. Sequence Element Lifecycle Operations

The FACE Technical Standard defines sequences as generic containers but does not prescribe a mechanism for sequences to perform deep cleanup or deep copy of their elements. This means that when a sequence contains elements of a complex type (such as strings or nested structures that allocate internal memory), freeing the sequence’s buffer alone does not release the memory owned by individual elements. This results in memory leaks.

6.10.1. C++ API

The C++ FACE::Sequence<T> template class handles element lifecycle automatically through C++ language features. No additional configuration is required:

  • Destruction: The sequence destructor calls each element’s destructor via FACE::internal::sequence_helper<T>::destroy_elements().

  • Deep copy: Copy construction and assignment use FACE::internal::sequence_helper<T>::copy_elements(), which invokes each element’s copy constructor via placement new.

  • Specializations: FACE::String, nested FACE::Sequence<T>, primitive types, and fixed-size arrays each have their own sequence_helper specialization with appropriate behavior.

For custom C++ types used as sequence elements, simply ensure your type has a proper copy constructor and destructor. The FACE::Sequence<T> template will invoke them automatically.

6.10.2. C API

The C API does not have constructors or destructors, so Connext TSS provides optional element lifecycle operations on the FACE_sequence type. Each sequence instance has an ops field with two function pointers:

typedef void (*FACE_sequence_clean_elem_fn)(void *elem);
typedef void (*FACE_sequence_copy_elem_fn)(void *dst_elem,
                                           const void *src_elem,
                                           FACE_unsigned_long type_size);

typedef struct {
    FACE_sequence_copy_elem_fn  copy_elem_fn;
    FACE_sequence_clean_elem_fn clean_elem_fn;
} FACE_sequence_ops;

When set, the sequence implementation calls clean_elem_fn on each element during FACE_sequence_free() and FACE_sequence_clear(), and calls copy_elem_fn during FACE_sequence_append(), FACE_sequence_append_elem(), FACE_sequence_init_managed_copy(), and FACE_sequence_init_managed_data() to perform deep copies. When these function pointers are NULL (the default), sequences behave as before (shallow memset/memcpy).

6.10.3. Move Semantics in FACE_sequence_append_elem

When copy_elem_fn is set, FACE_sequence_append_elem() uses move semantics: after deep-copying the source element into the sequence buffer, it automatically calls clean_elem_fn on the source to release its internal resources. This ensures that:

  • Existing code that does not free the source after appending will not leak memory — the source’s resources are consumed by the append operation.

  • New code that explicitly frees the source after appending remains safe — cleaning an already-cleaned element is a no-op.

FACE_sequence inner;
memset(&inner, 0, sizeof(FACE_sequence));
FACE_sequence_init_managed_bounded(&inner, sizeof(FACE_long), 10);
/* ... populate inner ... */

/* append_elem deep-copies inner into outer_seq, then cleans the source */
FACE_sequence_append_elem(&outer_seq, &inner, sizeof(FACE_sequence));

/* inner's internal buffer has been released — no leak even without
 * an explicit FACE_sequence_free(&inner) call. Calling free is still
 * safe but not required. */

Note

FACE_sequence_append() (which appends an entire sequence) does not apply move semantics to the source sequence — only individual element appends via FACE_sequence_append_elem() consume the source.

6.10.4. Enabling Sequence Element Operations

This feature is controlled by the LIFECYCLE_CLASS_EXT CMake option, which defaults to ON. When enabled, the LIFECYCLE_CLASS_EXT preprocessor macro is defined automatically during the build.

To explicitly disable this feature, pass -DLIFECYCLE_CLASS_EXT=OFF when configuring:

cmake -DLIFECYCLE_CLASS_EXT=OFF ...

When LIFECYCLE_CLASS_EXT is enabled (the default), the generated type plugin code (produced by rtiddsgen) automatically assigns the appropriate clean_elem_fn and copy_elem_fn for each sequence-of-user-type member during the type’s _initialize function. No application-level code changes are required.

With LIFECYCLE_CLASS_EXT set to OFF, the generated code compiles identically to previous releases (no ops assigned, shallow behavior).

Warning

Disabling LIFECYCLE_CLASS_EXT restores shallow sequence element behavior for C sequence elements that own internal resources. In this mode, application-level memory leaks can occur unless your code explicitly performs equivalent deep cleanup and deep-copy handling.

6.10.5. RTI FACE_string Reference Implementation

Connext TSS provides a reference implementation of the element lifecycle operations for the FACE_string type in FACE/string_ext.h and FACE/string_ext.c:

#include "FACE/string_ext.h"

/* Releases all resources held by a FACE_string element */
void FACE_string_seq_clean_elem(void *elem);

/* Deep-copies a FACE_string element (initializes dst, copies content) */
void FACE_string_seq_copy_elem(void *dst_elem,
                                const void *src_elem,
                                FACE_unsigned_long type_size);

These are automatically assigned by the generated code when LIFECYCLE_CLASS_EXT is ON (the default). They serve as the canonical example for implementing element operations for any custom type.

6.10.6. Generated Element Operations

When LIFECYCLE_CLASS_EXT is ON, the rtiddsgen code generator automatically produces <TypeName>_seq_clean_elem and <TypeName>_seq_copy_elem functions for every struct, union, and typedef defined in your IDL. These generated functions call the type’s _finalize and _copy functions respectively. No additional implementation is required for IDL-defined types.

The generated type plugin also assigns these functions to the ops field of each sequence member during the type’s _initialize function.

6.10.7. Implementing Element Operations for Custom Types

If you have a hand-written C type (not generated from IDL) that holds internal resources (allocated memory, file handles, etc.) and that type appears as a sequence element, you must implement two functions following this exact naming convention:

void <TypeName>_seq_clean_elem(void *elem);
void <TypeName>_seq_copy_elem(void *dst_elem,
                               const void *src_elem,
                               FACE_unsigned_long type_size);

Where <TypeName> is the fully-qualified C type name with :: replaced by _ (matching the name used in the generated code). For example, if your IDL defines module MyCompany { struct Widget { ... }; };, the type name in C is MyCompany_Widget and the functions must be named MyCompany_Widget_seq_clean_elem and MyCompany_Widget_seq_copy_elem.

Requirements for <TypeName>_seq_clean_elem:

  1. The function receives a void* pointer to a single element within the sequence buffer.

  2. Cast the pointer to your concrete type: MyType *typed = (MyType *)elem;

  3. If the pointer is not NULL, release all internal resources held by that element (free allocated memory, close handles, etc.).

  4. Do not free the element pointer itself — it is part of the sequence’s contiguous buffer.

Example:

void MyCompany_Widget_seq_clean_elem(void *elem)
{
    MyCompany_Widget *widget = (MyCompany_Widget *)elem;
    if (widget != NULL)
    {
        /* Release internal resources */
        free(widget->name_buffer);
        widget->name_buffer = NULL;
        widget->name_length = 0;
    }
}

Requirements for <TypeName>_seq_copy_elem:

  1. The function receives a void* destination pointer, a const void* source pointer, and the element type_size (which may be ignored if your type is fixed-size or you already know it).

  2. Cast both pointers to your concrete type.

  3. Finalize the destination element first (it may contain live resources from a previous value that must be released to avoid leaks), then initialize it to a clean state.

  4. Perform a deep copy of the source element’s content into the initialized destination.

  5. The destination may also be uninitialized memory (e.g., a freshly allocated buffer slot). Your implementation must handle both cases — finalize on uninitialized/zeroed memory should be a safe no-op.

Example:

void MyCompany_Widget_seq_copy_elem(
        void *dst_elem,
        const void *src_elem,
        FACE_unsigned_long type_size)
{
    (void)type_size;
    MyCompany_Widget *dst = (MyCompany_Widget *)dst_elem;
    const MyCompany_Widget *src = (const MyCompany_Widget *)src_elem;

    /* Finalize destination (releases any existing resources; safe on
     * zeroed/uninitialized memory since name_buffer will be NULL) */
    free(dst->name_buffer);

    /* Initialize destination to a clean state */
    memset(dst, 0, sizeof(MyCompany_Widget));

    /* Deep copy: allocate and copy the name buffer */
    if (src->name_buffer != NULL && src->name_length > 0)
    {
        dst->name_buffer = malloc(src->name_length + 1);
        if (dst->name_buffer != NULL)
        {
            memcpy(dst->name_buffer, src->name_buffer, src->name_length);
            dst->name_buffer[src->name_length] = '\0';
            dst->name_length = src->name_length;
        }
    }
    dst->id = src->id;
}

Linking: For hand-written (non-IDL) types, ensure that the object file or library containing your _seq_clean_elem and _seq_copy_elem implementations is linked into your final application. For IDL-defined types, these functions are generated automatically in the type plugin source file.

Summary of the approach:

  • The FACE specification does not define a mechanism for element-level resource

    management within sequences. This is a known gap that leads to memory leaks for sequences of types that own dynamically allocated resources.

  • Connext TSS extends the sequence with optional ops function pointers that,

    when set, enable proper deep cleanup and deep copy at the element level. This is a Connext TSS extension specifically implemented to address the above FACE specification shortcoming.

  • The LIFECYCLE_CLASS_EXT CMake option (defaulting to ON) controls this

    extension. Setting it to OFF disables the feature and does not affect binary compatibility or compliance, but can reintroduce memory leak risk for sequence element types that allocate internal resources.

  • For IDL-defined types, the generated type plugin code produces both the lifecycle functions and the ops assignments automatically — no manual implementation is required.

  • For hand-written C types, implementors provide the two lifecycle functions following the documented naming convention.