TCPLink.cc 7.06 KB
Newer Older
Don Gagne's avatar
Don Gagne committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
/*=====================================================================
 
 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>
33
#include <QSignalSpy>
Don Gagne's avatar
Don Gagne committed
34

35 36 37 38
/// @file
///     @brief TCP link type for SITL support
///
///     @author Don Gagne <don@thegagnes.com>
Don Gagne's avatar
Don Gagne committed
39

40 41 42 43 44
TCPLink::TCPLink(QHostAddress hostAddress, quint16 socketPort) :
    _hostAddress(hostAddress),
    _port(socketPort),
    _socket(NULL),
    _socketIsConnected(false)
Don Gagne's avatar
Don Gagne committed
45
{
46 47 48 49 50 51
    // Even though QAbstractSocket::SocketError is used in a signal by Qt, Qt doesn't declare it as a meta type.
    // This in turn causes debug output to be kicked out about not being able to queue the signal. We register it
    // as a meta type to silence that.
    static int idMetaType = qRegisterMetaType<QAbstractSocket::SocketError>();
    Q_UNUSED(idMetaType);
    
52 53
    _linkId = getNextLinkId();
    _resetName();
Don Gagne's avatar
Don Gagne committed
54
    
55
    qDebug() << "TCP Created " << _name;
Don Gagne's avatar
Don Gagne committed
56 57 58 59 60
}

TCPLink::~TCPLink()
{
    disconnect();
61
	deleteLater();
Don Gagne's avatar
Don Gagne committed
62 63 64 65 66 67 68
}

void TCPLink::run()
{
	exec();
}

69
void TCPLink::setHostAddress(QHostAddress hostAddress)
Don Gagne's avatar
Don Gagne committed
70
{
71 72 73
    bool reconnect = false;
    
	if (this->isConnected()) {
Don Gagne's avatar
Don Gagne committed
74 75 76
		disconnect();
		reconnect = true;
	}
77 78 79 80 81
    
	_hostAddress = hostAddress;
    _resetName();
    
	if (reconnect) {
Don Gagne's avatar
Don Gagne committed
82 83 84 85
		connect();
	}
}

86 87 88 89 90
void TCPLink::setHostAddress(const QString& hostAddress)
{
    setHostAddress(QHostAddress(hostAddress));
}

Don Gagne's avatar
Don Gagne committed
91 92
void TCPLink::setPort(int port)
{
93 94 95
    bool reconnect = false;
    
	if (this->isConnected()) {
Don Gagne's avatar
Don Gagne committed
96 97 98
		disconnect();
		reconnect = true;
	}
99 100 101 102 103
    
	_port = port;
    _resetName();
    
	if (reconnect) {
Don Gagne's avatar
Don Gagne committed
104 105 106 107
		connect();
	}
}

Don Gagne's avatar
Don Gagne committed
108
#ifdef TCPLINK_READWRITE_DEBUG
109
void TCPLink::_writeDebugBytes(const char *data, qint16 size)
Don Gagne's avatar
Don Gagne committed
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
{
    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);
        }
    }
126
    qDebug() << "Sent" << size << "bytes to" << _hostAddress.toString() << ":" << _port << "data:";
Don Gagne's avatar
Don Gagne committed
127 128
    qDebug() << bytes;
    qDebug() << "ASCII:" << ascii;
Don Gagne's avatar
Don Gagne committed
129 130 131 132 133 134
}
#endif

void TCPLink::writeBytes(const char* data, qint64 size)
{
#ifdef TCPLINK_READWRITE_DEBUG
135
    _writeDebugBytes(data, size);
Don Gagne's avatar
Don Gagne committed
136
#endif
137
    _socket->write(data, size);
138 139 140 141

    // 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
142 143 144 145 146 147 148 149 150 151
}

/**
 * @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()
{
152
    qint64 byteCount = _socket->bytesAvailable();
Don Gagne's avatar
Don Gagne committed
153 154 155 156 157 158
    
    if (byteCount)
    {
        QByteArray buffer;
        buffer.resize(byteCount);
        
159
        _socket->read(buffer.data(), buffer.size());
Don Gagne's avatar
Don Gagne committed
160 161
        
        emit bytesReceived(this, buffer);
Don Gagne's avatar
Don Gagne committed
162

163 164 165 166
        // 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
167 168 169
#ifdef TCPLINK_READWRITE_DEBUG
        writeDebugBytes(buffer.data(), buffer.size());
#endif
Don Gagne's avatar
Don Gagne committed
170 171 172 173 174 175 176 177 178 179
    }
}

/**
 * @brief Get the number of bytes to read.
 *
 * @return The number of bytes to read
 **/
