[mrpt-comms]
Communication utilities: serial ports, networking (TCP, DNS,…), pub/sub nodelets.
Library mrpt-comms
This C++ library is part of MRPT and can be installed in Debian-based systems with:
sudo apt install libmrpt-comms-dev
Read also how to import MRPT into your CMake scripts.
Find below some examples of use.
Nodelets-like Pub/Sub mechanism
MRPT provides a Publisher/Subscriber (Pub/Sub) pattern implementation for intra-process (multiple threads) zero-copy super fast communication.
Thread creation and handling is the responsibility of the user.
The main concepts are:
A
topic
: the name of a shared variable. If you are familiar with ROS, you already know what a topic is. Topics are identified by unique strings, e.g.odometry
,scan
.Directory: The central hub that holds all references to existing topics. Usually only one should exist per process, but there is no actual limitation.
Subscriber : An object that binds to a given topic and allows a user-given function to be called whenever new data is published to the topic.
Publish: The operation of publishing a new data piece to a named topic. It’s important to note that the call is blocking : all subscribers are executed from the thread invoking publish(). Users are encouraged to design subscribers such that their execution time are minimized, delegating heavy computation to other worker threads.
All these concepts are illustrated in the example below. Note that different subscribers are created: with a lambda, with a regular function, a std::bind(), etc.
See: comms_nodelets_example/NodeletsTest_impl.cpp
#include <mrpt/comms/nodelets.h> #include <mrpt/math/TPose3D.h> #include <chrono> #include <cstdio> // printf() #include <iostream> #include <thread> // Test payload: const mrpt::math::TPose3D p_tx(1.0, 2.0, 3.0, 0.2, 0.4, 0.6); // Create the topic directory. Only once per process, and must be shared // by all nodelets/threads. auto dir = mrpt::comms::TopicDirectory::create(); void thread_publisher() { using namespace mrpt::comms; using namespace std; try { #ifdef NODELETS_TEST_VERBOSE printf("[publisher] Started\n"); #endif for (int i = 0; i < 5; i++) { std::this_thread::sleep_for(100ms); dir->getTopic("/robot/odom")->publish(p_tx); } #ifdef NODELETS_TEST_VERBOSE printf("[publisher] Finish\n"); #endif } catch (const std::exception& e) { cerr << e.what() << endl; } catch (...) { printf("[thread_publisher] Runtime error!\n"); } } void onNewMsg(const mrpt::math::TPose3D& p) { #ifdef NODELETS_TEST_VERBOSE std::cout << "sub2: rx TPose3D" << p.asString() << std::endl; #endif } void onNewMsg2(int idx, const mrpt::math::TPose3D& p) { #ifdef NODELETS_TEST_VERBOSE std::cout << "onNewMsg2: idx=" << idx << " rx TPose3D" << p.asString() << std::endl; #endif } void thread_subscriber() { using namespace mrpt::comms; using namespace std; try { #ifdef NODELETS_TEST_VERBOSE printf("[subscriber] Connecting\n"); #endif #ifdef NODELETS_TEST_VERBOSE printf("[subscriber] Connected. Waiting for a message...\n"); #endif // Create a subscriber with a lambda: Subscriber::Ptr sub1 = dir->getTopic("/robot/odom") ->createSubscriber<mrpt::math::TPose3D>( [](const mrpt::math::TPose3D& p_rx) -> void { #ifdef NODELETS_TEST_VERBOSE std::cout << "sub1: rx TPose3D" << p_rx.asString() << std::endl; #endif nodelets_test_passed_ok = (p_rx == p_tx); }); // Create a subscriber with a regular function via std::function: auto sub2 = dir->getTopic("/robot/odom") ->createSubscriber<mrpt::math::TPose3D>( std::function<void(const mrpt::math::TPose3D&)>(&onNewMsg)); // Create a subscriber with a regular function: auto sub3 = dir->getTopic("/robot/odom") ->createSubscriber<mrpt::math::TPose3D>(&onNewMsg); // Create a subscriber with std::bind: using namespace std::placeholders; auto sub4 = dir->getTopic("/robot/odom") ->createSubscriber<mrpt::math::TPose3D>( [](auto&& arg1) { return onNewMsg2(123, arg1); }); // wait for messages to arrive. // The nodelet is up and live until "sub" gets out of scope. std::this_thread::sleep_for(2000ms); #ifdef NODELETS_TEST_VERBOSE printf("[subscriber] Finish\n"); #endif } catch (const std::exception& e) { cerr << e.what() << endl; } catch (...) { cerr << "[thread_subscriber] Runtime error!" << endl; } } void NodeletsTest() { using namespace std::chrono_literals; std::thread(thread_publisher).detach(); std::thread(thread_subscriber).detach(); std::this_thread::sleep_for(1000ms); }
HTTP request methods
mrpt::comms::net::http_get() is an easy way to GET an HTTP resource from any C++ program. You can also use mrpt::comms::net::http_request() to access APIs requiring the POST method.
See: comms_http_client/test.cpp
#include <mrpt/comms/net_utils.h> #include <mrpt/core/exceptions.h> #include <iostream> using namespace mrpt; using namespace mrpt::comms; using namespace mrpt::comms::net; std::string url = "http://www.google.es/"; void Test_HTTP_get() { std::string content; mrpt::comms::net::HttpRequestOptions httpOptions; mrpt::comms::net::HttpRequestOutput httpOut; std::cout << "Retrieving " << url << "..." << std::endl; http_errorcode ret = http_get(url, content, httpOptions, httpOut); if (ret != net::http_errorcode::Ok) { std::cout << " Error: " << httpOut.errormsg << std::endl; return; } string typ = httpOut.out_headers.count("Content-Type") ? httpOut.out_headers.at("Content-Type") : string("???"); std::cout << "Ok: " << content.size() << " bytes of type: " << typ << std::endl; }
Library contents
// namespaces namespace mrpt::comms::net; // enums enum mrpt::comms::net::http_errorcode; // structs struct mrpt::comms::net::HttpRequestOptions; struct mrpt::comms::net::HttpRequestOutput; struct mrpt::comms::TFTDIDevice; // classes class mrpt::comms::CClientTCPSocket; class mrpt::comms::CInterfaceFTDI; class mrpt::comms::CSerialPort; class mrpt::comms::CServerTCPSocket; class mrpt::comms::Subscriber; // global functions http_errorcode mrpt::comms::net::http_get( const string& url, std::vector<uint8_t>& out_content, const HttpRequestOptions& options = HttpRequestOptions(), mrpt::optional_ref<HttpRequestOutput> output = std::nullopt ); http_errorcode mrpt::comms::net::http_get( const string& url, string& out_content, const HttpRequestOptions& options = HttpRequestOptions(), mrpt::optional_ref<HttpRequestOutput> output = std::nullopt ); http_errorcode mrpt::comms::net::http_request( const string& http_method, const string& http_send_content, const string& url, std::vector<uint8_t>& out_content, const HttpRequestOptions& options = HttpRequestOptions(), mrpt::optional_ref<HttpRequestOutput> output = std::nullopt ); bool mrpt::comms::net::DNS_resolve_async( const std::string& server_name, std::string& out_ip, const unsigned int timeout_ms = 3000 ); std::string mrpt::comms::net::getLastSocketErrorStr(); bool mrpt::comms::net::Ping( const std::string& address, const int max_attempts, std::string* output_str = nullptr );
Global Functions
http_errorcode mrpt::comms::net::http_get( const string& url, std::vector<uint8_t>& out_content, const HttpRequestOptions& options = HttpRequestOptions(), mrpt::optional_ref<HttpRequestOutput> output = std::nullopt )
Perform an HTTP GET operation (version for retrieving the data as a std::vector<uint8_t>)
Parameters:
url |
Must be a simple string of the form “http://<servername>/<relative-address>”. |
port |
The server port, if different from 80. |
extra_headers |
If provided, the given extra HTTP headers will be sent. |
errormsg |
On exit will contain a description of the error or “Ok”. |
out_content |
The buffer with the retrieved data. |
out_http_responsecode |
If provided, will hold the HTTP code, eg: 200, 404… |
out_headers |
If provided, a copy of all the headers returned by the server will be saved here. |
auth_user |
|
auth_pass |
Send a basic HTTP authorization request with the given user & password. |
Returns:
The error or success code.
See also:
http_errorcode mrpt::comms::net::http_request( const string& http_method, const string& http_send_content, const string& url, std::vector<uint8_t>& out_content, const HttpRequestOptions& options = HttpRequestOptions(), mrpt::optional_ref<HttpRequestOutput> output = std::nullopt )
Generic function for HTTP GET & POST methods.
See also:
bool mrpt::comms::net::DNS_resolve_async( const std::string& server_name, std::string& out_ip, const unsigned int timeout_ms = 3000 )
Resolve a server address by its name, returning its IP address as a string - This method has a timeout for the maximum time to wait for the DNS server.
For example: server_name=”www.google.com” -> out_ip=”209.85.227.99”
Returns:
true on success, false on timeout or other error.
std::string mrpt::comms::net::getLastSocketErrorStr()
Returns a description of the last Sockets error.
bool mrpt::comms::net::Ping( const std::string& address, const int max_attempts, std::string* output_str = nullptr )
Ping an IP address.
{ I am redirecting stderr to stdout, so that the overall process is simplified. Otherwise see: https://jineshkj.wordpress.com/2006/12/22/how-to-capture-stdin-stdout-and-stderr-of-child-program/}
Parameters:
address |
Address to ping. |
max_attempts |
Number of attempts to try and ping. |
output |
String containing output information |
Returns:
True if responsive, false otherwise.