Skip to content
Snippets Groups Projects
UDPLink.cc 11.7 KiB
Newer Older
  • Learn to ignore specific revisions
  • pixhawk's avatar
    pixhawk committed
    /*=====================================================================
    
    
    lm's avatar
    lm committed
    QGroundControl Open Source Ground Control Station
    
    pixhawk's avatar
    pixhawk committed
    
    
    (c) 2009 - 2015 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
    
    pixhawk's avatar
    pixhawk committed
    
    
    lm's avatar
    lm committed
    This file is part of the QGROUNDCONTROL project
    
    pixhawk's avatar
    pixhawk committed
    
    
    lm's avatar
    lm committed
        QGROUNDCONTROL is free software: you can redistribute it and/or modify
    
    pixhawk's avatar
    pixhawk committed
        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.
    
    
    lm's avatar
    lm committed
        QGROUNDCONTROL is distributed in the hope that it will be useful,
    
    pixhawk's avatar
    pixhawk committed
        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
    
    lm's avatar
    lm committed
        along with QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.
    
    pixhawk's avatar
    pixhawk committed
    
    ======================================================================*/
    
    /**
     * @file
     *   @brief Definition of UDP connection (server) for unmanned vehicles
     *   @author Lorenz Meier <mavteam@student.ethz.ch>
     *
     */
    
    #include <QTimer>
    #include <QList>
    #include <QDebug>
    #include <QMutexLocker>
    
    #include <QNetworkProxy>
    
    pixhawk's avatar
    pixhawk committed
    #include <iostream>
    
    pixhawk's avatar
    pixhawk committed
    #include "UDPLink.h"
    
    #include <QHostInfo>
    
    pixhawk's avatar
    pixhawk committed
    
    
    UDPLink::UDPLink(UDPConfiguration* config)
        : _socket(NULL)
        , _connectState(false)
    
    pixhawk's avatar
    pixhawk committed
    {
    
        Q_ASSERT(config != NULL);
        _config = config;
        _config->setLink(this);
    
    
        // 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);
    
    pixhawk's avatar
    pixhawk committed
        // Set unique ID and add link to the list of links
    
        _id = getNextLinkId();
        qDebug() << "UDP Created " << _config->name();
    
    pixhawk's avatar
    pixhawk committed
    }
    
    UDPLink::~UDPLink()
    {
    
        // Disconnect link from configuration
        _config->setLink(NULL);
    
    Lorenz Meier's avatar
    Lorenz Meier committed
        // Tell the thread to exit
        quit();
        // Wait for it to exit
        wait();
    
    pixhawk's avatar
    pixhawk committed
    }
    
    /**
     * @brief Runs the thread
     *
     **/
    void UDPLink::run()
    {
    
    pixhawk's avatar
    pixhawk committed
    }
    
    
    pixhawk's avatar
    pixhawk committed
    {
    
        if(this->isConnected())
        {
            _disconnect();
            _connect();
        }
    
    pixhawk's avatar
    pixhawk committed
    }
    
    
    pixhawk's avatar
    pixhawk committed
    {
    
    }
    
    void UDPLink::addHost(const QString& host)
    {
    
    void UDPLink::removeHost(const QString& host)
    
    pixhawk's avatar
    pixhawk committed
    }
    
    
    pixhawk's avatar
    pixhawk committed
    void UDPLink::writeBytes(const char* data, qint64 size)
    {
        // Broadcast to all connected systems
    
        QString host;
        int port;
        if(_config->firstHost(host, port)) {
            do {
                if(UDPLINK_DEBUG) {
                    QString bytes;
                    QString ascii;
                    for (int i=0; i<size; i++)
                    {
                        unsigned char v = data[i];
                        bytes.append(QString().sprintf("%02x ", v));
                        if (data[i] > 31 && data[i] < 127)
                        {
                            ascii.append(data[i]);
                        }
                        else
                        {
                            ascii.append(219);
                        }
                    }
                    qDebug() << "Sent" << size << "bytes to" << host << ":" << port << "data:";
                    qDebug() << bytes;
                    qDebug() << "ASCII:" << ascii;
    
    lm's avatar
    lm committed
                }
    
                QHostAddress currentHost(host);
                _socket->writeDatagram(data, size, currentHost, (quint16)port);
                // Log the amount and time written out for future data rate calculations.
                QMutexLocker dataRateLocker(&dataRateMutex);
                logDataRateToBuffer(outDataWriteAmounts, outDataWriteTimes, &outDataIndex, size, QDateTime::currentMSecsSinceEpoch());
            } while (_config->nextHost(host, port));
    
    pixhawk's avatar
    pixhawk committed
    }
    
    /**
     * @brief Read a number of bytes from the interface.
     **/
    
    pixhawk's avatar
    pixhawk committed
    {
    
        {
            QByteArray datagram;
    
            datagram.resize(_socket->pendingDatagramSize());
    
    pixhawk's avatar
    pixhawk committed
    
    
            QHostAddress sender;
            quint16 senderPort;
    
            _socket->readDatagram(datagram.data(), datagram.size(), &sender, &senderPort);
    
    pixhawk's avatar
    pixhawk committed
    
    
            // FIXME TODO Check if this method is better than retrieving the data by individual processes
            emit bytesReceived(this, datagram);
    
    pixhawk's avatar
    pixhawk committed
    
    
            // Log this data reception for this timestep
            QMutexLocker dataRateLocker(&dataRateMutex);
            logDataRateToBuffer(inDataWriteAmounts, inDataWriteTimes, &inDataIndex, datagram.length(), QDateTime::currentMSecsSinceEpoch());
    
    
    //        // 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;
    
    pixhawk's avatar
    pixhawk 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
    
    dogmaphobic's avatar
    dogmaphobic committed
            _config->addHost(sender.toString(), (int)senderPort);
    
    pixhawk's avatar
    pixhawk committed
        }
    }
    
    /**
     * @brief Disconnect the connection.
     *
     * @return True if connection has been disconnected, false if connection couldn't be disconnected.
     **/
    
    bool UDPLink::_disconnect(void)
    
    pixhawk's avatar
    pixhawk committed
    {
    
        this->quit();
        this->wait();
        if (_socket) {
            // Make sure delete happen on correct thread
            _socket->deleteLater();
            _socket = NULL;
    
            emit disconnected();
    
        }
        // TODO When would this ever return false?
        _connectState = false;
        return !_connectState;
    
    pixhawk's avatar
    pixhawk committed
    }
    
    /**
     * @brief Connect the connection.
     *
     * @return True if connection has been established, false if connection couldn't be established.
     **/
    
    bool UDPLink::_connect(void)
    
    pixhawk's avatar
    pixhawk committed
    {
    
        if(this->isRunning())
        {
            this->quit();
            this->wait();
        }
        // TODO When would this ever return false?
    
        bool connected = true;
    
    oberion's avatar
    oberion committed
        start(HighPriority);
    
    oberion's avatar
    oberion committed
    }
    
    
    oberion's avatar
    oberion committed
    {
    
        QHostAddress host = QHostAddress::Any;
        _socket = new QUdpSocket();
    
        _socket->setProxy(QNetworkProxy::NoProxy);
    
        _connectState = _socket->bind(host, _config->localPort());
        QObject::connect(_socket, SIGNAL(readyRead()), this, SLOT(readBytes()));
        if (_connectState) {
    
    pixhawk's avatar
    pixhawk committed
    }
    
    /**
     * @brief Check if connection is active.
     *
     * @return True if link is connected, false otherwise.
     **/
    
    bool UDPLink::isConnected() const
    
    pixhawk's avatar
    pixhawk committed
    }
    
    
    pixhawk's avatar
    pixhawk committed
    {
    
    pixhawk's avatar
    pixhawk committed
    }
    
    
    qint64 UDPLink::getConnectionSpeed() const
    
    pixhawk's avatar
    pixhawk committed
    {
    
        return 54000000; // 54 Mbit
    }
    
    qint64 UDPLink::getCurrentInDataRate() const
    {
        return 0;
    
    pixhawk's avatar
    pixhawk committed
    }
    
    
    qint64 UDPLink::getCurrentOutDataRate() const
    
    pixhawk's avatar
    pixhawk committed
    {
    
    pixhawk's avatar
    pixhawk committed
    }
    
    
    //--------------------------------------------------------------------------
    //-- UDPConfiguration
    
    UDPConfiguration::UDPConfiguration(const QString& name) : LinkConfiguration(name)
    
    UDPConfiguration::UDPConfiguration(UDPConfiguration* source) : LinkConfiguration(source)
    
        _localPort = source->localPort();
        _hosts.clear();
        QString host;
        int port;
        if(source->firstHost(host, port)) {
            do {
                addHost(host, port);
            } while(source->nextHost(host, port));
        }
    
    void UDPConfiguration::copyFrom(LinkConfiguration *source)
    
        LinkConfiguration::copyFrom(source);
    
        UDPConfiguration* usource = dynamic_cast<UDPConfiguration*>(source);
        Q_ASSERT(usource != NULL);
        _localPort = usource->localPort();
        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)
    {
        if (host.contains(":"))
        {
            QHostInfo info = QHostInfo::fromName(host.split(":").first());
            if (info.error() == QHostInfo::NoError)
            {
                // Add host
                QList<QHostAddress> hostAddresses = info.addresses();
                QHostAddress address;
                for (int i = 0; i < hostAddresses.size(); i++)
                {
                    // Exclude loopback IPv4 and all IPv6 addresses
    
    dogmaphobic's avatar
    dogmaphobic committed
                    if (!hostAddresses.at(i).toString().contains(":") && !!hostAddresses.at(i).toString().startsWith("127"))
    
    dogmaphobic's avatar
    dogmaphobic committed
                _confMutex.lock();
    
                _hosts[address.toString()] = host.split(":").last().toInt();
    
    dogmaphobic's avatar
    dogmaphobic committed
                _confMutex.unlock();
                qDebug() << "UDP:" << "ADDING HOST:" << address.toString() << ":" << host.split(":").last();
    
    dogmaphobic's avatar
    dogmaphobic committed
            addHost(host, (int)_localPort);
    
        }
    }
    
    void UDPConfiguration::addHost(const QString& host, int port)
    {
    
    dogmaphobic's avatar
    dogmaphobic committed
        QMutexLocker locker(&_confMutex);
        if(_hosts.contains(host)) {
            if(_hosts[host] != port) {
                _hosts[host] = port;
            }
        } else {
            QHostInfo info = QHostInfo::fromName(host);
            if (info.error() == QHostInfo::NoError)
            {
                _hosts[info.addresses().first().toString()] = port;
                qDebug() << "UDP:" << "ADDING HOST:" << info.addresses().first().toString() << ":" << port;
            }
        }
    
    }
    
    void UDPConfiguration::removeHost(const QString& host)
    {
    
    dogmaphobic's avatar
    dogmaphobic committed
        QMutexLocker locker(&_confMutex);
    
        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()) {
            _hosts.erase(i);
        }
    }
    
    bool UDPConfiguration::firstHost(QString& host, int& port)
    {
    
    dogmaphobic's avatar
    dogmaphobic committed
        _confMutex.lock();
    
        _it = _hosts.begin();
        if(_it == _hosts.end()) {
    
    dogmaphobic's avatar
    dogmaphobic committed
            _confMutex.unlock();
    
    dogmaphobic's avatar
    dogmaphobic committed
        _confMutex.unlock();
    
        return nextHost(host, port);
    }
    
    bool UDPConfiguration::nextHost(QString& host, int& port)
    {
    
    dogmaphobic's avatar
    dogmaphobic committed
        QMutexLocker locker(&_confMutex);
    
        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's avatar
    dogmaphobic committed
        _confMutex.unlock();
        settings.beginGroup(root);
    
        _localPort = (quint16)settings.value("port", QGC_UDP_LOCAL_PORT).toUInt();
    
        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();
            }
        }