Skip to content
QGCFlightGearLink.cc 41.9 KiB
Newer Older
/*=====================================================================

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/>.

======================================================================*/

/**
 * @file
 *   @brief Definition of UDP connection (server) for unmanned vehicles
lm's avatar
lm committed
 *   @see Flightgear Manual http://mapserver.flightgear.org/getstart.pdf
 *   @author Lorenz Meier <mavteam@student.ethz.ch>
Thomas Gubler's avatar
Thomas Gubler committed
 *   @author Thomas Gubler <thomasgubler@student.ethz.ch>
 *
 */

#include <QTimer>
#include <QList>
#include <QDebug>
#include <QMutexLocker>
Don Gagne's avatar
Don Gagne committed
#include <QHostInfo>
Don Gagne's avatar
Don Gagne committed
#include <QMessageBox>
#include "QGCFlightGearLink.h"
#include "QGC.h"
Don Gagne's avatar
Don Gagne committed
#include "QGCFileDialog.h"
#include "QGCMessageBox.h"
Don Gagne's avatar
Don Gagne committed
// FlightGear _fgProcess start and connection is quite fragile. Uncomment the define below to get higher level of debug output
Don Gagne's avatar
Don Gagne committed
// for tracking down problems.
Don Gagne's avatar
Don Gagne committed
//#define DEBUG_FLIGHTGEAR_CONNECT
Don Gagne's avatar
Don Gagne committed

Thomas Gubler's avatar
Thomas Gubler committed
QGCFlightGearLink::QGCFlightGearLink(UASInterface* mav, QString startupArguments, QString remoteHost, QHostAddress host, quint16 port) :
Don Gagne's avatar
Don Gagne committed
    flightGearVersion(3),
Thomas Gubler's avatar
Thomas Gubler committed
    startupArguments(startupArguments),
    _sensorHilEnabled(true),
Don Gagne's avatar
Don Gagne committed
    barometerOffsetkPa(0.0f),
    _udpCommSocket(NULL),
    _fgProcess(NULL)
    // 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);

    this->port = port+mav->getUASID();
    this->currentPort = 49000+mav->getUASID();
lm's avatar
lm committed
    this->mav = mav;
Don Gagne's avatar
Don Gagne committed
    this->name = tr("FlightGear 3.0+ Link (port:%1)").arg(port);
LM's avatar
LM committed
    setRemoteHost(remoteHost);
