.. _getting_started_static_api: Getting Started Static API ========================== This section serves as a comprehensive guide to assist in creating a basic publication/subscription application with static memory management using Safe DDS. By following the step-by-step instructions provided, it is possible to quickly establish a functional example of Safe DDS and gain a better understanding of the library. .. admonition:: Download Code Try this example by downloading the code from :download:`this CMake project <../code/zip/getting_started_static_api.zip>`. .. _getting_started_static_api_create_project: Create a CMake project ---------------------- Create a CMake project with a folder structure as shown below: .. code-block:: bash static_api ├── CMakeLists.txt └── src └── main.cpp The ``CMakeLists.txt`` file is a crucial component, as it contains the build instructions for the project. The following example ``CMakeLists.txt`` file is provided to assist in properly configuring the project's build infrastructure: .. literalinclude:: ../code/getting_started_static_api/CMakeLists.txt :language: cmake :start-after: # GETTING_STARTED_STATIC_API_CMAKE_EXAMPLE :end-before: #! GETTING_STARTED_STATIC_API_CMAKE_EXAMPLE .. _getting_started_static_api_application: Safe DDS application -------------------- After the project folder and ``CMakeLists.txt`` file have been set up, the next step is to create a ``main.cpp`` file that will contain the application's source code: .. code-block:: bash mkdir src cd src touch main.cpp To further illustrate the usage of Safe DDS, a simple application that publishes a ``HelloWorld`` message on a created ``HelloWorldTopic`` topic using static API. To achieve this, the application requires both the **DDS headers** and a **type support**. The specifics of typesupport are be covered in the upcoming :ref:`getting_started_typesupport` section. .. literalinclude:: ../code/getting_started_static_api/src/main.cpp :language: c++ :start-after: // GETTING_STARTED_STATIC_API_INCLUDE_BLOCK :end-before: //! GETTING_STARTED_STATIC_API_INCLUDE_BLOCK Static Transport and Static Platform ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ To ensure the lack of heap allocations, this application will override the default :ref:`platform `: .. literalinclude:: ../code/getting_started_static_api/src/main.cpp :language: c++ :start-after: // GETTING_STARTED_STATIC_API_STATIC_PLATFORM :end-before: //! GETTING_STARTED_STATIC_API_STATIC_PLATFORM Also, it will generate a :ref:`static transport ` that inherits from the POSIX UDPv4 transport: .. literalinclude:: ../code/getting_started_static_api/src/main.cpp :language: c++ :start-after: // GETTING_STARTED_STATIC_API_STATIC_TRANSPORT :end-before: //! GETTING_STARTED_STATIC_API_STATIC_TRANSPORT In order to use both the static platform and the static transport, the application will set them at the beginning of the entry point: .. literalinclude:: ../code/getting_started_static_api/src/main.cpp :language: c++ :start-after: // GETTING_STARTED_STATIC_API_SET_STATIC_PLATFORM_AND_TRANSPORT :end-before: //! GETTING_STARTED_STATIC_API_SET_STATIC_PLATFORM_AND_TRANSPORT :dedent: 4 StaticDomainParticipant ^^^^^^^^^^^^^^^^^^^^^^^ To create a DDS :ref:`dds_layers_domain_participant` for the application, a :ref:`StaticDomainParticipant ` is manually instantiated. Also, in order to handle the relation between the :ref:`dds_layers_domain_participant` and the :ref:`architecture_transport` a `DomainTransportDispatcher` is created. .. literalinclude:: ../code/getting_started_static_api/src/main.cpp :language: c++ :start-after: // GETTING_STARTED_STATIC_API_CREATE_DOMAINPARTICIPANT :end-before: //! GETTING_STARTED_STATIC_API_CREATE_DOMAINPARTICIPANT :dedent: 4 .. _getting_started_static_api_typesupport: TypeSupport ^^^^^^^^^^^ Once the Static Domain Participant has been created, the next step is to register the Type Support. More information on how to handle Type Supports in Safe DDS can be found in :ref:`typesupport`. .. literalinclude:: ../code/getting_started_static_api/src/main.cpp :language: c++ :start-after: // GETTING_STARTED_STATIC_API_REGISTER_TYPE :end-before: //! GETTING_STARTED_STATIC_API_REGISTER_TYPE :dedent: 4 .. note:: For this particular application, the used Type Support class is the one provided as reference in :ref:`typesupport section ` StaticTopic ^^^^^^^^^^^ Once the TypeSupport has been created, the next step is to create Static DDS Topic. Creating the DDS :ref:`dds_layers_topic` involves specifying the name of the topic and the name of the type that it uses. .. literalinclude:: ../code/getting_started_static_api/src/main.cpp :language: c++ :start-after: // GETTING_STARTED_STATIC_API_STATIC_TOPIC :end-before: //! GETTING_STARTED_STATIC_API_STATIC_TOPIC :dedent: 4 .. note:: On this example there is not listener attached to StaticTopic. Publication entities ^^^^^^^^^^^^^^^^^^^^ After the StaticTopic has been created, next step is to create static versions of DDS :ref:`dds_layers_publication_publisher`, and :ref:`dds_layers_publication_datawriter`, as demonstrated below: .. literalinclude:: ../code/getting_started_static_api/src/main.cpp :language: c++ :start-after: // GETTING_STARTED_STATIC_API_CREATE_PUBLISHER :end-before: //! GETTING_STARTED_STATIC_API_CREATE_PUBLISHER :dedent: 4 Once the :ref:`dds_layers_topic` is created, a DDS :ref:`dds_layers_publication_publisher` is created, and finally, a DDS :ref:`dds_layers_publication_datawriter` is created, which is used to write messages to the topic. Subscription entities ^^^^^^^^^^^^^^^^^^^^^ Before a DDS :ref:`dds_layers_subscription_datareader` is created, an optional :ref:`dds_layers_subscription_datareaderlistener` class is implemented. This class will be notified when a new message is received. An example implementation ``HelloWorldListener`` is shown below: .. literalinclude:: ../code/getting_started_static_api/src/main.cpp :language: c++ :start-after: // GETTING_STARTED_STATIC_API_DATAREADERLISTENER :end-before: //! GETTING_STARTED_STATIC_API_DATAREADERLISTENER Once the Listener class has been implemented, a static version of :ref:`dds_layers_subscription_subscriber` and a DDS :ref:`dds_layers_subscription_datareader` can be created using the previously created DDS :ref:`dds_layers_topic` as demonstrated below: .. literalinclude:: ../code/getting_started_static_api/src/main.cpp :language: c++ :start-after: // GETTING_STARTED_STATIC_API_CREATE_SUBSCRIBER :end-before: //! GETTING_STARTED_STATIC_API_CREATE_SUBSCRIBER :dedent: 4 Creating the DDS :ref:`dds_layers_subscription_datareader` involves specifying the required :ref:`dds_layers_topic`, as well as the previously created Listener. Once the :ref:`dds_layers_subscription_datareader` has been created, it can be used to read messages from the topic. Enabling entities ^^^^^^^^^^^^^^^^^ Once all entities have been created, they need :ref:`to be enabled ` in order to start processing messages. .. literalinclude:: ../code/getting_started_static_api/src/main.cpp :language: c++ :start-after: // GETTING_STARTED_STATIC_API_ENABLING_ENTITIES :end-before: //! GETTING_STARTED_STATIC_API_ENABLING_ENTITIES :dedent: 4 Publishing ^^^^^^^^^^ To enable periodic publication of messages, a Safe DDS Timer can be used to trigger a publication at regular intervals. The ``publish`` function is responsible for creating a message and writing it to the DDS DataWriter, as shown below: .. literalinclude:: ../code/getting_started_static_api/src/main.cpp :language: c++ :start-after: // GETTING_STARTED_STATIC_API_PUBLISHING_TIMER :end-before: //! GETTING_STARTED_STATIC_API_PUBLISHING_TIMER :dedent: 4 Note that the message is created using the Type Support class and filled with the desired data. Once the message is created, it is written into the a type aware DDS DataWriter named :ref:`TypedDataWriter `, which will publish it to the topic. Spinning entities ^^^^^^^^^^^^^^^^^ Once the DDS entities have been created and the timer has been set up, they need to be spun in order to start processing messages. Spinning refers to the process of dispatching DDS events, such as incoming messages, timeouts, and status changes, to the appropriate DDS entities. The easiest way to spin the DDS entities is using the default executor as shown below: .. literalinclude:: ../code/getting_started_static_api/src/main.cpp :language: c++ :start-after: // GETTING_STARTED_STATIC_API_SPINNING_ENTITIES :end-before: //! GETTING_STARTED_STATIC_API_SPINNING_ENTITIES :dedent: 4 This creates an executor that will internally dispatch events to the appropriate DDS entities, such as the :ref:`dds_layers_subscription_datareader` and :ref:`dds_layers_publication_datawriter`. More information about Safe DDS execution can be found at :ref:`architecture_execution` section. By spinning the DDS entities, the application is now able to publish messages at regular intervals. The DDS :ref:`dds_layers_subscription_datareader` created will receive the messages from local and remote DDS DomainParticipants, and will notify the ``HelloWorldListener`` class when a new message is received. Running the application ----------------------- To run the application, first navigate to the project directory, create a build folder (`GETTING_STARTED_STATIC_API_FOLDER` on the example) and generate the build files with CMake. .. literalinclude:: ../code/build_documentation_code.sh :language: bash :start-after: # INSTALLATION_CMAKE_GETTING_STARTED_STATIC_API :end-before: #! INSTALLATION_CMAKE_GETTING_STARTED_STATIC_API :dedent: 4 This command will configure the project with the default settings, including the path to the Safe DDS library, and it will then build the application. Once the build is complete, the application can be run by executing the binary file from the build directory: .. code-block:: bash ./safedds_getting_started_static If the application is successfully connected to the network and running, the console output should show messages similar to the following: .. code-block:: bash [DW: 0] Message: HelloWorld with index: 0 [DW: 0] Message: HelloWorld with index: 1 [DW: 0] Message: HelloWorld with index: 2 [DW: 0] Message: HelloWorld with index: 3 [DW: 0] Message: HelloWorld with index: 4 [DW: 0] Message: HelloWorld with index: 5 [DW: 0] Message: HelloWorld with index: 6 [DW: 0] Message: HelloWorld with index: 7 [DW: 0] Message: HelloWorld with index: 8 [DW: 0] Message: HelloWorld with index: 9 [DW: 0] Message: HelloWorld with index: 10 The program will continue to publish messages every second until it is manually stopped. In other terminal the example referred in :ref:`getting_started` can be ran. The communication between both processes shall be established and the following output shall be shown: .. code-block:: bash [DW: 0] Message: HelloWorld with index: 0 [DW: 0] Message: HelloWorld with index: 1 [DW: 0] Message: HelloWorld with index: 2 [DW: 16777216] Message: HelloWorld with index: 0 [DW: 0] Message: HelloWorld with index: 3 [DW: 16777216] Message: HelloWorld with index: 1 [DW: 0] Message: HelloWorld with index: 4 [DW: 16777216] Message: HelloWorld with index: 2 [DW: 0] Message: HelloWorld with index: 5 [DW: 16777216] Message: HelloWorld with index: 3 [DW: 0] Message: HelloWorld with index: 6 [DW: 16777216] Message: HelloWorld with index: 4 [DW: 0] Message: HelloWorld with index: 7 [DW: 16777216] Message: HelloWorld with index: 5