qint64 TCPLink::bytesAvailable()
{
180
    return _socket->bytesAvailable();
Don Gagne's avatar
Don Gagne committed
181 182 183 184 185 186 187 188 189
}

/**
 * @brief Disconnect the connection.
 *
 * @return True if connection has been disconnected, false if connection couldn't be disconnected.
 **/
bool TCPLink::disconnect()
{
190 191
	quit();
	wait();
Don Gagne's avatar
Don Gagne committed
192
    
193
    if (_socket)
Don Gagne's avatar
Don Gagne committed
194
	{
195 196 197 198 199 200 201
        _socket->disconnectFromHost();
        _socketIsConnected = false;
		delete _socket;
		_socket = NULL;

        emit disconnected();
        emit connected(false);
Don Gagne's avatar
Don Gagne committed
202 203 204 205 206 207 208 209 210 211 212 213
	}
    
    return true;
}

/**
 * @brief Connect the connection.
 *
 * @return True if connection has been established, false if connection couldn't be established.
 **/
bool TCPLink::connect()
{
214
	if (isRunning())
Don Gagne's avatar
Don Gagne committed
215
	{
216 217
		quit();
		wait();
Don Gagne's avatar
Don Gagne committed
218
	}
219 220 221 222
    bool connected = _hardwareConnect();
    if (connected) {
        start(HighPriority);
    }
Don Gagne's avatar
Don Gagne committed
223 224 225
    return connected;
}

226
bool TCPLink::_hardwareConnect(void)
Don Gagne's avatar
Don Gagne committed
227
{
228 229
    Q_ASSERT(_socket == NULL);
	_socket = new QTcpSocket();
Don Gagne's avatar
Don Gagne committed
230
    
231
    QSignalSpy errorSpy(_socket, SIGNAL(error(QAbstractSocket::SocketError)));
Don Gagne's avatar
Don Gagne committed
232
    
233 234 235 236
    _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
237 238
    
    // Give the socket a second to connect to the other side otherwise error out
239
    if (!_socket->waitForConnected(1000))
Don Gagne's avatar
Don Gagne committed
240
    {
241 242 243 244 245 246 247
        // 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
248 249
        return false;
    }
Don Gagne's avatar
Don Gagne committed
250
    
251
    _socketIsConnected = true;
Don Gagne's avatar
Don Gagne committed
252
    emit connected(true);
253
    emit connected();
Don Gagne's avatar
Don Gagne committed
254

Don Gagne's avatar
Don Gagne committed
255 256 257
    return true;
}

258
void TCPLink::_socketError(QAbstractSocket::SocketError socketError)
Don Gagne's avatar
Don Gagne committed
259
{
260
    Q_UNUSED(socketError);
261
    emit communicationError(getName(), "Error on socket: " + _socket->errorString());
Don Gagne's avatar
Don Gagne committed
262 263 264 265 266 267 268 269 270
}

/**
 * @brief Check if connection is active.
 *
 * @return True if link is connected, false otherwise.
 **/
bool TCPLink::isConnected() const
{
271
    return _socketIsConnected;
Don Gagne's avatar
Don Gagne committed
272 273 274 275
}

int TCPLink::getId() const
{
276
    return _linkId;
Don Gagne's avatar
Don Gagne committed
277 278 279 280
}

QString TCPLink::getName() const
{
281
    return _name;
Don Gagne's avatar
Don Gagne committed
282 283
}

284
qint64 TCPLink::getConnectionSpeed() const
Don Gagne's avatar
Don Gagne committed
285 286
{
    return 54000000; // 54 Mbit
287 288 289 290 291 292 293 294 295 296 297
}

qint64 TCPLink::getCurrentInDataRate() const
{
    return 0;
}

qint64 TCPLink::getCurrentOutDataRate() const
{
    return 0;
}
298 299 300 301 302

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
303
}