Skip to content
Snippets Groups Projects
QGCPX4VehicleConfig.cc 73.5 KiB
Newer Older
  • Learn to ignore specific revisions
  • // 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 "QGCPendingParamWidget.h"
    
    #include "QGCToolWidget.h"
    
    #include "UASManager.h"
    #include "UASParameterCommsMgr.h"
    
    #include "ui_QGCPX4VehicleConfig.h"
    
    #include "px4_configuration/QGCPX4AirframeConfig.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),
    
        channelWanted(-1),
    
        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),
    
        rcAux1(0.0f),
        rcAux2(0.0f),
    
        dataModelChanged(true),
    
        calibrationEnabled(false),
    
        #ifdef QUPGRADE_SUPPORT
        firmwareDialog(NULL),
        #endif
    
        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);
    
    
        px4AirframeConfig = new QGCPX4AirframeConfig(this);
        ui->airframeLayout->addWidget(px4AirframeConfig);
    
    
    #ifdef QUPGRADE_SUPPORT
        firmwareDialog = new DialogBare(this);
        ui->firmwareLayout->addWidget(firmwareDialog);
    #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);
    
    
        ui->rcCalibrationButton->setCheckable(true);
        connect(ui->rcCalibrationButton, SIGNAL(clicked(bool)), this, SLOT(toggleCalibrationRC(bool)));
    
        connect(ui->writeButton, SIGNAL(clicked()),
                this, SLOT(writeParameters()));
    
    
        //TODO connect buttons here to save/clear actions?
    
        UASInterface* tmpMav = UASManager::instance()->getActiveUAS();
        if (tmpMav) {
            ui->pendingCommitsWidget->initWithUAS(tmpMav);
    
    tstellanova's avatar
    tstellanova committed
            ui->pendingCommitsWidget->update();
    
            setActiveUAS(tmpMav);
    
    tstellanova's avatar
    tstellanova committed
        }
    
        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()));
    
        //set rc values to defaults
    
        for (unsigned int i = 0; i < chanMax; i++) {
    
            rcValue[i] = UINT16_MAX;
    
            rcValueReversed[i] = UINT16_MAX;
    
            rcMapping[i] = i;
    
            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;
    
        }
    
        updateTimer.setInterval(150);
        connect(&updateTimer, SIGNAL(timeout()), this, SLOT(updateView()));
        updateTimer.start();
    }
    
    QGCPX4VehicleConfig::~QGCPX4VehicleConfig()
    {
        delete ui;
    }
    
    
    void QGCPX4VehicleConfig::rcMenuButtonClicked()
    {
    
    tstellanova's avatar
    tstellanova committed
        //TODO eg ui->stackedWidget->findChild("rcConfig");
        ui->stackedWidget->setCurrentIndex(WIDGET_INDEX_RC);
    
    }
    
    void QGCPX4VehicleConfig::sensorMenuButtonClicked()
    {
    
    tstellanova's avatar
    tstellanova committed
        ui->stackedWidget->setCurrentIndex(WIDGET_INDEX_SENSOR_CAL);
    
    }
    
    void QGCPX4VehicleConfig::generalMenuButtonClicked()
    {
    
    tstellanova's avatar
    tstellanova committed
        ui->stackedWidget->setCurrentIndex(WIDGET_INDEX_GENERAL_CONFIG);
    
    }
    
    void QGCPX4VehicleConfig::advancedMenuButtonClicked()
    {
    
    tstellanova's avatar
    tstellanova committed
        ui->stackedWidget->setCurrentIndex(WIDGET_INDEX_ADV_CONFIG);
    
    void QGCPX4VehicleConfig::airframeMenuButtonClicked()
    {
        ui->stackedWidget->setCurrentIndex(WIDGET_INDEX_AIRFRAME_CONFIG);
    }
    
    
    void QGCPX4VehicleConfig::firmwareMenuButtonClicked()
    {
        ui->stackedWidget->setCurrentIndex(WIDGET_INDEX_FIRMWARE);
    }
    
    
    void QGCPX4VehicleConfig::identifyChannelMapping(int aert_index)
    {
    
        if (chanCount == 0 || aert_index < 0)
    
    
        int oldmapping = rcMapping[aert_index];
    
        for (unsigned i = 0; i < chanMax; i++) {
    
            if (i >= chanCount) {
                channelWantedList[i] = 0;
    
        msgBox.setText(tr("Identifying %1 channel").arg(channelNames[channelWanted]));
        msgBox.setInformativeText(tr("Please move stick, switch or potentiometer for the %1 channel\n all the way up/down or left/right.").arg(channelNames[channelWanted]));
    
        msgBox.setStandardButtons(QMessageBox::Ok);
        skipActionButton = msgBox.addButton(tr("Skip"),QMessageBox::RejectRole);
        msgBox.setDefaultButton(QMessageBox::Ok);
        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::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";
        instructions << "PITCH: Move stick up";
        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("Identifying DIRECTION of %1 channel").arg(channelNames[channelReverseStateWanted]));
        msgBox.setInformativeText(tr("%2").arg((aert_index < instructions.length()) ? instructions[aert_index] : ""));
        msgBox.setStandardButtons(QMessageBox::Ok);
        skipActionButton = msgBox.addButton(tr("Skip"),QMessageBox::RejectRole);
        msgBox.setDefaultButton(QMessageBox::Ok);
        msgBox.exec();
        skipActionButton->hide();
        msgBox.removeButton(skipActionButton);
        if (msgBox.clickedButton() == skipActionButton ){
            channelReverseStateWanted = -1;
            rcRev[rcMapping[aert_index]] = oldstatus;
        }
        skipActionButton = NULL;
    
    }
    
    void QGCPX4VehicleConfig::startCalibrationRC()
    {
    
        configEnabled = true;
        QMessageBox::warning(0,"Warning!","You are about to start radio calibration.\nPlease ensure all motor power is disconnected AND all props are removed from the vehicle.\nAlso ensure transmitter and receiver are powered and connected.\nRESET ALL TRIMS TO CENTER!\n\nDo not move the RC sticks, then click OK to confirm");
    
        //go ahead and try to map first 8 channels, now that user can skip channels
        for (int i = 0; i < 8; i++) {
    
        // Try to identify inverted channels, but only for R/P/Y/T
        for (int i = 0; i < 4; i++) {
            detectChannelInversion(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,"Information","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("Save 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();
    }
    
    void QGCPX4VehicleConfig::stopCalibrationRC()
    {
        QMessageBox::information(0,"Trims","Ensure all sticks are centeres and throttle is in the downmost position, click OK to continue");
    
        calibrationEnabled = 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) {
    
            if (rcMax[i] < 1650) {
    
        qDebug() << "SETTING TRIM";
        setTrimPositions();
    
    
        QString statusstr;
        statusstr = "Below you will find the detected radio calibration information that will be sent to the autopilot\n";
    
        statusstr += "Normal values are around 1100 to 1900, with disconnected channels reading 1000, 1500, 2000\n\n";
    
        statusstr += "Channel\tMin\tCenter\tMax\n";
        statusstr += "--------------------\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();
    
    }
    
    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;
                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;
                }
            }
        }
    
        // Generate widgets for the Advanced tab.
        foreach (QString file,vehicledir.entryList(QDir::Files | QDir::NoDotAndDotDot))
        {
            if (file.toLower().endsWith(".qgw")) {
    
    tstellanova's avatar
    tstellanova committed
                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);
    
    tstellanova's avatar
    tstellanova committed
                    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(ui->leftNavScrollAreaWidgetContents);
    
            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")) {
                    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(ui->leftNavScrollAreaWidgetContents);
    
            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")) {
                    tool = new QGCToolWidget("", tab);
                    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;
                    }
                }
            }
        }
    
    
        // Load general calibration for autopilot
        //TODO: Handle this more gracefully, maybe have it scan the directory for multiple calibration entries?
        tool = new QGCToolWidget("", ui->sensorContents);
        tool->addUAS(mav);
        if (tool->loadSettings(autopilotdir.absolutePath() + "/general/calibration/calibration.qgw", false))
        {
            toolWidgets.append(tool);
            QGroupBox *box = new QGroupBox(ui->sensorContents);
            box->setTitle(tool->objectName());
            box->setLayout(new QVBoxLayout(box));
            box->layout()->addWidget(tool);
            ui->sensorLayout->addWidget(box);
        } else {
            delete tool;
        }
    
        // Load vehicle-specific autopilot configuration
        tool = new QGCToolWidget("", ui->sensorContents);
        tool->addUAS(mav);
        if (tool->loadSettings(autopilotdir.absolutePath() + "/" +  mav->getSystemTypeName().toLower() + "/calibration/calibration.qgw", false))
        {
            toolWidgets.append(tool);
            QGroupBox *box = new QGroupBox(ui->sensorContents);
            box->setTitle(tool->objectName());
            box->setLayout(new QVBoxLayout(box));
            box->layout()->addWidget(tool);
            ui->sensorLayout->addWidget(box);
        } else {
            delete tool;
        }
    
    
    //    //description.txt
    //    QFile sensortipsfile(autopilotdir.absolutePath() + "/general/calibration/description.txt");
    //    sensortipsfile.open(QIODevice::ReadOnly);
    ////    ui->sensorTips->setHtml(sensortipsfile.readAll());
    //    sensortipsfile.close();
    
    }
    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;
                                    }
                                    tool = new QGCToolWidget("", parent);
                                    tool->addUAS(mav);
                                    tool->setTitle(parametersname);
                                    tool->setObjectName(parametersname);
                                    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")
                                    {
                                        parent = ui->generalLeftContents;
                                    }
                                    else if (valuetype == "libraries")
                                    {
                                        parent = ui->generalRightContents;
                                    }
                                    tool = new QGCToolWidget("", parent);
                                    tool->addUAS(mav);
                                    tool->setTitle(parametersname);
                                    tool->setObjectName(parametersname);
                                    tool->setSettings(advset);
                                    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);
                                    }