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:
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.
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;
}