TCPLink.cc 7 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
    // 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);

50
51
    _linkId = getNextLinkId();
    _resetName();
Don Gagne's avatar
Don Gagne committed
52
    
53
    qDebug() << "TCP Created " << _name;
Don Gagne's avatar
Don Gagne committed
54
55
56
57
}

TCPLink::~TCPLink()
{
58
    _disconnect();
Lorenz Meier's avatar
Lorenz Meier committed
59
60
61
62
63

    // Tell the thread to exit
    quit();
    // Wait for it to exit
    wait();
Don Gagne's avatar
Don Gagne committed
64
65
66
67
}

void TCPLink::run()
{
68
69
    _hardwareConnect();

Don Gagne's avatar
Don Gagne committed
70
71
72
	exec();
}

73
void TCPLink::setHostAddress(QHostAddress hostAddress)
Don Gagne's avatar
Don Gagne committed
74
{
75
76
77
    bool reconnect = false;
    
	if (this->isConnected()) {
78
		_disconnect();
Don Gagne's avatar
Don Gagne committed
79
80
		reconnect = true;
	}
81
82
83
84
85
    
	_hostAddress = hostAddress;
    _resetName();
    
	if (reconnect) {
86
		_connect();
Don Gagne's avatar
Don Gagne committed
87
88
89
	}
}

90
91
92
93
94
void TCPLink::setHostAddress(const QString& hostAddress)
{
    setHostAddress(QHostAddress(hostAddress));
}

Don Gagne's avatar
Don Gagne committed
95
96
void TCPLink::setPort(int port)
{
97
98
99
    bool reconnect = false;
    
	if (this->isConnected()) {
100
		_disconnect();
Don Gagne's avatar
Don Gagne committed
101
102
		reconnect = true;
	}
103
104
105
106
107
    
	_port = port;
    _resetName();
    
	if (reconnect) {
108
		_connect();
Don Gagne's avatar
Don Gagne committed
109
110
111
	}
}

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

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

    // 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
146
147
148
149
150
151
152
153
154
155
}

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

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

/**
 * @brief Disconnect the connection.
 *
 * @return True if connection has been disconnected, false if connection couldn't be disconnected.
 **/
182
bool TCPLink::_disconnect(void)
Don Gagne's avatar
Don Gagne committed
183
{
184
185
	quit();
	wait();
Don Gagne's avatar
Don Gagne committed
186
    
187
    if (_socket)
Don Gagne's avatar
Don Gagne committed
188
	{
189
        _socketIsConnected = false;
Don Gagne's avatar
Don Gagne committed
190
		_socket->deleteLater(); // Make sure delete happens on correct thread
191
192
193
		_socket = NULL;

        emit disconnected();
Don Gagne's avatar
Don Gagne committed
194
195
196
197
198
199
200
201
202
203
	}
    
    return true;
}

/**
 * @brief Connect the connection.
 *
 * @return True if connection has been established, false if connection couldn't be established.
 **/
204
bool TCPLink::_connect(void)
Don Gagne's avatar
Don Gagne committed
205
{
206
	if (isRunning())
Don Gagne's avatar
Don Gagne committed
207
	{
208
209
		quit();
		wait();
Don Gagne's avatar
Don Gagne committed
210
	}
211
212
213
214

    start(HighPriority);

    return true;
Don Gagne's avatar
Don Gagne committed
215
216
}

217
bool TCPLink::_hardwareConnect(void)
Don Gagne's avatar
Don Gagne committed
218
{
219
220
    Q_ASSERT(_socket == NULL);
	_socket = new QTcpSocket();
Don Gagne's avatar
Don Gagne committed
221
    
222
    QSignalSpy errorSpy(_socket, SIGNAL(error(QAbstractSocket::SocketError)));
Don Gagne's avatar
Don Gagne committed
223
    
224
225
226
227
    _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
228
229
    
    // Give the socket a second to connect to the other side otherwise error out
230
    if (!_socket->waitForConnected(1000))
Don Gagne's avatar
Don Gagne committed
231
    {
232
233
234
        // 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) {
235
            emit communicationError(tr("Link Error"), QString("Error on link %1. Connection failed").arg(getName()));
236
237
238
        }
        delete _socket;
        _socket = NULL;
Don Gagne's avatar
Don Gagne committed
239
240
        return false;
    }
Don Gagne's avatar
Don Gagne committed
241
    
242
243
    _socketIsConnected = true;
    emit connected();
Don Gagne's avatar
Don Gagne committed
244

Don Gagne's avatar
Don Gagne committed
245
246
247
    return true;
}

248
void TCPLink::_socketError(QAbstractSocket::SocketError socketError)
Don Gagne's avatar
Don Gagne committed
249
{
250
    Q_UNUSED(socketError);
251
    emit communicationError(tr("Link Error"), QString("Error on link %1. Error on socket: %2.").arg(getName()).arg(_socket->errorString()));
Don Gagne's avatar
Don Gagne committed
252
253
254
255
256
257
258
259
260
}

/**
 * @brief Check if connection is active.
 *
 * @return True if link is connected, false otherwise.
 **/
bool TCPLink::isConnected() const
{
261
    return _socketIsConnected;
Don Gagne's avatar
Don Gagne committed
262
263
264
265
}

int TCPLink::getId() const
{
266
    return _linkId;
Don Gagne's avatar
Don Gagne committed
267
268
269
270
}

QString TCPLink::getName() const
{
271
    return _name;
Don Gagne's avatar
Don Gagne committed
272
273
}

274
qint64 TCPLink::getConnectionSpeed() const
Don Gagne's avatar
Don Gagne committed
275
276
{
    return 54000000; // 54 Mbit
277
278
279
280
281
282
283
284
285
286
287
}

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

qint64 TCPLink::getCurrentOutDataRate() const
{
    return 0;
}
288
289
290
291
292

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
293
}
294
295
296
297
298
299
300
301
302
303
304
305

void TCPLink::waitForBytesWritten(int msecs)
{
    Q_ASSERT(_socket);
    _socket->waitForBytesWritten(msecs);
}

void TCPLink::waitForReadyRead(int msecs)
{
    Q_ASSERT(_socket);
    _socket->waitForReadyRead(msecs);
}