Change of roscpp underlying communication protocol

Keywords: C++

         ROS provides robot developers with programming interfaces in different languages. The C + + interface is called roscpp, which is used to create topic, service and param to realize the communication function of ROS.

roscpp is a C++ implementation of ROS. It provides a client library that enables C++ programmers to quickly interface with ROS TopicsServices, and Parameters. roscpp is the most widely used ROS client library and is designed to be the high-performance library for ROS.

         The communication protocol used in the bottom layer of roscpp is TCP/UDP, that is, whether the topic or service of ros is used, it is transmitted through tcp or udp.

ROS melody topic transmission mechanism

         The pub sub process of ros topic is shown in the following figure. Talker registers pub topic with ros master through rpc, and listener registers sub topic with ros master through rpc. When the ros master finds that someone publishes the sub topic registered by the listener, it will send the talker information of this topic to the listener, and then the listener and talker establish a tcp/udp connection through rpc. The following two communicate through tcp/udp connection, and the ros master will no longer participate.

         The bottom layer of ros realizes the communication between talker and listener through transport. Transport is established in pairs in the form of point-to-point communication. When a topic published by talker is subscribed by multiple listeners, ros will establish a transport for each listener to communicate with. When talker publish es a message, ros will send a message for each listener by traversing all transports.

  roscpp_jbus communication principle

        roscpp is a form of peer-to-peer communication. Talker will create a transport for each listener. Jbus is point-to-point communication, so we need to change the behavior of pub, that is, no matter how many listeners there are, talker only creates one transport, which is broadcast by jbus to each listener through tipc.

 

        Class Transport is the base class for ros abstracting the underlying communication behavior. We inherit it to change the underlying communication protocol of roscpp, and write Class TransportJBUS by imitating Class TransportUDP.

#ifndef ROSCPP_TRANSPORT_JBUS_H
#define ROSCPP_TRANSPORT_JBUS_H

#include <ros/types.h>
#include <ros/transport/transport.h>

#include <boost/thread/mutex.hpp>
#include "ros/io.h"
#include <ros/common.h>
#include <queue.h>
#include <jbus.h>
#include <vector>
namespace ros {
#define ROSJBUS_ID_FMT_     "ROSJBUS_%d"
#define MAX_RECV_QUEUE_     32

class TransportJBUS;
typedef boost::shared_ptr<TransportJBUS> TransportJBUSPtr;

class PollSet;

class ROSCPP_DECL TransportJBUS : public Transport {
  public:
	TransportJBUS(PollSet* poll_set, int max_datagram_size = 0);
	~TransportJBUS();

	/**
	 * @brief It is used to link jbus. It is only called once in ConnectionManager::start
	 *
	 * @return true
	 * @return false
	 */
	bool connect_jbus();

	virtual void close();

	/**
	 * @brief Create a Incoming object
	 *
	 * @param topic_name
	 * @param user Jbus in essence_ handle_ t *
	 * @return true
	 * @return false
	 */
	bool createIncoming(const char *topic_name, void *user);

	/**
	 * @brief Create a Outgoing object
	 *
	 * @param max_datagram_size It depends on the settings of the sender
	 * @param topic_name
	 * @return TransportJBUSPtr
	 */
	TransportJBUSPtr createOutgoing(int max_datagram_size, const char *topic_name);
	// overrides from Transport
	virtual int32_t read(uint8_t* buffer, uint32_t size);
	virtual int32_t write(uint8_t* buffer, uint32_t size);

	virtual void enableWrite();
	virtual void disableWrite();
	virtual void enableRead();
	virtual void disableRead();

	virtual std::string getTransportInfo();

	virtual bool requiresHeader() {
		return false;
	}

	virtual const char* getType() {
		return "JBUSROS";
	}

	int getMaxDatagramSize() const {
		return max_datagram_size_;
	}

	void setJbusHandleIndex(int index) {
		jbus_handle_index_ = index;
	}

