RTI Routing Service: How to create a transformation plugin

RTI Routing Service provides a transformation SDK in C to support the creation of custom transformation plugins. A route can be created by RTI Routing Service. This route transforms the incoming data, using this custom transformation plugin, and then republishes the updated data.

This article will be based on an example provided by RTI rti_workspace/<version>/examples/routing_service/shapes:

  • topic_bridge_w_custom_transf.xml is the XML Configuration for RTI Routing Service.
  • transformation/src/shapestransf.c is the transformation plugin code, which will be compiled and loaded dynamically.
  • transformation/make: folder with the makefiles.
  • transformation/windows: folder with the Visual Studio project.

Steps to create a transformation plugin

  1. Implement the transformation plugin API and generate a shared library.
  2. Register that library in the configuration file by creating a <transformation_plugin> tag inside the <transformation_library> tag.
  3. Instantiate a transformation by creating a <transformation> tag inside the <topic_route> tag.

1. Implement the transformation plugin API

The transformation plugin API is documented here for the current release and here for release 5.3.0. (In 5.3.0 it was only supported in C.) 

Through this SDK, you can create a custom transformation plugin. Here is a UML diagram of the plugin model:

Once the transformation plugin is implemented, it is necessary to compile it through makefiles or Visual Studio.

Every transformation plugin will implement the following functions:

  • Create transformation plugin:  

    typedef struct RTI_RoutingServiceTransformationPlugin *(*RTI_RoutingServiceTransformationPlugin_create)(
            const struct RTI_RoutingServiceProperties * properties,
            RTI_RoutingServiceEnvironment * env);
      
  • Delete the transformation plugin:

    typedef void (*RTI_RoutingServiceTransformationPlugin_delete)(
            struct RTI_RoutingServiceTransformationPlugin * plugin,
            RTI_RoutingServiceEnvironment * env);
  • Create a new transformation:

    typedef RTI_RoutingServiceTransformation(*RTI_RoutingServiceTransformationPlugin_create_transformation)(
            struct RTI_RoutingServiceTransformationPlugin * plugin,
            const struct RTI_RoutingServiceTypeInfo * input_type_info,
            const struct RTI_RoutingServiceTypeInfo * output_type_info,
            const struct RTI_RoutingServiceProperties * properties,
            RTI_RoutingServiceEnvironment * env);     
  • Delete a transformation:

    typedef void (*RTI_RoutingServiceTransformationPlugin_delete_transformation)(
            struct RTI_RoutingServiceTransformationPlugin * transformationPlugin,
            RTI_RoutingServiceTransformation transformationObject,
            RTI_RoutingServiceEnvironment * env);
  • Transform input data into output data:
    • outSampleLst: it is possible to modify the Stream sample.
    • outInfoLst: It is also possible to modify the DDS_SampleInfo, which is the information that accompanies each sample.
    • out_count: number of samples to write by RTI Routing Service. 
    typedef void (*RTI_RoutingServiceTransformation_transform)(
            RTI_RoutingServiceTransformation transformation,
            RTI_RoutingServiceSample ** out_sample_lst,
            RTI_RoutingServiceSampleInfo ** out_info_lst,
            unsigned int * out_count,
            RTI_RoutingServiceSample * in_sample_lst,
            RTI_RoutingServiceSampleInfo * in_info_lst,
            unsigned int in_count,
            RTI_RoutingServiceEnvironment * env);
  • Return loan, indicates that Routing Service is done with the data:

    typedef void (*RTI_RoutingServiceTransformation_return_loan)(
           RTI_RoutingServiceTransformation transformation,
           RTI_RoutingServiceSample * sample_lst,
           RTI_RoutingServiceSampleInfo * info_lst,
           unsigned int count,
           RTI_RoutingServiceEnvironment * env);
  • Update, called as a result of the remote update command:

    typedef void (*RTI_RoutingServiceTransformation_UpdateFcn)(
           RTI_RoutingServiceTransformation transformation,
           const struct RTI_RoutingServiceProperties * properties,
           RTI_RoutingServiceEnvironment * env);

1.1 Implement a transformation between different DataType

In the case of creating a transformation between two different DataTypes is fundamental to take account of:

  • Define the types in the Transformation Structure:

    struct TestTransformation {
        /*
         * @brief A reference to the plugin
         */
        struct TestTransformationPlugin * _plugin;
    
        const struct DDS_TypeCode * TestA;
        const struct DDS_TypeCode * TestB;
    };
        
  • Create the dynamic data output from the type and set the value:

    struct DDS_DynamicData * output = DDS_DynamicData_new(
            self->TestB,
            &DDS_DynamicDataProperty_t_INITIALIZER);
    /* Set the values to the output sample */
    retCode = DDS_DynamicData_set_long(
            output,
            "my_long1",
            DDS_DYNAMIC_DATA_MEMBER_ID_UNSPECIFIED,
            my_long1);
            

Now it is not possible to create the output from the input sample because they are different dataType.

 retCode = DDS_DynamicData_copy(output, input);//Cannot use it for different types

2. Register Transformation library configuration

The transformation plugin should be provided in the XML configuration file a shared library to RTI Routing Service, which will be loaded dynamically. To register a transformation plugin with RTI Routing Service, you must use the tag <transformation_plugin> within <transformation_library>.

A transformation plugin is defined in the transformation_library tag in the XML Configuration file:

  • dll: Path to the shared library containing the implementation of the transformation plugin.
  • create_function: Name of the function used to create the transformation plugin, which is implemented in the shared library.

The example provided by RTI in topic_bridge_w_custom_transf.xml:

    <dds>
       <transformation_library name="MyTransformationLib">
            <transformation_plugin name="ShapesTransformation">
            <dll>
                $(RTI_EXAMPLES_DIR)/routing_service/shapes/transformation/$(RTI_SHARED_LIB_PREFIX)shapestransf$(RTI_SHARED_LIB_SUFFIX)
            </dll>
            <create_function>ShapesTransformationPlugin_create</create_function>
            </transformation_plugin>
        </transformation_library>
        ...
        <routing_service>
        ...
        </routing_service>
        ...
    </dds>

3. Create a transformation through a topic_route

Once a transformation plugin is registered, a route can use it to create a data transformation. In our example provided in the topic_bridge_w_custom_transf.xml, the following route uses a transformation to set a value in the x-coordinate of a ShapeType.  

<topic_route name="Squares">
   <input participant="1">
       <registered_type_name>ShapeType</registered_type_name>
       <topic_name>Square</topic_name>
   </input>
   <output>
       <topic_name>Square</topic_name>
       <registered_type_name>ShapeType</registered_type_name>
   </output>
   <transformation plugin_name="MyTransformationLib::ShapesTransformation">
       <property>
           <value>
               <element>
                   <name>x</name>
                   <value>50</value>
               </element>
           </value>
       </property>
   </transformation>
</topic_route>

4. Running the example

We can see how the transformation is done executing the example:

All the steps are here for the current release or here for release 5.3.0.   

$NDDSHOME/bin/rtiroutingservice -cfgFile topic_bridge_w_custom_transf.xml -cfgName example
./objs/x64Linux3gcc5.4.0/ShapeType_subscriber
./objs/x64Linux3gcc5.4.0/ShapeType_publisher