Newer
Older
// On Windows (for VS2010) stdint.h contains the limits normally contained in limits.h
// It also needs the __STDC_LIMIT_MACROS macro defined in order to include them (done
// in qgroundcontrol.pri).
#ifdef WIN32
#include <stdint.h>
#else
#include <limits.h>
#endif
#include <QTimer>
#include <QDir>
#include <QXmlStreamReader>
#include <QMessageBox>
#include "QGCPX4VehicleConfig.h"
#include "QGC.h"
#include "QGCToolWidget.h"
#include "ui_QGCPX4VehicleConfig.h"
#include "px4_configuration/QGCPX4AirframeConfig.h"
#include "px4_configuration/QGCPX4SensorCalibration.h"
#include <dialog_bare.h>
#define WIDGET_INDEX_FIRMWARE 0
#define WIDGET_INDEX_RC 1
#define WIDGET_INDEX_SENSOR_CAL 2
#define WIDGET_INDEX_AIRFRAME_CONFIG 3
#define WIDGET_INDEX_GENERAL_CONFIG 4
#define WIDGET_INDEX_ADV_CONFIG 5
#define MIN_PWM_VAL 800
#define MAX_PWM_VAL 2200
QGCPX4VehicleConfig::QGCPX4VehicleConfig(QWidget *parent) :
QWidget(parent),
mav(NULL),
chanCount(0),
channelReverseStateWanted(-1),
rcRoll(0.0f),
rcPitch(0.0f),
rcYaw(0.0f),
rcThrottle(0.0f),
rcMode(0.0f),
rcAssist(0.0f),
rcMission(0.0f),
rcReturn(0.0f),
rcFlaps(0.0f),
configEnabled(false),
px4AirframeConfig(NULL),
#ifdef QUPGRADE_SUPPORT
firmwareDialog(NULL),
#endif
planeBack(":/files/images/px4/rc/cessna_back.png"),
planeSide(":/files/images/px4/rc/cessna_side.png"),
ui(new Ui::QGCPX4VehicleConfig)
{
doneLoadingConfig = false;
channelNames << "Roll / Aileron";
channelNames << "Pitch / Elevator";
channelNames << "Yaw / Rudder";
channelNames << "Throttle";
channelNames << "Main Mode Switch";
channelNames << "Assist Switch";
channelNames << "Mission Switch";
channelNames << "Return Switch";
channelNames << "Flaps";
channelNames << "Aux1";
channelNames << "Aux2";
channelNames << "Aux3";
channelNames << "Aux4";
channelNames << "Aux5";
channelNames << "Aux6";
channelNames << "Aux7";
channelNames << "Aux8";
setObjectName("QGC_VEHICLECONFIG");
ui->setupUi(this);
Lorenz Meier
committed
ui->advancedMenuButton->setEnabled(false);
ui->airframeMenuButton->setEnabled(false);
ui->sensorMenuButton->setEnabled(false);
ui->rcMenuButton->setEnabled(false);
Lorenz Meier
committed
px4AirframeConfig = new QGCPX4AirframeConfig(this);
ui->airframeLayout->addWidget(px4AirframeConfig);
px4SensorCalibration = new QGCPX4SensorCalibration(this);
ui->sensorLayout->addWidget(px4SensorCalibration);
#ifdef QUPGRADE_SUPPORT
firmwareDialog = new DialogBare(this);
ui->firmwareLayout->addWidget(firmwareDialog);
connect(firmwareDialog, SIGNAL(connectLinks()), LinkManager::instance(), SLOT(connectAll()));
connect(firmwareDialog, SIGNAL(disconnectLinks()), LinkManager::instance(), SLOT(disconnectAll()));
#else
#error Please check out QUpgrade from http://github.com/LorenzMeier/qupgrade/ into the QGroundControl folder.
QLabel* label = new QLabel(this);
label->setText("THIS VERSION OF QGROUNDCONTROL WAS BUILT WITHOUT QUPGRADE. To enable firmware upload support, checkout QUpgrade WITHIN the QGroundControl folder");
ui->firmwareLayout->addWidget(label);
#endif
ui->rollWidget->setOrientation(Qt::Horizontal);
ui->rollWidget->setName("Roll");
ui->yawWidget->setOrientation(Qt::Horizontal);
ui->yawWidget->setName("Yaw");
ui->pitchWidget->setName("Pitch");
ui->throttleWidget->setName("Throttle");
ui->radio5Widget->setOrientation(Qt::Horizontal);
ui->radio5Widget->setName("Radio 5");
ui->radio6Widget->setOrientation(Qt::Horizontal);
ui->radio6Widget->setName("Radio 6");
ui->radio7Widget->setOrientation(Qt::Horizontal);
ui->radio7Widget->setName("Radio 7");
ui->radio8Widget->setOrientation(Qt::Horizontal);
ui->radio8Widget->setName("Radio 8");
connect(ui->rcMenuButton,SIGNAL(clicked()),
this,SLOT(rcMenuButtonClicked()));
connect(ui->sensorMenuButton,SIGNAL(clicked()),
this,SLOT(sensorMenuButtonClicked()));
connect(ui->generalMenuButton,SIGNAL(clicked()),
this,SLOT(generalMenuButtonClicked()));
connect(ui->advancedMenuButton,SIGNAL(clicked()),
this,SLOT(advancedMenuButtonClicked()));
connect(ui->airframeMenuButton, SIGNAL(clicked()),
this, SLOT(airframeMenuButtonClicked()));
connect(ui->firmwareMenuButton, SIGNAL(clicked()),
this, SLOT(firmwareMenuButtonClicked()));
connect(ui->advancedCheckBox, SIGNAL(clicked(bool)), ui->advancedGroupBox, SLOT(setVisible(bool)));
ui->advancedGroupBox->setVisible(false);
connect(ui->advancedCheckBox, SIGNAL(clicked(bool)), ui->graphicsView, SLOT(setHidden(bool)));
ui->graphicsView->setScene(&scene);
scene.addPixmap(planeBack);
scene.addPixmap(planeSide);
// XXX hide while WIP
ui->graphicsView->hide();
ui->rcCalibrationButton->setCheckable(true);
ui->rcCalibrationButton->setEnabled(false);
connect(ui->rcCalibrationButton, SIGNAL(clicked(bool)), this, SLOT(toggleCalibrationRC(bool)));
ui->spektrumPairButton->setCheckable(false);
ui->spektrumPairButton->setEnabled(false);
connect(ui->spektrumPairButton, SIGNAL(clicked(bool)), this, SLOT(toggleSpektrumPairing(bool)));
//TODO connect buttons here to save/clear actions?
UASInterface* tmpMav = UASManager::instance()->getActiveUAS();
if (tmpMav) {
ui->pendingCommitsWidget->initWithUAS(tmpMav);
connect(UASManager::instance(), SIGNAL(activeUASSet(UASInterface*)),
this, SLOT(setActiveUAS(UASInterface*)));
// Connect RC mapping assignments
connect(ui->rollSpinBox, SIGNAL(valueChanged(int)), this, SLOT(setRollChan(int)));
connect(ui->pitchSpinBox, SIGNAL(valueChanged(int)), this, SLOT(setPitchChan(int)));
connect(ui->yawSpinBox, SIGNAL(valueChanged(int)), this, SLOT(setYawChan(int)));
connect(ui->throttleSpinBox, SIGNAL(valueChanged(int)), this, SLOT(setThrottleChan(int)));
connect(ui->modeSpinBox, SIGNAL(valueChanged(int)), this, SLOT(setModeChan(int)));
connect(ui->assistSwSpinBox, SIGNAL(valueChanged(int)), this, SLOT(setAssistChan(int)));
connect(ui->missionSwSpinBox, SIGNAL(valueChanged(int)), this, SLOT(setMissionChan(int)));
connect(ui->returnSwSpinBox, SIGNAL(valueChanged(int)), this, SLOT(setReturnChan(int)));
connect(ui->flapsSpinBox, SIGNAL(valueChanged(int)), this, SLOT(setFlapsChan(int)));
connect(ui->aux1SpinBox, SIGNAL(valueChanged(int)), this, SLOT(setAux1Chan(int)));
connect(ui->aux2SpinBox, SIGNAL(valueChanged(int)), this, SLOT(setAux2Chan(int)));
// Connect RC reverse assignments
connect(ui->invertCheckBox, SIGNAL(clicked(bool)), this, SLOT(setRollInverted(bool)));
connect(ui->invertCheckBox_2, SIGNAL(clicked(bool)), this, SLOT(setPitchInverted(bool)));
connect(ui->invertCheckBox_3, SIGNAL(clicked(bool)), this, SLOT(setYawInverted(bool)));
connect(ui->invertCheckBox_4, SIGNAL(clicked(bool)), this, SLOT(setThrottleInverted(bool)));
connect(ui->invertCheckBox_5, SIGNAL(clicked(bool)), this, SLOT(setModeInverted(bool)));
connect(ui->assistSwInvertCheckBox, SIGNAL(clicked(bool)), this, SLOT(setAssistInverted(bool)));
connect(ui->missionSwInvertCheckBox, SIGNAL(clicked(bool)), this, SLOT(setMissionInverted(bool)));
connect(ui->returnSwInvertCheckBox, SIGNAL(clicked(bool)), this, SLOT(setReturnInverted(bool)));
connect(ui->flapsInvertCheckBox, SIGNAL(clicked(bool)), this, SLOT(setFlapsInverted(bool)));
connect(ui->aux1InvertCheckBox, SIGNAL(clicked(bool)), this, SLOT(setAux1Inverted(bool)));
connect(ui->aux2InvertCheckBox, SIGNAL(clicked(bool)), this, SLOT(setAux2Inverted(bool)));
connect(ui->rollButton, SIGNAL(clicked()), this, SLOT(identifyRollChannel()));
connect(ui->pitchButton, SIGNAL(clicked()), this, SLOT(identifyPitchChannel()));
connect(ui->yawButton, SIGNAL(clicked()), this, SLOT(identifyYawChannel()));
connect(ui->throttleButton, SIGNAL(clicked()), this, SLOT(identifyThrottleChannel()));
connect(ui->modeButton, SIGNAL(clicked()), this, SLOT(identifyModeChannel()));
connect(ui->assistSwButton, SIGNAL(clicked()), this, SLOT(identifyAssistChannel()));
connect(ui->missionSwButton, SIGNAL(clicked()), this, SLOT(identifyMissionChannel()));
connect(ui->returnSwButton, SIGNAL(clicked()), this, SLOT(identifyReturnChannel()));
connect(ui->flapsButton, SIGNAL(clicked()), this, SLOT(identifyFlapsChannel()));
connect(ui->aux1Button, SIGNAL(clicked()), this, SLOT(identifyAux1Channel()));
connect(ui->aux2Button, SIGNAL(clicked()), this, SLOT(identifyAux2Channel()));
connect(ui->persistRcValuesButt,SIGNAL(clicked()), this, SLOT(writeCalibrationRC()));
for (unsigned int i = 0; i < chanMax; i++) {
rcValueReversed[i] = UINT16_MAX;
rcToFunctionMapping[i] = i;
channelWantedList[i] = (float)UINT16_MAX;//TODO need to clean these up!
rcMin[i] = 1000.0f;
rcMax[i] = 2000.0f;
// Mapping not established here, so can't pick values via mapping yet!
rcMappedMin[i] = 1000;
rcMappedMax[i] = 2000;
rcMappedValue[i] = UINT16_MAX;
rcMappedNormalizedValue[i] = 0.0f;
}
for (unsigned int i = chanMax -1; i < chanMappedMax; i++) {
rcMapping[i] = -1;
rcMappedMin[i] = 1000;
rcMappedMax[i] = 2000;
rcMappedValue[i] = UINT16_MAX;
rcMappedNormalizedValue[i] = 0.0f;
firmwareMenuButtonClicked();
updateTimer.setInterval(150);
connect(&updateTimer, SIGNAL(timeout()), this, SLOT(updateView()));
updateTimer.start();
ui->rcLabel->setText(tr("NO RADIO CONTROL INPUT DETECTED. PLEASE ENSURE THE TRANSMITTER IS ON."));
QGCPX4VehicleConfig::~QGCPX4VehicleConfig()
{
delete ui;
}
void QGCPX4VehicleConfig::rcMenuButtonClicked()
{
//TODO eg ui->stackedWidget->findChild("rcConfig");
ui->stackedWidget->setCurrentIndex(WIDGET_INDEX_RC);
}
void QGCPX4VehicleConfig::sensorMenuButtonClicked()
{
ui->stackedWidget->setCurrentIndex(WIDGET_INDEX_SENSOR_CAL);
}
void QGCPX4VehicleConfig::generalMenuButtonClicked()
{
ui->stackedWidget->setCurrentIndex(WIDGET_INDEX_GENERAL_CONFIG);
ui->tabTitleLabel->setText(tr("General Configuration Options"));
}
void QGCPX4VehicleConfig::advancedMenuButtonClicked()
{
ui->stackedWidget->setCurrentIndex(WIDGET_INDEX_ADV_CONFIG);
ui->tabTitleLabel->setText(tr("Advanced Configuration Options"));
void QGCPX4VehicleConfig::airframeMenuButtonClicked()
{
ui->stackedWidget->setCurrentIndex(WIDGET_INDEX_AIRFRAME_CONFIG);
ui->tabTitleLabel->setText(tr("Airframe Configuration"));
}
void QGCPX4VehicleConfig::firmwareMenuButtonClicked()
{
ui->stackedWidget->setCurrentIndex(WIDGET_INDEX_FIRMWARE);
void QGCPX4VehicleConfig::identifyChannelMapping(int aert_index)
{
if (chanCount == 0 || aert_index < 0)
return;
int oldmapping = rcMapping[aert_index];
channelWanted = aert_index;
for (unsigned i = 0; i < chanMax; i++) {
if (i >= chanCount) {
channelWantedList[i] = 0;
channelWantedList[i] = rcValue[i];
}
}
msgBox.setText(tr("Detecting %1 ...\t\t").arg(channelNames[channelWanted]));
msgBox.setInformativeText(tr("Please move stick, switch or potentiometer for this channel all the way up/down or left/right."));
msgBox.setStandardButtons(QMessageBox::NoButton);
skipActionButton = msgBox.addButton(tr("Skip"),QMessageBox::RejectRole);
msgBox.exec();
skipActionButton->hide();
msgBox.removeButton(skipActionButton);
if (msgBox.clickedButton() == skipActionButton ){
channelWanted = -1;
rcMapping[aert_index] = oldmapping;
}
skipActionButton = NULL;
}
void QGCPX4VehicleConfig::toggleCalibrationRC(bool enabled)
{
if (enabled)
{
startCalibrationRC();
}
else
{
stopCalibrationRC();
}
}
void QGCPX4VehicleConfig::toggleSpektrumPairing(bool enabled)
{
if (!ui->dsm2RadioButton->isChecked() && !ui->dsmxRadioButton && !ui->dsmx8RadioButton) {
// Reject
QMessageBox warnMsgBox;
warnMsgBox.setText(tr("Please select a Spektrum Protocol Version"));
warnMsgBox.setInformativeText(tr("Please select either DSM2 or DSM-X\ndirectly below the pair button,\nbased on the receiver type."));
warnMsgBox.setStandardButtons(QMessageBox::Ok);
warnMsgBox.setDefaultButton(QMessageBox::Ok);
(void)warnMsgBox.exec();
UASInterface* mav = UASManager::instance()->getActiveUAS();
if (mav) {
int rxSubType;
if (ui->dsm2RadioButton->isChecked())
rxSubType = 0;
else if (ui->dsmxRadioButton->isChecked())
rxSubType = 1;
else // if (ui->dsmx8RadioButton->isChecked())
rxSubType = 2;
mav->pairRX(0, rxSubType);
}
}
void QGCPX4VehicleConfig::setTrimPositions()
{
int rollMap = rcMapping[0];
int pitchMap = rcMapping[1];
int yawMap = rcMapping[2];
int throttleMap = rcMapping[3];
// Reset all trims, as some might not be touched
for (unsigned i = 0; i < chanCount; i++) {
rcTrim[i] = 1500;
}
// Set trim to min if stick is close to min
if (abs(rcValue[throttleMap] - rcMin[throttleMap]) < 100) {
rcTrim[throttleMap] = rcMin[throttleMap]; // throttle
}
// Set trim to max if stick is close to max
else if (abs(rcValue[throttleMap] - rcMax[throttleMap]) < 100) {
rcTrim[throttleMap] = rcMax[throttleMap]; // throttle
QMessageBox warnMsgBox;
warnMsgBox.setText(tr("Throttle Stick Trim Position Invalid"));
warnMsgBox.setInformativeText(tr("The throttle stick is not in the min position. Please set it to the minimum value"));
warnMsgBox.setStandardButtons(QMessageBox::Ok);
warnMsgBox.setDefaultButton(QMessageBox::Ok);
(void)warnMsgBox.exec();
}
// Set trim for roll, pitch, yaw, throttle
rcTrim[rollMap] = rcValue[rollMap]; // roll
rcTrim[pitchMap] = rcValue[pitchMap]; // pitch
rcTrim[yawMap] = rcValue[yawMap]; // yaw
// Mode switch and optional modes, might not be mapped (== -1)
for (unsigned i = 4; i < chanMappedMax; i++) {
if (rcMapping[i] >= 0 && rcMapping[i] < (int)chanCount) {
rcTrim[rcMapping[i]] = ((rcMax[rcMapping[i]] - rcMin[rcMapping[i]]) / 2.0f) + rcMin[rcMapping[i]];
} else if (rcMapping[i] != -1){
qDebug() << "RC MAPPING FAILED #" << i << "VAL:" << rcMapping[i];
}
}
void QGCPX4VehicleConfig::detectChannelInversion(int aert_index)
if (chanCount == 0 || aert_index < 0 || aert_index >= (int)chanMappedMax)
return;
bool oldstatus = rcRev[rcMapping[aert_index]];
channelReverseStateWanted = aert_index;
// Reset search list
for (unsigned i = 0; i < chanMax; i++) {
if (i >= chanCount) {
channelReverseStateWantedList[i] = 0;
}
else {
channelReverseStateWantedList[i] = rcValue[i];
}
}
QStringList instructions;
instructions << "ROLL: Move stick left";
tstellanova
committed
instructions << "PITCH: Move stick down";//matches the other sticks: should cause DECREASE in raw rc channel value when not reversed
instructions << "YAW: Move stick left";
instructions << "THROTTLE: Move stick down";
instructions << "MODE SWITCH: Push down / towards you";
instructions << "ASSISTED SWITCH: Push down / towards you";
instructions << "MISSION SWITCH: Push down / towards you";
instructions << "RETURN SWITCH: Push down / towards you";
instructions << "FLAPS: Push down / towards you or turn dial to the leftmost position";
instructions << "AUX1: Push down / towards you or turn dial to the leftmost position";
instructions << "AUX2: Push down / towards you or turn dial to the leftmost position";
msgBox.setText(tr("%1 Direction").arg(channelNames[channelReverseStateWanted]));
msgBox.setInformativeText(tr("%2").arg((aert_index < instructions.length()) ? instructions[aert_index] : ""));
msgBox.setStandardButtons(QMessageBox::NoButton);
skipActionButton = msgBox.addButton(tr("Skip"),QMessageBox::RejectRole);
msgBox.exec();
skipActionButton->hide();
msgBox.removeButton(skipActionButton);
if (msgBox.clickedButton() == skipActionButton ){
channelReverseStateWanted = -1;
rcRev[rcMapping[aert_index]] = oldstatus;
}
skipActionButton = NULL;
}
void QGCPX4VehicleConfig::startCalibrationRC()
{
QMessageBox::warning(0,
tr("RC not Connected"),
tr("Is the RC receiver connected and transmitter turned on? Detected %1 radio channels. To operate PX4, you need at least 5 channels. ").arg(chanCount));
// reset all channel mappings above Ch 5 to invalid/unused value before starting calibration
for (unsigned int j= 5; j < chanMappedMax; j++) {
rcMapping[j] = -1;
}
configEnabled = true;
QMessageBox::warning(0,tr("Safety Warning"),
tr("Starting RC calibration.\n\nEnsure that motor power is disconnected, all props are removed, RC transmitter and receiver are powered and connected.\n\nReset transmitter trims to center, then click OK to continue"));
//go ahead and try to map first 8 channels, now that user can skip channels
for (int i = 0; i < 8; i++) {
identifyChannelMapping(i);
}
//QMessageBox::information(0,"Information","Additional channels have not been mapped, but can be mapped in the channel table below.");
configEnabled = false;
QMessageBox::information(0, tr("Information"),tr("Click OK, then move all sticks to their extreme positions, watching the min/max values to ensure you get the most range from your controller. This includes all switches"));
ui->rcCalibrationButton->setText(tr("Finish RC Calibration"));
resetCalibrationRC();
calibrationEnabled = true;
ui->rollWidget->showMinMax();
ui->pitchWidget->showMinMax();
ui->yawWidget->showMinMax();
ui->throttleWidget->showMinMax();
ui->radio5Widget->showMinMax();
ui->radio6Widget->showMinMax();
ui->radio7Widget->showMinMax();
ui->radio8Widget->showMinMax();
QMessageBox::information(0, tr("Information"), tr("Please click on the <Finish RC Calibration> button once finished"));
}
void QGCPX4VehicleConfig::stopCalibrationRC()
{
// Try to identify inverted channels, but only for R/P/Y/T
for (int i = 0; i < 4; i++) {
detectChannelInversion(i);
}
QMessageBox::information(0,"Trims","Ensure all controls are centered and throttle is in the lowest position. Click OK to continue");
configEnabled = false;
ui->rcCalibrationButton->setText(tr("Start RC Calibration"));
ui->rollWidget->hideMinMax();
ui->pitchWidget->hideMinMax();
ui->yawWidget->hideMinMax();
ui->throttleWidget->hideMinMax();
ui->radio5Widget->hideMinMax();
ui->radio6Widget->hideMinMax();
ui->radio7Widget->hideMinMax();
ui->radio8Widget->hideMinMax();
for (unsigned int i = 0; i < chanCount; i++) {
if (rcMin[i] > 1350) {
qDebug() << "SETTING TRIM";
setTrimPositions();
statusstr = tr("This is the RC calibration information that will be sent to the autopilot if you click OK. To prevent transmission, click Cancel.");
statusstr += tr(" Normal values range from 1000 to 2000, with disconnected channels reading 1000, 1500, 2000\n\n");
statusstr += tr("Channel\tMin\tCenter\tMax\n");
statusstr += "-------\t---\t------\t---\n";
for (unsigned int i=0; i < chanCount; i++) {
statusstr += QString::number(i) +"\t"+ QString::number(rcMin[i]) +"\t"+ QString::number(rcValue[i]) +"\t"+ QString::number(rcMax[i]) +"\n";
msgBox.setText(tr("Confirm Calibration"));
msgBox.setInformativeText(statusstr);
msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);//allow user to cancel upload after reviewing values
int msgBoxResult = msgBox.exec();
if (QMessageBox::Cancel == msgBoxResult) {
return;//don't commit these values
}
QMessageBox::information(0,"Uploading the RC Calibration","The configuration will now be uploaded and permanently stored.");
writeCalibrationRC();
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
}
void QGCPX4VehicleConfig::loadQgcConfig(bool primary)
{
Q_UNUSED(primary);
QDir autopilotdir(qApp->applicationDirPath() + "/files/" + mav->getAutopilotTypeName().toLower());
QDir generaldir = QDir(autopilotdir.absolutePath() + "/general/widgets");
QDir vehicledir = QDir(autopilotdir.absolutePath() + "/" + mav->getSystemTypeName().toLower() + "/widgets");
if (!autopilotdir.exists("general"))
{
//TODO: Throw some kind of error here. There is no general configuration directory
qWarning() << "Invalid general dir. no general configuration will be loaded.";
}
if (!autopilotdir.exists(mav->getAutopilotTypeName().toLower()))
{
//TODO: Throw an error here too, no autopilot specific configuration
qWarning() << "Invalid vehicle dir, no vehicle specific configuration will be loaded.";
}
// Generate widgets for the General tab.
QGCToolWidget *tool;
bool left = true;
foreach (QString file,generaldir.entryList(QDir::Files | QDir::NoDotAndDotDot))
{
if (file.toLower().endsWith(".qgw")) {
QWidget* parent = left?ui->generalLeftContents:ui->generalRightContents;
John Tapsell
committed
tool = new QGCToolWidget("", "", parent);
if (tool->loadSettings(generaldir.absoluteFilePath(file), false))
{
toolWidgets.append(tool);
QGroupBox *box = new QGroupBox(parent);
box->setTitle(tool->objectName());
box->setLayout(new QVBoxLayout(box));
box->layout()->addWidget(tool);
if (left)
{
left = false;
ui->generalLeftLayout->addWidget(box);
}
else
{
left = true;
ui->generalRightLayout->addWidget(box);
}
} else {
delete tool;
}
}
}
//TODO fix and reintegrate the Advanced parameter editor
// // Generate widgets for the Advanced tab.
// foreach (QString file,vehicledir.entryList(QDir::Files | QDir::NoDotAndDotDot))
// {
// if (file.toLower().endsWith(".qgw")) {
// QWidget* parent = ui->advanceColumnContents;
// tool = new QGCToolWidget("", parent);
// if (tool->loadSettings(vehicledir.absoluteFilePath(file), false))
// {
// toolWidgets.append(tool);
// QGroupBox *box = new QGroupBox(parent);
// box->setTitle(tool->objectName());
// box->setLayout(new QVBoxLayout(box));
// box->layout()->addWidget(tool);
// ui->advancedColumnLayout->addWidget(box);
// } else {
// delete tool;
// }
// }
// }
// Load tabs for general configuration
foreach (QString dir,generaldir.entryList(QDir::Dirs | QDir::NoDotAndDotDot))
{
QPushButton *button = new QPushButton(this);
connect(button,SIGNAL(clicked()),this,SLOT(menuButtonClicked()));
ui->navBarLayout->insertWidget(2,button);
button->setMinimumHeight(75);
button->setMinimumWidth(100);
button->show();
button->setText(dir);
QWidget *tab = new QWidget(ui->stackedWidget);
ui->stackedWidget->insertWidget(2,tab);
buttonToWidgetMap[button] = tab;
tab->setLayout(new QVBoxLayout());
tab->show();
QScrollArea *area = new QScrollArea(tab);
tab->layout()->addWidget(area);
QWidget *scrollArea = new QWidget(tab);
scrollArea->setLayout(new QVBoxLayout(tab));
area->setWidget(scrollArea);
area->setWidgetResizable(true);
area->show();
scrollArea->show();
QDir newdir = QDir(generaldir.absoluteFilePath(dir));
foreach (QString file,newdir.entryList(QDir::Files| QDir::NoDotAndDotDot))
{
if (file.toLower().endsWith(".qgw")) {
John Tapsell
committed
tool = new QGCToolWidget("", "", tab);
if (tool->loadSettings(newdir.absoluteFilePath(file), false))
{
toolWidgets.append(tool);
//ui->sensorLayout->addWidget(tool);
QGroupBox *box = new QGroupBox(tab);
box->setTitle(tool->objectName());
box->setLayout(new QVBoxLayout(tab));
box->layout()->addWidget(tool);
scrollArea->layout()->addWidget(box);
} else {
delete tool;
}
}
}
}
// Load additional tabs for vehicle specific configuration
foreach (QString dir,vehicledir.entryList(QDir::Dirs | QDir::NoDotAndDotDot))
{
QPushButton *button = new QPushButton(this);
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
connect(button,SIGNAL(clicked()),this,SLOT(menuButtonClicked()));
ui->navBarLayout->insertWidget(2,button);
QWidget *tab = new QWidget(ui->stackedWidget);
ui->stackedWidget->insertWidget(2,tab);
buttonToWidgetMap[button] = tab;
button->setMinimumHeight(75);
button->setMinimumWidth(100);
button->show();
button->setText(dir);
tab->setLayout(new QVBoxLayout());
tab->show();
QScrollArea *area = new QScrollArea(tab);
tab->layout()->addWidget(area);
QWidget *scrollArea = new QWidget(tab);
scrollArea->setLayout(new QVBoxLayout(tab));
area->setWidget(scrollArea);
area->setWidgetResizable(true);
area->show();
scrollArea->show();
QDir newdir = QDir(vehicledir.absoluteFilePath(dir));
foreach (QString file,newdir.entryList(QDir::Files| QDir::NoDotAndDotDot))
{
if (file.toLower().endsWith(".qgw")) {
John Tapsell
committed
tool = new QGCToolWidget("", "", tab);
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
tool->addUAS(mav);
if (tool->loadSettings(newdir.absoluteFilePath(file), false))
{
toolWidgets.append(tool);
//ui->sensorLayout->addWidget(tool);
QGroupBox *box = new QGroupBox(tab);
box->setTitle(tool->objectName());
box->setLayout(new QVBoxLayout(box));
box->layout()->addWidget(tool);
scrollArea->layout()->addWidget(box);
box->show();
//gbox->layout()->addWidget(box);
} else {
delete tool;
}
}
}
}
}
void QGCPX4VehicleConfig::menuButtonClicked()
{
QPushButton *button = qobject_cast<QPushButton*>(sender());
if (!button)
{
return;
}
if (buttonToWidgetMap.contains(button))
{
ui->stackedWidget->setCurrentWidget(buttonToWidgetMap[button]);
}
}
void QGCPX4VehicleConfig::loadConfig()
{
QGCToolWidget* tool;
QDir autopilotdir(qApp->applicationDirPath() + "/files/" + mav->getAutopilotTypeName().toLower());
QDir generaldir = QDir(autopilotdir.absolutePath() + "/general/widgets");
QDir vehicledir = QDir(autopilotdir.absolutePath() + "/" + mav->getSystemTypeName().toLower() + "/widgets");
if (!autopilotdir.exists("general"))
{
//TODO: Throw some kind of error here. There is no general configuration directory
qWarning() << "Invalid general dir. no general configuration will be loaded.";
}
if (!autopilotdir.exists(mav->getAutopilotTypeName().toLower()))
{
//TODO: Throw an error here too, no autopilot specific configuration
qWarning() << "Invalid vehicle dir, no vehicle specific configuration will be loaded.";
}
qDebug() << autopilotdir.absolutePath();
qDebug() << generaldir.absolutePath();
qDebug() << vehicledir.absolutePath();
QFile xmlfile(autopilotdir.absolutePath() + "/arduplane.pdef.xml");
if (xmlfile.exists() && !xmlfile.open(QIODevice::ReadOnly))
{
loadQgcConfig(false);
doneLoadingConfig = true;
return;
}
loadQgcConfig(true);
QXmlStreamReader xml(xmlfile.readAll());
xmlfile.close();
//TODO: Testing to ensure that incorrectly formatted XML won't break this.
while (!xml.atEnd())
{
if (xml.isStartElement() && xml.name() == "paramfile")
{
xml.readNext();
while ((xml.name() != "paramfile") && !xml.atEnd())
{
QString valuetype = "";
if (xml.isStartElement() && (xml.name() == "vehicles" || xml.name() == "libraries")) //Enter into the vehicles loop
{
valuetype = xml.name().toString();
xml.readNext();
while ((xml.name() != valuetype) && !xml.atEnd())
{
if (xml.isStartElement() && xml.name() == "parameters") //This is a parameter block
{
QString parametersname = "";
if (xml.attributes().hasAttribute("name"))
{
parametersname = xml.attributes().value("name").toString();
}
QVariantMap genset;
QVariantMap advset;
QString setname = parametersname;
xml.readNext();
int genarraycount = 0;
int advarraycount = 0;
while ((xml.name() != "parameters") && !xml.atEnd())
{
if (xml.isStartElement() && xml.name() == "param")
{
QString humanname = xml.attributes().value("humanName").toString();
QString name = xml.attributes().value("name").toString();
QString tab= xml.attributes().value("user").toString();
if (tab == "Advanced")
{
advset["title"] = parametersname;
}
else
{
genset["title"] = parametersname;
}
if (name.contains(":"))
{
name = name.split(":")[1];
}
QString docs = xml.attributes().value("documentation").toString();
paramTooltips[name] = name + " - " + docs;
int type = -1; //Type of item
QMap<QString,QString> fieldmap;
xml.readNext();
while ((xml.name() != "param") && !xml.atEnd())
{
if (xml.isStartElement() && xml.name() == "values")
{
type = 1; //1 is a combobox
if (tab == "Advanced")
{
advset[setname + "\\" + QString::number(advarraycount) + "\\" + "TYPE"] = "COMBO";
advset[setname + "\\" + QString::number(advarraycount) + "\\" + "QGC_PARAM_COMBOBOX_DESCRIPTION"] = humanname;
advset[setname + "\\" + QString::number(advarraycount) + "\\" + "QGC_PARAM_COMBOBOX_PARAMID"] = name;
advset[setname + "\\" + QString::number(advarraycount) + "\\" + "QGC_PARAM_COMBOBOX_COMPONENTID"] = 1;
}
else
{
genset[setname + "\\" + QString::number(genarraycount) + "\\" + "TYPE"] = "COMBO";
genset[setname + "\\" + QString::number(genarraycount) + "\\" + "QGC_PARAM_COMBOBOX_DESCRIPTION"] = humanname;
genset[setname + "\\" + QString::number(genarraycount) + "\\" + "QGC_PARAM_COMBOBOX_PARAMID"] = name;
genset[setname + "\\" + QString::number(genarraycount) + "\\" + "QGC_PARAM_COMBOBOX_COMPONENTID"] = 1;
}
int paramcount = 0;
xml.readNext();
while ((xml.name() != "values") && !xml.atEnd())
{
if (xml.isStartElement() && xml.name() == "value")
{
QString code = xml.attributes().value("code").toString();
QString arg = xml.readElementText();
if (tab == "Advanced")
{
advset[setname + "\\" + QString::number(advarraycount) + "\\" + "QGC_PARAM_COMBOBOX_ITEM_" + QString::number(paramcount) + "_TEXT"] = arg;
advset[setname + "\\" + QString::number(advarraycount) + "\\" + "QGC_PARAM_COMBOBOX_ITEM_" + QString::number(paramcount) + "_VAL"] = code.toInt();
}
else
{
genset[setname + "\\" + QString::number(genarraycount) + "\\" + "QGC_PARAM_COMBOBOX_ITEM_" + QString::number(paramcount) + "_TEXT"] = arg;
genset[setname + "\\" + QString::number(genarraycount) + "\\" + "QGC_PARAM_COMBOBOX_ITEM_" + QString::number(paramcount) + "_VAL"] = code.toInt();
}
paramcount++;
}
xml.readNext();
}
if (tab == "Advanced")
{
advset[setname + "\\" + QString::number(advarraycount) + "\\" + "QGC_PARAM_COMBOBOX_COUNT"] = paramcount;
}
else
{
genset[setname + "\\" + QString::number(genarraycount) + "\\" + "QGC_PARAM_COMBOBOX_COUNT"] = paramcount;
}
}
if (xml.isStartElement() && xml.name() == "field")
{
type = 2; //2 is a slider
QString fieldtype = xml.attributes().value("name").toString();
QString text = xml.readElementText();
fieldmap[fieldtype] = text;
}
xml.readNext();
}
if (type == -1)
{
//Nothing inside! Assume it's a value, give it a default range.
type = 2;
QString fieldtype = "Range";
QString text = "0 100"; //TODO: Determine a better way of figuring out default ranges.
fieldmap[fieldtype] = text;
}
if (type == 2)
{
if (tab == "Advanced")
{
advset[setname + "\\" + QString::number(advarraycount) + "\\" + "TYPE"] = "SLIDER";
advset[setname + "\\" + QString::number(advarraycount) + "\\" + "QGC_PARAM_SLIDER_DESCRIPTION"] = humanname;
advset[setname + "\\" + QString::number(advarraycount) + "\\" + "QGC_PARAM_SLIDER_PARAMID"] = name;
advset[setname + "\\" + QString::number(advarraycount) + "\\" + "QGC_PARAM_SLIDER_COMPONENTID"] = 1;
}
else
{
genset[setname + "\\" + QString::number(genarraycount) + "\\" + "TYPE"] = "SLIDER";
genset[setname + "\\" + QString::number(genarraycount) + "\\" + "QGC_PARAM_SLIDER_DESCRIPTION"] = humanname;
genset[setname + "\\" + QString::number(genarraycount) + "\\" + "QGC_PARAM_SLIDER_PARAMID"] = name;
genset[setname + "\\" + QString::number(genarraycount) + "\\" + "QGC_PARAM_SLIDER_COMPONENTID"] = 1;
}
if (fieldmap.contains("Range"))
{
float min = 0;
float max = 0;
//Some range fields list "0-10" and some list "0 10". Handle both.
if (fieldmap["Range"].split(" ").size() > 1)
{
min = fieldmap["Range"].split(" ")[0].trimmed().toFloat();
max = fieldmap["Range"].split(" ")[1].trimmed().toFloat();
}
else if (fieldmap["Range"].split("-").size() > 1)
{
min = fieldmap["Range"].split("-")[0].trimmed().toFloat();
max = fieldmap["Range"].split("-")[1].trimmed().toFloat();
}
if (tab == "Advanced")
{
advset[setname + "\\" + QString::number(advarraycount) + "\\" + "QGC_PARAM_SLIDER_MIN"] = min;
advset[setname + "\\" + QString::number(advarraycount) + "\\" + "QGC_PARAM_SLIDER_MAX"] = max;
}
else
{
genset[setname + "\\" + QString::number(genarraycount) + "\\" + "QGC_PARAM_SLIDER_MIN"] = min;
genset[setname + "\\" + QString::number(genarraycount) + "\\" + "QGC_PARAM_SLIDER_MAX"] = max;
}
}
}
if (tab == "Advanced")
{
advarraycount++;
advset["count"] = advarraycount;
}
else
{
genarraycount++;
genset["count"] = genarraycount;
}
}
xml.readNext();
}
if (genarraycount > 0)
{
QWidget* parent = this;
if (valuetype == "vehicles")
{
parent = ui->generalLeftContents;
}
else if (valuetype == "libraries")
{
parent = ui->generalRightContents;
}
John Tapsell
committed
tool = new QGCToolWidget(parametersname, parametersname, parent);
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
tool->addUAS(mav);
tool->setSettings(genset);
QList<QString> paramlist = tool->getParamList();
for (int i=0;i<paramlist.size();i++)
{
//Based on the airframe, we add the parameter to different categories.
if (parametersname == "ArduPlane") //MAV_TYPE_FIXED_WING FIXED_WING
{
systemTypeToParamMap["FIXED_WING"].insert(paramlist[i],tool);
}
else if (parametersname == "ArduCopter") //MAV_TYPE_QUADROTOR "QUADROTOR
{
systemTypeToParamMap["QUADROTOR"].insert(paramlist[i],tool);
}
else if (parametersname == "APMrover2") //MAV_TYPE_GROUND_ROVER GROUND_ROVER
{
systemTypeToParamMap["GROUND_ROVER"].insert(paramlist[i],tool);
}
else
{
libParamToWidgetMap.insert(paramlist[i],tool);
}
}
toolWidgets.append(tool);
QGroupBox *box = new QGroupBox(parent);
box->setTitle(tool->objectName());
box->setLayout(new QVBoxLayout(box));
box->layout()->addWidget(tool);
if (valuetype == "vehicles")
{
ui->generalLeftLayout->addWidget(box);
}
else if (valuetype == "libraries")
{
ui->generalRightLayout->addWidget(box);
}
box->hide();
toolToBoxMap[tool] = box;
}
if (advarraycount > 0)
{
QWidget* parent = this;
if (valuetype == "vehicles")