diff --git a/files/flightgear/Aircraft/EasyStar/easystar.xml b/files/flightgear/Aircraft/EasyStar/easystar.xml
index 3477038fef2894d34781562b11588913287261ce..742c6cb6318b5269fb93c35561020a5362a506b3 100644
--- a/files/flightgear/Aircraft/EasyStar/easystar.xml
+++ b/files/flightgear/Aircraft/EasyStar/easystar.xml
@@ -91,7 +91,7 @@
4.0
0
NONE
- FIXED
+ 0
@@ -107,7 +107,7 @@
4.0
0
NONE
- FIXED
+ 0
diff --git a/src/comm/QGCFlightGearLink.cc b/src/comm/QGCFlightGearLink.cc
index d0164958a60feb9d9e0a14f8560e4402bbe20432..ac1539c92161e6478015ee183a4c0c77fd24000d 100644
--- a/src/comm/QGCFlightGearLink.cc
+++ b/src/comm/QGCFlightGearLink.cc
@@ -40,11 +40,14 @@ This file is part of the QGROUNDCONTROL project
#include
#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; ishowCriticalMessage(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.
*
diff --git a/src/comm/QGCFlightGearLink.h b/src/comm/QGCFlightGearLink.h
index 603286b93d261920e025ef296a7e450d2de0be7c..3409d312c9bcfa2ebc9549138b84ca3710e790ac 100644
--- a/src/comm/QGCFlightGearLink.h
+++ b/src/comm/QGCFlightGearLink.h
@@ -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;
diff --git a/src/qgcunittest/FlightGearTest.cc b/src/qgcunittest/FlightGearTest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..28b970501cf3b663d8f8aa4052b268ad545a1527
--- /dev/null
+++ b/src/qgcunittest/FlightGearTest.cc
@@ -0,0 +1,85 @@
+/*=====================================================================
+
+ QGroundControl Open Source Ground Control Station
+
+ (c) 2009 - 2014 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 .
+
+ ======================================================================*/
+
+#include "FlightGearTest.h"
+#include "QGCFlightGearLink.h"
+
+/// @file
+/// @brief FlightGearUnitTest HIL Simulation class
+///
+/// @author Don Gagne
+
+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; iargs, 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);
+ }
+ }
+}
diff --git a/src/qgcunittest/FlightGearTest.h b/src/qgcunittest/FlightGearTest.h
new file mode 100644
index 0000000000000000000000000000000000000000..898006c6936bdc17fc8f35554da79afc71690814
--- /dev/null
+++ b/src/qgcunittest/FlightGearTest.h
@@ -0,0 +1,58 @@
+/*=====================================================================
+
+ QGroundControl Open Source Ground Control Station
+
+ (c) 2009 - 2014 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 .
+
+ ======================================================================*/
+
+#ifndef TCPLINKTEST_H
+#define TCPLINKTEST_H
+
+#include
+#include
+#include
+
+#include "AutoTest.h"
+#include "TCPLink.h"
+#include "MultiSignalSpy.h"
+
+/// @file
+/// @brief FlightGear HIL Simulation unit tests
+///
+/// @author Don Gagne
+
+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
diff --git a/src/ui/QGCHilConfiguration.ui b/src/ui/QGCHilConfiguration.ui
index 1db3ec1b621f675b463961f691482e8488222a1f..8da9ad667bf99458144e621b9eba2f211b95db14 100644
--- a/src/ui/QGCHilConfiguration.ui
+++ b/src/ui/QGCHilConfiguration.ui
@@ -39,7 +39,7 @@
-
- Flightgear
+ FlightGear 3.0+
-
@@ -69,7 +69,7 @@
-
- No simulator active..
+
diff --git a/src/ui/QGCHilFlightGearConfiguration.cc b/src/ui/QGCHilFlightGearConfiguration.cc
index 67f4eb21d8bf26de12135698970523e3c7a4e9de..f1c5efea3723696539b3d93d4adc15f5f8d9d88e 100644
--- a/src/ui/QGCHilFlightGearConfiguration.cc
+++ b/src/ui/QGCHilFlightGearConfiguration.cc
@@ -1,51 +1,127 @@
#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 << "";
}
- 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);
+}
+
diff --git a/src/ui/QGCHilFlightGearConfiguration.h b/src/ui/QGCHilFlightGearConfiguration.h
index 7e0e48901380da54c62d39dfc722bfd4a499ee5f..0b72996c6b3282c21e42e1316271829eddc0636a 100644
--- a/src/ui/QGCHilFlightGearConfiguration.h
+++ b/src/ui/QGCHilFlightGearConfiguration.h
@@ -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);
};
diff --git a/src/ui/QGCHilFlightGearConfiguration.ui b/src/ui/QGCHilFlightGearConfiguration.ui
index c91f992ac7de984ff0d31588d051ebdbf450b112..96bb40fcafc88f7a9642027e787c7e1121f5d009 100644
--- a/src/ui/QGCHilFlightGearConfiguration.ui
+++ b/src/ui/QGCHilFlightGearConfiguration.ui
@@ -68,7 +68,7 @@
- --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
+