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.

Table 50.2 Client Operations

Operation

 

Description

Reference

Waiting for Service

wait_for_service

Waits for services.

50.4 Waiting for Services

Sending Request

<operation_name>

Makes a synchronous remote function call.

50.5 Making Remote Function Calls

<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.

 

Chapter 51 Accessing Underlying DataWriters and DataReaders

 

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.