Repository navigation
Create message types
Only strongly-typed messages are allowed in the RIAPS C++ components. That means, you have to specify all the message types what you are planning to use in your application, with all their fields and their types.
In the C++ component framework you have only one option to define structures of messages: use Cap'n Proto. Capnp is using schema file to define the message structure and then the Cap'n Proto compiler can produce the language-specific source code. First step: grab your favorite text editor (preferable with capnp syntax highlighter, like vscode) and create a file with .capnp extension.
code ./riaps_messages.capnp
In this example we are defining two messages: (1) Query (temperature), (2) Response (temperature value).
@0x84c91b33e534518f; # unique file ID, generated by `capnp id`
# struct timespec from <time.h>
struct Timespec {
sec @0 : UInt64;
nsec @1 : UInt64;
}
struct QueryTemp {
messageCounter @0 : UInt32;
created @1 : Timespec;
}
struct ResponseTemp {
value @0 : Float32;
created @1 : Timespec;
}We are ready with the message definition, now you have to turn this schema file into C++ code:
capnp compile ./riaps_messages.capnp -oc++:./The -oc++ tells the capnp compiler to generate C++ code, while the :./ specifies that place the generated artifacts in the current folder. If everything goes well your directory contains a .c++ and a .h file.
At this point, please take your time and read about the Cap'n Proto schema language. On this page you will find more detailed and better walkthroughs about the capnp schema and its nuances.
So far, the things went well. You defined your message structures and generated code which compiles successfully, but now you have to use this message in your RIAPS component. We built two wrappers around the capnp API, which makes easier to build/read RIAPS messages in C++. Let's see an example how to use them:
{
// Wrapper around capnp to BUILD messages
MessageBuilder<QueryTemp> builder;
// Set the fields with `->` operator
builder->setMessageCounter(counter_++);
// Current time
timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
// Set the timestamp 'created' field in the message
auto created = builder->initCreated();
created.setSec(ts.tv_sec);
created.setNsec(ts.tv_nsec);
}The message is ready to be sent! The Send() functions are explained under the API section.
Reading happens in very similar way, however the MessageReader<ResponseTemp> is instantiated by the RIAPS framework (in the Recv() functions) and not by the developer. The MessageReader<> has two goals: (1) make the message reading easier for the developers, (2) hold the related pointers, structures together (and avoid memory leaks).
{
// Type of msg will be MessageReader<ResponseTemp>
auto [msg, err] = RecvTemp();
if (!err) {
// Access the message fields using the wrapper with '->getX()'
auto value = msg->getValue();
timespec created;
// No wrapper here, this is a field in the message.
// Access the fields with '.getX()'
created.tv_sec = msg->getCreated().getSec();
created.tv_nsec = msg->getCreated().getNsec();
} else {
// Error in recv();
}
}The Recv() functions are explained under the API section.
-
Welcome
- Install dependencies
- Install riaps-core
- Setting up your IDE
- Create message types
- API Overview
- Examples
- Build RIAPS applications
- Advanced Topics
- Distributed Coordination
- Group API
- Leader Election
- Consensus
- Time-Synchronized Coordinated Action
- Fault Management
- Resource Management
- Distributed Coordination