diff --git a/.gitignore b/.gitignore index 0637d11b3b6eeec8db06d147c1f82a186f2db6c8..15d9c7e7df97da1d4e0d1cff1cdf787f3db9740c 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,5 @@ qgroundcontrol qgroundcontrol.xcodeproj/** doc/html doc/doxy.log +deploy/mac +deploy/linux diff --git a/deploy/linux_create_debian_packet.sh b/deploy/linux_create_debian_packet.sh new file mode 100644 index 0000000000000000000000000000000000000000..8bd436bbd622ca8bc1d0574d71b64a22a17be768 --- /dev/null +++ b/deploy/linux_create_debian_packet.sh @@ -0,0 +1,13 @@ +#!/bin/sh +# Clean build directories +rm -rf linux +mkdir -p linux +# Change to build directory and compile application +cd .. +make -j4 +# Copy and build the application bundle +cd deploy +cp -r qgroundcontrol linux/. +cp -r ../audio linux/. +# FIXME Create debian packet +echo -e '\n QGroundControl Debian packet is now ready for publishing\n' diff --git a/deploy/mac_create_dmg.sh b/deploy/mac_create_dmg.sh new file mode 100644 index 0000000000000000000000000000000000000000..1994ed0714cffcecc7c86c29dc4d13cd35a68b53 --- /dev/null +++ b/deploy/mac_create_dmg.sh @@ -0,0 +1,13 @@ +#!/bin/sh +# Clean build directories +rm -rf mac +mkdir -p mac +# Change to build directory and compile application +cd .. +make -j4 +# Copy and build the application bundle +cd deploy +cp -r ../bin/mac/qgroundcontrol.app mac/. +cp -r ../audio mac/qgroundcontrol.app/Contents/MacOs/. +macdeployqt qgroundcontrol.app --bundle +echo -e '\n QGroundControl .DMG file is now ready for publishing\n' diff --git a/mac_audiotest.sh b/mac_audiotest.sh new file mode 100644 index 0000000000000000000000000000000000000000..12cd0fa3431c28d03f859a803e9a19a723339930 --- /dev/null +++ b/mac_audiotest.sh @@ -0,0 +1,2 @@ +#!/bin/sh +cp -r audio bin/mac/qgroundcontrol.app/Contents/MacOs/. diff --git a/src/LogCompressor.cc b/src/LogCompressor.cc index 5737c7379ac2131d8d989e22dd8989da6c0a9e2b..9b5dcc1964fc264584620746bd76cacf73d3d580 100644 --- a/src/LogCompressor.cc +++ b/src/LogCompressor.cc @@ -81,6 +81,11 @@ void LogCompressor::run() QString time = parts.first(); QString field = parts.at(2); QString value = parts.at(3); + // Enforce NaN if no value is present + if (value.length() == 0 || value == "" || value == " " || value == "\t" || value == "\n") + { + value = "NaN"; + } // Get matching output line quint64 index = times->indexOf(time); QString outLine = outLines->at(index); diff --git a/src/uas/UAS.cc b/src/uas/UAS.cc index 6092608bc09a58fa99367099597ea41a0101d0f8..4285580250358934eef1dfb70b9444783e976e56 100644 --- a/src/uas/UAS.cc +++ b/src/uas/UAS.cc @@ -285,6 +285,16 @@ void UAS::receiveMessage(LinkInterface* link, mavlink_message_t message) emit valueChanged(uasId, "vis. x", pos.x, time); emit valueChanged(uasId, "vis. y", pos.y, time); emit valueChanged(uasId, "vis. z", pos.z, time); + // FIXME Only for testing for now + emit valueChanged(uasId, "vis. rot r1", pos.r1, time); + emit valueChanged(uasId, "vis. rot r2", pos.r2, time); + emit valueChanged(uasId, "vis. rot r3", pos.r3, time); + emit valueChanged(uasId, "vis. rot r4", pos.r4, time); + emit valueChanged(uasId, "vis. rot r5", pos.r5, time); + emit valueChanged(uasId, "vis. rot r6", pos.r6, time); + emit valueChanged(uasId, "vis. rot r7", pos.r7, time); + emit valueChanged(uasId, "vis. rot r8", pos.r8, time); + emit valueChanged(uasId, "vis. rot r9", pos.r9, time); } break; case MAVLINK_MSG_ID_POSITION: @@ -533,7 +543,6 @@ void UAS::requestParameters() mavlink_msg_param_request_list_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, this->getUASID(), 0); // Send message twice to increase chance of reception sendMessage(msg); - sendMessage(msg); } void UAS::writeParameters() diff --git a/src/ui/MainWindow.cc b/src/ui/MainWindow.cc index 6259ceb009ca4e6e28ba89d329477011b1d6afcf..8a79d146366fa31bfd3c7334f18192e89ddc17eb 100644 --- a/src/ui/MainWindow.cc +++ b/src/ui/MainWindow.cc @@ -120,7 +120,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), QList hostAddresses = QNetworkInterface::allAddresses(); QString windowname = qApp->applicationName() + " " + qApp->applicationVersion(); - /* + windowname.append(" (" + QHostInfo::localHostName() + ": "); bool prevAddr = false; for (int i = 0; i < hostAddresses.size(); i++) @@ -135,7 +135,6 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), } windowname.append(")"); - */ setWindowTitle(windowname); #ifndef Q_WS_MAC @@ -216,18 +215,28 @@ void MainWindow::saveScreen() } } +/** + * Reload the style sheet from disk. The function tries to load "qgroundcontrol.css" from the application + * directory (which by default does not exist). If it fails, it will load the bundled default CSS + * from memory. + * To customize the application, just create a qgroundcontrol.css file in the application directory + */ void MainWindow::reloadStylesheet() { // Load style sheet - //QFile styleSheet(MG::DIR::getSupportFilesDirectory() + "/images/style-mission.css"); - QFile styleSheet(":/images/style-mission.css"); - if (styleSheet.open(QIODevice::ReadOnly | QIODevice::Text)) { - QString style = QString(styleSheet.readAll()); - style.replace("ICONDIR", MG::DIR::getIconDirectory()); + QFile* styleSheet = new QFile(QCoreApplication::applicationDirPath() + "/qgroundcontrol.css"); + if (!styleSheet->exists()) + { + styleSheet = new QFile(":/images/style-mission.css"); + } + if (styleSheet->open(QIODevice::ReadOnly | QIODevice::Text)) { + QString style = QString(styleSheet->readAll()); + style.replace("ICONDIR", QCoreApplication::applicationDirPath()+ "/images/"); qApp->setStyleSheet(style); } else { - qDebug() << "Style not set:" << styleSheet.fileName() << "opened: " << styleSheet.isOpen(); + qDebug() << "Style not set:" << styleSheet->fileName() << "opened: " << styleSheet->isOpen(); } + delete styleSheet; } void MainWindow::showStatusMessage(const QString& status, int timeout) @@ -235,14 +244,9 @@ void MainWindow::showStatusMessage(const QString& status, int timeout) statusBar->showMessage(status, timeout); } -void MainWindow::setLastAction(QString status) +void MainWindow::showStatusMessage(const QString& status) { - showStatusMessage(status, 5); -} - -void MainWindow::setLinkStatus(QString status) -{ - showStatusMessage(status, 15); + statusBar->showMessage(status, 5); } /** @@ -270,6 +274,7 @@ void MainWindow::connectActions() connect(ui.actionEngineerView, SIGNAL(triggered()), this, SLOT(loadEngineerView())); connect(ui.actionOperatorView, SIGNAL(triggered()), this, SLOT(loadOperatorView())); connect(ui.actionSettingsView, SIGNAL(triggered()), this, SLOT(loadSettingsView())); + connect(ui.actionShow_full_view, SIGNAL(triggered()), this, SLOT(loadAllView())); connect(ui.actionStyleConfig, SIGNAL(triggered()), this, SLOT(reloadStylesheet())); // Joystick configuration @@ -501,28 +506,65 @@ void MainWindow::loadEngineerView() void MainWindow::loadAllView() { + clearView(); + GAudioOutput::instance()->say("Loaded complete view"); + + QDockWidget* containerPFD = new QDockWidget(tr("Primary Flight Display"), this); + containerPFD->setWidget(headDown1); + addDockWidget(Qt::RightDockWidgetArea, containerPFD); + + QDockWidget* containerPayload = new QDockWidget(tr("Payload Status"), this); + containerPayload->setWidget(headDown2); + addDockWidget(Qt::RightDockWidgetArea, containerPayload); + + headDown1->start(); + headDown2->start(); + + // UAS CONTROL + QDockWidget* containerControl = new QDockWidget(tr("Control"), this); + containerControl->setWidget(control); + addDockWidget(Qt::LeftDockWidgetArea, containerControl); + + // UAS LIST + QDockWidget* containerUASList = new QDockWidget(tr("Unmanned Systems"), this); + containerUASList->setWidget(list); + addDockWidget(Qt::BottomDockWidgetArea, containerUASList); + + // UAS STATUS + QDockWidget* containerStatus = new QDockWidget(tr("Status Details"), this); + containerStatus->setWidget(info); + addDockWidget(Qt::LeftDockWidgetArea, containerStatus); + // WAYPOINT LIST + QDockWidget* containerWaypoints = new QDockWidget(tr("Waypoint List"), this); + containerWaypoints->setWidget(waypoints); + addDockWidget(Qt::BottomDockWidgetArea, containerWaypoints); + + // DEBUG CONSOLE + QDockWidget* containerComm = new QDockWidget(tr("Communication Console"), this); + containerComm->setWidget(debugConsole); + addDockWidget(Qt::BottomDockWidgetArea, containerComm); + + // OBJECT DETECTION + QDockWidget* containerObjRec = new QDockWidget(tr("Object Recognition"), this); + containerObjRec->setWidget(detection); + addDockWidget(Qt::RightDockWidgetArea, containerObjRec); + + // LINE CHART + linechart->setActive(true); + centerStack->setCurrentWidget(linechart); + + // ONBOARD PARAMETERS + QDockWidget* containerParams = new QDockWidget(tr("Onboard Parameters"), this); + containerParams->setWidget(parameters); + addDockWidget(Qt::RightDockWidgetArea, containerParams); + + this->show(); } void MainWindow::loadWidgets() { - loadOperatorView(); - //loadEngineerView(); + //loadOperatorView(); + loadEngineerView(); //loadPilotView(); } - -/* -void MainWindow::removeCommConfAct(QAction* action) -{ - ui.menuNetwork->removeAction(action); -}*/ - -void MainWindow::runTests() -{ - // TODO Remove after debugging: Add fake data - static double testvalue = 0.0f; - testvalue += 0.01f; - linechart->appendData(126, "test data", testvalue, MG::TIME::getGroundTimeNow()); -} - - diff --git a/src/ui/MainWindow.h b/src/ui/MainWindow.h index 8418eeed9a2bce5c5d9a7e47ed296d71b81b5a97..9e0a43e696a04e03ec12f679cc0f394872900ad7 100644 --- a/src/ui/MainWindow.h +++ b/src/ui/MainWindow.h @@ -76,32 +76,6 @@ public: MainWindow(QWidget *parent = 0); ~MainWindow(); - QSettings settings; - UASControlWidget* control; - LinechartWidget* linechart; - UASInfoWidget* info; - CameraView* camera; - UASListWidget* list; - WaypointList* waypoints; - ObjectDetectionView* detection; - HUD* hud; - PFD* pfd; - GaugePanel* gaugePanel; - - // Popup widgets - JoystickWidget* joystickWidget; - - JoystickInput* joystick; - - /** User interface actions **/ - QAction* connectUASAct; - QAction* disconnectUASAct; - QAction* startUASAct; - QAction* returnUASAct; - QAction* stopUASAct; - QAction* killUASAct; - QAction* simulateUASAct; - public slots: /** * @brief Shows a status message on the bottom status bar @@ -112,8 +86,15 @@ public slots: * @param timeout how long the status should be displayed */ void showStatusMessage(const QString& status, int timeout); - void setLastAction(QString status); - void setLinkStatus(QString status); + /** + * @brief Shows a status message on the bottom status bar + * + * The status message will be overwritten if a new message is posted to this function. + * it will be automatically hidden after 5 seconds. + * + * @param status message text + */ + void showStatusMessage(const QString& status); void addLink(); void addLink(LinkInterface* link); void configure(); @@ -133,10 +114,8 @@ public slots: /** @brief Load view with all widgets */ void loadAllView(); + /** @brief Reload the CSS style sheet */ void reloadStylesheet(); - - void runTests(); - protected: QStatusBar* statusBar; QStatusBar* createStatusBar(); @@ -151,16 +130,39 @@ protected: MAVLinkSimulationLink* simulationLink; LinkInterface* udpLink; - QDockWidget* controlDock; - QStackedWidget* centerStack; - + QSettings settings; + UASControlWidget* control; + LinechartWidget* linechart; + UASInfoWidget* info; + CameraView* camera; + UASListWidget* list; + WaypointList* waypoints; + ObjectDetectionView* detection; + HUD* hud; DebugConsole* debugConsole; MapWidget* map; ParameterInterface* parameters; XMLCommProtocolWidget* protocol; HDDisplay* headDown1; HDDisplay* headDown2; + GaugePanel* gaugePanel; + // Popup widgets + JoystickWidget* joystickWidget; + + JoystickInput* joystick; + + /** User interface actions **/ + QAction* connectUASAct; + QAction* disconnectUASAct; + QAction* startUASAct; + QAction* returnUASAct; + QAction* stopUASAct; + QAction* killUASAct; + QAction* simulateUASAct; + + QDockWidget* controlDock; + QStackedWidget* centerStack; LogCompressor* comp; QString screenFileName; diff --git a/src/ui/MainWindow.ui b/src/ui/MainWindow.ui index 8176b97ceb882832fc13f3e533bfc4d4c4c8fb6b..d77e4f297c5eced3d9e0484f34d9a234fa6fe96e 100644 --- a/src/ui/MainWindow.ui +++ b/src/ui/MainWindow.ui @@ -74,6 +74,7 @@ + @@ -243,6 +244,15 @@ Simulate one vehicle to test and evaluate this application + + + + :/images/status/network-transmit-receive.svg:/images/status/network-transmit-receive.svg + + + Show full view + + diff --git a/src/ui/QGCParamWidget.cc b/src/ui/QGCParamWidget.cc index f43096eb10e116541df4b425342dd8b66905e0af..f9170925b8325d6c3ff1633b78012c53ebe5beed 100644 --- a/src/ui/QGCParamWidget.cc +++ b/src/ui/QGCParamWidget.cc @@ -43,7 +43,8 @@ This file is part of the QGROUNDCONTROL project QGCParamWidget::QGCParamWidget(UASInterface* uas, QWidget *parent) : QWidget(parent), mav(uas), - components(new QMap()) + components(new QMap()), + changedValues()//QMap* >()) { // Create tree widget tree = new QTreeWidget(this); @@ -82,6 +83,8 @@ QGCParamWidget::QGCParamWidget(UASInterface* uas, QWidget *parent) : // Connect signals/slots connect(this, SIGNAL(parameterChanged(int,QString,float)), mav, SLOT(setParameter(int,QString,float))); + connect(tree, SIGNAL(itemChanged(QTreeWidgetItem*,int)), this, SLOT(parameterItemChanged(QTreeWidgetItem*,int))); + // New parameters from UAS connect(uas, SIGNAL(parameterChanged(int,int,QString,float)), this, SLOT(addParameter(int,int,QString,float))); } @@ -138,8 +141,26 @@ void QGCParamWidget::addParameter(int uas, int component, QString parameterName, { addComponent(uas, component, "Component #" + QString::number(component)); } - components->value(component)->addChild(item); - item->setFlags(item->flags() | Qt::ItemIsEditable); + + bool found = false; + QTreeWidgetItem* parent = components->value(component); + for (int i = 0; i < parent->childCount(); i++) + { + QTreeWidgetItem* child = parent->child(i); + QString key = child->data(0, Qt::DisplayRole).toString(); + if (key == parameterName) + { + qDebug() << "UPDATED CHILD"; + child->setData(1, Qt::DisplayRole, value); + found = true; + } + } + + if (!found) + { + components->value(component)->addChild(item); + item->setFlags(item->flags() | Qt::ItemIsEditable); + } //connect(item, SIGNAL()) tree->expandAll(); tree->update(); @@ -156,6 +177,40 @@ void QGCParamWidget::requestParameterList() mav->requestParameters(); } +void QGCParamWidget::parameterItemChanged(QTreeWidgetItem* current, int column) +{ + if (current && column > 0) + { + QTreeWidgetItem* parent = current->parent(); + while (parent->parent() != NULL) + { + parent = parent->parent(); + } + // Parent is now top-level component + int key = components->key(parent); + if (!changedValues.contains(key)) + { + changedValues.insert(key, new QMap()); + } + QMap* map = changedValues.value(key, NULL); + if (map) + { + bool ok; + QString str = current->data(0, Qt::DisplayRole).toString(); + float value = current->data(1, Qt::DisplayRole).toDouble(&ok); + // Send parameter to MAV + if (ok) + { + if (ok) + { + qDebug() << "PARAM CHANGED: COMP:" << key << "KEY:" << str << "VALUE:" << value; + map->insert(str, value); + } + } + } + } +} + /** * @param component the subsystem which has the parameter * @param parameterName name of the parameter, as delivered by the system @@ -171,36 +226,23 @@ void QGCParamWidget::setParameter(int component, QString parameterName, float va */ void QGCParamWidget::setParameters() { - //mav->setParameter(component, parameterName, value); // Iterate through all components, through all parameters and emit them - QMap::iterator i; - // Iterate through all components / subsystems - for (i = components->begin(); i != components->end(); ++i) + QMap*>::iterator i; + for (i = changedValues.begin(); i != changedValues.end(); ++i) { - // Get all parameters of this component + // Iterate through the parameters of the component int compid = i.key(); - QTreeWidgetItem* item = i.value(); - for (int j = 0; j < item->childCount(); ++j) + QMap* comp = i.value(); { - QTreeWidgetItem* param = item->child(j); - // First column is name, second column value - bool ok = true; - QString key = param->data(0, Qt::DisplayRole).toString(); - float value = param->data(1, Qt::DisplayRole).toDouble(&ok); - // Send parameter to MAV - if (ok) + QMap::iterator j; + for (j = comp->begin(); j != comp->end(); ++j) { - emit parameterChanged(compid, key, value); - qDebug() << "KEY:" << key << "VALUE:" << value; - } - else - { - qDebug() << __FILE__ << __LINE__ << "CONVERSION ERROR!"; + emit parameterChanged(compid, j.key(), j.value()); } } } - clear(); - //mav->requestParameters(); + + changedValues.clear(); qDebug() << __FILE__ << __LINE__ << "SETTING ALL PARAMETERS"; } diff --git a/src/ui/QGCParamWidget.h b/src/ui/QGCParamWidget.h index 43f766a94673c128bcb26c2f78e5ec57302ba884..13c1bf434286d4757f38197abf3d3711eb65d048 100644 --- a/src/ui/QGCParamWidget.h +++ b/src/ui/QGCParamWidget.h @@ -67,10 +67,13 @@ public slots: void writeParameters(); /** @brief Clear the parameter list */ void clear(); + /** @brief Update when user changes parameters */ + void parameterItemChanged(QTreeWidgetItem* prev, int column); protected: UASInterface* mav; ///< The MAV this widget is controlling QTreeWidget* tree; ///< The parameter tree QMap* components; ///< The list of components + QMap* > changedValues; ///< Changed values };