pxmlw6n2f/Gazebo_Distributed_TCP/gazebo/transport/Publication.cc

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();
}