Commit 69630975 authored by Don Gagne's avatar Don Gagne

commit

parent 96ef0dad
......@@ -91,7 +91,7 @@
<damping_coeff_rebound unit="LBS/FT/SEC"> 4.0 </damping_coeff_rebound>
<max_steer unit="DEG">0</max_steer>
<brake_group>NONE</brake_group>
<retractable>FIXED</retractable>
<retractable>0</retractable>
</contact>
<contact type="BOGEY" name="NOSE">
<location unit="M">
......@@ -107,7 +107,7 @@
<damping_coeff_rebound unit="LBS/FT/SEC"> 4.0 </damping_coeff_rebound>
<max_steer unit="DEG">0</max_steer>
<brake_group>NONE</brake_group>
<retractable>FIXED</retractable>
<retractable>0</retractable>
</contact>
<contact type="STRUCTURE" name="TAIL">
......
......@@ -40,11 +40,14 @@ This file is part of the QGROUNDCONTROL project
#include <QHostInfo>
#include "MainWindow.h"
// FlightGear process start and connection is quite fragile. Uncomment the define below to get higher level of debug output
// for tracking down problems.
#define DEBUG_FLIGHTGEAR_CONNECT
QGCFlightGearLink::QGCFlightGearLink(UASInterface* mav, QString startupArguments, QString remoteHost, QHostAddress host, quint16 port) :
socket(NULL),
process(NULL),
terraSync(NULL),
flightGearVersion(0),
flightGearVersion(3),
startupArguments(startupArguments),
_sensorHilEnabled(true),
barometerOffsetkPa(0.0f)
......@@ -58,7 +61,7 @@ QGCFlightGearLink::QGCFlightGearLink(UASInterface* mav, QString startupArguments
this->connectState = false;
this->currentPort = 49000+mav->getUASID();
this->mav = mav;
this->name = tr("FlightGear Link (port:%1)").arg(port);
this->name = tr("FlightGear 3.0+ Link (port:%1)").arg(port);
setRemoteHost(remoteHost);
}
......@@ -74,6 +77,7 @@ QGCFlightGearLink::~QGCFlightGearLink()
* @brief Runs the thread
*
**/
void QGCFlightGearLink::run()
{
qDebug() << "STARTING FLIGHTGEAR LINK";
......@@ -305,23 +309,23 @@ void QGCFlightGearLink::processError(QProcess::ProcessError err)
switch(err)
{
case QProcess::FailedToStart:
MainWindow::instance()->showCriticalMessage(tr("FlightGear/TerraSync Failed to Start"), tr("Please check if the path and command is correct"));
MainWindow::instance()->showCriticalMessage(tr("FlightGear Failed to Start"), tr("Please check if the path and command is correct"));
break;
case QProcess::Crashed:
MainWindow::instance()->showCriticalMessage(tr("FlightGear/TerraSync Crashed"), tr("This is a FlightGear-related problem. Please upgrade FlightGear"));
MainWindow::instance()->showCriticalMessage(tr("FlightGear Crashed"), tr("This is a FlightGear-related problem. Please upgrade FlightGear"));
break;
case QProcess::Timedout:
MainWindow::instance()->showCriticalMessage(tr("FlightGear/TerraSync Start Timed Out"), tr("Please check if the path and command is correct"));
MainWindow::instance()->showCriticalMessage(tr("FlightGear Start Timed Out"), tr("Please check if the path and command is correct"));
break;
case QProcess::WriteError:
MainWindow::instance()->showCriticalMessage(tr("Could not Communicate with FlightGear/TerraSync"), tr("Please check if the path and command is correct"));
MainWindow::instance()->showCriticalMessage(tr("Could not Communicate with FlightGear"), tr("Please check if the path and command is correct"));
break;
case QProcess::ReadError:
MainWindow::instance()->showCriticalMessage(tr("Could not Communicate with FlightGear/TerraSync"), tr("Please check if the path and command is correct"));
MainWindow::instance()->showCriticalMessage(tr("Could not Communicate with FlightGear"), tr("Please check if the path and command is correct"));
break;
case QProcess::UnknownError:
default:
MainWindow::instance()->showCriticalMessage(tr("FlightGear/TerraSync Error"), tr("Please check if the path and command is correct."));
MainWindow::instance()->showCriticalMessage(tr("FlightGear Error"), tr("Please check if the path and command is correct."));
break;
}
}
......@@ -633,12 +637,6 @@ bool QGCFlightGearLink::disconnectSimulation()
delete process;
process = NULL;
}
if (terraSync)
{
terraSync->close();
delete terraSync;
terraSync = NULL;
}
if (socket)
{
socket->close();
......@@ -653,6 +651,79 @@ bool QGCFlightGearLink::disconnectSimulation()
return !connectState;
}
/// @brief Splits a space seperated set of command line arguments into a QStringList.
/// Quoted strings are allowed and handled correctly.
/// @param uiArgs Arguments to parse
/// @param argList Returned argument list
/// @return Returns false if the argument list has mistmatced quotes within in.
bool QGCFlightGearLink::parseUIArguments(QString uiArgs, QStringList& argList)
{
// This is not as easy as it seams since some options can be quoted to preserve spaces within things like
// directories. There is likely some crazed regular expression which can do this. But after trying that
// route I gave up and instead here is the code which does it the hard way. Another thing to be aware of
// is that the QStringList passed to QProces::start is similar to what you would get in argv after the
// command line is processed. This means that quoted strings have the quotes removed before making it to argv.
bool inQuotedString = false;
bool previousSpace = false;
QString currentArg;
for (int i=0; i<uiArgs.size(); i++) {
const QChar chr = uiArgs[i];
if (chr == ' ') {
if (inQuotedString) {
// Space is inside quoted string leave it in
currentArg += chr;
continue;
} else {
if (previousSpace) {
// Disregard multiple spaces
continue;
} else {
// We have a space that is finishing an argument
previousSpace = true;
if (inQuotedString) {
MainWindow::instance()->showCriticalMessage(tr("FlightGear Failed to Start"), tr("Mismatched quotes in specified command line options"));
return false;
}
if (!currentArg.isEmpty()) {
argList += currentArg;
currentArg.clear();
}
}
}
} else if (chr == '\"') {
// Flip the state of being in a quoted string. Note that we specifically do not add the
// quote to the string. This replicates standards command line parsing behaviour.
if (chr == '\"') {
inQuotedString = !inQuotedString;
}
previousSpace = false;
} else {
// Flip the state of being in a quoted string
if (chr == '\"') {
inQuotedString = !inQuotedString;
}
previousSpace = false;
currentArg += chr;
}
}
// We should never end parsing on an unterminated quote
if (inQuotedString) {
return false;
}
// Finish up last arg
if (!currentArg.isEmpty()) {
argList += currentArg;
currentArg.clear();
}
return true;
}
/**
* @brief Connect the connection.
*
......
......@@ -87,6 +87,8 @@ public:
void sensorHilEnabled(bool sensorHilEnabled) {
_sensorHilEnabled = sensorHilEnabled;
}
static bool parseUIArguments(QString uiArgs, QStringList& argList);
void run();
......@@ -130,8 +132,6 @@ public slots:
bool connectSimulation();
bool disconnectSimulation();
void printTerraSyncOutput();
void printTerraSyncError();
void printFgfsOutput();
void printFgfsError();
void setStartupArguments(QString startupArguments);
......@@ -147,19 +147,8 @@ protected:
QUdpSocket* socket;
bool connectState;
quint64 bitsSentTotal;
quint64 bitsSentCurrent;
quint64 bitsSentMax;
quint64 bitsReceivedTotal;
quint64 bitsReceivedCurrent;
quint64 bitsReceivedMax;
quint64 connectionStartTime;
QMutex statisticsMutex;
QMutex dataMutex;
QTimer refreshTimer;
UASInterface* mav;
QProcess* process;
QProcess* terraSync;
unsigned int flightGearVersion;
QString startupArguments;
bool _sensorHilEnabled;
......
/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009 - 2014 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 "FlightGearTest.h"
#include "QGCFlightGearLink.h"
/// @file
/// @brief FlightGearUnitTest HIL Simulation class
///
/// @author Don Gagne <don@thegagnes.com>
FlightGearUnitTest::FlightGearUnitTest(void)
{
}
// Called before every test
void FlightGearUnitTest::init(void)
{
// Nothing here yet
}
// Called after every test
void FlightGearUnitTest::cleanup(void)
{
// Nothing here yet
}
/// @brief The QGCFlightGearLink::parseUIArguments method is fairly involved so we have a unit
// test for it.
void FlightGearUnitTest::_parseUIArguments_test(void)
{
typedef struct {
const char* args;
const char* expectedListAsChar;
bool expectedReturn;
} ParseUIArgumentsTestCase_t;
static const ParseUIArgumentsTestCase_t rgTestCases[] = {
{ "foo", "foo", true },
{ "foo bar", "foo|bar", true },
{ "--foo --bar", "--foo|--bar", true },
{ "foo=bar", "foo=bar", true },
{ "foo=bar bar=baz", "foo=bar|bar=baz", true },
{ "foo=\"bar\"", "foo=bar", true },
{ "foo=\"bar\" bar=\"baz\"", "foo=bar|bar=baz", true },
{ "foo=\"b ar\"", "foo=b ar", true },
{ "foo=\"b ar\" bar=\"b az\"", "foo=b ar|bar=b az", true },
{ "foo\"", NULL, false },
};
for (size_t i=0; i<sizeof(rgTestCases)/sizeof(rgTestCases[0]); i++) {
const ParseUIArgumentsTestCase_t* testCase = &rgTestCases[i];
QStringList returnedArgList;
bool actualReturn = QGCFlightGearLink::parseUIArguments(testCase->args, returnedArgList);
QCOMPARE(actualReturn, testCase->expectedReturn);
if (actualReturn) {
QStringList expectedArgList = QString(testCase->expectedListAsChar).split("|");
if (returnedArgList != expectedArgList) {
qDebug() << "About to fail: Returned" << returnedArgList << "Expected" << expectedArgList;
}
QVERIFY(returnedArgList == expectedArgList);
}
}
}
/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009 - 2014 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/>.
======================================================================*/
#ifndef TCPLINKTEST_H
#define TCPLINKTEST_H
#include <QObject>
#include <QtTest>
#include <QApplication>
#include "AutoTest.h"
#include "TCPLink.h"
#include "MultiSignalSpy.h"
/// @file
/// @brief FlightGear HIL Simulation unit tests
///
/// @author Don Gagne <don@thegagnes.com>
class FlightGearUnitTest : public QObject
{
Q_OBJECT
public:
FlightGearUnitTest(void);
private slots:
void init(void);
void cleanup(void);
void _parseUIArguments_test(void);
private:
};
DECLARE_TEST(FlightGearUnitTest)
#endif
......@@ -39,7 +39,7 @@
</item>
<item>
<property name="text">
<string>Flightgear</string>
<string>FlightGear 3.0+</string>
</property>
</item>
<item>
......@@ -69,7 +69,7 @@
<item row="2" column="0" colspan="2">
<widget class="QLabel" name="statusLabel">
<property name="text">
<string>No simulator active..</string>
<string></string>
</property>
</widget>
</item>
......
#include "QGCHilFlightGearConfiguration.h"
#include "ui_QGCHilFlightGearConfiguration.h"
#include "MainWindow.h"
QGCHilFlightGearConfiguration::QGCHilFlightGearConfiguration(UAS* mav,QWidget *parent) :
// Various settings groups and keys
const char* QGCHilFlightGearConfiguration::_settingsGroup = "QGC_HILCONFIG_FLIGHTGEAR";
const char* QGCHilFlightGearConfiguration::_mavSettingsSubGroupFixedWing = "FIXED_WING";
const char* QGCHilFlightGearConfiguration::_mavSettingsSubGroupQuadRotor = "QUADROTOR";
const char* QGCHilFlightGearConfiguration::_aircraftKey = "AIRCRAFT";
const char* QGCHilFlightGearConfiguration::_optionsKey = "OPTIONS";
const char* QGCHilFlightGearConfiguration::_barometerOffsetKey = "BARO_OFFSET";
const char* QGCHilFlightGearConfiguration::_sensorHilKey = "SENSOR_HIL";
// Default set of optional command line parameters. If FlightGear won't run HIL without it it should go into
// the QGCFlightGearLink code instead.
const char* QGCHilFlightGearConfiguration::_defaultOptions = "--roll=0 --pitch=0 --vc=0 --heading=300 --timeofday=noon --disable-hud-3d --disable-fullscreen --geometry=400x300 --disable-anti-alias-hud --wind=0@0 --turbulence=0.0 --prop:/sim/frame-rate-throttle-hz=30 --control=mouse --disable-sound --disable-random-objects --disable-ai-traffic --shading-flat --fog-disable --disable-specular-highlight --disable-random-objects --disable-panel --disable-clouds --fdm=jsb --units-meters --enable-terrasync --atlas=socket,out,1,localhost,5505,udp";
QGCHilFlightGearConfiguration::QGCHilFlightGearConfiguration(UAS* mav, QWidget *parent) :
QWidget(parent),
mav(mav),
ui(new Ui::QGCHilFlightGearConfiguration)
{
ui->setupUi(this);
_mav(mav),
_mavSettingsSubGroup(NULL),
_resetOptionsAction(tr("Reset to default options"), this)
QStringList items = QStringList();
if (mav->getSystemType() == MAV_TYPE_FIXED_WING)
{
Q_ASSERT(_mav);
_ui.setupUi(this);
QStringList items;
if (_mav->getSystemType() == MAV_TYPE_FIXED_WING)
{
items << "EasyStar";
items << "Rascal110-JSBSim";
items << "c172p";
items << "YardStik";
items << "Malolo1";
_mavSettingsSubGroup = _mavSettingsSubGroupFixedWing;
}
else if (mav->getSystemType() == MAV_TYPE_QUADROTOR)
else if (_mav->getSystemType() == MAV_TYPE_QUADROTOR)
{
items << "arducopter";
_mavSettingsSubGroup = _mavSettingsSubGroupQuadRotor;
}
else
{
// FIXME: Should disable all input, won't work. Show error message in the status label thing.
items << "<aircraft>";
}
ui->aircraftComboBox->addItems(items);
_ui.aircraftComboBox->addItems(items);
QSettings settings;
settings.beginGroup(_settingsGroup);
settings.beginGroup(_mavSettingsSubGroup);
QString aircraft = settings.value(_aircraftKey).toString();
QString options = settings.value(_optionsKey).toString();
QString baroOffset = settings.value(_barometerOffsetKey).toString();
bool sensorHil = settings.value(_sensorHilKey, QVariant(true)).toBool();
settings.endGroup();
settings.endGroup();
if (!aircraft.isEmpty()) {
int index = _ui.aircraftComboBox->findText(aircraft);
if (index != -1) {
_ui.aircraftComboBox->setCurrentIndex(index);
}
}
if (options.isEmpty()) {
options = _defaultOptions;
}
_ui.optionsPlainTextEdit->setPlainText(options);
_ui.barometerOffsetLineEdit->setText(baroOffset);
_ui.sensorHilCheckBox->setChecked(sensorHil);
// Provide an option on the context menu to reset the option back to default
_ui.optionsPlainTextEdit->setContextMenuPolicy(Qt::CustomContextMenu);
bool success = connect(&_resetOptionsAction, SIGNAL(triggered()), this, SLOT(_setDefaultOptions()));
Q_ASSERT(success);
success = connect(_ui.optionsPlainTextEdit, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(_showContextMenu(const QPoint &)));
Q_ASSERT(success);
}
QGCHilFlightGearConfiguration::~QGCHilFlightGearConfiguration()
{
delete ui;
QString aircraft = _ui.aircraftComboBox->currentText();
QString options = _ui.optionsPlainTextEdit->toPlainText();
QString baroOffset = _ui.barometerOffsetLineEdit->text();
bool sensorHil = _ui.sensorHilCheckBox->isChecked();
QSettings settings;
settings.beginGroup(_settingsGroup);
settings.beginGroup(_mavSettingsSubGroup);
if (aircraft.isEmpty()) {
settings.remove(_aircraftKey);
} else {
settings.setValue(_aircraftKey, aircraft);
}
if (options.isEmpty() || options == _defaultOptions) {
settings.remove(_optionsKey);
} else {
settings.setValue(_optionsKey, options);
}
// Double QVariant is to convert from string to float. It will change to 0.0 if invalid.
settings.setValue(_barometerOffsetKey, QVariant(QVariant(baroOffset).toFloat()));
settings.setValue(_sensorHilKey, QVariant(sensorHil));
settings.endGroup();
settings.endGroup();
}
void QGCHilFlightGearConfiguration::on_startButton_clicked()
{
//XXX check validity of inputs
QString options = ui->optionsPlainTextEdit->toPlainText();
options.append(" --aircraft=" + ui->aircraftComboBox->currentText());
mav->enableHilFlightGear(true, options, ui->sensorHilCheckBox->isChecked(), this);
QString options = _ui.optionsPlainTextEdit->toPlainText();
options.append(" --aircraft=" + _ui.aircraftComboBox->currentText());
_mav->enableHilFlightGear(true, options, _ui.sensorHilCheckBox->isChecked(), this);
}
void QGCHilFlightGearConfiguration::on_stopButton_clicked()
{
mav->stopHil();
_mav->stopHil();
}
void QGCHilFlightGearConfiguration::on_barometerOffsetLineEdit_textChanged(const QString& baroOffset)
......@@ -53,3 +129,16 @@ void QGCHilFlightGearConfiguration::on_barometerOffsetLineEdit_textChanged(const
emit barometerOffsetChanged(baroOffset.toFloat());
}
void QGCHilFlightGearConfiguration::_showContextMenu(const QPoint &pt)
{
QMenu* menu = _ui.optionsPlainTextEdit->createStandardContextMenu();
menu->addAction(&_resetOptionsAction);
menu->exec(_ui.optionsPlainTextEdit->mapToGlobal(pt));
delete menu;
}
void QGCHilFlightGearConfiguration::_setDefaultOptions(void)
{
_ui.optionsPlainTextEdit->setPlainText(_defaultOptions);
}
......@@ -7,6 +7,8 @@
#include "QGCFlightGearLink.h"
#include "UAS.h"
#include "ui_QGCHilFlightGearConfiguration.h"
namespace Ui {
class QGCHilFlightGearConfiguration;
}
......@@ -20,16 +22,33 @@ public:
~QGCHilFlightGearConfiguration();
protected:
UAS* mav;
private slots:
void on_startButton_clicked();
void on_stopButton_clicked();
void on_barometerOffsetLineEdit_textChanged(const QString& baroOffset);
void _setDefaultOptions(void);
void _showContextMenu(const QPoint& pt);
private:
Ui::QGCHilFlightGearConfiguration *ui;
Ui::QGCHilFlightGearConfiguration _ui;
UAS* _mav; /// mav associated with this ui
static const char* _settingsGroup; /// Top level settings group
const char* _mavSettingsSubGroup; /// We maintain a settings sub group per mav type
static const char* _mavSettingsSubGroupFixedWing; /// Subgroup if mav type is MAV_TYPE_FIXED_WING
static const char* _mavSettingsSubGroupQuadRotor; /// Subgroup is mav type is MAV_TYPE_QUADROTOR
static const char* _aircraftKey; /// Settings key for aircraft selection
static const char* _optionsKey; /// Settings key for FlightGear cmd line options
static const char* _barometerOffsetKey; /// Settings key for barometer offset
static const char* _sensorHilKey; /// Settings key for Sensor Hil checkbox
static const char* _defaultOptions; /// Default set of FlightGEar command line options
QAction _resetOptionsAction; /// Context menu item to reset options to default
signals:
void barometerOffsetChanged(float barometerOffsetkPa);
};
......
......@@ -68,7 +68,7 @@
</sizepolicy>
</property>
<property name="plainText">
<string>--roll=0 --pitch=0 --vc=0 --heading=300 --timeofday=noon --disable-hud-3d --disable-fullscreen --geometry=400x300 --disable-anti-alias-hud --wind=0@0 --turbulence=0.0 --prop:/sim/frame-rate-throttle-hz=30 --control=mouse --disable-sound --disable-random-objects --disable-ai-models --shading-flat --fog-disable --disable-specular-highlight --disable-random-objects --disable-panel --disable-clouds --fdm=jsb --units-meters --prop:/engines/engine/running=true</string>
<string></string>
</property>
</widget>
</item>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment