Bsongis/issue 4400 (#4425)
[opentx.git] / companion / src / simulation / radiooutputswidget.cpp
blob607e4dd117132078816fdf9c576f45a877f46033
1 /*
2 * Copyright (C) OpenTX
4 * Based on code named
5 * th9x - http://code.google.com/p/th9x
6 * er9x - http://code.google.com/p/er9x
7 * gruvin9x - http://code.google.com/p/gruvin9x
9 * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
21 #include "radiooutputswidget.h"
22 #include "ui_radiooutputswidget.h"
24 #include "appdata.h"
25 #include "constants.h"
26 #include "eeprominterface.h"
27 #include "radiodata.h"
28 #include "simulator.h"
29 #include "simulatorinterface.h"
31 extern AppData g; // ensure what "g" means
33 const int RadioOutputsWidget::m_dataUpdateFreqDefault = 10; // ms
34 const quint16 RadioOutputsWidget::m_savedViewStateVersion = 1;
36 RadioOutputsWidget::RadioOutputsWidget(SimulatorInterface * simulator, Firmware * firmware, QWidget *parent) :
37 QWidget(parent),
38 m_simulator(simulator),
39 m_firmware(firmware),
40 m_tmrUpdateData(new QTimer),
41 m_radioProfileId(g.sessionId()),
42 m_started(false),
43 ui(new Ui::RadioOutputsWidget)
45 ui->setupUi(this);
47 restoreState();
49 m_dataUpdateFreq = m_dataUpdateFreqDefault;
50 m_tmrUpdateData->setInterval(m_dataUpdateFreq);
52 connect(m_tmrUpdateData, &QTimer::timeout, this, &RadioOutputsWidget::setValues);
55 RadioOutputsWidget::~RadioOutputsWidget()
57 stop();
58 saveState();
59 if (m_tmrUpdateData)
60 delete m_tmrUpdateData;
61 delete ui;
64 void RadioOutputsWidget::changeEvent(QEvent *e)
66 QWidget::changeEvent(e);
67 switch (e->type()) {
68 case QEvent::LanguageChange:
69 ui->retranslateUi(this);
70 break;
71 default:
72 break;
76 void RadioOutputsWidget::showEvent(QShowEvent * event)
78 if (m_started)
79 m_tmrUpdateData->start();
82 void RadioOutputsWidget::hideEvent(QHideEvent * event)
84 m_tmrUpdateData->stop();
87 void RadioOutputsWidget::start()
89 setupChannelsDisplay();
90 setupGVarsDisplay();
91 setupLsDisplay();
92 m_tmrUpdateData->start();
93 m_started = true;
96 void RadioOutputsWidget::stop()
98 m_tmrUpdateData->stop();
99 m_started = false;
102 void RadioOutputsWidget::restart()
104 stop();
105 start();
108 void RadioOutputsWidget::saveState()
110 QByteArray state;
111 QDataStream stream(&state, QIODevice::WriteOnly);
112 stream << m_savedViewStateVersion
113 << ui->btnLogiSw->isChecked() << ui->btnGlobalVars->isChecked() << ui->btnChannels->isChecked()
114 << ui->splitter->saveState();
116 SimulatorOptions opts = g.profile[m_radioProfileId].simulatorOptions();
117 opts.radioOutputsState = state;
118 g.profile[m_radioProfileId].simulatorOptions(opts);
121 void RadioOutputsWidget::restoreState()
123 quint16 ver = 0;
124 QByteArray splitterState;
125 bool ls = true, gv = true, ch = true;
126 QByteArray state = g.profile[m_radioProfileId].simulatorOptions().radioOutputsState;
127 QDataStream stream(state);
129 stream >> ver;
130 if (ver && ver <= m_savedViewStateVersion)
131 stream >> ls >> gv >> ch >> splitterState;
133 ui->btnLogiSw->setChecked(ls);
134 ui->btnGlobalVars->setChecked(gv);
135 ui->btnChannels->setChecked(ch);
136 if (!splitterState.isEmpty())
137 ui->splitter->restoreState(splitterState);
141 void RadioOutputsWidget::setupChannelsDisplay()
143 int outputs = std::min(32, m_firmware->getCapability(Capability(Outputs)));
145 // delete old widgets if already exist
146 m_channelsMap.clear();
148 QWidget * oldChanW = ui->channelsScroll->takeWidget();
149 if (oldChanW)
150 oldChanW->deleteLater();
152 if (!outputs)
153 return;
155 QWidget * channelsWidget = new QWidget();
156 QGridLayout * channelsLayout = new QGridLayout(channelsWidget);
157 channelsLayout->setHorizontalSpacing(4);
158 channelsLayout->setVerticalSpacing(3);
159 channelsLayout->setContentsMargins(5, 5, 5, 5);
161 ui->channelsScroll->setWidget(channelsWidget);
163 // populate outputs
164 int column = 0;
165 for (int i=0; i < outputs; i++) {
166 QLabel * label = new QLabel(channelsWidget);
167 label->setText(" " + RawSource(SOURCE_TYPE_CH, i).toString() + " ");
168 label->setAlignment(Qt::AlignCenter);
169 label->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
170 channelsLayout->addWidget(label, 0, column, 1, 1);
172 QSlider * slider = new QSlider(channelsWidget);
173 slider->setEnabled(false);
174 slider->setMinimum(-1024);
175 slider->setMaximum(1024);
176 slider->setPageStep(128);
177 slider->setTracking(false);
178 slider->setOrientation(Qt::Vertical);
179 slider->setInvertedAppearance(false);
180 slider->setTickPosition(QSlider::TicksRight);
181 slider->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
182 channelsLayout->addWidget(slider, 2, column, 1, 1);
183 channelsLayout->setAlignment(slider, Qt::AlignHCenter);
185 QLabel * value = new QLabel(channelsWidget);
186 value->setMinimumSize(QSize(value->fontMetrics().size(Qt::TextSingleLine, "-100.0").width(), 0));
187 value->setAlignment(Qt::AlignCenter);
188 value->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
189 channelsLayout->addWidget(value, 1, column, 1, 1);
191 ++column;
193 m_channelsMap.insert(i, QPair<QLabel *, QSlider *>(value, slider));
197 void RadioOutputsWidget::setupGVarsDisplay()
199 int gvars = m_firmware->getCapability(Capability(Gvars));
200 int fmodes = m_firmware->getCapability(Capability(FlightModes));
202 // delete old widgets if already exist
203 m_globalVarsMap.clear();
205 QWidget * oldGv = ui->globalVarsScroll->takeWidget();
206 if (oldGv)
207 oldGv->deleteLater();
209 if (!gvars)
210 return;
212 QWidget * gvarsWidget = new QWidget();
213 QGridLayout * gvarsLayout = new QGridLayout(gvarsWidget);
214 gvarsLayout->setContentsMargins(5, 5, 5, 5);
215 gvarsLayout->setHorizontalSpacing(6);
216 gvarsLayout->setVerticalSpacing(3);
217 ui->globalVarsScroll->setWidget(gvarsWidget);
219 QPalette::ColorRole bgrole = QPalette::AlternateBase;
220 for (int fm=0; fm < fmodes; fm++) {
221 QLabel * label = new QLabel(gvarsWidget);
222 label->setText(QString("FM%1").arg(fm));
223 label->setAlignment(Qt::AlignCenter);
224 label->setBackgroundRole(bgrole);
225 gvarsLayout->addWidget(label, 0, fm+1);
227 QHash<int, QLabel *> fmMap;
228 for (int gv=0; gv < gvars; gv++) {
229 bgrole = ((gv % 2) ? QPalette::Background : QPalette::AlternateBase);
230 QLabel * label = new QLabel(gvarsWidget);
231 label->setText(QString("GV%1").arg(gv+1));
232 label->setAutoFillBackground(true);
233 label->setBackgroundRole(bgrole);
234 gvarsLayout->addWidget(label, gv+1, 0);
235 for (int fm=0; fm < fmodes; fm++) {
236 QLabel * value = new QLabel(gvarsWidget);
237 value->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
238 value->setAutoFillBackground(true);
239 value->setBackgroundRole(bgrole);
240 value->setText("0");
241 value->setStyleSheet("padding-right: .06em;");
242 gvarsLayout->addWidget(value, gv+1, fm+1);
244 fmMap.insert(fm, value);
246 m_globalVarsMap.insert(gv, fmMap);
250 void RadioOutputsWidget::setupLsDisplay()
252 int switches = m_firmware->getCapability(LogicalSwitches);
254 // delete old widgets if already exist
255 m_logicSwitchMap.clear();
257 QWidget * oldLsW = ui->logicalSwitchesScroll->takeWidget();
258 if (oldLsW)
259 oldLsW->deleteLater();
261 if (!switches)
262 return;
264 QWidget * logicalSwitches = new QWidget();
265 logicalSwitches->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum);
266 QGridLayout * logicalSwitchesLayout = new QGridLayout(logicalSwitches);
267 logicalSwitchesLayout->setHorizontalSpacing(3);
268 logicalSwitchesLayout->setVerticalSpacing(2);
269 logicalSwitchesLayout->setContentsMargins(5, 5, 5, 5);
270 ui->logicalSwitchesScroll->setWidget(logicalSwitches);
272 // populate logical switches
273 int rows = switches / (switches > 16 ? 4 : 2);
274 for (int i=0; i < switches; i++) {
275 logicalSwitchesLayout->addWidget(createLogicalSwitch(logicalSwitches, i), i / rows, i % rows, 1, 1);
279 QWidget * RadioOutputsWidget::createLogicalSwitch(QWidget * parent, int switchNo)
281 QLabel * swtch = new QLabel(parent);
282 swtch->setAutoFillBackground(true);
283 swtch->setFrameStyle(QFrame::Panel | QFrame::Raised);
284 swtch->setLineWidth(2);
285 swtch->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
286 QFont font = swtch->font();
287 font.setBold(true);
288 swtch->setFont(font);
289 swtch->setMinimumWidth(swtch->fontMetrics().width("99") + 10);
290 font.setBold(false);
291 swtch->setFont(font);
292 swtch->setText(QString("%1").arg(RawSwitch(SWITCH_TYPE_VIRTUAL, switchNo+1).toString().remove("L"), 2, QLatin1Char('0')));
293 swtch->setAlignment(Qt::AlignCenter);
294 m_logicSwitchMap.insert(switchNo, swtch);
295 return swtch;
298 // Read various values from firmware simulator and populate values in this UI
299 void RadioOutputsWidget::setValues()
301 static int lastPhase = -1;
302 static TxOutputs prevOutputs = TxOutputs();
303 static bool firstGvarsRun = true;
304 int currentPhase;
305 TxOutputs outputs;
306 QFont font;
308 m_simulator->getValues(outputs);
309 currentPhase = m_simulator->getPhase();
311 if (ui->channelsWidget->isVisible()) {
312 QHash<int, QPair<QLabel *, QSlider *> >::const_iterator ch;
313 for (ch = m_channelsMap.constBegin(); ch != m_channelsMap.constEnd(); ++ch) {
314 if (ch.key() >= CPN_MAX_CHNOUT)
315 continue;
316 ch.value().first->setText(QString("%1%").arg(outputs.chans[ch.key()] * 100 / 1024));
317 ch.value().second->setValue(qMin(1024, qMax(-1024, outputs.chans[ch.key()])));
321 if (ui->logicalSwitchesWidget->isVisible()) {
322 QHash<int, QLabel* >::const_iterator ls;
323 for (ls = m_logicSwitchMap.constBegin(); ls != m_logicSwitchMap.constEnd(); ++ls) {
324 if (ls.key() >= CPN_MAX_CSW || prevOutputs.vsw[ls.key()] == outputs.vsw[ls.key()])
325 continue;
326 ls.value()->setBackgroundRole(outputs.vsw[ls.key()] ? QPalette::Dark : QPalette::Background);
327 ls.value()->setForegroundRole(outputs.vsw[ls.key()] ? QPalette::BrightText : QPalette::WindowText);
328 ls.value()->setFrameShadow(outputs.vsw[ls.key()] ? QFrame::Sunken : QFrame::Raised);
329 font = ls.value()->font();
330 font.setBold(outputs.vsw[ls.key()]);
331 ls.value()->setFont(font);
332 prevOutputs.vsw[ls.key()] = outputs.vsw[ls.key()];
336 if (ui->globalVarsWidget->isVisible()) {
337 QPalette::ColorRole bgrole;
338 QHash<int, QHash<int, QLabel *> >::const_iterator gv;
339 QHash<int, QLabel *>::const_iterator fm;
340 for (gv = m_globalVarsMap.constBegin(); gv != m_globalVarsMap.constEnd(); ++gv) {
341 if (gv.key() >= CPN_MAX_GVARS)
342 continue;
343 for (fm = gv.value().constBegin(); fm != gv.value().constEnd(); ++fm) {
344 if (fm.key() >= CPN_MAX_FLIGHT_MODES)
345 continue;
346 if (currentPhase != lastPhase || prevOutputs.gvars[fm.key()][gv.key()] != outputs.gvars[fm.key()][gv.key()] || firstGvarsRun) {
347 if (fm.key() == currentPhase)
348 bgrole = QPalette::Dark;
349 else
350 bgrole = ((gv.key() % 2) ? QPalette::Background : QPalette::AlternateBase);
351 fm.value()->setBackgroundRole(bgrole);
352 fm.value()->setForegroundRole(fm.key() == currentPhase ? QPalette::BrightText : QPalette::WindowText);
353 font = fm.value()->font();
354 font.setBold(fm.key() == currentPhase);
355 fm.value()->setFont(font);
356 fm.value()->setText(QString::number(outputs.gvars[fm.key()][gv.key()]));
357 prevOutputs.gvars[fm.key()][gv.key()] = outputs.gvars[fm.key()][gv.key()];
358 firstGvarsRun = false;
364 lastPhase = currentPhase;