Modern c++ interface: How to lookup a topic and create DataReader from PublicationBuiltinTopicData

12 posts / 0 new
Last post
rgs
Offline
Last seen: 2 days 11 hours ago
Joined: 07/26/2022
Posts: 7
Modern c++ interface: How to lookup a topic and create DataReader from PublicationBuiltinTopicData

Hi all,

I'm trying to write a monitoring tool that discovers topics dynamically and can create custom alerts.
Hence, I can't use the normal template class Topic<T>, DataReader<T> to create my listener.
So I've created a listener to catch PublicationBuiltinTopicData.
My problem is that I can't get the topic from it.
I know the PublicationBuiltinTopicData gives me
-the topic_name(),
-type() gives me a dds::core::xtypes::DynamicType.

But how do I lookup my topic from that info?
I don't want to create a Topic<T> right, because the topic should already exist.
So how do I retrieve the Topic<dds::core::xtypes::DynamicData> from the DynamicType and then create my DataReader?

I've been through the api docs for days and all over community forum and I can't find any example for how to do this.
Can someone please post code for how to lookup my topic.

Thanks,
rgs

void MyApp::processTopicData(const dds::topic::PublicationBuiltinTopicData& topic)
{
    dds::sub::qos::DataReaderQos ReaderQos = dds::core::QosProvider::Default().datareader_qos();
    ReaderQos << dds::core::policy::Durability::Volatile();
    ReaderQos << dds::core::policy::Reliability::Reliable();

    dds::sub::find(m_ValidSubscriber, topic_name,)
    auto topic_type = topic->type();
    dds::core::xtypes::DynamicData data_type();
.
    dds::topic::Topic<dds::core::xtypes::DynamicData> Topic = dds::core::null;
    // ???
    // how to lookup the topic from the type
    // ???
          
    // create a contentfilter to catch data
    dds::topic::ContentFilteredTopic<dds::core::xtypes::DynamicData> cft = dds::core::null;
    try
    {
        cft = dds::topic::ContentFilteredTopic<dds::core::xtypes::DynamicData>(
            Topic,
            timer->name + "::raise",
                    dds::topic::Filter("action = 'start'"));

    }
    catch(std::exception& e)
    {
        continue;
    }

    dds::sub::DataReader<dds::core::xtypes::DynamicData> reader = dds::core::null;
    reader = dds::sub::DataReader<dds::core::xtypes::DynamicData>(m_ValidSubscriber, cft, ReaderQos);
    auto listener = std::make_shared<NoOpDataReaderListener<dds::core::xtypes::DynamicData>>();
    reader.set_listener(listener);
}

 

Howard's picture
Offline
Last seen: 3 days 14 hours ago
Joined: 11/29/2012
Posts: 608

Hi have you looked through this example?

https://community.rti.com/examples/builtin-topics

It focuses on the SubscriptionBuiltinTopic, but you can apply the same pattern to the PublicationBuiltinTopic.

There is a modern C++ (aka C++11) example.

rgs
Offline
Last seen: 2 days 11 hours ago
Joined: 07/26/2022
Posts: 7

Hi Howard,
The builtin-topics example doesn't help me.
I don't know the topic message type at compile-time.
Regards

Howard's picture
Offline
Last seen: 3 days 14 hours ago
Joined: 11/29/2012
Posts: 608

I don't quite understand.  Why wouldn't your code have access to the type?  It's defined in the header files.

dds::topic::PublicationBuiltinTopicData
 

 

 

rgs
Offline
Last seen: 2 days 11 hours ago
Joined: 07/26/2022
Posts: 7

It's not that I don't have the type, I don't want to use it.
If I hard-code the types then I have to recompile the code every time there is a new/changed type.
And I have dozens of types, I don't want to manually define a DataReader for every single one.
I'm trying to write generic code that will work regardless of the type.

Howard's picture
Offline
Last seen: 3 days 14 hours ago
Joined: 11/29/2012
Posts: 608

The PublicationBuiltinTopic data type is a part of the DDS specification.  It can only change if the DDS specification changes.  And that type has not changed for years and years.

User data types, yes, I understand.  But ones that are defined by the DDS specification...there are only 3 types that you may be interested in using, for the Participant, Publication and Subscription.

In any case, if you want to use Dynamic Data for all of your data types, you'll have to use the DynamicData API, which requires a typecode object to create the DynamicData which can be used to create the Topic.

That typecode object has to be created from a definition of the data type...which typically would be specified in an XML file.

So, if you want to use this method for the DDS Builtin Topics, you'll have to create the XML definition of the DDS DataTypes used for the builtin Topics...

 

Also, no matter what you have to create a different DataReader for every Topic that you subscribe to.  You can't use a single DataReader object and have it receive data for any Topic of any DataType.  When a DataReader is instantiated, DDS has to know which Topic it's subscribing to, and what data type is the Topic.

You can create a DataReader that uses a DynamicData Type.  But the DynamicDataType has to be instantiated with a concrete type definition via a typeobject.

rgs
Offline
Last seen: 2 days 11 hours ago
Joined: 07/26/2022
Posts: 7

I don't want to create any topics. The topics already exist. I just want to look them up.
if this method won't work, then what method will work for creating DataReader's
for my unknown custom user-data types from the PublicationBuiltinTopicData?

Howard's picture
Offline
Last seen: 3 days 14 hours ago
Joined: 11/29/2012
Posts: 608

First, for the DDS Built Topics, there are already DDS Bulitin DataReaders created for each topic.  You just need to use APIs to look them up and get a reference to them...so that you can set your own listener object on them.

