5#include <QtCore/QThread>
6#include <QtCore/QTimer>
7#include <QtNetwork/QHostInfo>
8#include <QtNetwork/QTcpSocket>
13 constexpr int CONNECT_TIMEOUT_MS = 3000;
14 constexpr int DISCONNECT_TIMEOUT_MS = 3000;
15 constexpr int TYPE_OF_SERVICE = 32;
20TCPConfiguration::TCPConfiguration(
const QString &name, QObject *parent)
23 qCDebug(TCPLinkLog) <<
this;
26TCPConfiguration::TCPConfiguration(
const TCPConfiguration *copy, QObject *parent)
31 qCDebug(TCPLinkLog) <<
this;
34TCPConfiguration::~TCPConfiguration()
36 qCDebug(TCPLinkLog) <<
this;
39void TCPConfiguration::setHost(
const QString &host)
41 const QString cleanHost = host.trimmed();
42 if (cleanHost != _host) {
48void TCPConfiguration::setPort(quint16 port)
58 LinkConfiguration::copyFrom(source);
60 const TCPConfiguration* tcpSource = qobject_cast<const TCPConfiguration*>(source);
62 setHost(tcpSource->host());
63 setPort(tcpSource->port());
66void TCPConfiguration::loadSettings(QSettings &settings,
const QString &root)
68 settings.beginGroup(root);
70 setHost(settings.value(QStringLiteral(
"host"), host()).toString());
71 setPort(
static_cast<quint16
>(settings.value(QStringLiteral(
"port"), port()).toUInt()));
76void TCPConfiguration::saveSettings(QSettings &settings,
const QString &root)
const
78 settings.beginGroup(root);
80 settings.setValue(QStringLiteral(
"host"), host());
81 settings.setValue(QStringLiteral(
"port"), port());
92 qCDebug(TCPLinkLog) <<
this;
99 qCDebug(TCPLinkLog) <<
this;
104 return (_socket && _socket->isOpen() && (_socket->state() == QAbstractSocket::ConnectedState));
110 _socket =
new QTcpSocket(
this);
113 _socket->setSocketOption(QAbstractSocket::LowDelayOption, 1);
114 _socket->setSocketOption(QAbstractSocket::KeepAliveOption, 1);
115 _socket->setSocketOption(QAbstractSocket::TypeOfServiceOption, TYPE_OF_SERVICE);
117 (void) connect(_socket, &QTcpSocket::connected,
this, &TCPWorker::_onSocketConnected);
118 (void) connect(_socket, &QTcpSocket::disconnected,
this, &TCPWorker::_onSocketDisconnected);
119 (void) connect(_socket, &QTcpSocket::readyRead,
this, &TCPWorker::_onSocketReadyRead);
120 (void) connect(_socket, &QTcpSocket::errorOccurred,
this, &TCPWorker::_onSocketErrorOccurred);
122 if (TCPLinkLog().isDebugEnabled()) {
125 (void) connect(_socket, &QTcpSocket::stateChanged,
this, [](QTcpSocket::SocketState state) {
126 qCDebug(TCPLinkLog) <<
"TCP State Changed:" << state;
129 (void) connect(_socket, &QTcpSocket::hostFound,
this, [
this]() {
130 qCDebug(TCPLinkLog) <<
"TCP Host Found" << _socket->peerName() << _socket->peerAddress() << _socket->peerPort();
138 qCWarning(TCPLinkLog) <<
"Already connected to" << _config->host() <<
":" << _config->port();
142 if (_config->host().isEmpty()) {
143 emit
errorOccurred(tr(
"Connection Failed: Host address is empty"));
147 _errorEmitted =
false;
149 qCDebug(TCPLinkLog) <<
"Attempting to connect to host:" << _config->host() <<
"port:" << _config->port();
150 _socket->connectToHost(_config->host(), _config->port());
152 if (!_socket->waitForConnected(CONNECT_TIMEOUT_MS)) {
153 qCWarning(TCPLinkLog) <<
"Connection to" << _config->host() <<
":" << _config->port() <<
"failed:" << _socket->errorString();
155 if (!_errorEmitted.exchange(
true)) {
156 emit
errorOccurred(tr(
"Connection Failed: %1").arg(_socket->errorString()));
159 _onSocketDisconnected();
161 qCDebug(TCPLinkLog) <<
"Successfully connected to host:" << _config->host() <<
"port:" << _config->port();
168 qCDebug(TCPLinkLog) <<
"Already disconnected from host:" << _config->host() <<
"port:" << _config->port();
172 qCDebug(TCPLinkLog) <<
"Attempting to disconnect from host:" << _config->host() <<
"port:" << _config->port();
174 _socket->disconnectFromHost();
176 if (_socket->state() != QAbstractSocket::UnconnectedState) {
177 _socket->waitForDisconnected(1000);
183 if (data.isEmpty()) {
193 qint64 totalBytesWritten = 0;
194 while (totalBytesWritten < data.size()) {
195 const qint64 bytesWritten = _socket->write(data.constData() + totalBytesWritten, data.size() - totalBytesWritten);
196 if (bytesWritten == -1) {
197 emit
errorOccurred(tr(
"Could Not Send Data - Write Failed: %1").arg(_socket->errorString()));
199 }
else if (bytesWritten == 0) {
200 emit
errorOccurred(tr(
"Could Not Send Data - Write Returned 0 Bytes"));
203 totalBytesWritten += bytesWritten;
206 emit
dataSent(data.first(totalBytesWritten));
209void TCPWorker::_onSocketConnected()
211 qCDebug(TCPLinkLog) <<
"Socket connected:" << _config->host() << _config->port();
212 _errorEmitted =
false;
216void TCPWorker::_onSocketDisconnected()
218 qCDebug(TCPLinkLog) <<
"Socket disconnected:" << _config->host() << _config->port();
219 _errorEmitted =
false;
223void TCPWorker::_onSocketReadyRead()
225 const QByteArray data = _socket->readAll();
226 if (!data.isEmpty()) {
231void TCPWorker::_onSocketBytesWritten(qint64 bytes)
233 qCDebug(TCPLinkLog) << _config->host() <<
"Wrote" << bytes <<
"bytes";
236void TCPWorker::_onSocketErrorOccurred(QAbstractSocket::SocketError socketError)
238 const QString
errorString = _socket->errorString();
239 qCWarning(TCPLinkLog) <<
"Socket error:" << socketError <<
errorString;
241 if (!_errorEmitted.exchange(
true)) {
252 , _workerThread(new QThread(this))
254 qCDebug(TCPLinkLog) <<
this;
256 _workerThread->setObjectName(QStringLiteral(
"TCP_%1").arg(_tcpConfig->name()));
258 _worker->moveToThread(_workerThread);
261 (void) connect(_workerThread, &QThread::finished, _worker, &QObject::deleteLater);
263 (void) connect(_worker, &
TCPWorker::connected,
this, &TCPLink::_onConnected, Qt::QueuedConnection);
267 (void) connect(_worker, &
TCPWorker::dataSent,
this, &TCPLink::_onDataSent, Qt::QueuedConnection);
269 _workerThread->start();
275 (void) QMetaObject::invokeMethod(_worker,
"disconnectFromHost", Qt::BlockingQueuedConnection);
279 _workerThread->quit();
280 if (!_workerThread->wait(DISCONNECT_TIMEOUT_MS)) {
281 qCWarning(TCPLinkLog) <<
"Failed to wait for TCP Thread to close";
284 qCDebug(TCPLinkLog) <<
this;
292bool TCPLink::_connect()
294 return QMetaObject::invokeMethod(_worker,
"connectToHost", Qt::QueuedConnection);
300 (void) QMetaObject::invokeMethod(_worker,
"disconnectFromHost", Qt::QueuedConnection);
304void TCPLink::_onConnected()
306 _disconnectedEmitted =
false;
310void TCPLink::_onDisconnected()
312 if (!_disconnectedEmitted.exchange(
true)) {
317void TCPLink::_onErrorOccurred(
const QString &
errorString)
319 qCWarning(TCPLinkLog) <<
"Communication error:" <<
errorString;
320 emit
communicationError(tr(
"TCP Link Error"), tr(
"Link %1: (Host: %2 Port: %3) %4").arg(_tcpConfig->name(), _tcpConfig->host()).arg(_tcpConfig->port()).arg(
errorString));
323void TCPLink::_onDataReceived(
const QByteArray &data)
328void TCPLink::_onDataSent(
const QByteArray &data)
333void TCPLink::_writeBytes(
const QByteArray& bytes)
335 (void) QMetaObject::invokeMethod(_worker,
"writeData", Qt::QueuedConnection, Q_ARG(QByteArray, bytes));
std::shared_ptr< LinkConfiguration > SharedLinkConfigurationPtr
#define QGC_LOGGING_CATEGORY(name, categoryStr)
Interface holding link specific settings.
The link interface defines the interface for all links used to communicate with the ground station ap...
void bytesReceived(LinkInterface *link, const QByteArray &data)
void communicationError(const QString &title, const QString &error)
void bytesSent(LinkInterface *link, const QByteArray &data)
bool isConnected() const override
void disconnect() override
bool isSecureConnection() const override
Returns true if the connection is secure (e.g. USB, wired ethernet)
TCPLink(SharedLinkConfigurationPtr &config, QObject *parent=nullptr)
void disconnectFromHost()
void errorOccurred(const QString &errorString)
void dataReceived(const QByteArray &data)
void dataSent(const QByteArray &data)
void writeData(const QByteArray &data)
TCPWorker(const TCPConfiguration *config, QObject *parent=nullptr)
bool isNetworkEthernet()
Check if current network connection is Ethernet.