mirror of
https://github.com/EdgeTX/edgetx.git
synced 2025-07-22 07:45:12 +03:00
[Simulator] Asynchronous SimulatorInterface & a few new features. (#4738)
* [Simulator] Create RadioKeyWidget class for UI buttons & refactor ButtonsWidget; Refactor SimulatedUIWidget (and subtypes) to use new RadioKeyWidgets/ButtonsWidget; Centralize help text for key mappings and get creative with some icons; Simplify some radio UI setups with rectangular buttons. * [Simulator] Convert all simulator data I/O to signals/slots mechanism: * SimulatorInterface/OpenTxSimulator: - Now inherits from QObject to allow signal/slot interface; - Allows data exchange on a per-item basis (eg. each I/O value is treated separately instead of sending whole arrays or structs of data); - Checks for data changes and only emits signals when change is detected (GUI can now assume only new values are being sent); - Manages its own 10ms timer (doesn't rely on GUI to do that); - Sends "heartbeat" signals @ 1Hz for status monitoring; * Simulator GUI: - All data is exchanged between GUI elements as well as SimulatorInterface via signals/slots using standardized methods; - Data is sent immediately, and only, when actually changed (eg. a control is moved) instead of in bulk at specific time intervals; - Similarly, an asynchronous method is used for reading incoming data, w/out timers or loops; - Improve VirtualJoystickWidget to be more encapsulated and configurable; - Pause telemetry simulator if window is hidden; * [Simulator] Move SimulatorInterface instance to separate thread, ensure safe asynchronous operations & proper timer interactions; Protect/remove some functions, & reorganize the order (cosmetics). * [Simulator] Traces are now delivered to OpenTxSimulator and one or more QIODevice(s) can be added as recipient(s); Add SimulatorInterface::getCapability() for compile-time settings; Remove reversed POT1/SLIDER1 mixer exception (Taranis) requirement for SIMU; Fix plus/minus key delay on wheel event w/out encoder. * [Simulator] Add current knob/slider/trim input value in tool-tips (KnobWidget and SliderWidget). * [Simulator] Fix trims widget internal value not properly updating, and remove trim influence on virtual joystick X/Y value display (closes #4671). * [SimulatorInterface] Add handling of transmitter input voltage, including a rough conversion of volts to ADC value for different boards, and default battery volts lookup function; Clear analogs array before starting. * [Simulator] Add SimulatorInterface::init() method to separate pre-startup tasks; Report actual trim range, not just extended on/off; Change how radio widget states are restored; VirtualJoystickWidget: Connect trim changes directly from simulator, connect joystick events directly, report stick mode directly instead of setting values/constraints externally. * [Simulator] Calculate default Tx V input based on configured range in radio settings (or warning V+2 for radios which don't support a range). * [Simulator] Add functional aux. trims for Horus (closes #4699). * [Companion] Remove problematic QMessageLogContext from AppDebugMessageHandler::messageOutput(). * [Simulator] Prevent trim change via slider if disabled for flight mode (closes #4600). * [OpenTxSimulator] Fixes for Qt < 5.4. * [OpenTxSimulator] Fix slot name.
This commit is contained in:
parent
36bb951314
commit
57dc0159d6
46 changed files with 2064 additions and 1327 deletions
|
@ -24,12 +24,13 @@
|
|||
#include "appdata.h"
|
||||
#include "appdebugmessagehandler.h"
|
||||
#include "radiofaderwidget.h"
|
||||
#include "radiokeywidget.h"
|
||||
#include "radioknobwidget.h"
|
||||
#include "radioswitchwidget.h"
|
||||
#include "radiotrimwidget.h"
|
||||
#include "radiouiaction.h"
|
||||
#include "sdcard.h"
|
||||
#include "simulateduiwidget.h"
|
||||
#include "simulatorinterface.h"
|
||||
#include "storage.h"
|
||||
#include "virtualjoystickwidget.h"
|
||||
#ifdef JOYSTICKS
|
||||
|
@ -41,36 +42,27 @@
|
|||
#include <QMessageBox>
|
||||
#include <iostream>
|
||||
|
||||
SimulatorWidget::SimulatorWidget(QWidget * parent, SimulatorInterface *simulator, quint8 flags):
|
||||
using namespace Simulator;
|
||||
|
||||
SimulatorWidget::SimulatorWidget(QWidget * parent, SimulatorInterface * simulator, quint8 flags):
|
||||
QWidget(parent),
|
||||
ui(new Ui::SimulatorWidget),
|
||||
simulator(simulator),
|
||||
firmware(getCurrentFirmware()),
|
||||
radioSettings(GeneralSettings()),
|
||||
timer(new QTimer(this)),
|
||||
radioUiWidget(NULL),
|
||||
vJoyLeft(NULL),
|
||||
vJoyRight(NULL),
|
||||
m_board(getCurrentBoard()),
|
||||
flags(flags),
|
||||
lastPhase(-1),
|
||||
buttonPressed(0),
|
||||
trimPressed(255),
|
||||
startupFromFile(false),
|
||||
deleteTempRadioData(false),
|
||||
saveTempRadioData(false),
|
||||
middleButtonPressed(false),
|
||||
firstShow(true)
|
||||
{
|
||||
|
||||
saveTempRadioData(false)
|
||||
#ifdef JOYSTICKS
|
||||
joystick = NULL;
|
||||
, joystick(NULL)
|
||||
#endif
|
||||
|
||||
// defaults
|
||||
setRadioProfileId(g.sessionId());
|
||||
setSdPath(g.profile[radioProfileId].sdPath());
|
||||
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
windowName = tr("Radio Simulator (%1)").arg(firmware->getName());
|
||||
|
@ -96,7 +88,7 @@ SimulatorWidget::SimulatorWidget(QWidget * parent, SimulatorInterface *simulator
|
|||
break;
|
||||
}
|
||||
|
||||
foreach (keymapHelp_t item, *radioUiWidget->getKeymapHelp())
|
||||
foreach (keymapHelp_t item, radioUiWidget->getKeymapHelp())
|
||||
keymapHelp.append(item);
|
||||
|
||||
ui->radioUiWidget->layout()->removeItem(ui->radioUiTempSpacer);
|
||||
|
@ -105,6 +97,7 @@ SimulatorWidget::SimulatorWidget(QWidget * parent, SimulatorInterface *simulator
|
|||
radioUiWidget->setFocusPolicy(Qt::WheelFocus);
|
||||
radioUiWidget->setFocus();
|
||||
|
||||
connect(radioUiWidget, &SimulatedUIWidget::controlValueChange, this, &SimulatorWidget::onRadioWidgetValueChange);
|
||||
connect(radioUiWidget, &SimulatedUIWidget::customStyleRequest, this, &SimulatorWidget::setUiAreaStyle);
|
||||
|
||||
vJoyLeft = new VirtualJoystickWidget(this, 'L');
|
||||
|
@ -113,25 +106,43 @@ SimulatorWidget::SimulatorWidget(QWidget * parent, SimulatorInterface *simulator
|
|||
vJoyRight = new VirtualJoystickWidget(this, 'R');
|
||||
ui->rightStickLayout->addWidget(vJoyRight);
|
||||
|
||||
connect(vJoyLeft, &VirtualJoystickWidget::trimButtonPressed, this, &SimulatorWidget::onTrimPressed);
|
||||
connect(vJoyLeft, &VirtualJoystickWidget::trimButtonReleased, this, &SimulatorWidget::onTrimReleased);
|
||||
connect(vJoyLeft, &VirtualJoystickWidget::trimSliderMoved, this, &SimulatorWidget::onTrimSliderMoved);
|
||||
connect(vJoyLeft, &VirtualJoystickWidget::valueChange, this, &SimulatorWidget::onRadioWidgetValueChange);
|
||||
connect(vJoyRight, &VirtualJoystickWidget::valueChange, this, &SimulatorWidget::onRadioWidgetValueChange);
|
||||
connect(this, &SimulatorWidget::stickModeChange, vJoyLeft, &VirtualJoystickWidget::loadDefaultsForMode);
|
||||
connect(this, &SimulatorWidget::stickModeChange, vJoyRight, &VirtualJoystickWidget::loadDefaultsForMode);
|
||||
connect(simulator, &SimulatorInterface::trimValueChange, vJoyLeft, &VirtualJoystickWidget::setTrimValue);
|
||||
connect(simulator, &SimulatorInterface::trimValueChange, vJoyRight, &VirtualJoystickWidget::setTrimValue);
|
||||
connect(simulator, &SimulatorInterface::trimRangeChange, vJoyLeft, &VirtualJoystickWidget::setTrimRange);
|
||||
connect(simulator, &SimulatorInterface::trimRangeChange, vJoyRight, &VirtualJoystickWidget::setTrimRange);
|
||||
|
||||
connect(vJoyRight, &VirtualJoystickWidget::trimButtonPressed, this, &SimulatorWidget::onTrimPressed);
|
||||
connect(vJoyRight, &VirtualJoystickWidget::trimButtonReleased, this, &SimulatorWidget::onTrimReleased);
|
||||
connect(vJoyRight, &VirtualJoystickWidget::trimSliderMoved, this, &SimulatorWidget::onTrimSliderMoved);
|
||||
connect(this, &SimulatorWidget::simulatorInit, simulator, &SimulatorInterface::init);
|
||||
connect(this, &SimulatorWidget::simulatorStart, simulator, &SimulatorInterface::start);
|
||||
connect(this, &SimulatorWidget::simulatorStop, simulator, &SimulatorInterface::stop);
|
||||
//connect(this, &SimulatorWidget::simulatorSetData, simulator, &SimulatorInterface::setRadioData);
|
||||
connect(this, &SimulatorWidget::inputValueChange, simulator, &SimulatorInterface::setInputValue);
|
||||
connect(this, &SimulatorWidget::simulatorSdPathChange, simulator, &SimulatorInterface::setSdPath);
|
||||
connect(this, &SimulatorWidget::simulatorVolumeGainChange, simulator, &SimulatorInterface::setVolumeGain);
|
||||
|
||||
timer->setInterval(10);
|
||||
connect(timer, SIGNAL(timeout()), this, SLOT(onTimerEvent()));
|
||||
connect(timer, SIGNAL(timeout()), radioUiWidget, SLOT(updateUi()));
|
||||
connect(simulator, &SimulatorInterface::started, this, &SimulatorWidget::onSimulatorStarted);
|
||||
connect(simulator, &SimulatorInterface::stopped, this, &SimulatorWidget::onSimulatorStopped);
|
||||
connect(simulator, &SimulatorInterface::heartbeat, this, &SimulatorWidget::onSimulatorHeartbeat);
|
||||
connect(simulator, &SimulatorInterface::runtimeError, this, &SimulatorWidget::onSimulatorError);
|
||||
connect(simulator, &SimulatorInterface::phaseChanged, this, &SimulatorWidget::onPhaseChanged);
|
||||
|
||||
m_timer.setInterval(SIMULATOR_INTERFACE_HEARTBEAT_PERIOD * 6);
|
||||
connect(&m_timer, &QTimer::timeout, this, &SimulatorWidget::onTimerEvent);
|
||||
|
||||
setupJoysticks();
|
||||
|
||||
// defaults
|
||||
setRadioProfileId(g.sessionId());
|
||||
setSdPath(g.profile[radioProfileId].sdPath());
|
||||
}
|
||||
|
||||
SimulatorWidget::~SimulatorWidget()
|
||||
{
|
||||
shutdown();
|
||||
|
||||
if (timer)
|
||||
timer->deleteLater();
|
||||
if (radioUiWidget)
|
||||
delete radioUiWidget;
|
||||
if (vJoyLeft)
|
||||
|
@ -142,10 +153,7 @@ SimulatorWidget::~SimulatorWidget()
|
|||
if (joystick)
|
||||
delete joystick;
|
||||
#endif
|
||||
|
||||
firmware = NULL; // Not sure we should delete this but at least release our pointer.
|
||||
// NOTE : <simulator> should be deleted (or not) in the parent process which gave it to us in the first place.
|
||||
|
||||
firmware = NULL;
|
||||
delete ui;
|
||||
}
|
||||
|
||||
|
@ -168,8 +176,7 @@ void SimulatorWidget::setPaths(const QString & sdPath, const QString & dataPath)
|
|||
{
|
||||
sdCardPath = sdPath;
|
||||
radioDataPath = dataPath;
|
||||
if (simulator)
|
||||
simulator->setSdPath(sdPath, dataPath);
|
||||
emit simulatorSdPathChange(sdPath, dataPath);
|
||||
}
|
||||
|
||||
void SimulatorWidget::setRadioSettings(const GeneralSettings settings)
|
||||
|
@ -264,7 +271,7 @@ bool SimulatorWidget::setRadioData(RadioData * radioData)
|
|||
|
||||
if (ret) {
|
||||
if (radioDataPath.isEmpty()) {
|
||||
startupData.fill(0, getEEpromSize(m_board));
|
||||
startupData.fill(0, Boards::getEEpromSize(m_board));
|
||||
if (firmware->getEEpromInterface()->save((uint8_t *)startupData.data(), *radioData, 0, firmware->getCapability(SimulatorVariant)) <= 0) {
|
||||
ret = false;
|
||||
}
|
||||
|
@ -410,8 +417,7 @@ void SimulatorWidget::deleteTempData()
|
|||
void SimulatorWidget::saveState()
|
||||
{
|
||||
SimulatorOptions opts = g.profile[radioProfileId].simulatorOptions();
|
||||
//opts.windowGeometry = saveGeometry();
|
||||
opts.controlsState = saveRadioWidgetsState();
|
||||
saveRadioWidgetsState(opts.controlsState);
|
||||
g.profile[radioProfileId].simulatorOptions(opts);
|
||||
}
|
||||
|
||||
|
@ -432,35 +438,58 @@ void SimulatorWidget::captureScreenshot(bool)
|
|||
|
||||
void SimulatorWidget::start()
|
||||
{
|
||||
emit simulatorInit(); // init simulator default I/O values
|
||||
|
||||
setupRadioWidgets();
|
||||
setupJoysticks();
|
||||
restoreRadioWidgetsState();
|
||||
|
||||
if (startupData.isEmpty())
|
||||
simulator->start((const char *)0);
|
||||
else if (startupFromFile)
|
||||
simulator->start(startupData.constData());
|
||||
else
|
||||
simulator->start(startupData, (flags & SIMULATOR_FLAGS_NOTX) ? false : true);
|
||||
|
||||
setTrims();
|
||||
getValues();
|
||||
setupTimer();
|
||||
bool tests = !(flags & SIMULATOR_FLAGS_NOTX);
|
||||
if (!startupData.isEmpty()) {
|
||||
if (startupFromFile) {
|
||||
emit simulatorStart(startupData.constData(), tests);
|
||||
}
|
||||
else {
|
||||
simulator->setRadioData(startupData);
|
||||
emit simulatorStart((const char *)0, tests);
|
||||
}
|
||||
}
|
||||
else {
|
||||
emit simulatorStart((const char *)0, tests);
|
||||
}
|
||||
}
|
||||
|
||||
void SimulatorWidget::stop()
|
||||
{
|
||||
if (timer)
|
||||
timer->stop();
|
||||
if (simulator) {
|
||||
simulator->stop();
|
||||
if (saveTempRadioData) {
|
||||
startupData.fill(0, getEEpromSize(m_board));
|
||||
simulator->readEepromData(startupData);
|
||||
emit simulatorStop();
|
||||
QElapsedTimer tmout;
|
||||
tmout.start();
|
||||
// block until simulator stops or times out
|
||||
while (simulator->isRunning()) {
|
||||
if (tmout.hasExpired(2000)) {
|
||||
onSimulatorError("Timeout while trying to stop simulation!");
|
||||
onSimulatorStopped();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SimulatorWidget::onSimulatorStarted()
|
||||
{
|
||||
m_heartbeatTimer.start();
|
||||
m_timer.start();
|
||||
}
|
||||
|
||||
void SimulatorWidget::onSimulatorStopped()
|
||||
{
|
||||
m_timer.stop();
|
||||
m_heartbeatTimer.invalidate();
|
||||
|
||||
if (simulator && !simulator->isRunning() && saveTempRadioData) {
|
||||
startupData.fill(0, getEEpromSize(m_board));
|
||||
simulator->readRadioData(startupData);
|
||||
}
|
||||
}
|
||||
|
||||
void SimulatorWidget::restart()
|
||||
{
|
||||
stop();
|
||||
|
@ -486,100 +515,112 @@ void SimulatorWidget::shutdown()
|
|||
void SimulatorWidget::setRadioProfileId(int value)
|
||||
{
|
||||
radioProfileId = value;
|
||||
if (simulator)
|
||||
simulator->setVolumeGain(g.profile[radioProfileId].volumeGain());
|
||||
emit simulatorVolumeGainChange(g.profile[radioProfileId].volumeGain());
|
||||
}
|
||||
|
||||
void SimulatorWidget::setupRadioWidgets()
|
||||
{
|
||||
int i, midpos, aIdx;
|
||||
QString wname;
|
||||
Board::Type board = firmware->getBoard();
|
||||
int i, midpos;
|
||||
const int ttlSticks = Boards::getCapability(m_board, Board::Sticks);
|
||||
const int ttlSwitches = Boards::getCapability(m_board, Board::Switches);
|
||||
const int ttlKnobs = Boards::getCapability(m_board, Board::Pots);
|
||||
const int ttlFaders = Boards::getCapability(m_board, Board::Sliders);
|
||||
const int extraTrims = Boards::getCapability(m_board, Board::NumTrims) - ttlSticks;
|
||||
|
||||
// First clear out any existing widgets.
|
||||
if (switches.size()) {
|
||||
foreach (RadioWidget * w, switches) {
|
||||
ui->radioWidgetsHTLayout->removeWidget(w);
|
||||
w->deleteLater();
|
||||
foreach (RadioWidget * rw, m_radioWidgets) {
|
||||
switch(rw->getType()) {
|
||||
case RadioWidget::RADIO_WIDGET_SWITCH :
|
||||
case RadioWidget::RADIO_WIDGET_KNOB :
|
||||
ui->radioWidgetsHTLayout->removeWidget(rw);
|
||||
break;
|
||||
case RadioWidget::RADIO_WIDGET_FADER :
|
||||
case RadioWidget::RADIO_WIDGET_TRIM :
|
||||
ui->VCGridLayout->removeWidget(rw);
|
||||
break;
|
||||
default :
|
||||
break;
|
||||
}
|
||||
switches.clear();
|
||||
}
|
||||
if (analogs.size()) {
|
||||
foreach (RadioWidget * w, analogs) {
|
||||
if (w->getType() == RadioWidget::RADIO_WIDGET_KNOB)
|
||||
ui->radioWidgetsHTLayout->removeWidget(w);
|
||||
else
|
||||
ui->VCGridLayout->removeWidget(w);
|
||||
w->deleteLater();
|
||||
}
|
||||
analogs.clear();
|
||||
disconnect(rw, 0, this, 0);
|
||||
disconnect(this, 0, rw, 0);
|
||||
rw->deleteLater();
|
||||
}
|
||||
m_radioWidgets.clear();
|
||||
|
||||
// Now set up new widgets.
|
||||
|
||||
// switches
|
||||
Board::SwitchInfo switchInfo;
|
||||
Board::SwitchType swcfg;
|
||||
for (i = 0; i < getBoardCapability(board, Board::Switches) && i < CPN_MAX_SWITCHES; ++i) {
|
||||
for (i = 0; i < ttlSwitches; ++i) {
|
||||
if (radioSettings.switchConfig[i] == Board::SWITCH_NOT_AVAILABLE)
|
||||
continue;
|
||||
|
||||
swcfg = Board::SwitchType(radioSettings.switchConfig[i]);
|
||||
|
||||
if ((wname = QString(radioSettings.switchName[i])).isEmpty()) {
|
||||
switchInfo = getSwitchInfo(board, i);
|
||||
wname = QString(switchInfo.name);
|
||||
}
|
||||
wname = RawSource(RawSourceType::SOURCE_TYPE_SWITCH, i).toString(NULL, &radioSettings);
|
||||
RadioSwitchWidget * sw = new RadioSwitchWidget(swcfg, wname, -1, ui->radioWidgetsHT);
|
||||
sw->setIndex(i);
|
||||
ui->radioWidgetsHTLayout->addWidget(sw);
|
||||
switches.append(sw);
|
||||
|
||||
m_radioWidgets.append(sw);
|
||||
}
|
||||
|
||||
midpos = (int)floorf(switches.size() / 2.0f);
|
||||
aIdx = 0;
|
||||
midpos = (int)floorf(m_radioWidgets.size() / 2.0f);
|
||||
|
||||
// pots in middle of switches
|
||||
for (i = 0; i < getBoardCapability(board, Board::Pots) && i < CPN_MAX_POTS; ++i) {
|
||||
for (i = 0; i < ttlKnobs; ++i) {
|
||||
if (!radioSettings.isPotAvailable(i))
|
||||
continue;
|
||||
|
||||
if ((wname = QString(radioSettings.potName[i])).isEmpty())
|
||||
wname = firmware->getAnalogInputName(4 + aIdx + i);
|
||||
|
||||
wname = RawSource(RawSourceType::SOURCE_TYPE_STICK, ttlSticks + i).toString(NULL, &radioSettings);
|
||||
RadioKnobWidget * pot = new RadioKnobWidget(Board::PotType(radioSettings.potConfig[i]), wname, 0, ui->radioWidgetsHT);
|
||||
pot->setIndex(aIdx + i);
|
||||
// FIXME : total hack here -- this needs to follow the exception in radio/src/mixer.cpp:evalInputs()
|
||||
if (i == 0 && IS_TARANIS(board) && !IS_TARANIS_X7(board))
|
||||
pot->setInvertValue(true);
|
||||
pot->setIndex(i);
|
||||
ui->radioWidgetsHTLayout->insertWidget(midpos++, pot);
|
||||
analogs.append(pot);
|
||||
|
||||
m_radioWidgets.append(pot);
|
||||
}
|
||||
|
||||
aIdx += i;
|
||||
|
||||
// faders between sticks
|
||||
int r = 0, c = 0;
|
||||
for (i = 0; i < getBoardCapability(board, Board::Sliders) && i + aIdx < CPN_MAX_POTS; ++i) {
|
||||
int c = extraTrims / 2; // leave space for any extra trims
|
||||
for (i = 0; i < ttlFaders; ++i) {
|
||||
if (!radioSettings.isSliderAvailable(i))
|
||||
continue;
|
||||
|
||||
if ((wname = QString(radioSettings.sliderName[i])).isEmpty())
|
||||
wname = firmware->getAnalogInputName(4 + aIdx + i);
|
||||
|
||||
wname = RawSource(RawSourceType::SOURCE_TYPE_STICK, ttlSticks + ttlKnobs + i).toString(NULL, &radioSettings);
|
||||
RadioFaderWidget * sl = new RadioFaderWidget(wname, 0, ui->radioWidgetsVC);
|
||||
sl->setIndex(aIdx + i);
|
||||
// FIXME : total hack here -- this needs to follow the exception in radio/src/mixer.cpp:evalInputs()
|
||||
if (i == 0 && IS_TARANIS(board) && !IS_TARANIS_X7(board))
|
||||
sl->setInvertValue(true);
|
||||
/* 2-row option
|
||||
if (!(i % 2)) {
|
||||
++r;
|
||||
c = 0;
|
||||
} */
|
||||
ui->VCGridLayout->addWidget(sl, r, c++, 1, 1);
|
||||
analogs.append(sl);
|
||||
sl->setIndex(i);
|
||||
ui->VCGridLayout->addWidget(sl, 0, c++, 1, 1);
|
||||
|
||||
m_radioWidgets.append(sl);
|
||||
}
|
||||
|
||||
// extra trims around faders
|
||||
int tridx = Board::TRIM_AXIS_COUNT;
|
||||
int trswidx = Board::TRIM_SW_COUNT;
|
||||
for (i = extraTrims; i > 0; --i) {
|
||||
trswidx -= 2;
|
||||
--tridx;
|
||||
wname = RawSource(RawSourceType::SOURCE_TYPE_TRIM, tridx).toString(NULL, &radioSettings);
|
||||
wname = wname.left(1) % wname.right(1);
|
||||
RadioTrimWidget * tw = new RadioTrimWidget(Qt::Vertical, ui->radioWidgetsVC);
|
||||
tw->setIndices(tridx, trswidx, trswidx + 1);
|
||||
tw->setLabelText(wname);
|
||||
if (i <= extraTrims / 2)
|
||||
c = 0;
|
||||
ui->VCGridLayout->addWidget(tw, 0, c++, 1, 1);
|
||||
|
||||
connect(simulator, &SimulatorInterface::trimValueChange, tw, &RadioTrimWidget::setTrimValue);
|
||||
connect(simulator, &SimulatorInterface::trimRangeChange, tw, &RadioTrimWidget::setTrimRangeQual);
|
||||
m_radioWidgets.append(tw);
|
||||
}
|
||||
|
||||
// connect all the widgets
|
||||
foreach (RadioWidget * rw, m_radioWidgets) {
|
||||
connect(rw, &RadioWidget::valueChange, this, &SimulatorWidget::onRadioWidgetValueChange);
|
||||
connect(this, &SimulatorWidget::widgetValueChange, rw, &RadioWidget::setValueQual);
|
||||
connect(this, &SimulatorWidget::widgetStateChange, rw, &RadioWidget::setStateData);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void SimulatorWidget::setupJoysticks()
|
||||
|
@ -600,6 +641,10 @@ void SimulatorWidget::setupJoysticks()
|
|||
joystick->deadzones[j] = 0;
|
||||
}
|
||||
connect(joystick, &Joystick::axisValueChanged, this, &SimulatorWidget::onjoystickAxisValueChanged);
|
||||
if (vJoyLeft)
|
||||
connect(this, &SimulatorWidget::stickValueChange, vJoyLeft, &VirtualJoystickWidget::setStickAxisValue);
|
||||
if (vJoyRight)
|
||||
connect(this, &SimulatorWidget::stickValueChange, vJoyRight, &VirtualJoystickWidget::setStickAxisValue);
|
||||
joysticksEnabled = true;
|
||||
}
|
||||
else {
|
||||
|
@ -609,164 +654,50 @@ void SimulatorWidget::setupJoysticks()
|
|||
else if (joystick) {
|
||||
joystick->close();
|
||||
disconnect(joystick, 0, this, 0);
|
||||
if (vJoyLeft)
|
||||
disconnect(this, 0, vJoyLeft, 0);
|
||||
if (vJoyRight)
|
||||
disconnect(this, 0, vJoyRight, 0);
|
||||
joystick->deleteLater();
|
||||
joystick = NULL;
|
||||
}
|
||||
if (vJoyRight) {
|
||||
vJoyRight->setStickConstraint(VirtualJoystickWidget::HOLD_X, joysticksEnabled);
|
||||
vJoyRight->setStickConstraint(VirtualJoystickWidget::HOLD_Y, joysticksEnabled);
|
||||
}
|
||||
if (vJoyLeft) {
|
||||
vJoyLeft->setStickConstraint(VirtualJoystickWidget::HOLD_X, joysticksEnabled);
|
||||
vJoyLeft->setStickConstraint(VirtualJoystickWidget::HOLD_Y, joysticksEnabled);
|
||||
}
|
||||
if (vJoyRight)
|
||||
vJoyRight->setStickConstraint((VirtualJoystickWidget::HOLD_X | VirtualJoystickWidget::HOLD_Y), joysticksEnabled);
|
||||
if (vJoyLeft)
|
||||
vJoyLeft->setStickConstraint((VirtualJoystickWidget::HOLD_X | VirtualJoystickWidget::HOLD_Y), joysticksEnabled);
|
||||
#endif
|
||||
}
|
||||
|
||||
void SimulatorWidget::setupTimer()
|
||||
{
|
||||
if (!timer)
|
||||
return;
|
||||
|
||||
if (timer->isActive())
|
||||
timer->stop();
|
||||
|
||||
timer->start();
|
||||
}
|
||||
|
||||
void SimulatorWidget::restoreRadioWidgetsState()
|
||||
{
|
||||
// All RadioWidgets
|
||||
RadioWidget::RadioWidgetState state;
|
||||
QMap<int, QByteArray> switchesMap;
|
||||
QMap<int, QByteArray> analogsMap;
|
||||
QList<QByteArray> states = g.profile[radioProfileId].simulatorOptions().controlsState;
|
||||
|
||||
foreach (QByteArray ba, states) {
|
||||
QDataStream stream(ba);
|
||||
stream >> state;
|
||||
if (state.type == RadioWidget::RADIO_WIDGET_SWITCH)
|
||||
switchesMap.insert(state.index, ba);
|
||||
else
|
||||
analogsMap.insert(state.index, ba);
|
||||
}
|
||||
|
||||
for (int i = 0; i < analogs.size(); ++i) {
|
||||
if (analogsMap.contains(analogs[i]->getIndex()))
|
||||
analogs[i]->setStateData(analogsMap.value(analogs[i]->getIndex()));
|
||||
}
|
||||
|
||||
for (int i = 0; i < switches.size(); ++i) {
|
||||
if (switchesMap.contains(switches[i]->getIndex()))
|
||||
switches[i]->setStateData(switchesMap.value(switches[i]->getIndex()));
|
||||
emit widgetStateChange(state);
|
||||
}
|
||||
|
||||
// Set throttle stick down and locked, side depends on mode
|
||||
if (radioSettings.stickMode & 1) {
|
||||
vJoyLeft->setStickConstraint(VirtualJoystickWidget::HOLD_Y, true);
|
||||
vJoyLeft->setStickY(1);
|
||||
}
|
||||
else {
|
||||
vJoyRight->setStickConstraint(VirtualJoystickWidget::HOLD_Y, true);
|
||||
vJoyRight->setStickY(1);
|
||||
}
|
||||
emit stickModeChange(radioSettings.stickMode);
|
||||
|
||||
// TODO : custom voltages
|
||||
qint16 volts = radioSettings.vBatWarn + 2;
|
||||
if (firmware->getCapability(Capability::HasBatMeterRange))
|
||||
volts = (radioSettings.vBatMin + 90) + ((radioSettings.vBatMax + 30) - radioSettings.vBatMin) / 2;
|
||||
emit inputValueChange(SimulatorInterface::INPUT_SRC_TXVIN, 0, volts);
|
||||
}
|
||||
|
||||
QList<QByteArray> SimulatorWidget::saveRadioWidgetsState()
|
||||
void SimulatorWidget::saveRadioWidgetsState(QList<QByteArray> & state)
|
||||
{
|
||||
QList<QByteArray> states;
|
||||
|
||||
for (int i = 0; i < analogs.size(); ++i)
|
||||
states.append(analogs[i]->getStateData());
|
||||
|
||||
for (int i = 0; i < switches.size(); ++i)
|
||||
states.append(switches[i]->getStateData());
|
||||
|
||||
return states;
|
||||
}
|
||||
|
||||
/*
|
||||
* Input/Output handlers for SimulatorInterface
|
||||
*/
|
||||
|
||||
// Read various values from firmware simulator and populate values in this UI
|
||||
void SimulatorWidget::setValues()
|
||||
{
|
||||
int currentPhase = simulator->getPhase();
|
||||
|
||||
// display current flight mode in window title
|
||||
if (currentPhase != lastPhase) {
|
||||
lastPhase = currentPhase;
|
||||
QString phase_name = QString(simulator->getPhaseName(currentPhase));
|
||||
if (phase_name.isEmpty())
|
||||
phase_name = QString::number(currentPhase);
|
||||
setWindowTitle(windowName + QString(" - Flight Mode %1").arg(phase_name));
|
||||
if (m_radioWidgets.size()) {
|
||||
state.clear();
|
||||
foreach (RadioWidget * rw, m_radioWidgets)
|
||||
state.append(rw->getStateData());
|
||||
}
|
||||
}
|
||||
|
||||
// "get" values from this UI and send them to the firmware simulator.
|
||||
void SimulatorWidget::getValues()
|
||||
{
|
||||
static const int numTrims = getBoardCapability(m_board, Board::NumTrimSwitches);
|
||||
int i;
|
||||
|
||||
TxInputs inp;
|
||||
memset(&inp, 0, sizeof(TxInputs));
|
||||
|
||||
inp.sticks[0] = int(1024 * vJoyLeft->getStickX()); // LEFT HORZ
|
||||
inp.sticks[1] = int(-1024 * vJoyLeft->getStickY()); // LEFT VERT
|
||||
inp.sticks[2] = int(-1024 * vJoyRight->getStickY()); // RGHT VERT
|
||||
inp.sticks[3] = int(1024 * vJoyRight->getStickX()); // RGHT HORZ
|
||||
|
||||
for (i = 0; i < analogs.size() && i < CPN_MAX_POTS; ++i)
|
||||
inp.pots[analogs[i]->getIndex()] = analogs[i]->getValue();
|
||||
|
||||
for (i = 0; i < switches.size() && i < CPN_MAX_SWITCHES; ++i)
|
||||
inp.switches[switches[i]->getIndex()] = switches[i]->getValue();
|
||||
|
||||
for (i = 0; i < numTrims; ++i)
|
||||
inp.trims[i] = (trimPressed == i);
|
||||
|
||||
foreach (RadioUiAction * act, radioUiWidget->getActions()) {
|
||||
if (act->getIndex() > -1 && act->getIndex() < CPN_MAX_KEYS)
|
||||
inp.keys[act->getIndex()] = act->isActive();
|
||||
}
|
||||
|
||||
if (radioUiWidget->getRotEncAction())
|
||||
inp.rotenc = radioUiWidget->getRotEncAction()->isActive();
|
||||
|
||||
simulator->setValues(inp);
|
||||
}
|
||||
|
||||
// Read stick trim values from firmware simulator and set joystick widgets as needed.
|
||||
void SimulatorWidget::setTrims()
|
||||
{
|
||||
using namespace Board;
|
||||
static Trims lastTrims;
|
||||
Trims trims;
|
||||
simulator->getTrims(trims);
|
||||
|
||||
if (trims.values[TRIM_AXIS_LH] != lastTrims.values[TRIM_AXIS_LH])
|
||||
vJoyLeft->setTrimValue(TRIM_AXIS_LH, trims.values[TRIM_AXIS_LH]);
|
||||
if (trims.values[TRIM_AXIS_LV] != lastTrims.values[TRIM_AXIS_LV])
|
||||
vJoyLeft->setTrimValue(TRIM_AXIS_LV, trims.values[TRIM_AXIS_LV]);
|
||||
if (trims.values[TRIM_AXIS_RV] != lastTrims.values[TRIM_AXIS_RV])
|
||||
vJoyRight->setTrimValue(TRIM_AXIS_RV, trims.values[TRIM_AXIS_RV]);
|
||||
if (trims.values[TRIM_AXIS_RH] != lastTrims.values[TRIM_AXIS_RH])
|
||||
vJoyRight->setTrimValue(TRIM_AXIS_RH, trims.values[TRIM_AXIS_RH]);
|
||||
|
||||
if (trims.extended != lastTrims.extended) {
|
||||
int trimMin = -125, trimMax = +125;
|
||||
if (trims.extended) {
|
||||
trimMin = -500;
|
||||
trimMax = +500;
|
||||
}
|
||||
vJoyLeft->setTrimRange(TRIM_AXIS_LH, trimMin, trimMax);
|
||||
vJoyLeft->setTrimRange(TRIM_AXIS_LV, trimMin, trimMax);
|
||||
vJoyRight->setTrimRange(TRIM_AXIS_RV, trimMin, trimMax);
|
||||
vJoyRight->setTrimRange(TRIM_AXIS_RH, trimMin, trimMax);
|
||||
}
|
||||
lastTrims = trims;
|
||||
}
|
||||
|
||||
/*
|
||||
* Event handlers/private slots
|
||||
|
@ -792,51 +723,95 @@ void SimulatorWidget::wheelEvent(QWheelEvent *event)
|
|||
|
||||
void SimulatorWidget::onTimerEvent()
|
||||
{
|
||||
static unsigned int lcd_counter = 0;
|
||||
if (m_heartbeatTimer.isValid() && m_heartbeatTimer.hasExpired(m_timer.interval())) {
|
||||
onSimulatorError("Heartbeat timeout!");
|
||||
onSimulatorStopped();
|
||||
}
|
||||
}
|
||||
|
||||
if (!simulator->timer10ms()) {
|
||||
QMessageBox::critical(this, "Companion", tr("Firmware %1 error: %2").arg(firmware->getName()).arg(simulator->getError()));
|
||||
timer->stop();
|
||||
void SimulatorWidget::onSimulatorHeartbeat(qint32 loops, qint64 timestamp)
|
||||
{
|
||||
Q_UNUSED(loops)
|
||||
Q_UNUSED(timestamp)
|
||||
m_heartbeatTimer.start();
|
||||
|
||||
#if 0
|
||||
static qint64 lastTs = 0;
|
||||
if (!(loops % 1000)) {
|
||||
qDebug() << "loops:" << loops << "ts:" << timestamp << "ts-delta:" << timestamp - lastTs << "This:" << QThread::currentThread() << "Simu:" << simulator->thread();
|
||||
lastTs = timestamp;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void SimulatorWidget::onSimulatorError(const QString & error)
|
||||
{
|
||||
QMessageBox::critical(this, windowName, tr("Radio firmware error: %1").arg(error.isEmpty() ? "Unknown reason" : error));
|
||||
}
|
||||
|
||||
void SimulatorWidget::onPhaseChanged(qint32 phase, const QString & name)
|
||||
{
|
||||
setWindowTitle(windowName + tr(" - Flight Mode %1 (#%2)").arg(name).arg(phase));
|
||||
}
|
||||
|
||||
void SimulatorWidget::onRadioWidgetValueChange(const RadioWidget::RadioWidgetType type, const int index, int value)
|
||||
{
|
||||
//qDebug() << type << index << value;
|
||||
if (!simulator || index < 0)
|
||||
return;
|
||||
|
||||
SimulatorInterface::InputSourceType inpType = SimulatorInterface::INPUT_SRC_NONE;
|
||||
|
||||
switch (type) {
|
||||
case RadioWidget::RADIO_WIDGET_SWITCH :
|
||||
inpType = SimulatorInterface::INPUT_SRC_SWITCH;
|
||||
break;
|
||||
|
||||
case RadioWidget::RADIO_WIDGET_KNOB :
|
||||
inpType = SimulatorInterface::INPUT_SRC_KNOB;
|
||||
break;
|
||||
|
||||
case RadioWidget::RADIO_WIDGET_FADER :
|
||||
inpType = SimulatorInterface::INPUT_SRC_SLIDER;
|
||||
break;
|
||||
|
||||
case RadioWidget::RADIO_WIDGET_TRIM :
|
||||
switch (value) {
|
||||
case RadioWidget::RADIO_TRIM_BTN_ON :
|
||||
inpType = SimulatorInterface::INPUT_SRC_TRIM_SW;
|
||||
value = 1;
|
||||
break;
|
||||
case RadioWidget::RADIO_TRIM_BTN_OFF :
|
||||
inpType = SimulatorInterface::INPUT_SRC_TRIM_SW;
|
||||
value = 0;
|
||||
break;
|
||||
default :
|
||||
inpType = SimulatorInterface::INPUT_SRC_TRIM;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case RadioWidget::RADIO_WIDGET_STICK :
|
||||
inpType = SimulatorInterface::INPUT_SRC_STICK;
|
||||
break;
|
||||
|
||||
case RadioWidget::RADIO_WIDGET_KEY :
|
||||
inpType = SimulatorInterface::INPUT_SRC_KEY;
|
||||
break;
|
||||
|
||||
default :
|
||||
return;
|
||||
}
|
||||
|
||||
getValues();
|
||||
|
||||
if (!(lcd_counter++ % 5)) {
|
||||
setValues();
|
||||
setTrims();
|
||||
centerSticks();
|
||||
}
|
||||
}
|
||||
|
||||
void SimulatorWidget::onTrimPressed(int index)
|
||||
{
|
||||
trimPressed = index;
|
||||
}
|
||||
|
||||
void SimulatorWidget::onTrimReleased(int)
|
||||
{
|
||||
trimPressed = 255;
|
||||
}
|
||||
|
||||
void SimulatorWidget::onTrimSliderMoved(int index, int value)
|
||||
{
|
||||
simulator->setTrim(index, value);
|
||||
}
|
||||
|
||||
void SimulatorWidget::centerSticks()
|
||||
{
|
||||
if (vJoyLeft)
|
||||
vJoyLeft->centerStick();
|
||||
|
||||
if (vJoyRight)
|
||||
vJoyRight->centerStick();
|
||||
emit inputValueChange(inpType, index, value);
|
||||
}
|
||||
|
||||
void SimulatorWidget::onjoystickAxisValueChanged(int axis, int value)
|
||||
{
|
||||
#ifdef JOYSTICKS
|
||||
static const int ttlSticks = CPN_MAX_STICKS;
|
||||
static const int ttlSticks = 4;
|
||||
static const int ttlKnobs = Boards::getCapability(m_board, Board::Pots);
|
||||
static const int ttlFaders = Boards::getCapability(m_board, Board::Sliders);
|
||||
static const int valueRange = 1024;
|
||||
|
||||
if (!joystick || axis >= MAX_JOYSTICKS)
|
||||
|
@ -845,7 +820,7 @@ void SimulatorWidget::onjoystickAxisValueChanged(int axis, int value)
|
|||
int dlta;
|
||||
int stick = g.joystick[axis].stick_axe();
|
||||
|
||||
if (stick < 0 || stick >= ttlSticks + analogs.count())
|
||||
if (stick < 0 || stick >= ttlSticks + ttlKnobs + ttlFaders)
|
||||
return;
|
||||
|
||||
int stickval = valueRange * (value - g.joystick[axis].stick_med());
|
||||
|
@ -861,20 +836,15 @@ void SimulatorWidget::onjoystickAxisValueChanged(int axis, int value)
|
|||
if (g.joystick[axis].stick_inv())
|
||||
stickval *= -1;
|
||||
|
||||
if (stick==1 ) {
|
||||
vJoyRight->setStickY(-stickval/1024.0);
|
||||
if (stick < ttlSticks) {
|
||||
emit stickValueChange(stick, stickval);
|
||||
}
|
||||
else if (stick==2) {
|
||||
vJoyRight->setStickX(stickval/1024.0);
|
||||
}
|
||||
else if (stick==3) {
|
||||
vJoyLeft->setStickY(-stickval/1024.0);
|
||||
}
|
||||
else if (stick==4) {
|
||||
vJoyLeft->setStickX(stickval/1024.0);
|
||||
}
|
||||
else if (stick > ttlSticks) {
|
||||
analogs[stick-ttlSticks-1]->setValue(stickval);
|
||||
else {
|
||||
stick -= ttlSticks;
|
||||
if (stick < ttlKnobs)
|
||||
emit widgetValueChange(RadioWidget::RADIO_WIDGET_KNOB, stick, stickval);
|
||||
else
|
||||
emit widgetValueChange(RadioWidget::RADIO_WIDGET_FADER, stick, stickval);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue