1
0
Fork 0
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:
Max Paperno 2017-04-02 06:17:37 -04:00 committed by Bertrand Songis
parent 36bb951314
commit 57dc0159d6
46 changed files with 2064 additions and 1327 deletions

View file

@ -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