Skip to content
Snippets Groups Projects
TCPLink.cc 7.07 KiB
Newer Older
  • Learn to ignore specific revisions
  • Don Gagne's avatar
    Don Gagne committed
    /*=====================================================================
     
     QGroundControl Open Source Ground Control Station
     
     (c) 2009 - 2011 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
     
     This file is part of the QGROUNDCONTROL project
     
     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/>.
     
     ======================================================================*/
    
    #include <QTimer>
    #include <QList>
    #include <QDebug>
    #include <QMutexLocker>
    #include <iostream>
    #include "TCPLink.h"
    #include "LinkManager.h"
    #include "QGC.h"
    #include <QHostInfo>
    
    #include <QSignalSpy>
    
    Don Gagne's avatar
    Don Gagne committed
    
    
    /// @file
    ///     @brief TCP link type for SITL support
    ///
    ///     @author Don Gagne <don@thegagnes.com>
    
    Don Gagne's avatar
    Don Gagne committed
    
    
    TCPLink::TCPLink(QHostAddress hostAddress, quint16 socketPort) :
        _hostAddress(hostAddress),
        _port(socketPort),
        _socket(NULL),
        _socketIsConnected(false)
    
    Don Gagne's avatar
    Don Gagne 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);
    
    
        _linkId = getNextLinkId();
        _resetName();
    
    Don Gagne's avatar
    Don Gagne committed
        
    
        qDebug() << "TCP Created " << _name;
    
    Don Gagne's avatar
    Don Gagne committed
    }
    
    TCPLink::~TCPLink()
    {
        disconnect();
    
    Lorenz Meier's avatar
    Lorenz Meier committed
    
        // Tell the thread to exit
        quit();
        // Wait for it to exit
        wait();
    
    
    	deleteLater();
    
    Don Gagne's avatar
    Don Gagne committed
    }
    
    void TCPLink::run()
    {
    
        _hardwareConnect();
    
    
    Don Gagne's avatar
    Don Gagne committed
    	exec();
    }
    
    
    void TCPLink::setHostAddress(QHostAddress hostAddress)
    
    Don Gagne's avatar
    Don Gagne committed
    {
    
        bool reconnect = false;
        
    	if (this->isConnected()) {
    
    Don Gagne's avatar
    Don Gagne committed
    		disconnect();
    		reconnect = true;
    	}
    
        
    	_hostAddress = hostAddress;
        _resetName();
        
    	if (reconnect) {
    
    Don Gagne's avatar
    Don Gagne committed
    		connect();
    	}
    }
    
    
    void TCPLink::setHostAddress(const QString& hostAddress)
    {
        setHostAddress(QHostAddress(hostAddress));
    }
    
    
    Don Gagne's avatar
    Don Gagne committed
    void TCPLink::setPort(int port)
    {
    
        bool reconnect = false;
        
    	if (this->isConnected()) {
    
    Don Gagne's avatar
    Don Gagne committed
    		disconnect();
    		reconnect = true;
    	}
    
        
    	_port = port;
        _resetName();
        
    	if (reconnect) {
    
    Don Gagne's avatar
    Don Gagne committed
    		connect();
    	}
    }
    
    
    Don Gagne's avatar
    Don Gagne committed
    #ifdef TCPLINK_READWRITE_DEBUG
    
    void TCPLink::_writeDebugBytes(const char *data, qint16 size)
    
    Don Gagne's avatar
    Don Gagne committed
    {
        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" << _hostAddress.toString() << ":" << _port << "data:";
    
    Don Gagne's avatar
    Don Gagne committed
        qDebug() << bytes;
        qDebug() << "ASCII:" << ascii;
    
    Don Gagne's avatar
    Don Gagne committed
    }
    #endif
    
    void TCPLink::writeBytes(const char* data, qint64 size)
    {
    #ifdef TCPLINK_READWRITE_DEBUG
    
        _writeDebugBytes(data, size);
    
    Don Gagne's avatar
    Don Gagne committed
    #endif
    
        _socket->write(data, size);
    
    
        // Log the amount and time written out for future data rate calculations.
        QMutexLocker dataRateLocker(&dataRateMutex);
        logDataRateToBuffer(outDataWriteAmounts, outDataWriteTimes, &outDataIndex, size, QDateTime::currentMSecsSinceEpoch());
    
    Don Gagne's avatar
    Don Gagne 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
     **/
    void TCPLink::readBytes()
    {
    
        qint64 byteCount = _socket->bytesAvailable();
    
    Don Gagne's avatar
    Don Gagne committed
        
        if (byteCount)
        {
            QByteArray buffer;
            buffer.resize(byteCount);
            
    
            _socket->read(buffer.data(), buffer.size());
    
    Don Gagne's avatar
    Don Gagne committed
            
            emit bytesReceived(this, buffer);
    
    Don Gagne's avatar
    Don Gagne committed
    
    
            // Log the amount and time received for future data rate calculations.
            QMutexLocker dataRateLocker(&dataRateMutex);
            logDataRateToBuffer(inDataWriteAmounts, inDataWriteTimes, &inDataIndex, byteCount, QDateTime::currentMSecsSinceEpoch());
    
    
    Don Gagne's avatar
    Don Gagne committed
    #ifdef TCPLINK_READWRITE_DEBUG
            writeDebugBytes(buffer.data(), buffer.size());
    #endif
    
    Don Gagne's avatar
    Don Gagne committed
        }
    }
    
    /**
     * @brief Get the number of bytes to read.
     *
     * @return The number of bytes to read
     **/
    qint64 TCPLink::bytesAvailable()
    {
    
        return _socket->bytesAvailable();
    
    Don Gagne's avatar
    Don Gagne committed
    }
    
    /**
     * @brief Disconnect the connection.
     *
     * @return True if connection has been disconnected, false if connection couldn't be disconnected.
     **/
    bool TCPLink::disconnect()
    {
    
    Don Gagne's avatar
    Don Gagne committed
        
    
        if (_socket)
    
    Don Gagne's avatar
    Don Gagne committed
    	{
    
            _socketIsConnected = false;
    		delete _socket;
    		_socket = NULL;
    
            emit disconnected();
            emit connected(false);
    
    Don Gagne's avatar
    Don Gagne committed
    	}
        
        return true;
    }
    
    /**
     * @brief Connect the connection.
     *
     * @return True if connection has been established, false if connection couldn't be established.
     **/
    bool TCPLink::connect()
    {
    
    	if (isRunning())
    
    Don Gagne's avatar
    Don Gagne committed
    	{
    
    Don Gagne's avatar
    Don Gagne committed
    	}
    
    
        start(HighPriority);
    
        return true;
    
    Don Gagne's avatar
    Don Gagne committed
    }
    
    
    bool TCPLink::_hardwareConnect(void)
    
    Don Gagne's avatar
    Don Gagne committed
    {
    
        Q_ASSERT(_socket == NULL);
    	_socket = new QTcpSocket();
    
    Don Gagne's avatar
    Don Gagne committed
        
    
        QSignalSpy errorSpy(_socket, SIGNAL(error(QAbstractSocket::SocketError)));
    
    Don Gagne's avatar
    Don Gagne committed
        
    
        _socket->connectToHost(_hostAddress, _port);
        
        QObject::connect(_socket, SIGNAL(readyRead()), this, SLOT(readBytes()));
        QObject::connect(_socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(_socketError(QAbstractSocket::SocketError)));
    
    Don Gagne's avatar
    Don Gagne committed
        
        // Give the socket a second to connect to the other side otherwise error out
    
        if (!_socket->waitForConnected(1000))
    
    Don Gagne's avatar
    Don Gagne committed
        {
    
            // Whether a failed connection emits an error signal or not is platform specific.
            // So in cases where it is not emitted, we emit one ourselves.
            if (errorSpy.count() == 0) {
                emit communicationError(getName(), "Connection failed");
            }
            delete _socket;
            _socket = NULL;
    
    Don Gagne's avatar
    Don Gagne committed
            return false;
        }
    
    Don Gagne's avatar
    Don Gagne committed
        
    
        _socketIsConnected = true;
    
    Don Gagne's avatar
    Don Gagne committed
        emit connected(true);
    
        emit connected();
    
    Don Gagne's avatar
    Don Gagne committed
    
    
    Don Gagne's avatar
    Don Gagne committed
        return true;
    }
    
    
    void TCPLink::_socketError(QAbstractSocket::SocketError socketError)
    
    Don Gagne's avatar
    Don Gagne committed
    {
    
        emit communicationError(getName(), "Error on socket: " + _socket->errorString());
    
    Don Gagne's avatar
    Don Gagne committed
    }
    
    /**
     * @brief Check if connection is active.
     *
     * @return True if link is connected, false otherwise.
     **/
    bool TCPLink::isConnected() const
    {
    
        return _socketIsConnected;
    
    Don Gagne's avatar
    Don Gagne committed
    }
    
    int TCPLink::getId() const
    {
    
        return _linkId;
    
    Don Gagne's avatar
    Don Gagne committed
    }
    
    QString TCPLink::getName() const
    {
    
        return _name;
    
    Don Gagne's avatar
    Don Gagne committed
    }
    
    
    qint64 TCPLink::getConnectionSpeed() const
    
    Don Gagne's avatar
    Don Gagne committed
    {
        return 54000000; // 54 Mbit
    
    }
    
    qint64 TCPLink::getCurrentInDataRate() const
    {
        return 0;
    }
    
    qint64 TCPLink::getCurrentOutDataRate() const
    {
        return 0;
    }
    
    
    void TCPLink::_resetName(void)
    {
        _name = QString("TCP Link (host:%1 port:%2)").arg(_hostAddress.toString()).arg(_port);
        emit nameChanged(_name);
    
    Don Gagne's avatar
    Don Gagne committed
    }
    
    
    void TCPLink::waitForBytesWritten(int msecs)
    {
        Q_ASSERT(_socket);
        _socket->waitForBytesWritten(msecs);
    }
    
    void TCPLink::waitForReadyRead(int msecs)
    {
        Q_ASSERT(_socket);
        _socket->waitForReadyRead(msecs);
    }