rve_rpc is essentially a service replacement. You may ask, then, what the advantages of rve_rpc are vs. services.

  • rve_rpc is topic based. This means any topic-based tools such as rosbag will work with rve_rpc. Note that things like rostopic echo will not work properly due to how rve_rpc works internally. A rostopic equivalent for rve_rpc is planned. Also, because it is topic based, rve_rpc clients are always connected to their servers, meaning every rve_rpc call is equivalent to a persistent service call.

  • rve_rpc allows multiple rpc methods to be called over a single connection. This means fewer lookups to the master, and the ability to order asynchronous calls to different rpc methods.

  • rve_rpc provides an asynchronous interface, both for server callback responses, and for client method calls. These could be implemented for services as well.

  • rve_rpc can use any pair of messages as a request/response, and does not require a separate definition or separate generated code.


rve_rpc was mainly designed for use by rve_interface_gen, so its API could probably use some work. Using it standalone is still fairly easy though.


A server callback looks something like this:

   1 void callback(rve_rpc::CallHandle<RequestMessage, ResponseMessage>& handle)
   2 {
   3   const RequestMessageConstPtr& req = handle.getRequest();
   4   // do stuff with req
   5   // ...
   6   TestResponsePtr res(new TestResponse);
   7   handle.respond(res);
   8 }

handle here allows you to either respond immediately (like in the example), or store it off and respond later. If an rve_rpc::CallHandle is destroyed without ever responding, it responds with an exception.

To indicate an error, you can either call handle.except("my error message goes here"), or you can throw an exception derived from std::exception from within your callback.

Setting up the server is done using the rve_rpc::Server class, like so:

   1 Server s("server_name", nh);
   2 s.addMethod<RequestMessage, ResponseMessage>("callMe", callback);
   3 s.ready();


Use of a client looks something like this:

   1 Client c("server_name", nh);
   2 Method<RequestMessage, ResponseMessage> m = c.addMethod<RequestMessage, ResponseMessage>("callMe");
   3 c.connect();
   5 ReqeustMessagePtr req(new RequestMessagePtr);
   6 // fill out req...
   7 ResponseMessageConstPtr res = m.call(req);

Alternatively, you can use it asynchronously like so:

   1 m.callAsync(req, callback);

where callback is of the form:

   1 void callback(const MethodResponse<ResponseMessage>& res)
   2 {
   3   if (res.error_code == rve_rpc::Response::SUCCESS)
   4   {
   5     const ResponseMessageConstPtr& msg = res.response;
   6     //...
   7   }
   8 }


An rve_rpc client/server pair operate on a "request/response" topic. For example:


These topics are always of the type rve_rpc::Request and rve_rpc::Response. The request/response messages are serialized into a uint8 array inside the container message. This is why "rostopic echo" won't give you what you might expect with these topics.

rve_rpc uses RequestWrapper and ResponseWrapper classes that serialize to the same wire format as the messages to support no-copy "serialization" when running intraprocess.

Wiki: rve_rpc (last edited 2010-11-18 03:16:05 by JoshFaust)