diff --git a/qgcunittest.pro b/qgcunittest.pro index 308ce32dc84423a9cdfd85bed225b2bd8ab109a0..b8b8a5d564e2a318327f50c45d160f652e402aa2 100644 --- a/qgcunittest.pro +++ b/qgcunittest.pro @@ -200,6 +200,7 @@ FORMS += src/ui/MainWindow.ui \ src/ui/QGCMAVLinkLogPlayer.ui \ src/ui/QGCWaypointListMulti.ui \ src/ui/QGCUDPLinkConfiguration.ui \ + src/ui/QGCTCPLinkConfiguration.ui \ src/ui/QGCSettingsWidget.ui \ src/ui/UASControlParameters.ui \ src/ui/map/QGCMapTool.ui \ @@ -323,6 +324,7 @@ HEADERS += src/MG.h \ src/uas/QGCMAVLinkUASFactory.h \ src/ui/QGCWaypointListMulti.h \ src/ui/QGCUDPLinkConfiguration.h \ + src/ui/QGCTCPLinkConfiguration.h \ src/ui/QGCSettingsWidget.h \ src/ui/uas/UASControlParameters.h \ src/uas/QGCUASParamManager.h \ @@ -490,6 +492,7 @@ SOURCES += src/QGCCore.cc \ src/uas/QGCMAVLinkUASFactory.cc \ src/ui/QGCWaypointListMulti.cc \ src/ui/QGCUDPLinkConfiguration.cc \ + src/ui/QGCTCPLinkConfiguration.cc \ src/ui/QGCSettingsWidget.cc \ src/ui/uas/UASControlParameters.cpp \ src/uas/QGCUASParamManager.cc \ diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro index e8940419a76eaef6b6f2ea33248f963c7b5f268c..3f48dc0f24f79ea010624249b3d1fcc023294fae 100644 --- a/qgroundcontrol.pro +++ b/qgroundcontrol.pro @@ -227,6 +227,7 @@ FORMS += src/ui/MainWindow.ui \ src/ui/QGCMAVLinkLogPlayer.ui \ src/ui/QGCWaypointListMulti.ui \ src/ui/QGCUDPLinkConfiguration.ui \ + src/ui/QGCTCPLinkConfiguration.ui \ src/ui/QGCSettingsWidget.ui \ src/ui/UASControlParameters.ui \ src/ui/map/QGCMapTool.ui \ @@ -352,6 +353,7 @@ HEADERS += src/MG.h \ src/ui/CameraView.h \ src/comm/MAVLinkSimulationLink.h \ src/comm/UDPLink.h \ + src/comm/TCPLink.h \ src/ui/ParameterInterface.h \ src/ui/WaypointList.h \ src/Waypoint.h \ @@ -407,6 +409,7 @@ HEADERS += src/MG.h \ src/uas/QGCMAVLinkUASFactory.h \ src/ui/QGCWaypointListMulti.h \ src/ui/QGCUDPLinkConfiguration.h \ + src/ui/QGCTCPLinkConfiguration.h \ src/ui/QGCSettingsWidget.h \ src/ui/uas/UASControlParameters.h \ src/uas/QGCUASParamManager.h \ @@ -578,6 +581,7 @@ SOURCES += src/main.cc \ src/ui/CameraView.cc \ src/comm/MAVLinkSimulationLink.cc \ src/comm/UDPLink.cc \ + src/comm/TCPLink.cc \ src/ui/ParameterInterface.cc \ src/ui/WaypointList.cc \ src/Waypoint.cc \ @@ -632,6 +636,7 @@ SOURCES += src/main.cc \ src/uas/QGCMAVLinkUASFactory.cc \ src/ui/QGCWaypointListMulti.cc \ src/ui/QGCUDPLinkConfiguration.cc \ + src/ui/QGCTCPLinkConfiguration.cc \ src/ui/QGCSettingsWidget.cc \ src/ui/uas/UASControlParameters.cpp \ src/uas/QGCUASParamManager.cc \ diff --git a/src/comm/TCPLink.cc b/src/comm/TCPLink.cc new file mode 100644 index 0000000000000000000000000000000000000000..f56ef84d3892df20241db3eeb7fc24d7b87bedf6 --- /dev/null +++ b/src/comm/TCPLink.cc @@ -0,0 +1,325 @@ +/*===================================================================== + + QGroundControl Open Source Ground Control Station + + (c) 2009 - 2011 QGROUNDCONTROL PROJECT + + 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 . + + ======================================================================*/ + +/** + * @file + * @brief Definition of TCP connection (server) for unmanned vehicles + * @author Lorenz Meier + * + */ + +#include +#include +#include +#include +#include +#include "TCPLink.h" +#include "LinkManager.h" +#include "QGC.h" +#include + +TCPLink::TCPLink(QHostAddress hostAddress, quint16 socketPort) : + host(hostAddress), + port(socketPort), + socket(NULL), + socketIsConnected(false) + +{ + // Set unique ID and add link to the list of links + this->id = getNextLinkId(); + this->name = tr("TCP Link (port:%1)").arg(this->port); + emit nameChanged(this->name); + + qDebug() << "TCP Created " << this->name; +} + +TCPLink::~TCPLink() +{ + disconnect(); + this->deleteLater(); +} + +void TCPLink::run() +{ + exec(); +} + +void TCPLink::setAddress(const QString &text) +{ + setAddress(QHostAddress(text)); +} + +void TCPLink::setAddress(QHostAddress host) +{ + bool reconnect(false); + if (this->isConnected()) + { + disconnect(); + reconnect = true; + } + this->host = host; + if (reconnect) + { + connect(); + } +} + +void TCPLink::setPort(int port) +{ + bool reconnect(false); + if(this->isConnected()) + { + disconnect(); + reconnect = true; + } + this->port = port; + this->name = tr("TCP Link (port:%1)").arg(this->port); + emit nameChanged(this->name); + if(reconnect) + { + connect(); + } +} + +#ifdef TCPLINK_READWRITE_DEBUG +void TCPLink::writeDebugBytes(const char *data, qint16 size) +{ + QString bytes; + QString ascii; + for (int i=0; i 31 && data[i] < 127) + { + ascii.append(data[i]); + } + else + { + ascii.append(219); + } + } + qDebug() << "Sent" << size << "bytes to" << host.toString() << ":" << port << "data:"; + qDebug() << bytes; + qDebug() << "ASCII:" << ascii; +} +#endif + +void TCPLink::writeBytes(const char* data, qint64 size) +{ +#ifdef TCPLINK_READWRITE_DEBUG + writeDebugBytes(data, size); +#endif + socket->write(data, size); +} + +/** + * @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(); + + 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 + } +} + + +/** + * @brief Get the number of bytes to read. + * + * @return The number of bytes to read + **/ +qint64 TCPLink::bytesAvailable() +{ + return socket->bytesAvailable(); +} + +/** + * @brief Disconnect the connection. + * + * @return True if connection has been disconnected, false if connection couldn't be disconnected. + **/ +bool TCPLink::disconnect() +{ + this->quit(); + this->wait(); + + if (socket) + { + socket->disconnect(); + socketIsConnected = false; + delete socket; + socket = NULL; + } + + return true; +} + +/** + * @brief Connect the connection. + * + * @return True if connection has been established, false if connection couldn't be established. + **/ +bool TCPLink::connect() +{ + if(this->isRunning()) + { + this->quit(); + this->wait(); + } + bool connected = this->hardwareConnect(); + start(HighPriority); + return connected; +} + +bool TCPLink::hardwareConnect(void) +{ + socket = new QTcpSocket(); + + socket->connectToHost(host, port); + + QObject::connect(socket, SIGNAL(readyRead()), this, SLOT(readBytes())); + QObject::connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(socketError(QAbstractSocket::SocketError))); + + // Give the socket a second to connect to the other side otherwise error out + if (!socket->waitForConnected(1000)) + { + emit communicationError(getName(), "connection failed"); + return false; + } + + socketIsConnected = true; + connectionStartTime = QGC::groundTimeUsecs()/1000; + emit connected(true); + + return true; +} + +void TCPLink::socketError(QAbstractSocket::SocketError socketError) +{ + emit communicationError(getName(), "Error on socket: " + socket->errorString()); +} + +/** + * @brief Check if connection is active. + * + * @return True if link is connected, false otherwise. + **/ +bool TCPLink::isConnected() const +{ + return socketIsConnected; +} + +int TCPLink::getId() const +{ + return id; +} + +QString TCPLink::getName() const +{ + return name; +} + +void TCPLink::setName(QString name) +{ + this->name = name; + emit nameChanged(this->name); +} + + +qint64 TCPLink::getNominalDataRate() const +{ + return 54000000; // 54 Mbit +} + +qint64 TCPLink::getTotalUpstream() +{ + statisticsMutex.lock(); + qint64 totalUpstream = bitsSentTotal / ((QGC::groundTimeUsecs()/1000 - connectionStartTime) / 1000); + statisticsMutex.unlock(); + return totalUpstream; +} + +qint64 TCPLink::getCurrentUpstream() +{ + return 0; // TODO +} + +qint64 TCPLink::getMaxUpstream() +{ + return 0; // TODO +} + +qint64 TCPLink::getBitsSent() const +{ + return bitsSentTotal; +} + +qint64 TCPLink::getBitsReceived() const +{ + return bitsReceivedTotal; +} + +qint64 TCPLink::getTotalDownstream() +{ + statisticsMutex.lock(); + qint64 totalDownstream = bitsReceivedTotal / ((QGC::groundTimeUsecs()/1000 - connectionStartTime) / 1000); + statisticsMutex.unlock(); + return totalDownstream; +} + +qint64 TCPLink::getCurrentDownstream() +{ + return 0; // TODO +} + +qint64 TCPLink::getMaxDownstream() +{ + return 0; // TODO +} + +bool TCPLink::isFullDuplex() const +{ + return true; +} + +int TCPLink::getLinkQuality() const +{ + /* This feature is not supported with this interface */ + return -1; +} diff --git a/src/comm/TCPLink.h b/src/comm/TCPLink.h new file mode 100644 index 0000000000000000000000000000000000000000..6c6131d6f4d3bdc85d2d190746e55ad1e39e6927 --- /dev/null +++ b/src/comm/TCPLink.h @@ -0,0 +1,131 @@ +/*===================================================================== + + QGroundControl Open Source Ground Control Station + + (c) 2009 - 2011 QGROUNDCONTROL PROJECT + + 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 . + + ======================================================================*/ + +/** + * @file + * @brief TCP connection (server) for unmanned vehicles + * @author Lorenz Meier + * + */ + +#ifndef TCPLINK_H +#define TCPLINK_H + +#include +#include +#include +#include +#include +#include +#include +#include + +//#define TCPLINK_READWRITE_DEBUG // Use to debug data reads/writes + +class TCPLink : public LinkInterface +{ + Q_OBJECT + +public: + TCPLink(QHostAddress hostAddress = QHostAddress::LocalHost, quint16 socketPort = 5760); + ~TCPLink(); + + void requestReset() { } + + bool isConnected() const; + qint64 bytesAvailable(); + int getPort() const { + return port; + } + QHostAddress getHostAddress() const { + return host; + } + + QString getName() const; + int getBaudRate() const; + int getBaudRateType() const; + int getFlowType() const; + int getParityType() const; + int getDataBitsType() const; + int getStopBitsType() const; + + /* Extensive statistics for scientific purposes */ + qint64 getNominalDataRate() const; + qint64 getTotalUpstream(); + qint64 getCurrentUpstream(); + qint64 getMaxUpstream(); + qint64 getTotalDownstream(); + qint64 getCurrentDownstream(); + qint64 getMaxDownstream(); + qint64 getBitsSent() const; + qint64 getBitsReceived() const; + + void run(); + + int getLinkQuality() const; + bool isFullDuplex() const; + int getId() const; + +public slots: + void setAddress(QHostAddress host); + void setPort(int port); + void readBytes(); + void writeBytes(const char* data, qint64 length); + bool connect(); + bool disconnect(); + void socketError(QAbstractSocket::SocketError socketError); + void setAddress(const QString &text); + + +protected: + QString name; + QHostAddress host; + quint16 port; + int id; + QTcpSocket* socket; + bool socketIsConnected; + + quint64 bitsSentTotal; + quint64 bitsSentCurrent; + quint64 bitsSentMax; + quint64 bitsReceivedTotal; + quint64 bitsReceivedCurrent; + quint64 bitsReceivedMax; + quint64 connectionStartTime; + QMutex statisticsMutex; + QMutex dataMutex; + + void setName(QString name); + +private: + bool hardwareConnect(void); +#ifdef TCPLINK_READWRITE_DEBUG + void writeDebugBytes(const char *data, qint16 size); +#endif + +signals: + //Signals are defined by LinkInterface + +}; + +#endif // TCPLINK_H diff --git a/src/ui/CommConfigurationWindow.cc b/src/ui/CommConfigurationWindow.cc index 462f1c42cbb61c8960ef6d3e96b7b4f5b3f6fb69..5da03235691c3a64da884ff50ed0ee5702c664d5 100644 --- a/src/ui/CommConfigurationWindow.cc +++ b/src/ui/CommConfigurationWindow.cc @@ -40,6 +40,7 @@ This file is part of the QGROUNDCONTROL project #include "SerialConfigurationWindow.h" #include "SerialLink.h" #include "UDPLink.h" +#include "TCPLink.h" #include "MAVLinkSimulationLink.h" #ifdef XBEELINK #include "XbeeLink.h" @@ -52,6 +53,7 @@ This file is part of the QGROUNDCONTROL project #include "MAVLinkProtocol.h" #include "MAVLinkSettingsWidget.h" #include "QGCUDPLinkConfiguration.h" +#include "QGCTCPLinkConfiguration.h" #include "LinkManager.h" #include "MainWindow.h" @@ -82,6 +84,7 @@ CommConfigurationWindow::CommConfigurationWindow(LinkInterface* link, ProtocolIn // add link types ui.linkType->addItem(tr("Serial"), QGC_LINK_SERIAL); ui.linkType->addItem(tr("UDP"), QGC_LINK_UDP); + ui.linkType->addItem(tr("TCP"), QGC_LINK_TCP); if(dynamic_cast(link)) { //Only show simulation option if already setup elsewhere as a simulation ui.linkType->addItem(tr("Simulation"), QGC_LINK_SIMULATION); @@ -148,6 +151,13 @@ CommConfigurationWindow::CommConfigurationWindow(LinkInterface* link, ProtocolIn ui.linkGroupBox->setTitle(tr("UDP Link")); ui.linkType->setCurrentIndex(ui.linkType->findData(QGC_LINK_UDP)); } + TCPLink* tcp = dynamic_cast(link); + if (tcp != 0) { + QWidget* conf = new QGCTCPLinkConfiguration(tcp, this); + ui.linkScrollArea->setWidget(conf); + ui.linkGroupBox->setTitle(tr("TCP Link")); + ui.linkType->setCurrentIndex(ui.linkType->findData(QGC_LINK_TCP)); + } MAVLinkSimulationLink* sim = dynamic_cast(link); if (sim != 0) { ui.linkType->setCurrentIndex(ui.linkType->findData(QGC_LINK_SIMULATION)); @@ -177,7 +187,7 @@ CommConfigurationWindow::CommConfigurationWindow(LinkInterface* link, ProtocolIn connect(xbee,SIGNAL(tryConnectEnd(bool)),ui.actionConnect,SLOT(setEnabled(bool))); } #endif // XBEELINK - if (serial == 0 && udp == 0 && sim == 0 + if (serial == 0 && udp == 0 && sim == 0 && tcp == 0 #ifdef OPAL_RT && opal == 0 #endif @@ -256,6 +266,14 @@ void CommConfigurationWindow::setLinkType(qgc_link_t linktype) break; } + case QGC_LINK_TCP: + { + TCPLink *tcp = new TCPLink(); + tmpLink = tcp; + MainWindow::instance()->addLink(tmpLink); + break; + } + #ifdef OPAL_RT case QGC_LINK_OPAL: { diff --git a/src/ui/CommConfigurationWindow.h b/src/ui/CommConfigurationWindow.h index 47e2b4c8ff50bc7c5732d107ae5b4328eb79b268..406331604cd9720c38418a8299ec196365e18e3d 100644 --- a/src/ui/CommConfigurationWindow.h +++ b/src/ui/CommConfigurationWindow.h @@ -42,6 +42,7 @@ This file is part of the QGROUNDCONTROL project enum qgc_link_t { QGC_LINK_SERIAL, QGC_LINK_UDP, + QGC_LINK_TCP, QGC_LINK_SIMULATION, QGC_LINK_FORWARDING, #ifdef XBEELINK diff --git a/src/ui/QGCTCPLinkConfiguration.cc b/src/ui/QGCTCPLinkConfiguration.cc new file mode 100644 index 0000000000000000000000000000000000000000..364631ad3dc1cbea24f25901a04630572dca4fcb --- /dev/null +++ b/src/ui/QGCTCPLinkConfiguration.cc @@ -0,0 +1,35 @@ +#include + +#include "QGCTCPLinkConfiguration.h" +#include "ui_QGCTCPLinkConfiguration.h" + +QGCTCPLinkConfiguration::QGCTCPLinkConfiguration(TCPLink* link, QWidget *parent) : + QWidget(parent), + link(link), + ui(new Ui::QGCTCPLinkConfiguration) +{ + ui->setupUi(this); + uint16_t port = link->getPort(); + ui->portSpinBox->setValue(port); + QString addr = link->getHostAddress().toString(); + ui->hostAddressLineEdit->setText(addr); + connect(ui->portSpinBox, SIGNAL(valueChanged(int)), link, SLOT(setPort(int))); + connect(ui->hostAddressLineEdit, SIGNAL(textChanged (const QString &)), link, SLOT(setAddress(const QString &))); +} + +QGCTCPLinkConfiguration::~QGCTCPLinkConfiguration() +{ + delete ui; +} + +void QGCTCPLinkConfiguration::changeEvent(QEvent *e) +{ + QWidget::changeEvent(e); + switch (e->type()) { + case QEvent::LanguageChange: + ui->retranslateUi(this); + break; + default: + break; + } +} \ No newline at end of file diff --git a/src/ui/QGCTCPLinkConfiguration.h b/src/ui/QGCTCPLinkConfiguration.h new file mode 100644 index 0000000000000000000000000000000000000000..0d8eb33ad7e8d07862dd57400a0ba443f47a9a54 --- /dev/null +++ b/src/ui/QGCTCPLinkConfiguration.h @@ -0,0 +1,32 @@ +#ifndef QGCTCPLINKCONFIGURATION_H +#define QGCTCPLINKCONFIGURATION_H + +#include + +#include "TCPLink.h" + +namespace Ui +{ +class QGCTCPLinkConfiguration; +} + +class QGCTCPLinkConfiguration : public QWidget +{ + Q_OBJECT + +public: + explicit QGCTCPLinkConfiguration(TCPLink* link, QWidget *parent = 0); + ~QGCTCPLinkConfiguration(); + +public slots: + +protected: + void changeEvent(QEvent *e); + + TCPLink* link; ///< TCP link instance this widget configures + +private: + Ui::QGCTCPLinkConfiguration *ui; +}; + +#endif // QGCTCPLINKCONFIGURATION_H diff --git a/src/ui/QGCTCPLinkConfiguration.ui b/src/ui/QGCTCPLinkConfiguration.ui new file mode 100644 index 0000000000000000000000000000000000000000..416e587970589d09aed7aba76722040fce971440 --- /dev/null +++ b/src/ui/QGCTCPLinkConfiguration.ui @@ -0,0 +1,51 @@ + + + QGCTCPLinkConfiguration + + + + 0 + 0 + 400 + 300 + + + + Form + + + + QFormLayout::FieldsStayAtSizeHint + + + + + TCP Port + + + + + + + 3000 + + + 100000 + + + + + + + Host Address + + + + + + + + + + +