Darc
Distributed Asynchronous Reactive Components (aka. roscpp implementation using boost.asio)
Install / Use
/learn @mkjaergaard/DarcREADME
darc
Summary
DARC 0.2 is the second round prototype of a ROS-like transportation layer. It is compatible with ROS-messages and use the catkin build system, thus integrated in the ROS ecosystem.
The 0.1-prototype branch contains the the first prototype written during my internship at WG in 2011/2012. https://github.com/mkjaergaard/darc/tree/0.1-prototype https://github.com/mkjaergaard/darc_examples/tree/0.1-prototype
Version 0.2 has been redesigned in several ways such as:
- The implementaion have been split into several sub-projects to motivate a higly seperated architecture
- Nodes have been replaced by Peers, to better symbolize their purpose in the p2p system
- A decentralized name service
- General architecture cleanup
Architecture Overview
Peer Layer:
A DARC control system is designed to run as a fully decentralized peer2peer system. (no rosmaster, no central parameter server etc.) The network communication is abstracted by the peer layer. A peer normally maps into a single executable. Peers connect to eachother, either manually (or at some point based on zeroconf), and form a routable network. Each peer is given a unique 16-byte identifier (boost::uuid).
The interface to the peer layer is basically a send_to(dest_peer_id, some_data) function and a few callbacks in case of data received, new peers connected etc.
Services Layer:
The peer layer is utilized by a number of DARC services running on each peer e.g:
- Distributed Nameserver Service
- Publisher/Subscribe Service
- Procedure Service
A service instance communicates with other instances of the same service on other peers.
Nameserver Service The nameserver is used to identify ressources (e.g. a topic, a parameter or a procedure) by a name. And be notified when other peers provides or needs a resource. Names are not remapped (like in ROS), but instead resourced with different names are 'matched' by adding aliases, normally in a parent namespace. Normally there is no concept of a root namespace, only the 'my current namespace' and 'my parent namespace'. This means namespaces can extend in either direction to allow for multi robot systems. The details requires a longer explaination, and the implementaion is not really working yet, so dont worry about the nameserver right now.
Publisher/Subscriber Service Message broker supporting the publisher/subscribe pattern. Build on boost::asio for asynchronous callbacks handled by different thread. The current version utilizes zero-copy transport of data when the publisher and susbcriber is attached to the same peer. When publishers and subscribers are on seperate peers the data is serialized and sent throught the peer layer.
Component Layer
The component layer wraps the peer and services layers in a more user friendly interface.
Components
User functionality is implemented in a class extending the darc::component class.
Example of the talker_component.cpp
#include <iris/static_scope.hpp>
#include <darc/component.hpp>
#include <darc/periodic_timer.hpp>
#include <darc/publisher.h>
class talker_component : public darc::component, public iris::static_scope<iris::Info>
{
int count_;
darc::periodic_timer timer_;
darc::publisher<std::string> chatter_pub_;
void timer_callback()
{
boost::shared_ptr<std::string> msg = boost::make_shared<std::string>();
*msg = std::string("Hello World ").append(boost::to_string(++count_));
slog<iris::Info>("Publishing",
"Msg", iris::arg<std::string>(*msg));
chatter_pub_.publish(msg);
}
public:
talker_component() :
count_(0),
timer_(this, &talker_component::timer_callback, boost::posix_time::seconds(1)),
chatter_pub_(this, "chatter")
{
}
};
DARC_REGISTER_COMPONENT(talker_component)
Notice:
- There is no spin loop. The component contains only event callbacks.
- The macro DARC_REGISTER_COMPONENT registers the component in the darc::registry which makes it possible to instatiate it.
The corresponding lister_component.cpp
#include <iris/static_scope.hpp>
#include <darc/component.hpp>
#include <darc/subscriber.h>
class listener_component : public darc::component, public iris::static_scope<iris::Info>
{
darc::subscriber<std::string> sub_;
void chatter_callback(const boost::shared_ptr<const std::string> msg)
{
slog<iris::Info>("Received",
"msg", iris::arg<std::string>(*msg));
}
public:
listener_component() :
sub_(this, "chatter", boost::bind(&listener_component::chatter_callback, this, _1))
{
}
};
DARC_REGISTER_COMPONENT(listener_component)
A component is normally compiled into a dynamic library.
cmake_minimum_required(VERSION 2.8)
project(darc_examples)
find_package(catkin)
find_package(catkin REQUIRED COMPONENTS darc_component)
include_directories(${catkin_INCLUDE_DIRS})
add_library(talker_component SHARED
src/talker_component.cpp)
target_link_libraries(talker_component ${catkin_LIBRARIES})
add_library(listener_component SHARED
src/listener_component.cpp)
target_link_libraries(listener_component ${catkin_LIBRARIES})
Component Manager To actually run components an instance of darc::component_manager must be created. The component_manager wraps the peer and services layers. The use case usually in an executables main() function is to:
- Instantiate the darc::component_manager
- Open connections to other component_managers (peers)
- Load components from .so files
- Instantiate and run components
The component_manager has been wrapped in python using boost::python so it is possible to start components throgh python.
Example of a script that loads and starts the listener_component (listener_peer.py):
import darc
# Create component_manager
mngr = darc.component_manager()
# Load the Listener component
darc.component_loader.load_component("liblistener_component.so")
# Instantiate the Listener component
c = darc.registry.instantiate_component("listener_component", mngr)
# Run the Listener component
c.run()
# Manually add connections to other peers in the 5000-5009 port range
mngr.connect("zmq+tcp://127.0.0.1:5000")
mngr.connect("zmq+tcp://127.0.0.1:5001")
mngr.connect("zmq+tcp://127.0.0.1:5002")
mngr.connect("zmq+tcp://127.0.0.1:5003")
mngr.connect("zmq+tcp://127.0.0.1:5004")
mngr.connect("zmq+tcp://127.0.0.1:5005")
mngr.connect("zmq+tcp://127.0.0.1:5006")
mngr.connect("zmq+tcp://127.0.0.1:5007")
mngr.connect("zmq+tcp://127.0.0.1:5008")
mngr.accept("zmq+tcp://127.0.0.1:5009")
# Run the component_manager in the python thread
mngr.run_current_thread()
Running the examples:
Download code:
mkdir darc_ws
cd darc_ws
rosinstall . --catkin https://raw.github.com/mkjaergaard/darc/master/share/darc.rosinstall
Configure:
mkdir build
cd build
cmake .. -DCMAKE_PREFIX_PATH=/opt/ros/groovy
Note: The ROS path is required since DARC supports ros serialization.
Build:
make -j8
make pybindings
Setup Paths:
source ./devel/setup.sh
Run Component Manager:
./devel/lib/darc_component/darc_component_manager
So nothing really happens since we dont have any running components:
[INFO 13:45:41.596249] Darc ComponentManager Running
[INFO 13:45:41.600465] ZeroMQ accept [URL:tcp://127.0.0.1:5000]
[INFO 13:45:41.601049] ZeroMQ connect [URL:tcp://127.0.0.1:5001] [Out-ID:6d52d5dc]
[INFO 13:45:41.601673] ZeroMQ connect [URL:tcp://127.0.0.1:5002] [Out-ID:8283b4c0]
[INFO 13:45:41.601909] ZeroMQ connect [URL:tcp://127.0.0.1:5003] [Out-ID:8aa83778]
[INFO 13:45:41.602153] ZeroMQ connect [URL:tcp://127.0.0.1:5004] [Out-ID:881cfc43]
[INFO 13:45:41.602390] ZeroMQ connect [URL:tcp://127.0.0.1:5005] [Out-ID:17184999]
[INFO 13:45:41.602631] ZeroMQ connect [URL:tcp://127.0.0.1:5006] [Out-ID:8570997f]
[INFO 13:45:41.602863] ZeroMQ connect [URL:tcp://127.0.0.1:5007] [Out-ID:e333ce40]
[INFO 13:45:41.603098] ZeroMQ connect [URL:tcp://127.0.0.1:5008] [Out-ID:d154002b]
[INFO 13:45:41.603341] ZeroMQ connect [URL:tcp://127.0.0.1:5009] [Out-ID:32c5f209]
No component to load
[INFO 13:45:41.603478] Running component_manager
CTRL+C to stop
The example executable can load and run components specified with -l:
./devel/lib/darc_component/darc_component_manager -l talker_component -l listener_component
Now we have the components running:
[INFO 13:48:10.104477] Darc ComponentManager Running
[INFO 13:48:10.105511] ZeroMQ accept [URL:tcp://127.0.0.1:5000]
[INFO 13:48:10.106011] ZeroMQ connect [URL:tcp://127.0
Related Skills
node-connect
352.2kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
111.1kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
352.2kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
352.2kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
