From 94348cb74ac192e2d477fa3743cf3e9fbb9c3b08 Mon Sep 17 00:00:00 2001 From: pixhawk <pixhawk@student.ethz.ch> Date: Sat, 8 Jan 2011 18:23:08 +0100 Subject: [PATCH] Added not yet working version of logging / log replay, fixed ALL bugs in main window, enabled full persistence in main window and serial link. --- qgroundcontrol.pro | 9 ++- src/QGC.h | 31 ++++++++++ src/comm/MAVLinkProtocol.cc | 18 ++++-- src/comm/MAVLinkProtocol.h | 7 ++- src/comm/MAVLinkSimulationLink.cc | 6 +- src/comm/SerialLink.cc | 113 ++++++++++++++++++++++++++++++++---- src/comm/SerialLink.h | 8 +++ src/comm/SerialLinkInterface.h | 3 + src/comm/UDPLink.cc | 2 +- src/uas/UAS.cc | 12 ++-- src/uas/UASWaypointManager.cc | 11 ++-- src/ui/CommConfigurationWindow.cc | 6 +- src/ui/DebugConsole.cc | 86 ++++++++++++++++++++++++++- src/ui/DebugConsole.h | 3 + src/ui/DebugConsole.ui | 97 +++++++++++++++++++++++++------ src/ui/HDDisplay.cc | 1 + src/ui/MAVLinkSettingsWidget.cc | 59 +++++++++++++++++++ src/ui/MAVLinkSettingsWidget.h | 6 ++ src/ui/MAVLinkSettingsWidget.ui | 62 +++++++++++++++----- src/ui/MainWindow.cc | 79 +++++++++++++++++-------- src/ui/MainWindow.h | 6 ++ src/ui/MainWindow.ui | 38 +++++++++++- src/ui/QGCMAVLinkLogPlayer.cc | 113 ++++++++++++++++++++++++++++++++++++ src/ui/QGCMAVLinkLogPlayer.h | 54 +++++++++++++++++ src/ui/QGCMAVLinkLogPlayer.ui | 111 +++++++++++++++++++++++++++++++++++ src/ui/SerialConfigurationWindow.cc | 29 +++++---- src/ui/linechart/LinechartWidget.cc | 36 +++++++----- src/ui/linechart/LinechartWidget.h | 1 + src/ui/uas/UASControlWidget.cc | 2 + src/ui/uas/UASListWidget.cc | 1 + 30 files changed, 881 insertions(+), 129 deletions(-) create mode 100644 src/ui/QGCMAVLinkLogPlayer.cc create mode 100644 src/ui/QGCMAVLinkLogPlayer.h create mode 100644 src/ui/QGCMAVLinkLogPlayer.ui diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro index bcbcd69..81789c5 100644 --- a/qgroundcontrol.pro +++ b/qgroundcontrol.pro @@ -153,7 +153,8 @@ FORMS += src/ui/MainWindow.ui \ src/ui/uas/QGCUnconnectedInfoWidget.ui \ src/ui/designer/QGCToolWidget.ui \ src/ui/designer/QGCParamSlider.ui \ - src/ui/designer/QGCActionButton.ui + src/ui/designer/QGCActionButton.ui \ + src/ui/QGCMAVLinkLogPlayer.ui INCLUDEPATH += src \ src/ui \ @@ -259,7 +260,8 @@ HEADERS += src/MG.h \ src/ui/designer/QGCToolWidget.h \ src/ui/designer/QGCParamSlider.h \ src/ui/designer/QGCActionButton.h \ - src/ui/designer/QGCToolWidgetItem.h + src/ui/designer/QGCToolWidgetItem.h \ + src/ui/QGCMAVLinkLogPlayer.h # Google Earth is only supported on Mac OS and Windows with Visual Studio Compiler macx|win32-msvc2008: { @@ -380,7 +382,8 @@ SOURCES += src/main.cc \ src/ui/designer/QGCToolWidget.cc \ src/ui/designer/QGCParamSlider.cc \ src/ui/designer/QGCActionButton.cc \ - src/ui/designer/QGCToolWidgetItem.cc + src/ui/designer/QGCToolWidgetItem.cc \ + src/ui/QGCMAVLinkLogPlayer.cc macx|win32-msvc2008: { SOURCES += src/ui/map3D/QGCGoogleEarthView.cc diff --git a/src/QGC.h b/src/QGC.h index 44305dd..3b1d2fd 100644 --- a/src/QGC.h +++ b/src/QGC.h @@ -3,6 +3,7 @@ #include <QDateTime> #include <QColor> +#include <QThread> namespace QGC { @@ -20,6 +21,36 @@ namespace QGC const QString APPNAME = "QGROUNDCONTROL"; const QString COMPANYNAME = "OPENMAV"; const int APPLICATIONVERSION = 80; // 0.8.0 + + class SLEEP : public QThread + { + public: + /** + * @brief Set a thread to sleep for seconds + * @param s time in seconds to sleep + **/ + static void sleep(unsigned long s) + { + QThread::sleep(s); + } + /** + * @brief Set a thread to sleep for milliseconds + * @param ms time in milliseconds to sleep + **/ + static void msleep(unsigned long ms) + { + QThread::msleep(ms); + } + /** + * @brief Set a thread to sleep for microseconds + * @param us time in microseconds to sleep + **/ + static void usleep(unsigned long us) + { + QThread::usleep(us); + } + }; + } #define QGC_EVENTLOOP_DEBUG 0 diff --git a/src/comm/MAVLinkProtocol.cc b/src/comm/MAVLinkProtocol.cc index 64fd625..1f68942 100644 --- a/src/comm/MAVLinkProtocol.cc +++ b/src/comm/MAVLinkProtocol.cc @@ -59,7 +59,7 @@ MAVLinkProtocol::MAVLinkProtocol() : heartbeatRate(MAVLINK_HEARTBEAT_DEFAULT_RATE), m_heartbeatsEnabled(false), m_loggingEnabled(false), - m_logfile(NULL), + m_logfile(new QFile(QCoreApplication::applicationDirPath()+"/mavlink.log")), m_enable_version_check(true), versionMismatchIgnore(false) { @@ -99,7 +99,7 @@ void MAVLinkProtocol::run() QString MAVLinkProtocol::getLogfileName() { - return QCoreApplication::applicationDirPath()+"/mavlink.log"; + return m_logfile->fileName(); } /** @@ -391,21 +391,27 @@ void MAVLinkProtocol::enableLogging(bool enabled) { if (enabled && !m_loggingEnabled) { - m_logfile = new QFile(getLogfileName()); + if (m_logfile->isOpen()) m_logfile->close(); m_logfile->open(QIODevice::WriteOnly | QIODevice::Append); } - else + else if (!enabled) { m_logfile->close(); - delete m_logfile; - m_logfile = NULL; } m_loggingEnabled = enabled; + emit loggingChanged(enabled); +} + +void MAVLinkProtocol::setLogfileName(const QString& filename) +{ + m_logfile->close(); + m_logfile->setFileName(filename); } void MAVLinkProtocol::enableVersionCheck(bool enabled) { m_enable_version_check = enabled; + emit versionCheckChanged(enabled); } bool MAVLinkProtocol::heartbeatsEnabled(void) diff --git a/src/comm/MAVLinkProtocol.h b/src/comm/MAVLinkProtocol.h index 76171e2..af1ba18 100644 --- a/src/comm/MAVLinkProtocol.h +++ b/src/comm/MAVLinkProtocol.h @@ -70,8 +70,10 @@ public: bool loggingEnabled(void); /** @brief Get protocol version check state */ bool versionCheckEnabled(void); + /** @brief Get the protocol version */ + int getVersion() { return MAVLINK_VERSION; } /** @brief Get the name of the packet log file */ - static QString getLogfileName(); + QString getLogfileName(); public slots: /** @brief Receive bytes from a communication interface */ @@ -89,6 +91,9 @@ public slots: /** @brief Enable/disable binary packet logging */ void enableLogging(bool enabled); + /** @brief Set log file name */ + void setLogfileName(const QString& filename); + /** @brief Enable / disable version check */ void enableVersionCheck(bool enabled); diff --git a/src/comm/MAVLinkSimulationLink.cc b/src/comm/MAVLinkSimulationLink.cc index a8043d8..4fb6edc 100644 --- a/src/comm/MAVLinkSimulationLink.cc +++ b/src/comm/MAVLinkSimulationLink.cc @@ -97,8 +97,8 @@ MAVLinkSimulationLink::MAVLinkSimulationLink(QString readFile, QString writeFile LinkManager::instance()->add(this); // Open packet log - mavlinkLogFile = new QFile(MAVLinkProtocol::getLogfileName()); - mavlinkLogFile->open(QIODevice::ReadOnly); + mavlinkLogFile = new QFile(); + //mavlinkLogFile->open(QIODevice::ReadOnly); } MAVLinkSimulationLink::~MAVLinkSimulationLink() @@ -141,7 +141,7 @@ void MAVLinkSimulationLink::run() } last = MG::TIME::getGroundTimeNow(); } - MG::SLEEP::msleep(2); + MG::SLEEP::msleep(3); } } diff --git a/src/comm/SerialLink.cc b/src/comm/SerialLink.cc index ee320e7..c59aad2 100644 --- a/src/comm/SerialLink.cc +++ b/src/comm/SerialLink.cc @@ -108,8 +108,9 @@ void SerialLink::loadSettings() setPortName(settings.value("SERIALLINK_COMM_PORT").toString()); setBaudRateType(settings.value("SERIALLINK_COMM_BAUD").toInt()); setParityType(settings.value("SERIALLINK_COMM_PARITY").toInt()); - setStopBitsType(settings.value("SERIALLINK_COMM_STOPBITS").toInt()); - setDataBitsType(settings.value("SERIALLINK_COMM_DATABITS").toInt()); + setStopBits(settings.value("SERIALLINK_COMM_STOPBITS").toInt()); + setDataBits(settings.value("SERIALLINK_COMM_DATABITS").toInt()); + setFlowType(settings.value("SERIALLINK_COMM_FLOW_CONTROL").toInt()); } } @@ -120,8 +121,9 @@ void SerialLink::writeSettings() settings.setValue("SERIALLINK_COMM_PORT", this->porthandle); settings.setValue("SERIALLINK_COMM_BAUD", getBaudRateType()); settings.setValue("SERIALLINK_COMM_PARITY", getParityType()); - settings.setValue("SERIALLINK_COMM_STOPBITS", getStopBitsType()); - settings.setValue("SERIALLINK_COMM_DATABITS", getDataBitsType()); + settings.setValue("SERIALLINK_COMM_STOPBITS", getStopBits()); + settings.setValue("SERIALLINK_COMM_DATABITS", getDataBits()); + settings.setValue("SERIALLINK_COMM_FLOW_CONTROL", getFlowType()); settings.sync(); } @@ -316,10 +318,10 @@ bool SerialLink::hardwareConnect() { emit connected(); emit connected(true); - - writeSettings(); } + writeSettings(); + return connectionUp; } @@ -531,6 +533,48 @@ int SerialLink::getStopBitsType() return stopBits; } +int SerialLink::getDataBits() +{ + int ret; + switch (dataBits) + { + case DATA_5: + ret = 5; + break; + case DATA_6: + ret = 6; + break; + case DATA_7: + ret = 7; + break; + case DATA_8: + ret = 8; + break; + default: + ret = 0; + break; + } + return ret; +} + +int SerialLink::getStopBits() +{ + int ret; + switch (stopBits) + { + case STOP_1: + ret = 1; + break; + case STOP_2: + ret = 2; + break; + default: + ret = 0; + break; + } + return ret; +} + bool SerialLink::setPortName(QString portName) { if(portName.trimmed().length() > 0) @@ -845,8 +889,7 @@ bool SerialLink::setParityType(int parity) } -// FIXME Works not as anticipated by user! -bool SerialLink::setDataBitsType(int dataBits) +bool SerialLink::setDataBits(int dataBits) { bool accepted = true; @@ -879,12 +922,12 @@ bool SerialLink::setDataBitsType(int dataBits) return accepted; } -// FIXME WORKS NOT AS ANTICIPATED BY USER! -bool SerialLink::setStopBitsType(int stopBits) +bool SerialLink::setStopBits(int stopBits) { bool reconnect = false; bool accepted = true; - if(isConnected()) { + if(isConnected()) + { disconnect(); reconnect = true; } @@ -907,3 +950,51 @@ bool SerialLink::setStopBitsType(int stopBits) if(reconnect) connect(); return accepted; } + +bool SerialLink::setDataBitsType(int dataBits) +{ + bool reconnect = false; + bool accepted = false; + + if (isConnected()) + { + disconnect(); + reconnect = true; + } + + if (dataBits >= (int)DATA_5 && dataBits <= (int)DATA_8) + { + DataBitsType newBits = (DataBitsType) dataBits; + + port->setDataBits(newBits); + if(reconnect) + { + connect(); + } + accepted = true; + } + + return accepted; +} + +bool SerialLink::setStopBitsType(int stopBits) +{ + bool reconnect = false; + bool accepted = false; + if(isConnected()) + { + disconnect(); + reconnect = true; + } + + if (stopBits >= (int)STOP_1 && dataBits <= (int)STOP_2) + { + StopBitsType newBits = (StopBitsType) stopBits; + + port->setStopBits(newBits); + accepted = true; + } + + if(reconnect) connect(); + return accepted; +} diff --git a/src/comm/SerialLink.h b/src/comm/SerialLink.h index 3bac202..b352ad9 100644 --- a/src/comm/SerialLink.h +++ b/src/comm/SerialLink.h @@ -74,6 +74,10 @@ public: */ QString getName(); int getBaudRate(); + int getDataBits(); + int getStopBits(); + + // ENUM values int getBaudRateType(); int getFlowType(); int getParityType(); @@ -103,6 +107,10 @@ public: public slots: bool setPortName(QString portName); bool setBaudRate(int rate); + bool setDataBits(int dataBits); + bool setStopBits(int stopBits); + + // Set ENUM values bool setBaudRateType(int rateIndex); bool setFlowType(int flow); bool setParityType(int parity); diff --git a/src/comm/SerialLinkInterface.h b/src/comm/SerialLinkInterface.h index 49b5335..ff16a66 100644 --- a/src/comm/SerialLinkInterface.h +++ b/src/comm/SerialLinkInterface.h @@ -43,6 +43,9 @@ class SerialLinkInterface : public LinkInterface { public: virtual QString getPortName() = 0; virtual int getBaudRate() = 0; + virtual int getDataBits() = 0; + virtual int getStopBits() = 0; + virtual int getBaudRateType() = 0; virtual int getFlowType() = 0; virtual int getParityType() = 0; diff --git a/src/comm/UDPLink.cc b/src/comm/UDPLink.cc index 0574c1e..5fcc271 100644 --- a/src/comm/UDPLink.cc +++ b/src/comm/UDPLink.cc @@ -49,7 +49,7 @@ UDPLink::UDPLink(QHostAddress host, quint16 port) // Set unique ID and add link to the list of links this->id = getNextLinkId(); - this->name = tr("UDP link ") + QString::number(getId()); + this->name = tr("UDP Link (port:%1)").arg(14550); LinkManager::instance()->add(this); } diff --git a/src/uas/UAS.cc b/src/uas/UAS.cc index 6887ead..7640a07 100644 --- a/src/uas/UAS.cc +++ b/src/uas/UAS.cc @@ -663,12 +663,12 @@ void UAS::receiveMessage(LinkInterface* link, mavlink_message_t message) mavlink_nav_filter_bias_t bias; mavlink_msg_nav_filter_bias_decode(&message, &bias); quint64 time = MG::TIME::getGroundTimeNow(); - emit valueChanged(uasId, "b_f[0]", bias.accel_0, time); - emit valueChanged(uasId, "b_f[1]", bias.accel_1, time); - emit valueChanged(uasId, "b_f[2]", bias.accel_2, time); - emit valueChanged(uasId, "b_w[0]", bias.gyro_0, time); - emit valueChanged(uasId, "b_w[1]", bias.gyro_1, time); - emit valueChanged(uasId, "b_w[2]", bias.gyro_2, time); + emit valueChanged(uasId, "b_f[0]", "raw", bias.accel_0, time); + emit valueChanged(uasId, "b_f[1]", "raw", bias.accel_1, time); + emit valueChanged(uasId, "b_f[2]", "raw", bias.accel_2, time); + emit valueChanged(uasId, "b_w[0]", "raw", bias.gyro_0, time); + emit valueChanged(uasId, "b_w[1]", "raw", bias.gyro_1, time); + emit valueChanged(uasId, "b_w[2]", "raw", bias.gyro_2, time); } break; case MAVLINK_MSG_ID_RADIO_CALIBRATION: diff --git a/src/uas/UASWaypointManager.cc b/src/uas/UASWaypointManager.cc index 0b03967..0c3581e 100644 --- a/src/uas/UASWaypointManager.cc +++ b/src/uas/UASWaypointManager.cc @@ -55,6 +55,7 @@ void UASWaypointManager::timeout() { protocol_timer.start(PROTOCOL_TIMEOUT_MS); current_retries--; + emit updateStatusString(tr("Timeout, retrying (retries left: %1)").arg(current_retries)); qDebug() << "Timeout, retrying (retries left:" << current_retries << ")"; if (current_state == WP_GETLIST) { @@ -517,7 +518,7 @@ void UASWaypointManager::sendWaypointClearAll() wpca.target_system = uas.getUASID(); wpca.target_component = MAV_COMP_ID_WAYPOINTPLANNER; - emit updateStatusString(QString("clearing waypoint list...")); + emit updateStatusString(QString("Clearing waypoint list...")); mavlink_msg_waypoint_clear_all_encode(uas.mavlink->getSystemId(), uas.mavlink->getComponentId(), &message, &wpca); uas.sendMessage(message); @@ -554,7 +555,7 @@ void UASWaypointManager::sendWaypointCount() wpc.count = current_count; qDebug() << "sent waypoint count (" << wpc.count << ") to ID " << wpc.target_system; - emit updateStatusString(QString("start transmitting waypoints...")); + emit updateStatusString(QString("Starting to transmit waypoints...")); mavlink_msg_waypoint_count_encode(uas.mavlink->getSystemId(), uas.mavlink->getComponentId(), &message, &wpc); uas.sendMessage(message); @@ -571,7 +572,7 @@ void UASWaypointManager::sendWaypointRequestList() wprl.target_system = uas.getUASID(); wprl.target_component = MAV_COMP_ID_WAYPOINTPLANNER; - emit updateStatusString(QString("requesting waypoint list...")); + emit updateStatusString(QString("Requesting waypoint list...")); mavlink_msg_waypoint_request_list_encode(uas.mavlink->getSystemId(), uas.mavlink->getComponentId(), &message, &wprl); uas.sendMessage(message); @@ -591,7 +592,7 @@ void UASWaypointManager::sendWaypointRequest(quint16 seq) wpr.target_component = MAV_COMP_ID_WAYPOINTPLANNER; wpr.seq = seq; - emit updateStatusString(QString("retrieving waypoint ID %1 of %2 total").arg(wpr.seq).arg(current_count)); + emit updateStatusString(QString("Retrieving waypoint ID %1 of %2 total").arg(wpr.seq).arg(current_count)); mavlink_msg_waypoint_request_encode(uas.mavlink->getSystemId(), uas.mavlink->getComponentId(), &message, &wpr); uas.sendMessage(message); @@ -612,7 +613,7 @@ void UASWaypointManager::sendWaypoint(quint16 seq) wp->target_system = uas.getUASID(); wp->target_component = MAV_COMP_ID_WAYPOINTPLANNER; - emit updateStatusString(QString("sending waypoint ID %1 of %2 total").arg(wp->seq).arg(current_count)); + emit updateStatusString(QString("Sending waypoint ID %1 of %2 total").arg(wp->seq).arg(current_count)); mavlink_msg_waypoint_encode(uas.mavlink->getSystemId(), uas.mavlink->getComponentId(), &message, wp); uas.sendMessage(message); diff --git a/src/ui/CommConfigurationWindow.cc b/src/ui/CommConfigurationWindow.cc index 33e57ba..484290d 100644 --- a/src/ui/CommConfigurationWindow.cc +++ b/src/ui/CommConfigurationWindow.cc @@ -106,7 +106,7 @@ CommConfigurationWindow::CommConfigurationWindow(LinkInterface* link, ProtocolIn QBoxLayout* layout = new QBoxLayout(QBoxLayout::LeftToRight, ui.linkGroupBox); layout->addWidget(conf); ui.linkGroupBox->setLayout(layout); - ui.linkGroupBox->setTitle(tr("serial link")); + ui.linkGroupBox->setTitle(tr("Serial Link")); //ui.linkGroupBox->setTitle(link->getName()); //connect(link, SIGNAL(nameChanged(QString)), ui.linkGroupBox, SLOT(setTitle(QString))); } @@ -142,13 +142,13 @@ CommConfigurationWindow::CommConfigurationWindow(LinkInterface* link, ProtocolIn // Open details pane for MAVLink if necessary MAVLinkProtocol* mavlink = dynamic_cast<MAVLinkProtocol*>(protocol); - if(mavlink != 0) + if (mavlink != 0) { QWidget* conf = new MAVLinkSettingsWidget(mavlink, this); QBoxLayout* layout = new QBoxLayout(QBoxLayout::LeftToRight, ui.protocolGroupBox); layout->addWidget(conf); ui.protocolGroupBox->setLayout(layout); - ui.protocolGroupBox->setTitle(protocol->getName()); + ui.protocolGroupBox->setTitle(protocol->getName()+" (Global Settings)"); } else { diff --git a/src/ui/DebugConsole.cc b/src/ui/DebugConsole.cc index f5af15c..a808812 100644 --- a/src/ui/DebugConsole.cc +++ b/src/ui/DebugConsole.cc @@ -105,6 +105,8 @@ DebugConsole::DebugConsole(QWidget *parent) : connect(m_ui->holdButton, SIGNAL(toggled(bool)), this, SLOT(hold(bool))); // Connect connect button connect(m_ui->connectButton, SIGNAL(clicked()), this, SLOT(handleConnectButton())); + // Connect the special chars combo box + connect(m_ui->specialComboBox, SIGNAL(activated(QString)), this, SLOT(appendSpecialSymbol(QString))); this->setVisible(false); } @@ -327,8 +329,78 @@ void DebugConsole::receiveBytes(LinkInterface* link, QByteArray bytes) } } +QByteArray DebugConsole::symbolNameToBytes(const QString& text) +{ + QByteArray b; + if (text == "LF") + { + b.append(static_cast<char>(0x0A)); + } + else if (text == "FF") + { + b.append(static_cast<char>(0x0C)); + } + else if (text == "CR") + { + b.append(static_cast<char>(0x0D)); + } + else if (text == "CR+LF") + { + b.append(static_cast<char>(0x0D)); + b.append(static_cast<char>(0x0A)); + } + else if (text == "TAB") + { + b.append(static_cast<char>(0x09)); + } + else if (text == "NUL") + { + b.append(static_cast<char>(0x00)); + } + else if (text == "ESC") + { + b.append(static_cast<char>(0x1B)); + } + else if (text == "~") + { + b.append(static_cast<char>(0x7E)); + } + else if (text == "<Space>") + { + b.append(static_cast<char>(0x20)); + } + return b; +} + +void DebugConsole::appendSpecialSymbol(const QString& text) +{ + QString line = m_ui->sendText->text(); + QByteArray symbols = symbolNameToBytes(text); + // The text is appended to the enter field + if (convertToAscii) + { + line.append(symbols); + } + else + { + + for (int i = 0; i < symbols.size(); i++) + { + QString add(" 0x%1"); + line.append(add.arg(static_cast<char>(symbols.at(i)), 2, 16, QChar('0'))); + } + } + m_ui->sendText->setText(line); +} + void DebugConsole::sendBytes() { + if (!currLink->isConnected()) + { + m_ui->sentText->setText(tr("Nothing sent. The link %1 is unconnected. Please connect first.").arg(currLink->getName())); + return; + } + QByteArray transmit; QString feedback; bool ok = true; @@ -382,8 +454,16 @@ void DebugConsole::sendBytes() if (ok && m_ui->sendText->text().toLatin1().size() > 0) { // Transmit only if conversion succeeded - currLink->writeBytes(transmit, transmit.size()); - m_ui->sentText->setText(tr("Sent: ") + feedback); +// int transmitted = + currLink->writeBytes(transmit, transmit.size()); +// if (transmit.size() == transmitted) +// { + m_ui->sentText->setText(tr("Sent: ") + feedback); +// } +// else +// { +// m_ui->sentText->setText(tr("Error during sending: Transmitted only %1 bytes instead of %2.").arg(transmitted, transmit.size())); +// } } else if (m_ui->sendText->text().toLatin1().size() > 0) { @@ -403,6 +483,8 @@ void DebugConsole::hexModeEnabled(bool mode) { convertToAscii = !mode; m_ui->receiveText->clear(); + m_ui->sendText->clear(); + m_ui->sentText->clear(); } /** diff --git a/src/ui/DebugConsole.h b/src/ui/DebugConsole.h index e3622ee..be064fe 100644 --- a/src/ui/DebugConsole.h +++ b/src/ui/DebugConsole.h @@ -83,6 +83,8 @@ public slots: void setAutoHold(bool hold); /** @brief Receive plain text message to output to the user */ void receiveTextMessage(int id, int component, int severity, QString text); + /** @brief Append a special symbol */ + void appendSpecialSymbol(const QString& text); protected slots: /** @brief Draw information overlay */ @@ -92,6 +94,7 @@ public slots: protected: void changeEvent(QEvent *e); + QByteArray symbolNameToBytes(const QString& symbol); QList<LinkInterface*> links; LinkInterface* currLink; diff --git a/src/ui/DebugConsole.ui b/src/ui/DebugConsole.ui index 6a32b3e..e22d2f2 100644 --- a/src/ui/DebugConsole.ui +++ b/src/ui/DebugConsole.ui @@ -6,8 +6,8 @@ <rect> <x>0</x> <y>0</y> - <width>463</width> - <height>159</height> + <width>447</width> + <height>181</height> </rect> </property> <property name="windowTitle"> @@ -106,25 +106,75 @@ </property> </widget> </item> - <item row="3" column="0"> - <widget class="QLineEdit" name="sendText"> - <property name="minimumSize"> - <size> - <width>80</width> - <height>0</height> - </size> - </property> - <property name="toolTip"> - <string>Type the bytes to send here, use 0xAA format for HEX (Check HEX checkbox above)</string> - </property> - </widget> - </item> - <item row="3" column="1"> - <layout class="QHBoxLayout" name="horizontalLayout" stretch="10,10,10,0"> + <item row="4" column="1"> + <layout class="QHBoxLayout" name="horizontalLayout" stretch="0,10,10,10,0"> <property name="spacing"> <number>5</number> </property> <item> + <widget class="QComboBox" name="specialComboBox"> + <property name="maxVisibleItems"> + <number>10</number> + </property> + <property name="sizeAdjustPolicy"> + <enum>QComboBox::AdjustToContentsOnFirstShow</enum> + </property> + <property name="minimumContentsLength"> + <number>1</number> + </property> + <item> + <property name="text"> + <string>Add..</string> + </property> + </item> + <item> + <property name="text"> + <string>CR+LF</string> + </property> + </item> + <item> + <property name="text"> + <string>LF</string> + </property> + </item> + <item> + <property name="text"> + <string>FF</string> + </property> + </item> + <item> + <property name="text"> + <string>CR</string> + </property> + </item> + <item> + <property name="text"> + <string>TAB</string> + </property> + </item> + <item> + <property name="text"> + <string>NUL</string> + </property> + </item> + <item> + <property name="text"> + <string>ESC</string> + </property> + </item> + <item> + <property name="text"> + <string>~</string> + </property> + </item> + <item> + <property name="text"> + <string><Space></string> + </property> + </item> + </widget> + </item> + <item> <widget class="QPushButton" name="transmitButton"> <property name="toolTip"> <string>Send the ASCII text or HEX values over the link</string> @@ -164,6 +214,19 @@ </item> </layout> </item> + <item row="3" column="0" colspan="2"> + <widget class="QLineEdit" name="sendText"> + <property name="minimumSize"> + <size> + <width>80</width> + <height>0</height> + </size> + </property> + <property name="toolTip"> + <string>Type the bytes to send here, use 0xAA format for HEX (Check HEX checkbox above)</string> + </property> + </widget> + </item> </layout> </widget> <resources> diff --git a/src/ui/HDDisplay.cc b/src/ui/HDDisplay.cc index dc17eee..8705466 100644 --- a/src/ui/HDDisplay.cc +++ b/src/ui/HDDisplay.cc @@ -812,6 +812,7 @@ float HDDisplay::refLineWidthToPen(float line) void HDDisplay::updateValue(const int uasId, const QString& name, const QString& unit, const double value, const quint64 msec) { Q_UNUSED(uasId); + Q_UNUSED(unit); // Update mean const float oldMean = valuesMean.value(name, 0.0f); const int meanCount = valuesCount.value(name, 0); diff --git a/src/ui/MAVLinkSettingsWidget.cc b/src/ui/MAVLinkSettingsWidget.cc index 054ff10..7cb49ec 100644 --- a/src/ui/MAVLinkSettingsWidget.cc +++ b/src/ui/MAVLinkSettingsWidget.cc @@ -27,6 +27,11 @@ This file is part of the QGROUNDCONTROL project * @author Lorenz Meier <mail@qgroundcontrol.org> */ +#include <QFileInfo> +#include <QFileDialog> +#include <QMessageBox> +#include <QDesktopServices> + #include "MAVLinkSettingsWidget.h" #include "ui_MAVLinkSettingsWidget.h" @@ -37,6 +42,8 @@ MAVLinkSettingsWidget::MAVLinkSettingsWidget(MAVLinkProtocol* protocol, QWidget { m_ui->setupUi(this); + m_ui->gridLayout->setAlignment(Qt::AlignTop); + // Initialize state m_ui->heartbeatCheckBox->setChecked(protocol->heartbeatsEnabled()); m_ui->loggingCheckBox->setChecked(protocol->loggingEnabled()); @@ -49,7 +56,59 @@ MAVLinkSettingsWidget::MAVLinkSettingsWidget(MAVLinkProtocol* protocol, QWidget connect(m_ui->loggingCheckBox, SIGNAL(toggled(bool)), protocol, SLOT(enableLogging(bool))); connect(protocol, SIGNAL(versionCheckChanged(bool)), m_ui->versionCheckBox, SLOT(setChecked(bool))); connect(m_ui->versionCheckBox, SIGNAL(toggled(bool)), protocol, SLOT(enableVersionCheck(bool))); + connect(m_ui->logFileButton, SIGNAL(clicked()), this, SLOT(chooseLogfileName())); + + // Update values + m_ui->versionLabel->setText(tr("MAVLINK_VERSION: %1").arg(protocol->getVersion())); + updateLogfileName(protocol->getLogfileName()); + + // Connect visibility updates + connect(protocol, SIGNAL(versionCheckChanged(bool)), m_ui->versionLabel, SLOT(setVisible(bool))); + m_ui->versionLabel->setVisible(protocol->versionCheckEnabled()); + //connect(m_ui->versionCheckBox, SIGNAL(toggled(bool)), m_ui->versionSpacer, SLOT(setVisible(bool))); + //connect(m_ui->loggingCheckBox, SIGNAL(toggled(bool)), m_ui->logFileSpacer, SLOT(setVisible(bool))); + connect(protocol, SIGNAL(loggingChanged(bool)), m_ui->logFileLabel, SLOT(setVisible(bool))); + m_ui->logFileLabel->setVisible(protocol->loggingEnabled()); + connect(protocol, SIGNAL(loggingChanged(bool)), m_ui->logFileButton, SLOT(setVisible(bool))); + m_ui->logFileButton->setVisible(protocol->loggingEnabled()); + + // Update settings + m_ui->loggingCheckBox->setChecked(protocol->loggingEnabled()); + m_ui->heartbeatCheckBox->setChecked(protocol->heartbeatsEnabled()); + m_ui->versionCheckBox->setChecked(protocol->versionCheckEnabled()); +} +void MAVLinkSettingsWidget::updateLogfileName(const QString& fileName) +{ + QFileInfo file(fileName); + m_ui->logFileLabel->setText(file.fileName()); +} + +void MAVLinkSettingsWidget::chooseLogfileName() +{ + QString fileName = QFileDialog::getSaveFileName(this, tr("Specify MAVLink log file name"), QDesktopServices::storageLocation(QDesktopServices::DesktopLocation), tr("MAVLink Logfile (*.mavlink);;")); + + if (!fileName.endsWith(".mavlink")) + { + fileName.append(".mavlink"); + } + + QFileInfo file(fileName); + if (file.exists() && !file.isWritable()) + { + QMessageBox msgBox; + msgBox.setIcon(QMessageBox::Critical); + msgBox.setText(tr("The selected logfile is not writable")); + msgBox.setInformativeText(tr("Please make sure that the file %1 is writable or select a different file").arg(fileName)); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.setDefaultButton(QMessageBox::Ok); + msgBox.exec(); + } + else + { + updateLogfileName(fileName); + protocol->setLogfileName(fileName); + } } MAVLinkSettingsWidget::~MAVLinkSettingsWidget() diff --git a/src/ui/MAVLinkSettingsWidget.h b/src/ui/MAVLinkSettingsWidget.h index 0941b2b..c6f28c7 100644 --- a/src/ui/MAVLinkSettingsWidget.h +++ b/src/ui/MAVLinkSettingsWidget.h @@ -15,6 +15,12 @@ public: MAVLinkSettingsWidget(MAVLinkProtocol* protocol, QWidget *parent = 0); ~MAVLinkSettingsWidget(); +public slots: + /** @brief Update the log file name display */ + void updateLogfileName(const QString& fileName); + /** @brief Start the file select dialog for the log file */ + void chooseLogfileName(); + protected: MAVLinkProtocol* protocol; void changeEvent(QEvent *e); diff --git a/src/ui/MAVLinkSettingsWidget.ui b/src/ui/MAVLinkSettingsWidget.ui index c24aadd..5f96e68 100644 --- a/src/ui/MAVLinkSettingsWidget.ui +++ b/src/ui/MAVLinkSettingsWidget.ui @@ -6,51 +6,85 @@ <rect> <x>0</x> <y>0</y> - <width>267</width> - <height>123</height> + <width>361</width> + <height>145</height> </rect> </property> <property name="windowTitle"> <string>Form</string> </property> - <layout class="QVBoxLayout" name="verticalLayout"> - <property name="margin"> - <number>6</number> - </property> - <item> + <layout class="QGridLayout" name="gridLayout" columnstretch="1,100,1"> + <item row="0" column="0" colspan="3"> <widget class="QCheckBox" name="heartbeatCheckBox"> <property name="text"> <string>Emit heartbeat</string> </property> </widget> </item> - <item> + <item row="1" column="0" colspan="3"> <widget class="QCheckBox" name="loggingCheckBox"> <property name="text"> <string>Log all MAVLink packets</string> </property> </widget> </item> - <item> + <item row="3" column="0"> + <spacer name="logFileSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::MinimumExpanding</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>8</width> + <height>0</height> + </size> + </property> + </spacer> + </item> + <item row="4" column="0" colspan="3"> <widget class="QCheckBox" name="versionCheckBox"> <property name="text"> <string>Only accept MAVs with same protocol version</string> </property> </widget> </item> - <item> - <spacer name="verticalSpacer"> + <item row="5" column="0"> + <spacer name="versionSpacer"> <property name="orientation"> - <enum>Qt::Vertical</enum> + <enum>Qt::Horizontal</enum> </property> <property name="sizeHint" stdset="0"> <size> - <width>20</width> - <height>84</height> + <width>8</width> + <height>0</height> </size> </property> </spacer> </item> + <item row="5" column="1" colspan="2"> + <widget class="QLabel" name="versionLabel"> + <property name="text"> + <string>MAVLINK_VERSION: </string> + </property> + </widget> + </item> + <item row="3" column="1"> + <widget class="QLabel" name="logFileLabel"> + <property name="text"> + <string>Logfile name</string> + </property> + </widget> + </item> + <item row="3" column="2"> + <widget class="QPushButton" name="logFileButton"> + <property name="text"> + <string>Select..</string> + </property> + </widget> + </item> </layout> </widget> <resources/> diff --git a/src/ui/MainWindow.cc b/src/ui/MainWindow.cc index 58df251..874da1a 100644 --- a/src/ui/MainWindow.cc +++ b/src/ui/MainWindow.cc @@ -27,6 +27,7 @@ #include "JoystickWidget.h" #include "GAudioOutput.h" #include "QGCToolWidget.h" +#include "QGCMAVLinkLogPlayer.h" #ifdef QGC_OSG_ENABLED #include "Q3DWidgetFactory.h" @@ -65,6 +66,7 @@ MainWindow::MainWindow(QWidget *parent): toolsMenuActions(), currentView(VIEW_ENGINEER), aboutToCloseFlag(false), + changingViewsFlag(false), settings() { // Get current settings @@ -128,6 +130,8 @@ MainWindow::MainWindow(QWidget *parent): // Setup user interface ui.setupUi(this); + setVisible(false); + // Bind together the perspective actions QActionGroup* perspectives = new QActionGroup(ui.menuPerspectives); perspectives->addAction(ui.actionEngineersView); @@ -145,7 +149,6 @@ MainWindow::MainWindow(QWidget *parent): // The pilot view is not available on startup ui.actionPilotsView->setEnabled(false); - buildCommonWidgets(); connectCommonWidgets(); @@ -154,9 +157,6 @@ MainWindow::MainWindow(QWidget *parent): configureWindowName(); - // Add status bar - //setStatusBar(createStatusBar()); - // Set the application style (not the same as a style sheet) // Set the style to Plastique qApp->setStyle("plastique"); @@ -205,6 +205,8 @@ MainWindow::MainWindow(QWidget *parent): this->addLink(link); } + connect(LinkManager::instance(), SIGNAL(newLink(LinkInterface*)), this, SLOT(addLink(LinkInterface*))); + // Enable and update view presentView(); } @@ -234,6 +236,7 @@ void MainWindow::buildCommonWidgets() if (!controlDockWidget) { controlDockWidget = new QDockWidget(tr("Control"), this); + controlDockWidget->setObjectName("UNMANNED_SYSTEM_CONTROL_DOCKWIDGET"); controlDockWidget->setWidget( new UASControlWidget(this) ); addToToolsMenu (controlDockWidget, tr("Control"), SLOT(showToolWidget()), MENU_UAS_CONTROL, Qt::LeftDockWidgetArea); } @@ -242,6 +245,7 @@ void MainWindow::buildCommonWidgets() { listDockWidget = new QDockWidget(tr("Unmanned Systems"), this); listDockWidget->setWidget( new UASListWidget(this) ); + listDockWidget->setObjectName("UNMANNED_SYSTEMS_LIST_DOCKWIDGET"); addToToolsMenu (listDockWidget, tr("Unmanned Systems"), SLOT(showToolWidget()), MENU_UAS_LIST, Qt::RightDockWidgetArea); } @@ -249,6 +253,7 @@ void MainWindow::buildCommonWidgets() { waypointsDockWidget = new QDockWidget(tr("Waypoint List"), this); waypointsDockWidget->setWidget( new WaypointList(this, NULL) ); + waypointsDockWidget->setObjectName("WAYPOINT_LIST_DOCKWIDGET"); addToToolsMenu (waypointsDockWidget, tr("Waypoints List"), SLOT(showToolWidget()), MENU_WAYPOINTS, Qt::BottomDockWidgetArea); } @@ -263,9 +268,18 @@ void MainWindow::buildCommonWidgets() { debugConsoleDockWidget = new QDockWidget(tr("Communication Console"), this); debugConsoleDockWidget->setWidget( new DebugConsole(this) ); + debugConsoleDockWidget->setObjectName("COMMUNICATION_DEBUG_CONSOLE_DOCKWIDGET"); addToToolsMenu (debugConsoleDockWidget, tr("Communication Console"), SLOT(showToolWidget()), MENU_DEBUG_CONSOLE, Qt::BottomDockWidgetArea); } + if (!logPlayerDockWidget) + { + logPlayerDockWidget = new QDockWidget(tr("MAVLink Log Player"), this); + logPlayerDockWidget->setWidget( new QGCMAVLinkLogPlayer(mavlink, this) ); + logPlayerDockWidget->setObjectName("MAVLINK_LOG_PLAYER_DOCKWIDGET"); + addToToolsMenu(logPlayerDockWidget, tr("MAVLink Log Replay"), SLOT(showToolWidget()), MENU_MAVLINK_LOG_PLAYER, Qt::RightDockWidgetArea); + } + // Center widgets if (!mapWidget) { @@ -376,6 +390,7 @@ void MainWindow::buildPxWidgets() { hsiDockWidget = new QDockWidget(tr("Horizontal Situation Indicator"), this); hsiDockWidget->setWidget( new HSIDisplay(this) ); + hsiDockWidget->setObjectName("HORIZONTAL_SITUATION_INDICATOR_DOCK_WIDGET"); addToToolsMenu (hsiDockWidget, tr("HSI"), SLOT(showToolWidget()), MENU_HSI, Qt::BottomDockWidgetArea); } @@ -383,6 +398,7 @@ void MainWindow::buildPxWidgets() { headDown1DockWidget = new QDockWidget(tr("Flight Display"), this); headDown1DockWidget->setWidget( new HDDisplay(acceptList, "Flight Display", this) ); + headDown1DockWidget->setObjectName("HEAD_DOWN_DISPLAY_1_DOCK_WIDGET"); addToToolsMenu (headDown1DockWidget, tr("Flight Display"), SLOT(showToolWidget()), MENU_HDD_1, Qt::RightDockWidgetArea); } @@ -390,6 +406,7 @@ void MainWindow::buildPxWidgets() { headDown2DockWidget = new QDockWidget(tr("Payload Status"), this); headDown2DockWidget->setWidget( new HDDisplay(acceptList2, "Payload Status", this) ); + headDown2DockWidget->setObjectName("HEAD_DOWN_DISPLAY_2_DOCK_WIDGET"); addToToolsMenu (headDown2DockWidget, tr("Payload Status"), SLOT(showToolWidget()), MENU_HDD_2, Qt::RightDockWidgetArea); } @@ -397,6 +414,7 @@ void MainWindow::buildPxWidgets() { rcViewDockWidget = new QDockWidget(tr("Radio Control"), this); rcViewDockWidget->setWidget( new QGCRemoteControlView(this) ); + rcViewDockWidget->setObjectName("RADIO_CONTROL_CHANNELS_DOCK_WIDGET"); addToToolsMenu (rcViewDockWidget, tr("Radio Control"), SLOT(showToolWidget()), MENU_RC_VIEW, Qt::BottomDockWidgetArea); } @@ -404,6 +422,7 @@ void MainWindow::buildPxWidgets() { headUpDockWidget = new QDockWidget(tr("HUD"), this); headUpDockWidget->setWidget( new HUD(320, 240, this)); + headUpDockWidget->setObjectName("HEAD_UP_DISPLAY_DOCK_WIDGET"); addToToolsMenu (headUpDockWidget, tr("Control Indicator"), SLOT(showToolWidget()), MENU_HUD, Qt::LeftDockWidgetArea); } @@ -525,10 +544,12 @@ void MainWindow::showCentralWidget() // uncheck all central widget actions QHashIterator<int, QAction*> i(toolsMenuActions); - while (i.hasNext()) { + while (i.hasNext()) + { i.next(); - qDebug() << "shCW" << i.key() << "read"; - if (i.value() && i.value()->data().toInt() > 255){ + //qDebug() << "shCW" << i.key() << "read"; + if (i.value() && i.value()->data().toInt() > 255) + { i.value()->setChecked(false); // update the settings @@ -538,7 +559,7 @@ void MainWindow::showCentralWidget() } // check the current action - qDebug() << senderAction->text(); + //qDebug() << senderAction->text(); senderAction->setChecked(true); // update the central widget @@ -547,7 +568,6 @@ void MainWindow::showCentralWidget() // store the selected central widget chKey = buildMenuKey (SUB_SECTION_CHECKED,static_cast<TOOLS_WIDGET_NAMES>(tool), currentView); settings.setValue(chKey,true); - settings.sync(); presentView(); } @@ -584,7 +604,7 @@ void MainWindow::addToToolsMenu ( QWidget* widget, // populate the Hashes toolsMenuActions[tool] = tempAction; dockWidgets[tool] = widget; - qDebug() << widget; + //qDebug() << widget; posKey = buildMenuKey (SUB_SECTION_LOCATION,tool, currentView); @@ -649,7 +669,7 @@ void MainWindow::showTheWidget (TOOLS_WIDGET_NAMES widget, VIEW_SECTIONS view) Qt::DockWidgetArea tempLocation; QDockWidget* tempWidget = static_cast <QDockWidget *>(dockWidgets[widget]); - tempVisible = settings.value(buildMenuKey (SUB_SECTION_CHECKED,widget,view), false).toBool(); + tempVisible = settings.value(buildMenuKey(SUB_SECTION_CHECKED,widget,view), false).toBool(); if (tempWidget) { @@ -708,16 +728,11 @@ void MainWindow::closeEvent(QCloseEvent *event) QMainWindow::closeEvent(event); } -/** - * Stores the visibility setting of each widget. This method - * will only change the settings if the application is not - * about to close. - */ + void MainWindow::updateVisibilitySettings (bool vis) { - if (!aboutToCloseFlag) + if (!aboutToCloseFlag && !changingViewsFlag) { - QDockWidget* temp = qobject_cast<QDockWidget *>(sender()); if (temp) @@ -730,7 +745,6 @@ void MainWindow::updateVisibilitySettings (bool vis) { QString chKey = buildMenuKey (SUB_SECTION_CHECKED,static_cast<TOOLS_WIDGET_NAMES>(i.key()), currentView); settings.setValue(chKey,vis); - settings.sync(); toolsMenuActions[i.key()]->setChecked(vis); break; } @@ -781,7 +795,7 @@ void MainWindow::connectCommonWidgets() void MainWindow::createCustomWidget() { - qDebug() << "ADDING CUSTOM WIDGET"; + //qDebug() << "ADDING CUSTOM WIDGET"; QGCToolWidget* tool = new QGCToolWidget(this); QDockWidget* dock = new QDockWidget("Unnamed Tool", this); dock->setWidget(tool); @@ -1028,6 +1042,7 @@ void MainWindow::connectCommonActions() // Connect internal actions connect(UASManager::instance(), SIGNAL(UASCreated(UASInterface*)), this, SLOT(UASCreated(UASInterface*))); + connect(UASManager::instance(), SIGNAL(activeUASSet(UASInterface*)), this, SLOT(setActiveUAS(UASInterface*))); // Unmanned System controls connect(ui.actionLiftoff, SIGNAL(triggered()), UASManager::instance(), SLOT(launchActiveUAS())); @@ -1132,6 +1147,8 @@ void MainWindow::addLink(LinkInterface *link) CommConfigurationWindow* commWidget = new CommConfigurationWindow(link, mavlink, this); ui.menuNetwork->addAction(commWidget->getAction()); + //qDebug() << "ADDING LINK:" << link->getName() << "ACTION IS: " << commWidget->getAction(); + // Special case for simulationlink MAVLinkSimulationLink* sim = dynamic_cast<MAVLinkSimulationLink*>(link); if (sim) @@ -1141,6 +1158,13 @@ void MainWindow::addLink(LinkInterface *link) } } +void MainWindow::setActiveUAS(UASInterface* uas) +{ + // Enable and rename menu + ui.menuUnmanned_System->setTitle(uas->getUASName()); + if (!ui.menuUnmanned_System->isEnabled()) ui.menuUnmanned_System->setEnabled(true); +} + void MainWindow::UASCreated(UASInterface* uas) { @@ -1269,7 +1293,7 @@ void MainWindow::UASCreated(UASInterface* uas) // the currently active UAS if (UASManager::instance()->getActiveUAS() == uas) { - qDebug() << "UPDATING THE VIEW SINCE THIS IS THE FIRST CONNECTED SYSTEM"; + //qDebug() << "UPDATING THE VIEW SINCE THIS IS THE FIRST CONNECTED SYSTEM"; // Load last view if setting is present if (settings.contains("CURRENT_VIEW_WITH_UAS_CONNECTED")) @@ -1292,6 +1316,8 @@ void MainWindow::UASCreated(UASInterface* uas) } } + + if (!ui.menuConnected_Systems->isEnabled()) ui.menuConnected_Systems->setEnabled(true); } /** @@ -1313,16 +1339,17 @@ void MainWindow::clearView() if (temp) { + //qDebug() << "TOOL:" << chKey << "IS:" << temp->isChecked(); settings.setValue(chKey,temp->isChecked()); } else { + //qDebug() << "TOOL:" << chKey << "IS DEFAULT AND UNCHECKED"; settings.setValue(chKey,false); } } - settings.sync(); - + changingViewsFlag = true; // Remove all dock widgets from main window QObjectList childList( this->children() ); @@ -1341,6 +1368,7 @@ void MainWindow::clearView() // Is there a way to unset a widget from QDockWidget? } } + changingViewsFlag = false; } void MainWindow::loadEngineerView() @@ -1490,6 +1518,9 @@ void MainWindow::presentView() // HEAD DOWN 2 showTheWidget(MENU_HDD_2, currentView); + // MAVLINK LOG PLAYER + showTheWidget(MENU_MAVLINK_LOG_PLAYER, currentView); + this->show(); } @@ -1500,7 +1531,7 @@ void MainWindow::showTheCentralWidget (TOOLS_WIDGET_NAMES centralWidget, VIEW_SE QWidget* tempWidget = dockWidgets[centralWidget]; tempVisible = settings.value(buildMenuKey (SUB_SECTION_CHECKED,centralWidget,view), false).toBool(); - qDebug() << buildMenuKey (SUB_SECTION_CHECKED,centralWidget,view) << tempVisible; + //qDebug() << buildMenuKey (SUB_SECTION_CHECKED,centralWidget,view) << tempVisible; if (toolsMenuActions[centralWidget]) { toolsMenuActions[centralWidget]->setChecked(tempVisible); diff --git a/src/ui/MainWindow.h b/src/ui/MainWindow.h index 43a0230..6241646 100644 --- a/src/ui/MainWindow.h +++ b/src/ui/MainWindow.h @@ -104,6 +104,9 @@ public slots: void addLink(); void addLink(LinkInterface* link); void configure(); + /** @brief Set the currently controlled UAS */ + void setActiveUAS(UASInterface* uas); + /** @brief Add a new UAS */ void UASCreated(UASInterface* uas); void startVideoCapture(); void stopVideoCapture(); @@ -208,6 +211,7 @@ protected: MENU_SLUGS_PID, MENU_SLUGS_HIL, MENU_SLUGS_CAMERA, + MENU_MAVLINK_LOG_PLAYER, CENTRAL_SEPARATOR= 255, // do not change CENTRAL_LINECHART, CENTRAL_PROTOCOL, @@ -297,6 +301,7 @@ protected: /** @brief Keeps track of the current view */ VIEW_SECTIONS currentView; bool aboutToCloseFlag; + bool changingViewsFlag; void clearView(); @@ -360,6 +365,7 @@ protected: QPointer<QDockWidget> watchdogControlDockWidget; QPointer<QDockWidget> headUpDockWidget; + QPointer<QDockWidget> logPlayerDockWidget; QPointer<QDockWidget> hsiDockWidget; QPointer<QDockWidget> rcViewDockWidget; diff --git a/src/ui/MainWindow.ui b/src/ui/MainWindow.ui index 2b904d9..e904558 100644 --- a/src/ui/MainWindow.ui +++ b/src/ui/MainWindow.ui @@ -49,23 +49,30 @@ <addaction name="actionNewCustomWidget"/> <addaction name="actionMuteAudioOutput"/> <addaction name="actionSimulate"/> + <addaction name="actionPreferences"/> <addaction name="separator"/> <addaction name="actionReloadStyle"/> <addaction name="actionExit"/> </widget> <widget class="QMenu" name="menuNetwork"> <property name="title"> - <string>Network</string> + <string>Communication</string> </property> <addaction name="actionAdd_Link"/> <addaction name="separator"/> </widget> <widget class="QMenu" name="menuConnected_Systems"> + <property name="enabled"> + <bool>false</bool> + </property> <property name="title"> <string>Select System</string> </property> </widget> <widget class="QMenu" name="menuUnmanned_System"> + <property name="enabled"> + <bool>false</bool> + </property> <property name="title"> <string>Unmanned System</string> </property> @@ -76,8 +83,6 @@ <addaction name="actionLand"/> <addaction name="actionEmergency_Land"/> <addaction name="actionEmergency_Kill"/> - <addaction name="separator"/> - <addaction name="actionConfiguration"/> </widget> <widget class="QMenu" name="menuTools"> <property name="title"> @@ -124,6 +129,9 @@ </property> </action> <action name="actionLiftoff"> + <property name="enabled"> + <bool>true</bool> + </property> <property name="icon"> <iconset resource="../../mavground.qrc"> <normaloff>:/images/control/launch.svg</normaloff> @@ -143,6 +151,10 @@ </property> </action> <action name="actionEmergency_Land"> + <property name="icon"> + <iconset resource="../../mavground.qrc"> + <normaloff>:/images/actions/process-stop.svg</normaloff>:/images/actions/process-stop.svg</iconset> + </property> <property name="text"> <string>Emergency Land</string> </property> @@ -151,6 +163,10 @@ </property> </action> <action name="actionEmergency_Kill"> + <property name="icon"> + <iconset resource="../../mavground.qrc"> + <normaloff>:/images/actions/process-stop.svg</normaloff>:/images/actions/process-stop.svg</iconset> + </property> <property name="text"> <string>Kill UAS</string> </property> @@ -312,6 +328,10 @@ </property> </action> <action name="actionNewCustomWidget"> + <property name="icon"> + <iconset resource="../../mavground.qrc"> + <normaloff>:/images/apps/utilities-system-monitor.svg</normaloff>:/images/apps/utilities-system-monitor.svg</iconset> + </property> <property name="text"> <string>New Custom Widget</string> </property> @@ -332,6 +352,18 @@ <string>Mute Audio Output</string> </property> </action> + <action name="actionPreferences"> + <property name="icon"> + <iconset resource="../../mavground.qrc"> + <normaloff>:/images/categories/preferences-system.svg</normaloff>:/images/categories/preferences-system.svg</iconset> + </property> + <property name="text"> + <string>Preferences</string> + </property> + <property name="toolTip"> + <string>QGroundControl global settings</string> + </property> + </action> </widget> <layoutdefault spacing="6" margin="11"/> <resources> diff --git a/src/ui/QGCMAVLinkLogPlayer.cc b/src/ui/QGCMAVLinkLogPlayer.cc new file mode 100644 index 0000000..ceccd9c --- /dev/null +++ b/src/ui/QGCMAVLinkLogPlayer.cc @@ -0,0 +1,113 @@ +#include <QFileDialog> +#include <QMessageBox> +#include <QDesktopServices> + +#include "QGCMAVLinkLogPlayer.h" +#include "ui_QGCMAVLinkLogPlayer.h" + +QGCMAVLinkLogPlayer::QGCMAVLinkLogPlayer(MAVLinkProtocol* mavlink, QWidget *parent) : + QWidget(parent), + lineCounter(0), + totalLines(0), + startTime(0), + endTime(0), + currentTime(0), + mavlink(mavlink), + ui(new Ui::QGCMAVLinkLogPlayer) +{ + ui->setupUi(this); + ui->gridLayout->setAlignment(Qt::AlignTop); + + // Setup buttons + connect(ui->selectFileButton, SIGNAL(clicked()), this, SLOT(selectLogFile())); + connect(ui->pauseButton, SIGNAL(clicked()), this, SLOT(pause())); + connect(ui->playButton, SIGNAL(clicked()), this, SLOT(play())); + +} + +QGCMAVLinkLogPlayer::~QGCMAVLinkLogPlayer() +{ + delete ui; +} + +void QGCMAVLinkLogPlayer::play() +{ + if (logFile.isOpen()) + { + ui->pauseButton->setChecked(false); + } + else + { + ui->playButton->setChecked(false); + QMessageBox msgBox; + msgBox.setIcon(QMessageBox::Information); + msgBox.setText(tr("No logfile selected")); + msgBox.setInformativeText(tr("Please select first a MAVLink log file before playing it.")); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.setDefaultButton(QMessageBox::Ok); + msgBox.exec(); + } +} + +void QGCMAVLinkLogPlayer::pause() +{ + ui->playButton->setChecked(false); +} + +void QGCMAVLinkLogPlayer::selectLogFile() +{ + QString fileName = QFileDialog::getOpenFileName(this, tr("Specify MAVLink log file name"), QDesktopServices::storageLocation(QDesktopServices::DesktopLocation), tr("MAVLink Logfile (*.mavlink);;")); + + loadLogFile(fileName); +} + +void QGCMAVLinkLogPlayer::loadLogFile(const QString& file) +{ + logFile.setFileName(file); + + if (!logFile.open(QFile::ReadOnly)) + { + QMessageBox msgBox; + msgBox.setIcon(QMessageBox::Critical); + msgBox.setText(tr("The selected logfile is unreadable")); + msgBox.setInformativeText(tr("Please make sure that the file %1 is readable or select a different file").arg(file)); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.setDefaultButton(QMessageBox::Ok); + msgBox.exec(); + logFile.setFileName(""); + } + else + { + ui->logFileNameLabel->setText(tr("%1").arg(file)); + } +} + +/** + * This function is the "mainloop" of the log player, reading one line + * and adjusting the mainloop timer to read the next line in time. + * It might not perfectly match the timing of the log file, + * but it will never induce a static drift into the log file replay. + * For scientific logging, the use of onboard timestamps and the log + * functionality of the line chart plot is recommended. + */ +void QGCMAVLinkLogPlayer::readLine() +{ + + // Ui update: Only every 20 messages + // to prevent flickering and high CPU load + + // Update status label + // Update progress bar +} + +void QGCMAVLinkLogPlayer::changeEvent(QEvent *e) +{ + QWidget::changeEvent(e); + switch (e->type()) { + case QEvent::LanguageChange: + ui->retranslateUi(this); + break; + default: + break; + } +} diff --git a/src/ui/QGCMAVLinkLogPlayer.h b/src/ui/QGCMAVLinkLogPlayer.h new file mode 100644 index 0000000..17454fd --- /dev/null +++ b/src/ui/QGCMAVLinkLogPlayer.h @@ -0,0 +1,54 @@ +#ifndef QGCMAVLINKLOGPLAYER_H +#define QGCMAVLINKLOGPLAYER_H + +#include <QWidget> +#include <QFile> + +#include "MAVLinkProtocol.h" + +namespace Ui { + class QGCMAVLinkLogPlayer; +} + +/** + * @brief Replays MAVLink log files + * + * This class allows to replay MAVLink logs at varying speeds. + * captured flights can be replayed, shown to others and analyzed + * in-depth later on. + */ +class QGCMAVLinkLogPlayer : public QWidget +{ + Q_OBJECT + +public: + explicit QGCMAVLinkLogPlayer(MAVLinkProtocol* mavlink, QWidget *parent = 0); + ~QGCMAVLinkLogPlayer(); + +public slots: + /** @brief Replay the logfile */ + void play(); + /** @brief Pause the logfile */ + void pause(); + /** @brief Select logfile */ + void selectLogFile(); + /** @brief Load log file */ + void loadLogFile(const QString& file); + +protected: + int lineCounter; + int totalLines; + quint64 startTime; + quint64 endTime; + quint64 currentTime; + MAVLinkProtocol* mavlink; + QFile logFile; + QTimer loopTimer; + void changeEvent(QEvent *e); + void readLine(); + +private: + Ui::QGCMAVLinkLogPlayer *ui; +}; + +#endif // QGCMAVLINKLOGPLAYER_H diff --git a/src/ui/QGCMAVLinkLogPlayer.ui b/src/ui/QGCMAVLinkLogPlayer.ui new file mode 100644 index 0000000..d035c04 --- /dev/null +++ b/src/ui/QGCMAVLinkLogPlayer.ui @@ -0,0 +1,111 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>QGCMAVLinkLogPlayer</class> + <widget class="QWidget" name="QGCMAVLinkLogPlayer"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>407</width> + <height>144</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QGridLayout" name="gridLayout" columnstretch="1,10,10,1,1,0"> + <property name="horizontalSpacing"> + <number>12</number> + </property> + <item row="0" column="0" colspan="3"> + <widget class="QLabel" name="logFileNameLabel"> + <property name="text"> + <string>Please choose logfile</string> + </property> + </widget> + </item> + <item row="0" column="3" colspan="3"> + <widget class="QPushButton" name="selectFileButton"> + <property name="text"> + <string>Select File</string> + </property> + </widget> + </item> + <item row="1" column="1" colspan="5"> + <widget class="QSlider" name="speedSlider"> + <property name="minimum"> + <number>1</number> + </property> + <property name="maximum"> + <number>100</number> + </property> + <property name="value"> + <number>50</number> + </property> + <property name="sliderPosition"> + <number>50</number> + </property> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item row="3" column="0" colspan="3"> + <widget class="QLabel" name="logStatsLabel"> + <property name="text"> + <string>No logfile selected..</string> + </property> + </widget> + </item> + <item row="3" column="5"> + <widget class="QToolButton" name="pauseButton"> + <property name="text"> + <string>...</string> + </property> + <property name="icon"> + <iconset resource="../../mavground.qrc"> + <normaloff>:/images/actions/media-playback-pause.svg</normaloff>:/images/actions/media-playback-pause.svg</iconset> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="4" column="0" colspan="6"> + <widget class="QProgressBar" name="progressBar"> + <property name="value"> + <number>0</number> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="speedLabel"> + <property name="text"> + <string>Speed</string> + </property> + </widget> + </item> + <item row="3" column="4"> + <widget class="QToolButton" name="playButton"> + <property name="text"> + <string>...</string> + </property> + <property name="icon"> + <iconset resource="../../mavground.qrc"> + <normaloff>:/images/actions/media-playback-start.svg</normaloff>:/images/actions/media-playback-start.svg</iconset> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </widget> + <resources> + <include location="../../mavground.qrc"/> + </resources> + <connections/> +</ui> diff --git a/src/ui/SerialConfigurationWindow.cc b/src/ui/SerialConfigurationWindow.cc index 11bd3dd..f7305bc 100644 --- a/src/ui/SerialConfigurationWindow.cc +++ b/src/ui/SerialConfigurationWindow.cc @@ -239,12 +239,17 @@ userConfigured(false) // Setup the user interface according to link type ui.setupUi(this); + //this->setVisible(false); + //this->hide(); // Create action to open this menu // Create configuration action for this link // Connect the current UAS action = new QAction(QIcon(":/images/devices/network-wireless.svg"), "", link); setLinkName(link->getName()); + + setupPortList(); + connect(action, SIGNAL(triggered()), this, SLOT(configureCommunication())); // Make sure that a change in the link name will be reflected in the UI @@ -258,10 +263,8 @@ userConfigured(false) connect(ui.parNone, SIGNAL(toggled(bool)), this, SLOT(setParityNone())); connect(ui.parOdd, SIGNAL(toggled(bool)), this, SLOT(setParityOdd())); connect(ui.parEven, SIGNAL(toggled(bool)), this, SLOT(setParityEven())); - connect(ui.dataBitsSpinBox, SIGNAL(valueChanged(int)), this->link, SLOT(setDataBitsType(int))); - connect(ui.stopBitsSpinBox, SIGNAL(valueChanged(int)), this->link, SLOT(setStopBitsType(int))); - - setupPortList(); + connect(ui.dataBitsSpinBox, SIGNAL(valueChanged(int)), this->link, SLOT(setDataBits(int))); + connect(ui.stopBitsSpinBox, SIGNAL(valueChanged(int)), this->link, SLOT(setStopBits(int))); //connect(this->link, SIGNAL(connected(bool)), this, SLOT()); ui.portName->setSizeAdjustPolicy(QComboBox::AdjustToContentsOnFirstShow); @@ -299,8 +302,8 @@ userConfigured(false) ui.baudRate->setCurrentIndex(this->link->getBaudRateType()); - ui.dataBitsSpinBox->setValue(this->link->getDataBitsType() + 5); - ui.stopBitsSpinBox->setValue(this->link->getStopBitsType() + 1); + ui.dataBitsSpinBox->setValue(this->link->getDataBits()); + ui.stopBitsSpinBox->setValue(this->link->getStopBits()); portCheckTimer = new QTimer(this); portCheckTimer->setInterval(1000); @@ -308,7 +311,7 @@ userConfigured(false) // Display the widget this->window()->setWindowTitle(tr("Serial Communication Settings")); - this->show(); + //this->show(); } else { @@ -323,18 +326,14 @@ SerialConfigurationWindow::~SerialConfigurationWindow() { void SerialConfigurationWindow::showEvent(QShowEvent* event) { - if (event->isAccepted()) - { - portCheckTimer->start(); - } + Q_UNUSED(event); + portCheckTimer->start(); } void SerialConfigurationWindow::hideEvent(QHideEvent* event) { - if (event->isAccepted()) - { - portCheckTimer->stop(); - } + Q_UNUSED(event); + portCheckTimer->stop(); } QAction* SerialConfigurationWindow::getAction() diff --git a/src/ui/linechart/LinechartWidget.cc b/src/ui/linechart/LinechartWidget.cc index d04bb0e..ba1b496 100644 --- a/src/ui/linechart/LinechartWidget.cc +++ b/src/ui/linechart/LinechartWidget.cc @@ -119,8 +119,7 @@ updateTimer(new QTimer()) curvesWidgetLayout->addWidget(value, labelRow, 3); // Unit - label->setText("Unit"); - curvesWidgetLayout->addWidget(new QLabel(tr("Unit")), labelRow, 4); + //curvesWidgetLayout->addWidget(new QLabel(tr("Unit")), labelRow, 4); // Mean mean = new QLabel(this); @@ -216,9 +215,8 @@ void LinechartWidget::createLayout() connect(logButton, SIGNAL(clicked()), this, SLOT(startLogging())); // Ground time button - QToolButton* timeButton = new QToolButton(this); + QCheckBox* timeButton = new QCheckBox(this); timeButton->setText(tr("Ground Time")); - timeButton->setCheckable(true); timeButton->setToolTip(tr("Overwrite timestamp of data from vehicle with ground receive time. Helps if the plots are not visible because of missing or invalid onboard time.")); timeButton->setWhatsThis(tr("Overwrite timestamp of data from vehicle with ground receive time. Helps if the plots are not visible because of missing or invalid onboard time.")); bool gTimeDefault = true; @@ -228,6 +226,13 @@ void LinechartWidget::createLayout() layout->setColumnStretch(4, 0); connect(timeButton, SIGNAL(clicked(bool)), activePlot, SLOT(enforceGroundTime(bool))); + unitsCheckBox = new QCheckBox(this); + unitsCheckBox->setText(tr("Show units")); + unitsCheckBox->setChecked(true); + unitsCheckBox->setToolTip(tr("Enable unit display in curve list")); + unitsCheckBox->setWhatsThis(tr("Enable unit display in curve list")); + layout->addWidget(unitsCheckBox, 1, 5); + // Create the scroll bar //scrollbar = new QScrollBar(Qt::Horizontal, ui.diagramGroupBox); //scrollbar->setMinimum(MIN_TIME_SCROLLBAR_VALUE); @@ -241,7 +246,7 @@ void LinechartWidget::createLayout() // Add scroll bar to layout and make sure it gets all available space //layout->addWidget(scrollbar, 1, 5); - layout->setColumnStretch(5, 10); + //layout->setColumnStretch(5, 10); ui.diagramGroupBox->setLayout(layout); @@ -254,8 +259,8 @@ void LinechartWidget::createLayout() // FIXME // Connect notifications from the plot to the user interface - connect(activePlot, SIGNAL(curveAdded(QString)), this, SLOT(addCurve(QString))); - connect(activePlot, SIGNAL(curveRemoved(QString)), this, SLOT(removeCurve(QString))); + //connect(activePlot, SIGNAL(curveAdded(QString)), this, SLOT(addCurve(QString))); + //connect(activePlot, SIGNAL(curveRemoved(QString)), this, SLOT(removeCurve(QString))); // Scrollbar @@ -313,7 +318,7 @@ void LinechartWidget::appendData(int uasId, const QString& curve, const QString& // Make sure the curve will be created if it does not yet exist if(!label) { - qDebug() << "ADDING CURVE IN APPENDDATE DOUBLE"; + //qDebug() << "ADDING CURVE IN APPENDDATE DOUBLE"; addCurve(curve, unit); } } @@ -594,8 +599,8 @@ void LinechartWidget::addCurve(const QString& curve, const QString& unit) value = new QLabel(this); value->setNum(0.00); value->setStyleSheet(QString("QLabel {font-family:\"Courier\"; font-weight: bold;}")); - value->setToolTip(tr("Current value of ") + curve); - value->setWhatsThis(tr("Current value of ") + curve); + value->setToolTip(tr("Current value of %1 in %2 units").arg(curve, unit)); + value->setWhatsThis(tr("Current value of %1 in %2 units").arg(curve, unit)); curveLabels->insert(curve+unit, value); curvesWidgetLayout->addWidget(value, labelRow, 3); @@ -603,17 +608,18 @@ void LinechartWidget::addCurve(const QString& curve, const QString& unit) unitLabel = new QLabel(this); unitLabel->setText(unit); unitLabel->setStyleSheet(QString("QLabel {color: %1;}").arg("#AAAAAA")); - qDebug() << "UNIT" << unit; + //qDebug() << "UNIT" << unit; unitLabel->setToolTip(tr("Unit of ") + curve); unitLabel->setWhatsThis(tr("Unit of ") + curve); curvesWidgetLayout->addWidget(unitLabel, labelRow, 4); + connect(unitsCheckBox, SIGNAL(clicked(bool)), unitLabel, SLOT(setVisible(bool))); // Mean mean = new QLabel(this); mean->setNum(0.00); mean->setStyleSheet(QString("QLabel {font-family:\"Courier\"; font-weight: bold;}")); - mean->setToolTip(tr("Arithmetic mean of ") + curve); - mean->setWhatsThis(tr("Arithmetic mean of ") + curve); + mean->setToolTip(tr("Arithmetic mean of %1 in %2 units").arg(curve, unit)); + mean->setWhatsThis(tr("Arithmetic mean of %1 in %2 units").arg(curve, unit)); curveMeans->insert(curve+unit, mean); curvesWidgetLayout->addWidget(mean, labelRow, 5); @@ -627,8 +633,8 @@ void LinechartWidget::addCurve(const QString& curve, const QString& unit) variance = new QLabel(this); variance->setNum(0.00); variance->setStyleSheet(QString("QLabel {font-family:\"Courier\"; font-weight: bold;}")); - variance->setToolTip(tr("Variance of ") + curve); - variance->setWhatsThis(tr("Variance of ") + curve); + variance->setToolTip(tr("Variance of %1 in (%2)^2 units").arg(curve, unit)); + variance->setWhatsThis(tr("Variance of %1 in (%2)^2 units").arg(curve, unit)); curveVariances->insert(curve+unit, variance); curvesWidgetLayout->addWidget(variance, labelRow, 6); diff --git a/src/ui/linechart/LinechartWidget.h b/src/ui/linechart/LinechartWidget.h index af76a4b..d7ebb4b 100644 --- a/src/ui/linechart/LinechartWidget.h +++ b/src/ui/linechart/LinechartWidget.h @@ -132,6 +132,7 @@ protected: QToolButton* scalingLinearButton; QToolButton* scalingLogButton; QToolButton* logButton; + QPointer<QCheckBox> unitsCheckBox; QFile* logFile; unsigned int logindex; diff --git a/src/ui/uas/UASControlWidget.cc b/src/ui/uas/UASControlWidget.cc index 842c150..9de5dd1 100644 --- a/src/ui/uas/UASControlWidget.cc +++ b/src/ui/uas/UASControlWidget.cc @@ -84,6 +84,8 @@ engineOn(false) connect(ui.setModeButton, SIGNAL(clicked()), this, SLOT(transmitMode())); ui.modeComboBox->setCurrentIndex(0); + + ui.gridLayout->setAlignment(Qt::AlignTop); } void UASControlWidget::setUAS(UASInterface* uas) diff --git a/src/ui/uas/UASListWidget.cc b/src/ui/uas/UASListWidget.cc index 498c3ff..e8a4f4e 100644 --- a/src/ui/uas/UASListWidget.cc +++ b/src/ui/uas/UASListWidget.cc @@ -52,6 +52,7 @@ UASListWidget::UASListWidget(QWidget *parent) : QWidget(parent), m_ui(new Ui::UA listLayout = new QVBoxLayout(this); listLayout->setAlignment(Qt::AlignTop); this->setLayout(listLayout); + setObjectName("UNMANNED_SYSTEMS_LIST"); // Construct initial widget uWidget = new QGCUnconnectedInfoWidget(this);