Question

I am trying to create the simplest C# application that will allow me to get the topics currently available in the selected DDS domain. But I can't seem to make it work.

// Create the DDS Domain participant on domain ID 0
DDS.DomainParticipant participant =                          
             DDS.DomainParticipantFactory.get_instance().create_participant(
                    0,
                    DDS.DomainParticipantFactory.PARTICIPANT_QOS_DEFAULT,
                    null, /* Listener */
                    DDS.StatusMask.STATUS_MASK_NONE);
DDS.Subscriber sub = participant.get_builtin_subscriber();
DDS.DataReader reader1 = sub.lookup_datareader("DCPSTopic");
DDS.TopicBuiltinTopicDataDataReader builtinReader1 =  
                       (DDS.TopicBuiltinTopicDataDataReader)reader1;
DDS.TopicBuiltinTopicDataSeq topicSeq = new DDS.TopicBuiltinTopicDataSeq(10) ;
DDS.SampleInfoSeq infoSeq = new DDS.SampleInfoSeq(10);
builtinReader1.read(topicSeq, 
                    infoSeq, 
                    10, 
                    DDS.SampleStateKind.ANY_SAMPLE_STATE, 
                    DDS.ViewStateKind.ANY_VIEW_STATE,
                    DDS.InstanceStateKind.ANY_INSTANCE_STATE);

When running the code above, I get a "Retcode_NoData" exception in the last line (builtinReader1.read(...)) even though both publishers and subscribers are running and posting in the same domain (domain 0). Do you have any idea about what could be wrong with my code?

By the way. I use RTI Connext 5.0 implementation of DDS.

Regards John

Was it helpful?

Solution

With RTI Connext, the TopicBuiltinTopic does not behave as one might expect. Check out the documentation of the C# API DDS::TopicBuiltinTopicData Class Reference:

Note: The DDS_TopicBuiltinTopicData built-in topic is meant to convey information about discovered Topics. This Topic's samples are not propagated in a separate packet on the wire. Instead, the data is sent as part of the information carried by other built-in topics (DDS::PublicationBuiltinTopicData and DDS::SubscriptionBuiltinTopicData). Therefore TopicBuiltinTopicData DataReaders will not receive any data.

What it basically says: because of the way discovery is implemented in Connext, you will not see any data in any TopicBuiltinTopicData DataReaders. This is what you observe in your code snippet.

Fortunately, it is still possible to get information about Topics on the bus. This has to happen via the PublicationBuiltinTopicData and SubscriptionBuiltinTopicData. If you look at the documentation of the C# API DDS::PublicationBuiltinTopicData Class Reference, you can see that the data contains the string fields topic_name and type_name. Additionally, you can get information about the structure of the type, but that is more advanced and implementation specific.


Three remarks in case you plan to implement the reading of the Publication and Subscription builtin Topics. First, in stead of hardcoding the name of the built-in Topic, like you did with "DCPSTopic", it is better to refer to the corresponding TypeSupport attribute, as in:

reader = sub.lookup_datareader(
             DDS.PublicationBuiltinTopicDataTypeSupport.PUBLICATION_TOPIC_NAME);

Then it is good to know that with Connext, built-in DataReaders will not contain any samples about Publications or Subscriptions that live in the same DomainParticipant as the built-in Subscriber. In other words, you can only see other Participant's Entities, but not your own.

Finally, while playing around with you code, I noticed that the reading of the samples did not work unless I replaced the constructor invocations of the sequences as follows:

DDS.SampleInfoSeq infoSeq = new DDS.SampleInfoSeq();

in stead of

DDS.SampleInfoSeq infoSeq = new DDS.SampleInfoSeq(10);

and similar for the data-sequence. I do not know why that is, but it should not have any practical implications.

OTHER TIPS

Rainier is right. Connext DDS does not propagate the Topics directly in the TopicBuiltinTopicData; rather it does it indirectly in the PublicationBuitinTopicData and SubscriptionBuiltinTopicData. This is permitted by the DDS Specification.

There is information on how to use the builtin Topics in this HOWTO titled Detect the presence of DomainParticipants, DataWriters and DataReaders in the DDS Domain. The HOWTO includes some working example code. It is in Java instead of C# but it should be easy to map. The example there reads one Sample at a time so you do not need to deal with the sequence sytax.

The reason why you must use:

DDS.SampleInfoSeq infoSeq = new DDS.SampleInfoSeq();

Instead of:

DDS.SampleInfoSeq infoSeq = new DDS.SampleInfoSeq(10);

Is that the read/take APIs that have two different behaviors depending on whether the passed sequences are "empty" (i.e. have a zero allocated/maximum length) or have an allocated length.

If the sequence is empty, which is what you get with the default constructor, then read/take behave as zero-copy API's. This means that the actual elements in the sequence are "loaned" from the ones already stored inside the middleware. For this reason the sequence that must be passed in has to be an "empty" sequence so that the middleware knows it is OK to replace the contents with references to the internal elements. After accessing the elements you must return the 'loaned elements' back to the middleware calling the DataReader::return_loan operation.

If the sequences are not empty. Then DDS will assume the elements are pre-allocated and will try to copy the data into the elements you passed. The problem with the code you showed is that

DDS.SampleInfoSeq infoSeq = new DDS.SampleInfoSeq(10);

Allocates just the sequence itself. But not the elements. So the read/take call will fail when it tries to make the copy. If you wanted to use this non zero-copy API you have to follow the call with the allocation/assignment of each element, that is:

for (int i=0; i< 10; i++ ) {
    infoSeq.set_at(i, new DDS.SampleInfo());
}

And the same would apply for the data sequence as well. If you do this you would not have to use the DataReader::return_loan operation. But there would be extra copies so it is less efficient.

You can find more information on how to use DDS Sequences in this HOWTO titled Howto use OMG DDS Sequences in C++. It is written with C++ examples but the principles apply to all the languages.

Regards, Gerardo

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top