Chapter 50 RPC Client
A client allows making remote function calls to the services that it is subscribed to, and receives the results to those calls from those services. To communicate, a client and a service must use the same service name, and be associated with the same DDS domain_id.
A client has an associated Requester. All the entities required by the Requester—including the DomainParticipant, the request and reply Topics, the DataWriter for writing the requests, and a DataReader for reading replies—are automatically created when the client is created.
You can configure the QoS for the underlying DataWriter and DataReader by setting them either in the DomainParticipant that the client is using or in a QoS Profile.
A client is also defined as an interface in IDL with the annotation @service("DDS") or @service.
50.1 Creating a Client
To create a client/Requester, you need a ClientParams, a DomainParticipant, and a service name:
dds::domain::DomainParticipant client_participant(domain_id);
dds::rpc::ClientParams client_params(client_participant);
client_params.service_name("Example RobotControl Service");
The service_name() function is used to generate the names of the request and reply Topics that the internal Requester and Replier will use to communicate. For example, if the service name is “MyService”, the Topic names for the Requester and Replier will be “MyServiceRequest” and “MyServiceReply”, respectively. Therefore, for communication to occur, you must use the same service name when creating the Requester and the Replier entities. If you want to use Topic names different from the ones that would be derived from the ServiceName, you can override the default names by setting the actual request and reply Topic names using the request_topic_name() and reply_topic_name() accessors to the RequesterParams structure prior to creating the Requester.
To create a client, use the constructor that receives the ClientParams:
RobotControlClient client(client_params);
Once you have created a client, you can use it to perform the operations in Table 50.2 Client Operations.
50.2 Setting the Client Parameters
To change the ClientParams that can be used when creating a client/Requester, you can use the operations listed in Table 50.1 Operations to Set Client Parameters.
Table 50.1 Operations to Set Client Parameters
|
Operation |
Description |
|---|---|
|
function_call_max_wait |
Specifies the maximum wait time for all the remote calls. |
|
service_name |
The service name that Replier and Requesters use to match and communicate. |
|
request_topic_name |
Sets a specific request Topic name. |
|
reply_topic_name |
Sets a specific reply Topic name. |
|
datawriter_qos |
Sets the Quality of Service of the request DataWriter. |
|
datareader_qos |
Sets the Quality of Service of the request DataReader. |
|
publisher |
Sets a specific Publisher. |
|
subscriber |
Sets a specific Subscriber. |
|
request_type |
The request type, when DynamicData is used. |
|
reply_type |
The reply type, when DynamicData is used. |
50.3 Summary of Client Operations
There are several kinds of operations an application can perform using the client/Requester:
- Waiting for service to be discovered
- Making remote function calls (synchronous or asynchronous)
The Client operations are summarized in Table 50.2 Client Operations.
The <operation_name> comes from the IDL file. In the example IDL file in Chapter 49 RPC Service, the client will have an operation with the same name as void command(Command com). In the case of an attribute, such as attribute long test, the client in C++ will have two operations, named long get_operation_test(); and void set_operation_test(long test);. The <attribute_name> is defined by adding 'attribute' before its type: attribute <type> <name>, such as attribute long test.
|
Operation |
Description |
Reference |
|
|---|---|---|---|
|
Waiting for Service |
wait_for_service |
Waits for services. |
|
|
Sending Request |
<operation_name> |
Makes a synchronous remote function call. |
|
|
<get/set>_attribute_<attribute_name> |
|||
|
<operation_name>_async |
Makes an asynchronous remote function call. |
||
|
<get/set>_attribute_<attribute_name>_async |
|||
|
Getting Underlying Entities |
request_datawriter |
Retrieves the underlying DataWriter that sends the request. |
|
|
|
reply_datareader |
Retrieves the underlying DataReader that receives the replies. |
50.4 Waiting for Services
The client/Requester provides two operations that can be used to wait for services:
1. wait_for_service (const dds::core::Duration &maxWait)
2. wait_for_service ()
The first operation blocks until one service is available or until maxWait time has passed, whichever comes first. The second operation blocks until one service is available for an unlimited period of time.
Calling wait_for_service() is required to ensure that the discovery process has finished. If the client/Requester sends a request without waiting, the result will be an exception if the discovery process has not finalized.
50.5 Making Remote Function Calls
To send a request, use the operations and attributes defined in the interface:
<operation_name>(<args>)
get_attribute_<attribute_name>(<args>)
set_attribute_<attribute_name>(<args>)
These functions block the client until the service sends a reply.
The client also provides asynchronous functions that don’t block the client:
<operation_name>_async(<args>)
get_attribute_<attribute_name>_async(<args>)
set_attribute_<attribute_name>_async(<args>)
Asynchronous functions return a std::future that will contain the result when it’s received:
std::future<float> future_speed = client.getSpeed_async();
...
std::cout << "Current speed is " << future_speed.get() << std::endl;
The call to std::future::get() provides the result if it’s already available or blocks until it is.