Getting Started
The full code of this example can be found in this GitHub repository.
Requirements
In order to follow this tutorial, you need to install in your computer:
- Microsoft .NET 6.0 SDK or greater
- CMake Version 3.8.2 or greater
- Perl, on Windows system is recommended to use Strawberry Perl
- For Apple ARM64 systems, you must install Rosetta 2 (
softwareupdate --install-rosetta
)
Install the OpenDDSharp Templates
OpenDDSharp provides .NET project templates in order to ease the process of creating OpenDDSharp applications.
Run the following command to install OpenDDSharp templates in your computer from the NuGet.org feed:
dotnet new --install OpenDDSharp.Templates::3.310.1
Two new templates will be added to the dotnet templates system, openddsharp-idl-project
and openddsharp-console-app
,
that can be used to create the projects for your OpenDDSharp solution.
For more information on how manage .NET project and item templates visit the microsoft documentation.
Create the IDL project
Applications that use the Data Distribution Service (DDS) define the data types in a way that is independent of the programming language or operating system/processor platform by using the Interface Definition Language (IDL).
OpenDDSharp generates the C# code based on the IDL4 to C# Language Mapping specification using his owns code generation tool. Check this link to get more information about the IDL language and the current implementation of the specification.
Execute the following command in order to create your OpenDDSharp IDL project:
dotnet new openddsharp-idl-project --name TestMessage --output TestMessage
A new C# IDL project will be created in the TestMessage
folder, where you can define your own data types using the
Interface Definition Language.
For the current example, modify the IDL/TestMessage.idl
file content as follow:
module HelloWorld{
@topic
struct Message {
string Content;
};
};
Build the project to auto-generate and compile the C# code for the defined structure:
dotnet build TestMessage/TestMessage.csproj --runtime <runtime_identifier> --configuration <Release|Debug>
Note
The implemented runtime identifiers are:
- win-x64
- win-x86
- linux-x64
- osx-x64
HelloWorld Publisher
Create the publisher project
Once you have defined your data transport types in the IDL project, it's time to create your first publisher application.
Using the following command, you will create a console application that includes the OpenDDSharp reference, some boilerplate code and the configuration required to initialize your domain participant:
dotnet new openddsharp-console-app --name HelloWorldPublisher --output HelloWorldPublisher
You should add a reference to the previously created data transport types project to be able to use them in your publisher application. You can do it with the following command:
dotnet add HelloWorldPublisher/HelloWorldPublisher.csproj reference TestMessage/TestMessage.csproj
Create the DDS DomainParticipant
The first entity to be created in a DDS application is the domain participant.
The domain participant is created with the domain participant factory and it is the factory for the DDS topics, publishers and subscribers. The following code shows how to create a domain participant in the domain 42:
Ace.Init();
DomainParticipantFactory dpf = ParticipantService.Instance.GetDomainParticipantFactory("-DCPSConfigFile", "rtps.ini");
DomainParticipant participant = dpf.CreateParticipant(42);
if (participant == null)
{
throw new Exception("Could not create the participant");
}
Note
This code snippet is already included in the boilerplate if you created the application with the template. Feel free to change the domain id or the configuration file in order to fit the requirements of your application.
Create the DDS Topic
To allow data to flow around the system first you need to define your topics. You can define multiple topics in the same domain, each topic consists in a topic name and the topic type that will be used to share information.
Before creating a topic, you must to register the type that is going to be used to share the data in that topic.
We are going to use the Message
structure defined in the IDL project. The following code shows how to register the Message
type
in the previously created domain participant:
MessageTypeSupport support = new MessageTypeSupport();
ReturnCode result = support.RegisterType(participant, support.GetTypeName());
if (result != ReturnCode.Ok)
{
throw new Exception("Could not register type: " + result.ToString());
}
After the type is registered, we can create the DDS topic entity where the messages will be shared with the subscribers:
Topic topic = participant.CreateTopic("MessageTopic", support.GetTypeName());
if (topic == null)
{
throw new Exception("Could not create the message topic");
}
Create the DDS Publisher and the DDS DataWriter
The publisher entity is the factory for the datawriter entities.
The datawriter entity is in charge of writing the data in the related topic.
You can group several datawriter entities in the same publisher entity.
In this example we only need one publisher and one datawriter related with the MessageTopic
created previously.
The following code shows how to create the publisher entity:
OpenDDSharp.DDS.Publisher publisher = participant.CreatePublisher();
if (publisher == null)
{
throw new Exception("Could not create the publisher");
}
To create the datawriter first we need to create a generic datawriter related with the topic and after that a specific datawriter for the registered type of the topic.
The following code shows how to create a datawriter for the MessageTopic
:
DataWriter writer = publisher.CreateDataWriter(topic);
if (writer == null)
{
throw new Exception("Could not create the data writer");
}
MessageDataWriter messageWriter = new MessageDataWriter(writer);
Wait for a subscription before write the message
The default QoS configuration for the datawriter use a volatile durability. Only the datareaders matched with the datawriters when the data is written will receive the message. The following code shows how to ensure that at least one datareader is present before writing the data:
Console.WriteLine("Waiting for a subscriber...");
PublicationMatchedStatus status = new PublicationMatchedStatus();
do
{
writer.GetPublicationMatchedStatus(ref status);
System.Threading.Thread.Sleep(500);
}
while (status.CurrentCount < 1);
Now that we are sure that at least one datareader is present in the system we can write the message to the topic:
Console.WriteLine("Subscriber found, writing data....");
messageWriter.Write(new Message
{
Content = "Hello, I love you, won't you tell me your name?"
});
Console.WriteLine("Press a key to exit...");
Console.ReadKey();
Shutdown the application
Before exiting the application you should release all the resources used by DDS. The following code shows how to release all the entities previously created:
participant.DeleteContainedEntities();
dpf.DeleteParticipant(participant);
ParticipantService.Instance.Shutdown();
Ace.Fini();
Note
This code snippet is already included in the boilerplate if you created the application with the template.
HelloWorld Subscriber
Create the subscriber project
Same than with the publisher application, you can create the subscriber application and reference the data transport types project using the OpenDDSharp console template by using the following commands:
dotnet new openddsharp-console-app --name HelloWorldSubscriber --output HelloWorldSubscriber
dotnet add HelloWorldSubscriber/HelloWorldSubscriber.csproj reference TestMessage/TestMessage.csproj
Create the domain participant, register the type and create the topic
You need to create the same entities as in the publisher project. Pay attention to use the same domain id (42) and
the same topic name(MessageTopic
) used in the publisher application.
Create the DDS Subscriber and the DDS DataReader
The subscriber entity is the factory for the datareader entities. The datareader entity is in charge of reading the data in the related topic.
You can group several datareader entities in the same subscriber entity. In this example we only need one subscriber and one datareader
related with the MessageTopic
created previously. The following code shows how to create the subscriber entity:
Subscriber subscriber = participant.CreateSubscriber();
if (subscriber == null)
{
throw new Exception("Could not create the subscriber");
}
To create the datareader first we need to create a generic datareader related with the topic and after that a specific datareader for the registered type of the topic.
The following code shows how to create a datareader for the MessageTopic
:
DataReader reader = subscriber.CreateDataReader(topic);
if (reader == null)
{
throw new Exception("Could not create the message data reader");
}
MessageDataReader messageReader = new MessageDataReader(reader);
Receive the message
There are different ways to read the information from the topic, listeners, waitsets or simply polling the information from the datareader. For this first application we are going to poll the information from the datareader. The following code shows how to do it:
while (true)
{
StatusMask mask = messageReader.StatusChanges;
if ((mask & StatusKind.DataAvailableStatus) != 0)
{
List<Message> receivedData = new List<Message>();
List<SampleInfo> receivedInfo = new List<SampleInfo>();
result = messageReader.Take(receivedData, receivedInfo);
if (result == ReturnCode.Ok)
{
bool messageReceived = false;
for (int i = 0; i < receivedData.Count; i++)
{
if (receivedInfo[i].ValidData)
{
Console.WriteLine(receivedData[i].Content);
messageReceived = true;
}
}
if (messageReceived)
break;
}
}
System.Threading.Thread.Sleep(100);
}
Shutdown the application
Same as with the publisher application, you should release all the resources used by DDS.
participant.DeleteContainedEntities();
dpf.DeleteParticipant(participant);
ParticipantService.Instance.Shutdown();
Ace.Fini();
Build and run the projects
Build the publisher and subscriber project with the following commands:
dotnet build HelloWorldPublisher/HelloWorldPublisher.csproj --configuration <Release|Debug> --runtime <runtime_identifier> --no-self-contained
dotnet build HelloWorldSubscriber/HelloWorldSubscriber.csproj --configuration <Release|Debug> --runtime <runtime_identifier> --no-self-contained
Note
The implemented runtime identifiers are:
- win-x64
- win-x86
- linux-x64
- osx-x64
Note
To run the program on Linux systems, you should set the LD_LIBRARY_PATH
pointing to the directory where the executable is created.
Similar, to run the program on MacOS systems, you should set the DYLD_FALLBACK_LIBRARY_PATH
pointing to the directory where the executable is created.