YARP
Yet Another Robot Platform
Specialized RPC ports

RPC stands for "Remote Procedure Call", and in YARP is used to refer to communication that consists of a message and a reply to that message.

Historically, YARP has stressed streaming communication (without replies) because in our experience it leads to more robust and scalable control logic. However, RPC definitely has its place, and this tutorial describes how to do it in YARP.

RPC using regular YARP ports

The basic mechanism for getting replies with YARP is described in the getting replies section of the Port Power tutorial. The RPC ports are a thin wrapper around regular ports, for the purpose of restricting flexibility rather than increasing it.

RPC using specialized YARP ports

If you intend to use ports for RPC alone, you can use the yarp::os::RpcClient and yarp::os::RpcServer classes. These are just like the regular yarp::os::Port class, except that streaming operation is prohibited. By using these classes, YARP can check for accidental misusage of the ports, and give more helpful error messages.

Here is an example server:

/*
* Copyright (C) 2006-2020 Istituto Italiano di Tecnologia (IIT)
* Copyright (C) 2006-2010 RobotCub Consortium
* All rights reserved.
*
* This software may be modified and distributed under the terms of the
* BSD-3-Clause license. See the accompanying LICENSE file for details.
*/
/*
* This is an example of using a specialized RpcServer port to receive
* and reply to messages. Regular YARP ports can do this as well (see
* summer.cpp), but use of RpcServer/RpcClient allows for better
* run-time checking of port usage to catch mistakes.
*/
#include <yarp/os/Bottle.h>
#include <yarp/os/Time.h>
#include <cstdio>
using namespace yarp::os;
int main(int argc, char* argv[])
{
if (argc < 2) {
fprintf(stderr, "Please supply a port name for the server\n");
return 1;
}
const char* name = argv[1];
RpcServer port;
port.open(name);
while (true) {
printf("Waiting for a message...\n");
Bottle cmd;
Bottle response;
port.read(cmd, true);
printf("Got message: %s\n", cmd.toString().c_str());
response.addString("you");
response.addString("said");
response.append(cmd);
printf("Sending reply: %s\n", response.toString().c_str());
port.reply(response);
}
}

And here is a client to go with it:

/*
* Copyright (C) 2006-2020 Istituto Italiano di Tecnologia (IIT)
* Copyright (C) 2006-2010 RobotCub Consortium
* All rights reserved.
*
* This software may be modified and distributed under the terms of the
* BSD-3-Clause license. See the accompanying LICENSE file for details.
*/
/*
* This is an example of using a specialized RpcClient port to send
* messages and receive replies. Regular YARP ports can do this as well,
* but use of RpcServer/RpcClient allows for better
* run-time checking of port usage to catch mistakes.
*/
#include <yarp/os/Bottle.h>
#include <yarp/os/Time.h>
#include <cstdio>
int main(int argc, char* argv[])
{
if (argc < 3) {
fprintf(stderr, "Please supply (1) a port name for the client\n");
fprintf(stderr, " (2) a port name for the server\n");
return 1;
}
Network yarp;
const char* client_name = argv[1];
const char* server_name = argv[2];
RpcClient port;
port.open(client_name);
int ct = 0;
while (true) {
if (port.getOutputCount() == 0) {
printf("Trying to connect to %s\n", server_name);
yarp::os::Network::connect(client_name, server_name);
} else {
Bottle cmd;
cmd.addString("COUNT");
cmd.addInt32(ct);
ct++;
printf("Sending message... %s\n", cmd.toString().c_str());
Bottle response;
port.write(cmd, response);
printf("Got response: %s\n", response.toString().c_str());
}
}
}

The server and client communicate with yarp::os::Bottle objects here, but as usual anything that implements yarp::os::Portable can be used.

Monitoring RPC communication

Normal streaming communication in YARP is very easy to monitor, just connect another port to the sender. For RPC communication, we have to be a bit more careful.

Suppose the server and client above are running (the source code is in example/os/rpc_server/rpc_server.cpp and example/os/rpc_client/rpc_client.cpp), in two separate terminals, as follows:

