Newer
Older
/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009 - 2015 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
QGROUNDCONTROL is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
QGROUNDCONTROL is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.
======================================================================*/
/**
* @file
* @brief Definition of UDP connection (server) for unmanned vehicles
* @author Lorenz Meier <mavteam@student.ethz.ch>
*
*/
#include <QtGlobal>
#if QT_VERSION > 0x050401
#define UDP_BROKEN_SIGNAL 1
#else
#define UDP_BROKEN_SIGNAL 0
#endif
#include <QTimer>
#include <QList>
#include <QDebug>
#include <QMutexLocker>
dogmaphobic
committed
static const char* kZeroconfRegistration = "_qgroundcontrol._udp";
static bool is_ip(const QString& address)
{
int a,b,c,d;
if (sscanf(address.toStdString().c_str(), "%d.%d.%d.%d", &a, &b, &c, &d) != 4
&& strcmp("::1", address.toStdString().c_str())) {
}
static QString get_ip_address(const QString& address)
{
if(is_ip(address))
return address;
// Need to look it up
QHostInfo info = QHostInfo::fromName(address);
if (info.error() == QHostInfo::NoError)
{
QList<QHostAddress> hostAddresses = info.addresses();
QHostAddress address;
for (int i = 0; i < hostAddresses.size(); i++)
{
// Exclude all IPv6 addresses
if (!hostAddresses.at(i).toString().contains(":"))
{
return hostAddresses.at(i).toString();
}
}
}
return QString("");
}
dogmaphobic
committed
UDPLink::UDPLink(UDPConfiguration* config)
: _socket(NULL)
, _connectState(false)
, _running(false)
dogmaphobic
committed
Q_ASSERT(config != NULL);
_config = config;
_config->setLink(this);
Lorenz Meier
committed
// We're doing it wrong - because the Qt folks got the API wrong:
// http://blog.qt.digia.com/blog/2010/06/17/youre-doing-it-wrong/
moveToThread(this);
Bryant Mairs
committed
//qDebug() << "UDP Created " << _config->name();
dogmaphobic
committed
// Disconnect link from configuration
_config->setLink(NULL);
_running = false;
while(_outQueue.count() > 0) {
delete _outQueue.dequeue();
}
dogmaphobic
committed
this->deleteLater();
}
/**
* @brief Runs the thread
*
**/
void UDPLink::run()
{
if(_hardwareConnect()) {
if(UDP_BROKEN_SIGNAL) {
bool loop = false;
while(true) {
//-- Anything to read?
loop = _socket->hasPendingDatagrams();
if(loop) {
readBytes();
}
//-- Loop right away if busy
if((_dequeBytes() || loop) && _running)
continue;
if(!_running)
break;
//-- Settle down (it gets here if there is nothing to read or write)
this->msleep(50);
}
} else {
exec();
}
}
dogmaphobic
committed
void UDPLink::_restartConnection()
dogmaphobic
committed
if(this->isConnected())
{
_disconnect();
_connect();
}
dogmaphobic
committed
QString UDPLink::getName() const
dogmaphobic
committed
return _config->name();
}
void UDPLink::addHost(const QString& host)
{
dogmaphobic
committed
_config->addHost(host);
dogmaphobic
committed
void UDPLink::removeHost(const QString& host)
dogmaphobic
committed
_config->removeHost(host);
}
void UDPLink::writeBytes(const char* data, qint64 size)
{
if(UDP_BROKEN_SIGNAL) {
QByteArray* qdata = new QByteArray(data, size);
QMutexLocker lock(&_mutex);
_outQueue.enqueue(qdata);
} else {
_sendBytes(data, size);
}
}
{
QMutexLocker lock(&_mutex);
if(_outQueue.count() > 0) {
QByteArray* qdata = _outQueue.dequeue();
lock.unlock();
_sendBytes(qdata->data(), qdata->size());
delete qdata;
lock.relock();
return (_outQueue.count() > 0);
void UDPLink::_sendBytes(const char* data, qint64 size)
{
QStringList goneHosts;
// Send to all connected systems
QString host;
int port;
if(_config->firstHost(host, port)) {
do {
QHostAddress currentHost(host);
if(_socket->writeDatagram(data, size, currentHost, (quint16)port) < 0) {
// This host is gone. Add to list to be removed
goneHosts.append(host);
}
_logOutputDataRate(size, QDateTime::currentMSecsSinceEpoch());
} while (_config->nextHost(host, port));
//-- Remove hosts that are no longer there
foreach (QString ghost, goneHosts) {
_config->removeHost(ghost);
}
}
}
/**
* @brief Read a number of bytes from the interface.
**/
void UDPLink::readBytes()
dogmaphobic
committed
while (_socket->hasPendingDatagrams())
{
QByteArray datagram;
dogmaphobic
committed
datagram.resize(_socket->pendingDatagramSize());
QHostAddress sender;
quint16 senderPort;
dogmaphobic
committed
_socket->readDatagram(datagram.data(), datagram.size(), &sender, &senderPort);
// FIXME TODO Check if this method is better than retrieving the data by individual processes
emit bytesReceived(this, datagram);
_logInputDataRate(datagram.length(), QDateTime::currentMSecsSinceEpoch());
Bryant Mairs
committed
// // Echo data for debugging purposes
// std::cerr << __FILE__ << __LINE__ << "Received datagram:" << std::endl;
// int i;
// for (i=0; i<s; i++)
// {
// unsigned int v=data[i];
// fprintf(stderr,"%02x ", v);
// }
// std::cerr << std::endl;
dogmaphobic
committed
// TODO This doesn't validade the sender. Anything sending UDP packets to this port gets
// added to the list and will start receiving datagrams from here. Even a port scanner
// would trigger this.
// Add host to broadcast list if not yet present, or update its port
if(UDP_BROKEN_SIGNAL && !_running)
break;
}
}
/**
* @brief Disconnect the connection.
*
* @return True if connection has been disconnected, false if connection couldn't be disconnected.
**/
bool UDPLink::_disconnect(void)
_running = false;
wait();
dogmaphobic
committed
if (_socket) {
// Make sure delete happen on correct thread
_socket->deleteLater();
_socket = NULL;
dogmaphobic
committed
}
_connectState = false;
}
/**
* @brief Connect the connection.
*
* @return True if connection has been established, false if connection couldn't be established.
**/
bool UDPLink::_connect(void)
if(this->isRunning() || _running)
dogmaphobic
committed
{
_running = false;
wait();
dogmaphobic
committed
}
_running = true;
dogmaphobic
committed
bool UDPLink::_hardwareConnect()
if (_socket) {
delete _socket;
_socket = NULL;
}
QHostAddress host = QHostAddress::AnyIPv4;
dogmaphobic
committed
_socket = new QUdpSocket();
_socket->setProxy(QNetworkProxy::NoProxy);
_connectState = _socket->bind(host, _config->localPort(), QAbstractSocket::ReuseAddressHint);
dogmaphobic
committed
if (_connectState) {
_registerZeroconf(_config->localPort(), kZeroconfRegistration);
//-- Connect signal if this version of Qt is not broken
if(!UDP_BROKEN_SIGNAL) {
QObject::connect(_socket, SIGNAL(readyRead()), this, SLOT(readBytes()));
}
emit connected();
} else {
emit communicationError("UDP Link Error", "Error binding UDP port");
}
dogmaphobic
committed
return _connectState;
}
/**
* @brief Check if connection is active.
*
* @return True if link is connected, false otherwise.
**/
bool UDPLink::isConnected() const
dogmaphobic
committed
return _connectState;
dogmaphobic
committed
qint64 UDPLink::getConnectionSpeed() const
dogmaphobic
committed
return 54000000; // 54 Mbit
}
qint64 UDPLink::getCurrentInDataRate() const
{
return 0;
dogmaphobic
committed
qint64 UDPLink::getCurrentOutDataRate() const
dogmaphobic
committed
return 0;
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
void UDPLink::_registerZeroconf(uint16_t port, const std::string ®Type)
{
#if defined(QGC_ZEROCONF_ENABLED)
DNSServiceErrorType result = DNSServiceRegister(&_dnssServiceRef, 0, 0, 0,
regType.c_str(),
NULL,
NULL,
htons(port),
0,
NULL,
NULL,
NULL);
if (result != kDNSServiceErr_NoError)
{
emit communicationError("UDP Link Error", "Error registering Zeroconf");
_dnssServiceRef = NULL;
}
#else
Q_UNUSED(port);
Q_UNUSED(regType);
#endif
}
void UDPLink::_deregisterZeroconf()
{
#if defined(QGC_ZEROCONF_ENABLED)
if (_dnssServiceRef)
{
DNSServiceRefDeallocate(_dnssServiceRef);
_dnssServiceRef = NULL;
}
#endif
}
dogmaphobic
committed
//--------------------------------------------------------------------------
//-- UDPConfiguration
dogmaphobic
committed
UDPConfiguration::UDPConfiguration(const QString& name) : LinkConfiguration(name)
dogmaphobic
committed
_localPort = QGC_UDP_LOCAL_PORT;
dogmaphobic
committed
UDPConfiguration::UDPConfiguration(UDPConfiguration* source) : LinkConfiguration(source)
dogmaphobic
committed
_localPort = source->localPort();
_hosts.clear();
QString host;
int port;
if(source->firstHost(host, port)) {
do {
addHost(host, port);
} while(source->nextHost(host, port));
}
dogmaphobic
committed
void UDPConfiguration::copyFrom(LinkConfiguration *source)
LinkConfiguration::copyFrom(source);
dogmaphobic
committed
UDPConfiguration* usource = dynamic_cast<UDPConfiguration*>(source);
Q_ASSERT(usource != NULL);
_localPort = usource->localPort();
dogmaphobic
committed
QString host;
int port;
if(usource->firstHost(host, port)) {
do {
addHost(host, port);
} while(usource->nextHost(host, port));
}
}
/**
* @param host Hostname in standard formatt, e.g. localhost:14551 or 192.168.1.1:14551
*/
void UDPConfiguration::addHost(const QString& host)
{
dogmaphobic
committed
if (host.contains(":"))
{
addHost(host.split(":").first(), host.split(":").last().toInt());
dogmaphobic
committed
}
dogmaphobic
committed
else
{
dogmaphobic
committed
}
}
void UDPConfiguration::addHost(const QString& host, int port)
{
QMutexLocker locker(&_confMutex);
if(_hosts.contains(host)) {
if(_hosts[host] != port) {
_hosts[host] = port;
}
} else {
QString ipAdd = get_ip_address(host);
if(ipAdd.isEmpty()) {
qWarning() << "UDP:" << "Could not resolve host:" << host << "port:" << port;
//qDebug() << "UDP:" << "Adding Host:" << ipAdd << ":" << port;
dogmaphobic
committed
}
void UDPConfiguration::removeHost(const QString& host)
{
dogmaphobic
committed
QString tHost = host;
if (tHost.contains(":")) {
tHost = tHost.split(":").first();
}
tHost = tHost.trimmed();
QMap<QString, int>::iterator i = _hosts.find(tHost);
if(i != _hosts.end()) {
//qDebug() << "UDP:" << "Removed host:" << host;
dogmaphobic
committed
_hosts.erase(i);
} else {
qWarning() << "UDP:" << "Could not remove unknown host:" << host;
dogmaphobic
committed
}
}
bool UDPConfiguration::firstHost(QString& host, int& port)
{
dogmaphobic
committed
_it = _hosts.begin();
if(_it == _hosts.end()) {
dogmaphobic
committed
return false;
}
dogmaphobic
committed
return nextHost(host, port);
}
bool UDPConfiguration::nextHost(QString& host, int& port)
{
dogmaphobic
committed
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
if(_it != _hosts.end()) {
host = _it.key();
port = _it.value();
_it++;
return true;
}
return false;
}
void UDPConfiguration::setLocalPort(quint16 port)
{
_localPort = port;
}
void UDPConfiguration::saveSettings(QSettings& settings, const QString& root)
{
_confMutex.lock();
settings.beginGroup(root);
settings.setValue("port", (int)_localPort);
settings.setValue("hostCount", _hosts.count());
int index = 0;
QMap<QString, int>::const_iterator it = _hosts.begin();
while(it != _hosts.end()) {
QString hkey = QString("host%1").arg(index);
settings.setValue(hkey, it.key());
QString pkey = QString("port%1").arg(index);
settings.setValue(pkey, it.value());
it++;
index++;
}
settings.endGroup();
_confMutex.unlock();
}
void UDPConfiguration::loadSettings(QSettings& settings, const QString& root)
{
_confMutex.lock();
_hosts.clear();
dogmaphobic
committed
_localPort = (quint16)settings.value("port", QGC_UDP_LOCAL_PORT).toUInt();
dogmaphobic
committed
int hostCount = settings.value("hostCount", 0).toInt();
for(int i = 0; i < hostCount; i++) {
QString hkey = QString("host%1").arg(i);
QString pkey = QString("port%1").arg(i);
if(settings.contains(hkey) && settings.contains(pkey)) {
addHost(settings.value(hkey).toString(), settings.value(pkey).toInt());
}
}
settings.endGroup();
}
void UDPConfiguration::updateSettings()
{
if(_link) {
UDPLink* ulink = dynamic_cast<UDPLink*>(_link);
if(ulink) {
ulink->_restartConnection();
}
}