diff --git a/docs/conf.py b/docs/conf.py index 913fa39a3b..78ca200e4c 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -207,7 +207,25 @@ def select_css(html_css_dir): def get_git_branch(): - """Get the git branch this repository is currently on.""" + """Get the git branch this repository is currently on. + + On Read the Docs the repo is checked out in detached-HEAD mode, so + ``git name-rev`` returns synthetic names like ``remotes/origin/external-1234`` + instead of the real branch. A workaround is provided using + ``READTHEDOCS_VERSION_TYPE`` and ``READTHEDOCS_VERSION`` according to the build type: + - ``"branch"`` builds: READTHEDOCS_VERSION is the branch name (e.g. ``"3.6.x"``) → use it. + - ``"tag"`` builds: READTHEDOCS_VERSION is the tag name (e.g. ``"v3.6.0"``) → use it. + - ``"external"`` (PR preview) builds: READTHEDOCS_VERSION is the PR number (e.g. ``"1241"``) + which is not a valid git ref. In this case we return None so resolve_fallback_branch falls + back to its default instead of generating broken GitHub URLs. + - Local builds: READTHEDOCS_VERSION_TYPE is unset → fall back to git name-rev. + """ + rtd_type = os.environ.get("READTHEDOCS_VERSION_TYPE") + if rtd_type in ("branch", "tag"): + return os.environ.get("READTHEDOCS_VERSION") + if rtd_type == "external": + return None + path_to_here = os.path.abspath(os.path.dirname(__file__)) # Invoke git to get the current branch which we use to get the theme @@ -229,12 +247,28 @@ def get_git_branch(): return p.communicate()[0].decode().rstrip() except Exception: + # Local build without git or some error occurred print("Could not get the branch") # Couldn't figure out the branch probably due to an error return None +def resolve_fallback_branch(env_var, docs_branch, default="master"): + """ + Resolve the branch to use for GitHub links. + + Priority: + 1. Environment variable ``env_var`` (e.g. FASTDDS_BRANCH) + 2. Current documentation branch (``docs_branch``) + 3. Hard-coded ``default`` + + This mirrors the checkout logic used in the ReadTheDocs clone block so + that extlinks and the actual checkout always point at the same branch. + """ + return os.environ.get(env_var) or docs_branch or default + + def configure_doxyfile( doxyfile_in, doxyfile_out, @@ -282,6 +316,27 @@ def configure_doxyfile( # Header files input_dir = os.path.abspath("{}/fastdds/include/fastdds".format(project_binary_dir)) +# Current branch of the documentation repository — resolved once, used everywhere. +docs_branch = get_git_branch() +if docs_branch: + print('Current documentation branch is "{}"'.format(docs_branch)) +else: + print("Current documentation branch could not be determined; " \ + "GitHub links will point to default branches instead of the corresponding branch.") + +# Resolve GitHub link branches: env var → current docs branch → default. +# Computed here so they are available both in the ReadTheDocs clone block and in extlinks. +fastdds_fallback_branch = resolve_fallback_branch("FASTDDS_BRANCH", docs_branch, "master") +fastdds_docs_fallback_branch = resolve_fallback_branch("FASTDDS_DOCS_BRANCH", docs_branch, "master") +fastdds_python_fallback_branch = resolve_fallback_branch("FASTDDS_PYTHON_BRANCH", docs_branch, "master") +fastdds_gen_fallback_branch = resolve_fallback_branch("FASTDDS_GEN_BRANCH", docs_branch, "master") + +print("Fallback branches for GitHub links:") +print(' Fast-DDS: "{}"'.format(fastdds_fallback_branch)) +print(' Fast-DDS-docs: "{}"'.format(fastdds_docs_fallback_branch)) +print(' Fast-DDS-Python: "{}"'.format(fastdds_python_fallback_branch)) +print(' Fast-DDS-Gen: "{}"'.format(fastdds_gen_fallback_branch)) + # Check if we're running on Read the Docs' servers read_the_docs_build = os.environ.get("READTHEDOCS", None) == "True" if read_the_docs_build: @@ -314,6 +369,7 @@ def configure_doxyfile( fastdds_repo_name, ) +<<<<<<< HEAD # Documentation repository branch docs_branch = get_git_branch() print('Current documentation branch is "{}"'.format(docs_branch)) @@ -325,13 +381,16 @@ def configure_doxyfile( # Else try with current documentation branch # Else checkout to 3.2.x if fastdds_branch and fastdds.refs.__contains__("origin/{}".format(fastdds_branch)): +======= + # Verify the desired branch actually exists in the cloned remote, falling back to master if not. + fastdds_branch = fastdds_fallback_branch + if fastdds.refs.__contains__("origin/{}".format(fastdds_branch)): +>>>>>>> 60e9c7d (Add fallback branch for master links (#1241)) fastdds_branch = "origin/{}".format(fastdds_branch) - elif docs_branch and fastdds.refs.__contains__("origin/{}".format(docs_branch)): - fastdds_branch = "origin/{}".format(docs_branch) else: print( - 'Fast DDS does not have either "{}" or "{}" branches'.format( - fastdds_branch, docs_branch + 'Fast DDS does not have branch "{}"; falling back to master'.format( + fastdds_branch ) ) fastdds_branch = "origin/3.2.x" @@ -347,6 +406,7 @@ def configure_doxyfile( fastdds_python_repo_name, ) +<<<<<<< HEAD # User specified Fast DDS branch fastdds_python_branch = os.environ.get("FASTDDS_PYTHON_BRANCH", None) @@ -356,15 +416,16 @@ def configure_doxyfile( if fastdds_python_branch and fastdds_python.refs.__contains__( "origin/{}".format(fastdds_python_branch) ): +======= + # Verify the desired branch actually exists in the cloned remote, falling back to master if not. + fastdds_python_branch = fastdds_python_fallback_branch + if fastdds_python.refs.__contains__("origin/{}".format(fastdds_python_branch)): +>>>>>>> 60e9c7d (Add fallback branch for master links (#1241)) fastdds_python_branch = "origin/{}".format(fastdds_python_branch) - elif docs_branch and fastdds_python.refs.__contains__( - "origin/{}".format(docs_branch) - ): - fastdds_python_branch = "origin/{}".format(docs_branch) else: print( - 'Fast DDS Python does not have either "{}" or "{}" branches'.format( - fastdds_python_branch, docs_branch + 'Fast DDS Python does not have branch "{}"; falling back to master'.format( + fastdds_python_branch ) ) fastdds_python_branch = "origin/2.2.x" @@ -444,9 +505,37 @@ def configure_doxyfile( "sphinx_copybutton", "sphinx_design", "sphinx.ext.autodoc", # Document Pydoc documentation from Python bindings. +<<<<<<< HEAD +======= + "sphinx.ext.extlinks", + "sphinx_substitution_extensions", +>>>>>>> 60e9c7d (Add fallback branch for master links (#1241)) "sphinx_toolbox.collapse", ] +extlinks = { + # Fast-DDS repo (tree = directory, blob = file) + "fastdds-tree": ( + f"https://github.com/eProsima/Fast-DDS/tree/{fastdds_fallback_branch}/%s", "%s" + ), + "fastdds-blob": ( + f"https://github.com/eProsima/Fast-DDS/blob/{fastdds_fallback_branch}/%s", "%s" + ), + # Fast-DDS-python repo + "fastdds-python-tree": ( + f"https://github.com/eProsima/Fast-DDS-Python/tree/{fastdds_python_fallback_branch}/%s", "%s" + ), + # Fast-DDS-docs repo (code examples embedded in the docs repo) + "fastdds-docs-tree": ( + f"https://github.com/eProsima/Fast-DDS-docs/tree/{fastdds_docs_fallback_branch}/%s", "%s" + ), + # Fast-DDS-Gen raw files + "fastddsgen-raw": ( + f"https://raw.githubusercontent.com/eProsima/Fast-DDS-Gen/{fastdds_gen_fallback_branch}/%s", + "%s", + ), +} + sphinx_tabs_disable_css_loading = False sphinx_tabs_disable_tab_closing = True @@ -611,7 +700,46 @@ def configure_doxyfile( html_use_smartypants = True +<<<<<<< HEAD html_css_files = [select_css(script_path)] +======= +# The CSS files referenced here should have a path relative to the _static folder. +# We use static_relative(download_file(...)) to ensure the resulting paths are relative to "_static". +html_css_files = [ + static_relative( + download_file( + "https://raw.githubusercontent.com/eProsima/all-docs/master/source/_static/css/eprosima-furo.css", + "{}/_static/css/eprosima-furo.css".format(script_path), + ) + ), + static_relative( + download_file( + "https://raw.githubusercontent.com/eProsima/all-docs/master/source/_static/css/pro-badge.css", + "{}/_static/css/pro-badge.css".format(script_path), + ) + ), +] + +# Custom substitutions that are included at the beginning of every source file. +# |Pro|: badge with PRO text. Place it after titles where needed as follows: +# Title |Pro| +# =========== +# rst_prolog = r""" +# .. |Pro| replace:: :bdg-primary-line:`Pro` +# """ +rst_prolog = f""" +.. |Pro| raw:: html + + Pro + +.. |ProjectVersion| replace:: {version} + +.. |FastDDSBranch| replace:: {fastdds_fallback_branch} + +.. |FastDDSPythonBranch| replace:: {fastdds_python_fallback_branch} +""" + +>>>>>>> 60e9c7d (Add fallback branch for master links (#1241)) # Add any paths that contain custom themes here, relative to this directory. # html_theme_path = [] diff --git a/docs/fastdds/discovery/discovery_server.rst b/docs/fastdds/discovery/discovery_server.rst index e669b7a9fa..28aa557c75 100644 --- a/docs/fastdds/discovery/discovery_server.rst +++ b/docs/fastdds/discovery/discovery_server.rst @@ -315,8 +315,9 @@ Full example ^^^^^^^^^^^^ The following constitutes a full example on how to configure *server* and *client* both programmatically and using XML. -You may also have a look at the *eProsima Fast DDS* Github repository, which contains `an example `_ -similar to the one discussed in this section, as well as multiple other examples for different use cases. +You may also have a look at the *eProsima Fast DDS* Github repository, which contains +:fastdds-tree:`an example ` similar to the one discussed in this section, as well as +multiple other examples for different use cases. Server side setup """"""""""""""""" diff --git a/docs/fastdds/discovery/static.rst b/docs/fastdds/discovery/static.rst index 7ccf1c6957..a05aeca688 100644 --- a/docs/fastdds/discovery/static.rst +++ b/docs/fastdds/discovery/static.rst @@ -63,7 +63,7 @@ and DataReaders) must be statically specified, which is done using dedicated XML A |DomainParticipant| may load several of such configuration files so that the information about different entities can be contained in one file, or split into different files to keep it more organized. *Fast DDS* provides a -`Static Discovery example `_ +:fastdds-blob:`Static Discovery example ` that implements this EDP discovery protocol. The following table describes all the possible elements of a STATIC EDP XML configuration file. diff --git a/docs/fastdds/getting_started/simple_app/includes/sum_and_next_steps.rst b/docs/fastdds/getting_started/simple_app/includes/sum_and_next_steps.rst index fbc8297e8e..17387aeb2e 100644 --- a/docs/fastdds/getting_started/simple_app/includes/sum_and_next_steps.rst +++ b/docs/fastdds/getting_started/simple_app/includes/sum_and_next_steps.rst @@ -10,4 +10,4 @@ Next steps In the *eProsima Fast DDS* Github repository you will find more complex examples that implement DDS communication for a multitude of use cases and scenarios. You can find them -`here `_. +:fastdds-tree:`here `. diff --git a/docs/fastdds/getting_started/simple_python_app/includes/sum_and_next_steps.rst b/docs/fastdds/getting_started/simple_python_app/includes/sum_and_next_steps.rst index 7b066b5358..2e84d3acfb 100644 --- a/docs/fastdds/getting_started/simple_python_app/includes/sum_and_next_steps.rst +++ b/docs/fastdds/getting_started/simple_python_app/includes/sum_and_next_steps.rst @@ -9,4 +9,4 @@ Next steps In the *eProsima Fast DDS* Github repository you will find more complex examples that implement DDS communication for a multitude of use cases and scenarios. You can find them -`here `_. +:fastdds-python-tree:`here `. diff --git a/docs/fastdds/rtps_layer/rtps_layer.rst b/docs/fastdds/rtps_layer/rtps_layer.rst index cc67026940..7414fffb8a 100644 --- a/docs/fastdds/rtps_layer/rtps_layer.rst +++ b/docs/fastdds/rtps_layer/rtps_layer.rst @@ -41,7 +41,7 @@ explaining the new features it presents. We recommend you to look at the example describing how to use the RTPS layer that come with the distribution while reading this section. It is located in -`examples/cpp/rtps `_. +:fastdds-tree:`examples/cpp/rtps `. Managing the Participant ^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/docs/fastdds/security/access_control_plugin/access_control_plugin.rst b/docs/fastdds/security/access_control_plugin/access_control_plugin.rst index b1589ad8f7..3a57e724a8 100644 --- a/docs/fastdds/security/access_control_plugin/access_control_plugin.rst +++ b/docs/fastdds/security/access_control_plugin/access_control_plugin.rst @@ -105,9 +105,9 @@ The following is an example of the Domain Governance XML file contents. :end-before: <--> :linenos: -The `Governance XSD file `_ and +The :fastdds-blob:`Governance XSD file ` and the -`Governance XML example `_ +:fastdds-blob:`Governance XML example ` can also be downloaded from the `eProsima Fast DDS Github repository `_. Domain Rules @@ -442,9 +442,9 @@ The following is an example of the DomainParticipant Permissions XML file conten :end-before: <--> :linenos: -The `Permissions XSD file `_ and +The :fastdds-blob:`Permissions XSD file ` and the -`Permissions XML example `_ +:fastdds-blob:`Permissions XML example ` can also be downloaded from the `eProsima Fast DDS Github repository `_. Grant Section diff --git a/docs/fastdds/transport/shared_memory/shared_memory.rst b/docs/fastdds/transport/shared_memory/shared_memory.rst index 161f5afe2e..98df020050 100644 --- a/docs/fastdds/transport/shared_memory/shared_memory.rst +++ b/docs/fastdds/transport/shared_memory/shared_memory.rst @@ -269,6 +269,6 @@ Delivery Mechanisms example --------------------------- A hello world example suitable for supported delivery mechanisms can be found in the -`delivery_mechanisms folder `_. +:fastdds-tree:`delivery_mechanisms folder `. It shows a publisher and a subscriber that communicate through the desired delivery mechanism (which can be set to shared memory only). diff --git a/docs/fastdds/transport/tcp/tcp.rst b/docs/fastdds/transport/tcp/tcp.rst index 5cbf11e810..fd85239d9b 100644 --- a/docs/fastdds/transport/tcp/tcp.rst +++ b/docs/fastdds/transport/tcp/tcp.rst @@ -461,6 +461,6 @@ Delivery Mechanisms example --------------------------- A hello world example suitable for supported delivery mechanisms can be found in the -`delivery_mechanisms folder `_. +:fastdds-tree:`delivery_mechanisms folder `. It shows a publisher and a subscriber that communicate through the desired delivery mechanism (which can be set to TCP only). diff --git a/docs/fastdds/use_cases/request_reply/request_reply.rst b/docs/fastdds/use_cases/request_reply/request_reply.rst index 9fe1509e7b..fb90d07274 100644 --- a/docs/fastdds/use_cases/request_reply/request_reply.rst +++ b/docs/fastdds/use_cases/request_reply/request_reply.rst @@ -49,7 +49,7 @@ The DDS communication schema will be: The key for making *Request-Reply* work is relating the Request with the Reply in the client's side. *Fast DDS* API provides |SampleIdentity-api| to achieve this. -A full example can be found in `Fast DDS repository`_. +A full example can be found in the :fastdds-tree:`Fast DDS repository `. Getting started --------------- @@ -147,4 +147,3 @@ For this the client application has to compare the stored |SampleIdentity-api| w :start-after: //REQUEST_REPLY_EXAMPLE_CLIENT_RECEIVE_REPLY :end-before: //! -.. _Fast DDS repository: https://github.com/eProsima/Fast-DDS/tree/master/examples/cpp/request_reply diff --git a/docs/fastdds/use_cases/statistics_module/statistics_module.rst b/docs/fastdds/use_cases/statistics_module/statistics_module.rst index 7c832c45aa..3a9da64df4 100644 --- a/docs/fastdds/use_cases/statistics_module/statistics_module.rst +++ b/docs/fastdds/use_cases/statistics_module/statistics_module.rst @@ -38,7 +38,7 @@ another application should be configured to subscribe to those topics. This application is a DDS standard application where the statistics DataReaders should be created. In order to create these statistics DataReaders, the user should follow the next steps: -* Using the `statistics IDL `_ +* Using the :fastdds-blob:`statistics IDL ` provided in the public API, generate the |TopicDataTypes-api| with :ref:`Fast DDS-Gen `. * Create the |DomainParticipant-api| and register the |TopicDataTypes-api| and the corresponding statistics diff --git a/docs/fastdds/use_cases/well_known_deployments/well_known_deployments.rst b/docs/fastdds/use_cases/well_known_deployments/well_known_deployments.rst index b641970451..61c7a6cad5 100644 --- a/docs/fastdds/use_cases/well_known_deployments/well_known_deployments.rst +++ b/docs/fastdds/use_cases/well_known_deployments/well_known_deployments.rst @@ -48,7 +48,7 @@ each other, so they can start sharing user data right away, avoiding the EDP pha A complete description of the feature can be found at :ref:`discovery_static`. There is also a fully functional hello world example implementing STATIC EDP in the -`examples/cpp/static_edp_discovery `_ +:fastdds-tree:`examples/cpp/static_edp_discovery ` folder. The following subsections present an example configuration where a Publisher in diff --git a/docs/fastdds/use_cases/zero_copy/zero_copy.rst b/docs/fastdds/use_cases/zero_copy/zero_copy.rst index d0287d77c2..6c232c47cc 100644 --- a/docs/fastdds/use_cases/zero_copy/zero_copy.rst +++ b/docs/fastdds/use_cases/zero_copy/zero_copy.rst @@ -164,6 +164,6 @@ Next steps ---------- A hello world example suitable for supported delivery mechanisms can be found in the -`delivery_mechanisms folder `_. +:fastdds-tree:`delivery_mechanisms folder `. It shows a publisher and a subscriber that communicate through the desired delivery mechanism. As long as it is zero-copy compatible, both intra-process and data-sharing delivery mechanisms are zero-copy if used. diff --git a/docs/fastdds/xml_configuration/making_xml_profiles.rst b/docs/fastdds/xml_configuration/making_xml_profiles.rst index 33fd7a8880..b73ba7e1b3 100644 --- a/docs/fastdds/xml_configuration/making_xml_profiles.rst +++ b/docs/fastdds/xml_configuration/making_xml_profiles.rst @@ -38,7 +38,7 @@ The following sections will show implementation examples for each of these profi The :ref:`examplexml` section shows an XML file with all the possible configurations and profile types. This example is useful as a quick reference to look for a particular property and how to use it. The - `Fast DDS XSD scheme `_ + :fastdds-blob:`Fast DDS XSD scheme ` can be used as a quick reference too. .. _loadingapplyingprofiles: @@ -218,6 +218,6 @@ It also gives the participant a name that mixes literal text with the content fr .. warning:: - The `Fast DDS XSD schema `_ + The :fastdds-blob:`Fast DDS XSD schema ` does not support the environment variables expansion feature, so validation of an XML file with environment variables expansion expressions will fail. diff --git a/docs/fastddsgen/pubsub_app/includes/sum_and_next_steps.rst b/docs/fastddsgen/pubsub_app/includes/sum_and_next_steps.rst index 2582884993..f523c7e344 100644 --- a/docs/fastddsgen/pubsub_app/includes/sum_and_next_steps.rst +++ b/docs/fastddsgen/pubsub_app/includes/sum_and_next_steps.rst @@ -5,6 +5,6 @@ In this tutorial, a publisher/subscriber DDS application using *Fast DDS-Gen* ha The tutorial also describes how to generate IDL files that contain the description of the Topic data type. To continue developing DDS applications please take a look at the -`eProsima Fast DDS examples on github `_ +:fastdds-tree:`eProsima Fast DDS examples on github ` for ideas on how to improve this basic application through different configuration options, and also for examples of advanced *Fast DDS* features. diff --git a/docs/fastddsgen/rpc_calculator_basic_app/includes/app.rst b/docs/fastddsgen/rpc_calculator_basic_app/includes/app.rst new file mode 100644 index 0000000000..9aeb364aab --- /dev/null +++ b/docs/fastddsgen/rpc_calculator_basic_app/includes/app.rst @@ -0,0 +1,280 @@ +Writing the application source code +-------------------------------------------- + +Now that the interface source code has been generated, the next step is to generate the application source code. +All the following files should be created in the ``workspace_CalculatorBasic/src`` directory. + +.. _fastddsgen_rpc_calculator_basic_server_application: + +Server application +^^^^^^^^^^^^^^^^^^ + +The first step is to create the server application. Running it, the user can run an RPC server +ready to process requests from client applications and send replies. + +Create a ``CalculatorServer.cpp`` file and +copy the following code into the file: + +.. literalinclude:: /../code/Examples/C++/RpcClientServerBasic/src/CalculatorServer.cpp + :language: cpp + +Additionally, create a ``ServerImplementation.hpp`` file with the implementation of the server-side operations: + +.. literalinclude:: /../code/Examples/C++/RpcClientServerBasic/src/ServerImplementation.hpp + :language: cpp + +.. warning:: + The user implementation of the server-side operations should be placed in a separate file + and inherit from the ``CalculatorServerImplementation`` struct to avoid being overridden + by *Fast DDS-Gen* when IDL interface's source code is regenerated. User should use this derived + class when creates the server instance. + +Examining the code +"""""""""""""""""" + +The ``CalculatorServer.cpp`` file contains the implementation of the ``Server`` class, formed by +a Server instance and its related DomainParticipant instance: + +.. literalinclude:: /../code/Examples/C++/RpcClientServerBasic/src/CalculatorServer.cpp + :language: cpp + :start-after: //!--SERVER_PROTECTED_MEMBERS + :end-before: //!-- + +When a ``Server`` instance is initialized, both ``DomainParticipant`` and ``CalculatorServer`` instances +are created. The ``CalculatorServer`` instance is created using the previously created +``DomainParticipant`` instance, the implementation of the server-side IDL interface's operations +and the name of the RPC service: + +.. literalinclude:: /../code/Examples/C++/RpcClientServerBasic/src/CalculatorServer.cpp + :language: cpp + :start-after: //!--INIT + :end-before: //!-- + +Once all the Server's entities are created, the RPC server is started using the +``CalculatorServer::run()`` method: + +.. literalinclude:: /../code/Examples/C++/RpcClientServerBasic/src/CalculatorServer.cpp + :language: cpp + :start-after: //!--RUN + :end-before: //!-- + +Similarly, when the application is stopped, the +RPC server is also stopped using the ``CalculatorServer::stop()`` method: + +.. literalinclude:: /../code/Examples/C++/RpcClientServerBasic/src/CalculatorServer.cpp + :language: cpp + :start-after: //!--STOP + :end-before: //!-- + +Before finishing the application, all the internal DDS and RPC entities involved in the RPC +communication are deleted by calling ``CalculatorServer`` and ``DomainParticipant`` destructors: + +.. literalinclude:: /../code/Examples/C++/RpcClientServerBasic/src/CalculatorServer.cpp + :language: cpp + :start-after: //!--DESTRUCTOR + :end-before: //!-- + +The ``main()`` function contains all the steps described above. It runs the ``Server::run()`` method +in a different thread to allow the user to stop the server by sending a signal (for example, Ctrl+C): + +.. literalinclude:: /../code/Examples/C++/RpcClientServerBasic/src/CalculatorServer.cpp + :language: cpp + :start-after: //!--MAIN + :end-before: //!-- + +Client application +^^^^^^^^^^^^^^^^^^ + +Now, we will create the client application. Running it, a new RPC client will be created +and ready to send requests to the server application. User can send requests +by specifying the operation name and through the CLI, and the results +will be printed on the screen after receiving the reply from the server. + +Create a ``CalculatorClient.cpp`` file with this +:fastdds-docs-tree:`content `: + +Examining the code +"""""""""""""""""" + +The ``CalculatorClient.cpp`` file contains the implementation of the ``Client`` class, formed by +a ``Calculator`` client instance and its related ``DomainParticipant``. +Additionally, the operation to be performed is also stored: + +.. literalinclude:: /../code/Examples/C++/RpcClientServerBasic/src/CalculatorClient.cpp + :language: cpp + :start-after: //!--CLIENT_PROTECTED_MEMBERS + :end-before: //!-- + +All this members are initialized calling ``init()`` method, in the same way as in the ``Server`` class: + +.. literalinclude:: /../code/Examples/C++/RpcClientServerBasic/src/CalculatorClient.cpp + :language: cpp + :start-after: //!--INIT + :end-before: //!-- + +When the client performs an operation (*i.e*: sends a request), three different situations can happen: + +* The operation is successful (*i.e* the client sends the request and receives the reply + from the server). +* The operation fails (*i.e* the client sends the request but an exception occurs). For example, if the + operation is not implemented in the server side or an RPC exception occurs (for example, if computing + the result raises an ``OverflowException``). +* A timeout is configured to avoid blocking the thread infinitely if no replies are received for a given + request and the maximum time is reached. + In this case, the client will stop waiting for the reply and return a timeout error. + +To process each operation in the same way, the following design pattern is used: each operation implements +a ``Operation`` abstract class, which contains a ``execute()`` method: + +.. literalinclude:: /../code/Examples/C++/RpcClientServerBasic/src/CalculatorClient.cpp + :language: cpp + :start-after: //!--OPERATION_INTERFACE + :end-before: //!-- + +After calling this method, it will +return an enum ``OperationStatus`` indicating the result of the operation (and addressing each of the +cases previously described): + +.. literalinclude:: /../code/Examples/C++/RpcClientServerBasic/src/CalculatorClient.cpp + :language: cpp + :start-after: //!--OPERATION_STATUS + :end-before: //!-- + +It makes easier to add new operations (for example, ``@feed`` operations) without modifying the +main execution flow. Each operation stores the data required to execute the operation, +for example, a reference to the client used to send the request, as well as the operation input data. + +When ``RepresentationLimits`` operation is executed, client sends a request to the server and waits +for the server reply, printing the received result. If something fails or the timeout is exceeded (for example, +if no servers are available), the operation fails: + +.. literalinclude:: /../code/Examples/C++/RpcClientServerBasic/src/CalculatorClient.cpp + :language: cpp + :start-after: //!--REPRESENTATION_LIMITS + :end-before: //!-- + +Similarly for ``Addition`` operation: + +.. literalinclude:: /../code/Examples/C++/RpcClientServerBasic/src/CalculatorClient.cpp + :language: cpp + :start-after: //!--ADDITION + :end-before: //!-- + +and ``Subtraction`` operation: + +.. literalinclude:: /../code/Examples/C++/RpcClientServerBasic/src/CalculatorClient.cpp + :language: cpp + :start-after: //!--SUBTRACTION + :end-before: //!-- + +The operation to be performed is configured from the parsed CLI input +using the ``set_operation()`` factory method. Input operands are hardcoded to simply +the input parsing: + +.. literalinclude:: /../code/Examples/C++/RpcClientServerBasic/src/CalculatorClient.cpp + :language: cpp + :start-after: //!--SET_OPERATION + :end-before: //!-- + +When ``send_request()`` method is called, the input operation is configured and the client executes it. +A boolean is returned, ``true`` if the operation is successful or ``false`` otherwise: + +.. literalinclude:: /../code/Examples/C++/RpcClientServerBasic/src/CalculatorClient.cpp + :language: cpp + :start-after: //!--SEND_REQUEST + :end-before: //!-- + +Finally, the ``main()`` function process the user input (specifying the operation to be performed), +initializes a new client and tries to execute the operation until a max number of attempts ``n_attempts``: + +.. literalinclude:: /../code/Examples/C++/RpcClientServerBasic/src/CalculatorClient.cpp + :language: cpp + :start-after: //!--MAIN + :end-before: //!-- + +Note that, before sending the first request, we are waiting some time to make sure +that all internal DDS entities are matched. This way, we avoid losing requests by sending them +before client and server are discovered each other. + +Update the *CMakeLists.txt* file +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Before building the application, we need to update the *CMakeLists.txt* file of the workspace +to add the executable and the required libraries. The following code should be added at the end +of the file: + +.. literalinclude:: /../code/Examples/C++/RpcClientServerBasic/CMakeLists.txt + :language: cmake + :lines: 44-56 + +Build the application +--------------------- + +Now that all the files are created, the application can be built. +To do so, open a terminal in the *workspace_CalculatorBasic* directory and run the following commands: + +.. code-block:: bash + + mkdir build && cd build + cmake .. + cmake --build . + +The generated executable will be located in the *build* subdirectory. + +Run the application +------------------- + +To test the application, open two terminals in the *workspace_CalculatorBasic* directory +and execute the following commands: + +* In the first terminal, run the server application: + +.. code-block:: bash + + ./build/basic_server + +* In the second terminal, run the client application and specify the operation to be performed. + For example, to perform an addition of two numbers (5 and 3), run the following command: + +.. code-block:: bash + + ./build/basic_client add + +You should see the result of the operation printed on the screen: + +.. code-block:: shell-session + + Attempting to send request, attempt 2/10 + Addition result: 5 + 3 = 8 + Request sent successfully + +The output of the rest operations should be similar to the following: + +* Subtraction: + +.. code-block:: shell-session + + ./build/basic_client sub + + Attempting to send request, attempt 2/10 + Subtraction result: 5 - 3 = 2 + Request sent successfully + + +* Representation limits: + +.. code-block:: shell-session + + ./build/basic_client rep + + Attempting to send request, attempt 2/10 + Representation limits received: min_value = -2147483648, max_value = 2147483647 + Request sent successfully + +Next steps +---------- + +The application that we have created only contains basic asynchronous RPC operations. +This example can be extended to include streaming of input and output data by defining +``@feed`` annotated operations in the interface of the IDL file. An example of this can be seen +in the next section (:ref:`fastddsgen_rpc_calculator_feed_app_intro`). diff --git a/docs/fastddsgen/rpc_calculator_basic_app/intro.rst b/docs/fastddsgen/rpc_calculator_basic_app/intro.rst new file mode 100644 index 0000000000..48b001381b --- /dev/null +++ b/docs/fastddsgen/rpc_calculator_basic_app/intro.rst @@ -0,0 +1,32 @@ +.. include:: ../../03-exports/aliases-api.include +.. include:: ../../03-exports/roles.include + +.. _fastddsgen_rpc_calculator_basic_app_intro: + +Building a RPC Client/Server application +======================================== + +*Fast DDS-Gen* can be used to generate the source code required to +build a *Remote Procedure Calls* (RPC) Client/Server application from an IDL file. + +This section provides an overview of the steps required to build a *Fast DDS* RPC application from scratch, +following the `RPC over DDS specification `_, using the *Fast DDS-Gen* tool. + +The example consists of a simple CLI tool for a calculator service that allows the client +to call asynchronously the following operations: + +* **addition**: Adds two 32-bit integers and returns the result. +* **subtraction**: Subtracts two 32-bit integers and returns the result. +* **representation_limits**: Returns the minimum and maximum representable values for a 32-bit integer. + +The entire example source code is available in this +:fastdds-docs-tree:`link ` + +.. include:: includes/background.rst +.. include:: includes/prerequisites.rst +.. include:: includes/workspace.rst +.. include:: includes/dependencies.rst +.. include:: includes/cmake.rst +.. include:: includes/idl.rst +.. include:: includes/code_generation.rst +.. include:: includes/app.rst diff --git a/docs/fastddsgen/rpc_calculator_feed_app/includes/app.rst b/docs/fastddsgen/rpc_calculator_feed_app/includes/app.rst new file mode 100644 index 0000000000..864f9a8221 --- /dev/null +++ b/docs/fastddsgen/rpc_calculator_feed_app/includes/app.rst @@ -0,0 +1,288 @@ +Writing the application source code +-------------------------------------------- + +Now that the interface source code has been generated, the next step is to generate the application source code. +All the following files should be created in the *workspace_CalculatorFeed/src* directory. + +Server application +^^^^^^^^^^^^^^^^^^ + +The server application that we will create is exactly the same as the one created in the basic example. +Thus, copy the code provided in :ref:`fastddsgen_rpc_calculator_basic_server_application` +into the ``CalculatorServer.cpp`` file. + +Additionally, create a ``ServerImplementation.hpp`` file with the implementation of the server-side operations: + +.. literalinclude:: /../code/Examples/C++/RpcClientServerFeed/src/ServerImplementation.hpp + :language: cpp + +.. warning:: + The user implementation of the server-side operations should be placed in a separate file + and inherit from the ``CalculatorServerImplementation`` struct to avoid being overridden + by *Fast DDS-Gen* when IDL interface's source code is regenerated. User should use this derived + class when creates the server instance. + +Client application +^^^^^^^^^^^^^^^^^^ + +The client application extends the functionality of the basic example by adding the new operations +defined in the IDL file. Create a ``CalculatorClient.cpp`` file with this +:fastdds-docs-tree:`content `: + +Examining the code +"""""""""""""""""" + +The ``CalculatorClient.cpp`` file extends the code of the basic example by adding the new operations +defined in the IDL file, following the same inheritance schema. + +Notice the following facts: + +* The operations that expect an output feed (*i.e* ``FibonacciSeq``, ``Accumulator`` and ``Filter`` operations) + store internally an ``RpcClientReader`` reference. As the client expects multiple replies from the server for + the same request, it will use the ``RpcClientReader::read()`` method to read the results until the output feed + is closed by the server. + +* The operations that expect an input feed for some of their parameters + (*i.e* ``SumAll``, ``Accumulator`` and ``Filter`` operations) store internally an + ``RpcClientWriter`` reference. As the client will send multiple values to the server, it will use the + ``RpcClientWriter::write()`` method to send the values and the ``RpcClientWriter::finish()`` method to notify + the server that the input feed is finished. + +For input feed operations, a simple ``InputFeedProcessor`` class is used to parse the input user data from terminal. +It allows the user to send a new number or close the input feed by accepting the dialog with empty data: + +.. literalinclude:: /../code/Examples/C++/RpcClientServerFeed/src/CalculatorClient.cpp + :language: cpp + :start-after: //!--INPUT_FEED_PROCESSOR + :end-before: //!-- + +For output feed operations, each time that ``Operation::execute()`` is called, the client will process the data +of only one reply, so multiple calls will be required to read all the results. To address this, the +``Operation::execute()`` method will return an enum ``OperationStatus::PENDING`` when a new output feed value +is read, indicating that the output feed has not been closed yet and that client should process more data. +When the output feed is closed, the ``Operation::execute()`` method will return an enum +``OperationStatus::SUCCESS``, indicating that the operation has successfully read each of the output feed values: + +.. literalinclude:: /../code/Examples/C++/RpcClientServerFeed/src/CalculatorClient.cpp + :language: cpp + :start-after: //!--OPERATION_STATUS + :end-before: //!-- + +A more detailed description of each operation execution is provided below: + +* ``FibonacciSeq`` stores the number of requested numbers using using a ``n_results_`` member. + The request is sent by calling the ``client_->fibonacci_seq()`` method, which returns an + ``RpcClientReader`` object. The client will read the results using the ``RpcClientReader::read()`` method + in each ``FibonacciSeq::execute()`` call, until the output feed is closed by the server. + +.. literalinclude:: /../code/Examples/C++/RpcClientServerFeed/src/CalculatorClient.cpp + :language: cpp + :start-after: //!--FIBONACCI_SEQ + :end-before: //!-- + +* In the first call of the ``SumAll::execute()`` method, the client will create an + ``RpcClientWriter`` object by calling the ``client_->sum_all()`` method and passing it as an output parameter. + Then, the input feed is parsed from the user terminal using the ``InputFeedProcessor`` class, sending each + input data value to the server using the ``RpcClientWriter::write()`` method. When the user finish the input feed + (by accepting the terminal dialog with an empty value), + the client closes the input feed using the ``RpcClientWriter::finish()`` + and waits for the reply. When the reply is received, + the result is stored in ``result_`` member and printed on the screen. + +.. literalinclude:: /../code/Examples/C++/RpcClientServerFeed/src/CalculatorClient.cpp + :language: cpp + :start-after: //!--SUM_ALL + :end-before: //!-- + +* For the ``Accumulator`` operation, both ``RpcClientReader`` and ``RpcClientWriter`` objects are created + by calling the ``client_->accumulator()`` method. The client will read the results using the + ``RpcClientReader::read()`` method in each ``Accumulator::execute()`` call, until the output feed is closed + by the server. The input feed is parsed from the user terminal using the ``InputFeedProcessor`` class, + sending each input data value to the server using the ``RpcClientWriter::write()`` method. + When the user finish the input feed (by accepting the terminal dialog with an empty value), + the client closes the input feed using the ``RpcClientWriter::finish()`` + and waits for the reply. + Each time that ``Accumulator::execute()`` is called, the client sends a new input value to the server + and waits for the accumulated sum result, until the input and output feeds are closed. + +.. literalinclude:: /../code/Examples/C++/RpcClientServerFeed/src/CalculatorClient.cpp + :language: cpp + :start-after: //!--ACCUMULATOR + :end-before: //!-- + +* For the ``Filter`` operation, both ``RpcClientReader`` and ``RpcClientWriter`` objects are created + by calling the ``client_->filter()`` method. First, the client will send to the server all the input feed + data values and the filter kind using the ``RpcClientWriter::write()`` method. + Then, each result is processed in a ``Filter::execute()`` call, until the output feed is closed by the server. + To simplify the input parsing, the filter is fixed to be ``FilterKind::EVEN``, *i.e* the input feed is filtered + to only return even numbers: + +.. literalinclude:: /../code/Examples/C++/RpcClientServerFeed/src/CalculatorClient.cpp + :language: cpp + :start-after: //!--FILTER + :end-before: //!-- + +``ClientApp::set_operation()`` has also been extended to include the new operations: + +.. literalinclude:: /../code/Examples/C++/RpcClientServerFeed/src/CalculatorClient.cpp + :language: cpp + :start-after: //!--SET_OPERATION + :end-before: //!-- + +Finally, a minimal change is required in the ``send_request()`` method to handle the feed operations: + +.. literalinclude:: /../code/Examples/C++/RpcClientServerFeed/src/CalculatorClient.cpp + :language: cpp + :start-after: //!--FEED_LOOP + :end-before: //!-- + +Note that this change does not modify the behavior of the non-feed operations, as they never return +``OperationStatus::PENDING`` status. + +Update the *CMakeLists.txt* file +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Before building the application, we need to update the *CMakeLists.txt* file of the workspace +to add the executable and the required libraries. The following code should be added at the end +of the file: + +.. literalinclude:: /../code/Examples/C++/RpcClientServerFeed/CMakeLists.txt + :language: cmake + :lines: 44-56 + +Build the application +--------------------- + +Now that all the files are created, the application can be built. +To do so, open a terminal in the *workspace_CalculatorFeed* directory and run the following commands: + +.. code-block:: bash + + mkdir build && cd build + cmake .. + cmake --build . + +The generated executable will be located in the *build* subdirectory. + +Run the application +------------------- + +To test the application, open two terminals in the *workspace_CalculatorFeed* directory +and execute the following commands: + +* In the first terminal, run the server application: + +.. code-block:: bash + + ./build/feed_server + +* In the second terminal, run the client application and specify the operation to be performed. + For example, to get the first five numbers of the Fibonacci sequence, run the following command: + +.. code-block:: bash + + ./build/feed_client fib + +You should see the result of the operation printed on the screen: + +.. code-block:: shell-session + + Attempting to send request, attempt 1/10 + Configuring FIBONACCI operation with 5 results + Fibonacci sequence value: 1 + Fibonacci sequence value: 1 + Fibonacci sequence value: 2 + Fibonacci sequence value: 3 + Fibonacci sequence value: 5 + Fibonacci sequence feed finished + Request sent successfully + +The output of the rest operations should be similar to the following: + +* SumAll: + +.. code-block:: shell-session + + ./build/feed_client sumall + + Configuring SUM_ALL operation + Input feed help: + - Enter a number to process it. + - Press Enter without typing anything to close the input feed. + 2 + Input sent: 2 + 3 + Input sent: 3 + -32 + Input sent: -32 + -25 + Input sent: -25 + -12 + Input sent: -12 + + Input feed closed. + Sum result: -64 + Request sent successfully + + +* Accumulator: + +.. code-block:: shell-session + + ./build/feed_client acc + + Configuring ACCUMULATOR operation + Input feed help: + - Enter a number to process it. + - Press Enter without typing anything to close the input feed. + 16 + Input sent: 16 + Accumulated sum: 16 + 32 + Input sent: 32 + Accumulated sum: 48 + -13 + Input sent: -13 + Accumulated sum: 35 + -22 + Input sent: -22 + Accumulated sum: 13 + -1 + Input sent: -1 + Accumulated sum: 12 + + Input feed closed. + Accumulator feed finished + Request sent successfully + +* Filter: + +.. code-block:: shell-session + + ./build/feed_client filter + + Configuring FILTER operation for even numbers + Input feed help: + - Enter a number to process it. + - Press Enter without typing anything to close the input feed. + 2 + Input sent: 2 + 11 + Input sent: 11 + 32 + Input sent: 32 + 15 + Input sent: 15 + -23 + Input sent: -23 + -15 + Input sent: -15 + 4 + Input sent: 4 + + Input feed closed. + Filtered sequence value: 2 + Filtered sequence value: 32 + Filtered sequence value: 4 + Filtered sequence feed finished + Request sent successfully diff --git a/docs/fastddsgen/rpc_calculator_feed_app/intro.rst b/docs/fastddsgen/rpc_calculator_feed_app/intro.rst new file mode 100644 index 0000000000..8e3bc62d9f --- /dev/null +++ b/docs/fastddsgen/rpc_calculator_feed_app/intro.rst @@ -0,0 +1,28 @@ +.. include:: ../../03-exports/aliases-api.include +.. include:: ../../03-exports/roles.include + +.. _fastddsgen_rpc_calculator_feed_app_intro: + +Building a RPC Client/Server application with data streaming +============================================================ + +*Fast DDS-Gen* supports the generation of source code for interfaces that specify +data streaming operations using the ``@feed`` builtin annotation. + +This section extends the previously discussed example of a calculator service +(see :ref:`fastddsgen_rpc_calculator_basic_app_intro`) to include the following data streaming operations: + +* **fibonacci_seq**: Returns a feed of results with the n_results first elements of the Fibonacci sequence. +* **sum_all**: Returns the sum of all the received values through a feed when it is closed. +* **accumulator**: Returns a feed of results with the sum of all received values. +* **filter**: Returns a feed of results with the received values that match an input filter. + +The entire example source code is available in this +:fastdds-docs-tree:`link ` + +.. include:: includes/background.rst +.. include:: includes/workspace.rst +.. include:: includes/cmake.rst +.. include:: includes/idl.rst +.. include:: includes/code_generation.rst +.. include:: includes/app.rst diff --git a/docs/fastddsgen/usage/usage.rst b/docs/fastddsgen/usage/usage.rst index 6460a0c661..142151c5cf 100644 --- a/docs/fastddsgen/usage/usage.rst +++ b/docs/fastddsgen/usage/usage.rst @@ -74,8 +74,13 @@ Where the options are: * - -extrastg