Skip to content
Snippets Groups Projects
UDPLink.cc 10.3 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 - 2011 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 <iostream>
    #include "UDPLink.h"
    #include "LinkManager.h"
    
    #include <QHostInfo>
    
    //#include <netinet/in.h>
    
    pixhawk's avatar
    pixhawk committed
    
    UDPLink::UDPLink(QHostAddress host, quint16 port)
    
    oberion's avatar
    oberion committed
    	: socket(NULL)
    
    pixhawk's avatar
    pixhawk committed
    {
        this->host = host;
        this->port = port;
        this->connectState = false;
        // Set unique ID and add link to the list of links
        this->id = getNextLinkId();
    
    oberion's avatar
    oberion committed
    	this->name = tr("UDP Link (port:%1)").arg(this->port);
    	emit nameChanged(this->name);
        // LinkManager::instance()->add(this);
    
    pixhawk's avatar
    pixhawk committed
    }
    
    UDPLink::~UDPLink()
    {
        disconnect();
    
    oberion's avatar
    oberion committed
    	this->deleteLater();
    
    pixhawk's avatar
    pixhawk committed
    }
    
    /**
     * @brief Runs the thread
     *
     **/
    void UDPLink::run()
    {
    
    oberion's avatar
    oberion committed
    	exec();
    
    pixhawk's avatar
    pixhawk committed
    }
    
    
    oberion's avatar
    oberion committed
    void UDPLink::setAddress(QHostAddress host)
    
    pixhawk's avatar
    pixhawk committed
    {
    
    oberion's avatar
    oberion committed
        bool reconnect(false);
    	if(this->isConnected())
    	{
    		disconnect();
    		reconnect = true;
    	}
    	this->host = host;
    	if(reconnect)
    	{
    		connect();
    	}
    
    pixhawk's avatar
    pixhawk committed
    }
    
    
    void UDPLink::setPort(int port)
    
    pixhawk's avatar
    pixhawk committed
    {
    
    oberion's avatar
    oberion committed
    	bool reconnect(false);
    	if(this->isConnected())
    	{
    		disconnect();
    		reconnect = true;
    	}
    
    pixhawk's avatar
    pixhawk committed
        this->port = port;
    
    oberion's avatar
    oberion committed
    	this->name = tr("UDP Link (port:%1)").arg(this->port);
    	emit nameChanged(this->name);
    	if(reconnect)
    	{
    		connect();
    	}
    
    }
    
    /**
     * @param host Hostname in standard formatting, e.g. localhost:14551 or 192.168.1.1:14551
     */
    void UDPLink::addHost(const QString& host)
    {
    
        //qDebug() << "UDP:" << "ADDING HOST:" << host;
    
        if (host.contains(":"))
        {
    
            //qDebug() << "HOST: " << host.split(":").first();
    
            QHostInfo info = QHostInfo::fromName(host.split(":").first());
    
    lm's avatar
    lm committed
            if (info.error() == QHostInfo::NoError)
    
    lm's avatar
    lm committed
                // Add host
                QList<QHostAddress> hostAddresses = info.addresses();
                QHostAddress address;
                for (int i = 0; i < hostAddresses.size(); i++)
    
    lm's avatar
    lm committed
                    // Exclude loopback IPv4 and all IPv6 addresses
                    if (!hostAddresses.at(i).toString().contains(":"))
                    {
                        address = hostAddresses.at(i);
                    }
    
    lm's avatar
    lm committed
                hosts.append(address);
    
    oberion's avatar
    oberion committed
    			this->setAddress(address);
    
                //qDebug() << "Address:" << address.toString();
    
    lm's avatar
    lm committed
                // Set port according to user input
                ports.append(host.split(":").last().toInt());
    
    oberion's avatar
    oberion committed
    			this->setPort(host.split(":").last().toInt());
    
            QHostInfo info = QHostInfo::fromName(host);
    
    lm's avatar
    lm committed
            if (info.error() == QHostInfo::NoError)
            {
                // Add host
                hosts.append(info.addresses().first());
                // Set port according to default (this port)
                ports.append(port);
            }
    
        }
    }
    
    void UDPLink::removeHost(const QString& hostname)
    {
        QString host = hostname;
        if (host.contains(":")) host = host.split(":").first();
        host = host.trimmed();
        QHostInfo info = QHostInfo::fromName(host);
        QHostAddress address;
        QList<QHostAddress> hostAddresses = info.addresses();
    
        for (int i = 0; i < hostAddresses.size(); i++)
        {
    
            // Exclude loopback IPv4 and all IPv6 addresses
    
            if (!hostAddresses.at(i).toString().contains(":"))
            {
    
                address = hostAddresses.at(i);
            }
        }
    
        for (int i = 0; i < hosts.count(); ++i)
        {
            if (hosts.at(i) == address)
            {
    
                hosts.removeAt(i);
                ports.removeAt(i);
            }
    
    pixhawk's avatar
    pixhawk committed
    }
    
    void UDPLink::writeBytes(const char* data, qint64 size)
    {
        // Broadcast to all connected systems
    
    lm's avatar
    lm committed
        for (int h = 0; h < hosts.size(); h++)
        {
    
            QHostAddress currentHost = hosts.at(h);
            quint16 currentPort = ports.at(h);
    
    lm's avatar
    lm committed
    //#define UDPLINK_DEBUG
    #ifdef UDPLINK_DEBUG
    
    lm's avatar
    lm committed
            QString bytes;
            QString ascii;
    
            for (int i=0; i<size; i++)
            {
    
    lm's avatar
    lm committed
                unsigned char v = data[i];
                bytes.append(QString().sprintf("%02x ", v));
    
    lm's avatar
    lm committed
                if (data[i] > 31 && data[i] < 127)
                {
                    ascii.append(data[i]);
                }
                else
                {
                    ascii.append(219);
                }
    
    lm's avatar
    lm committed
            qDebug() << "Sent" << size << "bytes to" << currentHost.toString() << ":" << currentPort << "data:";
            qDebug() << bytes;
            qDebug() << "ASCII:" << ascii;
    
    lm's avatar
    lm committed
    #endif
    
            socket->writeDatagram(data, size, currentHost, currentPort);
        }
    
    pixhawk's avatar
    pixhawk committed
    }
    
    /**
     * @brief Read a number of bytes from the interface.
     *
     * @param data Pointer to the data byte array to write the bytes to
     * @param maxLength The maximum number of bytes to write
     **/
    
    pixhawk's avatar
    pixhawk committed
    {
    
        while (socket->hasPendingDatagrams())
        {
            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
    
    
    //        // 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
    
    
    
            // Add host to broadcast list if not yet present
            if (!hosts.contains(sender))
            {
                hosts.append(sender);
                ports.append(senderPort);
                //        ports->insert(sender, senderPort);
            }
            else
            {
                int index = hosts.indexOf(sender);
                ports.replace(index, senderPort);
            }
    
    pixhawk's avatar
    pixhawk committed
        }
    }
    
    
    /**
     * @brief Get the number of bytes to read.
     *
     * @return The number of bytes to read
     **/
    
    qint64 UDPLink::bytesAvailable()
    {
    
    pixhawk's avatar
    pixhawk committed
        return socket->pendingDatagramSize();
    }
    
    /**
     * @brief Disconnect the connection.
     *
     * @return True if connection has been disconnected, false if connection couldn't be disconnected.
     **/
    bool UDPLink::disconnect()
    {
    
    oberion's avatar
    oberion committed
    	this->quit();
    	this->wait();
    
    
    oberion's avatar
    oberion committed
    	{
    		delete socket;
    		socket = NULL;
    	}
    
    pixhawk's avatar
    pixhawk committed
    
        connectState = false;
    
        emit disconnected();
        emit connected(false);
        return !connectState;
    }
    
    /**
     * @brief Connect the connection.
     *
     * @return True if connection has been established, false if connection couldn't be established.
     **/
    bool UDPLink::connect()
    {
    
    oberion's avatar
    oberion committed
    	if(this->isRunning())
    	{
    		this->quit();
    		this->wait();
    	}
    
        bool connected = this->hardwareConnect();
    
    oberion's avatar
    oberion committed
        start(HighPriority);
    
    oberion's avatar
    oberion committed
    }
    
    bool UDPLink::hardwareConnect(void)
    {
    	socket = new QUdpSocket();
    
    pixhawk's avatar
    pixhawk committed
    
    
    pixhawk's avatar
    pixhawk committed
        //Check if we are using a multicast-address
    //    bool multicast = false;
    //    if (host.isInSubnet(QHostAddress("224.0.0.0"),4))
    //    {
    //        multicast = true;
    //        connectState = socket->bind(port, QUdpSocket::ShareAddress);
    //    }
    //    else
    //    {
    
        connectState = socket->bind(host, port);
    
    pixhawk's avatar
    pixhawk committed
    //    }
    
        //Provides Multicast functionality to UdpSocket
        /* not working yet
        if (multicast)
        {
            int sendingFd = socket->socketDescriptor();
    
            if (sendingFd != -1)
            {
                // set up destination address
                struct sockaddr_in sendAddr;
                memset(&sendAddr,0,sizeof(sendAddr));
                sendAddr.sin_family=AF_INET;
                sendAddr.sin_addr.s_addr=inet_addr(HELLO_GROUP);
                sendAddr.sin_port=htons(port);
    
                // set TTL
                unsigned int ttl = 1; // restricted to the same subnet
                if (setsockopt(sendingFd, IPPROTO_IP, IP_MULTICAST_TTL, (unsigned int*)&ttl, sizeof(ttl) ) < 0)
                {
                    std::cout << "TTL failed\n";
                }
            }
        }
        */
    
    
    pixhawk's avatar
    pixhawk committed
        //QObject::connect(socket, SIGNAL(readyRead()), this, SLOT(readPendingDatagrams()));
    
        QObject::connect(socket, SIGNAL(readyRead()), this, SLOT(readBytes()));
    
    pixhawk's avatar
    pixhawk committed
    
        emit connected(connectState);
    
        if (connectState) {
    
            connectionStartTime = QGC::groundTimeUsecs()/1000;
    
    oberion's avatar
    oberion committed
    	return connectState;
    
    pixhawk's avatar
    pixhawk committed
    }
    
    
    oberion's avatar
    oberion committed
    
    
    pixhawk's avatar
    pixhawk committed
    /**
     * @brief Check if connection is active.
     *
     * @return True if link is connected, false otherwise.
     **/
    
    bool UDPLink::isConnected()
    {
    
    pixhawk's avatar
    pixhawk committed
        return connectState;
    }
    
    int UDPLink::getId()
    {
        return id;
    }
    
    QString UDPLink::getName()
    {
        return name;
    }
    
    void UDPLink::setName(QString name)
    {
        this->name = name;
        emit nameChanged(this->name);
    }
    
    
    qint64 UDPLink::getNominalDataRate()
    {
    
    pixhawk's avatar
    pixhawk committed
        return 54000000; // 54 Mbit
    }
    
    
    qint64 UDPLink::getTotalUpstream()
    {
    
    pixhawk's avatar
    pixhawk committed
        statisticsMutex.lock();
    
        qint64 totalUpstream = bitsSentTotal / ((QGC::groundTimeUsecs()/1000 - connectionStartTime) / 1000);
    
    pixhawk's avatar
    pixhawk committed
        statisticsMutex.unlock();
    
    pixhawk's avatar
    pixhawk committed
    }
    
    
    qint64 UDPLink::getCurrentUpstream()
    {
    
    pixhawk's avatar
    pixhawk committed
        return 0; // TODO
    }
    
    
    qint64 UDPLink::getMaxUpstream()
    {
    
    pixhawk's avatar
    pixhawk committed
        return 0; // TODO
    }
    
    
    qint64 UDPLink::getBitsSent()
    {
    
    pixhawk's avatar
    pixhawk committed
        return bitsSentTotal;
    }
    
    
    qint64 UDPLink::getBitsReceived()
    {
    
    pixhawk's avatar
    pixhawk committed
        return bitsReceivedTotal;
    }
    
    
    qint64 UDPLink::getTotalDownstream()
    {
    
    pixhawk's avatar
    pixhawk committed
        statisticsMutex.lock();
    
        qint64 totalDownstream = bitsReceivedTotal / ((QGC::groundTimeUsecs()/1000 - connectionStartTime) / 1000);
    
    pixhawk's avatar
    pixhawk committed
        statisticsMutex.unlock();
    
    pixhawk's avatar
    pixhawk committed
    }
    
    
    qint64 UDPLink::getCurrentDownstream()
    {
    
    pixhawk's avatar
    pixhawk committed
        return 0; // TODO
    }
    
    
    qint64 UDPLink::getMaxDownstream()
    {
    
    pixhawk's avatar
    pixhawk committed
        return 0; // TODO
    }
    
    
    bool UDPLink::isFullDuplex()
    {
    
    pixhawk's avatar
    pixhawk committed
        return true;
    }
    
    
    int UDPLink::getLinkQuality()
    {
    
    pixhawk's avatar
    pixhawk committed
        /* This feature is not supported with this interface */
        return -1;
    }