Commit 5c6b9306 authored by Valentin Platzgummer's avatar Valentin Platzgummer

rosbridge works good now

parent 585c0b82
...@@ -45,7 +45,7 @@ public: ...@@ -45,7 +45,7 @@ public:
bool isRunning(); bool isRunning();
private: private:
bool _running; std::shared_ptr<std::atomic_bool> _stopped;
TypeFactory _typeFactory; TypeFactory _typeFactory;
JsonFactory _jsonFactory; JsonFactory _jsonFactory;
CasePacker _casePacker; CasePacker _casePacker;
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
ROSBridge::ComPrivate::Server::Server(CasePacker &casePacker, RosbridgeWsClient &rbc) : ROSBridge::ComPrivate::Server::Server(CasePacker &casePacker, RosbridgeWsClient &rbc) :
_rbc(rbc) _rbc(rbc)
, _casePacker(casePacker) , _casePacker(casePacker)
, _stopped(std::make_shared<std::atomic_bool>(true))
{ {
} }
...@@ -14,10 +15,10 @@ void ROSBridge::ComPrivate::Server::advertiseService(const std::string &service, ...@@ -14,10 +15,10 @@ void ROSBridge::ComPrivate::Server::advertiseService(const std::string &service,
const Server::CallbackType &userCallback) const Server::CallbackType &userCallback)
{ {
std::string clientName = _serviceAdvertiserKey + service; std::string clientName = _serviceAdvertiserKey + service;
auto it = std::find(_clientList.begin(), _clientList.end(), clientName); auto it = _serviceMap.find(clientName);
if (it != _clientList.end()) if (it != _serviceMap.end())
return; // Service allready advertised. return; // Service allready advertised.
_clientList.push_back(clientName); _serviceMap.insert(std::make_pair(clientName, service));
_rbc.addClient(clientName); _rbc.addClient(clientName);
auto rbc = &_rbc; auto rbc = &_rbc;
...@@ -80,14 +81,16 @@ ROSBridge::ComPrivate::Server::~Server() ...@@ -80,14 +81,16 @@ ROSBridge::ComPrivate::Server::~Server()
void ROSBridge::ComPrivate::Server::start() void ROSBridge::ComPrivate::Server::start()
{ {
_running = true; _stopped->store(false);
} }
void ROSBridge::ComPrivate::Server::reset() void ROSBridge::ComPrivate::Server::reset()
{ {
if (!_running) if ( _stopped->load() )
return; return;
for (const auto& str : _clientList) for (const auto& item : _serviceMap){
_rbc.removeClient(str); _rbc.unadvertiseService(item.second);
_clientList.clear(); _rbc.removeClient(item.first);
}
_serviceMap.clear();
} }
...@@ -5,12 +5,14 @@ ...@@ -5,12 +5,14 @@
#include "ros_bridge/include/TypeFactory.h" #include "ros_bridge/include/TypeFactory.h"
#include "ros_bridge/include/CasePacker.h" #include "ros_bridge/include/CasePacker.h"
#include <unordered_map>
namespace ROSBridge { namespace ROSBridge {
namespace ComPrivate { namespace ComPrivate {
class Server class Server
{ {
typedef std::vector<std::string> ClientList; typedef std::unordered_map<std::string, std::string> ServiceMap;
public: public:
typedef std::function<JsonDocUPtr(JsonDocUPtr)> CallbackType; typedef std::function<JsonDocUPtr(JsonDocUPtr)> CallbackType;
...@@ -33,8 +35,8 @@ private: ...@@ -33,8 +35,8 @@ private:
RosbridgeWsClient &_rbc; RosbridgeWsClient &_rbc;
CasePacker &_casePacker; CasePacker &_casePacker;
ClientList _clientList; ServiceMap _serviceMap;
bool _running; std::shared_ptr<std::atomic_bool> _stopped;
}; };
} // namespace ComPrivate } // namespace ComPrivate
} // namespace ROSBridge } // namespace ROSBridge
#include "TopicPublisher.h" #include "TopicPublisher.h"
#include <unordered_map>
struct ROSBridge::ComPrivate::ThreadData
{
const ROSBridge::CasePacker &casePacker;
RosbridgeWsClient &rbc;
ROSBridge::ComPrivate::JsonQueue &queue;
std::mutex &queueMutex;
const std::atomic<bool> &running;
std::condition_variable &cv;
};
ROSBridge::ComPrivate::TopicPublisher::TopicPublisher(CasePacker &casePacker, ROSBridge::ComPrivate::TopicPublisher::TopicPublisher(CasePacker &casePacker,
RosbridgeWsClient &rbc) : RosbridgeWsClient &rbc) :
_running(false) _stopped(std::make_shared<std::atomic_bool>(true))
, _casePacker(casePacker) , _casePacker(casePacker)
, _rbc(rbc) , _rbc(rbc)
{ {
...@@ -30,25 +18,65 @@ ROSBridge::ComPrivate::TopicPublisher::~TopicPublisher() ...@@ -30,25 +18,65 @@ ROSBridge::ComPrivate::TopicPublisher::~TopicPublisher()
void ROSBridge::ComPrivate::TopicPublisher::start() void ROSBridge::ComPrivate::TopicPublisher::start()
{ {
if ( _running.load() ) // start called while thread running. if ( !_stopped->load() ) // start called while thread running.
return; return;
_running.store(true); _stopped->store(false);
ROSBridge::ComPrivate::ThreadData data{ _pThread = std::make_unique<std::thread>([this]{
_casePacker, // Init.
_rbc, std::unordered_map<std::string, std::string> topicMap;
_queue, // Main Loop.
_queueMutex, while( !this->_stopped->load()){
_running, std::unique_lock<std::mutex> lk(this->_mutex);
_cv // Check if new data available, wait if not.
}; if (this->_queue.empty()){
_pThread = std::make_unique<std::thread>(&ROSBridge::ComPrivate::transmittLoop, data); this->_cv.wait(lk); // Wait for condition, spurious wakeups don't matter in this case.
continue;
}
// Pop message from queue.
JsonDocUPtr pJsonDoc(std::move(this->_queue.front()));
this->_queue.pop_front();
lk.unlock();
// Get tag from Json message and remove it.
Tag tag;
bool ret = this->_casePacker.getTag(pJsonDoc, tag);
assert(ret); // Json message does not contain a tag;
(void)ret;
this->_casePacker.removeTag(pJsonDoc);
// Check if topic must be advertised.
// Advertised topics are stored in advertisedTopicsHashList as
// a hash.
std::string clientName = ROSBridge::ComPrivate::_topicAdvertiserKey
+ tag.topic();
auto it = topicMap.find(clientName);
if ( it == topicMap.end()) { // Need to advertise topic?
topicMap.insert(std::make_pair(clientName, tag.topic()));
this->_rbc.addClient(clientName);
this->_rbc.advertise(clientName,
tag.topic(),
tag.messageType() );
this->_rbc.waitForTopic(tag.topic(), this->_stopped); // Wait until topic is advertised.
}
// Publish Json message.
if ( !this->_stopped->load() )
this->_rbc.publish(tag.topic(), *pJsonDoc.get());
} // while loop
// Tidy up.
for (auto& it : topicMap){
this->_rbc.unadvertise(it.second);
this->_rbc.removeClient(it.first);
}
});
} }
void ROSBridge::ComPrivate::TopicPublisher::reset() void ROSBridge::ComPrivate::TopicPublisher::reset()
{ {
if ( !_running.load() ) // stop called while thread not running. if ( _stopped->load() ) // stop called while thread not running.
return; return;
_running.store(false); _stopped->store(true);
_cv.notify_one(); // Wake publisher thread. _cv.notify_one(); // Wake publisher thread.
if ( !_pThread ) if ( !_pThread )
return; return;
...@@ -56,51 +84,3 @@ void ROSBridge::ComPrivate::TopicPublisher::reset() ...@@ -56,51 +84,3 @@ void ROSBridge::ComPrivate::TopicPublisher::reset()
_queue.clear(); _queue.clear();
} }
void ROSBridge::ComPrivate::transmittLoop(ROSBridge::ComPrivate::ThreadData data)
{
// Init.
ClientMap clientMap;
// Main Loop.
while(data.running.load()){
std::unique_lock<std::mutex> lk(data.queueMutex);
// Check if new data available, wait if not.
if (data.queue.empty()){
data.cv.wait(lk); // Wait for condition, spurious wakeups don't matter in this case.
continue;
}
// Pop message from queue.
JsonDocUPtr pJsonDoc(std::move(data.queue.front()));
data.queue.pop_front();
lk.unlock();
// Get tag from Json message and remove it.
Tag tag;
bool ret = data.casePacker.getTag(pJsonDoc, tag);
assert(ret); // Json message does not contain a tag;
(void)ret;
data.casePacker.removeTag(pJsonDoc);
// Check if topic must be advertised.
// Advertised topics are stored in advertisedTopicsHashList as
// a hash.
std::string clientName = ROSBridge::ComPrivate::_topicAdvertiserKey
+ tag.topic();
HashType hash = ROSBridge::ComPrivate::getHash(clientName);
auto it = clientMap.find(hash);
if ( it == clientMap.end()) { // Need to advertise topic?
clientMap.insert(std::make_pair(hash, clientName));
data.rbc.addClient(clientName);
data.rbc.advertise(clientName,
tag.topic(),
tag.messageType() );
}
// Publish Json message.
data.rbc.publish(tag.topic(), *pJsonDoc.get());
} // while loop
// Tidy up.
for (auto& it : clientMap)
data.rbc.removeClient(it.second);
}
...@@ -37,7 +37,7 @@ public: ...@@ -37,7 +37,7 @@ public:
void publish(JsonDocUPtr docPtr){ void publish(JsonDocUPtr docPtr){
{ {
std::lock_guard<std::mutex> lock(_queueMutex); std::lock_guard<std::mutex> lock(_mutex);
_queue.push_back(std::move(docPtr)); _queue.push_back(std::move(docPtr));
} }
_cv.notify_one(); // Wake publisher thread. _cv.notify_one(); // Wake publisher thread.
...@@ -51,8 +51,8 @@ public: ...@@ -51,8 +51,8 @@ public:
private: private:
JsonQueue _queue; JsonQueue _queue;
std::mutex _queueMutex; std::mutex _mutex;
std::atomic<bool> _running; std::shared_ptr<std::atomic_bool> _stopped;
CasePacker &_casePacker; CasePacker &_casePacker;
RosbridgeWsClient &_rbc; RosbridgeWsClient &_rbc;
CondVar _cv; CondVar _cv;
...@@ -60,8 +60,5 @@ private: ...@@ -60,8 +60,5 @@ private:
}; };
void transmittLoop(ThreadData data);
} // namespace CommunicatorDetail } // namespace CommunicatorDetail
} // namespace ROSBridge } // namespace ROSBridge
...@@ -5,7 +5,7 @@ ROSBridge::ComPrivate::TopicSubscriber::TopicSubscriber(CasePacker &casePacker, ...@@ -5,7 +5,7 @@ ROSBridge::ComPrivate::TopicSubscriber::TopicSubscriber(CasePacker &casePacker,
RosbridgeWsClient &rbc) : RosbridgeWsClient &rbc) :
_casePacker(casePacker) _casePacker(casePacker)
, _rbc(rbc) , _rbc(rbc)
, _running(false) , _stopped(std::make_shared<std::atomic_bool>(true))
{ {
} }
...@@ -17,120 +17,78 @@ ROSBridge::ComPrivate::TopicSubscriber::~TopicSubscriber() ...@@ -17,120 +17,78 @@ ROSBridge::ComPrivate::TopicSubscriber::~TopicSubscriber()
void ROSBridge::ComPrivate::TopicSubscriber::start() void ROSBridge::ComPrivate::TopicSubscriber::start()
{ {
_running = true; _stopped->store(false);
} }
void ROSBridge::ComPrivate::TopicSubscriber::reset() void ROSBridge::ComPrivate::TopicSubscriber::reset()
{ {
if ( _running ){ if ( !_stopped->load() ){
_running = false; _stopped->store(true);
{ {
for (std::string clientName : _clientList){ for (auto &item : _topicMap){
_rbc.removeClient(clientName); _rbc.unsubscribe(item.second);
_rbc.removeClient(item.first);
} }
} }
{ _topicMap.clear();
std::lock_guard<std::mutex> lk(_callbackMapStruct.mutex);
_callbackMapStruct.map.clear();
}
_clientList.clear();
} }
} }
bool ROSBridge::ComPrivate::TopicSubscriber::subscribe( void ROSBridge::ComPrivate::TopicSubscriber::subscribe(
const char *topic, const char *topic,
const std::function<void(JsonDocUPtr)> &callback) const std::function<void(JsonDocUPtr)> &callback)
{ {
if ( !_running ) if ( _stopped->load() )
return false; return;
std::string clientName = ROSBridge::ComPrivate::_topicSubscriberKey std::string clientName = ROSBridge::ComPrivate::_topicSubscriberKey
+ std::string(topic); + std::string(topic);
_clientList.push_back(clientName); auto it = _topicMap.find(clientName);
if ( it != _topicMap.end() ){ // Topic already subscribed?
HashType hash = getHash(clientName); return;
{
std::lock_guard<std::mutex> lk(_callbackMapStruct.mutex);
auto ret = _callbackMapStruct.map.insert(std::make_pair(hash, callback)); //
if ( !ret.second )
return false; // Topic subscription already present.
} }
_topicMap.insert(std::make_pair(clientName, std::string(topic)));
using namespace std::placeholders; using namespace std::placeholders;
auto f = std::bind(&ROSBridge::ComPrivate::subscriberCallback, auto callbackWrapper = [callback](
hash, std::shared_ptr<WsClient::Connection>,
std::ref(_callbackMapStruct), std::shared_ptr<WsClient::InMessage> in_message){
_1, _2); // Parse document.
JsonDoc docFull;
// std::cout << std::endl; docFull.Parse(in_message->string().c_str());
// std::cout << "topic subscription" << std::endl; if ( docFull.HasParseError() ) {
// std::cout << "client name: " << clientName << std::endl; std::cout << "Json document has parse error: "
// std::cout << "topic: " << topic << std::endl; << in_message->string()
{ << std::endl;
auto start = std::chrono::high_resolution_clock::now(); return;
} else if (!docFull.HasMember("msg")) {
std::cout << "Json document does not contain a message (\"msg\"): "
<< in_message->string()
<< std::endl;
return;
}
// Extract message and call callback.
JsonDocUPtr pDoc(new JsonDoc());
pDoc->CopyFrom(docFull["msg"].Move(), docFull.GetAllocator());
callback(std::move(pDoc));
return;
};
if ( !_rbc.topicAvailable(topic) ){
// Wait until topic is available.
std::thread t([this, clientName, topic, callbackWrapper]{
this->_rbc.waitForTopic(topic, this->_stopped);
if ( !this->_stopped->load() ){
this->_rbc.addClient(clientName);
this->_rbc.subscribe(clientName, topic, callbackWrapper);
}
});
t.detach();
} else {
_rbc.addClient(clientName); _rbc.addClient(clientName);
auto end = std::chrono::high_resolution_clock::now(); _rbc.subscribe(clientName, topic, callbackWrapper);
std::cout << "add client time: "
<< std::chrono::duration_cast<std::chrono::milliseconds>(end-start).count()
<< " ms" << std::endl;
start = std::chrono::high_resolution_clock::now();
_rbc.subscribe(clientName,
topic,
f );
end = std::chrono::high_resolution_clock::now();
std::cout << "subscribe time: "
<< std::chrono::duration_cast<std::chrono::milliseconds>(end-start).count()
<< " ms" << std::endl;
} }
return true;
} }
using WsClient = SimpleWeb::SocketClient<SimpleWeb::WS>;
void ROSBridge::ComPrivate::subscriberCallback(
const HashType &hash,
ROSBridge::ComPrivate::MapStruct &mapWrapper,
std::shared_ptr<WsClient::Connection>,
std::shared_ptr<WsClient::InMessage> in_message)
{
// Parse document.
JsonDoc docFull;
docFull.Parse(in_message->string().c_str());
if ( docFull.HasParseError() ) {
std::cout << "Json document has parse error: "
<< in_message->string()
<< std::endl;
return;
} else if (!docFull.HasMember("msg")) {
std::cout << "Json document does not contain a message (\"msg\"): "
<< in_message->string()
<< std::endl;
return;
}
// std::cout << "Json document: "
// << in_message->string()
// << std::endl;
// Search callback.
CallbackType callback;
{
std::lock_guard<std::mutex> lk(mapWrapper.mutex);
auto it = mapWrapper.map.find(hash);
if (it == mapWrapper.map.end()) {
//assert(false); // callback not found
return;
}
callback = it->second;
}
// Extract message and call callback.
JsonDocUPtr pDoc(new JsonDoc());
pDoc->CopyFrom(docFull["msg"].Move(), docFull.GetAllocator());
callback(std::move(pDoc));
return;
}
...@@ -5,20 +5,17 @@ ...@@ -5,20 +5,17 @@
#include "ros_bridge/include/TypeFactory.h" #include "ros_bridge/include/TypeFactory.h"
#include "ros_bridge/include/CasePacker.h" #include "ros_bridge/include/CasePacker.h"
#include <unordered_map>
namespace ROSBridge { namespace ROSBridge {
namespace ComPrivate { namespace ComPrivate {
typedef std::function<void(JsonDocUPtr)> CallbackType; typedef std::function<void(JsonDocUPtr)> CallbackType;
struct MapStruct{
typedef std::map<HashType, CallbackType> Map;
Map map;
std::mutex mutex;
};
class TopicSubscriber class TopicSubscriber
{ {
typedef std::vector<std::string> ClientList; typedef std::unordered_map<std::string, std::string> TopicMap;
public: public:
TopicSubscriber() = delete; TopicSubscriber() = delete;
...@@ -34,7 +31,7 @@ public: ...@@ -34,7 +31,7 @@ public:
//! @return Returns false if a subscription to this topic allready exists. //! @return Returns false if a subscription to this topic allready exists.
//! //!
//! @note Only one callback can be registered. //! @note Only one callback can be registered.
bool subscribe(const char* topic, const CallbackType &callback); void subscribe(const char* topic, const CallbackType &callback);
private: private:
...@@ -42,14 +39,9 @@ private: ...@@ -42,14 +39,9 @@ private:
CasePacker &_casePacker; CasePacker &_casePacker;
RosbridgeWsClient &_rbc; RosbridgeWsClient &_rbc;
MapStruct _callbackMapStruct; TopicMap _topicMap;
ClientList _clientList; std::shared_ptr<std::atomic_bool> _stopped;
bool _running;
}; };
void subscriberCallback(const HashType &hash,
MapStruct &mapWrapper,
std::shared_ptr<WsClient::Connection> /*connection*/,
std::shared_ptr<WsClient::InMessage> in_message);
} // namespace ComPrivate } // namespace ComPrivate
} // namespace ROSBridge } // namespace ROSBridge
#include "ros_bridge/include/ROSBridge.h" #include "ros_bridge/include/ROSBridge.h"
ROSBridge::ROSBridge::ROSBridge() : ROSBridge::ROSBridge::ROSBridge() :
_running(false) _stopped(std::make_shared<std::atomic_bool>(true))
, _casePacker(&_typeFactory, &_jsonFactory) , _casePacker(&_typeFactory, &_jsonFactory)
, _rbc("localhost:9090") , _rbc("localhost:9090")
, _topicPublisher(_casePacker, _rbc) , _topicPublisher(_casePacker, _rbc)
...@@ -38,7 +38,7 @@ void ROSBridge::ROSBridge::start() ...@@ -38,7 +38,7 @@ void ROSBridge::ROSBridge::start()
_topicPublisher.start(); _topicPublisher.start();
_topicSubscriber.start(); _topicSubscriber.start();
_server.start(); _server.start();
_running = true; _stopped->store(false);
} }
void ROSBridge::ROSBridge::reset() void ROSBridge::ROSBridge::reset()
...@@ -46,7 +46,7 @@ void ROSBridge::ROSBridge::reset() ...@@ -46,7 +46,7 @@ void ROSBridge::ROSBridge::reset()
_topicPublisher.reset(); _topicPublisher.reset();
_topicSubscriber.reset(); _topicSubscriber.reset();
_server.reset(); _server.reset();
_running = false; _stopped->store(true);
} }
bool ROSBridge::ROSBridge::ping() bool ROSBridge::ROSBridge::ping()
...@@ -56,6 +56,6 @@ bool ROSBridge::ROSBridge::ping() ...@@ -56,6 +56,6 @@ bool ROSBridge::ROSBridge::ping()
bool ROSBridge::ROSBridge::isRunning() bool ROSBridge::ROSBridge::isRunning()
{ {
return _running; return !_stopped->load();
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment