diff --git a/src/Comms/TCPLink.cc b/src/Comms/TCPLink.cc index 94557c30915..62fe83bd24f 100644 --- a/src/Comms/TCPLink.cc +++ b/src/Comms/TCPLink.cc @@ -9,143 +9,149 @@ #include "TCPLink.h" #include "DeviceInfo.h" +#include "QGCLoggingCategory.h" -#include #include -#include -TCPLink::TCPLink(SharedLinkConfigurationPtr& config) - : LinkInterface(config) +QGC_LOGGING_CATEGORY(TCPLinkLog, "TEST.comms.tcplink") + +// MAVLINK_COMM_NUM_BUFFERS + +TCPLink::TCPLink(SharedLinkConfigurationPtr &config, QObject *parent) + : LinkInterface(config, parent) , _tcpConfig(qobject_cast(config.get())) - , _socket(nullptr) - , _socketIsConnected(false) + , _socket(new QTcpSocket()) { - Q_ASSERT(_tcpConfig); -} + // qCDebug(TCPLinkLog) << Q_FUNC_INFO << this; -TCPLink::~TCPLink() -{ - disconnect(); -} + Q_CHECK_PTR(_tcpConfig); -#ifdef TCPLINK_READWRITE_DEBUG -void TCPLink::_writeDebugBytes(const QByteArray data) -{ - QString bytes; - QString ascii; - for (int i=0, size = data.size(); i 31 && data[i] < 127) - { - ascii.append(data[i]); - } - else - { - ascii.append(219); - } - } - qDebug() << "Sent" << size << "bytes to" << _tcpConfig->host() << ":" << _tcpConfig->port() << "data:"; - qDebug() << bytes; - qDebug() << "ASCII:" << ascii; -} + // _socket->setSocketOption(QAbstractSocket::LowDelayOption, 1); + _socket->setSocketOption(QAbstractSocket::KeepAliveOption, 1); + // _socket->setSocketOption(QAbstractSocket::TypeOfServiceOption, 32); + _socket->setSocketOption(QAbstractSocket::SendBufferSizeSocketOption, 1024); // >= MAVLINK_MAX_PACKET_LEN + _socket->setSocketOption(QAbstractSocket::ReceiveBufferSizeSocketOption, 1024); // >= MAVLINK_MAX_PACKET_LEN + // _socket->setReadBufferSize(1024); + + (void) QObject::connect(_socket, &QTcpSocket::connected, this, &TCPLink::connected, Qt::AutoConnection); + (void) QObject::connect(_socket, &QTcpSocket::disconnected, this, &TCPLink::disconnected, Qt::AutoConnection); + + (void) QObject::connect(_socket, &QTcpSocket::errorOccurred, this, [this](QTcpSocket::SocketError error) { + qCWarning(TCPLinkLog) << "TCP Link Error:" << error << _socket->errorString(); + emit communicationError(QStringLiteral("TCP Link Error"), QStringLiteral("Link: %1, %2.").arg(_tcpConfig->name(), _socket->errorString())); + }, Qt::AutoConnection); + +#ifdef QT_DEBUG + (void) QObject::connect(_socket, &QTcpSocket::stateChanged, this, [](QTcpSocket::SocketState state) { + qCDebug(TCPLinkLog) << "TCP State Changed:" << state; + }, Qt::AutoConnection); + + (void) QObject::connect(_socket, &QTcpSocket::hostFound, this, []() { + qCDebug(TCPLinkLog) << "TCP Host Found"; + }, Qt::AutoConnection); #endif +} -void TCPLink::_writeBytes(const QByteArray &data) +TCPLink::~TCPLink() { -#ifdef TCPLINK_READWRITE_DEBUG - _writeDebugBytes(data); -#endif + _socket->deleteLater(); - if (_socket) { - _socket->write(data); - emit bytesSent(this, data); - } + // qCDebug(TCPLinkLog) << Q_FUNC_INFO << this; } -void TCPLink::_readBytes() +bool TCPLink::isConnected() const { - if (_socket) { - qint64 byteCount = _socket->bytesAvailable(); - if (byteCount) - { - QByteArray buffer; - buffer.resize(byteCount); - _socket->read(buffer.data(), buffer.size()); - emit bytesReceived(this, buffer); -#ifdef TCPLINK_READWRITE_DEBUG - writeDebugBytes(buffer.data(), buffer.size()); -#endif - } - } + return ((_socket->state() == QAbstractSocket::ConnectedState) || (_socket->state() == QAbstractSocket::ConnectingState)); } -void TCPLink::disconnect(void) +void TCPLink::disconnect() { - if (_socket) { - // This prevents stale signal from calling the link after it has been deleted - QObject::disconnect(_socket, &QIODevice::readyRead, this, &TCPLink::_readBytes); - _socketIsConnected = false; - _socket->disconnectFromHost(); // Disconnect tcp - _socket->deleteLater(); // Make sure delete happens on correct thread - _socket = nullptr; - emit disconnected(); - } + (void) QObject::disconnect(_socket, &QTcpSocket::readyRead, this, &TCPLink::_readBytes); + + _socket->disconnectFromHost(); } -bool TCPLink::_connect(void) +bool TCPLink::_connect() { - if (_socket) { - qWarning() << "connect called while already connected"; + if (isConnected()) { return true; } - return _hardwareConnect(); -} - -bool TCPLink::_hardwareConnect() -{ - Q_ASSERT(_socket == nullptr); - _socket = new QTcpSocket(); - QObject::connect(_socket, &QIODevice::readyRead, this, &TCPLink::_readBytes); + static const QString title = tr("TCP Link Connect Error"); + static const QString error = tr("Link %1: %2."); - QSignalSpy errorSpy(_socket, &QAbstractSocket::errorOccurred); - QObject::connect(_socket, &QAbstractSocket::errorOccurred, this, &TCPLink::_socketError); + (void) QObject::connect(_socket, &QTcpSocket::readyRead, this, &TCPLink::_readBytes, Qt::AutoConnection); + // (void) QObject::connect(_socket, &QTcpSocket::bytesWritten, this, [this](qint64 written) {}, Qt::AutoConnection); _socket->connectToHost(_tcpConfig->host(), _tcpConfig->port()); - // Give the socket a second to connect to the other side otherwise error out - if (!_socket->waitForConnected(1000)) - { - // 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(tr("Link Error"), tr("Error on link %1. Connection failed").arg(_config->name())); - } - delete _socket; - _socket = nullptr; + if (!_socket->isValid()) { return false; } - _socketIsConnected = true; - emit connected(); + + if (!_socket->waitForConnected(1000)) { + emit communicationError(title, error.arg(_tcpConfig->name(), tr("Connection failed"))); + } + return true; } -void TCPLink::_socketError(QAbstractSocket::SocketError socketError) +void TCPLink::_writeBytes(const QByteArray &bytes) { - Q_UNUSED(socketError); - emit communicationError(tr("Link Error"), tr("Error on link %1. Error on socket: %2.").arg(_config->name()).arg(_socket->errorString())); + if (!_socket->isValid()) { + return; + } + + static const QString title = tr("TCP Link Write Error"); + static const QString error = tr("Link %1: %2."); + + if (!isConnected()) { + emit communicationError(title, error.arg(_tcpConfig->name(), tr("Could Not Send Data - Link is Disconnected!"))); + return; + } + + const qint64 bytesWritten = _socket->write(bytes); + if (bytesWritten < 0) { + emit communicationError(title, error.arg(_tcpConfig->name(), tr("Could Not Send Data - Write Failed!"))); + return; + } + + if (bytesWritten < bytes.size()) { + qCWarning(TCPLinkLog) << "Wrote" << bytesWritten << "Out of" << bytes.size() << "total bytes"; + // const QByteArray remainingBytes = bytes.sliced(bytesWritten - 1, bytes.size() - bytesWritten); + // writeBytesThreadSafe(remainingBytes.constData(), remainingBytes.size()); + } + + emit bytesSent(this, bytes); } -/** - * @brief Check if connection is active. - * - * @return True if link is connected, false otherwise. - **/ -bool TCPLink::isConnected() const +void TCPLink::_readBytes() { - return _socketIsConnected; + if (!_socket->isValid()) { + return; + } + + static const QString title = tr("TCP Link Read Error"); + static const QString error = tr("Link %1: %2."); + + if (!isConnected()) { + emit communicationError(title, error.arg(_tcpConfig->name(), tr("Could Not Read Data - Link is Disconnected!"))); + return; + } + + const qint64 byteCount = _socket->bytesAvailable(); + if (byteCount <= 0) { // < MAVLINK_NUM_NON_PAYLOAD_BYTES + emit communicationError(title, error.arg(_tcpConfig->name(), tr("Could Not Read Data - No Data Available!"))); + return; + } + + QByteArray buffer(byteCount, Qt::Initialization::Uninitialized); + if (_socket->read(buffer.data(), buffer.size()) < 0) { // readAll() + emit communicationError(title, error.arg(_tcpConfig->name(), tr("Could Not Read Data - Read Failed!"))); + return; + } + + emit bytesReceived(this, buffer); } bool TCPLink::isSecureConnection() @@ -153,52 +159,59 @@ bool TCPLink::isSecureConnection() return QGCDeviceInfo::isNetworkWired(); } -//-------------------------------------------------------------------------- -//-- TCPConfiguration +/*===========================================================================*/ -TCPConfiguration::TCPConfiguration(const QString& name) : LinkConfiguration(name) +TCPConfiguration::TCPConfiguration(const QString &name, QObject *parent) + : LinkConfiguration(name, parent) { - _port = QGC_TCP_PORT; - _host = QLatin1String("0.0.0.0"); + // qCDebug(TCPLinkLog) << Q_FUNC_INFO << this; } -TCPConfiguration::TCPConfiguration(const TCPConfiguration* source) : LinkConfiguration(source) +TCPConfiguration::TCPConfiguration(const TCPConfiguration *copy, QObject *parent) + : LinkConfiguration(copy, parent) + , _host(copy->host()) + , _port(copy->port()) { - _port = source->port(); - _host = source->host(); -} + // qCDebug(TCPLinkLog) << Q_FUNC_INFO << this; -void TCPConfiguration::copyFrom(const LinkConfiguration *source) -{ - LinkConfiguration::copyFrom(source); - const TCPConfiguration* usource = qobject_cast(source); - Q_ASSERT(usource != nullptr); - _port = usource->port(); - _host = usource->host(); + Q_CHECK_PTR(copy); + + LinkConfiguration::copyFrom(copy); } -void TCPConfiguration::setPort(quint16 port) +TCPConfiguration::~TCPConfiguration() { - _port = port; + // qCDebug(TCPLinkLog) << Q_FUNC_INFO << this; } -void TCPConfiguration::setHost(const QString host) +void TCPConfiguration::copyFrom(const LinkConfiguration *source) { - _host = host; + Q_CHECK_PTR(source); + LinkConfiguration::copyFrom(source); + + const TCPConfiguration* const tcpSource = qobject_cast(source); + Q_CHECK_PTR(tcpSource); + + setHost(tcpSource->host()); + setPort(tcpSource->port()); } -void TCPConfiguration::saveSettings(QSettings& settings, const QString& root) +void TCPConfiguration::loadSettings(QSettings &settings, const QString &root) { settings.beginGroup(root); - settings.setValue("port", (int)_port); - settings.setValue("host", _host); + + setHost(settings.value(QStringLiteral("host"), host()).toString()); + setPort(static_cast(settings.value(QStringLiteral("port"), port()).toUInt())); + settings.endGroup(); } -void TCPConfiguration::loadSettings(QSettings& settings, const QString& root) +void TCPConfiguration::saveSettings(QSettings &settings, const QString &root) { settings.beginGroup(root); - _port = (quint16)settings.value("port", QGC_TCP_PORT).toUInt(); - _host = settings.value("host", _host).toString(); + + settings.setValue(QStringLiteral("host"), host()); + settings.setValue(QStringLiteral("port"), port()); + settings.endGroup(); } diff --git a/src/Comms/TCPLink.h b/src/Comms/TCPLink.h index 726bf632671..6810cbb141c 100644 --- a/src/Comms/TCPLink.h +++ b/src/Comms/TCPLink.h @@ -9,99 +9,74 @@ #pragma once - -#include "LinkInterface.h" -#include "LinkConfiguration.h" - +#include +#include #include -#include -#include +#include -//#define TCPLINK_READWRITE_DEBUG // Use to debug data reads/writes +#include "LinkConfiguration.h" +#include "LinkInterface.h" -class TCPLinkTest; -class LinkManager; class QTcpSocket; -#define QGC_TCP_PORT 5760 +Q_DECLARE_LOGGING_CATEGORY(TCPLinkLog) class TCPConfiguration : public LinkConfiguration { Q_OBJECT -public: - - Q_PROPERTY(quint16 port READ port WRITE setPort NOTIFY portChanged) Q_PROPERTY(QString host READ host WRITE setHost NOTIFY hostChanged) + Q_PROPERTY(quint16 port READ port WRITE setPort NOTIFY portChanged) + +public: + explicit TCPConfiguration(const QString &name, QObject *parent = nullptr); + explicit TCPConfiguration(const TCPConfiguration *copy, QObject *parent = nullptr); + virtual ~TCPConfiguration(); - TCPConfiguration(const QString& name); - TCPConfiguration(const TCPConfiguration* source); + LinkType type() const override { return LinkConfiguration::TypeTcp; } + void copyFrom(const LinkConfiguration *source) override; - quint16 port (void) const { return _port; } - QString host (void) const { return _host; } - void setPort (quint16 port); - void setHost (const QString host); + QString host() const { return _host.toString(); } + void setHost(const QString &host) { if (host != _host.toString()) { _host.setAddress(host); emit hostChanged(); } } + quint16 port() const { return _port; } + void setPort(quint16 port) { if (port != _port) { _port = port; emit portChanged(); } } - //LinkConfiguration overrides - LinkType type (void) const override { return LinkConfiguration::TypeTcp; } - void copyFrom (const LinkConfiguration* source) override; - void loadSettings (QSettings& settings, const QString& root) override; - void saveSettings (QSettings& settings, const QString& root) override; - QString settingsURL (void) override { return "TcpSettings.qml"; } - QString settingsTitle (void) override { return tr("TCP Link Settings"); } + void loadSettings(QSettings &settings, const QString &root) override; + void saveSettings(QSettings &settings, const QString &root) override; + QString settingsURL() override { return QStringLiteral("TcpSettings.qml"); } + QString settingsTitle() override { return tr("TCP Link Settings"); } signals: - void portChanged(void); - void hostChanged(void); + void hostChanged(); + void portChanged(); private: - QString _host; - quint16 _port; + QHostAddress _host; + quint16 _port = 5760; }; +/*===========================================================================*/ + class TCPLink : public LinkInterface { Q_OBJECT public: - TCPLink(SharedLinkConfigurationPtr& config); + explicit TCPLink(SharedLinkConfigurationPtr &config, QObject *parent = nullptr); virtual ~TCPLink(); - QTcpSocket* getSocket (void) { return _socket; } - void signalBytesWritten (void); - - // LinkInterface overrides - bool isConnected (void) const override; - void disconnect (void) override; - bool isSecureConnection (void) override; + void run() override {}; + bool isConnected() const override; + void disconnect() override; + bool isSecureConnection() override; private slots: - void _socketError (QAbstractSocket::SocketError socketError); - void _readBytes (void); - - // LinkInterface overrides - void _writeBytes(const QByteArray &data) override; + void _writeBytes(const QByteArray &bytes) override; + void _readBytes(); private: - // LinkInterface overrides - bool _connect(void) override; - - bool _hardwareConnect (void); -#ifdef TCPLINK_READWRITE_DEBUG - void _writeDebugBytes (const QByteArray data); -#endif - - const TCPConfiguration* _tcpConfig; - QTcpSocket* _socket; - bool _socketIsConnected; - - quint64 _bitsSentTotal; - quint64 _bitsSentCurrent; - quint64 _bitsSentMax; - quint64 _bitsReceivedTotal; - quint64 _bitsReceivedCurrent; - quint64 _bitsReceivedMax; - quint64 _connectionStartTime; - QMutex _statisticsMutex; -}; + bool _connect() override; + const TCPConfiguration *_tcpConfig = nullptr; + QTcpSocket *_socket = nullptr; +};