That's what's shown in the example code that I pointed out before.  So, you don't have to create a DataReader for any of the Builtin Topics...they are already there.

BTW, DDS depends on all apps using DDS to agree what the data type is for the ParticipantBulitinTopic, PublicationBuiltinTopic and SubscriptionBuiltinTopic.  These topics are used to exchange the discovery information needed to make a successful connection.  So, there's no way that the topic datatype will change.  If it happens to be different between 2 different versions of DDS, then those DDS applications won't be able to exchange information successfully and connect.

With respect to your statement of "topics" already exist...I'm not sure I understand what you mean.  How do they already exist?  Are you using XML application creation and have defined topics in an XML configuration file?   If so, you can also define the DataReaders in the XML configuration file and use a "lookup" method to get a handle to those DataReaders once yo have instantiated the Participant defined in XML...which will instantiate all of the defined topics, datawriters/datareaders, etc.

You can read about this in the XML APP creation documented here:

https://community.rti.com/static/documentation/connext-dds/6.1.1/doc/manuals/connext_dds_professional/xml_application_creation/index.htm

rgs
Offline
Last seen: 2 days 11 hours ago
Joined: 07/26/2022
Posts: 7

I apologize for the confusion. I'm new to dds so maybe I need to tighten up my language.
I'm not interested in the builtin topics directly. I'm only using PublicationBuiltinTopicData to discover the user-defined custom topics.
So let's ignore the builtin topics, I already know how to access them. I don't need them.

Once I've discovered the custom topics, I want to create DataReaders for them.
There's no headers, there's no XML, there's no way to know what the types are before-hand.
I'm not looking for introspection, I don't need to know the format of the custom-types.
I just need to create DataReaders for them, and I want to create a contentfiltertopic for the DataReader so I can filter them.
The contentfiltertopic query will be user-defined, so again: I don't need to know any details about the types.

Is this possible?

as far as types"already existing", that's my lack of experience with dds showing. I assumed once my participant becomes aware of the topic via the listener, that the topic + type are then registered .
But that may not be the case. please disregard.

Howard's picture
Offline
Last seen: 3 days 14 hours ago
Joined: 11/29/2012
Posts: 608

Yes, with the PublicationBuiltinTopicData sample that you receive, you have access to the datatype needed to create a DataReader of the DynamicData Type via the type() member.

https://community.rti.com/static/documentation/connext-dds/6.1.1/doc/api/connext_dds/api_cpp2/classdds_1_1topic_1_1PublicationBuiltinTopicData.html#aa4af5c32e3bc413bd263b709c367631d

However, you first use the type() to create a Topic.  Topic objects are local.  They are not propagated.  Discoverying topics on the network does not automatically create topics locally.  If you discover a publication, aka DataWriter of a topic that you want to subscribe to, you have to create the topic locally before you can create a DataReader.

You have to use this contructor for a topic:

https://community.rti.com/static/documentation/connext-dds/6.1.1/doc/api/connext_dds/api_cpp2/classdds_1_1topic_1_1Topic.html#a8bdc62fcdcb95aa55dd8c980e6293035

dds::topic::Topic<dds::core::xtypes::DynamicData>::Topic(participant, typename, type);

where you get the typename and type from the PublicationBuiltinTopicData object.

https://community.rti.com/static/documentation/connext-dds/6.1.1/doc/api/connext_dds/api_cpp2/classdds_1_1topic_1_1PublicationBuiltinTopicData.html

 

 

rgs
Offline
Last seen: 2 days 11 hours ago
Joined: 07/26/2022
Posts: 7

Thank you very much for the help. I resolved my issue.
Here is an excerpt of the code, I hope it helps somebody else.
Cheers.

void MyApp::processTopicData(const dds::topic::PublicationBuiltinTopicData& topic_data)
{
           std::string topic_name = topic_data.topic_name().to_std_string();
            auto topic_type = topic_data->type();

            // create the topic
            dds::topic::Topic<dds::core::xtypes::DynamicData> topic = dds::core::null;
            topic = dds::topic::Topic<dds::core::xtypes::DynamicData>(m_Participant, topic_name, topic_type.value());

            // create contentFilteredTopic and DataReader
            {
                // create a contentfilter
                dds::topic::ContentFilteredTopic<dds::core::xtypes::DynamicData> cft = dds::core::null;
                try
                {
                    cft = dds::topic::ContentFilteredTopic<dds::core::xtypes::DynamicData>(
                        topic,
                        "INSERT UNIQUE NAME",
                        dds::topic::Filter("INSERT YOUR QUERY HERE'"));
                }
                catch(std::exception& e)
                {
                    std::cerr << "error: creating filtered topic [" << name << "]: " << e.what() << std::endl;
                    continue;
                }

                // you need to copy the qos settings from the topic
                dds::sub::qos::DataReaderQos ReaderQos = dds::core::QosProvider::Default().datareader_qos();
                ReaderQos << dds::core::policy::Reliability::Reliable();
                ReaderQos << topic_data->durability();
                ReaderQos << topic_data->liveliness();
                ReaderQos << topic_data->ownership();

                Reader = dds::sub::DataReader<dds::core::xtypes::DynamicData>(m_Subscriber, cft, ReaderQos);
                if (Reader == dds::core::null)
                {
                    std::cout << "error: create_datareader failed" << std::endl;
                    continue;
                }
                auto listener = std::make_shared<MyListener>(timer);
                Reader.set_listener(listener);
            }
}

 

 

 

Howard's picture
Offline
Last seen: 3 days 14 hours ago
Joined: 11/29/2012
Posts: 608

Thanks for posting your solution!