525 lines
14 KiB
C++
525 lines
14 KiB
C++
/*
|
|
* Copyright (C) 2012 Open Source Robotics Foundation
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
*/
|
|
|
|
#include <boost/bind.hpp>
|
|
#include <boost/function.hpp>
|
|
#include "SubscriptionTransport.hh"
|
|
#include "Publication.hh"
|
|
#include "Node.hh"
|
|
|
|
using namespace gazebo;
|
|
using namespace transport;
|
|
|
|
extern void dummy_callback_fn(uint32_t);
|
|
unsigned int Publication::idCounter = 0;
|
|
|
|
//////////////////////////////////////////////////
|
|
Publication::Publication(const std::string &_topic, const std::string &_msgType)
|
|
: topic(_topic), msgType(_msgType), locallyAdvertised(false)
|
|
{
|
|
this->id = idCounter++;
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
Publication::~Publication()
|
|
{
|
|
boost::mutex::scoped_lock lock(this->callbackMutex);
|
|
this->publishers.clear();
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void Publication::AddSubscription(const NodePtr &_node)
|
|
{
|
|
std::list<NodePtr>::iterator iter, endIter;
|
|
|
|
{
|
|
boost::mutex::scoped_lock lock(this->nodeMutex);
|
|
endIter = this->nodes.end();
|
|
iter = std::find(this->nodes.begin(), this->nodes.end(), _node);
|
|
}
|
|
|
|
if (iter == endIter)
|
|
{
|
|
boost::mutex::scoped_lock lock(this->nodeMutex);
|
|
this->nodes.push_back(_node);
|
|
}
|
|
|
|
boost::mutex::scoped_lock lock(this->callbackMutex);
|
|
|
|
// Send latched messages to the subscription.
|
|
for (std::map<uint32_t, MessagePtr>::iterator pubIter =
|
|
this->prevMsgs.begin(); pubIter != this->prevMsgs.end(); ++pubIter)
|
|
{
|
|
if (pubIter->second)
|
|
{
|
|
_node->InsertLatchedMsg(this->topic, pubIter->second);
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void Publication::AddSubscription(const CallbackHelperPtr _callback)
|
|
{
|
|
boost::mutex::scoped_lock lock(this->callbackMutex);
|
|
|
|
std::list< CallbackHelperPtr >::iterator iter;
|
|
iter = std::find(this->callbacks.begin(), this->callbacks.end(), _callback);
|
|
|
|
if (iter == this->callbacks.end())
|
|
{
|
|
this->callbacks.push_back(_callback);
|
|
|
|
if (_callback->GetLatching())
|
|
{
|
|
// Send latched messages to the subscription.
|
|
for (std::map<uint32_t, MessagePtr>::iterator pubIter =
|
|
this->prevMsgs.begin(); pubIter != this->prevMsgs.end(); ++pubIter)
|
|
{
|
|
if (pubIter->second)
|
|
{
|
|
_callback->HandleMessage(pubIter->second);
|
|
}
|
|
}
|
|
_callback->SetLatching(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void Publication::SetPrevMsg(uint32_t _pubId, MessagePtr _msg)
|
|
{
|
|
boost::mutex::scoped_lock lock(this->callbackMutex);
|
|
this->prevMsgs[_pubId] = _msg;
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void Publication::ClearPrevMsgs()
|
|
{
|
|
boost::mutex::scoped_lock lock(this->callbackMutex);
|
|
this->prevMsgs.clear();
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void Publication::AddTransport(const PublicationTransportPtr &_publink)
|
|
{
|
|
bool add = true;
|
|
|
|
// Find an existing publication transport
|
|
std::list<PublicationTransportPtr>::iterator iter;
|
|
for (iter = this->transports.begin(); iter != this->transports.end(); ++iter)
|
|
{
|
|
if ((*iter)->GetTopic() == _publink->GetTopic() &&
|
|
(*iter)->GetMsgType() == _publink->GetMsgType() &&
|
|
(*iter)->GetConnection()->GetRemoteURI() ==
|
|
_publink->GetConnection()->GetRemoteURI())
|
|
{
|
|
add = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Don't add a duplicate transport
|
|
if (add)
|
|
{
|
|
_publink->AddCallback(boost::bind(&Publication::LocalPublish, this, _1));
|
|
this->transports.push_back(_publink);
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
bool Publication::HasTransport(const std::string &_host, unsigned int _port)
|
|
{
|
|
std::list<PublicationTransportPtr>::iterator iter;
|
|
for (iter = this->transports.begin(); iter != this->transports.end(); ++iter)
|
|
{
|
|
if ((*iter)->GetConnection()->GetRemoteAddress() == _host &&
|
|
(*iter)->GetConnection()->GetRemotePort() == _port)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void Publication::RemoveTransport(const std::string &host_, unsigned int port_)
|
|
{
|
|
std::list<PublicationTransportPtr>::iterator iter;
|
|
iter = this->transports.begin();
|
|
while (iter != this->transports.end())
|
|
{
|
|
if (!(*iter)->GetConnection()->IsOpen() ||
|
|
((*iter)->GetConnection()->GetRemoteAddress() == host_ &&
|
|
(*iter)->GetConnection()->GetRemotePort() == port_))
|
|
{
|
|
(*iter)->Fini();
|
|
this->transports.erase(iter++);
|
|
}
|
|
else
|
|
++iter;
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void Publication::RemoveSubscription(const NodePtr &_node)
|
|
{
|
|
boost::mutex::scoped_try_lock lock(this->nodeMutex);
|
|
if (!lock)
|
|
{
|
|
boost::mutex::scoped_lock removeLock(this->nodeRemoveMutex);
|
|
this->removeNodes.push_back(_node->GetId());
|
|
return;
|
|
}
|
|
|
|
std::list<NodePtr>::iterator iter;
|
|
|
|
for (iter = this->nodes.begin(); iter != this->nodes.end(); ++iter)
|
|
{
|
|
if ((*iter)->GetId() == _node->GetId())
|
|
{
|
|
this->nodes.erase(iter);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If no more subscribers, then disconnect from all publishers
|
|
if (this->nodes.empty() && this->callbacks.empty())
|
|
{
|
|
this->transports.clear();
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void Publication::RemoveSubscription(const std::string &_host,
|
|
unsigned int _port)
|
|
{
|
|
boost::mutex::scoped_try_lock lock(this->callbackMutex);
|
|
if (!lock)
|
|
{
|
|
boost::mutex::scoped_lock removeLock(this->nodeRemoveMutex);
|
|
this->removeCallbacks.push_back(std::make_pair(_host, _port));
|
|
return;
|
|
}
|
|
|
|
SubscriptionTransportPtr subptr;
|
|
std::list< CallbackHelperPtr >::iterator iter;
|
|
|
|
iter = this->callbacks.begin();
|
|
while (iter != this->callbacks.end())
|
|
{
|
|
subptr = boost::dynamic_pointer_cast<SubscriptionTransport>(*iter);
|
|
std::string host = subptr->GetConnection()->GetRemoteAddress();
|
|
if (!subptr || !subptr->GetConnection()->IsOpen() ||
|
|
((host.empty() || host == _host) &&
|
|
subptr->GetConnection()->GetRemotePort() == _port))
|
|
{
|
|
subptr.reset();
|
|
iter = this->callbacks.erase(iter);
|
|
}
|
|
else
|
|
++iter;
|
|
}
|
|
|
|
// If no more subscribers, then disconnect from all publishers
|
|
if (this->nodes.empty() && this->callbacks.empty())
|
|
{
|
|
this->transports.clear();
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void Publication::LocalPublish(const std::string &_data)
|
|
{
|
|
std::list<NodePtr>::iterator iter, endIter;
|
|
|
|
{
|
|
boost::mutex::scoped_lock lock(this->nodeMutex);
|
|
|
|
iter = this->nodes.begin();
|
|
endIter = this->nodes.end();
|
|
while (iter != endIter)
|
|
{
|
|
if ((*iter)->HandleData(this->topic, _data))
|
|
++iter;
|
|
else
|
|
this->nodes.erase(iter++);
|
|
}
|
|
}
|
|
|
|
// It's possible that the function pointed to by "HandleData" (above) will in
|
|
// turn call Publication::RemoveSubscription. The following function call
|
|
// will clean up the nodes that have then been marked for removal
|
|
this->RemoveNodes();
|
|
|
|
{
|
|
boost::mutex::scoped_lock lock(this->callbackMutex);
|
|
std::list< CallbackHelperPtr >::iterator cbIter;
|
|
cbIter = this->callbacks.begin();
|
|
while (cbIter != this->callbacks.end())
|
|
{
|
|
if ((*cbIter)->IsLocal())
|
|
{
|
|
if ((*cbIter)->HandleData(_data,
|
|
boost::bind(&dummy_callback_fn, _1), 0))
|
|
++cbIter;
|
|
else
|
|
cbIter = this->callbacks.erase(cbIter);
|
|
}
|
|
else
|
|
++cbIter;
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
int Publication::Publish(MessagePtr _msg, boost::function<void(uint32_t)> _cb,
|
|
uint32_t _id)
|
|
{
|
|
int result = 0;
|
|
std::list<NodePtr>::iterator iter, endIter;
|
|
|
|
{
|
|
boost::mutex::scoped_lock lock(this->nodeMutex);
|
|
|
|
iter = this->nodes.begin();
|
|
endIter = this->nodes.end();
|
|
while (iter != endIter)
|
|
{
|
|
if ((*iter)->HandleMessage(this->topic, _msg))
|
|
++iter;
|
|
else
|
|
this->nodes.erase(iter++);
|
|
}
|
|
}
|
|
|
|
// It's possible that the function pointed to by "HandleData" (above) will in
|
|
// turn call Publication::RemoveSubscription. The following function call
|
|
// will clean up the nodes that have then been marked for removal
|
|
this->RemoveNodes();
|
|
|
|
{
|
|
boost::mutex::scoped_lock lock(this->callbackMutex);
|
|
|
|
if (!this->callbacks.empty())
|
|
{
|
|
std::string data;
|
|
_msg->SerializeToString(&data);
|
|
std::list<CallbackHelperPtr>::iterator cbIter;
|
|
cbIter = this->callbacks.begin();
|
|
|
|
while (cbIter != this->callbacks.end())
|
|
{
|
|
if ((*cbIter)->HandleData(data, _cb, _id))
|
|
{
|
|
++result;
|
|
++cbIter;
|
|
}
|
|
else
|
|
this->callbacks.erase(cbIter++);
|
|
}
|
|
|
|
if (this->callbacks.empty() && !_cb.empty())
|
|
{
|
|
_cb(_id);
|
|
}
|
|
}
|
|
else if (!_cb.empty())
|
|
{
|
|
_cb(_id);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
std::string Publication::GetMsgType() const
|
|
{
|
|
return this->msgType;
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
unsigned int Publication::GetTransportCount() const
|
|
{
|
|
return this->transports.size();
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
unsigned int Publication::GetCallbackCount() const
|
|
{
|
|
boost::mutex::scoped_lock lock(this->callbackMutex);
|
|
return this->callbacks.size();
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
unsigned int Publication::PublisherCount() const
|
|
{
|
|
boost::mutex::scoped_lock lock(this->callbackMutex);
|
|
return this->publishers.size();
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
unsigned int Publication::GetNodeCount() const
|
|
{
|
|
boost::mutex::scoped_lock lock(this->nodeMutex);
|
|
return this->nodes.size();
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
unsigned int Publication::GetRemoteSubscriptionCount()
|
|
{
|
|
unsigned int count = 0;
|
|
|
|
boost::mutex::scoped_lock lock(this->callbackMutex);
|
|
std::list< CallbackHelperPtr >::iterator iter;
|
|
for (iter = this->callbacks.begin(); iter != this->callbacks.end(); ++iter)
|
|
{
|
|
if (!(*iter)->IsLocal())
|
|
count++;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
bool Publication::GetLocallyAdvertised() const
|
|
{
|
|
return this->locallyAdvertised;
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void Publication::SetLocallyAdvertised(bool _value)
|
|
{
|
|
this->locallyAdvertised = _value;
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void Publication::AddPublisher(PublisherPtr _pub)
|
|
{
|
|
boost::mutex::scoped_lock lock(this->callbackMutex);
|
|
this->publishers.push_back(_pub);
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void Publication::RemovePublisher(PublisherPtr _pub)
|
|
{
|
|
GZ_ASSERT(_pub, "Received a NULL PublisherPtr");
|
|
|
|
if (_pub)
|
|
this->RemovePublisher(_pub->Id());
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
bool Publication::RemovePublisher(const uint32_t _id)
|
|
{
|
|
boost::mutex::scoped_lock lock(this->callbackMutex);
|
|
|
|
// Find and erase the publiser
|
|
for (auto pubIter = this->publishers.begin();
|
|
pubIter != this->publishers.end(); ++pubIter)
|
|
{
|
|
if ((*pubIter)->Id() == _id)
|
|
{
|
|
this->publishers.erase(pubIter);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
void Publication::RemoveNodes()
|
|
{
|
|
boost::mutex::scoped_lock removeLock(this->nodeRemoveMutex);
|
|
|
|
// Remove queued nodes.
|
|
{
|
|
std::list<NodePtr>::iterator nodeIter;
|
|
|
|
for (std::list<unsigned int>::iterator iter = this->removeNodes.begin();
|
|
iter != this->removeNodes.end(); ++iter)
|
|
{
|
|
boost::mutex::scoped_lock lock(this->nodeMutex);
|
|
for (nodeIter = this->nodes.begin(); nodeIter != this->nodes.end();
|
|
++nodeIter)
|
|
{
|
|
if ((*nodeIter)->GetId() == (*iter))
|
|
{
|
|
this->nodes.erase(nodeIter);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Remove queued subscriptions.
|
|
{
|
|
SubscriptionTransportPtr subptr;
|
|
std::list< CallbackHelperPtr >::iterator iter;
|
|
std::list<std::pair<std::string, unsigned int> >::iterator cbIter;
|
|
|
|
// Iterate over all the queued subscriptions for removal
|
|
for (cbIter = this->removeCallbacks.begin();
|
|
cbIter != this->removeCallbacks.end(); ++cbIter)
|
|
{
|
|
boost::mutex::scoped_lock lock(this->callbackMutex);
|
|
|
|
// Find the callback that matches the host and port information, and
|
|
// remove it.
|
|
iter = this->callbacks.begin();
|
|
while (iter != this->callbacks.end())
|
|
{
|
|
subptr = boost::dynamic_pointer_cast<SubscriptionTransport>(*iter);
|
|
if (!subptr || !subptr->GetConnection()->IsOpen() ||
|
|
(subptr->GetConnection()->GetRemoteAddress() == (*cbIter).first &&
|
|
subptr->GetConnection()->GetRemotePort() == (*cbIter).second))
|
|
{
|
|
this->callbacks.erase(iter++);
|
|
}
|
|
else
|
|
++iter;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Clear the lists.
|
|
this->removeNodes.clear();
|
|
this->removeCallbacks.clear();
|
|
|
|
{
|
|
boost::mutex::scoped_lock lock(this->nodeMutex);
|
|
// If no more subscribers, then disconnect from all publishers
|
|
if (this->nodes.empty() && this->callbacks.empty())
|
|
{
|
|
this->transports.clear();
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
MessagePtr Publication::GetPrevMsg(uint32_t _pubId)
|
|
{
|
|
boost::mutex::scoped_lock lock(this->callbackMutex);
|
|
if (this->prevMsgs.find(_pubId) != this->prevMsgs.end())
|
|
return this->prevMsgs[_pubId];
|
|
else
|
|
return MessagePtr();
|
|
}
|
|
|