17.1.3. RPC Replier

A Replier is the RPC Entity used in the communication at the server side, processing the received Request samples and sending Reply samples back to the Requester when the result of the operation is ready.

Since each Replier is used in only one Request/Reply communication, represented by a Service, all of them are registered in a Service instance when created. The name of the service associated with a Replier can be accessed using Replier::get_service_name() method.

Similarly to the rest of the RPC entities, Replier instances can be enabled or disabled, according to whether they contain DDS entities or not:

  • Each active Replier contains two DDS entities: a DataWriter, which is used to publish Reply samples, and a DataReader, which is used to receive Request samples.

  • Each disabled Replier does not contain any DDS entity and is ignored by the middleware. Therefore, it cannot take requests nor send replies.

For consistency, the states of each Replier instance and its associated Service must follow the compatibility rule below:

\[replier\_state \leq service\_state\]

where replier_state is the state of the Replier instance and service_state is the state of the associated Service, and the order of the states is defined as \(disabled < enabled\).

17.1.3.1. Creating a Replier

A new Replier instance can be created in an enabled or disabled Service using DomainParticipant::create_service_replier() method. When a new Replier instance is created, it is registered internally in the Service. Each Service can contain multiple Replier instances.

Note

Following the state compatibility rule, calling DomainParticipant::create_service_replier() using a disabled Service instance as input parameter creates a disabled Replier.

Reciprocally, calling DomainParticipant::create_service_replier() with an enabled Service instance tries to return an enabled Replier. If the process of enabling the Replier fails (i.e., the creation of the internal DDS entities), DomainParticipant::create_service_replier() returns nullptr.

All Replier’s DDS entities Qos can be configured manually when creating the Replier instance using ReplierQos. Before creating the Replier, Service validates DataWriterQos and DataReaderQos provided in the ReplierQos instance: if some field is not valid, Service will notify it to the user using a log message error and Replier is not created.

User must ensure that ReliabilityQosPolicyKind of both DataWriter and DataReader are set to RELIABLE_RELIABILITY_QOS. This is configured automatically when a new ReplierQos instance is created.

Note

Before creating a new Replier, user must create its associated Service instance in the DomainParticipant. If a null pointer or a Service associated with a different participant are provided, Replier is not created and DomainParticipant::create_service_replier() method returns a null pointer.

17.1.3.2. Enabling and disabling a Replier

Replier instances can be enabled or disabled using enable() and close() methods, respectively.

When a disabled Replier instance is enabled, a new DataWriter on the Reply topic and a new DataReader on Request topic are created using the Qos configured at creation through ReplierQos.

User can access to the DataWriter and DataReader instances using Replier::get_replier_writer() and Replier::get_replier_reader() methods, respectively.

Reciprocally, when an enabled Replier is disabled, its respective DataWriter and DataReader are destroyed, making the Replier not participate in the communication through the Service.

Warning

A disabled Replier does not contain DDS entities, so Replier::get_replier_writer() and Replier::get_replier_reader() return a null pointer in this case. The user must be responsible for checking that the Replier is enabled using the is_enabled() method before accessing the Replier’s internal DDS entities.

17.1.3.3. Deleting a Replier

A Replier instance can be unregistered from a Service and deleted using DomainParticipant::delete_service_replier() method.

This method can be called on Repliers in any state: if enabled, it will try to disable the instance, returning a ReturnCode_t error if it was not possible to close the Replier.

Note

If there is no Service with the provided service_name or the Replier is associated with a different Service, DomainParticipant::delete_service_replier() method will return a ReturnCode_t error.

17.1.3.4. Sending and receiving samples

Request samples are taken using the Replier::take_request() method. When this method is called, Replier takes received Request samples from the history of the internal DataReader and fills the related_sample_identity member of the provided RequestInfo struct with the related_sample_identity` of the received Request sample.

The Replier public API provides two overloads for Replier::take_request(), depending on whether you want to take only the next sample or all samples from the DataReader history.

Note

In case of using the overload that takes all samples, the loan must be returned to avoid memory problems. The user must therefore be responsible for calling the Replier::return_loan() method after finishing processing the samples.

After processing the Request, Replier sends a new Reply sample using the Replier::send_reply() method and passing the previously created RequestInfo as an input parameter. When Replier::send_reply() method is called, the created DataWriter sends a new Reply sample with the provided data and the related_sample_identity of the Request sample that originated the Reply, to allow Request and Reply samples correlation at the Requester side.

Warning

If a Reply sample is sent before discovering the Requester Reply topic DataReader, the middleware will not be able to deliver the Reply sample and will discard it. The endpoint matching algorithm described in RPC over DDS Standard will be implemented in future releases.

Warning

RPC over DDS implementation in Fast DDS is designed to be incompatible with Listeners. If user needs to process status changes, it can be done creating a WaitSet on a different thread.

17.1.3.5. Example

The following code snippet shows how to use a Replier instance:

        ReturnCode_t ret;

        // Get a valid service
        Service* service = participant->find_service("Service");
        if (!service)
        {
            // Error
            return;
        }

        /* Create a new Replier instance */
        ReplierQos replier_qos;

        Replier* replier = participant->create_service_replier(service, replier_qos);
        if (!replier)
        {
            // Error
            return;
        }

        /* Take a received Request sample and send a new Reply sample */

        // Make sure that all RPC Entities are enabled
        if (!service->is_enabled())
        {
            ret = service->enable();
            if (RETCODE_OK != ret)
            {
                // Error
                return;
            }
        }

        //  Enabling the service enables the registered Replier unless
        //  the creation of the internal DataWriter and DataReader failed.
        //  We can make sure that they have been created correctly by checking if the Replier is enabled.
        if (!replier->is_enabled())
        {
            // Error
            return;
        }

        RequestInfo received_request_info;

        // Wait for some time until a Request sample is received
        replier->get_replier_reader()->wait_for_unread_message(Duration_t{3,0});

        void* received_data = nullptr;

        ret = replier->take_request(received_data, received_request_info);
        if (RETCODE_OK != ret)
        {
            // Error
            return;
        }

        // ... Process received data

        // Send a Reply with the received related_sample_identity
        void* reply_data = reply_type_support->create_data();
        ret = replier->send_reply(reply_data, received_request_info);
        if (RETCODE_OK != ret)
        {
            // Error
            return;
        }

        /* Delete created Replier */
        ret = participant->delete_service_replier(replier->get_service_name(), replier);
        if (RETCODE_OK != ret)
        {
          // Error
          return;
        }