17.1.2. RPC Requester

A Requester is the RPC Entity used in the communication at the client side, sending Request samples and processing the received Reply samples.

Since each Requester 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 Requester can be accessed using Requester::get_service_name() method.

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

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

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

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

\[requester\_state \leq service\_state\]

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

17.1.2.1. Creating a Requester

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

Note

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

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

All Requester’s DDS entities Qos can be configured manually when creating the Requester instance using RequesterQos. Before creating the Requester, Service validates DataWriterQos and DataReaderQos provided in the RequesterQos instance: if some field is not valid, Service will notify it to the user using a log message error and Requester 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 RequesterQos instance is created.

Note

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

17.1.2.2. Enabling and disabling a Requester

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

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

User can access to the DataWriter and DataReader instances using Requester::get_requester_writer() and Requester::get_requester_reader() methods, respectively.

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

Warning

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

17.1.2.3. Deleting a Requester

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

This method can be called on Requesters 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 Requester.

Note

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

17.1.2.4. Sending and receiving samples

Request samples can be sent using the Requester::send_request() method. When this method is called, the created DataWriter sends a new Request sample with the provided data and fills the related_sample_identity field in the input RequestInfo struct with the Guid_t of the DataWriter and a sequence number.

Similarly, received Reply samples are taken using Requester::take_reply() method. When this method is called, Requester takes received Reply samples from the history of the DataReader and fills the provided RequestInfo struct with the related_sample_identity of the received Reply sample. This way, user can match Request and Reply samples by checking if the related_sample_identity attributes of their RequestInfo instances are equal.

The Requester public API provides two overloads for Requester::take_reply(), 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 Requester::return_loan() method after finishing processing the samples.

Warning

If a Request sample is sent before discovering a Replier, the middleware will not be able to deliver the Request sample and will discard it. The endpoint matching algorithm described in RPC over DDS Standard 7.6.2.2 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.2.5. Example

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

        ReturnCode_t ret;

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

        /* Create a new Requester instance */
        RequesterQos requester_qos;

        Requester* requester = participant->create_service_requester(service, requester_qos);
        if (!requester)
        {
            // Error
            return;
        }

        /* Send a new Request sample and check if a received Reply sample is a match */
        // 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 Requester unless
        //  the creation of the internal DataWriter and DataReader failed.
        //  We can make sure that they have been created correctly by checking if the Requester is enabled.
        if (!requester->is_enabled())
        {
            // Error
            return;
        }

        RequestInfo expected_request_info;
        RequestInfo received_request_info;
        // Create a new Request sample
        void* request_data = request_type_support->create_data();
        ret = requester->send_request(request_data, expected_request_info);
        if (RETCODE_OK != ret)
        {
            // Error
            return;
        }
 
        // Wait for some time until a Reply sample is received
        requester->get_requester_reader()->wait_for_unread_message(Duration_t{3,0});

        void* data = nullptr;
        ret = requester->take_reply(data, received_request_info);
        if (RETCODE_OK != ret)
        {
            // Error
            return;
        }
    
        if (expected_request_info.related_sample_identity == received_request_info.related_sample_identity)
        {
          // Received Reply sample is associated to the sent Request sample
        }
    
        // Delete created Requester
        ret = participant->delete_service_requester(requester->get_service_name(), requester);
        if (RETCODE_OK != ret)
        {
          // Error
          return;
        }