	int getJbusHandleIndex() const {
		return jbus_handle_index_;
	}

	const char *getTopicName() {
		return (const char *)topic_name_;
	}
  private:
	/**
	 * @brief Initialize epoll and set topic name
	 *
	 * @param topic_name
	 * @return true
	 * @return false
	 */
	bool initializeSocket(const char *topic_name);

	/**
	 * @brief epoll Processing function
	 *
	 * @param events
	 */
	void socketUpdate(int events);

	/**
	 * @brief jbus Callback function, through which we receive data and trigger POLLIN
	 *
	 * @param arg jbus Data received
	 * @param user jbus_handle_t *,This is because if this pointer is passed, it cannot be judged by this pointer after the object is destructed
	 * @return enum jbus_cb_status
	 */
	static enum jbus_cb_status callback_raw(nmpk_msg_t * arg, void *user);

	/** data **/
	PollSet* poll_set_;
	uint32_t max_datagram_size_;		// Because the jbus layer has slices, we default to MSGSIZE_NMPK_MAX (64*1024*1024)
	bool closed_;
	boost::mutex close_mutex_;

	bool expecting_read_;				//
	bool expecting_write_;				//
	int notify_pipe[2];					// Handle actually used for epoll
	char *topic_name_;					// We use topic name to operate jbus transceiver
	queue_t *recv_queue_;				// This queue is used to temporarily receive data
	bytes_t *recv_buffer_;				// For data reading, the first time you read the header, the second time you really read the data, so you need a cache
	int reorder_bytes_;					// recv_buffer_ Read data length
	int jbus_handle_index_;				// When used with sub, remember its in_transport_vec_ Index number of
};

/**
 * @brief in Object management structure of type
 *
 */
struct jbus_handle_t {
	jbus_handle_t():handle_(nullptr), is_alive_(false) {};
	TransportJBUS *handle_;			// this pointer to the object
	boost::mutex handle_mutex_;
	bool is_alive_;					// Record whether the object is alive, structure: is_alive_ = true destructor: is_alive_ = false
};

}
#endif

See README for specific implementation details and usage

# ROSCPP with libjbus

##Rely on

* libcjson

* support_libs

* libjbus

* ros [and it`s dependencies]

##Compile and install

* mkdir build

* cd build

* cmake ..

* make

* sudo cp devel/lib/libroscpp.so /opt/ros/melodic/lib/libroscpp.so

*or catkin_make ??*

##Explain

In topic, jbus is forced to replace the TCP/UDP underlying communication protocol originally used by ros. When using, only the compiled libroscpp.so needs to replace the original libroscpp.so in ros without changing any code.

### transport_jbus

class TransportJBUS is derived from the base class Transport and written following TransportUDP to realize topic communication.

Because the original ros communication mechanism is point-to-point, while jbus is point-to-point, we no longer create a transport for each sub link in pub topic, but only create a TransportJBUS object for pub, which is controlled by out in JbusTransportManager_ transport_ map_ Manage.

In the sub topic, a TransportJBUS is created and a jbus is created_ handle_ T object to manage it. When jbus receives data, the object is passed into the callback function of jbus through the void * pointer. If we directly pass in the this pointer of the TransportJBUS object, the object cannot be judged through the this pointer after it is destructed, resulting in unpredictable problems. And jbus_handle_t this problem is avoided by marking the state of the TransportJBUS object. jbus_ handle_ Tby in in JbusTransportManager_ transport_ vec_ Manage and release uniformly when the program exits.

### jbus_transport_manager

class JbusTransportManager is used to manage TransportUDP objects. JbusTransportManager is a singleton object that prints JBUSROS: start!

Hello world

Indicates that roscpp is now used_ jbus

###Other modifications

Changes in other source files are annotated

```

/*** jbusros ***/

xxxxx;

/***************/

```

You can quickly locate changes by searching this note

Posted by MadRhino on Thu, 28 Oct 2021 02:14:37 -0700