Commit 501470b8 authored by Don Gagne's avatar Don Gagne

Remove DebugConsole

parent 886b40b5
......@@ -167,7 +167,6 @@ FORMS += \
src/QGCQmlWidgetHolder.ui \
src/ui/configuration/SerialSettingsDialog.ui \
src/ui/configuration/terminalconsole.ui \
src/ui/DebugConsole.ui \
src/ui/HDDisplay.ui \
src/ui/Linechart.ui \
src/ui/MainWindow.ui \
......@@ -278,7 +277,6 @@ HEADERS += \
src/ui/configuration/console.h \
src/ui/configuration/SerialSettingsDialog.h \
src/ui/configuration/terminalconsole.h \
src/ui/DebugConsole.h \
src/ui/flightdisplay/FlightDisplay.h \
src/ui/HDDisplay.h \
src/ui/HSIDisplay.h \
......@@ -408,7 +406,6 @@ SOURCES += \
src/ui/configuration/console.cpp \
src/ui/configuration/SerialSettingsDialog.cc \
src/ui/configuration/terminalconsole.cpp \
src/ui/DebugConsole.cc \
src/ui/flightdisplay/FlightDisplay.cc \
src/ui/HDDisplay.cc \
src/ui/HSIDisplay.cc \
......
/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009, 2010 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
This file is part of the QGROUNDCONTROL project
QGROUNDCONTROL is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
QGROUNDCONTROL is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.
======================================================================*/
/**
* @file
* @brief This file implements the Debug Console, a serial console built-in to QGC.
*
* @author Lorenz Meier <mavteam@student.ethz.ch>
*
*/
#include <QPainter>
#include <QSettings>
#include <QScrollBar>
#include <QDebug>
#include "DebugConsole.h"
#include "ui_DebugConsole.h"
#include "LinkManager.h"
#include "UASManager.h"
#include "protocol.h"
#include "QGC.h"
const float DebugConsole::inDataRateThreshold = 0.4f;
DebugConsole::DebugConsole(QWidget *parent) :
QWidget(parent),
currLink(NULL),
holdOn(false),
convertToAscii(true),
filterMAVLINK(true),
autoHold(true),
bytesToIgnore(0),
lastByte(-1),
escReceived(false),
escIndex(0),
sentBytes(),
holdBuffer(),
lineBuffer(""),
lastLineBuffer(0),
lineBufferTimer(),
snapShotTimer(),
lowpassInDataRate(0.0f),
lowpassOutDataRate(0.0f),
commandIndex(0),
m_ui(new Ui::DebugConsole)
{
// Setup basic user interface
m_ui->setupUi(this);
// Hide sent text field - it is only useful after send has been hit
m_ui->sentText->setVisible(false);
// Hide auto-send checkbox
//m_ui->specialCheckBox->setVisible(false);
// Make text area not editable
m_ui->receiveText->setReadOnly(false);
// Limit to 500 lines
m_ui->receiveText->setMaximumBlockCount(500);
// Allow to wrap everywhere
m_ui->receiveText->setWordWrapMode(QTextOption::WrapAnywhere);
// Load settings for this widget
loadSettings();
// Enable traffic measurements. We only start/stop the timer as our links change, as
// these calculations are dependent on the specific link.
connect(&snapShotTimer, SIGNAL(timeout()), this, SLOT(updateTrafficMeasurements()));
snapShotTimer.setInterval(snapShotInterval);
// First connect management slots, then make sure to add all existing objects
// Connect to link manager to get notified about new links
connect(LinkManager::instance(), SIGNAL(newLink(LinkInterface*)), this, SLOT(addLink(LinkInterface*)));
// Connect to UAS manager to get notified about new UAS
connect(UASManager::instance(), SIGNAL(UASCreated(UASInterface*)), this, SLOT(uasCreated(UASInterface*)));
// Add all existing links
foreach (LinkInterface* link, LinkManager::instance()->getLinks()) {
addLink(link);
}
// Get a list of all existing UAS
foreach (UASInterface* uas, UASManager::instance()->getUASList()) {
uasCreated(uas);
}
// Connect link combo box
connect(m_ui->linkComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(linkSelected(int)));
// Connect send button
connect(m_ui->transmitButton, SIGNAL(clicked()), this, SLOT(sendBytes()));
// Connect HEX conversion and MAVLINK filter checkboxes
connect(m_ui->mavlinkCheckBox, SIGNAL(clicked(bool)), this, SLOT(MAVLINKfilterEnabled(bool)));
connect(m_ui->hexCheckBox, SIGNAL(clicked(bool)), this, SLOT(hexModeEnabled(bool)));
connect(m_ui->holdCheckBox, SIGNAL(clicked(bool)), this, SLOT(setAutoHold(bool)));
// Connect hold button
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->addSymbolButton, SIGNAL(clicked()), this, SLOT(appendSpecialSymbol()));
// Connect Checkbox
connect(m_ui->specialComboBox, SIGNAL(highlighted(QString)), this, SLOT(specialSymbolSelected(QString)));
// Allow to send via return
connect(m_ui->sendText, SIGNAL(returnPressed()), this, SLOT(sendBytes()));
}
void DebugConsole::hideEvent(QHideEvent* event)
{
Q_UNUSED(event);
storeSettings();
}
DebugConsole::~DebugConsole()
{
storeSettings();
delete m_ui;
}
void DebugConsole::loadSettings()
{
// Load defaults from settings
QSettings settings;
settings.beginGroup("QGC_DEBUG_CONSOLE");
m_ui->specialComboBox->setCurrentIndex(settings.value("SPECIAL_SYMBOL", m_ui->specialComboBox->currentIndex()).toInt());
m_ui->specialCheckBox->setChecked(settings.value("SPECIAL_SYMBOL_CHECKBOX_STATE", m_ui->specialCheckBox->isChecked()).toBool());
hexModeEnabled(settings.value("HEX_MODE_ENABLED", m_ui->hexCheckBox->isChecked()).toBool());
MAVLINKfilterEnabled(settings.value("MAVLINK_FILTER_ENABLED", filterMAVLINK).toBool());
setAutoHold(settings.value("AUTO_HOLD_ENABLED", autoHold).toBool());
settings.endGroup();
}
void DebugConsole::storeSettings()
{
// Store settings
QSettings settings;
settings.beginGroup("QGC_DEBUG_CONSOLE");
settings.setValue("SPECIAL_SYMBOL", m_ui->specialComboBox->currentIndex());
settings.setValue("SPECIAL_SYMBOL_CHECKBOX_STATE", m_ui->specialCheckBox->isChecked());
settings.setValue("HEX_MODE_ENABLED", m_ui->hexCheckBox->isChecked());
settings.setValue("MAVLINK_FILTER_ENABLED", filterMAVLINK);
settings.setValue("AUTO_HOLD_ENABLED", autoHold);
settings.endGroup();
}
void DebugConsole::uasCreated(UASInterface* uas)
{
connect(uas, SIGNAL(textMessageReceived(int,int,int,QString)),
this, SLOT(receiveTextMessage(int,int,int,QString)), Qt::UniqueConnection);
}
/**
* Add a link to the debug console output
*/
void DebugConsole::addLink(LinkInterface* link)
{
// Add link to list
foreach (SharedLinkInterface sharedLink, _links) {
Q_ASSERT(sharedLink.data() != link);
}
_links.append(LinkManager::instance()->sharedPointerForLink(link));
m_ui->linkComboBox->insertItem(link->getMavlinkChannel(), link->getName());
// Set new item as current
m_ui->linkComboBox->setCurrentIndex(qMax(0, _links.size() - 1));
linkSelected(m_ui->linkComboBox->currentIndex());
// Register for name changes
connect(link, SIGNAL(nameChanged(QString)), this, SLOT(updateLinkName(QString)), Qt::UniqueConnection);
connect(LinkManager::instance(), &LinkManager::linkDisconnected, this, &DebugConsole::removeLink, Qt::UniqueConnection);
}
void DebugConsole::removeLink(LinkInterface* const link)
{
// Now if this was the current link, clean up some stuff.
if (link == currLink)
{
disconnect(currLink, &LinkInterface::bytesReceived, this, &DebugConsole::receiveBytes);
disconnect(currLink, &LinkInterface::connected, this, &DebugConsole::_linkConnected);
disconnect(currLink, &LinkInterface::communicationUpdate, this, &DebugConsole::linkStatusUpdate);
// Like disable the update time for the UI.
snapShotTimer.stop();
currLink = NULL;
}
bool found = false;
int linkIndex;
for (linkIndex=0; linkIndex<_links.count(); linkIndex++) {
if (_links[linkIndex].data() == link) {
found = true;
_links.removeAt(linkIndex);
break;
}
}
Q_UNUSED(found);
Q_ASSERT(found);
m_ui->linkComboBox->removeItem(linkIndex);
}
void DebugConsole::linkStatusUpdate(const QString& name,const QString& text)
{
Q_UNUSED(name);
m_ui->receiveText->appendPlainText(text);
// Ensure text area scrolls correctly
m_ui->receiveText->ensureCursorVisible();
}
void DebugConsole::linkSelected(int linkIndex)
{
// Clear data
m_ui->receiveText->clear();
// Connect new link
if (linkIndex != -1) {
currLink = _links[linkIndex].data();
connect(currLink, SIGNAL(bytesReceived(LinkInterface*,QByteArray)), this, SLOT(receiveBytes(LinkInterface*, QByteArray)));
disconnect(currLink, &LinkInterface::connected, this, &DebugConsole::_linkConnected);
connect(currLink,SIGNAL(communicationUpdate(QString,QString)),this,SLOT(linkStatusUpdate(QString,QString)));
_setConnectionState(currLink->isConnected());
snapShotTimer.start();
}
}
/**
* @param name new name for this link - the link is determined to the sender to this slot by QObject::sender()
*/
void DebugConsole::updateLinkName(QString name)
{
LinkInterface* link = qobject_cast<LinkInterface*>(sender());
if (link != NULL) {
bool found = false;
int linkIndex;
for (linkIndex=0; linkIndex<_links.count(); linkIndex++) {
if (_links[linkIndex].data() == link) {
found = true;
break;
}
}
if (found) {
m_ui->linkComboBox->setItemText(linkIndex, name);
}
}
}
void DebugConsole::setAutoHold(bool hold)
{
// Disable current hold if hold had been enabled
if (autoHold && holdOn && !hold) {
this->hold(false);
m_ui->holdButton->setChecked(false);
}
// Set auto hold checkbox
if (m_ui->holdCheckBox->isChecked() != hold) {
m_ui->holdCheckBox->setChecked(hold);
}
if (!hold)
{
// Warn user about not activated hold
m_ui->receiveText->appendHtml(QString("<font color=\"%1\">%2</font>\n").arg(QColor(Qt::red).name(), tr("WARNING: You have NOT enabled auto-hold (stops updating the console if huge amounts of serial data arrive). Updating the console consumes significant CPU load, so if you receive more than about 5 KB/s of serial data, make sure to enable auto-hold if not using the console.")));
}
else
{
m_ui->receiveText->clear();
}
// Set new state
autoHold = hold;
}
/**
* Prints the message in the UAS color
*/
void DebugConsole::receiveTextMessage(int id, int component, int severity, QString text)
{
Q_UNUSED(severity);
if (isVisible())
{
QString name = UASManager::instance()->getUASForId(id)->getUASName();
QString comp;
// Get a human readable name if possible
switch (component) {
// TODO: To be completed
case MAV_COMP_ID_IMU:
comp = tr("IMU");
break;
case MAV_COMP_ID_MAPPER:
comp = tr("MAPPER");
break;
case MAV_COMP_ID_MISSIONPLANNER:
comp = tr("MISSION");
break;
case MAV_COMP_ID_SYSTEM_CONTROL:
comp = tr("SYS-CONTROL");
break;
default:
comp = QString::number(component);
break;
}
//turn off updates while we're appending content to avoid breaking the autoscroll behavior
m_ui->receiveText->setUpdatesEnabled(false);
QScrollBar *scroller = m_ui->receiveText->verticalScrollBar();
m_ui->receiveText->appendHtml(QString("<font color=\"%1\">(%2:%3) %4</font>\n").arg(UASManager::instance()->getUASForId(id)->getColor().name(), name, comp, text));
// Ensure text area scrolls correctly
scroller->setValue(scroller->maximum());
m_ui->receiveText->setUpdatesEnabled(true);
}
}
/**
* This function updates the speed indicator text in the GUI.
* Additionally, if this speed is too high, the display of incoming characters is disabled.
*/
void DebugConsole::updateTrafficMeasurements()
{
// Calculate the rate of incoming data, converting to
// kilobytes per second from the received bits per second.
qint64 inDataRate = currLink->getCurrentInDataRate() / 1000.0f;
lowpassInDataRate = lowpassInDataRate * 0.9f + (0.1f * inDataRate / 8.0f);
// If the incoming data rate is faster than our threshold, don't display the data.
// We don't use the low-passed data rate as we want the true data rate. The low-passed data
// is just for displaying to the user to remove jitter.
if ((inDataRate > inDataRateThreshold) && autoHold) {
// Enable auto-hold
m_ui->holdButton->setChecked(true);
hold(true);
}
// Update the incoming data rate label.
m_ui->downSpeedLabel->setText(tr("%L1 kB/s").arg(lowpassInDataRate, 4, 'f', 1, '0'));
// Calculate the rate of outgoing data, converting to
// kilobytes per second from the received bits per second.
lowpassOutDataRate = lowpassOutDataRate * 0.9f + (0.1f * currLink->getCurrentOutDataRate() / 8.0f / 1000.0f);
// Update the outoing data rate label.
m_ui->upSpeedLabel->setText(tr("%L1 kB/s").arg(lowpassOutDataRate, 4, 'f', 1, '0'));
}
void DebugConsole::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event);
}
void DebugConsole::receiveBytes(LinkInterface* link, QByteArray bytes)
{
int len = bytes.size();
int lastSpace = 0;
if ((this->bytesToIgnore > 260) || (this->bytesToIgnore < -2)) this->bytesToIgnore = 0;
// Only add data from current link
if (link == currLink && !holdOn)
{
// Parse all bytes
for (int j = 0; j < len; j++)
{
unsigned char byte = bytes.at(j);
// Filter MAVLink (http://qgroundcontrol.org/mavlink/) messages out of the stream.
if (filterMAVLINK)
{
if (this->bytesToIgnore > 0)
{
if ( (j + this->bytesToIgnore) < len )
j += this->bytesToIgnore - 1, this->bytesToIgnore = 1;
else
this->bytesToIgnore -= (len - j - 1), j = len - 1;
} else
if (this->bytesToIgnore == -2)
{ // Payload plus header - but we got STX already
this->bytesToIgnore = static_cast<unsigned int>(byte) + MAVLINK_NUM_NON_PAYLOAD_BYTES - 1;
if ( (j + this->bytesToIgnore) < len )
j += this->bytesToIgnore - 1, this->bytesToIgnore = 1;
else
this->bytesToIgnore -= (len - j - 1), j = len - 1;
} else
// Filtering is done by setting an ignore counter based on the MAVLINK packet length
if (static_cast<unsigned char>(byte) == MAVLINK_STX)
{
this->bytesToIgnore = -1;
} else
this->bytesToIgnore = 0;
} else this->bytesToIgnore = 0;
if ( (this->bytesToIgnore <= 0) && (this->bytesToIgnore != -1) )
{
QString str;
// Convert to ASCII for readability
if (convertToAscii)
{
if (escReceived)
{
if (escIndex < static_cast<int>(sizeof(escBytes)))
{
escBytes[escIndex] = byte;
if (/*escIndex == 1 && */escBytes[escIndex] == 0x48)
{
// Handle sequence
// for this one, clear all text
m_ui->receiveText->clear();
escReceived = false;
}
else if (escBytes[escIndex] == 0x4b)
{
// Handle sequence
// for this one, do nothing
escReceived = false;
}
else if (byte == 0x5b)
{
// Do nothing, this is still a valid escape sequence
}
else
{
escReceived = false;
}
}
else
{
// Obviously something went wrong, reset
escReceived = false;
escIndex = 0;
}
}
else if ((byte <= 32) || (byte > 126))
{
switch (byte)
{
case (unsigned char)'\n': // Accept line feed
if (lastByte != '\r') // Do not break line again for LF+CR
str.append(byte); // only break line for single LF or CR bytes
break;
case (unsigned char)' ': // space of any type means don't add another on hex output
case (unsigned char)'\t': // Accept tab
case (unsigned char)'\r': // Catch and carriage return
if (lastByte != '\n') // Do not break line again for CR+LF
str.append(byte); // only break line for single LF or CR bytes
lastSpace = 1;
break;
/* VT100 emulation (partially */
case 0x1b: // ESC received
escReceived = true;
escIndex = 0;
//qDebug() << "GOT ESC";
break;
case 0x08: // BS (backspace) received
// Do nothing for now
break;
default: // Append replacement character (box) if char is not ASCII
QString str2;
if ( lastSpace == 1)
str2.sprintf("0x%02x ", byte);
else str2.sprintf(" 0x%02x ", byte);
str.append(str2);
lastSpace = 1;
escReceived = false;
break;
}
}
else
{
// Ignore carriage return, because that
// is auto-added with '\n'
if (byte != '\r') str.append(byte); // Append original character
lastSpace = 0;
}
}
else
{
QString str2;
str2.sprintf("%02x ", byte);
str.append(str2);
}
lineBuffer.append(str);
lastByte = byte;
}
else
{
if (filterMAVLINK) this->bytesToIgnore--;
}
}
// Plot every 200 ms if windows is visible
if (lineBuffer.length() > 0 && (QGC::groundTimeMilliseconds() - lastLineBuffer) > 200) {
if (isVisible())
{
m_ui->receiveText->appendPlainText(lineBuffer);
lineBuffer.clear();
lastLineBuffer = QGC::groundTimeMilliseconds();
// Ensure text area scrolls correctly
m_ui->receiveText->ensureCursorVisible();
}
if (lineBuffer.size() > 8192)
{
lineBuffer.remove(0, 4096);
}
}
}
else if (link == currLink && holdOn)
{
holdBuffer.append(bytes);
if (holdBuffer.size() > 8192)
holdBuffer.remove(0, 4096); // drop old stuff
}
}
QByteArray DebugConsole::symbolNameToBytes(const QString& text)
{
QByteArray b;
if (text.contains("CR+LF")) {
b.append(static_cast<char>(0x0D));
b.append(static_cast<char>(0x0A));
} else if (text.contains("LF")) {
b.append(static_cast<char>(0x0A));
} else if (text.contains("FF")) {
b.append(static_cast<char>(0x0C));
} else if (text.contains("CR")) {
b.append(static_cast<char>(0x0D));
} else if (text.contains("TAB")) {
b.append(static_cast<char>(0x09));
} else if (text.contains("NUL")) {
b.append(static_cast<char>(0x00));
} else if (text.contains("ESC")) {
b.append(static_cast<char>(0x1B));
} else if (text.contains("~")) {
b.append(static_cast<char>(0x7E));
} else if (text.contains("<Space>")) {
b.append(static_cast<char>(0x20));
}
return b;
}
QString DebugConsole::bytesToSymbolNames(const QByteArray& b)
{
QString text;
if (b.size() > 1 && b.contains(0x0D) && b.contains(0x0A)) {
text = "<CR+LF>";
} else if (b.contains(0x0A)) {
text = "<LF>";
} else if (b.contains(0x0C)) {
text = "<FF>";
} else if (b.contains(0x0D)) {
text = "<CR>";
} else if (b.contains(0x09)) {
text = "<TAB>";
} else if (b.contains((char)0x00)) {
text = "<NUL>";
} else if (b.contains(0x1B)) {
text = "<ESC>";
} else if (b.contains(0x7E)) {
text = "<~>";
} else if (b.contains(0x20)) {
text = "<Space>";
} else {
text.append(b);
}
return text;
}
void DebugConsole::specialSymbolSelected(const QString& text)
{
Q_UNUSED(text);
}
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::appendSpecialSymbol()
{
appendSpecialSymbol(m_ui->specialComboBox->currentText());
}
void DebugConsole::sendBytes()
{
// FIXME This store settings should be removed
// once all threading issues have been resolved
// since its called in the destructor, which
// is absolutely sufficient
storeSettings();
// Store command history
commandHistory.append(m_ui->sendText->text());
// Since text was just sent, we're at position commandHistory.length()
// which is the current text
commandIndex = commandHistory.length();
if (!m_ui->sentText->isVisible()) {
m_ui->sentText->setVisible(true);
}
if (!currLink->isConnected()) {
m_ui->sentText->setText(tr("Nothing sent. The link %1 is unconnected. Please connect first.").arg(currLink->getName()));
return;
}
QString transmitUnconverted = m_ui->sendText->text();
QByteArray specialSymbol;
// Append special symbol if checkbox is checked
if (m_ui->specialCheckBox->isChecked()) {
// Get auto-add special symbols
specialSymbol = symbolNameToBytes(m_ui->specialComboBox->currentText());
// Convert them if needed
if (!convertToAscii) {
QString specialSymbolConverted;
for (int i = 0; i < specialSymbol.length(); i++) {
QString add(" 0x%1");
specialSymbolConverted.append(add.arg(static_cast<char>(specialSymbol.at(i)), 2, 16, QChar('0')));
}
specialSymbol.clear();
specialSymbol.append(specialSymbolConverted);
}
}
QByteArray transmit;
QString feedback;
bool ok = true;
if (convertToAscii) {
// ASCII text is not converted
transmit = transmitUnconverted.toLatin1();
// Auto-add special symbol handling
transmit.append(specialSymbol);
QString translated;
// Replace every occurence of a special symbol with its text name
for (int i = 0; i < transmit.size(); ++i) {
QByteArray specialChar;
specialChar.append(transmit.at(i));
translated.append(bytesToSymbolNames(specialChar));
}
feedback.append(translated);
} else {
// HEX symbols are converted to bytes
QString str = transmitUnconverted.toLatin1();
str.append(specialSymbol);
str.remove(' ');
str.remove("0x");
str = str.simplified();
int bufferIndex = 0;
if ((str.size() % 2) == 0) {
for (int i = 0; i < str.size(); i=i+2) {
bool okByte;
QString strBuf = QString(str.at(i));
strBuf.append(str.at(i+1));
unsigned char hex = strBuf.toInt(&okByte, 16);
ok = (ok && okByte);
transmit[bufferIndex++] = hex;
if (okByte) {
// Feedback
feedback.append(str.at(i).toUpper());
feedback.append(str.at(i+1).toUpper());
feedback.append(" ");
} else {
feedback = tr("HEX format error near \"") + strBuf + "\"";
}
}
} else {
ok = false;
feedback = tr("HEX values have to be in pairs, e.g. AA or AA 05");
}
}
// Transmit ASCII or HEX formatted text, only if more than one symbol
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);
} else if (m_ui->sendText->text().toLatin1().size() > 0) {
// Conversion failed, display error message
m_ui->sentText->setText(tr("Not sent: ") + feedback);
}
// Select text to easy follow-up input from user
m_ui->sendText->selectAll();
m_ui->sendText->setFocus(Qt::OtherFocusReason);
}
/**
* @param mode true to convert all in and output to/from HEX, false to send and receive ASCII values
*/
void DebugConsole::hexModeEnabled(bool mode)
{
if (convertToAscii == mode) {
convertToAscii = !mode;
if (m_ui->hexCheckBox->isChecked() != mode) {
m_ui->hexCheckBox->setChecked(mode);
}
m_ui->receiveText->clear();
m_ui->sendText->clear();
m_ui->sentText->clear();
commandHistory.clear();
}
}
/**
* @param filter true to ignore all MAVLINK raw data in output, false, to display all incoming data
*/
void DebugConsole::MAVLINKfilterEnabled(bool filter)
{
if (filterMAVLINK != filter) {
filterMAVLINK = filter;
this->bytesToIgnore = 0;
if (m_ui->mavlinkCheckBox->isChecked() != filter) {
m_ui->mavlinkCheckBox->setChecked(filter);
}
}
}
/**
* @param hold Freeze the input and thus any scrolling
*/
void DebugConsole::hold(bool hold)
{
if (holdOn != hold) {
// Check if we need to append bytes from the hold buffer
if (this->holdOn && !hold) {
// TODO No conversion is done to the bytes in the hold buffer
m_ui->receiveText->appendPlainText(QString(holdBuffer));
holdBuffer.clear();
lowpassInDataRate = 0.0f;
}
this->holdOn = hold;
// Change text interaction mode
if (hold) {
m_ui->receiveText->setTextInteractionFlags(Qt::TextSelectableByKeyboard | Qt::TextSelectableByMouse | Qt::LinksAccessibleByKeyboard | Qt::LinksAccessibleByMouse);
} else {
m_ui->receiveText->setTextInteractionFlags(Qt::NoTextInteraction);
}
if (m_ui->holdCheckBox->isChecked() != hold) {
m_ui->holdCheckBox->setChecked(hold);
}
}
}
void DebugConsole::_linkConnected(void)
{
_setConnectionState(true);
}
void DebugConsole::_linkDisconnected(void)
{
_setConnectionState(false);
}
/**
* Sets the connection state the widget shows to this state
*/
void DebugConsole::_setConnectionState(bool connected)
{
if(connected) {
m_ui->connectButton->setText(tr("Disconn."));
m_ui->receiveText->appendHtml(QString("<font color=\"%1\">%2</font>\n").arg(QGC::colorGreen.name(), tr("Link %1 is connected.").arg(currLink->getName())));
} else {
m_ui->connectButton->setText(tr("Connect"));
m_ui->receiveText->appendHtml(QString("<font color=\"%1\">%2</font>\n").arg(QGC::colorOrange.name(), tr("Link %1 is unconnected.").arg(currLink->getName())));
}
}
/** @brief Handle the connect button */
void DebugConsole::handleConnectButton()
{
if (currLink) {
if (currLink->isConnected()) {
LinkManager::instance()->disconnect(currLink);
} else {
LinkManager::instance()->connectLink(currLink);
}
}
}
void DebugConsole::keyPressEvent(QKeyEvent * event)
{
if (event->key() == Qt::Key_Up) {
cycleCommandHistory(true);
} else if (event->key() == Qt::Key_Down) {
cycleCommandHistory(false);
} else {
QWidget::keyPressEvent(event);
}
}
void DebugConsole::cycleCommandHistory(bool up)
{
// Only cycle if there is a history
if (commandHistory.length() > 0) {
// Store current command if we're not in history yet
if (commandIndex == commandHistory.length() && up) {
currCommand = m_ui->sendText->text();
}
if (up) {
// UP
commandIndex--;
if (commandIndex >= 0) {
m_ui->sendText->setText(commandHistory.at(commandIndex));
}
// If the index
} else {
// DOWN
commandIndex++;
if (commandIndex < commandHistory.length()) {
m_ui->sendText->setText(commandHistory.at(commandIndex));
}
// If the index is at history length, load the last current command
}
// Restore current command if we went out of history
if (commandIndex == commandHistory.length()) {
m_ui->sendText->setText(currCommand);
}
// If we are too far down or too far up, wrap around to current command
if (commandIndex < 0 || commandIndex > commandHistory.length()) {
commandIndex = commandHistory.length();
m_ui->sendText->setText(currCommand);
}
// Bound the index
if (commandIndex < 0) commandIndex = 0;
if (commandIndex > commandHistory.length()) commandIndex = commandHistory.length();
}
}
void DebugConsole::changeEvent(QEvent *e)
{
QWidget::changeEvent(e);
switch (e->type()) {
case QEvent::LanguageChange:
m_ui->retranslateUi(this);
break;
default:
break;
}
}
/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009, 2010 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
This file is part of the QGROUNDCONTROL project
QGROUNDCONTROL is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
QGROUNDCONTROL is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.
======================================================================*/
/**
* @file
* @brief Debug console
*
* @author Lorenz Meier <mavteam@student.ethz.ch>
*
*/
#ifndef DEBUGCONSOLE_H
#define DEBUGCONSOLE_H
#include <QWidget>
#include <QList>
#include <QByteArray>
#include <QTimer>
#include <QKeyEvent>
#include "LinkInterface.h"
namespace Ui
{
class DebugConsole;
}
class UASInterface;
/**
* @brief Shows a debug console
*
* This class shows the raw data stream of each link
* and the debug / text messages sent by all systems
*/
class DebugConsole : public QWidget
{
Q_OBJECT
public:
DebugConsole(QWidget *parent = 0);
~DebugConsole();
public slots:
/** @brief Add a link to the list of monitored links */
void addLink(LinkInterface* link);
/** @brief Add a UAS to the list of monitored UAS */
void uasCreated(UASInterface* uas);
/** @brief Remove a link from the list */
void removeLink(LinkInterface* link);
/** @brief Update a link name */
void updateLinkName(QString name);
/** @brief Select a link for the active view */
void linkSelected(int linkId);
/** @brief Receive bytes from link */
void receiveBytes(LinkInterface* link, QByteArray bytes);
/** @brief Send lineedit content over link */
void sendBytes();
/** @brief Enable HEX display mode */
void hexModeEnabled(bool mode);
/** @brief Filter out MAVLINK raw data */
void MAVLINKfilterEnabled(bool filter);
/** @brief Freeze input, do not store new incoming data */
void hold(bool hold);
/** @brief Handle the connect button */
void handleConnectButton();
/** @brief Enable auto-freeze mode if traffic intensity is too high to display */
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);
/** @brief Append the special symbol currently selected in combo box */
void appendSpecialSymbol();
/** @brief A new special symbol is selected */
void specialSymbolSelected(const QString& text);
void linkStatusUpdate(const QString& name,const QString& text);
protected slots:
/** @brief Draw information overlay */
void paintEvent(QPaintEvent *event);
/** @brief Update traffic measurements */
void updateTrafficMeasurements();
void loadSettings();
void storeSettings();
protected:
void changeEvent(QEvent *e);
void hideEvent(QHideEvent* event);
/** @brief Convert a symbol name to the byte representation */
QByteArray symbolNameToBytes(const QString& symbol);
/** @brief Convert a symbol byte to the name */
QString bytesToSymbolNames(const QByteArray& b);
/** @brief Handle keypress events */
void keyPressEvent(QKeyEvent * event);
/** @brief Cycle through the command history */
void cycleCommandHistory(bool up);
LinkInterface* currLink;
bool holdOn; ///< Hold current view, ignore new data
bool convertToAscii; ///< Convert data to ASCII
bool filterMAVLINK; ///< Set true to filter out MAVLink in output
bool autoHold; ///< Auto-hold mode sets view into hold if the data rate is too high
int bytesToIgnore; ///< Number of bytes to ignore
char lastByte; ///< The last received byte
bool escReceived; ///< True if received ESC char in ASCII mode
int escIndex; ///< Index of bytes since ESC was received
char escBytes[5]; ///< Escape-following bytes
bool terminalReceived; ///< Terminal sequence received
QList<QString> sentBytes; ///< Transmitted bytes, per transmission
QByteArray holdBuffer; ///< Buffer where bytes are stored during hold-enable
QString lineBuffer; ///< Buffere where bytes are stored before writing them out
quint64 lastLineBuffer; ///< Last line buffer emission time
QTimer lineBufferTimer; ///< Line buffer timer
QTimer snapShotTimer; ///< Timer for measuring traffic snapshots
static const int snapShotInterval = 500; ///< Set the time between UI updates for the data rate (ms)
float lowpassInDataRate; ///< Lowpass filtered data rate (kilobytes/s)
static const float inDataRateThreshold; ///< Threshold where to enable auto-hold (kilobytes/s)
float lowpassOutDataRate; ///< Low-pass filtered outgoing data rate (kilobytes/s)
QStringList commandHistory;
QString currCommand;
int commandIndex;
private slots:
void _linkConnected(void);
void _linkDisconnected(void);
private:
/** @brief Set connection state of the current link */
void _setConnectionState(bool);
/// List of all links we are keeping track of. We keep SharedLinkInterface objects
/// which are QSharedPointer's in order to maintain reference counts. Otherwise signals
/// can get out of order and the link may be deleted before we clean up our side.
QList<SharedLinkInterface> _links;
Ui::DebugConsole *m_ui;
};
#endif // DEBUGCONSOLE_H
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>DebugConsole</class>
<widget class="QWidget" name="DebugConsole">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>570</width>
<height>211</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout" columnstretch="0,0">
<property name="horizontalSpacing">
<number>6</number>
</property>
<property name="verticalSpacing">
<number>4</number>
</property>
<property name="margin">
<number>6</number>
</property>
<item row="0" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_2" stretch="10,0,0,0,0,0,0,0,0">
<item>
<widget class="QComboBox" name="linkComboBox">
<property name="maximumSize">
<size>
<width>130</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>Select the link to monitor</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="downArrow">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>12</width>
<height>16</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>12</width>
<height>16</height>
</size>
</property>
<property name="baseSize">
<size>
<width>12</width>
<height>16</height>
</size>
</property>
<property name="autoFillBackground">
<bool>false</bool>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="../../qgroundcontrol.qrc">:/res/DownArrow</pixmap>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="downSpeedLabel">
<property name="text">
<string>00.0 kB/s</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="upArrow">
<property name="maximumSize">
<size>
<width>12</width>
<height>16</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="../../qgroundcontrol.qrc">:/res/UpArrow</pixmap>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="upSpeedLabel">
<property name="text">
<string>00.0 kB/s</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="mavlinkCheckBox">
<property name="toolTip">
<string>Ignore MAVLINK protocol messages in display</string>
</property>
<property name="statusTip">
<string>Ignore MAVLINK protocol messages in display</string>
</property>
<property name="text">
<string>Hide MAVLink</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="hexCheckBox">
<property name="toolTip">
<string>Display and enter bytes in HEX representation (e.g. 0xAA)</string>
</property>
<property name="statusTip">
<string>Display and enter bytes in HEX representation (e.g. 0xAA)</string>
</property>
<property name="text">
<string>HEX</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="holdCheckBox">
<property name="toolTip">
<string>Saves CPU ressources, automatically set view to hold if data rate is too high to prevent fast scrolling</string>
</property>
<property name="statusTip">
<string>Saves CPU ressources, automatically set view to hold if data rate is too high to prevent fast scrolling</string>
</property>
<property name="whatsThis">
<string>Enable auto hold to lower the CPU consumption</string>
</property>
<property name="text">
<string>Auto Hold</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="1" column="0" colspan="2">
<widget class="QPlainTextEdit" name="receiveText">
<property name="minimumSize">
<size>
<width>300</width>
<height>50</height>
</size>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
<property name="tabStopWidth">
<number>60</number>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QLineEdit" name="sentText">
<property name="text">
<string>Enter data/text below to send</string>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="4" column="1">
<layout class="QHBoxLayout" name="horizontalLayout" stretch="100,0,0,1,10,1,1,1">
<property name="spacing">
<number>5</number>
</property>
<item>
<widget class="QComboBox" name="specialComboBox">
<property name="maximumSize">
<size>
<width>100</width>
<height>16777215</height>
</size>
</property>
<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>ESC</string>
</property>
</item>
<item>
<property name="text">
<string>~</string>
</property>
</item>
<item>
<property name="text">
<string>&lt;Space&gt;</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QPushButton" name="addSymbolButton">
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../qgroundcontrol.qrc">
<normaloff>:/res/PlusSign</normaloff>:/res/PlusSign</iconset>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="specialCheckBox">
<property name="toolTip">
<string>Automatically send special char at end of message</string>
</property>
<property name="text">
<string>Auto-Add</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="transmitButton">
<property name="toolTip">
<string>Send the ASCII text or HEX values over the link</string>
</property>
<property name="text">
<string>Send</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>5</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="holdButton">
<property name="text">
<string>Hold</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="clearButton">
<property name="text">
<string>Clear</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="connectButton">
<property name="text">
<string>Disconn.</string>
</property>
</widget>
</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>
<include location="../../qgroundcontrol.qrc"/>
</resources>
<connections>
<connection>
<sender>clearButton</sender>
<signal>clicked()</signal>
<receiver>receiveText</receiver>
<slot>clear()</slot>
<hints>
<hint type="sourcelabel">
<x>356</x>
<y>278</y>
</hint>
<hint type="destinationlabel">
<x>199</x>
<y>146</y>
</hint>
</hints>
</connection>
</connections>
</ui>
......@@ -119,7 +119,6 @@ const char* MainWindow::_hdd2DockWidgetName = "HEAD_DOWN_DISPLAY_2_DOCKWIDGET";
const char* MainWindow::_pfdDockWidgetName = "PRIMARY_FLIGHT_DISPLAY_DOCKWIDGET";
const char* MainWindow::_hudDockWidgetName = "HEAD_UP_DISPLAY_DOCKWIDGET";
const char* MainWindow::_uasInfoViewDockWidgetName = "UAS_INFO_INFOVIEW_DOCKWIDGET";
const char* MainWindow::_debugConsoleDockWidgetName = "COMMUNICATION_CONSOLE_DOCKWIDGET";
static MainWindow* _instance = NULL; ///< @brief MainWindow singleton
......@@ -445,7 +444,6 @@ void MainWindow::_buildCommonWidgets(void)
{ _pfdDockWidgetName, "Primary Flight Display", Qt::RightDockWidgetArea },
{ _hudDockWidgetName, "Video Downlink", Qt::RightDockWidgetArea },
{ _uasInfoViewDockWidgetName, "Info View", Qt::LeftDockWidgetArea },
{ _debugConsoleDockWidgetName, "Communications Console", Qt::LeftDockWidgetArea },
};
static const size_t cDockWidgetInfo = sizeof(rgDockWidgetInfo) / sizeof(rgDockWidgetInfo[0]);
......@@ -586,8 +584,6 @@ void MainWindow::_createInnerDockWidget(const QString& widgetName)
QGCTabbedInfoView* pInfoView = new QGCTabbedInfoView(this);
pInfoView->addSource(mavlinkDecoder);
widget = pInfoView;
} else if (widgetName == _debugConsoleDockWidgetName) {
widget = new DebugConsole(this);
} else {
qWarning() << "Attempt to create unknown Inner Dock Widget" << widgetName;
}
......
......@@ -53,7 +53,6 @@ This file is part of the QGROUNDCONTROL project
#if (defined QGC_MOUSE_ENABLED_WIN) | (defined QGC_MOUSE_ENABLED_LINUX)
#include "Mouse6dofInput.h"
#endif // QGC_MOUSE_ENABLED_WIN
#include "DebugConsole.h"
#include "ParameterEditorWidget.h"
#include "HDDisplay.h"
#include "HSIDisplay.h"
......@@ -334,7 +333,6 @@ private:
static const char* _pfdDockWidgetName;
static const char* _hudDockWidgetName;
static const char* _uasInfoViewDockWidgetName;
static const char* _debugConsoleDockWidgetName;
QMap<QString, QDockWidget*> _mapName2DockWidget;
QMap<int, QDockWidget*> _mapUasId2HilDockWidget;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment