2 ******************************************************************************
4 * @file outputchannelform.cpp
5 * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2011.
6 * @addtogroup GCSPlugins GCS Plugins
8 * @addtogroup ConfigPlugin Config Plugin
10 * @brief Servo output configuration form for the config output gadget
11 *****************************************************************************/
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 3 of the License, or
16 * (at your option) any later version.
18 * This program is distributed in the hope that it will be useful, but
19 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
20 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
23 * You should have received a copy of the GNU General Public License along
24 * with this program; if not, write to the Free Software Foundation, Inc.,
25 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 #include "outputchannelform.h"
30 #include "ui_outputchannelform.h"
33 OutputChannelForm::OutputChannelForm(const int index
, QWidget
*parent
) :
34 ChannelForm(index
, parent
), ui(new Ui::outputChannelForm
), m_inChannelTest(false), m_updateChannelRangeEnabled(true)
38 // The convention for OP is Channel 1 to Channel 10.
39 ui
->actuatorNumber
->setText(QString("%1").arg(index
+ 1));
41 // Register for ActuatorSettings changes:
42 connect(ui
->actuatorMin
, SIGNAL(editingFinished()), this, SLOT(setChannelRange()));
43 connect(ui
->actuatorMax
, SIGNAL(editingFinished()), this, SLOT(setChannelRange()));
44 connect(ui
->actuatorRev
, SIGNAL(toggled(bool)), this, SLOT(reverseChannel(bool)));
45 // Now connect the channel out sliders to our signal to send updates in test mode
46 connect(ui
->actuatorNeutral
, SIGNAL(valueChanged(int)), this, SLOT(sendChannelTest(int)));
48 ui
->actuatorLink
->setChecked(false);
49 connect(ui
->actuatorLink
, SIGNAL(toggled(bool)), this, SLOT(linkToggled(bool)));
53 disableMouseWheelEvents();
56 OutputChannelForm::~OutputChannelForm()
61 QString
OutputChannelForm::name()
63 return ui
->actuatorName
->text();
66 QString
OutputChannelForm::bank()
68 return ui
->actuatorBankNumber
->text();
72 * Set the channel assignment label.
74 void OutputChannelForm::setName(const QString
&name
)
76 ui
->actuatorName
->setText(name
);
79 void OutputChannelForm::setColor(const QColor
&color
)
81 QString stylesheet
= ui
->actuatorNumberFrame
->styleSheet();
83 stylesheet
= stylesheet
.split("background-color").first();
85 QString("background-color: rgb(%1, %2, %3)")
86 .arg(color
.red()).arg(color
.green()).arg(color
.blue()));
87 ui
->actuatorNumberFrame
->setStyleSheet(stylesheet
);
91 * Set the channel bank label.
93 void OutputChannelForm::setBank(const QString
&bank
)
95 ui
->actuatorBankNumber
->setText(bank
);
99 * Restrict UI to protect users from accidental misuse.
101 void OutputChannelForm::enableChannelTest(bool state
)
103 if (m_inChannelTest
== state
) {
106 m_inChannelTest
= state
;
108 if (m_inChannelTest
) {
109 // Prevent stupid users from touching the minimum & maximum ranges while
110 // moving the sliders. Thanks Ivan for the tip :)
111 ui
->actuatorMin
->setEnabled(false);
112 ui
->actuatorMax
->setEnabled(false);
113 ui
->actuatorRev
->setEnabled(false);
114 } else if (!isDisabledOutput()) {
115 ui
->actuatorMin
->setEnabled(true);
116 ui
->actuatorMax
->setEnabled(true);
117 if (!isNormalMotorOutput()) {
118 ui
->actuatorRev
->setEnabled(true);
124 * Enable/Disable setChannelRange
126 void OutputChannelForm::setChannelRangeEnabled(bool state
)
128 if (m_updateChannelRangeEnabled
== state
) {
131 m_updateChannelRangeEnabled
= state
;
135 * Toggles the channel linked state for use in testing mode
137 void OutputChannelForm::linkToggled(bool state
)
141 if (!m_inChannelTest
) {
142 return; // we are not in Test Output mode
144 // find the minimum slider value for the linked ones
148 int min
= ui
->actuatorMax
->maximum();
149 int linked_count
= 0;
150 QList
<OutputChannelForm
*> outputChannelForms
= parent()->findChildren
<OutputChannelForm
*>();
151 // set the linked channels of the parent widget to the same value
152 foreach(OutputChannelForm
* outputChannelForm
, outputChannelForms
) {
153 if (!outputChannelForm
->ui
->actuatorLink
->checkState()) {
156 if (this == outputChannelForm
) {
159 int value
= outputChannelForm
->ui
->actuatorNeutral
->value();
166 if (linked_count
<= 0) {
167 return; // no linked channels
169 // set the linked channels to the same value
170 foreach(OutputChannelForm
* outputChannelForm
, outputChannelForms
) {
171 if (!outputChannelForm
->ui
->actuatorLink
->checkState()) {
174 outputChannelForm
->ui
->actuatorNeutral
->setValue(min
);
178 int OutputChannelForm::max() const
180 return ui
->actuatorMax
->value();
184 * Set maximal channel value.
186 void OutputChannelForm::setMax(int maximum
)
188 setRange(ui
->actuatorMax
->value(), maximum
);
191 int OutputChannelForm::min() const
193 return ui
->actuatorMin
->value();
197 * Set minimal channel value.
199 void OutputChannelForm::setMin(int minimum
)
201 setRange(minimum
, ui
->actuatorMin
->value());
204 int OutputChannelForm::neutral() const
206 return ui
->actuatorNeutral
->value();
210 * Set neutral of channel.
212 void OutputChannelForm::setNeutral(int value
)
214 ui
->actuatorNeutral
->setValue(value
);
221 void OutputChannelForm::setLimits(int actuatorMinMinimum
, int actuatorMinMaximum
, int actuatorMaxMinimum
, int actuatorMaxMaximum
)
223 ui
->actuatorMin
->setMaximum(actuatorMinMaximum
);
224 ui
->actuatorMax
->setMaximum(actuatorMaxMaximum
);
225 ui
->actuatorMin
->setMinimum(actuatorMinMinimum
);
226 ui
->actuatorMax
->setMinimum(actuatorMaxMinimum
);
227 // Neutral slider limits
228 ui
->actuatorNeutral
->setMinimum(actuatorMinMinimum
);
229 ui
->actuatorNeutral
->setMaximum(actuatorMaxMaximum
);
233 * Set minimal and maximal channel value.
235 void OutputChannelForm::setRange(int minimum
, int maximum
)
237 ui
->actuatorMin
->setValue(minimum
);
238 ui
->actuatorMax
->setValue(maximum
);
243 * Sets the minimum/maximum value of the channel output sliders.
244 * Have to do it here because setMinimum is not a slot.
246 * One added trick: if the slider is at its min when the value
247 * is changed, then keep it on the min.
249 void OutputChannelForm::setChannelRange()
251 // Disable outputs not already set in MixerSettings
252 if (isDisabledOutput()) {
253 setLimits(1000, 1000, 1000, 1000);
254 ui
->actuatorMin
->setValue(1000);
255 ui
->actuatorMax
->setValue(1000);
256 ui
->actuatorRev
->setChecked(false);
257 ui
->actuatorLink
->setChecked(false);
258 setControlsEnabled(false);
262 if (!m_updateChannelRangeEnabled
) {
263 // Nothing to do here
267 setControlsEnabled(true);
269 int minValue
= ui
->actuatorMin
->value();
270 int maxValue
= ui
->actuatorMax
->value();
272 int oldMini
= ui
->actuatorNeutral
->minimum();
273 int oldMaxi
= ui
->actuatorNeutral
->maximum();
275 // Red handle for Motors
276 if (isNormalMotorOutput() || isReversibleMotorOutput()) {
277 ui
->actuatorNeutral
->setStyleSheet("QSlider::handle:horizontal { background: rgb(255, 100, 100); width: 18px; height: 28px;"
278 "margin: -3px 0; border-radius: 3px; border: 1px solid #777; }");
280 ui
->actuatorNeutral
->setStyleSheet("QSlider::handle:horizontal { background: rgb(196, 196, 196); width: 18px; height: 28px;"
281 "margin: -3px 0; border-radius: 3px; border: 1px solid #777; }");
284 // Normal motor will be *** never *** reversed : without arming a "Min" value (like 1900) can be applied !
285 if (isNormalMotorOutput()) {
286 if (minValue
> maxValue
) {
288 ui
->actuatorMin
->setValue(oldMini
);
289 ui
->actuatorMax
->setValue(oldMaxi
);
291 ui
->actuatorRev
->setChecked(false);
292 ui
->actuatorRev
->setEnabled(false);
293 ui
->actuatorNeutral
->setInvertedAppearance(false);
294 ui
->actuatorNeutral
->setRange(ui
->actuatorMin
->value(), ui
->actuatorMax
->value());
296 // Others output (!Motor)
297 // Auto check reverse checkbox SpinBox Min/Max changes
298 ui
->actuatorRev
->setEnabled(true);
299 if (minValue
<= maxValue
) {
300 ui
->actuatorRev
->setChecked(false);
301 ui
->actuatorNeutral
->setInvertedAppearance(false);
302 ui
->actuatorNeutral
->setRange(minValue
, maxValue
);
304 ui
->actuatorRev
->setChecked(true);
305 ui
->actuatorNeutral
->setInvertedAppearance(true);
306 ui
->actuatorNeutral
->setRange(maxValue
, minValue
);
309 // If old neutral was Min, stay Min
310 if (ui
->actuatorNeutral
->value() == oldMini
) {
311 ui
->actuatorNeutral
->setValue(ui
->actuatorNeutral
->minimum());
316 * Reverses the channel when the checkbox is clicked
318 void OutputChannelForm::reverseChannel(bool state
)
320 // if 'state' (reverse channel requested) apply only if not already reversed
321 if ((state
&& (ui
->actuatorMax
->value() > ui
->actuatorMin
->value()))
322 || (!state
&& (ui
->actuatorMax
->value() < ui
->actuatorMin
->value()))) {
323 // Now, swap the min & max values (spin boxes)
324 int temp
= ui
->actuatorMax
->value();
325 ui
->actuatorMax
->setValue(ui
->actuatorMin
->value());
326 ui
->actuatorMin
->setValue(temp
);
327 ui
->actuatorNeutral
->setInvertedAppearance(state
);
335 * Enable/Disable UI controls
337 void OutputChannelForm::setControlsEnabled(bool state
)
339 if (isDisabledOutput()) {
342 ui
->actuatorMin
->setEnabled(state
);
343 ui
->actuatorMax
->setEnabled(state
);
344 ui
->actuatorValue
->setEnabled(state
);
345 ui
->actuatorLink
->setEnabled(state
);
346 // Reverse checkbox will be never checked
347 // or enabled for normal motor
348 if (isNormalMotorOutput()) {
349 ui
->actuatorRev
->setChecked(false);
350 ui
->actuatorRev
->setEnabled(false);
352 ui
->actuatorRev
->setEnabled(state
);
357 * Emits the channel value which will be send to the UAV to move the servo.
358 * Returns immediately if we are not in testing mode.
360 void OutputChannelForm::sendChannelTest(int value
)
362 int in_value
= value
;
364 QSlider
*ob
= (QSlider
*)QObject::sender();
371 ui
->actuatorValue
->setValue(value
);
373 if (ui
->actuatorLink
->checkState() && parent()) {
374 // the channel is linked to other channels
375 QList
<OutputChannelForm
*> outputChannelForms
= parent()->findChildren
<OutputChannelForm
*>();
376 // set the linked channels of the parent widget to the same value
377 foreach(OutputChannelForm
* outputChannelForm
, outputChannelForms
) {
378 if (this == outputChannelForm
) {
381 if (!outputChannelForm
->ui
->actuatorLink
->checkState()) {
386 if (val
< outputChannelForm
->ui
->actuatorNeutral
->minimum()) {
387 val
= outputChannelForm
->ui
->actuatorNeutral
->minimum();
389 if (val
> outputChannelForm
->ui
->actuatorNeutral
->maximum()) {
390 val
= outputChannelForm
->ui
->actuatorNeutral
->maximum();
393 if (outputChannelForm
->ui
->actuatorNeutral
->value() == val
) {
397 outputChannelForm
->ui
->actuatorNeutral
->setValue(val
);
398 outputChannelForm
->ui
->actuatorValue
->setValue(val
);
402 if (!m_inChannelTest
) {
403 // we are not in Test Output mode
406 emit
channelChanged(index(), value
);
411 * Returns current neutral value
413 int OutputChannelForm::neutralValue()
415 return ui
->actuatorNeutral
->value();
422 QString
OutputChannelForm::outputMixerType()
424 UAVDataObject
*mixer
= dynamic_cast<UAVDataObject
*>(getObjectManager()->getObject(QString("MixerSettings")));
428 QString mixerNumType
= QString("Mixer%1Type").arg(index() + 1);
429 UAVObjectField
*field
= mixer
->getField(mixerNumType
);
431 QString mixerType
= field
->getValue().toString();
438 * Returns true if a servo output
440 bool OutputChannelForm::isServoOutput()
442 return !isNormalMotorOutput() && !isReversibleMotorOutput() && !isDisabledOutput();
447 * Returns true if output is a normal Motor
449 bool OutputChannelForm::isNormalMotorOutput()
451 return outputMixerType() == "Motor";
456 * Returns true if output is a reversible Motor
458 bool OutputChannelForm::isReversibleMotorOutput()
460 return outputMixerType() == "ReversableMotor";
465 * Returns true if output is disabled
467 bool OutputChannelForm::isDisabledOutput()
469 return outputMixerType() == "Disabled";