DDS Blockset with RTI Connext Using Shapes Demo
This example shows how to import, configure, and deploy the Shapes Demo provided by RTI to introduce DDS concepts. You can subscribe to various shapes such as circles, squares, and triangles and watch their location as they move around a canvas. In this version of the demo, DDS Blockset provides an application that subscribes to a square and then publishes the location of a circle.
Hello, my name is Mark McBroom. I'm an application engineer at The MathWorks. Today, I'll be going through a demonstration of the new DDS Blockset, which was released in R2021a. In this demonstration, I'll be going through a typical workflow in which a user begins with an XML file. This XML file defines the DDS topic types, the quality of service, profiles, and the domains on which the topic types and quality of service profiles will be used. This XML file can be created by a text editor or through a third party authoring tools such as RTI System Designer.
In either case, once this XML file is available it can be imported into the Simulink Data Dictionary using a DDS Wizard. And at this point the contents of the data dictionary can be viewed and edited as needed to make any adjustments needed as far as types, domains, or profiles are concerned.
The next step is to construct the Simulink model. The Simulink model would contain one or more DDS block set blocks for subscribing and publishing the two DDS topics. In addition, any necessary processing that would be done on the DDS messages before sending them out. Each DDS published and subscribe block is then mapped using the Code Mappings GUI to one of the topics defined in the data dictionary.
Once the algorithm in the Simulink model is determined to be executing properly by various simulation tests, you can deploy the Simulink model by generating C++ code. The C++ code links to libraries provided by your DDS vendor. And then that application can be deployed to any system that has library support from your DDS vendor.
In my case, I'll be deploying to my same Windows computer that I'm running Simulink on. And I'll actually be running that deployed application which sends received messages to the RTI DDS Shapes demo. With that, I'll open up Simulink.
First, we'll take a quick look at the XML file we're going to be using today. This XML file has three main sections. The first defines the types that will be sending or receiving. In this case, we just have one type to find that has an x and y position and a size.
That's followed by a large section that defines a number of quality of service profiles. If you're familiar with DDS, you know that these profiles define the overall operation of each publisher and subscriber. Lots of different parameters that you can adjust to get the desired performance.
That's followed by a section that defines the domains on which we'll be sending our receiving topics. In this case, we just have one domain. It's on domain ID zero. And there are three topics to find, all using the one topic type to find at the beginning. And then lastly, there is a section that defines each of the publishers and subscribers on this domain and which of the three topics they'll be sending or receiving.
With that, we'll open up Simulink. Create a blank model. And the first thing I want to do is import that XML file we just looked at. We'll do that by using the DDS app. So the DDS app can be found by opening up the App tab, and then picking from the Code Generation section the DDS app.
Tool strip in Wizard. This will walk us through the process of importing the XML file into Simulink. You have the option of two different DDS implementations. I'll be using RTI Connext. And I'll be using the XML file we previously looked at.
You do have the option of using the default XML file that MathWorks provides or using an already existing data dictionary. We'll be importing from our XML file. And we're finished.
So the next step is to look at the XML information that was imported, make sure it all looks right, and possibly supplement with additional content. The data dictionary has three tabs-- one for Types, Domains, and Quality of Service. On the Types tab, if we expand, you'll see that we have one topic type to find. This type has three elements to it-- x, y, and shape size.
Again, notice this user interface looks very much like the Simulink Bus Editor in Simulink, if you're familiar with Simulink buses, because that is the mapping we made between DDS structures, topic types, and Simulink. You can define additional structures here, if you like-- additional topic types. You can also define Constants or Enumerated types that might be used in one of your structure type definitions. I won't add any here.
The second tab defines the domains. In this case, we just have one domain, assigned domain ID 0. And on that domain we have three topics to find-- Circle, Square, and Triangle. All using the same topic type.
If desired, you can add additional domains, or to the existing domains, you can add additional topics. And the editing would be done over here, on the right-hand side, where you associate topics with the types that you've defined on the Types tab.
And finally, there's a Quality of Service tab that defines the quality of service profiles. In this case, we've got four defined. You can browse through and look at the various settings and make any necessary adjustments. For example, for this data reader we have the history setting set to Keeping All History, but the depth is only 1. You could change either the kind of history and/or the depth of the queue that you'd like to specify.
So once you've gone through the data dictionary and confirmed proper settings or made any adjustments, it's time to actually construct the Simulink model. First, let's open up the Library Browser. And you'll see we have a new section here for DDS, with two blocks available.
We're going to have a simple model here that's just going to take in one DDS topic type-- or one DDS topic, I should say. And then publish a topic with the same type, but a different topic. We're just going to do some simple processing on the topic after we receive it.
The first thing we need to do is tell Simulink what the Simulink type is of this. So we have a corresponding Simulink bus that was created for that DDS topic type that we just viewed in the data dictionary. We go and turn on the overlays and show data type. Be able to see that we've got just one type propagating through the model.
This is showing a Simulink message coming in that's carrying the DDS sample. It's unpacked by the DDS Take block and converted into a bus. This bus is then processed by the subsystem, then packaged back up into a DDS message, and sent out as a Simulink message on the network.
So let's add some basic processing to this so our model has some simple meaning to it. So we're just going to pull out the x and y positions of this bus and offset them by a value of 10.
So I'll quickly draw this model.
So we're going to strip off the x and y elements from the bus, and write them back. Let's connect this up first. And write back the x and y.
So we're taking off the x and y position from our sample that comes in, we're offsetting it by 10, then writing it back out. So make sure everything's still updates, which it does. So now we've got a functioning Simulink model that updates and runs. At this point you could construct additional tests with Simulink Test to verify proper behavior of the content of your model. At this point, we'll assume the model's correct and we're ready to generate code.
So there's one final step before we actually generate code, and that is to associate each DDS block-- and you could have, by the way, multiple DDS Take and Receive blocks here. We have to map each input port or output port, each input message, each output message to a sample that we defined in the data dictionary.
So just to refresh your memory, for our domain here we defined three topics-- a circle, a square, and a triangle-- all with the same shape type. So what we're going to do for this example is we're going to read in a square. So we're going to subscribe, or take, a square topic, do some processing on it, and then publish a circle topic.
So we do that by associating one of the topics to each of the input ports and output ports. So for input port, we're going to select one of the three available topics. We're going to pick the square topic coming in. And for the output port we're going to publish a circle.
So now we've completed the Simulink model. We'll just make sure it updates again. At this point, we're ready to actually generate code. So you do that by just clicking the Build button on the DDS Tool strip.
It's now completed Code Generation and producing the Code Generation report. If you're not familiar with embedded coder, we produce this nice standalone browser of the generated code, but we'll also placed a generated code in the Simulink canvas, side-by-side with the model. That allows us to do side-by-side inspection of the C++ code associated with each block in the Simulink model.
So that view is being prepared. There it is. Let's make this a little bit bigger so you can see it. So we can click on a particular block in the Simulink model and see the resulting code. So here's the code for our Take block.
So it's calling a method that encapsulates all of the DDS APIs for receiving a sample. It is returning that sample on a local variable. The local variable then gets copied to another local variable, if a valid topic was received.
So then if we dive inside our subsystem here, we'll see some of the processing that's being performed on the bus that we unpacked from the DDS sample. So we're taking the x and y positions and offsetting them by 10, then associating them back with the bus, and then back at the top level, we're sending our sample back out. And again, there's another method here that's used to encapsulate the DDS APIs.
We'll just look at two other files here. The first is the source files called RTI Adapters. So these have the bulk of the APIs in them for actually communicating with the DDS libraries. If you're familiar with DDS, you'll recognize some of these. But the first is registering our Topic type. In this case, we just have one type called Shape Type 1.
Then further down, you'll see code that's creating our participant. This is creating the participant using the XML file we imported with all it's necessary types, publishers, and subscribers. And finally, we'll see the methods here that are responsible for reading and writing. So here's our method for taking a sample. So here you see we're taking a sample off the DDS network. And then finally, here is the code for publishing a sample on the network.
Two other things I'll point out quickly-- two other source files. One is our Main. So we do produce a fully functional executable. And the main shows you all that necessary calls needed to actually create instances of the publishers and subscribers. So see where we're making calls here to the methods I just showed you for registering our types, creating the participants, and then setting up a timer that's going to run the C++ code at the rate specified in the Simulink model.
And finally, one more last thing I'll show, as far as code generation goes, is that all of these C++ code produced in the model is placed in one directory. It has the name of the model with the RTW appended to
the end of the model name. And inside, you'll see all the C++ code that was produced, including code from rtiddsgen.
If you're familiar with rtiddsgen, you know that that's a tool provided by RTI. It takes in the XML file we looked at earlier and produces a streaming code needed for sending and receiving each topic type that's defined. And lastly, in this directory we have the executable that was produced from our Simulink model called Untitled.exe, which is the name of the Simulink model.
So at this point, we're ready to actually run our executable with the Shapes Demo. So open up the Shapes Demo. We're going to publish a square and subscribe to a circle, which is the opposite of what the Simulink model is doing. So I also have just a shell that's opened up pointing to the same directory where my executable is located. I'm going to just run the executable.
And we see in the Shapes Demo window that our Simulink model is, in fact, running. Just refresh your memory the Simulink model is taking in. It's taking a circle-- I'm sorry-- it's taking a square and publishing a circle. So let's take in the square and publishing the circle back out.
Finally, we can look at the Admin console and see that we've got circles being published by the shapes demo and subscribed by the Simulink model.
That concludes my demonstration for today showing you a workflow from an XML file, importing that into Simulink , building the Simulink model, and deploying the C++ application.
You can also select a web site from the following list:
How to Get Best Site Performance
Select the China site (in Chinese or English) for best site performance. Other MathWorks country sites are not optimized for visits from your location.