diff --git a/ground/openpilotgcs/src/plugins/config/config.pro b/ground/openpilotgcs/src/plugins/config/config.pro index b99fc9edc..091a7d45c 100644 --- a/ground/openpilotgcs/src/plugins/config/config.pro +++ b/ground/openpilotgcs/src/plugins/config/config.pro @@ -1,7 +1,6 @@ TEMPLATE = lib TARGET = Config QT += svg - include(../../openpilotgcsplugin.pri) include(../../libs/utils/utils.pri) include(../../plugins/uavtalk/uavtalk.pri) @@ -10,9 +9,7 @@ include(../../plugins/uavobjects/uavobjects.pri) include(../../plugins/uavobjectutil/uavobjectutil.pri) include(../../plugins/uavsettingsimportexport/uavsettingsimportexport.pri) INCLUDEPATH += ../../libs/eigen - OTHER_FILES += Config.pluginspec - HEADERS += configplugin.h \ configgadgetconfiguration.h \ configgadgetwidget.h \ @@ -39,8 +36,8 @@ HEADERS += configplugin.h \ smartsavebutton.h \ defaulthwsettingswidget.h \ inputchannelform.h \ - configcamerastabilizationwidget.h - + configcamerastabilizationwidget.h \ + outputchannelform.h SOURCES += configplugin.cpp \ configgadgetconfiguration.cpp \ configgadgetwidget.cpp \ @@ -69,10 +66,9 @@ SOURCES += configplugin.cpp \ smartsavebutton.cpp \ defaulthwsettingswidget.cpp \ inputchannelform.cpp \ - configcamerastabilizationwidget.cpp - -FORMS += \ - airframe.ui \ + configcamerastabilizationwidget.cpp \ + outputchannelform.cpp +FORMS += airframe.ui \ cc_hw_settings.ui \ pro_hw_settings.ui \ ahrs.ui \ @@ -84,10 +80,6 @@ FORMS += \ defaultattitude.ui \ defaulthwsettings.ui \ inputchannelform.ui \ - camerastabilization.ui - + camerastabilization.ui \ + outputchannelform.ui RESOURCES += configgadget.qrc - - - - diff --git a/ground/openpilotgcs/src/plugins/config/configoutputwidget.cpp b/ground/openpilotgcs/src/plugins/config/configoutputwidget.cpp index 0fc683da4..0b62c5442 100644 --- a/ground/openpilotgcs/src/plugins/config/configoutputwidget.cpp +++ b/ground/openpilotgcs/src/plugins/config/configoutputwidget.cpp @@ -26,6 +26,7 @@ */ #include "configoutputwidget.h" +#include "outputchannelform.h" #include "uavtalk/telemetrymanager.h" @@ -46,103 +47,23 @@ ConfigOutputWidget::ConfigOutputWidget(QWidget *parent) : ConfigTaskWidget(paren m_config = new Ui_OutputWidget(); m_config->setupUi(this); - ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance(); setupButtons(m_config->saveRCOutputToRAM,m_config->saveRCOutputToSD); addUAVObject("ActuatorSettings"); - // First of all, put all the channel widgets into lists, so that we can - // manipulate those: - - // NOTE: for historical reasons, we have objects below called ch0 to ch7, but the convention for OP is Channel 1 to Channel 8. - outLabels << m_config->ch0OutValue - << m_config->ch1OutValue - << m_config->ch2OutValue - << m_config->ch3OutValue - << m_config->ch4OutValue - << m_config->ch5OutValue - << m_config->ch6OutValue - << m_config->ch7OutValue - << m_config->ch8OutValue - << m_config->ch9OutValue; - - outSliders << m_config->ch0OutSlider - << m_config->ch1OutSlider - << m_config->ch2OutSlider - << m_config->ch3OutSlider - << m_config->ch4OutSlider - << m_config->ch5OutSlider - << m_config->ch6OutSlider - << m_config->ch7OutSlider - << m_config->ch8OutSlider - << m_config->ch9OutSlider; - - outMin << m_config->ch0OutMin - << m_config->ch1OutMin - << m_config->ch2OutMin - << m_config->ch3OutMin - << m_config->ch4OutMin - << m_config->ch5OutMin - << m_config->ch6OutMin - << m_config->ch7OutMin - << m_config->ch8OutMin - << m_config->ch9OutMin; - - outMax << m_config->ch0OutMax - << m_config->ch1OutMax - << m_config->ch2OutMax - << m_config->ch3OutMax - << m_config->ch4OutMax - << m_config->ch5OutMax - << m_config->ch6OutMax - << m_config->ch7OutMax - << m_config->ch8OutMax - << m_config->ch9OutMax; - - reversals << m_config->ch0Rev - << m_config->ch1Rev - << m_config->ch2Rev - << m_config->ch3Rev - << m_config->ch4Rev - << m_config->ch5Rev - << m_config->ch6Rev - << m_config->ch7Rev - << m_config->ch8Rev - << m_config->ch9Rev; - - links << m_config->ch0Link - << m_config->ch1Link - << m_config->ch2Link - << m_config->ch3Link - << m_config->ch4Link - << m_config->ch5Link - << m_config->ch6Link - << m_config->ch7Link - << m_config->ch8Link - << m_config->ch9Link; - + // NOTE: we have channel indices from 0 to 9, but the convention for OP is Channel 1 to Channel 10. // Register for ActuatorSettings changes: - for (int i = 0; i < ActuatorCommand::CHANNEL_NUMELEM; i++) { - connect(outMin[i], SIGNAL(editingFinished()), this, SLOT(setChOutRange())); - connect(outMax[i], SIGNAL(editingFinished()), this, SLOT(setChOutRange())); - connect(reversals[i], SIGNAL(toggled(bool)), this, SLOT(reverseChannel(bool))); - // Now connect the channel out sliders to our signal to send updates in test mode - connect(outSliders[i], SIGNAL(valueChanged(int)), this, SLOT(sendChannelTest(int))); - - addWidget(outMin[i]); - addWidget(outMax[i]); - addWidget(reversals[i]); - addWidget(outSliders[i]); - addWidget(links[i]); + for (unsigned int i = 0; i < ActuatorCommand::CHANNEL_NUMELEM; i++) + { + OutputChannelForm *form = new OutputChannelForm(i, this, i==0); + connect(m_config->channelOutTest, SIGNAL(toggled(bool)), + form, SLOT(enableChannelTest(bool))); + connect(form, SIGNAL(channelChanged(int,int)), + this, SLOT(sendChannelTest(int,int))); + m_config->channelLayout->addWidget(form); } connect(m_config->channelOutTest, SIGNAL(toggled(bool)), this, SLOT(runChannelTests(bool))); - for (int i = 0; i < links.count(); i++) - links[i]->setChecked(false); - for (int i = 0; i < links.count(); i++) - connect(links[i], SIGNAL(toggled(bool)), this, SLOT(linkToggled(bool))); - - refreshWidgetsValues(); firstUpdate = true; @@ -166,37 +87,6 @@ ConfigOutputWidget::~ConfigOutputWidget() // ************************************ -/** - Toggles the channel linked state for use in testing mode - */ -void ConfigOutputWidget::linkToggled(bool state) -{ - Q_UNUSED(state) - // find the minimum slider value for the linked ones - int min = 10000; - int linked_count = 0; - for (int i = 0; i < outSliders.count(); i++) - { - if (!links[i]->checkState()) continue; - int value = outSliders[i]->value(); - if (min > value) min = value; - linked_count++; - } - - if (linked_count <= 0) - return; // no linked channels - - if (!m_config->channelOutTest->checkState()) - return; // we are not in Test Output mode - - // set the linked channels to the same value - for (int i = 0; i < outSliders.count(); i++) - { - if (!links[i]->checkState()) continue; - outSliders[i]->setValue(min); - } -} - /** Toggles the channel testing mode by making the GCS take over the ActuatorCommand objects @@ -243,77 +133,41 @@ void ConfigOutputWidget::runChannelTests(bool state) mdata.gcsTelemetryAcked = false; mdata.gcsTelemetryUpdateMode = UAVObject::UPDATEMODE_ONCHANGE; mdata.gcsTelemetryUpdatePeriod = 100; - - // Prevent stupid users from touching the minimum & maximum ranges while - // moving the sliders. Thanks Ivan for the tip :) - foreach (QSpinBox* box, outMin) { - box->setEnabled(false); - } - foreach (QSpinBox* box, outMax) { - box->setEnabled(false); - } - foreach (QCheckBox* box, reversals) { - box->setEnabled(false); - } - } else { mdata = accInitialData; // Restore metadata - foreach (QSpinBox* box, outMin) { - box->setEnabled(true); - } - foreach (QSpinBox* box, outMax) { - box->setEnabled(true); - } - foreach (QCheckBox* box, reversals) { - box->setEnabled(true); - } - } obj->setMetadata(mdata); } +OutputChannelForm* ConfigOutputWidget::getOutputChannelForm(const int index) const +{ + QList outputChannelForms = findChildren(); + foreach(OutputChannelForm *outputChannelForm, outputChannelForms) + { + if( outputChannelForm->index() == index) + return outputChannelForm; + } + + // no OutputChannelForm found with given index + return NULL; +} + /** * Set the label for a channel output assignement */ void ConfigOutputWidget::assignOutputChannel(UAVDataObject *obj, QString str) { + //FIXME: use signal/ slot approach UAVObjectField* field = obj->getField(str); QStringList options = field->getOptions(); - switch (options.indexOf(field->getValue().toString())) { - case 0: - m_config->ch0Output->setText(str); - break; - case 1: - m_config->ch1Output->setText(str); - break; - case 2: - m_config->ch2Output->setText(str); - break; - case 3: - m_config->ch3Output->setText(str); - break; - case 4: - m_config->ch4Output->setText(str); - break; - case 5: - m_config->ch5Output->setText(str); - break; - case 6: - m_config->ch6Output->setText(str); - break; - case 7: - m_config->ch7Output->setText(str); - break; - case 8: - m_config->ch8Output->setText(str); - break; - case 9: - m_config->ch9Output->setText(str); - break; - } + int index = options.indexOf(field->getValue().toString()); + + OutputChannelForm *outputChannelForm = getOutputChannelForm(index); + if(outputChannelForm) + outputChannelForm->setAssignment(str); } /** @@ -335,48 +189,19 @@ void ConfigOutputWidget::setSpinningArmed(bool val) Sends the channel value to the UAV to move the servo. Returns immediately if we are not in testing mode */ -void ConfigOutputWidget::sendChannelTest(int value) +void ConfigOutputWidget::sendChannelTest(int index, int value) { - int in_value = value; + if (!m_config->channelOutTest->isChecked()) + return; - QSlider *ob = (QSlider *)QObject::sender(); - if (!ob) return; - int index = outSliders.indexOf(ob); - if (index < 0) return; + if(index < 0 || (unsigned)index >= ActuatorCommand::CHANNEL_NUMELEM) + return; - if (reversals[index]->isChecked()) - value = outMin[index]->value() - value + outMax[index]->value(); // the chsnnel is reversed - - // update the label - outLabels[index]->setText(QString::number(value)); - - if (links[index]->checkState()) - { // the channel is linked to other channels - // set the linked channels to the same value - for (int i = 0; i < outSliders.count(); i++) - { - if (i == index) continue; - if (!links[i]->checkState()) continue; - - int val = in_value; - if (val < outSliders[i]->minimum()) val = outSliders[i]->minimum(); - if (val > outSliders[i]->maximum()) val = outSliders[i]->maximum(); - - if (outSliders[i]->value() == val) continue; - - outSliders[i]->setValue(val); - outLabels[i]->setText(QString::number(val)); - } - } - - if (!m_config->channelOutTest->isChecked()) - return; - - UAVDataObject *obj = dynamic_cast(getObjectManager()->getObject(QString("ActuatorCommand"))); - if (!obj) return; - UAVObjectField *channel = obj->getField("Channel"); - if (!channel) return; - channel->setValue(value, index); + UAVDataObject *obj = dynamic_cast(getObjectManager()->getObject(QString("ActuatorCommand"))); + if (!obj) return; + UAVObjectField *channel = obj->getField("Channel"); + if (!channel) return; + channel->setValue(value, index); obj->updated(); } @@ -396,8 +221,11 @@ void ConfigOutputWidget::refreshWidgetsValues() UAVObjectManager *objManager = pm->getObject(); // Reset all channel assignements: - for (int i = 0; i < ActuatorCommand::CHANNEL_NUMELEM; i++) - outLabels[i]->setText("-"); + QList outputChannelForms = findChildren(); + foreach(OutputChannelForm *outputChannelForm, outputChannelForms) + { + outputChannelForm->setAssignment("-"); + } // Get the channel assignements: UAVDataObject * obj = dynamic_cast(objManager->getObject(QString("ActuatorSettings"))); @@ -447,34 +275,21 @@ void ConfigOutputWidget::refreshWidgetsValues() } } - // Get Channel ranges: - for (int i=0;igetField(QString("ChannelMin")); - int minValue = field->getValue(i).toInt(); - outMin[i]->setValue(minValue); + int minValue = field->getValue(outputChannelForm->index()).toInt(); field = obj->getField(QString("ChannelMax")); - int maxValue = field->getValue(i).toInt(); - outMax[i]->setValue(maxValue); - if (maxValue>minValue) { - outSliders[i]->setMinimum(minValue); - outSliders[i]->setMaximum(maxValue); - reversals[i]->setChecked(false); - } else { - outSliders[i]->setMinimum(maxValue); - outSliders[i]->setMaximum(minValue); - reversals[i]->setChecked(true); - } + int maxValue = field->getValue(outputChannelForm->index()).toInt(); + outputChannelForm->minmax(minValue, maxValue); + + field = obj->getField(QString("ChannelNeutral")); + int value = field->getValue(outputChannelForm->index()).toInt(); + outputChannelForm->neutral(value); } - field = obj->getField(QString("ChannelNeutral")); - for (int i=0; igetValue(i).toInt(); - outSliders[i]->setValue(value); - outLabels[i]->setText(QString::number(value)); - } setDirty(dirty); - } /** @@ -488,101 +303,31 @@ void ConfigOutputWidget::updateObjectsFromWidgets() Q_ASSERT(obj); // Now send channel ranges: - UAVObjectField * field = obj->getField(QString("ChannelMax")); - for (int i = 0; i < ActuatorCommand::CHANNEL_NUMELEM; i++) { - field->setValue(outMax[i]->value(),i); - } + UAVObjectField * field; + QList outputChannelForms = findChildren(); + foreach(OutputChannelForm *outputChannelForm, outputChannelForms) + { + field = obj->getField(QString("ChannelMax")); + Q_ASSERT(field); + field->setValue(outputChannelForm->max(), outputChannelForm->index()); - field = obj->getField(QString("ChannelMin")); - for (int i = 0; i < ActuatorCommand::CHANNEL_NUMELEM; i++) { - field->setValue(outMin[i]->value(),i); - } + field = obj->getField(QString("ChannelMin")); + Q_ASSERT(field); + field->setValue(outputChannelForm->min(), outputChannelForm->index()); - field = obj->getField(QString("ChannelNeutral")); - for (int i = 0; i < ActuatorCommand::CHANNEL_NUMELEM; i++) { - field->setValue(outSliders[i]->value(),i); + field = obj->getField(QString("ChannelNeutral")); + Q_ASSERT(field); + field->setValue(outputChannelForm->neutral(), outputChannelForm->index()); } field = obj->getField(QString("ChannelUpdateFreq")); + Q_ASSERT(field); field->setValue(m_config->outputRate1->value(),0); field->setValue(m_config->outputRate2->value(),1); field->setValue(m_config->outputRate3->value(),2); field->setValue(m_config->outputRate4->value(),3); } -/** - Sets the minimum/maximum value of the channel 0 to seven output sliders. - Have to do it here because setMinimum is not a slot. - - One added trick: if the slider is at its min when the value - is changed, then keep it on the min. - */ -void ConfigOutputWidget::setChOutRange() -{ - QSpinBox *spinbox = (QSpinBox*)QObject::sender(); - - int index = outMin.indexOf(spinbox); // This is the channel number - if (index < 0) - index = outMax.indexOf(spinbox); // We can't know if the signal came from min or max - - QSlider *slider = outSliders[index]; - - int oldMini = slider->minimum(); -// int oldMaxi = slider->maximum(); - - if (outMin[index]->value()value()) - { - slider->setRange(outMin[index]->value(), outMax[index]->value()); - reversals[index]->setChecked(false); - } - else - { - slider->setRange(outMax[index]->value(), outMin[index]->value()); - reversals[index]->setChecked(true); - } - - if (slider->value() == oldMini) - slider->setValue(slider->minimum()); - -// if (slider->value() == oldMaxi) -// slider->setValue(slider->maximum()); // this can be dangerous if it happens to be controlling a motor at the time! -} - -/** - Reverses the channel when the checkbox is clicked - */ -void ConfigOutputWidget::reverseChannel(bool state) -{ - QCheckBox *checkbox = (QCheckBox*)QObject::sender(); - int index = reversals.indexOf(checkbox); // This is the channel number - - // Sanity check: if state became true, make sure the Maxvalue was higher than Minvalue - // the situations below can happen! - if (state && (outMax[index]->value()value())) - return; - if (!state && (outMax[index]->value()>outMin[index]->value())) - return; - - // Now, swap the min & max values (only on the spinboxes, the slider - // does not change! - int temp = outMax[index]->value(); - outMax[index]->setValue(outMin[index]->value()); - outMin[index]->setValue(temp); - - // Also update the channel value - // This is a trick to force the slider to update its value and - // emit the right signal itself, because our sendChannelTest(int) method - // relies on the object sender's identity. - if (outSliders[index]->value()maximum()) { - outSliders[index]->setValue(outSliders[index]->value()+1); - outSliders[index]->setValue(outSliders[index]->value()-1); - } else { - outSliders[index]->setValue(outSliders[index]->value()-1); - outSliders[index]->setValue(outSliders[index]->value()+1); - } - -} - void ConfigOutputWidget::openHelp() { diff --git a/ground/openpilotgcs/src/plugins/config/configoutputwidget.h b/ground/openpilotgcs/src/plugins/config/configoutputwidget.h index 492109df1..3b475eb0c 100644 --- a/ground/openpilotgcs/src/plugins/config/configoutputwidget.h +++ b/ground/openpilotgcs/src/plugins/config/configoutputwidget.h @@ -37,6 +37,7 @@ #include class Ui_OutputWidget; +class OutputChannelForm; class ConfigOutputWidget: public ConfigTaskWidget { @@ -55,18 +56,12 @@ private: void assignChannel(UAVDataObject *obj, QString str); void assignOutputChannel(UAVDataObject *obj, QString str); + OutputChannelForm* getOutputChannelForm(const int index) const; int mccDataRate; UAVObject::Metadata accInitialData; - QList outSliders; - QList outMin; - QList outMax; - QList reversals; - QList links; - QList outLabels; - bool firstUpdate; @@ -74,10 +69,9 @@ private slots: virtual void refreshWidgetsValues(); void updateObjectsFromWidgets(); void runChannelTests(bool state); - void sendChannelTest(int value); - void setChOutRange(); - void reverseChannel(bool state); - void linkToggled(bool state); + void sendChannelTest(int index, int value); + void setChOutRange(); + void reverseChannel(bool state); void setSpinningArmed(bool val); void openHelp(); }; diff --git a/ground/openpilotgcs/src/plugins/config/output.ui b/ground/openpilotgcs/src/plugins/config/output.ui index b66c12cdc..47940e1ae 100644 --- a/ground/openpilotgcs/src/plugins/config/output.ui +++ b/ground/openpilotgcs/src/plugins/config/output.ui @@ -210,933 +210,7 @@ Leave at 50Hz for fixed wing. - - - - - Channel 1: - - - - - - - true - - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Sans'; font-size:8pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Minimum PWM value, beware of not overdriving your servo.</p></body></html> - - - 9999 - - - - - - - true - - - 9999 - - - Qt::Horizontal - - - - - - - true - - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Sans'; font-size:8pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Maximum PWM value, beware of not overdriving your servo.</p></body></html> - - - 9999 - - - - - - - Current value of slider. - - - 0000 - - - - - - - - FreeSans - 8 - - - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'FreeSans'; font-size:8pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Check to invert the channel.</p></body></html> - - - - - - - - - - Only used with Test Output mode - - - - - - - - - - Link - - - - - - - Rev. - - - - - - - Channel 2: - - - - - - - true - - - 9999 - - - - - - - true - - - 9999 - - - Qt::Horizontal - - - - - - - true - - - 9999 - - - - - - - 0000 - - - - - - - - FreeSans - 8 - - - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'FreeSans'; font-size:8pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Check to invert the channel.</p></body></html> - - - - - - - - - - Only used with Test Output mode - - - - - - - - - - Channel 3: - - - - - - - true - - - 9999 - - - - - - - true - - - 9999 - - - Qt::Horizontal - - - - - - - true - - - 9999 - - - - - - - 0000 - - - - - - - - FreeSans - 8 - - - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'FreeSans'; font-size:8pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Check to invert the channel.</p></body></html> - - - - - - - - - - Only used with Test Output mode - - - - - - - - - - Channel 4: - - - - - - - true - - - 9999 - - - - - - - true - - - 9999 - - - Qt::Horizontal - - - - - - - true - - - 9999 - - - - - - - 0000 - - - - - - - - FreeSans - 8 - - - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'FreeSans'; font-size:8pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Check to invert the channel.</p></body></html> - - - - - - - - - - Only used with Test Output mode - - - - - - - - - - Channel 5: - - - - - - - 9999 - - - - - - - 9999 - - - Qt::Horizontal - - - - - - - 9999 - - - - - - - 0000 - - - - - - - - FreeSans - 8 - - - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'FreeSans'; font-size:8pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Check to invert the channel.</p></body></html> - - - - - - - - - - Only used with Test Output mode - - - - - - - - - - Channel 6: - - - - - - - 9999 - - - - - - - 9999 - - - Qt::Horizontal - - - - - - - 9999 - - - - - - - 0000 - - - - - - - - FreeSans - 8 - - - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'FreeSans'; font-size:8pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Check to invert the channel.</p></body></html> - - - - - - - - - - Only used with Test Output mode - - - - - - - - - - Channel 7: - - - - - - - Channel 8: - - - - - - - 9999 - - - - - - - 9999 - - - Qt::Horizontal - - - - - - - 9999 - - - - - - - 0000 - - - - - - - - FreeSans - 8 - - - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'FreeSans'; font-size:8pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Check to invert the channel.</p></body></html> - - - - - - - - - - Only used with Test Output mode - - - - - - - - - - 9999 - - - - - - - 9999 - - - Qt::Horizontal - - - - - - - 9999 - - - - - - - 0000 - - - - - - - - FreeSans - 8 - - - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'FreeSans'; font-size:8pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Check to invert the channel.</p></body></html> - - - - - - - - - - Only used with Test Output mode - - - - - - - - - - - 9 - - - - - - - - Qt::AlignCenter - - - - - - - - 9 - - - - - - - - Qt::AlignCenter - - - - - - - - 9 - - - - - - - - Qt::AlignCenter - - - - - - - - 9 - - - - - - - - Qt::AlignCenter - - - - - - - - 9 - - - - - - - - Qt::AlignCenter - - - - - - - - 9 - - - - - - - - Qt::AlignCenter - - - - - - - - 9 - - - - - - - - Qt::AlignCenter - - - - - - - - 9 - - - - - - - - Qt::AlignCenter - - - - - - - Assignment - - - - - - - Min - - - - - - - Neutral (slowest for motor) - - - - - - - Max - - - - - - - Channel 9: - - - - - - - Channel 10: - - - - - - - - 9 - - - - - - - - Qt::AlignCenter - - - - - - - - 9 - - - - - - - - Qt::AlignCenter - - - - - - - 9999 - - - - - - - 9999 - - - - - - - 9999 - - - Qt::Horizontal - - - - - - - 9999 - - - Qt::Horizontal - - - - - - - 9999 - - - - - - - 9999 - - - - - - - 0000 - - - - - - - 0000 - - - - - - - - FreeSans - 8 - - - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'FreeSans'; font-size:8pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Check to invert the channel.</p></body></html> - - - - - - - - - - - FreeSans - 8 - - - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'FreeSans'; font-size:8pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Check to invert the channel.</p></body></html> - - - - - - - - - - Only used with Test Output mode - - - - - - - - - - Only used with Test Output mode - - - - - - - + @@ -1281,46 +355,6 @@ Applies and Saves all settings to SD outputRate2 outputRate3 outputRate4 - ch0OutMin - ch0OutMax - ch1OutMin - ch1OutMax - ch2OutMin - ch2OutMax - ch3OutMin - ch3OutMax - ch4OutMin - ch4OutMax - ch5OutMin - ch5OutMax - ch6OutMin - ch6OutMax - ch7OutMin - ch7OutMax - ch0OutSlider - ch0Rev - ch0Link - ch1OutSlider - ch1Rev - ch1Link - ch2OutSlider - ch2Rev - ch2Link - ch3OutSlider - ch3Rev - ch3Link - ch4OutSlider - ch4Rev - ch4Link - ch5OutSlider - ch5Rev - ch5Link - ch6OutSlider - ch6Rev - ch6Link - ch7OutSlider - ch7Rev - ch7Link channelOutTest saveRCOutputToRAM saveRCOutputToSD diff --git a/ground/openpilotgcs/src/plugins/config/outputchannelform.cpp b/ground/openpilotgcs/src/plugins/config/outputchannelform.cpp new file mode 100644 index 000000000..938e09b5b --- /dev/null +++ b/ground/openpilotgcs/src/plugins/config/outputchannelform.cpp @@ -0,0 +1,297 @@ +/** + ****************************************************************************** + * + * @file outputchannelform.cpp + * @author E. Lafargue & The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010. + * @addtogroup GCSPlugins GCS Plugins + * @{ + * @addtogroup ConfigPlugin Config Plugin + * @{ + * @brief Servo output configuration form for the config output gadget + *****************************************************************************/ +/* + * This program 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. + * + * This program 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 this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "outputchannelform.h" +#include "configoutputwidget.h" + +OutputChannelForm::OutputChannelForm(const int index, QWidget *parent, const bool showLegend) : + QWidget(parent), + ui(), + m_index(index), + m_inChannelTest(false) +{ + ui.setupUi(this); + if(!showLegend) + { + // Remove legend + QGridLayout *grid_layout = dynamic_cast(layout()); + Q_ASSERT(grid_layout); + for (int col = 0; col < grid_layout->columnCount(); col++) + { // remove every item in first row + QLayoutItem *item = grid_layout->itemAtPosition(0, col); + if (!item) continue; + // get widget from layout item + QWidget *legend_widget = item->widget(); + if (!legend_widget) continue; + // delete widget + grid_layout->removeWidget(legend_widget); + delete legend_widget; + } + } + + // The convention for OP is Channel 1 to Channel 10. + ui.actuatorNumber->setText(QString("%1:").arg(m_index+1)); + + // Register for ActuatorSettings changes: + connect(ui.actuatorMin, SIGNAL(editingFinished()), + this, SLOT(setChannelRange())); + connect(ui.actuatorMax, SIGNAL(editingFinished()), + this, SLOT(setChannelRange())); + connect(ui.actuatorRev, SIGNAL(toggled(bool)), + this, SLOT(reverseChannel(bool))); + // Now connect the channel out sliders to our signal to send updates in test mode + connect(ui.actuatorNeutral, SIGNAL(valueChanged(int)), + this, SLOT(sendChannelTest(int))); + + ui.actuatorLink->setChecked(false); + connect(ui.actuatorLink, SIGNAL(toggled(bool)), + this, SLOT(linkToggled(bool))); +} + +OutputChannelForm::~OutputChannelForm() +{ + // Do nothing +} + +/** + * Restrict UI to protect users from accidental misuse. + */ +void OutputChannelForm::enableChannelTest(bool state) +{ + if (m_inChannelTest == state) return; + m_inChannelTest = state; + + if(m_inChannelTest) + { + // Prevent stupid users from touching the minimum & maximum ranges while + // moving the sliders. Thanks Ivan for the tip :) + ui.actuatorMin->setEnabled(false); + ui.actuatorMax->setEnabled(false); + ui.actuatorRev->setEnabled(false); + } + else + { + ui.actuatorMin->setEnabled(true); + ui.actuatorMax->setEnabled(true); + ui.actuatorRev->setEnabled(true); + } +} + + +/** + * Toggles the channel linked state for use in testing mode + */ +void OutputChannelForm::linkToggled(bool state) +{ + Q_UNUSED(state) + + if (!m_inChannelTest) + return; // we are not in Test Output mode + + // find the minimum slider value for the linked ones + if (!parent()) return; + int min = 10000; + int linked_count = 0; + QList outputChannelForms = parent()->findChildren(); + // set the linked channels of the parent widget to the same value + foreach(OutputChannelForm *outputChannelForm, outputChannelForms) + { + if (!outputChannelForm->ui.actuatorLink->checkState()) + continue; + if (this == outputChannelForm) + continue; + int value = outputChannelForm->ui.actuatorNeutral->value(); + if(min > value) min = value; + linked_count++; + } + + if (linked_count <= 0) + return; // no linked channels + + // set the linked channels to the same value + foreach(OutputChannelForm *outputChannelForm, outputChannelForms) + { + if (!outputChannelForm->ui.actuatorLink->checkState()) + continue; + outputChannelForm->ui.actuatorNeutral->setValue(min); + } +} + +/** + * Set maximal channel value. + */ +void OutputChannelForm::max(int maximum) +{ + minmax(ui.actuatorMin->value(), maximum); +} + +/** + * Set minimal channel value. + */ +void OutputChannelForm::min(int minimum) +{ + minmax(minimum, ui.actuatorMax->value()); +} + +/** + * Set minimal and maximal channel value. + */ +void OutputChannelForm::minmax(int minimum, int maximum) +{ + ui.actuatorMin->setValue(minimum); + ui.actuatorMax->setValue(maximum); + setChannelRange(); + if(ui.actuatorMin->value() > ui.actuatorMax->value()) + ui.actuatorRev->setChecked(true); + else + ui.actuatorRev->setChecked(false); +} + +/** + * Set neutral of channel. + */ +void OutputChannelForm::neutral(int value) +{ + ui.actuatorNeutral->setValue(value); +} + +/** + * Set the channel assignment label. + */ +void OutputChannelForm::setAssignment(const QString &assignment) +{ + ui.actuatorName->setText(assignment); +} + +/** + * Sets the minimum/maximum value of the channel output sliders. + * Have to do it here because setMinimum is not a slot. + * + * One added trick: if the slider is at its min when the value + * is changed, then keep it on the min. + */ +void OutputChannelForm::setChannelRange() +{ + int oldMini = ui.actuatorNeutral->minimum(); +// int oldMaxi = ui.actuatorNeutral->maximum(); + + if (ui.actuatorMin->value() < ui.actuatorMax->value()) + { + ui.actuatorNeutral->setRange(ui.actuatorMin->value(), ui.actuatorMax->value()); + ui.actuatorRev->setChecked(false); + } + else + { + ui.actuatorNeutral->setRange(ui.actuatorMax->value(), ui.actuatorMin->value()); + ui.actuatorRev->setChecked(true); + } + + if (ui.actuatorNeutral->value() == oldMini) + ui.actuatorNeutral->setValue(ui.actuatorNeutral->minimum()); + +// if (ui.actuatorNeutral->value() == oldMaxi) +// ui.actuatorNeutral->setValue(ui.actuatorNeutral->maximum()); // this can be dangerous if it happens to be controlling a motor at the time! +} + +/** + * Reverses the channel when the checkbox is clicked + */ +void OutputChannelForm::reverseChannel(bool state) +{ + // Sanity check: if state became true, make sure the Maxvalue was higher than Minvalue + // the situations below can happen! + if (state && (ui.actuatorMax->value() < ui.actuatorMin->value())) + return; + if (!state && (ui.actuatorMax->value() > ui.actuatorMin->value())) + return; + + // Now, swap the min & max values (only on the spinboxes, the slider + // does not change! + int temp = ui.actuatorMax->value(); + ui.actuatorMax->setValue(ui.actuatorMin->value()); + ui.actuatorMin->setValue(temp); + + // Also update the channel value + // This is a trick to force the slider to update its value and + // emit the right signal itself, because our sendChannelTest(int) method + // relies on the object sender's identity. + if (ui.actuatorNeutral->value() < ui.actuatorNeutral->maximum()) + { + ui.actuatorNeutral->setValue(ui.actuatorNeutral->value()+1); + ui.actuatorNeutral->setValue(ui.actuatorNeutral->value()-1); + } + else + { + ui.actuatorNeutral->setValue(ui.actuatorNeutral->value()-1); + ui.actuatorNeutral->setValue(ui.actuatorNeutral->value()+1); + } +} + +/** + * Emits the channel value which will be send to the UAV to move the servo. + * Returns immediately if we are not in testing mode. + */ +void OutputChannelForm::sendChannelTest(int value) +{ + int in_value = value; + + QSlider *ob = (QSlider *)QObject::sender(); + if (!ob) return; + + if (ui.actuatorRev->isChecked()) + value = ui.actuatorMin->value() - value + ui.actuatorMax->value(); // the channel is reversed + + // update the label + ui.actuatorValue->setText(QString::number(value)); + + if (ui.actuatorLink->checkState() && parent()) + { // the channel is linked to other channels + QList outputChannelForms = parent()->findChildren(); + // set the linked channels of the parent widget to the same value + foreach(OutputChannelForm *outputChannelForm, outputChannelForms) + { + if (this == outputChannelForm) continue; + if (!outputChannelForm->ui.actuatorLink->checkState()) continue; + + int val = in_value; + if (val < outputChannelForm->ui.actuatorNeutral->minimum()) + val = outputChannelForm->ui.actuatorNeutral->minimum(); + if (val > outputChannelForm->ui.actuatorNeutral->maximum()) + val = outputChannelForm->ui.actuatorNeutral->maximum(); + + if (outputChannelForm->ui.actuatorNeutral->value() == val) continue; + + outputChannelForm->ui.actuatorNeutral->setValue(val); + outputChannelForm->ui.actuatorValue->setText(QString::number(val)); + } + } + + if (!m_inChannelTest) + return; // we are not in Test Output mode + + emit channelChanged(index(), value); +} diff --git a/ground/openpilotgcs/src/plugins/config/outputchannelform.h b/ground/openpilotgcs/src/plugins/config/outputchannelform.h new file mode 100644 index 000000000..e0cae75d2 --- /dev/null +++ b/ground/openpilotgcs/src/plugins/config/outputchannelform.h @@ -0,0 +1,92 @@ +/** + ****************************************************************************** + * + * @file outputchannelform.h + * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2011. + * @addtogroup GCSPlugins GCS Plugins + * @{ + * @addtogroup ConfigPlugin Config Plugin + * @{ + * @brief Servo output configuration form for the config output gadget + *****************************************************************************/ +/* + * This program 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. + * + * This program 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 this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef OUTPUTCHANNELFORM_H +#define OUTPUTCHANNELFORM_H + +#include +#include "ui_outputchannelform.h" + +class ConfigInputWidget; + +class OutputChannelForm : public QWidget +{ + Q_OBJECT + +public: + explicit OutputChannelForm(const int index, QWidget *parent = NULL, const bool showLegend = false); + ~OutputChannelForm(); + friend class ConfigInputWidget; + + void setAssignment(const QString &assignment); + int index() const; + +public slots: + void max(int maximum); + int max() const; + void min(int minimum); + int min() const; + void minmax(int minimum, int maximum); + void neutral(int value); + int neutral() const; + void enableChannelTest(bool state); + +signals: + void channelChanged(int index, int value); + +private: + Ui::outputChannelForm ui; + /// Channel index + int m_index; + bool m_inChannelTest; + +private slots: + void linkToggled(bool state); + void reverseChannel(bool state); + void sendChannelTest(int value); + void setChannelRange(); +}; + +inline int OutputChannelForm::index() const +{ + return m_index; +} + +inline int OutputChannelForm::max() const +{ + return ui.actuatorMax->value(); +} + +inline int OutputChannelForm::min() const +{ + return ui.actuatorMin->value(); +} + +inline int OutputChannelForm::neutral() const +{ + return ui.actuatorNeutral->value(); +} +#endif // OUTPUTCHANNELFORM_H diff --git a/ground/openpilotgcs/src/plugins/config/outputchannelform.ui b/ground/openpilotgcs/src/plugins/config/outputchannelform.ui new file mode 100644 index 000000000..b488872e3 --- /dev/null +++ b/ground/openpilotgcs/src/plugins/config/outputchannelform.ui @@ -0,0 +1,281 @@ + + + outputChannelForm + + + + 0 + 0 + 562 + 49 + + + + Form + + + + + + + 0 + 0 + + + + + 20 + 0 + + + + Channel Number + + + Qt::LeftToRight + + + TextLabel + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + + 90 + 0 + + + + TextLabel + + + Qt::AlignCenter + + + + + + + Minimum PWM value, beware of not overdriving your servo. + + + 9999 + + + + + + + Qt::Horizontal + + + QSizePolicy::Minimum + + + + 0 + 20 + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + 9999 + + + Qt::Horizontal + + + + + + + Qt::Horizontal + + + QSizePolicy::Minimum + + + + 5 + 20 + + + + + + + + Check to invert the channel. + + + + + + + Output mode + + + + + + + Maximum PWM value, beware of not overdriving your servo. + + + 9999 + + + + + + + + 0 + 0 + + + + Assignment + + + + + + + + 0 + 0 + + + + Min + + + + + + + + 0 + 0 + + + + Neutral (slowest for motor) + + + + + + + + 0 + 0 + + + + Max + + + + + + + + 0 + 0 + + + + Rev. + + + + + + + + 0 + 0 + + + + Link + + + + + + + + 0 + 0 + + + + Current value of slider. + + + 0000 + + + + + + + + 0 + 0 + + + + + 20 + 0 + + + + Qt::LeftToRight + + + # + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 0 + + + + + + + +