./rpc_server /server
./rpc_client /client /server

Here we have named the server port /server and the client port /client. Every second or so, the client sends a message to the server with a count in it, and the server replies with "you said" followed by that message.

How can we monitor this traffic remotely? YARP supports a special kind of logging connection called log.in which can report all the inputs a port is receiving (along with the corresponding replies). We make a connection from the server (not the client) to a logging port, set the connection to log.in mode, and we are done.

The syntax for doing so is either:

yarp read /read tcp+log.in://server

Or equivalently:

yarp read /read
yarp connect /server /read tcp+log.in

The protocol in this case is specified to be tcp (which is the default), but with an added +log.in decoration. This is a "carrier modifier" that tells YARP that the connection has a special purpose, in this case to log traffic that arrives at the /server port. Without that modifier, this connection would be forbidden if the RPC server is already connected to a client, and even if it were allowed it would misbehave.

The output shown by the "yarp read" command will be something like this:

[rpc] (COUNT 29) (you said COUNT 29)
[rpc] (COUNT 30) (you said COUNT 30)
[rpc] (COUNT 31) (you said COUNT 31)
[rpc] (COUNT 32) (you said COUNT 32)
[rpc] (COUNT 33) (you said COUNT 33)
[rpc] (COUNT 34) (you said COUNT 34)
[rpc] (COUNT 35) (you said COUNT 35)
[rpc] (COUNT 36) (you said COUNT 36)

The first element is a tag for RPC traffic. The second element is a nested list describing the input received by the port. The third element is a nested list describing the corresponding output produced by the port.

Note that we log from the server side, because we are logging inputs. At this time YARP does not have a log.out mode for logging outputs with their replies.

The logging connection will pass along any envelope or timestamp associated with the input, and will lack an envelope if the input lacks an envelope.

yarp::os::Bottle
A simple collection of objects that can be described and transmitted in a portable way.
Definition: Bottle.h:73
yarp::os::Bottle::toString
std::string toString() const override
Gives a human-readable textual representation of the bottle.
Definition: Bottle.cpp:214
Network.h
yarp::os::RpcClient
A port that is specialized as an RPC client.
Definition: RpcClient.h:26
main
int main(int argc, char *argv[])
Definition: yarpros.cpp:261
yarp::os::AbstractContactable::reply
bool reply(PortWriter &writer) override
Send an object as a reply to an object read from the port.
Definition: AbstractContactable.cpp:168
yarp::os::AbstractContactable::open
bool open(const std::string &name) override
Start port operation, with a specific name, with automatically-chosen network parameters.
Definition: AbstractContactable.cpp:12
yarp::os::Bottle::write
bool write(ConnectionWriter &writer) const override
Output a representation of the bottle to a network connection.
Definition: Bottle.cpp:233
RpcServer.h
yarp::os::NetworkBase::connect
static bool connect(const std::string &src, const std::string &dest, const std::string &carrier="", bool quiet=true)
Request that an output port connect to an input port.
Definition: Network.cpp:685
yarp::os::RpcServer
A port that is specialized as an RPC server.
Definition: RpcServer.h:27
yarp::os::Bottle::addString
void addString(const char *str)
Places a string in the bottle, at the end of the list.
Definition: Bottle.cpp:173
yarp::os
An interface to the operating system, including Port based communication.
Definition: AbstractCarrier.h:17
RpcClient.h
yarp::os::Network
Utilities for manipulating the YARP network, including initialization and shutdown.
Definition: Network.h:786
yarp
The main, catch-all namespace for YARP.
Definition: environment.h:18
Time.h
yarp::os::RpcServer::read
bool read(PortReader &reader, bool willReply=true) override
Read an object from the port.
Definition: RpcServer.cpp:52
yarp::os::Time::delay
void delay(double seconds)
Wait for a certain number of seconds.
Definition: Time.cpp:114
Bottle.h
yarp::os::Bottle::append
void append(const Bottle &alt)
Append the content of the given bottle to the current list.
Definition: Bottle.cpp:383