BlueZero (BØ)
Middleware for distributed applications
BlueWorkforce Middleware - A brief description

API Design

The main class used to create a node is b0::Node.

A node implements one part of the protocol, with the other counterpart of the protocol implemented by the resolver node.

Node uses two-phase initialization, so you must call b0::Node::init() after the constructor, and b0::Node::cleanup() before the destructor. Do not call b0::Node::init() from your node class constructor!

Also, b0::Node::spinOnce() must be called periodically to process incoming messages (or just call b0::Node::spin() once).

node-state-machine.png
Node state transtion diagram.

b0::Node::init() will initialize the node and announce its name to the resolver node, and it will initialize each of its publishers, subscribers, clients and servers.

Any publishers, subscribers, service client and servers must be constructed prior to calling b0::Node::init().

The two ways of interconnecting nodes are:

Threading and thread safety

The functions of the library are not thread-safe, and so are the functions of ZeroMQ. Thus, every node must be accessed always from the same thread.

Resolver node

The most important part of the network is the resolver node.

The resolver node implements a part of the protocol, with the other counterpart of the protocol implemented by the node.

The resolver node is implemented in b0::resolver::Resolver and will provide following services to other nodes:

Important: you must have the resolver node running prior to running any node. See Connecting remote nodes for more information about running distributed nodes.

Examples

Topics (Publisher/Subscriber)

Example of how to create a simple node with one publisher and sending some messages to some topic:

#include <b0/node.h>
#include <b0/publisher.h>
#include <iostream>
int main(int argc, char **argv)
{
/*
* Create a node named "publisher"
*/
b0::Node node("publisher");
/*
* Create a Publisher to publish on topic "A"
*/
b0::Publisher pub(&node, "A");
/*
* Initialize the node (will announce node name to the network, and do other nice things)
*/
node.init();
int i = 0;
while(!node.shutdownRequested())
{
/*
* Process messages from node's sockets
*/
node.spinOnce();
/*
* Create a message to send
*/
std::string msg = (boost::format("msg-%d") % i++).str();
/*
* Send the message on the "A" topic
*/
std::cout << "Sending: " << msg << std::endl;
pub.publish(msg);
/*
* Wait some time
*/
boost::this_thread::sleep_for(boost::chrono::milliseconds(500));
}
/*
* Perform cleanup (stop threads, notify resolver that this node has quit, ...)
*/
node.cleanup();
return 0;
}

And the corresponding example of a simple node with one subscriber:

#include <b0/node.h>
#include <b0/subscriber.h>
#include <iostream>
/*
* This callback will be called whenever a message is received on any
* of the subscribed topics
*/
void callback(const std::string &msg)
{
std::cout << "Received: " << msg << std::endl;
}
int main(int argc, char **argv)
{
/*
* Create a node named "subscriber"
*/
b0::Node node("subscriber");
/*
* Create a Subscriber to subscribe to topic "A"
* It will call the specified callback upon receiving messages
*/
b0::Subscriber sub(&node, "A", &callback);
/*
* Initialize the node (will announce node name to the network, and do other nice things)
*/
node.init();
/*
* Spin the node (continuously process incoming messages and call callbacks)
*/
node.spin();
/*
* Perform cleanup (stop threads, notify resolver that this node has quit, ...)
*/
node.cleanup();
return 0;
}

You can have multiple publishers and subscribers as well:

Node with multiple publishers

Node with multiple subscribers

And following is an example of using it in a more object-oriented way:

OOP publisher node

OOP subscriber node

Services (Client/Server)

Example of how to create a simple node with a service client:

#include <b0/node.h>
#include <b0/service_client.h>
#include <iostream>
int main(int argc, char **argv)
{
/*
* Create a node named "client"
*/
b0::Node node("client");
/*
* Create a ServiceClient that will connect to the service "control"
*/
b0::ServiceClient cli(&node, "control");
/*
* Initialize the node (will announce the node name to the network, and do other nice things)
*/
node.init();
/*
* Create a request message
*/
std::string req = "hello";
std::cout << "Sending: " << req << std::endl;
/*
* The response will be written here
*/
std::string rep;
/*
* Call the service (blocking)
*/
cli.call(req, rep);
std::cout << "Received: " << rep << std::endl;
/*
* Perform cleanup (stop threads, notify resolver that this node has quit, ...)
*/
node.cleanup();
return 0;
}

And the corresponding example of a simple node with a service server:

#include <b0/node.h>
#include <b0/service_server.h>
#include <iostream>
/*
* This callback will be called whenever a request message is read from the socket
*/
void callback(const std::string &req, std::string &rep)
{
std::cout << "Received: " << req << std::endl;
rep = "hi";
std::cout << "Sending: " << rep << std::endl;
}
int main(int argc, char **argv)
{
/*
* Create a node named "server"
*
* Note: if another node with the same name exists on the network, this node will
* get a different name
*/
b0::Node node("server");
/*
* Create a ServiceServer for a service named "control"
* It will call the specified callback upon receiving requests.
*/
b0::ServiceServer srv(&node, "control", &callback);
/*
* Initialize the node (will announce node name to the network, and do other nice things)
*/
node.init();
/*
* Spin the node (continuously process incoming requests and call callbacks)
*/
node.spin();
/*
* Perform cleanup (stop threads, notify resolver that this node has quit, ...)
*/
node.cleanup();
return 0;
}

And the same thing, object-oriented:

OOP client node

OOP server node