Don Gagne's avatar
Don Gagne committed
    
    // We need a mechanism so show error message from our FGLink thread on the UI thread. This signal connection will do that for us.
    connect(this, &QGCFlightGearLink::showCriticalMessageFromThread, qgcApp(), &QGCApplication::criticalMessageBoxOnMainThread);
{   //do not disconnect unless it is connected.
Don Gagne's avatar
Don Gagne committed
    //disconnectSimulation will delete the memory that was allocated for proces, terraSync and _udpCommSocket
    if(connectState){
       disconnectSimulation();
    }
Don Gagne's avatar
Don Gagne committed
/// @brief Runs the simulation thread. We do setup work here which needs to happen in the seperate thread.
Don Gagne's avatar
Don Gagne committed
    Q_ASSERT(mav);
    Q_ASSERT(!_fgProcessName.isEmpty());
        
Don Gagne's avatar
Don Gagne committed
    // We communicate with FlightGear over a UDP _udpCommSocket
    _udpCommSocket = new QUdpSocket(this);
    Q_CHECK_PTR(_udpCommSocket);
    _udpCommSocket->moveToThread(this);
    _udpCommSocket->bind(host, port);
    QObject::connect(_udpCommSocket, SIGNAL(readyRead()), this, SLOT(readBytes()));
Don Gagne's avatar
Don Gagne committed
    
    
    // Connect to the various HIL signals that we use to then send information across the UDP protocol to FlightGear.
    connect(mav, SIGNAL(hilControlsChanged(quint64, float, float, float, float, quint8, quint8)),
            this, SLOT(updateControls(quint64,float,float,float,float,quint8,quint8)));
    connect(this, SIGNAL(hilStateChanged(quint64, float, float, float, float,float, float, double, double, double, float, float, float, float, float, float, float, float)),
            mav, SLOT(sendHilState(quint64, float, float, float, float,float, float, double, double, double, float, float, float, float, float, float, float, float)));
    connect(this, SIGNAL(sensorHilGpsChanged(quint64, double, double, double, int, float, float, float, float, float, float, float, int)),
            mav, SLOT(sendHilGps(quint64, double, double, double, int, float, float, float, float, float, float, float, int)));
    connect(this, SIGNAL(sensorHilRawImuChanged(quint64,float,float,float,float,float,float,float,float,float,float,float,float,float,quint32)),
            mav, SLOT(sendHilSensors(quint64,float,float,float,float,float,float,float,float,float,float,float,float,float,quint32)));
    connect(this, SIGNAL(sensorHilOpticalFlowChanged(quint64, qint16, qint16, float,float, quint8, float)),
            mav, SLOT(sendHilOpticalFlow(quint64, qint16, qint16, float, float, quint8, float)));
Don Gagne's avatar
Don Gagne committed
    
Don Gagne's avatar
Don Gagne committed
    // Start a new QProcess to run FlightGear in
    _fgProcess = new QProcess(this);
    Q_CHECK_PTR(_fgProcess);
    _fgProcess->moveToThread(this);
Don Gagne's avatar
Don Gagne committed
    
Don Gagne's avatar
Don Gagne committed
    connect(_fgProcess, SIGNAL(error(QProcess::ProcessError)), this, SLOT(processError(QProcess::ProcessError)));
Don Gagne's avatar
Don Gagne committed
#ifdef DEBUG_FLIGHTGEAR_CONNECT
Don Gagne's avatar
Don Gagne committed
    connect(_fgProcess, SIGNAL(readyReadStandardOutput()), this, SLOT(_printFgfsOutput()));
    connect(_fgProcess, SIGNAL(readyReadStandardError()), this, SLOT(_printFgfsError()));
Don Gagne's avatar
Don Gagne committed
    
    if (!_fgProcessWorkingDirPath.isEmpty()) {
Don Gagne's avatar
Don Gagne committed
        _fgProcess->setWorkingDirectory(_fgProcessWorkingDirPath);
		qDebug() << "Working directory" << _fgProcess->workingDirectory();
Don Gagne's avatar
Don Gagne committed
    
#ifdef Q_OS_WIN32
	// On Windows we need to full qualify the location of the excecutable. The call to setWorkingDirectory only
Don Gagne's avatar
Don Gagne committed
	// sets the QProcess context, not the QProcess::start context. For some strange reason this is not the case on
Don Gagne's avatar
Don Gagne committed
	// OSX.
	QDir fgProcessFullyQualified(_fgProcessWorkingDirPath);
	_fgProcessName = fgProcessFullyQualified.absoluteFilePath(_fgProcessName);
Don Gagne's avatar
Don Gagne committed
    
#ifdef DEBUG_FLIGHTGEAR_CONNECT
Don Gagne's avatar
Don Gagne committed
    qDebug() << "\nStarting FlightGear" << _fgProcessWorkingDirPath << _fgProcessName << _fgArgList << "\n";
Don Gagne's avatar
Don Gagne committed
    
Don Gagne's avatar
Don Gagne committed
    _fgProcess->start(_fgProcessName, _fgArgList);
Don Gagne's avatar
Don Gagne committed
    connectState = true;
Don Gagne's avatar
Don Gagne committed
    
    emit simulationConnected(connectState);
Don Gagne's avatar
Don Gagne committed
    emit simulationConnected();
    exec();
}

void QGCFlightGearLink::setPort(int port)
{
    this->port = port;
lm's avatar
lm committed
    disconnectSimulation();
    connectSimulation();
lm's avatar
lm committed
void QGCFlightGearLink::processError(QProcess::ProcessError err)
{
    switch(err)
    {
    case QProcess::FailedToStart:
Don Gagne's avatar
Don Gagne committed
        emit showCriticalMessageFromThread(tr("FlightGear Failed to Start"), _fgProcess->errorString());
lm's avatar
lm committed
        break;
    case QProcess::Crashed:
Don Gagne's avatar
Don Gagne committed
        emit showCriticalMessageFromThread(tr("FlightGear Crashed"), tr("This is a FlightGear-related problem. Please upgrade FlightGear"));
lm's avatar
lm committed
        break;
    case QProcess::Timedout:
Don Gagne's avatar
Don Gagne committed
        emit showCriticalMessageFromThread(tr("FlightGear Start Timed Out"), tr("Please check if the path and command is correct"));
lm's avatar
lm committed
        break;
    case QProcess::WriteError:
Don Gagne's avatar
Don Gagne committed
        emit showCriticalMessageFromThread(tr("Could not Communicate with FlightGear"), tr("Please check if the path and command is correct"));
lm's avatar
lm committed
        break;
    case QProcess::ReadError:
Don Gagne's avatar
Don Gagne committed
        emit showCriticalMessageFromThread(tr("Could not Communicate with FlightGear"), tr("Please check if the path and command is correct"));
lm's avatar
lm committed
        break;
    case QProcess::UnknownError:
    default:
Don Gagne's avatar
Don Gagne committed
        emit showCriticalMessageFromThread(tr("FlightGear Error"), tr("Please check if the path and command is correct."));
lm's avatar
lm committed
        break;
    }
}

/**
 * @param host Hostname in standard formatting, e.g. localhost:14551 or 192.168.1.1:14551
 */
void QGCFlightGearLink::setRemoteHost(const QString& host)
{
    if (host.contains(":"))
    {
        //qDebug() << "HOST: " << host.split(":").first();
        QHostInfo info = QHostInfo::fromName(host.split(":").first());
        if (info.error() == QHostInfo::NoError)
        {
            // Add host
            QList<QHostAddress> hostAddresses = info.addresses();
            QHostAddress address;
            for (int i = 0; i < hostAddresses.size(); i++)
            {
                // Exclude loopback IPv4 and all IPv6 addresses
                if (!hostAddresses.at(i).toString().contains(":"))
                {
                    address = hostAddresses.at(i);
                }
            }
Loading
Loading full blame...